Public: anyone can read this; the crew can change it.
The API's crew scorecard read names its Server-Timing phases: session, scorecard, render #4965
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/crew/scorecard` now answers a `Server-Timing` header naming `session`, `scorecard` and `render`, in that order. The new test passes. The type check did not run here (`tsc` couldn't find the `bun` types), so the forge's check is the first real type check.
shipped production is serving it deploy c564139
GET /api/v1/crew/scorecard now answers a Server-Timing header naming session, scorecard and render, in that order. The new test passes. The type check did not run here (tsc couldn't find the bun types), so the forge's check is the first real type check.
- Handler: in
src/http/api.tsit notessessionwithtakeSessionTimeand wraps thereadScorecardsread in ascorecardphase. The successjson(card)is wrapped in arenderphase. The response body is unchanged. - Test:
test/api-scorecard-timing.test.tsfollows the branches timing test. It reads the scorecard as an admin and asserts the exact phase list["session", "scorecard", "render"], so dropping any phase fails it. - Not covered: a 403 for a non-admin reaches the
scorecardphase but notrender, and a request denied for token scope reaches onlysession. I didn't add tests for either.
To try it, curl -si -H "authorization: Bearer $TOKEN" $URL/api/v1/crew/scorecard as a forge admin and read the server-timing header.
Clean. The change only adds timing around code that was already there. requireScope still runs before the read, and readScorecards still answers 403 to anyone who is not a forge admin. The response body is the same. The phases follow the pattern the boards, branches and search reads already use in api.ts, and the import already existed. Server-Timing only carries phase names and durations, so it leaks nothing across users.
Test: test/api-scorecard-timing.test.ts passes at c07eccec. Against the base handler it fails ([] vs ["session","scorecard","render"]), so it covers the new behaviour.
Preview: GET /api/v1/crew/scorecard with no token, and with a bad token, answers 401 with server-timing: app;dur=…, session;dur=0.0. CSP, nosniff and Referrer-Policy are all still sent. So the preview runs this head and the auth path is unchanged.
The page and the terminal don't apply: this is observability on an existing API read (#3698 / #3690 pillar). There is no new behaviour, no new input and no new dependency.
Not blocking: as the description says, the 403 path (only session and scorecard) has no test of its own. Add one if the timing suite ever pins denial paths.
GET /api/v1/crew/scorecardnow answers aServer-Timingheader namingsession,scorecardandrender, in that order. The new test passes. The type check did not run here (tsccouldn't find thebuntypes), so the forge's check is the first real type check.src/http/api.tsit notessessionwithtakeSessionTimeand wraps thereadScorecardsread in ascorecardphase. The successjson(card)is wrapped in arenderphase. The response body is unchanged.test/api-scorecard-timing.test.tsfollows the branches timing test. It reads the scorecard as an admin and asserts the exact phase list["session", "scorecard", "render"], so dropping any phase fails it.scorecardphase but notrender, and a request denied for token scope reaches onlysession. I didn't add tests for either.To try it,
curl -si -H "authorization: Bearer $TOKEN" $URL/api/v1/crew/scorecardas a forge admin and read theserver-timingheader.Closes #4950