Skip to content

chore(sync): port the board onto upstream orchestration-v2 (v0.1.0) (T3O-55) — DO NOT SQUASH - #183

Merged
brentkelly merged 1063 commits into
t3ofrom
board/t3o-55
Oct 20, 2026
Merged

brentkelly merged 1063 commits into
t3ofrom
board/t3o-55

Conversation

@brentkelly

Copy link
Copy Markdown
Owner

Upstream v0.1.0 deleted orchestration v1, which the board was built on. Syncing past v0.0.45 meant porting the board rather than resolving conflicts.

This is the T3O-55 epic's integration branch. It brings in its four child cards, each already reviewed and merged here:

Land with a merge commit, never a squash (docs/t3o/seams.md). See the review on this PR: #182 was squash-merged into this branch, so upstream v0.1.0 is not yet in its ancestry.

🤖 Generated with Claude Code

Saikrishna1876 and others added 30 commits October 12, 2026 01:53
pingdotgg#18193)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
Return exit 125 when the Linux cgroup wrapper target is missing, including when /bin/sh is Bash. Preserve arguments and child exit codes.
…efault (pingdotgg#18461)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rs (pingdotgg#18476)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…odels (pingdotgg#18488)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#18454)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…uilds (pingdotgg#18560)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…otgg#18450)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
brentkelly and others added 2 commits October 20, 2026 17:29
…181)

* refactor(board): ThreadGateway, the board's one seam onto threads (T3O-58, WIP)

Adds board/ThreadGateway.ts (board-vocabulary commands, reads and signals)
and ThreadGatewayV1, which wraps today's v1 engine, projection, provider and
SQL reads one for one. Re-points the supervisor reactor, board engine,
todo-cache tail, board RPC, MCP board toolkit, worktree retirer, pull-request
gateway and usage-limit detector at it. Test suites follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): in-memory ThreadGateway fake, harness on it, V1 contract and boundary suites (T3O-58, WIP)

The supervisor harness drops its engine, snapshot and provider stubs for one
in-memory ThreadGateway; its signal builders speak ThreadSignal. Adds the
gateway contract suite on the real v1 stack and a source-level boundary test
that only ThreadGatewayV1 reaches upstream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(board): the thread gateway in the seam inventory and t3o-26 (T3O-58)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): the harness refuses an engine-shaped fixture instead of hanging (T3O-58, WIP)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): suites on the thread gateway's vocabulary (T3O-58)

Every board suite now speaks gateway calls (thread.spawn, thread.send,
thread.interrupt, thread.settle, thread.delete), BoardThreadState and
ThreadSignal instead of v1 commands, shells and engine events. Stubs of
upstream's engine, snapshot query and provider are gone from the supervisor
and PR-gateway suites. Adds unit coverage for the v1 shell-to-state rules
that moved out of the reactor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(board): auto-settle re-checks candidacy against a sequence captured first (T3O-58)

Review r1-i1. The engine's changed-since-snapshot guard was disabled because
the sequence was read at dispatch time, after the caller's candidacy read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* perf(board): gateway projects() reads projection_projects, not the read model (T3O-58)

Review r1-i2. The decider context and worktreeRetire paid for a full command
read model on every project-bearing command; back to one projects-only query.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): the fake gateway's delete leaves the thread readable as deleted (T3O-58)

Review r1-i3. One deletedThreadState builder now serves V1 and the fake, so
the two cannot disagree on what a deleted thread reads as.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): spawn deletes a thread whose first turn is refused; stale comment (T3O-58)

Review r1-n2 and r1-n1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): the fake gateway's observe delivers every signal to every subscription (T3O-58)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(board): the orphan-todo sweep spares rows written after the live list was taken (T3O-58)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(board): MCP board registration names ThreadGateway as a requirement (T3O-58)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: brentkelly <d-ai@zeald.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…-v2 (#182)

* ci(mobile): cut a store build when the native fingerprint has no build (pingdotgg#18496)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): terminal colors no longer look washed out (pingdotgg#17168)

* refactor(settings): centralize schema-declared secrets and inputs (pingdotgg#18505)

* fix(mobile): restore native scroll-edge effects on forms and PRs (pingdotgg#18507)

* feat(mobile): native-sized pull request action bar (pingdotgg#18527)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): preserve theme contrast across surfaces and actions (pingdotgg#18506)

* refactor(mobile): share themed list rows and sections (pingdotgg#18510)

* refactor(mobile): share themed toggle rows in settings (pingdotgg#18511)

* refactor(mobile): share themed choice rows in settings (pingdotgg#18516)

* fix(server): consolidate browser credential handling (pingdotgg#18509)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat: open T3 Code from a link with a prompt ready to send (pingdotgg#18454)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(release): drop placeholder allowBuilds entry that broke nightly builds (pingdotgg#18560)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* ci: fail PRs on invalid allowBuilds entries in pnpm-workspace.yaml (pingdotgg#18564)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): menu dismiss taps no longer press the view underneath (pingdotgg#18565)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(web): one sidebar click updates every machine and provider (pingdotgg#18053)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(release): avoid PowerShell crashes on Windows ARM (pingdotgg#18573)

* fix(server): Forgejo pull requests report their head commit (pingdotgg#18463)

* test(provider-acp): terminal kill test passes on Windows hosts (pingdotgg#18450)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): context meter no longer reads 1000k just under a million tokens (pingdotgg#17539)

* fix(desktop): avoid Hyprland 0.48–0.50 Wayland color-manager startup crashes (pingdotgg#18566)

Disable WaylandWpColorManagerV1 before Electron is ready only on native Wayland Hyprland sessions running a stock 0.48.0-0.50.1 release, which advertise FEATURE_SET_PRIMARIES but reject set_primaries (hyprwm/Hyprland#9444; fixed in 0.51.0 by hyprwm/Hyprland#11499). Any probe failure or explicit feature override keeps the default.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): iPhones on iOS 27.2 beta no longer open to a black screen (pingdotgg#18654)

Co-authored-by: T3 Code Test <t3code-test@example.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): installs skip the Enriched math download again (pingdotgg#18679)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): setup wizards follow custom themes (pingdotgg#18725)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): resolve configured PR bases to branch names (pingdotgg#18754)

* fix: expose running terminals and prevent setup install stalls (pingdotgg#18740)

* fix(mobile): hide Claude Read image placeholders when previews exist (pingdotgg#18761)

* fix(gitlab): merge request details report their head commit (pingdotgg#18608)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(server): rename built-in models from settings (pingdotgg#18840)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): Git actions follow the selected thread (pingdotgg#18862)

* fix(desktop): preserve updater failure causes in diagnostics (pingdotgg#18864)

* fix(web): attach dropped threads to pending answers (pingdotgg#18789)

* fix(web): keep button text stable during press scaling (pingdotgg#18866)

* fix(bitbucket): preserve PR head commits for review guides (pingdotgg#18402)

* fix(server): return null for unresolved optional commands (pingdotgg#18871)

* fix: show computer-use action titles and align app icons (pingdotgg#18865)

* fix(server): apply confirmed Claude subagent outcomes (pingdotgg#18778)

* fix(mobile): Browser page no longer logs a duplicate key warning (pingdotgg#18776)

* fix(web): a numeric q in the pull requests URL no longer crashes the page (pingdotgg#18666)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(client): T3 Connect clients reconnect once a new tunnel is ready (pingdotgg#16554)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(web): leaving the home page while the app loads no longer bounces you to a new draft (pingdotgg#18672)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): attachment images no longer re-download on every reconnect (pingdotgg#18824)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): a migration id clash stops startup with a clear error instead of crash-looping (pingdotgg#15925)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: PR Batch Tester <agent@local.test>

* fix(server): a thread with an event type from a newer build can still resync after a downgrade (pingdotgg#15937)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: PR Batch Tester <agent@local.test>

* fix(server): Codex titles and branch names no longer start the user's MCP servers (pingdotgg#17058)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Yash Singh <saiansh2525@gmail.com>

* fix(server): Claude thinking before a tool call stays a collapsed Thought (pingdotgg#18887)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

* perf(server): cached branch PR lookups no longer start git processes (pingdotgg#17063)

* fix(web): compact merge buttons show when they need a second click (pingdotgg#18878)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): pull requests load from another environment when theirs is offline (pingdotgg#18698)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(web): collect active tool calls without array shifts (pingdotgg#18853)

Co-authored-by: SAPHID <saphid@users.noreply.github.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>

* fix(web): center the pace icon on the limit card's "left" label (pingdotgg#18580)

* fix(web): update everything includes npm-installed servers again (pingdotgg#18709)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): review guides find Bitbucket heads reported by short hash (pingdotgg#18900)

Co-authored-by: Pawel Mikolajczuk <pmikolajczuk@atlassian.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): scheduled task model picker no longer shifts the dialog (pingdotgg#18940)

* fix(ci): CodeRabbit reviews no longer request changes (pingdotgg#18954)

* fix(web): links in HTML renders open like chat links (pingdotgg#18897)

* fix(web): remember editor selections before opening folders (pingdotgg#19078)

* fix(ci): retry Cursor webhook HTTP 400s on main (pingdotgg#19077)

* fix(web): let context handoff details use the available width (pingdotgg#19098)

* fix(server): snoozed threads allow queued automatic wakes (pingdotgg#19099)

* fix(web): merge button fits its label and animates width changes (pingdotgg#18478)

* fix(web): Cmd+Enter creates the new project while an option is highlighted (pingdotgg#18901)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(pi): queue turn-start prompts when native work is running (pingdotgg#18639)

* fix(pi): load T3 tool schemas for completion gateways (pingdotgg#18643)

* fix(devices): report agent tool startup failures (pingdotgg#18502)

* fix(web): settings warn when an environment silently drops a setting (pingdotgg#18334)

* fix(desktop): stop unsolicited Messages code autofill (pingdotgg#19118)

* feat(chat): show pending context handoff when selecting a model (pingdotgg#18369)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): collect SQLite planner statistics when the database opens (pingdotgg#18471)

* fix(server): failed threads no longer scan every tool item for errors (pingdotgg#18628)

* fix(web): keep clone folder names readable in the setup project picker (pingdotgg#12474)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Yash Singh <saiansh2525@gmail.com>

* fix(composer): allow slash skill search within messages (pingdotgg#19107)

* fix(claude): default background text generation to haiku 5.5 (pingdotgg#18781)

* chore(typecheck): bump tsc-rs to 0.6.0 (pingdotgg#19187)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): keep OpenCode timeouts from crashing the error formatter (pingdotgg#12469)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Yash Singh <saiansh2525@gmail.com>

* feat(server): run delegated tasks in separate worktrees (pingdotgg#19102)

* fix(git): preserve whitespace in file paths (pingdotgg#12464) (pingdotgg#12465)

Co-authored-by: Yash Singh <saiansh2525@gmail.com>

* fix(server): stop fsyncing SQLite on every commit so slow disks no longer drop clients (pingdotgg#19205)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): make links in agent questions clickable (pingdotgg#19197)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(usage): show one Antigravity account once, not once per machine (pingdotgg#19207)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): Android beta links point to the APK release, not the banned Google group (pingdotgg#18955)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(client): "Subagent needs input" no longer sticks after the subagent finishes (pingdotgg#18996)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): stop question answers from resetting the editor caret (pingdotgg#11337)

* fix(web): app shortcuts keep working after clicking into an HTML render (pingdotgg#18898)

* fix(source-control): keep distinct CI checks with overlapping names (pingdotgg#19162)

Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com>

* fix(server): avoid excessive backtracking in webhook templates (pingdotgg#19160)

Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com>

* fix(web): auto-merge button fits its label and animates width changes (pingdotgg#19111)

* fix(web): scroll terminal output with touch gestures (pingdotgg#11305)

* feat(web): view visual replies full screen with zoom (pingdotgg#17561)

* fix(web): removing a project no longer opens a new thread for it (pingdotgg#15864)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(server): usage hubs with a host-less URL no longer vanish from Limits (pingdotgg#11368)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): search bar animates in on iOS 26+ instead of popping (pingdotgg#19209)

* chore(deps): Effect 4.0.4 and OpenCode 2.0.28, dropping the patches they made unnecessary (pingdotgg#19213)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): a Claude 403 no longer tells you to log in again (pingdotgg#15872)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(server): persist only changed pull request links (pingdotgg#19248)

* fix(server): delegate_task explains that supervised runtime modes pause the child (pingdotgg#14416)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: SAPHID <saphid@users.noreply.github.com>

* fix(server): Claude subagent progress no longer re-stores the unchanged prompt (pingdotgg#18375)

* fix(server): thread list reads no longer freeze the server on large databases (pingdotgg#19226)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Alex Southwell <saphid@gmail.com>

* perf(server): streamed events no longer rewrite the whole thread row (pingdotgg#18890)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* docs: close PRs written by Gemini or Muse Wonder (pingdotgg#19286)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix: update all no longer runs agents twice or loses background work (pingdotgg#19291)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): enforce one database owner and guard update rollback (pingdotgg#16102)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

* perf(server): reuse stored turn item positions (pingdotgg#19230)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): avoid quadratic resource history replay (pingdotgg#18825)

Co-authored-by: SAPHID <saphid@users.noreply.github.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>

* fix(server): repair ownership UUID generation (pingdotgg#19298)

* fix(client-runtime): slow config snapshots no longer drop working connections (pingdotgg#14518)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: SAPHID <saphid@users.noreply.github.com>

* fix(server): bound activity publishes so stalled responses release the worker (pingdotgg#18830)

Co-authored-by: SAPHID <saphid@users.noreply.github.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>

* perf(shared): cached SQLite statements no longer hold large parameters (pingdotgg#19305)

Takeover of pingdotgg#17785 by @tris203. Executions binding more than 64 KiB of text or blob data run on a throwaway statement, in the writer and the reader worker, so cached statements no longer pin large payloads in native memory.

Co-authored-by: tris203 <admin@snappeh.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(provider-cursor): Cursor text generation no longer leaks SQLite file handles on every call (pingdotgg#19310)

Takeover of pingdotgg#18154. Per-call SqliteLocalAgentStore so Cursor text generation closes its SQLite handles, and the agent now closes before its store on sign-out. Fixes pingdotgg#18151.

Co-authored-by: Aaron Queen <bompus@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): make SQLite checkpoints durable on macOS (pingdotgg#19304)

Co-authored-by: Soroush Baraouf <soroushraouf44@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): Codex plan streaming no longer commits every token (pingdotgg#19317)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): large file edits no longer store their full diff twice (pingdotgg#19316)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): settings save again after a stored value fails decode (pingdotgg#19312)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): HTML and Markdown attachments no longer fail OpenCode turns (pingdotgg#19319)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): worktree paths no longer vanish on git before 2.36 (pingdotgg#19315)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(acp): custom models now reach the agent instead of failing to open (pingdotgg#19313)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): Claude no longer shows unavailable when its health check can't run in $HOME (pingdotgg#19320)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): stalled OAuth token requests no longer block credential calls (pingdotgg#19321)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): steered Claude turns no longer undercount token usage (pingdotgg#19314)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): closing a Claude session no longer leaves plugin MCP servers running (pingdotgg#19322)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(client-runtime): T3 Connect link timeouts explain the relay 504 (pingdotgg#19329)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): settle shortcut moves to the next thread like the sidebar (pingdotgg#19328)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(acp): Copilot's default agent no longer disappears from the Agent menu (pingdotgg#19330)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): SQLite checkpoints no longer block the event loop (pingdotgg#19324)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): incompatible environments now say whether to update the app or the host (pingdotgg#19331)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): live reasoning stays scrolled to the newest text while it streams (pingdotgg#19334)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): T3 Connect no longer stays down after a network change (pingdotgg#19335)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(clients): host images say when the connection lacks file access (pingdotgg#19337)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(clients): a resumed subagent no longer moves the later turn's Worked for row (pingdotgg#19338)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix: Claude and Codex no longer time out behind mise wrappers (pingdotgg#19340)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): turns no longer re-read merged PRs from GitHub (pingdotgg#19344)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* docs(settings): explain that provider update checks also gate the model catalog (pingdotgg#18208)

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(web): an unsaved citation comment survives a question or approval (pingdotgg#19350)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): changing Claude effort no longer fails while background agents run (pingdotgg#19341)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): service updates no longer fail on full disks or large databases (pingdotgg#19311)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: SAPHID <saphid@users.noreply.github.com>

* fix(clients): # suggestions include pull requests linked to the thread (pingdotgg#19357)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(clients): pairing a switched-off computer again turns it back on (pingdotgg#19354)

Co-authored-by: Mnigos <makowskiigor@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): t3 triage reports the server's version, not its own, as the user's build (pingdotgg#19359)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(client-runtime): a new WSL backend no longer shows Limited permissions until reload (pingdotgg#19361)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): action shortcuts recorded in the browser now save (pingdotgg#19365)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(clients): large pasted images no longer break later Claude image reads (pingdotgg#19364)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): settled and idle-released threads no longer stay Waiting (pingdotgg#19355)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(client-runtime): merged pull requests are no longer re-read every minute (pingdotgg#19374)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): new Codex threads no longer miss slow MCP servers' tools (pingdotgg#19368)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(github): requested teams now show in pull request reviewers (pingdotgg#19375)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ssh): SSH launch no longer picks a port another server answers on (pingdotgg#19372)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): a failed T3 Connect relink no longer leaves a stale managed tunnel (pingdotgg#19383)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(shared): Windows .cmd shims no longer print DEP0190 on every spawn (pingdotgg#19385)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): commit and title generation use your custom model when no text model is set (pingdotgg#19386)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): T3 Connect settings can repair a link that drifted to publish-only (pingdotgg#19389)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): failed Claude text generation shows the CLI's own error (pingdotgg#19394)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): terminal tells the shell its new width as soon as you release a resize drag (pingdotgg#19395)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): keybindings page says to connect an environment when none is connected (pingdotgg#19400)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): Codex follow-up suggestions show their label instead of raw markup (pingdotgg#19401)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): open in editor works over Tailscale SSH without a local sshd (pingdotgg#19402)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): a deregistered T3 Connect host shows it was removed and offers a relink (pingdotgg#19379)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): idle servers no longer scan every process every 5 seconds (pingdotgg#19399)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* chore(typecheck): bump tsc-rs to 0.7.0 (pingdotgg#19408)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): bound Bitbucket API requests with 30s timeout (pingdotgg#14140)

* fix(server): new worktrees no longer fail when a background fetch runs at the same time (pingdotgg#19411)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): thread search runs on the SQLite reader worker (pingdotgg#19382)

* perf(server): traces keep ~40% more history and no longer block the event loop (pingdotgg#19430)

Co-authored-by: Aaron Queen <bompus@users.noreply.github.com>
Co-authored-by: Daniyal <115576848+daniyal-sh@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(shared): SQLite checkpoints no longer fall back to the main thread silently (pingdotgg#19417)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): unchanged node updates no longer write to SQLite (pingdotgg#19429)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

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

Co-authored-by: puccez <emanuele.puccetti@icloud.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: luke2x <97411838+luke2x@users.noreply.github.com>

* feat(observability): SQL spans show lock wait and connection; stalls keep their own history (pingdotgg#19425)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): threads woken by notifications no longer load their whole history (pingdotgg#19423)

Co-authored-by: Eldar Politkin <32969822+EldarPolitkin@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): worktree cleanup no longer runs sweeps back to back (pingdotgg#19418)

Co-authored-by: Tanner Sharon <tannermsharon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): only Claude progress updates before a tool call show as text (pingdotgg#18923)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

* fix(pr): keep known state in sync across controls (pingdotgg#18408)

* fix(web): review summary only says optional when it is (pingdotgg#18451)

* fix(web): subagent lists in the composer banner no longer start lines with a comma (pingdotgg#19170)

* fix(web,mobile): pull request filters no longer offer the Scratch folder (pingdotgg#19179)

* fix(web,mobile): pull request project filter follows the sidebar order (pingdotgg#19180)

* feat(settings): inactivity auto-settle can keep threads with a PR (pingdotgg#14642)

Co-authored-by: maria-rcks <maria@kuuro.net>

* fix(server): Claude works next to an enterprise managed-mcp.json (pingdotgg#18706)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web,mobile): muse code uses meta's official blue logo (pingdotgg#19562)

* fix(desktop): import CA trust variables from the login shell (pingdotgg#19604)

* fix(server): preview copy only forwards text from the viewer's copy keys (pingdotgg#19561)

* fix(muse): Supervised threads ask before commands again (pingdotgg#19558)

* fix(server): restarts no longer stall on old subagent result deliveries (pingdotgg#19631)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(clients): a thread comes back when its subagent needs approval (pingdotgg#19630)

A delegated subagent waiting on an approval or question now shows on its parent thread: Working section, status badge, notifications, snooze, and the composer banner on web and mobile. Takes over pingdotgg#19614.

Co-authored-by: lovelycodemonkey <ghassemimahyad@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(usage): cold usage scans no longer stall the server (pingdotgg#19446)

Co-authored-by: Victor Vogel <263261067+only21mil@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): "Always allow this session" stops Codex asking for every command (pingdotgg#19635)

After "Always allow this session", the Codex adapter answers later command (or file change) approvals in that thread for the rest of the session. A turn in another runtime mode, or the thread unloading, ends it. Takes over pingdotgg#19598.

Co-authored-by: lovelycodemonkey <ghassemimahyad@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): keep vertical cursor stops outside styled markers (pingdotgg#19651)

* fix(web): mermaid code toggle stays in place when switching views (pingdotgg#19601)

* fix(server): Reveal in File Explorer and browser launches work on Windows again (pingdotgg#19656)

Windows PowerShell started with detached: true exits 0 without running its command. Native Windows PowerShell launches (reveal and browser) now start attached, unref'd, with ignored stdio.

Takes over pingdotgg#10387. Fixes pingdotgg#11780.

Co-authored-by: doubletwisted <118005273+doubletwisted@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): GitHub App tokens can merge and use other write actions (pingdotgg#19103)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): commands on busy threads stop decoding thousands of finished tool nodes (pingdotgg#19675)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): use workspace-scoped Bitbucket permissions API (pingdotgg#10984)

Co-authored-by: RealDoc06 <73218979+RealDoc06@users.noreply.github.com>
Co-authored-by: Theo Browne <me@t3.gg>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(composer): read dropped threads across environments (pingdotgg#15210)

* test(web): remove duplicate change request parser tests (pingdotgg#13591)

Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>

* fix(server): a delegated task's question ends the parent's blocking wait (pingdotgg#19538)

Co-authored-by: maria-rcks <maria@kuuro.net>

* fix(server): a delegated task's question now wakes its parent agent (pingdotgg#19521)

Co-authored-by: maria-rcks <maria@kuuro.net>

* fix(server): a cancelled delegated task no longer shows its child's question (pingdotgg#18459)

Co-authored-by: maria-rcks <maria@kuuro.net>

* feat(storage): clean up settled thread worktrees (pingdotgg#15208)

Co-authored-by: maria-rcks <maria@kuuro.net>

* feat(web,mobile): waiting status shows a dot-dashed icon and a timer (pingdotgg#19617)

* feat(pr): delete branches after merge or close (pingdotgg#19257)

* fix(web): the sidebar no longer keeps old renders alive (pingdotgg#19852)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): update everything no longer waits on slow servers, and shows progress in the sidebar (pingdotgg#19837)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): text generation no longer falls back to the expensive flagship model (pingdotgg#19722)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(web): setting to open pull request links on GitHub (pingdotgg#19719)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): middle-click opens pull requests in the browser, like cmd+click (pingdotgg#19854)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): the chat view and composer no longer keep old renders alive (pingdotgg#19869)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): steering no longer ends running subagents (pingdotgg#19210)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web,mobile): sent queued messages say "from queue", not "queued" (pingdotgg#19849)

Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Rodrigo Brechard <rodrigobrechard@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): OTLP exports say which machine and build they came from (pingdotgg#19370)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(prs): bound refreshes by each tracker read (pingdotgg#19403)

* fix(mobile): new threads no longer get stuck on Starting (pingdotgg#15870)

* fix(mobile): agent-spawned threads can be mentioned from the @ picker (pingdotgg#15841)

Co-authored-by: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): pull request lists fill the available scroll viewport (pingdotgg#18767)

* fix(server): Cursor threads in Auto use Cursor's Auto-review (pingdotgg#19712)

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): text selection keeps going under the turn minimap (pingdotgg#16748)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(server): Antigravity installs on Windows ARM (pingdotgg#18472)

* perf(server): cut fff-node FFI marshaling overhead on large workspaces (pingdotgg#18514)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(server): replace Windows PowerShell port polling with netstat (pingdotgg#18524)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): Windows Codex install uses system tar and retries rename (pingdotgg#18523)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(providers): fall back to windows-x86_64 ACP binaries on Windows on ARM (pingdotgg#18521)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): fail runs whose provider stream ends without a terminal (pingdotgg#19131)

Co-authored-by: GPT-6.1-Sol at high effort <noreply@openai.com>
Co-authored-by: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): a dropped HTTP/2 connection no longer crashes the server (pingdotgg#18378)

Co-authored-by: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): Stop re-probing hosting CLIs for unrecognized hosts (pingdotgg#19692)

Co-authored-by: Victor Vogel <263261067+only21mil@users.noreply.github.com>
Co-authored-by: Jorrin <43169049+JorrinKievit@users.noreply.github.com>
Co-authored-by: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): Android nested list markers no longer overlap their text (pingdotgg#19970)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): release iOS audio session after video playback (pingdotgg#18539)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): keep wrapped split diffs from jumping while scrolling (pingdotgg#20022)

* fix(web): model picker search lists newest models first (pingdotgg#18494)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Gabriel De Andrade <gabrieldeandradeleal@gmail.com>

* fix(acp): one malformed session update no longer ends the session (pingdotgg#20015)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* perf(web): HTML renders show a layout skeleton and reuse cached pages (pingdotgg#19654)

* chore: change product branding from Alpha to Beta (pingdotgg#20032)

* chore(sync): regenerate pnpm-lock.yaml for the fork's web font

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(contracts): board contracts build on v0.1.0

Board events get their own metadata schema (the v1 orchestration contracts
are gone), board.ts takes RuntimeMode from providerPolicy, the fork's
duplicate RuntimeMode and PullRequestDetail.headSha drop in favour of
upstream's, and effect's unstable module paths move to their v4 homes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(board): card thread state reads v2 thread shells

A shared adapter (`@t3tools/shared/boardThreadState`) fills the board's
thread-state source from an `OrchestrationV2ThreadShell`, so the client's
live card overlay and the server gateway read v2 the same way. The fork's
T3O-48 origin preference is re-seamed into the new GitHub package, and the
v1 gh-CLI scope-fallback tests drop with the CLI they drove.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(board): ThreadGatewayV2 over orchestration-v2 (WIP)

The board's thread seam re-implemented on v2: commands through
ThreadManagementService.dispatch, reads from the v2 projection store, and
signals and the todo log off the engine's stored event log. Board-minted
command ids still tell the board's turns from a human's. The gateway gains
the thread's usage-limit state and armLimitRecovery, so v2's own recovery
worker resumes a limited board thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(board): wire the board into the v2 server

One board-owned layer (board/serverLayer.ts) carries the supervisor, engine,
gateway, forge window and worktree retirer into upstream's runtime, with
upstream's own EventSinkV2 instance; the supervisor starts after v2's restart
recovery. Board RPCs drop their observeRpc wrappers for upstream's
instrumentation middleware and get their aggregate label; the board MCP tools
are declared through McpToolAccess, registered via toolkitRegistration, and
resolve their caller with requireThreadScope. ThreadGatewayV1, the v1
legibility instrument and the raw toolkit registration go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): ThreadGateway contract and signal suites on orchestration-v2

The board stack testkit now builds upstream's v2 engine (no provider) with
its thread management, stores and project service; the gateway contract
suite runs against ThreadGatewayV2 on it. Pure suites pin the signal
reader's once-per-fact reporting and the usage-limit reading of a shell.
Usage limits now come from v2's failure class: the detector reads it off the
thread, a limited board step arms v2's own recovery, and the board stops
waking a park whose thread v2 will resume itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(source-control): re-seam the GitHub scope fallback and origin rule on the API client

Upstream v0.1.0 reads GitHub through its own API client, not gh. The fork's
T3O-8 missing-scope fallback moves into the package: the pull request core
read gives up team reviewers, then checks, on a scope refusal, and marks
unread checks. T3O-48's origin-first rule is a one-line seam in the
repository resolution; upstream tests asserting gh's upstream preference
carry marked fork expectations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(server): the board's tests and the CLI typecheck on v0.1.0

Suites move onto the v2 board stack: v1 activity writes become v2 plan
events written through the engine's own sink, the decider fixtures get a
board-owned read-model shape, and the v1-only last-signal and pending-turn
SQL suites are replaced by contract cases. runServer carries an explicit
type: with the board's RPCs in WsRpcGroup the CLI's inference through the
whole layer graph otherwise gives up as any.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): the gateway boundary sees orchestration-v2

The boundary suite matched only orchestration/, so it was blind to v2
imports; it now names orchestration-v2 too, allows the MCP tools'
dependency on ThreadManagementService, and proves its SQL instrument on a
sample statement since the v2 gateway runs no SQL of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(server): upstream tests carry the board's capability and attached database

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(board): keep boards.sqlite as a v1 build left it, once

The first orchestration-v2 boot copies state.sqlite and leaves it alone;
boards.sqlite has no such copy, since a v2 build migrates it in place. The
board now takes one SQLite backup to boards.pre-v2.sqlite before anything
writes, keyed on a new no-op board migration (045) whose absence marks a
ledger last written by a v1 build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(providers): agent sessions take the project's gitenv token on v2

The t3o-34 seams lived in the v1 Claude, Codex and ACP runtimes, which
upstream deleted. gitenv moves to @t3tools/shared so the ACP package can
reach it, and the seams sit on the v2 Claude query options, the Codex
app-server spawn and the ACP spawn environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(web): the board UI builds on v0.1.0

Card thread state reads v2 shells through the shared adapter, the attention
grace keys on the latest run's completion, the embedded card chat mounts
ChatView the way v2's thread route does, and the board's model pickers and
concurrency rows follow upstream's provider-instance and environment APIs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(board): lint and format on v0.1.0's rules

Upstream's new rules flag board code: tests built in loops become each()
cases, raw ResizeObservers move to observeResize, a focus ring is inset, and
single-tag catches use catchTags. The touched files are formatted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(t3o): record the v0.1.0 sync

AGENTS.md's status line, the overview's D2 note and docs/t3o/seams.md (merge
log, decisions, census, unmarked edits, inventory rows and the startup-order
rule) describe the board on orchestration-v2. The gh-CLI origin pin
(githubRemote.ts), unused since GitHub moved to the API client, is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(board): only trust a v2 limit arm the recovery worker will act on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(board): resubscribe the thread signal stream after a failed read

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(board): read the latest assistant message from the latest run only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(sync): summary reads keep the check rollup's missing-scope fallback

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(sync): retire the duplicated headSha seam and record both fixes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(board): one reading of a v2 waiting run, shared by gateway and client

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(board): drive the stream resubscribe test through it.effect

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Julius Marminge <julius0216@outlook.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Prathmesh Vhatkar <psv2522@gmail.com>
Co-authored-by: Theo Browne <me@t3.gg>
Co-authored-by: oliver <97427849+flamboh@users.noreply.github.com>
Co-authored-by: AdEx-Partners-DE <stark.burns@adexpartners.com>
Co-authored-by: luke <97411838+luke2x@users.noreply.github.com>
Co-authored-by: kronk0s <bessaadkhalil@gmail.com>
Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com>
Co-authored-by: T3 Code Test <t3code-test@example.com>
Co-authored-by: Dara Adedeji <76637177+SunkenInTime@users.noreply.github.com>
Co-authored-by: Yash Singh <saiansh2525@gmail.com>
Co-authored-by: Scott Norteman <snorteman@gmail.com>
Co-authored-by: Paweł Mikołajczuk <pawel@mikolajczuk.in>
Co-authored-by: Tristan Knight <admin@snappeh.com>
Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: Bryce Bjork <brycedbjork@gmail.com>
Co-authored-by: maria <maria@kuuro.net>
Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
Co-authored-by: Antony <97451137+TonybynMp4@users.noreply.github.com>
Co-authored-by: Alex Southwell <saphid@gmail.com>
Co-authored-by: SAPHID <saphid@users.noreply.github.com>
Co-authored-by: GPT-6.1 Sol <noreply@openai.com>
Co-authored-by: Akash Moradiya <64416825+akash3444@users.noreply.github.com>
Co-authored-by: Pawel Mikolajczuk <pmikolajczuk@atlassian.com>
Co-authored-by: Wout Stiens <71498452+StiensWout@users.noreply.github.com>
Co-authored-by: Alex <me@pixp.cc>
Co-authored-by: Otavio Salvador <otavio@ossystems.com.br>
Co-authored-by: Hasan Shoaib <142208891+realhasanshoaib@users.noreply.github.com>
Co-authored-by: tachytelicdetonation <47115683+tachytelicdetonation@users.noreply.github.com>
Co-authored-by: Bilal Bakr <62337003+Bil0000@users.noreply.github.com>
Co-authored-by: Defmon3 <111503233+Defmon3@users.noreply.github.com>
Co-authored-by: Simone <lucenz@proton.me>
Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Guillermo Casanova <75276669+Gigioxx@users.noreply.github.com>
Co-authored-by: Hessboi <101525047+Baker-Harrison@users.noreply.github.com>
Co-authored-by: Tanner Polley <tannerwpolley@gmail.com>
Co-authored-by: Aaron Queen <bompus@users.noreply.github.com>
Co-authored-by: Soroush Baraouf <soroushraouf44@gmail.com>
Co-authored-by: fixfon <12512757+fixfon@users.noreply.github.com>
Co-authored-by: Mnigos <makowskiigor@gmail.com>
Co-authored-by: Kevin Rajan <7121943+kvnloo@users.noreply.github.com>
Co-authored-by: Daniyal <115576848+daniyal-sh@users.noreply.github.com>
Co-authored-by: puccez <emanuele.puccetti@icloud.com>
Co-authored-by: Eldar Politkin <32969822+EldarPolitkin@users.noreply.github.com>
Co-authored-by: Tanner Sharon <tannermsharon@gmail.com>
Co-authored-by: lovelycodemonkey <ghassemimahyad@gmail.com>
Co-authored-by: Victor Vogel <263261067+only21mil@users.noreply.github.com>
Co-authored-by: doubletwisted <118005273+doubletwisted@users.noreply.github.com>
Co-authored-by: Sebastian Pugliese <73218979+RealDoc06@users.noreply.github.com>
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
Co-authored-by: Rodrigo Brechard <rodrigobrechard@gmail.com>
Co-authored-by: nekohasekai <i@sekai.icu>
Co-authored-by: Lars Nieuwenhuis <35393046+lnieuwenhuis@users.noreply.github.com>
Co-authored-by: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Co-authored-by: Cameron Chisholm <cam@camchis.com>
Co-authored-by: owenferg <114884536+owenferg@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Robert Nisipeanu <github@nisipeanu.com>
Co-authored-by: Max Weinbach <maxweinbach5@gmail.com>
Co-authored-by: Alb11747 <albertli_042@hotmail.com>
Co-authored-by: Igor Makowski <56691628+Mnigos@users.noreply.github.com>
Co-authored-by: Mike Olson <mwolson@member.fsf.org>
Co-authored-by: Jorrin <43169049+JorrinKievit@users.noreply.github.com>
Co-authored-by: Christopher Ehrlich <ehrlich.christopher@gmail.com>
Co-authored-by: Gabriel De Andrade <gabrieldeandradeleal@gmail.com>
Co-authored-by: brentkelly <d-ai@zeald.com>

@brentkelly brentkelly left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-level summary

This is the T3O-55 epic's integration branch. It brings in its four child PRs: a board-owned engine and event log in boards.sqlite (#179), a board subscription off the orchestration shell (#180), ThreadGateway as the single board→thread seam (#181), and the upstream v0.1.0 merge with ThreadGatewayV2 on orchestration-v2 (#182). The engine and persistence layer looks solid. Event append, projection, receipt and watermark commit in one transaction, the one-time legacy copy is crash-safe and idempotent, board migrations stay on their own ledger, and the subscription's snapshot and resume path has no gap or duplicate. Scopes and MCP registration go through the right seams.

I found 3 critical issues. First, #182 was squash-merged into this branch, so v0.1.0 is not in its ancestry; this needs a graft and a hand --no-ff landing, never the merge button. The other two are in the v2 signal mapping. Every v2 run.created is read as a human turn, so engine wake runs revive stalled and escalated steps. And a Stop pressed before a run has started is read as a recoverable turn end, so the board nudges a card the human just stopped. I also found 4 improvements: a todo stream that dies silently, the untested usage-limit handoff, boards.sqlite pragmas, and a seam inventory that no longer matches the markers. There are 4 nitpicks. I left out the pre-existing ChatView.tsx embedded-chrome re-indent and the full-detail resend in board.subscribeCard, because both already exist on t3o and aren't introduced here.

GitHub can't anchor inline comments on this PR. It changes 16,118 files, because the squashed upstream merge brings in the whole of v0.1.0, and GitHub only resolves paths within the first 3,000 files of a diff. Every finding is below instead, with a permalink to its exact lines at the reviewed commit a2b77461d4. That 3,000-file limit is itself another reason to graft the ancestry (Critical 1): once v0.1.0 is an ancestor, this diff shrinks to the fork's own changes.


📍 docs/t3o/seams.md:286

Caution

Critical (1/3): The v0.1.0 sync was squash-merged into this branch, so upstream ancestry is gone

PR #182 landed on board/t3o-55 as a squash: a2b77461d4 has one parent, and git merge-base --is-ancestor v0.1.0 HEAD is false. This branch's tree is right: it matches #182's original head 830139cb20, which does descend from v0.1.0. Its history is not. If #183 lands like this (or through the merge button, which squashes again), the next git merge main resolves its merge base to v0.0.45 and re-conflicts the whole v1→v2 rewrite. This is the third occurrence after #57 and #110, which the runbook just above this row warns about. The fork's main mirror is also still at v0.0.45, so the runbook's git push origin main v0.1.0 step hasn't run either.

Suggested fix: Before landing, graft the ancestry back onto board/t3o-55 with git merge -s ours v0.1.0 (check git diff HEAD~1 HEAD is empty), or merge 830139cb20, whose tree is identical. Fast-forward main to v0.1.0 and push it with the tag. Then land #183 by hand with git merge --no-ff, never the merge button, with auto-merge off on this card, and check git merge-base --is-ancestor v0.1.0 t3o.


📍 apps/server/src/board/ThreadGatewayV2.ts:207-216

Caution

Critical (2/3): Every v2 run.created counts as a human turn, so engine-started runs revive stalled and escalated steps

v2 emits run.created for runs the engine starts on its own, as well as for human ones: background-work and notification wakes and delegated-task results (Orchestrator.ts:395-423), restart continuations (RestartContinuation.ts:291, on by default), PR-watch wakes (PullRequestWatchReactor.ts:315) and limit-resume: runs. None of these carry a board-minted command id, so all of them become turn-requested{byBoard:false}. handleTurnStartRequested (supervisorReactor.ts:8271) then calls resumeParkedStep, which un-parks stalled, paused and awaiting-input steps and zeroes the stall count, and noteHumanTurn, which gives the step a free turn ending. The resumeParkedStep doc forbids exactly this (the escalation "would quietly un-escalate itself"). Concretely: sweepBackgroundWait escalates a long background wait to stalled, the work finally reports back, v2's wake run starts, and the card goes back to running with no human involved. v1 only fired this signal on thread.turn.start, so this is a regression from the port, and no test covers a wake run.

Suggested fix: In makeSignalReader, return null for a run.created whose payload has workStartedAt, restartContinuationOfRunId, delegatedTaskId or delegatedCompletion. Treat limit-resume: as its own case: it should un-park the usage-limit step but should not count as a human turn. Add supervisor tests for a wake run hitting a stalled step and a paused step.


📍 apps/server/src/board/ThreadGatewayV2.ts:221-236

Caution

Critical (3/3): A human Stop pressed while a run is still starting is read as a normal turn end, and the board nudges the card the human stopped

Orchestrator.markStoppedRun (Orchestrator.ts:8810) returns early when run.rootNodeId or run.providerThreadId is null. A Stop pressed during provider start-up therefore records no run_interrupt_request, so this reader never emits interrupt-requested and the supervisor never pauses the step (T3O-23). The run then ends interrupted or cancelled with startedAt === null. Only failed takes the turn-start-failed branch here, so this becomes a plain turn-ended, and handleTurnCompleted goes to recoverStep and sends a recovery nudge. The window is narrow, but the result is the board overriding an explicit human Stop. I traced the code path; I did not reproduce it.

Suggested fix: Map an interrupted/cancelled run with startedAt === null to interrupt-requested, or have the supervisor treat it as a Stop rather than a recoverable turn end. Add a gateway test for a Stop issued before the run starts.


📍 apps/server/src/board/ThreadGatewayV2.ts:716-723

Warning

Improvement (1/4): The live todo stream stops for good, without a log, on its first error

subscribeTodoLog ends with Stream.catchCause(() => Stream.empty). The tail forks it with Stream.runForEach (boardThreadTail.ts:129-137), so one EventSinkV2Error (for example a decode failure on one stored event) ends the stream quietly and the fiber exits. The persisted cursor stops moving, card-threads deltas stop, card todo summaries freeze until restart, and drain callers wait forever. observe already gets this right through followStoredEvents, which resubscribes from its cursor.

Suggested fix: Reuse followStoredEvents (or the same cursor-and-retry pattern) starting from head, and log the cause. Add a test that injects a stream failure and checks the tail recovers.


📍 apps/server/src/board/ThreadGatewayV2.ts:556-572

Warning

Improvement (2/4): The board-to-v2 usage-limit handoff has no test where recovery is actually armed

The only thing preventing a double resume (the board's own probe plus v2's UsageLimitRecoveryWorker) is the resumeArmed filter in the supervisor (boardWokenLimitParks, supervisorReactor.ts:3632-3640). The contract test only covers the not-limited → false case. The supervisor fake's armLimitRecovery defaults to false (threadGateway.testkit.ts:403), and usageLimitAutoResume.test.ts never sets resumeArmed. Nothing checks that an armed park is skipped by the board's wake and un-parked exactly once by the limit-resume run, and that is the path that decides whether a limited card resumes twice.

Suggested fix: Add one gateway contract test (limited shell → arm → resumeArmed: true and a thread.metadata.update dispatched) and one supervisor test (an armed park is excluded from clearProviderLimitAndResume and the probes, and resumes exactly once).


📍 apps/server/src/board/boardDatabase.ts:149-155

Warning

Improvement (3/4): boards.sqlite misses the sync and WAL-size tuning main gets, so every board commit fsyncs on the main thread and its WAL is never truncated

persistence/Sqlite.ts:82,89 sets synchronous = NORMAL and journal_size_limit before this ATTACH runs. Both pragmas are per-database, so boards ends up with synchronous = FULL and journal_size_limit = -1. The checkpoint worker opens only statev2.sqlite (nodeSqliteClient.ts:531-582), so the board WAL is only checkpointed by the 10,000-frame autocheckpoint backstop (about 40 MB) and never shrinks. Every board commit also fsyncs on the event loop, which is the stall the upstream comment at Sqlite.ts:77-82 exists to avoid.

Suggested fix: After the WAL pragma, also run PRAGMA boards.synchronous = NORMAL and PRAGMA boards.journal_size_limit = <WAL_SIZE_LIMIT_BYTES>, optionally with a periodic PRAGMA boards.wal_checkpoint(PASSIVE). Add a file-backed test asserting the pragmas.


📍 docs/t3o/seams.md:799-802

Warning

Improvement (4/4): The seam inventory claims to match rg "T3o:" row for row, but it is missing about half the markers, and the boot order above is stale

There are about 150 T3o: markers across 70 files, but the table has about 75 rows. Files with markers and no rows include AppSidebarLayout.tsx (3), routes/welcome.tsx, Sidebar.tsx, serviceLauncher.ts (3), VcsProcess.ts (3), config.ts, serverSettings.ts, GitManager.ts and index.css (3). ws.ts has 7 markers and 6 rows (the SupervisorReactor resolution at ws.ts:1289 is missing), and ChatView.tsx has 6 markers and 2 rows (the embedded-chrome seam is unlisted). Separately, lines 133-136 say persistence/Layers/Sqlite.ts boots reconcileLegacyBoardLedger() → runMigrations() → runBoardMigrations(). v0.1.0 deleted persistence/Layers/; the seam is now persistence/Sqlite.ts:37-43, starting with initialiseBoardDatabase(). The next sync's operator uses this table to detect dropped seams, so missing rows will hide exactly the drops it exists to catch.

Suggested fix: Regenerate the rows from git grep -n "T3o:" over upstream-owned files, or reword this paragraph so the per-file git grep -c is the authority. Fix the boot-order path and sequence.


📍 apps/server/src/board/ThreadGatewayV2.ts:324-339

Note

Nitpick (1/4): Most v2 refusals are logged as failures, with a generic message

v2 rejects most invalid commands with OrchestratorDispatchError, whose cause is usually a plain string. That tag isn't in REFUSAL_TAGS, and describe ignores string causes. So something like "Thread … is not active" becomes a ThreadCommandFailedError warning with a generic message instead of a debug-level refusal that names the reason.

Suggested fix: Have describe fall back to a string cause, and decide explicitly whether OrchestratorDispatchError counts as a refusal.


📍 apps/server/src/board/boardSubscription.ts:99-104

Note

Nitpick (2/4): An unarchived card's todo strip doesn't come back until its todos change or the client reconnects

Archived cards' threads are left out of the snapshot, and archiving clears the card's thread entries on the client (boardReducer.ts:371-378). This refetch only fires for card-thread-linked/unlinked, so after board.card-unarchived the card has no thread entries until something else touches them.

Suggested fix: Add board.card-unarchived to this condition.


📍 apps/server/src/board/boardDatabase.ts:64-70

Note

Nitpick (3/4): The relocation-conflict recovery message names state.sqlite, but the main database is now statev2.sqlite

Under v2 the main connection is statev2.sqlite (config.ts:158). A user who follows this message drops tables from the v1 file, statev2.sqlite keeps its board tables, and boot keeps refusing.

Suggested fix: Name the real main file, which PRAGMA database_list already gives you a few lines below.


📍 apps/server/src/board/ThreadGatewayV2.ts:619-621

Note

Nitpick (4/4): sessionLive is true for any thread that has ever run, and turn-ended.errorMessage is always null

activeProviderThreadId stays set once a thread has run, so worktree retire sends thread.stop to every thread in the worktree. That's harmless, but the field no longer means what its name says. Separately, errorMessage: null (line 235) leaves the supervisor's comments about Grok quota refusals arriving through it (supervisorReactor.ts:8767-8770, 6145-6149) stale, since detection now uses lastErrorClass.

Suggested fix: Base sessionLive on activityRunStatus alone (or rename the field), and update the stale supervisor comments.

brentkelly and others added 6 commits October 20, 2026 20:12
#182 landed the v0.1.0 sync as a squash (a2b7746, one parent), so its
tree contains v0.1.0 but v0.1.0 was not an ancestor. This -s ours merge
changes no file and records the missing parent, so the next sync's
merge-base is v0.1.0 (docs/t3o/seams.md, "If a sync was squashed anyway").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rt Stop reads as a Stop

v2 run.created was read as a human turn for every run the engine starts
itself (background and steer wakes, restart and provider continuations,
delegated results, PR-watch wakes), so they un-parked stalled, paused
and awaiting-input steps and bought a free ending. Those now raise no
signal, and v2's usage-limit resume raises limit-resumed, which wakes
only a usage-limit park.

A Stop pressed before a run's provider started records no interrupt
request in v2, so it arrived as a bare turn end and the card was
nudged. A human-stopped unstarted run now raises interrupt-requested
ahead of its turn end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ain's WAL pragmas

subscribeTodoLog swallowed its first stream error and ended, freezing
todo summaries. It now follows the log through followStoredEvents from
the head it reads, resubscribing after the last entry on failure.

synchronous and journal_size_limit are per-database, so the attached
boards file ran with synchronous=FULL and an unbounded -wal. It now gets
NORMAL and main's 32 MiB limit.

Adds a supervisor test that a limit armed with v2 is resumed exactly
once, by the engine, and never by the board's probe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- An OrchestratorDispatchError the engine raises with a stated reason is
  a refusal, logged in its own words, not a failure with generic text.
- An unarchived card refetches its thread summaries, which its archive's
  card-removed dropped.
- The relocation-conflict message names the real main file
  (statev2.sqlite), not state.sqlite.
- sessionLive reads the thread's attached provider sessions, not the
  provider thread binding that outlives them, so retire stops only live
  sessions.
- A failed turn end carries the provider's failure text from v2's error
  turn item, so the usage-limit classifier sees Grok's quota refusal
  again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…persistence/Sqlite.ts

The inventory had 75 rows for 150 markers across 70 upstream-owned
files; it now has 129, one per seam. The migration-lineage paragraph
cited the deleted persistence/Layers/Sqlite.ts and the pre-v2 boot
order. The census figure was 71 files, a miscount.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brentkelly

brentkelly commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner Author

C1 (v0.1.0 ancestry lost) — fixed in ea429cb (git merge -s ours v0.1.0, first commit after a2b7746). git diff HEAD~1 HEAD is empty and git merge-base --is-ancestor v0.1.0 HEAD prints ok. Per the runbook in docs/t3o/seams.md, landing is still by hand: git merge --no-ff board/t3o-55 into t3o, then git push origin main v0.1.0 (main fast-forwards to v0.1.0; checked). I did not push main or the tag: that's the landing step, not this triage.

@brentkelly

Copy link
Copy Markdown
Owner Author

C2 (engine wake runs read as human turns) — fixed in 72c70d1. A new runOrigin in ThreadGatewayV2.ts classifies each run.created. Engine runs raise no signal: workStartedAt, restartContinuationOfRunId, delegatedTaskId or delegatedCompletion set, or a command:, provider-continuation: or server:pr-watch: command id. A limit-resume: run raises a new limit-resumed signal. The supervisor handles it with resumeParkedStep(…, { usageLimitOnly: true }), which wakes only a stalled/usage-limit park and records no human turn. Tests: ThreadGatewayV2.test.ts › "raises nothing for a run the engine started to carry on its own work" and "reads the engine's limit resume as its own signal". usageLimitAutoResume.test.ts › "the engine's limit resume wakes only the usage-limit park, and buys no free ending". That last one fails on the old behaviour, which woke the awaiting-input step and left the limit park alone.

@brentkelly

Copy link
Copy Markdown
Owner Author

C3 (Stop before run start read as a turn end) — fixed in 72c70d1. When a run ends interrupted/cancelled with startedAt === null, the stop came from a human command, and no run_interrupt_request item was seen for that run, the reader now emits interrupt-requested ahead of turn-ended. That's the same order a running turn produces, so pauseStepForHumanStop parks the step before the end is classified. The board's own interrupts still yield only turn-ended, because the board already tracks them. The reader now returns an array per event. Tests: ThreadGatewayV2.contract.test.ts › "a human's Stop before the run started reads as a Stop, not a bare turn end" runs on the real engine and fails on the old reader, which returned only turn-ended. Also ThreadGatewayV2.test.ts › "does not repeat a Stop the engine already recorded".

@brentkelly

Copy link
Copy Markdown
Owner Author

I1 (todo stream ends silently) — fixed in 211f8b4. subscribeTodoLog now goes through followStoredEvents, which logs and resubscribes from the last entry on a failed read. It gained an optional startAfter so it stays pinned at the head read at subscribe time. Test: ThreadGatewayV2.test.ts › "starts after a given cursor and resubscribes from the last event".

@brentkelly

Copy link
Copy Markdown
Owner Author

I2 (no armed-recovery test) — fixed in 211f8b4 with usageLimitAutoResume.test.ts › "a limit armed with the engine is resumed once, by the engine, never by the board". A thread fails on a named-reset limit and armLimitRecovery marks it resumeArmed. At the reset time the board's probe leaves the park alone and sends nothing. The limit-resumed signal then wakes the step, still with no board send.

@brentkelly

Copy link
Copy Markdown
Owner Author

I3 (boards.sqlite pragmas) — fixed in 211f8b4. attachBoardDatabase now sets PRAGMA boards.synchronous = NORMAL and boards.journal_size_limit to main's 32 MiB after the attach. Test: boardDatabase.test.ts › "gives the board file main's WAL durability and size limit", file-backed. It fails without the change (expected 2 to equal 1).

@brentkelly

Copy link
Copy Markdown
Owner Author

I4 (seam inventory incomplete; stale boot path) — fixed in 3e9e195. The inventory now has one row per seam: 129 rows, up from 75, covering 41 files that were missing, among them ws.ts, ChatView.tsx, serviceLauncher.ts and VcsProcess.ts. The migration paragraph now names persistence/Sqlite.ts and the real initialiseBoardDatabase() → runMigrations() → runBoardMigrations() order. The census reads 150 markers across 70 files; it said 71, which was a miscount. Also fixed the server-updates.md marker, which said launcher protocol 3 when the code is at 4.

@brentkelly

Copy link
Copy Markdown
Owner Author

N1 (dispatch refusals logged as failures) — fixed in ee61cb0. An OrchestratorDispatchError whose cause is a string is now a refusal; that's how the engine states a precondition, e.g. "Run … is not interruptible.". describe() returns string causes. A wrapped real failure is still ThreadCommandFailedError. Tests: ThreadGatewayV2.test.ts › commandError (both cases).

@brentkelly

Copy link
Copy Markdown
Owner Author

N2 (unarchived card's thread entries) — fixed in ee61cb0. board.card-unarchived now joins the card-threads refetch, since archive's card-removed drops the entries client-side (boardReducer.ts). Test: boardSubscription.test.ts › "gives an unarchived card back its thread summaries".

@brentkelly

Copy link
Copy Markdown
Owner Author

N3 (conflict message names state.sqlite) — fixed in ee61cb0. BoardRelocationConflictError now carries mainFile, read from PRAGMA database_list (e.g. statev2.sqlite), and the message uses it.

@brentkelly

Copy link
Copy Markdown
Owner Author

N4 (sessionLive / errorMessage) — fixed in ee61cb0. sessionLive now reads the thread's attached provider sessions (status not stopped/error) or a live run, not activeProviderThreadId, which outlives the session. Test: ThreadGatewayV2.contract.test.ts › "a thread holds a live session only while one is attached and not stopped", which fails on the old logic after detach. For errorMessage, I fixed the code rather than the comments: the reader keeps each run's v2 error turn-item failure text and puts it on that run's failed turn-ended (v2 emits the item before the failed run.updated, Orchestrator.ts ~8295). The usage-limit classifier sees Grok's refusal again, and I updated the supervisor comment. Test: ThreadGatewayV2.test.ts › "carries the provider's failure text onto the failed turn's end".

@brentkelly

Copy link
Copy Markdown
Owner Author

Adjudication: round 1 (fixes at 3e9e195)

All 11 findings: fix-upheld. I checked each fix in the code, ran the named tests and probed the risky claims.

  • C1, fix-upheld. v0.1.0 (81493ef) is now an ancestor of HEAD via ea429cb, a two-parent merge with an empty tree diff. Pushing main and the tag stays with the by-hand --no-ff landing in the runbook.
  • C2, fix-upheld. runOrigin matches how v2 really mints command ids. Engine runs use command: (restart-continuation, mailbox-accepted, steer-follow-up, start-queued and delegated delivery), provider-continuation: and server:pr-watch:. UsageLimitRecoveryWorker uses limit-resume:. Client commands are bare UUIDs, so human turns still read as human. workStartedAt is set only for wake runs (wakeWorkStartedAt). limit-resumed calls resumeParkedStep(..., {usageLimitOnly:true}), which wakes only a stalled/usage-limit park and records no human turn. Tests cover it in ThreadGatewayV2.test.ts and usageLimitAutoResume.test.ts.
  • C3, fix-upheld. When a human interrupts or cancels a run that hasn't started, the reader emits interrupt-requested and then turn-ended, deduplicated against a recorded run_interrupt_request. The contract test dispatches a real run.interrupt through the v2 stack.
  • I1, fix-upheld. subscribeTodoLog now uses followStoredEvents(..., head), which retries from its cursor. The startAfter test asserts it resubscribes after 5, then after 6.
  • I2, fix-upheld. In the new test the probe leaves the armed park alone. Only limit-resumed wakes it, and the board sends no turn of its own.
  • I3, fix-upheld. The pragmas are set on boards after the attach. I confirmed the new boardDatabase.test.ts case fails against the pre-fix boardDatabase.ts and passes now.
  • I4, fix-upheld. I compared every file with a T3o: marker against the inventory. Each one that exists in v0.1.0 has a row. The only files missing are fork-owned (board/, migrations/, web board/, commandOrigin.ts, boardThreadState.ts). The boot paragraph now names persistence/Sqlite.ts. server-updates.md "protocol 4" matches SERVICE_LAUNCHER_PROTOCOL = 4.
  • N1, fix-upheld. An OrchestratorDispatchError with a string cause is now a refusal and is described in its own words. Unit tests cover both branches.
  • N2, fix-upheld. board.card-unarchived now triggers the card-threads refetch, with a test.
  • N3, fix-upheld. The message names the main file from PRAGMA database_list. This is a nitpick fix and has no test.
  • N4, fix-upheld. sessionLive now reads attached sessions that aren't stopped or errored, not the bound provider thread. The contract test covers attach and detach. A failed run's turn end now carries the text of v2's error turn item.

I ran 5 files, 103 tests: all pass.

@brentkelly brentkelly left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-level summary — round 2 (re-review at 3e9e195)

PR #183 lands the T3O-55 epic: it moves the board onto upstream orchestration-v2 (v0.1.0) through board/ThreadGatewayV2.ts, with board-owned persistence in boards.sqlite. This round re-reviewed the round-1 fix commits (ea429cbb44..3e9e19545e) and re-checked the new run classification against every place v2 mints a message.dispatch command id. The fixes are solid. The ancestry graft, pragmas, todo tail retry, seam inventory, session liveness and failure text all hold. The weak spot is the new runOrigin classifier. It decides "human" by elimination over a hand-kept prefix list, and that list is wrong in both directions. A human's steer follow-up (command:steer-follow-up:) is silenced as an engine wake. An agent-authored server wake (server:delegated-task-question:) is read as a human and revives parked steps. That gives 2 improvements and 1 nitpick, with no criticals. Both improvements have one fix: read createdBy from the run's user message rather than guess from id prefixes. Nothing else blocks.

Status of round-1 findings

# prior finding status
C1 🔴 v0.1.0 sync squash-merged; upstream ancestry lost ✅ closed. ea429cbb44 grafts it with an empty diff. Pushing main and the tag is left to the by-hand landing
C2 🔴 Every v2 run.created read as a human turn ⚠️ partially addressed. Engine wakes on the listed prefixes are now silent, but the classifier misreads steer follow-ups and delegated-task-question wakes (Improvements 1/2 and 2/2)
C3 🔴 Stop before a run starts read as a turn end ✅ closed. One narrow edge remains (Nitpick 1/1)
I1 🟡 Live todo stream stops silently on first error ✅ closed
I2 🟡 Usage-limit handoff has no armed-recovery test ✅ closed
I3 🟡 boards.sqlite misses synchronous/journal_size_limit ✅ closed
I4 🟡 Seam inventory misses markers; stale boot path ✅ closed
N1 🟢 Dispatch refusals logged as failures ✅ closed
N2 🟢 Unarchived card's thread entries not refetched ✅ closed
N3 🟢 Relocation message names state.sqlite ✅ closed
N4 🟢 sessionLive / errorMessage ✅ closed

Findings

GitHub can't resolve inline paths on this PR because the diff is over its 3,000-file limit, so each finding is listed here with a permalink to its line range.

ThreadGatewayV2.ts:184-191

Warning

Improvement (1/2): A human's steer that becomes a follow-up run is read as an engine wake

The blanket command: prefix here also matches command:steer-follow-up:, and the test at ThreadGatewayV2.test.ts:103 pins that. But EffectWorker.ts:280-300 mints that id when a human's steer lands just as the provider turn completes. It re-dispatches the human's own message (createdBy: message.createdBy, creationSource: message.creationSource) as a new start_immediately run. The run is a human turn. It isn't v2 continuing its own work.

So run.created raises nothing, and noteHumanTurn never records the steer. The board then handles the ending of the run the human steered into, sees no human turn, and sends its resume nudge behind the human's message. That is the T3O-17 two-bubble race the turn-requested path exists to prevent. The human's follow-up turn also gets no free ending. A steer at a turn boundary is the common case: people type while the agent finishes.

Suggested fix: Check for command:steer-follow-up: before the blanket command: prefix and classify it as human, then change the test so it expects a human turn-requested. Better still, classify by the run's user message createdBy (run.userMessageId). That fixes this case and Improvement 2/2.

ThreadGatewayV2.ts:200-215

Warning

Improvement (2/2): Server-minted agent messages fall through to "human" and revive parked steps

Any command id that isn't board-minted, limit-resume: or on the engine-prefix list is classified human. v2 has at least one more run-creating server command: DelegatedTaskQuestionReactor.ts:49-64 dispatches message.dispatch with server:delegated-task-question:<key>, createdBy: "agent", creationSource: "server", queue_after_active, to wake a parent thread whose delegated child asked a question. The run is created queued (Orchestrator.ts:5072-5103), and wakeWorkStartedAt is only stamped when a queued run starts (Orchestrator.ts:1672), so none of the run.* fields catch it either.

Result: a card agent that used delegate_task gets turn-requested{byBoard:false} when its child asks a question. resumeParkedStep then un-parks a step a human paused, an escalated stalled step, or one in awaiting-input. noteHumanTurn gives it a free ending. That is the round-1 C2 failure class through a door the prefix list doesn't cover. The default-to-human rule means every future server-minted run id fails the same way.

Suggested fix: Decide "human" positively instead of by elimination. Read the run's user message (run.userMessageId) and treat only createdBy: "user" as a human. Or, more cheaply, treat any server: id that isn't server:board- as engine, and add server:delegated-task-question: to the wake test table.

ThreadGatewayV2.ts:300-306

Note

Nitpick (1/1): The pre-start Stop check mixes the interrupt's command id with the run's own wake fields

runOrigin(stored.commandId, run) gets the id of the command that stopped the run, but it also reads the run's workStartedAt, restartContinuationOfRunId and delegatedTaskId. A human who presses Stop on an immediate wake run (e.g. a restart continuation) before its provider starts gets "engine". No interrupt-requested is emitted, and the card is nudged after a Stop. The window is narrow.

Suggested fix: Classify the stopping command on its own: !isBoardMintedCommandId(id) && !engineCommandId(id). Don't pass the run's wake fields through runOrigin.

A human's steer that v2 re-dispatches as a follow-up run is a human turn,
and a server-minted agent message (a delegated task's question) is not.
The reader holds each run until its message, committed in the same command,
says who wrote it. A Stop before a run starts is classified by the Stop's
own command id alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brentkelly

Copy link
Copy Markdown
Owner Author

Round-2 triage (fixed in 2d1c3ad)

I1 — a human's steer that becomes a follow-up run reads as an engine wake: fixed.
runOrigin no longer decides by command-id prefix. It reads the author of the run's own message: a run is human only when its message is createdBy: "user" from a client, not server, and it carries no wake fields. v2 commits that message right after run.created in the same command (Orchestrator.ts:5171→5219, 5578→5617, 6464→6521), so the signal reader holds each non-board run until its message.updated arrives. command:steer-follow-up: carries the human's createdBy/creationSource over (EffectWorker.ts:302), so it is now a human turn. The board's own steer coming back as a follow-up stays silent, because the board authors as user/server (BOARD_AUTHOR). The test that pinned the wrong behaviour is replaced by "reads a human's steer that became a follow-up run as a human turn", which fails on the old reader.

I2 — server-minted agent messages fall through to human: fixed by the same change. A server:delegated-task-question: run carries an agent message, so it is engine even though it has no wake fields at run.created. That case is added to "raises nothing for a run the engine started to carry on its own work", which fails on the old reader. I chose to classify by author rather than add server: to a prefix list, because the author is the fact we actually need, and the prefix list missed this case once already. "waits for the run's own message, not any message on the thread" pins the pairing.

N1 — the pre-start Stop check mixed the interrupt's command id with the run's wake fields: fixed. The check is now isHumanCommandId(stored.commandId), which reads only the stopping command's id: a bare client UUID is a human, and any command:, provider-continuation:, limit-resume: or server: id is not. A new case in "reads a human's Stop before the run started as a Stop, then the turn's end" (a human cancelling an unstarted workStartedAt wake) fails on the old code.

Checks: ThreadGatewayV2.test.ts 23/23; the whole src/board/ suite, including the engine-driven ThreadGatewayV2.contract.test.ts and usageLimitAutoResume.test.ts, passes 1323/1323 across 90 files. Server typecheck is clean and lint is clean on both files.

@brentkelly

Copy link
Copy Markdown
Owner Author

Adjudication: round 2 (at 2d1c3ad)

I1, steer follow-up read as an engine wake: fix-upheld. runOrigin now classifies a run by its message's author. EffectWorker.ts:302 carries the steer's createdBy/creationSource over to the command:steer-follow-up: dispatch, so a human's steer reads as human. I checked all three run.created sites in Orchestrator.ts (5171, 5578, 6464): each emits message.updated for run.userMessageId later in the same command, so holding the run until its message arrives is sound. The new test fails against the old reader.

I2, server-minted agent messages read as human: fix-upheld. A delegated-task question is sent with createdBy: "agent", so it now reads as engine. Anything not user or not from a client source is treated as engine (creationSource === "server" covers BOARD_AUTHOR and scheduled tasks). The extended test case fails against the old reader.

N1, pre-start Stop mixed the stop's id with the run's wake fields: fix-upheld. The check now uses isHumanCommandId(stored.commandId) on the stopping command alone. The new case (a human cancelling an unstarted workStartedAt wake run) fails against the old code.

Run: ThreadGatewayV2.test.ts and usageLimitAutoResume.test.ts, 60/60 pass. With the pre-fix ThreadGatewayV2.ts swapped in, 4 tests fail.

@brentkelly brentkelly left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-level summary — round 3 (re-review at 2d1c3ad)

This PR is the epic merge that moves the board onto upstream orchestration-v2 (v0.1.0). The board now reaches threads only through board/ThreadGatewayV2.ts, and it has its own engine, subscription and boards.sqlite lineage. This round I re-read the round-2 fix. runOrigin now reads who wrote the run's message, and the signal reader holds each non-board run until that message arrives. I checked this against the engine. All three run.created sites in orchestration-v2/Orchestrator.ts (~5171, ~5578, ~6464) emit message.updated for run.userMessageId in the same command. observe runs a single live reader whose state survives followStoredEvents retries, and it starts at the committed head, so a command's events never split across a subscription boundary. The awaitingMessage map is capped by SEEN_LIMIT. Human client messages are user from web/mobile. Agent/MCP, delegated-task, provider-continuation and restart runs are agent, and limit-resume is caught by its prefix first. The pre-start Stop check now reads only the stopping command's id. ThreadGatewayV2.test.ts passes 23/23 locally. There are no new human comments on the PR since round 2.

Nothing blocks. This round has 0 critical, 0 improvement and 1 nitpick finding.

Status of round-2 findings

# prior finding status
I1 🟡 A human's steer that becomes a follow-up run is read as an engine wake ✅ closed
I2 🟡 Server-minted agent messages fall through to human and revive parked steps ✅ closed
N1 🟢 Pre-start Stop check mixes the interrupt's command id with the run's wake fields ✅ closed

Findings

The diff spans more than 3,000 files, so GitHub cannot anchor inline comments on it. As in earlier rounds, each finding is listed here with a permalink.

Note

Nitpick (1/1): The async-answer run is read as engine only implicitly, and no test covers it

apps/server/src/board/ThreadGatewayV2.ts#L215-L224

message.creationSource === "server" is there to keep the board's own BOARD_AUTHOR messages silent. It also catches v2's async answer. When a person answers a user-input question after its turn has ended, Orchestrator.ts:7546 dispatches message.dispatch with createdBy: "user", creationSource: "server" under the human's command id. That run therefore reads as engine. This looks correct, because the answer already raises input-resolved{answered:true}, which is what un-parks the step. Without a turn-requested, the run's end is then judged normally rather than given a human-turn free pass. But the comment names only the board's messages, and no test pins this path. A future change that narrows the check to BOARD_AUTHOR would turn every late answer into a "human turn".

Suggested fix: Mention the async-answer: message in the comment and add a case to ThreadGatewayV2.test.ts asserting that it raises no turn-requested.

…turn

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brentkelly

Copy link
Copy Markdown
Owner Author

Round 3 triage — N1 (async-answer run read as engine only implicitly): fixed in 9d82ae7.

The creationSource === "server" comment in runOrigin now names both cases: the board's own BOARD_AUTHOR messages, and v2's async-answer: run (Orchestrator.ts, message.dispatch with user/server under the human's command id), which must stay engine because input-resolved already un-parks the step. A case for it (client command id, { createdBy: "user", creationSource: "server" }) is added to ThreadGatewayV2.test.ts "raises nothing for a run the engine started to carry on its own work". No behaviour change, so the test passes before and after; it pins the classification. ThreadGatewayV2.test.ts 23/23 pass.

@brentkelly
brentkelly merged commit fed7cc3 into t3o Oct 20, 2026
1 of 2 checks passed
brentkelly pushed a commit that referenced this pull request Oct 20, 2026
chore(sync): port the board onto upstream orchestration-v2 (v0.1.0) (T3O-55)

Re-lands #183 as a merge commit. The PR was squash-merged by mistake
(fed7cc3, one parent), which dropped the v0.1.0 ancestry the next
`git merge main` relies on (docs/t3o/seams.md). This commit has the same
tree as the squash; it only restores the second parent.

Co-Authored-By: Claude Opus 5.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.