Skip to content

fix(notifications): drop events for another Chau7 instance's tabs - #281

Merged
schiste merged 5 commits into
mainfrom
agent/fix-chau7-next-log-findings-cross-app-no
Oct 23, 2026
Merged

schiste merged 5 commits into
mainfrom
agent/fix-chau7-next-log-findings-cross-app-no

Conversation

@schiste

@schiste schiste commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Fixes for the findings from reviewing the Chau7 Next test build's log (about 28,000 lines since 2026-10-11 plus the new build's session).

Changes

  1. Duplicate notifications across Chau7 apps. The AI event log and CLI hooks are shared by every Chau7 instance, so Chau7 Next received events tagged with the production app's tab ids. Styling failed (111 "Explicit tabID not found" warnings), and a desktop notification was still shown, 57 duplicates. Notification preparation now drops events whose tab this instance never hosted. A bounded record of seen tabs keeps results for a tab closed just before its event.
  2. Shells killed on close. Closing sent exit, then SIGTERM, then SIGKILL. Interactive zsh ignores SIGTERM, so the shell was killed and lost history and exit hooks whenever exit did not land. It now sends SIGHUP (like Terminal.app and iTerm) to the PTY's foreground job and the shell's group; app quit hangs up every shell.
  3. False input-latency spikes. Enter and keys typed while the shell loaded started the keystroke-to-echo clock (925 ms was Codex booting, 1,933 ms was .zshrc). Only echoed keystrokes are measured now.
  4. : window not found ERROR lines. Targetless GPUI records are labelled by source location, and GPUI's expected closed-window race is logged at DEBUG.
  5. Repeated Full Disk Access warning. Warns when access becomes denied or a child is refused, not on every re-probe.
  6. Routing retries of an unresolved notification log at DEBUG after the first attempt.

Not changed: events from AI sessions that cannot be mapped to any tab (for example a Claude session running in the other app) still enter the feed as untargeted events and are dropped after the routing retries; they may belong to a Next tab whose identity is not known yet.

Validation

  • New tests: events_for_another_instances_tab_are_dropped, known_tabs_are_bounded_oldest_first, closing_hangs_up_a_shell_that_ignores_sigterm (real PTY child that ignores SIGTERM), only_echoed_keystrokes_start_an_input_measure, library_records_without_a_target_are_labelled_by_location, only_gpuis_closed_window_race_is_demoted, unchanged_denial_is_not_rewarned; retry counter added to the existing suppression test.
  • Targeted core (34) and app (40) tests pass; cargo clippy --workspace --all-targets -D warnings clean; staged guardrail gate replayed locally. Full apps/chau7-next workspace: 3,341 passed, 0 failed, 2 ignored. crates/: clippy clean, 560 passed, 0 failed, 3 ignored. CI: macOS, Linux, Windows and quality gates pass.

🤖 Generated with Claude Code

schiste and others added 5 commits October 23, 2026 17:20
Problem: every Chau7 instance on the Mac shares the AI event log and CLI
hooks, so Chau7 Next received events tagged with the production app's
tab ids. Delivery treated any explicit tab id as its own: styling failed
("Explicit tabID not found", 111 warnings in the field log) and the
fallback still showed a desktop notification, 57 duplicates while both
apps ran. Routing retries of unresolved events also logged "delivery
prepared" at INFO on every attempt.

Decision: notification preparation now drops an event whose explicit
tab this instance does not own. Ownership is a bounded record (4096) of
tab ids seen live in this run, refreshed from the open tabs before each
decision, so a tab closed just before its event still counts as owned.
Retried preparations log at DEBUG.

Rationale: an id never hosted here can only belong to another instance;
acting on it duplicates that instance's alert. Remembering closed tabs
keeps results for a tab the user just closed.

Validation: new tests events_for_another_instances_tab_are_dropped and
known_tabs_are_bounded_oldest_first; retry counter covered in
routing_missing_identity_retries_and_exhaustion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem: closing a tab typed `exit`, then sent SIGTERM to the shell's
group and SIGKILL 0.5 s later; quitting sent SIGTERM. Interactive zsh
ignores SIGTERM, so whenever `exit` did not land (a full-screen program
in the foreground) the shell was SIGKILLed, losing history and exit
hooks; the field log shows exactly this sequence. A full-screen program
in its own foreground group never received a signal at all.

Decision: send SIGHUP, as Terminal.app and iTerm do, to the PTY's
foreground process group (new Chau7Terminal::foreground_pgrp) and to the
shell's group, keeping the same delays before SIGKILL. App quit hangs up
every live shell.

Rationale: shells and their jobs treat SIGHUP as the terminal going
away: they save history, run exit traps and pass it to their jobs.

Validation: new test closing_hangs_up_a_shell_that_ignores_sigterm (a
PTY child that ignores SIGTERM and never reads `exit` runs its HUP
handler; the previous sequence killed it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem: every write started the input-to-output clock, including Enter.
The next output after Enter is the command's (Codex booting took
925 ms), and keys typed while the shell still loaded its startup files
waited for them (1,933 ms), so the field log reported input lag that the
UI never had.

Decision: shell_latency::measures_echo: a write starts the measure only
when it has no line submit, the shell is not loading and echo is on.

Rationale: input latency is meant to be keystroke-to-echo; command run
time and shell startup are other measures.

Validation: new test only_echoed_keystrokes_start_an_input_measure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem: GPUI's caller-located errors carry an empty target outside a
`crates/` tree, so vendored GPUI lines were written as ": window not
found" at ERROR. Those come from window event senders firing after a
window closed, on every window close.

Decision: a record without a target is labelled with its source location
(for example gpui-pre/src/window.rs:1720); GPUI's "window not found"
with an empty target is written at DEBUG. Other library errors keep
their level.

Rationale: error lines must say where they come from, and an expected
close-time race should not count as an error.

Validation: new tests library_records_without_a_target_are_labelled_by_location
and only_gpuis_closed_window_race_is_demoted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem: the Full Disk Access guard re-probes every few seconds and
logged the same WARN each time access was still denied.

Decision: warn when access becomes denied or a child process is
actually refused; an unchanged denial logs at DEBUG.

Rationale: the warning reports a change worth acting on, not the
polling cadence.

Validation: new test unchanged_denial_is_not_rewarned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@schiste
schiste marked this pull request as ready for review October 23, 2026 16:02
@schiste
schiste merged commit 35caaad into main Oct 23, 2026
4 checks passed
@schiste
schiste deleted the agent/fix-chau7-next-log-findings-cross-app-no branch October 23, 2026 16:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant