Repository navigation
fix(ui-server): publish tracker completion after Activity commits - #2983
Merged
Merged
Conversation
2 tasks done
schlessera
marked this pull request as ready for review
October 26, 2026 05:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2900
What changed
The hosted session runner now waits for terminal Activity writes before sending its final recovery notification. Work that finishes while its conversation is unwatched reaches the tracker with the host's completion time and survives reload and seen observation. The affected product surface is ui-server session recovery for all backends. The tracker fixture, its 15-second waits, every existing assertion and all ui-react product code are unchanged.
Why this shape
A first-parent
git bisect run, using only the original 320px dark/coarse-pointer cell in offline real Chrome, identified b192ca0 (#2830) as the first bad main commit. Its parent 1f032f9 passes. There were no skipped revisions. The write-worker conversion maderecorder.finish()asynchronous but placed the commit wait after the finalidleframe.The captured terminal/catalog/WebSocket/store/view timeline shows
resultreaching the browser: the tracker becomesdonewithendedAt === null. The catalog names the correct request, revision and turn. The result-triggered recovery read returns running, and the final idle-triggered read returns unknown before the terminal rollup commits. The database later contains the successful terminal rollup and end time, but no subsequent notification requests another read.const rollup,packages/ui-server/src/ws/recovery.ts:133-144).const ended,packages/ui-react/src/lib/tracker-client.ts:323-330); an in-flight read queues a follow-up read.releaseActiveAuthorization();,packages/ui-server/src/ws/run-session.ts:945-956).This restores D52 Recovery A without new polling, inferred timestamps, wire shapes or schema changes. The SQLite write-worker decision records the ordering requirement. A patch changeset names
@schlessera/brain-ui-server; no compatibility contract changes.Proof
Base c670449. Head 16dc0c7.
tracker-runtime.test.ts:167on base and every bisected bad revision; the verified good parent passes. Both new held-commit success/error server cases fail before the fix onACTIVITY_RECOVERY_IDLE_AFTER_COMMITTED_TERMINAL: an early scoped idle exists while the write is held.packages/ui-server/src/ws/run-session.tsmakes both new held-commit cases fail on the same named assertion../tests: head 1966 pass / 0 fail, 74408 assertions, 179 files, 667.16 seconds. Base 1966 pass / 0 fail, the same assertions and files, 657.81 seconds. Both exit 0. The head control uses the same checkout and unchanged commit as the earlier failed head run.policy-write-boundary.test.ts:285(tests: policy-write-boundary claude governed native Write intermittently refused as not governed #2899), Pi interactive scratch-access assertion at line 80 (tests: Pi interactive policy boundary intermittently reports scratch unwritable #2987), and Pi governed shell-visibility assertion at line 287 (tests: Pi governed policy boundary intermittently misses the shell write #2988) remain recorded. Isolated complete policy files also pass 19/19 on head and base. There was no code/assertion/timeout change between the failed and passing head runs. The passing controls establish variability, not a cause or a repair of those failures.bun run check:pr --base origin/mainpasses on final head: 755 pass / 0 fail, 5033 assertions. Typecheck (bunx --no-install tsc --noEmit) passes. Pending changeset validation andbunx --no-install changeset statuspass; the lockstep release plan remains minor.61d679149has identical product/native inputs; the only later edit makes a new test's optional method call type-safe. A fresh Claude UID 1000 cell passes on final head16dc0c753, including complete cleanup.Completed package/root/preflight proof used the coordinator's shared heavy-command wrapper. The coordinator authorized the remaining #2900 broad-proof commands to run sequentially outside that wrapper as one temporary third slot; the additional unchanged-head root control also ran sequentially. Real Chrome, offline namespaces, capability drops and every guard remained enabled. Native proofs used the separate exclusive native lock. Completed logs and exit receipts are stored in persistent storage; reboot-interrupted attempts and queue cancellations are retained separately and are not counted as completed test proof.
The completed broad-command sequence was:
Checklist
docs/reflect the change, or nothing documented moved.docs/decisions/provider-history.md): the provider-history guard passes, or nothing on a turn's path changed.Anything left open
All required local proof is complete. Earlier continuity (#2932), liveness (#2986), storage-refusal (#2989) and policy-boundary (#2899, #2987, #2988) failures remain retained observations. The complete final-head UI and root controls pass; passing controls do not claim to repair or close those follow-ups.
The coordinator owns ready-candidate hosted CI, readiness and merge. The first checklist item remains open for that hosted proof. Leave this PR draft.