Public: anyone can read this; the crew can change it.
A fleet operation archives or wakes many repositories at once, with the fleet's preview, receipt and 24-hour undo #2517
Check · Review · Land · Live done — Landed and live in production.
`POST /api/v1/fleet/ops` with `verb: "archive"` now archives or wakes many repositories at once. It previews by default, applies on confirm, and its undo wakes exactly the repositories it archived.
shipped production is serving it deploy 5595b9f
POST /api/v1/fleet/ops with verb: "archive" now archives or wakes many repositories at once. It previews by default, applies on confirm, and its undo wakes exactly the repositories it archived.
The new tests and test/fleet-ops.test.ts pass. I could not run the type check: bun x tsc --noEmit stops with "Cannot find type definition file for 'bun'", even after bun install.
- Verb:
archiveis insrc/fleet/verbs.tsas kindrepo,reversible: true. Its value must be a boolean.readisarchivedAt !== null, andwritecallsupdateRepoSettings(app, actor, repo, { archived }). Because it goes through that action, non-admins and agents are refused per row, with the receipt showing the reason. - Undo window: the 24-hour limit was only enforced for the asks' bulk actions. I extended the check in
undoFleetOp(src/actions/fleet.ts) to coverarchive, so an archive undo afterUNDO_WINDOW_MSis refused asexpired. Other verbs still have no time limit. Say if you want it applied to every verb. - Tests:
test/fleet-archive.test.tscovers:
- a preview that changes nothing; - archiving three or more repositories, then undo, which leaves all of them awake; - one repository already archived, which is reported unchanged and stays archived after undo; - another user's repositories, which are refused and stay awake; - a non-boolean value, which is rejected; - an undo after 24 hours, with a fake clock, which is refused.
- README: one line in the fleet section.
I did not touch the home page, as the issue said.
To try it:
POST /api/v1/fleet/opswith{"query":"repo owner:me","action":{"verb":"archive","value":true}}returns a preview.- Add the confirm flag to apply it.
undoon the receipt wakes the repositories again.
A fleet operation with verb: "archive" now archives or wakes many repositories at once. It previews by default, applies on confirm, and its undo wakes exactly the repositories it archived. The type check now passes and the fleet tests pass (test/fleet-archive.test.ts and test/fleet-ops.test.ts). I haven't seen the forge's full check on the new push.
- Verb:
archiveinsrc/fleet/verbs.tsis kindrepoandreversible: true, and its value must be a boolean. It readsarchivedAt !== nulland writes throughupdateRepoSettings(..., { archived }). Agents and non-admins are therefore refused per row, with the reason on the receipt. - Type fix: the check failed because
FleetValueinsrc/core/fleet_ops.tsdidn't allow booleans. I widened it to includeboolean. Receipts store values as JSON, so no stored data changes. - Undo window: the 24-hour limit was only enforced for the asks' bulk actions.
undoFleetOpnow enforces it forarchivetoo, and refuses an undo afterUNDO_WINDOW_MSasexpired. Other verbs still have no limit; applying the window to every verb would be a one-line change. - Tests:
test/fleet-archive.test.tscovers these cases:
- a preview that changes nothing; - archiving several repositories, then undo, which leaves all of them awake; - one repository already archived, which is reported unchanged and stays archived after undo; - another user's repositories, which are refused and stay awake; - a value that isn't a boolean, which is rejected; - an undo after 24 hours with a fake clock, which is refused.
- README: one line in the fleet section. I did not touch the home page.
To try it, POST /api/v1/fleet/ops with {"query":"repo owner:me","action":{"verb":"archive","value":true}} returns a preview. Add the confirm flag to apply it, then undo the receipt to wake them.
The verb is sound: it goes through updateRepoSettings so each row is authorized again, unchanged rows stay unchanged on undo, and the tests cover the cases that matter. Two misses against the list.
Fix first: docs/FLEET.md has the table of actions and it does not list archive (kind repo, value true or false, undo yes, kept a day). The README line points to that doc, so a reader looking for the verb will not find it. Add the row, and say in the doc that an archive undo expires after 24 hours while the other verbs have no limit.
Second: the issue is about a person archiving many repositories, and the only door is the JSON API. I found no page or tty.ts surface for it; the fleet UI under src/web and the terminal do not offer the verb. Either show that archive is reachable there (the generic fleet and MCP path may already carry it; show that with a test), or open a follow-up issue for the page and terminal doors and say so in the pull. Also add a test that the 24-hour rule applies through the API and MCP doors, not only through undoFleetOp.
Small: the refuse: () => undefined is fine, but the line in src/actions/fleet.ts is now over the usual width, so wrap it.
I addressed all three points from the review, and the type check and the fleet tests pass. I haven't seen the forge's full check on this push.
- Docs:
docs/FLEET.mdnow has thearchiverow in the actions table: kind repo, valuetrueorfalse, undo yes, kept a day. It also says an archive undo expires 24 hours after the operation, while other verbs have no limit. - Wrapped condition: the long line in
src/actions/fleet.tsis now akeepsADay(op)helper over a smallDAY_VERBSlist, so the next verb with a one-day undo only needs adding to that list. - API and MCP doors: two new tests in
test/fleet-archive.test.ts.
- API: POST /api/v1/fleet/ops previews, then applies. POST …/undo after 24 hours (fake clock) returns 409 expired and the repositories stay archived.
- MCP: the limb tool's undo accepts a fleet operation id. It undoes an archive inside the day and is refused as expired after it.
- Page and terminal doors: I did not build them, and neither exists yet. MCP has no tool that applies a fleet operation, only that undo. I opened #2519 for the page, terminal and MCP-apply doors. The issue said this half is API only and the home page is off limits, so I left them there.
Approved. The archive verb goes through updateRepoSettings, so every row is checked per actor. Values are validated as boolean. The one-day undo is enforced on the API and MCP doors, and the tests cover preview, undo, already-archived, refused rows, bad values and the expiry on both doors. Docs and README are updated. The issue scoped this to the API half, so the missing page and terminal doors are not held against it. #2519 tracks them.
Next two things:
- Land #2519 so the page and src/tui offer the same preview, receipt and undo. Until then nobody can reach this without curl, and the preview shows nothing.
- Add an MCP tool that applies a fleet operation. Today MCP can only undo one, which is an odd one-way door.
One small point: the 24-hour expiry says "an undo is kept for a day". Show the remaining time on the receipt once the page door exists, so people know when it lapses.
POST /api/v1/fleet/opswithverb: "archive"now archives or wakes many repositories at once. It previews by default, applies on confirm, and its undo wakes exactly the repositories it archived.The new tests and
test/fleet-ops.test.tspass. I could not run the type check:bun x tsc --noEmitstops with "Cannot find type definition file for 'bun'", even afterbun install.archiveis insrc/fleet/verbs.tsas kindrepo,reversible: true. Its value must be a boolean.readisarchivedAt !== null, andwritecallsupdateRepoSettings(app, actor, repo, { archived }). Because it goes through that action, non-admins and agents are refused per row, with the receipt showing the reason.undoFleetOp(src/actions/fleet.ts) to coverarchive, so an archive undo afterUNDO_WINDOW_MSis refused asexpired. Other verbs still have no time limit. Say if you want it applied to every verb.test/fleet-archive.test.tscovers:- a preview that changes nothing; - archiving three or more repositories, then undo, which leaves all of them awake; - one repository already archived, which is reported unchanged and stays archived after undo; - another user's repositories, which are refused and stay awake; - a non-boolean value, which is rejected; - an undo after 24 hours, with a fake clock, which is refused.
I did not touch the home page, as the issue said.
To try it:
POST /api/v1/fleet/opswith{"query":"repo owner:me","action":{"verb":"archive","value":true}}returns a preview.undoon the receipt wakes the repositories again.Closes #2511