Skip to content

fix(rest,runtime): mount six ledgered-but-dead routes and gate the class that hid them (#7526) - #7584

Merged
os-help merged 3 commits into
mainfrom
claude/issue-7526-route-ledger-live-mount-parity
Aug 11, 2026
Merged

fix(rest,runtime): mount six ledgered-but-dead routes and gate the class that hid them (#7526)#7584
os-help merged 3 commits into
mainfrom
claude/issue-7526-route-ledger-live-mount-parity

Conversation

@os-help

@os-help os-help commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7526

The deliverable is the gate

The three dead /meta routes are the symptom. The five route ledgers are a declaration, and every guard built on them (#3563 / #3587 / #3636 / #3642) reads their union as an observation of what is mounted — so GET /meta/objects/:name/state/:field counted as mounted because it was ledgered, the SDK URL guard passed the method calling it, and it 404'd at runtime. The audit chain was green on this class by construction.

This adds the missing observation.

The gate — packages/qa/dogfood/test/route-ledger-live-mount-parity.dogfood.test.ts

Boots a real showcase stack, reads the mount table off the running server, asserts both directions:

  1. every ledgered route is reachably mounted — and reachable, not merely present. Every row is probed: a concrete path is built from the pattern and the live Hono router is asked which registration would answer it. The row passes only when the router names the row's own pattern back. Presence in the table would have "passed" /meta/types, which is the whole point.
  2. every mounted route is ledgered.

The mounted side is never hand-maintained — a second hand-written list of "what we mount" would drift exactly the way the ledgers did. IHttpServer grows two optional, feature-detected members for it, implemented by the Hono adapter:

  • getMountedRoutes() — the live mount table, in registration order;
  • resolveMountedRoute(method, path) — which registration answers a concrete request, per the router itself.

The gate fails rather than skips when either is absent: a parity gate that quietly passes because it could not look is the failure it exists to end.

Registration order is measured, not assumed. plugin-hono-server/src/mounted-route-introspection.test.ts registers a literal and a :param sibling in both orders and pins that the later one never runs.

Proof the gate works — reverted, red; reapplied, green

With rest-server.ts and dispatcher-plugin.ts reverted to origin/main and rebuilt:

FAIL  route ledger ↔ live mount parity (#7526) > every ledgered route is reachably mounted

runtime/route-ledger.ts: GET /meta/types — LEDGERED BUT NOT MOUNTED, and DISGUISED.
  Nothing registers `/api/v1/meta/types`; GET /api/v1/meta/types is swallowed by
  `/api/v1/meta/:type`, so the caller gets that route's answer — a plausible response
  rather than a 404.
runtime/route-ledger.ts: GET /meta/:type/:name/published — LEDGERED BUT NOT MOUNTED, and DISGUISED.
  … swallowed by `/api/v1/meta/:type/:section/:name` …
runtime/route-ledger.ts: GET /meta/objects/:name/state/:field — LEDGERED BUT NOT MOUNTED.
  The live router answers nothing for GET /api/v1/meta/objects/__parity_probe_0/state/__parity_probe_1;
  this URL 404s at runtime while every ledger-reading guard passes it.
runtime/route-ledger.ts: GET /automation/actions — LEDGERED BUT NOT MOUNTED, and DISGUISED.
runtime/route-ledger.ts: GET /automation/connectors — LEDGERED BUT NOT MOUNTED, and DISGUISED.
runtime/route-ledger.ts: GET /automation/_status — LEDGERED BUT NOT MOUNTED, and DISGUISED.
  (+ the 5 mirror rows in rest/rest-route-ledger.ts)

Test Files  3 failed (3)
Tests  9 failed | 5 passed | 6 skipped (20)

Reapplied and rebuilt: Test Files 3 passed (3) · Tests 20 passed (20).

The routes

route was now
GET /meta/types /meta/:type catch-all → {"type":"types","items":[]}, shape-identical to /meta/zzz_not_a_type registered before /meta/:type; same handler as GET /meta
GET /meta/:type/:name/published compound-name route → a stub identical before publish and for a bogus name registered before /:type/:section/:name; 404s for a bogus name. Compound arity /:type/:section/:name/published mounted too — the SDK documents getPublished('lead','views/all_leads')
GET /meta/objects/:name/state/:field Hono notFound; REST's /meta routes topped out at 3 segments, this needs 4 mounted, plus the singular /meta/object/... spelling the dispatcher branch accepted

Three more instances, found by the gate on its first run

GET /automation/actions, /automation/connectors, /automation/_status were ordered ahead of the /:name catch-all inside dispatch() — with a module comment saying the order was load-bearing — while the bridge that actually mounts /automation registered /:name and never those three. Same class, same fix. This is the card's "fixing the routes without the gate buys the next three", measured.

Unledgered live mounts (PENDING-GAPS §E)

Read off the booted server, not off a list:

  • /api/settings ×4 → new packages/services/service-settings/src/settings-route-ledger.ts (all server-only; the Setup UI consumes them over plain HTTP, the SDK expresses none).
  • GET /.well-known/objectstack → a dispatcher-ledger row + /.well-known/objectstack in NON_DISPATCH_MOUNT_PREFIXES. Needed a new absolute?: boolean field: a well-known URI under /api/v1 is not a well-known URI, and client-url-conformance.test.ts honours it too rather than compiling a pattern nothing serves.
  • POST /actions//:action (the object-less shape, deliberate since [17.0.0-rc.0] Global actions unreachable: registered under key 'global', REST fallback probes '*' — and handler failures return HTTP 200 {success:true,data:{success:false}} #3913) → a dispatcher-ledger row.
  • POST /actions/global/:action uses a new servedBy field: it is a calling convention served by /actions/:object/:action, not a registration. The gate does not take the field's word for it — it probes and asserts the live router answers with exactly that pattern.

Measured, not closed: /api/v1/datasources and /api/v1/datasources/drivers are 2 of 9 unledgered admin routes in service-datasource, and that plugin is not in this gate's boot. Ledgering 9 routes needs 9 reviewed dispositions I have not verified consumers for — see the report on the issue for the recommended follow-up.

The drifted pin

meta-types-create-seed.dogfood.test.ts said /meta/types in its header and called /meta. It calls /meta/types now, and pins the alias (/meta/types/meta) plus the disguise (/meta/zzz_not_a_type{type,items:[]}, /meta/typesentries[]). A systematic comment-vs-call scan of all 89 dogfood files found no other pin with the same gap — details in the issue report.

Checks

pnpm lint ✅ · pnpm typecheck (rest, runtime, client, plugin-hono-server, dogfood, spec) ✅ · pnpm check:route-envelope ✅ (new bodies conform; both ratchets unchanged) · rest 1348 ✅ · runtime 1990 ✅ · client 279 ✅ · plugin-hono-server 198 ✅ · dogfood 562 ✅ (89 files). origin/main merged and rebuilt before the final run.

Serial constraints honoured

Region touched in rest-server.ts is the /meta route registration only. The reports-delete handler (#7523) and mapDataError / the error-mapping region (#7525) are untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DnUXMJxLDLoQ9MTRthtAGd


Generated by Claude Code

claude added 3 commits August 11, 2026 05:48
…lass that hid them (#7526)

Three routes were in the ledgers, implemented in the dispatcher, and mounted by
nobody — two of them answering a plausible 200 instead of a 404:

  * GET /meta/types fell into the /meta/:type catch-all and answered
    {"type":"types","items":[]}, shape-identical to /meta/zzz_not_a_type;
  * GET /meta/:type/:name/published fell into the compound-name route and
    answered a stub identical before publish AND for a bogus name — a route
    that structurally could not 404;
  * GET /meta/objects/:name/state/:field needs four path segments and REST's
    /meta registrations topped out at three, so it answered Hono's notFound.

All three mount now, ahead of the catch-alls that were swallowing them, with
the compound-name /published arity the SDK documents.

The routes were the symptom. The ledgers are a DECLARATION and every guard
built on them (#3563/#3587/#3636/#3642) reads their union as an OBSERVATION of
what is mounted, so the audit chain was green on this class by construction.
This adds the missing observation: a route-ledger <-> live-mount parity gate
that boots a real server, reads the mount table off it, and asserts both
directions. It consults no second hand-written list of what is mounted, and it
PROBES reachability through the live router rather than checking presence in a
table — a literal registered after a catch-all sibling is mounted and
unreachable.

IHttpServer grows two optional feature-detected members for it —
getMountedRoutes() and resolveMountedRoute() — implemented by the Hono adapter.

On its first run the gate found three more instances of the same class:
/automation/actions, /automation/connectors and /automation/_status were
ordered ahead of the /:name catch-all inside dispatch(), with a comment calling
the order load-bearing, while the bridge that mounts /automation registered
/:name and never those three. It also found the unledgered live mounts — the
four /api/settings routes get a ledger of their own, and
GET /.well-known/objectstack plus the object-less POST /actions//:action get
rows in the dispatcher ledger.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnUXMJxLDLoQ9MTRthtAGd
…7526)

The parity gate proves they are MOUNTED. This proves they ANSWER — a
different question, and the one `/published` failed most visibly: it was
reachable in the sense that requests to it got a 200, from the compound-name
route, with a body identical before publish AND for a name that does not
exist. Booted with a MetadataPlugin because `getPublished` is a metadata-
SERVICE capability and `objectstack serve` composes one for every deployment;
without it the route answers a typed 501, which is the honest degradation but
not the behaviour under test.

Also conforms the new error bodies to the envelope `BaseResponseSchema`
declares (`pnpm check:route-envelope` ratchets the two non-conforming
dialects DOWN only), and retires a stale assertion in
rest-openapi-route.test.ts: `/api/v1/meta/types` was listed as a path that
'exists nowhere in the repo', which was true of the ROUTE and is no longer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnUXMJxLDLoQ9MTRthtAGd
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 11, 2026 6:26am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 6 package(s): @objectstack/plugin-hono-server, @objectstack/dogfood, @objectstack/rest, @objectstack/runtime, @objectstack/service-settings, @objectstack/spec.

116 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/connect-mcp.mdx (via @objectstack/rest)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via packages/runtime, @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime, packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/runtime, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/runtime, @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/plugin-hono-server, @objectstack/runtime, @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services/service-settings)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services/service-settings, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services/service-settings)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/qa/dogfood, packages/runtime, @objectstack/spec)
  • content/docs/permissions/delegated-administration.mdx (via packages/qa/dogfood)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/permissions/system-context.mdx (via packages/rest, packages/runtime, packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/runtime, @objectstack/service-settings, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/service-settings, @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/rest, @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-hono-server, @objectstack/rest, @objectstack/runtime, @objectstack/service-settings, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/plugin-hono-server, @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/service-settings, @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 11, 2026
@os-help
os-help marked this pull request as ready for review August 11, 2026 09:17
@os-help
os-help added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit cc3555e Aug 11, 2026
27 checks passed
@os-help
os-help deleted the claude/issue-7526-route-ledger-live-mount-parity branch August 11, 2026 09:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

2 participants