Repository navigation
[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
Description
Filing gate ③: a task the maintainer dispatched, quoted verbatim below.
Filed by domain:cli seat 2 (#22648) · os-steve · session session_01VoSxBQujKLZKPwK2u5ehQ6, on the maintainer's instruction in that session's chat. ⛔ Not a claim.
The maintainer's words, verbatim: 「这个文件经常改,为什么不分拆?rest-server.ts」. After the seat's measurement, the maintainer chose 「立卡,等 #22877 后 (推荐)」, an option the seat put as:
立一张 domain:cli 重构卡分片做。/meta 那块等 engine 车道的 #22877 第 2 阶段合入后再拆,免得双方重做合并;不碰 /meta 的区段可以先拆。每片只搬代码、路由表前后一致、门禁和文档锚点同 PR 迁完。
Reader who acts: the domain:cli seat (packages/rest is this lane's), one dispatch per slice.
Measured (on origin/main 38d98d8d83, 2026-10-12)
- Size and churn: 13,701 lines. 24 commits in 7 days, 70 in 30 days, 321 in total.
git blame --since=2026-09-12attributes 3,077 of its lines (22%) to the last 30 days.- Other lanes edit it too: engine seat 2's [finding] email_template: a Studio save of the same template in a second locale replaces the first locale's overlay at the next boot (sys_metadata keeps one overlay row per type and name) #22877 stage 2 declared into this lane today (
6123998192).
- Structure: one
RestServerclass with about 20register*Endpointsmethods, one per domain, over a block of shared private helpers. The hottest sections in the last 30 days:
| section | lines | lines from the last 30 days |
|---|---|---|
registerMetadataEndpointsInner (/meta) |
2,911 | 959 |
class head + shared helpers (enforceAuth, resolveExecCtx, computeExecCtx, metaListSource, translateMeta*, …) |
2,698 | 722 |
| module-level helpers before the class | 1,879 | 481 |
registerDataActionEndpoints |
1,181 | 174 |
registerFormEndpoints |
444 | 118 |
registerBatchEndpoints |
644 | 76 |
- What reads it by path (the cost of a split):
- 16
scripts/files, includingcheck-route-envelope,check-single-authz-resolver,check-system-context-census,check-meta-type-normalized,check-rest-log-declaredandcheck-durability-degradation-log-level; - the ADR anchor file
scripts/adr-anchors/packages__rest__src__rest-server.ts.json; - 41 files under
content/anddocs/; - 334 test files that mention
rest-server.
- 16
The ask
- Split along the existing seams, one area per PR. Each slice is move-only: no handler's logic changes.
- Shared state stays explicit: the moved routes reach the class's shared helpers through one named context object. ⛔ Not by a second copy of a helper, and ⛔ not by re-deriving one.
- Every slice proves it moved nothing:
RestServer.getRoutes()(and the route ledger the gates read) is byte-identical before and after;- every gate that reads
rest-server.tsby path, the ADR anchors and the docs anchors are migrated IN THE SAME PR and stay green. ⛔ A gate whose target moved must not go silently empty: the dead-pin shape this repo has paid for before (validateOrgAxisRedLines 读的 sharing-rule 键是 spec 拒收的:ADR-0105 D6 ① 在 criteria 路径上从不触发 #4984, fix(lint): 删除 validateOrgAxisRedLines 里 spec 合法 stack 到不了的四条分支 (#5009) #5018).
- Order:
- sections that do not touch
/metafirst (data actions, batch, forms, approvals, sharing and the like); registerMetadataEndpointsInneronly after [finding] email_template: a Studio save of the same template in a second locale replaces the first locale's overlay at the next boot (sys_metadata keeps one overlay row per type and name) #22877 stage 2 (claude/issue-22877-member-selector,domain:engineseat 2) lands, so neither side redoes a merge.
- sections that do not touch
- ⛔ No new gate. The proof is the existing gates staying green on the moved code, plus the route-table equality.
Not in this card
Any behaviour change in a handler. A design change to the shared helpers beyond giving them an explicit context. Splitting other large files.
Dedupe
MCP search_issues on this repository, closed included:
split rest-server.ts into modules large file refactor route registration per domain: 3 hits (The mirroredmakeRes()census in packages/rest names three files, but a fourth instance lives insiderest.test.tsitself — and it is the last uncheckedroute.handlercall in that file #15584, Route audit tranche 3: autonomously-mounted service routes (service-storage / service-i18n) — #3587 follow-up #3636, Route audit tranche 2: the @objectstack/rest surface (search / forms / reports / sharing / …) — #3563 follow-up #3587), none about splitting the file.rest-server.ts god file decompose extract: 0 hits.- CONTROL: the first query returns the
packages/restroute-audit cards (Route audit tranche 2: the @objectstack/rest surface (search / forms / reports / sharing / …) — #3563 follow-up #3587, Route audit tranche 3: autonomously-mounted service routes (service-storage / service-i18n) — #3587 follow-up #3636), so the instrument reaches the package.
Dedupe words: rest-server split; RestServer register endpoints modules; rest-server.ts refactor move-only.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 12, 2026
Triage: first grade,
tech-debt·priority:p2·domain:cli·area:api, intopm:queue. One move-only slice per dispatch./metawaits for #22877 stage 2Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T14:54Z. ⛔ Not a claim, ⛔ not a dispatch.domain:cliseat claims one slice at a time. Each claim names itsregister*Endpointssection and file surface. The card stays open across slices./metaslice (registerMetadataEndpointsInner) waits for [finding] email_template: a Studio save of the same template in a second locale replaces the first locale's overlay at the next boot (sys_metadata keeps one overlay row per type and name) #22877 stage 2 (dispatched,os-steve) to land.getRoutes()and the route ledger are byte-identical, and every gate, ADR anchor and docs citation that reads the file by path moves in the same PR.rest-server.tsgoes first. A slice claim reads the in-flight claims before it starts.packages/rest/src(domain:cli); rationale: one package, maintainer-directed.objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 6 (#23050 slice 1:
registerDataActionEndpointsmoves out ofrest-server.ts, and the split's pattern is set) · 2026-10-12T22:57ZSession:
session_01VoSxBQujKLZKPwK2u5ehQ6Account:
os-steve(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23050-s1-data-action-endpointsWorktree:
objectstack-issue-23050-s1Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)File surface (read on
origin/main36b2889cdd; stop on a breach and explain it in the report). This is a move-only slice, per triage6126748916:packages/rest/src/rest-server.ts: theregisterDataActionEndpointsmethod (about:9107-:10287, 1181 lines) leaves this file for ONE new module beside it underpackages/rest/src/. The class keeps only the call..d.tsof@objectstack/restdoes not change: no private member becomes public or protected onRestServer. If no move-only shape keeps it, stop and returnneeds_decision.scripts/adr-anchors/packages__rest__src__rest-server.ts.jsonand any other), docs citation and test that reads the moved lines by path or line moves in the same PR. ⛔ No gate is loosened to follow a move.skip-changeset). Otherwise the dev stops.register*Endpointssection;registerMetadataEndpointsInner(meta doors: the bundle-member selectormemberis dropped silently by the runtime dispatcher GET, by PUT, and by the by-name doors outside the ruling (no closed query sets); honour it or refuse it loudly #23072's doors; it waits for a later slice); any behaviour.Container & model:
L(1181 lines moved, every reader by path re-pointed, the pattern set),mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). No contract-review face:Clause-②: no. A move with byte-identical routes, route ledger and.d.tsmoves no accept set, export orspeckey.check-governed-merges --testover the claimed paths: not governed.Clause-②: no
Responsibility: n/a — not a defect card
Thread-read: 6126748916
Serial constraints cleared:
rest-server.ts, have both landed. No in-flight claim namesrest-server.tsnow, and no open PR touches it (the file lists of all open PRs were read in this act).memberis dropped silently by the runtime dispatcher GET, by PUT, and by the by-name doors outside the ruling (no closed query sets); honour it or refuse it loudly #23072 (pm:queue) will edit thePUTand by-name/metadoors inregisterMetadataEndpointsInner. That region is disjoint from this slice, and whichever lands second mergesmain. They stay serial, not folded: this slice is move-only, and meta doors: the bundle-member selectormemberis dropped silently by the runtime dispatcher GET, by PUT, and by the by-name doors outside the ruling (no closed query sets); honour it or refuse it loudly #23072 changes behaviour.timeout(the hook runs unbounded) andloadTranslationsserves three retired translation keys — the #23029 family's closing card #23090 (packages/runtime/src/app-plugin.ts), and PR feat(verify): the parent-derived write reach family, runnable as os verify --parent-write-reach (#15953 slice 4) #23091 (packages/verify, waiting on hourly full run: red on main (CI) #23084).Generated by Claude Code
refactor(rest): move registerDataActionEndpoints' body to data-action…
refactor(rest): move the ServerScopedDataRequest alias with the data-…
test(rest): re-point the path readers of the moved data-action body (#…
test: re-point the remaining readers of the moved data-action lines (#…
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{
"issue": 23050,
"status": "done",
"branch": "claude/issue-23050-s1-data-action-endpoints",
"pr": "#23116",
"session": "session_01VoSxBQujKLZKPwK2u5ehQ6 (mode:subagent, the dispatching PM's session)",
"premise_still_valid": true,
"summary": "Slice 1 is done and move-only. The body of RestServer#registerDataActionEndpoints (1029 lines: clone, sync and async import, the import-job reads, undo and cancel, the streaming export) moved unedited, re-indented one level, to packages/rest/src/data-action-routes.ts as registerDataActionEndpoints(this: DataActionRouteHost, basePath). The class keeps the private method (name, JSDoc and position are in the published d.ts). Its body builds a DataActionRouteHost literal of the 13 members the body reads and calls the moved function with it. Measured at the final head: dist/index.d.ts and index.d.cts are byte-identical (sha256 991a8fc9...), getRoutes() and the ordered server registrations are byte-identical under 3 configs, the route ledgers are untouched, and every reader of the moved lines by path is re-pointed with sums unchanged. Draft PR #23116 is open, assigned to os-steve, labelled skip-changeset.",
"pattern": {
"members_read": "fields config :1959, routeManager :1960, cancelledImportJobs :2064 (readonly); methods resolveProtocol :2338, enforceAuth :2390, enforceApiAccess :2572, enforceExportPermission :2675, enforceImportTemplateGates :2736, resolveExecCtx :2785, resolveMessageTranslator :3890, translateMetaItem :3912, answerImportTemplate :10161, resolveSecurityService :10267 (line numbers at base 36b2889, found by an AST walk of the body).",
"shapes_compared": [
"A. Delete the method and keep only the call at registerRoutes. d.ts NOT identical: tsup emits private members with their JSDoc, so base dist/index.d.ts lines 3153-3163 (the docblock plus 'private registerDataActionEndpoints;') would go.",
"C. The class passes itself as a narrow interface: registerDataActionEndpoints.call(this as unknown as DataActionRouteHost, basePath). Measured at 5a05b4b via scripts/ablation-replace.mjs, auto-restored, 'git diff HEAD' empty after. tsc --noEmit exits 2 with exactly 5 TS6133 errors, because root noUnusedLocals stops seeing the reads made through the cast: cancelledImportJobs, enforceExportPermission, enforceImportTemplateGates, resolveMessageTranslator, answerImportTemplate. The tsup DTS build fails too (exit 1). Not viable without the class reading every member some other way.",
"B (chosen). The class builds a host literal. tsc exits 0 and the d.ts is byte-identical. There is no cast and no visibility change. Every interface member is typed as an indexed access (RestServer['m']), so a misspelled member is a compile error. Methods are passed as late-bound closures (...a) => this.m(...a), so test doubles and spies on the instance or prototype still take effect. Fields are passed by value, which is valid here only because cancelledImportJobs is readonly and config/routeManager are read only during registration; a later slice whose body reads a reassignable field at request time passes a getter, checked per field."
],
"rule_for_later_slices": "(1) AST-list the this.X members the body reads. (2) Move the body to a new FILE-routes.ts as 'export function registerXEndpoints(this: XRouteHost, basePath)', unedited and re-indented. (3) Declare XRouteHost with one 'm: RestServer[m]' member per member read. (4) Keep the private method, with its JSDoc, as a host literal plus .call. (5) export the module-level helpers it needs from rest-server.ts. Exported functions and constants leave the d.ts unchanged; an exported TYPE ALIAS does not (measured: one blank line), so a type alias moves with the body instead. (6) Re-point every reader whose reading includes a moved line.",
"deviation_from_claim_range": "The claim's :9107-:10287 (1181 lines) covered three members. Moved: registerDataActionEndpoints' body, 9107-10137. Left in place: answerImportTemplate (10139-10253); it is section-private, but its name and JSDoc are pinned in the d.ts and the tests spy on it on the instance, so it is reached through the host. Also left in place: resolveSecurityService (10255-10281), a shared helper with 4 callers in the class head. Moved in addition: the ServerScopedDataRequest alias block (base 311-372), forced by the d.ts measurement."
},
"dts_identity": "packages/rest/dist/index.d.ts and index.d.cts are byte-identical to base 36b2889: sha256 991a8fc9cae955b56745db0b3b978a60d0d3a8f918a3a05e39ee8176601e5998 for both, before and after; cmp is silent at 5a05b4b after a fresh build. The ESM bundle's export list is unchanged (34 names), and the CJS and ESM bundles load with 34 exports each. The JS bundle bytes differ (code moved within it).",
"route_identity": "tsx boots a source RestServer on a recording mock server. getRoutes() (functions recorded as arity) and the ordered server.get/post/... calls are byte-identical base vs 5a05b4b, 6 of 6 dumps. default: routes sha256 1e1859c0ff1641540875991fc175e5378345cc7d529068a21afd9073664babd2, calls bc7196b91265731a25ef300bf9769a2919e340d2a3eaa1a1866448a7213fef8e (75 routes, 9 data-action). scoped-optional: d79275a35e12ac7c409fe0e4ffbf9e4a6685263f1b122e3cedeb932724d74b09 / 2fa050c0d6e01affa994c2bf11a1b4abe033c48814aabde114d076deb1ad3650 (150, 18). scoped-required: 066d85bfcdefd48e4c393abcf99f343d7fba8dd5947baf6af809656bdcaa5fc6 / 7595d734a01438a7d3b30649f7b9c71bcd4caa5976466fd5e243dcb4808775e7 (75, 9). Route ledgers (rest-route-ledger.ts, runtime route-ledger.ts) are untouched, and check:route-ledger-census output is identical base vs head.",
"moved_block_accounting": "git diff --color-moved=plain --color-moved-ws=allow-indentation-change 36b2889 HEAD over the two source files: 1098 deleted and 1115 added lines are detected as moved. Without ws allowance only 232/209 are, because of the re-indent. Non-moved hunks in rest-server.ts: (1) the stub body, 20 lines (comment + host literal + .call). (2) The new import line plus a 1-line comment, replacing the runImport/prepareImportRequest import lines. (3) A 3-line pointer comment where the ServerScopedDataRequest alias was. (4) 'export' added to 12 declarations (UNDECLARED_WHOLE_NUMBER_PARAM, readDeclaredQueryNumber, IMPORT_JOB_OBJECT, IMPORT_JOB_UNDO_MAX_ROWS, newImportJobId, capImportResults, parseUndoLog, importJobUndoable, importJobToProgress, importJobToSummary, rowsToCsv, createXlsxStream). (5) Removal of the imports the move left unused: ListImportJobsRequestSchema; IMPORT_JOB_MAX_ROWS and its 3-line comment; referenceFieldNames, formatRowForJson, cellFontColor, readTemplateMode, runImport, prepareImportRequest. (6) The [#20061] schema-import comment changed from 'three doors' to 'two of the three' plus a pointer (4 lines). Non-moved hunks in data-action-routes.ts: the module docblock, the imports, the 4-line note plus 'export' on the moved alias line, the DataActionRouteHost interface with its docblock, and the function docblock plus signature.",
"readers_repointed": [
"scripts/check-route-envelope.mjs: data-action-routes.ts is declared dialectOnly, ratchet #9559, stringError 13, siblingCode 15, measured by the gate. rest-server.ts stringError 39->26 and siblingCode 54->39, both noted as a MOVE. The sums are unchanged. --self-test passes.",
"packages/rest/src/execctx-consumer-census.test.ts: sites keyed file:line over both files (62 = 53+9; 16 caught / 46 bare unchanged). Stack attribution steps over the one host-literal forwarder frame, whose line is pinned (FORWARDER_LINE > 0). Mentions 77->81: the +4 are the forwarder and the interface member, not consumers. catchArguments reads both files.",
"packages/rest/src/rest-server-query-number-census.test.ts: coercion sites over both files. The ledger is unchanged, because keys are route/function anchors, not files.",
"packages/rest/src/rest-server-canonical-query-ast.test.ts: CENSUS rest-server.ts minQuerySlots 4->1, plus data-action-routes.ts 3 under the same door rule. The two by-name literals are asserted on the new module.",
"packages/rest/src/ui-view-route-identity.measurement.test.ts and ui-view-route-tenancy.measurement.test.ts: the whole-file spelling reverse checks count over both files (enforceAuth 49, resolveExecCtx 63, enforceEnvironmentOwnership 1).",
"packages/rest/src/rest-server-docblock-position.test.ts: the no-stacked-docblock rule also reads data-action-routes.ts, floor 4 (its measured count), because the alias docblock moved there.",
"packages/qa/dogfood/test/authz-probe-blind-spot.census.ts: the rest-server row's registration population reads both files, so 70/19/51 is unchanged; the 9 moved routes are still RestServer's and all are in the blind spot. New control 'data-action-routes.ts: this.routeManager.register(' = 9. The 'this.routeManager.register(' control stays 63. enforceAuth 57->61 re-anchored: the 4 new occurrences are the seam, and no call site was added.",
"docs/qa/platform-checklist/areas/records-forms.json: the clone item's source anchor rest-server.ts#registerDataActionEndpoints is now data-action-routes.ts#registerDataActionEndpoints."
],
"readers_examined_zero_contribution": "Measured 0 matches from the moved lines in each: the dogfood realtime TRIPWIREs on rest-server.ts (0 mechanics hits, 0 route hits in the module); objectql-slot-consumer-census (0 provider reads; a new one would need a host-literal entry in rest-server.ts, where it reads); meta-draft-read-door-census (0 draft switches); auth-gate-allowlist-fault-window, execctx-authz-input-seam-reachability and runtime meta-list-projection-parity (they slice other members). These gates' output is identical base vs head except +1 scanned file: check-durability-degradation-log-level, check-system-context-census, check-single-authz-resolver, check-undeclared-dep-imports, check-dispatcher-error-vocabulary, check-meta-type-normalized. The ADR anchor packages__rest__src__rest-server.ts.json (ADR-0045 filterAppForUser) is unaffected. No path:line citation in the repo points into the moved ranges (base 9107-10137, 311-372).",
"tests": "At 5a05b4b, all through os-verify-lock. pnpm --filter @objectstack/rest test: 'Test Files 275 passed (275)' before (36b2889) and after; Tests 5759 passed | 326 skipped (6085) before, 5761 passed | 326 skipped (6087) after (+2 = the docblock pin's new describe); VERDICT command-exit 0. pnpm --filter @objectstack/rest test:repo: 'Test Files 5 passed (5)' before and after; Tests 209|1 skipped (210) before, 213|2 skipped (215) after (+5 = the canonical-query-ast census's new file entry); VERDICT command-exit 0. pnpm --filter @objectstack/rest typecheck: VERDICT command-exit 0 (tsc --noEmit plus check:test-typecheck: 0 files / 0 errors). dogfood test/authz-probe-blind-spot.test.ts plus test/authz-conformance.test.ts: 2 files, 92 passed, VERDICT command-exit 0. Before the re-point, the census was red (expected 61 to be 70). Before the re-points, the rest suite showed the readers red: Tests 8 failed (execctx census x4, query-number census x2, ui-view x2); all green after. Shape-C measurement: ablation-replace mutation landed (blob d5a5f618619b -> c0caf3fe736f), tsc exit 2 with 5 x TS6133, build exit 1. Restored: blob == HEAD, 'git diff HEAD' empty; then rest was rebuilt and the d.ts is identical again. Lint, proven narrowing: eslint --no-inline-config --format json over the 10 touched lintable files gives 10 results, 0 errors, 0 warnings. ESLint isPathIgnored is false for all 10, and calculateConfigForFile gives 2-6 rules each. There is no parserOptions.project (no type-aware lint), and the config's only file inputs (eslint.config.mjs, scripts/slot-lookup-baseline.json, scripts/query-options-erasure-baseline.json) are untouched, so no untouched file's verdict can move.",
"gates": {
"head": "5a05b4bde6",
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths: 79 commands, against PM's 57 at dispatch. It grew with the readers touched: declaration-mirrors, scripts-symbol-anchors, self-test-wired, the check:liveness family, platform-checklist, pm-dispatch-gates, type-check-coverage/debt and others.",
"reconciliation": "--ran with exit annotations: 'Run reconciliation — 79 derived, 78 run, 1 NOT-MEASURED, 0 UNRUN.' 77 exit 0.",
"red_not_mine": "pnpm check:platform-checklist exit 1: the same 8 findings on base 36b2889 (metadata-protocol protocol.ts #anonymousFormIntake*/#envWideRawViewRows/#countable, service-storage #canEdit, the attachments-storage anchor floor). None are in records-forms.json, and the output is identical base vs head.",
"not_measured": "check:dual-build-cjs-loads: NOT MEASURED, reason: PREREQUISITE NOT MET (exit 3; 19 workspace packages have no dist/ in this container; a whole-workspace build queued twice behind other agents' runs, VERDICT queue-timeout exit 99). Narrow stand-in measured: packages/rest/dist/index.cjs and index.js both load, 34 exports each. CI runs the gate over a full build.",
"per_command": [
"node scripts/check-ci-filter-parity.mjs => exit 0 | OK: all 21 build input(s) turbo.json declares outside the packages (itself, 2 globalDependencies, and every
$TURBO_ROOT$input of a build Build Core runs) and all 11 script(s) th","node scripts/check-closing-keyword-parity.mjs => exit 0 | check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 11128 tracked file(s), all regis",
"node scripts/check-closing-keyword-parity.mjs --self-test => exit 0 | ✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red.",
"node scripts/check-comment-mask-adoption.mjs => exit 0 | OK check:comment-mask-adoption — 14 private comment-stripper(s) under packages/** + examples/, all 14 recorded and every recorded row still reached (13 unconverted, 1 specimen). ",
"node scripts/check-comment-mask-adoption.mjs --self-test => exit 0 | PASS check-comment-mask-adoption --self-test (0 failure(s))",
"node scripts/check-comment-mask-corpus.mjs => exit 0 | ✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 8901 files, 0 disagree, 0 unparseable, 117.7s (comparator self-test: 26 cases pass).",
"node scripts/check-declaration-mirrors.mjs => exit 0 | OK: 10 hand-written declaration(s) agree with their modules on name, kind and required arity.",
"node scripts/check-declaration-mirrors.mjs --self-test => exit 0 | All 29 self-test cases passed.",
"node scripts/check-dts-emitted.mjs --self-test => exit 0 | check-dts-emitted self-test: all assertions passed.",
"node scripts/check-issue-citations.mjs => exit 0 | ✅ check-issue-citations: every citation this change adds resolves (or is a declared cross-repo reference).",
"node scripts/check-keyed-text-bounds.mjs => exit 0 | ✓ check:keyed-text-bounds: 112 .object.ts files under packages/ + apps/* + examples/** (walk is repo-wide; 0 outside), 117 object declarations, 242 declared index entries, 591 ",
"node scripts/check-keyed-text-bounds.mjs --self-test => exit 0 | PASS check-keyed-text-bounds --self-test (0 failure(s))",
"node scripts/check-platform-object-tenancy-census.mjs => exit 0 | ✓ platform-object tenancy census matches the tree: 84 platform-namespace objects, 49 in the machinery's reach, 35 outside it, every exclusion explained by a declaration on its own ",
"node scripts/check-platform-object-tenancy-census.mjs --self-test => exit 0 | ✓ check-platform-object-tenancy-census self-test: all checks pass (84 objects, 35 outside the machinery)",
"node scripts/check-plugin-teardown-shape.mjs => exit 0 | ✓ check:plugin-teardown-shape: 75 Plugin implementation(s) across 8315 source(s) under packages/; every teardown-shaped method (stop / shutdown / close / dispose) sits beside a r",
"node scripts/check-plugin-teardown-shape.mjs --self-test => exit 0 | ✓ check-plugin-teardown-shape self-test: 48 cases pass (real pre-#10375 fixture reds, the repaired file and both delegating-alias directions stay green, every roster name reds, eve",
"node scripts/check-registry-log-declared.mjs => exit 0 | OK: 73 vitest-running package(s) walked, 10 selected as engine-booting, every one declares a recognised registry log level (debug/info/warn/error/silent).",
"node scripts/check-registry-log-declared.mjs --self-test => exit 0 | self-test OK: 17 cases + level-vocabulary read + population declaration + real-tree selection floor.",
"node scripts/check-rest-log-spy-declared.mjs => exit 0 | OK: 32 of 281 test file(s) beside packages/rest/src/log.ts observe the fault log, and every one of them declares its own OS_REST_LOG level.",
"node scripts/check-rest-log-spy-declared.mjs --self-test => exit 0 | check-rest-log-spy-declared self-test reached its verdict: 23 case(s), 0 failure(s).",
"node scripts/check-scripts-symbol-anchors.mjs => exit 0 | ✅ check-scripts-symbol-anchors: 3842 anchors across 285 scripts resolve — 52 symbol (52 declaration, 0 literal), 3790 file-level, 0 cross-repo, 1 exempt, 2 continuation. 0 line anc",
"node scripts/check-scripts-symbol-anchors.mjs --self-test => exit 0 | ✅ check-scripts-symbol-anchors --self-test: every finding class provoked, comment-prose projection wired, declined shapes counted not missed, allowance rows exact both ways, popula",
"node scripts/check-self-test-wired.mjs => exit 0 | ✓ check-self-test-wired: every one of the 233 script(s) CI runs that ship a
--self-testhas that self-test run by CI, and every one of the 169 root package.json alias(es) declari","node scripts/check-self-test-wired.mjs --self-test => exit 0 | check-self-test-wired --self-test: 3 live ledger row(s) verified, plus the comment mask, the right boundary, alias resolution, the declared-alias population and both audit directio",
"node scripts/check-self-test-workflow-commands.mjs => exit 0 | ✓ check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner would parse as a workflow command.",
"node scripts/check-self-test-workflow-commands.mjs --self-test => exit 0 | check-self-test-workflow-commands --self-test: both measured parse rules pinned (legacy form anywhere in a line, current form only at line start), the innocent-output and Perl-name",
"node scripts/check-system-context-census.mjs => exit 0 | check-system-context-census: OK — 131 elevation read sites in 20 packages across 59 files, living in 111 symbol(s); the page cites 124 symbol(s) against 124 required, over 147 anch",
"node scripts/check-system-context-census.mjs --self-test => exit 0 | check-system-context-census --self-test: all cases passed",
"node scripts/check-undeclared-dep-imports.mjs => exit 0 | ✓ check:undeclared-dep-imports: 81 workspace packages under packages/ + apps/** + examples/, 3048 non-test src files, 2551 @objectstack/* specifiers (0 assembled, not judged); ",
"node scripts/check-undeclared-dep-imports.mjs --self-test => exit 0 | PASS check-undeclared-dep-imports --self-test (0 failure(s))",
"node scripts/check-whole-set-label-write.mjs => exit 0 | ✓ check-whole-set-label-write: 0 violations — 351 file(s) over 3 root(s) · 10 raw mention(s) · 10 in comments/prose (cleared) · 0 in EXECUTABLE content (judged) · 227
uses:pin(s","node scripts/check-whole-set-label-write.mjs --self-test => exit 0 | ✓ check-whole-set-label-write --self-test: all cases pass (24 fixture trees + 5 refusals + 1 allowlist hatch)",
"node scripts/docs-audit/check-affected-docs.mjs => exit 0 | ✓ affected-docs self-test: 605 cases pass.",
"node scripts/docs-audit/check-drift-comment.mjs => exit 0 | ✓ check-drift-comment: 66 cases pass across 5 fixture diff(s).",
"node scripts/pm/bare-root-worklist.mjs --self-test => exit 0 | OK self-test: 82 live row(s), 60 unreachable as spelled, 47 recorded verdict(s) — none stale, none missing, none contradicted (12 row(s) whose gate carries the dispatch-gates wide-",
"pnpm --filter @objectstack/lint run check:doc-formula-expressions => exit 0 | ✓ check:doc-formula-expressions (field-level
*When, #11407): 15 predicate(s) on a statically determinable field layer judged clean; 6 skipped as undeterminable.","pnpm --filter @objectstack/spec run check:duration-unit-keys => exit 0 | ✓ check:duration-unit-keys — 197 unit-declaring numeric key(s) across 3053 source file(s) all carry their unit in the key name (or in a sibling
unit, or under a declared exemptio","pnpm --filter @objectstack/spec run check:empty-state => exit 0 | ✓ all classified (2 closed, 2 open, 4 output, 9 scope)",
"pnpm --filter @objectstack/spec run check:liveness => exit 0 | ✓ packages/spec/liveness/state-counts/ is current — one shard per governed type, the same 41 row(s) as the README, no count column left in the README.",
"pnpm --filter @objectstack/spec run check:strictness-ledger => exit 0 | ✓ docs/audits/2026-07-unknown-key-strictness-ledger.counts/ is current — 14 shard(s), one per source directory with sites, 479 triaged site(s) measured, 1 authorable strip site(s) ",
"pnpm --filter @objectstack/spec run check:variant-docs => exit 0 | ✓ variant/doc gate: 18 discriminated union(s) — 8 governed (every variant mentioned in a bound doc), 10 exempt.",
"pnpm check:agent-test-spelling => exit 0 | ✓ check-agent-test-spelling: 0 violations — 585 file(s) · 9955 bare
--token(s) · 1990 launcher-rooted run(s) · 10 separator(s) JUDGED · 6 vitest-backed script name(s) derived fr","pnpm check:authz-resolver => exit 0 | ✓ check:authz-resolver: single shared authorization resolver intact; both entry points delegate.",
"pnpm check:bash32-floor => exit 0 | ✓ check-bash32-floor: 30 tracked shell file(s) under scripts/, .claude/hooks/, .githooks/ name no bash 4+ construct outside a comment, a guarded ${VAR:-} read, or a non-comma",
"pnpm check:cli-command-ids => exit 0 | ✓ check-cli-command-ids: 67 module(s) under packages/cli/src/commands examined, all of them default-export a class whose inheritance chain reaches oclif's
Command(0 declared exe","pnpm check:cross-package-test-inputs => exit 0 | OK: 30 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split "test:repo" task); 13 walked root(s) judged, 3 on ACCEPTED_",
"pnpm check:dispatcher-error-vocabulary => exit 0 | check-dispatcher-error-vocabulary: OK — 56 unregistered code-stamping site(s), all classified; 2 awaiting a ledger entry (#8846).",
"pnpm check:doc-authoring => exit 0 | ✓ doc authoring guard: sibling-package prose ids hold the baseline — 0 pinned site(s) across 0 file(s), 94849 string(s) read in 1321 parsed source(s) (seen floor 40000 strings / 60",
"pnpm check:driver-memory-census => exit 0 | check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states "#6664 census: 2 ruled consumers". This gate polices the cen",
"pnpm check:dts-closure => exit 0 | check-dts-closure: 47 built package(s) swept - 142/142 declared declaration file(s) present across 47 package(s); 0 built package(s) declare no declaration entry point and owe none",
"pnpm check:dual-build-cjs-loads => exit 3 | PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/.",
"pnpm check:engine-double-contract => exit 0 | check-engine-double-contract: 902 (file, verb) row(s) held by the RETAINED ledger — a pin that leaves names itself.",
"pnpm check:entry-guard => exit 0 | ✓ check:entry-guard: 285 scripts/ file(s) — every entry guard goes through invoked-as.mjs; 224 export bindings, 224 of them inert on import (0 known-unsafe, ⛔ SHRINK-ONLY).",
"pnpm check:error-status-conformance => exit 0 | ✓ every derivable runtime status is documented, and every documented status is reachable.",
"pnpm check:gitlink-declared => exit 0 | check-gitlink-declared: OK (11128 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing is declared; nothing to declare).",
"pnpm check:issue-citations => exit 0 | ✅ check-issue-citations --self-test: grammar narrowed, every spelling enumerated, qualifier a closed set of repositories, four 404 causes kept apart, both board strategies agree, d",
"pnpm check:lean-entry-closure => exit 0 | ✓ check-lean-entry-closure: 2 published condition(s) measured from a real load.",
"pnpm check:logger-receiver-detach => exit 0 | OK every log channel keeps its receiver: 3346 non-test TS file(s) walked, 0 detach(es) on the 5 declared receiver-sensitive sink spelling(s).",
"pnpm check:nul-bytes => exit 0 | check-nul-bytes: OK (scanned 11119 text file(s) -- 11119 tracked, 0 untracked-not-ignored; skipped 9 binary; no raw ASCII control bytes).",
"pnpm check:objectql-double-limit => exit 0 | OK ObjectQL double
limitconformance holds: 479 double(s) graded, 287 apply the caller's bound or refuse it loudly.","pnpm check:org-identifier => exit 0 | check-org-identifier: OK (3368 author-facing source file(s), 18 session binding(s) resolved, no removed session.tenantId alias).",
"pnpm check:page-declaration-shape => exit 0 | check-page-declaration-shape: OK — 36 page entries across 3369 sources under packages/, examples/, apps/** all reach the kernel through a discoverable declaration (
: Pageor ","pnpm check:parse-guard => exit 0 | ✓ check:parse-guard: 284 scripts/ file(s) — every TypeScript parse goes through ts-parse.mjs.",
"pnpm check:platform-checklist => exit 1 | ✗ areas/attachments-storage.json: SYMBOL ANCHORS LOST — 27 anchor(s) resolve here but the floor is 28. A citation that gives up its
#symbolhalf stops being resolvable by anythin","pnpm check:pm-dispatch-gates => exit 0 | check:pm-dispatch-gates: the battery (the fast tier) took 279.5s on this box.",
"pnpm check:pnpm-filter-targets => exit 0 | ✓ check:pnpm-filter-targets: 159/210
--filteroccurrence(s) across 44 file(s) resolve against 81 workspace package(s); 51 not judged (1 foreign, 30 interpolated, 20 path); 56 mor","pnpm check:published-files => exit 0 | ✓ check:published-files — 70 publishable package(s) of 81 workspace member(s) declare a
fileswhitelist that covers every entry point plus CHANGELOG.md and admits no test, test-h","pnpm check:query-options-erasure => exit 0 | ✓ query-options-erasure ratchet holds: 65 unswept non-test site(s) in 17 file(s), none new, and every file measured parsed. Every other non-test file under packages/ is covered by ",
"pnpm check:ratchet-remedy-authority => exit 0 | OK check-ratchet-remedy-authority: 275 scripts swept (scripts/.{mjs,mts} + scripts/pm/.{mjs,mts}); 14 mark the expanding remedy ⛔ MAINTAINER-ONLY, 5 turn it down outright, 256 ha",
"pnpm check:refd-timer-probe => exit 0 | OK check-refd-timer-probe: 8895 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.",
"pnpm check:route-envelope => exit 0 | ✓ Express-style response modules — 4 module(s) discovered and audited (walked, not enumerated — #9937), 12 hand-built body/bodies (count reported, NOT pinned): 2 conformant, 2 ratc",
"pnpm check:slot-lookup => exit 0 | ✓ slot-lookup ratchet holds: 104 unswept site(s) in 25 file(s), none new, and every file in the population parsed. Every other file under packages/ is covered by
pnpm lint.","pnpm check:sourcemap-no-sources-content => exit 0 | check-sourcemap-no-sources-content: 47 built package(s) swept - 208 map(s), none embed source text.",
"pnpm check:test-source-alias => exit 0 | check-test-source-alias OK — 73 packages with tests scanned; 60 registered as still resolving a workspace dep through
dist/; 53 published subpath(s) resolved through every alias ","pnpm check:tier-file-adoption => exit 0 | OK: 81 workspace package(s) walked, 82 nightly-tier test file(s) on disk (82 e2e, 0 live), owned by 1 package(s); every one reads OS_TEST_TIERS.",
"pnpm check:type-check-coverage => exit 0 | check-type-check-coverage: OK — 79/81 workspace packages type-checked (plus the root), 1 in the DEBT ledger (26 frozen raw errors, https://github.com/objectstack-ai/objectstack/iss",
"pnpm check:type-check-debt => exit 0 | check-type-check-coverage --re-measure: OK — 1 ledger entr(ies) re-measured in 119.3s, 26 raw tsc error(s) total, none above its recorded number.",
"pnpm check:watch-hint-literal => exit 0 | ✓ check-watch-hint-literal: 73 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 49, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECLARED_WATCH_HINTS 8 -- every on",
"pnpm check:where-matcher => exit 0 | ✓ where-matcher conformance holds: 500 matcher(s) discovered, 500 answer the combinator battery correctly or refuse it loudly (321 refuse)."
]
},
"line_budget": "11 files, +1501/-1184 = 2685 changed lines (human-merge threshold 3000: under). rest-server.ts 13821 -> 12745 (+40/-1116). data-action-routes.ts 1219 lines new: 1029 moved body, 62 moved alias block, the rest are header, imports, interface and signature.",
"files_changed": [
"packages/rest/src/rest-server.ts",
"packages/rest/src/data-action-routes.ts",
"packages/rest/src/execctx-consumer-census.test.ts",
"packages/rest/src/rest-server-query-number-census.test.ts",
"packages/rest/src/rest-server-canonical-query-ast.test.ts",
"packages/rest/src/rest-server-docblock-position.test.ts",
"packages/rest/src/ui-view-route-identity.measurement.test.ts",
"packages/rest/src/ui-view-route-tenancy.measurement.test.ts",
"packages/qa/dogfood/test/authz-probe-blind-spot.census.ts",
"scripts/check-route-envelope.mjs",
"docs/qa/platform-checklist/areas/records-forms.json"
],
"deviations": [
"Claim/dispatch says 'the class keeps only the call at the site that registers it'. That shape (A) changes the d.ts, which the same order forbids. The private method is kept as a stub, measured.",
"Moved range is narrower than the claim's ':9107-:10287': answerImportTemplate and resolveSecurityService stay in the class (see pattern.deviation_from_claim_range).",
"The ServerScopedDataRequest type alias (base 311-372, module-level, outside the section) moved with the body. Exporting it from rest-server.ts changed dist/index.d.ts by one blank line (measured at 98982b1; exporting it via an end-of-file 'export type {}' measured the same), and a copy is ruled out by the card. The CRUD, form and batch doors in rest-server.ts now import it from data-action-routes.ts; a later slice may rehome it.",
"Touched files outside the claim's literal surface, all re-pointed readers of moved lines that the order asks for: packages/qa/dogfood census, scripts/check-route-envelope.mjs, docs/qa/platform-checklist/areas/records-forms.json.",
"origin/main (965a1bf) was merged into the branch (AGENTS.md section 10). It is disjoint from this diff. #23105 (the main-red repair named in the claim) is among the merged commits.",
"check:dual-build-cjs-loads is NOT MEASURED, as a declared narrowing (see gates.not_measured).",
"A read-only comparison worktree ../objectstack-issue-23050-s1-cmp at base was used for before/after gate diffs (AGENTS.md's sanctioned -cmp form). It has been removed."
],
"mcp_calls": "0",
"api_writes": "3 relay strokes (each a POST /repos/objectstack-ai/objectstack/dispatches executed by fleet-write.yml as objectstack-fleet[bot]): (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23116/assignees [os-steve]; (2) label-write = POST /repos//issues/23116/labels [skip-changeset]; (3) this os-dev-report comment = POST /repos//issues/23050/comments. git push x6 to the branch (not REST). No other writes.",
"open_questions": [
{
"question": "Two packages/spec/liveness evidence strings cite rest-server.ts#registerDataActionEndpoints: crud_endpoints.json dataPrefix ('each builds its own dataPath') and route_generation.json overrides ('reads body.overrides'). They still resolve, to the private method that registers these routes by calling the moved body, and check:liveness is green. But the dataPath and body.overrides code they describe is now in data-action-routes.ts. The claim excludes packages/spec. Who re-points them?",
"options": [
"A: leave them. The anchor still names the registrar, and the next slice that touches packages/spec/liveness re-points them.",
"B: a one-line follow-up PR in packages/spec/liveness re-points both to data-action-routes.ts#registerDataActionEndpoints."
],
"recommendation": "B, because the evidence prose describes code by location, and the move made two of its sentences point at a stub. It is a 2-line, behaviour-free edit that a PM can attach to the next slice's surface."
},
{
"question": "Where should the ServerScopedDataRequest alias finally live? Its majority users (CRUD, batch, forms) are still in rest-server.ts and now import it from data-action-routes.ts.",
"options": [
"A: the CRUD slice moves it next to its doors, or into a small shared module, when that slice runs.",
"B: leave it in data-action-routes.ts."
],
"recommendation": "A, because the CRUD slice is the first point where a better home exists without a second new module in one slice, and the move can be repeated mechanically."
}
],
"out_of_scope_findings": [
"class: none (pre-existing red gate) · reach: n/a · evidence: pnpm check:platform-checklist exits 1 with 8 identical findings on base 36b2889 and on 5a05b4b. Absent symbols in metadata-protocol protocol.ts (anonymousFormIntakeOrgScopeRefusal, anonymousFormIntakeReopenRefusal, envWideRawViewRows, countable) and service-storage attachment-access-hooks.ts#canEdit, plus the attachments-storage anchor floor 27/28. · dedupe words: platform checklist absent symbol; anonymousFormIntakeReopenRefusal anchor; attachments-storage anchor floor",
"carrier: whoever removed or renamed those protocol.ts symbols (a platform-checklist re-anchor). Noted, not filed."
]
}
Generated by Claude Code
119 remaining items
objectstack-fleet commentedon Oct 17, 2026
Release:
domain:cliseat 1 ·os-sales· sessionsession_01BVm1pAXVXQ3PjC42j1kPFf· 2026-10-17T18:58Z · cause: PR #24205 landed (Part of #23050, slice 12) · destination:pm:queue, unassigned, in this act's next strokeLanded: PR #24205 →
bb7ccde66(#23050 slice 12, CRUD,Part of #23050)origin/mainbb7ccde66"refactor(rest): move registerCrudEndpoints into crud-routes.ts (#23050 slice 12) (#24205)";git rev-list --parents -n1gives 1 parent, a squash; it carries no closing keywordc333dafdaagainst its base.git diff c333dafda bb7ccde66differs only byscripts/check-route-envelope.mjs' print-door row, which ismain's ownd9a749bae(#24194), not this PR'sgit ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-24205-*'gives 0 refsmerged: trueat 2026-10-17T18:57:33Z6235962355; ACCEPT6238147426, the seat's review of record (no contract face is touched, so CI plus the seat's read decide it). Cross-lane declarations: [PM seat] domain:spec — 🟢 os-project-manager · session_01TyRhWbVbbQpQJTDbW8VahJ · p1/p2 only (maintainer) #60176235976157, amended as6238125655; [PM seat] domain:devx @ objectstack — ⏳ vacant #60236238132617.registerCrudEndpoints' body moved unedited topackages/rest/src/crud-routes.tsbehindCrudRouteHost. The route table, registrations and handler digests are byte-identical under 13 configs, and the.d.tsand the 39-entry export list are unchanged. Every by-path reader moved in the same PR.rest-server.ts: 9,512 → 9,086 lines.main's next full battery reads the dogfood pair this slice re-anchored.Dogfood Regression Gatehas no PR-time verdict; the dev's local reading was 108/108.What remains on this card
/meta(registerMetadataEndpoints/registerMetadataEndpointsInner, about 3,000 lines inside the guardedrouteManagerswap). Its order gate is met: #22877 stage 2 landed as038d4c9588. It wants its own slicing plan, and the slice 12 report (os-dev-reporton this card) lists its prerequisites:routeManagerhandover must be measured inside the guarded swap;TransportScopedMetaRequest's re-home out ofui-routes.ts;metadata_endpoints.json,doc.json,book.json,api-backend.jsonandcontent/docs/permissions/system-context.mdx:184;private registerMetadataEndpoints(spelling;The carried notes from
6228233788still stand. The next claimant writes a newClaim:with the full protocol.objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 1, serial, p1/p2 only (the maintainer to this seat, verbatim 「串行派发,只处理 p1 p2」, and 「当前任务处理完,后续改为串行派发」, confirmed as "serial, p1/p2 only"). Slice 12 landed and was released (
6239076198), so nothing of this seat's is in flight. Label order read in this act: #23050 is this lane's only queued p1/p2 card (#24137 ispm:blockedon #24166). #23050 slice 13, the/metaslicing plan: measure-first, no code change and no PR. The plan is posted on this card, and its sub-slices are claimed one at a time · 2026-10-17T19:01ZSession:
session_01BVm1pAXVXQ3PjC42j1kPFfAccount:
os-sales(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23050-s13-meta-plan(a measurement branch; nothing merges from it)Worktree:
objectstack-issue-23050-s13Domain:
domain:cliSeat:
domain:cli#1(seat post #6024)Ruling-ref: the card body's five rules (the maintainer's 「立卡,等 #22877 后 (推荐)」), whose order gate for
/metais met (#22877 stage 2 →038d4c9588); seat 2's Release6228233788("/metawants its own slicing plan"); slice 12's report on this card (/metaprerequisites)File surface (read on
origin/mainbb7ccde66): none edited. The dev readspackages/rest/src/rest-server.ts(registerMetadataEndpoints:5084,registerMetadataEndpointsInner:5262to about:8300, and whatever else still lives in the class besides the stubs) and every by-path reader of that code, and returns a measured slicing plan in its report.Container & model:
M(a measurement over ~3,000 lines),mode:subagent,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate)Clause-②: no
Basis for the Clause-② line: a measurement with no diff. Each sub-slice it plans is move-only and declares its own line at its own claim.
Responsibility:
packages/rest/src/rest-server.ts(this lane) | the/metablock is the file's largest and hottest section (959 of its lines from the 30 days before filing), and the remaining slices cannot be cut safely without a measured plan | every seat editing/meta(domain:engine's #24097 among them today)Thread-read: 6239076198
Serial constraints cleared:
packages/rest/srcpath. fix(rest)!: format=json honours an explicit ?fields= #24206 (rest(export):format=jsonignores?fields=, so a list view's JSON export carries every readable field instead of the view's columns #24183, seat 2's) touchesdata-action-routes.ts. fix(metadata-protocol)!: the by-name /meta read refuses an object of a disabled package (404 OBJECT_PACKAGE_DISABLED), as the list and the data plane do #24208 (domain:engine's metadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097) touchesmeta-item-read-gate.ts, which is/meta-adjacent and which the plan must schedule around.objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{
"issue": 23050,
"status": "reading",
"branch": "claude/issue-23050-s13-meta-plan",
"pr": null,
"session": "session_01BVm1pAXVXQ3PjC42j1kPFf (mode:subagent; the dispatching PM's session id)",
"premise_still_valid": true,
"summary": "Slice 13 measured the /meta block on origin/main bb7ccde (rest-server.ts is byte-identical at today's 8259ea0). registerMetadataEndpoints is :5084-:5172 and registerMetadataEndpointsInner is :5262-:8269, 3,008 lines. Of those, 2,944 lines are 15 movable top-level statements that register 19 routes per base. The only closure state shared across route groups is the inner method's four head bindings (metadata, metaPath, isScoped, registerPerItemRoute). So every group can move as a pure move, in place, once those four reach its module through one scope parameter. The plan is five serial sub-slices, 13a-13e, of 443 / 891 / 405 / 809 / 396 moved lines, with 13a first. Two measured facts shape every sub-slice. First, three of the five target modules read no routeManager at all: they register only through the inner method's own registerPerItemRoute closure. Second, slice 12's handler-digest proof is blind to /meta bodies, because the server-visible handler of every /meta route is the same guard wrapper. The proof therefore needs a direct inner-registration leg. Nothing was edited, committed (beyond the empty branch push) or opened.",
"slicing_plan": {
"measured_on": "origin/main bb7ccde, using the TypeScript 6.0.3 compiler API: a symbol-resolved AST walk of this.* members, RestServer.* statics and module-level names, which is slice 12's method. rest-server.ts is unchanged at 8259ea0 (git diff --quiet bb7ccde 8259ea0 exit 0). Each group's range starts on the line after the previous statement ends and runs to the statement's own end, so it includes the group's leading comments. Counts are inclusive.",
"meta_block": {
"umbrella": "registerMetadataEndpoints :5084-:5172 (JSDoc :5066-:5083). It stays whole in rest-server.ts.
const realRouteManager = this.routeManageris at :5085, the guarded register literal at :5086-:5165, the swapthis.routeManager = guardedRouteManagerat :5166, the inner call at :5168 and the restore in finally at :5170.","inner": "registerMetadataEndpointsInner :5262-:8269 (3,008 lines; JSDoc :5174-:5261). The head :5262-:5297 (metadata, metaPath, isScoped, and registerPerItemRoute with its JSDoc) stays, and so does the tail comment :8242-:8268 on the retired compound twins. The 15 top-level statements at :5298-:8241 (2,944 lines) can move. Per base they make 11 direct this.routeManager.register calls and 8 registerPerItemRoute calls, 19 routes in all.",
"groups": [
"G1 type list | :5298-:5354, 57 lines | GET METAPATH and GET METAPATH/types (one handler, listMetaTypes, on two paths) | switch endpoints.types | this.: resolveProtocol 1, translateMetaTypesResponse 1, routeManager 2 (registration time) | statics: none",
"G2 whole-store maintenance | :5355-:5617, 263 lines: GET METAPATH/diagnostics :5355-:5413 (59), GET METAPATH/_drafts :5414-:5498 (85), POST METAPATH/_migrate-stored :5499-:5617 (119) | three endpoints.maintenance blocks | this.: routeManager 3, resolveProtocol 3, resolveExecCtx 2, walledCatalogRefusal 1, resolveMetaWriteActor 1 | statics: none",
"G3 per-type list | :5618-:5740, 123 lines | GET METAPATH/:type | switch endpoints.items | this.: routeManager 1, resolveProtocol 1, refuseUnknownMetaListType 1, resolveExecCtx 1, metaListAnswerSources 1 | static RestServer.metaTypeSingular :5719 | reads TransportScopedMetaRequest (type)",
"G4 per-item reads | :5741-:6631, 891 lines | ONE statement,
if (metadata.endpoints.item !== false), holding four registrations with no block-local declaration between them: GET METAPATH/:type/:name/references :5741-:5845, GET .../layers :5846-:5911, GET METAPATH/book/:name/tree :5912-:5967, GET METAPATH/:type/:name :5968-:6630 (663) | this.: routeManager 4, resolveProtocol 4, resolveObjectMasker 2, serveMetaItemLayered 2, extractLocale 2, translateMetaEnvelope 2, and 1 each of metaReadAudienceSources, resolveExecCtx, resolveI18nService, metaItemReadGate, maskObjectDocument, anonymousFormIntakeWarnings, metaItemAnswerSources | statics: metaTypeSingular :5879 (layers) and :6028 (item), STORED_VERSION_DOOR_POLICY :6533 (a private static readonly FIELD), sendMetaReadRefusal :6541 | head locals: metadata 2 (theifand metadata.enableCache :6231), metaPath 4, isScoped 4","G5 per-item writes | :6632-:7036, 405 lines: PUT METAPATH/:type/:name :6632-:6871 (240), DELETE :6872-:7036 (165), both via registerPerItemRoute | this.: resolveExecCtx 2, walledCatalogRefusal 2, resolveProtocol 2, resolveMetaWriteActor 2, and NO routeManager | static metaSaveVerdict :6688 (PUT)",
"G6 history family | :7037-:7845, 809 lines: GET .../history :7037-:7176 (140), GET .../audit :7177-:7338 (162), POST .../publish :7339-:7530 (192), POST .../rollback :7531-:7650 (120), GET .../diff :7651-:7845 (195), all via registerPerItemRoute | this.: resolveExecCtx 5, resolveProtocol 5, eventDoorRefusal 2 (history, audit), walledCatalogRefusal 2 and resolveMetaWriteActor 2 (publish, rollback), and 1 each of resolveObjectMasker, fetchCurrentMetaDocument, metaItemReadGate, maskObjectDocument (diff); NO routeManager | statics, all six in the diff handler: metaTypeSingular :7734, gatesPerCaller :7745, gatesOnItsOwnKey :7758, diffSides :7788, STORED_VERSION_DOOR_POLICY :7791, diffEmittedFrom :7802",
"G7 object FSM state read | :7846-:7983, 138 lines | GET METAPATH/object/:name/state/:field, a direct this.routeManager.register under NO per-family switch (api.enableMetadata only) | this.: routeManager 1 (registration time); objectQLProvider 2, read at request time (
Boolean(this.objectQLProvider)and() => this.objectQLProvider!(environmentId)inside wiredEngineOrLoud) | dynamic import('@objectstack/objectql') at :7950","G8 published | :7984-:8241, 258 lines | GET METAPATH/:type/:name/published via registerPerItemRoute, inside a bare block | this.: resolveProtocol 2, and 1 each of resolveObjectMasker, metaItemReadGate, maskObjectDocument, resolveMetadataService; NO routeManager | static metaTypeSingular :8083"
],
"shared_closure_state": "Only the four head bindings, all const, are shared. metadata is read by G1, G2 (x3), G3 and G4 (x2). metaPath and isScoped are read by every group. registerPerItemRoute is called by G5 (x2), G6 (x5) and G8 (x1). Every other top-level statement is an if block, a bare block or an expression statement, so no other declaration crosses a group boundary (G1's listMetaTypes is block-local). Every group can therefore move independently and in place, which keeps the registration order, once those four reach its module through one parameter.",
"walk_anomalies": "None. There is no bare this, no this[...], no function keyword, and no assignment to any this.X inside the inner body. The inner method is synchronous (void, no await at registration level).",
"per_group_census_tokens": "Draft sites (the meta-draft-read-door predicate): G2 _drafts 2, G3 1, G4 2, G5 DELETE 1, which is the census's rest-server.ts 6. Numeric coercions (the query-number predicate): 1, rollback's Number(toVersionRaw) :7616; the other 7 of the census's 8 sit outside /meta. Route-envelope (its own exported scanSource): stringError at :5896 (G4 layers), :7093 (history), :7417 (publish), :7601 and :7618 (rollback), :7696 (diff); siblingCode at :5896 and :7618. :2716 and :2765, both class head, stay. Response write sites: G1 1, G2 9, G3 1, G4 9, G5 9, G6 15, G7 3, G8 3. TSDoc blocks: 0 in every group. logError: 0. Realtime tripwire hits: 0. query: keys counted by canonical-query-ast: 0 (rest-server.ts' floor is already 0)."
},
"guarded_swap": {
"what_the_guard_reads": "Registration time: realRouteManager (the const captured at :5085, before the swap) and
typeof entry.handler. Request time: req.params.environmentId, then this.resolveExecCtx (late-bound) with .catch(rethrowAuthzStoreUnavailable), RestServer.isPublicAudienceRead(entry, req), this.enforceAuth, metaReadGate.metaTypeReadRefusal(req.method, req.params.type, ctx), metaReadGate.metaTypeWriteRefusal(entry.method, req.params.type, ctx), sendEnvelopeError, and finally the inner handler. None of these move.","what_registerPerItemRoute_reads": "It reads metadata.endpoints.item from the closure, and this.routeManager on RestServer at CALL time (:5296). All 8 of its call sites are registration-time statements inside the inner call.",
"routeManager_reads": "11 direct this.routeManager.register reads, all at registration time and none inside a handler: G1 2, G2 3, G3 1, G4 4, G7 1. registerPerItemRoute adds 1 more. Nothing in the inner body assigns this.routeManager.",
"handover_rule": "Build every /meta host literal INSIDE registerMetadataEndpointsInner, at the position of the statements it replaces, and pass routeManager BY VALUE (slice 12's shape). This is equivalent for three reasons: (1) every original read happens synchronously during the inner call; (2) the umbrella assigns guardedRouteManager before that call (:5166) and restores it only in the finally (:5170); (3) nothing inside the inner body reassigns it. The value captured at the stub line is therefore the same object every original read returned. A test that calls registerMetadataEndpointsInner directly (execctx census :401, :1126, :1297) captures whatever manager that test installed, exactly as the original reads did.",
"modules_with_no_routeManager": "G5, G6 and G8 read no routeManager, so their hosts carry none. They register through the inner method's own registerPerItemRoute closure, handed over in the scope parameter. That closure still reads RestServer's this.routeManager at call time, so it is the same closure object, the same switch read and the same guarded manager. A module-local copy of registerPerItemRoute would be a second copy of a helper (card rule 2) and would read the host's field instead.",
"wrong_placements": "A host built in registerRoutes, in the constructor, cached on the instance, or in the umbrella above :5166 captures the REAL route manager. Every moved direct-registered route would then mount outside the anonymous-deny gate (#3963) and outside the type-level read and write admissions (#21087, #21124). That is slice 12's by-value shape captured outside the swap.",
"ablation_per_sub_slice": "13a, 13b and 13e-state: hoist the routeManager capture above :5166. 13c, 13d and 13e-published: give the module its own registerPerItemRoute reading a host routeManager captured above :5166. Then drive an anonymous request at every moved route: head answers 401, the ablation does not. Standing pins that should go red: rest-meta-auth.test.ts (GET METAPATH/:type, GET .../history, /meta/object), meta-type-read-capability.test.ts (every GET METAPATH/:type... door) and meta-type-write-capability.test.ts (every non-GET METAPATH/:type... door). No standing anonymous pin covers GET METAPATH, METAPATH/types, /diagnostics, /_drafts, /book/:name/tree or /object/:name/state/:field, so the one-off probe is the evidence for those. No permanent test is added."
},
"scope_handover": "The four head bindings reach each module as ONE parameter, scope: MetaRouteScope = { metadata, metaPath, isScoped, registerPerItemRoute }. It is built once in the inner head, just after registerPerItemRoute's declaration. Each module function opens with one destructuring line that names only the bindings it uses, because packages/rest compiles with noUnusedLocals and noUnusedParameters (root tsconfig). The moved body keeps every token. MetaRouteScope types metadata as RestServer['config']['metadata'], the slices' indexed-access typing. This is the one new element compared with slices 1-12: there the head lines (
const { crud } = this.config; ... isScoped) moved with the body, but /meta's head is shared by five modules, so it stays (Q1).","transport_scoped_meta_request": "Readers at bb7ccde: ui-routes.ts declares it at :93; form-routes.ts imports it; the rest-server.ts class head uses it at :2647, :3649, :4209 and :8599. Those class-head readers STAY after every /meta move, so its last reader never leaves rest-server.ts. In /meta, G3 reads it once, G4 twice, G5 twice and G6 twice. Recommendation (Q2 A): 13a moves it into 13a's new module, beside MetaRouteScope. G3 is its first /meta reader in registration order, and every later /meta module then imports both from their final home from its first commit. Readers 13a must re-point: ui-routes.ts (the block leaves and an import is added), form-routes.ts (the import path, plus its header comment :59), rest-server.ts (the import at :307 and slice 10's pointer comment at :255), and rest-server-docblock-position.test.ts (the ui-routes.ts floor goes 4 -> 3 and the new module's floor carries the block). The .d.ts should stay unchanged: index.ts re-exports only RestServer, RestProtocol, MountedRoute and metaItemPackageBinding from rest-server.js, and slice 10 measured the same kind of move as byte-identical. Do not use meta-item-read-gate.ts as the home while #24208 is open.",
"sub_slices": [
{
"id": "13a (must go first)",
"moves": "G1 + G2 + G3, :5298-:5740, one contiguous run",
"target": "NEW packages/rest/src/meta-collection-routes.ts: export function registerMetaCollectionRoutes(this: MetaCollectionRouteHost, scope: MetaRouteScope). It also exports MetaRouteScope and, under Q2 A, TransportScopedMetaRequest.",
"moved_lines": 443,
"routes_per_base": "7: GET METAPATH, GET METAPATH/types, GET /diagnostics, GET /_drafts, POST /_migrate-stored, GET /:type",
"host": "9 members, in first-read order: resolveProtocol, translateMetaTypesResponse, routeManager BY VALUE (6 registration-time register calls; built inside the inner method), resolveExecCtx, walledCatalogRefusal, resolveMetaWriteActor, refuseUnknownMetaListType, metaListAnswerSources, all as late-bound closures (...a) => this.m(...a), plus the static metaTypeSingular as a closure on RestServer typed (typeof RestServer)['metaTypeSingular']. Ruling A gives 1 receiver token (:5719 -> this.metaTypeSingular). Scope used: metadata, metaPath, isScoped.",
"module_level": "No body-only helper. metaItemPackageBinding (package entry export) and mayReadPendingDrafts (analytics imports it) stay in rest-server.ts and are imported from rest-server.js. Every import G1-G3 read is still read elsewhere in rest-server.ts, so no import line leaves.",
"readers_same_pr": [
"rest-server-registrar-files.ts gains meta-collection-routes.ts (length 13 -> 14). Its readers move with it: execctx-consumer-census.test.ts (3 sites move: _drafts, _migrate-stored, /:type; +1 forwarder spelled exactly
resolveExecCtx: (...a) => this.resolveExecCtx(...a),), meta-draft-read-door-census.test.ts (3 sites), rest-server-query-number-census.test.ts (0), rest-server-canonical-query-ast.test.ts (repo project, floor 0), rest-server-docblock-position.test.ts (floor = the module's measured blocks), and ui-view-route-identity / ui-view-route-tenancy measurement tests (this.resolveExecCtx( +1 for the forwarder, this.enforceAuth( +0). dogfood authz-conformance.test.ts: the realtime tripwires read 0 hits; update the list length.","dogfood authz-probe-blind-spot.census.ts: population 70 is unchanged. reachable must stay 19, so its rule must learn the /meta modules (Q3). Without that, reachable falls 19 -> 13 and blindSpot rises 51 -> 57, a false reading. New control 'meta-collection-routes.ts: this.routeManager.register(' = 6. populationRule names the module. Keep the bare enforceAuth control in mind: the moved text has 0 occurrences, but a new header comment that names enforceAuth moves the count.",
"scripts/check-route-envelope.mjs (domain:spec target table): declare meta-collection-routes.ts as dialectOnly + ratchet #9559 at stringError 0 / siblingCode 0 (11 response sites move). rest-server.ts stays at 8 / 4.",
"packages/runtime/src/domains/meta-list-projection-parity.test.ts: REST_SERVER_SOURCE must read the module. The test finds the GET METAPATH/:type registration by AST and asserts it was found, so it is a loud cross-package read; re-run check:cross-package-test-inputs.",
"packages/spec/liveness/metadata_endpoints.json: the endpoints.types, endpoints.items and endpoints.maintenance evidence moves to meta-collection-routes.ts#registerMetaCollectionRoutes. prefix stays, because metaPath is in the inner head. Silent if forgotten.",
"docs/qa/platform-checklist/areas/api-backend.json:2449: add the module anchor for the types, items and maintenance gates beside the inner anchor (slice 7's split form). studio-authoring.json:1397
rest-server.ts#route(GET /diagnostics) -> the module's function; this one already resolves to an unrelated loop variable at :8960 (out_of_scope_findings). FOLLOW-UPS.md:613 (E1, prose) is optional.","If Q2 A: the TransportScopedMetaRequest readers listed under transport_scoped_meta_request."
],
"declarations": "#6017: metadata_endpoints.json (3 evidence paths) and check-route-envelope.mjs. #6023: api-backend.json, studio-authoring.json. Changesets: @objectstack/rest patch (the dist bytes change) and @objectstack/spec patch (liveness ships), both Clause-2 no.",
"container": "L (a hypothetical 20-path surface derives 94 gate commands, the count slice 12 ran; dispatch-gates --tier: no path-derived mandate)",
"risk": "It sets every new /meta element at once, on the simplest handlers: the scope parameter, a host built inside the swap (prove it with the ablation), the census reachable rule (Q3) and the re-home (Q2). The runtime parity test is a cross-package reader."
},
{
"id": "13b",
"moves": "G4, :5741-:6631. The single
ifstatement moves whole, so the endpoints.item switch moves with it.","target": "NEW packages/rest/src/meta-item-read-routes.ts: registerMetaItemReadRoutes",
"moved_lines": 891,
"routes_per_base": "4: references, layers, book/:name/tree, GET item",
"host": "16 members: routeManager BY VALUE (4 register calls); 12 closures (resolveProtocol, resolveObjectMasker, serveMetaItemLayered, metaReadAudienceSources, extractLocale, resolveExecCtx, resolveI18nService, metaItemReadGate, maskObjectDocument, anonymousFormIntakeWarnings, translateMetaEnvelope, metaItemAnswerSources); the statics metaTypeSingular and sendMetaReadRefusal as closures; and STORED_VERSION_DOOR_POLICY BY VALUE. That last one is private static readonly, set once when the class is evaluated (from metaReadGate.STORED_VERSION_DOOR_POLICY) and never reassigned. It is the first static FIELD on any host, so the claim should name it up front (Q4). 4 receiver tokens: :5879, :6028, :6533, :6541. Scope used: metadata (the
ifand enableCache), metaPath, isScoped.","module_level": "Body-only and free to move: notImplementedRefusalAnswer :1655 and anonymousFormIntakeFingerprint :1960. These need
export(two homes): sendMetaItemAbsent :1710 (G6's diff reads it too) and stampAnonymousFormIntakeWarnings :1948 (class head :4146). Imports leaving rest-server.ts: META_ITEM_LAYERS_PARAMS, GetMetaItemCachedRequest, resolveObjectSchemaRuntimeView, foldVisibilityFingerprintIntoEtag, normalizeIfNoneMatch, thrownAnswerIsBareNotFound. META_ITEM_REFERENCES_PARAMS stays, because the meta door tests import it from rest-server.js.","readers_same_pr": [
"Registrar list length 15, with its readers: execctx (1 site, GET item; +1 forwarder), draft census (2 sites), identity/tenancy (+1 resolveExecCtx).",
"dogfood census: control 'meta-item-read-routes.ts: this.routeManager.register(' = 4; reachable is held by the Q3 rule.",
"check-route-envelope: stringError 1 and siblingCode 1 move (both at :5896, the layers route); rest-server.ts goes 8/4 -> 7/3; 9 response sites move.",
"liveness (silent): metadata_endpoints.json enableCache and endpoints.item (the
ifhalf moves; the registerPerItemRoute half stays in the inner head, so that citation splits across two files); doc.json _note and content.evidence (the single-item and book-tree branches; the list-branch quote is already stale, out_of_scope_findings); book.json _note (the book/:name/tree branch).","Checklist and docs (silent): api-backend.json:2449 (the item gate); platform-core.json:1266
rest-server.ts#metaPath(book tree) -> the module, which matters because metaPath keeps resolving in the inner head; content/docs/permissions/system-context.mdx:184 row 50 (GET item ?state= is a stored-version read door) -> add the module anchor."],
"declarations": "#6017: metadata_endpoints.json (2), doc.json (2), book.json (1), check-route-envelope.mjs. #6023: api-backend.json, platform-core.json. The system-context.mdx owner. Changesets: rest patch and spec patch.",
"container": "L",
"risk": "This moves the file's hottest code (the GET item handler, 663 lines) and the widest host. It is 891 lines, over the 300-800 band, though slice 1 moved 1,181 in one PR (Q5). #24208 (domain:engine, meta-item-read-gate.ts) does not overlap textually. It is a semantic neighbour, because GET METAPATH/:type/:name serves the by-name read, but the handler text does not change, so the digests hold whichever lands first.",
"alternative": "Q5 B, a move-only split: move GET item alone (:5968-:6630, 663 lines) and call it INSIDE the remaining brace in rest-server.ts, then the three small reads (217 lines) the same way. The brace keeps the switch and no token changes."
},
{
"id": "13c",
"moves": "G5, :6632-:7036",
"target": "NEW packages/rest/src/meta-item-write-routes.ts: registerMetaItemWriteRoutes",
"moved_lines": 405,
"routes_per_base": "2: PUT and DELETE METAPATH/:type/:name",
"host": "5 members and NO routeManager: resolveExecCtx, walledCatalogRefusal, resolveProtocol and resolveMetaWriteActor as closures, plus the static metaSaveVerdict as a closure (1 receiver token, :6688). Scope used: metaPath, isScoped, registerPerItemRoute. This is the first registerPerItemRoute pass-through.",
"module_level": "Imports leaving rest-server.ts: META_ITEM_SAVE_PARAMS, metaSaveRequestOptions, SaveMetaItemRequest, DeleteMetaItemRequest. META_ITEM_RESET_PARAMS stays (rest-server-meta-member-selector.test.ts imports it from rest-server.js).",
"readers_same_pr": [
"Registrar list length 16: execctx (2 sites; +1 forwarder), draft census (1 site, DELETE), identity/tenancy (+1).",
"dogfood census needs two changes. First, this module has ZERO direct register sites, so a per-module control spelled 'X: this.routeManager.register(' reads 0, and the census's non-zero-first assertion fails. For such modules the control must count registerPerItemRoute( (both spellings, as the population rule does). Second, the 'registerPerItemRoute(' control counts rest-server.ts alone and falls 8 -> 6; re-anchor it over rest-server.ts plus the /meta modules (or keep it per file).",
"check-route-envelope: declare 0/0 (9 response sites move).",
"content/docs/permissions/system-context.mdx:184 row 50 (REST's save door) -> add the module anchor. Silent.",
"No liveness edit."
],
"declarations": "#6017: check-route-envelope.mjs. The system-context.mdx owner. Changeset: rest patch.",
"container": "M",
"risk": "Low size, new mechanism. Measure that the handed-over closure is the inner method's own: under endpoints.item=false the PUT and DELETE routes vanish, and anonymous requests get 401, on both legs."
},
{
"id": "13d",
"moves": "G6, :7037-:7845",
"target": "NEW packages/rest/src/meta-item-history-routes.ts: registerMetaItemHistoryRoutes",
"moved_lines": 809,
"routes_per_base": "5: history, audit, publish, rollback, diff",
"host": "15 members and NO routeManager: 9 closures (resolveExecCtx, resolveProtocol, eventDoorRefusal, walledCatalogRefusal, resolveMetaWriteActor, resolveObjectMasker, fetchCurrentMetaDocument, metaItemReadGate, maskObjectDocument); the static methods metaTypeSingular, gatesPerCaller, gatesOnItsOwnKey, diffSides and diffEmittedFrom as closures; and STORED_VERSION_DOOR_POLICY by value. 6 receiver tokens, all in the diff handler. Scope used: metaPath, isScoped, registerPerItemRoute.",
"module_level": "refuseNonAuthoringCaller :1911 is body-only (history, audit, diff), but its callee mayReadAuthoringTrail :1877 is anchored by system-context.mdx:108. Recommendation: keep it in rest-server.ts and add
export(Q6). Imports leaving rest-server.ts: HistoryMetaItemRequestSchema, HistoryMetaItemRequest, AuditMetaItemRequestSchema, AuditMetaItemRequest, PublishMetaItemRequest. Staying exported: META_ITEM_HISTORY/AUDIT/PUBLISH/ROLLBACK/DIFF_PARAMS (tests import them from rest-server.js), and readDeclaredQueryNumber and UNDECLARED_WHOLE_NUMBER_PARAM (approvals, data-action and search import them).","readers_same_pr": [
"Registrar list length 17: execctx (5 sites; +1 forwarder), query-number census (1 site, rollback; rest-server.ts 8 -> 7), identity/tenancy (+1), dogfood per-item control 5.",
"check-route-envelope: stringError 5 (:7093, :7417, :7601, :7618, :7696) and siblingCode 1 (:7618) move. rest-server.ts goes 7/3 -> 2/2, leaving the two class-head sites. 15 response sites move.",
"studio-authoring.json:459
rest-server.ts#toVersion(history/audit/diff/rollback) -> the module. This one is SILENT: after the move toVersion still resolves in rest-server.ts as a literal at :684 and :721 (META_ITEM_ROLLBACK_PARAMS, META_ITEM_DIFF_PARAMS).","system-context.mdx:184 row 50 (diff is a stored-version read door) -> add the module anchor. :108 changes only if Q6 moves the chain."
],
"declarations": "#6017: check-route-envelope.mjs. #6023: studio-authoring.json. The system-context.mdx owner. Changeset: rest patch.",
"container": "L",
"risk": "Six static receivers sit in one handler: diff's inner digest has to map back 6 tokens. This sub-slice moves the most route-envelope dialect sites, and the #toVersion anchor fails silently."
},
{
"id": "13e",
"moves": "G7 :7846-:7983 into NEW packages/rest/src/meta-object-state-routes.ts (registerMetaObjectStateRoute); G8 :7984-:8241 into 13d's meta-item-history-routes.ts as a second export, registerMetaItemPublishedRoute (the history family's sixth member, per the inner JSDoc's family list). Each is called at its own position.",
"moved_lines": 396,
"routes_per_base": "2: object state/:field, published",
"host": "The state host has 2 members: routeManager BY VALUE and objectQLProvider as a GETTER. objectQLProvider is a provider field read at request time (ruling 6215084478; slice 2's batch shape
get objectQLProvider() { return readObjectQLProvider(); }). The published host has 6 members and NO routeManager: closures resolveObjectMasker, resolveProtocol, metaItemReadGate, maskObjectDocument, resolveMetadataService, and the static metaTypeSingular (1 receiver token, :8083).","module_level": "wiredEngineOrLoud stays (batch imports it). Imports leaving rest-server.ts: META_ITEM_PUBLISHED_PARAMS, canonicalMetaUrlType. The dynamic import('@objectstack/objectql') moves with G7.",
"readers_same_pr": [
"Registrar list length 18 (+meta-object-state-routes.ts; the history module is already listed). execctx: 0 sites and no forwarder; the state module joins the reads-no-execctx set. Docblock floors: the history module +2 (the published host interface and its function). dogfood: the state module's control is 1 (direct); the history module's per-item control goes 5 -> 6.",
"objectql-slot-consumer-census.test.ts (loud) needs four changes. SLOT_FILES gains the module, and consumers stay 3 over three files. Its CONTROL
expect(SOURCE).toContain('this.objectQLProvider(environmentId).catch(() => undefined)')reads a comment at :7896 inside G7, so it must read the module's source. The getter count in rest-server.ts goes 1 -> 2, which equals SLOT_FILES.length - 1. HOST_MEMBER_TYPE appears once in the module.","scripts/check-undeclared-dep-imports.mjs LEDGER (loud): the @objectstack/objectql optional-runtime-probe row's file moves rest-server.ts -> meta-object-state-routes.ts.",
"check-route-envelope: declare the state module 0/0 (3 response sites); the history module +0/0 (+3).",
"system-context.mdx:184 row 50 (published) -> add the module anchor and remove the inner anchor. After 13e the inner method is the head, 6 host literals with their calls, and the tail comment."
],
"declarations": "#6017: check-route-envelope.mjs and check-undeclared-dep-imports.mjs. The system-context.mdx owner. Changeset: rest patch.",
"container": "M (a hypothetical 18-path surface with the content/docs row derives 116 commands)",
"risk": "The provider getter and three loud ledgers. Once 13e lands, the ADR-0076 Tier H carry is due."
}
],
"order": "13a must go first. It creates MetaRouteScope and, under Q2 A, the home of TransportScopedMetaRequest; every later module imports both. It widens the dogfood census's reachable rule; without that, every later move inflates blindSpot. And it proves the inside-the-swap handover with an ablation on the simplest handlers. 13c must come before 13d and 13e-published: it is the first to pass registerPerItemRoute through the scope and to re-key the census's per-module control, and it does so on 405 lines rather than 809. 13d must come before 13e, because 13e adds published to 13d's module. 13b is independent once 13a has landed. Recommended order: 13a, 13b, 13c, 13d, 13e. That is registration order, so each PR replaces the next contiguous run and the inner method shrinks top-down. All five edit the inner method and the same nine list readers, so they run serially.",
"not_pure_moves": [
"No group is interleaved with another group's closure state. The only shared state is the four head bindings, and they reach the modules through one parameter without changing a body token. No preparatory step is needed to make any group movable.",
"Necessary edits of the already-ruled class, to be named up front in each claim: 13 static-receiver tokens RestServer.X -> this.X (ruling A, 6205442867): 13a 1, 13b 4, 13c 1, 13d 6, 13e 1. One of them, STORED_VERSION_DOOR_POLICY (13b :6533, 13d :7791), is a static readonly FIELD, the first on any host (Q4). Also
exporton module-level helpers that two homes read: sendMetaItemAbsent and stampAnonymousFormIntakeWarnings (13b), and refuseNonAuthoringCaller (13d, if kept per Q6). This is slices 1/2's shape and leaves the .d.ts unchanged.","G4 (891 lines) is one statement. Moving it whole is a pure move above the line band. Splitting it move-only means calling the moved functions INSIDE the remaining brace (Q5 B). Converting its four registrations to registerPerItemRoute would make it splittable without the brace, but that is a non-move edit: the smallest preparatory step. It was not performed and is not recommended inside this card.",
"The dogfood census's reachable rule is lexical ('inside registerMetadataEndpoints'). The moved registrations are reachable at runtime but not lexically. Re-anchoring the rule is a test-side reader migration, not a production edit, but its shape is the seat's call (Q3)."
],
"proof_method": [
"Slice 12's handler digest is BLIND to /meta bodies. realRouteManager.register receives { ...entry, handler: wrapper }, and that wrapper is one source text for all 19 /meta routes per base. So getRoutes() and the server's registration calls carry the same digest before and after any /meta body edit, and a positive control on a /meta handler would not move. Add an inner leg: give a RestServer a recording routeManager, call registerMetadataEndpointsInner(basePath) directly for each base (the execctx census's own isolation), and take a whitespace-free digest of every recorded inner handler, with the ruled receiver tokens mapped back. The positive control, a one-character edit in a moved handler, must move exactly that route's inner digest.",
"Keep the full-boot legs: getRoutes(), the ordered server registrations with arity, the .d.ts / .d.cts, and the 39-name export list. These stay identical because the umbrella and the inner method both stay as private members by name.",
"Config matrix: slice 12's 13 configs, plus the /meta switches: metadata.endpoints.types, items, item and maintenance each set false (scoped-optional), all four false together, a custom metadata.prefix, and api.enableMetadata false. meta-route-registration-order.test.ts and rest-config-mount-table.pin.test.ts are the standing order and switch pins.",
"Run the guard ablation (guarded_swap.ablation_per_sub_slice), and the late-binding ablation as slices 11 and 12 did (turn one closure into .bind(this)).",
"Show readers red before re-point in slice 12's two stages: the move commit with the list not grown, then the list grown with the readers still at base text."
],
"readers": {
"loud_if_forgotten": [
"rest-server-registrar-files.ts and its 8 readers: the execctx census, draft census, query-number census, canonical-query-ast (repo project), docblock-position, ui-view identity, ui-view tenancy, and dogfood authz-conformance (list length). Follows every sub-slice.",
"dogfood authz-probe-blind-spot.census.ts (rest-server.ts row and controls). Follows every sub-slice; the rule changes at 13a and the control re-key at 13c.",
"scripts/check-route-envelope.mjs (a new -routes.ts file is auto-discovered and reported NOT DECLARED). Follows every sub-slice.",
"packages/runtime/src/domains/meta-list-projection-parity.test.ts. Follows 13a.",
"packages/rest/src/objectql-slot-consumer-census.test.ts. Follows 13e.",
"scripts/check-undeclared-dep-imports.mjs LEDGER. Follows 13e."
],
"silent_if_forgotten": [
"packages/spec/liveness/metadata_endpoints.json: 13a (types, items, maintenance) and 13b (enableCache, the
ifhalf of item). prefix stays. check:liveness resolves rest-server.ts#registerMetadataEndpointsInner by name, so it stays green.","packages/spec/liveness/doc.json (_note, content.evidence) and book.json (_note): 13b.",
"docs/qa/platform-checklist/areas/api-backend.json:2449: 13a and 13b. The :2390 clause stays true; the :2457 change log is not touched.",
"platform-core.json:1266 #metaPath: 13b. studio-authoring.json:1397 #route: 13a. studio-authoring.json:459 #toVersion: 13d.",
"docs/qa/platform-checklist/FOLLOW-UPS.md:613 (E1, prose): 13a, optional.",
"content/docs/permissions/system-context.mdx:184 row 50: 13b, 13c, 13d and 13e each add their module, and 13e drops the inner anchor. check-system-context-census resolves anchors by declaration, so the inner anchor keeps resolving. :108 (#mayReadAuthoringTrail) changes only under Q6 B. :191 (#walledCatalogRefusal) stays.",
"docs/adr/0076 (the D11 status line, and section 4's 'registerMetadataEndpointsInner is 2,545 lines'): a Tier H carry for the maintainer after 13e. ADR-0005:239 and ADR-0017:134 are file-level prose, Tier H, optional."
],
"examined_unaffected": [
"dogfood authz-conformance.test.ts: the probe /private\s+registerMetadataEndpoints\s\(/ and the dead-probe control at :1149. The umbrella stays, and its spelling must be kept.",
"ui-view-route-identity routesByRegistrar (runtime prototype wrapping): routes stay attributed to the umbrella.",
"core authz-store-unavailable.test.ts: it keys on
resolveAuthzContext({, which is in computeExecCtx in the class head.","check-system-context-census NON_READ_ANCHORS (enforceAuth) and check-single-authz-resolver DELEGATORS: both class head.",
"scripts/adr-anchors/packages__rest__src__rest-server.ts.json (ADR-0045): its 2 mentions outside /meta stay. The moved groups carry 3 more, but anchors are opt-in, so no anchor is owed.",
"Directory-scan gates (check-meta-type-normalized, check-dispatcher-error-vocabulary, check-error-status-conformance, check-affected-docs, check-rest-log-declared, check-durability-degradation-log-level): +1 file per module and no ledger edit. /meta has no logError, and no FAILURE_PROPAGATION entry is keyed on rest-server.ts.",
"The execctx census's direct registerMetadataEndpointsInner calls: the method stays. sharing-routes.ts:37 and ui-routes.ts:31 ('its one routeManager reassignment wraps registerMetadataEndpointsInner') stay true."
]
},
"remaining_class": {
"after_13e": "rest-server.ts drops to about 6,250 lines (9,086 - 2,944 + about 110 for host literals, calls and pointer comments; an estimate, not measured). What remains: imports and module-level helpers :1-:1990. The class head :1991-:4776: fields and constructor (166 lines); environment, protocol and service resolution (about 267); auth and permission gates (about 427); exec ctx (about 668, of which computeExecCtx is 437); meta read and door helpers (about 363); translation and masking (about 553); header extraction; config normalization (about 296). Then registerRoutes :4777-:4851; the discovery helpers and two stubs :4852-:5064; the umbrella and the inner shell; and the stubs, helpers and public accessors :8270-:9086 (817).",
"candidates": "None under the card's rules. (1) Every registerEndpoints seam other than /meta is already a stub, and after 13e /meta is too. (2) The remaining class members are either shared state that the hosts forward to, which stays by rule 2, or private helpers. That includes about 20 members (roughly 600 lines) that only /meta reads, such as resolveMetaWriteActor, eventDoorRefusal, the diff statics, serveMetaItemLayered and metaListAnswerSources, plus answerImportTemplate (data-action only), the form helpers, and probeMcpServeable / loadOpenApiSpec (discovery only). Moving any private member changes the published .d.ts private-member list, so it fails the slice proof's .d.ts identity, and it is a helper design change the card excludes. (3) The module-level helpers are exported building blocks that moved modules and tests import from rest-server.js. Co-locating the single-consumer ones would not change the .d.ts, but they are not seams, so it would be a separate decision (not filed). (4) registerRoutes and the public accessors are the enumerator and the published API. NOT MEASURED, reason: no build in a read-only dispatch. The .d.ts private-member premise was not re-read on this tree; slices 1-12 measured .d.ts byte-identity while keeping the private stubs."
},
"in_flight": [
"#24206 (#24183: data-action-routes.ts +38/-3 and export-json-fields.test.ts) MERGED as 8259ea0 after the claim was written. rest-server.ts is byte-identical across bb7ccde..8259ea0. No /meta sub-slice touches data-action-routes.ts, so there is no conflict.",
"#24208 (domain:engine #24097, open draft, head 81e9289): meta-item-read-gate.ts +5 (loadObjectItem, the API-exposure gate's by-name read that enforceApiAccess reaches from the class head, not /meta), two rest tests, metadata-protocol and runtime meta.ts. No sub-slice edits those files, so there is no textual overlap. It is a semantic neighbour of 13b (GET METAPATH/:type/:name), but the handler text does not change, so 13b's digests hold whichever lands first. Do not use meta-item-read-gate.ts as TransportScopedMetaRequest's home while it is open.",
"#24209 (platform-objects) and #24168 (Version Packages) touch no packages/rest path. The claude/issue-24137 and claude/issue-24166 heads (oauth) touch no packages/rest/src path."
]
},
"tests": "None run. This is a read-only measurement dispatch: no closure build, no test run, no diff. Instruments (all read-only, in the scratchpad's issue-23050-s13 directory): walk-toplevel.cjs and walk-members.cjs (the TypeScript 6.0.3 compiler API, a no-resolve Program with its checker, a symbol-resolved walk of members and module-level names: slice 12's method); tokens.cjs (a per-group token census that mirrors the draft-door and query-number census predicates); class-inventory.cjs; liveness-map.cjs; and check-route-envelope.mjs' own exported scanSource run over rest-server.ts. Cross-checks against the standing censuses: draft sites over /meta read 6, the census's rest-server.ts 6; numeric coercions read 7 outside /meta plus 1 in rollback, the census's 8; scanSource reads stringError 8 / siblingCode 4, the declared 8 / 4; registration sites read 11 direct + 8 via the helper, the dogfood census's reachable 19.",
"gates": "No local gate is owed: this is the empty measurement branch.
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat bb7ccde exited 2 with 'this branch changes nothing against origin/main (merge base bb7ccde) - nothing to derive'. Hypothetical surfaces, for container sizing only: 13a's 20 paths derive 94 commands and 13e's 18 paths derive 116. dispatch-gates --tier on 13a's paths: 'no path-derived mandate'. No PR, so no CI.","mcp_calls": "0",
"api_writes": "1 relay write: this os-dev-report comment, POST /repos//issues/23050/comments, through scripts/pm/post-stamped.mjs. git push x1 (the empty branch, also the write probe) is not a REST write. Reads only (REST GET): the card and its 55 comments (2 pages), #8851 (404), the open pulls list, PRs #24205, #24054, #24206, #24208 and #24209, and the files of the last three. No label write, no assignee write, no claim.",
"open_questions": [
{
"question": "Q1. How do the four inner-head bindings (metadata, metaPath, isScoped, registerPerItemRoute) reach a moved /meta module?",
"options": ["A: one scope parameter, MetaRouteScope, built once in the inner head; each module destructures what it uses; body tokens unchanged; the host stays class members only (the host + basePath split every slice used)", "B: fold them into each host and destructure them from
thisat the function top; one context object per module, but the host interface mixes class members and per-call values", "C: each module re-derives metaPath and isScoped from basePath and keeps its own registerPerItemRoute; this duplicates the head and makes a second copy of a helper (card rule 2), so it is rejected"],"recommendation": "A. It is a pure move with one added destructuring line, and registerPerItemRoute stays one closure that reads RestServer's guarded routeManager at call time."
},
{
"question": "Q2. Where do TransportScopedMetaRequest and MetaRouteScope live, and when?",
"options": ["A: in 13a's meta-collection-routes.ts, moved by 13a", "B: a dedicated type-only module created in 13a; it is not on the registrar list, so the alias's TSDoc block leaves every docblock pin", "C: leave TransportScopedMetaRequest in ui-routes.ts and re-home it in a micro-slice after 13e; every /meta module meanwhile imports a meta type from the UI module"],
"recommendation": "A. G3 is its first /meta reader, later modules import from the final home from their first commit, its block stays under the docblock pins, and the .d.ts is unaffected (it is not on the package entry; slice 10 measured the same kind of move)."
},
{
"question": "Q3. How does the dogfood census's lexical reachable rule ('inside registerMetadataEndpoints') learn the /meta modules?",
"options": ["A: rest-server-registrar-files.ts exports a second, named subset of the /meta modules; reachable = the umbrella slice plus every site in that subset; a control pins the subset against the number of module calls inside the inner method", "B: a naming convention (meta--routes.ts)", "C: change no rule and let blindSpot rise; that is false, because the routes are guarded at runtime"],
"recommendation": "A, declared in 13a with the runtime proof (the anonymous 401 ablation). A name convention can drift silently."
},
{
"question": "Q4. Ruling A covers private static METHODS. How is the private static readonly FIELD STORED_VERSION_DOOR_POLICY handed over (13b and 13d)?",
"options": ["A: by value on the host; it is set once when the class is evaluated and never reassigned", "B: as a getter onto RestServer.STORED_VERSION_DOOR_POLICY"],
"recommendation": "A, named in the 13b claim up front together with the 13 receiver tokens (seat 2 asked for statics to be named up front)."
},
{
"question": "Q5. G4 is 891 lines in one
ifstatement. Move it whole or split it?","options": ["A: whole, in one PR; the switch moves with it; slice 1 moved 1,181 lines", "B: move-only split, GET item (663) and then the three small reads (217), each called inside the remaining brace in rest-server.ts", "C: first convert the four registrations to registerPerItemRoute; this is a non-move preparatory edit"],
"recommendation": "A. B is the fallback if the review budget is the binding constraint. C does not belong in this card."
},
{
"question": "Q6. Body-only module-level helpers: move them with the body, or keep and export?",
"options": ["A: move them (the slice 12 dispatch rule): notImplementedRefusalAnswer and anonymousFormIntakeFingerprint in 13b, and the refuseNonAuthoringCaller -> mayReadAuthoringTrail -> AUTHORING_TRAIL_READ_CAPABILITIES chain in 13d, re-pointing system-context.mdx:108", "B: keep them and add export"],
"recommendation": "A for the two 13b helpers, which no outside reader names. B for refuseNonAuthoringCaller, whose callee is a docs anchor."
}
],
"out_of_scope_findings": [
"carrier: domain:spec seat, through 13b's #6017 declaration. packages/spec/liveness/doc.json content.evidence quotes
const { content: _content, ...rest } = itas rest-server.ts#registerMetadataEndpointsInner's doc list branch. At bb7ccde that line is in packages/rest/src/meta-item-read-gate.ts:3456, with zero hits in rest-server.ts. The evidence resolves by name, so check:liveness stays green. This drift predates slice 13. Noted, not filed. Dedupe words: liveness doc content evidence list branch meta-item-read-gate; stale quote rest-server","carrier: domain:devx seat, through 13a's #6023 declaration. docs/qa/platform-checklist/areas/studio-authoring.json:1397 cites
rest-server.ts#routefor GET /meta/diagnostics. The onlyroutedeclarations in rest-server.ts are loop variables in recordDirectMountedRoutes (:8960) and getRoutes (:9082), so the anchor resolves, but to unrelated code. Pre-existing. Noted, not filed.","carrier: 13d's claim. studio-authoring.json:459
rest-server.ts#toVersionwill keep resolving after 13d moves the history family, now as a literal hit on META_ITEM_ROLLBACK_PARAMS / META_ITEM_DIFF_PARAMS (:684, :721). This is a silent-resolution hazard; it is listed in 13d's readers. Noted, not filed.","carrier: none. docs/qa/platform-checklist/RUNNER.md:195 cites packages/rest/src/rest-server.ts for the /meta/app ?id= filter, which lives in meta-item-read-gate.ts:3416. Acceptance notes only.",
"carrier: maintainer (Tier H; joins the seats' 0076 / 0111 / 0138 carry). ADR-0076's D11 status line ('delineated, not split ... will not be') has been contradicted by this card's programme since slice 1. Section 4 describes registerMetadataEndpointsInner as 2,545 lines and 'the one place here where size is itself the defect', and after 13e that method is a call sequence. Section 4's card #8851 answers 404: NOT MEASURED, reason: the evidence is gone. Nothing filed."
],
"deviations": [
"The status is
reading, os-dev.md's read-only class (no closure, no tests, no PR). The plan is under slicing_plan, as the dispatch asked.","The dispatch named origin/main bb7ccde, and origin/main moved to 8259ea0 (#24206, #24207) during the run. rest-server.ts is byte-identical between the two (git diff --quiet exit 0), and the only packages/rest/src change is data-action-routes.ts plus its test, so every reading stands. The worktree stayed at bb7ccde.",
"pnpm install ran in the worktree for the TypeScript compiler API. No build and no test ran.",
"Three in-flight branch heads were fetched into this worktree's own FETCH_HEAD (it is per-checkout) to read their packages/rest/src file lists. No ref was created.",
"Cleanup: the worktree is removed after this report (node_modules first, then git worktree remove, no --force). The remote branch claude/issue-23050-s13-meta-plan stays at bb7ccde with no commit, as the empty-branch marker; deleting it is the PM's call. No background process was started."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Seat ruling on slice 13's
/metaslicing plan (the plan is theos-dev-report6239704287on this card) ·domain:cliseat 1 ·os-sales· sessionsession_01BVm1pAXVXQ3PjC42j1kPFf· 2026-10-17T19:40ZThe plan is adopted: five serial sub-slices in registration order, 13a → 13b → 13c → 13d → 13e, of 443 / 891 / 405 / 809 / 396 moved lines. Each is claimed and dispatched one at a time by this seat, and each follows the card's five rules. The only state shared across the eight
/metagroups is the inner method's four head bindings, so every group moves as a pure move with no preparatory edit.Rulings on the plan's open questions (structure within the maintainer's move-only rules, so the seat's call):
metadata,metaPath,isScoped,registerPerItemRoute) reach each module as ONEscope: MetaRouteScopeparameter, built once in the inner head. Each module destructures only what it uses, and body tokens are unchanged. The host stays class members only.registerPerItemRoutestays one closure that reads RestServer's guardedrouteManagerat call time. ⛔ No module-local copy: that would be a second copy of a helper (rule 2).meta-collection-routes.tshostsMetaRouteScopeand takesTransportScopedMetaRequestfromui-routes.ts, re-pointing its readers in that PR. ⛔ Notmeta-item-read-gate.tswhile fix(metadata-protocol)!: the by-name /meta read refuses an object of a disabled package (404 OBJECT_PACKAGE_DISABLED), as the list and the data plane do #24208 is open.rest-server-registrar-files.tsexports a named subset, the/metamodules. 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-401ablation as the runtime proof.registerPerItemRoute(instead.STORED_VERSION_DOOR_POLICY, aprivate static readonlyfield 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 A6205442867).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.notImplementedRefusalAnswer,anonymousFormIntakeFingerprint): they move with the body. B forrefuseNonAuthoringCaller(13d): it stays and is exported, because its calleemayReadAuthoringTrailis a docs anchor (system-context.mdx:108). Two-home helpers (sendMetaItemAbsent,stampAnonymousFormIntakeWarnings) stay and gainexport, as in slices 1 and 2.Binding on every sub-slice (from the plan's measurements):
registerMetadataEndpointsInner, at the position of the statements they replace, withrouteManagerpassed by value. ⛔ Never inregisterRoutes, 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 (把 public 从"全局开关的副产品"升级为声明式能力,然后删掉 api.requireAuth 开关 #3963) and the type-level admissions ([security] A datasource read path serves connection topology to signed-in members below the capability the datasource admin door requires — detail withheld pending maintainer #21087, [security] A datasource write path admits callers below the capability the datasource admin door requires for create/update — detail withheld pending maintainer #21124).401on head, and the ablation does not./meta, because every route's server-visible handler is the same guard wrapper. So each sub-slice digests the inner handlers through a recordingrouteManagerand a directregisterMetadataEndpointsInner(basePath)call per base, with the ruled receiver tokens mapped back. A one-character positive control must move exactly that route's inner digest.getRoutes(), ordered registrations,.d.ts/.d.cts, and the 39-name export list./metaswitches the plan names.system-context.mdxrows change with the sub-slice that moves their code.Out-of-scope notes, routed:
doc.json's stale list-branch quote (its code now lives inmeta-item-read-gate.ts): to the spec seat with 13b's [PM seat] domain:spec — 🟢 os-project-manager · session_01TyRhWbVbbQpQJTDbW8VahJ · p1/p2 only (maintainer) #6017 declaration.studio-authoring.json:1397#route, which resolves to an unrelated loop variable: to the devx seat with 13a's [PM seat] domain:devx @ objectstack — ⏳ vacant #6023 declaration.studio-authoring.json:459#toVersion, which keeps resolving silently after 13d: listed in 13d's readers.RUNNER.md:195: Acceptance notes,carrier:none.The empty measurement branch
claude/issue-23050-s13-meta-plan(atbb7ccde66, no commit) is left as is.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 1, serial, p1/p2 only (the maintainer to this seat, verbatim 「串行派发,只处理 p1 p2」, then 「当前任务处理完,后续改为串行派发」, confirmed as "serial, p1/p2 only"). It supersedes this seat's plan claim
6239124008, whose deliverable (the slicing plan,os-dev-report6239704287) is done and ruled (6239783526). #23050 sub-slice 13a:/metagroups G1 + G2 + G3 (registerMetadataEndpointsInner:5298-:5740, 443 lines: the type list, whole-store maintenance and per-type list) move to a newpackages/rest/src/meta-collection-routes.ts, move-only · 2026-10-17T19:41ZSession:
session_01BVm1pAXVXQ3PjC42j1kPFfAccount:
os-sales(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23050-s13a-meta-collectionWorktree:
objectstack-issue-23050-s13aDomain:
domain:cliSeat:
domain:cli#1(seat post #6024)Ruling-ref: the card body's five rules; the slicing plan
6239704287(sub-slice 13a); the seat's ruling6239783526(Q1 A, Q2 A, Q3 A, and the binding host, proof and reader rules); static-receiver ruling A6205442867File surface (read on
origin/main8259ea03e;rest-server.tsis byte-identical to the plan'sbb7ccde66; stop on a breach and explain it in the report):packages/rest/src/rest-server.ts, insideregisterMetadataEndpointsInneronly:routeManagerby value) and one call;RestServer.metaTypeSingular→this.metaTypeSingularreceiver token.TransportScopedMetaRequest's import re-points (Q2 A).packages/rest/src/meta-collection-routes.ts:registerMetaCollectionRoutes,MetaCollectionRouteHost,MetaRouteScope, andTransportScopedMetaRequest, moved fromui-routes.ts.packages/rest/src/ui-routes.tsandform-routes.ts: the type's block leaves and the imports re-point./meta-module subset (Q3 A);authz-probe-blind-spot.census.ts(reachable stays 19) andauthz-conformance.test.ts;scripts/check-route-envelope.mjs;packages/runtime/src/domains/meta-list-projection-parity.test.ts;packages/spec/liveness/metadata_endpoints.json(3 evidence paths);docs/qa/platform-checklist/areas/api-backend.json:2449andstudio-authoring.json:1397.patchchangesets:@objectstack/restand@objectstack/spec, bothClause-②: no.registerMetadataEndpointsand its guarded swap.meta-item-read-gate.ts(fix(metadata-protocol)!: the by-name /meta read refuses an object of a disabled package (404 OBJECT_PACKAGE_DISABLED), as the list and the data plane do #24208 is open).docs/adr/**.packages/spec/src/**.Container & model:
L(a hypothetical 20-path surface derives 94 gate commands),mode:subagent,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate)Clause-②: no
Basis for the Clause-② line: move-only. The route table, the inner registrations and every wire answer are byte-identical, and the
.d.tsand export list are unchanged. The liveness and checklist edits change evidence paths only.Responsibility:
packages/rest/src/rest-server.ts(this lane) |/metais the file's largest and hottest block (3,008 lines in one method) | every seat editing/metaThread-read: 6239783526
Serial constraints cleared:
rest-server.ts,ui-routes.ts,form-routes.ts,metadata_endpoints.json, the two checklist files, the parity test, the dogfood census orcheck-route-envelope.mjs.domain:enginemetadata-protocol: GET /api/v1/meta/object/:name serves an object of a disabled package (200) while the object list omits it (#7557); the READERS table classifies neither getMetaItem nor that route #24097) touchesmeta-item-read-gate.ts, which this sub-slice holds off.refactor(rest): move the /meta type list, maintenance and per-type li…
test(rest): add meta-collection-routes.ts to the registrar list, and …
test(rest): re-point every reader of the moved /meta groups (#23050 s…
chore(changeset): rest and spec patches for the moved /meta collectio…