Skip to content

fix(mailbox): serialize completion registration writers - #264

Draft
nestharus wants to merge 3 commits into
mainfrom
age-300-registration-contention
Draft

fix(mailbox): serialize completion registration writers#264
nestharus wants to merge 3 commits into
mainfrom
age-300-registration-contention

Conversation

@nestharus

@nestharus nestharus commented Aug 12, 2026

Copy link
Copy Markdown
Owner

What this means

Completion registrations that share one pid-identity.db now serialize before writable sidecar open. If registration-lock polling or SQLite busy waiting exhausts the shared contention-wait budget, the requested command still does not launch.

This does not promise that the complete registration operation finishes within five seconds. Synchronous setup, SQLite execution, filesystem I/O, and commit durability are not deadline-interruptible.

What's broken

Seven preserved production completion registrations across three provider sessions exited 74 before workload launch under concurrent activity; reduced-concurrency or isolated retries succeeded.

The retained failures do not identify the exact SQLite statement, extended error code, lock holder, admission order, or installed source identity. Shared pid-identity.db contention in the mutable registration path is the strongest supported failure class, but exact historical causation remains unknown.

What this PR does

  • Adds a cross-process file lock in crates/oulipoly-state/src/mailbox.rs so completion registrations for one pid-identity.db enter the complete mutable registration path one at a time.
  • Acquires registration admission before writable SQLite open and retains it through WAL/durability setup, identity/mailbox schema work, the event/listener transaction, and commit.
  • Uses one five-second contention-wait budget for registration-lock polling and SQLite busy waits. Every SQLite busy timeout installed after admission receives only the budget then remaining.
  • Preserves operation-specific errors for lock-file, lock-admission, SQLite-open, transaction, and commit failures. An admission timeout is pre-open and fail-closed; exact replay remains idempotent.
  • Updates src-tauri/build.rs to watch the actual Git worktree metadata paths, ensuring the embedded commit is rebuilt when the checked-out ref changes.
  • Adds focused contention, remaining-budget, admission-boundary, idempotency, lock-open error, command-boundary, and binary commit identity coverage.

How it works at runtime

  1. The runner validates the completion registration request and owner binding.
  2. It opens pid-identity.registration.lock beside pid-identity.db and repeatedly attempts a nonblocking exclusive lock while registration's contention-wait budget remains.
  3. After admission, it opens the sidecar and installs the then-remaining duration as SQLite's busy timeout before selected SQLite phases.
  4. The event and listener are written in one immediate transaction and committed atomically, then the file lock is released when the registration attempt returns.

Guarantee and limits

  • The five-second accounting is one shared contention-wait budget for registration-lock polling and SQLite busy waits.
  • It is not a hard wall-clock upper bound over parent-directory work, lock-file open, SQLite connection open, ordinary SQLite execution, filesystem I/O, or commit durability. A phase started before budget expiry may finish after five seconds and return success.
  • Admission is bounded best-effort mutual exclusion, not FIFO and not starvation resistant.
  • Unrelated pid-identity.db writers do not acquire the registration lock; they remain SQLite-coordinated and can consume the remaining SQLite busy-wait budget.
  • Event and listener registration remain one transaction. A lock-admission timeout occurs before writable sidecar open and creates no event or listener.
  • Exact historical SQLite causation remains unknown.

Existing exact-head verification

These commands were already executed against head 8dff7f496ab0bde5d9ffd85cf05d6ca064e68dc5; this claim-only correction did not rerun tests or change source bytes.

  • cargo test -p oulipoly-state --test age300_registration_contention -- --nocapture: 4 passed, covering a six-registration burst, the remaining shared SQLite busy-wait budget, independent sustained-arrival disposition, and idempotent replay after admission timeout.
  • cargo test -p oulipoly-state --lib completion_registration_ -- --nocapture: 3 passed, covering lock-open error context, admission before writable open, and admission through atomic commit.
  • cargo test -p oulipoly-agent-runner --test wu_b_mailbox_integration command_registration_over_bound_fails_closed_and_replay_is_idempotent -- --exact --nocapture: 1 passed.
  • cargo test -p oulipoly-state: passed.
  • cargo test -p oulipoly-agent-runner --test wu_b_mailbox_integration: 35 passed.
  • cargo test -p oulipoly-agent-runner --test initiative_06_schema_probe: 15 passed; the rebuilt binary reported the exact source commit.
  • cargo fmt --all -- --check: passed.
  • cargo clippy -p oulipoly-state --all-targets -- -D warnings: passed.
  • cargo clippy -p oulipoly-agent-runner --test wu_b_mailbox_integration --test initiative_06_schema_probe --test wu_d_proactive_wake_integration -- -D warnings: passed.

Pending AGE-300 proof

  • Installing exact-source runner and helper binaries.
  • Verifying installed binary identity.
  • Running joined concurrent and sequential stress against the installed runner/helper pair.

@linear

linear Bot commented Aug 12, 2026

Copy link
Copy Markdown

AGE-300

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.

1 participant