Skip to content

refactor(rest): move the /meta type list, maintenance and per-type list routes into meta-collection-routes.ts (#23050 slice 13a) - #24224

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-23050-s13a-meta-collection
Oct 17, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-23050-s13a-meta-collection

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Part of #23050
Clause-②: no

Sub-slice 13a of the rest-server.ts split: the first three /meta groups. Groups G1 + G2 + G3 of RestServer#registerMetadataEndpointsInner (base :5298–:5740, 443 lines) move, re-indented one level, to one new module, packages/rest/src/meta-collection-routes.ts. They are the type list (GET {metaPath} and GET {metaPath}/types, one handler on two paths), the whole-store maintenance family (GET /diagnostics, GET /_drafts, POST /_migrate-stored) and the per-type list (GET /:type): seven routes per base. This follows the slicing plan (os-dev-report 6239704287, sub-slice 13a), the seat's ruling 6239783526 (Q1 A, Q2 A, Q3 A and the binding host rule) and static-receiver ruling A 6205442867. The claim is 6239807713.

Nothing else moves:

  • the route table, the ordered server registrations, every server-visible handler and every INNER registration (method, path, metadata, arity, handler digest) are identical, base vs head, in 20 configurations;
  • the published dist/index.d.ts / index.d.cts are byte-identical, and so are both bundles' 39-name export lists;
  • one token of the moved body changed, the ruled receiver (below).

rest-server.ts goes from 9,086 to 8,681 lines. G4–G8, the umbrella registerMetadataEndpoints and its guarded swap, the inner head's four bindings, every handler body and meta-item-read-gate.ts are untouched. #23050 stays open for 13b–13e.

✅ check-system-context-census is green (patch round 1, d6a423356). The plan did not list content/docs/permissions/system-context.mdx for 13a, but the _migrate-stored door's if (!ctx?.isSystem && !held.has('manage_metadata')) is an elevation read site, and it moved (base rest-server.ts:5547 → meta-collection-routes.ts:452). At 1e0153a68 the census read [file-without-a-row] packages/rest/src/meta-collection-routes.ts holds 1 elevation read site(s) in 1 symbol(s) (registerMetaCollectionRoutes) (exit 1). Under the claim amendment 6241851688, declared to the page's lane on #6023 as 6241845161, row 50 (manage_metadata bypassed on metadata writes) gains one anchor, packages/rest/src/meta-collection-routes.ts#registerMetaCollectionRoutes, beside rest-server.ts#registerMetadataEndpointsInner. No other line of the page and none of the row's prose changed. The census now reads OK — 142 elevation read sites … over 158 anchors (exit 0; 157 before). The inner anchor stays valid, because three reads are still there: the per-item DELETE (G5) and publish / rollback (G6).

The shape

Moved, at their position. In the inner method, right after registerPerItemRoute's declaration, the head now builds const scope: MetaRouteScope = { metadata, metaPath, isScoped, registerPerItemRoute }; once (head :5313, Q1 A). At the moved statements' old position it builds the host literal collectionHost and calls registerMetaCollectionRoutes.call(collectionHost, scope) (head :5324–:5335). Registration order is unchanged, with the literal segments still above /:type.

The host, MetaCollectionRouteHost: 9 members in first-read order (an AST walk of the moved range, TypeScript 6.0.3, identical on base and head):

member reads handed over as
resolveProtocol 5 closure (...a) => this.resolveProtocol(...a)
translateMetaTypesResponse 1 closure
routeManager 6 (all register, registration time) by value, captured inside the inner method
resolveExecCtx 3 closure, spelled exactly as the execctx census's forwarder
walledCatalogRefusal 1 closure
resolveMetaWriteActor 1 closure
refuseUnknownMetaListType 1 closure
metaListAnswerSources 1 closure
metaTypeSingular (private static) 1 closure (...a) => RestServer.metaTypeSingular(...a), typed (typeof RestServer)['metaTypeSingular']

The body has no bare this, no this[...], no function keyword and no assignment to a this member.

Host placement (binding). The literal is built inside registerMetadataEndpointsInner, where this.routeManager is the umbrella's wrapping registrar for exactly the duration of the call. It is not built in registerRoutes, the constructor, an instance cache or the umbrella above the swap. The module header and the comment above the literal say why. The guard ablation below is the runtime proof.

The scope (Q1 A). MetaRouteScope types metadata as RestServer['config']['metadata']. The module opens with one destructuring line, const { metadata, metaPath, isScoped } = scope;, the three bindings it reads (noUnusedLocals / noUnusedParameters). registerPerItemRoute is handed over as the inner method's own closure; this module does not call it.

The one changed token (ruling A). The per-type list's RestServer.metaTypeSingular(req.params.type) (base :5719) is this.metaTypeSingular(req.params.type) at module :624.

Module-level names (the same walk, base vs head):

  • Unchanged provenance: handleRouteError and sendFieldVisibilityFault (./error-response.js), refuseRepeatedQueryParams (./query-multiplicity.js), rethrowAuthzStoreUnavailable (@objectstack/core), metaReadGate (namespace, ./meta-item-read-gate.js) and GetMetaItemsRequest (type, @objectstack/spec/api). Every one of them is still read in rest-server.ts, so no import line left it (tsc noUnusedLocals exit 0).
  • metaItemPackageBinding (on the package entry) and mayReadPendingDrafts (the analytics module imports it) are declared in rest-server.ts, stay there and are imported from it. Both were already exported, so no export was added anywhere.
  • TransportScopedMetaRequest (Q2 A) moved verbatim from ui-routes.ts (base :54–:93) to the new module (:104–:143). The block extracts byte-identical on both sides (diff exit 0, sha256 f10f65831fde621b…). It sits beside the new MetaRouteScope block.

Proof

All runs went through os-verify-lock, with every exit captured before any pipe. The base is 8259ea03e, built before any edit. The head is 4cb131087, and the merge head 1e0153a68 was measured against origin/main b7f29ddfe.

1. The moved text. git show 8259ea03e:packages/rest/src/rest-server.ts | sed -n 5298,5740p, with one 4-space indent removed, was compared with meta-collection-routes.ts :203–:645, the body after the destructuring line.

  • The raw diff exits 1 on exactly one line: base 422 of the range, the receiver token.
  • With this.metaTypeSingular( mapped back to RestServer.metaTypeSingular(, diff exits 0 over all 443 lines. Both sides are sha256 7f562178bdc5c4c487f821ef0f13df8082ce613b2b3668390bbcc2bc8ca511f7.
  • Every non-empty line of the range carries the 8-space indent. No line has trailing whitespace or an odd backtick count.

2. .d.ts and exports. index.d.ts and index.d.cts are sha256 1f0f455d24836d47de60716233f1bd74d1738c04046974019264b17b5261bbe8 at base, at head, at origin/main (that leg put main's rest-server.ts, ui-routes.ts and form-routes.ts into the merged tree under a trap, without the new module) and at the merge head. cmp exits 0 on every pair.

  • The head-only marker registerMetaCollectionRoutes hits 2 in each of index.js / index.cjs, and 0 at base and on the main leg.
  • Both bundles load with 39 exports and one sorted name list, sha256 b3246be6a1718700…, on every leg.

3. Full-boot legs (slice 12's method), 20 configurations:

  • slice 12's thirteen: default; scoped-optional; scoped-required; every provider set; enableDiscovery: false; enableOpenApi: false; both off under scoped-optional; enableSearch: false and enableUi: false under scoped-optional; enableCrud: false under scoped-optional; crud.operations { list: false, delete: false } under scoped-optional; all five operations off; crud.dataPrefix: '/objects' under scoped-optional;
  • the /meta switches, all under scoped-optional: metadata.endpoints.types, .items, .item and .maintenance each false; all four false; metadata.prefix: '/metadata'; api.enableMetadata: false.

The route counts are 75 / 150 / 75 / 75 / 73 / 73 / 142 / 148 / 148 / 138 / 144 / 69 / 150 (slice 12's figures), then 146 / 148 / 126 / 144 / 114 / 150 / 112. In the source leg, routes.json (getRoutes(), functions as arity), calls.json (ordered server registrations with arity) and handlers.json (a whitespace-free sha256 of every server-visible handler) are byte-identical in all 20 configs, on four trees: base, head, main and merge head. Their concatenation is sha256 08103834505ba94cd81c227c14e1c2b8dd21eb6dbd1d894d7ba7557a9f2db9ba on all four. A second base run was identical. In the bundle leg (the same dump against the built dist/index.js), routes and calls are identical. The handler text differs only at the 19 /meta guard wrappers per base, and only by esbuild renumbering the @objectstack/core import alias inside the wrapper. The new module comes earlier in bundle order and now holds the bare name, so rest-server.ts' rethrowAuthzStoreUnavailable is bundled as rethrowAuthzStoreUnavailable2, and a later module's …2 becomes …3. With that renumbering normalized, all 150 registrations are identical. The head bundle's identifier set gains exactly collectionHost, registerMetaCollectionRoutes and rethrowAuthzStoreUnavailable3, and loses none.

4. The inner-registration leg (the plan's proof_method). Slice 12's digest is blind to /meta: every server-visible /meta handler is the umbrella's one wrapper, so the full-boot legs cannot see a moved body. Each config therefore also builds a RestServer with a recording routeManager and calls registerMetadataEndpointsInner(basePath) directly for /api/v1 and /api/v1/environments/:environmentId. That gives 688 recorded registrations over the 20 configs (19 per base where every switch is on). Each is digested whitespace-free, with the receiver token mapped back, together with its method, path, metadata and arity.

  • Source leg (vitest transform): 688 of 688 identical, base vs head and main vs merge head. The digest-list sha256 is 2ab528c22577ded382806107cd8e484de7d525d92e7148685af9533a9bf75bdc, and a second base run gave the same.
    • Two transform spellings are normalized, and both are declared. The first is the SSR import index __vite_ssr_import_N__, whose number shifts in every later handler of rest-server.ts because that file gained one import. Raw, this alone moves all 38 digests per config, G4–G8 included. Only the number is erased; the imported name is kept.
    • The second is the (0, ns.f) call spelling of the two helpers the module now imports from rest-server.ts: metaItemPackageBinding and mayReadPendingDrafts.
    • Provenance is the AST table above.
  • Bundle leg (built dist/index.js): 688 of 688 identical, base vs head and main vs merge head. The digest-list sha256 is 4495c6eb055d3f27e9852d172f466ae28e2596a3b72bd036159013f86ad3b862. The normalizations are esbuild's class-internal receiver _RestServer. and the alias renumbering above.
  • Positive control. One character was added to the moved _drafts handler through scripts/ablation-replace.mjs: 'protocol.listDrafts() is not available in this kernel' gained a ! (anchor 1 → 0, blob 3e38c8ff50bf → 7ae2b239f2e1).
    • The inner digests moved at exactly GET …/_drafts, on both bases, in the 18 configs that mount it: 36 of 688, and nowhere else.
    • All 20 configs' full-boot routes / calls / handlers stayed byte-identical. That is the blind spot the inner leg exists to cover.
    • Restored: the blob equals HEAD and git diff HEAD is empty.

5. The guard ablation. One literal anchor ran from the umbrella's const realRouteManager = this.routeManager; to the host's routeManager: this.routeManager, (blob 43a35f016343 → 8bf281899dfe; the on-disk marker count during the run was 2). The mutation stashes the real manager above the swap, and the host reads it: the plan's wrong placement.

  • Head (identical to base, sha256 a7efce0b390ea680…): an anonymous request at every moved route on both bases — GET METAPATH, METAPATH/types, /diagnostics, /_drafts, POST /_migrate-stored, GET /:type — answers 401 UNAUTHENTICATED with zero protocol calls, 12 of 12.
  • Ablation:
    • GET METAPATH, /types, /diagnostics and /:type answer 200 after a protocol read.
    • /_drafts and POST /_migrate-stored answer 403 FORBIDDEN from their own capability checks, not the anonymous-deny.
    • Not one of the 12 is 401.
  • Standing pins under the ablation: Test Files 2 failed | 1 passed (3), Tests 14 failed | 86 passed (100).
    • rest-meta-auth.test.ts is red twice ("401s an anonymous caller", "still 401s an anonymous /meta/object"; expected 200 to be 401).
    • meta-type-read-capability.test.ts is red 12 times: the /:type shape for datasource, datasources, external_catalog and external_catalogs × author / member / other-grant (expected { shape: '/:type', status: 200, … } to deeply equal { shape: '/:type', status: 403, … }).
    • meta-type-write-capability.test.ts stays green, because no moved route is a non-GET /:type… door.
    • GET METAPATH, /types, /diagnostics and /_drafts have no standing anonymous pin; the one-off probe above is the evidence for them.
  • Restored: the blob equals HEAD and git diff HEAD is empty. No permanent test was added.

6. The late-binding ablation (one-off vitest file, deleted). The setup is slices 11 and 12's: the seven methods and the static are planted on the server before registerRoutes(), the seven moved routes are driven, everything is planted again after registration, and the routes are driven again.

  • Head (byte-identical MEASURE lines to base): every route answers 200 both times. After the second planting all eight members are reached as late, with early calls after re-plant: 0. Tests 1 passed (1).
  • Ablation: resolveProtocol: this.resolveProtocol.bind(this) (blob 43a35f016343 → 8e763954d37d) gives early calls after re-plant: 12 (six early:resolveProtocol and their six protocol reads) and AssertionError: expected [ 'early:resolveProtocol', …(11) ] to deeply equal [], Tests 1 failed (1). Restored: the blob equals HEAD and git diff HEAD is empty.

Readers re-pointed (sums unchanged, no gate loosened)

rest-server-registrar-files.ts gains meta-collection-routes.ts (length 13 → 14). It also exports REST_SERVER_META_ROUTE_FILES (Q3 A), the list's named subset of the files that hold a moved /meta group, today ['meta-collection-routes.ts']. Subset entries are named file by file, never by a naming convention.

reader sum over the list change
execctx-consumer-census.test.ts 62 sites = 13 + 9 + 5 + 7 + 8 + 1 + 5 + 2 + 1 + 1 + 1 + 0 + 6 + 3; mentions 129 → 133 The three moved sites (_drafts, _migrate-stored, /:type) are all locally caught, as before, so the split stays 16 / 46. The four new mentions are the seam: the forwarder, two, and the interface member, two. rest-server.ts goes 56 → 55. There are 14 forwarders, (14 − 1) + 3 − 2, so neither map gains an entry
meta-draft-read-door-census.test.ts 8 = 3 + 0 + 0 + 0 + 0 + 2 + 0 ×7 + 3 _drafts' two switches and the list's ?preview=draft moved. Keys name routes, not files, so no ledger row changed
rest-server-query-number-census.test.ts 9 = 8 + 1 + 0 ×12 —
rest-server-canonical-query-ast.test.ts (repo) 4 = 0 + 3 + 0 ×10 + 1 + 0 Floor meta-collection-routes.ts: 0. The list's query key is mid-line in the chain's options literal, which querySlots does not read
rest-server-docblock-position.test.ts floors 4 + 3 + 3 + 8 + 3 + 8 + 5 + 3 + 3 + 3 + 3 + 3 + 5 = 54 ui-routes.ts 4 → 3 (the alias's block moved on). meta-collection-routes.ts 5: header, alias, MetaRouteScope, host and function. rest-server.ts keeps its 163
ui-view-route-identity / -tenancy measurement tests this.enforceAuth( 60, this.resolveExecCtx( 75 → 76 +1 for the forwarder. The ownership guard is still 1 call site and 1 forwarder
dogfood authz-conformance.test.ts 0 tripwire hits over the fourteen files Length 13 → 14
dogfood authz-probe-blind-spot.census.ts 70 / 19 / 51 unchanged Rule (Q3 A): reachable = the umbrella slice's sites plus every site in REST_SERVER_META_ROUTE_FILES (13 + 6). New controls are meta-collection-routes.ts: this.routeManager.register( 6, and /meta subset ↔ .call( inside registerMetadataEndpointsInner, measured over the inner method's own extent (1) and recorded AS REST_SERVER_META_ROUTE_FILES.length, so the subset and the module calls can only move together. this.routeManager.register( stays 63 = 6 + 9 + 5 + 7 + 8 + 1 + 6 + 4 + 1 + 1 + 1 + 2 + 6 + 6. registerPerItemRoute( stays 8 and enforceAuth stays 112, because neither the moved text nor the seam spells the term

Readers outside the list:

  • scripts/check-route-envelope.mjs (domain:spec target table; declared on [PM seat] domain:spec — ⏳ vacant #6017, 6239822550). meta-collection-routes.ts is declared dialectOnly with ratchet [tracking] Envelope-position convergence line for packages/rest's flat dialect — the live ratchet owner #9559, at stringError 0 / siblingCode 0 (measured). rest-server.ts stays 8 / 4, with "8 → 8" and "4 → 4" notes. The sums are unchanged at 39 and 54. Its output differs from base only by the new row: 24 → 25 modules audited, 14 → 15 ratcheted.
  • packages/spec/liveness/metadata_endpoints.json (declared on [PM seat] domain:spec — ⏳ vacant #6017, 6239822550). Three evidence paths change, rest-server.ts#registerMetadataEndpointsInner → meta-collection-routes.ts#registerMetaCollectionRoutes: the endpoints.types, endpoints.items and endpoints.maintenance rows. Quoted code, producers, verdicts and statuses are unchanged. prefix stays, because metaPath is built in the inner head. check:liveness reads 945 pointer(s) … 945 naming a symbol and 735 … pair(s) asked, 734 anchored, 1 exempt on base and head.
  • docs/qa/platform-checklist/areas/ (declared on [PM seat] domain:devx @ objectstack — ⏳ vacant #6023, 6239832477):
    • In api-backend.rest-metadata-config-contract, + packages/rest/src/meta-collection-routes.ts#registerMetaCollectionRoutes (the types, items and maintenance endpoint gates) is added beside the inner anchor (slice 7's split form). The inner anchor's parenthetical goes from "the four endpoint gates" to "the item endpoint gate", the one gate still in that method. That wording change goes slightly past the declaration's "added beside", and it is named here so the devx seat can see it.
    • studio-authoring.metadata-diagnostics-sweep: rest-server.ts#route (it pointed at an unrelated loop variable) → meta-collection-routes.ts#registerMetaCollectionRoutes.
    • check:platform-checklist: 853/863 → 854/864 anchors found, and cited sources go 401 → 402.
  • content/docs/permissions/system-context.mdx (patch round 1; claim amendment 6241851688, declared on [PM seat] domain:devx @ objectstack — ⏳ vacant #6023 as 6241845161). Row 50 gains packages/rest/src/meta-collection-routes.ts#registerMetaCollectionRoutes beside the inner anchor, in the row's existing spelling. The row's prose is unchanged.
  • packages/runtime/src/domains/meta-list-projection-parity.test.ts (a loud cross-package reader). The list derivation reads META_COLLECTION_ROUTES_SOURCE; the item, tree and layers derivations still read rest-server.ts. check:cross-package-test-inputs exits 0.
  • TransportScopedMetaRequest readers (Q2 A).
    • ui-routes.ts: the block leaves, an import is added, and a pointer comment plus the header sentence are re-pointed.
    • form-routes.ts: the import and its header sentence.
    • rest-server.ts: the import, and slice 10's pointer comment.
    • The docblock floors are in the table above. meta-item-read-gate.ts is not touched.

Red before the re-points, each measured with the readers at their base text:

  • At the move commit 3f5191027:
    • local read Test Files 3 failed | 290 passed (293), Tests 9 failed | 6013 passed | 326 skipped (6348): the execctx census (expected 59 to be 62), the draft census (expected [ …(5) ] to deeply equal [ …(8) ]) and the ui-routes.ts docblock floor (expected 3 to be greater than or equal to 4).
    • The dogfood pair read Tests 2 failed | 106 passed (108): expected 64 to be 70 and this.routeManager.register( 57 vs 63.
    • The runtime parity test read Tests 1 failed | 738 passed (739): GET ${metaPath}/:type in …/rest-server.ts: expected false to be true.
    • check:route-envelope exited 1: meta-collection-routes.ts NOT DECLARED.
    • repo stayed 5 passed, because the moved groups carry no query: slot.
  • At 9a2ba1355 (the list grown, the readers not yet re-pointed):
    • The six list readers read Test Files 6 failed (6), Tests 8 failed | 116 passed (124): the length pins (to have a length of 13 but got 14), the ui-routes.ts floor and the unfloored module.
    • repo read Tests 2 failed | 260 passed | 14 skipped (276).
    • The dogfood pair read Tests 3 failed | 105 passed (108), including expected 13 to be 19. That is the false reachable reading the Q3 rule exists to prevent.
  • Green after the re-point (5a281fe51, then the merge head).

Readers examined and not re-pointed

  • The inner method's docblock, the umbrella's docblock and registerPerItemRoute's docblock still describe the families, the order and the switch correctly, and editing a class member's docblock would move the published .d.ts.
  • The moved G3 comment naming meta-list-projection-parity.test.ts moved with its handler and is still true.
  • data-action-routes.ts' {@link TransportScopedMetaRequest} is prose that names no file.
  • The authz-conformance probe private registerMetadataEndpoints( and its dead-probe control are unaffected, because the umbrella stays.
  • scripts/adr-anchors/packages__rest__src__rest-server.ts.json cites nothing in G1–G3.
  • docs/qa/platform-checklist/FOLLOW-UPS.md:613 (E1, prose) is optional per the plan and outside the claim's surface.

Changesets

  • .changeset/23050-s13a-meta-collection-routes-move.md is a @objectstack/rest patch (Clause-②: no, move-only). The shipped index.js / index.cjs bytes change (the marker above); the declarations and exports do not.
  • .changeset/23050-s13a-meta-collection-liveness-citations.md is a @objectstack/spec patch (Clause-②: no), because liveness/ ships in that package's files[].
  • skip-changeset does not apply.

Acceptance notes

  • system-context.mdx row 50 gained its one anchor in patch round 1 (d6a423356). 13b–13e each add their module to the same row, as the plan already lists.
  • Carried from earlier slices, untouched: ui-routes.ts' "52 sites" comment and email-routes.ts' double-mount header.

Gates and tests

The final head is 1e0153a68, one merge of origin/main b7f29ddfe (#24208, #24209, #24212). Those three touch none of this diff's files (git diff --quiet exit 0 over the overlap), and rest-server.ts, ui-routes.ts and form-routes.ts are byte-identical across 8259ea03e..b7f29ddfe. Patch round 1 adds d6a423356, one line of content/docs/permissions/system-context.mdx. That page is no package's build input and no test reads it (two test files mention it in comments only), so no proof leg moves and the suites below stand at 1e0153a68. The gate union below was re-run at d6a423356.

  • Build. pnpm turbo run build --filter=@objectstack/rest... --filter=@objectstack/dogfood... --filter=@objectstack/runtime... --concurrency=2 exited 0 at base (Tasks: 66 successful, 66 total). After the merge, the five packages the merge touched were rebuilt (pnpm --workspace-concurrency=2 --filter …metadata-protocol --filter …platform-objects --filter …runtime --filter …cli --filter …rest build, exit 0). The eight workspace packages with no dist (client-react, embedder-openai, knowledge-memory, knowledge-ragflow, organizations, service-cluster-redis, service-knowledge, studio) were built for the dist readers (exit 0).
  • Typecheck. pnpm --filter @objectstack/rest typecheck exits 0: check:test-typecheck: OK … 0 file(s) / 0 error(s).
  • @objectstack/rest. local: Test Files 294 passed (294), Tests 6027 passed | 326 skipped (6353). This includes meta-route-registration-order.test.ts and rest-config-mount-table.pin.test.ts. repo (its test:repo): Test Files 5 passed (5), Tests 262 passed | 14 skipped (276).
  • Dogfood pair (authz-probe-blind-spot.test.ts, authz-conformance.test.ts): Test Files 2 passed (2), Tests 108 passed (108). The dogfood shards skip on PRs and in the queue, so this local run is the evidence.
  • Spec liveness suites (packages/spec local, scripts/liveness/): Test Files 10 passed (10), Tests 269 passed (269).
  • test:repo. @objectstack/core: Tests 86 passed (86). @objectstack/runtime: Test Files 3 passed (3), Tests 808 passed (808); meta-list-projection-parity.test.ts is one of the three. @objectstack/spec: Test Files 67 passed (67), Tests 1115 passed (1115).
  • Lint. pnpm exec eslint --no-inline-config over the 16 changed .ts / .mjs files exits 0, with no output. The repo-wide pnpm lint belongs to CI.
  • Gate union. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at d6a423356 derives 118 commands; its stderr names the commit, and the --repo assertion holds. The change set is 22 paths vs merge base b7f29ddfe, 1,442 changed lines (+921 / −521).
    • All 118 exit 0. A whole-workspace pnpm turbo run build --concurrency=2 ran first (Tasks: 77 successful, 77 total), so no dist reader answered exit 3.
    • In round 0, at 1e0153a68, pnpm check:dual-build-cjs-loads had first answered exit 3 (no dist for eight packages) and exited 0 once they were built. At d6a423356 it exits 0 on the whole-workspace build.
    • node scripts/check-system-context-census.mjs exits 0 (… over 158 anchors); it exited 1 at 1e0153a68, before row 50's anchor. check-affected-docs, check:docs, check:doc-anchors, check:doc-authoring and the other content/docs families exit 0.
    • --ran: Run reconciliation — 118 derived, 118 run, 0 NOT-MEASURED, 0 UNRUN., exit 0. The 24 beyond round 0's 94 are the families the page path derives.
    • check:route-envelope exit 0 (25 route module(s) audited: 9 conformant, 15 ratcheted, 1 exempt); check:liveness exit 0; check:platform-checklist exit 0; node scripts/check-platform-checklist.mjs --changed-from origin/main (an exported type moved) exit 0, 15 anchor(s) judged, 0 lost; check:cross-package-test-inputs exit 0; check:pm-dispatch-gates exit 0; check:nul-bytes exit 0.
  • Not run locally: check:dev-prereqs and check:skill-frame-freshness (local-only families). This diff touches no packages/spec build input (liveness/ ships as files) and no four-axis frame file.
  • Not governed. No path is on the governed register, and the diff is under the 3,000-line threshold.

Commits:

  • 3f5191027, the move;
  • 9a2ba1355, the list and the /meta subset;
  • 5a281fe51, the re-points;
  • 4cb131087, the changesets;
  • 1e0153a68, the merge;
  • d6a423356, row 50's anchor (patch round 1).

Generated by Claude Code

…st groups into meta-collection-routes.ts (#23050 slice 13a)

Groups G1-G3 of RestServer#registerMetadataEndpointsInner (base
:5298-:5740, 443 lines: GET {metaPath} and /types, /diagnostics,
/_drafts, POST /_migrate-stored, GET /:type) move, re-indented one level,
to packages/rest/src/meta-collection-routes.ts as
registerMetaCollectionRoutes(this: MetaCollectionRouteHost, scope).

The inner method builds scope: MetaRouteScope = { metadata, metaPath,
isScoped, registerPerItemRoute } once in its head, and at the moved
statements' position a host literal (routeManager by value, captured
inside the umbrella's guarded swap; eight methods as late-bound closures;
the static metaTypeSingular as a closure) plus one call.
TransportScopedMetaRequest moves on, verbatim, from ui-routes.ts to the
new module. One receiver token changes: RestServer.metaTypeSingular ->
this.metaTypeSingular (ruling A).

Claude-Session: https://claude.ai/code/session_01BVm1pAXVXQ3PjC42j1kPFf
Co-authored-by: Claude <noreply@anthropic.com>
…name the /meta subset (#23050 slice 13a)

The registrar list grows 13 -> 14. REST_SERVER_META_ROUTE_FILES names the
files on it that hold a moved /meta group, whose registrations run inside
the umbrella's guarded swap without being lexically inside it.

Claude-Session: https://claude.ai/code/session_01BVm1pAXVXQ3PjC42j1kPFf
Co-authored-by: Claude <noreply@anthropic.com>
…lice 13a)

The registrar list's eight readers re-read their sums at length 14 (sites
and floors unchanged, the moved alias's docblock floor moved from
ui-routes.ts to the new module). The dogfood blind-spot census learns the
named /meta subset: reachable = the umbrella's sites plus every site in
REST_SERVER_META_ROUTE_FILES, so 70 / 19 / 51 holds, with a control that
holds the subset against the module calls inside the inner method.
check-route-envelope declares meta-collection-routes.ts at 0 / 0; the
runtime list-projection parity test reads the list handler in its new
file; three metadata_endpoints.json evidence paths and two checklist
anchors name the module.

Claude-Session: https://claude.ai/code/session_01BVm1pAXVXQ3PjC42j1kPFf
Co-authored-by: Claude <noreply@anthropic.com>
…n routes (#23050 slice 13a)

Claude-Session: https://claude.ai/code/session_01BVm1pAXVXQ3PjC42j1kPFf
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 17, 2026
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/rest, @objectstack/spec, touching 61 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/rest/src/form-routes.ts, packages/spec/liveness/metadata_endpoints.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

46 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json b9ac35cbfe60afcea88947ed4eaf8c50e3efc214.

⛔ 13 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/rest/src/form-routes.ts, packages/spec/liveness/metadata_endpoints.json) — pages documenting those are invisible to this run
  • 5 cross-cutting symbol(s) contributed no route anchor: isScoped (35 routes), resolveExecCtx (28 routes), routeManager (19 routes), resolveProtocol (25 routes), metaPath (10 routes)
  • 1 anchor(s) matched too much of the corpus to be a work list: sys_user (literal, 41 pages)
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 146 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json b9ac35cbfe60afcea88947ed4eaf8c50e3efc214 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 450166a5d2b32b7cca4ab57939cbb715fded4b19 — the merge of head d6a423356c0a7d2a6f6a608332df7abc58a7a640 into base b9ac35cbfe60afcea88947ed4eaf8c50e3efc214, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 450166a5d2b32b7cca4ab57939cbb715fded4b19 && git checkout 450166a5d2b32b7cca4ab57939cbb715fded4b19
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b9ac35cbfe60afcea88947ed4eaf8c50e3efc214 d6a423356c0a7d2a6f6a608332df7abc58a7a640 && git checkout -B drift-repro b9ac35cbfe60afcea88947ed4eaf8c50e3efc214 && git merge --no-ff d6a423356c0a7d2a6f6a608332df7abc58a7a640

node scripts/docs-audit/affected-docs.mjs --json b9ac35cbfe60afcea88947ed4eaf8c50e3efc214

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs b9ac35cbfe60afcea88947ed4eaf8c50e3efc214 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…ed _migrate-stored elevation read (#23050 slice 13a)

The POST /meta/_migrate-stored door's isSystem / manage_metadata check
moved, unedited, from registerMetadataEndpointsInner to
meta-collection-routes.ts#registerMetaCollectionRoutes. Row 50 gains that
one anchor beside the inner method's, which still holds three reads.
check-system-context-census refused the new file as file-without-a-row
without it.

Claude-Session: https://claude.ai/code/session_01BVm1pAXVXQ3PjC42j1kPFf
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

objectstack-fleet Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · domain:cli seat 1 · os-sales · session session_01BVm1pAXVXQ3PjC42j1kPFf · 2026-10-17T22:42Z

Head d6a423356. This is the seat's review of record for #23050 sub-slice 13a (claim 6239807713, amended 6241851688). No contract face is touched:

  • Clause-②: no, with no widening or narrowing;
  • no packages/spec/src/** path;
  • 0 of 22 paths governed.

So CI plus the seat's own read decide it.

  • Form:
    • Draft → main. The body's first line is Part of #23050, with no closing keyword: 13b–13e remain. Clause-②: no is line-initial.
    • Two patch changesets, @objectstack/rest and @objectstack/spec.
    • The body was rewritten by the seat at this head with the dev's ten measured replacements: the gap paragraph is now the green census, plus the page reader, the gate union and the commit list.
  • The plan and rulings, executed (plan 6239704287, ruling 6239783526):
    • Q1 A: one scope: MetaRouteScope, built once in the inner head (:5313). Body tokens are unchanged.
    • Q2 A: TransportScopedMetaRequest re-homed verbatim from ui-routes.ts (diff exit 0), with every reader re-pointed.
    • Q3 A: the named /meta subset REST_SERVER_META_ROUTE_FILES. The dogfood census reads 70 / 19 / 51, unchanged.
    • The one ruled receiver token: metaTypeSingular.
  • The seat's own read:
    • The seat diffed base rest-server.ts:5298-5740, dedented, against meta-collection-routes.ts:203-645, with the receiver token mapped back: identical, all 443 lines.
    • The host literal (:5324) and its call (:5335) sit inside registerMetadataEndpointsInner (from :5272). The umbrella's swap is at :5176, so the by-value routeManager is the guarded one, as the ruling binds.
  • Proof (round 0 at 1e0153a68, which stands; round 1 is one docs line that no build or test reads):
    • .d.ts sha256 1f0f455d… on every leg, and 39 exports.
    • The full boot over 20 configs is byte-identical.
    • The inner-registration leg (new for /meta, because the guard wrapper hides handler bodies from the full boot): 688 registrations identical. A one-character control moved exactly GET …/_drafts (36 of 688), while the full boot stayed blind, as predicted.
    • The guard ablation: head answers 401 on all 12 anonymous probes. The ablation answers none, and rest-meta-auth and meta-type-read-capability go red.
    • The late-binding ablation: red.
    • Readers were red before re-point in three stages.
  • Patch round 1 (d6a423356): one anchor in content/docs/permissions/system-context.mdx row 50, declared on [PM seat] domain:devx @ objectstack — ⏳ vacant #6023 (6241845161). check-system-context-census went from exit 1 to exit 0 over 158 anchors. The union is 118 of 118 exit 0.
  • CI on this head: 36 success and 5 skipped (opt-in and full-battery lanes), including Lint & Repo Gates, TypeScript Type Check and Test Core. The earlier head's failure and cancelled rows were the census (fixed) and supersession.
  • Cross-lane declarations: [PM seat] domain:spec — ⏳ vacant #6017 6239822550; [PM seat] domain:devx @ objectstack — ⏳ vacant #6023 6239832477 and 6241845161.
  • Acceptance notes:
    • 13c and 13d must each add their module to system-context.mdx row 50, for the three elevation reads still in the inner method.
    • data-action-routes.ts:97's prose mention of TransportScopedMetaRequest: carrier: none.
    • studio-authoring.json:1397's pre-existing wrong anchor is corrected by this PR.
  • What comes after this sub-slice: under the maintainer's order, as recorded in seat post [PM seat] domain:cli — 🟢 zhuangjianguo · session_01SYEJqDL3NXxrJRBVqaMu9T #6024's scope line (「用中文,当前任务处理完,后续只派小卡」, confirmed as "S-size, any priority"), this seat dispatches no further /meta sub-slices. When this PR lands, [maintainer] refactor(rest): split packages/rest/src/rest-server.ts (13,701 lines, 70 commits in 30 days) along its register*Endpoints seams, one move-only slice per PR, /meta after #22877 stage 2 #23050 is released to pm:queue with the plan, the rulings and the 13b handover from the report.
  • Landing: not governed (0 of 22; 1,442 changed lines ≤ 3000). Every check on this head is green, so the PR goes ready with auto-merge armed in this act's next stroke.

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

Development

Successfully merging this pull request may close these issues.

2 participants