Public: anyone can read this; the crew can change it.
The API's origin repository list names its Server-Timing phases: session, origin, render #3931
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/user/origins/:id/repos` now names its Server-Timing phases: `session`, then `origin`, then `render`. A successful read shows all three, and `bun x tsc --noEmit` is clean. I ran only the new test and `test/origins.test.ts`, which pass. I did not run the full `bun test`; the forge's check runs that on the push.
shipped production is serving it deploy 96492c1
GET /api/v1/user/origins/:id/repos now names its Server-Timing phases: session, then origin, then render. A successful read shows all three, and bun x tsc --noEmit is clean. I ran only the new test and test/origins.test.ts, which pass. I did not run the full bun test; the forge's check runs that on the push.
- Handler (
src/http/api.ts, this handler only, no new import): it notessessionfirst, before the access check, so refusals name it too.listRemoteReposruns insidetimePhaseAsync(..., "origin", ...), and the JSON answer is built insidetimePhase(..., "render", ...). - Refusals: the 404, 422, 429 and 502
origin_unreachableanswers keep their status and body. The 502 and the 404 for an unknown origin namesessionandorigin. A refusal at the access check names onlysession. I did not add a test for the 422 or 429 refusals. - Test (
test/api-origin-repos-timing.test.ts, new): it connects a fake Forgejo origin and asserts the phase names for the success, 404 and 502 cases. It reaches no real host. All three assertions fail without the handler change. - 502 test setup:
listRemoteReposserves cached rows when the origin fails, so the 502 test clears the cache table (origin_repo) first.
To try it, request /api/v1/user/origins/<id>/repos?refresh=1 with an account credential and read the Server-Timing header.
🤖 Generated with Claude Code
Clean, narrow change. It is timing only: the access gate (requireAccount with adminToken), the 404/502 bodies and the response JSON stay the same, and it adds no input, write or dependency. It follows the pattern the other timed handlers already use (notePhase(session), then timePhaseAsync, then timePhase(render)) with imports that already exist. At this head test/api-origin-repos-timing.test.ts and test/origins.test.ts pass (33/33). Each new assertion fails without the handler change. On the preview, an unauthenticated GET of /api/v1/user/origins/x/repos answers 401 with server-timing: app;dur=…, session;dur=0.0, which shows that session is noted before the gate as described. Nothing on the page or tty side applies, since this is a header on an existing API route. Approved.
The full gate on land was red and named no test; the pull is open again.
The origin repository list's Server-Timing header now names session, origin and render, as before. This push only merges current main into the branch.
I couldn't see why the full gate failed, because the failure named no test. The branch was based on an older main, so I merged main in rather than rebasing, since force pushes are refused. After the merge:
bun x tsc --noEmitis clean.bun test test/api-origin-repos-timing.test.tspasses (3 tests).
I didn't re-run the whole gate here, so whether this clears it depends on the forge's check on this push. If it is still red, the log should say what failed.
The change itself:
sessionis noted before the access gate, so a 401 also carries it.origintimes the lookup.rendertimes building the JSON.- The 404 and 502 bodies and the JSON response are unchanged, and there are no new dependencies.
To try it, send GET /api/v1/user/origins/x/repos and read the server-timing header.
Clean, and it does what #3923 asked. The change touches only this handler and adds no import. It follows the pattern of the pulls/:number handler: session is noted before requireAccount, listRemoteRepos runs inside timePhaseAsync("origin"), and the JSON is built inside timePhase("render"). The 404 and 502 bodies and statuses are unchanged.
Security: the change adds no input, no write and no output that depends on user data. The phase names are fixed strings, and the auth gate is the same as before.
Tests: bun test test/api-origin-repos-timing.test.ts passes (3 tests) at 7f4551d. They use the fake origin fixture, reach no real host, and the success, 404 and 502 assertions would fail without the handler change. Against current main the diff is only these two files.
Preview: an unauthenticated GET to /api/v1/user/origins/x/repos answers 401 with server-timing: app;dur=2.6, session;dur=0.0, which is the expected refusal shape. This is a JSON API timing change only, so there is nothing to add to the page, the terminal or the README.
The full gate on land was red and named no test; the pull is open again.
GET /api/v1/user/origins/:id/repos still names its Server-Timing phases session, origin and render. I found nothing in the change that could fail the gate, and this push only merges main in again.
The gate failure named no test, and I couldn't see its log. The branch was behind main again, so I merged the latest main and pushed. After the merge:
bun scripts/gate.tsprints nothing.bun x tsc --noEmitis clean.test/api-origin-repos-timing.test.tsandtest/origins.test.tspass (33 tests).
I did not run the full test suite here. If the gate is still red on this push, its log should say which test failed, and I'll fix it from there.
What the change does:
sessionis noted before the access gate, so a 401 carries it too.origintimes thelistRemoteReposcall.rendertimes building the JSON.- The 404 and 502 bodies and the JSON response are unchanged, and there are no new dependencies.
To try it, send GET /api/v1/user/origins/<id>/repos?refresh=1 with an account credential and read the server-timing header.
The full gate on land was red and named no test; the pull is open again.
The last check failed because the npm registry refused the package downloads with 429, not because of this change. The log shows the check's egress was already at its cap of 8 connections to registry.npmjs.org, and bun install failed on @types/node. The change is still only src/http/api.ts and test/api-origin-repos-timing.test.ts. It touches no package.json or lockfile, so I had no code or dependency fix to make.
I pushed an empty commit so the check runs again. Locally, tsc is clean and test/api-origin-repos-timing.test.ts and test/origins.test.ts pass (33 tests). I did not run the full suite here. If the registry throttles the next install too, the cause is the egress cap on the check's install step, and that needs a person.
What the pull changes: GET /api/v1/user/origins/:id/repos now names its Server-Timing phases session, origin and render. session is noted before the access gate, so a 401 carries it too. The 404 and 502 bodies and the JSON response are unchanged, and there are no new dependencies.
To try it, request /api/v1/user/origins/<id>/repos?refresh=1 with an account credential and read the server-timing header.
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
Clean. The handler copies the timing pattern already used at api.ts:2313 and :3712: session is noted before requireAccount, the listRemoteRepos call runs inside origin, and building the JSON runs inside render. Access, status codes and bodies are unchanged. There is no new input, no write and no dependency, so there is nothing new to gate in the terminal. The phase names reveal nothing beyond what the 401/404/502 statuses already say.
Tests: the new suite checks the success, unknown-origin 404 and unreachable 502 paths against a fake origin, and all three would fail without the change.
Preview: GET /api/v1/user/origins/x/repos answers 401 with server-timing: app;dur=3.7, session;dur=0.6. Production answers app only. The earlier red gates were registry 429s in the check environment, not this diff.
GET /api/v1/user/origins/:id/reposnow names its Server-Timing phases:session, thenorigin, thenrender. A successful read shows all three, andbun x tsc --noEmitis clean. I ran only the new test andtest/origins.test.ts, which pass. I did not run the fullbun test; the forge's check runs that on the push.src/http/api.ts, this handler only, no new import): it notessessionfirst, before the access check, so refusals name it too.listRemoteReposruns insidetimePhaseAsync(..., "origin", ...), and the JSON answer is built insidetimePhase(..., "render", ...).origin_unreachableanswers keep their status and body. The 502 and the 404 for an unknown origin namesessionandorigin. A refusal at the access check names onlysession. I did not add a test for the 422 or 429 refusals.test/api-origin-repos-timing.test.ts, new): it connects a fake Forgejo origin and asserts the phase names for the success, 404 and 502 cases. It reaches no real host. All three assertions fail without the handler change.listRemoteReposserves cached rows when the origin fails, so the 502 test clears the cache table (origin_repo) first.To try it, request
/api/v1/user/origins/<id>/repos?refresh=1with an account credential and read theServer-Timingheader.🤖 Generated with Claude Code
Closes #3923