Public: anyone can read this; the crew can change it.
The data explorer is easy: start from a question, not from a query form #9539
Check · Review · Land · Live done — Landed and live in production.
`/boards/data` now opens on a question instead of the old query form. The model fallback for sentences is not built. `tsc`, `gate.ts`, `design-lint.ts` and the boards tests I ran (guide, page, selectors) are green. I did not run the rest of the suite or the pages in a browser. I couldn't use `keep_plan` because I had no such tool in this run.
shipped production is serving it deploy f21b126
/boards/data now opens on a question instead of the old query form. The model fallback for sentences is not built. tsc, gate.ts, design-lint.ts and the boards tests I ran (guide, page, selectors) are green. I did not run the rest of the suite or the pages in a browser. I couldn't use keep_plan because I had no such tool in this run.
What changed
- Plain names and defaults: every source has a plain name ("Machine readings") and says what a row is. A link with no aggregate opens on the source's default chart. Sample sources get an average of their main value per day, and machine and reach readings are narrowed to one series so the average means something. Event sources get a count per day.
- Count wording: the count option says what it counts, for example "count of 15-second readings (counts readings, not time)". Count is still offered on sample sources, just labelled.
- Ready questions: a "Start from a question" card lists the four featured ones (CPU over the last week, Slowest pages today, Who used the forge's time, Apps shipped per day), plus the current source's own. Each one opens a working chart in one press, and its link carries every choice, so it can be shared or pinned.
- Pickers: the source, column, group-by and filter lists show each column's meaning and unit, and the source list shows every column's meaning under the selected source. The operator list is in words.
- One line per chart: a line above every chart says what it shows, for example "Average CPU cores per day, where target is forge." The Pin form's default title uses the same line.
- Sentence box:
?ask=memory over the last 3 daysis read by plain rules insrc/boards/guide.tsand redirects to the filled form. A sentence it can't read says so on the page. - Docs:
docs/features/data-explorer.mddescribes it.
Left out
- The model fallback for phrasings the rules miss. I found no simple model helper in the repo, so I did not invent one.
- Choosing filter values from a list. They are still typed.
- I did not restyle the page beyond the existing card, field and button pieces, so it uses the home page's components rather than anything new.
Test edits: two existing tests were out of date. test/boards-page.test.ts expected the heading <h2>runs</h2>, which is now "Crew and build runs". test/boards-selectors.test.ts took the first GET form on the page, which is now the sentence box, so it finds the query form by its chart field. The new tests are in test/boards-guide.test.ts.
To try it: sign in as an admin and open /boards/data?source=machine, press "CPU over the last week", or type "memory over the last 3 days" into the sentence box.
🤖 Generated with Claude Code
The preview didn't fail on my change. I changed nothing and pushed nothing for this report.
The log you pasted ends with serving … done, and a request to /healthz on the preview just now returned 200. The line "no tests declared, none run" is a note about the preview step. forge.json runs tests in check, not in the app's deploy, so the preview step doesn't run any.
The tests that cover my change, bun x tsc --noEmit, gate.ts and design-lint.ts were green before I pushed.
If the forge still marks the preview red, please send the check log or the exact failing step. The deploy log shows no cause for me to fix.
The check failed because no commit message had a Try: line, which the gate wants whenever src/web/pages/boards.ts changes. I added an empty commit with that line and pushed it. No code changed.
The gate passed locally before the push, with no output. The forge's check will give its own verdict on this push.
The Try: line tells you to open /boards/data?source=machine on the preview as an admin, press "CPU over the last week", or type "memory over the last 3 days" into the sentence box.
The pull itself is unchanged: plain source names and default charts, ready questions, a one-line description above each chart, and the sentence box. I did not build the model fallback for sentences the plain rules miss. Filter values are still typed rather than picked from a list.
🤖 Generated with Claude Code
Changes needed, in one round:
- Both doors (blocker): the explorer is page-only. Add a screen in src/tui/ with its entry where AGENTS.md says. It needs plain source names, the ready questions, the one-line description, and the sentence box via parseSentence. It must gate reads exactly as the page does: admin only, guests see nothing.
- Dead junk in src/boards/guide.ts: the NOTES map has
state_: "", an empty entry that does nothing. Remove it. Also check that every NOTES key is a real column in some source.
- Box 5 and the docs: the pull ships the rule-based parser and says the model fallback is not built. That is acceptable for this round, but docs/features/data-explorer.md must say how a person uses the sentence box. It should also say plainly that a phrase the parser cannot read gets an error message and nothing more.
- Unit and meaning on the pickers (box 3): options show
column — meaningtruncated to 56 chars, with the unit only when a doc happens to include it. Make sure every number column shows its unit (bytes, ms, USD, cores). Add a test that fails when a number column has no unit in its note.
- Count on sample sources (box 1): it is still offered, only relabelled. That is OK, but add a test for the label text on a samples source, in the rendered page and not just in aggregateOption.
- Source-list links: they now pass aggregate:"", by:"", fop:"". Add a test that following one of them lands on that source default chart. This guards the
search.has("aggregate")check in readParams against an empty-but-present parameter.
- Design: the query form now sits below the chart. Confirm the page keeps one primary button per page. The sentence box and the query form each have their own primary "Show" or submit button, so the page may have two. Make one of them secondary or ghost.
- Time-window parsing: "yesterday" returns days=2, which is a sliding window and not yesterday. Either name it honestly ("last 2 days") or set both from and to. Add a test for it.
All eight points from the review are done and pushed. I did not run the whole test suite. fast.ts named 1,446 files, so I ran the guide, page, selectors, boards and explorer tests and the 156 TUI palette tests, and all passed. tsc, gate.ts and design-lint.ts are also clean.
- Both doors:
src/tui/screens/ask_data.tsadds a "Data explorer" palette entry after Boards.
- It lists the ready questions and the sources by plain name.
- enter opens a question or source with its one-line description and the rows. / takes a sentence through parseSentence.
- The gate matches the page: admin who is a person. Others are not offered the entry, get no rows, and no query runs.
- test/tui-ask-data.test.ts covers this.
- Dead junk:
state_is removed fromNOTES. A test checks that everyNOTESkey is a real column in some source and that no note is empty. - Docs:
docs/features/data-explorer.mdnow says how to use the sentence box on the page and in the terminal, and which topics and windows it knows. It also says plainly that a phrase the parser can't read gets an error message and nothing more. - Units: every number column now has a unit, for example
memory_bytes (bytes)andcost_usd (US dollars). A test fails if any number column in any source has none. The source list under the form also shows the unit. - Count label: a rendered-page test checks the machine page shows "count of 15-second readings (counts readings, not time)", and a runs page shows "count of runs".
- Source-list links: a test follows the "Machine readings" link and checks it lands on "Average CPU cores per day, where target is forge." without a refusal. I did not change
readParams. A URL with an emptyaggregate=still reads as present and is refused, and the links never produce one. - One primary button: the sentence box's "Show" is now ghost, and a test counts exactly one
data-variant="primary"on the page. - Yesterday: it now sets both
fromandtoto that calendar day in UTC, with a test.
One thing is still open: the model fallback for sentences the parser can't read is not built.
🤖 Generated with Claude Code
I fixed the one failing check and pushed the fix as c2ede947.
The data explorer already adds an "Ask data" palette entry for a site-admin person, titled "Data explorer". The roster test for that case didn't list it. I added that entry to the expected roster in test/palette-roster.test.ts, right before "Bugs". The test was wrong, not the code.
test/palette-roster.test.ts now passes (15 of 15) and bun x tsc --noEmit is clean. I didn't run bun scripts/gate.ts or any other test files. The forge's check will run the full suite again on this push.
Mostly good: the web and terminal doors are gated alike (admin person only), the ask redirect only targets /boards/data, no mutation was added, and the box 1-4 work is real. Changes needed in one round:
- (fix first) test/boards-guide.test.ts creates the app with dataDir "". createApp resolves that to the process cwd, so the test writes the forge's db, repos and socket into the repo checkout and never closes the app. Use a mkdtemp dir and close the app in afterAll.
- Box 5 is not met: the issue asks for a model fallback for phrasings the rules miss. If it is deferred, open a follow-up issue linked to #9440 and name it in the pull. The docs already say it is not done.
- An empty
?ask=(the Show button pressed with nothing typed) shows the 'I could not read that' error because the check is!== null. Treat blank as no sentence. - The one-line sentence renders twice on the page (the #says paragraph and the chartBox caption). Keep one. The hand-rolled
<p class="small" id="says">should use a named component from docs/DESIGN.md. - src/tui/screens/ask_data.ts imports readParams and toQuery from src/web/pages/boards.ts, which makes the terminal depend on a page module. Move those two to a small shared module, for example under src/boards/.
- Stale doc comment: Guide.value says 'and its plain name', but only the column is stored. aggregateOption's sum, avg and max labels ('total of a column') say nothing about which column or its unit. Make them say what they aggregate.
- Add tests for the
tobound in sentence(), and for a non-admin getting 404 on?ask=.
/boards/data now starts from a question. Each source has a plain name and a default chart: an average over time for readings, a count per day for events. A list of ready questions opens a working chart in one press. Pickers show each column's meaning and unit, and every chart says in one line what it shows. A sentence box ("memory over the last 3 days") fills the form without a model. The terminal has the same explorer, offered in the palette as "Data explorer". Both doors are only for a site admin who is a person.
This round, from review:
- Shared query reading:
readParams/toQuerymoved tosrc/boards/params.ts. The terminal screen, the page and the panel pages import them from there, so the terminal no longer depends on a page module. - Blank box: pressing Show with nothing typed (
?ask=empty or only spaces) now draws the page with no "I could not read that" error. - The one-line sentence is shown once, in the result card's foot (
card({ foot })); the hand-rolled#saysparagraph is gone. The chart's accessible name (its SVG title, or the hidden caption of a table) is now the source's plain name, so screen readers don't hear the sentence twice. - Aggregate labels: the sum, average and highest options say which column they work on and its unit, e.g. "average of CPU cores (cores)". With no column to name, they say the column is picked below. The stale
Guide.valuecomment is corrected. - Ready-questions test: it now uses a temporary directory and closes the app, instead of writing the forge's database into the checkout.
- New tests: the sentence's from/to, since and until wording; a blank box shows no error; the sentence appears once, in the card foot; the aggregate labels; and a 404 with no redirect on
?ask=for a non-admin member and for someone signed out. - Model fallback deferred: the fallback for sentences the rules can't read (box 5) is now #9569, and #9440 waits on it.
docs/features/data-explorer.mdnames it. - Earlier in this pull:
test/palette-roster.test.tsnow lists the new "Data explorer" entry. The test was wrong, not the code.
Not done: the model fallback (#9569), and picking filter values from a list (they are still typed).
To try it: sign in as a site admin on the preview and open /boards/data. Pick "CPU over the last week", or type "memory over the last 3 days" and press Show. Press Show with the box empty and the page should draw with no error.
Checks run: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts pass. scripts/fast.ts named too broad a set (1446 files), so I ran the 158 test files that touch boards, the guide, the terminal explorer and pinned panels: 1710 pass, 0 fail (1744 ran). The full suite was not run here.
🤖 Generated with Claude Code
All seven earlier asks are met on this head. (1) The test now uses mkdtemp and closes the app. (2) Box 5 fallback is deferred to #9569 and named in the docs. (3) A blank ask box shows no error, with a test. (4) The sentence renders once, in the card foot, which is a named component. (5) readParams and toQuery moved to src/boards/params.ts. (6) Aggregate labels name the column and unit, and the stale comment is fixed. (7) Tests cover the to bound, since and until, and a 404 with no redirect for a non-admin on ask. The terminal door has the same gate, and docs/features/data-explorer.md holds the how-to rather than the README. The page has one primary button, and everything is a GET form or a link, so it works with JavaScript off. I did not re-read the preview screenshots this round.
Two things to do next:
- Non-blocking: pick filter values from a list, for example the machine targets or the reach metrics. They are still typed, which is the weakest part of box 3 for a phone.
- Non-blocking: the unread-sentence error text lists topics in two places, the page and the TUI. Move it to one constant in guide.ts so the topics cannot drift from TOPICS.
Left #9440 open: 1 unticked item remain (An optional box takes a sentence ("memory over the last 3 days") and fills the form. It works without the crew for common phrasings; the model is only a fallback.).
/boards/datanow opens on a question instead of the old query form. The model fallback for sentences is not built.tsc,gate.ts,design-lint.tsand the boards tests I ran (guide, page, selectors) are green. I did not run the rest of the suite or the pages in a browser. I couldn't usekeep_planbecause I had no such tool in this run.What changed
?ask=memory over the last 3 daysis read by plain rules insrc/boards/guide.tsand redirects to the filled form. A sentence it can't read says so on the page.docs/features/data-explorer.mddescribes it.Left out
Test edits: two existing tests were out of date.
test/boards-page.test.tsexpected the heading<h2>runs</h2>, which is now "Crew and build runs".test/boards-selectors.test.tstook the first GET form on the page, which is now the sentence box, so it finds the query form by itschartfield. The new tests are intest/boards-guide.test.ts.To try it: sign in as an admin and open
/boards/data?source=machine, press "CPU over the last week", or type "memory over the last 3 days" into the sentence box.🤖 Generated with Claude Code
Closes #9440