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

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

/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

opened this

/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.
  • /packs page: 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 old pack.json packs 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 /packs makes one.
  • Starting a forge: the button opens /forges/new?pack=name with 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} and PUT /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.ts has 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 /packs had no cards and that /packs/business was a 404. The packs and OpenAPI suites pass, and the gate, tsc --noEmit and 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.

Closes #11006

commented

/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.
  • /packs page: 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 old pack.json packs 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 /packs makes one.
  • Starting a forge: the button opens /forges/new?pack=name with 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} and PUT /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.ts has 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 /packs had no cards and that /packs/business was a 404. The packs and OpenAPI suites pass, and the gate, tsc --noEmit and 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.

commented

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.ts and bun x tsc --noEmit ran clean.
  • bun test test/packs-curated.test.ts test/packs-settings.test.ts test/packs.test.ts gave 26 passed, 0 failed.
  • bun scripts/design-lint.ts reported nothing outside the design system.
  • bun scripts/fast.ts said 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.

commented

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 requested — before the latest push

Changes needed, in order of weight.

  1. HIGH, data leak: GET /api/v1/packs and /api/v1/packs/{name} are anonymous and return repos, 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 (repos equals [bazaar, secret-one]). Fix: return only shown to 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 contains trevato/secret-one.
  1. 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 update docs/features/packs.md to name it. The 'Not done' list there does not excuse it.
  1. 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_PACK is printed as the page sub and again inside the empty-state card. Say it once.
  1. Tests weakened: test/packs.test.ts drops not.toMatch(/class="card[" ]/) and packs-settings.test.ts drops the /packs/business 404 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.
  1. Smaller: in packRepos, an unknown op on an existing repo does nothing but still answers 'Saved.' and logs nothing, so reject it. getPack loads 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.

commented

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/packs and /api/v1/packs/{name} now return shown only (public repos on this forge) to everyone but a person admin. A person admin also gets repos and left_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 that secret-one never appears in anonymous or plain-user API output, on the item or the list.
  • Terminal: src/tui/screens/packs.ts now 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 same changeRepos and editPack as 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 in test/packs-tui.test.ts, and docs/features/packs.md names 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[" ]/) in packs.test.ts, since the page has no cards now. In packs-settings.test.ts I added an assertion that the seeded curated Business pack still links at /packs/business with no deploy command, and kept the check that nothing is listed before marking. The old /packs/business 404 is gone because Business is now a real curated pack. The "later edit" test now opens the form, edits the pack, posts do=plan and 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 op is refused with a message and changes nothing. getPack now 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/gamer as an admin.
  • Run ssh to the terminal, open the palette's "Forge packs", press Enter on Gamer, then s.
commented

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.

Approved

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs