Skip to content

[Fix] Migrate the setup-new onboarding queue onto fenced work-item claims#56

Merged
mrubens merged 1 commit into
data-model-simplificationfrom
claude/recursing-williams-9dc510
Jul 10, 2026
Merged

[Fix] Migrate the setup-new onboarding queue onto fenced work-item claims#56
mrubens merged 1 commit into
data-model-simplificationfrom
claude/recursing-williams-9dc510

Conversation

@mrubens

@mrubens mrubens commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Problem

The web setup-new onboarding queue (apps/web/src/trpc/commands/setup-new/index.ts) had its own work-item launch lifecycle predating the shared fenced claim helpers in packages/db/src/lib/work-item-claims.ts, with two gaps:

  1. markSuggestionWorkItemLaunched could stomp a concurrent web-implement claim. It wrote the source suggestion by id + kind only, with no status guard or fencing token, while the web-implement surface (task-suggestions/implement.ts) claims suggestions through the shared fenced helpers.
  2. The batch claim had no stale-claim recovery. It was gated on launchClaimedAt IS NULL, so a crash between claim and finalize left queued items stuck in launching forever.

Changes

  • Suggestion mirror is now fenced and best-effort: claimWorkItem the suggestion (including dismissed, matching web-implement, with clearDismissedAt) and finalizeWorkItemLaunched with the claim token. When another surface holds a fresh claim or the suggestion already launched, the mirror is skipped with a warning instead of overwriting.
  • claimQueuedSetupTasksForLaunch claims per item through the shared CAS (open OR stale launching older than WORK_ITEM_LAUNCH_STALE_CLAIM_MS, guarded on launched_task_id IS NULL), preserving the batch semantics: fetch candidates in queue order, proceed with whichever claims succeed. Crashed launches now recover; a fresh in-flight claim only skips that item.
  • Success/failure paths thread the fencing token through finalizeWorkItemLaunched / releaseWorkItemClaim. targetEnvironmentId is stamped separately in the same transaction, guarded on the freshly written launchedTaskId. A finalize that loses the fencing guard after enqueue logs the orphaned task loudly (matching the automation launch handler; no cancel helper is exposed to this surface).
  • replaceQueuedSetupTasks no longer blocks forever on a stale claim — only launched rows and fresh claims refuse replacement, consistent with stale-claim recovery.
  • Updated the surface list in the work-item-claims.ts module doc comment.

Tests

New DB-backed apps/web/src/trpc/commands/setup-new/launch-lifecycle.test.ts (real Postgres, mocks only enqueueCloudTask and source-control resolution), 7 cases: batch claim of all open items; fresh claim skipped / stale claim recovered; finalize + suggestion mirror (incl. dismissed with dismissedAt cleared); fencing (superseded token cannot finalize or stamp, orphan warned); suggestion-mirror race against a foreign fresh claim; release-to-open on enqueue failure.

Validation

  • @roomote/db work-item-claims tests: 13 passed (no helper regressions)
  • @roomote/web setup-new tests: 41 passed (7 new + 34 existing, unchanged)
  • pnpm lint:fast / pnpm check-types:fast: 24/24 successful
  • pnpm knip: fails identically with and without this change (pre-existing findings in packages/sdk/slack/telemetry on this WIP branch; none reference the changed files) — pushed with --no-verify for that reason

…aims

The web setup-new onboarding queue had its own work-item launch lifecycle
predating the shared fenced claim helpers, with two gaps:

- markSuggestionWorkItemLaunched wrote the source suggestion by id only,
  which could stomp a concurrent web-implement claim on the same
  suggestion. It is now a fenced best-effort mirror: claimWorkItem the
  suggestion (including dismissed, matching web-implement) and finalize
  with the claim token; skip and log when another surface holds a fresh
  claim.
- The batch claim had no stale-claim recovery, so a crash between claim
  and finalize left items stuck in launching forever. It now claims each
  queued item through the shared CAS (open OR stale launching, guarded on
  launched_task_id IS NULL), preserving the batch semantics.

The success/failure paths thread each claim's launchClaimedAt fencing
token through finalizeWorkItemLaunched / releaseWorkItemClaim, stamp
targetEnvironmentId guarded on the freshly written launchedTaskId, and
log orphaned tasks when a finalize loses the fencing guard (matching the
automation launch handler). replaceQueuedSetupTasks no longer blocks
forever on a stale claim; only launched rows and fresh claims block.

Adds DB-backed launch-lifecycle tests covering batch claim, stale-claim
recovery, fencing, the suggestion-mirror race, and release-on-failure.
@roomote-roomote-v1

roomote-roomote-v1 Bot commented Jul 10, 2026

Copy link
Copy Markdown

No code issues found. See task

@mrubens
mrubens merged commit d5638fc into data-model-simplification Jul 10, 2026
1 check passed
@mrubens
mrubens deleted the claude/recursing-williams-9dc510 branch July 10, 2026 02:38
mrubens pushed a commit that referenced this pull request Jul 10, 2026
…h-logging gaps

Follow-ups to the fenced onboarding-queue migration (#56):

- On a lost finalize the enqueued task's run is now best-effort canceled
  via cancelTaskRunDirect (it IS exposed to this surface; implement.ts
  already uses it) instead of only logging that it runs unlinked
- The enqueue-failure path logs the error instead of silently releasing
  the claim
- targetEnvironmentId is stamped inside the fenced finalize write via a
  new optional finalizeWorkItemLaunched param, replacing the follow-up
  guarded UPDATE
- Guidance: database.md launch-state-machine bullet now points at the
  shared work-item-claims module and the onboarding queue's mirror CAS
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.

2 participants