Skip to content

OG unfurls of a not-yet-released commit serve the neutral placeholder permanently (/internal 503s on NotYetReleasedError) #150

Description

@lukaso-bot

/internal/result/* returns 503 for a commit that is not yet released, so web-og renders the neutral placeholder — and the crawler keeps it.

What happens

findRelease throws NotYetReleasedError for a commit that is merged but not in any release yet. resolveResult in packages/web/src/routes/internal.ts has no success path for that: resolveLookup returns {status:'not_yet'} and every non-ok status falls through to

return new Response(JSON.stringify({ error: failureMessage(resolved) }), { status: 503, ... });

web-og turns a 503 into PlaceholderCard at max-age=60. A crawler unfurls a link once and keeps what it got, so that placeholder is effectively permanent for that URL. Nothing is written to the cache either (the catch in resolve.ts writes no result and no :neg marker for NotYetReleasedError), so every later unfurl repeats the full uncached lookup against the 24s soft deadline and 503s again.

Why it matters

This is the highest-traffic shape of the share flow: a commit merged an hour ago is the single most likely thing to be freshly linked on Slack or X, and it is exactly the commit that is not yet in a release. The badge surface handles "not yet released" as a first-class state (CUJ #3); the OG card is the one surface that degrades to a neutral placeholder instead.

Why it is not part of #144

#144 aligns the /internal cache key and origin with the public routes. This behaviour is unchanged by that PR — main 503s on NotYetReleasedError too, via the old catch (err). It is a separate defect on a separate code path (the JSON contract between web and web-og), so it gets its own PR rather than growing #144's review surface. Raised as a review thread on #144 and split out.

Fix sketch

web-og's ResultCard already renders firstRelease: null as "not yet released", so the renderer needs no change — only /internal refuses to supply it. The work:

  1. In resolveResult, handle resolved.status === 'not_yet' by returning 200 with a not-yet-shaped LookupResult (firstRelease: null) rather than 503.
  2. Decide the cache story deliberately: "not yet released" is a pending state that must flip promptly once a release lands, so it wants the short freshness window the badge route already uses — not a terminal TTL. Writing it to the shared slot at pending TTL would also let the public routes benefit, but that changes what a public route reads back, so verify against result.tsx/badge.ts before doing it.
  3. Guard: a test that a not-yet commit renders the "not yet released" card and not the placeholder, mutation-proven red against today's 503.

Note the interaction with #147 (OG cards key on a 7-char SHA, permalinks on the full SHA): until that is fixed, the OG path and the public path are separate slots anyway.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions