Skip to content

Bell for the model's own push notification; every notification type carries a decision; agent teams in Windows Terminal - #519

Merged
seathatflowsinourveins merged 5 commits into
mainfrom
claude/push-notification-bell-20260929
Sep 29, 2026
Merged

seathatflowsinourveins merged 5 commits into
mainfrom
claude/push-notification-bell-20260929

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Scope

  • What this PR changes, in one or two sentences: the Windows Terminal bell now rings for the model's own PushNotification (push_notification), the one notification type of the 17 the installed client knows that a long unattended workflow uses to call the user back and that the overlay's matcher missed; every type gets a decision and a reason (8 ring, 9 quiet) in one table that the scan and a test share, and a test reads the installed client against it; and the recipe and decision record state how agent teams behave in Windows Terminal (default in-process display; upstream says split panes are not supported there; BEL forwarding through tmux measured).
  • Base commit: 3c9438d6 (origin/main at the last rebuild; head a495f549)
  • Lane: lane:foundation
  • Owned paths touched: adoption/templates/claude.settings.linux-wsl2.overlay.json, adoption/platforms/linux-wsl2.md, recipes/claude-native-profile.md, docs/decisions/2026-09-28-terminal-experience.md, tests/test_windows_terminal_defaults.py, evidence/receipts/notification-types-20260929.json, evidence/artifacts/notification-types-20260929/, and manifests/evidence.json (hot-file protocol: main's copy, the one receipt entry and the hashes of the changed files).
  • Host change outside git, reported to the Gate A owner before any seal announcement (who verified the hash and the single-group difference): ~/.claude/settings.json on this host and the second distro's, each backed up and differing from its original only in the bell group. This host's file was rewritten later the same day (its hash moved; the cause is not established here); the bell group is unchanged (the final tool's dry run reports nothing to do).
  • Authorship: written by a Claude Sonnet 5.5 coordinator session, hence the commit trailer. Reviewed by headless Opus reviewers at effort max; see Review.

SOTA sources

  • Claude Code hooks reference, "Notification" (the 12 documented types and their timing) and the terminalSequence hook output, code.claude.com/docs/en/hooks, fetched 2026-09-29; the installed binary (2.1.283, 2.1.284 and 2.1.285 all know the same 17 types; read by notification_types_scan.py) for the five types the reference does not document, push_notification among them.
  • Claude Code agent teams, code.claude.com/docs/en/agent-teams (fetched 2026-09-29): "Choose a display mode" (in-process is the default and works in any terminal; split panes need tmux or iTerm2), the limitation "Split-pane mode isn't supported in VS Code's integrated terminal, Windows Terminal, or Ghostty", "Enforce quality gates with hooks" (TeammateIdle, TaskCreated, TaskCompleted); settings reference for teammateMode and --teammate-mode.
  • The installed client (2.1.285) for the PushNotification tool's own logic (its presence check, config_off and the server-side flag), the matcher catalog of the Notification event and teammateMode's values; Windows Terminal DispatchTypes.hpp L541 (mode 1004 exists at v1.24.11911.0).
  • tmux 3.4 manual tmux.1 at tag 3.4: bell-action (L3982) and visual-bell (L4355); measured with the installed 3.4.
  • Windows Terminal: BEL is the only notification signal it acts on (the earlier decision record's source table, microsoft/terminal v1.24.11911.0).
  • No code was written where a maintained upstream exists: the bell hook and its merge tool are the repository's existing ones, and the probes are measurement tools for this record's claims (the pty probe is derived from the merged alert_probe.py).

Evidence-class table

Claim Evidence class Command / receipt
The installed client knows 17 notification types, the same in 2.1.283, 2.1.284 and 2.1.285 (base array 15, catalog found, 17 matcher values each); 8 ring, 6 documented types stay quiet, 3 undocumented types stay quiet, none unclassified. The scan's first catalog pattern read 15 of 17 on 2.1.283 (a $ in the minified name); the reader now reads 17, exits 1 when it finds nothing, and two mutants each fail exactly their tests native_proven (the binary read by a script; negative controls) receipt notification-types-20260929 data.binary_scan; notification_types_scan.py, scan_reader_mutants.py, recorded/
A model push raises one push_notification Notification event, rings nothing without a bell hook, rings once with one, and rings once on this host's own settings after the change; the tool sends only while the client judges the user away (once the terminal has sent a focus report its last state decides, else input within 60 s): both branches measured; a tab last reported focused counts as present however long the user is away native_proven (interactive pty session, five arms, BEL counter that follows Windows Terminal's parser) receipt data.push_probe; push_notification_probe.py arms A-E. The 'before' run on this host's settings (1 event, 0 BEL) is recorded as coordinator output, values only
tmux 3.4 forwards a pane's BEL to the outer terminal (a pty) by default; bell-action none and visual-bell on stop it native_proven (private tmux server, negative controls) receipt data.tmux_bell_forwarding; tmux_bell_probe.py
Both Claude settings files differ from their originals only in the bell group; a rerun reports nothing to do; the hardened replacement tool refuses the unsafe cases (17 controls; 9 mutants, one guard removed each, each failing exactly its cases) and installs through the merge tool's own backup and atomic write native_proven (the repository's merge tool on a private copy, read-back; synthetic homes for the controls) receipt data.settings_files; replace_bell_group.py, replace_bell_group_controls.py, replace_bell_group_mutants.py, recorded/
Agent teams: in-process works in any terminal, split panes need tmux or iTerm2 and are not supported in Windows Terminal, three team hooks exist source_review (documentation fetched 2026-09-29) decision record, last update
Every type carries a decision and a reason in one table; the overlay's matcher equals the ring set; the installed client knows no type without a decision (skipped without a client); the matcher quoted in the recipe and decision is the overlay's local_integration tests/test_windows_terminal_defaults.py; notification_types_scan.py exits 0

Local commands run

$ python3 scripts/validate.py
exit 0: {"components": 69, "hashed_files": 8206, "profiles": 4, "receipts": 173, "status": "passed"}
$ python3 scripts/validate_foundation.py ; python3 scripts/component_matrix.py --check ; python3 scripts/new_host_grand_list.py --check
exit 0, 0 and 0
$ python3 -B -m unittest tests.test_adoption_status tests.test_windows_terminal_defaults tests.test_adoption_docs_consistency tests.test_render_config tests.test_apply_claude_settings
Ran 228 tests ... OK (skipped=1)
$ python3 -B evidence/artifacts/notification-types-20260929/push_notification_probe.py push --no-user-settings --bell-hook
1 Notification event (push_notification), 1 bare BEL byte after the tool call; with --no-focus-report --sleep 15: no event, no bell
$ python3 -B evidence/artifacts/notification-types-20260929/tmux_bell_probe.py
default 1, bell-action any 1, bell-action none 0, visual-bell on 0, no-BEL pane 0
$ python3 -B evidence/artifacts/notification-types-20260929/notification_types_scan.py <2.1.283, 2.1.284, 2.1.285 binary> .
exit 0 each: 17 types, base 15, catalog found, 17 matcher values, no type without a decision
$ python3 -B evidence/artifacts/notification-types-20260929/replace_bell_group_controls.py . ; python3 -B .../replace_bell_group_mutants.py . ; python3 -B .../scan_reader_mutants.py . <the three binaries>
17 cases, 0 problems; 10 runs, 0 problems; 2 mutants, 0 problems (the first catalog pattern reads 15 of 17 on 2.1.283)

Decision record

docs/decisions/2026-09-28-terminal-experience.md: "Update 2026-09-29 (late): the model's own push signal, every notification type and agent teams in Windows Terminal" (decisions with rejected alternatives and overturn conditions), plus the matcher lists it already quoted.

Host evidence

This PR adds no file under evidence/hosts/.

  • python3 scripts/host_receipts.py validate is part of scripts/validate.py, which passes.
  • Independent review is recorded in this description (Review).
  • No platform_status change is made from a host receipt alone.

Review

  • Round 1 (two headless Opus reviewers at effort max, read-only, a frozen checkout of the first head 7ad36431; lenses: behaviour, tests and evidence accuracy; upstream sources and the terminal claims): reviewer A returned 8 findings (3 medium, 5 low) and reviewer B 9 (3 medium, 6 low), both changes-needed. All were verified against source or by running the thing, and repaired:
    • the "away" mechanism was misdescribed. The client decides presence from the terminal's own focus report and, only when none has arrived, from input within the last 60 s (read from the binary, function V4r, var KWt=60000; CLAUDE_CODE_DISABLE_NOTIFICATION_PRESENCE_CHECK bypasses it), and the probe's synthetic focus-out report, not the 45 s sleep, had decided its arms. The probe now waits for focus reporting, prints whether it sent the report and the seconds from Enter to the tool call, and two new arms measure the other branch (no focus report: 75 s fires, 15 s does not). The first recorded runs of two arms had failed under a heavily loaded host (a peer's Next.js build) and were rerun;
    • the conditions under which a push cannot ring are now stated (config_off with Remote Control connected and agentPushNotifEnabled off; a server-side flag gates the tool);
    • the claim that a test pins the table was false. One table (DECISIONS, with a reason per type, in the scan) is now imported by the test, the test also reads the installed client against it (skipped where none is installed) and the scan exits 1 on a type without a decision; two more tests tie the matcher quoted in the recipe and the decision to the overlay;
    • replace_bell_group.py could lose another hook in the bell group, accepted any target word as the second distro, used asserts, ignored a symlink and exited 0 after a failed write. It now refuses each, finds a hand-requoted bell command, compares the whole Notification list, and has nine controls (replace_bell_group_controls.py);
    • upstream's agent-teams limitations say split-pane mode isn't supported in Windows Terminal; the recipe, decision and receipt now quote it and say what was measured (only BEL forwarding through tmux), and the auto rejection is corrected;
    • smaller: agent_completed quiet also silences a background failure (stated, with the overturn condition); all 17 types are matcher values (15 base plus two); the tmux arm label and "Windows Terminal" wording (the outer terminal is a pty); the type count claims that no output supported.
  • Round 2 (a re-check of the repairs by two fresh headless Opus reviewers at effort max on the head after round 1, f8feb268): reviewer A returned 10 entries and reviewer B 7, both changes-needed. Every entry was checked against the binary or by running the thing; all real ones are repaired in 78fe3b5e (with wording-only fixes in 34f0fec4), none is left open except the residuals below:
    • a focused tab counts as present however long the user is away: with focus reporting, the last reported state decides (the tool is the only caller of the rule), so a push and its bell reach a user whose tab or window was last reported unfocused, not one who left a focused tab. The recipe, decision, platform page and receipt now say so, name CLAUDE_CODE_DISABLE_NOTIFICATION_PRESENCE_CHECK and its price, and the decision records the presence check as a kept default with an overturn condition. The function that raises the permission dialog's notification is a plain fixed-delay timer with no presence check in it (its callers were not read);
    • the scan's catalog pattern could not match a $ in the minified spread name (P$o in 2.1.283), so it read 15 of 17 matcher values without an error: fixed with a [\w$]+ pattern, catalog_found, an exit 1 for a blind reader, two synthetic-binary tests, and scan_reader_mutants.py (measured 15 against 17 on 2.1.283, and each mutant fails exactly its tests);
    • replace_bell_group.py followed a symlink at its fixed staging name, reported "nothing to do" on the matcher alone, and could drop a hook key, a user-added matcher type or an existing preferredNotifChannel: it now installs through the merge tool's write_backup and atomic_write, uses mktemp and refuses a symlink and an existing backup on the second distro, reports "nothing to do" only when merging changes nothing, and refuses each of those losses; 17 controls (with --apply cases) and 9 mutants;
    • the receipt's test count was stale (217) and several statements were paraphrases or overclaims: rebuilt from the recorded outputs (228 tests); the client's own "Not sent because you're active in this terminal." replaces a paraphrase; the headless -p run is described as short and unmeasured for longer runs; computer_use_exit has an emit site ("Claude is done using your computer") and the two others say what a text search found; "teammates share the lead's tab" no longer implies a measured teammate alert; the last "45 s without input" is gone;
    • the probe outputs behind the receipt were not retained: evidence/artifacts/notification-types-20260929/recorded/ now holds them as text (arms A to E, the failed first attempts of A and B, the scan per release, controls, mutants, dry runs, tests, exit codes), hashed in the receipt.
    • Residuals (recorded, no third round): Windows Terminal's real focus reports (background tab, unfocused window, the last report when a user walks away) and an audible bell were not observed; the second distro's polaris write path was not run after the hardening (its dry run and a scratch run of its shell snippet under dash on a synthetic home were); an alert raised by an agent-team teammate was not observed; the presence-check override is a user decision (a long workflow that ends while a focused tab is left stays silent unless it is set).
  • Codex GPT-6 cross-family round: not run. All six pooled GPT-6 accounts and the native account were at their weekly limit (a 429 Too Many Requests from the gateway at about 14:00Z on 2026-09-29, pool windows 99-100% until 2026-10-03/04); the round runs on the merged commit when a login or the reset restores the lane.

Checklist

  • New/changed GitHub Actions are pinned to a full commit SHA with a version comment (no workflow is changed).
  • New/changed workflows declare top-level permissions: contents: read (no workflow is changed).
  • No secrets are printed, logged or committed; no new required secret was added. The added lines were scanned for user names, home paths, GUIDs, credential shapes and e-mail addresses: 0 hits.
  • No new paid hosting, subscription or billing surface was introduced.
  • Peer-owned untracked files and worktrees were preserved (not deleted, moved or overwritten).

🤖 Generated with Claude Code

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Sep 29, 2026
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/push-notification-bell-20260929 branch from 7ad3643 to f8feb26 Compare September 29, 2026 22:05
Scout and others added 3 commits September 29, 2026 18:42
…arries a decision; agent teams in Windows Terminal

The installed Claude Code knows 17 notification types; the overlay's bell rang for seven. The one that mattered for long workflows was push_notification, the model's
own PushNotification: with the old matcher it raised a Notification event and rang nothing in Windows Terminal. It is added to the overlay matcher, and to this host's and the
second distro's settings (backed up; only the bell group differs). One table (DECISIONS in the scan, imported by the test) gives every type its decision and reason (8 ring, 9 quiet),
and the test reads the installed client against it, so a type a later release adds fails until it has one. The push probe measures both branches of the client's presence rule
(the terminal's focus report, else 60 s since the last input). The recipe and the decision record state how agent teams behave in Windows Terminal (default in-process display; upstream
says split panes are not supported there) and that tmux 3.4 forwards a pane's BEL by default (measured with a negative control).

Evidence: receipt notification-types-20260929 with its scan, a pty probe (five arms), a tmux probe, the settings replacement script and its nine controls.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Scan reader: the catalog pattern accepts `$` in the minified spread name (2.1.283's `P$o` made the first pattern read 15 of 17 matcher values without an error); scan()
reports catalog_found and the extra values, exits 1 on a blind reader, and the installed-client test asserts both; two synthetic-binary tests and scan_reader_mutants.py
(the first pattern on the three releases, two mutants) cover it. computer_use_exit has an emit site; the reasons for computer_use_enter and model_refusal_fallback say what a
text search did and did not find.

Replacement tool: install through the merge tool's write_backup and atomic_write (a random staging name), a mktemp staging file and symlink/backup refusals on the second
distro, "nothing to do" only when the bell group equals the overlay's and merging changes nothing, refusals for extra hook keys, a matcher naming a type the overlay lacks or
no matcher, an existing different preferredNotifChannel, an unknown flag; 17 controls (--apply cases included) and 9 mutants that each fail exactly their named cases.

Wording: a tab last reported focused counts as present however long the user is away (the presence rule read from the binary, its bypass named); the client's own "Not sent"
string instead of a paraphrase; headless run stated as short and unmeasured for longer; teammates' alerts stated as unobserved; the remaining "45 s" claim gone.
Receipt rebuilt from recorded outputs kept under the artifact directory (228 tests, exit codes, the failed first attempts of two probe arms).

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… (hot-file protocol, rebuilt on 3c9438d)

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/push-notification-bell-20260929 branch from f8feb26 to ca52f44 Compare September 29, 2026 22:42
Scout and others added 2 commits September 29, 2026 18:59
…hout a stated cause, the permission-dialog timer

The decision record, the receipt and its builder no longer call the later rewrite of this host's settings file "unrelated" (the recorded fact is that its hash moved and the
bell group is unchanged); the presence-rule text now says what was read about the permission dialog's notification (a fixed-delay timer with no presence check in the raising
function; callers not read) instead of "not checked".

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins merged commit e45328d into main Sep 29, 2026
29 of 30 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/push-notification-bell-20260929 branch September 29, 2026 23:50
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…ough the packaged Codex lane on OmniRoute), 46 findings checked, 39 repaired

Nine read-only jobs (#484, #498, #510, #519; two lenses each, #519 in three parts) returned 46 findings (3 high, 34 medium, 9 low). Each was graded by an independent Claude verifier
(23: 17 confirmed, 4 partly, 2 refuted) or checked by the coordinator against the code or the upstream source before any change; 39 led to a repair, 2 were refuted, 3 concerned the removed
second-distro target, 2 duplicates of one convention were kept.

Repairs, each with a control that fails without it: the settings-swap tool installs only over the bytes it read, refuses keys beyond matcher and hooks and prints no matcher value (24 controls,
15 mutant runs); the notification-type scan resolves the catalog's spread identifier instead of choosing an array by contents (eight reader tests, seven mutants); the login-shell check needs an
answer for every command and an end marker, uses type -P and lets only the first startup file bash reads shadow ~/.profile; the digest, Codex strict-config, overlay, tmux and pty probes exit
nonzero when they measured nothing (selftests, an end-to-end SIGTERM case with a negative control); the BEL counter ignores UTF-8 C1 characters like the pinned output engine; the colour counter
counts SGR only; the 512-combination bash oracle compares first_read; the practice repository's sound rule is exact.

Wording corrected: tmux has a sync feature (measured); the bell icon clears after about 2 s on a focused tab; the push presence rule is for a local session; a profiles.defaults key beats a
fragment-only profile; a terminal's permission notification is timed by inactivity (the fixed-delay timer is the Agent SDK path, which the coordinator's own earlier correction had confused).

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
seathatflowsinourveins added a commit that referenced this pull request Sep 30, 2026
… (gpt-6.1-sol and gpt-6-astra-ultra through the packaged Codex lane on OmniRoute) (#532)

Nine read-only jobs of the packaged Codex lane (gpt-6.1-sol, effort max, through the local OmniRoute gateway) reviewed the four merged terminal-lane pull requests (#484, #498, #510, #519)
and returned 46 findings; each was graded by an independent Claude verifier or checked against the code or the upstream source before any change (39 repaired, 2 refuted, 3 removed with the
second-distro target, 2 kept). A bounded re-check of those repairs (three gpt-6.1-sol jobs and one gpt-6-astra-ultra job) returned 33 more findings (19 graded: 16 confirmed, 3 partly), all repaired in a
fifth round: a false recovery claim in the settings-swap tool, signal handling that still left the client behind (first signal wins, deferred cleanups, client created under a spawn flag, a mutant
runner that shows each case failing without its guard), controls that could not fail, two claims without a recorded rerun. Adds the sanitized review record, a receipt with the re-check data, a correction
note in the four older receipts and native reruns of the scrub probe and the PATH digest.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant