fix(seedgo): name-scope unused_function bypasses (kill silent line-drift)
unused_function bypasses matched by file+line; the line was the function's def line, so any code shift above it staled the bypass and silently re-flagged the exempted function, dropping the branch below 100% (S216/S217). Mechanism: is_bypassed() gains a 'functions' field + name param; name-scoped match takes precedence, 'lines' kept for back-compat (no other standard changes). unused_function_check passes the function name. +7 tests (1093), seedgo self-audit 100%, bypass schema documented. Migration: converted 10 line-scoped entries to functions: across drone/memory/skills; removed 3 dead memory/vector_search entries already pointing past EOF (file is 152 lines). drone/memory/skills re-audit: Unused_Function 100% (names verified live). Closes td-009. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QEQZXCtgnF3NQtcttTErpq
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
03c95e6881
commit
feecd263eb
@@ -18,6 +18,12 @@ PyPI version — not the changelog header.
|
||||
central audit collector. An in-place disabled module no longer trips a false
|
||||
"missing module" violation; disabled test files no longer inflate README test
|
||||
counts (td-103).
|
||||
- **seedgo `unused_function` bypasses are now name-scoped** — bypasses match by
|
||||
function name (`functions: [...]`) instead of line number (`lines: [...]`),
|
||||
which drifted silently when code shifted and re-flagged exempted functions
|
||||
(bit us S216/S217). `lines` stays supported for other standards. Migrated the
|
||||
10 existing line-scoped entries across drone/memory/skills and dropped 3 dead
|
||||
entries already pointing past EOF (td-009).
|
||||
|
||||
## [2026-06-23]
|
||||
|
||||
|
||||
@@ -252,25 +252,25 @@
|
||||
{
|
||||
"file": "apps/handlers/command_registry/ops.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [232, 263],
|
||||
"functions": ["update_command", "command_exists"],
|
||||
"reason": "Public CRUD API surface (update_command, command_exists) — tested, available for programmatic use. No CLI subcommand wired yet."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/git/pr_handler.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [108],
|
||||
"functions": ["create_pr"],
|
||||
"reason": "create_pr() is deprecated per FPLAN-0210 — pr command blocked at auth tier. Handler kept for backwards compatibility; still tested in test_git_module.py."
|
||||
},
|
||||
{
|
||||
"file": "apps/modules/git_module.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [665],
|
||||
"functions": ["get_introspective"],
|
||||
"reason": "get_introspective() called dynamically via getattr() by module_registry_handler.py:219 for internal module introspection. Also tested in test_git_module, test_system_pr, test_devpulse_plugins, test_git_access."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/broker/daemon.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [422],
|
||||
"functions": ["start_background"],
|
||||
"reason": "Threaded broker entrypoint, exercised by tests/test_broker.py; production uses blocking start(); intentionally not called in shipped non-test code."
|
||||
}
|
||||
],
|
||||
|
||||
@@ -559,31 +559,13 @@
|
||||
{
|
||||
"file": "apps/handlers/learnings/manager.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [759],
|
||||
"functions": ["update_status_counts"],
|
||||
"reason": "Public API for learning extraction pipeline — called dynamically by symbolic extraction."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/search/vector_search.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [247],
|
||||
"reason": "Public API surface for vector search — legacy handler retained for direct-import consumers."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/search/vector_search.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [316],
|
||||
"reason": "Public API surface for vector search — legacy handler retained for direct-import consumers."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/search/vector_search.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [380],
|
||||
"reason": "Public API surface for vector search — legacy handler retained for direct-import consumers."
|
||||
},
|
||||
{
|
||||
"file": "apps/handlers/symbolic/extractor.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [455],
|
||||
"functions": ["analyze_conversation_llm"],
|
||||
"reason": "LLM-based extraction function — called conditionally when API key is available."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,3 +3,26 @@
|
||||
Seedgo audit bypass config for `SEEDGO`.
|
||||
|
||||
When an audit flags a false positive that doesn't apply to your architecture, add a bypass entry in `bypass.json` with a reason explaining why it's justified.
|
||||
|
||||
## Fields
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `file` | yes | Relative path from branch root |
|
||||
| `standard` | yes | Standard name (cli, imports, naming, etc.) |
|
||||
| `lines` | no | Specific line numbers to bypass |
|
||||
| `functions` | no | Function names for name-scoped bypass (required for `unused_function`) |
|
||||
| `reason` | yes | Why this bypass exists |
|
||||
|
||||
## Name-scoped bypass (unused_function)
|
||||
|
||||
Use `functions` instead of `lines` for `unused_function` bypasses — function names are stable across edits, line numbers drift silently:
|
||||
|
||||
```json
|
||||
{
|
||||
"file": "apps/handlers/registry.py",
|
||||
"standard": "unused_function",
|
||||
"functions": ["get_skill", "get_skill_names"],
|
||||
"reason": "Public API surface — called by external consumers"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -277,8 +277,7 @@ def check_branch(branch_path: str, bypass_rules: list | None = None) -> dict:
|
||||
unused_functions: list[dict] = []
|
||||
|
||||
for func_name, lineno, py_file in all_functions:
|
||||
# Check bypass at file+line level
|
||||
if is_bypassed(str(py_file), "unused_function", lineno, bypass_rules):
|
||||
if is_bypassed(str(py_file), "unused_function", lineno, bypass_rules, name=func_name):
|
||||
continue
|
||||
|
||||
total_refs = _count_references_in_corpus(func_name, corpus)
|
||||
|
||||
@@ -37,14 +37,13 @@ BYPASS_TEMPLATE = {
|
||||
"file": "apps/modules/logger.py",
|
||||
"standard": "cli",
|
||||
"lines": [146, 177],
|
||||
"pattern": "if __name__ == '__main__'",
|
||||
"reason": "Circular dependency - logger cannot import CLI",
|
||||
},
|
||||
"fields": {
|
||||
"file": "Relative path from branch root (required)",
|
||||
"standard": "Standard name: cli, imports, naming, etc. (required)",
|
||||
"lines": "Optional - specific line numbers to bypass",
|
||||
"pattern": "Optional - pattern to match (e.g. 'if __name__')",
|
||||
"functions": "Optional - list of function names for name-scoped bypass (required for unused_function)",
|
||||
"reason": "Required - why this bypass exists",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ def is_bypassed(
|
||||
standard: str,
|
||||
line: int | None = None,
|
||||
bypass_rules: list | None = None,
|
||||
name: str | None = None,
|
||||
) -> bool:
|
||||
"""Check if a violation should be bypassed.
|
||||
|
||||
@@ -26,6 +27,7 @@ def is_bypassed(
|
||||
standard: Standard name (e.g., 'cli', 'imports')
|
||||
line: Optional specific line number of the violation
|
||||
bypass_rules: List of bypass rules from .seedgo/bypass.json
|
||||
name: Optional function/symbol name for name-scoped bypasses
|
||||
|
||||
Returns:
|
||||
True if this violation should be bypassed
|
||||
@@ -40,15 +42,20 @@ def is_bypassed(
|
||||
rule_file = rule.get("file", "")
|
||||
if rule_file and rule_file not in file_path_posix:
|
||||
continue
|
||||
rule_lines = rule.get("lines", [])
|
||||
if rule_lines and line is not None and line not in rule_lines:
|
||||
continue
|
||||
functions = rule.get("functions")
|
||||
if functions and name is not None:
|
||||
if name not in functions:
|
||||
continue
|
||||
elif rule.get("lines") and line is not None:
|
||||
if line not in rule["lines"]:
|
||||
continue
|
||||
json_handler.log_operation(
|
||||
"bypass_matched",
|
||||
{
|
||||
"file": file_path,
|
||||
"standard": standard,
|
||||
"line": line,
|
||||
"name": name,
|
||||
"rule_file": rule_file,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -44,6 +44,7 @@ def _mock_infrastructure(monkeypatch):
|
||||
for mod_name in [
|
||||
"aipass.seedgo.apps.handlers.bypass.bypass_handler",
|
||||
"aipass.seedgo.apps.handlers.bypass.ignore_handler",
|
||||
"aipass.seedgo.apps.handlers.bypass.utils",
|
||||
]:
|
||||
monkeypatch.delitem(sys.modules, mod_name, raising=False)
|
||||
|
||||
@@ -210,3 +211,214 @@ def test_get_deprecated_patterns_returns_dict():
|
||||
for key, value in patterns.items():
|
||||
assert isinstance(key, str)
|
||||
assert isinstance(value, str)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests -- utils.is_bypassed name-scoped bypass
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_utils_name_match_suppresses_regardless_of_line():
|
||||
"""Name-scoped bypass matches by function name, ignoring line number."""
|
||||
from aipass.seedgo.apps.handlers.bypass.utils import is_bypassed
|
||||
|
||||
rules = [
|
||||
{
|
||||
"file": "apps/ops.py",
|
||||
"standard": "unused_function",
|
||||
"functions": ["update_command"],
|
||||
"reason": "public API",
|
||||
}
|
||||
]
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/ops.py",
|
||||
"unused_function",
|
||||
line=999,
|
||||
bypass_rules=rules,
|
||||
name="update_command",
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
|
||||
def test_utils_name_not_in_functions_list():
|
||||
"""Name-scoped bypass rejects function not in the functions list."""
|
||||
from aipass.seedgo.apps.handlers.bypass.utils import is_bypassed
|
||||
|
||||
rules = [
|
||||
{
|
||||
"file": "apps/ops.py",
|
||||
"standard": "unused_function",
|
||||
"functions": ["update_command"],
|
||||
"reason": "public API",
|
||||
}
|
||||
]
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/ops.py",
|
||||
"unused_function",
|
||||
line=232,
|
||||
bypass_rules=rules,
|
||||
name="delete_command",
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def test_utils_line_drift_no_longer_breaks_name_scoped():
|
||||
"""Line drift doesn't affect name-scoped bypass — name is stable."""
|
||||
from aipass.seedgo.apps.handlers.bypass.utils import is_bypassed
|
||||
|
||||
rules = [
|
||||
{
|
||||
"file": "apps/ops.py",
|
||||
"standard": "unused_function",
|
||||
"functions": ["get_skill", "get_skill_names"],
|
||||
"reason": "public API",
|
||||
}
|
||||
]
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/ops.py",
|
||||
"unused_function",
|
||||
line=52,
|
||||
bypass_rules=rules,
|
||||
name="get_skill",
|
||||
)
|
||||
is True
|
||||
)
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/ops.py",
|
||||
"unused_function",
|
||||
line=9999,
|
||||
bypass_rules=rules,
|
||||
name="get_skill",
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
|
||||
def test_utils_functions_present_name_none_falls_back_to_lines():
|
||||
"""When functions is set but name=None (other checker), fall back to line matching."""
|
||||
from aipass.seedgo.apps.handlers.bypass.utils import is_bypassed
|
||||
|
||||
rules = [
|
||||
{
|
||||
"file": "apps/ops.py",
|
||||
"standard": "unused_function",
|
||||
"functions": ["update_command"],
|
||||
"lines": [10],
|
||||
"reason": "test",
|
||||
}
|
||||
]
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/ops.py",
|
||||
"unused_function",
|
||||
line=10,
|
||||
bypass_rules=rules,
|
||||
name=None,
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
|
||||
def test_utils_existing_lines_only_rules_still_work():
|
||||
"""Existing line-only rules (no functions field) still match by line."""
|
||||
from aipass.seedgo.apps.handlers.bypass.utils import is_bypassed
|
||||
|
||||
rules = [
|
||||
{
|
||||
"file": "apps/foo.py",
|
||||
"standard": "cli",
|
||||
"lines": [10, 20],
|
||||
"reason": "circular",
|
||||
}
|
||||
]
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/foo.py",
|
||||
"cli",
|
||||
line=10,
|
||||
bypass_rules=rules,
|
||||
)
|
||||
is True
|
||||
)
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/foo.py",
|
||||
"cli",
|
||||
line=99,
|
||||
bypass_rules=rules,
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def test_utils_file_only_bypass_still_matches():
|
||||
"""File-level bypass (no lines, no functions) still works."""
|
||||
from aipass.seedgo.apps.handlers.bypass.utils import is_bypassed
|
||||
|
||||
rules = [
|
||||
{
|
||||
"file": "apps/foo.py",
|
||||
"standard": "unused_function",
|
||||
"reason": "whole file bypassed",
|
||||
}
|
||||
]
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/foo.py",
|
||||
"unused_function",
|
||||
line=50,
|
||||
bypass_rules=rules,
|
||||
name="anything",
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
|
||||
def test_utils_multiple_functions_in_one_rule():
|
||||
"""A single rule can list multiple function names."""
|
||||
from aipass.seedgo.apps.handlers.bypass.utils import is_bypassed
|
||||
|
||||
rules = [
|
||||
{
|
||||
"file": "apps/registry.py",
|
||||
"standard": "unused_function",
|
||||
"functions": ["get_skill", "get_skill_names"],
|
||||
"reason": "public API",
|
||||
}
|
||||
]
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/registry.py",
|
||||
"unused_function",
|
||||
line=1,
|
||||
bypass_rules=rules,
|
||||
name="get_skill",
|
||||
)
|
||||
is True
|
||||
)
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/registry.py",
|
||||
"unused_function",
|
||||
line=1,
|
||||
bypass_rules=rules,
|
||||
name="get_skill_names",
|
||||
)
|
||||
is True
|
||||
)
|
||||
assert (
|
||||
is_bypassed(
|
||||
"/branch/apps/registry.py",
|
||||
"unused_function",
|
||||
line=1,
|
||||
bypass_rules=rules,
|
||||
name="other_func",
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
@@ -39,8 +39,7 @@
|
||||
{
|
||||
"file": "apps/handlers/registry.py",
|
||||
"standard": "unused_function",
|
||||
"lines": [52, 68],
|
||||
"pattern": "def get_skill|def get_skill_names",
|
||||
"functions": ["get_skill", "get_skill_names"],
|
||||
"reason": "Public API functions — used by test_registry.py and available for external callers; part of the registry module's contract"
|
||||
}
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user