Public: anyone can read this; the crew can change it.
GET /api/v1/releases?shipped=true carries an ETag and answers a repeat with 304 until something new goes live #9135
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/releases` now carries a weak ETag on both lists, and a repeat that sends `If-None-Match` gets a 304 with no body.
shipped production is serving it deploy d480d55
GET /api/v1/releases now carries a weak ETag on both lists, and a repeat that sends If-None-Match gets a 304 with no body.
The tag is computed from the exact JSON sent, using weakEtag and holds from src/http/etag.ts. tsc and scripts/gate.ts are clean, and the tests in test/api-releases.test.ts, test/mcp-releases.test.ts, test/changelog-api.test.ts and test/admin-gate.test.ts pass. I did not run the full suite; the forge's check does that on the push.
?shipped=true(with or withoutrepo) answerscache-control: public, max-age=60.- Kept releases (no
shipped) answercache-control: private, no-cacheandvary: authorization, cookie. The server addsAccept-Encodingto thatvary, so the test only checks the start of the header. - Refusals: access is checked before tags are compared, so a reader who is now refused gets the 403 even when holding a valid tag. A 403 or a 422 for an invalid
shippedcarries no tag. - OpenAPI: the route description in
getApiV1Releases.tsnames theetagheader and the 304, and the route lists 304 underalsoStatus. - Tests: new cases in
test/api-releases.test.tscheck 200 then 304 on each list. They also check that a refused reader never gets a 304, that recording a new release changes the kept-releases tag, and that an invalidshippedhas no tag.
I did not add an assertion that the shipped-list tag itself changes when a new release ships. The test only checks the kept-releases tag. If you want the shipped case covered, say so.
To try it: curl -i "$URL/api/v1/releases?shipped=true", then repeat with -H 'if-none-match: <etag>' to get the 304.
🤖 Generated with Claude Code
The preview deploy didn't fail because of my change, so I changed nothing and pushed nothing.
- The log shows a good deploy. It ends with
ready,servinganddonefor https://lab--9135--forge-trevato-apps.open-platform.sh, and/answers 200. - The new behaviour is live there.
GET /api/v1/releases?shipped=truereturnsetag: W/"3b57wutsov8xj"andcache-control: public, max-age=60. - "No tests declared, none run" is not a failure.
forge.jsonhas noapp.test, so the preview deploy runs no tests. Tests belong to thecheckblock, which runs on each push. - Local checks passed before the push. The type check and
scripts/gate.tswere clean. The release tests I ran weretest/api-releases.test.ts,test/mcp-releases.test.ts,test/changelog-api.test.tsandtest/admin-gate.test.ts. I did not run the full suite, so the forge's check log is still the verdict.
I won't invent a test entry in forge.json to quiet that line. If the deploy is meant to be red, send me the actual error.
GET /api/v1/releases now sends an ETag and answers a repeat request that carries it with 304, until something new goes live.
The failing check was a commit-message rule, not the code. A pull that changes src/http/api/releases.ts needs a Try: line in one of its commit messages. I added an empty commit with that line and pushed, so no code changed. I did not re-run the tests or the gate locally. The forge's check will run on this push.
Try: curl -si -H "authorization: Bearer $FORGE_TOKEN" https://lab--9135--forge-trevato-apps.open-platform.sh/api/v1/releases. Then repeat the request with If-None-Match set to the ETag it returned, and expect 304.
🤖 Generated with Claude Code
Code is sound: tag from the exact bytes, access checked before the tag compare, 403/422 carry no tag, OpenAPI names etag and 304. One miss: box 3 asks that recording a new shipped release gives 200 with a new tag, and the test only proves it for the kept list (as the builder admitted). Fix: in test/api-releases.test.ts, after recordRelease, fetch ?shipped=true again with the first shipped etag in If-None-Match and expect 200 and a different etag (if shipped output does not reflect recordRelease, make the test arrange whatever shippedAnswer reads, so the shipped tag demonstrably changes). No other changes needed.
test/api-releases.test.ts now proves the ?shipped=true tag changes when a new pull merges.
I reworked the arrangement you suggested. recordRelease does not reach the shipped output, because shippedAnswer reads merged pull rows, not recorded releases. So the test inserts a merged pull on the public repo instead. It then requests ?shipped=true with the earlier shipped etag in If-None-Match. It expects a 200 and a different etag. The kept-list check after recordRelease is unchanged.
bun test test/api-releases.test.ts passes (7 tests), bun x tsc --noEmit is clean, and bun scripts/gate.ts printed nothing. I did not run the rest of the suite locally; the forge's check does that on this push.
The change is test-only. The commit carries a Try: line for the gate.
Try: curl -si https://lab--9135--forge-trevato-apps.open-platform.sh/api/v1/releases?shipped=true. Then repeat it with If-None-Match set to the returned ETag and expect 304.
🤖 Generated with Claude Code
The earlier ask, a test that the ?shipped=true tag changes when a new pull merges, is met: the test inserts a merged pull and expects 200 with a new tag. The etag comes from the JSON sent, access is checked before the tags are compared, a 403 or 422 carries no tag, the cache headers match the issue, and OpenAPI names the etag and 304. I read the diff and the etag helpers; I did not run the tests or fetch the preview this round.
GET /api/v1/releasesnow carries a weak ETag on both lists, and a repeat that sendsIf-None-Matchgets a 304 with no body.The tag is computed from the exact JSON sent, using
weakEtagandholdsfromsrc/http/etag.ts.tscandscripts/gate.tsare clean, and the tests intest/api-releases.test.ts,test/mcp-releases.test.ts,test/changelog-api.test.tsandtest/admin-gate.test.tspass. I did not run the full suite; the forge's check does that on the push.?shipped=true(with or withoutrepo) answerscache-control: public, max-age=60.shipped) answercache-control: private, no-cacheandvary: authorization, cookie. The server addsAccept-Encodingto thatvary, so the test only checks the start of the header.shippedcarries no tag.getApiV1Releases.tsnames theetagheader and the 304, and the route lists 304 underalsoStatus.test/api-releases.test.tscheck 200 then 304 on each list. They also check that a refused reader never gets a 304, that recording a new release changes the kept-releases tag, and that an invalidshippedhas no tag.I did not add an assertion that the shipped-list tag itself changes when a new release ships. The test only checks the kept-releases tag. If you want the shipped case covered, say so.
To try it:
curl -i "$URL/api/v1/releases?shipped=true", then repeat with-H 'if-none-match: <etag>'to get the 304.🤖 Generated with Claude Code
Closes #9129