Repository navigation
fix(crews): the twelve-seat cap counts pending additions atomically - #239
Conversation
|
Claimed for review by Jackson with Claude Fable 5.1 and GPT-6-Astra (the crews review thread). Other agents: please skip this one. |
|
The launch model these three PRs share is being replaced with "launch once, report once"; the full note, with an apology for the churn on our side, is on #235: #235 (comment). This PR is affected as described there. |
A Crew may hold at most twelve seats counting requests still open, but `requestMember` counted pending seats and inserted the proposal in two separate steps, so two requests racing at eleven held seats were both filed. A replayed request also counted its own proposal before finding it, so the request holding seat twelve was refused as a thirteenth on retry. And the refusal told the Captain to let a member finish, which frees no seat. The proposal store's new `admit` runs the existence check, the count (member rows plus the seats of every open request), and the insert in one transaction. A replay finds its row before anything is counted, and only one of two requests for the last seat is filed. While a failed addition launch still reopens its proposal, the rows it reserved count as members and not again as its pending seats; that subtraction goes when reopen does. Approval only has to fit the live rows. The card refuses that up front, and `addMembers` still enforces it when it reserves the rows. Under launch-once a proposal is resolved once, so the claim takes no count of its own. The refusal now says the Crew is full and a new Crew is how you get more hands, and the MCP `next_step` carries the error's own next step instead of a fixed "correct and retry". Closes #222 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
959e98c to
af22b50
Compare
|
Agreed. I rebased this onto j5/main on its own, since #235 and #236 are closing. What's left is the admission transaction: I also carried the one live piece of #236 here as its own commit, af22b50: the proposal-to-Crew link write no longer gets ignored, so a failed link fails the approval before any seat spawns instead of launching a Crew the report can't find. It's two commits now, 1a97982 and af22b50. |
Jacksondr5
left a comment
There was a problem hiding this comment.
The narrowed PR is right and I am approving it. The admission transaction does what #222 asked: existence lookup, held-seat count, and insert in one transaction on the single serialized connection, replay found before anything is counted, reserved rows subtracted once, and both race tests present. With the claim-time recount gone, approval only has to fit live rows, and addMembers' transactional cap is the backstop before any seat spawns, so no thirteenth member can land. No upstream file touched.
Three notes, none blocking:
- The
declining-reopen hole is still on this branch and correctly scoped as leaving with reopen: claim P for decline, admit Q, P's cleanup fails and reopens, and thirteen commitments exist until one approval is refused. The code comment ties the subtraction to reopen's removal but does not name this case; one line in the launch-once follow-up so it is not rediscovered. - The link-write commit's stated reason is off (inline). The behavior is right; the rationale in the comment and commit message is not.
- The body predates the narrowing. It still says stacked on #236, describes the dropped claim recount, approving-count, and preview check, references PRs 223 and 224 that do not exist, and lists tests that were deleted. AGENTS.md: the body is the problem in a sentence or two, then how you fixed it.
For the launch-once follow-up: the reopen-dependent tests here are AgentCrewProposalService.test.ts (the subtraction) and the failed-approval test in CrewProposalService.test.ts that asserts reopened status and a null link; both go with reopen. And #222's "approve an addition the person expanded while another request is open" has no gate-level test after the narrowing; the addMembers test covers the invariant, so this is a judgment call.
Reviewed by Claude Fable 5.1 in Claude Code, with an independent pass by GPT-6-Astra in Codex.
A roster launch records its Crew and then links the proposal to it before any seat spawns. The link write was wrapped in Effect.ignore, so a lost write left an approved proposal that does not name its Crew. Declining a reopened proposal retires the Crew through that link, so the cleanup would find nothing to retire. The link write's failure is now a CrewLaunchOperationError that aborts the launch before any seat spawns, the same as a failed record write, and onRecorded's type says it can fail. The record's id is deterministic, so clicking approve again is safe. When reopen goes, this stays as plain correctness: an approved proposal names its Crew. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
af22b50 to
b0366c0
Compare
|
I addressed the review in b0366c0: the link-write comment and commit message now give the real reason. I also rewrote the body for the narrowed PR. The declining-reopen hole and the reopen-dependent tests are listed under the launch-once follow-up so nobody has to rediscover them. I'm leaving the expanded-approval case covered by the addMembers test, not a gate test. |
Closes #222.
Problem
A Crew may hold at most twelve seats counting requests still open, but
requestMembercounted pending seats and inserted the proposal in two separate steps, so two requests racing at eleven held seats were both filed. A replayed request counted its own proposal before finding it, so the request holding seat twelve was refused as a thirteenth on retry. The refusal told the Captain to let a member finish, which frees no seat. Separately, the proposal-to-Crew link write was ignored on failure, so an approved roster could end up not naming its Crew.Fix
The proposal store's new
admitruns the existence check, the held-seat count (member rows plus the seats of every open request), and the insert in one transaction. A replay finds its row before anything is counted, and only one of two requests for the last seat is filed. While a failed addition launch still reopens its proposal, the rows it reserved count once; that subtraction goes when reopen does. Approval only has to fit the live rows, andaddMembersstill refuses a thirteenth row atomically before any seat spawns.The refusal now says the Crew is full and a new Crew is how you get more hands, and the MCP
next_stepcarries the error's own next step.The link write's failure now aborts the approval before any seat spawns. Declining a reopened proposal retires the Crew through that link; once reopen goes, the write stays as plain correctness.
Two commits: 1a97982 (admission transaction) and b0366c0 (link write). All changes are under
apps/server/src/j5/a2a/; no upstream file, migration, or contract change.Tests
admitcounts rows and open requests and files one of two requests racing for the last seat; a failed addition's reserved row is counted once.Follow-up (launch once, report once)
That PR deletes the reserved-row subtraction here, along with its test in
AgentCrewProposalService.test.tsand the failed-approval test inCrewProposalService.test.tsthat asserts a reopened status and a null link. Until then, one known hole stays open: claim P for decline, admit Q, P's cleanup fails and reopens P, and thirteen commitments exist until one approval is refused. It closes when reopen goes.Claude Opus 5.5 via Claude Code.
🤖 Generated with Claude Code