chore(memory): BDR-087 + LRN-146 + BLK-020 — capitalize

This commit is contained in:
Bastien Chanot
2026-09-03 00:28:45 +02:00
parent ea9e5c1dab
commit 6aca40a810
4 changed files with 41 additions and 0 deletions
+20
View File
@@ -222,3 +222,23 @@ rules:
- **Solution**: `"accessibility.signals.terminalBell": { "sound": "on" }` in CLIENT-side user settings.json (`c:/Users/<u>/AppData/Roaming/Code/User/`). Unreachable from remote: real SSH remote, not WSL (no `/mnt/c`, `/proc/version` no Microsoft). User applied, retest → both channels OK.
- **Status**: resolved (per-client-machine, not repo-portable).
- **Reference**: `~/.claude/hooks/notify-attention.sh` header already documented the setting; never applied. New client machine → bell mute again while toast works. Silent-degradation class [[LRN-047]].
## BLK-020 — notify-attention: both channels dead on one VS Code client — 2026-09-02
- **Friction**: client-side prereqs applied on Windows box (ext `wenbopan.vscode-terminal-osc-notifier` + `accessibility.signals.terminalBell` sound:on), window reloaded. AskUserQuestion → nothing. `idle_prompt` 90s wait → nothing. Direct write `\a\a` + OSC 777 to claude own pty (`/dev/pts/2`) → nothing. Second client machine, same SSH server, same hook, same registries → both channels OK.
- **Server side cleared**: hook dry-run emits `BELx2 + OSC 777 + ST` correctly, `jq` present, matcher covers `idle_prompt`, ext NOT wrongly installed remote-side. Not a hook bug — same class as [[BLK-019]] (client default silently degrades).
- **Real cause**: unresolved. Facts: claude runs under `dtach -c ~/.dtach/claude-190012`; claude fd1 = `/dev/pts/2` (inner pty, dtach master side), REAL VS Code terminal = `/dev/pts/1` held by dtach client pid 742794. `VSCODE_SHELL_INTEGRATION` unset this terminal; ext marketplace doc requires shell integration ON. BUT other working session (`claude-154323`) also runs under dtach → dtach alone insufficient explanation, weight shifts back to client-side.
- **Probes run**: direct write to `/dev/pts/1` (real VS Code pty, chain alive: bash pts/1 → dtach client 742794 S+ → master → claude pts/2) → no bell, no toast. Visible-marker injection both paths → user saw neither, BUT inconclusive: claude TUI repaints, injected text clobbered next frame. Only BEL is repaint-proof, and BEL stays silent.
- **Client settings verified by user**: settings.json path correct (no VS Code profile indirection), `terminalBell` sound on, ext installed + enabled local side. VS Code recent (server dirs 2026-08), so ≥ 1.93 ext requirement met.
- **Next probe**: user opens FRESH VS Code integrated terminal (no dtach, no claude TUI) and runs `printf '\a\a\033]777;notify;Test;hello\033\\'`. Isolates client renderer from claude/dtach path. Beep+toast there → fault in claude/dtach path; nothing → client-side, diff against working machine.
- **Fresh-terminal probe (decisive)**: user ran `printf '\a\a\033]777;notify;Test;hello\033\\'` in NEW VS Code terminal → toast OK, bell still silent. Splits one symptom into TWO independent faults.
- **Fault A (toast in claude session)**: ext parses only terminals created AFTER its activation. Claude terminal pts/1 born 19:00, ext installed later same day → that terminal never hooked. Fix: restart claude in fresh terminal, or re-attach existing dtach session from one (`dtach -a ~/.dtach/<sess>`; dtach broadcasts to multiple clients, no session loss). NOT a dtach filtering bug — earlier hypothesis wrong.
- **Fault B (bell)**: silent even in fresh terminal where toast works → not terminal path, VS Code audio side. Toast sound = Windows notification (works); bell = VS Code process audio (mute). Suspects: signal volume option, Windows volume mixer entry for Code, output device. Probe: palette `Help: List Signal Sounds` → Terminal Bell plays preview or not.
- **Fault A RESOLVED (verified 2026-09-02)**: re-attached session from fresh terminal (`dtach -a ~/.dtach/claude-190012`, new client pts/3). Both sends toasted — one through session path (pts/2, dtach broadcast), one direct. Rule: ext hooks only terminals born AFTER its activation → install ext, THEN start/re-attach claude session. dtach broadcast means zero session loss.
- **Fault B still open**: bell silent on every path. New signal: toasts arrive but user reports NO sound at all, while [[BLK-019]] machine got audible Windows toast sound. Both audio channels dead + both visual channels fine → common factor is client audio output, not terminal stream. Suspects ranked: Windows per-app notification sound off for Code, system/app volume mixer mute, wrong output device, `accessibility.signalOptions.volume` 0.
- **Fault B ROOT CAUSE ISOLATED (2026-09-02)**: palette `Help: List Signal Sounds` → Terminal Bell preview plays NO sound, while Windows toast sound IS audible. Preview bypasses terminal, BEL, hook, dtach, ext entirely → VS Code renderer audio itself mute on this box. Toast sound emitted by Windows shell, not by Code → explains why one audio channel works and other does not.
- **Fix candidates (client, ranked)**: (1) Windows per-app volume mixer — Code muted/0, or per-app OUTPUT DEVICE pointing at disconnected device (mixer only lists app after it attempts playback → hit preview first, then open mixer); (2) VS Code `accessibility.signalOptions.volume` = 0 kills all signals; (3) compare both against working machine.
- **Pragmatic out**: toast already carries audible Windows sound → attention signal functional without bell. Bell is redundant channel, not blocker.
- **Fault B RESOLVED (2026-09-03)**: cause = Windows per-app volume mixer, Code entry at 0. Toast audible throughout because Windows shell emits that sound, not Code → masked a plain app-volume mute. User set volume up → bell audible.
- **Status**: resolved (A: ext hooks only terminals born after activation → install ext THEN start/re-attach session; B: Code app volume 0 in Windows mixer).
- **Lesson**: two independent client faults presented as one symptom ("nothing works"). Splitting probe = run signal in FRESH terminal + play VS Code's own sound preview. Preview bypasses terminal/BEL/hook/dtach/ext → isolates renderer audio in one step. Do that FIRST next time, before any server-side archaeology.
- **Reference**: [[BLK-019]] bell-only variant (resolved differently — setting alone insufficient here), [[LRN-145]] terminalSequence-not-/dev/tty pattern. Silent-degradation class [[LRN-047]].
+10
View File
@@ -96,6 +96,7 @@ rules:
| BDR-084 | 2026-08-24 | /tour multi-project: parallel runners (LRN-083 derogation, bounded), runner inherits session model | accepted |
| BDR-085 | 2026-08-25 | User permanent rules: writing-style always-on in rules/, web build+security path-scoped | accepted |
| BDR-086 | 2026-08-26 | darwin: threshold gates full loops; verified defects fixed regardless of unit score (paired-validated, batched checkpoint) | accepted |
| BDR-087 | 2026-09-03 | Stop hook = attention signal only, never control flow; one script for Notification + Stop | accepted |
---
@@ -1113,3 +1114,12 @@ Branch feature/user-writing-web-rules, UNMERGED (human gate).
- **Why**: leaving a verified destructive path (hotfix `git restore .` wiping tolerated user edits, file scored 85) unfixed = score-worship; rubric serves quality, not the inverse.
- **Alternatives rejected**: strict threshold (ships known bugs); optimize-everything (cost, HL-4 diminishing returns).
- **Reference**: run 2026-08-26, commits 6eceedb..6eac7fb, `.claude/audits/DARWIN-2026-08-26.md`.
## BDR-087 — Stop hook = attention signal only, never control flow
- **Date**: 2026-09-03
- **Decision**: `hooks/notify-attention.sh` wired on BOTH `Notification` (matcher = input-needed set) AND `Stop` (no matcher). One script, branches on `.hook_event_name` when `.message`/`.notification_type` absent → Stop yields "Claude has finished responding". Bell + toast now fire every turn end.
- **Why**: Notification types cover input-needed ONLY. Turn-end had no event; nearest was `idle_prompt`, ~60s late — useless for Remote-SSH user away from screen. User enumerated turn-end as required case.
- **Alternatives rejected**: second dedicated script (duplicates terminalSequence + jq logic, two files to keep in sync); `idle_prompt` alone (60s lag); SubagentStop too (noise, subagent completion not user-visible moment).
- **Guard vs prior refusal**: [[BDR-083]] (unlazy review, GATE 0) REFUSED a Stop hook using `decision:"block"` (forces continuation, inverts human gates). THIS Stop hook returns `terminalSequence` + `suppressOutput` only, exit 0, zero control-flow effect. Signal ≠ control. Do not read the refusal as banning Stop outright.
- **Status**: accepted.
- **Reference**: [[LRN-146]] event-coverage gap, [[BLK-020]] client-side faults, [[LRN-145]] terminalSequence pattern. Verified live: turn-end + AskUserQuestion both ring; `permission_prompt` unexercisable under `defaultMode: auto`.
+5
View File
@@ -452,3 +452,8 @@ rules:
## 2026-09-01
- Attention signal shipped: hooks/notify-attention.sh + Notification entry in settings.json (bell x2 + OSC 777 toast via terminalSequence). Client-side VS Code steps pending: terminalBell sound:on + osc-notifier ext. [[LRN-145]]. Branch chore/notify-attention-hook, UNMERGED.
- Pre-existing model switch opus[1m] committed separately on same branch.
## 2026-09-03
- Attention signal completed + verified end-to-end. Two client faults isolated ([[BLK-020]] resolved): ext instruments only terminals born AFTER activation (re-attach via `dtach -a`, no session loss); Code app volume 0 in Windows mixer killed bell while Windows-emitted toast sound masked it.
- Coverage gap found + closed: `Notification` matcher covers input-needed only, turn-end had no event. `Stop` wired on same script, branches on `.hook_event_name` ([[BDR-087]], [[LRN-146]]). Verified live: turn-end + AskUserQuestion ring; `permission_prompt` unexercisable under `defaultMode: auto`.
- BDR-087 + LRN-146 + BLK-020 capitalized. Branch feature/notify-stop-event, UNMERGED (human gate).
+6
View File
@@ -1394,3 +1394,9 @@ Rule: when editing a doctrine file under structure locks, grep the test's lock s
- **Context**: 2026-09-01 — attention bell for VS Code Remote-SSH (CLI on remote Linux). Hook subprocess has no controlling TTY; /dev/tty unreliable. Docs: terminalSequence = supported side-effect field, fires even on events that discard output.
- **Pattern**: Notification hook → stdout JSON `{suppressOutput:true, terminalSequence:"<BELx2><OSC 777 notify><ST>"}`. VS Code terminal ignores OSC 777/9 natively (claude-code #28338); client-side ext wenbopan.vscode-terminal-osc-notifier converts to native toast over Remote-SSH; beep needs accessibility.signals.terminalBell sound:on. permission_prompt fires ~6s late, idle_prompt ~60s.
- **Future**: any hook ringing/notifying the terminal (bell, toast, title) — terminalSequence, never /dev/tty. Input-needed matcher set: permission_prompt|idle_prompt|agent_needs_input|elicitation_dialog|elicitation_url_dialog.
## LRN-146 — Notification event alone misses end-of-turn; Stop is the missing event
- **Context**: 2026-09-03 — attention signal verified end-to-end after [[BLK-020]]. Matcher `permission_prompt|idle_prompt|agent_needs_input|elicitation_*` covers input-needed cases ONLY. "Claude finished speaking" has no notification_type — nearest was `idle_prompt`, ~60s late. Gap invisible until explicitly enumerated by user.
- **Pattern**: wire SAME hook script on TWO events — `Notification` (matcher = input-needed set) + `Stop` (fires once per turn end, supports terminalSequence, no matcher). Script branches on `.hook_event_name` when `.message`/`.notification_type` absent: Stop → "Claude has finished responding", else default. Read stdin ONCE into var, jq the var (stdin not re-readable).
- **Verified**: turn-end bip+toast OK, AskUserQuestion selector bip+toast OK. `permission_prompt` NOT exercisable under `defaultMode: auto` — ask-rules (`python3 -c *`, `curl`…) auto-approved, no prompt raised. Hooks hot-reloaded by file watcher, no restart.
- **Future**: enumerate the events a signal must cover BEFORE wiring, one per user-visible moment. Notification ≠ lifecycle-complete. SubagentStop exists too for agent completion.