diff --git a/.changeset/sdui-react-page-tier.md b/.changeset/sdui-react-page-tier.md new file mode 100644 index 0000000000..17117a8dd3 --- /dev/null +++ b/.changeset/sdui-react-page-tier.md @@ -0,0 +1,20 @@ +--- +"@objectstack/spec": minor +"@objectstack/lint": minor +"@objectstack/cli": minor +--- + +ADR-0081: split the AI page-authoring surface into honest tiers. + +- `PageSchema.kind` gains `'html'` and `'react'`. `'html'` is the constrained + parse-never-execute tier (the renamed `'jsx'`, kept as a deprecated alias); + `'react'` is the real-React tier (executed at render by + `@object-ui/react-runtime`). It runs author JS, so it is gated by a host + capability that **defaults ON** (the platform trusts reviewed, draft-gated + authors) and is disabled **server-side** via the `OS_PAGE_REACT=off` + env toggle. The completeness gate now requires `source` for all three kinds. +- `@objectstack/cli` console serving injects the disable global into the served + HTML when `OS_PAGE_REACT=off` (read per request, no rebuild). +- `validate-jsx-pages` lints `html`/`jsx` (constrained parse). A new + `validate-react-pages` transpiles `react` source with Sucrase (transpile-only, + never executed) so syntax errors fail at `os build` instead of at render. diff --git a/docs/adr/0081-trusted-react-page-tier.md b/docs/adr/0081-trusted-react-page-tier.md new file mode 100644 index 0000000000..1b4dcc80f9 --- /dev/null +++ b/docs/adr/0081-trusted-react-page-tier.md @@ -0,0 +1,66 @@ +# ADR-0081: A trusted `kind:'react'` page tier — real React executed in the main tree, gated by a host capability; and renaming `kind:'jsx'` → `kind:'html'` + +**Status**: Proposed (2026-06-30) +**Deciders**: ObjectStack Protocol Architects +**Builds on**: [ADR-0080](./0080-ai-authored-ui-jsx-source.md) (AI authors a *constrained* JSX text, parsed-never-executed, into the SDUI tree; the component registry is the contract; capability ≠ contract), [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (AI writes metadata via draft-gated `saveMetaItem` — the human-review boundary), [ADR-0077](./0077-authoring-surface-boundary-hook-flow-validation.md) (route by intent; verifiability tier = friction tier; **code is the flagged escape hatch**), [ADR-0078](./0078-no-silently-inert-metadata.md) (no silently-inert metadata). +**Consumers**: `@objectstack/spec` (`packages/spec/src/ui/page.zod.ts` — `kind` gains `'html'` and `'react'`; `'jsx'` kept as a deprecated alias), `@objectstack/lint` (`validate-jsx-pages` — lints `html`/`jsx`, **skips** `react`), `../objectui` `@object-ui/react-runtime` (**new** — the trusted runtime-React executor) + `@object-ui/core` (the host capability gate) + `@object-ui/components` (PageRenderer routing + the full HTML tag set). + +**Premise**: ADR-0080 shipped `kind:'jsx'` — a *constrained* JSX dialect parsed (never executed) into the SDUI tree. In practice that name oversold it: authors expected "JSX" to mean *any* HTML and *any* JavaScript (`useState`, `.map`, `onClick`), and the constrained, no-execution model categorically cannot do that. Two distinct things were conflated under one name. This ADR splits them: it makes the safe constrained tier **live up to a narrower, honest name (`html`)**, and adds a separate, explicitly-trusted **`react`** tier for the full-power case — available only where the operator accepts running author code. + +> **Trigger**: testing the merged `kind:'jsx'` feature, the author found it "名不副实" (misnamed) — basic HTML tags didn't render and scripts were impossible — and recognized that the real-React capability belongs behind a switch. The platform trusts its (reviewed, draft-gated) page authors, so that switch defaults ON; a deployment that does not trust its authors turns it off server-side. "Running author code in the main React app" is an accepted, default trust decision, reversible per deployment — not an enterprise-only feature. + +--- + +## TL;DR + +1. **[rename] `kind:'jsx'` → `kind:'html'`.** The constrained, parse-never-execute tier (ADR-0080) is renamed to match what it is: author-written **HTML + Tailwind** (expressed as constrained JSX) compiled to the SDUI tree. `'jsx'` stays as a **deprecated alias** (already-saved pages keep loading); all authored examples/docs move to `'html'`. +2. **[capability] The `html` tier now resolves the full safe native HTML tag set.** Previously only `div/span/table/code/label` + semantic sectioning tags were registered, so `

/

//