Skip to content

feat(spec)!: retire the page kind jsx — refused naming html, its D2 conversion leaves the load path (F5-jsx of #23543) - #23638

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-23543-retire-jsx-page-kind
Oct 15, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-23543-retire-jsx-page-kind

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #23543
Clause-②: yes (narrowing)

This PR lands family F5-jsx of ruling 6182001882 (item 5) only. F3, F2, F4 and F5-http stay on #23543, one PR each, in the ruled order. #23543 remains open.

What

Ruling F5-jsx A: 'jsx' retires from PageSchema.kind through enumWithRetiredValues naming html, together with the COMPILED_PAGE_KINDS branch; the entry retires.

  • Authoring refusal. PageSchema.kind is now enumWithRetiredValues(['full', 'slotted', 'html', 'react'], PAGE_KIND_RETIRED_GUIDANCE) (packages/spec/src/ui/page.zod.ts). An authored kind: 'jsx' fails tsc (it left z.input) and, at parse, gets the prescription in place of zod's anonymous enum error: "Page kind: 'jsx' was removed in @objectstack/spec 18 (ADR-0087 D2) … Use kind: 'html'." At defineStack that is STACK_SCHEMA_INVALID / 422 with one issue at pages.0.kind. The guidance carries no os migrate meta sentence on purpose: the rename belongs to protocol 11, below MIGRATION_SUPPORT_FLOOR, so the tool lists no edit for it (the flow-node-crud-filter-alias precedent).
  • COMPILED_PAGE_KINDS is ['html']. checkPageSourceCompleteness's source kinds are html / react. The checkPageRequiresKind refusal no longer names jsx.
  • The D2 entry. page-kind-jsx-to-html gets retiredFromLoadPath: true and retiredAfter: '17.7.0' (the stamp PR feat(spec)!: retire seven overdue ADR-0087 D2 conversions from the load path at 18; record why eight stay #23523 and PR feat(spec)!: retire the datasource driver spelling mongo — refused naming mongodb, its D2 conversion leaves the load path (F1 of #23543) #23600 used). Its docblock's "stays on the load path — a recorded verdict" paragraph is rewritten as the retirement record.
  • The D3 entry. 18.page-kind-jsx-retired (semantic, step 18) states the hand edit. It carries no conversionIds: no migration step replays a protocol-11 conversion, and migrations.test.ts refuses an id no step replays.
  • The verdict-list pin in conversions.test.ts moves the entry to the retired list. A new describe('page-kind-jsx-to-html') pins three claims: a stored jsx row still converges to html and the converted row parses; the authoring funnel no longer rewrites an authored jsx; and defineStack refuses it with STACK_SCHEMA_INVALID / 422 at pages.0.kind, message first sentence asserted, with html and a typo (htm) as controls. The removal-version pin's JOIN gains the row.

Mechanism assumptions, measured

  1. The stored-row path. Assumption 1's first branch holds: the rehydration seams replay the retired conversion. applyConversionsToStoredItem('page', …) runs with includeRetired: true. The stored-row readers are metadata-protocol convertStoredItemDetailed (protocol.ts) and metadata database-loader.ts, so a sys_metadata page row written as jsx reaches PageSchema as html. This is pinned in conversions.test.ts. The metadata save door does not replay page conversions (its applyConversionsToFlow fallback is for flows only), so a page saved with kind: 'jsx' is refused 422. The D3 entry and the changeset both say so. No credential-redactor-style reader judges a raw page body by kind, so F1's "keep it on a contract face" situation does not arise here.
  2. The 'jsx' readers. Re-taken on this tree with git grep (non-test packages/*/src). Each branch is now unreachable because the parse refuses the kind, so each is deleted; none keeps consumer-side tolerance:
    • metadata-protocol/src/runtime-authoring-gate.ts: the local COMPILED_PAGE_KINDS is ['html'];
    • cli/src/utils/sdui-manifest.ts: checkedPageKeys reads html only;
    • cli/src/utils/i18n-extract.ts: comments only;
    • lint/src/page-walk.ts: SOURCE_AUTHORED_KINDS is html / react;
    • lint/src/validate-jsx-pages.ts: reads html only;
    • lint/src/validate-page-source-styling.ts: reads html / react;
    • lint/src/authoring-rules.ts, rule-explanations.ts, validate-translation-references.ts: comment and explanation text;
    • spec/src/ui/page.form.ts: the source field's visibleWhen and help text. The generated en.metadata-forms.generated.ts was regenerated with node scripts/check-i18n-bundles.mjs --write, and the three translated help-text values were edited by hand to drop the alias.
  3. registry.ts's docblock is rewritten as the retirement record (as above).
  4. Generated outputs. Regenerated through the generators only: content/docs/references/ui/page.mdx (check:generated --fix) and en.metadata-forms.generated.ts. check:generated reported no change to api-surface/, export-origins/, authorable-surface/ or the strictness ledger: an enum-value narrowing is byte-invisible to them.
  5. Re-pointed suites. jsx was the "live conversion" subject of several suites (assumption 5's question). Each now drives flow-node-http-callout-rename, the live entry that retires last in the ruled order:
    • cli lint-conversion-notices.e2e, build-json-failure-conversions.e2e and validate-json-failure-conversions.e2e;
    • spec stack-conversions-record.test.ts (the "two conversions" case) and conversions.test.ts (copy-on-write, normalizeStackInput);
    • metadata-core artifact-forward-conversion.test.ts FIRING CONTROL 1, which drives page-component-visibility-to-visibleWhen.

Fixture triage (pin sweep, whole repo: kind: 'jsx' and the conversion id)

  • Re-spelled to html (they used the alias): lint validate-jsx-pages.test.ts, page-walk-conversion-parity.test.ts and validate-action-name-refs.test.ts; cli sdui-manifest.test.ts (two fixtures).
  • Replaced. These rows pinned the deleted branch:
    • spec object-refinement-check-exports.test.ts (two jsx rows) and page-print.test.ts (one row);
    • spec page-requires-compiled-kinds.test.ts, whose vocabulary pin now reads four kinds and one compiled kind;
    • lint page-walk.test.ts, validate-page-source-styling.test.ts and validate-jsx-pages.test.ts, where the jsx rows now assert the rule does not read the retired kind.
  • Unchanged on purpose:
    • metadata-protocol protocol.batch-verb-driver-text.test.ts seeds stored jsx rows. That is still a non-canonical stored row that the stored pass rewrites, so only its comment changed.
    • cli format.conversion-notice.test.ts formats a synthetic notice.
  • Text that became false:
    • content/docs/ui/pages.mdx and react-pages.mdx;
    • liveness/page.json (the requires row's evidence);
    • D3 entries 18.page-requires-non-compiled-kind-refused and 18.ui-html-page-div-refused, and the step-18 rationale paragraph for page-requires-non-compiled-kind-refused (registry.ts.template).

Changeset

.changeset/23543-f5-jsx-retire-page-kind-jsx.md:

  • Bumps: @objectstack/spec minor; metadata-protocol, cli and lint patch.
  • It is BREAKING and carries Clause-②: yes (narrowing). It names the refused spelling, the prescription (html), and how stored rows keep loading.
  • ADR-0087 disposition: registered page-kind-jsx-retired.

Verification

Head before the main merge: 5bf25840. Merged origin/main f7cd580b through scripts/pm/os-regen-merge.sh (no conflicts; check:generated then read "All 14 generated artifacts are up to date").

  • After the merge, on head 831a9fa8: spec (src/conversions, src/migrations, src/ui, src/shared, stack-conversions-record) 195 files / 6977 passed; metadata-core 18 / 719 passed; metadata-protocol 236 files / 20550 passed, 19 skipped (closure rebuilt first). The readings below were taken on 5bf25840, before the merge; main's incoming commits touched none of this diff's files.
  • @objectstack/spec: full suite 740 files, 21785 passed (the one failing file, page-requires-compiled-kinds.test.ts, was then fixed and re-run green with the conversion, pin and migration suites: 8 files / 567 passed); typecheck exit 0; check:generated all 14 up to date.
  • @objectstack/lint: 139 files / 6771 passed; typecheck exit 0.
  • @objectstack/metadata-protocol: 237 files / 28429 passed, 19 skipped; typecheck exit 0.
  • @objectstack/metadata-core: 18 files / 701 passed; typecheck exit 0.
  • @objectstack/cli: unit tier 290 files / 4323 passed; typecheck exit 0. The three edited nightly-tier e2e suites (OS_TEST_TIERS=nightly): 3 files / 35 passed.
  • pnpm check:i18n: OK, all bundles in sync.
  • Gates. dispatch-gates.mjs --commands derived 118 commands. Each was run one by one, with its exit code captured before any pipe. --ran (with exit codes): "118 derived famil(ies) accounted for — 116 run, 2 NOT-MEASURED".
    • 115 exit 0.
    • The 2 NOT MEASURED, both prerequisites: check-plugin-teardown-shape --self-test (shallow clone) and check:dual-build-cjs-loads (full build absent).
    • check:platform-checklist exits 1 on symbol anchors in metadata-protocol/src/protocol.ts and service-storage (countable, canEdit, anonymousFormIntake*, envWideRawViewRows). This diff touches neither file; it is the same main-tree red F1's report recorded.
    • Notable greens: check-adr-0087-registration, check:spec-changes, check:upgrade-guide, check:docs, check:liveness, check:doc-authoring, check:nul-bytes, check:i18n-stale-fill.
  • Ablation (one behaviour, through scripts/ablation-replace.mjs). The kind enum was spelled back as a bare z.enum([...four kinds]), which keeps the narrowing and drops the prescription.
    • On-disk proof: anchor 1 → 0, blob 4ceaa5dffafc → 35fbaad1835c.
    • Result: 2 tests went red, the defineStack refusal case in conversions.test.ts and the page-kind-jsx-to-html row of retired-key-removal-version.pin.test.ts (direction: red).
    • Restore proven: blob == HEAD and git diff HEAD empty. Spec tests resolve src, so no dist leg applies.

Acceptance notes

  • objectui (not touched here; release(v18): objectui's first 18.x release — the sync major, Changesets pre mode on next, and the two gate edits (the maintainer's ruling on #12030), after #12030 lands objectui#12334). The ruling sized objectui's half as "one line" (the console sample apps/console/src/sdui-jsx-preview.tsx:87). At the pinned .objectui-sha b9814b334 there are three more readers:
    • packages/types/src/layout.ts:1795 declares PageNodeSchema.kind?: 'full' | 'slotted' | 'html' | 'jsx' | 'react' on an interface that extends the spec's Page with some keys omitted, with kind not in PAGE_SPEC_EXCLUDED. Once objectui builds against this spec, that override no longer narrows the spec's kind (an "incorrectly extends" error, by reading; not compiled here).
    • packages/components/src/renderers/layout/page.tsx:611 keeps a kind === 'jsx' branch.
    • The types zod mirror (SpecPageFields from SpecPageSchema.shape) will refuse jsx at runtime.
    • The Console Pin Gate is not affected now: scripts/build-console.sh injects this tree's spec only at the vite bundle step, and objectui's own tsc resolves the spec from objectui's lockfile.
  • skills/objectstack-ui/rules/pages.md:138 still says "The legacy value kind:'jsx' is a deprecated alias for kind:'html'." That file is a governed (Tier H) surface, so it is left for a separate skills-only PR rather than making this one Tier H.
  • Load-path suites. After this PR, the conversions still on the load path are flow-node-http-callout-rename, F4's two visibility entries, F3's two lifts and retry-policy-converged. The suites re-pointed here ride flow-node-http-callout-rename, which retires last (F5-http). That PR inherits the "no live conversion left" question F1's report recorded.

Generated by Claude Code

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/cli, @objectstack/lint, @objectstack/metadata-protocol, @objectstack/platform-objects, @objectstack/spec, touching 14 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/lint/src/validate-translation-references.ts, packages/spec/liveness/page.json, packages/spec/src/conversions/walk.ts, …), 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/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
  • 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 pageKindJsxToHtml), retiredFromLoadPath (symbol, a field of const object pageKindJsxToHtml))

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
  • 4 changed file(s) yielded no anchor (packages/lint/src/validate-translation-references.ts, packages/spec/liveness/page.json, packages/spec/src/conversions/walk.ts, …) — pages documenting those are invisible to this run
  • 7 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 — 149 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 23e689dc7d1de961a168997685295ddf009d5c2c → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 19a19bd1898e24742059dea73e169fa3fabb9cf0 — the merge of head 2e1b65d066242d602f855ba62d5e4f247a09871b into base 23e689dc7d1de961a168997685295ddf009d5c2c, 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 19a19bd1898e24742059dea73e169fa3fabb9cf0 && git checkout 19a19bd1898e24742059dea73e169fa3fabb9cf0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 23e689dc7d1de961a168997685295ddf009d5c2c 2e1b65d066242d602f855ba62d5e4f247a09871b && git checkout -B drift-repro 23e689dc7d1de961a168997685295ddf009d5c2c && git merge --no-ff 2e1b65d066242d602f855ba62d5e4f247a09871b

node scripts/docs-audit/affected-docs.mjs --json 23e689dc7d1de961a168997685295ddf009d5c2c

⚠️ 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 23e689dc7d1de961a168997685295ddf009d5c2c → 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

席位核对 — ACCEPT · domain:spec seat 2 (#18549) · session session_01LFqdRsuM5nkqoJdHd5ztVk · 2026-10-15T08:15Z

This is the seat review of record for #23543 family F5-jsx (claim 6185261661, ruling 6182001882 items 5 and 7) on head:

831a9fa8ea

It is not the contract review. That review is owed at CONTRACT_REVIEW_TIER (Clause-②: yes, narrowing: a retired published spec value).

  1. Form.
    • Draft to main. The first line is Part of #23543, the body has no closing keyword, and Clause-②: yes sits at the start of a line.
    • 43 files, +394/−198. Not governed: check-governed-merges --pr 23638 reads NOT governed.
    • A BREAKING changeset grades spec minor, beside patch for the runtime readers.
    • The one generated path is content/docs/references/ui/page.mdx (merge=os-regen). It was regenerated after main was merged through os-regen-merge.sh, and main has not touched it since the merge base.
  2. Against the ruling.
    • 'jsx' retires from PageSchema.kind through enumWithRetiredValues, with the prescription naming html. tsc and the parse refuse it.
    • COMPILED_PAGE_KINDS is ['html']. page-kind-jsx-to-html sets retiredFromLoadPath, and a D3 entry records the retirement.
    • Every non-test 'jsx' reader in metadata-protocol, cli and lint drops its branch, with no consumer-side tolerance left.
    • MAJOR_18_CONVERSIONS and every other entry are untouched.
  3. Mechanism assumptions measured.
    • Stored rows: the rehydration seams replay the retired entry, so a stored jsx page loads as html, which is pinned. The save door refuses jsx with a 422.
    • The os migrate meta suites that had jsx as their live subject are re-pointed, as F1's review foresaw.
    • The three translated help strings are hand edits that their files' own header sanctions ("Edit translations in place"). The key sets are unchanged, and check:i18n passes.
  4. Gates.
    • Locally: 118 derived families, 116 run, 2 NOT MEASURED (shallow clone; full build). check:platform-checklist is red on main's tree, outside this diff.
    • An ablation of the prescription went red as predicted and was restored.
    • CI on 831a9fa8ea is converging, and landing waits on every check.
  5. Accepted deviations. The files beyond the claim's surface are each a pin-sweep or false-text hit, named in the report: the docs pages, the D3 rationale texts, the re-pointed suites and the translated strings. This record amends the claim's surface to the PR's file list.
  6. The dev's open question: A, deferred to the same file's Tier H PR.
  7. Out-of-scope findings.
  8. Landing terms. First a contract review PASS at CONTRACT_REVIEW_TIER on the head, then every check green. Then the seat marks the PR ready and enables auto-merge. On merge, spec(conversions): eight ADR-0087 D2 conversions stay on the 18 load path after #23487 — five families each need a ruling before they can retire #23543 is released to pm:queue with F3, F2, F4 and F5-http left.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 831a9fa8eaa189ba7c0c57f84f8e0cf5f3ed2c4d
Local-runs: none

Family F5-jsx of ruling 6182001882 (items 5 and 7) on card #23543, PR #23638, read at 2026-10-15T08:32Z. Inputs: the card body and its thirteen comments, the PR body and its 43-file list, the net diff against main at merge-base f7cd580bff (+394/−198, equal to the three-dot diff against origin/main), and the check-runs on this head, read twice. Nothing was built, run or re-run; every tree reading below is git show / git grep against the fetched head, and the objectui readings are against the pinned .objectui-sha b9814b334.

Gate verdicts on this head (second reading): 44 check-runs, 33 success, 8 skipped, 3 in_progress. Lint & Repo Gates, TypeScript Type Check (and its four lanes), Build Core, Governed Surface Queue Guard, Check Changeset (both firings), Spec property liveness, Dogfood Regression Gate, the nine Lint · lanes, and the card, claim, single-writer and Part-of guards are all success; Test Core 2/6, 5/6 and 6/6 are success. NOT MEASURED at this reading: Test Core 1/6, 3/6 and 4/6 (in_progress) and therefore the Test Core aggregate; landing waits on them whatever this record says. The 8 skipped are the PR fast-set skips (Console Pin Gate, Temporal Conformance (live PG + MySQL), the dogfood shards and Dogfood Verify CLI, both opt-in packed-tarball smokes, the re-fired Auto Label / Check PR Size duplicates), measured by main's full battery, not on the PR.

Governance: none of the 43 paths is a governed surface (Governed Surface Queue Guard success; by eye no docs/adr/**, skills/**, .claude/**, .github/workflows/**, AGENTS.md, CLAUDE.md, docs/NORTH-STAR.md); head repo equals base repo; Part of #23543 is the first line and no closing keyword follows; draft; no content/docs/releases/ edit (the drift check's read-only row was left alone, correctly).

① Derived judgments

Accept-set changes (each narrows what an author may write):

  1. PageSchema.kind refuses jsx by name (packages/spec/src/ui/page.zod.ts): enumWithRetiredValues(['full', 'slotted', 'html', 'react'], PAGE_KIND_RETIRED_GUIDANCE).default('full'). The helper returns a plain z.enum with an error map (shared/retired-key.ts), so z.input drops the member (tsc refuses it), .default, .options and the JSON Schema projection follow the four-member enum, and the helper's own constructor guards (a retired name may not stay in values, the prescription may not be blank) hold by construction. An authored jsx is refused at kind with the prescription ("Page kind: 'jsx' was removed in @objectstack/spec 18 (ADR-0087 D2) … Use kind: 'html'"); a misspelling (htm) keeps zod's own message (pinned as the control); at defineStack it is STACK_SCHEMA_INVALID / 422 with the ONE issue at pages.0.kind (the fixture is otherwise a complete html page, so nothing else can fire). RIGHT — ruling F5-jsx A's "retire 'jsx' from PageSchema.kind through enumWithRetiredValues naming html", and item 7's "tsc and the parse-time prescription are the whole channel". The prescription carries no os migrate meta sentence, rightly: the conversion is protocol 11 and MIGRATION_SUPPORT_FLOOR is 16 (migrations/registry.ts.template:79), so the tool lists nothing for it. The published JSON Schema ui/Page narrows with it; json-schema.manifest/ui.json is a names-only list, so no regeneration is owed and none is missing.
  2. COMPILED_PAGE_KINDS is ['html'], checkPageSourceCompleteness reads html / react, and the checkPageRequiresKind refusal no longer sends an author to jsx (pinned: the message contains html and not jsx). RIGHT — the ruling's "together with the COMPILED_PAGE_KINDS branch". The exported tuple's type narrows from two members to one; api-surface/ui.json records "COMPILED_PAGE_KINDS (const)" by name only, so check:api-surface cannot see this narrowing — the changeset is its carrier, judged in ②.
  3. page-kind-jsx-to-html leaves the load path (conversions/registry.ts, the only hunk in that file: the docblock and the two new fields; MAJOR_18_CONVERSIONS and every other entry untouched): retiredFromLoadPath: true, retiredAfter: '17.7.0' — the value the seven spec(conversions): 15 ADR-0087 D2 conversions are still on the load path at 18 though their window closed (10 toMajor: 17, 5 older): retire each or record why it stays, one verdict per entry (family closing card after #23421) #23487 retirements and F1 carry, pinned by retired-after.census. The authoring funnel no longer rewrites an authored jsx (pinned: the stack comes back equal, zero notices), so the author meets judgment 1. The stored-row path holds, and through one seam: applyConversionsToStoredItem (conversions/stored.ts) hard-codes includeRetired: true; metadata-protocol reaches it only through convertStoredItemDetailed (protocol.ts:6444), which every stored-row reader funnels into via convertStoredItem — eleven call sites on the head: boot hydration, get, list, draft, overlay, package duplication and migrateStoredMetadata among them — and packages/metadata/src/loaders/database-loader.ts:874 is the second reader. The artifact door replays a retired entry while the artifact's declared floor is at or below its retiredAfter (artifact-forward-conversion.ts:37), which is what the changeset says. So a sys_metadata page row written as jsx reaches PageSchema as html; pinned in conversions.test.ts (applyConversionsToStoredItem('page', …) → html, and the converted row parses). RIGHT.
  4. The save door refuses, it does not convert. resolveOverlaySchema → getMetadataTypeSchema('page') → safeParse(request.item) (protocol.ts:820, :22562); only flow gets a verdict-time applyConversionsToFlow. A page saved with kind: 'jsx' is therefore 422 INVALID_METADATA with the prescription at kind, as the changeset and the D3 entry state. RIGHT — and it is the substance of item 7's "no double spelling": after this PR no door accepts the spelling from an author or an API caller; only data at rest is converged, and only on the seam that replays retired entries.
  5. Every reader of the retired value dropped its branch; none tolerates it. Head-tree sweep of packages/*/src outside tests for the quoted value: zero hits. metadata-protocol runtime-authoring-gate.ts COMPILED_PAGE_KINDS (its one read, :739, the html-page compile gate — a stored jsx row reaches it as html by judgment 3); cli/src/utils/sdui-manifest.ts checkedPageKeys (html only); lint page-walk.ts SOURCE_AUTHORED_KINDS, validate-jsx-pages.ts and validate-page-source-styling.ts (html / react); the rest comments. The three lint rules now yield nothing for a jsx page (pinned as "does NOT read"): that is non-reading, not acceptance — os lint does not Zod-parse by design (lint.ts:623), and the refusal is the parse's at defineStack, os validate, os build and the save door. RIGHT. One wording point on that list is in ③.
  6. Verdict-list pin (conversions.test.ts): the kept-without-ruling list drops to the six entries of F3, F2, F4 and F5-http; the retired list gains the entry with retiredAfter: '17.7.0'; the new describe('page-kind-jsx-to-html') pins the three claims that must hold together (stored row converges and parses; funnel no longer rewrites; defineStack refuses naming html and the release, with html and htm as controls); retired-key-removal-version.pin.test.ts gains the refusal row in its JOIN, so every retired entry still has a renderer. The excludeConversionIds firing control still drives this entry under includeRetired: true, so it still fires; its comment is corrected. RIGHT — item 7's "verdict-list pin updated".
  7. D3 entry 18.page-kind-jsx-retired.ts — a SemanticMigration in the sibling shape, with no conversionIds. RIGHT: page-kind-jsx-to-html is in no step's conversionIds on the head (protocol 11 is below the floor), and migrations.test.ts:80 refuses a link no step replays; ADR-0087 D2's "graduates into the D3 chain" cannot apply to a conversion older than the chain, and the entry says so. The check:adr-0087-registration marker names this id; check:spec-changes and check:upgrade-guide rode green inside Lint & Repo Gates.
  8. Re-pointed suites. The three cli e2e suites (nightly tier — not run by the PR's Test Core; the dev's local 3 files / 35 passed is the only reading), stack-conversions-record.test.ts and two conversions.test.ts cases now drive flow-node-http-callout-rename (live, protocol 11); metadata-core artifact-forward-conversion.test.ts FIRING CONTROL 1 drives page-component-visibility-to-visibleWhen (live, F4's entry). RIGHT for F5-jsx; the cost is carried in ③ with one precision the report lacks.
  9. Fixture triage: the html re-spellings keep their assertions; the deleted jsx rows pinned the deleted branch; protocol.batch-verb-driver-text.test.ts P13 still seeds a stored jsx row, which is still a non-canonical row the stored pass rewrites (judgment 3), so a comment was the right edit; format.conversion-notice.test.ts formats a synthetic notice. RIGHT.
  10. Text edits beyond the claim's surface state only what is true, each checked against the head: content/docs/ui/pages.mdx and react-pages.mdx (retired at 18, refused with the prescription, stored pages keep loading — true by judgments 1 and 3); the generated references/ui/page.mdx matches the new .describe() text; page.form.ts's visibleWhen and help text; the four translation bundles, where the three hand edits drop exactly the alias parenthetical and the key set is unchanged (check:i18n OK, Spec property liveness and Lint & Repo Gates success); the two D3 entries, the step-18 rationale and liveness/page.json's requires evidence. RIGHT. Residues the sweep found: liveness/page.json's requires row keeps a sentence in its note that now over-states ("the key exists only on kind 'html' / 'jsx' pages") — the same row whose evidence the diff corrected; page.form.ts:51's comment still says "the three source-compiled kinds" (there are two); docs/adr/0081 still says 'jsx' stays a deprecated alias (Tier H, superseded in fact by the ruling); skills/objectstack-ui/rules/pages.md:138 (the dev's open question). The source row's note names objectui's kind:'jsx' renderer branch, which is still true at the pin. All four are in ③.

Public-surface changes: no new export (PAGE_KIND_RETIRED_GUIDANCE is module-private; api-surface/ and export-origins/ are unchanged, as the dev reports). The one widening is the published migration catalogue gaining the D3 entry (judgment 7), the same half F1's record counted.

② Semver level

  • .changeset/23543-f5-jsx-retire-page-kind-jsx.md: @objectstack/spec: minor; metadata-protocol, cli, lint: patch; feat(spec)!:; **BREAKING (authoring):** banner; Clause-②: yes (narrowing); FROM → TO (kind: 'jsx' → kind: 'html', and the API caller's one-word change); the honest statement that os migrate meta does not list the edit; how stored rows and artifacts keep loading; the ADR-0087 disposition marker registered page-kind-jsx-retired, whose id exists (judgment 7). The repo is in Changesets pre mode (.changeset/pre.json, tag next), where a BREAKING spec change grades minor; the frontmatter, banner and arm match F1's changeset on the head byte-for-shape. Check Changeset is success on both firings; check-adr-0087-registration rode green. The three runtime readers' patch is RIGHT: metadata-protocol's and cli's changed constants are module-private, lint's exported rules change behaviour only on an input the spec no longer admits, and none of the three narrows a surface of its own — the narrowing is the spec row's, and the body names what each reader now reads.
  • WRONG: @objectstack/platform-objects publishes and has no row. The diff changes packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.metadata-forms.generated.ts — the Studio page form's source help text in four locales — and @objectstack/platform-objects is a published package (18.0.0-next.1, not private). The precedent is explicit and uniform: the last six landings that touched this bundle each carry '@objectstack/platform-objects': patch with a one-line body row, three of them retirements of exactly this shape ([finding] AdminScope.manageBindings and authorEnvironmentSets are honoured by no write path after C3; the ADR-0090 D9/D12 junction gates are unmeasured; permissions docs describe removed gates #23488 23488-admin-scope-binding-authoring-retired.md, [finding] email_template.category and email_template.isSystem have no consumer once templates resolve from the registry: retire both in the next enforce-or-remove batch #22876 22876-email-template-category-is-system-retired.md, spec: once the pin carries objectui#12270, drop the two [PLANNED — not yet rendered] empty-state markers and flip their ledger rows #23395 23395-page-form-empty-state.md). AGENTS.md Post-Task Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3 ("Add a changeset for anything that publishes … a patch changeset — never none") is the rule. Check Changeset stayed green because its two scripts (check-changeset-no-major.mjs, check-empty-changeset.mjs) read the bump level and the Clause-② line, not per-package coverage; this record is where coverage is judged. The changeset does not match what the diff publishes, so the verdict below is FAIL; the remedy is one frontmatter row and one body line.
  • The Clause-②: line. The changeset's yes (narrowing) is the ruling's arm and the right one (a D3 entry widens the catalogue; judgments 1–4 narrow). The PR body's line reads bare Clause-②: yes, which scripts/pm/clause2-line.mjs defines as "a widening" with no arm — the same gap F1's record flagged (③ item 1 there), with the same provenance: the claim comment 6185261661 wrote the bare line and the dev copied it as instructed. F1 landed with its body line unamended, so no gate turns on it (check-changeset-no-major reads yes and finds spec at minor; the breaking-signal reader takes the arm from the changeset). Not a verdict condition; ③ item 3.

③ Boundary flags

Dev open_questions (6187521557):

  1. skills/objectstack-ui/rules/pages.md:138 still calls jsx a deprecated alias; the file is Tier H. Answered: A, as the seat review 6187645640 already routed it: the sentence rides spec: PageTypeSchema says home is "the platform-level landing page" and utility a "floating utility panel", but the landing is the first navigation item and both types render as ordinary inline pages #23610's Tier H PR on the same file, the seat's ledger carries it until then, and this PR stays contract-tier. Confirmed present on the head. Escalated beside it, same carrier, the seat's choice: docs/adr/0081 (status line and decisions 1 and 6) still records 'jsx' as a kept deprecated alias; ruling 6182001882 is the decision of record that retires it, and a dated status note on ADR-0081 citing the ruling would keep Prime Directive [WIP] Add Chinese version of the documentation #13's "an ADR binds until a superseding record says otherwise" findable. Not a condition of this record.

Dev deviations:

  1. Files beyond the claim's surface. Accepted — each is a pin-sweep or false-text hit named in the report and re-checked in ① judgments 8–10; the seat review amended the claim's surface to the file list.
  2. metadata-protocol, cli, lint at patch. Accepted (②). The body's Clause-② line. The seat spells the arm at source in the F3 claim's line, and amends this body to Clause-②: yes (narrowing) if it touches the body for the FAIL remedy anyway — one line, read back, no second footer.
  3. Worktree cut from a newer main; the model-named trailer not used. Accepted — process notes; check:commit-card-trailers rode green on every push and the merge from origin/main f7cd580b came through os-regen-merge.sh with check:generated reading all 14 up to date.

Dev out_of_scope_findings:

  1. objectui reads the retired value in more places than the ruling's one console sample. Verified at the pin b9814b334: packages/types/src/layout.ts:1795 (kind?: 'full' | 'slotted' | 'html' | 'jsx' | 'react' on the interface that extends the spec's Page), packages/components/src/renderers/layout/page.tsx:611 (kind === 'html' || kind === 'jsx'), apps/console/src/sdui-jsx-preview.tsx:87 (kind: 'jsx'). The renderer branch is dead against this spec — a stored row arrives as html and no author can save jsx — not a tolerance that admits a spelling. Escalated to release(v18): objectui's first 18.x release — the sync major, Changesets pre mode on next, and the two gate edits (the maintainer's ruling on #12030), after #12030 lands objectui#12334, the ruling's objectui half, with the three sites named; the seat posts them at landing as its review says. The Console Pin Gate is skipped on the PR and measured by main's battery; the dev's reading that it stays green — scripts/build-console.sh injects this tree's spec through OBJECTSTACK_SPEC_DIST at the vite bundle step and objectui's own tsc resolves the spec from its lockfile — is consistent with the script's header on the head; it is NOT re-measured here, so the first main run after landing is the reading.
  2. The re-pointed suites ride flow-node-http-callout-rename, which retires last. Escalated to the F5-http dispatch order on spec(conversions): eight ADR-0087 D2 conversions stay on the 18 load path after #23487 — five families each need a ruling before they can retire #23543 — with one precision the report lacks: metadata-core's FIRING CONTROL 1 now rides page-component-visibility-to-visibleWhen, which is F4's entry, so the F4 dispatch order (not F5-http's) must carry that control's next re-point or synthetic probe. F5-http inherits the three cli e2e suites, stack-conversions-record and the two conversions.test.ts cases.
  3. check:platform-checklist red on main's tree. Zero overlap with this diff; the watchdog's card owns it. Not this PR's.

Residual text (not in the report), to ride the FAIL remedy push:

  1. packages/spec/liveness/page.json, the requires row's note: "the key exists only on kind 'html' / 'jsx' pages" now over-states; one phrase, in a file the diff already edits.
  2. packages/spec/src/ui/page.form.ts:51: "only the three source-compiled kinds" — two.
  3. Wording, both this changeset and the D3 entry (F1's changeset carries the same list): "every door that parses a page (… os lint …)". os lint does not Zod-parse (packages/cli/src/commands/lint.ts:623); under it the refusal fires only when the config is built with defineStack (thrown at load), and a plain-object stack's jsx page is simply not read by the three page rules. Precedent passed the same sentence, so this is a precision note, not a condition: "at defineStack — so under os lint too when the config calls it —" would be exact.

Report corrections, not flags: the dev report says 44 files; the PR has 43 (its own enumeration lists 43). The report read 24 check-runs with 22 in progress; at this record's second reading there are 44, with 3 in progress, named above.

Premise re-read on the head: no examples/** or packages/apps/** source authors kind: 'jsx'; the only jsx writers left are the conversion entry's own fixture, the prescription, the D3 entry and tests.

Remedy for the FAIL (one push; the record is re-rendered on the new head): add '@objectstack/platform-objects': patch to the changeset frontmatter and one body row naming the page form's source help text in four locales; and, since the push is owed anyway, items 8 and 9 above and the body's Clause-② arm (item 3). Everything in ① stands as RIGHT and needs no change.

Implemented-by: claude/issue-23543-retire-jsx-page-kind
Reviewed-by: session_01LFqdRsuM5nkqoJdHd5ztVk

VERDICT: FAIL

…the jsx-retirement prose names the doors that parse a page

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2e1b65d066242d602f855ba62d5e4f247a09871b
Local-runs: none

Family F5-jsx of ruling 6182001882 (items 5 and 7) on card #23543, PR #23638, second record, read at 2026-10-15T08:50Z. Inputs: the card body and its fourteen comments (the ruling, the F5-jsx claim 6185261661, both dev reports 6187521557 and 6188089228), the PR body and its 43-file list, the PR's three comments (the drift check, the seat review 6187645640 and the earlier record 6187937404 on 831a9fa8, read as an input and not inherited), the net diff of this head against main at merge-base f7cd580bff (+399/−200, the three-dot diff against origin/main, read whole), and the check-runs on this head, read four times. Nothing was built, run or re-run; every tree reading below is git show / git grep against the fetched head, and the objectui readings are against the pinned .objectui-sha b9814b334. This head is 831a9fa8 plus one commit (2e1b65d0, four files: the changeset, liveness/page.json, the D3 entry and page.form.ts), so the record judges the whole head and, in particular, whether the earlier record's ② remedy is complete and whether the four text edits state only what is true.

Gate verdicts on this head (fourth reading): 39 check-runs, 27 success, 5 skipped, 7 in_progress. success: Lint & Repo Gates and its eight Lint · lanes (repo & tooling gates carries check-adr-0087-registration, check-empty-changeset, check:spec-changes, check:upgrade-guide, check:api-surface and the check:generated reconcile), Check Changeset (check-changeset-no-major), Governed Surface Queue Guard, Spec property liveness, Build Core, Build Docs, Type Check · source gates, · consumer gates and · debt ledger, Dogfood Regression Gate, Check Documentation Links, Flag docs affected by code changes, and the card, claim, single-writer, Part-of and PR-size guards. NOT MEASURED at this reading: Test Core 1/6 to 6/6 and Type Check · workspace (all in_progress); landing waits on them whatever this record says. The 5 skipped are the PR fast-set skips (Console Pin Gate, Temporal Conformance, the dogfood matrix and Dogfood Verify CLI, the opt-in packed-tarball smoke), measured by main's full battery, not on the PR.

Governance: none of the 43 paths is a governed surface (Governed Surface Queue Guard success; by eye no docs/adr/**, skills/**, .claude/**, .github/workflows/**, AGENTS.md, CLAUDE.md, docs/NORTH-STAR.md); head repo equals base repo; draft; Part of #23543 is the first line and no closing keyword follows; no content/docs/releases/ edit (the drift check's read-only row, v17/17-5.mdx, was left alone, correctly).

① Derived judgments

Accept-set changes, each a narrowing of what an author may write:

  1. PageSchema.kind refuses jsx by name (packages/spec/src/ui/page.zod.ts): enumWithRetiredValues(['full', 'slotted', 'html', 'react'], PAGE_KIND_RETIRED_GUIDANCE).default('full'). The helper (shared/retired-key.ts:394) returns a plain z.enum with an error map, so the TypeScript input type drops the member (tsc refuses it), .default and .options follow the four-member enum, and the helper's own constructor guards (a retired name may not stay in values; the prescription may not be blank) hold by construction. An authored jsx is refused at kind with the prescription ("Page kind: 'jsx' was removed in @objectstack/spec 18 (ADR-0087 D2) … Use kind: 'html'"); a misspelling (htm) keeps zod's own message (pinned as the control); at defineStack it is STACK_SCHEMA_INVALID / 422 with the one issue at pages.0.kind. RIGHT — ruling F5-jsx A and item 7's "tsc and the parse-time prescription are the whole channel". The prescription carries no os migrate meta sentence, rightly: protocol 11 is below MIGRATION_SUPPORT_FLOOR. The runtime strings carry no tracker number (ADR citations only). The JSON Schema projection narrows with the enum; json-schema/ is built, not tracked, so no regeneration is owed in git.
  2. COMPILED_PAGE_KINDS is ['html'], checkPageSourceCompleteness reads html / react, and the checkPageRequiresKind refusal no longer sends an author to jsx (pinned: the message contains html and not jsx). RIGHT — the ruling's "together with the COMPILED_PAGE_KINDS branch". The exported tuple's type narrows from two members to one; api-surface/ui.json:76 and export-origins/ui.json:73 record the const by name only, so the changeset is the carrier (②).
  3. page-kind-jsx-to-html leaves the load path (conversions/registry.ts, the only hunk in that file: the docblock rewritten as the retirement record, plus retiredFromLoadPath: true and retiredAfter: '17.7.0' — the value F1 and the seven spec(conversions): 15 ADR-0087 D2 conversions are still on the load path at 18 though their window closed (10 toMajor: 17, 5 older): retire each or record why it stays, one verdict per entry (family closing card after #23421) #23487 retirements carry; MAJOR_18_CONVERSIONS and every other entry untouched). The authoring funnel no longer rewrites an authored jsx (pinned: the stack comes back equal, zero notices). The stored-row path holds through one seam: applyConversionsToStoredItem (conversions/stored.ts) replays retired entries; metadata-protocol reaches it through convertStoredItemDetailed, which every stored-row reader funnels into (boot hydration, get, list, draft, overlay, package duplication, migrateStoredMetadata), and packages/metadata/src/loaders/database-loader.ts is the second reader; the artifact door replays a retired entry while the artifact's floor is at or below its retiredAfter (artifact-forward-conversion.ts:37). So a sys_metadata page row written as jsx reaches PageSchema as html; pinned in conversions.test.ts (converges, and the converted row parses). RIGHT.
  4. The save door refuses, it does not convert. resolveOverlaySchema (protocol.ts:820) → getMetadataTypeSchema('page') (spec/src/kernel/metadata-type-schemas.ts:113, page: PageSchema) → safeParse(request.item) at :22562; only the flow verdict gets applyConversionsToFlow (:22535). A page saved with kind: 'jsx' is 422 with the prescription at kind, as the changeset and the D3 entry state. RIGHT — item 7's "no double spelling": after this PR no door accepts the spelling from an author or an API caller; only data at rest is converged, and only on the seam that replays retired entries.
  5. Every reader of the retired value dropped its branch; none tolerates it. Head-tree sweep of packages/*/src outside tests for the quoted value, excluding the entry, the guidance and the D3 entry: zero hits. metadata-protocol runtime-authoring-gate.ts COMPILED_PAGE_KINDS (html); cli/src/utils/sdui-manifest.ts checkedPageKeys (html); lint page-walk.ts SOURCE_AUTHORED_KINDS, validate-jsx-pages.ts (html) and validate-page-source-styling.ts (html / react); the rest comments and explanation text. The three lint rules now yield nothing for a jsx page (pinned as "does NOT read"): non-reading, not acceptance — os lint does not Zod-parse (lint.ts:623), and the refusal is the parse's at defineStack, os validate (validate.ts:349), os build (compile.ts:408; build.ts:5 is the alias) and the save door. RIGHT.
  6. Verdict-list pin (conversions.test.ts): the kept-without-ruling list drops to the six entries of F3, F2, F4 and F5-http; the retired list gains the entry with retiredAfter: '17.7.0'; the new describe('page-kind-jsx-to-html') pins the three claims that must hold together; retired-key-removal-version.pin.test.ts gains the refusal row in its JOIN, so every retired entry still has a renderer; the excludeConversionIds firing control still drives the entry under includeRetired: true. RIGHT — item 7's "verdict-list pin updated".
  7. D3 entry 18.page-kind-jsx-retired.ts — a SemanticMigration in the sibling shape, no conversionIds (protocol 11 is below the floor; migrations.test.ts refuses a link no step replays), surface with no backtick or pipe. src/migrations/registry.ts is generated from the template at build and is not tracked, so the entry registers by its file alone; check:spec-changes and check:upgrade-guide rode success inside Lint · repo & tooling gates; the check:adr-0087-registration marker names this id. RIGHT.
  8. Re-pointed suites. The three cli e2e suites (nightly tier — not run by the PR's Test Core; the dev's local 3 files / 35 passed is the only reading, not re-taken here), stack-conversions-record.test.ts and two conversions.test.ts cases now drive flow-node-http-callout-rename (live, protocol 11); metadata-core artifact-forward-conversion.test.ts FIRING CONTROL 1 drives page-component-visibility-to-visibleWhen (live, F4's entry). RIGHT for F5-jsx; the cost is carried in ③.
  9. Fixture triage: the html re-spellings keep their assertions; the deleted jsx rows pinned the deleted branch; protocol.batch-verb-driver-text.test.ts P13 still seeds a stored jsx row, which the stored pass still rewrites (judgment 3), so a comment was the right edit; format.conversion-notice.test.ts formats a synthetic notice. RIGHT.
  10. Text states only what is true — the four edits of 2e1b65d0 checked one by one against the head:
    • (a) the changeset's @objectstack/platform-objects row: the four files are src/apps/translations/{en,zh-CN,ja-JP,es-ES}.metadata-forms.generated.ts, imported by src/metadata-translations/index.ts:4-7 and exported from src/index.ts:33 as MetadataFormsTranslations, so the package does publish them; each diff drops exactly the alias parenthetical from the page form's source help text and nothing else (key sets unchanged; Spec property liveness and Lint & Repo Gates success). True.
    • (b) liveness/page.json, the requires row's note: "the key exists only on kind 'html' pages, the kind compiled at save" — true by judgment 2.
    • (c) page.form.ts:51: "only the two source kinds (html / react) ever read it" — true; the row's visibleWhen is data.kind in ['html','react'].
    • (d) the changeset body and the D3 entry's reason: "refused by name at os validate, os build, the metadata save door and defineStack, so under os lint too when the config calls it" (the D3 text spells the CLI objectstack) — exact by judgments 4 and 5: the three parsing doors and the save door refuse; os lint does not parse and meets the refusal only through a config that calls defineStack, which throws at load.
      The pre-existing text edits of 831a9fa8 re-checked: content/docs/ui/pages.mdx and react-pages.mdx (retired at 18, refused with the prescription, stored pages keep loading); the generated references/ui/page.mdx matches the new .describe() text; the two D3 entries, the step-18 rationale and the requires evidence. A whole-tree sweep of content/docs (excluding releases) finds no other sentence that still calls jsx a live or deprecated kind; the drift check's two hand-written pages (validating-metadata.mdx, layout-dsl.mdx) mention JSX only as the language. RIGHT.

Public-surface changes: no new export (PAGE_KIND_RETIRED_GUIDANCE is module-private; api-surface/ and export-origins/ unchanged). The one widening is the published migration catalogue gaining the D3 entry (judgment 7), the same half F1's record counted.

② Semver level

  • .changeset/23543-f5-jsx-retire-page-kind-jsx.md: @objectstack/spec: minor; @objectstack/metadata-protocol, @objectstack/cli, @objectstack/lint, @objectstack/platform-objects: patch; feat(spec)!: title; **BREAKING (authoring):** banner; Clause-②: yes (narrowing); FROM → TO (kind: 'jsx' → kind: 'html', and the API caller's one-word change); the honest statement that os migrate meta does not list the edit; how stored rows and artifacts keep loading; a body row per runtime reader; the ADR-0087 disposition marker registered page-kind-jsx-retired, whose id exists (judgment 7). The repo is in Changesets pre mode (.changeset/pre.json, tag next), where a BREAKING spec change grades minor (check-changeset-no-major.mjs header). Check Changeset and Lint · repo & tooling gates (check-adr-0087-registration, check-empty-changeset) are success on this head.
  • Coverage, the earlier record's FAIL condition, is now complete. The diff's non-test source touches exactly five published packages — spec, metadata-protocol, cli, lint, platform-objects (the drift check's own count of five) — and each has a frontmatter row with a body sentence. @objectstack/metadata-core changes one test file only, and its files is dist / README.md / CHANGELOG.md, so nothing of it publishes and no row is owed. content/docs/** and .changeset/** are not packages. RIGHT.
  • The three runtime readers at patch and platform-objects at patch: RIGHT — metadata-protocol's and cli's changed constants are module-private, lint's exported rules change behaviour only on an input the spec no longer admits, platform-objects ships a help-text wording change, and none of the four narrows a surface of its own; the narrowing is the spec row's.
  • The Clause-②: line. The changeset's yes (narrowing) is the ruling's spelling (item 7) and the right arm: judgments 1 to 4 narrow, judgment 7 widens the catalogue. The PR body's line now reads Clause-②: yes (narrowing) at the start of a line — the gap the earlier record's ③ item 3 named is closed. (The newest dev report says the body was untouched by the dev's commit; the line reads as above on the PR now, and that is what this record judges.) RIGHT.

③ Boundary flags

Dev open_questions (6187521557; the patch-round report 6188089228 carries none):

  1. skills/objectstack-ui/rules/pages.md:138 still calls jsx a deprecated alias; Tier H. Answered: A, as the seat review 6187645640 routed it: the sentence rides spec: PageTypeSchema says home is "the platform-level landing page" and utility a "floating utility panel", but the landing is the first navigation item and both types render as ordinary inline pages #23610's Tier H PR on the same file, the seat's ledger carries it until then, and this PR stays contract-tier. Still present on the head, correctly left alone. Escalated beside it, same Tier H carrier at the seat's choice: docs/adr/0081-trusted-react-page-tier.md (status line, Consumers, decision 1 and line 47) still records 'jsx' as a kept deprecated alias; ruling 6182001882 is the superseding decision of record, and a dated status note citing it keeps Prime Directive [WIP] Add Chinese version of the documentation #13 findable. Not a condition of this record.

Dev deviations (6187521557; none in 6188089228):

  1. Files beyond the claim's surface. Accepted — each is a pin-sweep or false-text hit named in the report and re-checked in ① judgments 8 to 10; the seat review amended the claim's surface to the file list.
  2. metadata-protocol, cli, lint at patch. Accepted (②).
  3. Worktree cut from a newer main; the model-named trailer not used. Accepted — process notes; the merge from origin/main f7cd580b came through os-regen-merge.sh and check:generated reads up to date on the head (Lint · repo & tooling gates success).

Dev out_of_scope_findings:

  1. objectui reads the retired value in more places than the ruling's one console sample. Verified at the pin b9814b334: packages/types/src/layout.ts:1795 (kind?: 'full' | 'slotted' | 'html' | 'jsx' | 'react'), packages/components/src/renderers/layout/page.tsx:611 (kind === 'html' || kind === 'jsx'), apps/console/src/sdui-jsx-preview.tsx:87 (kind: 'jsx'). The renderer branch is dead against this spec (a stored row arrives as html; no author can save jsx), not a tolerance that admits a spelling. Escalated to release(v18): objectui's first 18.x release — the sync major, Changesets pre mode on next, and the two gate edits (the maintainer's ruling on #12030), after #12030 lands objectui#12334, the ruling's objectui half, with the three sites; the seat posts them at landing as its review says. Console Pin Gate is skipped on the PR and measured by main's battery; the dev's reading that it stays green (build-console.sh injects this tree's spec at the vite bundle step only) is consistent with the script on the head and is NOT re-measured here, so the first main run after landing is the reading. One residue rides the same carrier: liveness/page.json's source row note names objectui's "kind:'jsx' branch" (page.tsx) — true at the pin, stale the day objectui#12334 lands.
  2. The re-pointed suites ride flow-node-http-callout-rename, which retires last. Escalated to the F5-http dispatch order on spec(conversions): eight ADR-0087 D2 conversions stay on the 18 load path after #23487 — five families each need a ruling before they can retire #23543, with the precision the report lacks: metadata-core's FIRING CONTROL 1 rides page-component-visibility-to-visibleWhen, which is F4's entry, so the F4 dispatch order (not F5-http's) carries that control's next re-point or synthetic probe. F5-http inherits the three cli e2e suites, stack-conversions-record and the two conversions.test.ts cases.
  3. check:platform-checklist red on main's tree. Zero overlap with this diff; the watchdog's card owns it. Not this PR's.

Residual text from the earlier record (items 8, 9 and 10): all three answered on this head by ① judgment 10 (b), (c) and (d); nothing further is owed. No new residue was found on the head beyond the objectui-dependent note in item 5.

Report corrections, not flags: the first dev report says 44 files; the PR has 43. The patch-round report's gate readings are the dev's own and are not inputs here; the check-runs above are.

Premise re-read on the head: no examples/** or packages/apps/** source authors kind: 'jsx'; the only jsx writers left are the conversion entry's own fixture, the prescription, the D3 entry and tests.

Landing terms are the seat's (6187645640 item 8): this record, then every check green — the seven in_progress runs named above are not yet a reading, and this PASS does not stand in for them.

Implemented-by: claude/issue-23543-retire-jsx-page-kind
Reviewed-by: session_01LFqdRsuM5nkqoJdHd5ztVk

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 15, 2026 09:15
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 15, 2026 09:15
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit c5dd7fc Oct 15, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23543-retire-jsx-page-kind branch October 15, 2026 09:48
baozhoutao pushed a commit to objectstack-ai/objectui that referenced this pull request Oct 15, 2026
… and move the console sample to html (objectui#12464)

@objectstack/spec 18 refuses the page kind jsx (objectstack-ai/objectstack#23638).
PageNodeSchema.kind drops 'jsx'; the console's sdui-jsx-preview sample authors
kind 'html'. The page renderer keeps its 'jsx' arm for stored rows until the
resolved spec carries the retirement; it reads kind as a widened wire value so
that arm still compiles against the narrowed type.

Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB
Co-authored-by: Claude <noreply@anthropic.com>
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 protocol:ui size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants