Public: anyone can read this; the crew can change it.
The fleet graph is reachable: the board, the spend page and each app's page link to /fleet/graph with their selection #10982
Check · Review · Land · Live done — Landed and live in production.
Three spend views now link to `/fleet/graph`, and each link carries what that view was showing as the graph's selection. The graph reads its selection from repeated `repo=owner/name` values, not a `q` parameter, so the links use `repo=`. A new helper, `fleetGraphHref` in `src/web/fleet-graph.ts`, builds them. It URL-encodes each name, drops repeats, and with no names gives plain `/fleet/graph`, wh…
shipped production is serving it deploy 99b5196
Three spend views now link to /fleet/graph, and each link carries what that view was showing as the graph's selection. The graph reads its selection from repeated repo=owner/name values, not a q parameter, so the links use repo=. A new helper, fleetGraphHref in src/web/fleet-graph.ts, builds them. It URL-encodes each name, drops repeats, and with no names gives plain /fleet/graph, which is the graph's default of every repository you can write. The pages print the link through html, so it is escaped too.
- Board (
src/web/pages/board.ts): the "Tokens, 7 days: …" spend line now ends with· graph. When?apps=narrows the board, the link carries those apps (capped at 100, the graph's limit). Otherwise it goes to the unfiltered graph. The page is drawn on the server and no script adds the link, so nothing moves on the board. - Spend page (
src/web/pages/spend.ts, used by/settings/spendand the board's spend details):/settings/spendhas no filter of its own to carry. Instead, a "The fleet graph" link sits above the tables, and each name in the App table links to the graph for just that app. "a removed app" stays plain text. - An app's page: the issue names
src/web/pages/apps-name.ts, but that file only works out a new app's name. The app's page issrc/web/studio/app.ts. Its "On its own" card, which only the app's owner sees, now has a Graph button linking to/fleet/graph?repo=owner%2Fname.
Tests: test/returns.test.ts checks the board's link, test/spend-tab.test.ts checks the spend page's links, and test/studio-shipping.test.ts checks the owner's link and that anonymous and read-only visitors get none. The new test/fleet-graph-href.test.ts covers the encoding and that the graph reads the names back. It also checks that a hostile name can't add a parameter or break out of href.
The board test only checks the unfiltered link. Its test data has no app records, so the spend line doesn't appear under ?apps=. The narrowed link is only covered by the helper's tests.
scripts/gate.ts, bun x tsc --noEmit and scripts/design-lint.ts passed. scripts/fast.ts named 1538 test files, about two hours, so I ran the board, spend, studio, fleet and returns test files instead: 659 passed, none failed. The keep_plan tool wasn't available in this run, so the issue has no plan.
To try it: on /board as an app owner, follow "graph" at the end of the Tokens line. On /settings/spend as an admin, click an app's name. On an app you own, press Graph in "On its own".
🤖 Generated with Claude Code
Links work and are plain escaped <a>, but several things need fixing in one round:
- Board: the narrowed (?apps=) link is untested, and its logic is wrong when no listed app has ownerFacts: the list is empty so the link silently widens to the whole fleet. Drop the link or scope it to what was asked; test both.
- Spend page: the added sentence explains itself twice (principle 1) and in non-plain words (8). Make the link one short line or just the per-app links.
- Both doors / docs: no docs/features/<name>.md describes these entries, and the terminal side (src/tui/screens/usage.ts / fleetgraph.ts) gets no matching way from spend to the graph. Add the how-to and say in the pull how the terminal reaches the same graph.
- Board link text 'graph' in muted small type is a tiny tap target on a 360px phone and unclear alone; use a clearer label with padding to reach 44px.
- Box 2 says the filter is carried as q; the graph uses repo=, so that box can't be ticked as written.
The fleet graph (/fleet/graph) can now be reached from the board, the spend page, an app's page and a repository's Usage in the terminal. Each link opens the graph narrowed to what that page showed. This round fixes the five review points.
Board. The link sits beside the Tokens line and reads "Fleet graph", not a muted "graph". It has a 44px tap target and wraps under the line on a phone (#returns and #returns-graph in src/web/pages/board_style.ts).
- The money on the line and the graph link now come from one list (
owned), so they can't disagree. narrowedGraphHrefinsrc/web/fleet-graph.tsdecides the link:
- Without ?apps=, it opens the viewer's whole graph, as before.
- With ?apps=, it opens exactly the apps listed.
- If that list is empty or longer than the graph's 100-repository limit, there is no link, so it never widens or quietly cuts what the board showed.
- Tests:
test/fleet-graph-href.test.tscovers the not-narrowed, narrowed, empty and too-many cases. Intest/returns.test.ts,/board?apps=alice/millshows no link while mill is not an app, and showsrepo=alice%2Fmillonce it is.
Spend page. The explaining sentence is now one line, <a href="/fleet/graph">Fleet graph</a>. Each owner/name in "By app" still opens the graph on that app.
Terminal. On a repository's Usage (the palette's "Usage of a repository"), f now opens the graph on that repository alone, titled Fleet graph · owner/name. On the forge-wide Usage, f opens the whole graph as before. This replaces the old test "f does nothing on a repository's Usage" in test/tui-fleetgraph.test.ts: that test described the old behaviour, which the review asked to change. docs/features/forge-usage-terminal.md is updated to match.
Docs. docs/features/fleet-graph-doors.md lists every way to the graph, on the web and in the terminal.
Issue's second box. It says the spend page's filter goes to the graph as q, but the graph's selection parameter is repo= (one per owner/name, URL-encoded), so I used that. The spend page has no filter of its own to carry; the per-app links carry the selection.
To try it:
- Open
/boardas an app owner, then/board?apps=<owner/name>. - On
/settings/spend, click an app's name. - In the terminal, open "Usage of a repository" and press
f.
Gate, tsc --noEmit and the design lint pass. scripts/fast.ts named too many files (the board reaches most tests), so I picked the board, spend, studio, returns, fleet-graph, terminal Usage and both-doors suites: 147 pass, 0 fail.
🤖 Generated with Claude Code
From the board's spend line, the spend tables and an app's page, the fleet graph is one click away, and the link keeps what was selected there (#578).
- Board (
src/web/pages/board.ts): an app owner's spend line ends in a "Fleet graph" link. It is at least 44px tall and its?apps=holds only the apps on the board. - Spend (
src/web/pages/spend.ts): clicking an app's name in/settings/spendopens the graph for that app. - App page (
src/web/studio/app.ts): the "On its own" block has a Graph button. - Terminal (
src/tui/screens/usage.ts): a repository's Usage screen opens that repository's own graph. - Links (
src/web/fleet-graph.ts): every one of these doors builds its link in this one place, andtest/fleet-graph-href.test.tscovers it. - Docs:
docs/features/fleet-graph-doors.mddescribes the doors.
The branch now has main merged in. That merge brought a test that was already failing on main: #10954 added test/security-floor-doors.test.ts expecting answered to read compress(...).then(securityFloor), and #10975 then put resourcePolicy inside securityFloor on that line. The test was out of date, not the code, so only its expected line changed; server.ts is untouched. The other failure, "the screen shows the share, the budgets that refused and the agent refused" in test/tui-crawlers.test.ts, passed three runs in a row here after the merge, and nothing in this pull changes it. It only failed in the sharded check run, which could not be reproduced here.
Gate, tsc --noEmit, both previously failing test files and the six test files this change touches all pass.
To try it: open /board as an app owner and follow "Fleet graph" at the end of the spend line. Open /settings/spend and click an app's name. Open an app you own and press Graph under "On its own".
🤖 Generated with Claude Code
From the board's spend line, the spend tables and an app's page, the fleet graph is one click away, and the link keeps what was selected there (#578).
- Board (
src/web/pages/board.ts): an app owner's spend line ends in a "Fleet graph" link. It is at least 44px tall and its?apps=holds only the apps on the board. - Spend (
src/web/pages/spend.ts): clicking an app's name in/settings/spendopens the graph for that app. - App page (
src/web/studio/app.ts): the "On its own" block has a Graph button. - Terminal (
src/tui/screens/usage.ts): a repository's Usage screen opens that repository's own graph. - Links (
src/web/fleet-graph.ts): every one of these doors builds its link in this one place, andtest/fleet-graph-href.test.tscovers it. - Docs:
docs/features/fleet-graph-doors.mddescribes the doors.
The branch has main merged in again. The one conflict was in test/security-floor-doors.test.ts: this branch had updated a stale expected line about how answered compresses and floors a response, and main fixed the same line on its own with a looser pattern. Main's version is kept, and this pull no longer changes that file. The crawlers test that failed once in the sharded check passes here.
Gate, tsc --noEmit and the eight affected test files (78 tests) pass.
To try it: open /board as an app owner and follow "Fleet graph" at the end of the spend line. Open /settings/spend and click an app's name. Open an app you own and press Graph under "On its own".
🤖 Generated with Claude Code
Clean. Links are plain escaped <a> built in one helper (fleetGraphHref), the narrowed board never widens to the whole fleet, the terminal gets the same door (f on a repository Usage) with a docs/features how-to, README untouched, no new dependency, and tests cover each page plus the helper. The preview answers with a sign-in redirect for an anonymous probe, so I could not see the signed-in pages; I read the code and tests instead. Box 2 says q, but the graph selection is repo= and the spend page has no filter of its own, so the per-app repo= links are the faithful form; I leave that box for a person to reword.
Left #578 open: 1 unticked item remain (the spend page (src/web/pages/spend.ts) links to the graph with its current filter carried as the graph's q).
Three spend views now link to
/fleet/graph, and each link carries what that view was showing as the graph's selection. The graph reads its selection from repeatedrepo=owner/namevalues, not aqparameter, so the links userepo=. A new helper,fleetGraphHrefinsrc/web/fleet-graph.ts, builds them. It URL-encodes each name, drops repeats, and with no names gives plain/fleet/graph, which is the graph's default of every repository you can write. The pages print the link throughhtml, so it is escaped too.src/web/pages/board.ts): the "Tokens, 7 days: …" spend line now ends with· graph. When?apps=narrows the board, the link carries those apps (capped at 100, the graph's limit). Otherwise it goes to the unfiltered graph. The page is drawn on the server and no script adds the link, so nothing moves on the board.src/web/pages/spend.ts, used by/settings/spendand the board's spend details):/settings/spendhas no filter of its own to carry. Instead, a "The fleet graph" link sits above the tables, and each name in the App table links to the graph for just that app. "a removed app" stays plain text.src/web/pages/apps-name.ts, but that file only works out a new app's name. The app's page issrc/web/studio/app.ts. Its "On its own" card, which only the app's owner sees, now has a Graph button linking to/fleet/graph?repo=owner%2Fname.Tests:
test/returns.test.tschecks the board's link,test/spend-tab.test.tschecks the spend page's links, andtest/studio-shipping.test.tschecks the owner's link and that anonymous and read-only visitors get none. The newtest/fleet-graph-href.test.tscovers the encoding and that the graph reads the names back. It also checks that a hostile name can't add a parameter or break out ofhref.The board test only checks the unfiltered link. Its test data has no app records, so the spend line doesn't appear under
?apps=. The narrowed link is only covered by the helper's tests.scripts/gate.ts,bun x tsc --noEmitandscripts/design-lint.tspassed.scripts/fast.tsnamed 1538 test files, about two hours, so I ran the board, spend, studio, fleet and returns test files instead: 659 passed, none failed. Thekeep_plantool wasn't available in this run, so the issue has no plan.To try it: on
/boardas an app owner, follow "graph" at the end of the Tokens line. On/settings/spendas an admin, click an app's name. On an app you own, press Graph in "On its own".🤖 Generated with Claude Code
Closes #578