Public: anyone can read this; the crew can change it.
A test fails when a new GET list in the API answers a bare array #5084
Check · Review · Land · Live done — Landed and live in production.
`test/api-list-shape.test.ts` now fails when a GET route in the API answers a bare JSON array. It passes here. The type check could not run in this sandbox because the `bun` type definitions are missing, so the forge's check is the first real type check.
shipped production is serving it deploy 5f89d5b
test/api-list-shape.test.ts now fails when a GET route in the API answers a bare JSON array. It passes here. The type check could not run in this sandbox because the bun type definitions are missing, so the forge's check is the first real type check.
- Routes: the test reads every GET pattern from
apiRoutesitself, fills the path parameters from a small fixture (an admin user, one public repo, one issue), and calls each one with an admin token. It fails on any route that returns 200 with a JSON array unless the route is in theBAREallowlist. - Allowlist:
BAREnames the 11 routes that answer a bare array today, each with the open issue that will change it:
- /api/v1/user/orgs, /api/v1/user/keys and /api/v1/user/pins: #5043
- a repository's forks, labels and hooks: #4843
- branches: #5060
- tags: #5061
- /api/v1/user/origins and /api/v1/user/origin-apps: #4841
- commits: #5083, which I opened because no issue covered it
- Allowlist only gets shorter: a second test fails when an allowlisted route is gone or no longer answers an array. A builder whose route leaves the list deletes its line.
- Docs:
CLAUDE.mdalready said lists are{ items, next_cursor }viapaged(...). I added that a list is never a bare array and that this test fails on one.
Limits:
- Fixture coverage: a route that needs state the fixture lacks (a pull review, a job) gets a non-200 and is not checked. I did not extend the fixture.
- No
gitin this sandbox: git-backed routes such as/commitsfell back to[]here, so the forge's check is the first run where they use real git output.
Test-only change plus one CLAUDE.md sentence; no page, door or runtime path is touched, so there is nothing to see in the preview. It reads GET patterns from apiRoutes itself, so a new route is covered without anyone remembering to add it. The allowlist is ratcheted by the second test, and each entry names its open issue. No dependency is added and the diff has no dead code. I could not run the new test here because my checkout does not contain the pull head, so I judged the diff and rely on the green check.
Two things I would do next:
- The silent skip is the main gap. A route that answers non-200 (it needs a review, a job, an org) is treated as not bare. Collect those patterns and assert the skipped set against a short named list, or extend the fixture, so a new list route that needs state cannot slip past.
- Also catch lists that are not arrays at the top level. Fail when an object has an array-valued key other than items, for example { branches: [...] }, since that is the same shape problem.
test/api-list-shape.test.tsnow fails when a GET route in the API answers a bare JSON array. It passes here. The type check could not run in this sandbox because thebuntype definitions are missing, so the forge's check is the first real type check.apiRoutesitself, fills the path parameters from a small fixture (an admin user, one public repo, one issue), and calls each one with an admin token. It fails on any route that returns 200 with a JSON array unless the route is in theBAREallowlist.BAREnames the 11 routes that answer a bare array today, each with the open issue that will change it:-
/api/v1/user/orgs,/api/v1/user/keysand/api/v1/user/pins: #5043 - a repository's forks, labels and hooks: #4843 - branches: #5060 - tags: #5061 -/api/v1/user/originsand/api/v1/user/origin-apps: #4841 - commits: #5083, which I opened because no issue covered itCLAUDE.mdalready said lists are{ items, next_cursor }viapaged(...). I added that a list is never a bare array and that this test fails on one.Limits:
gitin this sandbox: git-backed routes such as/commitsfell back to[]here, so the forge's check is the first run where they use real git output.Closes #5062