6f9efcf2e8
Review findings on PR #30, all three applied. 1 [P1]. The claim that the handshake watchdog overwrites a genuine mid-session disconnect 8s later is FALSE on any tree that has PR #21. `close` routes BOTH arms through `setShellFailure` (app.js:248-250), which clears the timer synchronously (app.js:165-169). It was believed because it was measured honestly — against a tree predating PR #21 — and then reported as "surviving" that PR on the strength of a simulation that re-created only the `opened` flag and not the other half of the fix, the routing. A simulated fix that is not the fix you named reproduces a bug the real code does not have, and reads exactly like a confirmation. Rather than delete the claim, execute the property it denied: the open-then-dropped test now runs past HANDSHAKE_TIMEOUT_MS + 2s and asserts "sent no document" never appears. Proved discriminating by removing `setShellFailure`'s `clearTimeout` — the assertion then fails with exactly the message xps8 described. gotcha 48 and the spec's comment block corrected; the ticket is cited in neither. Recorded as doctrine(a_simulated_fix_must_be_the_fix_you_named), the sibling of an_injected_fault_must_be_the_fault_you_named that fails the other way: a wrong fault proves a check that cannot fire, a wrong simulated fix proves a bug that does not exist. 2 [P1]. The previous commit created the contradiction it was meant to remove: design-principles recorded the measured ~2.2s while kb.pl still said "under 2 seconds". The ambiguity between per-binary and wall clock is what let it drift, so both now state two numbers — no single binary over 2s, wall clock is the sum, measured 1309 tests / 33 binaries / ~2.2s / longest 1.42s — in kb.pl, design-principles and repo-map alike. Also 16 -> 17 in doctrine(binding_dump_diff). 3 [P2]. The new audit exclusion was unanchored, so `improvise-gp` was a prefix match that would silently exclude a real future ticket. Anchored `^…$`. It immediately caught the example id the explanatory paragraph itself spelled out — a recipe is code, twice over. CLAUDE.md: declined, with evidence. Both copies of the duplicated tail sit inside `<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->` markers written by `bd setup claude` and `bd setup codex` and refreshed against that hash. A process change there is one tool invocation, not two hand edits, and a pointer makes `bd setup <recipe> --check` report it modified. Recorded as kb_issue_found(claude_md_tail_is_generated_not_duplicated) so it is not re-proposed. kb.pl: doctrine 54->55, kb_issue_found 11->12; invariant 51, decision 47, location 34 unchanged. kb_consistent passes. cargo test --workspace: 1309 passed, 0 failed, 2 ignored. harness (Playwright): 12 passed, 3 skipped (webkit has no OPFS). Dead-ID audit with the anchored recipe: 157 tokens, all resolve. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>