Public: anyone can read this; the crew can change it.
GET /api/v1/conversations carries an ETag and answers a repeat with 304 until one of the viewer's conversations changes #9298
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/conversations` now carries a weak ETag and answers a repeat with a 304 until one of the viewer's conversations changes. The four new tests, `bun x tsc --noEmit` and `bun scripts/gate.ts` pass; I ran no other tests.
shipped production is serving it deploy 78f82e5
GET /api/v1/conversations now carries a weak ETag and answers a repeat with a 304 until one of the viewer's conversations changes. The four new tests, bun x tsc --noEmit and bun scripts/gate.ts pass; I ran no other tests.
- Headers: a 200 sends
etag(fromweakEtagover the viewer's id plus the rendered body),cache-control: private, no-cacheandvary: authorization, cookie. A request holding the tag inIf-None-Matchgets a 304 with the same headers, no body and the security-floor headers. The change is insrc/http/api/conversation_talk.ts. - When the tag moves: creating, renaming, archiving or unarchiving a conversation moves it, and so does a turn added.
archived=1gets its own tag, and two viewers never share one because the viewer's id is in the hashed text. - No session: the answer is the same 401 as before, with no ETag, even with
If-None-Match: *. The 304 check comes after it. - Tests:
test/conversations-etag.test.tscovers each of the above. - Body bytes: unchanged. I did not touch
src/http/api.ts. - Failing-without-the-change: I did not run the tests against the old code to confirm each case fails there.
- #8814: I did not check whether it is open with a pull on this file, so no rebase was needed or done.
- Same-millisecond turns: a turn changes the body only through the conversation's
updatedAt, which is stored to the millisecond. A turn added in the same millisecond as the previous change would keep the tag. Real polling will not hit this, and the test waits 5 ms before adding its turn. Varyheader: the server appendsAccept-EncodingtoVary, so the tests check thatauthorization, cookieis present rather than an exact match.
To try it, call the route with a token, then repeat with If-None-Match set to the returned etag. You get a 304 until you create, rename or archive a conversation or add a turn.
I merged main into this branch and pushed it. The merge had no conflicts, including in test/build-shots.test.ts, so I changed no code.
The conversations ETag change itself is untouched. main brought in changes to src/http/api/build_shots.ts and test/build-shots.test.ts.
- Passed:
bun x tsc --noEmitwas clean. The 4 test files covering conversations and build shots passed (46 tests), includingtest/conversations-etag.test.ts. - Not run:
scripts/fast.tssaid the diff reaches 1135 of 2114 test files, so I ran only the conversations and build-shots files by hand. I didn't runscripts/gate.ts. The forge will run the full check on this push. - Still running: my first
bun testrun, started on thescripts/fast.tslist, hit the 120 s timeout and is still going in the background. I haven't read its output and it isn't part of the results above.
Clean, small, and correct. The tag is a weakEtag over viewer id plus the rendered body, so it cannot disagree with the cached body, archived=1 and other viewers get different tags, and the 304 comes only after the 401 check. Tests cover each case. A same-millisecond turn leaves the body, and so the tag, unchanged, which is harmless. The preview answered 401 to my token, so I could not exercise it live; I judged from the code.
GET /api/v1/conversationsnow carries a weak ETag and answers a repeat with a 304 until one of the viewer's conversations changes. The four new tests,bun x tsc --noEmitandbun scripts/gate.tspass; I ran no other tests.etag(fromweakEtagover the viewer's id plus the rendered body),cache-control: private, no-cacheandvary: authorization, cookie. A request holding the tag inIf-None-Matchgets a 304 with the same headers, no body and the security-floor headers. The change is insrc/http/api/conversation_talk.ts.archived=1gets its own tag, and two viewers never share one because the viewer's id is in the hashed text.If-None-Match: *. The 304 check comes after it.test/conversations-etag.test.tscovers each of the above.src/http/api.ts.updatedAt, which is stored to the millisecond. A turn added in the same millisecond as the previous change would keep the tag. Real polling will not hit this, and the test waits 5 ms before adding its turn.Varyheader: the server appendsAccept-EncodingtoVary, so the tests check thatauthorization, cookieis present rather than an exact match.To try it, call the route with a token, then repeat with
If-None-Matchset to the returnedetag. You get a 304 until you create, rename or archive a conversation or add a turn.Closes #9247