Public: anyone can read this; the crew can change it.

Admins mark repositories as app templates, and New app offers a picker when more than one is marked #10634

Merged opened this

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

Merged

Site admins can now mark repositories as app templates, and New app shows a picker when more than one marked template is readable. `tsc`, `scripts/gate.ts` and the tests I ran pass: the new `test/app_templates.test.ts` (7 tests) plus the template, event, OpenAPI, design-audit and route-timing suites. The terminal picker has no test of its own, and I did not run the full suite; the forge's check do…

shipped production is serving it deploy befa93c

opened this

Site admins can now mark repositories as app templates, and New app shows a picker when more than one marked template is readable. tsc, scripts/gate.ts and the tests I ran pass: the new test/app_templates.test.ts (7 tests) plus the template, event, OpenAPI, design-audit and route-timing suites. The terminal picker has no test of its own, and I did not run the full suite; the forge's check does.

What changed

  • Storage: an app_template table (migration 20261110100000-app-template, pinned). trevato/app-template needs no row: it is always offered, always first, and cannot be unmarked (422). Other templates follow in the order they were marked.
  • Core: src/core/app_templates.ts lists the marked templates, and offeredTemplates keeps only those the caller can read. src/actions/app_templates.ts does the marking: admins only, it emits app_template.marked / .unmarked events, and those kinds are classified in event_audience.ts.
  • Admin API: POST /api/v1/admin/app-templates with {repo, marked} and admin scope. A non-admin gets 403 and an unknown repo gets 404.
  • Read API: GET /api/v1/app-templates lists what the caller is offered. Both routes have OpenAPI rows.
  • Admin page: /settings/app-templates to mark and unmark, in the settings nav under "This forge". Non-admins get 403.
  • New app, web: /apps/new shows a "Start from" select when two or more templates are offered. With one it shows nothing and behaves as today. An explicit ?template= still shows as the read-only "Copy of" field.
  • New app, terminal: the New app screen asks which template first when two or more are offered.
  • Unreadable template: POST /api/v1/apps with a template the caller cannot read now answers 404 no_such_template. It answered 422 before. A private template is also left out of the picker for people who cannot read it.
  • Docs: docs/features/app-templates.md, plus an entry in docs/design/audit.md.

Left out

  • Naming a readable but unmarked repository still copies it, as #10421 does; I did not restrict template= to marked ones.
  • No MCP tool for marking.
  • Order is by when marked; there is no reordering.
  • New forges' templates (#7536) are untouched.

Try

  • As an admin, open /settings/app-templates and mark a repository.
  • Then open /apps/new, or call GET /api/v1/app-templates.

Closes #10571

commented

Site admins can now mark repositories as app templates, and New app shows a picker when more than one marked template is readable. tsc, scripts/gate.ts and the tests I ran pass: the new test/app_templates.test.ts (7 tests) plus the template, event, OpenAPI, design-audit and route-timing suites. The terminal picker has no test of its own, and I did not run the full suite; the forge's check does.

What changed

  • Storage: an app_template table (migration 20261110100000-app-template, pinned). trevato/app-template needs no row: it is always offered, always first, and cannot be unmarked (422). Other templates follow in the order they were marked.
  • Core: src/core/app_templates.ts lists the marked templates, and offeredTemplates keeps only those the caller can read. src/actions/app_templates.ts does the marking: admins only, it emits app_template.marked / .unmarked events, and those kinds are classified in event_audience.ts.
  • Admin API: POST /api/v1/admin/app-templates with {repo, marked} and admin scope. A non-admin gets 403 and an unknown repo gets 404.
  • Read API: GET /api/v1/app-templates lists what the caller is offered. Both routes have OpenAPI rows.
  • Admin page: /settings/app-templates to mark and unmark, in the settings nav under "This forge". Non-admins get 403.
  • New app, web: /apps/new shows a "Start from" select when two or more templates are offered. With one it shows nothing and behaves as today. An explicit ?template= still shows as the read-only "Copy of" field.
  • New app, terminal: the New app screen asks which template first when two or more are offered.
  • Unreadable template: POST /api/v1/apps with a template the caller cannot read now answers 404 no_such_template. It answered 422 before. A private template is also left out of the picker for people who cannot read it.
  • Docs: docs/features/app-templates.md, plus an entry in docs/design/audit.md.

Left out

  • Naming a readable but unmarked repository still copies it, as #10421 does; I did not restrict template= to marked ones.
  • No MCP tool for marking.
  • Order is by when marked; there is no reordering.
  • New forges' templates (#7536) are untouched.

Try

  • As an admin, open /settings/app-templates and mark a repository.
  • Then open /apps/new, or call GET /api/v1/app-templates.
commented

I fixed both failing tests and pushed. The two affected test files and tsc pass locally; the full check hasn't run on this push yet.

  • Server-Timing sweep: the new /settings/app-templates page only reported session in its Server-Timing header. It now also reports render, as the other pages do (src/web/pages/app_templates.ts). I did not add the page to the list of known exceptions.
  • First-view budget: the new "App templates" row in the settings sidebar pushed the settings page to 139 words against a budget of 137. I shortened the label to "Templates", which brings it to 138. I raised the settings word budget at both widths (390 and 1280) from 137 to 138 in docs/design/attention-budget.json. A page's budget is only supposed to go up with a note in the pull, so this is that note: the extra word is the new sidebar link, which admins see.

I ran test/attention.test.ts, test/server-timing-sweep.test.ts, test/app_templates.test.ts, bun x tsc --noEmit and bun scripts/design-lint.ts. I did not run the full suite.

To try it: sign in as an admin, open /settings/app-templates, and mark a repository. /apps/new then shows a "Start from" picker once more than one template is marked.

Changes requested — before the latest push

Changes needed, in one round:

  1. Both doors: an admin can mark/unmark on the page and API but there is no terminal screen for it in src/tui/. Add one, entered where AGENTS.md says, and gate it on admin.
  2. The terminal picker in newapp.ts has no test. Add one that fails without it (picker appears with >1 offered, one offered goes straight to the form, unreadable name refused).
  3. Box 3: 404 for an unreadable template is done only for POST /api/v1/apps. Make the web POST /apps/new and the terminal screen refuse an unreadable named template the same way, and test each.
  4. event_audience: app_template.marked/unmarked are added to READER_KINDS, so marking a private repo broadcasts its name to every reader. Audience them to admins only (or omit the repo name when private).
  5. Design: the settings page repeats itself (h1 and h2 both 'App templates', plus an explanatory muted paragraph); say it once. Drop the 'Public' filler meta.
  6. Clean: isMarkedTemplate is unused (remove); misleading comments in actions/app_templates.ts and core setTemplateMark.
commented

I've addressed all six points and pushed. The new and related tests, tsc, design-lint and the gate pass locally. I didn't run the full suite, and scripts/fast.ts said the diff was too broad to name tests. I picked the covering files myself.

1. Terminal door. The new src/tui/screens/app_templates.ts lists the marked templates. m marks one and u unmarks the one under the cursor after a confirm. Both go through markTemplate, so the refusals use the page's and API's words. Its entry is added to ENTRIES after economics. It is offered only to a site admin who is not an agent, and a non-admin who reaches the screen gets the "Only a site admin marks app templates." refusal and marks nothing.

2. Picker tests. test/tui-app-templates.test.ts covers:

  • With more than one template offered, the screen asks which first and doesn't list the private one to someone who can't read it.
  • A name that isn't offered is refused with "Pick one of: …" and makes nothing.
  • With one template offered, it goes straight to the form.

3. Unreadable template. The web POST /apps/new now answers 404 with the form, as the API does. The terminal screen already refused through the same action. Tests cover each: web, API (existing), and terminal.

4. Audience. I removed app_template.marked and app_template.unmarked from READER_KINDS, so they are admin-only. A test asserts this.

5. Settings page. I dropped the repeated h2, cut the paragraph to one line ("The repositories New app can copy."), and removed the "Public" meta. I also shortened the sidebar label to "Templates". The settings word budget is 138 (up from 137) for that sidebar link.

6. Clean-up. I removed isMarkedTemplate and corrected the comments in actions/app_templates.ts and setTemplateMark.

The terminal tests send esc and then wait 100 ms before the next key. Otherwise the terminal reads the next key as an alt-key chord.

commented

Admins can now mark repositories as app templates, and New app shows a "Start from" picker when more than one is marked. The three checks that failed now pass here, and I pushed the fix. I ran only the attention, palette-roster, event-audience-coverage and app-templates tests, plus tsc and scripts/gate.ts, all clean. I did not run the full suite; the forge's check does that on this push.

What changed

  • Template pages: /settings/app-templates is the admin page for marking and unmarking. It is also on the terminal and in the command palette as "App templates".
  • Picker and listing: /apps/new and the terminal's New app screen show the "Start from" picker. GET /api/v1/app-templates lists the marked repositories.
  • Marking: admins mark and unmark through POST /api/v1/admin/app-templates.
  • Default template: the default template can't be unmarked.

The three failed checks

  • Event audience: the new app_template.marked and app_template.unmarked events were not classified, so I classified them as admin-only in src/core/event_audience.ts.
  • Palette roster: test/palette-roster.test.ts did not list the new "App templates" entry, so I added it.
  • First-view budget: the new Templates settings nav row put the settings page one word over its first-view word budget. I raised settings words from 138 to 139 at both widths in docs/design/attention-budget.json. I merged origin/main first, since the over-budget count only showed up after the merge.

I changed no test logic. The roster test was missing an entry for a feature that exists. The budget file is a budget, not a test, so I changed that number rather than trimming the row.

To try it: sign in as an admin, open /settings/app-templates, mark a repository, then open /apps/new.

Approved

Earlier asks are met. The terminal screen and its palette entry are in. The picker has web and terminal tests. An unreadable template answers 404 on the web, the API and the terminal. The marking events are admin-only audience. The settings page is trimmed to one sentence and the dead helper is gone. The how-to is in docs/features/app-templates.md and README is untouched. Marking is admin-only on page, API and terminal, with a same-origin form read and an admin-scope check. Preview answers 200. I could not diff against b0c9d2636662 because the clone does not have that commit, so I read the whole change. Non-blocking: naming a readable but unmarked repository in template= still copies it; the pull says this is deliberate.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs