Skip to content

fix(crews): Crew groups keep seats without thread facts as unknown - #300

Merged
bryantderosier merged 3 commits into
j5/crews-launch-oncefrom
j5/issue-227-unknown-seats
Sep 28, 2026
Merged

bryantderosier merged 3 commits into
j5/crews-launch-oncefrom
j5/issue-227-unknown-seats

Conversation

@bryantderosier

@bryantderosier bryantderosier commented Sep 24, 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. #292 isn't in the stack but has to merge before #313.

  1. fix(crews): Crew notices report only what the platform measured #292: Crew notices report only what the platform measured (adds migration 020). Not stacked, but it must merge before fix(crews): a proposal launches once and reports every seat #313.
  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)
  5. fix(crews): Crew groups keep seats without thread facts as unknown #300: Crew groups keep seats without thread facts as unknown ⬅️ this PR
  6. fix(crews): a Crew follows its Captain through every lifecycle step #315: A Crew follows its Captain through every lifecycle step (adds migration 022)
  7. feat(crews): custom seats default to Full access and the roster flags seats that will stop #347: custom seats default to Full access

Why #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's state is its seats' states from measured facts, but both web surfaces dropped seats the client had no thread facts for (#227). The sidebar expander skipped any seat whose thread wasn't in client state, and the Fleet page built Crew groups only from placed participants. A reserved seat that was never created, or one recorded but not yet placed, simply vanished, so a Crew under-counted or disappeared entirely.

What I changed

  • apps/server/src/j5/a2a/SpawnedChildrenHttp.ts → projectSpawnedChildren: a Captain's row now also carries every seat on its live rosters that no parent holds (never registered, or registered with no placement). The route looks up Crews the requested rows command (listInvolving with participantIds) and reads each roster seat's membership and placement in one query.
  • apps/server/src/j5/a2a/FleetReadsHttp.ts → projectFleetSquadron: a live roster seat with no ledger row is emitted as an agent row with threadId: null, placed under its Captain, named by its seat.
  • apps/web/src/j5/squadron/spawnedChildren.logic.ts → selectSpawnedChildRows: a Crew seat with no thread stays as a row with thread: undefined (sorted last) instead of being dropped; solo peers still wait for their thread.
  • apps/web/src/j5/squadron/SpawnedChildren.tsx: such a seat renders as a plain, non-clickable row: seat name and "Unknown".
  • apps/web/src/j5/fleet/fleet.logic.ts → buildFleetTree: a Crew seat with no placement parent hangs under its Captain, so the group counts it.

Sidebar, before → after (the reserved critic seat was missing; now "2 seats · 1 unknown"):

Fleet Crew group, before → after:

Why this shape

The live Fleet read already carries full rosters, and the ledger is the only place that knows whether a seat was ever recorded, so I measure "this seat has no row" once on the server and let both clients render what they're given. I rejected building groups from the roster on each client: the sidebar read doesn't carry the roster, and each client would have to re-derive archived-versus-never-created from partial data. crewState.ts already had an unknown state; nothing could reach it until now.

Invariants

  • Nothing is inferred from silence: a seat with no facts is unknown, never idle or settled, and an unknown seat keeps its Crew in Active (fleet-page AC31).
  • A seat whose membership is archived is retired, not unknown, and is never re-added. Retired Crews add nothing.
  • A seat placed under another parent is shown there, not duplicated under the Captain.
  • The Fleet poll still re-reads only involved rows: synthesized seats have crew set, so fleetInvolvedThreadRefs names their Captain and nothing new.

Surfaces

Surface Decision
Entry points (chat, Settings, command palette, keybinding) Unaffected: sidebar expander and Fleet page only.
Clients (web, desktop, mobile) Web and desktop changed. Mobile doesn't use either read.
Providers Unaffected.
Contracts (packages/contracts) Unchanged; the reads return more rows of the existing shapes.
Reverse states A seat moves from unknown to its measured state as soon as its thread arrives (tested).
Connection modes (local, remote, tunnel) Unaffected: same J5 routes, same per-environment scoping.
Upstream files / FORK.md None touched; FORK.md unchanged.
Docs crews.md AC22 and fleet-page.md already describe unknown seats; no change.

Out of scope

  • A seat whose thread exists but hasn't registered yet shows its facts in the sidebar but reads unknown on Fleet until it registers (Fleet gives unregistered seats no thread). This is a launch-time window only.
  • On Fleet, a synthesized seat's title and badge both read the seat name.
  • Unit stop and archive complete when a reserved seat was never created #224 handles the same never-created seats on the stop and archive side.

Upgrade and data

None. No migration; older clients just see the extra rows (the sidebar's older logic still drops them, the Fleet's older tree roots them).

Verification

  • vp test run apps/server/src/j5/a2a: 339 passed, 1 skipped. New cases in SpawnedChildrenHttp.test.ts and FleetReadsHttp.test.ts.
  • vp test run apps/web/src/j5/fleet apps/web/src/j5/squadron apps/web/src/j5/crew/crewState.test.ts: 93 passed. New cases cover a running seat plus an unknown one, a Crew with no placed seat, a thread arriving later, and colliding thread ids across environments.
  • Typecheck clean for web and server; lint clean on the changed files.
  • Screenshots from an isolated dev server on a copy of my data, with a fixture Crew of one placed seat and one reserved, never-created seat.

Review focus

  • The skip rule in projectSpawnedChildren (archived membership or placement under another parent): is there a real state where a live seat is placed elsewhere and should still count under its Captain?
  • The extra listInvolving lookup by Captain runs on every spawned-children read. It's indexed (j5_agent_crew_instance_captain_idx), but check that it stays cheap for 500 requested rows.

Closes #227

Claude Opus 5.5 via Claude Code

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 24, 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: 4d04746c-f296-4815-855c-d42ad2a006b5


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

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 effective changed lines (test files excluded in mixed PRs). labels Sep 24, 2026
@bryantderosier bryantderosier self-assigned this Sep 24, 2026
@bryantderosier
bryantderosier force-pushed the j5/issue-227-unknown-seats branch from c0aef97 to 55a31ee Compare September 25, 2026 14:12
@bryantderosier
bryantderosier changed the base branch from j5/main to j5/crews-launch-once September 25, 2026 14:12
@github-actions github-actions Bot added size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). and removed size:L 100-499 effective changed lines (test files excluded in mixed PRs). labels Sep 25, 2026
@bryantderosier
bryantderosier added this pull request to stack #280 September 25, 2026 14:13
@github-actions github-actions Bot added size:L 100-499 effective changed lines (test files excluded in mixed PRs). and removed size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). labels Sep 25, 2026
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>
@bryantderosier
bryantderosier force-pushed the j5/issue-227-unknown-seats branch from 3912ba5 to 5124a1e Compare September 25, 2026 20:24
@Jacksondr5

Copy link
Copy Markdown
Owner

Decision (Jackson, 2026-09-26): keep #300 as is, server part included. The case worth covering is the few seconds between a seat being recorded and its thread appearing during a normal launch. Only the server read knows about a roster seat with no ledger row, so the web change can't cover this alone. A seat whose thread failed to create is handled by #313 (row dropped, and the Captain told "Not created"). Seats left over after a server restart mid-launch are deliberately not covered: after a restart, the person should expect manual repair. This supersedes the panel's "trim to the web part" suggestion.

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

@Jacksondr5 Jacksondr5 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted by an AI agent on Jackson's behalf.

Approved as is, server part included, per Jackson's 2026-09-26 decision. Verified live by the review panel (Opus 5.5 + Astra): a reserved seat shows as Unknown in the sidebar and on Fleet.

Merge order: #292, #313, #300, then #315 (after #349 lands). #306 rebases after the stack.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bryantderosier
bryantderosier merged commit 80da59f into j5/main Sep 28, 2026
29 checks passed
@bryantderosier
bryantderosier deleted the j5/issue-227-unknown-seats branch September 28, 2026 17:32
bryantderosier added a commit that referenced this pull request Sep 28, 2026
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>
bryantderosier added a commit that referenced this pull request Sep 28, 2026
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 28, 2026
Conflict: #313 removed spawnBriefWithoutCrewContext along with the retry comparison that used it, so this branch's edits to it and its tests are dropped.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bryantderosier added a commit that referenced this pull request Sep 28, 2026
Conflict in FORK.md: kept main's rewritten proposal-flow paragraph (migration 21, resolve-once approvals) with this branch's shared J5_PREAPPROVED_TOOLS wording.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bryantderosier added a commit that referenced this pull request Sep 28, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Development

Successfully merging this pull request may close these issues.

Crew groups and Fleet rows keep seats without thread facts as unknown

2 participants