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

The explorer keeps its filters in the URL, so a filtered view is a link that reloads the same #10732

Merged opened this

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

Merged

The explorer's filters are now separate query parameters: `/explore?kind=app&sort=forks&owner=olive&updated=7d&archived=yes&q=garden`. Reloading one gives the same page. I ran `tsc`, the gate and the explore, MCP-size and related tests locally, and all pass. I did not run the full suite; the forge runs that on push. The `keep_plan` tool wasn't available to me, so there is no plan on the issue.

shipped production is serving it deploy f9da504

opened this

The explorer's filters are now separate query parameters: /explore?kind=app&sort=forks&owner=olive&updated=7d&archived=yes&q=garden. Reloading one gives the same page. I ran tsc, the gate and the explore, MCP-size and related tests locally, and all pass. I did not run the full suite; the forge runs that on push. The keep_plan tool wasn't available to me, so there is no plan on the issue.

What changed

  • src/actions/explore.ts gains exploreLineFrom, which turns a request's q plus the separate parameters into the existing one-line form. exploreParams does the reverse for links.
  • /explore and /api/v1/explore read the parameters through exploreLineFrom. The page's chips, "Drop a term" links and "Did you mean" link now use the separate-parameter form. The page is still server-rendered from the URL.
  • The MCP explore tool takes kind, sort, updated, owner and archived as arguments, named the same as the URL parameters. A parameter wins over the same key typed in q. test/mcp-size.test.ts passes, no ceiling raised.
  • A value is cut to one word, so kind=app garden can't add text or another key. An invalid value is dropped for the default and named in a note. As before, the page and API answer 422 when a value is invalid, and the page still shows the default results. Unknown parameters are ignored. Output stays escaped.
  • Ranking is untouched, and explore-rank and explore-parity pass.
  • test/explore-url.test.ts is new. It requests two filtered URLs and checks they return different result sets, that a reload equals the first load, and that unknown parameters, bad values, escaping and the chip links behave as above. docs/MCP.md is updated.

Existing tests I changed

  • Several tests in explore.test.ts, explore-unknown-words.test.ts and explore-parity.test.ts asserted the old href="/explore?q=kind%3Aapp" link form. They now expect the separate-parameter form. I changed them because the link format is what this issue changes. The ?q= line form still works on the page, API and MCP, and the search box still submits it.

What I chose not to do

  • No in-page pushState filtering. The filters are plain links, so Back already returns to the previous filter and there is no script to ship.
  • I did not add a way for the search box to submit separate parameters. It still puts the whole line into q.

To try it

  • Open /explore?kind=app&sort=forks, then /explore?kind=person, and reload either.
  • Run bun test test/explore-url.test.ts.

I had to add an empty commit with a "Try:" line because the pre-push hook asked for one.

Closes #10727

commented

The explorer's filters are now separate query parameters: /explore?kind=app&sort=forks&owner=olive&updated=7d&archived=yes&q=garden. Reloading one gives the same page. I ran tsc, the gate and the explore, MCP-size and related tests locally, and all pass. I did not run the full suite; the forge runs that on push. The keep_plan tool wasn't available to me, so there is no plan on the issue.

What changed

  • src/actions/explore.ts gains exploreLineFrom, which turns a request's q plus the separate parameters into the existing one-line form. exploreParams does the reverse for links.
  • /explore and /api/v1/explore read the parameters through exploreLineFrom. The page's chips, "Drop a term" links and "Did you mean" link now use the separate-parameter form. The page is still server-rendered from the URL.
  • The MCP explore tool takes kind, sort, updated, owner and archived as arguments, named the same as the URL parameters. A parameter wins over the same key typed in q. test/mcp-size.test.ts passes, no ceiling raised.
  • A value is cut to one word, so kind=app garden can't add text or another key. An invalid value is dropped for the default and named in a note. As before, the page and API answer 422 when a value is invalid, and the page still shows the default results. Unknown parameters are ignored. Output stays escaped.
  • Ranking is untouched, and explore-rank and explore-parity pass.
  • test/explore-url.test.ts is new. It requests two filtered URLs and checks they return different result sets, that a reload equals the first load, and that unknown parameters, bad values, escaping and the chip links behave as above. docs/MCP.md is updated.

Existing tests I changed

  • Several tests in explore.test.ts, explore-unknown-words.test.ts and explore-parity.test.ts asserted the old href="/explore?q=kind%3Aapp" link form. They now expect the separate-parameter form. I changed them because the link format is what this issue changes. The ?q= line form still works on the page, API and MCP, and the search box still submits it.

What I chose not to do

  • No in-page pushState filtering. The filters are plain links, so Back already returns to the previous filter and there is no script to ship.
  • I did not add a way for the search box to submit separate parameters. It still puts the whole line into q.

To try it

  • Open /explore?kind=app&sort=forks, then /explore?kind=person, and reload either.
  • Run bun test test/explore-url.test.ts.

I had to add an empty commit with a "Try:" line because the pre-push hook asked for one.

Changes requested — before the latest push

The URL side is sound: server-rendered from separate params, values cut to one word, escaped, unknown params ignored. Two behaviours the pull adds have no test, and box 4 rests on one of them. Fix first: add an MCP test for the new arguments.

  1. MCP: no test calls the explore tool with kind/sort/updated/owner/archived as arguments. Add one that asserts the same items as the API for the same params (in test/explore-parity.test.ts). Without it, dropping the ...Object.fromEntries(EXPLORE_PARAMS...) schema line or the str(args[name]) lookup stays green.
  2. Precedence: docs/MCP.md says a parameter wins over the same key in q, but nothing tests it. exploreLineFrom just appends kind:app after q, so q=kind:person with kind=app depends on how readExploreLine resolves duplicate keys. Add a test on page, API and MCP that proves the parameter wins, or fix the code so it does.

Boxes 1, 3 and 5 are met. Box 2 is met by plain links (Back works, no script). Box 4 is left until finding 1 is fixed.

commented

Both findings are fixed and pushed. tsc, scripts/gate.ts and the explore tests pass: test/explore-parity.test.ts plus the five other explore test files I ran.

What changed

  • Precedence is now explicit. exploreLineFrom in src/actions/explore.ts drops a key: word typed in q when a separate parameter of that key is given. Before this, a duplicate key resolved by the order the reader happened to scan the line. The parameter now wins whatever the order, with no change to the line grammar. The one-word cut on values is unchanged.
  • The MCP schema is tighter. The new arguments carry maxLength: 100, and kind, sort, updated and archived carry an enum taken from the same word lists the parser uses (EXPLORE_VALUES). owner is a free name, so it has no enum.
  • MCP test (finding 1). In test/explore-parity.test.ts, the explore tool is called with kind, sort, updated, owner and archived as arguments. Four parameter sets are checked: the API, the MCP, the page and the equivalent one-line q all return the same rows in the same order. Dropping the schema line or the str(args[name]) lookup would now fail this.
  • Precedence test (finding 2). The test sends q=kind:person sort:forks together with kind=app&sort=name. All three surfaces return the app-only, name-sorted rows. On the page I checked the row count and that every row is an app link, not the exact order.

Not done

  • I did not check that the MCP server enforces the enum. It looks like the server does not validate arguments against inputSchema, so the enum is documentation for clients. An unknown value still returns the existing 422 unknown_filter error, which suggests a fix, and I kept that path.
  • scripts/fast.ts said the diff was too broad to name tests, so I picked the explore suites myself and did not run the full suite.

Try it Open /explore?kind=app&sort=forks, or call the explore tool with { "kind": "app", "sort": "name" }.

commented

The explorer's filters are now separate URL parameters (?kind=app&sort=forks&owner=x&updated=7d&archived=yes) on the page, the API and the MCP explore tool. A filtered view is a link that reloads the same. tsc and the touched tests pass; I did not re-run the full suite. The forge's check will run it.

What changed

  • exploreLineFrom in src/actions/explore.ts builds one line from q plus each parameter as a one-word key:value. A parameter wins over the same key typed in q: that word is dropped from q, so the result no longer depends on the order the line is read.
  • The MCP explore tool takes kind, sort, updated, owner and archived as arguments, each capped at 100 characters.
  • test/explore-parity.test.ts calls the tool with those arguments and checks it returns the same rows as the API, the page and the one-line form. A second test sends q=kind:person sort:forks with kind=app&sort=name and checks all three surfaces return the app-only, name-sorted rows.

Fix after the failed check

  • Cause: the value enums I had added to the explore schema made the tool larger, which pushed forge_view onto the second tools/list page. That is what failed mcp-forge-view.
  • Fix: I removed the enums from the listing, so the arguments carry only the length cap. An unknown value still returns the existing 422 unknown_filter error that names the allowed words. This leaves out the enum the reviewer asked for, because the listing is paged by size (20 KB a page) and the test needs forge_view on page one.
  • Flaky test: pull-quiet ("the pull list wears a dot per row…") passed when I ran it here, and I did not find a cause. It looks like a flake in that shard.

What I chose not to do

  • No client-side script. Filters are plain links, so Back works.

To try it Open /explore?kind=app&sort=forks and reload it, or call the explore tool with { "kind": "app", "sort": "name" }.

Changes requested — before the latest push

The behaviour works in the preview: /explore?kind=app&sort=forks answers 200 and the parameters are tested on the page, API and MCP. One thing to fix first: docs/features/explore.md still describes the old form. Line 31 says 'The page's chips are plain links to /explore?q=<line>', and the table at lines 35-39 shows ?q=sort:forks, ?q=kind:app and so on. This change makes the chips separate parameters (?sort=forks) and adds kind, sort, updated, owner and archived as MCP arguments, so the how-to has to say that. Update it. Earlier asks: the MCP argument test and the precedence test are met. The dropped enum is acceptable given the tools/list size budget. Non-blocking: the precedence test on the page checks only row count and link shape, not exact order.

commented

/explore now keeps each filter in its own query parameter (?kind=app&sort=forks&owner=x), so a filtered view is a link that reloads the same. The page's links are built in this form, and GET /api/v1/explore and the MCP explore tool accept the same names. The only argument the tool had before was the single q line, which still works everywhere, so old /explore?q=… links keep working.

  • src/actions/explore.ts: exploreLineFrom turns the request's q plus the separate parameters kind, sort, updated, owner and archived into the one line the explorer already reads. A separate parameter overrides the same key typed in q, so ?q=sort:name&sort=forks sorts by forks. Each value is one word: whitespace is removed and it is cut at 100 characters, so it can't sneak in a second key. A value the explorer doesn't know gets the same 422 as an unknown word in q. exploreParams writes a query back out as separate parameters, which is the form the page links to.
  • Page, API and MCP: the three entry points (src/web/studio/explore.ts, src/http/api.ts, src/http/mcp/tools/explore.ts) all read through exploreLineFrom. The page's tabs, chips, owner links and "Did you mean" link use the separate parameters. On MCP, the five keys are their own string arguments without the list of allowed values: adding it would push forge_view off the first page of tools/list. The terminal's search line still takes only the single line.
  • Docs: in docs/features/explore.md, as asked in review, the table now shows the parameter form (?sort=forks, { sort: "forks" }). It also says the q line still works, that a parameter wins over the same key in q, and that the "Did you mean" link uses the parameter form. docs/MCP.md describes the new arguments.
  • Tests: test/explore-url.test.ts covers the URL form and the precedence rule. test/explore-parity.test.ts checks that the page, API and MCP return the same rows for the separate parameters. The test on the page that a parameter beats the same key in q checks only the number of rows and the shape of the links, not the exact order; I left that as it was.

The gate passes, the type check is clean, and the explore, MCP-size and docs tests pass (99 tests across 8 files). scripts/fast.ts named 1680 test files for this branch, which is over its five-minute budget, so I chose those eight myself instead.

To try it: open /explore?kind=app&sort=forks, reload it, and click a chip; the address keeps the separate parameters. Compare /explore?q=sort:name&sort=forks, which sorts by forks, and GET /api/v1/explore?kind=person.

🤖 Generated with Claude Code

Approved

Earlier asks met: MCP arguments are tested against API and page, and a parameter now wins over the same key in q on all three surfaces; docs table shows the parameter form. Dropping the enum to keep forge_view on page one of tools/list is reasonable; an unknown value still gets the 422 naming the allowed words. The preview works: chips are /explore?kind=app and ?sort=forks, a bad sort gives 422 with the default rows, and the API takes the same parameters. The change adds no markup, so the design rules are unchanged. Non-blocking: the precedence test on the page checks only the row count and link shape, not the order.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs