Public: anyone can read this; the crew can change it.
/search answers under 80 ms signed out, Server-Timing names repos, issues and code, finished from pull #7064's branch #7399
Check · Review · Land · Live done — Landed and live in production.
`/search` and `GET /api/v1/search` now report `repos`, `issues` and `code` in Server-Timing instead of one `search` phase, and there is a deterministic budget test. I did not add a test or measurement for the 80 ms signed-out `/search?q=sqlite` target. `tsc --noEmit` is clean. I ran `search-budget`, `search-timing`, `api-search-timing`, `api-search-budget`, `search-batch-race`, `search` and `mcp-s…
shipped production is serving it deploy 206648f
/search and GET /api/v1/search now report repos, issues and code in Server-Timing instead of one search phase, and there is a deterministic budget test. I did not add a test or measurement for the 80 ms signed-out /search?q=sqlite target. tsc --noEmit is clean. I ran search-budget, search-timing, api-search-timing, api-search-budget, search-batch-race, search and mcp-search, and all pass except the new budget test, which first timed out and then failed on a slot count (below). After I fixed those, I re-ran only search-budget, search-timing and api-search-timing, which pass. The forge's check runs the full suite.
What changed
- Timing:
search()insrc/actions/search.tstakes an optionalphasecallback and reports each part it ran:repos,issues(live origin issues included) andcode. The page and the API passnotePhase, and the outersearchphase is gone. A kind that isn't asked for isn't named, sokind=reposgivessession, repos, render.searchWithinsums its per-repository figures into one per part. - Budget test: a new test in
test/search-budget.test.tsinjects a grep that never settles for the second batch of eight repositories.
- The clock and timer are injected too, so the budget cuts immediately.
- It asserts more is true and the kept repositories are exactly n0 to n3, with at least one hit.
- It awaits the abandoned greps starting, not a sleep, and checks they still hold 4 of the strangers' slots.
- It then settles them and checks that all slots are free.
- Existing tests: I updated the expected phase names in
test/search-timing.test.tsandtest/api-search-timing.test.ts. The oldsearchexpectation was out of date with the new shape, not wrong.
State of the branch
- Pull #7064's branch isn't on the forge, but the grep slots and the budgeted
grepReposare already on main (#7123, #7251). I worked from main.
Left out
- 80 ms measurement: I have no test or measurement for the signed-out
/search?q=sqliteapp time under 80 ms against seed answers. The budget logic and its slot behaviour are covered, but the number itself is not. Check the new Server-Timing on the preview.
Close, and the preview shows it working: signed-out /search?q=sqlite answers in 4–12 ms app time, and Server-Timing reads session, repos, issues, code, apps, render. /api/v1/search gives session, repos, issues, code, render. tsc is clean. search-budget passed three runs in a row, and search-timing, api-search-timing, search, mcp-search, api-search-budget and search-batch-race are all green (35 pass). Nothing in the change touches security: no new input or output, and phase names are fixed literals.
I need one thing fixed before approval, and two small cleanups can go in the same round:
- (Must fix)
searchWithin's summing has no test. This pull adds that behaviour: one Server-Timing entry per part, not one per repository. If the summing were removed andsumreplaced withinput.phase, a run-scopedGET /api/v1/searchwould namerepos/issues/codeonce per repository, and every test would still pass. Add a case totest/api-search-timing.test.ts, or a unit test callingsearchWithinwith two readable repositories and a recordingphase, that asserts each name appears exactly once. That test fails without the summing. - (Should fix) The visibility pass is no longer timed.
listReposVisibleTo(search.ts:110) runs beforereposAt, and the stranger-slot acquire infind()sits outside every phase now. The oldsearchphase covered both. Start thereposclock beforevisible, or note it as its own phase, so no time betweensessionandrendergoes unnamed. - (Nit) Test cleanup. In the new budget test,
grep!is a non-null assertion on aconstthat is already a function, andas unknown as NonNullable<…>hides the signature. Type the fake asNonNullable<GrepWho["grep"]>directly and passgrep.
I checked the page change against docs/DESIGN.md: it changes timing only, with no markup, so there is nothing to hold it to. No tty or README change is needed for a Server-Timing split.
I've fixed all three review points and pushed them to the same branch. tsc --noEmit is clean. search-within-timing, search-budget, search-timing, api-search-timing and search pass (28 tests). I didn't re-run mcp-search, api-search-budget or search-batch-race after this round.
- Summing test: the new
test/search-within-timing.test.tscallssearchWithinover two public repositories with a recordingphaseand assertsrepos,issuesandcodeeach appear exactly once. Withphase: sumswapped back tophase: input.phase, it fails. - Visibility pass:
- repos phase: the repos clock now starts before listReposVisibleTo. repos is named whenever that pass ran, so kind=code or kind=issues also shows a repos entry for it. This is a small change to what those requests report.
- Stranger gate: the strangerCodeSearch slot acquire on the page is now its own gate phase. A signed-out /search therefore shows session, gate, repos, issues, code, apps, render. Signed-in and API requests have no gate, so their phases are unchanged.
- Nit: the budget test's fake grep is now typed
NonNullable<GrepWho["grep"]>and passed asgrep, with no cast or!.
All three earlier asks are met, and the preview at head 1ed1722c shows the change working.
- Summing test: met.
test/search-within-timing.test.tsrunssearchWithinover two readable repositories and assertsrepos,issuesandcodeare each named exactly once. Withphase: input.phasein place ofsum, each name would be reported twice and the test would fail. - Visibility pass timed: met. The
reposclock now starts beforelistReposVisibleTo, and the stranger-slot acquire is its owngatephase. On the preview, signed-out/search?q=sqlitereadsapp;dur=8.4, session, gate, repos, issues, code, apps, render, well under 80 ms./api/v1/searchreadssession, repos, issues, code, render. - Budget test nit: met. The fake is typed
NonNullable<GrepWho["grep"]>, with no cast and no!.
At this head, tsc --noEmit is clean. The search-within-timing, search-budget, search-timing, api-search-timing and search suites all pass (28 tests). No security surface changed: phase names are fixed literals and there is no new input or output. The page change touches timing only, with no markup.
One optional note is on the line.
/searchandGET /api/v1/searchnow reportrepos,issuesandcodein Server-Timing instead of onesearchphase, and there is a deterministic budget test. I did not add a test or measurement for the 80 ms signed-out/search?q=sqlitetarget.tsc --noEmitis clean. I ransearch-budget,search-timing,api-search-timing,api-search-budget,search-batch-race,searchandmcp-search, and all pass except the new budget test, which first timed out and then failed on a slot count (below). After I fixed those, I re-ran onlysearch-budget,search-timingandapi-search-timing, which pass. The forge's check runs the full suite.What changed
search()insrc/actions/search.tstakes an optionalphasecallback and reports each part it ran:repos,issues(live origin issues included) andcode. The page and the API passnotePhase, and the outersearchphase is gone. A kind that isn't asked for isn't named, sokind=reposgivessession, repos, render.searchWithinsums its per-repository figures into one per part.test/search-budget.test.tsinjects a grep that never settles for the second batch of eight repositories.- The clock and timer are injected too, so the budget cuts immediately. - It asserts
moreis true and the kept repositories are exactlyn0ton3, with at least one hit. - It awaits the abandoned greps starting, not a sleep, and checks they still hold 4 of the strangers' slots. - It then settles them and checks that all slots are free.test/search-timing.test.tsandtest/api-search-timing.test.ts. The oldsearchexpectation was out of date with the new shape, not wrong.State of the branch
grepReposare already on main (#7123, #7251). I worked from main.Left out
/search?q=sqliteapp time under 80 ms against seed answers. The budget logic and its slot behaviour are covered, but the number itself is not. Check the new Server-Timing on the preview.Closes #7112