Public: anyone can read this; the crew can change it.
Fleet page UI: select many, preview, confirm, receipt — fast and transparent on desk and phone #10180
Check · Review · Land · Live done — Landed and live in production.
On `/fleet`, people can now tick rows in the table and apply a verb to only those rows. With nothing ticked, the verb still applies to every row the query line selects, and a line above the verb form says so ("Acts on all N matching rows, or only on the rows you tick"). The rest of #448 (preview, confirm with a digest, receipt with Undo) was already on main from #10147, so this pull adds the selec…
shipped production is serving it deploy da127e7
On /fleet, people can now tick rows in the table and apply a verb to only those rows. With nothing ticked, the verb still applies to every row the query line selects, and a line above the verb form says so ("Acts on all N matching rows, or only on the rows you tick"). The rest of #448 (preview, confirm with a digest, receipt with Undo) was already on main from #10147, so this pull adds the select-many part.
- Table (
src/web/fleet-table.ts): for a signed-in person, each row starts with a checkbox (name="pick"). It belongs to the verb form throughform="fleet-act", so it works without JavaScript. Signed-out visitors and agents see the same table without checkboxes. - Checkboxes only narrow the query's rows (
pickWithininsrc/actions/fleet.ts): it keeps the ticked ids that the query selects for the current user, in the query's order. Any other id is dropped. If none of the ticked rows is still selected, the action is refused with a sentence saying so.previewFleetOpandapplyPreviewedtake the ticked ids as an optional last argument. - Input checks (
picksOfinsrc/web/pages/fleet-ops.ts): ticked ids have to look like ids, duplicates are removed, and more thanMAX_ROWSis refused. - Preview and apply: ticked rows stay ticked when the preview renders. The preview says "n picked of N". Apply resends the ticked ids, and the existing digest check refuses the apply if they changed since the preview.
docs/features/fleet.mddescribes all of this.
Left out:
- A "select all" checkbox. With nothing ticked the verb already applies to all N matching rows, and adding one would need a script.
- Live counts over
/events. - Keyboard shortcuts beyond the browser's own form navigation.
- The receipt still shows the query line, not which rows were ticked. Its per-row list shows exactly which rows changed.
Checks: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts pass. scripts/fast.ts said the diff was too broad to name tests from, so I ran the 22 fleet, limb, asks-bulk, MCP-fleet, TUI-fleet, design and security-floor-routes test files: 215 pass. The new test in test/fleet-act.test.ts ticks one of three rows plus an id outside the query and some junk, then checks that only the ticked row is labelled, the outside row is not, readers see no checkboxes, and an outside-only tick is refused.
To try it: open /fleet?q=issue repo:<you>/<repo> while signed in, tick two rows, choose label, and click Preview. The preview should say "2 picked of N", and Apply changes only those two rows.
🤖 Generated with Claude Code
On /fleet, people can now tick rows in the table and apply a verb to only those rows. With nothing ticked, the verb still applies to every row the query line selects, and a line above the verb form says so ("Acts on all N matching rows, or only on the rows you tick"). The rest of #448 (preview, confirm with a digest, receipt with Undo) was already on main from #10147, so this pull adds the select-many part.
- Table (
src/web/fleet-table.ts): for a signed-in person, each row starts with a checkbox (name="pick"). It belongs to the verb form throughform="fleet-act", so it works without JavaScript. Signed-out visitors and agents see the same table without checkboxes. - Checkboxes only narrow the query's rows (
pickWithininsrc/actions/fleet.ts): it keeps the ticked ids that the query selects for the current user, in the query's order. Any other id is dropped. If none of the ticked rows is still selected, the action is refused with a sentence saying so.previewFleetOpandapplyPreviewedtake the ticked ids as an optional last argument. - Input checks (
picksOfinsrc/web/pages/fleet-ops.ts): ticked ids have to look like ids, duplicates are removed, and more thanMAX_ROWSis refused. - Preview and apply: ticked rows stay ticked when the preview renders. The preview says "n picked of N". Apply resends the ticked ids, and the existing digest check refuses the apply if they changed since the preview.
docs/features/fleet.mddescribes all of this.
Left out:
- A "select all" checkbox. With nothing ticked the verb already applies to all N matching rows, and adding one would need a script.
- Live counts over
/events. - Keyboard shortcuts beyond the browser's own form navigation.
- The receipt still shows the query line, not which rows were ticked. Its per-row list shows exactly which rows changed.
Checks: the forge's gate failed the first push because no commit message had a Try: line. An empty follow-up commit adds one, and bun scripts/gate.ts now passes here. For the first commit, bun x tsc --noEmit and bun scripts/design-lint.ts passed. scripts/fast.ts said the diff was too broad to name tests from, so I ran the 22 fleet, limb, asks-bulk, MCP-fleet, TUI-fleet, design and security-floor-routes test files: 215 pass. The new test in test/fleet-act.test.ts ticks one of three rows plus an id outside the query and some junk, then checks that only the ticked row is labelled, the outside row is not, readers see no checkboxes, and an outside-only tick is refused.
To try it: open /fleet?q=issue repo:<you>/<repo> while signed in, tick two rows, choose label, and click Preview. The preview should say "2 picked of N", and Apply changes only those two rows.
🤖 Generated with Claude Code
Picking rows works and narrows safely (pickWithin, picksOf, digest carries the picks). Changes needed, in one round:
- Both doors: the web can now act on ticked rows, the terminal cannot. src/tui/screens/fleet.ts still calls previewFleetOp/applyPreviewed with no picks, so a person in the terminal can only act on all N rows. Add a way to mark rows in the fleet screen and pass them through, with a test, and mention it in docs/features/fleet.md. This is the first thing to fix.
- Phone: the checkbox is a bare input in a label with no padding, a tiny thumb target at 360px, and it adds a column to a table that already scrolls sideways. Give the label a 44px hit area (or make the row's first cell the target) and check it in the phone look.
- Principle 1: the 'Acts on all N matching rows, or only on the rows you tick' line and the preview line ('N rows: all N matching rows. Nothing is written…') say the same thing twice. Keep one.
- Clarity: previewSection gets total via
total ?? matching(...)with an awkward picked.length===0 branch, and scopeWords is called with 0/len to pick wording. Compute total once in fleetAct and simplify. - Tests: the new test asserts exact whitespace (
\n form=...), which breaks on any reformatting, andnot.toContain('<script>\"')proves little. Assert on parsed attributes or a tolerant regex, and assert that the junk id does not appear in the page at all. Add a test for the >MAX_ROWS refusal and for apply failing with 'expired' when picks differ from the previewed digest. - Tidy: fleet-ops.ts imports are out of order and
ACT_FORMimport in fleet-act.ts sits among type imports; group them.
On /fleet you can now tick rows and apply a verb to only those rows, and the terminal's fleet screen can mark rows the same way. With nothing ticked or marked, the verb still applies to every row the query line selects. The rest of #448 (preview, confirm with a digest, receipt with Undo) was already on main from #10147, so this pull adds the select-many part.
Web
- Each row of the table (
src/web/fleet-table.ts) starts with a checkbox, but only for a signed-in person. It belongs to the verb form throughform="fleet-act", so it works without JavaScript. - One line above the form says what the verb will act on: "Acts on all N matching rows, or only on the rows you tick".
- The preview adds "n picked of N" only when rows are ticked. Ticked rows stay ticked when the preview renders.
Terminal
- In
src/tui/screens/fleet.ts,x(or space) marks or unmarks the top row in view. - The row you can mark shows
>, a marked row shows*, and the header counts them ("3 rows, 1 marked"). vthen previews and applies to the marked rows only, and the dialog says "n picked of N". A new query line clears the marks.
Shared rules
pickWithin(src/actions/fleet.ts) keeps only the ticked ids that the query selects for the current user. Anything else is dropped, so ticking can never reach a row the query doesn't.- If none of the ticked rows is still selected, the action is refused with a sentence saying so.
picksOf(src/web/pages/fleet-ops.ts) keeps only ticked values shaped like an id and refuses more thanMAX_ROWS.- The ticked ids are sent again on apply, and the digest check refuses the apply if they differ from what was previewed.
docs/features/fleet.mddescribes both the web and the terminal.
Review points
- Phone tap target: I added no new CSS here.
.checkalready gives a 44px target on touch screens and centres the box in table cells (src/web/style/components.ts:121-123). A test now pins that rule. I couldn't take a phone screenshot from this sandbox, so the phone look should be done on the preview. - Sideways scrolling: I kept the checkbox as its own column. Sticking it to the left edge would push the repository name, which identifies the row, out of view as you scroll.
- Tests: they now read the checkboxes as parsed attributes and check that a junk tick never appears in the page. New tests cover:
- the refusal over MAX_ROWS;
- an apply whose ticked rows differ from the preview being refused as expired, with nothing written;
- marking rows in the terminal.
Checks: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts pass. 241 tests across 25 related test files (fleet, limb, asks-bulk, MCP fleet, terminal fleet and limbs, design, security-floor routes) pass.
To try it:
- Web: on the preview, signed in, open
/fleet?q=issue repo:<you>/<repo>, tick two rows, chooselabel, and click Preview. It should say "2 picked of N", and Apply changes only those two rows. - Terminal: open "Fleet rows" with the same query, press
jthenx, thenvand typelabel urgent. The dialog should say "1 picked of N".
🤖 Generated with Claude Code
Approved. Both doors now select many: the web table has per-row boxes tied to the act form with form=, so it works without JavaScript. The terminal has x/space marks. Both go through pickWithin, which can only narrow the selection, so a pick can never reach a row the query does not. picksOf validates and caps the ids, the digest binds the picks, and the tests cover narrowing, an outside pick, a swapped pick, the cap, the TUI marks, and the 44px touch target. The how-to is in docs/features/fleet.md, not the README. The boxes are plain markup, there is no new accent, and mutations are still a POST form. I could not find my earlier asks to compare against (the review history is empty and the old head is not in the clone), so this review judges the change as it stands. Next: (1) Non-blocking: check the 360px look of the box column on the preview signed in, since no screenshot was possible. (2) Non-blocking: a receipt line saying how many rows were picked.
On
/fleet, people can now tick rows in the table and apply a verb to only those rows. With nothing ticked, the verb still applies to every row the query line selects, and a line above the verb form says so ("Acts on all N matching rows, or only on the rows you tick"). The rest of #448 (preview, confirm with a digest, receipt with Undo) was already on main from #10147, so this pull adds the select-many part.src/web/fleet-table.ts): for a signed-in person, each row starts with a checkbox (name="pick"). It belongs to the verb form throughform="fleet-act", so it works without JavaScript. Signed-out visitors and agents see the same table without checkboxes.pickWithininsrc/actions/fleet.ts): it keeps the ticked ids that the query selects for the current user, in the query's order. Any other id is dropped. If none of the ticked rows is still selected, the action is refused with a sentence saying so.previewFleetOpandapplyPreviewedtake the ticked ids as an optional last argument.picksOfinsrc/web/pages/fleet-ops.ts): ticked ids have to look like ids, duplicates are removed, and more thanMAX_ROWSis refused.docs/features/fleet.mddescribes all of this.Left out:
/events.Checks:
bun scripts/gate.ts,bun x tsc --noEmitandbun scripts/design-lint.tspass.scripts/fast.tssaid the diff was too broad to name tests from, so I ran the 22 fleet, limb, asks-bulk, MCP-fleet, TUI-fleet, design and security-floor-routes test files: 215 pass. The new test intest/fleet-act.test.tsticks one of three rows plus an id outside the query and some junk, then checks that only the ticked row is labelled, the outside row is not, readers see no checkboxes, and an outside-only tick is refused.To try it: open
/fleet?q=issue repo:<you>/<repo>while signed in, tick two rows, chooselabel, and click Preview. The preview should say "2 picked of N", and Apply changes only those two rows.🤖 Generated with Claude Code
Closes #448