diff --git a/.claude/rules/glossary-maintenance.md b/.claude/rules/glossary-maintenance.md index ce688888..aef1dd8a 100644 --- a/.claude/rules/glossary-maintenance.md +++ b/.claude/rules/glossary-maintenance.md @@ -55,7 +55,7 @@ The same decision tree applies when finding contradictions (e.g., two entries de - **Canonical source**: [`docs/glossary.md`](../../docs/glossary.md) (Maintenance section also summarizes triggers; this rule is the operational expansion) - **Acceptance check**: `acceptance-check.js` Q9 — Orchestrator's mechanical verification step during PR review -- **Sibling rule**: [`pre-pr-completeness.md`](pre-pr-completeness.md) — covers process completeness for new mechanisms; this rule is adjacent (terminology) and orthogonal (Gap-Scan does not include glossary checks) +- **Sibling rule**: [`pre-pr-completeness.md`](pre-pr-completeness.md) — covers process completeness for new mechanisms; this rule is adjacent (terminology) and orthogonal (Gap-Scan does not perform glossary judgment). **The orthogonality forbids duplicating the substance, not pointing at it.** Gap-Scan Q1.7 is a one-line recall point that sends the reader here; the trigger list and the drift-handling tree stay in this file as their single writer. A future reader should not "restore" the orthogonality by deleting that pointer — it was added (2026-08-20) precisely because recall, not judgment, was the layer that was missing: two PRs in two days reached the acceptance check with the entry absent, and the author had no step in their flow that would have surfaced this rule. - **Future automation**: Issue [#671](https://github.com/ms2sato/agent-console/issues/671) — referenced-but-not-defined linter (separate concern, this rule remains the manual gate until the linter lands) ## How this rule is expected to evolve diff --git a/.claude/rules/pre-pr-completeness.md b/.claude/rules/pre-pr-completeness.md index 6c081290..edff46fb 100644 --- a/.claude/rules/pre-pr-completeness.md +++ b/.claude/rules/pre-pr-completeness.md @@ -11,6 +11,8 @@ Before opening a PR that introduces a **new skill, script, rule, file type, or c - If a similar mechanism exists: is this new thing a genuine extension, a replacement, or a duplicate? Duplicate → stop and reuse. Extension → cross-link. Replacement → document migration. - **1.5 (cross-doc citation sub-check):** When this PR cites another document's technical claim (schema, API, command behaviour), verify the claim against the actual code, not just the other document. Documents describe intent; code describes reality. When the two drift, cite the code's current state. (Lesson: Sprint 2026-04-20 PR #677 claimed `multi-user-shared-setup.md` "declared REFERENCES users(id)"; CodeRabbit caught that migration v14 shipped without the REFERENCES DDL. The design doc described the spec; the code did something different.) - **1.6 (adjacent-fallback sub-check):** When designing a new "X-fallback" / "X-recovery" / "X-retry" mechanism, do not stop at "grep the function I'm modifying". Also grep adjacent code paths for the same pattern: `catch` blocks within the same function family, sibling functions that handle the same failure mode, helper functions in the same file with `force` / `fallback` / `recovery` / `retry` keywords. The risk is **duplicating existing recovery logic** because the new mechanism's intent is described in different words than the existing one. Read the full function body of any nearby `catch (error)` block before committing to the design. (Lesson: Sprint 2026-06-26 PR #897 — agent designed a new `pruneWorktrees` helper + a dedicated orphan-recovery branch in `WorktreeService.removeWorktree`, without auditing `lib/git.ts:removeWorktree`'s existing force-fallback catch block which already did `fs.rm` + `git worktree prune` for the same orphan case. Owner caught the duplicate during review. The agent had grepped `removeWorktree` for callsites but did not read the function body or the adjacent catch block.) + - **1.7 (glossary recall sub-check):** When this PR introduces a new domain concept — an exported type or interface, a DB table or column, an API endpoint or MCP tool parameter — check it against `glossary-maintenance.md`'s trigger list and add the `docs/glossary.md` entry in the same PR if it matches. **The judgment lives there, not here.** This item is a recall point, not a second copy of the decision tree: do not restate the triggers or the drift-handling steps in this file. (Lesson: Sprint 2026-08-20 PRs [#1373](https://github.com/ms2sato/agent-console/pull/1373) and [#1384](https://github.com/ms2sato/agent-console/pull/1384) both reached the Orchestrator's acceptance check with the glossary entry missing. The #1384 delegate's retrospective identified the mechanism precisely: they had the glossary rule loaded and did not actively exclude it — their pre-implementation checklist was built from the AC plus the Orchestrator's emphasis, and a glossary step was simply never in the flow. Of the three layers that could catch this — AC drafting, this checklist, and acceptance-check Q9 — only Q9 was doing so, because this one had no entry at all.) + 2. **Is the invocation or trigger of this new thing documented in a canonical procedure?** - If it is a script or a skill that needs to run at a specific point, find where that point is described (e.g., `core-responsibilities.md §N`, `sprint-lifecycle.md`, or equivalent) - Add the invocation instruction there in the same PR diff --git a/.claude/rules/workflow.md b/.claude/rules/workflow.md index 8a8f66af..302d6e3e 100644 --- a/.claude/rules/workflow.md +++ b/.claude/rules/workflow.md @@ -194,6 +194,22 @@ Claims of the form "**still waiting on** X" are the highest-risk shape, because (Lessons, both Sprint 2026-08-05: the Architect held "PR #1270 is awaiting owner gate" across two days of intermittent turns, restating it in each status summary without re-reading, then raised an urgent warning that a commit-message keyword would close an Issue on merge — the PR had merged two days earlier and the Issue had closed with it. Separately, the Orchestrator left a self-check timer whose action text still described a PR merged hours before; it fired repeatedly while a delegate's completed PR sat unreviewed for six hours, and the stall was surfaced by the owner asking what the hold-up was, not by the timer.) +## Cheap refutation: universal claims + +A sister criterion to Inference vs Verification, and a different axis. That rule asks *which signal you are treating as ground truth*. This one asks *what shape the claim has*, regardless of who made it. + +**When a claim is universal — "nowhere else in the codebase", "always", "never", "none of them", "every" — estimate what it would cost to find one counterexample before accepting it.** Usually that is a single `ls` or `grep`. Run it. + +The asymmetry is what makes this worth a rule. Refuting a universal costs one command; accepting a false one costs everything built on top of it, because a universal claim is what people stop searching after. "There is no precedent anywhere" ends the search. "There is no test for this elsewhere either" ends the objection. + +It fires on the claim's form, not its source — a subagent's report, a review comment, a design doc, or your own reasoning from ten minutes ago. + +**The boundary: this is not "distrust universal claims". It is "price the refutation".** Some universals are expensive to refute — "this distributed system never deadlocks" is not settled by a grep, and needs its own kind of verification. The criterion fires only when a counterexample search is cheap. There, one command before accepting wins on expected value; everywhere else, use the verification the claim actually calls for. + +This names a point several existing disciplines already circle: the vacuous truth of `[].every()` in boundary-value testing, and the way an "accepted risk" paragraph can quietly remove something from the verification surface. All three are the same shape — a universal, an empty set, or a negation, cheap to state and expensive to have wrong. + +(Lessons, both Sprint 2026-08-20: a specialist reported "no dedicated sibling test anywhere else in the codebase either", which the delegate refuted with one `ls` — the precedent existed and the gap was real, requiring a re-delegation. Separately, the Orchestrator inferred that a `queryFn` signature trap was silently affecting 18 sibling call sites, framed it to the owner as a probable latent-debt finding, and had a delegate measure it: `typecheck` catches it at every site. Both claims were plausible, both were refutable in under a minute, and only one of them was actually checked before it travelled.) + ## Commands ```bash