Symptom
https://ekgf.org/dprod/spec-versions/ advertises 22 branches while only 6 pull requests are open. Eleven of the listed branches no longer exist in the repository at all:
add-dprod-contracts, agent/github-issues-source-of-truth, complete-dprod-migration, contracts/lifecycle-abstract-and-reject-5.8-5.9, dependabot/…/deploy-pages-5, dependabot/…/upload-pages-artifact-5, DPROD-64, issue/214-lifecycle-status-model, issue/215-reject-odrl-request, ns-dprod-segment, spec-latestversion-iri
Not a staleness problem
The page updates correctly: a branch pushed minutes ago appears while develop has not been redeployed, so ISR and the Vercel deployments API both work. ekgf-website is also not involved — its src/middleware.ts rewrites /dprod/* verbatim to the dprod zone and holds no version logic.
Cause
fetchActiveBranches() in site/src/lib/spec-versions.ts is failing, and the caller is written to fail open:
// Fail open: when the GitHub lookup failed, keep every branch.
if (activeBranches && !activeBranches.has(branch)) continue;
GITHUB_TOKEN appears in exactly one line of the repository (spec-versions.ts:121) with no .env.example, no documentation and no workflow that sets it, while the VERCEL_* variables are clearly provisioned since deployment discovery works. Authentication is optional in the code:
if (token) headers.Authorization = `Bearer ${token}`;
so without the token the request runs unauthenticated, where GitHub permits 60 requests/hour per IP shared across Vercel's serverless egress. That is exhausted continuously → non-2xx → null → every branch is kept.
(The Vercel project's environment variables could not be read directly to confirm the token is absent, so this last step is inferred. Every observable is consistent with it, and nothing else explains a persistent filter failure alongside working Vercel API calls.)
Three defects, not one
- The token is treated as optional when the unauthenticated path cannot work in production.
- The failure is silent — no log, no signal, no degraded-state indicator. It has been broken indefinitely with nothing to notice.
- The predicate is wrong. Open-PR status is not the question.
issue/241-doc-metadata-drift exists upstream but has no open PR, so even a working token would hide it; conversely the eleven branches above are gone from the repository entirely. Branch existence is what the picker actually means.
Fix
- Replace the open-PR lookup with
GET /repos/EKGF/dprod/branches, paginated.
- Require
GITHUB_TOKEN and console.warn when it is missing or the lookup fails.
- Split the two consumers, which need opposite failure behaviour:
getSpecVersions() stays permissive for middleware routing — a preview URL already handed out must keep resolving during a GitHub outage;
getListedSpecVersions() fails closed for the picker, showing only the archive and develop when existence cannot be determined.
- Exclude
dependabot/*: a dependency bump produces a valid preview deployment but is not a version of the specification.
- Document the variables in
site/.env.example and site/README.md.
Simulated against live data, the listing goes from 23 entries to 8 plus the archive, with every drop attributable to a deleted branch or a dependency bump.
GITHUB_TOKEN still has to be set on the Vercel project — a fine-grained token needs only Metadata: Read-only on EKGF/dprod. Until it is, the picker will correctly show just the archive and develop, and log why.
Symptom
https://ekgf.org/dprod/spec-versions/ advertises 22 branches while only 6 pull requests are open. Eleven of the listed branches no longer exist in the repository at all:
add-dprod-contracts,agent/github-issues-source-of-truth,complete-dprod-migration,contracts/lifecycle-abstract-and-reject-5.8-5.9,dependabot/…/deploy-pages-5,dependabot/…/upload-pages-artifact-5,DPROD-64,issue/214-lifecycle-status-model,issue/215-reject-odrl-request,ns-dprod-segment,spec-latestversion-iriNot a staleness problem
The page updates correctly: a branch pushed minutes ago appears while
develophas not been redeployed, so ISR and the Vercel deployments API both work.ekgf-websiteis also not involved — itssrc/middleware.tsrewrites/dprod/*verbatim to the dprod zone and holds no version logic.Cause
fetchActiveBranches()insite/src/lib/spec-versions.tsis failing, and the caller is written to fail open:GITHUB_TOKENappears in exactly one line of the repository (spec-versions.ts:121) with no.env.example, no documentation and no workflow that sets it, while theVERCEL_*variables are clearly provisioned since deployment discovery works. Authentication is optional in the code:so without the token the request runs unauthenticated, where GitHub permits 60 requests/hour per IP shared across Vercel's serverless egress. That is exhausted continuously → non-2xx →
null→ every branch is kept.(The Vercel project's environment variables could not be read directly to confirm the token is absent, so this last step is inferred. Every observable is consistent with it, and nothing else explains a persistent filter failure alongside working Vercel API calls.)
Three defects, not one
issue/241-doc-metadata-driftexists upstream but has no open PR, so even a working token would hide it; conversely the eleven branches above are gone from the repository entirely. Branch existence is what the picker actually means.Fix
GET /repos/EKGF/dprod/branches, paginated.GITHUB_TOKENandconsole.warnwhen it is missing or the lookup fails.getSpecVersions()stays permissive for middleware routing — a preview URL already handed out must keep resolving during a GitHub outage;getListedSpecVersions()fails closed for the picker, showing only the archive anddevelopwhen existence cannot be determined.dependabot/*: a dependency bump produces a valid preview deployment but is not a version of the specification.site/.env.exampleandsite/README.md.Simulated against live data, the listing goes from 23 entries to 8 plus the archive, with every drop attributable to a deleted branch or a dependency bump.
GITHUB_TOKENstill has to be set on the Vercel project — a fine-grained token needs onlyMetadata: Read-onlyonEKGF/dprod. Until it is, the picker will correctly show just the archive anddevelop, and log why.