Repository navigation
fix(ui-server): commit local exchanges, classified blocks and saved worker preferences on the write worker - #3011
Merged
Conversation
…overs-offloop-044
…overs-offloop-044 # Conflicts: # docs/decisions/http-api-boundary.md # docs/decisions/session-principals.md # docs/decisions/sqlite-write-worker.md # docs/plans/async-collaboration-requirements.md # docs/plans/async-collaboration.md # docs/plans/navigation-recovery.md
…overs-offloop-044 # Conflicts: # docs/decisions/sqlite-write-worker.md
…overs-offloop-044 # Conflicts: # docs/decisions/governed-writable-workers.md # docs/decisions/http-api-boundary.md # docs/decisions/session-principals.md # docs/decisions/sqlite-write-worker.md # docs/plans/async-collaboration-requirements.md # docs/plans/async-collaboration.md # docs/plans/navigation-recovery.md
schlessera
marked this pull request as ready for review
October 26, 2026 11:23
This was referenced Oct 26, 2026
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 #2996
Refs #2850
Eighth slice of #2850: local exchanges, a turn's classified blocks and classifier confidence, and a saved worker preference.
What changed
The hosted server no longer commits these on its app connection. They now commit on the
ui.dbwrite worker atsynchronous=FULL, each the statement or transaction that ran inline:packages/ui-server/src/ws/session-catalog-writes.ts).packages/ui-server/src/classification/classification-writes.ts).PUT …/profile): checked and stored in one immediate transaction (saveProfile,packages/ui-server/src/ws/session-profile-writes.ts).What callers wait for:
WsHost.historySettled()) waits for every classifier write submitted before it, as it already did for the catalog and policy proposals.SessionProfileErrorwith status 401, 404, 409 or 507) keep their class and status across the worker.Retained retries, drafts, policy proposal inserts and saves, voice transcription claims and settings still commit on the app connection. They are the next slices.
Why this shape
A local exchange is recorded on one message and carried by the next prompt. Committing it later must not let a prompt miss it or carry it twice. So the catalog keeps the pending exchange in memory and serves it to the next prompt, the way it already does for other submitted catalog writes. The saved preference's checks (session known, origin, policy profile, draft cap) read the rows the write depends on. They run inside the same immediate transaction on the worker. The brain's policy profile per origin is resolved where the request arrived and passed in as data.
Proof
Head
a907f62a0. The work commitse8080257d…4bf8cc0ff; merges oforigin/main71503546f(#2992),e69ade405(#2994) andc47483196(#3005, #3006), with doc conflicts only, resolved; and a docs-only commit (a907f62a0) that removes a duplicated "other writers" paragraph. Every heavy run went through the host slot wrapper. The suites ran on7d55f72fc, which is this head minus that docs commit.check:prran again on this head.bun run check:pr --base origin/mainona907f62a0(and on7d55f72fc)bun run test ./testson7d55f72fcbun run test packages/ui-serveron7d55f72fcws-turn-budgetshost-token-fallback cases, filed as #3009. That timing race also fails on main: 2 of 22 alone onc47483196, against 6 of 33 on this branch; the gap is not significant at these counts. Nothing in this branch touches budgets.app-writes-offloop,classification,ws-local-exchange, the retry/turn-failure replays, dispatch, governance-policy, turn-lifecycle,api-surfacetsc --noEmit,bun run lint,decision-citationsprove-native-host.ts, all four cells, underflockon the fixture lock,dd … conv=fsyncwriters outside the lock) on7d55f72fcAn earlier ui-server run, on
fcb02e5ea, failed onesaved-transcriptiontiming case (a fixed 20 ms wait under host load). It passed 3/3 alone and in the run above.Focused tests. The app connection refuses writes to the watched tables. The worker refuses any commit below FULL.
app-writes-offloop.test.ts:classification.test.ts: with a write worker, a pass commits its blocks and confidence there, settles once its blocks have, and logs no write failure.Mutations (each applied, its named test run, restored)
APP_CONNECTION_WRITE local_exchangesEXCHANGE_PENDING_TAKEN_BY_NEXT_PROMPTEXCHANGE_TAKEN_ONCECLASSIFY_WORKER_WRITES_DID_NOT_FAIL(APP_CONNECTION_WRITE classification_confidence,… message_blocks)CLASSIFY_BLOCKS_COMMITTED_BEFORE_RUN_SETTLESCLASSIFY_WORKER_WRITES_DID_NOT_FAIL(SQLITE_WRITE_BELOW_FULL)HISTORY_WAITS_FOR_PENDING_CLASSIFIED_BLOCKSAPP_CONNECTION_WRITE session_worker_profilesPROFILE_REFUSAL_CLASS_KEPTK1 and K3 first failed only on a missing block: the pass turns a write failure into a log line. The test now asserts that log is empty first, so the failure names its cause.
Lag attribution (#2416 method with the turn wait joined, as on #2884–#2994; diagnostic not committed). Native pi UID 1000 cell, 3 fsync writers, 3 runs each, before (
origin/main2d4671ac1) and after (this change).local-exchanges.ts,classify-turn.ts/blocks-store.ts/confidence-store.tsorsession-profiles.ts, so no window is attributed to them.readywere the Activity notifier's tick (431 ms, its own commit; moved in fix(ui-server): commit the Activity notifier and push bookkeeping on the write worker #2994) and, before, the governed-admission records (253 and 472 ms; moved in fix(ui-server): commit a governed turn's admission records on the write worker #3005).So this slice's evidence is the focused tests and mutations above, not the native cell.
Checklist
.changeset/app-writes-chat-leftovers-off-event-loop.md)docs/reflect the change, or nothing documented moved. (docs/decisions/sqlite-write-worker.md; shifted and re-pointed citations.)docs/decisions/provider-history.md): the provider-history guard passes, or nothing on a turn's path changed.Anything left open
The remaining groups are the open sub-issues of epic #2850.
🤖 Generated with Claude Code