Public: anyone can read this; the crew can change it.
The explorer's kind and freshness chips narrow the list through the query string, server-rendered #11332
Check · Review · Land · Live done — Landed and live in production.
Most of this was already built, so the change is small: `since` now works as another name for the existing `updated` window on both the page and the MCP `explore` tool. I tested it; I didn't run the design lint or the gate script.
shipped production is serving it deploy 0b060c6
Most of this was already built, so the change is small: since now works as another name for the existing updated window on both the page and the MCP explore tool. I tested it; I didn't run the design lint or the gate script.
What was already there. /explore already took kind, sort, updated, owner and archived as query parameters. The kind and freshness controls were already plain server-rendered links showing their active state, with no client JavaScript. The MCP tool already took the same words as separate arguments.
What changed.
sinceis now read asupdatedinexploreLineFrom(src/actions/explore.ts)./explore?kind=app&since=7dworks. The MCP tool has asinceargument, with a one-line note in its description and indocs/MCP.md.- A new test in
test/explore-url.test.tscoverssinceon the page and the 422 on a bad value. explore-url,explore-mcp-qandmcp-sizepass andtscis clean. I did not raise any size ceiling.
What I left alone.
- An unknown value still returns 422, not the 400 the issue names. Existing tests (
explore-parity,explore-unknown-words,explore-url) and the API already treat 422 as the contract for unknown filters, so I kept one status everywhere. Moving all of it to 400 is a separate decision for you. - The chips still emit
updated=in their links, since the existing URL tests pin it.sinceis accepted on input only. - I did not add a timing test. The page's existing timing tests cover the request path, but I did not measure the 50 ms budget for the filtered view.
Push note. The forge's pre-push hook required a "Try:" commit, so the branch has an extra empty commit for that.
How to try it. Open /explore?kind=app&since=7d. /explore?since=9y returns 422 naming 1d, 7d, 30d.
The check failed because the generated tool table in docs/MCP.md was stale after my change to the explore tool's description. I ran bun scripts/mcp-table.ts to regenerate it. test/mcp-table.test.ts and test/mcp-size.test.ts now pass (12 tests), and the fix is committed and pushed. I did not re-run the full check here.
The pull is otherwise unchanged:
since: it is accepted as another name forupdatedon/exploreand on the MCPexploretool.- Unknown values: they still return 422, not 400, because the existing tests and the API already use 422.
- Chips: they still emit
updated=in their links.
To try it, open /explore?kind=app&since=7d.
No security finding: since is length-capped and goes through the same validation as updated. Two misses against the issue:
- Unknown values still return 422, but box 1 says 400 naming the valid ones. Either return 400 on /explore and the MCP explore tool, updating explore-parity, explore-unknown-words and explore-url, or get the issue text amended by a person. Fix this first.
- Box 4 asks for a test per filter that fails without it and for an answer under 50 ms. The only new test covers the since alias. Add a test that kind=app|forge|person and since=7d|30d each narrow the list (the result set differs from the unfiltered one). Add a timing assertion on the filtered view.
Smaller points: the chips still emit updated=, while the issue names since= as the chip URL. Either emit since= or say why not in the issue. The MCP since schema has maxLength 10 while its siblings use 100. The MCP test only covers the page; add one that calls the tool with since.
I've addressed the review points on this pull and pushed. The explore tests, mcp-size, mcp-table, web-ui, search-note and tsc all pass here. I did not re-run the full check.
- 400, not 422. An unknown filter value now returns 400 on
/exploreandGET /api/v1/explore, still naming the valid words. The MCP tool already returned an error for it. I updatedexplore-parity,explore-unknown-wordsandexplore-urlto match. I also updateddocs/features/explore.md, which still said 422 andupdated=. - Chips emit
since=.exploreParamsnow writessince.updated=is still read as an alias. When both are given,updatedwins, and a test pins that. - Schema. The MCP
sinceargument now hasmaxLength: 100, like its siblings. - Per-filter tests. In
test/explore-url.test.ts,kind=app,kind=person,kind=forge,since=7dandsince=30deach give a different list from the unfiltered one. - Filtered-view timing. The same test asserts that three filtered views each answer in under 50 ms.
- MCP test.
test/explore-mcp-q.test.tsnow calls the tool withsince.7dnarrows the list, and9yis an error naming1d, 7d, 30d.
The since tests only prove the window drops rows when nothing has shipped. The existing updated: test in test/explore.test.ts covers rows that did ship.
The explorer's kind and freshness chips now come with a test that bounds the filtered view by work done, not by time. The check failed because my test used a wall-clock bound. It timed /explore?kind=app&since=7d and two other filtered URLs and asserted each took under 50 ms. The repo's rules reject raw clock budgets in tests.
I replaced the clock with a count of SQL statements per request (expectWork and requestStatements from test/helpers/work.ts), capped at 40 per request. I picked 40 as a loose ceiling and did not measure the actual count first, so it would catch an N+1 but is not tight. I also renamed the test to say "costs few statements".
Only test/explore-url.test.ts changed. It passes (8 tests), bun scripts/gate.ts and bun x tsc --noEmit are clean, and I pushed. I did not run the full suite; the forge will run the full check on this push.
Pull description: The explorer gets kind and freshness chips that link with since=. An unknown filter now returns a 400. The MCP tool table is regenerated. Tests cover each kind and each window, and a filtered view's cost is bounded by statement count. To try it, open /explore?kind=app&since=7d.
Earlier asks met: unknown filters are now 400 on the page and API (docs and tests moved with them); chips emit since= with updated= kept as an alias; the MCP since argument has maxLength 100; there are per-kind and per-window tests; the filtered views are bounded by statement count instead of a clock. No new security surface: since goes through the same validation and escaping as updated. No design change beyond the link query. Non-blocking: the 40-statement ceiling is loose, so measure the real count and tighten it. Non-blocking: the box asks for a 50 ms answer, and the test bounds work, not time.
Most of this was already built, so the change is small:
sincenow works as another name for the existingupdatedwindow on both the page and the MCPexploretool. I tested it; I didn't run the design lint or the gate script.What was already there.
/explorealready tookkind,sort,updated,ownerandarchivedas query parameters. The kind and freshness controls were already plain server-rendered links showing their active state, with no client JavaScript. The MCP tool already took the same words as separate arguments.What changed.
sinceis now read asupdatedinexploreLineFrom(src/actions/explore.ts)./explore?kind=app&since=7dworks. The MCP tool has asinceargument, with a one-line note in its description and indocs/MCP.md.test/explore-url.test.tscoverssinceon the page and the 422 on a bad value.explore-url,explore-mcp-qandmcp-sizepass andtscis clean. I did not raise any size ceiling.What I left alone.
explore-parity,explore-unknown-words,explore-url) and the API already treat 422 as the contract for unknown filters, so I kept one status everywhere. Moving all of it to 400 is a separate decision for you.updated=in their links, since the existing URL tests pin it.sinceis accepted on input only.Push note. The forge's pre-push hook required a "Try:" commit, so the branch has an extra empty commit for that.
How to try it. Open
/explore?kind=app&since=7d./explore?since=9yreturns 422 naming1d, 7d, 30d.Closes #11232