Repository navigation
feat(playbooks): the Captain runs a Crew's playbook and each seat receives its steps - #384
Conversation
…eives its steps
A Captain starts the playbook its Crew follows with playbook_start(...,
crew_instance_id). Every landing (start, next, back, reselect) writes one
delivery row in the move's transaction, and the new PlaybookCrewRelay
hands the step's live prompt to the live seat that owns it, once per
landing, under the Crew's lock. Unowned steps, seats never created, and
archived seats are the Captain's, with no notice. The step tools and
playbook_current report delivery { state, seat, threadId }; only
state "captain" means the Captain does it, and a pending hand-off blocks
the next move with delivery_pending until it resolves. A boot sweep
finishes hand-offs a crash or a transient failure left pending.
Archiving the Crew (MCP, Fleet, or the Captain cascade) cancels its run;
stopping it doesn't. The launch report names the playbook and how to
start it. Fleet shows each Crew's step and who holds it, links a
playbook run to its Crew, and refreshes on playbook changes.
Migration 024 adds the run's Crew link and the delivery table.
Closes #322
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
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 |
…sh statement The SQLite client caches prepared statements by SQL text. The upgrade test read `SELECT * FROM j5_playbook_run` before and after migration 024 with the same text, so the second read reused the pre-ALTER statement, and on Node 24.14 (CI's .nvmrc) that statement keeps its old column list without crew_instance_id. The post-migration read now names its columns. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
[Review panel: Opus 5.5, Astra, Sonnet 5.5, Sol 6.1]
The panel agreed on three findings, which are inline. There's also one FORK.md item, which can't go inline because FORK.md isn't in this diff:
FORK.md case 45 is now inaccurate. It says: "advancement never creates turns or controls provider lifecycle." With this PR, a Crew-linked playbook_start, next, back, or reselect dispatches a message.dispatch into the owning seat's thread. That message starts a turn, or queues behind the seat's active one. Please rewrite that sentence in case 45 in this PR, so the case says what Crew-linked advancement does.
Astra, Sonnet 5.5 and Sol 6.1 live-tested the stack tip, and all six scenarios passed: persona steps, proposal and roster, per-seat delivery, the Fleet step line, archive cancelling the run, and a plain Crew. Opus 5.5 reviewed the code and ran focused tests. Product questions go to Jackson separately. Those are where the Crew–playbook link lives, how big the delivery ledger should be, and whether the Captain should be told when an archive cancels its run.
A pending step hand-off is now finished only by the Captain's next step call or a retry of the same one: drain-before-move, delivery_pending with "retry the same call", the persisted target, and the deterministic command and message ids are unchanged. The relay no longer forks a reconcile at boot, so the layer has one shape. The crash-window integration test now recovers through the Captain's next move and still proves exactly one seat message through the command receipt. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When an active Crew run's live YAML can't be read, or no longer has the recorded step, Fleet used to drop the run. It now shows the recorded step id marked "needs attention", with the reason on hover. The contract gains an optional `issue` on FleetCrew.playbookRun (position is then 0), so an older client still decodes the read. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…allback Rewrites the Definition, AC5, AC12, and the release scenario for Crew runs that hand each step to the one seat that owns it, with the Captain doing unowned steps, and separates Crew-run archive (cancels) from a single agent's (resumable). The 2026-09-29 History line now only records and links the decision. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s thread Case 45 said advancement never creates turns. A Crew-linked start, next, back, or reselect now dispatches a message into the owning seat's thread, which starts or queues a turn there; a thread run still creates none. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…j5/322-crew-playbook-runs
|
@Jacksondr5 on the FORK.md item from your review: case 45 is rewritten in cccf51b. It no longer says advancement never creates turns. It now says a Crew-linked |
…bered to 29 j5/main's peering migrations take J5 ids 23-27 and #383 moves CrewPlaybooks to 28, so CrewPlaybookRuns becomes 29. The upgrade test now runs through 28 before 29 and keeps its named-columns read. The playbooks definition keeps its rewrite and gains main's pre-approval sentence. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
FORK.md case 45 keeps #314's suggestion-seam wording and this branch's Crew-linked dispatch sentence. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
[Review panel lead, Opus 5.5] The FORK.md case 45 item from the panel review is verified in cccf51b. The case now says a thread run's advancement creates no turns, while a Crew-linked start, next, back or reselect dispatches into the owning seat's thread through |
Jacksondr5
left a comment
There was a problem hiding this comment.
[Review panel: Opus 5.5, Astra, Sonnet 5.5, Sol 6.1] One low docs note from round 2 is inline. All round-1 findings are verified fixed (replies on each thread).
Jacksondr5
left a comment
There was a problem hiding this comment.
[Review panel: Opus 5.5, Astra, Sonnet 5.5, Sol 6.1]
Approving at dea99cb. Every round-1 finding is fixed, and the panel checked each one against the source:
- the boot sweep is removed;
- Fleet's "needs attention" header now shows for a Playbook file that can't be read;
- the definition is rewritten;
- FORK.md case 45 is corrected.
C4, the "unowned step" wording, is a non-blocking docs nit. The size of the delivery machinery is deferred to #389.
This stack merges with #387, which is waiting on Jackson's decisions on C1 and C2.
instructions.ts keeps #381's mention bullet and this branch's Captain delivery bullet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ain's The release scenario said a step naming no Role is the Captain's. What decides it is ownership: a step no seat owns is the Captain's, whether or not it names a persona. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Problem
With #321 a Crew records the playbook it follows and which seat owns which step, but nothing runs it: a Captain couldn't start the Crew's playbook, no seat ever got its step, and Fleet couldn't show where the run was (#322). This PR lets the Captain start the Crew's playbook, hands each step's live prompt to the seat that owns it exactly once per landing, cancels the run when the Crew is archived, and shows the current step and who holds it on Fleet.
This PR is stacked on #383 (#321), which is stacked on #380 (#319).
What I changed
packages/contracts/src/j5/playbook.ts→ optionalPlaybookRun.crewInstanceId(also onPlaybookProgress),PlaybookStepDelivery { state: "delivered" | "captain" | "pending", seat, threadId },PlaybookStepResponse.delivery, and thecrew_not_linkableanddelivery_pendingerror codes.packages/contracts/src/j5.ts→ optionalFleetCrew.playbookRun.apps/server/src/j5/a2a/migrations/029_CrewPlaybookRuns.ts→crew_instance_idonj5_playbook_runand a newj5_playbook_step_deliverytable, one row per landing keyed(run_id, request_id).apps/server/src/j5/playbooks/PlaybookStore.ts→starttakes an optionalcrewInstanceId(part of the request JSON, so replaying a key with a different Crew isrequest_conflict). For a Crew-linked run,start,next,back, andreselectwrite the landing row in the same transaction as the move. New readslatestLanding,activeRunForCrew, andcancelForCrew.apps/server/src/j5/playbooks/PlaybookCrewRelay.ts:startlinks the run undercrews.serialize(crewInstanceId)and refusescrew_not_linkablewhen the Crew is missing, archived, commanded from another thread, or follows a different definition.deliverresolves the owner from live members at the first attempt, persists the target seat and thread before dispatching, and sends the notice with acommandIdandmessageIdderived fromrunId:requestId. A step with no owner, a seat that never launched, or an archived seat is the Captain's, with no notice.mutatechecks the run's owner, then drains every pending landing oldest first before moving. A landing that can't be handed off yet returnsdelivery_pendingand the run doesn't move.currentDeliveryis read-only and backsplaybook_currentand Fleet.crewStepNotice.ts→ the<j5_playbook_step>notice with the live prompt in<step_prompt>, escaped.playbooks/mcp.ts→playbook_starttakescrew_instance_id; the step tools andplaybook_currentreturndelivery.playbooks/instructions.ts→ one bullet: branch ondelivery.state; onlycaptainmeans do it yourself;pendingmeans don't start it yet.ArchiveCrewService.ts→ archiving a Crew cancels its active run, beforemarkArchivedand again on thealready_archivedretry, so the MCP tool, the Fleet button, and the Captain cascade all cancel. Stop leaves the run alone.crewGateNotice.ts/CrewLaunchReporter.ts→ the launch report names the playbook and how to start it withcrew_instance_id.FleetReadsHttp.ts→ each live Crew'splaybookRun(position, total, live step title, delivery state, seat). WebFleetPage.tsxandfleet.logic.ts→ "Step N of M: <title> · ", "· Captain", or "· handing off to "; a run whose YAML can't be read or no longer has the step shows "Step · needs attention" through an optionalissueonFleetCrew.playbookRun(position 0), so older clients still decode it. Fleet refetches on playbook changes.PlaybookRunsSection.tsx→ a Crew-linked run shows "Crew · ", which opens and focuses that Crew's group, including a retired one.docs/user/playbooks.md("With a Crew"),docs/j5/product/features/playbooks.md,docs/j5/product/a2a/agent-tools.md.Before (#321's head: the Crew row shows its playbook but not the step, and the run doesn't link to the Crew):
After (the Crew row shows "Step 3 of 4: Review · Captain", and the run card links to the Crew):
Why this shape
commandId, so if the server crashes after dispatching but before marking the row, the retry is replayed by the orchestrator's command receipts and the seat still gets one message.delivery_pendingif one can't be handed off. I chose blocking over silently dropping a step.playbook_cancelnever drains, so cancelling always works.state: "captain"means the Captain does the step;seat: nullalone never does. Guidance,playbook_current, and Fleet all branch onstate, so a pending hand-off is never shown as the Captain's.commandId. Ownership changes apply to the next landing;backandreselectre-deliver under current ownership.Invariants
playbook_currentand Fleet never dispatch.crew_instance_idbehave exactly as before, with nodeliveryfield.Surfaces
playbook_startand step tools, archive through MCP, the Fleet button, and the Captain cascade, and the Fleet page; no Settings, palette, or keybinding surfacepackages/contracts)src/j5.tsandsrc/j5/playbook.tscrew_instance_id); stop leaves the run;pendingclears on the Captain's retry or next step calldocs/user/playbooks.md,docs/j5/product/features/playbooks.md,docs/j5/product/a2a/agent-tools.mdOut of scope
<j5_playbook_step>; it renders as plain message text.Upgrade and data
Migration 029 adds a nullable column and a new table. Existing runs read back with no Crew link and behave as before. Older clients ignore the optional fields. Rolling back leaves a Crew-linked run behaving like a thread run. Numbering: j5/main's peering work took J5 migrations 23–27, so #383's is now 028 and this one is now 029. A dev database that ran an earlier build of this stack already recorded 23 and 24 as ours and would skip the peering migration with that number, so reset or re-copy such a database before testing. Real installs never ran the old number.
Verification
vp test run apps/server/src/j5/playbooks/PlaybookCrewRelay.test.ts apps/server/src/j5/playbooks/PlaybookCrewRelay.integration.test.ts apps/server/src/j5/playbooks/crewStepNotice.test.ts apps/server/src/j5/playbooks/PlaybookStore.test.ts apps/server/src/j5/playbooks/mcp.test.ts apps/server/src/j5/playbooks/PlaybookHttp.test.ts apps/server/src/j5/a2a/ArchiveCrewService.test.ts apps/server/src/j5/a2a/CrewCaptainArchiveCascade.test.ts apps/server/src/j5/a2a/CrewStopService.test.ts apps/server/src/j5/a2a/crewGateNotice.test.ts apps/server/src/j5/a2a/CrewLaunchReporter.test.ts apps/server/src/j5/a2a/FleetReadsHttp.test.ts apps/server/src/j5/a2a/Migrations.test.ts apps/server/src/j5/a2a/runtimeLayer.test.ts apps/web/src/j5/fleet/fleet.logic.test.ts apps/web/src/j5/crew/crewNotices.logic.test.ts apps/server/src/j5/a2a/ClientReadsHttp.test.ts apps/server/src/j5/a2a/ThreadHomesHttp.test.ts apps/server/src/j5/a2a/CrewLaunchService.test.ts: 19 files, 174 tests pass.PlaybookCrewRelay.integration.test.tsruns through the real orchestrator and command receipt store: a failure injected after a real dispatch and before marking leaves the row pending; the Captain's nextplaybook_nextdrains it and replays the samecommandId, and the seat thread has exactly one message with the deterministicmessageId, including when the YAML prompt is edited in between.nextdelivering once;delivery_pendingblocking the move and draining in landing order;playbook_currentreportingpendingrather thancaptain;already_archivedretry, and Captain cascade) cancelling the run while stop doesn't;vp exec tsc --noEmit -p .inpackages/contracts,packages/client-runtime,apps/server, andapps/web: clean.vp linton the touched files: clean.Review focus
PlaybookCrewRelay.mutateanddeliver: the owner check before the drain, the target persisted before dispatch, and which failures resolve ascaptainversus stay pending. I most want the exactly-once argument challenged.ArchiveCrewService.archive: cancelling beforemarkArchivedand on thealready_archivedretry, under the Crew lock.Closes #322 (stacked on #383)
Claude Opus 5.5 via J5 Code (crew: planner, builder, and two reviewers on Claude and Codex)
🤖 Generated with Claude Code