Repository navigation
Description
Part of epic #2850.
Context
Action notices (createActionNotifier, packages/ui-server/src/inbox/notify.ts:271) commit on the app connection. Most of these run on the Activity runtime's 20-second tick (packages/ui-server/src/activity/runtime.ts:136):
enroll,packages/ui-server/src/inbox/notify.ts:361-391(frompackages/ui-server/src/activity/runtime.ts:153);generateDue→generate,:507→:295-352(from:154);- the delivery attempt's
beginAttempt:400-456andfinishAttempt:463-465, fromdeliverActions,packages/ui-server/src/activity/push-sender.ts:332and:373.
Outside the tick:
recordActionEpisode(:207-223) runs inside inbox store commits (packages/ui-server/src/inbox/store.ts:451,:748);digest,dismiss,reportZoneandsetDeviceZone(:493,:497-503,:469-480,:288-291) are reached frompackages/ui-server/src/routes/activity.tsandpackages/ui-server/src/routes/push.ts.
The Activity notifier and push bookkeeping moved in #2994; this is the action half of the same tick.
Scope
Move the tick's enrollment, digest generation and delivery attempt writes, and the route writes, to the write worker. A delivery pass settles only once its attempt state has committed. recordActionEpisode moves with the inbox store commits that carry it, which are a separate sub-issue: say in the PR which side it landed on.
Out of scope
Inbox store mutations themselves (inbox lifecycle sub-issue).
Acceptance criteria
- Each moved write commits on the
ui.dbwrite worker (openSqliteWriter,packages/ui-server/src/db/write-worker.ts) atsynchronous=FULL, keeping its transaction contents and its order relative to journal, Activity and the other worker writes. - A focused test fails if a moved write runs on the app connection (
APP_CONNECTION_WRITEviawatchWriteSites,packages/ui-server/tests/helpers/write-sites.ts) or commits below FULL (SQLITE_WRITE_BELOW_FULL). Mutation receipts in the PR show each guard failing on its named assertion. - Every reader that relied on the write having committed when the call returned still sees it (awaits the write, or waits for writes submitted before it). Refusal classes are kept across the worker.
- Before/after receipts on the native pi UID 1000 cell under three
dd … conv=fsyncwriters (tests: attribute recurring one-second native hosted health timeouts #2416 method, turn wait joined): no window ≥ 250 ms afterreadyattributed to a moved writer, or a recorded reason the cell does not exercise it.
Verification
bun run test packages/ui-server, bun run test ./tests, bun run check:pr --base origin/main, and the native hosted proof (scripts/prove-native-host.ts) under the fixture lock, with and without the fsync load.
Contract impact
None expected. A retyped public signature (api-report/ui-server.txt) needs the classification in docs/integration-contract/package-api.md checked first.
Activity
schlessera commentedon Oct 26, 2026
Claim
bk3000-action-notices-20261026: implementing this slice onfix/3000-action-notices-offloop-044, using the merged write-worker procedures as the pattern. Will hand off a draft PR with worker/FULL mutation receipts and native proof; the coordinator owns readiness and merge.