feat(zcode-parity): S5 — warn-subagent-report ZCode variant (4D hybrid) - #1046
Merged
artyhoo merged 1 commit intoJul 19, 2026
Merged
Conversation
Two arms per decisions.md Fork 2: - Arm A (PostToolUse:Agent + 120KB payload read): real-time catch - Arm B (Stop + transcript_path rollout scan): completeness sweep - Dedup via session-scoped toolCallId state file
artyhoo
pushed a commit
that referenced
this pull request
Jul 21, 2026
…isting blocker) inject-subagent-context.test.ts shipped RED by #1047 and never passed on staging (merged past — Principles job is non-blocking). Its 5 zcode-branch tests called the hook with ZCODE_PROJECT_DIR set (for the _is_zcode gate) but relied on the REAL repo's .claude/session-bootstrap.md carrying a `<!-- digest:start/end -->` block — which it does not, by design: the framework's main-session digest is emitted dynamically by inject-session-bootstrap.sh's heredoc, never cached as a static block (a static copy would drift from the heredoc's dynamic AIF_HOOK_LANG line). So the hook correctly no-ops (empty stdout) → JSON.parse throws. Fix mirrors the sibling inject-project-digest.test.ts (which is hermetic): build a temp fixture repo whose session-bootstrap.md carries a digest block seeded with inject-session-bootstrap.sh's own output, and point the hook at it via CLAUDE_PROJECT_DIR (the root the hook reads at :44). The SSOT/no-drift assertion stays meaningful (verifies the hook reads + appends the block verbatim). Test-only change; the hook and the maintainer-owned session-bootstrap.md are untouched. Verified: test:hooks 825/826 pass (the remaining 1 = harness-config-drift, a distinct zcode-parity SSOT-drift bug from #1036/#1046 filed as #1053 — needs a domain decision, not a blind regen). Prior-art: skipped — test hermeticity fix for an existing hook, no new capability.
artyhoo
pushed a commit
that referenced
this pull request
Jul 21, 2026
…pre-existing blocker) harness-config-drift.test.ts failed on staging (render --check exit 1) — the LAST pre-existing Principles-job red. Root cause: #1036 (inject-project-digest, inject-output-language) and #1046 (warn-subagent-report-zcode) hand-added their plugin-channel entries directly to the GENERATED plugin/hooks/hooks.json but never registered them in PLUGIN_INTERNAL_HOOKS — the renderer's SSOT for plugin-only hooks that have no framework-model counterpart. So `render --write` removed them (43-line drift) and `--check` reported it. These three are genuinely plugin-only: absent from .claude/settings.json AND from harness-model.json, each with a real plugin/hooks/ sibling script, reaching ZCode consumers ONLY via the plugin channel. Regenerating them away would revert shipped #1036/#1046 functionality; the SubagentStart arm is also inexpressible from the model (event ∉ ZCODE_EVENTS). PLUGIN_INTERNAL_HOOKS is exactly their intended home. Changes: - render-harness-config.mjs: register the 4 twin entries in PLUGIN_INTERNAL_HOOKS (UserPromptSubmit ×2, SubagentStart, PostToolUse, Stop); export PLUGIN_INTERNAL_HOOKS + PLUGIN_INCOMPATIBLE. - harness-config-drift.test.ts: the N6 coverage assertion computed expected = model count, which held only before twins landed; now expected = model-derived (minus PLUGIN_INCOMPATIBLE) + plugin-internal, mirroring emitPlugin's actual composition. - plugin/hooks/hooks.json: regenerated (twins preserved; same-event ordering now internal-first — cosmetic for independent context/advisory hooks). settings.json / .mcp.json / zcode.json UNTOUCHED (model unchanged). Closes #1053. Verified: test:hooks 826/826, test:principles 311/311, render --check exit 0. Prior-art: skipped — SSOT-registration fix for existing shipped hooks, no new capability.
artyhoo
pushed a commit
that referenced
this pull request
Jul 21, 2026
…pre-existing blocker) harness-config-drift.test.ts failed on staging (render --check exit 1) — the LAST pre-existing Principles-job red. Root cause: #1036 (inject-project-digest, inject-output-language) and #1046 (warn-subagent-report-zcode) hand-added their plugin-channel entries directly to the GENERATED plugin/hooks/hooks.json but never registered them in PLUGIN_INTERNAL_HOOKS — the renderer's SSOT for plugin-only hooks that have no framework-model counterpart. So `render --write` removed them (43-line drift) and `--check` reported it. These three are genuinely plugin-only: absent from .claude/settings.json AND from harness-model.json, each with a real plugin/hooks/ sibling script, reaching ZCode consumers ONLY via the plugin channel. Regenerating them away would revert shipped #1036/#1046 functionality; the SubagentStart arm is also inexpressible from the model (event ∉ ZCODE_EVENTS). PLUGIN_INTERNAL_HOOKS is exactly their intended home. Changes: - render-harness-config.mjs: register the 4 twin entries in PLUGIN_INTERNAL_HOOKS (UserPromptSubmit ×2, SubagentStart, PostToolUse, Stop). PLUGIN_INTERNAL_HOOKS / PLUGIN_INCOMPATIBLE stay module-private (no export) — the drift test no longer imports them (see below). - harness-config-drift.test.ts: rewrite the N6 plugin-coverage assertion from count-equality (which only held before the twins landed, and would have required importing the registries from the untyped .mjs — breaking `tsc --noEmit` with TS7016, no declaration file) to coverage-BY-NAME: for every zcode-supported event, each model-derived hook — except the maintainer-only PLUGIN_INCOMPATIBLE set (link-coordination) — must appear by dispatch name in the plugin output. Extra plugin-internal twins are allowed; the drift gate (test above) already asserts their presence + byte-reproducibility. - plugin/hooks/hooks.json: regenerated (twins preserved; same-event ordering now internal-first — cosmetic for independent context/advisory hooks). settings.json / .mcp.json / zcode.json UNTOUCHED (model unchanged). Closes #1053. Verified: tsc --noEmit clean, test:hooks 826/826, test:principles 311/311, render-harness-config --check exit 0. Prior-art: skipped — SSOT-registration fix for existing shipped hooks, no new capability.
artyhoo
added a commit
that referenced
this pull request
Jul 21, 2026
…inciples + rule-index + install-sh + hooks + synth-bundle) (#1051) * fix(gates): restore green principles + rule-index on staging (3 pre-existing blockers) `staging` HEAD failed three push gates independently of any in-flight branch, blocking every `git push` in the repo: 1. principle 11 F1 — `.claude/rules/zcode-parity-doctrine.md` (#1045) landed with neither a `Prior-art:` trailer nor a verbatim-path SSOT citation. Closed with SSOT row #220 (the only reachable closure form once the adding commit merged; direct precedent #211 after PR #907). The doctrine file is left BYTE-IDENTICAL to staging — editing it would drag its pre-existing gitignored-plan link (.ai-factory/plans/, not in git) into lychee's changed-file scan. 2. `render-rule-index --check` — the doctrine's 312-char `Fires:` row pushed the index to 3357B over a 3072B ceiling that was ALREADY 99% consumed (3044/3072) before it landed, i.e. structurally unmeetable rather than violated. Raised INDEX_MAX_BYTES 3KB->4KB (3357 < 4096) with the reasoning recorded at the constant; index + AGENTS.md region regenerated, doctrine row unchanged. 3. principle 13 — `2026-07-18-universal-skill-stack-driven-prep.md` (#1048) landed with no §1.7 self-review section. Appended §10 as a self-review of the patch as merged (findings untouched). Verified: `npm --prefix packages/core run test:principles` 311/311 pass, `test:backends` 191/191 pass, `render-rule-index --check` exit 0. Prior-art: prior-art-evaluations.md#220 (REFERENCE — own-stack gate maintenance; F1 accepts only trailer-on-adding-commit or verbatim-path SSOT row, and the adding commits are already merged). * fix(gates): regenerate install-sh baselines drifted by #1043 (4th pre-existing blocker) PR #1043 (S6 2B-standardize, 2026-07-19) rewrote three hooks — inject-matching-rule.sh, inject-output-language.sh, inject-project-digest.sh (env-first REPO_ROOT standardization) — but did NOT regenerate the byte-identical install-sh baseline fingerprints. Proof: on origin/staging the source hook inject-matching-rule.sh hashes to 8edd7f… while every baseline fingerprint still records the pre-#1043 e1f96e… (baselines last updated by #1031, 2026-07-18, one day BEFORE #1043). The install-sh battery is pull_request-gated (audit-self.yml :622 if github.event_name == 'pull_request'), so pushes to staging never ran it and the drift sat latent; every PR since #1043 inherits the red. SNAPSHOT_MODE=capture regenerated all 8 npm baselines; diff is EXACTLY the 3 hook hashes (verified: no other file changed, new hashes match the real hook files). `SNAPSHOT_MODE=compare` → 11/11 byte-identical. Prior-art: skipped — test-fixture baseline regen after an upstream hook edit (#1043), no new capability. * fix(gates): make inject-subagent-context.test.ts hermetic (5th pre-existing blocker) inject-subagent-context.test.ts shipped RED by #1047 and never passed on staging (merged past — Principles job is non-blocking). Its 5 zcode-branch tests called the hook with ZCODE_PROJECT_DIR set (for the _is_zcode gate) but relied on the REAL repo's .claude/session-bootstrap.md carrying a `<!-- digest:start/end -->` block — which it does not, by design: the framework's main-session digest is emitted dynamically by inject-session-bootstrap.sh's heredoc, never cached as a static block (a static copy would drift from the heredoc's dynamic AIF_HOOK_LANG line). So the hook correctly no-ops (empty stdout) → JSON.parse throws. Fix mirrors the sibling inject-project-digest.test.ts (which is hermetic): build a temp fixture repo whose session-bootstrap.md carries a digest block seeded with inject-session-bootstrap.sh's own output, and point the hook at it via CLAUDE_PROJECT_DIR (the root the hook reads at :44). The SSOT/no-drift assertion stays meaningful (verifies the hook reads + appends the block verbatim). Test-only change; the hook and the maintainer-owned session-bootstrap.md are untouched. Verified: test:hooks 825/826 pass (the remaining 1 = harness-config-drift, a distinct zcode-parity SSOT-drift bug from #1036/#1046 filed as #1053 — needs a domain decision, not a blind regen). Prior-art: skipped — test hermeticity fix for an existing hook, no new capability. * fix(gates): register plugin twins in PLUGIN_INTERNAL_HOOKS SSOT (6th pre-existing blocker) harness-config-drift.test.ts failed on staging (render --check exit 1) — the LAST pre-existing Principles-job red. Root cause: #1036 (inject-project-digest, inject-output-language) and #1046 (warn-subagent-report-zcode) hand-added their plugin-channel entries directly to the GENERATED plugin/hooks/hooks.json but never registered them in PLUGIN_INTERNAL_HOOKS — the renderer's SSOT for plugin-only hooks that have no framework-model counterpart. So `render --write` removed them (43-line drift) and `--check` reported it. These three are genuinely plugin-only: absent from .claude/settings.json AND from harness-model.json, each with a real plugin/hooks/ sibling script, reaching ZCode consumers ONLY via the plugin channel. Regenerating them away would revert shipped #1036/#1046 functionality; the SubagentStart arm is also inexpressible from the model (event ∉ ZCODE_EVENTS). PLUGIN_INTERNAL_HOOKS is exactly their intended home. Changes: - render-harness-config.mjs: register the 4 twin entries in PLUGIN_INTERNAL_HOOKS (UserPromptSubmit ×2, SubagentStart, PostToolUse, Stop). PLUGIN_INTERNAL_HOOKS / PLUGIN_INCOMPATIBLE stay module-private (no export) — the drift test no longer imports them (see below). - harness-config-drift.test.ts: rewrite the N6 plugin-coverage assertion from count-equality (which only held before the twins landed, and would have required importing the registries from the untyped .mjs — breaking `tsc --noEmit` with TS7016, no declaration file) to coverage-BY-NAME: for every zcode-supported event, each model-derived hook — except the maintainer-only PLUGIN_INCOMPATIBLE set (link-coordination) — must appear by dispatch name in the plugin output. Extra plugin-internal twins are allowed; the drift gate (test above) already asserts their presence + byte-reproducibility. - plugin/hooks/hooks.json: regenerated (twins preserved; same-event ordering now internal-first — cosmetic for independent context/advisory hooks). settings.json / .mcp.json / zcode.json UNTOUCHED (model unchanged). Closes #1053. Verified: tsc --noEmit clean, test:hooks 826/826, test:principles 311/311, render-harness-config --check exit 0. Prior-art: skipped — SSOT-registration fix for existing shipped hooks, no new capability. * fix(gates): regenerate stale synth-bundle for semver 7.8.5 (7th pre-existing blocker) The committed packages/core/install/synth-and-wire.bundle.mjs was built with an older semver (pre-7.8.x — lacks functions/truncate.js + the isPrereleaseIdentifier helper). CI's synth-bundle drift gate (#755) rebuilds with the semver that root `npm install` pins per the ROOT package-lock (packages/core/node_modules/semver = 7.8.5): audit-self.yml runs the root `npm install` step ("hoists tsx to root for the hooks-suite gate") BEFORE the synth-bundle gate, so the gate builds against 7.8.5 — not the 7.8.1 that the earlier `npm ci --prefix packages/core` installs. A fresh 7.8.5 build differs from the stale committed bundle → DRIFT. Why this was masked until now: the Principles job runs principles-meta-tests FIRST, and that step has been red on staging since 2026-07-18 (the principle-11/13 blockers this PR's 4e1f6d4 fixes), which short-circuits the job before the synth-bundle step ever runs. Once the earlier blockers go green the job reaches the synth-bundle gate and the pre-existing staleness surfaces — a hidden-behind-a-red-gate regression, not introduced by any commit in this PR. Fix: rebuild with CI-parity node_modules — replicated audit-self's install sequence (`npm ci --prefix packages/core` then root `npm install` → packages/core semver 7.8.5), then committed the result. The build-synth-bundle.sh:50 path-normalization is untouched; only the bundled semver library content differs (+72/-8). Verified: `NODE_ENV=development bash scripts/build-synth-bundle.sh --check` exit 0; test:hooks 826/826; test:principles 311/311 — all green in the same node_modules state. Prior-art: skipped — snapshot regeneration of a generated artifact, no new capability. --------- Co-authored-by: t <t@t.co>
artyhoo
added a commit
that referenced
this pull request
Jul 21, 2026
…ent-gate/@matcher-parity preventer (#1054) * docs(spec): matcher-widening design — 6-hook fix + lightweight probe (3-round review) Captures the design after 3 rounds of dual top-down + bottom-up review. Scope: widen 6 PostToolUse gate hooks Edit|Write -> Edit|Write|MultiEdit (incl. check-doc-authority-header from merged #1009, discovered when the worktree base was refreshed off stale de7fc9d -> 4a9b508). Single atomic PR: the fix + a ~15-LOC grep probe (parity for case-TOOL hooks, @file-content-gate marker for path-only hooks) + a live RED fixture. Full principle No.33 deferred to incidence N>=2. * docs(spec): round-4 — dual-layer gate (edit-time + CI-backstop) + missed edit-site Round-4 review (fresh base 4a9b508) found: - R1: reuse-first path — extend check-hook-marker.sh (edit-time) + channel-coverage.sh (CI) instead of a new standalone probe. Lands preventer at earliest reachable channel (rule-enforcement-channel- selection §4), avoids consumer dead-token (R2). - R5: missed edit-site tests/install-sh/gh-934-ship-doc-authority-hook.test.sh:56 (hardcoded Edit|Write equality from #1009) — added to §2 inventory. - R4 nit: citation BFR §5 -> dual-implementation-discipline §9 (probe != principle slot). Decision (option B): DUAL-LAYER gate — edit-time catches early, CI catches reliably (the original bug was itself an edit-time-hook bypass; single-layer trust in the same mechanism class is not robust). * fix(hooks): widen 6 PostToolUse gate matchers Edit|Write -> Edit|Write|MultiEdit MultiEdit emits a SEPARATE PostToolUse event (tool_name:MultiEdit) that an Edit|Write matcher does NOT catch (falsifier: docs/2026-07-17-matcher- widening-design.md §1). A MultiEdit that violates a gate's rule slipped past silently. The real bug shape is body<->registration drift: 3 of 6 hooks already had 'case "$TOOL" in Edit|Write|MultiEdit)' in their body but were registered Edit|Write. SSOT-first: harness-model.json (5) -> render --write -> setup.d/10-skills.sh (206,246) + install.sh (469,493) (inject-matching-rule + check-doc-authority- header shipped) + gh-934 tests (session-ux 56/57/136, doc-authority-hook 56) + re-capture 4 greenfield npm baselines (brownfield stable, 3 python stable). inject-memory-codification stays Write-only (A5 — creation moment). check-doc-authority-header from merged #1009 (was invisible until the worktree base was refreshed off stale de7fc9d -> 4a9b508). Gates GREEN: render --check, byte-identical 11/11, refresh-covers-full- delivery, both gh-934 tests (11/11 + 14/14), shellcheck clean. STEP 2 (dual-layer preventer: extend check-hook-marker.sh + channel- coverage.sh + RED fixtures) follows in this same PR. * docs(spec): night-mode kickoff for STEP 2 resume Self-contained SSOT for an unattended resume session. Includes the STEP 0 hypothesis-verification block (verify state on disk BEFORE work), the resume point (STEP 1 done at f707d68d1; STEP 2 Layer 1 in-progress uncommitted), the strict task order A-G, the autonomy/fork policy for an operator-away run, critical nuances (shipped-only tolerance, A5, frozen intentional-skip list), and the policy reminders (Decision B dual-layer APPROVED, do not re-litigate). * fix(hooks): propagate matcher widening to plugin channel (post-rebase) STEP 1 widened 5 PostToolUse gate matchers Edit|Write -> Edit|Write|MultiEdit in the CC channel (.claude/settings.json) + SSOT (.ai-factory/harness-model.json). At STEP 1's base the plugin channel (plugin/hooks/hooks.json, added by staging zcode-parity #1030/#1036/#1046) did not exist; post-rebase it carried the same 5 gate matchers still narrow. Widened them surgically (exact-string sed, 5 lines) rather than via `render-harness-config.mjs --write`, which would clobber the hand-maintained ZCode-parity twins (inject-project-digest, inject-output-language, warn-subagent-report-zcode) that emitPlugin does not reproduce from the SSOT. Diff = 5 matcher lines only; twins intact (5 refs); valid JSON. See docs/superpowers/specs/2026-07-17-matcher-widening-decisions.md D2 + finding F1 (pre-existing, ungated staging SSOT/render drift — out of scope, surfaced only). Prior-art: skipped — rendered-output matcher widening + session decisions doc, no new capability (no dependency, no new code module). * feat(hooks): @file-content-gate edit-time gate (Layer 1 of matcher-widening preventer) Prevents the body<->registration matcher-drift class that STEP 1 fixed from recurring. A hook that validates a file's *content* (path-only — no internal tool_name filter) MUST be registered with matcher Edit|Write|MultiEdit, else a MultiEdit edit that violates the rule slips past silently (the registration matcher is the gate's ONLY tool-filter). 3 of 6 hooks had drifted this way. Mechanism (edit-time, earliest reachable channel per README goal): - New `# @file-content-gate:` marker declared on the 4 path-only content-gate hooks (validate-prompt, check-doc-authority, check-doc-authority-header, and check-hook-marker documents its own extension). - check-hook-marker.sh extended: when a hook declares @file-content-gate AND is registered in the framework-self .claude/settings.json, assert its matcher contains Edit, Write AND MultiEdit; else _adv_violation (exit 1 on CC). - Tolerance: a shipped-only hook (no framework-self settings.json entry, e.g. check-doc-authority-header) is SKIPPED — the gate has no framework-side matcher to read for it; its matcher is enforced by the install-sh firing tests + Layer 2. Tests: 3 new arms in check-hook-marker.test.ts (paired-negative Edit|Write -> exit 1; paired-positive Edit|Write|MultiEdit -> exit 0; shipped-only absent-from-settings -> exit 0). Full suite 11/11 green on the rebased base (staging #1030 runHook object shape + ZCODE arm reconciled). CI-backstop (population sweep) follows in Layer 2. Design: 4-round-reviewed spec docs/superpowers/specs/2026-07-17-matcher-widening-design.md. Prior-art: skipped — edit-time gate extension to an existing hook + marker comments on existing hooks, no new capability (no dependency, no new code module >=50/80 LOC, no new subdir). * docs(spec): refine F1 — two plugin generators (sidecar gated, hooks.json twin-entry not render-reproducible) * feat(hooks): @matcher-parity rule + Layer 2 CC-config population backstop Closes the case-TOOL coverage gap (cold-review MAJOR) and places the CI backstop in the CC-config bucket, NOT the harness-agnostic channel-coverage probe (operator-flagged). Layer 1 (edit-time, check-hook-marker.sh): - New @matcher-parity invariant: a hook with `case "$TOOL" in <tools>)` must have its registered matcher ⊇ that case-arm set. Covers the 3 case-TOOL gates that carry no @file-content-gate marker (check-kickoff-traps, check-worker-dispatch-channel, check-hook-marker itself). Uses each hook's existing `case` author-declaration — no new marker, no allowlist. Self-calibrating: inject-memory-codification (A5, Write-only) stays GREEN (matcher Write ⊇ case-arm Write) with no hardcoded MultiEdit demand. - Refactored the settings.json matcher lookup into _reg_matcher(), anchored on the closing `"` so check-doc-authority no longer substring-matches check-doc-authority-header. Layer 2 (CI backstop, packages/core/hooks/check-hook-marker.test.ts): - A vitest describe block runs the REAL edit-time gate against EVERY tracked hook against the live settings.json — catches a matcher narrowed directly in harness-model.json/settings.json even when no hook .sh is edited (the vector Layer 1 cannot see). Reuses the gate verbatim (no parallel population reader). 2 RED fixtures prove non-vacuousness; ≥5-hook sentinel. - Bucket: CC-config (hooks), NOT tests/agnosticism/ — the MultiEdit requirement is CC-specific (inert on other harnesses), so it must not contaminate the harness-agnostic PORTABLE verdict. Supersedes the kickoff Task-C plan (channel-coverage.sh extension + tests/fixtures/matcher-drift/). Suite 17/17 green. See decisions doc D3 (bucket) + D4 (parity rule). Prior-art: skipped — edit-time gate extension + test arms for the existing hook capability, no new capability (no dependency, no new code module >=50/80 LOC, no new subdir). * chore(install-sh): re-capture byte-identical baselines (marker edit + staging inject-* staleness) The @file-content-gate marker added to the shipped check-doc-authority-header.sh shifts its install fingerprint. Re-capturing (snapshot.sh, SNAPSHOT_MODE=capture) also corrects three baselines that origin/staging left STALE — inject-matching-rule / inject-output-language / inject-project-digest — hooks staging changed via zcode-parity but never re-captured (my source for them is byte-identical to staging's; only staging's recorded baseline was old). byte-identical is an all-or-nothing gate → a full re-capture is mandatory to green it; there is no way to refresh only the marker-touched hook. Captured hashes match the live source (verified via shasum). See decisions doc F2. Framework-internal hooks (check-hook-marker, check-doc-authority, validate-prompt) ship to 0 consumers → untouched here. Prior-art: skipped — regenerated install snapshot baselines after a shipped-hook marker edit, no new capability. * fix(hooks): @matcher-parity extraction strips comment lines (cold-review MINOR) The case-arm extraction matched `case "$TOOL" in …` on ANY line, including comments — and this hook's own @matcher-parity doc block now contains that pattern in prose. Current population was safe only because the real code arm precedes the comment mentions and head -1 takes the first; one reorder would mis-extract the prose arm → false parity verdict. Strip comment lines (^[[:space:]]*#) before extraction, mirroring the ^#-anchored marker check. New test arm PARITY comment-immunity: a prose `case "$TOOL" in Read)` above the real `case "$TOOL" in Write)` with matcher Write → exit 0 (reads the real {Write}, not {Read}). Suite 18/18 green. Prior-art: skipped — hardening the edit-time gate's parser + one test arm, no new capability. * docs(spec): D5 — rebased onto staging trunk-restore (8 inherited reds + F1 cleared); 1147/1147 green * fix(test): annotate runRealHook fullEnv as NodeJS.ProcessEnv (tsc --noEmit) vitest passed but tsc --noEmit (all-workspaces CI job) rejected `delete fullEnv.ZCODE_PROJECT_DIR` — the {...process.env, CLAUDE_PROJECT_DIR} literal lost ProcessEnv's index signature. Explicit annotation restores it, mirroring the sibling runHook helper. Prior-art: skipped — type annotation on a test helper, no new capability. * docs(spec): matcher-widening morning report — PR #1054 green, awaiting operator merge --------- Co-authored-by: t <t@t.co>
artyhoo
added a commit
that referenced
this pull request
Jul 25, 2026
…g" doc claims (#1151) Tier-1 (bulky-simple) kickoff for a bounded mechanical sweep: every claim in CLAUDE.md + .claude/rules/*.md asserting that something is "not yet landed" is verified against origin/staging and either corrected or kept with its proof. Two anchors are pre-proven stale by command in §2: - CLAUDE.md:132 says AifHandoffBackend._resolveProfileId has "no exact-match priority"; staging's AifHandoffBackend.ts:137-141 has exactly that. - zcode-parity-doctrine.md §3 marks Wave B stages 5/6/7B/9C "impl pending"; all four merged (#1046/#1043/#1047/#1044). The renderer-side D3 declaration sync is a named park trigger (§5), not scope. Prior-art: skipped — dispatch-input document only, no new capability: adds no dependency and no code module; the sweep it scopes edits existing docs. Co-authored-by: Test <test@example.com>
10 tasks
artyhoo
added a commit
that referenced
this pull request
Jul 25, 2026
…rule + zcode Wave B cascade (stale-pending-claims) (#1156) * docs(rules): truth-sweep stale «pending» claims (Anchor A + Wave B merged) Mechanical truth-sweep over the bounded §1 population defined in the kickoff grep — every «pending» / «not yet» / «impl pending» claim in CLAUDE.md + .claude/rules/*.md re-verified live against origin/staging `fab189d09e`. 5 files, 14 adjudicated hits: 11 STALE (corrected), 3 STILL-TRUE (left + inline evidence). Anchor A — CLAUDE.md:132: the «Marker value rule» paragraph misdescribed the AifHandoffBackend resolver as «no exact-match priority». Verified false: AifHandoffBackend.ts:137-141 carries the exact-name short-circuit that landed to save the Z.AI GLM-5.2 / Z.AI GLM-5.2 SDK prefix case. Corrected (a) mechanism description, (b) line anchor :131 → :137-141, (c) «only channel» → two channels (runtime short-circuit + authoring rule as belt-and-braces). Binding advice kept. Anchor B — zcode-parity-doctrine.md: Wave B Stages 5/6/7B/9C (#1043/#1044/#1046/#1047) ARE merged; the doctrine's §3 status column, §4 rationales, and §7 self-reflexive note still claimed «Decided; impl pending». Flipped past-tense; re-counted §5 ZCode tier (13→16 framework hook twins — Stage 6 added 3); §2 census rows 3/12 (plugin-gap → parity, both plugin twins shipped via Stage 6) and 19 (cc-only → parity via 4D hybrid) updated with rollup. PARK-1 honoured: scripts/render-harness-config.mjs left untouched (out of scope); the doc is honest about the *outstanding* runtime loud-declaration sync. memory-codification.md §4(0): «PENDING settings.json WIRING» → WIRED (.claude/settings.json:168 verified live; park-2 condition not met). autonomous-loop-continuity.md + destination-environment-verification.md: H11 (idle watchdog) + H12 (branch→umbrella gate) STILL-TRUE — added inline grep evidence on origin/staging `fab189d09e`. Acceptance: principle 09 (44 tests) + principle 12 (44 tests) green; rule-index check clean (no header changes); all 5 files ≤600 lines; T-SPC-B post-edit cascade sweep on zcode-parity-doctrine.md clean. Cursor :97 «live end-to-end testing is a follow-up» STILL-TRUE (no live-test commits in `git log origin/staging`). §1.7 forward+backward applied in PR body. Per-hit table there. * docs(rules): fix §4(0) contradiction — CTX Stage 1 IS activated (rework) Rework iteration 2 finding [d7b1cd54dfbb]: line 47 of memory-codification.md read "shipped but pending activation" while line 49 (edited in the prior commit) read "WIRED" + "live" — a self-contradiction introduced by this PR's own edit two lines apart. Verified the hook IS wired: jq '.hooks.PostToolUse[7]' .claude/settings.json → { matcher:"Write", command:"bash … inject-memory-codification.sh" } Updated line 47: "shipped but pending activation" → "shipped and activated". Single-line, in-place; no other changes. §4 acceptance gates re-run green: - wc -l .claude/rules/memory-codification.md → 96 (≤600) - npx tsx scripts/render-rule-index.mjs --check → up-to-date - npx vitest run packages/core/principles/09-doc-authority-hierarchy.test.ts → 37/37 pass - npx vitest run packages/core/principles/12-ai-laziness-traps.test.ts → 7/7 pass * docs(rules): revert row-3 parity flip — Stage 6 shipped row 4 twin, not row 3 (rework round 2) Rework round-1 content: row 3 (`check-doc-authority-header`) reverted to `plugin-gap` after cold-audit found Stage 6 shipped row 4's `check-doc-authority` twin, NOT row 3's. Rollup counts accordingly (parity strict 12→11, plugin-gap 0→1). §4 split into «Row 12 closed» + «Row 3 STAYS» with the live grep evidence. Cursor tier row augmented with the live proving commands (ls + grep exit 2 / 0). Dangling `(PARK-1)` references removed from §3 note and §4 Row 19 — the anchor was undefined; prose reads cleanly without it. Round-1 rework content reviewed ACCEPTED (iter-4 / iter-3 reviewers); this commit just lands it on the branch (harvest of HEAD would otherwise drop it). * docs(rules): un-dangle the §6 plan pointer — gitignored path, never tracked (lychee, harvest-fix) Pre-existing on staging (not introduced by the sweep): §6 linked .ai-factory/plans/zcode-parity-s10-doctrine-doc.md, a gitignored local plan absent from every checkout. Same defect class as the sweep's population — a doc pointing at a non-existent artifact. Restated the rationale inline, kept the path as a code span (not a link). Prior-art: skipped — one-line doc link fix, no new capability --------- Co-authored-by: Test <test@example.com>
artyhoo
added a commit
that referenced
this pull request
Aug 17, 2026
…as wired (D8) (#1434) ## What and why Stage **S2b** of the pipeline-chips ADR — the D8 PreCompact residue writer. **F8 closed 2026-08-17** (operator GO on the SSOT #108 PreCompact item, given in a live session), so the stage is unblocked; the bench ran first, per D8's own ordering. A compacted session left nothing behind. The D7 context-arm's debounce flag stays spent across an auto-compact, so a session re-climbing after one gets no second nudge — and no record of what it had been doing. D8's shape is the one already registered in SSOT #108: the hook itself **writes** the state, deterministically. Round-1's alternative («remind the model to write a handoff before compaction») is undeliverable as specified — a non-blocking PreCompact hook gives the model no execution window. ## The bench, and what it did not prove D8 makes a liveness bench the gate and orders it **before** the writer. It ran, and its outcome is split three ways rather than summed — [`docs/meta-factory/research-patches/2026-08-17-precompact-liveness-bench.md`](docs/meta-factory/research-patches/2026-08-17-precompact-liveness-bench.md): - **Contract — PROVEN.** 15 cases green; the suite is not vacuous (seeding `select(.isSidechain != true)` → `select(true)` flipped exactly one case, then restored); run against one real 202-line CC transcript, not only fixtures; and end-to-end **reader/writer agreement** — the hook wrote to `.claude/orchestrator-prompts/_residue-e2e-proof.md` (gitignored, `.gitignore:7`) and the `/pipeline` §1 fence, run verbatim, printed it back. - **Manual delivery — NOT FIRED.** Registration needs `.claude/settings.json`, whose own deny-list carries `Write(.claude/settings.json)`; hooks snapshot at session start; `/compact` is an operator action. - **Auto delivery — NOT OBSERVED**, and not observable from history either: 0 files matched `"isCompactSummary":true` across the 60 most-recent transcripts. Consistent with a 1M window, but per T14 that is «coverage insufficient», never «works». D8 pre-accepted this split («an honest *auto path unverified, manual path proven* is an acceptable bench outcome»). What ships is one notch below even that — manual is proven at the CONTRACT level, unfired at the DELIVERY level — so it is stated rather than rounded up. ## Two deviations from D8-as-written (recorded in the ADR, not silent) - **The `auto` matcher is dropped; the hook registers on all triggers.** With `auto` alone the only half a human can trigger on demand is unfireable *by construction*, and a manual `/compact` discards the same context. Bench finding B-2; the writer is matcher-independent (asserted). - **The renderer surface is `emitPlugin`, not `emitClaude`.** `emitClaude` already passes any event through, so there was nothing to change there. The real gap: `emitPlugin` `continue`d silently past every event outside `ZCODE_EVENTS`, so a hook on an inexpressible event vanished with no trace — the `#warning-nobody-reads` shape inverted. It now names each dropped event in a note op (fires today on `SubagentStart`/`SubagentStop`). ## Changes - `.claude/hooks/precompact-residue.sh` — new. Extracts the ai-title anchor (fallback: first user turn) and the **last** recap block, marker sourced from the lang pack; fallback is the last main-thread assistant excerpt. `select(.isSidechain != true)` is load-bearing (subagent turns share the transcript). Writes even with no transcript — «a session existed here and was compacted» is itself the fact the reader needs. Every path exits 0 and stdout stays empty: PreCompact *can* block on exit 2, and blocking compaction on a residue-write failure would strand a session at a full window. - `packages/core/hooks/precompact-residue.test.ts` — 15 cases. Every case pins `AIF_HOOK_LANG`: the same fixture scored `recap` under `en` and `excerpt` under the ambient `ru`, caught by a `bash -x` trace, so an unpinned suite is a coin-flip. - `scripts/render-harness-config.mjs` — the off-`ZCODE_EVENTS` skip becomes a note op. - `.claude/rules/zcode-parity-doctrine.md` — census row 21 (`cc-only`), rollup `Total = 20 → 21` and `cc-only 2 → 3`, §4 rationale, §5 tier-table counts. - `.claude/skills/pipeline/SKILL.md` — §1 Step-1 injection extended **inside the existing line** (the file sits at 599/600). The line still opens `head -200 "$(bash …print-orch-home.sh…)` because principle 39's paired-negative seeds its hardcode by matching that prefix — reshaping the line broke the test, so the shape was kept rather than the test edited. - `docs/meta-factory/prior-art-evaluations.md` — #108 `Last reviewed` → 2026-08-17, PreCompact item marked BUILT with both deviations from its sketch. ## Registration is NOT in this PR — and why it cannot be `.claude/settings.json` is tracked **and** rendered from `.ai-factory/harness-model.json`, with the drift gate checking the real tree every CI run. Editing only the SSOT was tried in a sandbox copy of both files: `✗ harness-config drift: - .claude/settings.json: drift vs SSOT`, exit 1. So the two files move in ONE commit — and one of them is agent-blocked. The hand-off block is below. ## Verification - `npx vitest run packages/core/hooks/precompact-residue.test.ts` → 15/15; seeded-break → `1 failed | 14 passed`; restored → 15/15. - `npx vitest run packages/core/principles/` → 402 (401 pass, 1 skip), 42 files. Principle 39 caught the fence reshape and is green after the fix. - `node scripts/render-harness-config.mjs --check` → `✓ harness-config up-to-date`, with the new note firing. - `bash tests/plugin/hook-paths.test.sh` → PASS=82 FAIL=0. `SNAPSHOT_MODE=compare bash tests/install-sh/snapshot.sh` → 15 pass / 0 fail (byte-identical; the hook is framework-internal, not installer-delivered). `npm run format:check`, `npm run typecheck`, `npm run check:skill-drift` → green. - Worth flagging for the next session in this worktree: `node_modules` was **empty** on arrival (the unprovisioned-worktree class), which made three unrelated suites fail with spawn `-1` before `bash scripts/worktree-node-modules.sh --apply` fixed them. ## Operator hand-off — registration (agent-blocked, per CLAUDE.md «settings.json uncommittable») Idempotent, backs up first, validates before moving, and touches **both** files so the drift gate stays green. Paste as one block: ```bash ( set -euo pipefail cd /Users/art/code/rules-as-tests-aif || exit 1 H='bash "$CLAUDE_PROJECT_DIR/.claude/hooks/precompact-residue.sh"' test -f .claude/hooks/precompact-residue.sh || { echo "hook missing on this checkout — merge the S2b PR first"; exit 1; } if jq -e --arg c "$H" 'any(.hooks.PreCompact[]?.hooks[]?; .command == $c)' .claude/settings.json >/dev/null; then echo "already registered — nothing to do"; exit 0 fi cp .claude/settings.json ".claude/settings.json.bak.$(date +%Y%m%dT%H%M%S)" cp .ai-factory/harness-model.json ".ai-factory/harness-model.json.bak.$(date +%Y%m%dT%H%M%S)" jq --arg c "$H" '.hooks.PreCompact = [{hooks:[{type:"command",command:$c}]}]' .claude/settings.json > /tmp/s.json jq -e . /tmp/s.json >/dev/null && mv /tmp/s.json .claude/settings.json jq --arg c "$H" '.hooks.PreCompact = [{command:$c}]' .ai-factory/harness-model.json > /tmp/m.json jq -e . /tmp/m.json >/dev/null && mv /tmp/m.json .ai-factory/harness-model.json node scripts/render-harness-config.mjs --check && echo "OK — registered, drift-clean. Commit BOTH files together." ) ``` **No matcher on purpose** (bench finding B-2 — an `auto`-only matcher makes the manual half unfireable). Then the residual bench, which only you can run: start a **fresh** session in this repo, run `/compact`, and check that `_residue-<session>.md` appeared under `.claude/orchestrator-prompts/`. The auto half closes itself the first time a session reaches the ceiling. Until that fire is observed, D8's delivery leg is honestly **unverified**; the bench file is where the observation gets recorded. Prior-art: prior-art-evaluations.md#108 (Claude Code orchestrator mechanical-gate hooks, verdict ADOPT) — this is that row's PreCompact item, built rather than re-derived: the «save wave-state before compaction» shape is taken from the entry, and the two places this implementation departs from its sketch (per-session file in the resolved orch-home instead of one clobbered `.claude/session-state.md`; the hook writes rather than reminds) are recorded in the row itself, whose `Last reviewed` date is updated in the same commit. The row's other four items are untouched, and its DECISION-NEEDED note is why registration stays operator-landed. ## Fidelity verdict FIDELITY: skipped — spec-homed contour with no orchestrator-prompts umbrella; an in-session stage authored against the merged ADR, not a deliverable against a kickoff. Same basis as S3 (#1426). ### §1.7 Forward-check applied `attention-is-not-a-mechanism.md:1` — the residue has a NAMED reader (`.claude/skills/pipeline/SKILL.md:85`), not «someone will find the file»; and the renderer change exists precisely because a silent skip is `#warning-nobody-reads` (`scripts/render-harness-config.mjs:421`). `dual-implementation-discipline.md:6` — the hook carries `@cc-only-rationale` with a real reason (PreCompact ∉ `ZCODE_EVENTS`, `scripts/render-harness-config.mjs:46`) plus `@dual-pair: hook-lang-i18n` for the pack it sources; §3 audience triage is done explicitly (framework-internal, not installer-delivered) rather than defaulted. `no-paid-llm-in-ci.md:1` — deterministic bash + jq, zero inference. `language-discipline.md:1` — the residue's own prose is English (category 1); the recap marker stays lang-pack-sourced (category 3), and one test asserts the ru/en split in both directions. `build-first-reuse-default.md:1` — the capability is REUSE-of-verdict: SSOT #108 already carries ADOPT for this hook class, so no new BUILD verdict was minted; the two departures from its sketch are recorded in the row. `doc-authority-hierarchy.md:2` — the new research-patch carries a Type/Owner/Feeds header. ### §1.7 Backward-check applied Class of this change = **«a CC hook is added on an event ZCode cannot express»**. Surfaces where that class occurs, enumerated from `.ai-factory/harness-model.json` + `ZCODE_EVENTS` rather than from this PR's own diff: (a) `SubagentStart` → `inject-subagent-digest`, census row 16 — already classified `cc-only` with a §4 rationale and a documented `PreToolUse:Agent` fallback, **SWEPT-CLEAN**, and it is now also named by the new renderer note; (b) `SubagentStop` → `warn-subagent-report`, row 19 — has a 4D-hybrid parity variant (#1046), so the note naming it is informational, not a gap, **SWEPT-CLEAN**; (c) `WorktreeCreate` → `worktree-setup`, row 20 — `cc-only`, not in default settings, **SWEPT-CLEAN**; (d) the rollup arithmetic in `.claude/rules/zcode-parity-doctrine.md:105` and the tier-table counts at `:114`/`:115` — both restate the census totals and both were stale the moment row 21 landed, **GAP-FOUND → fixed in this PR** (`Total 20→21`, `cc-only 2→3`, «all 20 hooks»→21, «Two rows»→«Three rows», three CC-only events→four); (e) the plugin-twin population, `tests/plugin/hook-paths.test.sh` — generation correctly produced **no** twin for the new hook (`ls plugin/hooks/ | grep -i precompact` → empty), which is the right answer for an event the plugin channel cannot carry, **SWEPT-CLEAN**; (f) the install-sh baselines — the hook is framework-internal, so the fingerprints stayed byte-identical (15/15), **SWEPT-CLEAN**. No surface superseded. ## Parked **Should the residue writer ship to consumers?** It is framework-internal today (audience triage recorded in the hook header). The consumer-facing consequence is that the `/pipeline` §1 line, which IS shipped, reads a residue a consumer never gets — inert and harmless (`2>/dev/null`), but asymmetric. Widening is a separate decision with its own installer + baseline consequences, not a side effect of this stage. **Still open in the contour:** the first live chip click has still never happened — it would close F3 and F9 by observation alone. F4 and F7 remain open.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
§1.7 Forward-check applied
§1.7 Backward-check applied