Skip to content

fix(core): bound CSI ?997 re-query with a cooldown damper (stacked on #1213)#1228

Open
DEAN-Cherry wants to merge 2 commits into
anomalyco:mainfrom
DEAN-Cherry:fix/theme-mode-bounded-requery
Open

fix(core): bound CSI ?997 re-query with a cooldown damper (stacked on #1213)#1228
DEAN-Cherry wants to merge 2 commits into
anomalyco:mainfrom
DEAN-Cherry:fix/theme-mode-bounded-requery

Conversation

@DEAN-Cherry

Copy link
Copy Markdown

Note

Stacked on #1213. The first commit here is #1213's Bug-A fix (honor late OSC replies); until #1213 merges, this diff includes it. Please review only the second commit.

Follows up on the review feedback in #1213: the original bounded re-query reintroduced the #975 feedback loop and missed its own target case. This PR replaces the #976 silent in-flight drop with a follow-up query behind a real loop damper, per the suggested path forward.

Problem

The #976 fix silently drops any ?997 notification arriving while an OSC 10/11 query is in flight. A terminal whose colors are still settling when it notifies gets stuck on the pre-transition mode until some later event. But naively re-querying per dropped notification re-opens #975, where the OSC round-trip itself provokes the next ?997.

Design

  • A ?997 during an in-flight query sets a single-slot requeryPending flag instead of being dropped.
  • The pending follow-up is consumed on both completion paths — the 250ms timeout and early completion via a full OSC reply — so a terminal that answers quickly with pre-settle colors no longer strands the deferred notification (the settle-lag race called out in review).
  • Cooldown: at most one follow-up query per 5s (FOLLOW_UP_COOLDOWN_MS) regardless of how many notifications arrive. In the 0.1.103: DSR 997 (color-scheme updates) creates an infinite OSC 10/11 query loop under tmux on macOS #975 shape (each query provokes the next ?997), queries settle at 2 instead of looping at one per 250ms.
  • Provoked window: an idle ?997 arriving within 1s (PROVOKED_WINDOW_MS) of the last query activity is treated as a follow-up candidate under the same cooldown. This closes the fast-reply bypass: a terminal that answers within the 250ms window completes the query before its provoked ?997 lands, so that notification would otherwise count as "fresh" and sustain the loop around the in-flight guard.
  • Genuine notifications outside the provoked window always start a fresh query immediately, never gated.

Tests

All modeled with ManualClock in renderer.input.test.ts:

  1. tmux 0.1.103: DSR 997 (color-scheme updates) creates an infinite OSC 10/11 query loop under tmux on macOS #975 simulation (adapted from the reviewer's test in the fix(core): honor late OSC replies + bound re-query on CSI ?997 (runtime theme switching on non-2031 terminals) #1213 review — thank you): each queryThemeColors provokes a ?997 50ms later, terminal never answers → queries stay ≤ 2 over 10s of virtual time.
  2. Fast-reply 0.1.103: DSR 997 (color-scheme updates) creates an infinite OSC 10/11 query loop under tmux on macOS #975 variant: terminal answers in 10ms and the provoked ?997 lands at 50ms while idle → still ≤ 2.
  3. Settle-lag early consumption: ?997 Support Node/Deno (NAPI instead of bun:ffi) #2 in-flight + fast pre-settle reply → follow-up runs immediately via early completion, settled colors applied.
  4. Single-follow-up bound: in-flight ?997 produces exactly one follow-up; further notifications within cooldown produce none.
  5. Genuine change after cooldown → fresh query fires immediately.

All 102 tests in renderer.input.test.ts pass; tests 2–4 fail on the Bug-A-only base (#1213), confirming they exercise this change.

…eout

A terminal that answers OSC 10/11 slower than the 250ms query window —
common under WezTerm, where a color-scheme change can take ~1s to settle
in the OSC 11 reply — previously had its late answer silently dropped by
the themeQueryPending guard, leaving the runtime theme mode stuck on the
old value.

Accept late replies instead: fg/bg are reset to null at the start of each
query, so stale values from a prior query cannot leak in, and
applyThemeMode dedupes unchanged modes.

With the guard gone, themeQueryPending became write-only, so the field is
removed entirely. This also means unsolicited OSC 10/11 replies can
mutate the theme mode at any time; post-suspend inertness relies on the
stdin data listener being detached.
…op damper

The anomalyco#976 fix silently drops any ?997 notification that arrives while an
OSC 10/11 query is in flight. A terminal whose colors are still settling
when it notifies (settle-lag) therefore gets stuck on the pre-transition
mode until some later event.

Replace the drop with a bounded follow-up query:

- A ?997 during an in-flight query sets a single-slot requeryPending flag
  instead of being discarded.
- The flag is consumed on BOTH completion paths: the 250ms timeout and
  early completion (full reply before the timeout), so fast terminals
  answering with pre-settle colors still get the follow-up.
- Follow-up queries are throttled by a 5s cooldown: at most one follow-up
  per cooldown no matter how many notifications arrive. This breaks the
  query→provoked-?997→re-query cycle from anomalyco#975, where the OSC round-trip
  itself provokes the next notification.
- An idle ?997 arriving within 1s of the last query activity is treated
  as a follow-up candidate under the same cooldown (a fast-answering
  terminal completes the query before its provoked ?997 lands), closing
  the fast-reply bypass around the in-flight guard.
- Genuine notifications outside the provoked window always start a fresh
  query immediately.

Includes a regression test modeling the anomalyco#975 shape (each query provokes
the next ?997): queries stay bounded at <=2 over 10s, matching main's
behavior, plus a fast-reply variant of the same loop.
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