14 Commits
Author SHA1 Message Date
Bastien Chanot 4560114c59 Merge release/1.5.0 into main 2026-09-13 21:26:44 +02:00
Bastien Chanot 9b3b96a8f2 chore(release): 1.5.0 — version.txt + CHANGELOG 2026-09-13 21:25:37 +02:00
Bastien Chanot 8d5d154c28 chore(memory): LRN-149 — background_tasks gates the turn-end signal 2026-09-10 03:03:22 +02:00
Bastien Chanot 0e8018ae7b fix(hooks): no turn-end signal while background work runs
Ending a turn right after spawning a subagent fired the bell and a toast
saying the response was finished, while the work continued. The Stop
payload carries background_tasks, so skip the signal when it is not
empty; the next turn end signals once the work is really done.

Interaction requests still signal during background work. A missing
field still signals, so an older client loses nothing. Also drop the
message suffix when it merely restates the label.
2026-09-10 03:03:22 +02:00
Bastien Chanot 12d7fc1483 fix(hooks): stay silent on events that need no attention
Only turn end and the moments needing the user should signal. Any other
event reaching the hook, such as agent_completed or auth_success, now
exits without emitting, so a subagent finishing rings nothing even if the
Notification matcher is ignored.
2026-09-03 02:56:49 +02:00
Bastien Chanot d8962824c0 Merge release/1.4.0 into main 2026-07-22 15:48:41 +02:00
Bastien Chanot 648bc6e90d Merge release/1.3.1 into main 2026-07-20 22:23:47 +02:00
Bastien Chanot 1cb77c3f57 Merge release/1.3.0 into main 2026-07-20 19:40:23 +02:00
Bastien Chanot 3c243ece97 Merge release/1.2.1 into main 2026-07-20 14:34:09 +02:00
Bastien Chanot 51b65727e7 Merge release/1.2.0 into main 2026-07-20 14:07:28 +02:00
Bastien Chanot 2f8dc6be1a Merge release/1.1.0 into main 2026-07-16 13:57:07 +02:00
Bastien Chanot dc4f78b1f0 Merge release/1.0.0 into main 2026-07-16 13:22:29 +02:00
Bastien Chanot 709facfb52 Merge release/4.0.0 into main 2026-06-30 15:33:22 +02:00
Bastien Chanot d3d72fd3ca Merge hotfix/gstack-ignore-gitmodules into main 2026-06-29 13:57:12 +02:00
5 changed files with 97 additions and 14 deletions
+1
View File
@@ -459,3 +459,4 @@ rules:
- BDR-087 + LRN-146 + BLK-020 capitalized. Branch feature/notify-stop-event, merged to develop (f90ee74). - 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. - 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. - 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.
+7
View File
@@ -1414,3 +1414,10 @@ Rule: when editing a doctrine file under structure locks, grep the test's lock s
- **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. - **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. - **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. - **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
View File
@@ -6,7 +6,23 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased] ## [Unreleased]
## [1.5.0] — 2026-09-13
### Added ### 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 permanent rules (BDR-085)** — three new rules/ files from the
user's rule text: `writing-style.md` (always-on: em-dash ban, no slop user's rule text: `writing-style.md` (always-on: em-dash ban, no slop
vocabulary, no hedging chains, deliverable self-check), 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 hotfix explicitly runs no floor. Behavioral RED: 16/16 fresh unprimed
runs followed the new doctrine (EVAL-027). runs followed the new doctrine (EVAL-027).
64 new assertions in `lib/tests/gates.test.sh`. 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 ### 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)** — - **seo-analyzer + geo-analyzer de-prescribed for Opus 5 (BDR-082)** —
process choreography converted to when-guidance under an process choreography converted to when-guidance under an
audience×mode-range invariant; self-output verification demands removed 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 proven by a controlled before/after `/seo` dogfood — judge replay on
frozen signals, 42/42 presence assertions on both runs, blind structural frozen signals, 42/42 presence assertions on both runs, blind structural
reader: interchangeable, recall improved. 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)** — - **Global instruction layer recalibrated for the Claude 5 family (BDR-081)** —
delegation block is now model-neutral when-guidance (the Opus 4.8 delegation block is now model-neutral when-guidance (the Opus 4.8
under-delegation counter inverted on Opus 5, which over-delegates and gets under-delegation counter inverted on Opus 5, which over-delegates and gets
an injected harness cap); "staff engineer" self-check bar dropped (Opus 5 an injected harness cap); "staff engineer" self-check bar dropped (Opus 5
over-verification trigger); finish-whole-task clause added to Deviations; over-verification trigger); finish-whole-task clause added to Deviations;
written-deliverable length rule added. 308/320 lines. 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 - **design-toolchain hook** — dropped `\bux\b` (2 French-prose false
positives; 3rd tightening pass, series LRN-1005/1007); `\bui\b` kept and positives; 3rd tightening pass, series LRN-1005/1007); `\bui\b` kept and
locked by a must-fire test row. locked by a must-fire test row.
- **plan-challenger** — grounded-but-uncertain findings now file as `[MINOR]` - **Agent and skill defects found by the campaign's judges** —
with the uncertainty stated, instead of being self-censored (Opus 5 follows `init-project` allowed-tools lacked `Agent` and `Skill` while every step
conservative-reporting clauses literally). 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 ## [1.4.0] — 2026-07-22
+24 -3
View File
@@ -24,20 +24,41 @@ read_field() {
| tr -d '\000-\037' | cut -c1-160 | tr -d '\000-\037' | cut -c1-160
} }
# 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') event=$(read_field '.notification_type')
[ -n "$event" ] || event=$(read_field '.hook_event_name') [ -n "$event" ] || event=$(read_field '.hook_event_name')
case "$event" in case "$event" in
Stop) label="Finished responding" ;; # 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" ;; permission_prompt) label="Needs your permission" ;;
agent_needs_input) label="Asks you a question" ;; agent_needs_input) label="Asks you a question" ;;
idle_prompt) label="Waiting for you" ;; idle_prompt) label="Waiting for you" ;;
elicitation_dialog|elicitation_url_dialog) label="Needs your input" ;; elicitation_dialog|elicitation_url_dialog) label="Needs your input" ;;
*) label="Needs your attention" ;; # anything else (agent_completed, auth_success, quota_*) stays silent:
# signal only for turn end and moments needing the user.
*) exit 0 ;;
esac esac
detail=$(read_field '.message') detail=$(read_field '.message')
[ -z "$detail" ] || label="${label}: ${detail}" 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') bell=$(printf '\a')
esc=$(printf '\033') esc=$(printf '\033')
+1 -1
View File
@@ -1 +1 @@
1.4.0 1.5.0