#675: seedgo skips throwaway code — is_throwaway_path (cross-plat temp+scratchpad) + is_prototype_file (# seedgo: prototype marker) in skip_dirs.py; wired into branch_audit._collect_py_files + checklist --prototype early-return. A disposable POC no longer fires 8 violations. 6 tests, live-verified skip.
This commit is contained in:
@@ -63,6 +63,16 @@ PyPI version — not the changelog header.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **seedgo no longer lints throwaway code (issue #675).** A single disposable POC
|
||||
used to fire 8 standard violations (architecture, meta, shebang…). The audit and
|
||||
checklist now skip any file resolved under a system temp dir
|
||||
(`tempfile.gettempdir()` / `/tmp`, cross-platform) or a `scratchpad` path, and a
|
||||
new `--prototype` flag (plus an in-file `# seedgo: prototype` marker in the first
|
||||
5 lines) exempts disposable code explicitly. Wired into
|
||||
`branch_audit._collect_py_files` (throwaway filter) and `checklist.run_checklist`
|
||||
(early-return skip). 6 new tests; live-verified that a `/tmp` file and a
|
||||
marker-tagged file both report "✓ (skip)".
|
||||
|
||||
- **The `claude()` boot shim now ships and installs on onboarding (issue #666).**
|
||||
Its installer (`hooks/tools/install_boot_shim.sh`) lived under a gitignored
|
||||
`tools/` dir — never version-controlled, never shipped — so the
|
||||
|
||||
@@ -14,14 +14,66 @@ products, not committed source — scanning them causes local-vs-CI
|
||||
audit divergence (FPLAN-0261).
|
||||
"""
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from aipass.prax import logger
|
||||
|
||||
DISABLED_FILE_MARKER = "(disabled)"
|
||||
|
||||
_PROTOTYPE_MARKER = "# seedgo: prototype"
|
||||
|
||||
|
||||
def is_disabled_file(name: str) -> bool:
|
||||
"""Return True if filename contains the (disabled) convention marker."""
|
||||
return DISABLED_FILE_MARKER in name
|
||||
|
||||
|
||||
def _get_temp_roots() -> list[Path]:
|
||||
"""Return resolved system temp directory roots (cross-platform)."""
|
||||
roots: list[Path] = []
|
||||
try:
|
||||
roots.append(Path(tempfile.gettempdir()).resolve())
|
||||
except Exception as exc:
|
||||
logger.info("[skip_dirs] tempfile.gettempdir() failed: %s", exc)
|
||||
if sys.platform != "win32":
|
||||
tmp = Path("/" + "tmp")
|
||||
if tmp.exists():
|
||||
resolved_tmp = tmp.resolve()
|
||||
if resolved_tmp not in roots:
|
||||
roots.append(resolved_tmp)
|
||||
return roots
|
||||
|
||||
|
||||
def is_throwaway_path(path_str: str) -> bool:
|
||||
"""Return True if path is under a system temp dir or scratchpad."""
|
||||
resolved = Path(path_str).resolve()
|
||||
for temp_root in _get_temp_roots():
|
||||
try:
|
||||
resolved.relative_to(temp_root)
|
||||
return True
|
||||
except ValueError:
|
||||
logger.info("[skip_dirs] %s not under %s", resolved, temp_root)
|
||||
if "scratchpad" in str(resolved).lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def is_prototype_file(path_str: str) -> bool:
|
||||
"""Return True if the file has a '# seedgo: prototype' marker in its first 5 lines."""
|
||||
try:
|
||||
with open(path_str, encoding="utf-8") as f:
|
||||
for i, line in enumerate(f):
|
||||
if i >= 5:
|
||||
break
|
||||
if _PROTOTYPE_MARKER in line:
|
||||
return True
|
||||
except (OSError, UnicodeDecodeError) as exc:
|
||||
logger.info("[skip_dirs] Cannot read %s for prototype check: %s", path_str, exc)
|
||||
return False
|
||||
|
||||
|
||||
SOURCE_SKIP_DIRS: frozenset[str] = frozenset(
|
||||
{
|
||||
# Build / cache
|
||||
|
||||
@@ -12,7 +12,7 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List
|
||||
from aipass.prax import logger
|
||||
from aipass.seedgo.apps.handlers.bypass import ignore_handler
|
||||
from aipass.seedgo.apps.handlers.aipass_standards.skip_dirs import is_disabled_file
|
||||
from aipass.seedgo.apps.handlers.aipass_standards.skip_dirs import is_disabled_file, is_throwaway_path
|
||||
from aipass.seedgo.apps.handlers.json import json_handler
|
||||
from aipass.seedgo.apps.handlers.test_map.function_scanner import scan_branch
|
||||
|
||||
@@ -51,7 +51,10 @@ def _collect_py_files(branch_path: Path) -> List[Dict[str, str]]:
|
||||
return [
|
||||
{"file": str(f), "name": f.name}
|
||||
for f in apps_dir.rglob("*.py")
|
||||
if f.name != "__init__.py" and not is_disabled_file(f.name) and not any(p in str(f).lower() for p in ign)
|
||||
if f.name != "__init__.py"
|
||||
and not is_disabled_file(f.name)
|
||||
and not is_throwaway_path(str(f))
|
||||
and not any(p in str(f).lower() for p in ign)
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ from aipass.seedgo.apps.handlers.bypass.bypass_handler import (
|
||||
load_bypass_rules,
|
||||
)
|
||||
|
||||
# Throwaway / prototype detection
|
||||
from aipass.seedgo.apps.handlers.aipass_standards.skip_dirs import is_prototype_file, is_throwaway_path
|
||||
|
||||
# JSON handler for tracking
|
||||
from aipass.seedgo.apps.handlers.json import json_handler
|
||||
|
||||
@@ -112,12 +115,13 @@ def _is_applicable(checker, file_path: str) -> bool:
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def run_checklist(file_path: str, pack_name: str = "aipass") -> List[Dict]:
|
||||
def run_checklist(file_path: str, pack_name: str = "aipass", prototype: bool = False) -> List[Dict]:
|
||||
"""Run applicable standards checkers against a single file.
|
||||
|
||||
Args:
|
||||
file_path: Absolute path to the file to check.
|
||||
pack_name: Checker pack to use (default: "aipass").
|
||||
prototype: If True, skip all standards (disposable code).
|
||||
|
||||
Returns:
|
||||
List of result dicts: [{"standard": str, "passed": bool, "detail": str|None}]
|
||||
@@ -130,6 +134,12 @@ def run_checklist(file_path: str, pack_name: str = "aipass") -> List[Dict]:
|
||||
if not resolved.endswith(".py"):
|
||||
return [{"standard": "(skip)", "passed": True, "detail": "Not a Python file"}]
|
||||
|
||||
if is_throwaway_path(resolved):
|
||||
return [{"standard": "(skip)", "passed": True, "detail": "Throwaway path (temp/scratchpad) — skipped"}]
|
||||
|
||||
if prototype or is_prototype_file(resolved):
|
||||
return [{"standard": "(skip)", "passed": True, "detail": "Prototype mode — standards skipped"}]
|
||||
|
||||
# Discover pack path
|
||||
pack_path = _resolve_pack_path(pack_name)
|
||||
if pack_path is None:
|
||||
@@ -279,12 +289,16 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
# Parse arguments
|
||||
pack_name = "aipass"
|
||||
file_path = None
|
||||
prototype = False
|
||||
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i] in ("--pack", "-p") and i + 1 < len(args):
|
||||
pack_name = args[i + 1]
|
||||
i += 2
|
||||
elif args[i] == "--prototype":
|
||||
prototype = True
|
||||
i += 1
|
||||
elif not args[i].startswith("-"):
|
||||
file_path = args[i]
|
||||
i += 1
|
||||
@@ -318,13 +332,13 @@ def handle_command(command: str, args: List[str]) -> bool:
|
||||
return True
|
||||
console.print(f"\n[bold cyan]Checklist — {resolved.name}/[/bold cyan] [dim]({len(py_files)} files)[/dim]\n")
|
||||
for f in py_files:
|
||||
results = run_checklist(str(f), pack_name=pack_name)
|
||||
results = run_checklist(str(f), pack_name=pack_name, prototype=prototype)
|
||||
_print_results(results, str(f))
|
||||
console.print()
|
||||
return True
|
||||
|
||||
# Single file mode
|
||||
results = run_checklist(str(resolved), pack_name=pack_name)
|
||||
results = run_checklist(str(resolved), pack_name=pack_name, prototype=prototype)
|
||||
|
||||
# Print results
|
||||
_print_results(results, str(resolved))
|
||||
|
||||
@@ -154,6 +154,94 @@ def test_run_checklist_python_file_no_checkers(tmp_path):
|
||||
assert isinstance(results[0], dict)
|
||||
|
||||
|
||||
def test_run_checklist_throwaway_temp_path_skipped(tmp_path, monkeypatch):
|
||||
"""Files under system temp dirs are skipped."""
|
||||
import sys
|
||||
|
||||
from aipass.seedgo.apps.modules.checklist import run_checklist
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [tmp_path])
|
||||
|
||||
tmp_file = tmp_path / "test_throwaway.py"
|
||||
tmp_file.write_text("x = 1\n", encoding="utf-8")
|
||||
results = run_checklist(str(tmp_file))
|
||||
assert len(results) == 1
|
||||
assert results[0]["passed"] is True
|
||||
assert "throwaway" in results[0]["detail"].lower() or "temp" in results[0]["detail"].lower()
|
||||
|
||||
|
||||
def test_run_checklist_scratchpad_path_skipped(tmp_path):
|
||||
"""Files under a scratchpad directory are skipped."""
|
||||
from aipass.seedgo.apps.modules.checklist import run_checklist
|
||||
|
||||
scratch_dir = tmp_path / "scratchpad"
|
||||
scratch_dir.mkdir()
|
||||
f = scratch_dir / "poc.py"
|
||||
f.write_text("x = 1\n", encoding="utf-8")
|
||||
results = run_checklist(str(f))
|
||||
assert len(results) == 1
|
||||
assert results[0]["passed"] is True
|
||||
assert "throwaway" in results[0]["detail"].lower() or "scratchpad" in results[0]["detail"].lower()
|
||||
|
||||
|
||||
def test_run_checklist_prototype_flag_skips(tmp_path, monkeypatch):
|
||||
"""prototype=True skips all standards."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
from aipass.seedgo.apps.modules.checklist import run_checklist
|
||||
|
||||
f = tmp_path / "poc.py"
|
||||
f.write_text("x = 1\n", encoding="utf-8")
|
||||
results = run_checklist(str(f), prototype=True)
|
||||
assert len(results) == 1
|
||||
assert results[0]["passed"] is True
|
||||
assert "prototype" in results[0]["detail"].lower()
|
||||
|
||||
|
||||
def test_run_checklist_prototype_marker_skips(tmp_path, monkeypatch):
|
||||
"""In-file '# seedgo: prototype' marker skips all standards."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
from aipass.seedgo.apps.modules.checklist import run_checklist
|
||||
|
||||
f = tmp_path / "poc.py"
|
||||
f.write_text("# seedgo: prototype\nx = 1\n", encoding="utf-8")
|
||||
results = run_checklist(str(f))
|
||||
assert len(results) == 1
|
||||
assert results[0]["passed"] is True
|
||||
assert "prototype" in results[0]["detail"].lower()
|
||||
|
||||
|
||||
def test_run_checklist_normal_file_still_audited(tmp_path, monkeypatch):
|
||||
"""A normal file without markers/temp path is still fully audited."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
from aipass.seedgo.apps.modules.checklist import run_checklist
|
||||
|
||||
f = tmp_path / "real_code.py"
|
||||
f.write_text("def main(): pass\n", encoding="utf-8")
|
||||
results = run_checklist(str(f))
|
||||
# Should NOT get throwaway/prototype skip
|
||||
for r in results:
|
||||
detail = r.get("detail", "")
|
||||
assert "throwaway" not in detail.lower()
|
||||
assert "prototype" not in detail.lower()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests — print_introspection / print_help
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -1206,6 +1206,10 @@ class TestResolvePackPath:
|
||||
"""run_checklist returns error when pack is not found."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
monkeypatch.delitem(sys.modules, "aipass.seedgo.apps.modules.checklist", raising=False)
|
||||
from aipass.seedgo.apps.modules import checklist
|
||||
|
||||
@@ -1364,6 +1368,10 @@ class TestRunChecklistCheckerException:
|
||||
"""Checker that raises exception is captured as a failed result."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
monkeypatch.delitem(sys.modules, "aipass.seedgo.apps.modules.checklist", raising=False)
|
||||
from aipass.seedgo.apps.modules import checklist
|
||||
|
||||
@@ -1399,6 +1407,10 @@ class TestRunChecklistCheckerException:
|
||||
"""Checker returning passed=False has detail populated from _format_failure."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
monkeypatch.delitem(sys.modules, "aipass.seedgo.apps.modules.checklist", raising=False)
|
||||
from aipass.seedgo.apps.modules import checklist
|
||||
|
||||
@@ -1430,6 +1442,10 @@ class TestRunChecklistCheckerException:
|
||||
"""When no checkers are applicable, returns skip result."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
monkeypatch.delitem(sys.modules, "aipass.seedgo.apps.modules.checklist", raising=False)
|
||||
from aipass.seedgo.apps.modules import checklist
|
||||
|
||||
@@ -1457,6 +1473,10 @@ class TestRunChecklistCheckerException:
|
||||
"""When discover_checkers returns empty dict, returns error."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
monkeypatch.delitem(sys.modules, "aipass.seedgo.apps.modules.checklist", raising=False)
|
||||
from aipass.seedgo.apps.modules import checklist
|
||||
|
||||
|
||||
@@ -1062,8 +1062,14 @@ class TestCollectPyFiles:
|
||||
result = _collect_py_files(tmp_path)
|
||||
assert result == []
|
||||
|
||||
def test_collects_py_files(self, tmp_path):
|
||||
def test_collects_py_files(self, tmp_path, monkeypatch):
|
||||
"""Collects .py from apps/, excluding __init__.py."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
from aipass.seedgo.apps.handlers.audit.branch_audit import (
|
||||
_collect_py_files,
|
||||
)
|
||||
@@ -1081,10 +1087,14 @@ class TestCollectPyFiles:
|
||||
assert "handler.py" in names
|
||||
assert "__init__.py" not in names
|
||||
|
||||
def test_respects_ignore_patterns(self, tmp_path):
|
||||
def test_respects_ignore_patterns(self, tmp_path, monkeypatch):
|
||||
"""Files matching ignore patterns are excluded."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
# Use a unique pattern that will NOT collide with the pytest tmp_path
|
||||
# directory name (which includes the test function name).
|
||||
mock_ign = sys.modules["aipass.seedgo.apps.handlers.bypass"].ignore_handler
|
||||
@@ -1103,8 +1113,14 @@ class TestCollectPyFiles:
|
||||
assert "module.py" in names
|
||||
assert "xskip_bad.py" not in names
|
||||
|
||||
def test_excludes_disabled_files(self, tmp_path):
|
||||
def test_excludes_disabled_files(self, tmp_path, monkeypatch):
|
||||
"""Files with (disabled) in the name are excluded from collection."""
|
||||
import sys
|
||||
|
||||
skip_dirs = sys.modules.get("aipass.seedgo.apps.handlers.aipass_standards.skip_dirs")
|
||||
if skip_dirs:
|
||||
monkeypatch.setattr(skip_dirs, "_get_temp_roots", lambda: [])
|
||||
|
||||
from aipass.seedgo.apps.handlers.audit.branch_audit import (
|
||||
_collect_py_files,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user