renderImage in packages/web-og/src/index.tsx picks its cache lifetime purely
on whether it got a result at all:
const longCache = `public, max-age=${24 * 60 * 60}, s-maxage=${24 * 60 * 60}`;
const shortCache = 'public, max-age=60';
const cacheControl = result ? longCache : shortCache;
A LookupResult with firstRelease: null is a real result, so it takes
longCache — and ResultCard renders it as the string "not yet released"
(const tag = r.firstRelease?.tag ?? 'not yet released').
So the one card whose whole job is to flip is pinned for 24h. A commit shared
before its release lands unfurls as "not yet released", and Slack/X keep that
PNG for a day after the release ships. This is the OG analogue of the badge
invariant the project already states in CLAUDE.md and priorities.md:
Badge cache semantics. Released → long cache; not-yet/checking → short
cache. Get this wrong and badges stop flipping (or flip too slowly).
badge.ts gets this right (the liveness probe asserts both arms: released →
max-age=86400, not-yet → max-age=300). web-og does not.
Not from #144
This is on main today and is not caused by #144 — that PR only changed which
cache slot /internal reads. It surfaced in #144's round-4 review as the
amplifier behind a stale-answer finding: #144 bounded how stale an answer it
will hand back (SWR_MAX_STALE, 30 min) precisely because web-og pins whatever
it gets for 24h. Bounding staleness limits the damage; it doesn't fix this.
Suggested fix
Key the lifetime on the answer's terminality, not its presence — the same test
resolve.ts already uses for the cache TTL (hardTtlFor):
const cacheControl = result?.firstRelease ? longCache : shortCache;
A partial result should be short-cached too, for the same reason.
Note on overlap
#141 is open against this same function (renderImage's cache-control being
overridden by workers-og's 1-year immutable default). Whoever lands second
should rebase rather than both editing these lines. Fixing it inside #141 is
reasonable if that PR is still in flight — it is the same header, one decision.
Guard
Whatever lands, it needs a test that goes red on the current code: render a
result with firstRelease: null and assert the response's cache-control is
the short one. packages/web-og/test currently asserts the placeholder arm only.
renderImageinpackages/web-og/src/index.tsxpicks its cache lifetime purelyon whether it got a result at all:
A
LookupResultwithfirstRelease: nullis a real result, so it takeslongCache— andResultCardrenders it as the string "not yet released"(
const tag = r.firstRelease?.tag ?? 'not yet released').So the one card whose whole job is to flip is pinned for 24h. A commit shared
before its release lands unfurls as "not yet released", and Slack/X keep that
PNG for a day after the release ships. This is the OG analogue of the badge
invariant the project already states in CLAUDE.md and priorities.md:
badge.tsgets this right (the liveness probe asserts both arms: released →max-age=86400, not-yet →max-age=300).web-ogdoes not.Not from #144
This is on
maintoday and is not caused by #144 — that PR only changed whichcache slot
/internalreads. It surfaced in #144's round-4 review as theamplifier behind a stale-answer finding: #144 bounded how stale an answer it
will hand back (
SWR_MAX_STALE, 30 min) precisely because web-og pins whateverit gets for 24h. Bounding staleness limits the damage; it doesn't fix this.
Suggested fix
Key the lifetime on the answer's terminality, not its presence — the same test
resolve.tsalready uses for the cache TTL (hardTtlFor):A
partialresult should be short-cached too, for the same reason.Note on overlap
#141 is open against this same function (
renderImage's cache-control beingoverridden by workers-og's 1-year immutable default). Whoever lands second
should rebase rather than both editing these lines. Fixing it inside #141 is
reasonable if that PR is still in flight — it is the same header, one decision.
Guard
Whatever lands, it needs a test that goes red on the current code: render a
result with
firstRelease: nulland assert the response'scache-controlisthe short one.
packages/web-og/testcurrently asserts the placeholder arm only.