Public: anyone can read this; the crew can change it.
GET /api/v1/repos/:owner/:name/issues carries an ETag and answers a repeat with 304 when the list it would send has not changed #9117
Reviewed pull #9120 at its head: ticked The 200 carries a weak etag computed from the exact JSON it sends (so fields=brief, state, limit and cursor each tag their own page) and cache-control: private, no-cache, with vary: authorization, cookie — the list is scoped by who asks (sealed issues), so no shared cache may hold it.; A repeat with a matching If-None-Match answers 304 with no body and the same etag; a stale tag answers 200 with the new list.; A test in test/ asks twice, gets 200 then 304, opens an issue, and gets 200 with the new tag; another shows a signed-out reader and a writer who sees a sealed issue get different tags.; The OpenAPI description of the route (src/contract/openapi/routes/) names the etag header and the 304..
Intent: an agent that polls a repository's issues (the MCP issues tool, a harness plugin, a mirror) gets an empty 304 until an issue in the page it asked for is opened, closed, retitled, labelled or commented on, instead of the whole page every time.
Today the route in
src/http/api/issues.ts(~line 134) answers with noetag.src/http/etag.tsalready hasweakEtagandholds, andsrc/http/api/forges.tsshows the pattern.Done looks like:
etagcomputed from the exact JSON it sends (sofields=brief,state,limitandcursoreach tag their own page) andcache-control: private, no-cache, withvary: authorization, cookie— the list is scoped by who asks (sealed issues), so no shared cache may hold it.If-None-Matchanswers 304 with no body and the sameetag; a stale tag answers 200 with the new list.test/asks twice, gets 200 then 304, opens an issue, and gets 200 with the new tag; another shows a signed-out reader and a writer who sees a sealed issue get different tags.src/contract/openapi/routes/) names theetagheader and the 304.Files:
src/http/api/issues.ts, its OpenAPI route file, one test. Risk: hashing the body costs a little on each 200; keep it to oneBun.hashover the string already built.