Skip to content

Route audit tranche 2: the @objectstack/rest surface (search / forms / reports / sharing / …) — #3563 follow-up #3587

Description

@os-zhuang

Split from #3563. The dispatcher tranche is complete (gap ratchet 27→0, #3569 #3571 #3575 #3577 #3579), but the dispatcher is not the only server surface: @objectstack/rest mounts a second, larger route set the client also reaches — and it has never been audited with the same rigor.

Known zero-expression routes (from the PR-1 sweep, rest-server.ts line refs in the audit doc)

Route family Routes
GET /search?q&objects&limit&perObject 1
POST /email/send 1
forms/:slug (get / submit / lookup) 3
Record shares GET/POST/DELETE /data/:object/:id/shares[/:shareId] 3
POST /data/:object/:id/clone 1
POST /analytics/dataset/query 1
sharing/rules (incl. /evaluate) 5
reports (incl. /run, /schedule, /schedules) 8
external-datasource/* (external-datasource-routes.ts) family

Plus the reverse direction that tranche 1 deliberately left out of scope: client surfaces that target REST-only routes (views CRUD, permissions, workflow, approvals, realtime, notification devices/preferences, presigned storage, import jobs) — currently expressed but with no ledger row anywhere asserting their server side exists.

Proposed method — same as tranche 1, one new problem to solve

  1. Enumerate the REST mounts (rest-server.ts is large; the mounts are server.<verb>() calls — scriptable).
  2. Extend the ledger (or add a sibling rest-route-ledger.ts in packages/rest) with per-route dispositions.
  3. Guard on both sides, same split as feat(runtime): route ledger + conformance guard for the dispatcher↔client surface (#3563) #3569: enumeration-side conformance next to where routes are declared; client-method existence next to the SDK. The tranche-1 lesson applies verbatim: no runtime→client package edge (build cycle) — each package verifies its own half.
  4. New problem tranche 1 didn't have: REST route registration is method calls, not a registry with .list(). Either add an introspection seam to the REST server (preferred — mirrors DomainHandlerRegistry.list()), or pin the mount list the way LEGACY_CHAIN_PREFIXES pins the if-chain.

Acceptance

  • Every REST-mounted route has a reviewed disposition.
  • A ratchet equivalent to tranche 1's, wired into CI.
  • The reverse direction closed: every client method's target route provably exists on some server surface.

Refs: audit doc docs/audits/2026-07-dispatcher-client-route-coverage.md §8; ledger scope note at packages/runtime/src/route-ledger.ts header.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions