Repository navigation
fix(desktop): keep harnesses available when login-shell capture is slow - #459
Conversation
A warm interactive zsh takes about 3.5s, and the 5s capture cap killed probes that had not finished. POSIX installIntoProcess still reported prepared, so the server skipped its own PATH capture. Share a 12s POSIX capture window and report prepared only when a login shell returned PATH. A launchctl PATH still applies and leaves server recovery enabled. Windows and WSL behavior stay as they were. Model: grok-4.7 Harness: T3 Code / Grok
The feature entry used a compare URL until the draft pull request existed. Model: grok-4.7 Harness: T3 Code / Grok
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
lukemaj
left a comment
There was a problem hiding this comment.
Independent review: approve, no blocking findings
Reviewed exact head e9fcf546876a6683f3c283495a045c271617f479 against base c1d3960a1d. The touched files are unchanged on current main (def07e920a), and GitHub reports the PR mergeable. Reviewer: Claude Opus 5.5 (Claude Code), read-only, in a temporary worktree.
Verdict: the change is correct and fixes both defects in #458. It does not prove the Cavallo outcome; the live gate below does that.
What I checked
- Prepared flag.
installPosixEnvironmentnow returns true only when a login-shell candidate returned PATH. A launchctl-only PATH, an empty capture, a timeout and a spawn failure all return false. That value reaches the server throughbuildWindowsPrimaryConfig, which builds every non-WSL primary including darwin and linux, then throughshellEnvironmentPreparedandfixPath, sohydratePosixPathruns again. This now matches the Windows branch. - Timeout. The 12s window is shared by the desktop probe and the server's
readEnvironmentFromLoginShell. Windows PowerShell probes keep 5s. Launchctl stays at 2s. Upstream pingdotgg/t3code#10534 shows the same failure: about 1.1s warm, more than 5s at cold start, and a 6105ms span (5000ms plus the 1s kill grace). That matches Cavallo's 6042ms trace. A capture that misses the window on a cold desktop launch now gets a second, warm attempt in the server. - Regressions. Linux: a failed capture now also re-enables server hydration, and a successful one is unchanged. Windows and WSL: no behavior change. The WSL config does not use the flag. Caching, PATH order, launchctl merge and copying the other variables are unchanged.
- Tests. At this head,
vp test run apps/desktop/src/shell/DesktopShellEnvironment.test.ts packages/shared/src/shell.test.ts apps/server/src/os-jank.test.tspassed: 3 files, 80 tests. RED is real: with baseDesktopShellEnvironment.tsandshell.tsrestored and the PR tests kept, 5 tests fail: the sharedtimeout: 5000, the launchctl fallback and the empty capture both reporting preparedtrue, the 6s PATH falling through to launchctl, and the spawn-failure case. The 6s test uses TestClock againstEffect.timeoutOption, so it exercises the real bound. The newos-janktest passes on base too: it checks the existingfixPathbehavior the change relies on, and the PR does not claim it as RED.fork-check.shandfork-features.mjs checkprint OK.
Non-blocking notes
- The Cavallo acceptance gate cannot tell this fix from the mitigation. Gavin's
launchctl setenv PATH(runbook00144d3) is still in place until logout. With it, even the old build finds Codex and Claude: a timed-out probe falls back to launchctl. Seeing all three harnesses after a GUI launch is therefore necessary but not enough. Also confirm indesktop.trace.ndjsonthat thelogin-shellrunCommandOutputspan completed with noDesktopShellEnvironmentCommandTimeoutError, and thatinstallIntoProcessreturned preparedtrue. Removing the mitigation first is the user's decision, not part of this PR. - Startup cost when the shell truly hangs.
installIntoProcessruns beforewhenReady, so no window appears until it finishes. A capture that finishes between 5s and 12s now delays the window by that long, which is the purpose of the change. If the shell hangs, the wait before the window grows from about 8s to about 15s on macOS (12s, 1s kill grace, 2s launchctl). The server then makes a synchronousexecFileSyncattempt of up to 12s per candidate before listening. In a d275 build that attempt was skipped. On Linux, with up to 3 distinct candidates, both waits multiply. Both stay bounded, and a capture that finishes now counts as success, so I accept this tradeoff. A later improvement could be one server attempt with a shorter cap after the desktop has already timed out. - Test gap. No test shows a capture slower than 12s still being cut off with prepared
false, and the newshell.tscomment makes that claim ("still stops a hang"). A test with a delay of about 13s would pin both sides of the bound. - Nit. The five new lines were added at the top of
scripts/fork-upstream-edits.txtinstead of next to the otherapps/desktopentries. The file was not sorted before either, and the fork checks pass.
Not run: live packaged provider discovery, repo-wide checks, any build or install. CI on this head is green.
Render thin and consent research pages through an isolated local public-network browser, preserving origin restrictions, cache continuation, provenance and bounded subpage traversal. Closes #442. Refs #423. Proof: 124 focused tests including 13 installed-browser cases, drained effects and no sleeps; scoped typecheck/lint/exports/fork checks. CI39163100867 and Fork39163100918 passed on19a987a5051ae99c10e0f2657ec922f05e898b8c. Independent review: Claude Opus5.5 R5 PASS on exacthead, comment6254854977. User-approved ordered landing coordinated on411. Preserves incoming #459 atc95b531803. No deployment/restart/install/cache clearing or version bump. Optional limits remain on447. Authored with Codex through T3 Code; reviewed with Claude Opus5.5 through Claude Code in T3 Code.
What changed: Desktop login-shell capture now waits up to 12 seconds and reports a prepared environment only when that capture returns a PATH.
Why: A warm interactive zsh takes about 3.5 seconds. The old 5 second cap killed probes that had not finished, and POSIX startup still reported the environment as prepared, so the server skipped its own PATH capture and installed harnesses disappeared.
So what: This stays a draft. Independent exact-head review is still required, and the parent dispatches that review. Do not merge, install, or restart Cavallo from this PR.
Problem
On macOS,
DesktopShellEnvironment.installIntoProcessrunszsh -ilcand a marker-delimited capture of PATH and related variables. The capture timeout was 5 seconds. A probe that had not exited was killed, partial output was discarded, and the POSIX branch still returned preparedtrue. The server then skippedfixPath. The same timeout and unconditional prepared flag are still on upstreamd218d5b397d5c23098b697a44ae282b094d68ac9. Upstream issue 10534 is open. Open PR 11275 changes job-control flags for a different issue and was not ported.Expected behavior: a login shell that returns PATH within a bounded window keeps that PATH and may count as prepared. A slow or empty capture must not claim success.
launchctl getenv PATHcan still fill the process PATH and must leave server recovery enabled.Change
LOGIN_SHELL_CAPTURE_TIMEOUT_MSis 12000 and is shared by the desktop POSIX probe andreadEnvironmentFromLoginShell, which the server uses for its second capture. Windows probes stay at 5 seconds.installPosixEnvironmentreturns true only when a candidate's captured environment contains PATH. Launchctl merge, caching, PATH order, and the WSL handoff are unchanged. There is no retry loop and no per-provider path pin.The fork feature
login-shell-capturerecords the five carried upstream files.Scope and approval
Issue 458 is the requested repair. It is one existing-owner bug: slow or failed login-shell capture was treated as a successful preparation.
Verification
RED, before the production edit, on the public desktop and shared seams:
timeout: 5000while the test expected12000.true.GREEN, on the sources in
8e129c71a666fdc29e40c2dd6a530f06b1d13fac(tests at 21:21 Warsaw, 2026-10-18, immediately before that commit; the commit did not change those source lines):3 files, 80 tests passed, duration 1.30s.
./node_modules/.bin/vp run --filter @t3tools/shared --filter @t3tools/desktop --filter t3 typecheckexited 0 on these shell sources before the fast-forward ontoc1d3960a1d. That fast-forward did not edit the shell owner.vp linton the five source and test files reported 0 warnings and 0 errors.scripts/fork-check.shandnode scripts/fork-features.mjs checkboth printed OK on this commit.Not run: repo-wide
vp check, the full test suite, and full CI. CI on this draft is the remaining automated gate.Read-only Cavallo measurement of the real multi-variable
zsh -ilccapture completed in 2.547s with empty stderr.zsh -lc truecompleted in 0.031s and is the wrong semantics because it skips interactive startup. Live packaged provider discovery was not run. No Cavallo, Rocky, or bestia process was restarted.Website impact: none. No public site page describes this startup path.
Version: not bumped. This repository has no
VERSIONfile, and Chromeria builds are identified byt3codeCommitHash.Elon record
Requirements and who asked: Gavin asked, in Issue 458, for a robust login-shell probe so installed harnesses stay available when capture is slow or fails, without another Cavallo restart.
Deleted: The job-control port from open PR 11275, ignoring stderr, per-provider path pins, recursive retries, counting launchctl alone as full preparation, and the claim that d275565 alone caused the timeout.
Bottleneck: The 5 second cap against a warm interactive zsh of about 3.5 seconds, plus a prepared flag that skips the server's second capture.
Checked myself: Desktop, shared, and server source; upstream issue 10534 and the full open PR 11275 diff; the read-only Cavallo timings above; focused tests, typecheck, lint, and both fork checks. Live provider discovery was not run.
Model: grok-4.7
Harness: T3 Code / Grok
Refs #458