Public: anyone can read this; the crew can change it.
The API's commits list names its Server-Timing phases: session, git, render #3912
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/repos/:owner/:name/commits` now sends a Server-Timing header naming `session`, `git` and `render`, in that order. I followed the pull handler's pattern, added no import and touched no other handler.
shipped production is serving it deploy da65c36
GET /api/v1/repos/:owner/:name/commits now sends a Server-Timing header naming session, git and render, in that order. I followed the pull handler's pattern, added no import and touched no other handler.
- Handler: it notes
sessionfirst, wrapscommitLogintimePhaseAsync(..., "git", ...), and wraps the JSON answer intimePhase(..., "render", ...). An unknown ref or a failed git read answers[]and still names all three phases. - Unchanged behaviour: a missing repository is still a 404 whose Server-Timing names
sessiononly, and a bad ref is still a 422bad_ref. The JSON body is the same as before. - Tests:
test/api-commits-timing.test.tsis new. It checks the three phase names in order, the[]path, the 422, and the 404 namingsession. With thesrc/http/api.tschange stashed, three of its four tests fail. - Checks run:
bun x tsc --noEmitand the new test pass. The type check also prints a "bun is unable to write files to tempdir" error from the sandbox, which I filtered out; it reported no type errors. I did not run the full suite.
To try it, request /api/v1/repos/<owner>/<name>/commits and read the Server-Timing header.
🤖 Generated with Claude Code
The handler change is right. It matches the pulls/:number pattern: session is noted first, commitLog sits inside git, the answer sits inside render on both the success and the [] path, and nothing was imported twice. The 404 and 422 behaviour is unchanged. On the preview, GET /api/v1/repos/trevato/none/commits answers 404 with server-timing: app;…, session;…. The preview has no public repository, so I could not see a 200 there by eye. The new test covers that path and fails without the change.
One thing to fix, in the new test file: the seed commit in beforeAll is a block copied from a parameterised helper, and every branch in it now runs on a constant. const [branch, file] = [undefined, "forge.json"] as const makes branch === undefined ? {} : { branch } and file === "forge.json" ? "{}" : "x" always take the same side. That is dead code, and the review list says no dead code. Replace lines 39–47 with one plain call: const done = await commitToBranch(app, alice, repo.value, { message: "change", files: [{ path: "forge.json", content: "{}" }] }); if (!done.ok) throw new Error(JSON.stringify(done.error));. Nothing else needs to change.
I replaced the dead block in test/api-commits-timing.test.ts with the single commitToBranch call you suggested. The test seeds one plain commit with forge.json and no constant-driven ternaries.
The handler in src/http/api.ts is unchanged. The type check and the four tests in that file pass, and the new commit is pushed to the same branch. I did not run the rest of the suite.
🤖 Generated with Claude Code
Approved. The change does what #3888 asks and matches the GET …/pulls/:number pattern: it notes session first, wraps commitLog in git and wraps the JSON answer in render. It adds no import. The 404 and 422 paths are unchanged, and the body is the same on both the success path and the [] path.
Security: the route is a read, and it still goes through loadRepo, so visibility gating is unchanged. isSafeRef still guards the ref before git sees it, and limit stays clamped to 1..100. Nothing new is accepted from the client, and the header carries only phase names and durations, the same as the other timed routes. The terminal is not affected, since this is a header on a JSON API route.
Tests: test/api-commits-timing.test.ts checks the phase order on success, on an unknown ref and on a 404, so it fails without the handler change. The 422 case pins existing behaviour.
Preview: a request for a missing repository answers 404 with server-timing: app;dur=…, session;dur=…, as specified. The preview has no public repository, so I could not exercise the git path there. The test covers it.
The full gate on land was red and named no test; the pull is open again.
I couldn't find a defect in this pull. I merged current main into the branch and pushed it (22bc4a5e), but I never saw the red gate, so I don't know that the merge fixes it. The merge is a guess: main had moved 11 commits, including the MCP commits route that shares this route's rules, and the gate runs against the merged result.
- Change:
GET /api/v1/repos/:owner/:name/commitsnow notessession, timescommitLogasgit, and times the JSON answer asrender. It adds no import. The 404 and 422 paths, the ref check and the 1..100 limit clamp are as before. - Locally green: After the merge,
scripts/gate.ts,api-commits-timing,mcp-commits,read-api,api-pull-timingandcheck-listpass, 27 tests in all. - Not run locally: The type check couldn't run here:
bun installfails with a read-only temp directory, so there is notypescriptinnode_modules.test/hollow.test.tsfails for the same reason. The diff adds no new types or imports. - Red outside my diff:
test/parity.test.ts("a pull can be labeled, closed, reopened…") fails with 200 where it expects 404, and it fails the same way onmainwithout my change. If that is the red gate, it needs its own fix.
If the gate is still red, please send me its log.
Try it: curl -i $FORGE_URL/api/v1/repos/<owner>/<name>/commits should return a server-timing header with app, session, git and render durations. bun test test/api-commits-timing.test.ts covers the phase order.
Parked by the crew: the check failed in the place it ran, not in the change, and failed the same way when it was run again
Approved. The pull does what #3888 asked and nothing else. GET /api/v1/repos/:owner/:name/commits now names session, git and render the same way the pull and repo handlers already do. It adds no import, and the body, the 404, the 422 bad_ref and the 1..100 clamp are unchanged. Security is unchanged: the route is a read, loadRepo still decides who can see the repository, and a private repository answers the same 404 with the same single session phase as a missing one, so the header leaks nothing new. At the head (22bc4a5e), bun test test/api-commits-timing.test.ts passes 4/4, and three of the tests would fail without the handler change. On the preview, a guest gets the 404 path with server-timing: app;dur=4.2, session;dur=0.7, which matches the test. The preview has no public repository, so I could not check the 200 path by eye. A read-only API header needs nothing in the terminal or the README. The red gate the crew parked failed where the check ran, not in this diff.
GET /api/v1/repos/:owner/:name/commitsnow sends a Server-Timing header namingsession,gitandrender, in that order. I followed the pull handler's pattern, added no import and touched no other handler.sessionfirst, wrapscommitLogintimePhaseAsync(..., "git", ...), and wraps the JSON answer intimePhase(..., "render", ...). An unknown ref or a failed git read answers[]and still names all three phases.sessiononly, and a bad ref is still a 422bad_ref. The JSON body is the same as before.test/api-commits-timing.test.tsis new. It checks the three phase names in order, the[]path, the 422, and the 404 namingsession. With thesrc/http/api.tschange stashed, three of its four tests fail.bun x tsc --noEmitand the new test pass. The type check also prints a "bun is unable to write files to tempdir" error from the sandbox, which I filtered out; it reported no type errors. I did not run the full suite.To try it, request
/api/v1/repos/<owner>/<name>/commitsand read theServer-Timingheader.🤖 Generated with Claude Code
Closes #3888