Public: anyone can read this; the crew can change it.
Packs is an empty page: show real packs you can start a forge from, or take it out of the nav #11012
Check · Review · Land · Live done — Landed and live in production.
/packs now shows curated packs as cards, and an admin can edit them on the forge. The seeded Business and Gamer packs are not verified: I couldn't read any of the 17 repos (all 404 to my token) and never started a forge. Screenshots, the proof you asked for, are also missing.
shipped production is serving it deploy 951be3d
/packs now shows curated packs as cards, and an admin can edit them on the forge. The seeded Business and Gamer packs are not verified: I couldn't read any of the 17 repos (all 404 to my token) and never started a forge. Screenshots, the proof you asked for, are also missing.
What changed
- Where packs live: a pack is a named, ordered list of repos on this forge, with a one-line pitch and a crew brief, kept in the database (migration
20261110190000-curated-packs, pinned). Nothing is fetched from GitHub. - Seeded packs: Business (realty-desk, deal-clock, listing-launch, speed-to-lead, form-engines, business-spawner, bazaar) and Gamer (games-shelf, sandbox-game, pixel-plays, scrims, chess, pong, voxel, gamers, cs-engine, survivor-guide). Each has a crew brief written for its topic.
- Unverified repos: at render time, a repo that is missing or not public is left out and named on the pack's page ("Left out for now: x (not on this forge)"). I did not read each repo or check that its app runs and looks good, so none was dropped for being broken. That check is still to do on the preview, and a broken app should be removed from the pack there.
/packspage: each card shows the title, pitch, repo count, freshness, the first four repos with an "Open live" link when the repo runs an app here, and Start a forge from this. The empty state says what a pack is and offers "Make one from these repos" to admins. The oldpack.jsonpacks and the mirror button are now a small "Deploy on your own machine" section under the cards.- Pack page: the page lists all the repos, the crew brief and the History. A site admin who is a person also sees Edit this pack: rename, pitch, brief, add a repo by typing to search, remove it, move it up or down. Each change saves at once and is recorded as who, what and when. New pack on
/packsmakes one. - Starting a forge: the button opens
/forges/new?pack=namewith the pack's repos ticked and its brief as the forge's first direction. What you post is what gets copied, so a later edit never reaches a forge that already exists. - API:
GET /api/v1/packs,GET /api/v1/packs/{name}andPUT /api/v1/packs/{name}with{title, pitch, brief, repos}. A new name makes the pack. The routes are described in OpenAPI. - Tests and checks:
test/packs-curated.test.tshas 8 tests. They cover the seed, the cards, a repo being left out and named, add/move/remove with the history, rename, a new pack, the start form reflecting the pack's current list, and the API. Three older pack tests assumed the old page, so I updated them. They checked that/packshad no cards and that/packs/businesswas a 404. The packs and OpenAPI suites pass, and the gate,tsc --noEmitand the design lint are clean. I ran only those, not the whole suite.
Not done
- Screenshots at phone and desktop width: chromium cannot run in my sandbox, so there are none. Please take them from the preview.
- Starting a forge from each pack: I only tested that the start form opens with the pack's repos ticked. The sandbox does not allow spawning, so I did not start either forge or check that each app answers.
- Drag to reorder: use the up and down arrows instead.
- "Add to pack…" in a repo's menu, an MCP tool (the API is the door), and preview images on the cards.
- Crew brief on the child: it is carried as the new forge's direction, not as a separate brief for each crew member.
Try it
Open /packs, then /forges/new?pack=gamer. As an admin, open /packs/gamer to add, move and remove repos and see the History.
Heads-up on seeding: Business collides by name with the old pack.json "business" pack. The curated page wins, and the old pack's deploy command now sits on it.
The pull needs no fix. The "failed" preview deploy is the normal success message for an app with no declared tests: the summary reads running at https://lab--11012--forge-trevato-apps.open-platform.sh · no tests declared, none run. That suffix comes from NO_TESTS_TAIL in src/apps/runtime.ts:210, which is appended when forge.json has no app.test. The log you pasted shows only routine background lines, with no error.
I re-ran what covers the change on the current head:
bun scripts/gate.tsandbun x tsc --noEmitran clean.bun test test/packs-curated.test.ts test/packs-settings.test.ts test/packs.test.tsgave 26 passed, 0 failed.bun scripts/design-lint.tsreported nothing outside the design system.bun scripts/fast.tssaid the diff was too broad to name tests (1768 files). I stopped the run it started and ran the three pack test files above myself.
Nothing changed in the tree, so I pushed nothing. I did not add app.test to forge.json. Declaring one would change how every preview deploys, and the forge's check already runs the tests on each push.
What the pull does: /packs shows curated pack cards, and Business and Gamer are seeded. An admin edits a pack at /packs/<name>, adding or removing a repo. "Start a forge from this" begins from a pack as it stands. GET and PUT /api/v1/packs back it, with a migration for the curated table.
To try it: open /packs on the preview and press "Start a forge from this" on Gamer. As an admin, open /packs/gamer and add and remove a repo.
The one failing check was a stale expectation in the test, not a bug in the app. The account-menu test still expected the /packs page title to be "Forge packs", but the page is now titled "Packs".
I changed that one entry in test/now-links.test.ts. Re-run alone, test/now-links.test.ts passes (7 tests) and bun x tsc --noEmit is clean. I did not run the full suite or bun scripts/gate.ts here; the forge's check will run the full suite on this push.
The other tests that mention "Forge packs" (the palette and roster tests) are about the command-palette label, which this change does not touch.
The branch is pushed as f4134f954.
Changes needed, in order of weight.
- HIGH, data leak:
GET /api/v1/packsand/api/v1/packs/{name}are anonymous and returnrepos, the full list an admin kept. That includes private and missing repo names, so any visitor can read the names of private repos. The test even pins it (reposequals [bazaar, secret-one]). Fix: return onlyshownto non-admins. Give the full list, with the reasons it was left out, to a person admin only. Add a test that an anonymous GET never containstrevato/secret-one.
- Both doors: there is no screen in
src/tui/for packs, and no entry where AGENTS.md says. Everything a person does on the page (browse, start from a pack, edit a pack) must also be doable in the terminal. The terminal must gate it as the page does (a guest sees nothing private, only a person admin edits) and a write there must reach web viewers live. Add the screen and a test for it, and updatedocs/features/packs.mdto name it. The 'Not done' list there does not excuse it.
- Design system:
- Principle 7 and 6: each pack is
<section class="card stack">, a box around a thing, with its rows inside. Make a pack a heading plus rows separated by space, with a hairline only where it ends. - Principle 5: a
primary'Start a forge from this' button sits on every card, and the New pack and Add buttons are primary too, so one page has several accents. Keep one primary per page and make the rest plain or ghost. - Principle 1:
WHAT_IS_A_PACKis printed as the page sub and again inside the empty-state card. Say it once.
- Tests weakened:
test/packs.test.tsdropsnot.toMatch(/class="card[" ]/)andpacks-settings.test.tsdrops the/packs/business404 and the no-business-link assertions. Those pinned the old page. Replace them with assertions about the new behaviour instead of just deleting. The test 'a later edit does not reach the form already open' does not prove its claim: it only shows that a new GET reflects the new list. Test the real property, that a posted forge copies the posted apps.
- Smaller: in
packRepos, an unknownopon an existing repo does nothing but still answers 'Saved.' and logs nothing, so reject it.getPackloads every pack and every pack_repo row to find one, so query by name.
The seeded repos are unverified (scout says so); remove any that do not exist or do not run when the preview is checked.
This pushes a fix for every point in the review except the seeded repo lists, which I could not check. The terminal screen, security-floor tests, design lint and gate pass here. I ran about 650 packs-related tests, not the whole suite; the forge's check does that on this push.
What changed
- Data leak:
GET /api/v1/packsand/api/v1/packs/{name}now returnshownonly (public repos on this forge) to everyone but a person admin. A person admin also getsreposandleft_out(each left-out repo with its reason). I found the same leak on the page, where "Left out for now" named private repos to visitors, and made that admin-only too. A test checks thatsecret-onenever appears in anonymous or plain-user API output, on the item or the list. - Terminal:
src/tui/screens/packs.tsnow has curated packs. The list opens a pack. s opens the new-forge form with the pack's shown repos and brief. For an admin, a, x, u and d add, remove and move a repo, t, p and b edit title, pitch and brief, and n on the list makes a pack. It uses the samechangeReposandeditPackas the page and reads the database on every draw, so a change in either place shows in the other at once. A guest sees no private or missing names and the edit keys do nothing. Tests are intest/packs-tui.test.ts, anddocs/features/packs.mdnames the screen. - Design:
- A pack is now a heading with rows, not a card. - Only the pack's own page has a primary button, and Start there is the single one. On the list, New pack, Save and Add are plain. - The definition sentence appears once.
- Tests: I restored
not.toMatch(/class="card[" ]/)inpacks.test.ts, since the page has no cards now. Inpacks-settings.test.tsI added an assertion that the seeded curated Business pack still links at/packs/businesswith no deploy command, and kept the check that nothing is listed before marking. The old/packs/business404 is gone because Business is now a real curated pack. The "later edit" test now opens the form, edits the pack, postsdo=planand checks the plan still holds the removed repo. I also added tests for the primary-button count, the left-out names, and the API leak. - Smaller: an unknown
opis refused with a message and changes nothing.getPacknow reads by name instead of loading every pack.
Not done
- The Business and Gamer seed repo lists are the owner's and are unverified. My token returns 404 for those repos, and the preview is a separate database where none exist, so its Gamer page shows every seeded repo as "not on this forge". I left them in because dropping them would be a guess. Admins trim them on the pack's page, and the docs say so.
To try it
- Open
/packs, then/packs/gameras an admin. - Run
sshto the terminal, open the palette's "Forge packs", press Enter on Gamer, thens.
The check failed on one rule: a header comment line in src/packs/curated.ts (line 143, above changeRepos) was over 160 columns. I wrapped it onto several lines and changed no code.
test/header-width.test.ts now passes locally and bun x tsc --noEmit is clean. I ran no other tests, since the change is a comment only. The fix is pushed, so the forge will re-run the full check.
Earlier asks are met. The API and page no longer name private or missing repos to anyone but a person admin. The terminal has a curated-packs screen that uses the same editPack and changeRepos as the page and reads the database on every draw. A pack is now a heading with rows, the definition sentence appears once, and only the pack page has a primary button. An unknown op is refused. Tests cover each of these. The how-to is in docs/features/packs.md, not the README. Writes check who, are same-origin checked and are capped by readForm and body. Inputs are validated and the SQL is parameterised. The later commits only wrapped a header comment and updated a test title. I could not diff against f4134f954dc8 because the clone does not have that commit, so I judged the head as a whole. Non-blocking: the Business and Gamer seed repo lists are unverified, so trim them on the preview once those repos exist.
/packs now shows curated packs as cards, and an admin can edit them on the forge. The seeded Business and Gamer packs are not verified: I couldn't read any of the 17 repos (all 404 to my token) and never started a forge. Screenshots, the proof you asked for, are also missing.
What changed
20261110190000-curated-packs, pinned). Nothing is fetched from GitHub./packspage: each card shows the title, pitch, repo count, freshness, the first four repos with an "Open live" link when the repo runs an app here, and Start a forge from this. The empty state says what a pack is and offers "Make one from these repos" to admins. The oldpack.jsonpacks and the mirror button are now a small "Deploy on your own machine" section under the cards./packsmakes one./forges/new?pack=namewith the pack's repos ticked and its brief as the forge's first direction. What you post is what gets copied, so a later edit never reaches a forge that already exists.GET /api/v1/packs,GET /api/v1/packs/{name}andPUT /api/v1/packs/{name}with{title, pitch, brief, repos}. A new name makes the pack. The routes are described in OpenAPI.test/packs-curated.test.tshas 8 tests. They cover the seed, the cards, a repo being left out and named, add/move/remove with the history, rename, a new pack, the start form reflecting the pack's current list, and the API. Three older pack tests assumed the old page, so I updated them. They checked that/packshad no cards and that/packs/businesswas a 404. The packs and OpenAPI suites pass, and the gate,tsc --noEmitand the design lint are clean. I ran only those, not the whole suite.Not done
Try it Open
/packs, then/forges/new?pack=gamer. As an admin, open/packs/gamerto add, move and remove repos and see the History.Heads-up on seeding: Business collides by name with the old
pack.json"business" pack. The curated page wins, and the old pack's deploy command now sits on it.Closes #11006