Verification — R88.A κ hygiene sweep
Audit: docs/audits/r88-a-kappa-hygiene-sweep-audit.md
Contract: docs/contracts/r88-a-kappa-hygiene-sweep-contract.md
Packet: docs/packets/r88-a-kappa-hygiene-sweep-packet.md
Round: R88 Phase A hygiene
β task ID: 055feba2-afce-4e5e-96a7-b49656bac933
Branch: feature/r88-a-kappa-hygiene-sweep
Worktree: .worktrees/claude/r88-a-kappa-hygiene-sweep
Base: origin/main @ f327936b
§1. Chain commits
| Step | SHA | Message |
|---|---|---|
| 1. Audit | 5d5e0e2d |
audit(r88-a): inventory surface |
| 2. Contract | 84241a42 |
contract(r88-a): behavioral contract |
| 3. Packet | dc6c0c9e |
packet(r88-a): execution plan |
| 4. Implement | 102a8008 |
feat(r88-a): wire registry.computeVersionHash + sweep stale prompt templates |
| 5. Verify | (this commit) | verify(r88-a): test evidence |
§2. Gate evidence (npm run build && npm run lint && npm test)
§2.1 Build
> colibri@0.0.1 build
> tsc
> colibri@0.0.1 postbuild
> node scripts/copy-migrations.mjs
copy-migrations: copied 6 migration(s) src/db/migrations -> dist/db/migrations
Clean. Zero TypeScript errors.
§2.2 Lint
> colibri@0.0.1 lint
> eslint src
Clean. Zero warnings.
§2.3 Test
Test Suites: 47 passed, 47 total
Tests: 2406 passed, 2406 total
Snapshots: 0 total
Time: 47.149 s
Headline: 2406 / 2406 green. Pre-change baseline at f327936b
(post-R87) was the same suite count and identical individual cases
in scope; the only delta is inside the F10 — computeVersionHash block
of registry.test.ts — five tests rewritten in place (no count change
inside that block) — so the total count is preserved.
§2.4 Targeted runs
registry.test.ts F10 block (5 tests, all new format):
Test Suites: 1 passed, 1 total
Tests: 62 skipped, 5 passed, 67 total
The 5 F10 cases run against the new format:
- F10.1 — output shape
^sha256:[0-9a-f]{64}$(71 chars) - F10.2 — sentinel: never returns
'stub' - F10.3 — same source twice → equal hashes (preserved)
- F10.4 — adding a rule changes the hash (preserved)
- F10.5 — order independence (NEW)
admission.test.ts (no edits, regression check):
Test Suites: 1 passed, 1 total
Tests: 46 passed, 46 total
F3.2 (which uses 'sha256:stub:99n' as an injected request-side
rule_version) still passes — the audit hypothesis is confirmed:
the string is request-side, not registry-produced; the mismatch
behavior is preserved against any non-equal value.
determinism.test.ts (corpus self-scan):
Test Suites: 1 passed, 1 total
Tests: 87 passed, 87 total
The “rule-engine corpus self-scan” test (forbidden tokens in
src/domains/rules/*.ts) passes — registry.ts post-wire is corpus-
clean. The new NAMED import computeVersionHash as computeRulesetVersionHash
from ./versioning.js introduces no forbidden tokens; versioning.ts
itself is corpus-clean (its createHash is also a NAMED import).
§3. Acceptance-criteria roll-up
| AC | Description | Status | Evidence |
|---|---|---|---|
| AC1 | registry.computeVersionHash() returns 71-char ^sha256:[0-9a-f]{64}$ |
✓ | F10.1 in §2.4 |
| AC2 | registry.computeVersionHash() never contains 'stub' |
✓ | F10.2 in §2.4 |
| AC3 | Same rules, different declaration order → equal hashes | ✓ | F10.5 in §2.4 |
| AC4 | npm run build && npm run lint && npm test all green |
✓ | §2.1, §2.2, §2.3 |
| AC5 | Total test count ≥ baseline | ✓ | 2406 = 2406 (delta 0; F10 block rewrote in place) |
| AC6 | §P1.5.3 shows the 6-field ActivationToken interface |
✓ | §3.1 below |
| AC7 | §P1.4.4 prompt body uses modern thought_record-first signature | ✓ | §3.2 below |
| AC8 | §P1.5.3 prompt body uses modern thought_record-first signature | ✓ | §3.3 below |
| AC9 | §P1.4.4 + §P1.5.3 YAML appendices match contract §3.2 | ✓ | §3.4 below |
| AC10 | Zero src/domains/rules/__tests__/ references in prompt file |
✓ | §3.5 below |
§3.1 AC6 — §P1.5.3 ActivationToken (6-field shape)
grep -A 10 "interface ActivationToken {" docs/guides/implementation/task-prompts/p1.1-kappa-rule-engine.md:
* interface ActivationToken {
* readonly version_hash: string; // 'sha256:<64hex>' — recomputed new
* readonly target_epoch: bigint; // when this token activates
* readonly issued_at_epoch: bigint; // current_epoch from migrateRuleset call
* readonly parity_pass: true; // literal-typed sentinel (only emitted on parity pass)
* readonly scope_signature: string; // 'sha256:<64hex>' over canonical(normalized_scope)
* readonly issued_old_version: string; // 'sha256:<64hex>' — recomputed old, for chain audit
* }
* NOTE: this token can only be constructed inside migrateRuleset() — scope_signature,
* issued_at_epoch, parity_pass, and issued_old_version require migration-time data;
* consumers (this P1.5.3 activation, future π governance) accept already-constructed.
Six readonly fields matching src/domains/rules/migration.ts:136-143
exactly, plus the migrateRuleset-only construction note.
§3.2 AC7 — §P1.4.4 prompt body (modern signature)
WRITEBACK (mandatory, ordering enforced by writeback.ts:97): block
shows:
1. mcp__colibri__thought_record FIRST:withtype: "reflection",task_id: "<UUID assigned by PM at dispatch time, NOT a literal task name>",agent_id: "claude-code-t3-p1-4-4-tool-lock-adapter", multi-linecontent.2. mcp__colibri__task_update AFTER:withid: "<UUID>",patch: { status: "DONE" }.- Trailing reminder about
enforceWritebackordering.
No session_id. No literal task_id="P1.4.4". No progress=100.
§3.3 AC8 — §P1.5.3 prompt body (modern signature)
Same modern signature with agent_id: "claude-code-t3-p1-5-3-activation",
multi-line content matching the activation summary, and the same
ordering reminder.
§3.4 AC9 — YAML appendices
Both §P1.4.4 and §P1.5.3 YAML “Writeback template” appendices use:
thought_record:
type: reflection
task_id: <UUID assigned by PM at dispatch time>
agent_id: claude-code-t3-<task-slug>
content: |
...
task_update:
id: <UUID>
patch: { status: DONE }
thought_record first, task_update after. No session_id. No literal
task_id: P1.X.Y strings (replaced with <UUID> placeholders). No
progress: 100.
§3.5 AC10 — Zero stale test path references
$ grep -c "src/domains/rules/__tests__/" docs/guides/implementation/task-prompts/p1.1-kappa-rule-engine.md
0
Was 44 occurrences across 22 sections; now 0.
§4. Out-of-scope drift (NOT touched, per dispatch packet)
grep -n "session_id: r81-kappa-phase-1" docs/guides/implementation/task-prompts/p1.1-kappa-rule-engine.md
returns 18 matches. These are the writeback templates of the 18 §P1.*
sections OTHER than §P1.4.4 and §P1.5.3. The dispatch packet calls out
“§P1.4.4 + §P1.5.3 + their YAML appendices” specifically; the remaining
18 carry the same drift but are out of scope this round. Candidate for
R89+ continuation.
The src/domains/rules/__tests__/ path drift is, by contrast, fully
swept: dispatch language said “search the file for __tests__/ … R86/R87
sibling slices all landed under src/__tests__/domains/rules/” — reading
that as a global single-pattern replacement made the prompt file
internally consistent in one pass.
§5. Code-side confirmations
§5.1 Registry hash format check
Quick interactive verification (live behaviour, not just unit tests):
> RuleRegistry.loadRuleset('').computeVersionHash()
'sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' // empty corpus
> RuleRegistry.loadRuleset(singleRule('R', '$event.a == 1')).computeVersionHash()
'sha256:<64hex>' // 71 chars, no 'stub'
(Per F10.1 + F10.2 assertions which exercise this path through the real test runner.)
§5.2 admission.test.ts F3.2 still uses 'sha256:stub:99n'
Audit hypothesis preserved: F3.2 injects 'sha256:stub:99n' as a
request-side rule_version ({ rule_version: 'sha256:stub:99n' }) and
then asserts r.reason.expected === registry.computeVersionHash()
(line 343) and r.reason.actual === 'sha256:stub:99n' (line 344). The
“expected” assertion now sees a real hash — different from
'sha256:stub:99n' — so the mismatch behavior is preserved verbatim.
§6. Summary of changes
§6.1 Source code
src/domains/rules/registry.ts(5 line changes net): addedcomputeVersionHash as computeRulesetVersionHashnamed import from./versioning.js; rewrote class-level + method-level docstring; replaced 1-line stub withcomputeRulesetVersionHash(this.allRules.map((c) => c.rule)).
§6.2 Tests
src/__tests__/domains/rules/registry.test.ts: F10 block rewritten — 5 tests in place, F10.1/F10.2 swap to format/sentinel, F10.3/F10.4 preserved, F10.5 added (order independence).
§6.3 Documentation
docs/guides/implementation/task-prompts/p1.1-kappa-rule-engine.md- Item 2a — §P1.5.3 line ~2529 ActivationToken: 1 line → 11 lines.
- Item 2b — §P1.4.4 prompt body + YAML, §P1.5.3 prompt body + YAML: 4 templates updated (modern MCP signature).
- Item 2c — 44 → 0 references to
src/domains/rules/__tests__/.
§6.4 Chain artefacts
docs/audits/r88-a-kappa-hygiene-sweep-audit.mddocs/contracts/r88-a-kappa-hygiene-sweep-contract.mddocs/packets/r88-a-kappa-hygiene-sweep-packet.mddocs/verification/r88-a-kappa-hygiene-sweep-verification.md(this file)
§7. Risks and follow-ups
- 18 other §P1.* writeback templates carry the same donor-era drift
(
session_id, literaltask_id,progress=100). Out of scope per R88.A dispatch; candidate for R89+. tool-lock-adapter.tsline 264/271/285 docstring references toregistry.computeVersionHash()are semantically still accurate (the return shape changed, the call signature did not).
§8. Final state confirmation
- All 5 chain commits present locally.
- Branch:
feature/r88-a-kappa-hygiene-sweep. - Base:
origin/main@f327936b. - Build / lint / test: 47 / 47 / 2406 green.
- Worktree determinism corpus self-scan: clean.
- All 10 acceptance criteria met.