Skip to content

[finding] The MOUNT half of every RestServerConfig switch is unpinned — the tests assert what a switch normalizes to, never that the route leaves the table #15544

Description

@claude

Filed unassigned and unlabelled for triage, from the capability-coverage work on #14961 (PR #15541). Not that PR's change and not addressed there.

Measured on origin/main 6f944589

Nine live RestServerConfig switches gate route mounts:

  • crud.operations.{create,read,update,delete,list} in packages/rest/src/rest-server.ts#registerCrudEndpoints
  • batch.enableBatchEndpoint and batch.operations.{createMany,updateMany,deleteMany} in #registerBatchEndpoints
  • metadata.endpoints.{types,items,item} in #registerMetadataEndpointsInner

What the test suite pins:

  • packages/rest/src/rest-sub-config-parse-not-cast.test.ts §D asserts what each switch normalizes tonormalized({ crud: { operations: { list: false } } }).crud.operations equals the expected object, and so on. §E asserts the retired keys refuse.
  • packages/rest/src/rest-batch-size-cap.test.ts asserts the effect of batch.maxBatchSize on all five bulk doors.

What nothing pins: that a false switch removes its route from the mounted table. No test constructs a server with a switch off and asserts the absence in RestServer#getRoutes() — even though that method is public, is already used by rest-batch-size-cap.test.ts to find routes, and makes the assertion a two-line one.

Why it is worth a decision

The unpinned direction is the dangerous one. A refactor that stops reading a switch at the registrar — or reads the wrong one — leaves every existing test green: the normalized config is still correct, the cap still works, and the route is simply still mounted. That is precisely the declared-not-enforced state ADR-0049 exists to catch, and here the switch would be config an operator sets while nothing honours it.

Two shapes worth pinning in the same test, both read out of the registrars and both easy to break silently:

  • crud.operations.list gates two mounts (GET {dataPrefix}/:object and POST {dataPrefix}/:object/query); the query door has no switch of its own.
  • The four batch gates are each switch AND protocol member (operations.createMany && this.protocol.createManyData), so a mount can be absent for two different reasons.

Options (not decided here)

  1. Add the mount-table pins to rest-sub-config-parse-not-cast.test.ts (or a sibling): per switch, construct, registerRoutes(), diff getRoutes() against the all-true baseline, assert the difference is exactly the expected paths.
  2. Leave it to the QA ledger — the new api-backend.rest-*-config-contract items each carry a mount clause, so a checklist run observes it. ⚠️ That ledger is manual and not CI-wired ([Decision] check:platform-checklist has no reporting channel — it is deliberately not CI-wired, so its red is visible to nobody until a runner reads it #11730), so this route means a break is caught at the next run, not on the PR that causes it.

Where it is already captured

docs/qa/platform-checklist/FOLLOW-UPS.md §10b E3, and in the knownGaps of the three new config items, which say the harness is the only observation until such a pin exists.


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions