Skip to content

refactor(rest): move registerFormEndpoints into form-routes.ts (#23050 slice 11) - #24054

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-23050-s11-form-endpoints
Oct 17, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-23050-s11-form-endpoints

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #23050
Clause-②: no

Slice 11 of the rest-server.ts split, by slice 1's pattern (landed note 6136102437, steps 1–6), slice 2's getter refinement (6189011700) and slices 3–10's shape (slice 10: PR #23990). The body of RestServer#registerFormEndpoints — the public (anonymous) form doors, GET {basePath}/forms/:slug and POST {basePath}/forms/:slug/submit — moves to ONE new module, packages/rest/src/form-routes.ts. The route table, the ordered registrations, the published dist/*.d.ts and 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.ts goes from 9,915 to 9,493 lines (base fd0e72abc). #23050 stays open for the later slices (CRUD, /meta).

packages/spec/liveness/view.json: seat ruling A (6225503839) and its formViews amendment are applied. form.sections and form.sharing cite form-routes.ts; formViews cites packages/spec/src/ui/anonymous-form-intake.ts#anonymousFormIntakeCandidates, the code on the read path that names the key. The quoted code is left as it stands. check:liveness exits 0 (734 pair(s) asked, 733 anchored, 1 exempt). Both edits are declared on domain:spec's #6017 (6225495254, amended 6225900470).

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).

kind member reads class declaration
method resolveProtocol 3 (:9149, :9230, :9489) :2371, private async
method resolveAnonymousFormTenancy 1 (:9151) :8973, private async
method readFormObjectDefinitions 1 (:9170) :8997, private async
field routeManager 2, the two register calls (:9177, :9382) :1993, private
method resolveI18nService 1 (:9269) :2390, private async
method buildTranslationBundle 1 (:9270) :3881, private
method extractLocale 1 (:9271) :3932, private
static translateOptionsFor 2, as RestServer.translateOptionsFor(…) (:9281, :9312) :3978, private static
method packagedObjectBase 2 (:9282, :9313) :3909, private

The body has no bare this, no element access on this, no this alias and no function keyword. The seat's text count (the same eight instance members with the same counts, plus the two static accesses) is confirmed; this.resolveI… is resolveI18nService.

Control walk (the same walker, same base): anonymousFormIntakeWarnings (:9017) reads resolveAnonymousFormTenancy and readFormObjectDefinitions once each, and the registerEmailEndpoints stub (:8952) reads emailServiceProvider, routeManager, resolveExecCtx and enforceAuth once 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.ts is read, so no export was 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 in rest-server.ts, so their imports stay and the module imports the same names from the same modules.
  • anonymousFormIntakeWithdrawnIn, PUBLIC_FORM_SERVER_MANAGED_FIELDS and isExpectedRouteError: 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 in rest-server.ts's import block, as slices 5–7 and 10 did.
  • TransportScopedMetaRequest (type): imported from ui-routes.ts, as rest-server.ts imports it — slice 10's seat ruling A (6220753007). Not moved, not copied.
  • ServerScopedDataRequest (type): imported from data-action-routes.ts, where slice 1 moved it, as rest-server.ts imports 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 typed RestServer['m'] — and the static as (typeof RestServer)['translateOptionsFor'], as slice 7 typed overlayDocumentationInfo. The class keeps the private method with its name, docblock and position (JSDoc + signature, base :9048–:9083 against head :9053–:9088, diff to 0). Its body is the host literal plus registerFormEndpoints.call(host, basePath). Methods go as late-bound closures, (...a) => this.m(...a); the static as (...a) => RestServer.translateOptionsFor(...a). The call site in registerRoutes' 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:

field read when reassigned after registration? handed over as
routeManager registration only (two register calls, top-level statements of the body) its one swap (:5151/:5155 at head) wraps registerMetadataEndpointsInner, not this registrar value

No 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 static exception (6205442867), its two tokens: RestServer.translateOptionsFor(i18n, locale) at base :9281 (the object schema's translation) and :9312 (the section headings' translation) are now this.translateOptionsFor(i18n, locale) at form-routes.ts :307 and :338. translateOptionsFor is a private static whose whole body is return metaReadGate.metaTranslateOptions(i18n, locale);: it reads no this and 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 — registerForBase calls it unconditionally, and RestServerConfig declares no api.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 RestServer gets the seven instance methods planted BEFORE registerRoutes() 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.

  • Head: 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:proto and MEASURE 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.
  • Ablation A (scripts/ablation-replace.mjs): the host literal's resolveAnonymousFormTenancy closure replaced by this.resolveAnonymousFormTenancy.bind(this) (anchor 1 → 0, blob 7958e3cf728f → e7a647bd6557): late:tenancy never runs, the early member is called twice more, Tests 1 failed (1). Restored by the tool: blob 7958e3cf728f equals HEAD, git diff HEAD empty.
  • Ablation B: the static's closure replaced by RestServer.translateOptionsFor read at registration (anchor 1 → 0, blob 7958e3cf728f → c8a0d8f605b8): late:opts never runs, the early static is called twice more, Tests 1 failed (1). Restored, blob equals HEAD, diff empty.
  • Base leg: the base rest-server.ts (blob b429a90b19d3) put in the tree under a trap gave the same two MEASURE lines, Tests 1 passed (1); restored with git checkout HEAD, blob and diff proof.

Proof of move-only

  • Moved body: base rest-server.ts lines 9084–9527, one 4-space indent removed, against form-routes.ts lines 110–553: diff reports exactly the two receiver lines (body lines 198 and 229); with this.translateOptionsFor( mapped back to RestServer.translateOptionsFor(, diff exits 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.ts and index.d.cts, built at base fd0e72abc before any edit and at the re-point head de5848dae, are both sha256 7489de84bc6feccb7cb1f28bcbe4a97775324959c61c9bfc35e8a8c45a6f3bc9 (the value slice 10 reported). The head bundles carry a head-only marker (registerFormEndpoints.call: 1 hit in each of index.js and index.cjs, 0 at base), so the declaration was rebuilt from the moved source. src/index.ts is untouched.
  • Export lists: the base and head ESM and CJS bundles each load with 39 exports and one sorted export-name list (sha256 b3246be6a1718700… on all four). No form-module name is among them.
  • Routes: a source RestServer on 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: false under scoped-optional; enableUi: false under scoped-optional. Each dump holds getRoutes() (functions as arity), the ordered server registration calls, a whitespace-free sha256 of every handler's source, and that source.
    • Route counts 75 / 150 / 75 / 75 / 73 / 73 / 142 / 148 / 148 — slice 10's — with 2 or 4 form doors each. A second base run was identical to the first.
    • routes.json and calls.json are byte-identical between base and head in all nine configs.
    • Handler digests differ in exactly the resolve door, GET …/forms/:slug, once per mounted base (13 handlers over the nine configs), and in each of them the head source equals base once this.translateOptionsFor( is mapped back to RestServer.translateOptionsFor( (13 of 13). The submit door's digest is identical everywhere.
  • The handler digest can fail: a positive control added one character to the moved resolve handler ('[REST] Public form resolve error:' → '…error:!', through ablation-replace.mjs; anchor 1 → 0, blob 464bbaa4d12c → 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: blob 464bbaa4d12c equals HEAD, git diff HEAD empty.

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

packages/rest/src/rest-server-registrar-files.ts gains form-routes.ts with a [#23050 slice 11] comment (length 11 → 12). Every reader that pins its length re-reads its number:

reader sum over the list change
rest-server-query-number-census.test.ts 9 = 8 + 1 + 0 ×10 the form doors read only the slug path parameter, as a string; length 11 → 12
rest-server-canonical-query-ast.test.ts (repo project) 4 = 1 + 3 + 0 ×10 query: slots floor form-routes.ts: 0 (the Record over the list needs the key: without it the floor reads undefined); length 11 → 12
meta-draft-read-door-census.test.ts 8 = 6 + 0 + 0 + 0 + 0 + 2 + 0 ×6 the form doors read the environment's lists, never a draft; length 11 → 12
rest-server-docblock-position.test.ts floors 4 + 3 + 3 + 8 + 3 + 8 + 5 + 3 + 3 + 4 + 3 = 47 floor form-routes.ts: 3 (the module header, the host interface's block and the function's). The form body held no TSDoc block, so rest-server.ts keeps its 163. Length 11 → 12
execctx-consumer-census.test.ts 62 sites = 22 + 9 + 5 + 7 + 8 + 1 + 5 + 2 + 1 + 1 + 1 + 0; 125 mentions the form body reads no resolveExecCtx, so its host literal forwards none: MOVED_REGISTRARS_READING_NO_EXECCTX gains form-routes.ts: 1 (1 → 2), as slice 7 entered the discovery body. Forwarders stay 12 = (12 − 1) + 3 − 2. Length 11 → 12
ui-view-route-identity.measurement.test.ts this.enforceAuth( 59, this.resolveExecCtx( 74 unchanged the form body reads neither term; length 11 → 12
ui-view-route-tenancy.measurement.test.ts as above; the ownership guard 1 call site + 1 forwarder, unchanged length 11 → 12
dogfood authz-conformance.test.ts (realtime tripwires) 0 hits over the twelve files length 11 → 12
dogfood authz-probe-blind-spot.census.ts, rest-server.ts row 70 / 19 / 51 unchanged; this.routeManager.register( 63 = 18 + 9 + 5 + 7 + 8 + 1 + 6 + 4 + 1 + 1 + 1 + 2; private register*Endpoints( 16; enforceAuth 108 new control form-routes.ts: this.routeManager.register( 2; registrar list length 12; populationRule names the new module. enforceAuth does not move: the doors never call it, and the seam names no such member

Readers outside the list:

  • scripts/check-route-envelope.mjs (a gate's target table, domain:spec): form-routes.ts is declared dialectOnly with 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 measured stringError 4 (each door's 400 INVALID_REQUEST 'slug is required' and each door's 404 FORM_NOT_FOUND, a template literal) and siblingCode 6 (those four, plus the resolve door's 500 FORM_RESOLVE_FAILED and the submit door's 400 VALIDATION_ERROR). rest-server.ts drops by exactly those bodies: stringError 15 → 11 and siblingCode 13 → 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 the rest-server.ts row and the new row; --self-test is green.
  • scripts/adr-anchors/packages__rest__src__form-routes.ts.json (new): the moved body carries two ADR-0131 D6 citations, which left rest-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 reading rest-server.ts#registerFormEndpoints (resolveFormBySlug: …) now names form-routes.ts#registerFormEndpoints, where resolveFormBySlug lives. 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:

  • At the move commit 73d2a78fa (the list not yet grown): the dogfood blind-spot census read expected 68 to be 70 (population) and control "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/rest local (287 files) and repo (5 files) were green there, which is the reach loss the list exists to close. check:route-envelope read form-routes.ts NOT DECLARED, stringError: found 11, declared 15 and siblingCode: found 7, declared 13; check-adr-anchors read 1770 decision-letter citations (base 1772).
  • At 9c26739ec (the list grown, the readers not yet re-pointed): local read Test 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). repo read Tests 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 read Tests 2 failed | 106 passed (108) — the tripwire's length pin and control "registrar list length" moved … expected 12 to be 11.
  • Each is green after the re-point (de5848dae, then the merge head).

The packages/spec reader, re-pointed

Three evidence strings in packages/spec/liveness/view.json cited the moved code, and check:liveness exited 1 on the first head. Ruling A put four path substitutions in this PR. Its formViews amendment made that row cite anonymousFormIntakeCandidates, because the moved findPublicFormView never spells the key. The consumer text, verdicts and notes are unchanged. At e62024d87, check:liveness exits 0.

Readers examined and not re-pointed

  • Prose that names the registrar without a file or line, still true: content/docs/ui/forms.mdx ("rest-server scans views" names the component, and RestServer still serves both doors); packages/verify/src/handle.ts and 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 dogfood anonymous-door-sweep.classification.ts (enforcedBy: 'RestServer.registerFormEndpoints; …') and the conformance matrix row anonymous-door-public-form, whose enforcement must name RestServer.registerFormEndpoints (ADR-0138 D2's table; the method is still that class's). No gate reds on any of them.
  • docs/adr/0138-… cites packages/rest/src/rest-server.ts#RestServer.registerFormEndpoints twice; 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.ts reads rest-server.ts by path, but only the GET ${metaPath}/:type handler, which did not move.
  • Gates that walk packages/rest/src reach the moved code in its new file. Their outputs at origin/main 2b274600b and at the final head differ only by the one scanned file or by this diff's own re-points: check-meta-type-normalized 39 → 40 files; check-undeclared-dep-imports +1 file, +2 specifiers; check-dispatcher-error-vocabulary and check-error-status-conformance +1 file (classified sites unchanged); check-affected-docs route sources 29 → 30 and registration call sites 24 → 25, by the *-routes.ts convention; check-scripts-symbol-anchors 3904 → 3908 file-level anchors (the route-envelope gate's new comments); check:platform-checklist cited sources 396 → 397 with 834 of 844 anchors resolving on both (the re-pointed source); check-adr-anchors 69 → 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-level and check-filter-alias-parity.
  • Released CHANGELOG.md text: not edited.

Acceptance notes

Gates and tests

Final head e62024d87 (a second merge, of origin/main 8133cc780, after #24056 landed): 17 files, +715 / −469. The dogfood pair reads 108 / 108, so the earlier main-red note is void. The derived union is 95 / 95, all exit 0. The .d.ts is 0a3edce6…, equal to main's own build. CI: 41 runs, 35 success, 6 roster skips.

Measured at this branch: base fd0e72abc; the move 73d2a78fa; the list 9c26739ec; the re-points de5848dae; one merge of origin/main 2b274600b (22352ffd7: #24026, #24005, #24004, #24015, #24009, #24013, #24019; none touches a file in this diff, and rest-server.ts' blob is the same at fd0e72abc and 2b274600b). Then, after #24056 landed, a second merge of origin/main 8133cc780 (e62024d87). It changes one file of this diff, the dogfood census: git combined main's route-ledger row (92) with this slice's lines. All builds and tests went through os-verify-lock, and every exit code was written to a file before any pipe. Final head: e62024d87. The readings below are at e62024d87 unless a line names another commit.

  • .d.ts at the merge head: main moved @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 with main's own: a base leg put origin/main's rest-server.ts (blob b429a90b19d3) into the merged tree under a trap and built @objectstack/rest, giving dist/index.d.ts / .d.cts sha256 0a3edce6ab41821f… with the head-only marker at 0; restored (blob and git diff HEAD proof) and rebuilt, the head gives the same 0a3edce6ab41821f… 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 equals fd0e72abc's, and the merge head's equal the re-point head's, over all nine configs (diff -r exit 0 both). The late-binding legs ran at de5848dae; the merge changed neither rest-server.ts nor form-routes.ts.
  • pnpm --workspace-concurrency=2 --filter '@objectstack/rest...' --filter '@objectstack/verify...' build: exit 0 at base and at the merge head. Then pnpm turbo run build --concurrency=2, the whole workspace: exit 0, Tasks: 76 successful, 76 total (3 cached), after which the .d.ts still reads 0a3edce6….
  • pnpm --filter @objectstack/rest typecheck: exit 0 (tsc --noEmit, then check:test-typecheck: 0 file(s) / 0 error(s)).
  • pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2: Test Files 289 passed (289), Tests 5955 passed | 326 skipped (6281).
  • 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: Test Files 2 passed (2), Tests 108 passed (108). At 22352ffd7, before dogfood: main's Dogfood Regression Gate (1/3, 3/3) is red since #24009 — the authz census and the #17111 docblock pin still read packages/runtime/src/route-ledger.ts as 88 rows; #24009 added four /keys rows (92) #24056 landed, the pair read 4 failed. All four were main's route-ledger drift from fix(runtime): mount every /api/v1/keys verb so an anonymous caller is answered 401 on the standalone server #24009, which dogfood: main's Dogfood Regression Gate (1/3, 3/3) is red since #24009 — the authz census and the #17111 docblock pin still read packages/runtime/src/route-ledger.ts as 88 rows; #24009 added four /keys rows (92) #24056 fixed, and none was this diff's.
  • Lint (at 22352ffd7; no .ts/.mjs file changed after it): pnpm exec eslint --no-inline-config --format json over the 13 changed .ts/.mjs files: exit 0, 13 results, 0 errors, 0 warnings. Repo-wide pnpm lint is CI's (Lint & Repo Gates).
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, at e62024d87: 17 paths against merge base 8133cc780, 1184 changed lines (under the 3000 human-merge threshold). It derived 95 commands, all run at e62024d87 after a whole-workspace build, and all exit 0, check:liveness included (734 pair(s) asked, 733 anchored, 1 exempt). --ran reads: Run reconciliation — 95 derived, 95 run, 0 NOT-MEASURED, 0 UNRUN.
  • check:dev-prereqs and check:skill-frame-freshness (local-only) were not run: this diff touches no packages/spec build input and no four-axis frame file.
  • node scripts/pm/check-governed-merges.mjs --pr 24054: 0 of 17 path(s) hit the register, NOT governed; 1184 changed lines.
  • Changeset: one @objectstack/spec patch (.changeset/23050-s11-form-liveness-citations.md, Clause-②: no), because liveness/ ships in that package's files. @objectstack/rest's published surface is byte-identical (the .d.ts, the export lists and the routes above), so it gets none. No skip-changeset.
  • The verify lock queued twice past its budget (another holder at 10–20 minutes); each 99 was re-queued under the same slot, and nothing was read off a run that did not get a turn.

Generated by Claude Code

…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>
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

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

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

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

Which tree this was computed on

This run read content/docs from 2967f94728bedcc75c10067bf4b2c4039e3b3327 — the merge of head e62024d87b7e8a2be6340cafcaf0f4dfae496e11 into base 8133cc78052c95b6efd59487b64fe78f69860759, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 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

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

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

…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>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: e62024d87b7e8a2be6340cafcaf0f4dfae496e11
Local-runs: none

Face: the declaration face. This is a move-only slice of @objectstack/rest plus four evidence paths in @objectstack/spec's shipped liveness ledger. The diff does seven things:

  • it moves one registrar body into one new module, packages/rest/src/form-routes.ts, with two receiver tokens changed under the seat's static exception;
  • it moves three body-only import names with that body and leaves a pointer comment for each;
  • it grows the registrar list 11 → 12 and re-points the nine readers of that list;
  • it re-points one gate's target table, scripts/check-route-envelope.mjs;
  • it adds one ADR anchor file for the new module;
  • it re-points one platform-checklist source string;
  • it re-points four evidence paths in packages/spec/liveness/view.json and adds a @objectstack/spec patch changeset.

record-recognisers --template --paths over the 17-path list finds no published schema and no governed path and defers to the changeset's Clause-②: line. That line reads no with no arm, so the face is the declaration face, TIER_DEFAULT. Governed Surface Queue Guard concluded success.

Inputs:

Method: read-only. Every count below was read off the git objects of the merge base and the head with git show, text diff and grep, plus two text-only count scripts. One applies the dogfood census's population rule (direct sites, less the forwarder slice, plus the helper's call sites) and its two realtime tripwire regexes to the text. The other sums the route-envelope gate's MODULES table. No repository code was built, tested or re-run.

The rest-server.ts blob is b429a90b19 at the dev's base fd0e72abc, at the first merge's 2b274600b and at the merge base 8133cc780. So the PR's base line numbers apply as written. The branch has two merges of origin/main, not one:

① Derived judgments

No accept set changes. @objectstack/rest's published surface does not change. @objectstack/spec's published bytes change only in four evidence strings of liveness/view.json. Each change the diff makes is judged below.

  1. The moved body is byte-identical once de-indented, except the two ruled receiver tokens: right.

    • Base rest-server.ts :9084–:9527 is 444 lines. With one 4-space indent removed, it is diffed against head form-routes.ts :110–:553. diff reports exactly body lines 198 and 229 (base :9281 and :9312, head :307 and :338): ...RestServer.translateOptionsFor(i18n, locale), became ...this.translateOptionsFor(i18n, locale),.
    • With this.translateOptionsFor( mapped back to RestServer.translateOptionsFor(, diff exits 0 over all 444 lines.
    • Every non-empty line in the range starts with 4 spaces. The range has 7 empty lines and no trailing whitespace. Its only two odd-backtick lines (body :340–:341) are // comment lines, so removing the indent changed no runtime string.
    • RestServer. appears in the body on those two lines only. The member is private static translateOptionsFor (head rest-server.ts :3983), whose whole body is return metaReadGate.metaTranslateOptions(i18n, locale);. It reads nothing but its two arguments, so the receiver is the whole difference. This is the static exception ruled in 6205442867 and named for this slice in the claim 6222884615.
    • The rest of rest-server.ts changes by +25 / −447:
      • three import names removed (anonymousFormIntakeWithdrawnIn with its one comment line, PUBLIC_FORM_SERVER_MANAGED_FIELDS, isExpectedRouteError), each replaced by a pointer comment;
      • the new import and its comment (head :315–:316);
      • the stub (head :9089–:9105): a 5-line comment, the 9-member host literal and registerFormEndpoints.call(host, basePath).
    • The file goes from 9,915 to 9,493 lines: 9,915 − 447 + 25.
    • The private method keeps its name, JSDoc and position. Base :9048–:9083 against head :9053–:9088 diffs to 0.
    • registerRoutes is identical: base :4757–:4831 against head :4762–:4836, 75 lines. The call this.registerFormEndpoints(bp); moved from base :4782 to head :4787 only because of the import lines above it. It is under no switch, after registerEmailEndpoints and before the capability routes, so the registration order holds on every base.
  2. The host lists exactly what the body reads: right.

    • The body's code reads of this. (no comment in the body mentions this.): resolveProtocol 3, resolveAnonymousFormTenancy 1, readFormObjectDefinitions 1, routeManager 2, resolveI18nService 1, buildTranslationBundle 1, extractLocale 1, packagedObjectBase 2, plus the static twice.
    • FormRouteHost (:91–:101) declares exactly these 9. Each is typed by indexed access on the class, and the static is typed on typeof RestServer.
    • The body has no bare this, no element access on this and no function keyword. The two 'function' hits are typeof string literals.
    • Base rest-server.ts has 58 top-level declarations. The body names only one of them, RestServer, in the two static accesses. Every other free name is an import, and form-routes.ts imports each one from the module rest-server.ts imports it from: the four anonymousForm* names from @objectstack/metadata-core, GetMetaItemsRequest and CreateDataRequest from @objectstack/spec/api, PUBLIC_FORM_SERVER_MANAGED_FIELDS from @objectstack/spec/security, mapDataError and isExpectedRouteError from ./error-response.js, logError from ./log.js.
    • The three names only this body reads each had exactly one other occurrence at base, its import line. At head rest-server.ts holds them only in the pointer comments (0 code reads). So moving their import names is required, and right. The other eight still have code reads in rest-server.ts, so their imports stay.
    • The module's imports from sibling modules are all type-only: RestServer from ./rest-server.js, TransportScopedMetaRequest from ./ui-routes.js (slice 10's ruling A, 6220753007) and ServerScopedDataRequest from ./data-action-routes.js. They are erased at build, so the slice adds no runtime import cycle and copies no type.
  3. Value or closure, member by member: right.

    • routeManager goes by value.
      • Its two reads (form-routes.ts :203 and :408) are top-level register statements, which run during registration.
      • The class assigns the field only in the constructor (head :2143) and in the try/finally swap (:5151 / :5155) inside registerMetadataEndpoints (:5069). That method has returned before registerForBase reaches the form call.
      • The interface marks the member readonly.
    • The seven methods and the static go as late-bound closures. Their receiver is still the server, and the static is read from the class at call time.
    • No getter is owed. The body reads no field at request time and no provider, so the provider ruling (6215084478) does not apply.
    • The dev's late-binding measurement, its two closure ablations and its base leg are the dev's, not re-run here. They match this reading.
  4. Nothing @objectstack/rest publishes moves: right.

    • The package publishes dist, README.md and CHANGELOG.md. Its exports map has one entry, ., which resolves to dist/index.
    • src/index.ts is untouched. It re-exports from ./rest-server.js by name only (RestServer, RestProtocol, MountedRoute, metaItemPackageBinding) and names no form module.
    • rest-server.ts has 39 lines starting with export at base and at head, line for line the same.
    • form-routes.ts exports FormRouteHost and registerFormEndpoints. Neither is reachable from src/index.ts.
    • A private member is emitted in the .d.ts with no type, and its docblock is unchanged (item 1).
    • The dev's .d.ts sha 0a3edce6…, equal to main's own build at 8133cc780 through a base leg, and the 39-export lists are the dev's evidence, not re-run here. They are what this construction predicts.
  5. The registrar list and every reader were re-counted, and no reader was loosened: right.

    • The list grows 11 → 12 by one appended entry. Each of the nine readers moves its length pin 11 → 12.
    • execctx census.
      • Sites (this.resolveExecCtx(environmentId, req)): 62 at both. Head: 22 + 9 + 5 + 7 + 8 + 1 + 5 + 2 + 1 + 1 + 1 + 0.
      • Mentions of resolveExecCtx: 125 at both. rest-server.ts reads 60 at both, and form-routes.ts reads 0.
      • MOVED_REGISTRARS_READING_NO_EXECCTX gains form-routes.ts: 1. That is exact, not a loosening. The forwarder pin reads (12 − 1) + 3 − 2 = 12, the same 12 as before ((11 − 1) + 3 − 1). The host carries no resolveExecCtx member. A form body that started reading the seam would gain a forwarder, read 13 and red. It is the shape slice 7 used for discovery-routes.ts.
    • ui-view pair: over the list, this.enforceAuth( reads 59 and this.resolveExecCtx( 74, at base and at head. The floors of 40 are unchanged. The body spells neither term, nor the ownership guard, so that pin's readings are unchanged.
    • dogfood blind-spot row:
      • Recomputed with the census's own rule, the population is 70 / 19 / 51 at base and at head.
      • this.routeManager.register( reads 63 at both: base 20 + 9 + 5 + 7 + 8 + 1 + 6 + 4 + 1 + 1 + 1, head 18 + 9 + 5 + 7 + 8 + 1 + 6 + 4 + 1 + 1 + 1 + 2.
      • private register*Endpoints( 16, registerPerItemRoute( 8, const registerPerItemRoute = 1 and the forwarder slice 1 are each unchanged.
      • enforceAuth reads 108 at both. form-routes.ts spells it 0 times, so no call site moved and no seam mention was added.
      • The new control form-routes.ts: this.routeManager.register( reads 2, and registrar list length reads 12. The form doors are outside registerMetadataEndpoints before and after.
    • docblock pin:
      • The module has exactly 3 TSDoc openers (:3, :82, :103), none stacked, and its floor is 3.
      • rest-server.ts keeps 163 at both, because the body held none.
      • The floors' sum goes 44 → 47 = 4 + 3 + 3 + 8 + 3 + 8 + 5 + 3 + 3 + 4 + 3, and the sum is pinned exactly.
    • query-number 9, canonical-query 4, meta-draft 8:
      • The module has 0 Number / parseInt / parseFloat tokens and no unary plus.
      • It has 0 query: slot lines (by querySlots' own regex) and 0 draft switches ('draft', previewDrafts, listDrafts).
      • Each new floor is 0, and each sum is unchanged. The canonical census's form-routes.ts: 0 key is required by its Record type.
    • dogfood realtime tripwires: both probe regexes read 0 on each of the twelve files.
  6. scripts/check-route-envelope.mjs is re-pointed, not loosened: right.

    • The gate's diff is +33 / −2. The two removed lines are the rest-server.ts row's two numbers, 15 → 11 and 13 → 7, each with a "NOT progress — the same MOVE" history comment. The rest is one new MODULES entry and its comment. No rule or other entry changes.
    • Counted on the module by scanSource's own rules, over its nine .json( calls:
      • :211 and :416, each door's 400 INVALID_REQUEST with a string-literal error: stringError and siblingCode;
      • :216 and :421, each door's 404 FORM_NOT_FOUND with a template-literal error: stringError and siblingCode;
      • :394, the 500 FORM_RESOLVE_FAILED with a call-expression error: siblingCode only;
      • :477, the 400 VALIDATION_ERROR, whose error is a + concatenation, not a literal: siblingCode only;
      • :385 and :538: object literals with neither key; :546: the argument is not a literal.
      • So the counts are 4 and 6, as declared.
    • rest-server.ts's .json( calls go 84 → 75: exactly the nine moved calls, and the stub has none. So its row drops by exactly 4 and 6.
    • Both sums hold, read off the head's table:
      • stringError: 11 + 13 + 8 + 1 + 0 + 0 + 0 + 0 + 1 + 0 + 1 + 4 = 39;
      • siblingCode: 7 + 15 + 8 + 9 + 4 + 1 + 0 + 0 + 3 + 0 + 1 + 6 = 54.
      • These equal the base table's 39 and 54.
    • auditDialectOnly requires each count to equal its pin and reds in both directions, so the pins are as tight as before. The module matches discover()'s *-routes.ts convention. The entry is dialectOnly with ratchet #9559, like the ten moved modules before it.
    • Lint & Repo Gates concluded success.
  7. packages/spec/liveness/view.json: four path substitutions, and each one resolves: right.

    • The diff is 3 lines, each an evidence value. No status, verifiedAt, evidenceScope or note changes.
    • form.sections → packages/rest/src/form-routes.ts registerFormEndpoints. The module names sections 16 times (the key-mention rule), and registerFormEndpoints is its export (:109). The edit also drops base's registerPublicFormEndpoints, a name the tree never had, as the declaration listed.
    • form.sharing → form-routes.ts#findPublicFormView (:128) and #resolveFormBySlug (:170). Both are present as words (the isSymbolNamed rule), and the module names sharing twice.
    • formViews → packages/spec/src/ui/anonymous-form-intake.ts#anonymousFormIntakeCandidates, the amendment's citation.
      • The symbol is declared at :84 and reads v.formViews at :89–:90. The file names formViews 5 times.
      • It is on the doors' read path. findPublicFormView (form-routes.ts :134) iterates anonymousFormIntakeCandidates(view), imported from @objectstack/metadata-core, which re-exports the same binding from @objectstack/spec/ui (metadata-core anonymous-form-intake.ts :52–:57).
      • The first substitution could not hold: form-routes.ts spells formViews 0 times, so the key-mention check refuses it. So the amendment is what makes the row hold.
      • The row's second pointer already cites packages/spec/src/ui/view.zod.ts, so spec-side evidence is not a new form for this row.
    • The quotes are not re-pointed: slugMatchesPublicLink(sharing.publicLink, slug) and const formViews = view.formViews occur nowhere in the head tree outside view.json. That was already true at base; it is carried (③ item 4).
    • Leaving verifiedAt as it was is right. A path substitution re-verifies nothing, so a new date would overstate the claim's age.
    • Spec property liveness concluded success.
  8. The new ADR anchor is faithful: right.

    • scripts/adr-anchors/packages__rest__src__form-routes.ts.json names ADR-0131, ADR-0056 and ADR-0112. The moved body cites ADR-0131 twice (:145, :259), ADR-0112 once (:466) and ADR-0056 once (:502), the same counts it had at base. All three ADR files exist.
    • The invariant matches the code: the submit runs as publicFormGrant: { object: match.object }, filters by allowedFields and PUBLIC_FORM_SERVER_MANAGED_FIELDS, and refuses a zero-field form with the catalog's 400 VALIDATION_ERROR.
    • Slices 4, 6 and 7 added anchors of the same kind. The path is not governed.
  9. The checklist source re-point is right, but it covers half of the item.

    • Item 14 of access-security.json, access-security.public-form-withdrawal-layers: its source string now names form-routes.ts#registerFormEndpoints. That anchor resolves (:109), and resolveFormBySlug lives there.
    • The same item's acceptance verify prose (head :1556) still reads "packages/rest/src/rest-server.ts#registerFormEndpoints reads the environment's view list alone and drops a candidate that … anonymousFormIntakeWithdrawnIn finds withdrawn". The code that does that moved.
    • It still resolves by name, because the private method stays. That is the case slice 1's ruling A judged harmless for anchors that name the registrar. So this is not blocking (③ item 8).
  10. The readers left untouched read no moved line: right.

    • Line citations: the tree holds 50 rest-server.ts:NNNN citations outside released CHANGELOG text, the highest :5872. None falls in the moved range :9084–:9527 or on the four import hunks.
    • The door tests (public-form-*.test.ts and the rest) drive RestServer at runtime and read no source by path.
    • Scripts that pin rest-server.ts by path name no moved token: check-single-authz-resolver's DELEGATORS, check-system-context-census's enforceAuth row and check-undeclared-dep-imports' objectql row.
    • ADR-0138 :155 and :509 cite rest-server.ts#RestServer.registerFormEndpoints, which still resolves (③ item 6).
    • The view.json notes (:337, :357, :393) still name rest-server.ts in prose and in line ranges, two of which the notes already call superseded. Ruling A kept the notes unchanged, so this slice does not touch them.

② Semver level

  • '@objectstack/spec': patch is right.
    • @objectstack/spec's files includes liveness, so view.json ships, and this diff changes its bytes. So a changeset is owed, and skip-changeset would be wrong. That label is for a diff that publishes nothing from any released package.
    • Only evidence strings change. No status moves, so no state count, author warning or lint verdict moves. No schema, export or key moves. patch is both the floor and the ceiling.
    • The precedents are the liveness-only changesets in 73c7dc4a3 (an evidence re-point) and 3fed1eb00, each a @objectstack/spec patch.
    • Every factual sentence of the changeset holds against the diff: three rows; three path references for form.sections and form.sharing; formViews now cites the rule where it is declared; quotes, verdicts and notes unchanged.
  • No changeset for @objectstack/rest is right.
    • The .d.ts and the export list hold by construction (① item 4), and the routes and their order hold by the move (① item 1).
    • The bundle's code is reorganised with no behaviour change, so there is nothing for a changelog to say. Slices 1–10 landed on the same reading.
    • The one runtime difference is the handler's this, now the host literal. Every member it reaches forwards to the server.
  • Clause-②: no is right on both carriers: PR body line 2 and the changeset.
    • The changeset's parenthetical is reasoning, not an arm, so it reads no with no arm.
    • The doors read req.params.slug (and req.params.environmentId on the scoped mount) and the body exactly as before. No accept set widens or narrows, so no (narrowing) arm is owed.
  • Check Changeset concluded success on both of its runs, and the PR carries no skip-changeset label.

③ Boundary flags

Every dev flag is answered or escalated. The flags come from the PR body's Acceptance notes, and from the three reports' deviations, out_of_scope_findings and open_questions.

  1. open_questions[0] (round 1, 6225333915): route the packages/spec reader. Answered: ruling A (6225503839) and its amendment (6225900470) are applied, and verified (① item 7).
  2. Round 2's deviation: the ruled formViews substitution was falsified by measurement. The dev pushed the ruled, red state (162435c41, 12b325723) before the amendment. Answered: superseded. The head carries the amended row, and Spec property liveness is green.
    • For the seat: round 1's report called the four substitutions sufficient without having run them, and the ruling relied on that. Non-blocking.
  3. Round 2's deviation: a patch changeset outside the resume list. Answered: right (②).
  4. Out-of-scope, rounds 1–3: the two quotes that exist nowhere. Answered: carried by spec liveness view.json: the props.form.children.sharing and props.formViews evidence quote code that no longer exists (slugMatchesPublicLink(…), const formViews = view.formViews) — verdicts right, citations stale #24063 (domain:spec, pm:blocked until this PR lands or closes).
  5. Out-of-scope, round 1: fix(runtime): mount every /api/v1/keys verb so an anonymous caller is answered 401 on the standalone server #24009's route-ledger drift. Answered: resolved. dogfood: main's Dogfood Regression Gate (1/3, 3/3) is red since #24009 — the authz census and the #17111 docblock pin still read packages/runtime/src/route-ledger.ts as 88 rows; #24009 added four /keys rows (92) #24056 landed through PR test(dogfood): the authz census and the matrix docblock read the runtime route ledger as the 92 rows it holds #24069 as 8133cc780, and e62024d87 merged it. The combined census file is verified (Method).
  6. Out-of-scope, round 1: ADR-0138's two rest-server.ts#RestServer.registerFormEndpoints citations. Answered: they still resolve. Tier H, joined to the seat's ADR carry (0076, 0111, 0138).
  7. Out-of-scope, round 1, and the Acceptance notes: prose left true by name.
  8. New in this review: checklist item 14's verify prose was not re-pointed (① item 9). ESCALATED to the seat, non-blocking. Fold that one path into this PR before landing: it is the same item, the same lane and the same declaration. Otherwise name platform-checklist: revise the items the #23878 full run and the #23879 since:v18 run found stale (knownGaps, steps, fixtures, oracles, source anchors across 15 areas; no clause red) — ledger follow-up #24036's half A, which owns access-security.json (claim 6226218270), as its carrier.
  9. New in this review: access-security.json is domain:devx's, and no cross-lane declaration was found.
  10. New in this review: the PR body's "Gates and tests" still carries round 1's lines.
    • Those lines contradict the section's own opening paragraph: "Final head: 22352ffd7.", the dogfood "Tests 4 failed | 104 passed (108)", the 82-command union with check:liveness exiting 1, and check-governed-merges --test over 15 paths.
    • The body becomes the squash message on main.
    • ESCALATED to the seat, non-blocking: correct them before arming.
  11. The other round 1 deviations:
    • The two-stage red before the re-point: answered, a stronger proof than one stage.
    • The merge of main before the PR: answered, verified disjoint (Method).
    • Late binding measured at de5848dae and not re-run: answered, both blobs are the head's (Method).
    • Not contract matters: the runs outside the lock, the lock's queue timeouts, the shared-scratchpad overwrite, the trailers and footer, the cleanup.
  12. Round 3's deviations are not contract matters: the nohup run with a blocking tail, the repeated whole-workspace build, and the body left unedited by the dev.
  13. Cross-lane:
  14. The dogfood pair has no PR-time verdict.
    • The sharded Dogfood Regression Gate leg and Dogfood Verify CLI skipped. The aggregate Dogfood Regression Gate concluded success.
    • The edits are attested by the dev's local run at this head (108 / 108) and by the static recount in ① item 5.
    • Not blocking, by CI's design. The seat watches main's first full battery, as for slices 4–10.
  15. The Docs Drift Check (advisory) lists 25 hand-written and 8 release-owned pages, and says view.json yielded no anchor. The routes, their order and each handler's logic are unchanged, and the ledger edit changes evidence strings only. So nothing listed can be falsified by this diff.
  16. PR shape:
    • The PR is a draft with base main.
    • Line 1 is Part of #23050 and line 2 is Clause-②: no.
    • The body has no closing keyword, and Part-of PR must not also close its card concluded success.
    • The assignee is os-tesla. The labels are documentation, size/xl, tests and tooling; there is no skip-changeset.
    • The diff has 1,184 changed lines, under 3,000.
    • Marking it ready is the seat's act.
  17. Tier: the file list and the changeset's line derive TIER_DEFAULT. That is the tier this record serves.
  18. Gate verdicts on this head. There are 48 runs over 41 names. Taking the latest run per name: 33 success, 8 skipped, 0 failure, 0 in progress. Every run concluded success or skipped.
    • A second batch started at 2026-10-17T00:58Z, after the PR body edit.
    • The 8 skips are Auto Label and Check PR Size (their second-batch re-runs; both first runs succeeded), Build Docs, Console Pin Gate, Dogfood Regression Gate (matrix), Dogfood Verify CLI, Packed-tarball smoke (opt-in) and Temporal Conformance (live PG + MySQL).
    • The successes include:
      • all five required contexts: Lint & Repo Gates, TypeScript Type Check, Test Core with all six shards, Build Core and Governed Surface Queue Guard;
      • the eight Lint · lanes and the four Type Check · lanes;
      • Spec property liveness and Check Changeset;
      • both No other open PR may claim … checks and The card this PR closes must claim this branch.

Implemented-by: claude/issue-23050-s11-form-endpoints
Reviewed-by: session_018t6a5rvqPRvEDMCVVW9kYT

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · #23050 slice 11 (forms → form-routes.ts, Part of) · domain:cli seat 2 · os-tesla · session session_018t6a5rvqPRvEDMCVVW9kYT · 2026-10-17T01:21Z · reviewed head e62024d87b7e8a2be6340cafcaf0f4dfae496e11, against GitHub and the branch, not against the report's prose. Holder of claim 6222884615; reports 6225333915, 6225878705 and 6227643349 on #23050.

  1. Shape.
    • Draft, base main, assignee os-tesla. Line 1 is Part of #23050; Clause-②: no is in the body and the changeset.
    • 17 files, +715 / −469.
    • check-governed-merges --pr 24054: NOT governed; 1184 changed lines.
    • git merge-tree is clean against origin/main; mergeable_state reads clean.
  2. The change. A move-only slice:
    • registerFormEndpoints' body moves to form-routes.ts. Only the two ruled receiver tokens change (RestServer.translateOptionsFor → this.translateOptionsFor).
    • The registrar list goes 11 → 12, and its readers are re-pointed with their sums unchanged.
    • check-route-envelope.mjs' table is re-pointed (39 / 54 hold).
    • One ADR anchor file is added.
    • One checklist source string is re-pointed.
    • Four evidence paths in packages/spec/liveness/view.json change (ruling A 6225503839, amended 6225900470), and a @objectstack/spec patch changeset rides them.
  3. Review of record: TIER_DEFAULT (the declaration face), 6227854418 on this head: PASS, Local-runs: none. The review proved move-only from the git objects, recounted the readers and the gate sums, and resolved all four citations.
  4. Changeset: @objectstack/spec patch only, because liveness/ ships in its files. @objectstack/rest's .d.ts (0a3edce6…), export lists and routes equal main's own.
  5. Gates, pinned to the head. Check-runs on e62024d87, read at this stamp, latest run per name: 41 names, 33 success, 8 skipped, none failed, none running.
    • The successes include all five required contexts, Spec property liveness and Check Changeset.
    • The dogfood pair has no PR-time verdict by CI's design. The dev's local reading at this head is 108 / 108, and this seat reads main's first full battery after the merge.
  6. The review's escalations, each answered:
  7. Accepted deviations:
    • the two merges, each verified disjoint;
    • the shipped red push before the formViews amendment, superseded;
    • the patch changeset outside the resume list;
    • the nohup lock runs with a blocking tail.

Landing: pr_ready + automerge_enable through the relay, after this record lands. Part of: #23050 stays open for the CRUD and /meta slices.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants