Skip to content

feat(crews): custom seats default to Full access and the roster flags seats that will stop - #347

Merged
bryantderosier merged 16 commits into
j5/mainfrom
j5/crews-seat-full-access
Sep 29, 2026
Merged

bryantderosier merged 16 commits into
j5/mainfrom
j5/crews-seat-full-access

Conversation

@bryantderosier

@bryantderosier bryantderosier commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Important

⚠️ Merge order: merge these in this exact order

This PR is part of the Crews stack. Merge top to bottom, one at a time, and let each land on j5/main before the next. Everything before this PR has merged, so it can go next.

  1. fix(crews): Crew notices report only what the platform measured #292: Crew notices report only what the platform measured (adds migration 020) ✅ merged
  2. 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 ✅ merged
  3. fix(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 ✅ merged
  4. fix(crews): a proposal launches once and reports every seat #313: A proposal launches once and reports every seat (adds migration 021) ✅ merged
  5. fix(crews): Crew groups keep seats without thread facts as unknown #300: Crew groups keep seats without thread facts as unknown ✅ merged
  6. fix(crews): J5 stream daemons start from the event store's high-water mark #352: J5 stream daemons start from the event store's real high-water mark (fixes fix(crews): J5 stream daemons replay all history on every boot (high-water reads an empty table) #349) ✅ merged
  7. fix(crews): a Crew follows its Captain through every lifecycle step #315: A Crew follows its Captain through every lifecycle step (adds migration 022) ✅ merged
  8. feat(crews): custom seats default to Full access and the roster flags seats that will stop #347: custom seats default to Full access ⬅️ this PR

Why #352 goes first: without it the finish notifier's stream starts at sequence 0 and replays every event on boot. #315's cascade also acts on thread.unarchived, thread.settled, and thread.unsettled, so a restart would settle, unsettle, and restore Crews from history (#349).

Problem

A custom Crew seat with no runtime_mode inherited its Captain's access. A Captain you supervise in Supervised mode handed that mode to every seat it didn't configure, and those seats stopped for approvals in threads nobody watches (#326).

What I changed

  • apps/server/src/j5/a2a/CrewLaunchService.ts → resolveSeats: an unset custom seat resolves to full-access. Model, harness, and reasoning still come from the Captain. Saved-persona seats keep their persona's policy. The Captain-access lookup is gone.
  • ACP: the refusal now fires only on an explicit Auto or Auto-accept edits choice. An unset ACP custom seat launches in Full access, which ACP can enforce.
  • apps/web/src/j5/crew/crewSeatRuntime.ts → crewSeatStopsForApprovals: flags a seat whose resolved access isn't Full access. It skips a seat whose runtime hasn't loaded, and a saved persona left on its own policy.
  • apps/web/src/j5/crew/CrewProposalCard.tsx: flagged seats get a Stops for approvals badge, with a line above the buttons ("N seats will stop for approvals in their own threads"). The card serves both the roster gate and the Inbox addition card.
  • apps/server/src/j5/a2a/mcp/tools.ts: the propose_crew / request_crew_member descriptions and field descriptions now say access defaults to Full access.
  • apps/server/src/provider/T3OrchestrationInstructions.ts (upstream, existing FORK.md case 8): the "Custom seats inherit your configuration" clause is reworded to match.
  • Docs: crews.md (Definition, AC3, History), agent-tools.md (description copies re-synced with the shipped strings, seat tables list model_selection and runtime_mode, History), and docs/user/personas.md.

Screenshots

Both use the same seeded roster proposal on an isolated copy of real data, light theme, 1440×1000. The Captain runs in Supervised. code-reviewer sets no access, docs-writer asks for Supervised, and test-author asks for Auto-accept edits. Before is j5/main; #315, this PR's base, doesn't change the roster card.

Before: code-reviewer inherits the Captain's Approval required, and nothing says which seats will stop.

Before: roster card with the default seat on Approval required and no warnings

After: code-reviewer defaults to Full access. The two seats that will stop carry "Stops for approvals", and the footer counts them.

After: roster card with the default seat on Full access and two Stops for approvals badges

Why this shape

This is Jackson's decision on #326. Leaving it to each Captain to set access well is a footgun, because there are several ways a seat ends up in a mode that stops. The person still picks any seat's access on the card before approving. The warning is per seat, from the same resolved runtime the preview shows, so it can't disagree with the card.

Invariants

  • A seat's resolved access, the preview, and the approval token all come from one resolution, so they stay in step.
  • Saved-persona seats are unchanged.
  • An explicit runtime_mode always wins.

Surfaces

Surface Decision
Entry points (chat, Settings, command palette, keybinding) Changed: the roster gate and the Inbox addition card, which share one component.
Clients (web, desktop, mobile) Web and desktop. Mobile has no roster gate.
Providers All custom seats; ACP now launches unset seats in Full access instead of refusing them.
Contracts (packages/contracts) Unaffected.
Reverse states The person can change any seat's access on the card before approving.
Connection modes (local, remote, tunnel) Unaffected.
Upstream files / FORK.md T3OrchestrationInstructions.ts under existing case 8; FORK.md updated.
Docs Changed: crews.md, agent-tools.md, personas.md.

Out of scope

Upgrade and data

No migration. A proposal previewed before the upgrade and approved after it gets a token mismatch, because its resolved access changed, and the card asks for a fresh preview.

Verification

  • apps/server, apps/web, and packages/contracts typecheck (exit 0).
  • vp test run apps/server/src/j5 apps/web/src/j5 plus the orchestration-instructions test: 1,054 passed, 1 skipped.
  • Lint and format are clean.
  • New tests:
    • An unset custom seat resolves to Full access under Supervised, Auto, and Auto-accept edits Captains, on Codex and on ACP.
    • A persona seat keeps its policy.
    • An explicit Supervised choice wins.
    • An explicit Auto or Auto-accept edits choice on ACP is still refused.
    • crewSeatStopsForApprovals covers custom, persona, persona-default, and not-yet-loaded seats.

UI evidence: before/after screenshots of the roster card are under What I changed.

Review focus

  • Whether a saved persona on its own default policy should also be flagged. It isn't now: a read-only persona reports Supervised but refuses rather than asks, so flagging it would warn about stops that never happen.

Closes #326

Claude Opus 5.5 via Claude Code in J5 Code

🤖 Generated with Claude Code

bryantderosier and others added 13 commits September 25, 2026 16:23
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>
The sidebar expander dropped any Crew seat whose thread was not in client
state, and the Fleet page grouped a Crew only from placed participants, so a
Crew could under-count or vanish while its seats were unknown.

The spawned-children read now gives a Captain's row every seat on its live
rosters that no parent holds, and the Fleet read emits a thread-less row for
a roster seat the ledger never recorded. The sidebar keeps a seat with no
thread as an unknown row, and the Fleet tree hangs an unplaced seat under its
Captain, so both summaries count every seat.

Closes #227

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There is never a Crew without its Captain. The Captain cascade already
retired a Captain's live Crews on archive or delete; it now also brings
back the Crews that retired with it when the Captain is unarchived, seat
threads included, and sends a Captain's settle or unsettle to every seat
of its live Crews. Unsettle reaches only seats that were settled, so a
seat that never settled keeps upstream's automatic settlement.

Migration 022 records whether a Crew retired with its Captain, so a Crew
retired on its own through archive_crew or Archive crew stays retired.
The boot sweep also settles the seats of a settled Captain and restores
Crews under a Captain that is live again. A seat is still never archived
on its own.

Closes #312

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The upstream sync withheld the archive Undo notice whenever an archive might
retire a Captain's Crews (decision #7, 2026-09-24), because unarchiving the
Captain did not bring them back. With this branch it does: the Crews that
retired with their Captain return with its unarchive. So the undoable plumbing
is gone, useThreadActions.archiveThread is upstream's again, and the Sidebar,
header menu, and LegacySidebar doors call it as upstream does.

Refs #312

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…th their own ids

The Captain cascade derived each seat's archive command ids from the Captain
and the Crew alone, so archive, unarchive, then archive again reused the first
archive's ids: the orchestrator replayed the old receipt, the seat stayed
unarchived, and the Crew stayed live under an archived Captain. The retire
request key now carries the occurrence, the triggering event id on the stream
and the Captain's archive or delete time on the boot sweep.

Each Crew step runs once and logs its failure with the cause; the two
in-session retry loops are gone. The boot sweep keeps only its retire leg for
Crews of an archived or gone Captain; its settle and restore legs are removed,
so unarchive, settle, and unsettle have no restart recovery.

A Captain's settle skips a seat with a pending runtime request, a live run, or
background work, mirroring upstream's isAutoSettlementCandidate through
threadShellFromProjection. Seat reads use getThreadProjectionIfPresent, so only
a genuine not-found skips a seat and any other failure fails the step.

Refs #312

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Crews AC18 now reads that J5 never settles a seat because its run finished,
settling the Captain settles its seats, and upstream's settle rules apply to
every thread; the Definition says the same. The docs no longer claim the boot
sweep settles or restores Crews, say that Undo and unarchive don't restore
interrupted runs or dropped Exchanges, and give the repair for an unarchive
that stops partway. The retired Crew roster shows why each seat joined, since
it records no approver, and the Fleet page history records the AC28 change.
FORK.md's useThreadActionMenu row points at case 21, which now names the file
and describes the cascade's run-once steps and retire-only sweep.

Refs #312

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
… seats that will stop

A custom Crew seat with no runtime_mode now runs in Full access instead of
inheriting the Captain's access, so a supervised Captain no longer hands
approval prompts to seats in threads nobody is watching. Model, harness, and
reasoning still come from the Captain; saved-persona seats keep their policy.

The roster and addition card marks each seat that will stop for approvals
(anything short of Full access, other than a persona on its own sandboxed
policy) and counts them above the approve button. Tool descriptions, the
standing Crew instructions, and the Crew and persona docs say the new default.

Closes #326

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bryantderosier bryantderosier added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. jackson-direct Taken by Jackson + Astra outside the fleet methodology; lanes never staff these labels Sep 28, 2026
@bryantderosier bryantderosier self-assigned this Sep 28, 2026
@bryantderosier
bryantderosier added this pull request to stack #280 September 28, 2026 15:24
@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: Jacksondr5/j5code/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 8c48d8af-0c3f-4017-b527-51cf71c7cb4b


Comment @coderabbitai help to get the list of available commands.

bryantderosier and others added 2 commits September 28, 2026 13:37
Conflicts: the J5 migration list now holds 020 (#292), 021 (#313), and this branch's 022 in id order; kept this branch's cascade comment in runtimeLayer.ts and its crews.md History line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

# Conflicts:
#	docs/j5/product/features/crews.md
bryantderosier added a commit that referenced this pull request Sep 29, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Base automatically changed from j5/crews-follow-captain to j5/main September 29, 2026 13:33
Conflicts: FORK.md takes main's proposal-flow paragraph with this branch's Full-access default for custom seats; crews.md keeps this branch's Definition sentence and adds #348's rule that seats ask their Captain, and keeps both 2026-09-26 History lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bryantderosier
bryantderosier merged commit d19f497 into j5/main Sep 29, 2026
29 checks passed
@bryantderosier
bryantderosier deleted the j5/crews-seat-full-access branch September 29, 2026 14:41
bryantderosier added a commit that referenced this pull request Sep 29, 2026
Conflicts: AC3 keeps #347's approval flag and adds this branch's preview-binding sentence; the personas guide keeps #301's and #347's text and adds the preview and ACP sentences, now in #344's access-mode names (Supervised, Auto-accept edits); both 2026-09-24 History lines stay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bryantderosier added a commit that referenced this pull request Sep 29, 2026
Conflicts: agent-tools.md takes main's quoted propose_crew and request_crew_member descriptions and tables, which match the shipped strings after #347; crews.md takes main's Definition (Full access default, seats ask their Captain) with this branch's no-approver roster wording, and History keeps one date-ordered list with #301's and #307's 2026-09-24 lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jackson-direct Taken by Jackson + Astra outside the fleet methodology; lanes never staff these size:L 100-499 effective changed lines (test files excluded in mixed PRs). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

1 participant