Skip to content

feat(spec)!: page requires only on the compiled kinds — refused at parse on react, full and slotted pages (#21459) - #21547

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-21459-page-requires-compiled-kinds
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-21459-page-requires-compiled-kinds

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21459
Clause-②: yes (narrowing)

Summary

This PR executes ruling A (record 5964312254). PageSchema now accepts requires only when kind is html or jsx, the two kinds the metadata save door compiles. On a react, full or slotted page it is refused at parse, and so is a page that omits kind, which defaults to full. The refusal names the key, the page's kind and the compiled kinds.

  • The refusal: checkPageRequiresKind (packages/spec/src/ui/page.zod.ts). It is an exported object-level check chained onto PageSchema with .superRefine(...), right after checkPageSourceCompleteness. That is the mechanism this file already uses for kind-conditional rules, so no second one is added. Its vocabulary is the new export COMPILED_PAGE_KINDS = ['html', 'jsx']. The issue is code: 'custom' at path: ['requires']. The message opens with "requires is refused on a kind: 'react' page" (or the page's kind), names html and its deprecated alias jsx, says "Delete the key.", and ends with the house os migrate meta --from 17 sentence. It carries no tracker number.
  • The describe now says the key exists only on html / jsx pages and is refused at parse on the other kinds. Its opening sentence, "derived from the source at save — omit it", is unchanged byte for byte, because the reconciliation ledger row quotes it.
  • The kit (ADR-0087):
    • D2 conversion page-requires-non-compiled-kind-removed (step 18, order: 58, retiredFromLoadPath: true, retiredAfter: '17.6.0'). It strips the key from react, full, slotted and kind-less pages.
    • D3 semantic entry page-requires-non-compiled-kind-refused, which carries conversionIds: [page-requires-non-compiled-kind-removed].
    • A hand-written STEP18_RATIONALE fragment (order: 66, placed after ui-ai-chat-window-retired, which landed first at 65). The registry regions were regenerated with gen:migration-registry.
    • No tombstone and no RETIRED_KEYS_BY_MAJOR row: the key stays live on html pages.
  • Ledgers:
    • liveness/page.json: the requires row stays live. Its evidence gains the PARSE reader, its note's per-kind clause shrinks to the compiled kinds, and verifiedAt is now 2026-10-03. The state counts do not move.
    • The reconciliation row in metadata-form-zod-reconciliation.test.ts: its per-kind clause now reads "on every other kind the parse refuses it".
  • Generated: api-surface/ui.json and export-origins/ui.json gain the two exports, and content/docs/references/ui/page.mdx re-renders the describe. All three were produced by check:generated --fix, never edited by hand.
  • Changeset: @objectstack/spec minor with the BREAKING banner, Clause-②: yes (narrowing), a FROM → TO table, and the ADR-0087 marker registered page-requires-non-compiled-kind-removed, page-requires-non-compiled-kind-refused.

No runtime code changes. The save door, the load report and objectui are untouched.

Premise checks (at base c98a72d69e, re-read on the merged tree)

  • A1 census: zero producers. Every requires: hit in examples/**, packages/apps/** (none there), content/docs/** and skills/** is the stack-level capability list. In examples that is app-crm, app-showcase and app-todo's objectstack.config.ts, plus prose comments naming capability tokens. The only page bodies that carry requires are the 11 htmlPage(...) sites in packages/metadata-protocol/src/protocol.runtime-authoring-gate.test.ts, all kind: 'html', so they are still accepted. The proximity scan (each requires: hit within 15 lines of a page marker) found them, which is its positive control. No test pinned acceptance on a non-compiled kind. objectui at the .objectui-sha pin 89cad75d55 shows page requires only on stamped html fixtures, plus one compile-only type fixture (twins-spec-by-reference-9736.test.ts:158). The ruling's cloud and hotcrm census stands.
  • A2, the default kind. An omitted kind parses as full, and the refinement runs on the parsed value. So { requires: [...] } with no kind is refused as a full page, and the message adds "(full is also the kind of a page that omits kind)". A .shape mirror without the default reaches the check with kind absent and gets the same issue, which the exports-parity fixture pins.
  • A2, the empty array. On a non-compiled kind the save door never looks at []: compileHtmlPage returns undefined, so findHtmlPageSourceGaps answers null and stampHtmlPageRequires returns the body as written. The load report answers [] for it, which reports nothing. So [] had no effect anywhere. The ruling's words refuse the key ("accepts requires only when kind is html or jsx"), not its contents, so [] is refused too, and the conversion strips it too. Both are pinned.

A4: the stored-row disposition is a mechanical drop, so it has a D2 conversion

The drop is lossless. On those kinds nothing derived the list, no renderer read it, and the Studio page editor already drops it on every save. Its one reader was the load report's warning. With the conversion:

  • a stored row at rest replays it at every rehydration seam, loading with one conversion notice instead of a metadata_spec_invalid warning and a _diagnostics badge at every boot;
  • an artifact built by 17.6.0 or earlier replays it too;
  • an authored source is still refused at parse, because the conversion is retired from the load path, and os migrate meta --from 17 lists the edit.

This is pinned at the real seam: loadMetaFromDb over a seeded stored react page carrying requires gives loaded: 1, errors: 0, invalid: 0, one notice naming the conversion, no [page_requires_plugin_absent] line (the manifest lacks the plugin, so an unconverted list would have been reported) and no [metadata_spec_invalid] line.

A6: reader branches the parse boundary now makes unreachable for non-compiled kinds (none changed here)

  • packages/metadata-protocol/src/runtime-authoring-gate.ts:616-627, findPageRequiresAbsentFromManifest, which is "Kind-agnostic on purpose" (TSDoc at :610). A non-compiled page can no longer reach it: the save door refuses the key, and at load the stored-row conversion strips it before reportPageRequiresAbsentAtLoad (protocol.ts:24418, called on the converted body) reads it. The kind-agnostic reach now serves only html / jsx rows, and its TSDoc sentence is a follow-up candidate.
  • packages/metadata-protocol/src/protocol.ts:24526, the reportPageRequiresAbsentAtLoad TSDoc "How a stored page gets here". It is still true, but now only of html rows.
  • runtime-authoring-gate.ts:679 and :728 were already gated to html / jsx by compileHtmlPage (:634). No change.
  • objectui builtinComponents.tsx pageSaveBody (at the pin) deletes requires on every kind. On non-compiled kinds that is now redundant, and harmless.

No test pinned the old acceptance, so no existing test changed meaning.

A9: merge state

PR #21531 landed as 48eb9c193f. This branch merged origin/main twice through scripts/pm/os-regen-merge.sh: dafb0f6d1e after #21531, and e38149267c at 49161683fb.

  • ui/page.zod.ts and migrations/registry.ts merged textually clean. Both rationale fragments are kept (ui-ai-chat-window-retired 65, this one 66), and the regenerated regions match check:migration-registry.
  • The three os-regen artifacts both sides touched were regenerated on the merged tree, and the delta against main is exactly the two exports and the describe row.
  • Sibling entries survive, with equal counts on main and head for 'ui-ai-chat-window-retired' (3), 'ui/AIChatWindowProps' (2) and 'ai:chat_window' (17).
  • None of the 10 open PRs touches ui/page.zod.ts, migrations/registry.ts, conversions/registry.ts, liveness/page.json or the reconciliation test.
  • Since 49161683fb, main gained 1ac7308d7a and 41b13331cd. Neither touches a file in this diff.

Tests (all at head e38149267c unless noted)

  • @objectstack/spec test (vitest run --project local): 604 files, 17888 passed, 1 todo. Run at d7cd797549; the later merge moved nothing under packages/spec.
  • @objectstack/spec typecheck: exit 0. That covers tsc --noEmit, check:scripts-typecheck and check:test-typecheck (52 files / 246 errors held, unchanged).
  • @objectstack/metadata-protocol test: 205 files passed, 3 skipped (3157 tests passed). Typecheck exit 0, and --listFiles includes protocol.runtime-authoring-gate.test.ts.
  • New and changed pins:
    • packages/spec/src/ui/page-requires-compiled-kinds.test.ts: the refusal on react, full and slotted, checked for code, path and named subjects; the omitted-kind default; the empty array; html / jsx controls; no requires on every kind; the stack door envelope (STACK_SCHEMA_INVALID, 422, at pages.1.requires); and the conversion. The conversion pins cover the stored-row strip with a notice, html / jsx kept, strip-iff-refused over the whole kind vocabulary, unknown kind left alone, artifact replay, idempotence and retired-from-load-path. The file also pins the ledger wiring and that there is no tombstone, with ui/Page:assignedProfiles as the control.
    • object-refinement-check-exports.test.ts: the new export is catalogued with 8 fixtures, and the parity, bijection, attachment-by-identifier and barrel-identity legs hold.
    • protocol.runtime-authoring-gate.test.ts: the save door refuses requires on a react, full, slotted or kind-less page with { code: 'INVALID_METADATA', status: 422 }, one custom issue at requires, no jsx-* compile finding, and nothing persisted. The html control still saves and stamps. The load pin is described above.
  • Ablation through scripts/ablation-replace.mjs, run under the verify lock. The mutation replaces the kind condition if ((COMPILED_PAGE_KINDS as readonly string[]).includes(kind)) return; with a bare return;, so the key is accepted on every kind again. The source-resolved spec suites need no rebuild.
    • Mutation landed: anchor 1 → 0, blob 6b13a7df8e44 → 1fd2575e28e3, marker on disk 1.
    • Red leg: 13 failed, 174 passed. The three non-compiled-kind refusal pins are red, along with the omitted-kind, empty-array, stack-door and strip-iff-refused pins and the exports parity and bijection legs. The html / jsx controls stayed green.
    • Restore: blob equals HEAD (6b13a7df8e44) and git diff HEAD is empty. Green leg: 187 passed.

Gates

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 116 commands at e38149267c. All 116 were run and their exit codes recorded. --ran reports: "116 derived famil(ies) accounted for — 115 run, 1 NOT-MEASURED". The NOT-MEASURED one is pnpm check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET: it needs every package's dist/, a repo-wide build), and it is declared to CI. check:skill-examples and check:lean-entry-closure first exited 3 for missing client-react / objectql builds. After building those closures they re-ran to exit 0. Highlights, each exit 0:

  • check:generated reports "all up to date" for 15 artifacts;
  • check:liveness, check:migration-registry, check:spec-changes, check:upgrade-guide, check:api-surface, check:export-origins, check:docs;
  • check:adr-0087-registration, check-changeset-no-major (also run with this body as the --event payload), check-empty-changeset;
  • check:doc-authoring, check:nul-bytes, check:cross-package-test-inputs, check:engine-double-contract, check:type-check-debt.

A narrowed eslint run (--no-inline-config --format json) covered the 8 changed .ts files and found 0 errors and 0 warnings. This repo's eslint config enables no type-aware linting, so the diff cannot move a verdict on an untouched file.

Acceptance notes

  • objectui census tripwire. objectui's spec-object-refinements-7715.test.ts census (at the pin) counts PageSchema's object-level checks and lists attached: ['checkPageSourceCompleteness'] for PageNodeSchema. It is built to go red when the spec adds a check. At objectui's next @objectstack/spec bump, that row will ask for checkPageRequiresKind to be attached, or declared not attachable. Runtime behaviour needs no objectui change, as the ruling says, but that census will need one row. The Console Pin Gate only builds objectui, and nothing objectui compiles against changed type, so it is unaffected.
  • examples/app-showcase and every other example author no page-level requires, so no example changed.

Generated by Claude Code

claude added 6 commits October 3, 2026 04:15
…, D3 entry, ledger rows (#21459)

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
…rence for the compiled-kind requires check

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
… conversion of page requires on non-compiled kinds; add the changeset

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
…rder the requires rationale fragment after the ai:chat_window one that landed first

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the size/l label Oct 3, 2026
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 16 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/spec/api-surface/ui.json, packages/spec/export-origins/ui.json, packages/spec/liveness/page.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/automation/flows.mdx (via lead_record (literal, a string literal in fixture))
  • content/docs/protocol/objectui/layout-dsl.mdx (via PageSchema (symbol, a top-level const))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-5.mdx (via retiredAfter (symbol, a field of const object pageRequiresNonCompiledKindRemoved), retiredFromLoadPath (symbol, a field of const object pageRequiresNonCompiledKindRemoved))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/api-surface/ui.json, packages/spec/export-origins/ui.json, packages/spec/liveness/page.json) — pages documenting those are invisible to this run
  • 9 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 — 138 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 41b13331cdf096c8a940e1430bd535c67049cea5 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 41b13331cdf096c8a940e1430bd535c67049cea5

⚠️ 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 41b13331cdf096c8a940e1430bd535c67049cea5 → 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: e38149267c16b962c1298b4b76a9b8b7137b7640
Local-runs: none

Inputs read: card #21459 (body; comments 5964312254 ruling A, 5965316789 claim, 5965934994 dev report), PR #21547 (body, 13-file list, net diff against main, comment 5965922135), the head's 32 check-runs (polled twice, last at 2026-10-03T05:33Z), and PR #21451 at cfa4d740b7 (the requires describe, liveness row and reconciliation row; all three unchanged from there to this PR's merge base 49161683fb). Nothing was built, run or re-run locally.

① Derived judgments

Each accept-set or public-surface change the diff implies, judged against ruling A and the refs.

  1. Accept-set narrowing — right. PageSchema gains .superRefine(checkPageRequiresKind) after checkPageSourceCompleteness; requires is refused at path ['requires'], code: 'custom', when kind is not in COMPILED_PAGE_KINDS = ['html', 'jsx']. That is the ruling's own sentence ("accepts requires only when kind is html or jsx"). The message names the key, the page's kind and both compiled kinds, and carries no tracker number (pinned).
  2. Kind-less page refused as full — right. At head kind: z.enum([...]).default('full') (page.zod.ts:928), so the parsed value the refinement sees is full; the check also reads page.kind ?? 'full' for a .shape mirror without the default. Pinned in both the new test file and the exports-parity matrix.
  3. Empty list refused — right. The ruling refuses the key, not its contents; the dev measured [] as inert at the save door and silent at the load report, so nothing is lost. Pinned.
  4. html / jsx unchanged — right. Controls pinned at the parse, the stack door and the save door (the html control still saves and the compile still stamps). The engine gate is untouched: runtime-authoring-gate.ts:571 holds a Set of html and jsx and :634 returns early for every other kind, so the new spec constant and the save door agree.
  5. Two new public exports on @objectstack/spec/ui — right, additive. checkPageRequiresKind (function) and COMPILED_PAGE_KINDS (const), both landed in api-surface/ui.json and export-origins/ui.json by check:generated --fix; the parity, bijection, attach-by-identifier and barrel-identity legs of object-refinement-check-exports.test.ts are extended to the new check. The engine keeps its private Set rather than importing the spec constant; acceptable, since the claim forbade save-door edits.
  6. D2 conversion page-requires-non-compiled-kind-removed — right. Step 18 is unreleased (@objectstack/spec is 17.6.0 at head); retiredFromLoadPath: true, retiredAfter: '17.6.0' (six step-18 siblings carry that value), order: 58 (new; absent on main). Kind-gated exactly as the refusal is, with kind ?? 'full'; an unknown kind is left as stored; html / jsx keep their list. The strip is lossless: on those kinds the only reader was the load warning. The stored-row seam pin (loadMetaFromDb over a seeded react row: loaded 1, errors 0, invalid 0, one conversion notice, no page_requires_plugin_absent line, no metadata_spec_invalid line) is the disposition the ruling left to the entry to prescribe.
  7. D3 entry page-requires-non-compiled-kind-refused — right. Reason, replacement (the prescription), acceptance criteria and conversionIds present; surface carries no backticks or pipes; the registry copy is byte-identical to the entry file and sits in id order. No tombstone and no RETIRED_KEYS_BY_MAJOR row, correctly: the key stays live on html pages (pinned, with ui/Page:assignedProfiles as the control).
  8. Rationale fragment order: 66 — right. New at head, no collision; 65 is ui-ai-chat-window-retired from PR feat(spec)!: ai:chat_window is retired — refused by name at the schema door, the floating chat overlay is the AI chat entry point (#21504) #21531, which landed first. The duplicate orders already present in MAJOR_18_CONVERSIONS (55, 57) and STEP18_RATIONALE (56, 60, 62) are on main before this PR and are not its doing.
  9. Ledgers narrowed to the compiled kinds — right. liveness/page.json requires stays live, gains the PARSE reader as evidence, its note's per-kind clause shrinks to html / jsx and names the conversion, verifiedAt moves to 2026-10-03; the state counts do not move (Spec property liveness: success). The reconciliation row's why now reads "on every other kind the parse refuses it". Both are exactly PR fix(spec,lint): page requires is live — refused at save, reported at load #21451's rows at cfa4d740b7, narrowed, and nothing else in either ledger moved.
  10. Describe and generated doc — right. The opening sentence "derived from the source at save — omit it" is byte-identical (the reconciliation row quotes it); the kind clause is added after it; content/docs/references/ui/page.mdx is the regenerated render (Build Docs and Check Documentation Links: success).
  11. Nothing outside the ruling moved — right. No runtime file in metadata-protocol, no objectui, no skills, no governed surface (Governed Surface Queue Guard: success). Stack-level requires (the manifest capability list in objectstack.config.ts; also every requires hit in flows.mdx that the drift advisory listed) is a different schema and is untouched; layout-dsl.mdx, the advisory's other page, does not mention requires. The release-owned 17-5.mdx is not in the file list.

② Semver level

  • .changeset/21459-page-requires-compiled-kinds.md: @objectstack/spec: minor, BREAKING banner, a FROM → TO table, and the adr-0087 registered marker naming both the D2 and the D3 id. It matches what the diff publishes: the only package whose published surface moves is @objectstack/spec (a narrowed accept set and two new exports); the metadata-protocol change is test-only. minor for an accept-set narrowing is this repo's launch-window convention (scripts/check-changeset-no-major.mjs: "During the launch window we ship breaking changes as minor"), and the sibling narrowing changeset at head (20274-agent-memory-contract.md) carries the same level and banner. Check Changeset: success.
  • Clause-②: yes (narrowing) — at line 2 of the PR body and in the changeset, and true: the accept set shrinks on three of five kinds.
  • Not skip-changeset, correctly: spec publishes.

③ Boundary flags

Dev deviations (5) and open_questions (0), each answered:

  1. PR assignee not set — not a contract item: the write was refused by a local permission check and the dev stopped rather than re-route. The seat owes the assignee. No verdict weight.
  2. Tests added in packages/metadata-protocol — accepted. The claim's ⛔ was on the save door and the load report as runtime; tests were its carve-out. Only protocol.runtime-authoring-gate.test.ts moved there, adding the public-door refusal pins and the stored-row disposition pin. No runtime file in that package is in the file list.
  3. Second new export COMPILED_PAGE_KINDS — accepted. Additive, ledgered, and it is the one vocabulary the check, the D3 text and the test's enum-minus-compiled pin share.
  4. Rationale order 65 → 66 — verified: 66 is unique at head.
  5. Not re-merged onto 1ac7308d7a / 41b13331cd — verified: git diff --stat 49161683fb 41b13331cd over this PR's 13 paths is empty.

Dev out_of_scope_findings (2), each with its carrier named; neither needs escalation:

  • objectui's spec-object-refinements-7715.test.ts census row will ask for checkPageRequiresKind at the next @objectstack/spec bump. A census row, not a runtime change, so the ruling's "objectui needs no change" holds for runtime and is qualified by one test row. Carrier: the objectui seat at that bump.
  • findPageRequiresAbsentFromManifest's TSDoc "Kind-agnostic on purpose" now describes a reach only html / jsx rows use. Doc drift; carrier: the next PR that touches runtime-authoring-gate.ts.

Check-runs on the head, read as they stand at 2026-10-03T05:33Z: 20 completed success, 2 completed skipped (Console Pin Gate and Packed-tarball smoke, both opt-in), 0 failed, 10 in progress: Lint & Repo Gates; Test Core (1/6) through (5/6); Type Check · consumer gates; Type Check · workspace; Dogfood Regression Gate (1/3) and (2/3). Concluded and relied on above: Type Check · source gates, Check Changeset, Spec property liveness, Build Docs, Check Documentation Links, Governed Surface Queue Guard, Build Core, Test Core (6/6), Dogfood Regression Gate (3/3), Dogfood Verify CLI, Temporal Conformance, Type Check · debt ledger, Check PR Size, and the four claim and path guards. An in-progress gate is not a pass: this record judges the contract, and the merge waits on those ten as it would regardless.

Reads carrying no verdict: the PR is a draft; its mergeable_state: blocked is the draft plus the pending checks, not a conflict.

Implemented-by: claude/issue-21459-page-requires-compiled-kinds
Reviewed-by: session_01YDt3PzwfrkuFzUBF89WPmM

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

ACCEPT — PR #21547 @ e3814926 (#21459)

domain:spec seat 2 (session_01YDt3PzwfrkuFzUBF89WPmM), holder of claim 5965316789 · 2026-10-03T05:51Z

  • Shape (read on GitHub): a draft against main. The first line is Fixes #21459, and Clause-②: yes (narrowing) sits at a line start. 13 files, +641 / −24.
  • Review: the contract review at CONTRACT_REVIEW_TIER, 5966012133, reads PASS on this head. It executes ruling A (5964312254):
    • PageSchema accepts requires only when kind is html or jsx. On react, full and slotted it is refused at requires, and so is a page that omits kind, which defaults to full. The refusal names the key, the page's kind and the compiled kinds, and carries no tracker number. An empty list is refused too, because the ruling refuses the key.
    • The check is the exported checkPageRequiresKind, beside checkPageSourceCompleteness, with COMPILED_PAGE_KINDS as its vocabulary. The engine's save door is untouched and agrees with it.
    • Stored rows: a D2 lossless-strip conversion, page-requires-non-compiled-kind-removed. It is step 18, retired from the load path and replayed for 17.6.0 artifacts. A stored non-compiled page loads clean, with one conversion notice instead of an invalid-row warning on every boot. On those kinds the key's only reader was the load warning, so nothing is lost.
    • The D3 entry page-requires-non-compiled-kind-refused, its step-18 rationale fragment, and the narrowed ledger and reconciliation rows.
    • Release: @objectstack/spec minor with the BREAKING banner, a FROM → TO table and the ADR-0087 marker.
  • No producer is refused. Zero page bodies author requires on a non-compiled kind in this repo, and the ruling's record measured zero in cloud, hotcrm and objectui.
  • CI on e3814926: 33 success and 2 skipped by design (Console Pin Gate, packed-tarball smoke).
  • Governed surface: check-governed-merges --pr 21547 reads NOT governed.
  • Serial: PR feat(spec)!: ai:chat_window is retired — refused by name at the schema door, the floating chat overlay is the AI chat entry point (#21504) #21531 landed first. Both rationale fragments are kept (65 and 66), and the registry regions are regenerated on the merged tree. spec(ui): the ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 #21464 stage 2 (in flight) also adds a step-18 entry, and runtime strings in the domain:spec packages carry tracker numbers (spec 175 and lint 83 messages): this lane's share of the #20513 A/A burn-down #20749 stage 4 (in flight) edits conversions/registry.ts in other entries; each merges main after this lands.
  • The PR has no assignee. The dev's assignee write was refused by the session's permission check. That is not a contract item, and it is raised with the maintainer rather than worked around here.

Carried, decided here:

  • objectui, at its next @objectstack/spec bump: packages/types/src/__tests__/spec-object-refinements-7715.test.ts lists PageSchema's attached checks by name, and will ask for checkPageRequiresKind (attach it to PageNodeSchema, or declare it not attachable). That is a census row, not a runtime change. → noted on the card at landing, not filed.
  • findPageRequiresAbsentFromManifest's TSDoc ("kind-agnostic on purpose", runtime-authoring-gate.ts:610) now describes a reach only html / jsx rows use. → It rides the next PR that edits that file.

Next: ready, auto-merge, the queue.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 3, 2026 05:52
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 3, 2026 05:52
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 3, 2026
Merged via the queue into main with commit 72af58c Oct 3, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21459-page-requires-compiled-kinds branch October 3, 2026 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant