api-specs: the vendored specs are hand-refreshed and Gitea's is a minor version stale #538
Labels
No labels
breaking
bug
documentation
enhancement
epic
good first issue
help wanted
refactoring
resolution/duplicate
resolution/invalid
resolution/wontfix
security
severity/critical
severity/high
severity/low
severity/medium
status/abandoned
status/blocked
status/needs-decision
status/needs-info
status/needs-verification
testing
upstream
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
#544 Epic: madtea's external contracts are asserted, not verified
sixfold-space/madtea
Reference
sixfold-space/madtea#538
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
surface-diffchecks madtea's endpoint surface againstdocs/api-specs/gitea-1.26.1.jsonanddocs/api-specs/forgejo-15.0.json. Those files are hand-vendored snapshots. Nothing verifies they match a live install, so the check is only as good as the last manual refresh.Measured on 2026-09-17 against live instances:
Missing from the vendored Gitea spec:
Why it matters
TestAPISurfaceMatchesSpecclassifies an endpoint missing from every spec asorphaned, which it treats as a bug. Against a stale spec that verdict is wrong: an endpoint that exists in Gitea 1.27 reads as nonexistent, and the obvious response to a failing test is to delete the calling code. A check that produces confident false verdicts is worse than no check.The Forgejo copy happens to be accurate because patch releases do not move the API surface. That is recency, not a guarantee. The exposure is a minor or major bump, which is exactly what happened on the Gitea side.
Why it went unnoticed
docs/api-specs/when a new stable lands." Manual steps rot.forgejo-smokedoes exercise a live Forgejo, but it is gated onvars.RUN_FORGEJO_SMOKE == 'true'and this repository defines no Actions variables at all, so it has never run.Ask
Both platforms serve their own spec at
/swagger.v1.json, so this is mechanical rather than aspirational. Fetching it from a live instance of each platform and comparing against the vendored copy takes one request per platform, and it is how the vendored files should have been validated all along.Check each platform independently. They diverge, that divergence is the entire reason two specs are vendored, and a single combined check would hide exactly the case that just occurred.
Acceptance
Related: #537 covers the same class of gap for the git CLI surface, which has no generated guarantee at all.
A second consequence of the vendored specs, which strengthens the case for fetching them live:
surface-diffon the daily cron is deterministic and cannot discover anything.It compares the endpoint surface extracted from the code against spec files in
docs/api-specs/. Both inputs live in the commit. Neither can change between cron runs, so a scheduled run computes an identical result every day. If something did change, that change arrived in a commit, and thepushtrigger already ran the check.So the daily run currently does this:
govulnchecksurface-diffforgejo-smokeRUN_FORGEJO_SMOKE, and this repository defines no Actions variables.drift.yml's stated purpose is "catch regressions that appear WITHOUT the code changing".surface-diffcannot serve that purpose while its contract is a file in the same commit as the code. A drift check whose reference is frozen in the repository is not a drift check.Fetching each platform's spec from a live instance fixes both symptoms at once. The stale Gitea copy stops being possible, and the cron run starts answering a question it currently only appears to answer: did Gitea or Forgejo change under us since yesterday.
Worth deciding alongside the fix: whether
surface-diffshould run on the cron at all until its reference is live. Today it consumes a slot on the single runner to recompute a known answer.Cross-link: #504 already recorded this on 2026-09-15, two days before I measured it here. Its closing note reads "The vendored specs are older than the live servers (
docs/api-specs/gitea-1.26.1.jsonagainst a live 1.27.3)", and its own ask ends with "Check both live forges before merge, not only the vendored specs."So the staleness was observed, written down as an aside inside a different issue, and then found again independently. That is worth noting on its own: the finding existed and still did not get acted on, because it was a supporting detail in an issue about something else rather than a tracked item of its own.
#543 is the third instance of the same root cause: hand-maintained claims about platform behaviour with nothing verifying them.