Skip to content

fix(ssh): reconnecting keeps the remote server and its agents running - #19432

Merged
t3dotgg merged 4 commits into
mainfrom
t3/ssh-reconnect-keeps-agents
Oct 14, 2026
Merged

t3dotgg merged 4 commits into
mainfrom
t3/ssh-reconnect-keeps-agents

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Oct 14, 2026

Copy link
Copy Markdown
Member

A desktop SSH reconnect (laptop sleep, Wi-Fi drop, app restart) could stop the managed T3 server on the remote host and end every agent running there. It had three causes:

  1. Self-kill. The managed server runs on the default home, so it writes ~/.t3/userdata/server-runtime.json itself. The launch script took that record for another server and stopped its own managed PID. This happened on every reconnect.
  2. Readiness kill. A live managed server got a 2-second reuse probe. A server that was only busy for longer was stopped and replaced.
  3. Stale forward. After sleep or a network drop, the dead local forward was closed through the path that also stops the remote server.

Fix

#17919 also changed the reuse order and made adoption of another server wait 60 seconds with no launch fallback. That part is not included: #17684's PID check already stops the self-kill, and keeping main's adoption fallback means a stale runtime record cannot block reconnects. With #16102, that fallback cannot start a second server on the same home, because the new server exits with code 78.

Verification

  • New and imported tests run the real launch script against real processes. They fail on main and pass here: own server reused, busy server (stopped, resumed after 5 s) waited for, unresponsive server left running, zombie managed server replaced, stale forward replaced without a remote stop, adopted server not probed again. The update-path and no-marker cases pass on both.
  • End to end with the real server (fix(server): enforce one database owner and guard update rollback #16102 lock, real runtime record) in an isolated home:
    • main: each reconnect started a new server (3 reconnects, 3 PIDs).
    • this branch: reconnects kept the same PID. A 6 s stall was waited out (6.0 s). A server that never answered failed the launch after 60.6 s and kept running. A changed runner replaced the server, and the replacement took the lock. A separately started server was adopted, and the stop script left it alone.
    • fix(ssh): reconnecting no longer restarts the managed remote server #17684 alone: a 6 s stall still killed the server, and its replacement exited 78.
  • vp test run packages/ssh/src/tunnel.test.ts, lint, and @t3tools/ssh typecheck pass. "archive runner script > installs once…" flakes locally on clean main too. That installer-lock code is unchanged here.
  • Not verified: a real SSH hop, a macOS remote, the archive-mode helper binary (it uses the same checks as the node path), and the desktop app's stale-forward path outside the unit test.
  • Known limit: during a stall longer than the client's 15 s connection setup budget, the first attempt reports a timeout. A later retry reuses the tunnel the desktop finished building. Showing the launch's own message there is a follow-up.

Fixes #15986. Fixes #16477.

Combines #17684 by @puccez and #17919 by @luke2x. Their commits are kept with authorship. This supersedes both PRs and overlaps #13521.

Combined, tested, and reviewed by Claude Opus 5.5 in Claude Code (T3 Code). Reviewed with sol-loop: 2 rounds with GPT-6.1-Sol.

Co-authored-by: puccez 133161547+puccez@users.noreply.github.com
Co-authored-by: luke2x 97411838+luke2x@users.noreply.github.com

🤖 Generated with Claude Code

puccez and others added 4 commits October 14, 2026 05:34
The managed server runs on the default T3 home, so it writes the default
runtime file too. On every reconnect the launch script read that file, took
the managed server for an external one, stopped it, and launched a new one,
which ended every provider session running on the remote.

When the default runtime file names the PID this launcher recorded as
managed, treat it as our own server and take its port from that file, so a
stale remembered port cannot hide it either. Without the managed marker the
server is adopted as external, as before. Adopting an external server no
longer falls back to stopping the PID the runtime file names, and a server
that just answered adoption is not probed again while it is alive, so a
momentary stall cannot launch a second server on the same home beside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reconnect could kill the managed remote server and its running agents in
two ways. A stale local forward ran the manager-owned remote stop when it
was closed, and the launch script killed a live managed server that missed
the 2-second reuse probe.

Reconnect now closes a stale forward without the remote stop; explicit
Disconnect/Remove keeps it. A live managed server gets the 60-second
readiness budget, and if it still does not answer, the launch fails with a
retryable error and leaves the server running. A changed runner still
replaces the managed server.

Squashed from #17919 and applied on top of #17684's launch script, whose
same-PID guard replaces this PR's reordering of the reuse branches.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Drive the real launch script against a stopped fake server: a reconnect
waits for a server that resumes after the old 2-second probes, and fails
without stopping one that never answers. A changed runner still replaces
the server. These replace #17919's shimmed-node cases with #17684's
real-server harness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kill -0 also succeeds for a zombie. Now that reconnect waits for a live
managed server instead of replacing it, a crashed server left unreaped
(for example under an init that does not reap orphans) would block every
reconnect. Managed reuse now uses the same zombie check as adoption.
Without ps to tell, both trust kill -0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 14, 2026
@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 14, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 4572c6b

Macroscope's review found this PR approvable — This is a localized SSH reconnect bug fix that prevents stale tunnel cleanup from stopping the remote server or its agents, while preserving explicit disconnect and update replacement behavior. The added tests cover the affected lifecycle scenarios, including busy, adopted, and zombie processes.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB +7 B (+0.1%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB +7 B (+0.6%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.9 KiB 0 B (0.0%) 29.3 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB −7 B (−0.1%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB −7 B (−0.6%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB 0 B (0.0%) 29.3 KiB ✅
Claude Live turn messages 2 2 0 (0.0%) 8 ✅

Baseline: 8414d16 · PR result: 4572c6b · Source CI: success

Scenario and decoded snapshot size

10 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.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@coderabbitai

coderabbitai Bot commented Oct 14, 2026

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Team
  • Run ID: 3e78fa38-0088-437e-87d2-e2342152ab93
📥 Commits

Reviewing files that changed from the base of the PR and between 8414d16 and 4572c6b.

📒 Files selected for processing: 2
  • packages/ssh/src/tunnel.test.ts
  • packages/ssh/src/tunnel.ts

Limit details: You’ve used all 10 included reviews currently available.

📝 Walkthrough

The SSH launch script now distinguishes its managed server from other default-runtime servers, treats zombie PIDs as stopped, and retains a live managed server when readiness checks time out. Reconnect cleanup also removes stale tunnel entries before closing them. Tests exercise these cases with shell processes.

Changes: SSH reconnect lifecycle

Layer / File(s) Summary
Remote server reuse and handover
packages/ssh/src/tunnel.ts, packages/ssh/src/tunnel.test.ts
The launcher checks process state, preserves a managed server whose PID matches the default runtime record, and adopts other ready default-runtime servers. It retains a managed server when readiness checks time out, while runner changes still trigger replacement. Tests cover reuse, adoption, readiness failures, runner changes, and exited processes.
Stale local tunnel cleanup
packages/ssh/src/tunnel.ts, packages/ssh/src/tunnel.test.ts
Reconnect removes a stale tunnel entry before closing the tunnel. A test checks that replacing the local forward does not stop the remote server, while explicit disconnect does.

Priority: ⬆️ High

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix · Severity of issue fixed: High

Sequence Diagram(s)

sequenceDiagram
  participant SSHClient
  participant REMOTE_LAUNCH_SCRIPT
  participant DEFAULT_RUNTIME_FILE
  participant RemoteServer
  SSHClient->>REMOTE_LAUNCH_SCRIPT: Start reconnect launch
  REMOTE_LAUNCH_SCRIPT->>DEFAULT_RUNTIME_FILE: Read runtime PID and port
  DEFAULT_RUNTIME_FILE-->>REMOTE_LAUNCH_SCRIPT: Return runtime record
  REMOTE_LAUNCH_SCRIPT->>RemoteServer: Check process liveness and readiness
  RemoteServer-->>REMOTE_LAUNCH_SCRIPT: Return liveness and readiness results
  REMOTE_LAUNCH_SCRIPT-->>SSHClient: Reuse, adopt, replace, or report launch failure
Loading

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 4572c

The inspected reconnect paths preserve the remote server when it is slow to respond and replace a stale local forward without stopping it. No issue identified here needs resolution before merge.

Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Linked Issues check Warning The changes satisfy #15986: when server-runtime.json reports the PID recorded by this launcher, the script reuses that server and its recorded port. server_running also treats zombie processes as … Implement the #16477 policy for runner changes and conflicting default-runtime adoption so a live managed server with active work is not stopped during reconnect. Defer replacement, request confirmation, or use another issue-approved behavi…
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title is concise, specific, and accurately describes the primary change: preserving the remote server and its agents during SSH reconnects.
Description check Passed The description clearly explains the problem, the three causes, the implemented fixes, scope, verification results, limitations, and linked issues. It uses a "Fix" heading instead of the template's "C…
Out of Scope Changes check Passed The stale-forward change closes only the local tunnel and supports the linked reconnect objective of preserving the remote server. The launch-script tests exercise the linked PID, readiness, zombie, a…
Full details: Linked Issues check

Explanation

The changes satisfy #15986: when server-runtime.json reports the PID recorded by this launcher, the script reuses that server and its recorded port. server_running also treats zombie processes as stopped. The changes satisfy the slow-readiness part of #16477: a live managed server receives the full readiness timeout and remains running after an unsuccessful probe. However, #16477 also identifies runner changes and another live default-runtime server as cases that can stop a managed server with active work. The script still kills REMOTE_PID when RUNNER_CHANGED=1, and it still kills a managed server when it adopts a different ready default-runtime server. The PR description confirms that the defer-or-prompt behavior is not included.

Resolution

Implement the #16477 policy for runner changes and conflicting default-runtime adoption so a live managed server with active work is not stopped during reconnect. Defer replacement, request confirmation, or use another issue-approved behavior. Add automated coverage for these cases.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Usage-based review receipt

Note

This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.

Comment @coderabbitai help to get the list of available commands.

@t3dotgg

t3dotgg commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

Note

🤖 Claude Opus 5.5 responding on behalf of Theo

The linked-issues warning is a deliberate scope limit, not a gap in the #16477 fix.

  • RUNNER_CHANGED is the update path. The #16477 triage leaves deferring updates during active runs as a separate maintainer decision. Julius also asked #17919 to keep that restart. The test "still replaces its managed server when the runner changes" covers it.
  • The adoption handover is unchanged from main. It stops the recorded managed PID only when a different live server already owns ~/.t3. Since fix(server): enforce one database owner and guard update rollback #16102, a current server cannot start on a home that our managed server owns. With current versions on both sides, a reconnect does not reach this branch while that server is running.

The case #16477 reports, a busy server killed by the 2 s probe, is fixed and covered by the busy-server and never-ready tests.

@t3dotgg
t3dotgg merged commit 36f91c4 into main Oct 14, 2026
33 checks passed
@t3dotgg
t3dotgg deleted the t3/ssh-reconnect-keeps-agents branch October 14, 2026 12:51
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Oct 14, 2026
## What's Changed
* fix(clients): # suggestions include pull requests linked to the thread by @t3dotgg in pingdotgg/t3code#19357
* fix(clients): pairing a switched-off computer again turns it back on by @t3dotgg in pingdotgg/t3code#19354
* fix(cli): t3 triage reports the server's version, not its own, as the user's build by @t3dotgg in pingdotgg/t3code#19359
* fix(client-runtime): a new WSL backend no longer shows Limited permissions until reload by @t3dotgg in pingdotgg/t3code#19361
* fix(web): action shortcuts recorded in the browser now save by @t3dotgg in pingdotgg/t3code#19365
* fix(clients): large pasted images no longer break later Claude image reads by @t3dotgg in pingdotgg/t3code#19364
* fix(server): settled and idle-released threads no longer stay Waiting by @t3dotgg in pingdotgg/t3code#19355
* perf(client-runtime): merged pull requests are no longer re-read every minute by @t3dotgg in pingdotgg/t3code#19374
* fix(server): new Codex threads no longer miss slow MCP servers' tools by @t3dotgg in pingdotgg/t3code#19368
* fix(github): requested teams now show in pull request reviewers by @t3dotgg in pingdotgg/t3code#19375
* fix(ssh): SSH launch no longer picks a port another server answers on by @t3dotgg in pingdotgg/t3code#19372
* fix(server): a failed T3 Connect relink no longer leaves a stale managed tunnel by @t3dotgg in pingdotgg/t3code#19383
* fix(shared): Windows .cmd shims no longer print DEP0190 on every spawn by @t3dotgg in pingdotgg/t3code#19385
* fix(server): commit and title generation use your custom model when no text model is set by @t3dotgg in pingdotgg/t3code#19386
* fix(web): T3 Connect settings can repair a link that drifted to publish-only by @t3dotgg in pingdotgg/t3code#19389
* fix(server): failed Claude text generation shows the CLI's own error by @t3dotgg in pingdotgg/t3code#19394
* fix(web): terminal tells the shell its new width as soon as you release a resize drag by @t3dotgg in pingdotgg/t3code#19395
* fix(web): keybindings page says to connect an environment when none is connected by @t3dotgg in pingdotgg/t3code#19400
* fix(chat): Codex follow-up suggestions show their label instead of raw markup by @t3dotgg in pingdotgg/t3code#19401
* fix(server): open in editor works over Tailscale SSH without a local sshd by @t3dotgg in pingdotgg/t3code#19402
* fix(server): a deregistered T3 Connect host shows it was removed and offers a relink by @t3dotgg in pingdotgg/t3code#19379
* perf(server): idle servers no longer scan every process every 5 seconds by @t3dotgg in pingdotgg/t3code#19399
* chore(typecheck): bump tsc-rs to 0.7.0 by @t3dotgg in pingdotgg/t3code#19408
* fix(server): bound Bitbucket API requests with 30s timeout by @kvnloo in pingdotgg/t3code#14140
* fix(server): new worktrees no longer fail when a background fetch runs at the same time by @t3dotgg in pingdotgg/t3code#19411
* perf(server): thread search runs on the SQLite reader worker by @bompus in pingdotgg/t3code#19382
* perf(server): traces keep ~40% more history and no longer block the event loop by @t3dotgg in pingdotgg/t3code#19430
* fix(shared): SQLite checkpoints no longer fall back to the main thread silently by @t3dotgg in pingdotgg/t3code#19417
* perf(server): unchanged node updates no longer write to SQLite by @t3dotgg in pingdotgg/t3code#19429
* fix(ssh): reconnecting keeps the remote server and its agents running by @t3dotgg in pingdotgg/t3code#19432
* feat(observability): SQL spans show lock wait and connection; stalls keep their own history by @t3dotgg in pingdotgg/t3code#19425
* fix(server): threads woken by notifications no longer load their whole history by @t3dotgg in pingdotgg/t3code#19423
* fix(server): worktree cleanup no longer runs sweeps back to back by @t3dotgg in pingdotgg/t3code#19418


**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261014.3105...v0.0.46-nightly.20261014.3118

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261014.3118
github-actions Bot added a commit to davidvanderklay/t3code-flake that referenced this pull request Oct 14, 2026
## What's Changed
* fix(clients): # suggestions include pull requests linked to the thread by @t3dotgg in pingdotgg/t3code#19357
* fix(clients): pairing a switched-off computer again turns it back on by @t3dotgg in pingdotgg/t3code#19354
* fix(cli): t3 triage reports the server's version, not its own, as the user's build by @t3dotgg in pingdotgg/t3code#19359
* fix(client-runtime): a new WSL backend no longer shows Limited permissions until reload by @t3dotgg in pingdotgg/t3code#19361
* fix(web): action shortcuts recorded in the browser now save by @t3dotgg in pingdotgg/t3code#19365
* fix(clients): large pasted images no longer break later Claude image reads by @t3dotgg in pingdotgg/t3code#19364
* fix(server): settled and idle-released threads no longer stay Waiting by @t3dotgg in pingdotgg/t3code#19355
* perf(client-runtime): merged pull requests are no longer re-read every minute by @t3dotgg in pingdotgg/t3code#19374
* fix(server): new Codex threads no longer miss slow MCP servers' tools by @t3dotgg in pingdotgg/t3code#19368
* fix(github): requested teams now show in pull request reviewers by @t3dotgg in pingdotgg/t3code#19375
* fix(ssh): SSH launch no longer picks a port another server answers on by @t3dotgg in pingdotgg/t3code#19372
* fix(server): a failed T3 Connect relink no longer leaves a stale managed tunnel by @t3dotgg in pingdotgg/t3code#19383
* fix(shared): Windows .cmd shims no longer print DEP0190 on every spawn by @t3dotgg in pingdotgg/t3code#19385
* fix(server): commit and title generation use your custom model when no text model is set by @t3dotgg in pingdotgg/t3code#19386
* fix(web): T3 Connect settings can repair a link that drifted to publish-only by @t3dotgg in pingdotgg/t3code#19389
* fix(server): failed Claude text generation shows the CLI's own error by @t3dotgg in pingdotgg/t3code#19394
* fix(web): terminal tells the shell its new width as soon as you release a resize drag by @t3dotgg in pingdotgg/t3code#19395
* fix(web): keybindings page says to connect an environment when none is connected by @t3dotgg in pingdotgg/t3code#19400
* fix(chat): Codex follow-up suggestions show their label instead of raw markup by @t3dotgg in pingdotgg/t3code#19401
* fix(server): open in editor works over Tailscale SSH without a local sshd by @t3dotgg in pingdotgg/t3code#19402
* fix(server): a deregistered T3 Connect host shows it was removed and offers a relink by @t3dotgg in pingdotgg/t3code#19379
* perf(server): idle servers no longer scan every process every 5 seconds by @t3dotgg in pingdotgg/t3code#19399
* chore(typecheck): bump tsc-rs to 0.7.0 by @t3dotgg in pingdotgg/t3code#19408
* fix(server): bound Bitbucket API requests with 30s timeout by @kvnloo in pingdotgg/t3code#14140
* fix(server): new worktrees no longer fail when a background fetch runs at the same time by @t3dotgg in pingdotgg/t3code#19411
* perf(server): thread search runs on the SQLite reader worker by @bompus in pingdotgg/t3code#19382
* perf(server): traces keep ~40% more history and no longer block the event loop by @t3dotgg in pingdotgg/t3code#19430
* fix(shared): SQLite checkpoints no longer fall back to the main thread silently by @t3dotgg in pingdotgg/t3code#19417
* perf(server): unchanged node updates no longer write to SQLite by @t3dotgg in pingdotgg/t3code#19429
* fix(ssh): reconnecting keeps the remote server and its agents running by @t3dotgg in pingdotgg/t3code#19432
* feat(observability): SQL spans show lock wait and connection; stalls keep their own history by @t3dotgg in pingdotgg/t3code#19425
* fix(server): threads woken by notifications no longer load their whole history by @t3dotgg in pingdotgg/t3code#19423
* fix(server): worktree cleanup no longer runs sweeps back to back by @t3dotgg in pingdotgg/t3code#19418


**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261014.3105...v0.0.46-nightly.20261014.3118

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261014.3118
aorwall added a commit to aorwall/t3code that referenced this pull request Oct 15, 2026
Merges `pingdotgg/t3code` `e536bfd33b` (106 commits since `6002c5c68c`)
into the fork. One merge commit; nothing stacked.

## Merge summary

- **Landed:** 475 files against 476 in the upstream range. The one
missing is `package.json`: its only upstream change was the `tsc-rs`
0.7.0 bump, which the fork drops (`root-package-json`). The fork delta
is 809 files.
- **Conflicts (5):**
  - `package.json`: dropped the `tsc-rs` bump.
- `pnpm-lock.yaml`: stripped the `tsc-rs@0.7.0` entries from upstream's
copy and re-derived it.
- `Sidebar.tsx`: kept the fork's thread-list filter and took upstream's
new `useSidebarProjectGroups` hook. That hook sorts project groups by
all thread shells, not by the filtered list.
- `SettingsPanels.tsx`: kept the `serverBehaviorSettings` gate and took
upstream's new description.
- `threadShell.ts`: kept the fork's subagent predicate and took
upstream's `subagentRequests` argument.
- **Fix after the merge:** upstream's new
`useProjectScriptSettings.test.tsx` used `previewUrl`/`autoOpenPreview`.
I changed it to the fork's `port`.
- **Unsupported methods:** nothing to add or drop. `vcs.switchRef` and
`git.preparePullRequestThread` stay as they are.
- **Verification:** `verify.mjs` passes every check except `test`:
- duplicate-adds, tripwires, resolution-check, lockfile, fmt, lint,
typecheck (re-run after the fix) and build all pass.
  - Six test files fail on their own, all because of the sandbox:
- the five recorded in earlier merges: `AgentScope` (`oom_score_adj`),
`ServerBrowserPage` (no Playwright browser), `AcpAdapterV2`,
`OrchestratorReplayFixtures`, `OpenCodeServerLedger`;
- one new: `@t3tools/ssh` `tunnel.test.ts` from upstream pingdotgg#19432. It
fails the same way `AcpAdapterV2` does: a killed process is never
reaped, so it still reads as alive. The fork does not touch
`packages/ssh`.
  - `@t3tools/web` tests pass after the fix.
- Details are in `docs/fork/upstream-merge-log.md`. `docs/fork/gaps.md`
has new notes under five existing entries: capabilities, settlement
rules, pull request link, Orchestrator V2 (transcript read) and runtime
fixes.

## Usable as-is

- Waiting status icon and elapsed timer in the sidebar (pingdotgg#19617)
- The settle shortcut moves on to the next thread, the same way the
sidebar does (pingdotgg#19328)
- Terminal resize, recorded-shortcut save, a keybindings page that
prompts to connect, and settings that save after a stored value fails to
decode (pingdotgg#19395, pingdotgg#19365, pingdotgg#19400, pingdotgg#19312)
- The Mermaid code toggle stays in place, cursor stops stay outside
styled markers, and live reasoning stays scrolled while it streams
(pingdotgg#19601, pingdotgg#19651, pingdotgg#19334)
- PR filters hide Scratch and the project filter follows sidebar order;
`#` suggestions include the thread's PRs; merged PRs are no longer
re-read every minute (pingdotgg#19179, pingdotgg#19180, pingdotgg#19357, pingdotgg#19374)
- New provider-update-checks copy (pingdotgg#18208), shown only where the fork's
`serverBehaviorSettings` gate already shows that row
- Dependency bumps: Effect 4.0.4, OpenCode 2.0.28. `tsc-rs` 0.7.0 is
deliberately dropped.

## Unsupported in Moatless / needs implementation

- **Auto-settle keeps threads with a PR** (pingdotgg#14642): adds the
`sidebarAutoSettleKeepPullRequests` setting and the
`threadAutoSettlementScope` capability. Moatless does not report the
capability, so the row stays hidden.
- **Settled-worktree cleanup** (pingdotgg#15208): adds the
`worktreeSettledAfterDays` setting and the `settledWorktreeCleanup`
capability (Storage settings). Not reported, so the row is hidden.
- **Thread transcript HTTP read** (pingdotgg#15210): `GET
/api/orchestration/threads/:threadId/transcript`, used only for a thread
dropped in from another environment. Moatless does not route it, and a
single-environment deployment cannot reach it.
- **`thread.pull-request-link-updated` event** (pingdotgg#19248): Moatless sends
no pull-request frames; it changes the listing instead.
- **Delegated-task question handling**: `hasPendingQuestion` on the
orchestrator MCP task result (pingdotgg#18459, pingdotgg#19521, pingdotgg#19538).
- T3 Connect revoked links, SSH and Tailscale, pairing, and desktop- and
mobile-only items: not used by the Moatless web client.

## Backend behavior to consider reproducing in Moatless

- Inactivity auto-settle that skips PR-linked threads (pingdotgg#14642); settled
and idle-released threads stop reading as Waiting (pingdotgg#19355)
- A retention sweep for settled threads' workspaces (pingdotgg#15208, pingdotgg#19418)
- Store a PR link only when it changes, and stop re-reading merged PRs
each turn (pingdotgg#19248, pingdotgg#19344)
- GitHub reviewers include requested teams (pingdotgg#19375); new worktrees
survive a concurrent fetch (pingdotgg#19411)
- Commit and title generation fall back to the custom model (pingdotgg#19386)
- Claude: `managed-mcp.json` is honoured and a 403 no longer says to log
in (pingdotgg#18706, pingdotgg#15872)
- Codex: "Always allow this session" works and slow MCP servers no
longer drop tools (pingdotgg#19635, pingdotgg#19368)
- CLIs behind mise wrappers no longer time out (pingdotgg#19340); cold usage
scans stop stalling the server (pingdotgg#19446); a single database owner is
enforced, with exit code 78 (pingdotgg#16102)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---
Moatless task:
https://moatless.soaplabstest.com/tasks/4979b4c3-e4cc-4911-849b-adb94cb65be6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

4 participants