diff --git a/docs/agent-setup-playbook.md b/docs/agent-setup-playbook.md index 86206c6..a6d0e8c 100644 --- a/docs/agent-setup-playbook.md +++ b/docs/agent-setup-playbook.md @@ -135,6 +135,20 @@ Skip it when: If `.audit/` artifacts may be created, make sure `.audit/` is ignored before running the tool. +When Lumin Repo Lens is approved and available, use it as an evidence workflow, not as a magic reviewer: + +- No structural claim without evidence: cite a produced artifact, a direct file inspection, or both before claiming a duplicate helper, public surface, cycle, fan-in hotspot, re-export drift, or missing boundary. +- No absence claim without scan range: say "not observed in this scan range" instead of "does not exist" unless the checked range, files, and exclusions make that claim true. +- For a first pass or branch-level review, prefer the upstream full audit workflow. Treat `quick, full, ci` as audit cadences with different confidence, and read the produced `manifest.json` before using summaries. +- Before writing code, capture the pre-write intent with these keys: `names, shapes, files, dependencies, plannedTypeEscapes`. The agent should infer this from the user's task and repository evidence; the user should not need to hand-write JSON. +- Read the invocation-specific advisory produced for that pre-write run before editing. Do not rely only on a `latest` pointer when a command produced a more specific advisory path. +- After writing, run the matching post-write delta against the same invocation-specific advisory. Review `silent-new` type escapes, planned-not-observed type escapes, unexpected files outside the intent, scan range changes, and degraded confidence. +- Use canon draft or check-canon only for Lumin-supported canon sources. +- Lumin canon sources are limited to type ownership, helper registry, topology, and naming. +- Use living audit or project docs for boundary policy, public API policy, dependency direction, and re-export policy. +- Keep a living audit when a repo needs ongoing structural tracking. Prefer a shareable repository doc such as `docs/current/audit/lumin-structural-audit.md` when it is safe to commit; otherwise keep the same index shape in an `Obsidian or private wiki` page. +- Do not treat Lumin warnings as automatic refactor permission. The agent still needs to inspect cited files, avoid false-positive cleanup, and make the smallest change approved by the task. + ### Dependency, Type, Cycle, And Complexity Checks Dependency lint, strict type checks, cycle detection, complexity limits, and file/function size limits are strong controls, but they are tooling changes. diff --git a/docs/local-project-knowledge-template.md b/docs/local-project-knowledge-template.md index 52f2ba9..6769469 100644 --- a/docs/local-project-knowledge-template.md +++ b/docs/local-project-knowledge-template.md @@ -16,6 +16,23 @@ Copy this template into a project-local `local.md`, an untracked note, or an Obs - Storage or persistence: - UI or delivery surfaces: +## Structural Evidence Index + +- Last refreshed: +- Scan range: +- Claim rule: state scan range before saying something was not found. +- Confidence or blind spots: +- `.audit/manifest.json`: +- `.audit/audit-summary.latest.md`: +- `.audit/pre-write-advisory.latest.json`: +- `.audit/post-write-delta.latest.json`: +- `docs/current/audit/lumin-structural-audit.md`: +- Canon or source-of-truth docs: +- Files that must be inspected before touching shared helpers: +- Files that must be inspected before changing public APIs: +- Files that must be inspected before changing import topology: +- Local-only notes that should stay untracked: + ## Source Of Truth - Shared utility source of truth: diff --git a/docs/vibe-coding-guardrails.md b/docs/vibe-coding-guardrails.md index 03dcbed..fd58811 100644 --- a/docs/vibe-coding-guardrails.md +++ b/docs/vibe-coding-guardrails.md @@ -222,6 +222,55 @@ LUMIN_REPO_LENS_NO_AUTO_INSTALL=1 node skills/lumin-repo-lens/scripts/audit-repo The tool output is evidence. The agent must still inspect the cited files and make the engineering call. +## Lumin Evidence Lifecycle + +Use this lifecycle only when the target repository is TS/JS-heavy and Lumin Repo Lens is installed or explicitly approved. If Lumin is missing, the agent should still follow the same evidence rules with native repo tools and direct inspection. + +### Structural claim contract + +The contract is stricter than "run a tool and summarize it": + +- No structural claim without evidence. A claim about duplicate helpers, hidden coupling, dead exports, cycles, topology, barrel drift, public-surface risk, god files, or fan-in/fan-out hotspots needs a cited artifact or direct file evidence. +- No absence claim without scan range. `not observed is not does not exist`; report the files, directories, excludes, language coverage, and confidence behind the statement. +- Read `.audit/manifest.json` first when it exists. It is the source for scan range, produced artifacts, skipped lanes, confidence, and blind spots. +- Treat `.audit/audit-summary.latest.md` as an artifact map, not a final review. The engineering review still belongs to the agent. +- Label structural conclusions as `grounded, degraded, unknown` when artifact quality, parse coverage, or scan range changes affect confidence. +- Avoid false-positive cleanup. Static "no consumer found" evidence is not enough to delete code when resolver blindness, parse taint, runtime imports, framework conventions, generated files, or public API promises might apply. + +### Audit cadence + +Use the smallest cadence that gives enough evidence: + +- `quick, full, ci`: quick is for focused checks, full is for first pass or branch-level reviews, and ci is for automated guardrail enforcement when the repo has opted in. +- Use full evidence for a first checkup, large refactor, post-refactor review, stale artifacts, or an explicit structural audit request. +- Use focused checks for a narrow feature after a recent comparable full audit. +- Do not compare two audit results unless their scan range and confidence are compatible enough for the claim being made. + +### Pre-write and post-write + +For a scoped code task: + +1. Build the pre-write intent from the requested change. Track `names`, `shapes`, `files`, `dependencies`, and `plannedTypeEscapes`. +2. Run the approved Lumin pre-write flow or native equivalent. +3. Read the invocation-specific `pre-write-advisory` before editing. Do not rely only on a `latest` pointer when a specific advisory path exists. +4. Implement with TDD and the write gate in this guide. +5. Run the matching `post-write-delta` against the same advisory. +6. Lumin post-write machine evidence covers `silent-new` type escapes, planned-not-observed type escapes, unexpected new files outside the intent, scan range changes, and degraded confidence. + +If the post-write delta says a change was not observed, inspect the actual diff and the scan range before concluding anything. The correct closeout may be "unknown until a wider audit runs." + +Manual post-write review still covers duplicate helpers, changed dependencies, public API drift, dependency direction, re-export drift, hidden coupling, and weak tests. Keep those as engineering review claims backed by direct file evidence or repository-native tools, not as unsupported Lumin delta fields. + +### Canon and living audit + +Use canon or living audit docs when the repo has enough structure to justify durable memory: + +- Lumin canon sources are limited to type ownership, helper registry, topology, and naming. +- Use living audit or project docs for boundary policy, public API policy, dependency direction, and re-export policy. +- Keep a living audit for unresolved structure risks, stale evidence, recurring hotspots, and accepted deviations. +- Prefer `docs/current/audit/lumin-structural-audit.md` for shareable project facts. +- Keep private local details in `local.md`, an untracked note, or an Obsidian page. The repository should link to the kind of knowledge needed, not to Hun's personal vault path. + ## Local Wiki Or Obsidian Index Use `docs/local-project-knowledge-template.md` as the shape of a project wiki page. diff --git a/prompts/apply-vibe-coding-guardrails.md b/prompts/apply-vibe-coding-guardrails.md index 7ce1fd2..0b8beb0 100644 --- a/prompts/apply-vibe-coding-guardrails.md +++ b/prompts/apply-vibe-coding-guardrails.md @@ -13,18 +13,28 @@ Before changing files: - inspect existing docs, tests, lint, type checks, and dependency tooling - identify module boundaries, existing helpers, types, shapes, public APIs, re-exports, and error boundaries - inventory optional tools that may matter for this repo, including Obsidian, Lumin Repo Lens, dependency lint, strict type checks, cycle detection, and complexity limits +- if Lumin Repo Lens is installed or approved for a TS/JS repo, read .audit/manifest.json first when it exists and treat scan range, skipped lanes, and confidence as part of every structural claim +- do not claim that a helper, shape, public API, cycle, or consumer does not exist unless you state the scan range; say "not observed in this scan range" when the evidence is partial - do not invent tool commands, config options, package names, or API details Set up only the smallest useful guardrails for this repository: - add .audit/ to .gitignore if local evidence artifacts may be created - add or update local project knowledge guidance without committing private paths - document the pre-write lens, write gate, and post-write review loop for this repo +- include a structural evidence index that points to shareable audit docs, .audit artifact names, source-of-truth docs, and any private wiki or Obsidian page type without committing personal vault paths - keep optional tools such as Lumin Repo Lens optional unless this repo already uses TS/JS and I explicitly approve installing it - for Obsidian or any other optional desktop app, plugin, CLI, browser extension, or package dependency, explain whether it is required, recommended, optional, or skipped - ask before installing optional tools; do not install them only because a guide mentions them - on macOS, verify the package name or official source before installing; on Windows PowerShell, use commands such as winget search Obsidian to verify the package name before installation - do not commit personal vault paths, credentials, tokens, MCP endpoints, auth state, or machine-specific trust settings +When documenting the daily workflow: +- make pre-write intent explicit with names, shapes, files, dependencies, and plannedTypeEscapes +- require the agent to read the invocation-specific pre-write advisory before editing when one is produced +- require post-write delta review against the same advisory after editing +- check Lumin machine evidence for `silent-new` type escapes, planned-not-observed type escapes, unexpected new files, and degraded scan confidence +- check duplicate helpers, changed dependencies, public API drift, and re-export drift as manual post-write review claims backed by direct evidence + Use TDD for any behavior changes. Tests must cover edge cases, failure paths, and side effects. Mocks belong at external boundaries only; do not mock internal implementation details. After setup: diff --git a/prompts/start-with-vibe-coding-guardrails.md b/prompts/start-with-vibe-coding-guardrails.md index 7a5b2c6..f0e003c 100644 --- a/prompts/start-with-vibe-coding-guardrails.md +++ b/prompts/start-with-vibe-coding-guardrails.md @@ -11,6 +11,10 @@ Before implementation, run a pre-write lens: - search for existing helpers, types, shapes, public APIs, and tests before creating new ones - identify edge cases, failure paths, side effects, and concurrency risks - check whether .audit/ or other local evidence artifacts should remain untracked +- if Lumin Repo Lens or a comparable structure tool is available, read the current manifest or scan summary before making structural claims +- state the scan range before saying something was not found +- capture pre-write intent as names, shapes, files, dependencies, and plannedTypeEscapes +- read the invocation-specific pre-write advisory when one is produced Use TDD: - write the failing test first @@ -29,6 +33,8 @@ During implementation: After implementation, run post-write review: - check for duplicate helpers, duplicate shapes, dead code, commented-out code, hidden coupling, initialization order dependencies, global state, unmanaged re-exports, fan-in/fan-out hotspots, and weak tests +- compare the diff against the pre-write advisory or local evidence index, and check Lumin machine evidence for `silent-new` type escapes, planned-not-observed type escapes, unexpected new files, and degraded scan confidence +- keep duplicate helpers, dependency drift, public API drift, and re-export drift as manual review claims backed by direct evidence - run the repository's real verification commands - report exact commands, results, files changed, and remaining risks ``` diff --git a/scripts/inventory_optional_tools.py b/scripts/inventory_optional_tools.py index 9a297aa..f3e92e4 100644 --- a/scripts/inventory_optional_tools.py +++ b/scripts/inventory_optional_tools.py @@ -134,6 +134,15 @@ def classify_lumin_repo_lens( detected = True evidence.append("lumin-repo-lens command is on PATH") evidence.append("Target repository appears TS/JS-heavy" if ts_js else "No TS/JS repository signal detected") + for artifact_name in ( + "manifest.json", + "audit-summary.latest.md", + "pre-write-advisory.latest.json", + "post-write-delta.latest.json", + ): + artifact_path = repo_root / ".audit" / artifact_name + if artifact_path.exists(): + evidence.append(f".audit/{artifact_name} exists") return ToolInventory( name="lumin-repo-lens", diff --git a/tests/test_optional_tool_inventory.py b/tests/test_optional_tool_inventory.py index 68ee671..e18cd7f 100644 --- a/tests/test_optional_tool_inventory.py +++ b/tests/test_optional_tool_inventory.py @@ -69,6 +69,34 @@ def test_json_output_reports_required_decision_fields(self) -> None: self.assertIn("reason", first) self.assertIn("install_status", first) + def test_lumin_inventory_reports_existing_audit_artifacts(self) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + repo_root = Path(tmpdir) + audit_dir = repo_root / ".audit" + audit_dir.mkdir() + (audit_dir / "manifest.json").write_text("{}", encoding="utf-8") + (audit_dir / "audit-summary.latest.md").write_text("# audit\n", encoding="utf-8") + (audit_dir / "pre-write-advisory.latest.json").write_text( + "{}", encoding="utf-8" + ) + (audit_dir / "post-write-delta.latest.json").write_text( + "{}", encoding="utf-8" + ) + (repo_root / "package.json").write_text("{}", encoding="utf-8") + + results = inventory_optional_tools.build_inventory( + repo_root, + command_lookup=lambda command: None, + path_exists=lambda path: False, + platform_name="Linux", + ) + lumin = next(result for result in results if result.name == "lumin-repo-lens") + + self.assertIn(".audit/manifest.json exists", lumin.evidence) + self.assertIn(".audit/audit-summary.latest.md exists", lumin.evidence) + self.assertIn(".audit/pre-write-advisory.latest.json exists", lumin.evidence) + self.assertIn(".audit/post-write-delta.latest.json exists", lumin.evidence) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_vibe_coding_guardrails.py b/tests/test_vibe_coding_guardrails.py index 5eae126..7784c68 100644 --- a/tests/test_vibe_coding_guardrails.py +++ b/tests/test_vibe_coding_guardrails.py @@ -195,6 +195,113 @@ def test_playbook_points_agents_at_optional_tool_inventory_script(self) -> None: for phrase in expected_phrases: self.assertIn(phrase, playbook) + def test_playbook_covers_lumin_evidence_backed_workflow(self) -> None: + playbook = (REPO_ROOT / "docs" / "agent-setup-playbook.md").read_text( + encoding="utf-8" + ) + + expected_phrases = ( + "No structural claim without evidence", + "No absence claim without scan range", + "pre-write intent", + "names, shapes, files, dependencies, plannedTypeEscapes", + "invocation-specific advisory", + "post-write delta", + "silent-new", + "canon draft", + "living audit", + "docs/current/audit/lumin-structural-audit.md", + "Obsidian or private wiki", + ) + for phrase in expected_phrases: + self.assertIn(phrase, playbook) + + def test_vibe_coding_guide_covers_lumin_structural_claim_contract(self) -> None: + guide = (REPO_ROOT / "docs" / "vibe-coding-guardrails.md").read_text( + encoding="utf-8" + ) + + expected_phrases = ( + "Lumin Evidence Lifecycle", + "quick, full, ci", + "manifest.json", + "audit-summary.latest.md", + "grounded, degraded, unknown", + "not observed is not does not exist", + "scan range", + "false-positive", + "pre-write-advisory", + "post-write-delta", + ) + for phrase in expected_phrases: + self.assertIn(phrase, guide) + + def test_lumin_docs_do_not_overclaim_machine_post_write_outputs(self) -> None: + checked_paths = ( + REPO_ROOT / "docs" / "agent-setup-playbook.md", + REPO_ROOT / "docs" / "vibe-coding-guardrails.md", + REPO_ROOT / "prompts" / "apply-vibe-coding-guardrails.md", + REPO_ROOT / "prompts" / "start-with-vibe-coding-guardrails.md", + ) + + for path in checked_paths: + with self.subTest(path=path.name): + content = path.read_text(encoding="utf-8") + self.assertNotIn("silent-new helpers", content) + + guide = (REPO_ROOT / "docs" / "vibe-coding-guardrails.md").read_text( + encoding="utf-8" + ) + lifecycle = guide.split("## Lumin Evidence Lifecycle", maxsplit=1)[1].split( + "## Local Wiki Or Obsidian Index", + maxsplit=1, + )[0] + + self.assertIn( + "Lumin post-write machine evidence covers `silent-new` type escapes", + lifecycle, + ) + self.assertIn( + "Manual post-write review still covers duplicate helpers", + lifecycle, + ) + self.assertNotIn("changed dependencies, and degraded scan confidence", lifecycle) + + def test_lumin_canon_sources_are_limited_to_upstream_sources(self) -> None: + guide = (REPO_ROOT / "docs" / "vibe-coding-guardrails.md").read_text( + encoding="utf-8" + ) + playbook = (REPO_ROOT / "docs" / "agent-setup-playbook.md").read_text( + encoding="utf-8" + ) + + expected_phrases = ( + "Lumin canon sources are limited to type ownership, helper registry, topology, and naming.", + "Use living audit or project docs for boundary policy, public API policy, dependency direction, and re-export policy.", + ) + for phrase in expected_phrases: + self.assertIn(phrase, guide) + self.assertIn(phrase, playbook) + + def test_local_project_knowledge_template_indexes_structural_evidence(self) -> None: + template = (REPO_ROOT / "docs" / "local-project-knowledge-template.md").read_text( + encoding="utf-8" + ) + + expected_phrases = ( + "Structural Evidence Index", + ".audit/manifest.json", + ".audit/audit-summary.latest.md", + ".audit/pre-write-advisory", + ".audit/post-write-delta", + "docs/current/audit/lumin-structural-audit.md", + "Canon or source-of-truth docs", + "scan range", + "Last refreshed", + ) + for phrase in expected_phrases: + self.assertIn(phrase, template) + def test_vibe_coding_guide_maps_original_problem_checklist_to_repo_artifacts(self) -> None: guide = (REPO_ROOT / "docs" / "vibe-coding-guardrails.md").read_text( encoding="utf-8"