Repository navigation
refactor(rest): move registerFormEndpoints into form-routes.ts (#23050 slice 11) - #24054
objectstack-fleet[bot] wants to merge 9 commits into
Conversation
…23050 slice 11) The body of RestServer#registerFormEndpoints (GET {basePath}/forms/:slug and POST {basePath}/forms/:slug/submit) moves, re-indented one level, to one new module, packages/rest/src/form-routes.ts. The class keeps the private method, its docblock and its position as a FormRouteHost literal plus .call. Two tokens change: the two RestServer.translateOptionsFor(...) calls become this.translateOptionsFor(...) through the host, because a private static is not reachable by name outside the class. Body-only imports move with the body. Readers are re-pointed in the next commit. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
REST_SERVER_REGISTRAR_FILES grows 11 -> 12, so every whole-file reader of the registrars' code reads the moved form doors too. The readers that pin the list's length re-read their numbers in the next commit. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
…ce 11) Each reader whose reading included a moved line now reads it in form-routes.ts, with its sum unchanged: - the registrar-list readers re-read their numbers (length 11 -> 12): the query-number, canonical-query-AST, meta-draft read-door and execctx consumer censuses, the docblock-position pin, the two ui-view measurements and both dogfood authz readers; - check-route-envelope declares form-routes.ts at stringError 4 and siblingCode 6, and the rest-server.ts row goes 15 -> 11 and 13 -> 7 (sums 39 and 54 unchanged); - a new ADR anchor for form-routes.ts keeps check-adr-anchors' anchored decision-letter reading at 1772; - the access-security checklist source naming resolveFormBySlug points at form-routes.ts. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 25 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 145 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 2967f94728bedcc75c10067bf4b2c4039e3b3327 && git checkout 2967f94728bedcc75c10067bf4b2c4039e3b3327
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8133cc78052c95b6efd59487b64fe78f69860759 e62024d87b7e8a2be6340cafcaf0f4dfae496e11 && git checkout -B drift-repro 8133cc78052c95b6efd59487b64fe78f69860759 && git merge --no-ff e62024d87b7e8a2be6340cafcaf0f4dfae496e11
node scripts/docs-audit/affected-docs.mjs --json 8133cc78052c95b6efd59487b64fe78f69860759
|
…s.ts (#23050 slice 11) The seat's ruling A on slice 11's open question: packages/spec/liveness/view.json reads the moved form doors in three evidence strings, so their four path references follow the code to packages/rest/src/form-routes.ts (form.sections, form.sharing x2, formViews). Consumer text, verdicts and notes unchanged. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
…eness rows (#23050 slice 11) packages/spec ships liveness/ in its files[], so the three re-pointed evidence strings change what @objectstack/spec publishes: a patch changeset, as the earlier liveness-only evidence rewrites carried. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
…rs read it through (#23050 slice 11) The seat's amendment to ruling A for this one row: form-routes.ts never spells formViews, so the key-mention check refused the re-pointed citation. findPublicFormView reaches every form view through anonymousFormIntakeCandidates (re-exported by @objectstack/metadata-core as the same binding), declared in packages/spec/src/ui/anonymous-form-intake.ts, which reads v.formViews. The row cites that symbol; its quoted text is left as it stands. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
…'s new citation (#23050 slice 11) Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
Part of #23050
Clause-②: no
Slice 11 of the
rest-server.tssplit, by slice 1's pattern (landed note6136102437, steps 1–6), slice 2's getter refinement (6189011700) and slices 3–10's shape (slice 10: PR #23990). The body ofRestServer#registerFormEndpoints— the public (anonymous) form doors,GET {basePath}/forms/:slugandPOST {basePath}/forms/:slug/submit— moves to ONE new module,packages/rest/src/form-routes.ts. The route table, the ordered registrations, the publisheddist/*.d.tsand both bundles' export lists are byte-identical. Every handler's source is identical except the resolve door's, which differs by the two ruled receiver tokens alone (below).rest-server.tsgoes from 9,915 to 9,493 lines (basefd0e72abc). #23050 stays open for the later slices (CRUD,/meta).⚠ One reader is NOT re-pointed, by the dispatch's own rule, and it is red:
packages/spec/liveness/view.jsoncites the moved code in three evidence strings, andpnpm --filter @objectstack/spec run check:livenessexits 1 on this PR because two of them anchorrest-server.ts#findPublicFormView, a closure that now lives inform-routes.ts.packages/spec/**is not this lane's to edit, so the slice stops at it. The exact re-point and the routing question are in the section "Thepackages/specreader stopped at" below. This PR cannot go green until that reader is routed.The pattern, applied
Members the moved body reads, from an AST walk of the method (TypeScript compiler API with a type checker, symbol-resolved, base
fd0e72abc):registerFormEndpoints(JSDoc:9048, method:9083–:9528, body:9084–:9527, 444 lines).resolveProtocol:9149,:9230,:9489):2371, private asyncresolveAnonymousFormTenancy:9151):8973, private asyncreadFormObjectDefinitions:9170):8997, private asyncrouteManagerregistercalls (:9177,:9382):1993, privateresolveI18nService:9269):2390, private asyncbuildTranslationBundle:9270):3881, privateextractLocale:9271):3932, privatetranslateOptionsForRestServer.translateOptionsFor(…)(:9281,:9312):3978, private staticpackagedObjectBase:9282,:9313):3909, privateThe body has no bare
this, no element access onthis, nothisalias and nofunctionkeyword. The seat's text count (the same eight instance members with the same counts, plus the two static accesses) is confirmed;this.resolveI…isresolveI18nService.Control walk (the same walker, same base):
anonymousFormIntakeWarnings(:9017) readsresolveAnonymousFormTenancyandreadFormObjectDefinitionsonce each, and theregisterEmailEndpointsstub (:8952) readsemailServiceProvider,routeManager,resolveExecCtxandenforceAuthonce each — slice 8's host literal. Both match their source.Module-level reads (the same walk), every one an import; no module-level declaration of
rest-server.tsis read, so noexportwas added anywhere:anonymousFormIntakeCandidates,anonymousFormIntakePosture,anonymousFormIntakeUnavailability,anonymousFormObjectName(@objectstack/metadata-core),GetMetaItemsRequest,CreateDataRequest(types,@objectstack/spec/api),mapDataError(./error-response.js),logError(./log.js): still read elsewhere inrest-server.ts, so their imports stay and the module imports the same names from the same modules.anonymousFormIntakeWithdrawnIn,PUBLIC_FORM_SERVER_MANAGED_FIELDSandisExpectedRouteError: read ONLY by this body (the import line was their one other occurrence). Their import names moved with the body, leaving a pointer comment each inrest-server.ts's import block, as slices 5–7 and 10 did.TransportScopedMetaRequest(type): imported fromui-routes.ts, asrest-server.tsimports it — slice 10's seat ruling A (6220753007). Not moved, not copied.ServerScopedDataRequest(type): imported fromdata-action-routes.ts, where slice 1 moved it, asrest-server.tsimports it.Host shape (slice 1's shape B):
FormRouteHost(9 members) lists exactly the members the body reads, in the order it first reads them, each typedRestServer['m']— and the static as(typeof RestServer)['translateOptionsFor'], as slice 7 typedoverlayDocumentationInfo. The class keeps the private method with its name, docblock and position (JSDoc + signature, base:9048–:9083against head:9053–:9088, diff to 0). Its body is the host literal plusregisterFormEndpoints.call(host, basePath). Methods go as late-bound closures,(...a) => this.m(...a); the static as(...a) => RestServer.translateOptionsFor(...a). The call site inregisterRoutes'registerForBase(this.registerFormEndpoints(bp);, base:4782, head:4787, under no switch) is unchanged:registerRoutes(75 lines) diffs to 0 between base and head; the call moved 5 lines only because of the import block above it.Fields, checked one by one:
routeManagerregistercalls, top-level statements of the body):5151/:5155at head) wrapsregisterMetadataEndpointsInner, not this registrarNo other field is read, and no provider field: the protocol, the tenancy service, the object definitions, the i18n service, its bundle and locale, and the packaged base are each resolved per request by a METHOD, so no getter is owed and the provider ruling (
6215084478) has nothing to apply to.The
staticexception (6205442867), its two tokens:RestServer.translateOptionsFor(i18n, locale)at base:9281(the object schema's translation) and:9312(the section headings' translation) are nowthis.translateOptionsFor(i18n, locale)atform-routes.ts:307and:338.translateOptionsForis aprivate staticwhose whole body isreturn metaReadGate.metaTranslateOptions(i18n, locale);: it reads nothisand nothing but its two arguments, so its behaviour does not depend on its receiver (assumption 2, confirmed). No other token changed.Route switches (assumption 4): the registrar has no switch of its own —
registerForBasecalls it unconditionally, andRestServerConfigdeclares noapi.enable*flag or forms config for it — so no tenth configuration exists to add. Slice 10's nine configurations are used.Late binding, measured (a one-off vitest file, copied in, run under the verify lock, deleted; not committed). A source
RestServergets the seven instance methods planted BEFOREregisterRoutes()and the class static replaced; both doors are driven. Then seven new methods are planted and the static replaced again AFTER registration, and both doors are driven again. Each records its call.MEASURE before: GET 200 POST 201 {"id":"rec_early"} | order early:proto,early:tenancy,early:objects,early:proto,early:i18n,early:bundle,early:locale,early:opts,early:packaged,early:opts,early:packaged,early:proto,early:tenancy,early:objects,early:protoandMEASURE after: GET 200 POST 201 {"id":"rec_late"} | order late:proto,…,late:opts,late:packaged,late:opts,late:packaged,… | early calls after re-plant 0,Tests 1 passed (1). Every member, the static included, is reached at request time.scripts/ablation-replace.mjs): the host literal'sresolveAnonymousFormTenancyclosure replaced bythis.resolveAnonymousFormTenancy.bind(this)(anchor 1 → 0, blob7958e3cf728f→e7a647bd6557):late:tenancynever runs, the early member is called twice more,Tests 1 failed (1). Restored by the tool: blob7958e3cf728fequalsHEAD,git diff HEADempty.RestServer.translateOptionsForread at registration (anchor 1 → 0, blob7958e3cf728f→c8a0d8f605b8):late:optsnever runs, the early static is called twice more,Tests 1 failed (1). Restored, blob equalsHEAD, diff empty.rest-server.ts(blobb429a90b19d3) put in the tree under a trap gave the same twoMEASURElines,Tests 1 passed (1); restored withgit checkout HEAD, blob and diff proof.Proof of move-only
rest-server.tslines 9084–9527, one 4-space indent removed, againstform-routes.tslines 110–553:diffreports exactly the two receiver lines (body lines 198 and 229); withthis.translateOptionsFor(mapped back toRestServer.translateOptionsFor(,diffexits 0 over all 444 lines. Every non-empty line of the range carries the method indent, no line has trailing whitespace, and the two lines with an odd backtick count are//comment lines (inline code wrapped across them), so no template literal spans the cut..d.ts:packages/rest/dist/index.d.tsandindex.d.cts, built at basefd0e72abcbefore any edit and at the re-point headde5848dae, are both sha2567489de84bc6feccb7cb1f28bcbe4a97775324959c61c9bfc35e8a8c45a6f3bc9(the value slice 10 reported). The head bundles carry a head-only marker (registerFormEndpoints.call: 1 hit in each ofindex.jsandindex.cjs, 0 at base), so the declaration was rebuilt from the moved source.src/index.tsis untouched.b3246be6a1718700…on all four). No form-module name is among them.RestServeron a recording mock server, with a protocol answering every member, dumped under nine configs: default; scoped-optional; scoped-required; every provider set;enableDiscovery: false;enableOpenApi: false; both off under scoped-optional;enableSearch: falseunder scoped-optional;enableUi: falseunder scoped-optional. Each dump holdsgetRoutes()(functions as arity), the ordered server registration calls, a whitespace-free sha256 of every handler's source, and that source.routes.jsonandcalls.jsonare byte-identical between base and head in all nine configs.GET …/forms/:slug, once per mounted base (13 handlers over the nine configs), and in each of them the head source equals base oncethis.translateOptionsFor(is mapped back toRestServer.translateOptionsFor((13 of 13). The submit door's digest is identical everywhere.'[REST] Public form resolve error:'→'…error:!', throughablation-replace.mjs; anchor 1 → 0, blob464bbaa4d12c→9ec2ba34d8cf). Routes and calls were unchanged in all nine configs, and the handler digests moved in exactly the resolve door of each mounted base (1 or 2 per config) and nowhere else. Restored: blob464bbaa4d12cequalsHEAD,git diff HEADempty.Readers re-pointed (sums unchanged, no gate loosened)
packages/rest/src/rest-server-registrar-files.tsgainsform-routes.tswith a[#23050 slice 11]comment (length 11 → 12). Every reader that pins its length re-reads its number:rest-server-query-number-census.test.tsrest-server-canonical-query-ast.test.ts(repoproject)query:slotsform-routes.ts: 0(theRecordover the list needs the key: without it the floor readsundefined); length 11 → 12meta-draft-read-door-census.test.tsrest-server-docblock-position.test.tsform-routes.ts: 3(the module header, the host interface's block and the function's). The form body held no TSDoc block, sorest-server.tskeeps its 163. Length 11 → 12execctx-consumer-census.test.tsresolveExecCtx, so its host literal forwards none:MOVED_REGISTRARS_READING_NO_EXECCTXgainsform-routes.ts: 1(1 → 2), as slice 7 entered the discovery body. Forwarders stay 12 = (12 − 1) + 3 − 2. Length 11 → 12ui-view-route-identity.measurement.test.tsthis.enforceAuth(59,this.resolveExecCtx(74 unchangedui-view-route-tenancy.measurement.test.tsauthz-conformance.test.ts(realtime tripwires)authz-probe-blind-spot.census.ts,rest-server.tsrowthis.routeManager.register(63 = 18 + 9 + 5 + 7 + 8 + 1 + 6 + 4 + 1 + 1 + 1 + 2;private register*Endpoints(16;enforceAuth108form-routes.ts: this.routeManager.register(2;registrar list length12;populationRulenames the new module.enforceAuthdoes not move: the doors never call it, and the seam names no such memberReaders outside the list:
scripts/check-route-envelope.mjs(a gate's target table,domain:spec):form-routes.tsis declareddialectOnlywith ratchet [tracking] Envelope-position convergence line for packages/rest's flat dialect — the live ratchet owner #9559, as the ten earlier modules are, at its measuredstringError 4(each door's 400INVALID_REQUEST'slug is required'and each door's 404FORM_NOT_FOUND, a template literal) andsiblingCode 6(those four, plus the resolve door's 500FORM_RESOLVE_FAILEDand the submit door's 400VALIDATION_ERROR).rest-server.tsdrops by exactly those bodies:stringError15 → 11 andsiblingCode13 → 7, each recorded as "NOT progress — the same MOVE". The sums are unchanged: 11 + 13 + 8 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + 1 + 4 = 39, and 7 + 15 + 8 + 9 + 4 + 1 + 0 + 0 + 3 + 0 + 1 + 6 = 54. Its output differs from base only in therest-server.tsrow and the new row;--self-testis green.scripts/adr-anchors/packages__rest__src__form-routes.ts.json(new): the moved body carries twoADR-0131 D6citations, which leftrest-server.ts, an anchored file.check-adr-anchors' anchored decision-letter reading went 1772 → 1770 at the move commit; the new anchor (ADR-0131, ADR-0056 and ADR-0112, each named in the module) brings it back to 1772. Its invariant spells decisions as "its D6", so it adds no letter citation of its own. As slices 4, 6 and 7 did.docs/qa/platform-checklist/areas/access-security.json(domain:devx): the one source string readingrest-server.ts#registerFormEndpoints (resolveFormBySlug: …)now namesform-routes.ts#registerFormEndpoints, whereresolveFormBySluglives. The text after the anchor is unchanged, and no history entry is added (slice 7's precedent).Red before the re-points, each measured with the readers at their base text:
73d2a78fa(the list not yet grown): the dogfood blind-spot census readexpected 68 to be 70(population) andcontrol "this.routeManager.register(" moved … expected 61 to be 63: the two form registrations had left the reach of a reader that did not yet know the module.@objectstack/restlocal(287 files) andrepo(5 files) were green there, which is the reach loss the list exists to close.check:route-envelopereadform-routes.ts NOT DECLARED,stringError: found 11, declared 15andsiblingCode: found 7, declared 13;check-adr-anchorsread 1770 decision-letter citations (base 1772).9c26739ec(the list grown, the readers not yet re-pointed):localreadTest Files 6 failed | 281 passed (287),Tests 7 failed | 5897 passed | 326 skipped (6230)— the six length pins (execctx, meta-draft, query-number, docblock, and both ui-view tests) and the docblock pin's unfloored new module (finds at least undefined TSDoc blocks).reporeadTests 2 failed | 252 passed | 12 skipped (266)— the canonical-query-AST length pin and its missing floor (expected value must be number or bigint, received "undefined"). The dogfood pair readTests 2 failed | 106 passed (108)— the tripwire's length pin andcontrol "registrar list length" moved … expected 12 to be 11.de5848dae, then the merge head).The
packages/specreader stopped at (red, not edited)pnpm --filter @objectstack/spec run check:livenessexits 0 at base and 1 on this PR:All three are evidence strings in
packages/spec/liveness/view.jsonthat read the moved body:findPublicFormViewandresolveFormBySlugare closures ofregisterFormEndpoints, andsectionsis read only there. A fourth citation in the same file,form.sharing'srest-server.ts#resolveFormBySlug, stays green only because a docblock left inrest-server.tsnamesresolveFormBySlugin prose; it also reads a moved line. The re-point is mechanical, the consumer text unchanged:props.form.children.sections:packages/rest/src/rest-server.ts registerPublicFormEndpoints (…)→packages/rest/src/form-routes.ts registerFormEndpoints (…);props.form.children.sharing:packages/rest/src/rest-server.ts#findPublicFormView→packages/rest/src/form-routes.ts#findPublicFormView, andpackages/rest/src/rest-server.ts#resolveFormBySlug→packages/rest/src/form-routes.ts#resolveFormBySlug;props.formViews:packages/rest/src/rest-server.ts#findPublicFormView→packages/rest/src/form-routes.ts#findPublicFormView.packages/spec/**is not edited by this lane (the dispatch's rule), so the slice stops at it and the routing is asked of the seat in the report on #23050. These anchors cannot be pointed atform-routes.tsbefore this PR lands (the file does not exist onmain), and the PR cannot land with them pointing atrest-server.ts, so whichever route is chosen, the edit lands with this PR or ahead of it at a home that resolves on both trees.Readers examined and not re-pointed
content/docs/ui/forms.mdx("rest-server scans views" names the component, andRestServerstill serves both doors);packages/verify/src/handle.tsand its public-form-door test;packages/metadata-core/src/anonymous-form-intake.ts;packages/spec/src/ui/anonymous-form-intake.ts;packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts; the dogfoodanonymous-door-sweep.classification.ts(enforcedBy: 'RestServer.registerFormEndpoints; …') and the conformance matrix rowanonymous-door-public-form, whoseenforcementmust nameRestServer.registerFormEndpoints(ADR-0138 D2's table; the method is still that class's). No gate reds on any of them.docs/adr/0138-…citespackages/rest/src/rest-server.ts#RestServer.registerFormEndpointstwice; the symbol still resolves. Tier H, not edited; it joins the seat's ADR carry (0076, 0111, 0138).packages/runtime/src/domains/meta-list-projection-parity.test.tsreadsrest-server.tsby path, but only theGET ${metaPath}/:typehandler, which did not move.packages/rest/srcreach the moved code in its new file. Their outputs atorigin/main2b274600band at the final head differ only by the one scanned file or by this diff's own re-points:check-meta-type-normalized39 → 40 files;check-undeclared-dep-imports+1 file, +2 specifiers;check-dispatcher-error-vocabularyandcheck-error-status-conformance+1 file (classified sites unchanged);check-affected-docsroute sources 29 → 30 and registration call sites 24 → 25, by the*-routes.tsconvention;check-scripts-symbol-anchors3904 → 3908 file-level anchors (the route-envelope gate's new comments);check:platform-checklistcited sources 396 → 397 with 834 of 844 anchors resolving on both (the re-pointed source);check-adr-anchors69 → 70 anchored files, 1772 decision-letter citations on both. Identical:check-system-context-census,check-single-authz-resolver,check-rest-log-declared,check-durability-degradation-log-levelandcheck-filter-alias-parity.CHANGELOG.mdtext: not edited.Acceptance notes
rest-server.ts(kept, it ships in the.d.ts) still namesresolveFormBySlugand the two routes;resolveAnonymousFormTenancy's docblock points at "the comment aboveresolveFormBySlugin {@link registerFormEndpoints}", which now resolves to the private stub by name while the comment lives inform-routes.ts. A prose pointer, not edited by a move (no carrier).content/docs/ui/forms.mdx's architecture sketch shows the submit answering{ object, id, record }; the same page'sPOST …/submitsection (and the moved code, since rest(public forms):POST /forms/:slug/submitanswers the anonymous submitter with the whole stored record, so any field a hook derives from existing data (a duplicate match, an owner) reaches the internet #22437) answers{ id }only. Pre-existing doc drift, untouched by this move (no carrier).UNDECLARED_WHOLE_NUMBER_PARAM's docblock andui-routes.ts' "52 sites" comment.Gates and tests
Measured at this branch: base
fd0e72abc; the move73d2a78fa; the list9c26739ec; the re-pointsde5848dae; one merge oforigin/main2b274600b(22352ffd7: #24026, #24005, #24004, #24015, #24009, #24013, #24019; none touches a file in this diff, andrest-server.ts' blob is the same atfd0e72abcand2b274600b). All builds and tests went throughos-verify-lock, and every exit code was written to a file before any pipe. Final head:22352ffd7..d.tsat the merge head:mainmoved@objectstack/rest's declaration (meta-item-read-gate.ts, fix(rest)!: the /meta item and list reads serve a view stored with no label without an invented label #24013), so the merge head is compared withmain's own: a base leg putorigin/main'srest-server.ts(blobb429a90b19d3) into the merged tree under a trap and built@objectstack/rest, givingdist/index.d.ts/.d.ctssha2560a3edce6ab41821f…with the head-only marker at 0; restored (blob andgit diff HEADproof) and rebuilt, the head gives the same0a3edce6ab41821f…with the marker at 1. The export lists at the merge head are the base's (39,b3246be6a1718700…, ESM and CJS). Route dumps:origin/main's base equalsfd0e72abc's, and the merge head's equal the re-point head's, over all nine configs (diff -rexit 0 both). The late-binding legs ran atde5848dae; the merge changed neitherrest-server.tsnorform-routes.ts.pnpm --workspace-concurrency=2 --filter '@objectstack/rest...' --filter '@objectstack/verify...' build: exit 0 at base and at the merge head. Thenpnpm turbo run build --concurrency=2, the whole workspace: exit 0,Tasks: 76 successful, 76 total(3 cached), after which the.d.tsstill reads0a3edce6….pnpm --filter @objectstack/rest typecheck: exit 0 (tsc --noEmit, thencheck:test-typecheck: 0 file(s) / 0 error(s)).pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2:Test Files 288 passed (288),Tests 5943 passed | 326 skipped (6269).pnpm --filter @objectstack/rest exec vitest run --project repo --maxWorkers=2:Test Files 5 passed (5),Tests 254 passed | 12 skipped (266).pnpm --filter @objectstack/dogfood exec vitest run test/authz-probe-blind-spot.test.ts test/authz-conformance.test.ts:Tests 4 failed | 104 passed (108)at the merge head — all four aremain's, not this diff's:packages/runtime/src/route-ledger.ts — population … expected 92 to be 88, its positive control (same), and the matrix docblock'spackages/runtime/src/route-ledger.ts: docblock says 88 rows, the table holds 92(two cases). fix(runtime): mount every /api/v1/keys verb so an anonymous caller is answered 401 on the standalone server #24009 (9e797a897) added four rows topackages/runtime/src/route-ledger.tsand none of these pins moved. The same two blind-spot failures reproduce atorigin/main2b274600bin a clean comparison tree without this diff (the conformance file did not load there, its@objectstack/verifyunbuilt); every reader this PR re-points passes, and atde5848dae(before the merge) the pair readTest Files 2 passed (2),Tests 108 passed (108).pnpm exec eslint --no-inline-config --format jsonover the 13 changed.ts/.mjsfiles: exit 0, 13 results, 0 errors, 0 warnings. Repo-widepnpm lintis CI's (Lint & Repo Gates).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, at22352ffd7: its stderr names that commit, the--repoassertion holds, and it reads 15 paths against merge base2b274600b, 1167 changed lines (under the 3000 human-merge threshold). It derived 82 commands, all run at22352ffd7after the whole-workspace build: 81 exit 0, andpnpm --filter @objectstack/spec run check:livenessexits 1 (thepackages/specreader above).pnpm check:type-check-debtfirst answered 3 (PREREQUISITE NOT MET: its closure build never got a lock turn), and re-run under the lock it exits 0 (1 ledger entr(ies) re-measured … 26 raw tsc error(s) total, none above its recorded number).pnpm check:pm-dispatch-gatesran on its own (nohup, the battery took 305.5s) and exits 0.--ranreads:Run reconciliation — 82 derived, 82 run, 0 NOT-MEASURED, 0 UNRUN.check:dev-prereqsandcheck:skill-frame-freshness(local-only) were not run: this diff touches nopackages/specbuild input and no four-axis frame file.node scripts/pm/check-governed-merges.mjs --testover the 15 paths with--additions 701 --deletions 466:0 of 15 path(s) hit the register— NOT governed..d.ts, the export lists and the routes above).skip-changesetis the seat's to apply on its own check.99was re-queued under the same slot, and nothing was read off a run that did not get a turn.Generated by Claude Code