Compare commits
20
Commits
f90ee74a19
..
v1.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4560114c59 | ||
|
|
9b3b96a8f2 | ||
|
|
8d5d154c28 | ||
|
|
0e8018ae7b | ||
|
|
12d7fc1483 | ||
|
|
e801b90307 | ||
|
|
92eb27c4e2 | ||
|
|
679c2cda7b | ||
|
|
2c0439a0a8 | ||
|
|
2ed51573f7 | ||
|
|
a627201bee | ||
|
|
d8962824c0 | ||
|
|
648bc6e90d | ||
|
|
1cb77c3f57 | ||
|
|
3c243ece97 | ||
|
|
51b65727e7 | ||
|
|
2f8dc6be1a | ||
|
|
dc4f78b1f0 | ||
|
|
709facfb52 | ||
|
|
d3d72fd3ca |
@@ -456,4 +456,7 @@ rules:
|
||||
## 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).
|
||||
- BDR-087 + LRN-146 + BLK-020 capitalized. Branch feature/notify-stop-event, merged to develop (f90ee74).
|
||||
- Post-merge regression: toast dead again after re-attach from a RESTORED terminal, bell fine. Root cause [[LRN-147]]: ext hooks only terminals born after its activation; `enablePersistentSessions` restores terminals before it. Fix = disable persistent sessions, or fresh terminal + `dtach -a`. Verified: 3/3 toasts on fresh pty.
|
||||
- Same-day counter-example broke that cause: second session's terminal deaf though created LATER, same window, ext global, shells identical. Trigger unknown; [[LRN-148]] adds the 5s pre-flight test + demotes LRN-147's mechanism claim.
|
||||
- Attention signal refined: per-event labels (BDR-087 follow-on), silence on non-attention events, and no turn-end signal while `background_tasks` non-empty ([[LRN-149]]). Payload dump beat the docs: `background_tasks` undocumented for Stop but present on the wire. Branch bugfix/notify-subagent-spawn.
|
||||
|
||||
@@ -1400,3 +1400,24 @@ Rule: when editing a doctrine file under structure locks, grep the test's lock s
|
||||
- **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.
|
||||
|
||||
## LRN-147 — VS Code restores terminals BEFORE ext activation → toast dies every restart
|
||||
- **Context**: 2026-09-03, second hit same day. Bell OK, toast gone, after user re-attached session from a restored terminal. Probe on that pty: OSC 777 unique + OSC 777 repeated + OSC 9 → all three silent, while BEL rang. Same pty, bell works ⇒ bytes arrive, ext just not hooked to that terminal.
|
||||
- **Pattern**: `wenbopan.vscode-terminal-osc-notifier` instruments a terminal only if it exists AFTER ext activation. `terminal.integrated.enablePersistentSessions` (default true) restores terminals at window startup, i.e. BEFORE lazy ext activation → every restored terminal is permanently deaf to OSC. Recurs at each VS Code restart, silently, bell still ringing so it reads as "half broken".
|
||||
- **Fix**: client setting `"terminal.integrated.enablePersistentSessions": false` → no terminal pre-exists activation. Fallback without it: after VS Code start, open a FRESH terminal then `dtach -a ~/.dtach/<session>` (dtach broadcasts, old client can stay or be closed, session never lost).
|
||||
- **Diagnostic shortcut**: bell rings + toast dead on the SAME pty = terminal-instrumentation fault, not audio, not hook, not server. Bell dead + toast alive = audio fault ([[BLK-020]] fault B). The two channels split the search space; check which one survives before anything else.
|
||||
- **Future**: any client-side terminal-parsing ext over Remote-SSH inherits this. Verify instrumentation on the ACTUAL attached pty after every restart, never assume yesterday's terminal.
|
||||
|
||||
## LRN-148 — terminal instrumentation is per-terminal + unpredictable; pre-flight test before attaching
|
||||
- **Refines**: [[LRN-147]] blamed restored-terminals-born-before-activation. Too narrow — counter-example same day: two terminals SAME VS Code window, pts/3 (born 01:58:33) instrumented, pts/7 (born 01:59:29, LATER) deaf. Ext is GLOBAL (marketplace: Enable/Disable pause parsing extension-wide, no per-terminal setting), shells identical on every server-side measurable: `VSCODE_INJECTION=1`, TERM, TERM_PROGRAM, same `--init-file` shell-integration path, ~2-3s between shell start and dtach. Trigger NOT identified.
|
||||
- **Pattern**: treat instrumentation as a per-terminal property that can silently fail for unknown reasons. Cheap pre-flight before committing a long-lived session to a terminal: `printf '\a\a\033]777;notify;NEUF;test\033\\'` typed IN that terminal. Toast → instrumented, attach. Bell only → deaf terminal, open another. Costs 5s, replaces an hour of pty archaeology.
|
||||
- **Recovery**: deaf terminal never repairs. Open fresh terminal, pre-flight it, `dtach -a ~/.dtach/<session>`. dtach broadcasts, so old client may stay attached; session never at risk.
|
||||
- **Diagnostic split (holds)**: bell alive + toast dead = terminal instrumentation. Toast alive + bell dead = client audio ([[BLK-020]]). Neither = bytes never arrive.
|
||||
- **Future**: do NOT assert the born-before-activation cause as established — it fits the first incident, not the second. Unknown trigger is the honest state.
|
||||
|
||||
## LRN-149 — Stop hook payload carries background_tasks; use it to skip premature signals
|
||||
- **Context**: 2026-09-03. User: "notif à la création d'un sous-agent alors qu'il faudrait pas". Instrumented hook, ran probe subagents: NEITHER subagent creation NOR completion calls the hook. Only event = `Stop`, fired when the turn ends right after spawning. Signal was real but LIED ("Finished responding" while work continued).
|
||||
- **Pattern**: dump the real payload (`printf '%s' "$payload" >> file.jsonl`) instead of trusting docs — docs list Stop fields without `background_tasks`, the wire has it: `[{"id","type":"subagent","status":"running","description","agent_type"}]`. Rule: on Stop, `(.background_tasks // []) | length` > 0 → exit 0 silent. Next turn end signals for real. Interaction events (permission/question) always signal, background or not.
|
||||
- **Fail-open**: field absent (older client) → still signal. Missed notification worse than extra one.
|
||||
- **Cross-session gotcha**: hook is user-scope, so EVERY session runs it. A single-file dump (`> file`) gets overwritten by another project's session — append JSONL and filter on `.cwd`. That accident proved `permission_prompt` fires with `message="Claude needs your permission"` (unexercisable in this session under `defaultMode: auto`).
|
||||
- **Future**: any hook needing turn-completion semantics must check background_tasks; "turn ended" ≠ "work done". Verified live: Stop with 0 tasks signals, Stop with 1 running subagent silent.
|
||||
|
||||
+64
-10
@@ -6,7 +6,23 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.5.0] — 2026-09-13
|
||||
|
||||
### Added
|
||||
- **Attention signals on the terminal (BDR-087)** — new
|
||||
`hooks/notify-attention.sh`, wired on `Notification` (input-needed
|
||||
matcher) and on `Stop` (no matcher). Returns a double BEL plus an
|
||||
OSC 777 toast through the `terminalSequence` JSON field, since hooks
|
||||
have no controlling TTY. Signal only: `suppressOutput`, exit 0, zero
|
||||
control-flow effect, which is what separates it from the `decision:
|
||||
"block"` Stop hook [[BDR-083]] refused. Each event reaches the toast
|
||||
as a readable label instead of a snake_case type; events needing no
|
||||
attention (`agent_completed`, `auth_success`) exit silently; a turn
|
||||
that ends with `background_tasks` still running stays quiet and
|
||||
signals at the real end. Client-side prerequisites over Remote-SSH
|
||||
are documented in the script header ([[BLK-020]]): VS Code
|
||||
`accessibility.signals.terminalBell` for the beep, an OSC notifier
|
||||
extension for the Windows toast.
|
||||
- **User permanent rules (BDR-085)** — three new rules/ files from the
|
||||
user's rule text: `writing-style.md` (always-on: em-dash ban, no slop
|
||||
vocabulary, no hedging chains, deliverable self-check),
|
||||
@@ -44,8 +60,23 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
||||
hotfix explicitly runs no floor. Behavioral RED: 16/16 fresh unprimed
|
||||
runs followed the new doctrine (EVAL-027).
|
||||
64 new assertions in `lib/tests/gates.test.sh`.
|
||||
- **`lib/tests/seo-geo-contract.test.sh`** — census locking the seo/geo
|
||||
agent ⇄ dispatcher machine contract: judge verdict grammar, FIX BUNDLE +
|
||||
READY-TO-APPLY sentinel, signals handoff, every STEP header (interiors
|
||||
included), bundle item fields, score labels, scoring blocks, envelope
|
||||
keys (46→71 assertions across the C1 chantier).
|
||||
|
||||
### Changed
|
||||
- **Skill and agent quality campaign, 54 units (BDR-086)** — full darwin
|
||||
v2.1 pass over the 31 personal skill-systems and 23 agents, excluding
|
||||
the gstack/external symlinks and machine-owned units. Fresh baseline
|
||||
mean 83.4; the 13 units under the user-set threshold of 80 were
|
||||
optimized to completion, and verified defects in above-threshold units
|
||||
were fixed in a grouped pass rather than left to ship because the score
|
||||
was good enough. Every round was validated by a paired 3-judge majority
|
||||
reading before and after in one call: 36 unit-round verdicts, 24 batch
|
||||
verdicts, all better, zero reverts. Full report and residual findings:
|
||||
`.claude/audits/DARWIN-2026-08-26.md`.
|
||||
- **seo-analyzer + geo-analyzer de-prescribed for Opus 5 (BDR-082)** —
|
||||
process choreography converted to when-guidance under an
|
||||
audience×mode-range invariant; self-output verification demands removed
|
||||
@@ -56,25 +87,48 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
||||
proven by a controlled before/after `/seo` dogfood — judge replay on
|
||||
frozen signals, 42/42 presence assertions on both runs, blind structural
|
||||
reader: interchangeable, recall improved.
|
||||
|
||||
### Added
|
||||
- **`lib/tests/seo-geo-contract.test.sh`** — census locking the seo/geo
|
||||
agent ⇄ dispatcher machine contract: judge verdict grammar, FIX BUNDLE +
|
||||
READY-TO-APPLY sentinel, signals handoff, every STEP header (interiors
|
||||
included), bundle item fields, score labels, scoring blocks, envelope
|
||||
keys (46→71 assertions across the C1 chantier).
|
||||
- **Global instruction layer recalibrated for the Claude 5 family (BDR-081)** —
|
||||
delegation block is now model-neutral when-guidance (the Opus 4.8
|
||||
under-delegation counter inverted on Opus 5, which over-delegates and gets
|
||||
an injected harness cap); "staff engineer" self-check bar dropped (Opus 5
|
||||
over-verification trigger); finish-whole-task clause added to Deviations;
|
||||
written-deliverable length rule added. 308/320 lines.
|
||||
- **Default session model is now `opus[1m]`** (was `claude-fable-5[1m]`).
|
||||
- **`skills-external/emil-design-eng/` untracked** — the file is curl'd
|
||||
from upstream by `install-plugins.sh` when absent and re-fetched by
|
||||
every `update-all.sh` run, so tracking it produced a repo diff on each
|
||||
upstream edit. Same category as `frontend-design/` and `impeccable/`,
|
||||
already ignored on that rationale; a fresh clone re-fetches it.
|
||||
`design-motion-principles/` has the same overwrite behaviour but no
|
||||
bootstrap clone yet, so it stays tracked until that gap closes.
|
||||
|
||||
### Fixed
|
||||
- **hotfix wiped tolerated in-progress edits on its revert path** — every
|
||||
failure branch ran `git restore .`, destroying user edits the run had
|
||||
tolerated. Now a `git stash create` pre-flight snapshot plus a
|
||||
file-scoped restore, and the security gate is fresh-dispatch only.
|
||||
- **skills-perso listed 8 of 31 personal skills** — detection rebuilt on
|
||||
the `link.sh` symlink convention (symlink = external, real dir =
|
||||
personal, gitignored = machine-generated). Live result 31/31, no false
|
||||
positives.
|
||||
- **plan-challenger** — `ERROR` joined the load-bearing verdict grammar
|
||||
(STEP 1 emitted it, the parser enum omitted it); grounded-but-uncertain
|
||||
findings now file as `[MINOR]` with the uncertainty stated, instead of
|
||||
being self-censored (Opus 5 follows conservative-reporting clauses
|
||||
literally).
|
||||
- **design-toolchain hook** — dropped `\bux\b` (2 French-prose false
|
||||
positives; 3rd tightening pass, series LRN-1005/1007); `\bui\b` kept and
|
||||
locked by a must-fire test row.
|
||||
- **plan-challenger** — grounded-but-uncertain findings now file as `[MINOR]`
|
||||
with the uncertainty stated, instead of being self-censored (Opus 5 follows
|
||||
conservative-reporting clauses literally).
|
||||
- **Agent and skill defects found by the campaign's judges** —
|
||||
`init-project` allowed-tools lacked `Agent` and `Skill` while every step
|
||||
dispatches; `commit-change` conflict grep now covers all 7 unmerged
|
||||
codes; `tour --report-only` no longer commits; `harden` severity defers
|
||||
to the calibrated guide and the late SSL Labs grade has an assigned
|
||||
actor; handover writers' stale chapter refs corrected and the anchor
|
||||
gate ordered; `security-auditor` documents the hotfix no-verifier
|
||||
carve-out; `close` enumerates STEP 5C and passes `--no-push` through;
|
||||
`prune-memory` drops a false "v1-untested" note; `code-clean` attributes
|
||||
its executor correctly; plugin-check and onboard fixtures de-drifted.
|
||||
|
||||
## [1.4.0] — 2026-07-22
|
||||
|
||||
|
||||
+40
-11
@@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# Notification + Stop hook — signal the user through the terminal when
|
||||
# Claude needs input (permission prompt, question, idle wait) or has
|
||||
# finished responding.
|
||||
# Claude needs input (permission, question, idle wait) or has finished
|
||||
# responding. Each case gets its own readable label so the toast says
|
||||
# which one fired.
|
||||
#
|
||||
# Runs on the remote (Linux); the only channel that crosses SSH into the
|
||||
# VS Code client is the terminal stream. Hooks have no controlling TTY,
|
||||
@@ -12,27 +13,55 @@
|
||||
# volume for Code in the Windows volume mixer (BLK-020).
|
||||
# - OSC 777 notify -> Windows toast via the client-side extension
|
||||
# "Terminal Notification" (wenbopan.vscode-terminal-osc-notifier).
|
||||
# The extension only instruments terminals created AFTER it is
|
||||
# active: install it first, then start or re-attach the session.
|
||||
# A terminal can be deaf to OSC while the bell still rings; test it
|
||||
# before attaching a session to it (LRN-148).
|
||||
# Both are invisible no-ops in terminals that ignore them.
|
||||
set -u
|
||||
|
||||
payload=$(cat 2>/dev/null)
|
||||
read_field() {
|
||||
printf '%s' "$payload" | jq -r "$1 // empty" 2>/dev/null \
|
||||
| tr -d '\000-\037' | cut -c1-200
|
||||
| tr -d '\000-\037' | cut -c1-160
|
||||
}
|
||||
|
||||
msg=$(read_field '.message // .notification_type')
|
||||
if [ -z "$msg" ]; then
|
||||
case "$(read_field '.hook_event_name')" in
|
||||
Stop) msg="Claude has finished responding" ;;
|
||||
*) msg="Claude Code needs your input" ;;
|
||||
# How many background tasks are still running as the hook fires.
|
||||
background_count() {
|
||||
count=$(printf '%s' "$payload" | jq -r '(.background_tasks // []) | length' 2>/dev/null)
|
||||
case "$count" in ''|*[!0-9]*) echo 0 ;; *) echo "$count" ;; esac
|
||||
}
|
||||
|
||||
event=$(read_field '.notification_type')
|
||||
[ -n "$event" ] || event=$(read_field '.hook_event_name')
|
||||
|
||||
|
||||
|
||||
case "$event" in
|
||||
# Turn end while a subagent still runs is not the real end: stay silent,
|
||||
# the next turn end will signal once the work is actually done.
|
||||
Stop) [ "$(background_count)" -eq 0 ] || exit 0
|
||||
label="Finished responding" ;;
|
||||
permission_prompt) label="Needs your permission" ;;
|
||||
agent_needs_input) label="Asks you a question" ;;
|
||||
idle_prompt) label="Waiting for you" ;;
|
||||
elicitation_dialog|elicitation_url_dialog) label="Needs your input" ;;
|
||||
# anything else (agent_completed, auth_success, quota_*) stays silent:
|
||||
# signal only for turn end and moments needing the user.
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
detail=$(read_field '.message')
|
||||
if [ -n "$detail" ]; then
|
||||
# Claude Code's own wording often restates the label ("Claude needs your
|
||||
# permission"). Append it only when it actually adds something.
|
||||
short=$(printf '%s' "$detail" | tr '[:upper:]' '[:lower:]' | sed 's/^claude //')
|
||||
case "$(printf '%s' "$label" | tr '[:upper:]' '[:lower:]')" in
|
||||
*"$short"*) : ;;
|
||||
*) label="${label}: ${detail}" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
bell=$(printf '\a')
|
||||
esc=$(printf '\033')
|
||||
seq="${bell}${bell}${esc}]777;notify;Claude Code;${msg}${esc}\\"
|
||||
seq="${bell}${bell}${esc}]777;notify;Claude Code;${label}${esc}\\"
|
||||
jq -cn --arg seq "$seq" '{suppressOutput: true, terminalSequence: $seq}'
|
||||
exit 0
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.4.0
|
||||
1.5.0
|
||||
|
||||
Reference in New Issue
Block a user