Public: anyone can read this; the crew can change it.
docs/design/audit.md says, for every top-level page, what it is for and what is wrong with it against DESIGN.md #2725
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/2709-docs-design-audit-md-says-for-every-top → main 2 commits fork 2 threads · 2 unresolved
2 files
changed +170
−17
docs/design/audit.md +119 −17
| @@ -33,34 +33,136 @@ their own vocabulary instead of composing the named one. | ||
| 33 | 33 | |
| 34 | 34 | ## Finding 2 — the pages, and what each is for |
| 35 | 35 | |
| 36 | Judged by the question "what would the owner do here that they cannot do | |
| 37 | elsewhere?". Page sizes are source lines, a rough proxy for how much each | |
| 38 | invented. | |
| 36 | There are 59 top-level pages, one entry each: every static `GET` route in `src/http/server.ts` | |
| 37 | that returns a page (`test/design-audit.test.ts` holds this list to the router, | |
| 38 | both ways). Each entry reads `route` — module · what it is for · what | |
| 39 | duplicates it · what breaks `docs/DESIGN.md`. "Breaks" counts colour literals | |
| 40 | (§2) and off-scale `font-size`s (§3) in the module as of | |
| 41 | `docs/design/baseline.json`; "none seen" means the lint finds nothing, not that | |
| 42 | the page was looked at (no screenshots here). Page-local classes are not | |
| 43 | repeated per page: the module's share of the 619 is the stylesheet it uses. | |
| 39 | 44 | |
| 40 | | page | for | verdict | | |
| 41 | | ------------------ | ----------------------------------------------- | ------------------------------------------------------------------- | | |
| 42 | | `/` (studio home) | where I am, what needs me | keep; redo as KPIs the owner pins, not a to-do list (#2665) | | |
| 43 | | `/boards` | saved queries as panels | keep; selectors unusable (#2666); becomes the home's panel source | | |
| 44 | | `/system` | realtime machine state (btop) | keep; redo on the system (#2678 plan item 4) | | |
| 45 | | `/flow` | pipeline and bottleneck (topbar button, admin) | overlaps `/system` and boards: merge as a board panel set | | |
| 46 | | `/settings/usage`, `/settings/capacity`, `/settings/telemetry`, spend | four pages about "how much is being used" | one page, tabs by `tabs()` — usage and spend are the same question | | |
| 47 | | `/settings/model`, `/settings/cloud`, `/settings/backups`, `/settings/resilience` | machine/provider/recovery configuration | keep as settings sections; resilience (51 lines) folds into backups | | |
| 48 | | `/map` | 1,806 lines, 12 off-scale sizes, own colours | the largest private vocabulary; review what it shows that `/boards` and the issue dependency list do not | | |
| 49 | | `/audit`, `waves`, `variants`, `looks`, `returns`, `chain` | operator views of the crew's work | each needs a stated reason to be a page rather than a board panel | | |
| 50 | | `/settings/*` rest | account, keys, tokens, origins, crew | fine as one settings shell (`settingsLayout()`); no change | | |
| 45 | Machine routes are not pages and have no entry: `/audit.csv`, | |
| 46 | `/fleet/graph/live`, `/fleet/graph.csv`, `/settings/origins/callback`. | |
| 51 | 47 | |
| 52 | Duplication to resolve: three places answer "what is the forge doing right now" | |
| 53 | (home, `/flow`, `/system`); four answer "what does it cost / how full is it". | |
| 48 | ### Home, work and the crew | |
| 49 | ||
| 50 | - `/` — `pages/site.ts` (`home`, studio mode in `studio/home.ts`) · where I am, what needs me · `/front` (same page for anyone), `/board`, `/asks` · colours 3 in `site.ts`, 7 in `studio/home.ts`. | |
| 51 | - `/front` — `studio/home.ts` (`studioDoor`) · the signed-out door, and the site admin's controls over it · `/` for a signed-out reader · colours 7. | |
| 52 | - `/explore` — `studio/explore.ts` · browse what the forge holds · `/search`, `/apps` · colours 2. | |
| 53 | - `/board` — `pages/board.ts` (1,231 lines) · every app the viewer can see, with a wall mode · `/boards` (saved queries as panels), `/flow`, `/` · colour 1, sizes 6. | |
| 54 | - `/board/returns` — `pages/returns.ts` · what the window's dollars bought · `/settings/subscriptions` spend tables, `/settings/usage` · none seen. | |
| 55 | - `/big` — `pages/board.ts` (`bigRedirect`) · a redirect kept for old addresses into the board's wall mode · it is `/board` · none (no page of its own). | |
| 56 | - `/boards` — `pages/boards-view.ts` · saved queries as panels · `/board` · none seen. | |
| 57 | - `/boards/data` — `pages/boards.ts` · the boards' query/data surface · `/board` · none seen. | |
| 58 | - `/asks` — `studio/asks_page.ts` · what the crew is waiting on me for · `/` "needs me" list · colours 2. | |
| 59 | - `/asked` — `studio/asked_page.ts` · what I answered, as a record · `/asks` · none seen. | |
| 60 | - `/flow` — `pages/flow.ts` · the pipeline from ready issue to live deploy, and the bottleneck · `/system`, `/board`, `/` · none seen. A topbar button points here. | |
| 61 | - `/map` — `pages/map.ts` (1,806 lines) · the dependency map of work across the forge · `/board`, issue dependency lists, `/:owner/:name/map` · colours 3, sizes 12: the largest private vocabulary. | |
| 62 | - `/audit` — `pages/audit.ts` · every app's contract, who can do what · the app page's one-line policy · none seen. Names the audit of apps, not this document. | |
| 63 | - `/releases` — `pages/releases.ts` · the forge's kept releases and rollback · `/settings/backups` (both are "go back") · none seen. | |
| 64 | - `/contribute` — `pages/contribute.ts` · what this forge is and where outside work is welcome, open to anyone · `/llms.txt`, MCP `contribute` tool · none seen. | |
| 65 | - `/search` — `pages/search.ts` · find across what I can read · the palette (`palette.ts`) · none seen. | |
| 66 | - `/-/confirm` — `pages/search.ts` (`confirmPage`) · the "are you sure" step for a destructive action · none · none seen. | |
| 67 | ||
| 68 | ### Making things | |
| 69 | ||
| 70 | - `/new` — `pages/site.ts` (`newRepoPage`) · make a repository · `/apps/new`, `/migrate` · colours 3 (file). | |
| 71 | - `/apps` — `pages/apps.ts` · the apps the forge runs · `/audit`, `/explore` · colour 1. | |
| 72 | - `/apps/new` — `pages/apps.ts` (`newAppPage`) · make an app from the template · `/new` · colour 1. | |
| 73 | - `/apps/open` — `pages/apps.ts` (`appsOpen`) · a door from a name to a running app · `/apps` · colour 1. | |
| 74 | - `/organizations/new` — `pages/org.ts` · make an organization · none · none seen. | |
| 75 | - `/migrate` — `pages/migrate.ts` · bring a repository in from elsewhere · `/settings/origins` (mirror jobs) · none seen. | |
| 76 | ||
| 77 | ### Forges (the family) | |
| 78 | ||
| 79 | - `/forges` — `pages/forges.ts` · the forges this one sent, as a client list · `/family` · colour 1. | |
| 80 | - `/forges/new` — `pages/new-forge.ts` · send a new forge · none · colour 1. | |
| 81 | - `/family` — `pages/family.ts` · parent, this forge and children as a tree · `/forges` (the same children) · colour 1. | |
| 82 | - `/claim` — `pages/claim.ts` · claim a forge that was sent to me · none · colour 1. | |
| 83 | - `/first-admin` — `pages/first-admin.ts` · the first person becomes admin · none · none seen. | |
| 84 | - `/fleet/graph` — `pages/fleet-graph.ts` · the fleet as a graph · `/family`, `/forges` · none seen. | |
| 85 | ||
| 86 | ### Machine and operation (admin) | |
| 87 | ||
| 88 | - `/system` — `pages/system.ts` · the machine as it is now: cores, load, memory, processes · `/flow`, `/` · none seen; its own refresh script. | |
| 89 | - `/resilience` — `pages/resilience.ts` · each protection in `docs/THREATS.md` against the last gate's tests · `/settings/resilience` (similar name, different content) · none seen. | |
| 90 | - `/admin/limits` — `pages/admin-limits.ts` · every limit with value, default and reason · `/settings/capacity` (also a numbers-you-can-change page) · none seen. | |
| 91 | - `/settings/capacity` — `pages/capacity.ts` · how many runs and checks go at once · `/admin/limits`, `/settings/usage` · none seen. | |
| 92 | - `/settings/usage` — `pages/usage.ts` · route patterns by use, unused first · `/settings/telemetry`, `/board/returns` · none seen. Not about cost, despite the name. | |
| 93 | - `/settings/telemetry` — `pages/telemetry.ts` · which agents may read telemetry · `/settings/crew` (agent grants) · none seen. | |
| 94 | - `/settings/resilience` — `pages/domains.ts` · failure domains and their last drill · `/resilience` · none seen. | |
| 95 | - `/settings/backups` — `pages/backups.ts` (621 lines) · backups and restore · `/settings/resilience`, `/releases` · none seen. | |
| 96 | - `/settings/flags` — `pages/flags.ts` · the forge's switches · none · none seen. | |
| 97 | - `/settings/model` — `pages/model.ts` · which model the crew runs on · `/settings/crew` · none seen. | |
| 98 | - `/settings/cloud` — `pages/cloud.ts` · cloud provider connection · none · none seen. | |
| 99 | - `/settings/cloud/audit` — `pages/cloud.ts` · what the cloud connection did · `/audit` (different subject, same word) · none seen. | |
| 100 | - `/settings/access` — `pages/access.ts` · who may reach the forge · `/settings/people`, `/settings/accounts` · none seen. | |
| 101 | ||
| 102 | ### Sign-in and account | |
| 103 | ||
| 104 | - `/signin` — `pages/site.ts` · sign in · `/signin/parent` · colours 3 (file). | |
| 105 | - `/signin/parent` — `http/rp.ts` · sign in with the forge that sent this one · `/signin` · no page (redirect). | |
| 106 | - `/signin/parent/callback` — `http/rp.ts` · the return leg of that sign-in · none · no page (redirect). | |
| 107 | - `/signup` — `pages/site.ts` · make an account · `/signup` passkey flow (`signup-passkey.ts`) · colours 3 (file). | |
| 108 | - `/verify` — `pages/site.ts` · confirm an email address · none · colours 3 (file). | |
| 109 | - `/recover` — `pages/passkeys.ts` · get back in without a password · `/settings/passkeys` · none seen. | |
| 110 | - `/settings` — `pages/account.ts` · my account · the rest of settings · colour 1. | |
| 111 | - `/settings/password` — `pages/account.ts` · change password · `/settings/passkeys` · colour 1. | |
| 112 | - `/settings/tokens` — `pages/account.ts` · my API tokens · `/settings/keys` (credentials of two kinds on two pages) · colour 1. | |
| 113 | - `/settings/keys` — `pages/account.ts` · my SSH keys · `/settings/tokens` · colour 1. | |
| 114 | - `/settings/passkeys` — `pages/passkeys.ts` · my passkeys · `/settings/password` · none seen. | |
| 115 | - `/settings/secrets` — `pages/secrets.ts` · secrets apps may read · none · none seen. | |
| 116 | - `/settings/subscriptions` — `pages/subscriptions.ts` (624 lines) · the model subscriptions the crew spends from, with spend tables (`pages/spend.ts`) · `/board/returns` · none seen. | |
| 117 | - `/settings/accounts` — `pages/accounts.ts` · every account on the forge · `/settings/people` · none seen. | |
| 118 | - `/settings/people` — `pages/people.ts` · people and their roles · `/settings/accounts`, `/settings/crew` · none seen. | |
| 119 | - `/settings/crew` — `pages/crew.ts` · the named agents and what each may do · `/settings/model`, `/settings/telemetry` · none seen. | |
| 120 | - `/settings/origins` — `pages/origins.ts` (1,497 lines) · other forges this one reads and mirrors from · `/migrate` · none seen. | |
| 121 | ||
| 122 | ## Finding 2b — what the pages do wrong, in sum | |
| 123 | ||
| 124 | - Four pages answer "how full / what does it cost": `/settings/usage` (request | |
| 125 | use), `/settings/capacity`, `/settings/subscriptions` (spend), `/board/returns`. | |
| 126 | Two answer "what is the forge doing now": `/flow`, `/system`, plus `/`. | |
| 127 | - Two pages are named "resilience" and mean different things (`/resilience`, | |
| 128 | `/settings/resilience`); two are named "audit" (`/audit`, `/settings/cloud/audit`). | |
| 129 | - Two pages hold the same lists under different names: `/settings/accounts` and | |
| 130 | `/settings/people`; `/forges` and `/family`. | |
| 131 | - The module that invents most: `pages/map.ts` (colours 3, sizes 12), then | |
| 132 | `pages/board.ts` (colour 1, sizes 6); the colour literals are spread thin over | |
| 133 | the studio and the site pages (see the table above). | |
| 134 | - Repository pages (`/:owner/:name/*`) are not listed: the repo header and tabs | |
| 135 | already unify them. | |
| 54 | 136 | |
| 55 | 137 | ## Finding 3 — what the clean app template does better |
| 56 | 138 | |
| 57 | 139 | - One page, one purpose, one primary action. The forge's top bar carries |
| 58 | 140 | Flow, search, help, theme, New, user — six controls before the page begins. |
| 59 | - It has no page-local CSS. Every forge page file that carries colour or size | |
| 141 | - It has no page-local CSS. Its stylesheet uses only token words | |
| 142 | (`font-size: var(--text-sm)`, no literal) and is linked by content hash and | |
| 143 | cached for a year. Every forge page file that carries colour or size | |
| 60 | 144 | literals (list above) is a page that reinvented a token. |
| 61 | 145 | - First paint is the product. Several forge pages are tables of everything; |
| 62 | 146 | the template shows three numbers and a list. |
| 63 | 147 | |
| 148 | ## Merge or retire | |
| 149 | ||
| 150 | These match the filed issues; each is a decision for its pull, not done here. | |
| 151 | ||
| 152 | - **#2692** — usage, capacity, subscriptions' spend and the telemetry switch | |
| 153 | become one "Usage" page with `tabs()`. `/board/returns` stays: it is the | |
| 154 | board's tokens line opened. | |
| 155 | - **#2693** — `/flow` folds into the board as panels; the topbar Flow button goes. | |
| 156 | - **#2694** — `/resilience` folds into `/settings/backups` with | |
| 157 | `/settings/resilience`'s failure domains. | |
| 158 | - **#2695** — `/map` is accounted for: keep only what `/board` and an issue's | |
| 159 | dependency list cannot show; move its 12 sizes and 3 colours onto tokens. | |
| 160 | - **#2696** — operator views (`/audit`, waves, variants, looks, returns, | |
| 161 | following) each state why they are a page or become board panels. | |
| 162 | - Not filed yet, noticed here: `/settings/people` into `/settings/accounts`; | |
| 163 | `/family` into `/forges`; `/settings/tokens` and `/settings/keys` into one | |
| 164 | credentials page; `/big` can be deleted once nothing links it. | |
| 165 | ||
| 64 | 166 | ## Not covered |
| 65 | 167 | |
| 66 | 168 | Screenshots, contrast measurement per palette, the TUI surface, and pages |
test/design-audit.test.ts added +51
| @@ -0,0 +1,51 @@ | ||
| 1 | /** | |
| 2 | * docs/design/audit.md has one entry for every top-level page, and names no | |
| 3 | * route the router does not have (#2709). The router is read as text: the | |
| 4 | * static GET paths in src/http/server.ts, less the ones that are not pages. | |
| 5 | */ | |
| 6 | import { expect, test } from "bun:test"; | |
| 7 | import { readFileSync } from "node:fs"; | |
| 8 | ||
| 9 | const server = readFileSync("src/http/server.ts", "utf8"); | |
| 10 | const doc = readFileSync("docs/design/audit.md", "utf8"); | |
| 11 | ||
| 12 | /** Routes that answer a file, a fragment, a redirect hop or a callback. */ | |
| 13 | const NOT_PAGES = | |
| 14 | /^\/(assets\/|\.well-known\/|icons\/|og\/|live\/|__forge\/|go\/|events$|sw\.js$|robots\.txt$|sitemap\.xml$|manifest\.webmanifest$|llms\.txt$|releases\.json$|audit\.csv$|fleet\/graph\/live$|fleet\/graph\.csv$|settings\/origins\/callback$|home\/archived\/)/; | |
| 15 | ||
| 16 | const getRoutes = [ | |
| 17 | ...server.matchAll(/router\.add\(\s*"GET",\s*"([^"]+)"/g), | |
| 18 | ].map((m) => m[1]!); | |
| 19 | ||
| 20 | const pages = [ | |
| 21 | ...new Set( | |
| 22 | getRoutes.filter((r) => !/[:*]/.test(r) && !NOT_PAGES.test(r)), | |
| 23 | ), | |
| 24 | ]; | |
| 25 | ||
| 26 | const entries = [...doc.matchAll(/^- `(\/[^`]*)`/gm)].map((m) => m[1]!); | |
| 27 | ||
| 28 | test("every top-level page has an entry in the audit", () => { | |
|
||
| 29 | const stated = /There are (\d+) top-level pages/.exec(doc)?.[1]; | |
| 30 | expect(pages.length).toBe(Number(stated)); | |
| 31 | expect(entries.length).toBe(pages.length); | |
| 32 | expect(pages.filter((p) => !entries.includes(p))).toEqual([]); | |
| 33 | }); | |
| 34 | ||
| 35 | test("the audit names no route that does not exist", () => { | |
| 36 | const known = new Set(getRoutes); | |
| 37 | const named = [...doc.matchAll(/`(\/[a-z0-9\-/._:]*)`/g)].map((m) => m[1]!); | |
| 38 | const ghosts = named.filter( | |
| 39 | (r) => | |
| 40 | !known.has(r) && | |
| 41 | // a path inside a repository, a file, or a prefix, not a route | |
| 42 | !/^\/(:owner|\.|src|docs)/.test(r) && | |
| 43 | !/^\/(llms\.txt|tokens\.css)/.test(r), | |
| 44 | ); | |
| 45 | expect(ghosts).toEqual([]); | |
| 46 | expect(entries.filter((e) => !known.has(e))).toEqual([]); | |
| 47 | }); | |
| 48 | ||
| 49 | test("each entry is written once", () => { | |
| 50 | expect(entries.length).toBe(new Set(entries).size); | |
| 51 | }); |
new above
offline
Duplicate of Finding 3 (same title, same subject). Merge into Finding 3 and remove this section.