From ef8a521bc18b1f6fe2c0eda416e87c736412d149 Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Fri, 11 Sep 2026 02:24:05 -0700 Subject: [PATCH] docs: the always-on rules, README and gap analysis name the tag always-on/evidence-check.md and corpus/CLAUDE.learned.md load into every session and still said to write bare `UNVERIFIED:`, which the hooks now reject. The root README's hook table and the gap analysis described it as what clears a hook. All four now name the tag. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01VKsvxJk65w6q7KnPSRYvNg --- README.md | 2 +- always-on/evidence-check.md | 2 +- corpus/CLAUDE.learned.md | 2 +- docs/hooks-gap-analysis.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 75337c17..7297c3d4 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ Full sourcing notes, including what was left out and why: [docs/provenance.md](d | `restated-constraint` | User repeats a must/never/don't they already gave: apply it, don't re-acknowledge it. | | `named-verb-guard` | User said test/repro/run/show/delete/revert/stop, or asked for proof twice: the reply must carry the evidence. | | `wait-needs-wakeup` | Waiting on CI, a queue, a subagent, or a job: schedule a wakeup and name a clock-time ETA. Blocks foreground poll loops and ETA-less "will report" replies. | -| `hedge-runs-prove-it` | "I think" / "probably" / "should work" / bare `UNVERIFIED:` about code with nothing run this turn: verify now or say why you cannot. | +| `hedge-runs-prove-it` | "I think" / "probably" / "should work" / a retired bare `UNVERIFIED:` about code with nothing run this turn: verify now, or tag the claim and name the blocker. | | `new-file-callout` | A new untracked file at the repo root or under `scripts/`: the reply must name it and say why. | | `agent-relay-attribution` | Advisory: facts relayed from a subagent's report must say so or be re-verified. | | `scratchpad-collision` | Two agents writing the same scratchpad file within ten minutes: use a uniquely named file. | diff --git a/always-on/evidence-check.md b/always-on/evidence-check.md index e8e15704..1ee8c3b4 100644 --- a/always-on/evidence-check.md +++ b/always-on/evidence-check.md @@ -5,7 +5,7 @@ A Grep or name hit is **not** a check. - Do not cite a file, line, or "the bug is X" until this turn's Read or command output is in the same message. - If two files could match, Read both before picking one. -- Prefix `UNVERIFIED:` until you have that evidence. +- Run the check. Only when it cannot run, tag the claim `{{CAT-UNVERIFIED: -- cannot verify: }}`; bare `UNVERIFIED:` is retired and excuses nothing. - "My earlier check was wrong" means the claim went out before the check — that is a process failure, not a polite recovery. The `wrong-check-reflect` hook will force `/reflect` when it sees that diff --git a/corpus/CLAUDE.learned.md b/corpus/CLAUDE.learned.md index 5f14424b..b2045cb4 100644 --- a/corpus/CLAUDE.learned.md +++ b/corpus/CLAUDE.learned.md @@ -18,7 +18,7 @@ Engine-only install drops this file; reflect Accepted global rules land here. - Never accept a dry run as proof of a write path. Dry run proves the decision and never the effect. Guards, permission checks, and connections to a running service fire only on the real write, so a `--dry-run` that lists what it would create (PRs, filed items) says nothing about whether they get created. Prove the write by the artifact it leaves: the PR exists, the row is filed, the tick reports success. - When a fix changes which identity, binary, or database a process uses, verify the one the *service* resolves, not the one your shell resolves. A login shell's `PATH` can pick a different install (a system `/usr/lib/node_modules` copy versus a user `~/.local/npm-global/bin` copy), and a helper script run from a checkout can open a stale copy of the database instead of the live one. Read the running process's own view — `/proc//environ` for its `PATH`, `/proc//exe` for its binary, its open files or config for its database — and check that the data is current (newest record, row count) before trusting a query. - Absence of a field in a projection (CLI, MCP, or API output) is not proof of absent state. Say "not projected" and find the emitter before retrying, resubmitting, or switching agents. A status reason the owner stores but no headless query prints (for example, why a task sits in `needs_input`) is still there; guessing causes and resubmitting cannot change state that was never missing. Read the emitter or the stored record first. -- When delegating a file-finding task to a subagent and two files could plausibly hold the same answer (a duplicate, a moved file, a same-named symbol in two packages), tell the subagent to state whether each file:line claim is "read-confirmed" (it opened the actual reference/import and traced it) or "name-matched" (it assumes the file is the one in use because the name/path looked right). A subagent that reasons by name-proximity instead of tracing the real reference can hand back a confident wrong file — a judgment call about how the subagent qualifies its own confidence, not something a mechanical check can catch. Relay a name-matched citation as `UNVERIFIED:` until a direct Read traces the real reference, because a dead duplicate file matches by name exactly as well as the live one. +- When delegating a file-finding task to a subagent and two files could plausibly hold the same answer (a duplicate, a moved file, a same-named symbol in two packages), tell the subagent to state whether each file:line claim is "read-confirmed" (it opened the actual reference/import and traced it) or "name-matched" (it assumes the file is the one in use because the name/path looked right). A subagent that reasons by name-proximity instead of tracing the real reference can hand back a confident wrong file — a judgment call about how the subagent qualifies its own confidence, not something a mechanical check can catch. Relay a name-matched citation tagged `{{CAT-UNVERIFIED: -- cannot verify: }}` until a direct Read traces the real reference, because a dead duplicate file matches by name exactly as well as the live one. - A `file:line` citation, mine or a subagent's, also names the ref it was read at: working tree, `HEAD`, `origin/`, or the installed bundle. A working-tree read in a checkout with untracked or modified files under the cited path is name-matched, not read-confirmed, until the same line is shown at the ref the change will actually run on. Subagent prompts that ask for read-confirmed vs name-matched must also ask for the ref. Show the line at that ref with `git show :` or `git grep -n -- `; a feature branch or an untracked copy can hold a line the base branch has already removed or changed. - When a pipeline switches from test/synthetic inputs to my real inputs, re-derive or explicitly re-validate every artifact built under the old conditions — voice clones, cached device lists, presigned URLs, browser sessions that predate a driver install. State which artifacts were rebuilt and which were kept. An artifact carries the inputs it was built from, so a clone trained on a test recording stays a clone of the test recording; and a process enumerates devices at startup, so a browser started before an audio driver install will not see the new device until it is fully relaunched. diff --git a/docs/hooks-gap-analysis.md b/docs/hooks-gap-analysis.md index c89401f2..eafc2e36 100644 --- a/docs/hooks-gap-analysis.md +++ b/docs/hooks-gap-analysis.md @@ -19,7 +19,7 @@ Tiers follow the reflect fix hierarchy in | Class | What the user had to do | Existing cover | Verdict | Cheapest tier | | --- | --- | --- | --- | --- | | A. invent-under-prohibition | Told the agent not to fabricate values; it did anyway; escalated to all-caps | none that sees values | **Not a catstack hook.** Whether a number was invented needs the data source; only the product repo can check it. Tier 1 in the product: an export gate that fails any row without a source citation. Catstack covers the *repeat* of the prohibition (class C). | 1 (product) | -| B. done/PASS without proof | Asked "did you run it?" after bare "tests pass" / "fixed" | `diu-stop` (banned openers, causal claims), `prove-it-ship-gate` (live nouns only), `wrong-check-reflect` (after the retraction) | **GAP, built: `named-verb-guard`.** Stop hook. Trigger: the user's last message named test / repro / run / rerun / regenerate / prove / show / delete / revert / a short "stop". Rule: the reply must carry a closed fenced block or `path:line` (URL or table row also count for run / regenerate / show; a delete command this turn for delete / revert; no mutating tool calls for stop). Guards: imperative position only, `UNVERIFIED:` or a question always passes, hook-feedback lines are not the user. Blocking. | 3 | +| B. done/PASS without proof | Asked "did you run it?" after bare "tests pass" / "fixed" | `diu-stop` (banned openers, causal claims), `prove-it-ship-gate` (live nouns only), `wrong-check-reflect` (after the retraction) | **GAP, built: `named-verb-guard`.** Stop hook. Trigger: the user's last message named test / repro / run / rerun / regenerate / prove / show / delete / revert / a short "stop". Rule: the reply must carry a closed fenced block or `path:line` (URL or table row also count for run / regenerate / show; a delete command this turn for delete / revert; no mutating tool calls for stop). Guards: imperative position only, a well-formed `{{CAT-UNVERIFIED}}` tag or a question always passes, hook-feedback lines are not the user. Blocking. | 3 | | C. restated constraint | Re-typed "stock-agnostic" three times, "one parser per form type", lookback semantics | `frustration-watchdog` (only verbatim re-sends within 10 min) | **GAP, built: `restated-constraint`.** UserPromptSubmit hook. Trigger: prompt carries must / never / always / don't / one-X-per-Y / again / I told you, and an earlier human message shares a hyphenated term, the same constraint clause, or is a near-duplicate. Injects "already named at turn N, FAIL class, apply before replying". Guards: generic hyphen words (`to-do`, `follow-up`) never match; a prompt re-sent 3+ times is a template, not a correction; the transcript's own copy of the current prompt is skipped. Advisory. | 3 | | D. proof polling | "prove it" / "show me" / "are you sure" repeated (7 times) because rows carried no citation | none | **Folded into `named-verb-guard`.** Second proof demand in a session requires a fenced block, `path:line`, or URL in the reply. Same evidence parser, so one hook instead of two. | 3 | | E. hook feedback counted as the user | `frustration-watchdog` read `Stop hook feedback:` lines as human turns | `frustration-watchdog` | **Fixed in place.** Hook-feedback prefixes join the injected-line skip list; a verbatim re-send is no longer pushed out of the 8-message window by nine diu blocks. | 3 |