Skip to content

Commit 646303e

Browse files
refactor(rest): move the /meta per-item read routes into meta-item-read-routes.ts (#23050 slice 13b) (#24240)
Part of #23050 Clause-②: no Sub-slice **13b** of the `rest-server.ts` split: the fourth `/meta` group. Group G4 of `RestServer#registerMetadataEndpointsInner` is ONE statement, `if (metadata.endpoints.item !== false)`, of 891 lines (base `rest-server.ts:5336`–`:6226` at `6e43b7bc3`). It moves whole, its switch included and re-indented one level, to a new module, `packages/rest/src/meta-item-read-routes.ts`, as `registerMetaItemReadRoutes`. The group registers four routes per base: the reverse reference sweep (`GET {metaPath}/:type/:name/references`), the layered read (`GET {metaPath}/:type/:name/layers`), the book tree (`GET {metaPath}/book/:name/tree`) and the item read (`GET {metaPath}/:type/:name`). This follows the slicing plan (`os-dev-report` `6239704287`, sub-slice 13b), seat 1's ruling on it `6239783526` (quoted below), static-receiver ruling A `6205442867`, 13a's `handover_13b` list (report `6241822317`) and the claim `6243326550`. PR #24224 (13a, `df000d4b2`) is the shape copied. 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 and `origin/main` vs merge 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; - four tokens of the moved body changed, the ruled receivers (below). `rest-server.ts` goes from 8,699 to 7,733 lines on the merged tree. G5–G8, the umbrella `registerMetadataEndpoints` and its guarded swap, the inner head's bindings, every handler body, `fetchCurrentMetaDocument` and the rest of the class head, `meta-item-read-gate.ts`, `docs/adr/**` and `packages/spec/src/**` are untouched. #23050 stays open for 13c–13e. ## The ruling this follows (seat 1, `6239783526`, verbatim) > - **Q1, A.** The four head bindings (`metadata`, `metaPath`, `isScoped`, `registerPerItemRoute`) reach each module as ONE `scope: MetaRouteScope` parameter, built once in the inner head. Each module destructures only what it uses, and body tokens are unchanged. The host stays class members only. > - `registerPerItemRoute` stays one closure that reads RestServer's guarded `routeManager` at call time. ⛔ No module-local copy: that would be a second copy of a helper (rule 2). > - **Q2, A.** 13a's `meta-collection-routes.ts` hosts `MetaRouteScope` and takes `TransportScopedMetaRequest` from `ui-routes.ts`, re-pointing its readers in that PR. ⛔ Not `meta-item-read-gate.ts` while #24208 is open. > - **Q3, A.** `rest-server-registrar-files.ts` exports a named subset, the `/meta` modules. The dogfood census's reachable count becomes "the umbrella's sites plus every site in that subset", and a control pins the subset against the number of module calls inside the inner method. This is declared at 13a with the anonymous-`401` ablation as the runtime proof. > - From 13c on, a module with no direct register site is controlled on `registerPerItemRoute(` instead. > - ⛔ No naming convention, and ⛔ never "let blindSpot rise". > - **Q4, A.** `STORED_VERSION_DOOR_POLICY`, a `private static readonly` field set once at class evaluation, is handed over by value. 13b and 13d name it in their claims, together with each sub-slice's static-receiver tokens (13 in all, ruling A `6205442867`). > - **Q5, A.** G4 (891 lines, one `if`) moves whole in 13b, with its switch. Slice 1 moved 1,181 lines. The fallback, if the review budget binds, is B: a move-only split called inside the remaining brace. ⛔ C (re-shaping the registrations) is not a move and not this card's. > - **Q6.** A for the two body-only 13b helpers (`notImplementedRefusalAnswer`, `anonymousFormIntakeFingerprint`): they move with the body. B for `refuseNonAuthoringCaller` (13d): it stays and is exported, because its callee `mayReadAuthoringTrail` is a docs anchor (`system-context.mdx:108`). Two-home helpers (`sendMetaItemAbsent`, `stampAnonymousFormIntakeWarnings`) stay and gain `export`, as in slices 1 and 2. > > **Binding on every sub-slice (from the plan's measurements):** > - **Hosts are built inside `registerMetadataEndpointsInner`**, at the position of the statements they replace, with `routeManager` passed by value. ⛔ Never in `registerRoutes`, the constructor, an instance cache or the umbrella above the swap: those capture the real manager, and the moved routes would mount outside the anonymous-deny gate (#3963) and the type-level admissions (#21087, #21124). > - Each sub-slice proves this with the guard ablation in the plan: an anonymous request at every moved route gets `401` on head, and the ablation does not. > - **The proof gains an inner-registration leg.** Slice 12's handler digest is blind to `/meta`, because every route's server-visible handler is the same guard wrapper. So each sub-slice digests the inner handlers through a recording `routeManager` and a direct `registerMetadataEndpointsInner(basePath)` call per base, with the ruled receiver tokens mapped back. A one-character positive control must move exactly that route's inner digest. > - The full-boot legs stay: `getRoutes()`, ordered registrations, `.d.ts`/`.d.cts`, and the 39-name export list. > - The config matrix is slice 12's 13 configs plus the `/meta` switches the plan names. > - **Readers move in the same PR**, loud and silent alike, as the plan lists per sub-slice. Cross-lane edits are declared on #6017 (spec: liveness and gate tables) and #6023 (devx: checklist) before the PR. The `system-context.mdx` rows change with the sub-slice that moves their code. Q5 A is taken: the statement moves whole, and fallback B was not needed. ## The shape **Moved, at its position.** In the inner method, right after 13a's `registerMetaCollectionRoutes.call(collectionHost, scope);`, the head now builds the host literal `itemReadHost` and calls `registerMetaItemReadRoutes.call(itemReadHost, scope)` with the ONE existing `scope` (no second one is built). Registration order is unchanged: after the per-type list, above the item writes, with `/references` and `/layers` still above the item read. **The host, `MetaItemReadRouteHost`: 16 members in first-read order** (a symbol-resolved walk of G4 with the TypeScript 6.0.3 compiler API, on base and head; H1 of the dispatch holds: G4 opened at `:5345`, its range ran `:5336`–`:6226`): | member | reads | handed over as | |---|---:|---| | `routeManager` | 4 (all `register`, registration time) | **by value, captured inside the inner method** | | `resolveProtocol` | 4 | closure `(...a) => this.resolveProtocol(...a)` | | `metaTypeSingular` (private static) | 2 | closure `(...a) => RestServer.metaTypeSingular(...a)`, typed `(typeof RestServer)['metaTypeSingular']` | | `resolveObjectMasker` | 2 | closure | | `serveMetaItemLayered` | 2 | closure | | `metaReadAudienceSources` | 1 | closure | | `extractLocale` | 2 | closure | | `resolveExecCtx` | 1 | closure, spelled exactly as the execctx census's forwarder | | `resolveI18nService` | 1 | closure | | `metaItemReadGate` | 1 | closure | | `maskObjectDocument` | 1 | closure | | `anonymousFormIntakeWarnings` | 1 | closure | | `translateMetaEnvelope` | 2 | closure | | `STORED_VERSION_DOOR_POLICY` (private static readonly field) | 1 | **by value** (Q4 A), typed `(typeof RestServer)['STORED_VERSION_DOOR_POLICY']` | | `metaItemAnswerSources` | 1 | closure | | `sendMetaReadRefusal` (private static) | 1 | closure `(...a) => RestServer.sendMetaReadRefusal(...a)` | The body has no bare `this`, no `this[...]`, no `function` keyword and no assignment to a `this` member. It reads no instance FIELD at request time, so no member is a getter. `STORED_VERSION_DOOR_POLICY` is the first static FIELD any host carries. It is assigned once, at class evaluation, from `meta-item-read-gate.ts`' constant of the same name, and never reassigned, so the value captured is the object every read of it returned. **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).** The module opens with `const { metadata, metaPath, isScoped } = scope;`, the three bindings it reads: `metadata` for the switch and `enableCache`, `metaPath`, `isScoped`. `registerPerItemRoute` is not called by this module. **The four changed tokens (ruling A, named in the claim).** At base `rest-server.ts` `:5474` and `:5623`, the layered read and the item read each call `RestServer.metaTypeSingular(req.params.type)`. In the item read's stored-version branch, `:6128` reads `RestServer.STORED_VERSION_DOOR_POLICY` and `:6136` calls `RestServer.sendMetaReadRefusal(res, answer.refusal)`. Each is now `this.…` through the host (module `:321`, `:470`, `:975`, `:983`). Neither static method reads anything but its arguments, so the receiver is the whole difference. **Module-level names** (the same walk, base vs head): - **Moved with the body (Q6 A):** `notImplementedRefusalAnswer` (base `:1560`–`:1673`, with its 106-line docblock) and `anonymousFormIntakeFingerprint` (base `:1969`–`:1972`). They are byte-identical at the module's end, each under a pointer comment, and a pointer comment is left at each old position. No other file names either one. - **Gained `export` (Q6, two homes):** `sendMetaItemAbsent` (also read by the class's `sendMetaReadRefusal` and the `/diff` door) and `stampAnonymousFormIntakeWarnings` (also read by the class head). The module imports both from `./rest-server.js`. - **Already exported, imported from `./rest-server.js`:** `META_ITEM_REFERENCES_PARAMS` (the meta door tests import it from there), `metaItemPackageBinding` and `mayReadPendingDrafts`. All five names are read only at request time, so the two modules importing each other cannot see one uninitialised. - **Imports that left `rest-server.ts`** (read only by the moved code, so `noUnusedLocals` would refuse them; a pointer comment is left at each): `foldVisibilityFingerprintIntoEtag`, `normalizeIfNoneMatch`, `resolveObjectSchemaRuntimeView` and `objectFieldVisibilityFingerprint` (`@objectstack/metadata-core`), `GetMetaItemCachedRequest` (`@objectstack/spec/api`), the local `META_ITEM_LAYERS_PARAMS` import (`./meta-item-read-gate.js`; the re-export that names it stays), and `boundedDeclaredRefusalMessage`, `declaredHttpStatus` and `thrownAnswerIsBareNotFound` (`./error-response.js`). The plan listed six of these nine. The other three left because the two helpers moved: `objectFieldVisibilityFingerprint` is read by the item read and by `anonymousFormIntakeFingerprint`, and the two `error-response.js` names only by `notImplementedRefusalAnswer`. - **Unchanged provenance:** every other name is imported from the module `rest-server.ts` imports it from. `MetaRouteScope` and `TransportScopedMetaRequest` come from `./meta-collection-routes.js`. ## Proof All runs went through `os-verify-lock`, with every exit captured before any pipe, unless noted. The base is `6e43b7bc3`. The head is `7b562222d`, and the merge head `083b60f20` (one merge of `origin/main` `2bcd406a2`) was measured against `origin/main` itself. Base and main were measured in a read-only comparison worktree at each commit. **1. The moved text.** `git show 6e43b7b:packages/rest/src/rest-server.ts | sed -n 5336,6226p`, with one 4-space indent removed, was compared with `meta-item-read-routes.ts` `:183`–`:1073`, the body after the destructuring line. - The raw `diff` exits 1 on exactly four lines: the four receiver tokens. - With `this.(metaTypeSingular|STORED_VERSION_DOOR_POLICY|sendMetaReadRefusal)` mapped back to `RestServer.…`, `diff` exits 0 over all 891 lines. Both sides are sha256 `c9ac1ae545c47bc93ab05e8aa18f3b6493eea7afafa84f6b480c460e3de3327d`. - Every non-empty line of the range carries the 8-space indent, no line has trailing whitespace, and every odd-backtick line is a comment. - The two helpers: base `:1560`–`:1673` vs module `:1079`–`:1192` (114 lines) and base `:1969`–`:1972` vs module `:1196`–`:1199`, `diff` exit 0 on both. **2. `.d.ts` and exports.** `index.d.ts` and `index.d.cts` are sha256 `1f0f455d24836d47de60716233f1bd74d1738c04046974019264b17b5261bbe8` at base and at head. At `origin/main` `2bcd406a2` and at the merge head they are both `caa84a36073f1dd5a79f66d3bc71b877e3a60806d1f380d39b310987fb15d255` (main's #24223 edited a class-head docblock). `cmp` exits 0 on every pair. - The head-only marker `registerMetaItemReadRoutes` hits 2 in each of `index.js` / `index.cjs`, and 0 at base and at main. - 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, then the `/meta` switches 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, then 146 / 148 / 126 / 144 / 114 / 150 / 112 (13a's figures). - **Source leg** (a `tsx` boot of `src/rest-server.ts`): `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: 60 of 60 files, base vs head and main vs merge head. The concatenation is sha256 `bd4f6685175a61ec…` on all four trees. - **Bundle leg** (the same dump against the built `dist/index.js`): 60 of 60 identical, concatenation `20103136b32ff534…` on all four, under one declared normalization: esbuild's numeric alias suffix on an import name two bundled modules share. Measured by an identifier-set diff of the two bundles, the head gains exactly `itemReadHost`, `registerMetaItemReadRoutes` and three renumbered aliases (`rethrowAuthzStoreUnavailable4`, `ObjectSchemaMaskEvaluationError3`, `relateObjectSchemaMaskPosture3`), and loses none. Those three names are the ones normalized. **4. The inner-registration leg (the plan's `proof_method`).** Each config builds a `RestServer` with a recording `routeManager` and calls `registerMetadataEndpointsInner(basePath)` directly for `/api/v1` and `/api/v1/environments/:environmentId`: 688 recorded registrations over the 20 configs. Each is digested whitespace-free, with the four receiver tokens mapped back, together with its method, path, metadata and arity. - **Source leg:** 688 of 688 identical, base vs head and main vs merge head; the digest-list sha256 is `4f306a2a19ffdd181b36b43e4d8b49d0844dc396c50a3144e8221aeab48b4511` on all four. Unnormalized, exactly 72 digests differ: the `/layers` and item routes on both bases in the 18 configs that mount them, the receiver tokens. This leg runs through `tsx`, whose per-file transform keeps import names as written, so 13a's vitest SSR renumbering does not arise and is not normalized. - **Bundle leg:** 688 of 688 identical under the same three alias names; the digest-list sha256 is `8c47816fb3991d34bf792d04aab002f8e2c945de2e736ec93a93502c27ca3b7b` on all four. - **Positive control.** One character was added in the moved book-tree handler through `scripts/ablation-replace.mjs`: `META_QUERY_ARITY_DOORS['book-tree']` became `['book-tree!']` (anchor 1 → 0, blob `5f398b048095` → `5b5bea5dcfbd`). The inner digests moved at exactly `GET …/book/:name/tree`, 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: the blind spot the inner leg covers. Restored: the blob equals `HEAD` and `git diff HEAD` is empty. **5. The guard ablation** (13a's recipe). One literal multi-line anchor ran from the umbrella's `const realRouteManager = this.routeManager;` through `itemReadHost`'s `routeManager: this.routeManager,` (blob `8f0876b93743` → `f2e20e0ca141`). The mutation stashes the real manager above the swap and has `itemReadHost` read it: the plan's wrong placement. - **Head** (identical to base): an anonymous request at each moved route on both bases. `/references`, `/layers` and the item read answer `401 UNAUTHENTICATED` with zero protocol calls (6 of 6). The book tree is the declared anonymous public-audience read (#3963): the umbrella lets it through to the handler's own audience gate, which answers `401` "This documentation requires sign-in". All 8 server-visible handlers are the umbrella's wrapper. - **Ablation:** `/references` answers `200` and the item read `200`, each after a protocol read; `/layers` answers `404 RESOURCE_NOT_FOUND` after a protocol read. Not one of the six is `401`, and none of the 8 handlers is the wrapper. - **Standing pins under the ablation:** `Test Files 1 failed | 2 passed (3)`, `Tests 19 failed | 81 passed (100)`. `meta-type-read-capability.test.ts` is red 19 times (the `/:type/:name/layers` shape and the item read's shapes for the datasource family × member / author / other-grant, the existing-vs-absent byte identity, and the admin-door parity for three bearers). `rest-meta-auth.test.ts` stays green (its probes are routes 13b does not move), and `meta-type-write-capability.test.ts` stays green (no moved non-GET door). - Restored: the blob equals `HEAD` and `git diff HEAD` is empty. No permanent test was added. **6. The late-binding ablation** (one-off script, deleted). The 12 methods are planted on the server and the 2 static methods on the class before `registerRoutes()`, as recorders that delegate to the originals. Ten request shapes are driven over the four moved routes (`?state=draft`, `?layers=true`, a view, an app, a doc and a masked object posture among them). Then everything is planted again and the shapes are driven again. - **Head** (byte-identical `MEASURE` lines to base): all 14 closure members are reached before and after the re-plant, with `early calls after re-plant: 0`. - **Ablation:** `metaItemReadGate: this.metaItemReadGate.bind(this)` (blob `8f0876b93743` → `85cc12e79126`) gives `early calls after re-plant: 3 ["early:metaItemReadGate"]` and 13 late members, exit 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-item-read-routes.ts` in `REST_SERVER_REGISTRAR_FILES` (length 14 → 15) and in the named `/meta` subset `REST_SERVER_META_ROUTE_FILES` (1 → 2). H2 holds: the dogfood control counts `.call(` inside the inner method, now 2, so the subset and the call moved together. | reader | sum over the list | change | |---|---|---| | `execctx-consumer-census.test.ts` | 62 sites = 12 + 9 + 5 + 7 + 8 + 1 + 5 + 2 + 1 + 1 + 1 + 0 + 6 + 3 + 1; mentions 133 → 137 | The item read's site is locally caught on its continuation line, as before, so the split stays 16 / 46, and §7's and §8's four continuation sites are found again. `rest-server.ts` stays at 55 mentions (the site and one prose mention out, one forwarder's two in); the module carries 4. Fifteen forwarders: (15 − 1) + 3 − 2 | | `meta-draft-read-door-census.test.ts` | 8 = 1 + 0 + 0 + 0 + 0 + 2 + 0 ×7 + 3 + 2 | The item read's `?state=draft` and `?preview=draft` moved. Keys name routes, so no ledger row changed | | `rest-server-query-number-census.test.ts` | 9 = 8 + 1 + 0 ×13 | — | | `rest-server-canonical-query-ast.test.ts` (`repo`) | 4 = 0 + 3 + 0 ×10 + 1 + 0 + 0 | Floor `meta-item-read-routes.ts: 0` | | `rest-server-docblock-position.test.ts` | floors sum 54 → 59 | The module carries 5: the header, the host interface, the function and the two moved helpers' blocks. `rest-server.ts` 163 → 161 blocks, floor 100 | | `ui-view-route-identity` / `-tenancy` measurement tests | `this.enforceAuth(` 60, `this.resolveExecCtx(` 76 → 77 | +1 for the forwarder. The ownership guard is still 1 call site and 1 forwarder | | dogfood `authz-conformance.test.ts` | 0 tripwire hits over fifteen files | Length 14 → 15 | | dogfood `authz-probe-blind-spot.census.ts` | 70 / 19 / 51 unchanged | Reachable holds by 13a's Q3 rule, with no rule change. New control `meta-item-read-routes.ts: this.routeManager.register(` 4; `/meta subset ↔ .call(` 2. `this.routeManager.register(` stays 63 = 2 + 9 + 5 + 7 + 8 + 1 + 6 + 4 + 1 + 1 + 1 + 2 + 6 + 6 + 4; `registerPerItemRoute(` 8 and `enforceAuth` 112 unchanged | Readers outside the list: - **`scripts/check-route-envelope.mjs`** (`MODULES` only; declared on #6017, `6243336904`). `meta-item-read-routes.ts` is declared `dialectOnly` with ratchet #9559 at `stringError 1` / `siblingCode 1`: the `/layers` door's 501 `{ error, code }` body. `rest-server.ts` goes 8 → 7 and 4 → 3, each with a "NOT progress — the same MOVE" note. The sums are unchanged at 39 and 54. Output: 25 → 26 modules audited, 15 → 16 ratcheted; `--self-test` exit 0. - **`packages/runtime/src/domains/meta-list-projection-parity.test.ts`** (a loud cross-package reader). ⚠️ **The plan's 13b reader list missed it** (H3): 13a left its item, tree and layers derivations reading `rest-server.ts`, and at the move commit it reads `GET ${metaPath}/:type/:name in …/rest-server.ts: expected false to be true`. The route derivation now finds the handlers in `meta-item-read-routes.ts` and still reads the class method `serveMetaItemLayered` in `rest-server.ts`. Outside the declared list, so it is named here for the seat. `check:cross-package-test-inputs` exits 0. - **`packages/spec/liveness/metadata_endpoints.json`** (declared on #6017). `enableCache` now cites `meta-item-read-routes.ts#registerMetaItemReadRoutes`. `endpoints.item` splits across two files, as the declaration says: the `if` half cites the module, and the `registerPerItemRoute` half keeps `rest-server.ts#registerMetadataEndpointsInner`. `prefix` stays. - **`packages/spec/liveness/doc.json`** and **`book.json`** (declared on #6017). In `doc.json`, `content.evidence` gains the module for the single-item branch, and `_note` names it for the book-tree and single-item branches. The `doc` list branch keeps its `rest-server.ts#registerMetadataEndpointsInner` citation and its quote: the quote is already stale (that code lives in `meta-item-read-gate.ts`), and that is the spec seat's to repair, as routed, so it is not edited here. In `book.json`, `_note` names the module for the `book/:name/tree` branch. - `check:liveness`: `947 pointer(s) … 947 naming a symbol` (945 before) and `737 … pair(s) asked, 736 anchored, 1 exempt` (735 / 734 / 1 before). Measured at the move commit, before these edits, it stayed green: these readers are silent. - **`docs/qa/platform-checklist/areas/`** (declared on #20163, `6243347725`): - `api-backend.rest-metadata-config-contract`: `+ packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes (the item endpoint gate on the per-item reads)` is added beside the inner anchor. The inner anchor's parenthetical goes from "metaPath, the item endpoint gate and registerPerItemRoute" to "metaPath and registerPerItemRoute", because the `if` moved (13a made the same kind of edit). - `platform-core` (the book tree): `rest-server.ts#metaPath` → `meta-item-read-routes.ts#registerMetaItemReadRoutes`. The text after the anchor is unchanged. `metaPath` still resolves in the inner head, so leaving it would have stayed silently wrong. - `check:platform-checklist` exit 0; `node scripts/check-platform-checklist.mjs --changed-from origin/main` exit 0, `12 anchor(s) judged, 0 lost`. - **`scripts/adr-anchors/packages__rest__src__meta-item-read-routes.ts.json`** (NEW; outside the declared list, named for the seat). `check-adr-anchors` checks `ADR-NNNN Dk` letters only in anchored files. The move took G4's letter citations out of the anchored `rest-server.ts`, and the gate read 1776 → 1760 while green: a reader that narrowed silently, the shape card rule 3 forbids. Slices 4, 6, 7 and 11 met this with an anchor, and this does the same. It anchors ADR-0106 and ADR-0046, its invariant adds no letter citation of its own, and the reading is back to 1776. - **`scripts/adr-anchors/packages__rest__src__meta-collection-routes.ts.json`** (NEW, its own commit `7b562222d`, an in-place repair of 13a's same narrowing). Measured on 13a's own two commits, `b9ac35cbf` → `df000d4b2` moved the reading 1783 → 1776 with the gate green. This anchors ADR-0131 and ADR-0106, its invariant adds no letter citation, and the reading becomes 1783. It is the same defect class, the same mechanical shape, a new file no claim holds, and the same gate. Dropping the commit restores 1776 and nothing else. **Red before the re-points**, each measured with the readers at their base text: - **At the move commit `6fa5946df`:** - `local` read `Test Files 2 failed | 292 passed (294)`, `Tests 6 failed | 6021 passed | 326 skipped (6353)`. The execctx census failed four times: `expected 61 to be 62`, `expected 15 to be 16`, and §7 and §8 `expected 3 to be greater than or equal to 4` (a continuation site out of reach). The draft census failed twice (`expected [ …(6) ] to deeply equal [ …(8) ]`). - The dogfood pair read `Tests 2 failed | 106 passed (108)`: `expected 66 to be 70`, and `this.routeManager.register(` `expected 59 to be 63`. - The runtime parity test read `Tests 1 failed | 738 passed (739)`. - `check:route-envelope` exited 1: `meta-item-read-routes.ts NOT DECLARED` (`MODULES` and the express table), `stringError: found 7, declared 8`, `siblingCode: found 3, declared 4`. - `check-adr-anchors` stayed green at 1760 (base 1776), `check:liveness` and `check:platform-checklist` stayed green, and `repo` stayed `5 passed` (G4 carries no `query:` slot). Those are the silent readers. - **At `8a8a105cb`** (the list grown, the readers not yet re-pointed): - The six list readers read `Test Files 6 failed (6)`, `Tests 7 failed | 119 passed (126)`: the length pins and the unfloored module. - `repo` read `Tests 2 failed | 85 passed | 15 skipped (102)`. - The dogfood pair read `Tests 2 failed | 106 passed (108)`: `control "registrar list length" … expected 15 to be 14` and the tripwire length. - **Green after the re-points** (`5cb0f9c9b`, then the merge head). ## Not done as planned, measured **`content/docs/permissions/system-context.mdx` row 50 is NOT edited.** The plan, the claim and the #20163 declaration listed a row-50 anchor for the module (the GET item `?state=` stored-version read door). Measured on this tree, `check-system-context-census` refuses it: `[anchor-is-not-a-read-site] the page anchors packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes, which the census does not call an elevation read and NON_READ_ANCHORS does not declare` (exit 1). G4 holds no `isSystem` read: H4 holds, and the census output is identical base vs head (`142 elevation read sites … 135 symbol(s) against 135 required, over 158 anchors`). Row 50's stored-version prose is carried by its `rest-server.ts#metaSaveVerdict` anchor, where the read lives. Adding the anchor would need a new `NON_READ_ANCHORS` ledger row in the gate script, which this move does not owe, so the page is untouched. 13c and 13d still owe their row-50 anchors, because G5's DELETE and G6's publish and rollback do hold reads. ## Changesets - `.changeset/23050-s13b-meta-item-read-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-s13b-meta-item-read-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 - The plan's 13b reader list missed `meta-list-projection-parity.test.ts`, and its row-50 item does not apply to G4 (both above). Its import list named six of the nine imports that left. - `docs/qa/platform-checklist/FOLLOW-UPS.md:613` (E1) is a closed, dated entry that still says the inner method holds "four `endpoints.item` gates". It is prose, read by no gate, and optional per the plan, so it is not edited. - The moved comments keep their deixis: "this block's closing brace" and "below" in the item read's leading comment describe the inner method's order. The module header says so rather than editing moved text. - 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 `083b60f20`, one merge of `origin/main` `2bcd406a2` (#24223, #24229). Of those, only #24223 touches a file near this diff: `rest-server.ts`' class-head `fetchCurrentMetaDocument`, outside G4, and it merged cleanly. After the merge, the four packages main touched were rebuilt. `origin/main` has since moved to `7be3cc728` (5 commits), none of whose 34 paths is one of this diff's 23, so it was not merged again; PR CI runs the merge ref. - **Build.** At base, `pnpm turbo run build --filter=@objectstack/rest... --filter=@objectstack/dogfood... --filter=@objectstack/runtime... --concurrency=2` exited 0 (`Tasks: 66 successful, 66 total`). At the merge head, `pnpm --workspace-concurrency=2 --filter @objectstack/metadata-protocol --filter @objectstack/plugin-security --filter @objectstack/runtime --filter @objectstack/rest build` exited 0. Then a whole-workspace `pnpm turbo run build --concurrency=2` exited 0 (`Tasks: 77 successful, 77 total`), for the dist readers. - **Typecheck.** `pnpm --filter @objectstack/rest typecheck` exits 0 (`check:test-typecheck: OK … 0 file(s) / 0 error(s)`). `pnpm --filter @objectstack/spec typecheck` exits 0. - **`@objectstack/rest`.** `local`: `Test Files 294 passed (294)`, `Tests 6030 passed | 326 skipped (6356)`. 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 266 passed | 15 skipped (281)`. - **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. - **`@objectstack/spec` local** (the liveness suites among them): `Test Files 691 passed (691)`, `Tests 20974 passed | 1 todo (20975)`. - **`test:repo`.** `@objectstack/runtime`: `Test Files 3 passed (3)`, `Tests 808 passed (808)`, `meta-list-projection-parity.test.ts` among them. `@objectstack/core`: `Test Files 6 passed (6)`, `Tests 86 passed (86)`. - **Lint.** `pnpm exec eslint --no-inline-config` over the 14 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 `083b60f20` derives 95 commands; its stderr names the commit, and the `--repo` assertion holds. The change set is 23 paths vs merge base `2bcd406a2`, 2,524 changed lines (+1,456 / −1,068), under 3,000. - All 95 exit 0. `pnpm check:dual-build-cjs-loads` first answered exit 3 (no whole-workspace `dist`). After the whole-workspace build it was re-run with the five other dist readers (`check:dts-closure`, `check:entry-guard`, `check:lean-entry-closure`, `check:published-files`, `check:sourcemap-no-sources-content`), all exit 0, and those exits are the ones recorded. - `--ran`: `Run reconciliation — 95 derived, 95 run, 0 NOT-MEASURED, 0 UNRUN.`, exit 0. It also printed a STALE TREE note: `origin/main` moved after the merge and changed `scripts/check-position-name-fold-loaders.mjs`, a family this diff does not touch. CI runs it on the merge ref. - Selected readings: `check:route-envelope` exit 0 (`26 route module(s) audited: 9 conformant, 16 ratcheted, 1 exempt`); `check:adr-anchors` exit 0 (`72 anchored file(s)`, `1783 decision-letter citation(s)`); `check:liveness` exit 0; `check:platform-checklist` exit 0; `node scripts/check-system-context-census.mjs` exit 0 (`over 158 anchors`, identical to base); `check:cross-package-test-inputs` exit 0; `check:pm-dispatch-gates` exit 0 (`1884 cases`); `check:type-check-debt` exit 0; `check:nul-bytes` exit 0. - By-path scan gates diffed base vs head: `check-meta-type-normalized`, `check-dispatcher-error-vocabulary`, `check-error-status-conformance` and `check-undeclared-dep-imports` differ only by the new file (and the latter by +3 specifiers). `check-rest-log-declared`, `check-durability-degradation-log-level`, `check-single-authz-resolver` and `check-system-context-census` are identical. - **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.** `check-governed-merges --test` over the 23 paths: `0 of 23 path(s) hit the register`, and the size is under the human-merge threshold. Commits: - `6fa5946df`, the move; - `8a8a105cb`, the list and the `/meta` subset; - `5cb0f9c9b`, the re-points, with the module's ADR anchor; - `d00b152fe`, the changesets; - `7b562222d`, 13a's module ADR anchor (in-place repair); - `083b60f20`, the merge. --- _Generated by [Claude Code](https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 03ac4ec commit 646303e

23 files changed

Lines changed: 1456 additions & 1068 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
The liveness ledger's `/meta` cache-flag, per-item switch, doc and book rows cite the code that reads their keys where it now lives
6+
7+
Clause-②: no (only the rows' evidence paths and notes move and every status stays `live`: `MetadataEndpointsConfigSchema`, `DocSchema` and `BookSchema` accept exactly what they accepted before, and no state count moves)
8+
9+
- `liveness/metadata_endpoints.json`: the `enableCache` row and the `if (metadata.endpoints.item !== false)` half of the `endpoints.item` row cited `packages/rest/src/rest-server.ts#registerMetadataEndpointsInner`. `@objectstack/rest` moved the per-item reads to `packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes` with no behaviour change, so those references follow them. The `registerPerItemRoute` half of the `endpoints.item` row keeps its citation: that call still lives in the inner method's head.
10+
- `liveness/doc.json`: the `content` row's single-item branch, and the `_note`'s book-tree and single-item branches, cite the new module. The `doc` list branch keeps its citation.
11+
- `liveness/book.json`: the `_note` names the new module for the `book/:name/tree` branch.
12+
- The quoted code, the producers, the verdicts and the row notes are unchanged.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/rest': patch
3+
---
4+
5+
The `/meta` per-item read routes live in their own module, with no route, handler or answer changed
6+
7+
Clause-②: no (a move: the four routes per base, their place in the `/meta` registration order, every handler, every response and the anonymous-deny gate in front of them are unchanged, and the published declarations are byte-identical)
8+
9+
- `RestServer#registerMetadataEndpointsInner` keeps its name, docblock, place and head. The statement that registers `GET {metaPath}/:type/:name/references`, `GET {metaPath}/:type/:name/layers`, `GET {metaPath}/book/:name/tree` and `GET {metaPath}/:type/:name` moved, re-indented, to `packages/rest/src/meta-item-read-routes.ts`, and the method calls it at its old position, inside the umbrella's guarded registration. The `metadata.endpoints.item` switch gates the same mounts.
10+
- Nothing to do for a consumer: no export of the package entry, no route, no configuration key and no response moved.

‎docs/qa/platform-checklist/areas/api-backend.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2446,7 +2446,7 @@
24462446
"source": [
24472447
"packages/spec/liveness/metadata_endpoints.json (the ADR-0049 ledger this item answers: prefix, enableCache, maskObjectFields and three endpoints.* live, cacheTtl and endpoints.schema dead)",
24482448
"packages/spec/src/api/rest-server.zod.ts#MetadataEndpointsConfigSchema (the declared contract — four endpoints.* switches whose describe() strings enumerate every mount they gate since #15542/#15854, the ADR-0106 D8 docblock and the two retiredKey tombstones)",
2449-
"packages/rest/src/rest-server.ts#registerMetadataEndpointsInner (metaPath, the item endpoint gate and registerPerItemRoute — the per-item switch spelled as a call so the later members of that face carry it), (resolveObjectMasker — the metaType/maskObjectFields fork), (normalizeConfig — isObjectSchemaMaskingEnabled and the OS_ALLOW_UNMASKED_OBJECT_METADATA escape hatch), (getRoutes) + packages/rest/src/meta-collection-routes.ts#registerMetaCollectionRoutes (the types, items and maintenance endpoint gates) + packages/rest/src/discovery-routes.ts#registerDiscoveryEndpoints (discovery.routes.metadata built from prefix)",
2449+
"packages/rest/src/rest-server.ts#registerMetadataEndpointsInner (metaPath and registerPerItemRoute — the per-item switch spelled as a call so the later members of that face carry it), (resolveObjectMasker — the metaType/maskObjectFields fork), (normalizeConfig — isObjectSchemaMaskingEnabled and the OS_ALLOW_UNMASKED_OBJECT_METADATA escape hatch), (getRoutes) + packages/rest/src/meta-collection-routes.ts#registerMetaCollectionRoutes (the types, items and maintenance endpoint gates) + packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes (the item endpoint gate on the per-item reads) + packages/rest/src/discovery-routes.ts#registerDiscoveryEndpoints (discovery.routes.metadata built from prefix)",
24502450
"ADR-0106 D8 (per-caller field masking of served object schemas) · #14691 (the two retirements) · #7526 (the /meta/types ordering fix the `types` switch takes with it) · ADR-0076 D12 · #15542 / #15854 (the switch-radius ruling that created `maintenance` and gave `item` its own writes) · #15543 (why the measured blast radius of that move is zero: no shipped boot path authors a RestServerConfig)",
24512451
"sibling clause: platform-core.metadata-registry-serving (it drives the served REGISTRY on the default config; this item covers the config that decides what is served and to whom) · platform-core.docs-audience-gate (the audience-gated types that are a cache carve-out here)"
24522452
],

‎docs/qa/platform-checklist/areas/platform-core.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,7 @@
12631263
"ADR-0090 (audience vocabulary)",
12641264
"ADR-0049 (fail closed — unresolvable holdings deny)",
12651265
"packages/spec/src/system/book.zod.ts#audienceAllows — audienceAllows / resolveDocAudiences / docAudienceAllows / resolveBookClaimedDocs / deriveImplicitPackageBook / resolveBookTree (UNCATEGORIZED_KEY exclusion)",
1266-
"packages/rest/src/rest-server.ts#metaPath — GET <metaPath>/book/:name/tree (audience gate, per-entry filter, resolveDocLocale, refuseRepeatedQueryParams)",
1266+
"packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes — GET <metaPath>/book/:name/tree (audience gate, per-entry filter, resolveDocLocale, refuseRepeatedQueryParams)",
12671267
"#2774 (feat(security)!: enforce book audience at the REST read layer)",
12681268
"packages/spec/liveness/book.json + doc.json (the ledgers whose coverage.json waivers this item retires)"
12691269
],

‎packages/qa/dogfood/test/authz-conformance.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,8 @@ describe('#9084 — the rest-server.ts transport tripwire keys off mechanics', (
16351635
// [#23050 slice 11] Length 11 → 12 (`form-routes.ts`), still 0 over it.
16361636
// [#23050 slice 12] Length 12 → 13 (`crud-routes.ts`), still 0 over it.
16371637
// [#23050 slice 13a] Length 13 → 14 (`meta-collection-routes.ts`), still 0 over it.
1638-
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(14);
1638+
// [#23050 slice 13b] Length 14 → 15 (`meta-item-read-routes.ts`), still 0 over it.
1639+
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(15);
16391640
expect(REST_SERVER_REGISTRAR_PATHS[0]).toBe(REST_SERVER);
16401641
const listed = REST_SERVER_REGISTRAR_PATHS.map((file) => readFileSync(join(REPO_ROOT, file), 'utf8'));
16411642
for (const probe of [MECHANICS, ROUTES]) {

‎packages/qa/dogfood/test/authz-probe-blind-spot.census.ts‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -540,16 +540,18 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
540540
'module on the registrar list (`packages/rest/src/rest-server-registrar-files.ts`) — ' +
541541
'`data-action-routes.ts`, `batch-routes.ts`, `approvals-routes.ts`, `sharing-routes.ts`, ' +
542542
'`analytics-routes.ts`, `security-routes.ts`, `discovery-routes.ts`, `email-routes.ts`, ' +
543-
'`search-routes.ts`, `ui-routes.ts`, `form-routes.ts`, `crud-routes.ts` and `meta-collection-routes.ts`, ' +
543+
'`search-routes.ts`, `ui-routes.ts`, `form-routes.ts`, `crud-routes.ts`, `meta-collection-routes.ts` and ' +
544+
'`meta-item-read-routes.ts`, ' +
544545
'the modules the bodies of ' +
545546
'`registerDataActionEndpoints`, `registerBatchEndpoints`, `registerApprovalsEndpoints`, ' +
546547
'`registerSharingEndpoints`, `registerSharingRuleEndpoints`, `registerAnalyticsEndpoints`, ' +
547548
'`registerSecurityExplainEndpoints`, `registerSecurityEndpoints`, `registerDiscoveryEndpoints`, ' +
548549
'`registerOpenApiEndpoints`, `registerEmailEndpoints`, `registerSearchEndpoints`, ' +
549-
'`registerUiEndpoints`, `registerFormEndpoints` and `registerCrudEndpoints`, and the first three `/meta` ' +
550+
'`registerUiEndpoints`, `registerFormEndpoints` and `registerCrudEndpoints`, and the first four `/meta` ' +
550551
'groups of `registerMetadataEndpointsInner`, moved to; ' +
551552
'reachable = those inside registerMetadataEndpoints, PLUS every site in the registrar list\'s named `/meta` ' +
552-
'subset (`REST_SERVER_META_ROUTE_FILES`: `meta-collection-routes.ts`), whose registrations the inner method ' +
553+
'subset (`REST_SERVER_META_ROUTE_FILES`: `meta-collection-routes.ts` and `meta-item-read-routes.ts`), whose ' +
554+
'registrations the inner method ' +
553555
'runs inside the umbrella\'s swap',
554556
// [#15542 / #15854] ⭐ THE POPULATION RULE LEARNED A SECOND SPELLING, and
555557
// the numbers it produces did NOT move: 80 / 19 / 61, exactly as before.
@@ -849,10 +851,25 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
849851
// `private register*Endpoints(` stays 16, `registerPerItemRoute(` stays 8 and
850852
// `enforceAuth` stays 112: the moved groups spell neither term, and the seam
851853
// spells neither (the umbrella's wrapper is what gates them).
854+
// [#23050 slice 13b] 70 / 19 / 51 UNCHANGED, and `reachable` holds by the rule
855+
// 13a taught, with no rule change: the fourth `/meta` group, the per-item reads
856+
// (four direct registration sites: `/references`, `/layers`, the book tree and
857+
// the item read, all REACHABLE through the same umbrella key), moved to
858+
// `meta-item-read-routes.ts`, which joins the list (length 14 -> 15) and the
859+
// named `/meta` subset (1 -> 2) with its own control. The inner method builds
860+
// its host literal inside the umbrella's swap and calls the module at the
861+
// group's old position, so the subset's length and the module calls inside the
862+
// inner method move together (both 2). Measured at the move commit, before the
863+
// list learned the module: population 66 and `this.routeManager.register(` 59
864+
// over fourteen files, the four sites out of reach. `this.routeManager.register(` stays 63
865+
// over the fifteen files (2 here + 9 + 5 + 7 + 8 + 1 + 6 + 4 + 1 + 1 + 1 + 2 +
866+
// 6 + 6 + 4), `private register*Endpoints(` stays 16, `registerPerItemRoute(`
867+
// stays 8 and `enforceAuth` stays 112: the moved group spells neither term,
868+
// and neither does the seam.
852869
controls: {
853870
'private register*Endpoints(': 16,
854871
'this.routeManager.register(': 63,
855-
'registrar list length': 14,
872+
'registrar list length': 15,
856873
'data-action-routes.ts: this.routeManager.register(': 9,
857874
'batch-routes.ts: this.routeManager.register(': 5,
858875
'approvals-routes.ts: this.routeManager.register(': 7,
@@ -866,6 +883,7 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
866883
'form-routes.ts: this.routeManager.register(': 2,
867884
'crud-routes.ts: this.routeManager.register(': 6,
868885
'meta-collection-routes.ts: this.routeManager.register(': 6,
886+
'meta-item-read-routes.ts: this.routeManager.register(': 4,
869887
// [#23050 slice 13a] Recorded AS the subset's length, not as a literal: the
870888
// measured side is the module calls inside the inner method, so the two
871889
// can only move together.

‎packages/rest/src/execctx-consumer-census.test.ts‎

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ const SOURCE = readFileSync(resolve(HERE, 'rest-server.ts'), 'utf8');
124124
* `registerMetadataEndpointsInner`' body to `meta-collection-routes.ts`, with
125125
* their three: the `_drafts` and `_migrate-stored` doors' and the per-type
126126
* list's, each locally caught (the list's on its continuation line), as before.
127+
*
128+
* [#23050 slice 13b] …and the fourth, the per-item reads, to
129+
* `meta-item-read-routes.ts`, with its one: the item read's, locally caught on
130+
* its continuation line, as before — one of §7's and §8's four continuation
131+
* sites, which those sections find there because they read every file here.
127132
*/
128133
const SITE_FILES = REST_SERVER_REGISTRAR_FILES;
129134
const SOURCES = new Map<string, string>(SITE_FILES.map((file) => [
@@ -139,9 +144,9 @@ const siteKey = (file: string, line: number) => `${file}:${line}`;
139144
* `registerSecurityExplainEndpoints`, `registerSecurityEndpoints`,
140145
* `registerOpenApiEndpoints`, `registerEmailEndpoints`,
141146
* `registerSearchEndpoints`, `registerUiEndpoints`,
142-
* `registerCrudEndpoints`) hands its moved body — and the one
143-
* `registerMetadataEndpointsInner` builds for its moved `/meta` groups (#23050
144-
* slice 13a), counted as the moved module's registrar —
147+
* `registerCrudEndpoints`) hands its moved body — and each one
148+
* `registerMetadataEndpointsInner` builds for a moved `/meta` group's module
149+
* (#23050 slices 13a and 13b), counted as that module's registrar —
145150
* forwards each member to the server with `(...a) => this.<member>(...a)`, so
146151
* a moved site's stack shows
147152
* that forwarder above the site itself. The site is the first censused frame
@@ -748,9 +753,22 @@ describe('[#13160] §2 the consumer surface, counted from the tree', () => {
748753
// forwarder's two mentions in). Fourteen forwarders now: (14 - 1) + 3 - 2,
749754
// the module carrying one host literal that reads the seam, so neither map
750755
// gains an entry.
751-
expect(SITE_FILES).toHaveLength(14);
756+
//
757+
// [#23050 slice 13b] 62 sites UNCHANGED / 133 → 137 mentions — the same MOVE.
758+
// The fourth `/meta` group, the per-item reads, left for
759+
// `meta-item-read-routes.ts` with its one LOCALLY CAUGHT site (the item read's,
760+
// on its continuation line, as before):
761+
// 12 + 9 + 5 + 7 + 8 + 1 + 5 + 2 + 1 + 1 + 1 + 0 + 6 + 3 + 1 = 62 sites. The
762+
// body also carries one prose mention, which moved with it. The four new
763+
// mentions are this slice's seam, the two spellings once (the second host
764+
// literal the inner method builds, its forwarder, two; the host interface's
765+
// member, two): `rest-server.ts` stays at 55 (the site and the prose mention
766+
// out, one forwarder's two mentions in). Fifteen forwarders now:
767+
// (15 - 1) + 3 - 2, the module carrying one host literal that reads the
768+
// seam, so neither map gains an entry.
769+
expect(SITE_FILES).toHaveLength(15);
752770
expect(SITES.length).toBe(62);
753-
expect([...SOURCES.values()].reduce((n, text) => n + text.split('resolveExecCtx').length - 1, 0)).toBe(133);
771+
expect([...SOURCES.values()].reduce((n, text) => n + text.split('resolveExecCtx').length - 1, 0)).toBe(137);
754772
const extraMovedRegistrars = Object.values(EXTRA_MOVED_REGISTRARS).reduce((n, k) => n + (k ?? 0), 0);
755773
expect(extraMovedRegistrars).toBe(3);
756774
const registrarsReadingNoExecCtx = Object.values(MOVED_REGISTRARS_READING_NO_EXECCTX).reduce((n, k) => n + (k ?? 0), 0);

‎packages/rest/src/meta-draft-read-door-census.test.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,11 @@ describe('[#20338] census: every draft switch in rest-server.ts is ledgered', ()
268268
// per-type list's `?preview=draft`. A key names its route, never its file,
269269
// so no ledger row changed:
270270
// 8 = 3 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 3, length 13 → 14.
271-
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(14);
271+
// [#23050 slice 13b] The per-item reads moved to `meta-item-read-routes.ts`
272+
// with two more: the item read's `?state=draft` and `?preview=draft`. Keys
273+
// name the route, so again no ledger row changed:
274+
// 8 = 1 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 3 + 2, length 14 → 15.
275+
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(15);
272276
expect(SOURCE_FILES.map((file) => file.fileName)).toEqual([...REST_SERVER_REGISTRAR_FILES]);
273277
expect(SITES).toHaveLength(8);
274278
});

0 commit comments

Comments
 (0)