Repository navigation
refactor(rest): move registerApprovalsEndpoints into approvals-routes.ts, with one registrar list (#23050 slice 3) - #23670
Conversation
…pprovals-routes.ts Move-only: the body moves unedited, re-indented one level, and the class keeps the private method with its docblock as an ApprovalsRouteHost literal plus .call. routeManager goes by value (read only at registration); approvalsServiceProvider goes as a getter (read at request time, not readonly). strandedDecisionDetails' import moves with handleApprovalError. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG
…proval lines packages/rest/src/rest-server-registrar-files.ts is the one list of the files RestServer's registrars live in. The zero-contribution whole-file readers (query-number census, canonical-query-AST census, meta-draft door census, docblock-position pin, dogfood realtime tripwires) and the contributing ones (execctx census, ui-view measurements, dogfood blind-spot census) read every file on it, keep their sums, and pin its length. The route-envelope MODULES entry, the dispatcher-error-vocabulary row and three checklist anchors follow the moved lines, each sum unchanged. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG
…ire-code pin's prose Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FLwgbTSmGMrqsXkBDsiwfG
…-approvals-endpoints
📓 Docs Drift CheckThis PR changes 2 package(s): 49 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 35 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 5de455389665e7d1e2709b85b9de6b86705c8f3c && git checkout 5de455389665e7d1e2709b85b9de6b86705c8f3c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 57994c311f4acc576e1a1eb9ad4162d191d15905 9305039c63991fede85feddd2ea86dcdc1c10cb7 && git checkout -B drift-repro 57994c311f4acc576e1a1eb9ad4162d191d15905 && git merge --no-ff 9305039c63991fede85feddd2ea86dcdc1c10cb7
node scripts/docs-audit/affected-docs.mjs --json 57994c311f4acc576e1a1eb9ad4162d191d15905
|
✅ ACCEPT: PR #23670 at
|
Contract reviewServed-tier: Face: two gates' target paths edited by a move-only refactor ( ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each judged:
② Semver level
③ Boundary flagsEvery dev flag (PR acceptance notes,
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
Seat note: the contract-review record
The seat flips the PR ready and arms auto-merge through the relay. Generated by Claude Code |
Part of #23050
Clause-②: no
Slice 3 of the
rest-server.tssplit, by slice 1's pattern (landed note6136102437, steps 1-6) with slice 2's getter refinement (6189011700): the body ofRestServer#registerApprovalsEndpoints(the approval request list and read, theapprove/rejectdecisions,recall, therevise/resubmitflow moves, thereassign/remind/request-info/commentthread interactions and the audit trail; 12 routes from 7 registration sites) moves, unedited, to the new modulepackages/rest/src/approvals-routes.ts. It is move-only. Routes, the route ledgers and the publisheddist/*.d.tsare byte-identical, and every reader whose reading included a moved line now reads it where it is.The slice also adds the one list the seat ruled for (
6189011700, answering PR #23642's open question 2):packages/rest/src/rest-server-registrar-files.ts, the filesRestServer's registrars live in. The whole-file readers ofrest-server.tsimport it, keep their sums over it, and pin its length. It is not a gate.rest-server.tsgoes from 12,520 to 12,070 lines. The card stays open for the later slices.The pattern, applied
Members the moved body reads, from an AST walk of the method at base
c7dd48ab4(line numbers there): fieldsrouteManager:1960 andapprovalsServiceProvider:2031; methodsenforceAuth:2406 andresolveExecCtx:2806. That is 4 members (7 reads each ofrouteManager,enforceAuthandresolveExecCtx, 2 ofapprovalsServiceProvider). The body has no barethisand no element access onthis.Module-level names the body reads:
APPROVAL_REQUEST_LIST_PARAMS,UNDECLARED_WHOLE_NUMBER_PARAMandreadDeclaredQueryNumber, all three already exported fromrest-server.ts; andstrandedDecisionDetailsandsendError(assendEnvelopeError) from@objectstack/types,classifiedRefusalAnswer,nestedRefusalEnvelope,sandboxFaultAnswerandhandleRouteErrorfrom./error-response.js,logErrorfrom./log.js,refuseRepeatedQueryParamsandrefuseUnknownQueryParamsfrom their modules. No export was added anywhere.strandedDecisionDetailswas used only by the moved body, so its import and its comment move with it, andrest-server.tskeeps a two-line pointer comment where it was.Host shape (slice 1's shape B): the class keeps
private registerApprovalsEndpoints(basePath)with its name, docblock and position. Its body is now oneApprovalsRouteHostliteral andregisterApprovalsEndpoints.call(host, basePath). Each interface member is typed as the class member (an indexed access onRestServer). Methods go as late-bound closures,(...a) => this.m(...a).Fields, checked one by one:
routeManagerregistercall, the three route factories' included, since each factory runs during registration)registerMetadataEndpoints) wrapsregisterMetadataEndpointsInner, not this registrarapprovalsServiceProviderresolveService, from every handler)readonlyThe getter is measured, not argued. A one-off test boots a source
RestServerwith no provider, drivesGET /api/v1/approvals/requests/:id, then assignsapprovalsServiceProvideron the instance AFTERregisterRoutes()and drives it again:501 NOT_IMPLEMENTED, then200 {"id":"r1","status":"pending"}. The door reads the live field, as it did when it readthis.approvalsServiceProvideron the server.scripts/ablation-replace.mjs, the getter replaced byapprovalsServiceProvider: this.approvalsServiceProvider,; anchor 1 to 0, replacement 0 to 1, read back on disk inside the run):501, then501again. The value shape would freeze the field at registration. Restored by the tool and by a trap: blobda10d3841b4dequalsHEAD,git diff HEADempty, the one-off test file deleted.The registrar list
packages/rest/src/rest-server-registrar-files.tsexportsREST_SERVER_REGISTRAR_FILES(rest-server.ts,data-action-routes.ts,batch-routes.ts,approvals-routes.ts), its element type, and the repo-relative directory for readers outside the package. It is test-side only: nothing insrc/index.tsreaches it, so tsup never emits it (measured:dist/names it nowhere), the same placement assrc/http-request-test-builder.ts.The five zero-contribution whole-file readers now read every file on it. Each keeps its sum and pins the length (4). The moved body contributes zero to each, measured per file with each reader's own rule at base and head:
rest-server-query-number-census.test.tsrest-server-canonical-query-ast.test.tsquery:slotsmeta-draft-read-door-census.test.tsrest-server-docblock-position.test.tsrest-server.ts, sum 10authz-conformance.test.ts)rest-server.tsprobes gainalsoReads(every other list file);probeSourcereads them withfile; the ZERO case pins length 4 and that each probe's text is exactly the four files joinedThe tripwire probes'
filestayspackages/rest/src/rest-server.tsand their keys are unchanged, so the probe-table census (entries 25 / files 20 / keys 33) and the matrix header's count do not move.Three readers that slices 1 and 2 extended by hand, because the moved lines contributed to them, now read the list too rather than gaining a fourth name:
execctx-consumer-census.test.ts, the twoui-view-route-*.measurement.test.tsfiles and the dogfood blind-spot census'srest-server.tsrow (sums below).Proof of move-only
.d.ts:packages/rest/dist/index.d.tsandindex.d.ctsare byte-identical to basec7dd48ab4, sha2567489de84bc6feccb7cb1f28bcbe4a97775324959c61c9bfc35e8a8c45a6f3bc9for all four files (cmpsilent), rebuilt at the final head9305039c6. The ESM and CJS bundles load, 39 exports each, one sorted export-name list (sha256b3246be6a171...base and head).packages/rest/src/index.tsis untouched. The bundle JS bytes differ, because code moved within it.c7dd48ab4, head9305039c6): a sourceRestServeron a recording mock server, with a protocol that implements every member, dumped under three configs.getRoutes()(functions recorded as arity), the ordered server registration calls, and a whitespace-free source hash of every handler are byte-identical base vs head, 9 dumps of 9:ee3a764e19bd...ecde0d, calls3c5754404a85...6319dd, handler sources236986995419...40ef05(75 routes, 12 approvals)eb7a21beb5de...56d66a/64c05f47329e...6b5f00/6e7abd35ba0c...70ab2c(150 routes, 24 approvals)d6d0e5068384...9263e6/8ede6c93247d...a94b02e/54656e8e8abf...ac66c424(75 routes, 12 approvals)packages/rest/src/rest-route-ledger.ts,packages/runtime/src/route-ledger.tsandpackages/rest/src/index.tsare untouched.rest-server.tslines 11887-12347 (git show c7dd48ab4:packages/rest/src/rest-server.ts | sed -n 11887,12347p), with one 4-space indent level removed (sed 's/^ //'), againstapprovals-routes.tslines 87-547:diffexits 0 and prints nothing (461 lines). The body holds no multi-line string or template literal, so re-indenting changes no runtime string.git diff --color-moved=plain --color-moved-ws=allow-indentation-change c7dd48ab4 HEADover the two files): 464 deleted and 471 added lines are detected as moved.rest-server.tsis +15 / -465: the 12-line stub, the new import plus its comment (2), and the 2-line pointer comment replacing thestrandedDecisionDetailsimport (5 lines); one};line the diff keeps in place.approvals-routes.tsadds the module docblock, the imports, theApprovalsRouteHostinterface and its docblock, and the function docblock and signature.Readers re-pointed (sums unchanged, no gate loosened)
scripts/check-route-envelope.mjs(a gate's target paths; see the note at the end):approvals-routes.tsis discovered by the*-routes.tsconvention and declareddialectOnlywith ratchet [tracking] Envelope-position convergence line for packages/rest's flat dialect — the live ratchet owner #9559, as the two earlier modules are.rest-server.tsstringError 18 to 17 and siblingCode 31 to 22; the new module carries 1 and 9, both measured by the gate. Sums 17 + 13 + 8 + 1 = 39 and 22 + 15 + 8 + 9 = 54, unchanged. The gate's output differs from base only in those lines and the module counts (13 to 14 modules, 4 to 5 ratcheted).packages/runtime/src/dispatcher-error-vocabulary.ts, the declaration half ofcheck:dispatcher-error-vocabulary(also a gate target path): theruntime-pinnedrow for the template familyAPPROVAL_*_FAILEDnamesapprovals-routes.tswhere it namedrest-server.ts. Re-pointed, not re-judged: 57 sites, 57 classified, as at base. The file is not reachable from@objectstack/runtime's entry, so nothing published moves.docs/qa/platform-checklist/areas/approvals.json: the two anchorsrest-server.ts#flowMoveRouteandrest-server.ts#handleApprovalErrornameapprovals-routes.ts, where both declarations now live.docs/qa/platform-checklist/areas/records-forms.json:rest-server.ts#mappingresolved, at base, to the onlymappingdeclaration in that file, which ishandleApprovalError's prefix map. It now namesapprovals-routes.ts#mapping, the same declaration. See the acceptance notes: that anchor's prose describes something the declaration never held.check:platform-checklistoutput is identical base vs head for all three anchors (same per-family resolved counts, so no floor moves).packages/rest/src/execctx-consumer-census.test.ts: reads the list; 62 sites = 41 + 9 + 5 + 7, unchanged. Mentions 85 to 89: the four new ones are this slice's seam (forwarder, two; interface member, two). Three forwarders now, one per moved module, pinned as before (SITE_FILES.length - 1); the stack-frame pattern is built from the list.packages/qa/dogfood/test/authz-probe-blind-spot.census.ts: therest-server.tsrow's population rule reads the list, so 70 / 19 / 51 is unchanged; the seven moved registration sites are stillRestServer's and still in the blind spot.this.routeManager.register(stays 63 (42 + 9 + 5 + 7), with one control per module (the new one 7) andregistrar list length4.enforceAuth68 to 72, re-anchored: four seam occurrences, no call site added.ui-view-route-identity.measurement.test.tsandui-view-route-tenancy.measurement.test.ts:REGISTRAR_SOURCESis the list. Over itthis.enforceAuth(reads 50 andthis.resolveExecCtx(65 (49 and 64 at base, +1 each for the forwarder);this.enforceEnvironmentOwnership(stays 1. Both pin the list's length.rest-approvals-wire-codes.test.ts: the header's present-tense pointer to the three route factories namesapprovals-routes.ts.Red before the re-points, each measured with the move in place and that reader not yet moved. The suites ran at
eaadafb41, the move alone: therestsuite readTests 5 failed | 5840 passed | 326 skipped (6171), all five inexecctx-consumer-census.test.ts(62 sites read 55, 46 bare read 39 twice, and the two driven sweeps 40 against at least 46 and 26 against at least 30); the dogfood pair readTests 2 failed | 106 passed (108), population 63 against 70 and the controlthis.routeManager.register(56 against 63.check:route-envelope:approvals-routes.ts NOT DECLARED, andrest-server.tsstringError 17 against 18, siblingCode 22 against 31.check:dispatcher-error-vocabulary:[stale-row] ... 'APPROVAL_*_FAILED' at packages/rest/src/rest-server.ts (objlittemplate) but the scan no longer finds it, and[unclassified-site] packages/rest/src/approvals-routes.ts.check:platform-checklist: 11 problems against base's 8, the three ABSENT SYMBOL anchors above.ui-viewreaders would have stayed GREEN: their whole-file checks are floors (greater than 40), and without the list they read 43 and 58. That is the silent narrowing the list exists to close, and the reason they import it rather than staying as they were.Readers examined and not re-pointed
auth-gate-allowlist-fault-window.measurement.test.ts,execctx-authz-input-seam-reachability.test.ts, runtimemeta-list-projection-parity.test.ts: they slice other members ofrest-server.ts(the gate block,computeExecCtx,/meta).authz-store-unavailable.test.ts: it discovers transports byresolveAuthzContext, which the moved body does not call.packages__rest__src__rest-server.ts.json(ADR-0045,filterAppForUser) is unaffected.docs/adr/0076-...mdnamesregisterApprovalsEndpointsas an example; the private method is still the registrar, so the pointer still lands.60d82243b):check-durability-degradation-log-level,check-system-context-census,check-single-authz-resolver,check-rest-log-declared,check:liveness,check:cross-package-test-inputs,check:issue-citations,check:test-source-alias,check:rest-log-spy-declared. Identical except the scanned-file counts (+2, the two new files):check-meta-type-normalized,check-nul-bytes,check-adr-anchors,check:error-status-conformance,check:doc-authoring,check-undeclared-dep-imports(+1 specifier).check-scripts-symbol-anchors+4 file-level anchors (the new comments incheck-route-envelope.mjs);check-affected-docs21 to 22 route sources (the new module, admitted by the filename convention).rest-server.ts:NNNNNcitation at or above :11000 is:11108, in a release-owned CHANGELOG.Acceptance notes
packages/specprose, not edited (the claim keepspackages/specout):packages/spec/src/api/error-code-ledger.zod.tsnamesrest-server.tsas the home of the approvals route factories andhandleApprovalErrorin three comments (about :199, :291 and :1274). They are//comments, read by no gate. They now describe code inapprovals-routes.ts. Named in the report for thedomain:specseat.docs/qa/platform-checklist/areas/records-forms.json's#mappinganchor (the OCC item, "CONCURRENT_UPDATE → 409 mapping") lands, at base and at head, onhandleApprovalError's prefix map, which holds noCONCURRENT_UPDATErow;packages/rest/srcspellsCONCURRENT_UPDATEonly in one comment. The move keeps it landing where it landed. Re-judging which declaration that item should cite is not a move.packages/plugins/plugin-approvals/src/record-reader-visibility.test.ts(about :363) cites "rest-server.tshandleApprovalError" in a comment. Another lane's file, read by nothing; not edited.scripts/check-self-test-wired.mjs's header quotes a past[stale-row]message that namesrest-server.ts. It is a quotation of output, not a pointer; not edited.rest-approvals-wire-codes.test.tskeeps one dated measurement sentence ("deleting the row fromrest-server.tsreds THREE cases"), true when written; only its present-tense pointer was re-pointed.docs/adr/0076-...mdD11 records the maintainer's 2026-08-15 ruling thatrest-server.tswould be delineated, not split; the card's later direction (quoted on [maintainer] refactor(rest): splitpackages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050) is what slices 1 to 3 execute. The ADR is a governed surface and is not edited here.rest-server.tsfrom about :42 to :318 shift by -3, from :322 to the moved block by -1, and after it by -450 (base :12351 is head :11901).Gates and tests
Measured at
9305039c6: this branch (basec7dd48ab4) plus one merge oforigin/main1ced0f82b, which touched no file in this diff (cli, metadata-core, pm scripts and four other checklist areas). All builds and tests went throughos-verify-lock; exit codes were captured to files before any pipe.pnpm --filter @objectstack/rest test(projectlocal): Test Files 285 passed (285), Tests 5852 passed / 326 skipped (6178). Basec7dd48ab4: 5845 passed / 326 skipped (6171); the +7 are this PR's new cases (query-number, meta-draft and docblock list pins, plus the docblock pin's two modules that are new to it).pnpm --filter @objectstack/rest test:repo(projectrepo): Test Files 5 passed (5), Tests 222 passed / 4 skipped (226). Base: 213 passed / 2 skipped (215); the +9 / +2 are the canonical-query-AST census's two new modules and its list control.pnpm --filter @objectstack/rest typecheck: exit 0 (tsc --noEmit, thencheck:test-typecheck: 0 file(s) / 0 error(s)).pnpm --filter @objectstack/dogfood exec vitest run test/authz-probe-blind-spot.test.ts test/authz-conformance.test.ts: Test Files 2 passed (2), Tests 108 passed (108).pnpm --filter @objectstack/runtime exec vitest run src/error-envelope.conformance.test.ts(the test that imports the edited ledger): 53 passed.pnpm --filter @objectstack/runtime typecheck: exit 0.pnpm --filter @objectstack/dogfood typecheck, reason: exit 2 on 52 errors, 51 of them TS2307 for workspace packages with nodistin this container (connector-mcp,connector-rest,connector-openapi,cloud-connection,setup,connector-slack,account) and one TS7006 behind them in an untouched file; none in either touched dogfood file. CI's typecheck builds first.eslint --no-inline-config --format jsonoverpackages/rest, the two touched dogfood files,packages/runtime/src/dispatcher-error-vocabulary.tsandscripts/check-route-envelope.mjs: 327 files, 0 errors, 0 warnings, every touched lintable file in the result set. Repo-widepnpm lintis CI's.dispatch-gates --commands --repo objectstack-ai/objectstack, no paths, after the last commit: 80 commands (stderr names commit9305039c6).--ran:Run reconciliation — 80 derived, 79 run, 1 NOT-MEASURED, 0 UNRUN.79 exit 0.pnpm check:dual-build-cjs-loads, reason: PREREQUISITE NOT MET (exit 3; workspace packages withoutdist/in this container). Stand-in:packages/rest/dist/index.cjsandindex.jsboth load, 39 exports each. CI runs the gate over a full build.pnpm check:route-envelopeandpnpm check:durability-log-levelare in the derivation and both exit 0.pnpm check:platform-checklistexits 0 at this head (origin/mainbrought the fix for its eight pre-existing findings); before the merge its output was identical to base's.check:dev-prereqsandcheck:skill-frame-freshness(local-only) were not run: nopackages/specbuild input and no four-axis frame file is touched.Per command (exit code):
node scripts/check-ci-filter-parity.mjsexit 0node scripts/check-closing-keyword-parity.mjs --self-testexit 0node scripts/check-closing-keyword-parity.mjsexit 0node scripts/check-comment-mask-adoption.mjs --self-testexit 0node scripts/check-comment-mask-adoption.mjsexit 0node scripts/check-comment-mask-corpus.mjsexit 0node scripts/check-declaration-mirrors.mjs --self-testexit 0node scripts/check-declaration-mirrors.mjsexit 0node scripts/check-dts-emitted.mjs --self-testexit 0node scripts/check-issue-citations.mjsexit 0node scripts/check-keyed-text-bounds.mjs --self-testexit 0node scripts/check-keyed-text-bounds.mjsexit 0node scripts/check-platform-object-tenancy-census.mjs --self-testexit 0node scripts/check-platform-object-tenancy-census.mjsexit 0node scripts/check-plugin-teardown-shape.mjs --self-testexit 0node scripts/check-plugin-teardown-shape.mjsexit 0node scripts/check-registry-log-declared.mjs --self-testexit 0node scripts/check-registry-log-declared.mjsexit 0node scripts/check-rest-log-spy-declared.mjs --self-testexit 0node scripts/check-rest-log-spy-declared.mjsexit 0node scripts/check-scripts-symbol-anchors.mjs --self-testexit 0node scripts/check-scripts-symbol-anchors.mjsexit 0node scripts/check-self-test-wired.mjs --self-testexit 0node scripts/check-self-test-wired.mjsexit 0node scripts/check-self-test-workflow-commands.mjs --self-testexit 0node scripts/check-self-test-workflow-commands.mjsexit 0node scripts/check-system-context-census.mjs --self-testexit 0node scripts/check-system-context-census.mjsexit 0node scripts/check-undeclared-dep-imports.mjs --self-testexit 0node scripts/check-undeclared-dep-imports.mjsexit 0node scripts/check-whole-set-label-write.mjs --self-testexit 0node scripts/check-whole-set-label-write.mjsexit 0node scripts/docs-audit/check-affected-docs.mjsexit 0node scripts/docs-audit/check-drift-comment.mjsexit 0node scripts/pm/bare-root-worklist.mjs --self-testexit 0pnpm --filter @objectstack/lint run check:doc-formula-expressionsexit 0pnpm --filter @objectstack/spec run check:duration-unit-keysexit 0pnpm --filter @objectstack/spec run check:empty-stateexit 0pnpm --filter @objectstack/spec run check:livenessexit 0pnpm --filter @objectstack/spec run check:strictness-ledgerexit 0pnpm --filter @objectstack/spec run check:variant-docsexit 0pnpm check:agent-test-spellingexit 0pnpm check:authz-resolverexit 0pnpm check:bash32-floorexit 0pnpm check:cli-command-idsexit 0pnpm check:cross-package-test-inputsexit 0pnpm check:dispatcher-error-vocabularyexit 0pnpm check:doc-authoringexit 0pnpm check:driver-memory-censusexit 0pnpm check:dts-closureexit 0pnpm check:dual-build-cjs-loadsexit 3pnpm check:durability-log-levelexit 0pnpm check:engine-double-contractexit 0pnpm check:entry-guardexit 0pnpm check:error-status-conformanceexit 0pnpm check:gitlink-declaredexit 0pnpm check:issue-citationsexit 0pnpm check:lean-entry-closureexit 0pnpm check:logger-receiver-detachexit 0pnpm check:nul-bytesexit 0pnpm check:objectql-double-limitexit 0pnpm check:org-identifierexit 0pnpm check:page-declaration-shapeexit 0pnpm check:parse-guardexit 0pnpm check:platform-checklistexit 0pnpm check:pm-dispatch-gatesexit 0pnpm check:pnpm-filter-targetsexit 0pnpm check:published-filesexit 0pnpm check:query-options-erasureexit 0pnpm check:ratchet-remedy-authorityexit 0pnpm check:refd-timer-probeexit 0pnpm check:route-envelopeexit 0pnpm check:slot-lookupexit 0pnpm check:sourcemap-no-sources-contentexit 0pnpm check:test-source-aliasexit 0pnpm check:tier-file-adoptionexit 0pnpm check:type-check-coverageexit 0pnpm check:type-check-debtexit 0pnpm check:watch-hint-literalexit 0pnpm check:where-matcherexit 0Changeset: none.
@objectstack/restshipsfiles: ["dist", "README.md", "CHANGELOG.md"]. Itsdist/index.d.tsandindex.d.ctsare byte-identical, its export list is identical, and its route table is identical under three configs; the bundle JS differs only by where the code sits.@objectstack/runtime's one edit is in a module its entry does not reach (rebuilt at9305039c6:packages/runtime/distholds no file containingAPPROVAL_*_FAILED,UNREGISTERED_CODE_SITESorapprovals-routes.ts, while the controlHttpDispatcheris found in 4 files). The other files are tests, a private package, a script and QA data.skip-changesetis the seat's to apply on its own check.Contract review: this PR edits two gate target paths:
scripts/check-route-envelope.mjs(one newMODULESentry and two loweredrest-server.tscounts) and thefileof one row inpackages/runtime/src/dispatcher-error-vocabulary.ts, the declaration half ofcheck:dispatcher-error-vocabulary.Generated by Claude Code