From 26893fb66a8cd66e9e122c5f7b680d39f45cc6d1 Mon Sep 17 00:00:00 2001 From: AIOSAI Date: Fri, 3 Jul 2026 09:01:59 -0700 Subject: [PATCH] =?UTF-8?q?fix(seedgo):=20template=20checker=20=E2=80=94?= =?UTF-8?q?=20stop=20false-flagging=20memory=20prose=20+=20README=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The advisory 'template' stale-checker matched marker strings anywhere in a file, firing on documentation ABOUT templates rather than un-rendered stubs. Two root causes fixed: 1. Scanned .trinity/*.json (all memory) — local.json/observations.json accumulate marker mentions (seedgo's own note about the checker, prax's template_pusher note). Now scans passport.json only, the sole spawn-templated trinity file. 2. Single-curly {…} regex ran on every .md, matching inline JSON/f-strings/code paths in READMEs. Now single-curly detection runs on the branch prompt only (README template has no single-curly placeholders) and strips fenced + inline code first. Definitive-marker detection unchanged — real stubs (cli/drone/prax prompts) still flag. Verified live: seedgo 100%, drone/prax flag only the real prompt stub. +4 tests (21/21). Dispatched to @seedgo (owner), verified independently. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_013uzDhtcZ6wT1T9e2AHPQig --- CHANGELOG.md | 17 +++++ .../aipass_standards/template_check.py | 26 +++++-- .../seedgo/tests/test_template_check.py | 75 +++++++++++++++++++ 3 files changed, 110 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c8206ae..0cd2af9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,23 @@ Post-2.6.1 cycle — **unreleased** (held for a later merge). flipping `edit`→`send`; +2 regression tests in `TestAdvancePending` (114/114). (fixed by @hooks, `f42a98b`, PR #651 — not yet merged) +- **`template` audit checker no longer false-flags memory prose or README code.** + The advisory stale-template checker (`seedgo/.../template_check.py`) matched its + marker strings anywhere in a file, so it fired on documentation *about* templates + rather than un-rendered stubs. Two root causes, both fixed: (1) it globbed + **`.trinity/*.json`** — scanning live memory (`local.json`, `observations.json`) + that naturally accumulates marker mentions (e.g. seedgo's own note "Detects + NEEDS CONFIGURATION", prax's note about `template_pusher` restoring + `{{BRANCHNAME}}`); now scans **`passport.json` only**, the sole spawn-templated + trinity file. (2) the single-curly `{…}` regex ran on every `.md` and matched + inline JSON / f-strings / code paths in READMEs (`{"new": 3}`, `{e}`, + `apps/plugins/{name}/`); now single-curly detection runs on the branch **prompt + only** (the spawn README template has no single-curly placeholders) and strips + fenced + inline code first. Definitive-marker detection unchanged, so real + unconfigured stubs (cli/drone/prax prompts) still flag correctly. Verified live: + seedgo `100%` (was flagged), drone/prax flag only the real prompt stub; +4 tests + (21/21). (fixed by @seedgo, dispatched by @devpulse) + ## [2026-07-02] Released as **2.6.1**. Rolls up the DPLAN-0226 / FPLAN-0289 / TDPLAN-0010 / diff --git a/src/aipass/seedgo/apps/handlers/aipass_standards/template_check.py b/src/aipass/seedgo/apps/handlers/aipass_standards/template_check.py index a2c495f2..a131f653 100644 --- a/src/aipass/seedgo/apps/handlers/aipass_standards/template_check.py +++ b/src/aipass/seedgo/apps/handlers/aipass_standards/template_check.py @@ -16,7 +16,7 @@ branches know they have unconfigured stub files. Checks: 1. .aipass/aipass_local_prompt.md for template markers 2. README.md for template markers -3. .trinity/*.json for template markers (definitive only — no curly brace regex) +3. .trinity/passport.json for template markers (the only spawn-templated artifact) AUDIT_SCOPE: branch_level — runs once per branch via check_branch(). ADVISORY: always passes so it never blocks commits or audits. @@ -43,9 +43,17 @@ _DEFINITIVE_MARKERS = [ _SINGLE_CURLY_RE = re.compile(r"\{[^{}\n]+\}") _DOUBLE_CURLY_RE = re.compile(r"\{\{[^}]*\}\}") +_FENCED_BLOCK_RE = re.compile(r"(?:```|~~~).*?(?:```|~~~)", re.DOTALL) +_INLINE_CODE_RE = re.compile(r"`[^`\n]+`") -def _find_markers(content: str, is_markdown: bool) -> list[str]: +def _strip_markdown_code(content: str) -> str: + """Remove fenced code blocks and inline code spans.""" + content = _FENCED_BLOCK_RE.sub("", content) + return _INLINE_CODE_RE.sub("", content) + + +def _find_markers(content: str, check_single_curly: bool) -> list[str]: """Return list of matched template marker descriptions.""" found: list[str] = [] content_upper = content.upper() @@ -54,8 +62,9 @@ def _find_markers(content: str, is_markdown: bool) -> list[str]: if marker.upper() in content_upper: found.append(marker) - if is_markdown: - stripped = _DOUBLE_CURLY_RE.sub("", content) + if check_single_curly: + stripped = _strip_markdown_code(content) + stripped = _DOUBLE_CURLY_RE.sub("", stripped) curly_matches = _SINGLE_CURLY_RE.findall(stripped) if curly_matches: examples = curly_matches[:3] @@ -80,8 +89,8 @@ def _check_file(file_path: Path, bypass_rules: list | None) -> Dict: logger.info("Cannot read %s for template check", file_path) return {"name": rel, "passed": True, "message": f"{rel} unreadable (skipped)"} - is_md = file_path.suffix == ".md" - markers = _find_markers(content, is_md) + is_prompt = file_path.name == "aipass_local_prompt.md" + markers = _find_markers(content, check_single_curly=is_prompt) if not markers: return {"name": rel, "passed": True, "message": "no template markers"} @@ -137,8 +146,9 @@ def check_branch(branch_path: str, bypass_rules: list | None = None) -> Dict: bp / ".aipass" / "aipass_local_prompt.md", bp / "README.md", ] - for trinity_file in sorted((bp / ".trinity").glob("*.json")) if (bp / ".trinity").is_dir() else []: - targets.append(trinity_file) + passport = bp / ".trinity" / "passport.json" + if passport.exists(): + targets.append(passport) for target in targets: checks.append(_check_file(target, bypass_rules)) diff --git a/src/aipass/seedgo/tests/test_template_check.py b/src/aipass/seedgo/tests/test_template_check.py index 60fe5a27..12f41850 100644 --- a/src/aipass/seedgo/tests/test_template_check.py +++ b/src/aipass/seedgo/tests/test_template_check.py @@ -272,6 +272,47 @@ class TestCheckBranch: trinity_checks = [c for c in result["checks"] if c["name"].endswith(".json")] assert all(c["passed"] for c in trinity_checks) + def test_trinity_memory_prose_not_flagged(self, tmp_path): + from aipass.seedgo.apps.handlers.aipass_standards.template_check import ( + check_branch, + ) + + aipass_dir = tmp_path / ".aipass" + aipass_dir.mkdir() + (aipass_dir / "aipass_local_prompt.md").write_text("Real prompt.") + (tmp_path / "README.md").write_text("# Branch\nReal docs.") + trinity = tmp_path / ".trinity" + trinity.mkdir() + (trinity / "passport.json").write_text(json.dumps({"branch": "test"})) + (trinity / "local.json").write_text( + json.dumps({"key_learnings": [{"value": "Detects NEEDS CONFIGURATION + mustache + curly placeholders"}]}) + ) + (trinity / "observations.json").write_text( + json.dumps({"observations": [{"note": "template_pusher restoring {{BRANCHNAME}}"}]}) + ) + + result = check_branch(str(tmp_path)) + assert result["score"] == 100 + + def test_trinity_passport_with_markers_flagged(self, tmp_path): + from aipass.seedgo.apps.handlers.aipass_standards.template_check import ( + check_branch, + ) + + aipass_dir = tmp_path / ".aipass" + aipass_dir.mkdir() + (aipass_dir / "aipass_local_prompt.md").write_text("Real prompt.") + (tmp_path / "README.md").write_text("# Branch\nReal docs.") + trinity = tmp_path / ".trinity" + trinity.mkdir() + (trinity / "passport.json").write_text(json.dumps({"branch": "{{BRANCHNAME}}", "role": "NEEDS CONFIGURATION"})) + + result = check_branch(str(tmp_path)) + assert result["score"] < 100 + passport_check = next(c for c in result["checks"] if "passport" in c["name"]) + assert not passport_check["passed"] + assert "template markers" in passport_check["message"] + def test_standard_level_bypass(self, tmp_path): from aipass.seedgo.apps.handlers.aipass_standards.template_check import ( check_branch, @@ -291,6 +332,40 @@ class TestCheckBranch: assert result["passed"] is True assert result["advisory"] is True + def test_readme_code_braces_not_flagged(self, tmp_path): + from aipass.seedgo.apps.handlers.aipass_standards.template_check import ( + check_branch, + ) + + aipass_dir = tmp_path / ".aipass" + aipass_dir.mkdir() + (aipass_dir / "aipass_local_prompt.md").write_text("Real prompt.") + (tmp_path / "README.md").write_text( + "# Branch\n\nExample:\n```python\nwrite_section(data, {'new': 3, 'total': 5})\n```\n" + "Use `apps/plugins/{name}/` for plugins.\n" + ) + + result = check_branch(str(tmp_path)) + readme_check = next(c for c in result["checks"] if c["name"] == "README.md") + assert readme_check["passed"] + + def test_prompt_code_fence_braces_not_flagged(self, tmp_path): + from aipass.seedgo.apps.handlers.aipass_standards.template_check import ( + check_branch, + ) + + aipass_dir = tmp_path / ".aipass" + aipass_dir.mkdir() + (aipass_dir / "aipass_local_prompt.md").write_text( + "Real configured prompt.\n\n```python\nprint(f'Error: {e}')\nresult = {k: v}\n```\n" + "Also `{inline_code}` is fine.\n" + ) + (tmp_path / "README.md").write_text("# Branch\nConfigured.") + + result = check_branch(str(tmp_path)) + prompt_check = next(c for c in result["checks"] if "aipass_local_prompt" in c["name"]) + assert prompt_check["passed"] + def test_curly_placeholders_in_prompt(self, tmp_path): from aipass.seedgo.apps.handlers.aipass_standards.template_check import ( check_branch,