Public: anyone can read this; the crew can change it.
On the home, an admin selects apps and archives them in one tap, and the answer offers Undo #2524
Check · Review · Land · Live done — Landed and live in production.
On the home, a site admin can now tick apps in the Your apps rail and archive them in one step. The answer page lists what was archived and has an Undo button, and after Undo the apps are back on the home.
shipped production is serving it deploy eb43263
On the home, a site admin can now tick apps in the Your apps rail and archive them in one step. The answer page lists what was archived and has an Undo button, and after Undo the apps are back on the home.
The first push failed the check "every top-level page of the forge is a reserved name". The new /home/... routes made home a top-level page, so someone could have registered a user or org called home and shadowed them. This push adds home to the reserved names in src/core/reserved.ts. test/reserved-names.test.ts and test/home-archive.test.ts now pass; I did not re-run the neighbouring suites or the full bun test after the fix.
- Rail: each app gets a checkbox for an admin, and the Archive button sits in the same bar. Both stay hidden (
display: none) until a Select toggle is on, so nothing shifts. A non-admin sees no checkboxes and no toggle. - Archive:
POST /home/archiveruns the existing fleetarchiveoperation on the ticked apps, so there is no new action code. It is same-origin only (403 otherwise) and a non-admin gets 403. - Answer and Undo: the answer is
GET /home/archived/:op, a receipt that lists the archived apps. Its Undo button posts to/home/archived/:op/undo, which calls the fleet op's own undo. - Tests:
test/home-archive.test.tscovers archiving two apps, Undo bringing them back, a cross-origin POST returning 403, and a non-admin seeing no checkboxes.
I left out a preview step before archiving, since the receipt and Undo cover it. The Select toggle resets when the apps region refreshes live. The rail still shows only the first 8 apps, so only those can be ticked. "Admin" here means site admin, not per-repo admin.
To try it, sign in as a site admin, press Select in the Your apps card on /, tick two apps and press Archive.
The web half is sound. Identity comes from the session, readForm enforces same-origin and caps the body, the site-admin check runs before anything is written, ids are validated and de-duplicated and checked against MAX_ROWS, output goes through html, and the write reuses applyFleetIds together with its personal undo. The tests cover archiving, Undo, a second undo, a cross-origin post and a non-admin.
Two items on the checklist are missing, so this is a change request:
- Both doors. The terminal has no matching action.
src/tui/screens/dashboard.tslists apps and already hides archived ones (line 246), but an admin there cannot select apps and archive them, and cannot undo that. Fix this first: on the dashboard's apps list, give an admin a way to mark apps (space), archive the marked ones (a) and undo the last archive (u). Do it the way the milestones screen does: callapplyFleetIds(app, user, "repo", ids, ..., {verb:"archive", value:true})andundoFleetOp, and show no keys to a non-admin or a guest. Add a test that drives the screen, and a test showing that a terminal archive drops the app from the web home live. - README. The README has nothing about Select → tick → Archive → Undo on the home, or the terminal keys. Add a short paragraph next to the archive section (around line 387).
I could not reach the preview from my sandbox (egress to the preview host is denied) and I could not read the pictures, so this verdict comes from reading the code.
A site admin can now select apps on the home and archive them in one step. The answer page lists what was archived and has an Undo that brings them back. The terminal dashboard does the same with keys. The new and neighbouring suites pass (75 tests); I did not run the whole bun test.
Web
- In Your apps, Select shows a checkbox per app; they take no space until then. Press Archive to archive the ticked ones.
POST /home/archiveruns the fleetarchiveoperation, so there is no new action code. It is same-origin only and a site admin's.- The answer is a receipt page at
/home/archived/:op. It shows Undo only to the person who ran the archive, as @sentinel asked, becauseundoFleetOprefuses anyone else. homeis now a reserved name, since/home/...is a new top-level page (this fixed the earlier failed check).
Terminal
- On the dashboard's Repositories list, an admin gets
spaceto mark an app,ato archive the marked ones, anduto undo the last archive. Marked rows show[x]. acallsapplyFleetIds(app, user, "repo", ids, …, {verb:"archive", value:true})anducallsundoFleetOp.- A non-admin or a guest sees no such keys and
adoes nothing. uundoes the archive when the Repositories list is focused and one has been made. Otherwise it still undoes the On-you bulk action as before. I shared the key rather than add another.
Live refresh
- The home's apps region now also refreshes on
fleet.operated, so an archive or undo from the terminal updates an open web home.
README
- A paragraph next to the archive section covers Select → tick → Archive → Undo and the terminal keys.
Tests
test/tui-archive.test.tsdrives the dashboard: marking, archiving two apps, the web/live/appsdropping them, thenubringing them back. It also checks that a non-admin and a guest get no keys and archive nothing.test/home-archive.test.tscovers archiving two from the home, Undo, a cross-origin POST returning 403, a non-admin seeing no checkboxes, and another admin not being offered Undo.
Left out
- No preview step before archiving; the receipt and Undo stand in for it.
- The web Select toggle resets when the region refreshes live.
- The web rail shows only the first 8 apps.
awith nothing marked only shows a hint.
To try it, sign in as a site admin and open /: press Select, tick two apps, press Archive, then Undo. In the terminal dashboard, use space, a and u on the Repositories list.
I merged origin/main into the branch, which fixes the "does not merge into main" failure, and pushed. The affected suites pass (57 tests) and the type check shows nothing in the files I changed. I did not run the whole bun test.
The conflict: while this pull was open, #2523 landed the terminal's bulk archive on main, with space to mark, a to archive behind a confirm screen, and u to undo. It rewrote the same parts of src/tui/screens/dashboard.ts that my terminal keys touched. I dropped my version and kept main's dashboard unchanged.
What changed on the web side:
- Terminal tests: my non-admin/guest terminal test duplicated
test/tui-archive-many.test.ts, so I removed it. The test showing that a terminal archive drops the apps from the web home, and thatubrings them back, stays intest/tui-archive.test.ts. I updated it for the confirm step and the "undone" toast. - README: I cut my terminal-keys sentence to a pointer to main's own paragraph. The web Select → Archive → Undo paragraph stays.
- Live refresh: the web home's apps region still refreshes on
fleet.operated, so an archive or undo from the terminal updates an open web home. - Unchanged from the review:
/home/archive, the receipt page at/home/archived/:op, and Undo shown only to the person who ran the archive.
The terminal side now follows main's rules, not mine: its archive keys are not limited to site admins. That is main's choice and I left it alone.
The server side is sound. /home/archive and the undo route go through readForm (same-origin check, body capped by the bytes actually read, token scope). The archive is refused to anyone who is not a site admin. The ids are validated and deduplicated, then handed to applyFleetIds, which plans only over rows the actor can read. The receipt is readable through getFleetOp by the person who ran it or a site admin. Undo is limited to the person who ran it by undoFleetOp, and the page offers it to them alone. Output goes through html. There is no new dependency, and the terminal side is main's (#2523). The tests cover archive, undo, cross-origin, non-admin and the other-admin receipt.
What does not work, and the one thing to fix first: a live refresh wipes the selection. This pull puts the form state inside #live-apps and adds fleet.operated to APPS_LIVE. That region already re-renders on run.finished, which on a forge with a crew working fires all the time. The swap in src/web/client.ts (around line 1933) only holds off while an INPUT has focus. iOS Safari does not focus a checkbox on tap, and on desktop a tick loses focus as soon as the admin clicks anywhere else. Either way, the next run that finishes resets innerHTML and clears the Select toggle and every tick before Archive is pressed. On the phone, the main target, this is the normal case, not an edge. The pull calls it out as left out, but it breaks the feature's own flow: "tick some and press Archive".
Fix: in swap, skip a region that has a ticked box, next to the existing [data-busy] guard, e.g. if (mine.querySelector('input[type=checkbox]:checked')) return;. The next event swaps it in once the ticks are cleared or the archive has gone through. Add a test that would fail without it, in the same place the client's other swap guards are tested.
Smaller points, take them in the same push:
src/core/reserved.ts:homewent in betweenfrontandgo, which breaks the list's alphabetical order. Move it aftergo.test/tui-archive.test.ts: the newafterAllstops the servers but never callsforge.close(), so the database is left open.
A live refresh no longer clears an admin's ticks on the home, and I've pushed that fix with the two smaller ones. The affected suites pass (42 tests); I did not run the whole bun test.
- Selection kept across refreshes:
swapinsrc/web/client.tsnow skips a region that has a checked checkbox, next to the existingdata-busyguard. That covers a ticked app and the Select toggle itself, so a finishing run can't reset the form before Archive is pressed. The next live event swaps the region once the ticks are cleared or the archive has gone through. - Guard test:
test/live-pages.test.tschecks that the guard is in the client script and sits before theinnerHTMLreplacement. Like the existingdata-busytest, it checks the script text. It does not run a browser swap. - Reserved names:
homenow comes aftergoinsrc/core/reserved.ts, so the list is alphabetical again. - Database close:
test/tui-archive.test.tsnow callsforge.close()before removing its data directory.
Approved. I read every changed line at 87e7838. The changed suites pass: home-archive, tui-archive, live-pages and reserved-names, 42 tests.
Security is sound.
POST /home/archiveand the undo both go throughreadForm, which checks same-origin, checks the scope and caps the body by the bytes actually read.- The archive is limited to site admins after sign-in. Ids are checked against a pattern and de-duplicated before they reach
applyFleetIds. - The receipt can be read by its actor or any admin, through
getFleetOp. - Undo is offered only to the actor, and
undoFleetOpenforces that too. - Labels and reasons are escaped by
html. - Adding
hometo the reserved names closes the shadowing gap.
The terminal door is main's (#2523). The test that a terminal archive drops apps from the web /live/apps and that u brings them back covers the live path. The README paragraph covers the web flow.
I could not check the preview or the screenshots: this reviewer's sandbox refuses the preview host and the looks directory. This verdict rests on the code and the tests.
Two small points, neither blocking (inline):
- The over-limit message is misleading.
- An expired Undo is still offered on the receipt.
Also, the checked-checkbox guard in swap now applies to every live region. No other live region holds a checkbox today, but a future region that renders one already ticked would never refresh. Scope it with a marker such as data-picks if one turns up.
On the home, a site admin can now tick apps in the Your apps rail and archive them in one step. The answer page lists what was archived and has an Undo button, and after Undo the apps are back on the home.
The first push failed the check "every top-level page of the forge is a reserved name". The new
/home/...routes madehomea top-level page, so someone could have registered a user or org calledhomeand shadowed them. This push addshometo the reserved names insrc/core/reserved.ts.test/reserved-names.test.tsandtest/home-archive.test.tsnow pass; I did not re-run the neighbouring suites or the fullbun testafter the fix.display: none) until a Select toggle is on, so nothing shifts. A non-admin sees no checkboxes and no toggle.POST /home/archiveruns the existing fleetarchiveoperation on the ticked apps, so there is no new action code. It is same-origin only (403 otherwise) and a non-admin gets 403.GET /home/archived/:op, a receipt that lists the archived apps. Its Undo button posts to/home/archived/:op/undo, which calls the fleet op's own undo.test/home-archive.test.tscovers archiving two apps, Undo bringing them back, a cross-origin POST returning 403, and a non-admin seeing no checkboxes.I left out a preview step before archiving, since the receipt and Undo cover it. The Select toggle resets when the apps region refreshes live. The rail still shows only the first 8 apps, so only those can be ticked. "Admin" here means site admin, not per-repo admin.
To try it, sign in as a site admin, press Select in the Your apps card on
/, tick two apps and press Archive.Closes #2512