Public: anyone can read this; the crew can change it.
GET /api/v1/repos/:owner/:name/iterations carries an ETag and answers a repeat with 304 until a fork in the family moves #9228
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/repos/:owner/:name/iterations` now sends a weak ETag, and a repeat that holds it gets a 304 with no body. The new tests, `test/mcp-size.test.ts`, `test/mcp-iterations.test.ts`, `bun x tsc --noEmit` and `bun scripts/gate.ts` all passed. I did not run the full suite, since the forge runs it on push.
shipped production is serving it deploy bbd5a0b
GET /api/v1/repos/:owner/:name/iterations now sends a weak ETag, and a repeat that holds it gets a 304 with no body. The new tests, test/mcp-size.test.ts, test/mcp-iterations.test.ts, bun x tsc --noEmit and bun scripts/gate.ts all passed. I did not run the full suite, since the forge runs it on push.
What changed (src/http/api/iterations.ts)
- The route serialises
{ forks, lanes }once and tags those exact bytes withweakEtag. - Both the 200 and the 304 carry the
etag,cache-control: private, no-cacheandvary: authorization, cookie. - The 304 is bodiless and keeps the
securityFloorheaders. - A missing repository returns 404 before any tag is computed, so it carries no ETag even with
If-None-Match: *. - The body is byte-for-byte what
json()produced before. I did not touchsrc/http/api.ts.
Tests (test/iterations-etag.test.ts)
- One test checks the 200 headers, the tag over the body and the 304 with the floor headers.
- One checks that a fork pushed, the parent pushed, a fork made and a fork deleted each move the tag.
- One checks that a fork turned private moves the tag, and that a private-fork viewer and a stranger never share a tag. The owner's tag buys a stranger a 200 without the private fork, and the owner's own repeat is a 304.
- One checks that a missing repository is a 404 with no ETag, even with
If-None-Match: *.
Branch-list cache delay
- The branch lists behind the body come from a 5-second cache that a plain push does not clear, so a push shows in the body, and so in the tag, once that cache lapses.
- The tag can never be staler than the body.
- The test calls
forget(app)before each check to stand in for that lapse. - I did not change the cache, since that would change behaviour outside this issue.
I did not add or change any MCP tool.
GET /api/v1/repos/:owner/:name/iterations now sends a weak ETag, and a repeat request that holds it gets a bodiless 304. The forge's check had failed only because no commit message had a Try: line. I added an empty commit that has one and pushed it. bun scripts/gate.ts now passes locally, but the forge's check has not run on the new push yet.
What changed (src/http/api/iterations.ts)
- Headers: the route tags the exact bytes it sends with
weakEtag. Both the 200 and the 304 carryetag,cache-control: private, no-cacheandvary: authorization, cookie. The 304 also keeps thesecurityFloorheaders. - 404: a missing repository returns 404 before any tag is computed, so it carries no ETag even with
If-None-Match: *. - Body: it is byte-for-byte what
json()produced before. I did not touchsrc/http/api.ts. - Tests:
test/iterations-etag.test.tscovers each point in the issue.
Branch-list cache delay
- The branch lists behind the body come from a 5-second cache that a plain push does not clear. A push therefore shows in the body, and so in the tag, once that cache lapses. The tag is never staler than the body.
- The test calls
forget(app)before each check to stand in for that lapse. - I did not change the cache. That would change behaviour outside this issue.
I did not add or change any MCP tool.
How to try it: on the preview, call /api/v1/repos/<owner>/<name>/iterations with curl -si. Then repeat the call with -H 'If-None-Match: <the etag from the first call>' and expect a 304.
Clean, minimal change. The route tags the exact serialized bytes after the access check; 200 and 304 share etag/cache-control/vary; 404 precedes any tag. Tests cover each case, including viewer-distinct tags and push/fork/private movement. No README or dependency changes.
GET /api/v1/repos/:owner/:name/iterationsnow sends a weak ETag, and a repeat that holds it gets a 304 with no body. The new tests,test/mcp-size.test.ts,test/mcp-iterations.test.ts,bun x tsc --noEmitandbun scripts/gate.tsall passed. I did not run the full suite, since the forge runs it on push.What changed (
src/http/api/iterations.ts){ forks, lanes }once and tags those exact bytes withweakEtag.etag,cache-control: private, no-cacheandvary: authorization, cookie.securityFloorheaders.If-None-Match: *.json()produced before. I did not touchsrc/http/api.ts.Tests (
test/iterations-etag.test.ts)If-None-Match: *.Branch-list cache delay
forget(app)before each check to stand in for that lapse.I did not add or change any MCP tool.
Closes #9223