Skip to content

feat(lint)!: the two page component rules run on the runtime publish door (ruling B) - #23418

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-23388-page-rules-runtime-door
Oct 15, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-23388-page-rules-runtime-door

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Fixes #23388

Clause-②: no (narrowing)

Ruling of record 6161173492 (letter B), quoting the maintainer: 「23388 B 是不是最简单?」「23388 如果页面有问题,不是应该修改了再保存吗?」

What changes

  • packages/lint/src/authoring-rules.ts: validateComponentProps and validateComponentTypes go from surfaces: CLI_ONLY to CLI_AND_RUNTIME, with runtimeTypes: ['page'] (ruling items 1 and 2). Both entry comments drop the [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716 stored-row false-refusal discipline and the two pending surfaceReasons, and cite this ruling as the ruling of record (item 3). The validatePrintPageBlocks entry comment said "the false-refusal budget validateComponentTypes is held on"; it now says that budget was replaced by this ruling. The rules' own code and ComponentPropsMap are not touched.
  • Header lines (item 3): the header lines the dispatch pointed at (~494-511, RUNTIME_OBJECT_ADVISORY_VOLUME) describe the [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716 split of the OBJECT rules, not these two page rules. They are still true and are not rewritten. No other header line in the file names these two rules.
  • The crossing needed no mechanism edit. page already maps to pages in TYPE_TO_STACK_KEY. The runtime gate judges a finding located on the written item against the baseline without the item, so the next save of a stored page with a finding is that save's own finding. The read and boot path runs no authoring rule.
  • Pins (item 4) are in packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts and run through the real saveMetaItem / publishMetaItem / loadMetaFromDb / getMetaItem:
    • (a) An active save of a page whose page:header carries titel is refused with 422 INVALID_METADATA, rule component-props-unknown-key at pages[0].regions[0].components[0].properties.titel. The message carries `titel` → `title` and the hint carries Remove `titel`. Nothing is stored. The draft-to-active promotion of the same page is refused the same way.
    • (c) A component typed page:headr is refused with 422, rule component-type-unknown, hint exactly Rename `page:headr` → `page:header`.
    • Control: the declared spelling saves, and so does acme:pipeline_board, a type outside the reserved namespaces.
    • (b) A page row carrying both findings is seeded into the store. Then: loadMetaFromDb returns loaded: 1, errors: 0, invalid: 0 and registers the page; getMetaItem serves the stored body; the next save of the same body is refused with both rule ids and the stored row is unchanged; the fixed page then saves. This mirrors the precedent sentence at 18.page-default-record-page-one-per-object-refused.ts:42.
  • Changeset (item 5): .changeset/23388-page-component-rules-runtime-door.md. @objectstack/lint gets minor with the BREAKING banner, and @objectstack/spec gets patch (text only). It carries the ruling's sentence verbatim and the ADR-0087 disposition not-required (already-registered …), listing the same D3 ids the 23147 changeset names. The card's step ① (a stored-row listing) is not built.

Overlap with #23357 (ruling item 6, for the seat)

Once this lands, #23357's narrow judge (the retired page data binding, in packages/lint/src/validate-component-props.ts / packages/metadata-protocol/src/protocol.ts) overlaps the general rule at the save door. One operation has two implementations, and the governed side wins. I did not touch it, because seat 1 holds those files. The same seat should fix one sentence while it is in those files: validate-component-props.ts:111-116 ("The runtime publish door … does not run this rule") becomes false with this PR.

File surface beyond the claim, and why

Ruling item 3 limits the rewrite to authoring-rules.ts. My change also made the texts below false, and os-dev requires a published text that a change makes false to be fixed in that same change, so I corrected them here:

  • packages/lint/src/validate-print-page-blocks.ts and packages/lint/src/validate-component-types.ts headers: comment-only. Each named the props/types rules' pending stored-row budget or surfaceReason.
  • 24 step-18 D3 entries under packages/spec/src/migrations/entries/semantic/18.ui-*, 18.element-data-source-and-object-block-filter-rule-array.ts, and packages/spec/src/migrations/registry.ts.template: these are shipped upgrade-guide and os migrate meta text. Each said "a stored page still saves and loads, because a page component's properties is not parsed on the metadata save or load path", or for one entry "a re-save through the metadata API is not refused there". After this PR, the next save of such a page is refused by the component-props gate at the save door. Each now says the page still loads and that its next save meets the component-props gate at the metadata save door. Code comments that said "a stored page is never refused" now say "never refused at load". Text only: no key, schema or conversion changes. No committed generated artifact moves (registry.ts is gitignored, and check:spec-changes / check:upgrade-guide are green).
  • One more now-false sentence is NOT fixed, because spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332 holds that file: the doc comment on the object-grid export-options row at packages/spec/src/ui/component.zod.ts:5991-5994 ("a stored page still saves and loads"). It is a comment, not runtime text.

Deliberate correction of pending changesets

(Seat update at head 3301d5f2f: 18.0.0-next.1 was cut on main (0c52e4307), which moved both notes into .changeset/pre/; they are retained for the GA 18.0.0 note that changeset pre exit assembles, so the correction still applies there. The shipped 18.0.0-next.1 CHANGELOG is history and is not edited. Paths below are the current ones.)

check:empty-changeset is red by design: this PR changes two pending changesets it did not add, and those sentences became false because of this PR's diff. I added no label. The seat decides at review, against the head sha.

  1. .changeset/pre/23147-component-props-every-finding-refused.md
    • Before: "Not judged here. The runtime publish door (the Studio, REST /meta, MCP) does not run this rule, so a page saved there is not refused for any of these. defineStack and the stack schema keep the open properties bag as written. …"
    • After: "Not judged here. defineStack and the stack schema keep the open properties bag as written. …"
    • What makes the before false: packages/lint/src/authoring-rules.ts, the validateComponentProps entry, - surfaces: CLI_ONLY, becomes + surfaces: CLI_AND_RUNTIME, / + runtimeTypes: ['page'],.
  2. .changeset/pre/23103-page-action-row-on-success-and-executor.md
    • Before: "… as a component-props-invalid finding (an error, like every finding of that gate). A stored page still saves and loads."
    • After: "… as a component-props-invalid finding (an error, like every finding of that gate). A stored page still loads; the metadata save door runs the same gate, so the next save of a page carrying a refused row is refused with the same text."
    • What makes the before false: the same validateComponentProps line in authoring-rules.ts.

The gate's exact failing output (node scripts/check-empty-changeset.mjs --base origin/main, exit 1, at ba20e780e):

This PR changes a changeset it did not add:

   .changeset/pre/23103-page-action-row-on-success-and-executor.md
     present on the merge base and CHANGED by this PR -- this is somebody else's release note
   .changeset/pre/23147-component-props-every-finding-refused.md
     present on the merge base and CHANGED by this PR -- this is somebody else's release note

It ends with one ::error file=… annotation for each of the two files: "… exists on the merge base and was not added by this PR … DELIBERATE CORRECTION -- your change may have made this PENDING release note false, and you rewrote it in the same stroke. Remedy: do NOT restore it -- say so on the PR and get it confirmed …"

Acceptance notes

Verification (head ba20e780e)

  • pnpm --filter @objectstack/lint test: 138 files, 6699 tests passed. typecheck passed.
  • pnpm --filter @objectstack/metadata-protocol test: 235 files passed, 3 skipped; 28368 tests passed, 19 skipped. typecheck passed.
  • Consumers that save pages through the door: @objectstack/objectql (default-record-page-save-door, protocol-meta) 113 passed; @objectstack/rest (meta-alternate-door-read-gates, meta-history-diff-authoring-door, meta-plural-i18n, rest) 667 passed; @objectstack/runtime (domains/meta-list-projection-parity) 739 passed.
  • @objectstack/spec: vitest --project local over src/migrations, src/conversions and src/stack-conversions-record.test.ts, 763 passed; test:repo, 59 files, 1011 tests passed.
  • Ablation: done with scripts/ablation-replace.mjs (anchor 1 → 0, blob changed), then a lint rebuild and ablation-dist-preflight (marker present in 4 built files).
    • validateComponentProps back on CLI_ONLY: (a), the draft promotion and (b) go red; (c) and the control stay green.
    • validateComponentTypes back on CLI_ONLY: (c) and (b) go red, and (a) goes red too (its rulesRun assertion names both rules).
    • Restore leg: git checkout HEAD, hash equal to the HEAD blob, rebuild, both markers --absent, and the 5 pins pass.
  • Derived gates (dispatch-gates --ran): 89 derived; 85 run, 4 NOT MEASURED, 0 unrun.
    • Red: check-empty-changeset (by design, above).
    • NOT MEASURED:
      • check-plugin-teardown-shape --self-test: shallow clone, so the pinned control commit is unreachable.
      • check:dual-build-cjs-loads: needs a whole-repo build.
      • check:type-check-debt: its re-measure build was OOM-killed (137) on the shared box.
      • check:query-options-erasure: the whole-repo eslint ratchet was killed by a 240 s timeout.
    • All other gates exit 0.

Generated by Claude Code

… the runtime publish door for page writes

Ruling of record 6161173492 (letter B): both page component rules cross to
the Studio/REST/MCP save door with runtimeTypes ['page']; the #4716
stored-row discipline the entries cited is replaced by that ruling.

Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz
Co-authored-by: Claude <noreply@anthropic.com>
…the D3 texts that said a stored page still saves, and the changeset

Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/spec, touching 4 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/lint/src/validate-component-types.ts, packages/lint/src/validate-print-page-blocks.ts, packages/spec/src/migrations/registry.ts.template), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object), runtimeTypes (symbol, a field of const object AUTHORING_RULES))
What this run could not see
  • 3 changed file(s) yielded no anchor (packages/lint/src/validate-component-types.ts, packages/lint/src/validate-print-page-blocks.ts, packages/spec/src/migrations/registry.ts.template) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • 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 — 142 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 394f65e891f0ac6c6be708fb807740d5cb446520 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c7a44fe9ce8cb2dab65098a6c186e1ece6dd9459 — the merge of head 3301d5f2f1545c2bcc0e566254c0b0597ab85941 into base 394f65e891f0ac6c6be708fb807740d5cb446520, 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 c7a44fe9ce8cb2dab65098a6c186e1ece6dd9459 && git checkout c7a44fe9ce8cb2dab65098a6c186e1ece6dd9459
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 394f65e891f0ac6c6be708fb807740d5cb446520 3301d5f2f1545c2bcc0e566254c0b0597ab85941 && git checkout -B drift-repro 394f65e891f0ac6c6be708fb807740d5cb446520 && git merge --no-ff 3301d5f2f1545c2bcc0e566254c0b0597ab85941

node scripts/docs-audit/affected-docs.mjs --json 394f65e891f0ac6c6be708fb807740d5cb446520

⚠️ 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 394f65e891f0ac6c6be708fb807740d5cb446520 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ba20e780ec9f344104d6a17c19f47f38b68a74ab
Local-runs: none

Inputs read: card #23388 (body + 5 comments: delta 6160833647, ruling of record 6161173492 letter B, triage 6161267305, claim 6161640267, os-dev report 6163297010); PR #23418 body, 35-file list, net diff main...ba20e780e (merge base e4ec6ec1a, 2 commits, +289/−136); the 39 check-runs on the head, read once.

Check-runs on the head (their conclusions are the gate verdicts). Success: Auto Label · Check Documentation Links · Check PR Size · Dogfood Regression Gate · Flag docs affected by code changes · Governed Surface Queue Guard · Lint · CI, release & engine gates · Lint · PM dispatch-gates · Lint · scoped · query-options erasure · Lint · scoped · slot lookup & corpus · Lint · scoped · tooling self-tests · No other open PR may claim the same issue · No other open PR may claim the same single-writer path · Part-of PR must not also close its card · Spec property liveness · The card this PR closes must claim this branch · Type Check · source gates · filter. Failure: Check Changeset — the DELIBERATE CORRECTION class (pr-automation.yml route 0, ruling D on #18375): the PR rewrites two pending changesets it did not add; that job is not a required context and is red by design, judged in ③-1 below. Skipped (not applicable): Build Docs, Console Pin Gate, Dogfood Regression Gate shards, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance. In progress at brief time (named, not watched): Build Core · Lint · ESLint · Lint · docs & code guards · Lint · repo & tooling gates · Test Core (1/6)–(6/6) · Type Check · consumer gates · Type Check · debt ledger · Type Check · workspace. The four gates the dev reported NOT MEASURED locally (check-plugin-teardown-shape --self-test, check:dual-build-cjs-loads, check:type-check-debt, check:query-options-erasure) are answered by those CI jobs; Lint · scoped · query-options erasure is already green.

① Derived judgments

  1. validateComponentProps: surfaces: CLI_ONLY → CLI_AND_RUNTIME, runtimeTypes: ['page'] (packages/lint/src/authoring-rules.ts). An accept-set narrowing on the metadata save door for page writes: saveMetaItem active save, publishMetaItem draft→active promotion, REST PUT /api/v1/meta/page/:name, MCP through runtime-authoring-gate.ts. A page whose component carries an undeclared, wrong-typed or retired property is refused 422 INVALID_METADATA with the rule's existing finding (component-props-unknown-key / component-props-invalid), fix included. Right — ruling item 1, verbatim. page already maps to pages in TYPE_TO_STACK_KEY (runtime-gate.ts:99), so no mechanism edit was owed and none was made.
  2. validateComponentTypes: same crossing, same PR. A component type inside a spec-reserved namespace the platform does not declare is refused (component-type-unknown) with the rename; a type outside the reserved namespaces (acme:pipeline_board) still saves. Right — ruling item 2; the open string arm is untouched (pinned by the control).
  3. Both surfaceReasons removed, runtimeTypes added. The wiring guard (authoring-rule-wiring.test.ts) requires exactly this pair for a runtime-published rule and refuses a reason shorter than 40 chars on a CLI-only one; the diff leaves neither half-written. Right.
  4. Read and boot path unchanged. loadMetaFromDb / getMetaItem run no authoring rule; a stored page carrying both findings loads (loaded: 1, errors: 0, invalid: 0), registers and is served as stored; its next save is refused and the row is untouched; the fixed page saves. Right — ruling item 4(b) and the precedent sentence at 18.page-default-record-page-one-per-object-refused.ts:42. A draft save is not judged until promotion, consistent with the gate's existing draft semantics, and the promotion is pinned as no bypass.
  5. Rule text, ComponentPropsMap, validate-component-props.ts, protocol.ts, page.zod.ts, component.zod.ts untouched. The card's protocol line and the claim's ⛔ list are honoured; the file list carries none of page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's or spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332's files.
  6. [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716 citations rewritten to cite ruling 6161173492 in both entry comments, both surfaceReasons (deleted), the validatePrintPageBlocks entry comment, and the two sibling rule headers (validate-component-types.ts, validate-print-page-blocks.ts). Right — ruling item 3. The header block at ~494–511 (RUNTIME_OBJECT_ADVISORY_VOLUME) describes the [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716 split of the OBJECT rules and names neither page rule; leaving it is right.
  7. Door asymmetry on two live-window aliases — a real accept-set consequence, correctly disclosed. The runtime gate hands every rule the body as written (runtime-gate.ts runRules → rule.run(stack, ctx)); the CLI feeds a rule declared input: 'normalized' the D2-converted stack (authoring-rules.ts:2242). So page:header description and an inline type: 'api' action's object params (conversions page-header-subtitle-alias, inline-action-api-params-to-body-extra, toMajor: 17, not yet retiredFromLoadPath) are converted by os validate but refused at the save door, with the canonical key named. The changeset's "Who is affected" paragraph says so, and stored rows are rehydrated through the conversions on read. Named right as a disclosed narrowing beyond the ruling's three classes, carried as flag ③-6 (a follow-up, not a blocker: the refusal carries the fix, the maintainer's principle is fix-then-save, and the asymmetry is the gate's pre-existing raw-body behaviour, shared with validatePrintPageBlocks).
  8. Two pending changesets corrected (23147-…: the sentence "the runtime publish door … does not run this rule" deleted; 23103-…: "a stored page still saves and loads" → loads, next save refused by the same gate). Both sentences are made false by judgment 1; the corrections say only what the diff makes true. Right (process in ③-1).
  9. 24 step-18 D3 entries + registry.ts.template: text only. Each reason / rationale string that said "a stored page still saves and loads" now says it still loads and its next save meets the component-props gate at the save door; code comments "never refused" → "never refused at load". These strings are shipped in os migrate meta and the upgrade guide, so correcting them in the same change is right; no key, schema, conversion or entry id moves; registry.ts is gitignored and regenerated. Right, with two nits (③-2): in the ui-object-grid-export-options-closed and ui-object-master-detail-form-details-closed rationale fragments the concatenation '…still loads ' + ' and no conversion…' yields a double space in the rendered text; five D3 code comments keep "not parsed on the metadata save or load path, so a stored page is never refused at load" (comment-only, slightly self-contradictory; the reason strings beside them are correct).
  10. Pins (ruling item 4) in packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts, through the real saveMetaItem / publishMetaItem / loadMetaFromDb / getMetaItem: (a) undeclared prop refused with `titel` → `title` and Remove titel, nothing stored, `rulesRun` names both rules, draft promotion refused too; (c) `page:headr` refused with hint exactly Rename page:headr → page:header. ``; control saves the declared spelling and the out-of-namespace type; (b) as in judgment 4. All helpers used (makeProtocol, `makeStubEngine`, `keyOf`, `Row`, `ObjectStackProtocolImplementation`) exist in the file. Right; the dev's ablation legs (props rule back to `CLI_ONLY` reds (a)+(b); types rule back reds (c)+(b)+(a)) show each pin reads its rule.
  11. No public API, export, type or schema moves: no index.ts, no *.zod.ts, no protocol.ts; @objectstack/lint's only public-surface change is the registry's behaviour on the runtime door. No governed path (Governed Surface Queue Guard green). Right.

② Semver level

.changeset/23388-page-component-rules-runtime-door.md: '@objectstack/lint': minor with the BREAKING banner, '@objectstack/spec': patch. Matches the diff. The narrowing lives in @objectstack/lint's registry; under the launch-window convention (check-changeset-no-major.mjs, maintainer ruling 2026-09-04) a breaking change ships as minor and breaking-ness is carried by the banner and the ADR-0087 disposition — both present (not-required (already-registered …), listing the same D3 ids the 23147 note names; Lint · CI, release & engine gates is green). major would also have passed under the RC exemption (pre.json mode pre), as 23147 chose; minor is the convention and is right. @objectstack/spec moves only reason text in migration entries and the registry template, so patch is right. The changeset carries the ruling's item-5 sentence verbatim ("The next save of a stored page … is refused with the fix; stored pages keep serving until then."), names the doors, the FROM → TO table, the measured-population caveat and the alias asymmetry. Not skip-changeset, correctly — the skip-changeset label is refused on a PR that edits an existing changeset (ruling D, #18375) and none was applied.

Clause-②: no — an accept-set narrowing on a published door; the PR body, the changeset, the claim and triage all say no, and this review at CONTRACT_REVIEW_TIER is the review the narrowing owes before the queue.

③ Boundary flags

  1. Dev flag — deliberate correction of two pending changesets; check:empty-changeset / Check Changeset red by design. Answered: accepted. Route 0 of the workflow fits exactly: both rows are M, the before-sentences are made false by this diff's surfaces line, the rewritten text says nothing the diff does not make true, the PR body carries the written confirmation naming each note and the change under it, no skip-changeset label was applied, and the job is not a required context. This record, once adopted, is the confirmation "in writing" the route asks for; (struck by the adopting seat: route 0 in pr-automation.yml puts the decision "in front of a person" and "the approver merges over it", so the confirmation is the maintainer's, not this record's; it is requested on this PR.) The red stands.
  2. Dev flag — file surface beyond the claim (validate-print-page-blocks.ts, validate-component-types.ts headers; 24 D3 entries; registry.ts.template; the two changesets), on os-dev's "shipped text a change makes false is fixed in that change" overriding the claim's "stop on a breach". Answered: accepted. Every edit is text that judgment 1 falsifies, none touches a file page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 or spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332 holds, none is a governed path, and check:spec-changes / check:upgrade-guide are reported green with the Lint · docs & code guards job still in progress. The two nits in judgment 9 (double space in two rationale fragments; five code comments still reading "save or load path … never refused at load") are cosmetic and comment-level; fix in passing on the next touch of those files, no new card.
  3. Dev flag — ruling item 3 "header lines that name them" (~494–511). Answered: verified. Those lines are RUNTIME_OBJECT_ADVISORY_VOLUME, about the [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716 OBJECT-rule split; they do not name either page rule and remain true. No other header in the file names the two rules with the old budget after this diff.
  4. Dev out-of-scope finding — packages/lint/src/validate-component-props.ts:111–116 ("The runtime publish door … does not run this rule … a page saved there is not refused") becomes false on landing; file held by page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 (seat 1). Escalated: the owning seat relays it to page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's PR (that PR already owes the deletion of the narrow judge under ruling item 6); if page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 lands without it, a one-line follow-up on this card's thread.
  5. Dev out-of-scope finding — packages/spec/src/ui/component.zod.ts:5991–5994 doc comment ("a stored page still saves and loads"); file held by spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332 (this seat). Escalated: fold into spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332's PR as a comment-only line; no card.
  6. Dev out-of-scope finding, class b — the save door refuses two live-window page aliases that os validate converts (page:header description → subtitle; inline api action params → bodyExtra; both toMajor: 17, retiresIn 18, neither retiredFromLoadPath). Escalated — a follow-up card, filed by the owning seat with the dev's dedupe words (retiredFromLoadPath step 18, page-header-subtitle-alias, inline-action-api-params-to-body-extra, live-window conversion overdue). Two routes for that card: retire the two overdue aliases from the load path at 18 (then the CLI and the door agree by construction), or have the runtime gate feed input: 'normalized' rules a D2-converted body (a gate-wide change that also touches validatePrintPageBlocks). Not a blocker here: the refusal names the canonical key, the maintainer's principle is fix-then-save, and the changeset discloses it.
  7. open_questions: [] — none to answer.
  8. Ruling item 6 (the page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 narrow judge is deleted once this lands) is not this PR's work and is correctly left to page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's PR or a one-line follow-up; the PR body says so for the seat.

Implemented-by: claude/issue-23388-page-rules-runtime-door
Reviewed-by: session_01D6F9axfoPzR7t8G1J7Ytzz

VERDICT: PASS

Adopted by domain:spec seat 3 (#18883), baozhoutao, session session_01D6F9axfoPzR7t8G1J7Ytzz, at 2026-10-14T07:41Z: rendered by an isolated read-only reviewer at CONTRACT_REVIEW_TIER on the brief from record-recognisers.mjs --brief-template; spot-checked by the seat (runtime-gate.ts:99 page: 'pages'; route 0's text). One sentence in ③-1 struck, as marked.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Confirmation requested: two pending release notes corrected (DELIBERATE CORRECTION) · domain:spec seat 3 · 2026-10-14T07:43Z

Session session_01D6F9axfoPzR7t8G1J7Ytzz · baozhoutao. The seat ACCEPTed this PR (card #23388) and the contract review PASSed on ba20e780e. Check Changeset stays red by design: route 0 of pr-automation.yml says correcting somebody else's pending release note is "a decision about a release" that is confirmed on the PR in writing by a person. The seat does not confirm it for you.

note sentence before sentence after what in this PR makes "before" false
.changeset/23147-component-props-every-finding-refused.md "The runtime publish door (the Studio, REST /meta, MCP) does not run this rule, so a page saved there is not refused for any of these." (deleted) validateComponentProps moves to surfaces: CLI_AND_RUNTIME, runtimeTypes: ['page']
.changeset/23103-page-action-row-on-success-and-executor.md "A stored page still saves and loads." "A stored page still loads; the metadata save door runs the same gate, so the next save of a page carrying a refused row is refused with the same text." the same line

维护者速读

  • 改了什么: 页面组件的两条校验规则(属性、类型)从只在 CLI 跑,改为在保存门(Studio / REST / MCP)也跑。顺带改了两条尚未发布的 release note 里因此变假的一句。
  • 为什么改: 您对 [Decision] Pages saved through Studio, REST or MCP skip the component-props rule that os validate refuses: cross it to the runtime door, and on what evidence #23388 的裁决 B(「如果页面有问题,不是应该修改了再保存吗?」)。
  • 风险与代价(含回滚): 已存储页面照常加载;只有带问题的页面下次保存会被拒并给出修法。回滚 = revert 本 PR。
  • 席位意见: 两处改句都只说本 PR 让其成真的内容,建议确认。
  • 你要做的(一个动作): 在本 PR 回一句「确认两条 changeset 改句」;CI 其余检查全绿后,席位入队。

Generated by Claude Code

This was referenced Oct 14, 2026
… door; the normalized-tier comment and the changeset say so

Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 3301d5f2f1545c2bcc0e566254c0b0597ab85941
Local-runs: none

Supersedes the record on ba20e780e (PR comment 6163431386, PASS). Inputs read this round: card #23388 (body + all 7 comments, including the ACCEPT 6163448876 and the p1 regrade 6164135164); PR #23418 body, 36-file list, net diff main...3301d5f2f (merge base 394f65e89, ahead 5 / behind 0, +294/−142: the two original commits, the merge of origin/main 159a710d1, 0b788de33 and 3301d5f2f); the diff of that diff against the ba20e780e diff; the 41 check-runs on this head, read once; origin/main facts read through gh api for the delta (packages/spec/src/conversions/registry.ts, packages/lint/CHANGELOG.md, .changeset/pre/, commit 0c52e4307, PR #23485).

Check-runs on this head — all completed, nothing in progress. Success: Auto Label · Build Core · Build Docs · Check Documentation Links · Check PR Size · Dogfood Regression Gate · Flag docs affected by code changes · Governed Surface Queue Guard · Lint & Repo Gates · Lint · CI, release & engine gates · Lint · ESLint · Lint · PM dispatch-gates · Lint · docs & code guards · Lint · repo & tooling gates · Lint · scoped · query-options erasure · Lint · scoped · slot lookup & corpus · Lint · scoped · tooling self-tests · No other open PR may claim the same issue · No other open PR may claim the same single-writer path · Part-of PR must not also close its card · Spec property liveness · Test Core · Test Core (1/6)–(6/6) · The card this PR closes must claim this branch · Type Check · consumer gates · Type Check · debt ledger · Type Check · source gates · Type Check · workspace · TypeScript Type Check · filter. Skipped (not applicable): Console Pin Gate, Dogfood Regression Gate shards, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance. Failure: Check Changeset — its annotations on this head name exactly .changeset/pre/23103-… and .changeset/pre/23147-… as "exists on the merge base and was not added by this PR" (the DELIBERATE CORRECTION class, route 0 of pr-automation.yml), plus the ADR-0087 notice accepting the 23388 changeset's already-registered exemption. Not a required context; judged in ③-1. The four gates the dev could not measure locally on the previous head are now answered green by Build Core, Type Check · debt ledger, Lint · scoped · query-options erasure and Lint · scoped · tooling self-tests.

① Derived judgments

Carried forward from the ba20e780e record, re-read against this head's diff (the +/− lines of the two diffs differ only in the four deltas judged in 12–15 below; every other hunk is byte-identical):

  1. validateComponentProps: surfaces: CLI_ONLY → CLI_AND_RUNTIME, runtimeTypes: ['page'] (packages/lint/src/authoring-rules.ts). An accept-set narrowing on the metadata save door for page writes (saveMetaItem active save, publishMetaItem draft→active promotion, REST PUT /api/v1/meta/page/:name, MCP through runtime-authoring-gate.ts): an undeclared, wrong-typed or retired component property is refused 422 INVALID_METADATA with the rule's existing finding and fix. Right — ruling item 1. page already maps to pages in TYPE_TO_STACK_KEY (runtime-gate.ts:99); no mechanism edit owed, none made.
  2. validateComponentTypes: same crossing, same PR. A type inside a spec-reserved namespace the platform does not declare is refused (component-type-unknown) with the rename; acme:pipeline_board still saves. Right — ruling item 2; the open string arm untouched (pinned by the control).
  3. Both surfaceReasons removed, runtimeTypes added — the pair authoring-rule-wiring.test.ts requires; neither half-written. Right.
  4. Read and boot path unchanged: loadMetaFromDb / getMetaItem run no authoring rule; a stored page carrying both findings loads (loaded: 1, errors: 0, invalid: 0), registers and is served as stored; its next save is refused with the row untouched; the fixed page saves. Right — ruling item 4(b), the precedent sentence at 18.page-default-record-page-one-per-object-refused.ts:42. Draft saves are judged at promotion, which is pinned as no bypass.
  5. Rule bodies, ComponentPropsMap, validate-component-props.ts, protocol.ts, page.zod.ts, component.zod.ts untouched; none of page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's or spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332's files in the list. Right.
  6. [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716 citations rewritten to cite ruling 6161173492 in both entry comments, both deleted surfaceReasons, the validatePrintPageBlocks entry comment and the two sibling rule headers. Right — item 3. The header block at ~494–511 (RUNTIME_OBJECT_ADVISORY_VOLUME) is about the OBJECT rules and names neither page rule; left as is, rightly.
  7. Door asymmetry on the two page prop aliases — dissolved on this head. On ba20e780e the save door (which hands a rule the body as written, runtime-gate.ts runRules) refused page:header description and an inline api action's object params while os validate converted them. PR feat(spec)!: retire page-header-subtitle-alias and inline-action-api-params-to-body-extra from the load path at 18 #23485 (finding(spec): two live-window page-property aliases (page:header description, an inline api action's object params) are converted at os validate and at load but not at the metadata save door, which now runs the component-props rule (PR #23418) #23421, merged 8693110c4 2026-10-14T13:47Z, now below this head's merge base) retired both conversions from the load path: on origin/main conversions/registry.ts carries retiredFromLoadPath: true, retiredAfter: '17.7.0' for page-header-subtitle-alias (:4947) and inline-action-api-params-to-body-extra (:5602). Both spellings are now refused with the rename at every door, so the save door and the CLI agree by construction. Right; flag ③-6 of the previous record is closed, no follow-up card.
  8. Two consumed changesets corrected (now .changeset/pre/23103-… and .changeset/pre/23147-…; see ③-1 for why the paths moved). Text identical to the previous head. Right (process in ③-1).
  9. 24 step-18 D3 entries + registry.ts.template: text only, shipped in os migrate meta and the upgrade guide; no key, schema, conversion or entry id moves; registry.ts is gitignored and regenerated; Lint · docs & code guards green on this head. Right, two nits carried in ③-2 (a double space from '…still loads ' + ' and no conversion…' in two rationale fragments; five code comments still read "not parsed on the metadata save or load path … never refused at load").
  10. Pins (ruling item 4) in packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts through the real saveMetaItem / publishMetaItem / loadMetaFromDb / getMetaItem: (a) titel refused with `titel` → `title` and Remove titel, nothing stored, `rulesRun` names both rules, promotion refused too; (c) `page:headr` refused with hint exactly Rename page:headr → page:header. ``; the control saves the declared spelling and the out-of-namespace type; (b) as in 4. Unchanged on this head; Test Core (1/6)–(6/6) green. Right.
  11. No public API, export, type or schema moves; no governed path (Governed Surface Queue Guard green). Right.

The delta ba20e780e → 3301d5f2f:

  1. Merge of origin/main (159a710d1), no conflict: the merge base moves from e4ec6ec1a to 394f65e89; the branch is 0 behind. The net diff gains nothing from main except the renamed paths of the two corrected notes (judgment 8). Right.
  2. authoring-rules.ts, validateComponentProps normalized-tier comment (0b788de33): "a converted alias (page-header-subtitle-alias rewrites properties.description → subtitle) is already canonical here" → "a prop spelling a live D2 conversion still rewrites on the load path is already canonical here". Right: the comment no longer names a conversion that main has retired, and the general statement is what normalizeStackInput does.
  3. Changeset "Who is affected" sentence (0b788de33): the two aliases are no longer described as live-window spellings refused only at this door; they "are retired from the load path at 18 and refused at every door with the rename (objectstack validate, load, and this save door), so the save door and objectstack validate agree on them". Right — verified against origin/main (judgment 7). Frontmatter, BREAKING banner, the ruling's item-5 sentence, the FROM → TO table and the ADR-0087 disposition are unchanged, and the gate's notice on this head accepts the disposition.
  4. content/docs/deployment/validating-metadata.mdx (3301d5f2f): the "SDUI component props — every ComponentPropsMap verdict …" row's runtime-publish cell — → ✓ᵖ, citation "(lint/spec: a retired page-component prop (object-grid defaultFilters) passes os validate with only a warning, while the conversion registry says the loader refuses it — the props gate's error upgrade has no carrier since its blockers closed #23026, lint: the rest of the component-props gate's recorded plan — every component-props-invalid class and component-props-unknown-key go to error, after an external-corpus inventory #23147; at the save door [Decision] Pages saved through Studio, REST or MCP skip the component-props rule that os validate refuses: cross it to the runtime door, and on what evidence #23388)". Right: ✓ᵖ is the table's own code for a rule the runtime door runs on page writes (legend at :497–500, read longest-first, ᵖ alone is page), which is exactly runtimeTypes: ['page']. The table has no row for the component-type rule (validateComponentTypes), so nothing is owed there; Check Documentation Links, Build Docs and Flag docs affected by code changes are green.

② Semver level

Unchanged from the previous head and still matching the diff. .changeset/23388-page-component-rules-runtime-door.md: '@objectstack/lint': minor with the BREAKING banner, '@objectstack/spec': patch. The narrowing lives in @objectstack/lint's registry; under the launch-window convention (check-changeset-no-major.mjs, maintainer ruling 2026-09-04) a breaking change ships as minor with the banner and the ADR-0087 disposition as the carriers — both present, the disposition accepted by the gate's notice on this head. major would also pass under the RC exemption (pre.json still mode: pre, now with 18.0.0-next.1 cut on main at 0c52e4307); minor is the convention. @objectstack/spec moves only migration-entry and registry-template text: patch is right. The docs page is not a published package. Not skip-changeset, correctly: that label is refused on a PR that edits an existing changeset (ruling D, #18375) and none was applied.

Clause-②: no — an accept-set narrowing on a published door; the PR body, the changeset, the claim, triage and the p1 regrade all say no, and this review at CONTRACT_REVIEW_TIER is the review the narrowing owes before the queue.

③ Boundary flags

  1. Dev flag — deliberate correction of two changesets this PR did not add; Check Changeset red by design. Re-judged on this head, where the two notes are no longer pending. Between the two heads chore: version packages (next) (#23382) landed on main as 0c52e4307 (2026-10-14T10:19Z): it cut 18.0.0-next.1, wrote every consumed note into the package CHANGELOGs, and renamed the consumed changesets into .changeset/pre/ (245 renames, the Changesets pre-mode retention that changeset pre exit later consumes into the final 18.0.0 note and deletes — the gate's own header, check-empty-changeset.mjs ~:207, describes that deletion). So on this head the PR edits .changeset/pre/23103-… and .changeset/pre/23147-…, the gate reads them as foreign changesets (its annotations name both), and packages/lint/CHANGELOG.md on main (## 18.0.0-next.1, :58) already carries the 23147 note's original sentence "The runtime publish door (the Studio, REST /meta, MCP) does not run this rule …". Answered: the correction stands and is still right, for a narrower reason than before. (i) The shipped 18.0.0-next.1 CHANGELOG entry is not edited, and must not be: that sentence was true when next.1 was cut (this PR had not landed) and a CHANGELOG is history. (ii) The retained copies in .changeset/pre/ are what the GA 18.0.0 note is assembled from at pre exit; left as they were, the GA note would say the runtime door does not run the rule on the very line where this PR's own note says it does. Re-reading both rewritten sentences on this head: 23147 — the deleted sentence is exactly the claim judgment 1 falsifies, and what remains ("defineStack and the stack schema keep the open properties bag as written. A component type that ComponentPropsMap carries no row for … is not judged.") is still true after this PR; 23103 — "A stored page still loads; the metadata save door runs the same gate, so the next save of a page carrying a refused row is refused with the same text" is true after this PR: the door runs validateComponentProps on page writes and its component-props-invalid finding carries the row's own path and text (pin (a) shows the identical message at the door). Neither rewrite claims anything about 18.0.0-next.1. Route 0's conditions hold (both rows M, no A under those names, no skip-changeset label, confirmation written on the PR at 6163463570 and in this record), and the job is not a required context; the red stands. One nit for the seat: the PR body's "Deliberate correction" section and the earlier confirmation comment name the pre-rename paths .changeset/23103-… / .changeset/23147-… and call the notes "pending"; this record names the current paths .changeset/pre/… and is the written confirmation of record for this head.
  2. Dev flag — file surface beyond the claim (two lint rule headers; 24 D3 entries; registry.ts.template; the two changesets; on this head also validating-metadata.mdx). Answered: accepted. Every edit is text that judgment 1 falsifies or a docs cell that judgment 1 flips; none touches a file page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 or spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332 holds; none is a governed path; Lint · docs & code guards, Build Docs and Check Documentation Links are green on this head. The two nits in judgment 9 are cosmetic and comment-level; fix in passing on the next touch, no card.
  3. Dev flag — ruling item 3 "header lines that name them" (~494–511). Answered: verified on the previous head and unchanged here; those lines are RUNTIME_OBJECT_ADVISORY_VOLUME, about the OBJECT rules.
  4. Dev out-of-scope finding — packages/lint/src/validate-component-props.ts:111–116 ("The runtime publish door … does not run this rule") becomes false on landing; file held by page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 (seat 1). Escalated, unchanged: relay to page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's PR (which already owes the deletion of the narrow judge under ruling item 6); if page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 lands without it, a one-line follow-up on this card's thread.
  5. Dev out-of-scope finding — packages/spec/src/ui/component.zod.ts:5991–5994 doc comment ("a stored page still saves and loads"); file held by spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332 (this seat). Escalated, unchanged: fold into spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332's PR as a comment-only line; no card. (PR feat(spec)!: retire page-header-subtitle-alias and inline-action-api-params-to-body-extra from the load path at 18 #23485 edited component.zod.ts on main; whether the line moved is for spec(ui): a page action row with operation: 'update' and onSuccess parses, while ActionSchema refuses the same shape — the rows type operation as z.unknown() and never carried the rule #23332's dev to read.)
  6. Dev out-of-scope finding, class b — the two live-window page prop aliases refused at the save door but converted by os validate. Closed on this head: PR feat(spec)!: retire page-header-subtitle-alias and inline-action-api-params-to-body-extra from the load path at 18 #23485 (finding(spec): two live-window page-property aliases (page:header description, an inline api action's object params) are converted at os validate and at load but not at the metadata save door, which now runs the component-props rule (PR #23418) #23421) retired both from the load path (judgment 7), the changeset and the normalized-tier comment now say so (judgments 13–14), and the previous record's call for a follow-up card is withdrawn. The PR body's "Acceptance notes · Door asymmetry, measured" paragraph still describes the pre-feat(spec)!: retire page-header-subtitle-alias and inline-action-api-params-to-body-extra from the load path at 18 #23485 state (5 + 3 raw-only findings, "neither is retiredFromLoadPath yet"); it is a PR-body nit for the seat, not a diff defect.
  7. open_questions: [] — none to answer.
  8. Ruling item 6 (the page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357 narrow judge is deleted once this lands) remains page: a stored page component's data can still carry the retired view api source — the props gate runs at the CLI doors only, so the runtime save door and boot accept it, and the renderer still sends it with its stored headers #23357's PR's work or a one-line follow-up; the PR body says so for the seat.
  9. Regrade 6164135164 (p2 → p1, target:v18) changes the queue rank, not the contract; nothing in this record depends on it.

Implemented-by: claude/issue-23388-page-rules-runtime-door
Reviewed-by: session_01D6F9axfoPzR7t8G1J7Ytzz

VERDICT: PASS

Adopted by domain:spec seat 3 (#18883), baozhoutao, session session_01D6F9axfoPzR7t8G1J7Ytzz, at 2026-10-14T14:35Z: rendered by an isolated read-only reviewer at CONTRACT_REVIEW_TIER, superseding the record on ba20e780e. The seat has corrected the PR body (the two notes' current .changeset/pre/… paths; the door-asymmetry acceptance note, closed by PR #23485). The enqueue of this PR still waits on the maintainer's choice put to them in session (confirm the two corrections on this PR, or authorize the seat to confirm and enqueue over the red Check Changeset).


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Maintainer confirmation: the two pending release notes corrected (DELIBERATE CORRECTION) · recorded by domain:engine seat 2 · session_01ADzJtzYTLUfgrRZHxkagkX · 2026-10-15T22:27Z

The maintainer, in that seat's session, answered seat 3's confirmation request (6163463570) with 「同意」.

The question put to them named three corrections. The two on this PR were:

  1. .changeset/pre/23147-component-props-every-finding-refused.md: the sentence "The runtime publish door (the Studio, REST /meta, MCP) does not run this rule, so a page saved there is not refused for any of these." is deleted. It becomes false when validateComponentProps moves to surfaces: CLI_AND_RUNTIME with runtimeTypes: ['page'].
  2. .changeset/pre/23103-page-action-row-on-success-and-executor.md: "A stored page still saves and loads." becomes "A stored page still loads; the metadata save door runs the same gate, so the next save of a page carrying a refused row is refused with the same text." The same line of this PR makes the old sentence false.

(The third was PR #23524's 23332 sentence, recorded there.)

  • The context they answered in: the maintainer's 「排查 pr 23418 23524 , 跟进到合并」 asked this seat to take both PRs to merge.
  • The check. Check Changeset stays red by design (route 0 of pr-automation.yml). It is not a required context, and it runs only on pull_request, never on merge_group.
  • This head. It carries contract record PASS 6170860213 on 3301d5f2f1. Its other checks are green, and git merge-tree against current main is clean.
  • Next. This seat readies the PR and arms auto-merge.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 15, 2026 22:30
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 15, 2026 22:30
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit d0c09e4 Oct 15, 2026
47 of 49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23388-page-rules-runtime-door branch October 15, 2026 23:03
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/m tests tooling

Projects

None yet

2 participants