Skip to content

CONTRACT: fix(ui-server): commit inbox lifecycle writes on the write worker - #3014

Merged
schlessera merged 7 commits into
mainfrom
fix/3001-inbox-lifecycle-offloop-044
Oct 26, 2026
Merged

schlessera merged 7 commits into
mainfrom
fix/3001-inbox-lifecycle-offloop-044

Conversation

@schlessera

@schlessera schlessera commented Oct 26, 2026 •

Copy link
Copy Markdown
Owner

Closes #3001
Refs #2850 (epic)

What changed

The hosted inbox now commits intake receipts and replay, lifecycle recovery and budget reconciliation, maintenance and heartbeat, cleanup claims and acknowledgements, interactive-share records, decisions, and hygiene review on the app's SQLite write worker at synchronous=FULL. The transaction bodies keep their reads, mutations and change records together. Intake and decisions answer after commit, share tools queue behind their session bind, share start precedes backend execution, and share end precedes slot release. Boot stops before sweep or heartbeat if recovery fails.

The missed @internal tags on WsHost.inboxTools and WsHostOptions.inboxTools now implement the existing #1053 narrowing ruling. The integration-contract paragraph names inboxTools explicitly, the API report is regenerated, and the patch changeset names the narrowing. The concrete inbox helpers now await their commits.

Why this shape

This uses the procedure-module pattern from #2933, #2949, #2992 and #2994 and the decision in docs/decisions/sqlite-write-worker.md. Every procedure uses the shared queue, preserving commit order with journal and Activity. Standalone callers use the same code inline. File staging/application remain outside SQLite writes; completion still verifies authoritative receipts and bytes under the application lock. Disposable share lifetime locks stay held through unwind. Refusals keep their classes across the worker.

The native timing wrappers also await the asynchronous bridge. The intake failure fixture installs its trigger in the shared schema, so it still injects a failure on the write connection. Moved source citations are updated.

Proof

Current candidate e77b90c2f9dd6682234f4ba426d44fb08d392a91; merged base 07fbce896099b87903bb994e3b6a939f232669fd. All package/root/preflight/pack commands use the shared two-slot heavy wrapper; native fixture commands use the exclusive native lock. The refresh preserves main's notice/local-exchange paragraphs and limits the remaining-writers edit to inbox.

  • Failing first on the original tree: intake, interactive-share start and heartbeat fail with APP_CONNECTION_WRITE; 0 pass / 3 fail. Boot ordering separately fails INBOX_FAILED_RECOVERY_SUBMITS_NO_LATER_WRITES before the ordering correction. These are behavioral failures, not module-load failures.
  • Refreshed focused placement/order/refusal/rollback tests: 11 pass / 0 fail, 37 assertions. Citation tests on the merged source: 44 pass / 0 fail, 113 assertions. This includes concurrent submissions with a rejected transaction, gap-free global/thread sequences, shared Activity ordering, commit-before-reply behavior, and a held worker that leaves the app loop responsive.
  • Refreshed keyless provider-history files: 31 pass / 0 fail, 259 assertions, both backends; no expectations changed.
  • Refreshed TypeScript: bunx --no-install tsc --noEmit passed.
  • Refreshed package suite: passed on the current head through heavy.sh, 4113 pass / 0 fail, 28462 assertions, 346 files, 2114.01 seconds. The complete current-main comparison (07fbce896099b87903bb994e3b6a939f232669fd) finished 4101 pass / 1 fail, 28421 assertions, 345 files, 2029.88 seconds. Its sole failure is the unchanged private-index advance test (private-index-cache.test.ts:647), filed as tests: private-index advance returns an empty rebound result in the package suite #3016; the shared-storage identity case passed.
  • Refreshed root suite: passed on the current head through heavy.sh, 1966 pass / 0 fail, 74416 assertions, 179 files, 788.96 seconds.
  • Refreshed bun run check:pr --base origin/main: passed on the current head through heavy.sh, including lint and 1204 fast invariant tests (0 fail, 7919 assertions, 83.51 seconds).
  • Refreshed pack smoke: passed on the current head through heavy.sh; every publishable package packed, installed and passed its consumer probes.
  • Refreshed native full matrix: passed on the current head in 389.61 seconds, all four backend/UID cells, expected retained-index negative control and all five owned cleanup receipts. Refreshed worker identity: passed on the current head with owned cleanup (0 records, source directories or workers retained). No completed native run reported the leaked-loop-device error, and no native proof was killed.

Completed receipts from previous candidate b9b7d099ba362838f22407902c9d3a4b9fc70cc6, base c47483196f02c1098128207ce68c4f0de6b571ab (retained separately from the refreshed proof):

  • Full UI-server suite: 4094 pass / 1 fail, 28408 assertions, 345 files, 2251.88 seconds. The sole failure is STORAGE_SHARED_ONE_COLLECTION in the unchanged shared-storage scan test. That complete file passes on the same head and base: 7 tests / 38 assertions each. Attribution is filed as tests: shared storage scan intermittently returns two collection IDs #3013. The complete current-main control passed the shared-storage case; its sole unrelated failure is in tests: private-index advance returns an empty rebound result in the package suite #3016. The refreshed-head package suite passes all 4113 tests; no storage implementation or fixture change was made. All 11 new inbox tests pass within the full run.
  • Full root suite: the unchanged-head retry passes 1966/1966, 74414 assertions, 743.99 seconds. The initial run remains failed: 1963 pass / 3 fail at AREAS_WRITABLE in Claude interactive/voice and pi interactive policy-boundary cases. The complete focused file passes on both head and base (19 tests, 587 assertions each); the complete base root suite passes 1966/1966 (74412 assertions). The unchanged full-head retry establishes variability, not a repair or cause. Receipts are recorded in tests: full suites intermittently lose writable scratch #3010, with the narrower observations in tests: the voice policy-write boundary case reports the scratch area unwritable #2923 and tests: Pi interactive policy boundary intermittently reports scratch unwritable #2987.
  • Preflight passed: lint plus 1204 fast invariant tests, 0 fail, 7919 assertions. Pack smoke passed: every publishable package packed, installed and passed its consumer probes. Pending changeset validation passed; the lockstep plan is minor, with no majors.
  • Native full no-load matrix: all four backend/UID cells, the expected retained-index negative control and all five owned cleanup receipts passed (413 seconds).
  • Native full under three fsync writers: all four backend/UID cells, the expected negative control and all five cleanup receipts passed (459 seconds).
  • Native worker identity passed with owned cleanup (no retained records, sources or workers). Apply-timing passed on pi UID 1000. Observation-timing recorded all 220 calls with zero tool errors, then failed the final automatic-index drain with NATIVE_AUTOMATIC_INDEX_COMPLETED: stale maintenance output: .brain-index/brain.db-shm, the existing ui-sdk: native index publication intermittently refuses changed database or sidecar stamps #2878 failure. The same command fails on base c47483196f02c1098128207ce68c4f0de6b571ab with the exact error (114.2 seconds vs 118.6 seconds on the branch); both diagnostic commands remain recorded as failed.

Mutation receipts, rerun on current head e77b90c2f9dd6682234f4ba426d44fb08d392a91 (all nonzero exits with the actual named error line verified, then restored):

  • M01: all inbox procedures on the app connection → APP_CONNECTION_WRITE.
  • M02: intake preparation below FULL → SQLITE_WRITE_BELOW_FULL.
  • M03: share start below FULL → SQLITE_WRITE_BELOW_FULL.
  • M04: share receipt below FULL → SQLITE_WRITE_BELOW_FULL.
  • M05: heartbeat below FULL → SQLITE_WRITE_BELOW_FULL.
  • M06: cleanup claim below FULL → SQLITE_WRITE_BELOW_FULL.
  • M07: decision below FULL → SQLITE_WRITE_BELOW_FULL.
  • M08: hygiene command below FULL → SQLITE_WRITE_BELOW_FULL.
  • M09: thread sequence increments twice → INBOX_THREAD_SEQUENCES_GAP_FREE_AFTER_ROLLBACK.
  • M10: intake refusal loses its class → INBOX_REFUSAL_CLASS_KEPT.
  • M11: boot phases continue after recovery fails → INBOX_FAILED_RECOVERY_SUBMITS_NO_LATER_WRITES.
  • M12: lease recovery below FULL → SQLITE_WRITE_BELOW_FULL.
  • M13: periodic sweep below FULL → INBOX_TICK_COMMITTED_BEFORE_REPLY.
  • M14: cleanup acknowledgement below FULL → INBOX_CLEANUP_COMMITTED_BEFORE_REPLY.
  • M15: share completion below FULL → SQLITE_WRITE_BELOW_FULL.
  • M16: share bind below FULL → SQLITE_WRITE_BELOW_FULL.
  • M17: share end below FULL → SQLITE_WRITE_BELOW_FULL.

Native pi UID 1000 lag comparison under three dd ... conv=fsync writers, with CPU profiles and app-turn/worker timing joined: before source e69ade405 plus timing probes had no window ≥250 ms after ready; after source 62919c407 plus the same probes had one at 273 ms. The latter has no app-connection turn wait over 20 ms, overlaps a worker upsertSession lasting 288 ms, and has no inbox frame samples; it remains unattributed. This is not a claim of overall lag improvement. These ordinary native turns invoke no-op share start/bind checks and a boot heartbeat, but create no bound share, intake, hygiene review or expired inbox lease after ready; this cell cannot qualify nonempty lifecycle traffic. The focused worker tests exercise those writes directly.

Checklist

  • Focused local preflight/behavioral receipts and complete selected hosted proof passed; actual head/base/job checkout evidence is in Proof.
  • Acceptance criteria from the issue are all met, or the gap is named below.
  • The PR and completed/advanced issues have verified release milestones under docs/process/github.md.
  • A changeset is included, or this changes nothing a consumer can see.
  • No personal data anywhere in the diff — the leakage gate covers the whole tree.
  • The integration contract index or relevant component is updated in this same commit, or nothing in the machine surface moved.
  • Docs under docs/ reflect the change, or nothing documented moved.
  • Provider history stays append-only (docs/decisions/provider-history.md): the provider-history guard passes, or nothing on a turn's path changed.

Anything left open

Draft only. All required local proof passes on the current head. The completed current-main package control has one unrelated failure in #3016. Refreshed root, preflight, pack smoke, native matrix, identity and focused proof pass. The coordinator owns readiness, complete selected CI confirmation and merge. Scratch variability remains in #3010, storage collection attribution in #3013, the current-main private-index advance observation in #3016, and the optional observation-timing index-publication failure in #2878. Native nonempty-inbox traffic is outside the existing hosted cell; no general performance guarantee is claimed.

…cle-offloop-044

# Conflicts:
#	docs/decisions/scheduled-tasks.md
#	docs/decisions/sqlite-write-worker.md
# Conflicts:
#	docs/decisions/http-api-boundary.md
#	docs/decisions/session-principals.md
#	docs/decisions/sqlite-write-worker.md
#	docs/decisions/voice-permission.md
#	docs/plans/async-collaboration-requirements.md
#	docs/plans/async-collaboration.md
#	docs/plans/navigation-recovery.md
@schlessera schlessera added this to the 0.44.0 milestone Oct 26, 2026
@schlessera schlessera added type: fix Something is broken and must behave the way it is documented. area: ui-server Hono app factory: turn coordinator, auth, session catalog, routes, cron. contract Touches the integration contract: CONTRACT: commit, contract doc updated in the same commit. labels Oct 26, 2026
@schlessera
schlessera marked this pull request as ready for review October 26, 2026 14:16
@schlessera
schlessera merged commit b99489b into main Oct 26, 2026
19 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ui-server Hono app factory: turn coordinator, auth, session catalog, routes, cron. contract Touches the integration contract: CONTRACT: commit, contract doc updated in the same commit. type: fix Something is broken and must behave the way it is documented.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ui-server: the inbox lifecycle commits on the write worker

1 participant