Skip to content

revert(startup): flat Claude launch stagger; keep the adaptive gate at shutdown - #111

Merged
AThraen merged 1 commit into
mainfrom
revert/adaptive-gate-on-restore
Sep 3, 2026
Merged

revert(startup): flat Claude launch stagger; keep the adaptive gate at shutdown#111
AThraen merged 1 commit into
mainfrom
revert/adaptive-gate-on-restore

Conversation

@AThraen

@AThraen AThraen commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Reverting part of my own #96. Three attempts to make the adaptive gate hold its cap on the launch path have failed.

The measurements

Same 10-session machine, three consecutive builds:

build gate sum max cap
pre-#107 ~61s 22,953ms 2000ms
#107 (off the UI thread) ~62s 31,378ms 2000ms
#110 (off the blocked thread) ~34s 12,574ms 2000ms

Each fix was real — #110 in particular halved it — but neither bounded it.

What it was actually buying

Across all 30 samples:

  • ~1/3 pinned at ~2000ms (2003, 2012, 2017, 2018, 2023, 1970, 1999, 2317) — no write observed, waited the full cap. Identical to a flat delay, with more machinery.
  • ~1/3 genuine wins (0, 0, 354, 603, 713, 752, 808, 823, 1211) — roughly 1.2s saved each.
  • The rest 5–31s.

A mechanism whose best case saves ~1.2s and whose worst case costs 12–31s doesn't belong on a path the user waits through at every launch. Predictable beats occasionally-clever here.

Both launch paths go back to Task.Delay(staggerMs).

Kept at shutdown, because there the data says the opposite

cfgSettle measures a consistent ~304ms against the flat 1000ms — every run, both machines. The machine is quiet at shutdown, so polling is reliable. Same mechanism, different contention, different answer. Both call sites now carry comments explaining which is which, so neither gets "unified" later.

#110 stays regardless

Parking a thread-pool thread per PTY is wrong on its own merits, and it halved this. This PR doesn't depend on it and touches different files.

Net effect

For a 10-Claude-session restore: a bounded ~20s of stagger instead of ~34s with 12-second outliers. Restore becomes predictable, and what's left is launch= — the WebView2 cost, which is the genuine remaining half of #82.

Check Result
Unit tests 312/312
App + Tests build 0 errors, 0 warnings

🤖 Generated with Claude Code

https://claude.ai/code/session_01NDsEfog5kVkT5NmX1Ya5be

…t shutdown

The adaptive config-watching gate from #96 could not hold its own 2000ms cap on
the launch path. Measured across three runs on a 10-session machine:

    pre-#107   gate sum ~61s   max 22953ms
    #107       gate sum ~62s   max 31378ms
    #110       gate sum ~34s   max 12574ms

Two follow-up fixes — #107 moving it off the UI thread, #110 removing the
blocked-thread-per-PTY behind it — roughly halved the cost but never bounded
it. Three attempts is enough.

What it bought when it worked was ~1.2s per session. Across 30 samples: about a
third were pinned at ~2000ms (no write observed, waited the full cap — i.e.
identical to a flat delay with more machinery), about a third were genuine wins,
and the rest ran 5-31s. A mechanism whose best case saves ~1.2s and whose worst
case costs 12-31s is not worth keeping on a path the user waits through at
every launch. Predictable beats occasionally-clever here.

Both launch paths go back to Task.Delay(staggerMs).

KEPT at shutdown, where the data says the opposite: cfgSettle measures a
consistent ~304ms against the flat 1000ms, on every run and both machines. The
machine is quiet at shutdown, so polling is reliable. Same mechanism, different
contention, different answer — the comments say so at both sites.

#110 stays regardless: parking a thread-pool thread per PTY is wrong on its own
merits and it halved this.

Net for a 10-Claude-session restore: a bounded ~20s of stagger instead of ~34s
with 12-second outliers.

312/312 pass, 0 warnings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDsEfog5kVkT5NmX1Ya5be
@AThraen
AThraen merged commit ac82141 into main Sep 3, 2026
1 check passed
@AThraen
AThraen deleted the revert/adaptive-gate-on-restore branch September 3, 2026 13:16
AThraen added a commit that referenced this pull request Sep 5, 2026
Release review of the 20 commits since v0.6.0. Three documentation defects,
one of them actively wrong.

WRONG: the active-terminal highlight section claimed the ring's accent "is
stashed on Border.Tag at build time so the highlight method doesn't need to
look up the VM". The code does the opposite and its own comment says so —
RepoRoot is populated asynchronously by GitService and AccentColor changes when
it lands, so a cached Tag goes stale and stops matching the sidebar. The Tag is
only a fallback.

MISSING: three services added this cycle were undocumented — DbGate (#102),
PwshLocator (#104), ClaudeConfigGate (now shutdown-only after #111).

MISSING: nothing described what actually makes a session active, which is now
the most subtle thing in the codebase and was got wrong twice. Added a section
covering all three routes and, more importantly, why two of them must come from
the page:

  - WebView2 is an HwndHost, so mouse input on hosted native content raises no
    WPF routed events at all — a Preview handler on the host Border only ever
    sees the ring around the terminal (#108).
  - xterm's onData is not "the user typed": it also carries device-attribute
    replies, cursor-position reports, OSC colour replies and focus in/out, none
    of which are distinguishable from typing by inspecting the bytes (#106).

Also recorded why the font-load re-fit exists (#113) and why the ClaudeLaunch
stagger must stay a flat delay — including that a plain Task.Delay(2000) still
logged gate=36339ms after the revert, which is what finally showed the gate was
measuring an app-wide stall rather than causing one. Written as a
"do not try this again" note with the measurements, since the adaptive version
looks obviously better on paper and was attempted three times.

Verified while here: every service on disk is now documented, and all nine
keybindings in MainWindow match the documented table.


Claude-Session: https://claude.ai/code/session_01NDsEfog5kVkT5NmX1Ya5be

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
AThraen added a commit that referenced this pull request Sep 6, 2026
…nt (#117)

Two problems, both found by running it rather than reasoning about it.

1. The adaptive config gate overshoots at shutdown too.

   #111 reverted it on the launch path but KEPT it here, on my reasoning that
   "the machine is quiet at shutdown, so polling is reliable". Measurement
   falsified that. A real run logged:

       SHUTDOWN 'SiteAnalysis': exit=4656ms cfgSettle=8731ms

   against a 1000ms cap — 8.7x over, and 56% of the entire shutdown budget
   spent in one session, which is what forced the remaining six to be killed
   with no exit wait. Same disease as the launch path: when the thread stalls,
   the gate measures the stall rather than the file.

   Now a flat Task.Delay, on both the shutdown loop and the restart path.
   Recomputing that run flat gives 8969ms instead of 15506ms with nothing
   force-disposed — the gate's typical ~300ms beats a flat 1000ms right up
   until it doesn't, and the tail is what costs.

   ClaudeConfigGate now has no callers and is deleted along with its tests,
   rather than left as dead code for someone to reintroduce.

2. The 15s budget was sized from the wrong data.

   It was chosen when the only measurements available were idle sessions
   exiting in 460-770ms. Busy sessions measure 2.3-4.7s each, so nine of them
   need roughly 30s, and 15s force-disposed over half the fleet on an ordinary
   close. Raised to 30s: a clean exit lets Claude finish writing its config,
   and ShutdownOverlay is already on screen explaining the wait. The budget is
   there to bound a wedged session, not to hurry a healthy one.

Confirmed while here: the HasExited fix from #115 works. No exit= value is near
the 10s cap (max observed 4656ms), so nothing is timing out on a process that
already died — which was the whole point.

CLAUDE.md updated: the gate is gone from both paths, and the budget carries its
measurements plus a note to re-measure exit= before shrinking it, since the
summary line cannot distinguish slow exits from waits that never return.

304/304 pass (10 fewer — ClaudeConfigGateTests removed), 0 warnings.


Claude-Session: https://claude.ai/code/session_01NDsEfog5kVkT5NmX1Ya5be

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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