Repository navigation
fix(crews): a proposal launches once and reports every seat - #313
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Repository: Jacksondr5/j5code/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: Comment |
A Crew proposal now resolves exactly once, open to approved or declined, by compare-and-set under a per-proposal lock. The claimed states, reopen, and the boot sweep for lost claims are gone; migration 021 hands any leftover approving or declining row back as open. Once seats start spawning, a seat that fails does not stop the others. A seat whose thread was never created is dropped from the roster and the launch report names it on a seat_not_created line; a seat whose brief never went out reports not_started. The web launch card shows both. Closes #311 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The migration 021 test this branch adds still called NodeSqliteClient.layerMemory(), which the sync replaced with NodeSqliteClient.layer({ filename: ":memory:" }).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The upstream sync's Badge lint (shadcn/no-restyle) refuses spacing and typography classes on <Badge>; the not-created row now uses size="sm" like the roster rows beside it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
c822121 to
08f7b30
Compare
|
[Review panel: Opus 5.5 + coordinator (Opus 5.5)] Docs: missing History line. This PR changes |
|
[Review panel: Opus 5.5 + Astra] FORK.md: this PR leaves the Crew proposal-flow paragraph inaccurate. The PR says FORK.md is unaffected, but the "proposal flow's server modules" paragraph (FORK.md, just before case 35's section, around line 190) still describes the retry model this PR deletes:
A rebaser following this text would look for machinery that no longer exists. Suggested fix: rewrite that sentence in place, not as an appended history note. Something like: "an approval records the Crew and resolves the proposal once before any seat spawns; every seat is attempted, a seat whose thread was never created is dropped from the roster, and the launch report names each outcome." Then add migration 21 next to 14/15/16. |
|
Decisions (Jackson, 2026-09-26).
|
FORK.md's proposal-flow paragraph still described the retry model this PR removes (the handed-back gate, converging retries, a decline that archives spawned seats) and listed migration 16's claims as current. It now says a proposal resolves once before any seat spawns and names migration 21. The agent-tools page gains its History line for the change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@Jacksondr5 Both remaining items are done (eff0f68):
I've resolved the restart thread as withdrawn, merged the latest |
|
[Review panel: Opus 5.5 + Astra] Verified in eff0f68:
The restart-mid-launch finding is withdrawn per Jackson's decision and not re-ruled. |
Jacksondr5
left a comment
There was a problem hiding this comment.
Posted by an AI agent on Jackson's behalf.
Approved. The FORK.md proposal-flow paragraph and the agent-tools.md History line are both in, verified by the review panel (Opus 5.5 + Astra). The restart finding stays withdrawn, per Jackson's decision. Live: a partial launch still shows "Not created".
Merge order: #292, #313, #300, then #315 (after #349 lands). #306 rebases after the stack.
Conflict in the J5 migration list: #292's migration 020 now sits on j5/main, so 020 and this branch's 021 both stay, in id order. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflict: #313 rewrote CrewProposalService.resolve; kept main's version and re-applied this branch's stale-preview refusal text naming the roster, a seat's runtime, and the Captain's branch or worktree. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Conflicts in the Crews docs resolved to the shipped wording from #313 and #315, keeping this branch's corrections: - crews.md: the Lifecycle sentence now says a Crew comes back only with its Captain, and the 2026-09-24 History line drops "Crews never unarchive as a unit" (Jackson's suggestion). The roster snapshot records each member's reason and no approver. History is back in date order. - agent-tools.md: the launch report keeps not_started, seat_not_created, and resolve-once, and the seat finish notice adds the unavailable status and over-read-limit case. - fleet-page.md: AC28 takes #315's wording, with both History lines. - personas.md: #315's paragraph, with Retired crews described as one section across every Squadron. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Important
This PR is part of the Crews stack. Merge top to bottom, one at a time, and let each land on
j5/mainbefore the next. #292 isn't in the stack but has to merge before #313.fix(crews): no seat launches into a retired Crew or under a gone Captain #271: No seat launches into a retired Crew or under a gone Captain✅ mergedfix(crews): unit stop and archive finish over a seat that was never created #279: Unit stop and archive finish over a seat that was never created✅ mergedWhy #292 goes first: J5 migrations run in id order, and the migrator skips any id at or below the newest one a database has already applied. If #313's migration 021 ships before #292's 020, 020 never runs on that database.
Problem
A Crew proposal still acted like something you retry. Approval claimed the gate (
approving/declining), a failed spawn handed the card back with a retry prompt, and a boot sweep settled claims the server lost.spawnSeatsstopped at the first failing seat, so later seats were never tried and the Captain heard nothing about them. A seat whosethread.createfailed kept its roster row, even though nothing could message it. Jackson and I agreed on #271 that every approval launches once and reports what happened (#311).What I changed
apps/server/src/j5/a2a/AgentCrewProposalService.ts: status isopen | approved | declined.claim,complete,reopenandlistClaimedare replaced by one compare-and-setresolve(WHERE status = 'open').countHeldSeatsis members plus seats in open proposals.CrewProposalService.ts→resolveruns under a per-proposal lock (makeKeyedSerialExecutor), always taken before the crew lock.onRecordedlinks it and records the approval before any seat spawns.onReservedrecords the approval. If that write fails, the rows are released.reconcileandbootSweepLayerare gone, and so is their wiring inruntimeLayer.ts.CrewLaunchService.ts→spawnSeatscarries on past failures and returns aCrewSeatLaunchOutcomeper seat:creatednot_created: the thread never existed, so its row is dropped.not_started: the thread exists but its home or brief failed.launch/addSeatsreturn{ instance, seats }. The retry-convergence checks (assertReusableSeats,sameCrewRuntime,spawnBriefWithoutCrewContext) are removed.CrewLaunchReporter.ts→watch(proposalId, outcomes)uses the launch's own outcomes first. A seat with no thread projection reads asnot_created, which covers a restart mid-launch. Approved seats missing from the roster are reported by name.crewGateNotice.ts:seat_not_created: <seat> | <detail>lines, andnot_startedrenders asseat_failed: <seat> | not_started | ….request_crew_member.migrations/021_CrewProposalsResolveOnce.ts: rows a server leftapproving/declininggo back toopenwith no approved seats.packages/contracts/src/j5.tsCrewProposal.status,mcp/tools.tsJ5CrewProposalResult.status,apps/web/src/j5/crew/crewProposals.logic.ts: status narrowed to the three values.Web:
crewNotices.logic.tsparsesseat_not_createdintonotCreated, which the title and footer count.CrewNoticeRenderer.tsxshows each one as a "Not created" row with its reason.CrewProposalCard.tsxseeds fromrequestedSeats, because an open proposal no longer holds approved seats.Docs:
docs/j5/product/features/crews.md(AC6, AC7, the flaky-launch scenario, History) anddocs/j5/product/a2a/agent-tools.md(propose_crewresult and launch report,request_crew_membersemantics, AC18).Launch card for a roster whose
criticseat was never created, before → after:Before, the card said every seat started. After, it names the seat that was never created and why.
Why this shape
Invariants
NotOpen.seat_not_createdline.approvedSeatsis null.Surfaces
packages/contracts)CrewProposal.statusnarrowed to open, approved, declined (J5-ownedj5.ts).request_crew_member.crews.mdandagent-tools.md.Out of scope
Upgrade and data
approving/decliningrows back toopenwithapproved_seatsandresolved_atcleared, and leaves approved and declined rows alone.Verification
vp test run apps/server/src/j5 apps/web/src/j5/crew: 733 tests pass (1 skipped).seat_not_created.Review focus
CrewProposalService.resolveandCrewLaunchService.launch/addSeats: lock order, and whether any failure path can spawn before the approval is recorded.CrewLaunchReporter.refreshSeat: reading a missing projection asnot_created. Challenge whether a slow projection could misreport a seat that is still being created.Closes #311
Claude Opus 5.5 via Claude Code in J5 Code
🤖 Generated with Claude Code