Skip to content

feat(web): the Fleet page and the sidebar's spawned-children expander - #149

Merged
bryantderosier merged 8 commits into
crews/04-crews-roster-gatefrom
crews/05-fleet-page-and-expander
Sep 21, 2026
Merged

bryantderosier merged 8 commits into
crews/04-crews-roster-gatefrom
crews/05-fleet-page-and-expander

Conversation

@bryantderosier

@bryantderosier bryantderosier commented Sep 15, 2026 •

Copy link
Copy Markdown
Collaborator

Once agents spawn agents the flat sidebar stops telling the truth about what is running. This PR adds the two surfaces that carry it.

The Fleet page lists every agent in every Squadron across connected environments as the placement tree, with each Crew as a collapsible unit under its Captain and a seat-state summary from measured facts; a rail badge beside the inbox bell counts agents owing a reply. Agent-spawned Peer Agents leave the flat sidebar unless pinned, and the row that spawned them expands into its placed children so the work stays one click away, one named collapsible group per Crew it commands plus one for solo peers, since a Captain may run several Crews at once. Retired agents drop from the Fleet read except as placeholders above active descendants, and retired children leave the expander.

Two performance points I'd draw attention to. Crew memberships and children are read incrementally in batches under the route cap; an empty answer stays loaded, and the Fleet poll re-reads only the rows the roster names as involved (seats, their Captains, spawners with placed children), so the per-thread reads follow Crew activity rather than the length of the thread list, and an unchanged batch keeps its snapshot so rows skip their render. Rows without children never subscribe to the shell list. Deferred on purpose: the Fleet page is reachable from the rail icon only (no command palette entry yet), and a hidden agent-spawned peer can be re-shown only by pinning it from the chat view.

Built with Claude Fable 5.1 in Claude Code.

Evidence

New surfaces, so after-only. Before shots of the old text badge and the doubled seat names are in Jackson's #152 comment.

Sidebar: Captain anchor mark, the per-Crew expander, seat rows mirroring the parent card (time on top, provider below).

Sidebar expander

The anchor's tooltip names the commanded Crews.

Captain tooltip

Fleet page with two Captains, three live Crews and a retired one.

Fleet page

🤖 Generated with Claude Code

Closes #205.

@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Sep 15, 2026
@bryantderosier
bryantderosier added this pull request to stack #153 September 15, 2026 21:36
@github-actions github-actions Bot added the size:XXL 1,000+ effective changed lines (test files excluded in mixed PRs). label Sep 15, 2026
@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch from 146de83 to 9ca78c7 Compare September 15, 2026 21:44
@bryantderosier bryantderosier self-assigned this Sep 15, 2026
@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch from 9ca78c7 to 74750ea Compare September 15, 2026 22:25
@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch 4 times, most recently from bb7539f to 261ebbb Compare September 17, 2026 01:59

@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.

The Fleet page and the per-Crew expander groups are the right surfaces, the cross-environment scoping is correct, retired agents are handled as the definition says, and childless rows really do avoid the shell subscription. The grouping logic is stable and its tests are pure logic.

Requesting changes on the performance claim. The body says memberships and children are read incrementally so the reads stop scaling with the thread list. That holds for the shells-change path only. The 30s poll (inline on fleetClient.ts) re-pends every requested thread id via refreshRequested in packages/client-runtime/src/j5/threadHomes.ts L135, and the sidebar requests every thread, so each tick re-reads memberships and children for the whole list in batches of 200 per environment and swaps the store map so every row re-renders. Empty results also delete their keys rather than marking the row loaded, so a reorder of the requested set refetches ordinary rows too. With the inbox count and the proposals poll from 148 that is three foreground polls. AGENTS.md: performance regressions are usually too much data over the wire. Fix inside this PR: poll only rows that already have memberships or children, and cache negative results; the longer fix is a J5 change stream, which I will file separately.

Minor:

  • SidebarChrome.tsx L85 changes the upstream wrapper's classes rather than appending (FORK.md: do not refactor surrounding upstream code).
  • Expansion keys omit the environment; two environments with the same thread id share state. Negligible today, but FORK.md case 34 scopes identities for this reason.
  • Hit every surface: the Fleet page is reachable only from the rail icon (no palette entry), and a hidden agent-spawned peer can be re-shown only by pinning from the chat view. Fine if deferred; say so in the body.

Reviewed by Claude Fable 5.1 in Claude Code, with an independent pass by GPT-6-Astra in Codex.

Comment thread apps/web/src/j5/fleet/fleetClient.ts Outdated
Comment thread apps/web/src/components/sidebar/SidebarChrome.tsx Outdated
@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch from 261ebbb to a453d8a Compare September 17, 2026 03:24
@bryantderosier

Copy link
Copy Markdown
Collaborator Author

The poll is bounded in a453d8a7f: it re-reads only the rows the roster names as involved, negatives stay loaded, and an unchanged batch keeps its snapshot. The gap sits on the J5 rail entry, expansion keys carry the environment, and the body now says what is deferred.

@bryantderosier

Copy link
Copy Markdown
Collaborator Author

From your #152 screenshots, in eac842764: the Captain badge is an anchor mark with the commanded Crews in its tooltip, on the thread card and the Fleet row; seat rows mirror the parent card (time on the top line, provider icon on the bottom); and the seat badge shows only when the thread was renamed, since a seat thread is now titled by its seat (#148) and the badge repeated it.

@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.

Round three. The poll now reads only involved rows and the negative cache is correct across environments and request-set changes; expansion keys are scoped; the deferrals are stated; the anchor mark, tooltip, and seat-row layout are J5-owned files with no continuous animation.

Retraction: my note on SidebarChrome.tsx L85 was wrong. That wrapper was added by the fork under FORK.md case 6, so editing it was never an upstream refactor. Sorry for the noise.

Requesting changes on one regression the narrowed poll introduced (inline on fleet.logic.ts). One line.

Minor: useFleetRefresh's .then has no .catch; if the sources refresh rejects, the involved re-read is skipped and the rejection is unhandled.

Reviewed by Claude Fable 5.1 in Claude Code, with an independent pass by GPT-6-Astra in Codex.

Comment thread apps/web/src/j5/fleet/fleet.logic.ts
@bryantderosier

Copy link
Copy Markdown
Collaborator Author

05424434e restores the invalidation: every Crew's Captain is involved on the poll, retired Crews included, and the refresh has a catch. Thanks for the SidebarChrome retraction; I closed that thread with the change standing.

@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch 2 times, most recently from 1b17525 to 7978a19 Compare September 18, 2026 14:22

@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.

Round five. The failed state on the Fleet row and expander is right (driven by the latest run's status, cleared by a later run), and the held-row refresh clears a retired Crew's Captain mark on the next tick. One residual on that fix, inline.

Reviewed by Claude Fable 5.1 in Claude Code, with an independent pass by GPT-6-Astra in Codex.

Comment thread packages/client-runtime/src/j5/threadHomes.ts
@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch from 7978a19 to 5c864ce Compare September 21, 2026 11:56
@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch from 5c864ce to 4f0a8b9 Compare September 21, 2026 12:16

@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.

Archived instances are now omitted from a Captain's commanded Crews on the server, so a retired Captain leaves the client's held set and the poll cost follows involvement. Approving.

Two cleanups for whenever: the crew reference's archived flag is now always false on the wire, so the client's archived filter is dead weight; and the test covering the archived Captain is the projection function, not the HTTP route, which is fine but narrower than the reply says.

Reviewed by Claude Fable 5.1 in Claude Code, with an independent pass by GPT-6-Astra in Codex.

@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch from 4f0a8b9 to 95346f7 Compare September 21, 2026 14:16
@bryantderosier

Copy link
Copy Markdown
Collaborator Author

Both cleanups are in 95346f75c: CrewRef drops the always-false archived flag on the wire and the client filters that read it, and the tests follow. You are right that the archived-Captain coverage is on the projection function, not the route; the route test still covers the authenticated read path, and the projection is where the omission lives.

bryantderosier and others added 8 commits September 21, 2026 11:07
The Fleet page lists every agent in every Squadron across connected environments as the placement
tree, with each Crew as a collapsible unit under its Captain and a seat-state summary from measured
facts; a rail badge counts agents owing a reply. Agent-spawned Peer Agents leave the flat sidebar
unless pinned, and the row that spawned them expands into its placed children so the work stays one
click away. Retired agents drop from the Fleet read except as placeholders above active descendants,
and retired children leave the expander. Crew memberships and children are read incrementally in
batches under the route cap and re-read on the Fleet poll, so the reads stop scaling with the
thread list; rows without children never subscribe to the shell list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A Captain may command several Crews at once, so its sidebar row no longer folds every placed child
into one "N crew" toggle. The children are grouped by Crew, newest activity first, and each group is
its own collapsible header carrying the Crew's name and a state summary from its seats' measured
facts; solo Peer Agents the row spawned outside any Crew form one more group. Expansion is
remembered per group under its parent instead of per parent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The 30-second Fleet poll re-pended every thread id the sidebar had requested, so each tick
re-read Crew memberships and children for the whole list in batches and swapped the store
map so every row re-rendered. The poll now re-reads only the rows the roster response names
as involved (seats, their Captains, spawners with placed children), an empty answer stays
loaded so a reordered row set never re-reads it, and a batch that changes nothing keeps the
previous snapshot so rows skip their render. A Captain that gained a Crew on another device
is named by the roster on the next poll and gets its chip that way.

The rail entry carries its own gap so the upstream header wrapper keeps its spacing classes,
and expander state is keyed by environment as well as parent and group.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rent card

From Jackson's screenshot on PR 152: the sidebar card was out of room, so the Captain badge is now an anchor mark with the commanded Crews in its tooltip, on the thread card and the Fleet row alike. Seat rows in the expander mirror the parent card (time on the top line, provider icon on the bottom) and show the seat badge only when the thread was renamed, since seat threads are now titled by seat.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The narrowed poll built its involved set from live seats and placement
parents only, so once a Crew retired its Captain fell out of the set and
the cached anchor mark stayed until reload. Every Crew the read returns,
retired included, now names its Captain for the re-read, and a failed
roster refresh is logged instead of left as an unhandled rejection.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…a retired Crew's Captain clears

The badge poll reads the live roster only, and the server filters retired Crews out of it before
the response leaves, so the involved-refs helper never saw a retired Crew on that read; only the
Fleet page's detail read did. The stale Captain mark survived the last Crew retiring until reload.

Rather than paying for retired rosters on every tick, the store's poll re-read now also names every
requested row that currently holds a value: a Captain whose last Crew retired is not named by the
live roster, but its row still shows a chip, so it is re-read once more and clears. The cost still
follows involvement, since a row holds a value only while it was involved. The helper goes back to
naming live involvement only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ow and the expander

The seat states were running, needs-you, settled, idle, archived, and unknown, so a seat that died
on its first turn fell through to idle, the quiet default, and the Fleet row and the Captain's
expander showed nothing (Jackson's dogfood, 2026-09-17). A failed last run is now its own state,
counted and said in the summary ahead of what the seat may still owe the person.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@bryantderosier
bryantderosier force-pushed the crews/05-fleet-page-and-expander branch from 95346f7 to d41da36 Compare September 21, 2026 15:07
@bryantderosier
bryantderosier merged commit ee73aed into j5/main Sep 21, 2026
20 checks passed
@bryantderosier
bryantderosier deleted the crews/05-fleet-page-and-expander branch September 21, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ 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.

Fleet page and sidebar expander for agent-spawned agents

2 participants