You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
security(components): the ui 'html' renderer injects schema.html unsanitised — the spec declares no html component; retire the type #13449
Sink: ComponentRegistry.register('html', …) in packages/components/src/renderers/basic/html.tsx renders a div whose inner HTML is schema.html; the registration's own inputs row describes it as "Raw HTML content". HtmlSchema lives in packages/types/src/data-display.ts.
Reachable: imported by packages/components/src/renderers/basic/index.ts (the line import './html';); admitted by packages/types/src/zod/data-display.zod.ts (type: z.literal('html')); present in the built console's apps/console/dist/sdui.manifest.json.
No sanitiser on the path: DOMPurify is imported by 0 source files (the root dompurify override only serves a transitive dependency); PR objectui#13431 added rehype-sanitize to fields' html / richtext values and did not touch this renderer.
Spec side: packages/spec/src/ui/component.zod.ts mentions html only in prose; the page-level kind: 'html' (page.zod.ts, COMPILED_PAGE_KINDS) is constrained JSX compiled by @objectstack/sdui-parser, whose FORBIDDEN_ATTRS in packages/sdui-parser/src/parse.ts includes dangerouslySetInnerHTML.
Session token lives in localStorage (packages/auth/src/createAuthClient.ts, TOKEN_STORAGE_KEY), which makes a script injection a session-theft primitive.
Issue dump search for dangerouslySetInnerHTML|XSS|sanitiz over 2,012 cards: only objectui#13431 (fields). Other dangerouslySetInnerHTML sites were read and are not metadata-driven raw strings (print stylesheet, scoped CSS, shiki output, mermaid SVG, an escaped hand-rolled markdown preview).
Recommendation
Retire the html component type: remove the registration, HtmlSchema, its zod row and docs; rich HTML belongs to the already-sanitised fields path or to a kind: 'html' page. One ! changeset for components and types.
If a production consumer of type: 'html' turns up (未实测: external npm consumers), the fallback is sanitising through the same rehype-sanitize schema fields use — but that keeps an objectui-only component the spec does not know.
Effort S. Priority input: NORTH-STAR says security and data integrity never wait for the path.
Refs: objectui#13431.
Filed by the read-only whole-repo review session https://claude.ai/code/session_01BDtwYUWhLGyWLzYQjZ95Cf on 2026-10-24; the maintainer approved filing (「同意,创建任务」). Full evidence files sit in that session's scratchpad (objectui-review-report.md, findings-*.md). ⛔ Not a claim; for triage to grade and route.
⛔ Not a claim, ⛔ not a dispatch. finding comes off.
p1: a stored-XSS sink on a published renderer. The trust boundary is "can write metadata", and the session token sits in localStorage. Security does not wait for the path.
Route: retire, as recommended. The spec declares no html component, so this is an objectui-only type the contract does not know. It gets one ! changeset for components and types. Under the 18.x standing order a removal is direct, with no retirement stage. Sanitising would keep a component the spec does not declare, so it is the fallback only if a real consumer turns up.
Claim: PM loop round 1
Session: session_01432GBvPzfZ6CsLanp9xzkZ
Account: sunhaolin
Branch: claude/issue-13449-retire-html-renderer
Worktree: objectui-issue-13449
Domain: domain:ui
Seat: domain:ui#1
File surface: packages/components/src/renderers/basic/**, packages/types/src/data-display.ts, packages/types/src/zod/data-display.zod.ts, the docs and tests that name the html component type, .changeset/13449-retire-html-renderer.md (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: default (dispatch-gates --tier: no path-derived mandate), effort: xhigh
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Responsibility: objectui's own code (@object-ui/components html renderer, @object-ui/types HtmlSchema + zod row) | the sanitised fields html/richtext path (objectui#13431) and the spec's compiled kind:'html' page cover rich HTML | any metadata author reaches it; no in-repo producer of type:'html' measured, external npm consumers not measured
Thread-read: 6385669346
Premise: holds
Serial constraints cleared: none (no open code PR in objectui at 2026-10-24T16:49Z; no other pm:dispatched card in the lane)
Clause-②: no — the card removes an objectui-only component type the spec does not declare: it narrows @object-ui/types' accept set (BREAKING, ! changeset) and widens nothing. It owes one contract-review-tier review before the queue.
Premise reading at objectui origin/maina3c0da9: ComponentRegistry.register('html' at packages/components/src/renderers/basic/html.tsx:14; import './html'; at basic/index.ts:15; z.literal('html') at packages/types/src/zod/data-display.zod.ts:1652; export interface HtmlSchema at packages/types/src/data-display.ts:3697.
Amended 2026-10-25 by the claiming seat: Clause-②: no → Clause-②: no (narrowing). The card narrows a published accept set, and a bare no declares no direction. Finding source: contract review on objectui#13475, comment 6393057967.
{
"issue": 13449,
"status": "done",
"branch": "claude/issue-13449-retire-html-renderer",
"pr": "#13475",
"session": "session_01432GBvPzfZ6CsLanp9xzkZ",
"premise_still_valid": true,
"summary": "Retired the raw-HTML html node type per the triage route: deleted the ui:html/bare html registration and its import, dropped html from PUBLIC_BLOCKS (so getPublicConfigs and the published sdui.manifest.json no longer carry it), removed HtmlSchema from the types entry, DataDisplaySchema and SchemaRegistry, and turned its zod arm into an unexported retiredNodeType refusal (the objectui#8802 / #12954 shape) so objectui validate refuses type: \"html\" by name with the remedy. Docs page, catalog example and the types example entry removed; known-schema-types and the catalog index regenerated by their generators; one breaking changeset marked minor with FROM/TO and the one-line fix. Reach measured at runtime before the fix: both a JSON node and a kind: 'html' page (an html tag carrying an html attribute compiled with ZERO diagnostics) injected an img element with an onerror attribute; after the fix the JSON node draws OBJUI-001 and the page compile refuses the tag (forbidden-tag, unknown-component). H2: content/docs/fields/rich-text.mdx :43 and :150 are the FIELD html type (HtmlFieldMetadata) and stay; check:doc-types had passed :43 only via the retired bare key, so it now carries a field-vocabulary exemption. H4 holds: no in-repo product producer (git grep of quoted html over every tracked file, read hit by hit). File surface went beyond the claim's list to every list naming html as a component type, as the dispatch's suggested route asked (listed in files_changed and the PR body); no governed surface (check-governed-queue-guard --test: NOT GOVERNED, 31 paths).",
"tests": "Union at 3c6faea under the verify lock (VERDICT command-exit 0, held 52m11s shared box), exits captured to file: vitest packages/types+core+sdui-parser+cli+examples/schema-catalog Test Files 697 passed (697)Tests 17792 passed | 27 skipped; packages/components+fields Test Files 705 passed | 2 skipped (707)Tests 8517 passed | 31 skipped; apps/console Test Files 243 passed (243)Tests 2460 passed; 48 reader files outside those packages (every test naming PUBLIC_BLOCKS / getPublicConfigs / KNOWN_SCHEMA_TYPES / FIELD_TYPES_SKIP_FALLBACK / the examples file / the catalog / react-pages.md / ui:html / check-doc-component-types, plus unit-registry-absence-collision) Test Files 48 passed (48)Tests 1543 passed | 16 skipped. type-check core/components/fields/cli exit 0, types exit 2 on a vacuous compile pin in the new test ('kbd' is no SchemaRegistry key; tsc -p tsconfig.test.json named the new file, which proves it is in the program) fixed in bfd3722. Final head bfd3722 (differs from 3c6faea by that one test file): type-check types/core/components/fields/cli all exit 0; html-node-retired-13449 (types) Tests 6 passed (6). Ablation (one-shot, fix committed at bfd3722, trap-guarded): nine impl files put back from a3c0da9 incl. html.tsx, landing proved by on-disk counts (register('html' 1, import './html'; 1, PUBLIC_BLOCKS 'html', row 1, HtmlSchema arm 1, RetiredHtmlNodeSchema 0); tests resolve @object-ui/* via root vitest resolve.alias to src, so no dist in the path; five pin files Test Files 5 failed (5)Tests 11 failed | 65 passed (76) = every new assertion red, every control green; restore from HEAD: git diff HEAD 0 bytes, porcelain 0, html.tsx absent, every restored path's hash equals its HEAD blob.",
"gates": "Local subset at bfd3722, each exit captured to file before reading: check:doc-types 0 (after the field-type exemption; it was 1 naming rich-text.mdx:43 before), check:registry-bare-names 0 ('no new bare-name collision'), check:handler-key-reads 0, check:doc-example-ids 0, check:docs-route-closure 0, check:prompt-keys 0, check:entry-guard 0, check:action-forward-parity 0, check:spec-symbols 0, check:new-line-citations 0 ('VERDICT new-cross-file-line-citations: 0 new citation(s)'), check:control-bytes 0, check:doc-fences 0, check:unreferenced-sources 0, check:side-effects-array 0, check:doc-example-readers 0, check:icon-record-names 0, check:changeset-claims 0, check:pending-changeset-literals 0, check:test-path-roots 0, check:doc-json 0, check-changeset-presence 0 ('22 source file(s) of 7 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major 0, regenerate-catalog-index.py --check 0, regenerate-known-schema-types.mjs --check 0 ('matches the registry derivation'), check-governed-queue-guard --test 0 (NOT GOVERNED). NOT MEASURED locally: check:sdui-registration-pins (exit 2, needs a console build), check:readme-exports (population collapsed, packages unbuilt), check:doc-snippets and check:doc-examples (exit 2 PRECONDITION NOT MET, need the wider build), apps/console and packages/app-shell type-check (need the whole closure), pnpm lint (CI-owned). objectui has no dispatch-gates derivation, so no --ran reconciliation exists; the subset was chosen by grepping scripts for the touched paths and symbols. CI check-runs on bfd3722 at report time: Changeset Bump Policy, Changeset Claim Re-read, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, Test (dist pins), label all success; Test (coverage) skipped; Type Check, Lint, Repo Gates, Build & E2E, Build Docs, Test (shard 1..16/16), Bundle Analysis, Live E2E (informational) in_progress.",
"dogfood_battery": "n/a — objectui has no dogfood battery (the battery is objectstack's @objectstack/dogfood; this PR lands in objectui). The title carries ! and the changeset is breaking, so the repo's full farm in CI (Test shard 1..16/16, Build & E2E) is the whole-tree reading.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 — (1) pr_create through the fleet relay: POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos//issues/13475/assignees, read back 11837 bytes sent = stored, assignee sunhaolin; (2) this os-dev-report comment, POST /repos//issues/13449/comments via post-stamped. git push x6 (empty branch probe + 5 commits) is not a REST write.",
"line_budget": "n/a — no skills/** or ratcheted ledger touched",
"files_changed": [
".changeset/13449-retire-html-renderer.md",
"apps/console/src/tests/html-node-retired-13449.test.tsx",
"apps/console/src/tests/html-tier-manifest.test.ts",
"apps/console/src/tests/public-contract.test.ts",
"content/docs/components/basic/html.mdx (deleted)",
"content/docs/components/basic/meta.json",
"content/docs/components/index.md",
"content/docs/guide/react-pages.md",
"examples/schema-catalog/src/index.ts (regenerated)",
"examples/schema-catalog/src/schemas/components-basic-html/basic-html.json (deleted)",
"packages/app-shell/src/tests/widget-dom-leak-sweep.test.tsx",
"packages/cli/src/tests/registered-types-validate-ratchet-10859.test.ts",
"packages/cli/src/utils/known-schema-types.ts (regenerated)",
"packages/components/src/tests/basic-renderers.test.tsx",
"packages/components/src/renderers/basic/html.tsx (deleted)",
"packages/components/src/renderers/basic/index.ts",
"packages/core/src/registry/public-blocks.ts",
"packages/fields/src/tests/registry-collision.test.ts",
"packages/fields/src/index.tsx",
"packages/types/examples/data-display-examples.json",
"packages/types/src/tests/arm-named-export-8784.test.ts",
"packages/types/src/tests/content-channel-family-d-9256.test.ts",
"packages/types/src/tests/html-node-retired-13449.test.ts",
"packages/types/src/tests/zod-mirror-parity.test.ts",
"packages/types/src/data-display.ts",
"packages/types/src/index.ts",
"packages/types/src/registry.ts",
"packages/types/src/zod/README.md",
"packages/types/src/zod/data-display.zod.ts",
"packages/types/src/zod/index.zod.ts",
"scripts/check-doc-component-types.mjs"
],
"deviations": [
"Declared narrowing: the full vitest union ran at 3c6faea, not at the final head bfd3722; bfd3722 changes only packages/types/src/tests/html-node-retired-13449.test.ts (a compile-time alias anchored on 'statistic' instead of 'kbd'), so at bfd3722 I re-ran the five type-checks, that file's vitest and the whole local gate subset. CI runs the farm on bfd3722.",
"File surface beyond the claim's list (claim: renderers/basic/**, the two types files, docs and tests naming the component, the changeset): packages/core/src/registry/public-blocks.ts, packages/types/src/{registry.ts,index.ts,zod/index.zod.ts,zod/README.md}, packages/types/examples/data-display-examples.json, packages/cli/src/utils/known-schema-types.ts (generated), packages/fields/src/index.tsx (regroup and comment, no behaviour change), the schema-catalog example and generated index, scripts/check-doc-component-types.mjs (one exemption; its suite ran green in the 48-file set), content/docs/guide/react-pages.md, and count/list pins in apps/console, packages/app-shell, packages/cli, packages/fields. Each is a list naming html as a component type, which the dispatch's suggested route asked to search; declared in the PR body.",
"Harness attribution reminder asked for a model-named Co-Authored-By trailer and a 'Generated with' PR footer; commits carry the model-free pair objectui AGENTS.md requires (Claude-Session + Co-authored-by: Claude) and the PR body the session-URL footer from os-dev.md, per the precedence both files state.",
"My first lock call (pnpm run setup) was queued without OS_VERIFY_LOCK_SLOT; its wrapper was stopped, the queued child kept its place and ran (VERDICT command-exit 0). Every later call set OS_VERIFY_LOCK_SLOT=issue-13449.",
"A temporary probe test (apps/console/src/tests/zz-probe-13449.test.tsx) was written in the worktree to measure reach before and after, never committed, deleted before the first commit; its findings are carried by the committed console pin.",
"Worktree removed after the PR opened (node_modules first, then git worktree remove, no --force)."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: objectstack's next .objectui-sha bump · objectstack's tracked repo-root sdui.manifest.json still names html (Raw HTML content); it is generated from objectui's pinned build by scripts/gen-sdui-manifest-node.mjs and check-sdui-manifest's staleness check forces the regenerate on the bump, which drops the entry; until then objectstack's server-side validateJsxPages admits the tag while the console's own compile refuses it · noted, not filed",
"carrier: 承接者:无 · objectstack ADR-0080 names renderers/basic/html.tsx the 'today unsafe' escape hatch the constrained JSX tier replaces, and docs/audits/2026-08-zod-to-json-schema-fidelity.md lists HtmlSchema; both are dated records, left as written · Acceptance notes only"
]
}
{
"issue": 13449,
"status": "done",
"branch": "claude/issue-13449-retire-html-renderer",
"pr": "https://github.com/objectstack-ai/objectui/pull/13475",
"session": "session_01432GBvPzfZ6CsLanp9xzkZ",
"premise_still_valid": true,
"summary": "Patch round: in .changeset/13449-retire-html-renderer.md the line-initial 'Clause-②: no' now reads 'no (narrowing)'. One line changed, no other file. Pushed as a fast-forward (no force, no rebase) from bfd37223d to 0841978a5.",
"tests": "No code touched; no tests run. New head 0841978a548e74c745e103544717496f1ddb7273. objectstack's clause2-line.mjs has no CLI (--help prints nothing), so it was called as a module: readClause2Line on the changeset returned {kind:declared,value:no,arm:narrowing,line:'Clause-②: no (narrowing)'}; readMetadataProtocolLine returned narrowed.",
"gates": "Head 0841978a5. Only the clause2-line reader was run locally (output above). No other gate was run locally and CI results are not cited here; CI convergence is left to PM.",
"dogfood_battery": "n/a — not run this round; the one-line changeset edit does not touch code",
"mcp_calls": "0",
"api_writes": "1 — this os-dev-report card comment via post-stamped.mjs (git push is not a REST write)",
"open_questions": [],
"out_of_scope_findings": []
}
merged: true, merge commit 610d9efd5, through the merge queue
origin/main tip
610d9efd5 feat(components,types)!: retire the raw-HTML html node type (objectui#13449) (#13475)
content on origin/main
packages/components/src/renderers/basic/html.tsx absent; register('html' in packages/components/src: 0 hits; control RetiredHtmlNodeSchema in packages/types/src/zod/data-display.zod.ts: 2 hits (the refusal arm is there, so the zero is not an empty read)
State: the card closed completed through Fixes #13449, and pm:dispatched was removed in this act (security, priority:p1, domain:ui, area:records stay).
Still open after this landing (named, not owned by this lane): objectstack's shipped console renders the html sink until objectstack bumps .objectui-sha past 610d9efd5. That bump regenerates the tracked sdui.manifest.json, which still names html; check-sdui-manifest forces the regenerate. The bump is objectstack's own card per the lane rule, not this card. Publishing @object-ui/* is the changesets release PR, a human action.
Activity
objectstack-fleet commentedon Oct 24, 2026
Triage: first grade —
bug·security·priority:p1·domain:ui·area:records·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-24T14:54Z⛔ Not a claim, ⛔ not a dispatch.
findingcomes off.localStorage. Security does not wait for the path.htmlcomponent, so this is an objectui-only type the contract does not know. It gets one!changeset for components and types. Under the 18.x standing order a removal is direct, with no retirement stage. Sanitising would keep a component the spec does not declare, so it is the fallback only if a real consumer turns up.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim: PM loop round 1
Session:
session_01432GBvPzfZ6CsLanp9xzkZAccount:
sunhaolinBranch:
claude/issue-13449-retire-html-rendererWorktree:
objectui-issue-13449Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/components/src/renderers/basic/**,packages/types/src/data-display.ts,packages/types/src/zod/data-display.zod.ts, the docs and tests that name thehtmlcomponent type,.changeset/13449-retire-html-renderer.md(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: default (dispatch-gates --tier: no path-derived mandate),effort: xhighClause-②: no (narrowing)
Metadata-protocol: narrowed
Responsibility:
objectui's own code (@object-ui/components html renderer, @object-ui/types HtmlSchema + zod row) | the sanitised fields html/richtext path (objectui#13431) and the spec's compiled kind:'html' page cover rich HTML | any metadata author reaches it; no in-repo producer of type:'html' measured, external npm consumers not measuredThread-read: 6385669346
Premise: holds
Serial constraints cleared: none (no open code PR in objectui at 2026-10-24T16:49Z; no other
pm:dispatchedcard in the lane)Clause-②: no— the card removes an objectui-only component type the spec does not declare: it narrows@object-ui/types' accept set (BREAKING,!changeset) and widens nothing. It owes one contract-review-tier review before the queue.Premise reading at objectui
origin/maina3c0da9:ComponentRegistry.register('html'atpackages/components/src/renderers/basic/html.tsx:14;import './html';atbasic/index.ts:15;z.literal('html')atpackages/types/src/zod/data-display.zod.ts:1652;export interface HtmlSchemaatpackages/types/src/data-display.ts:3697.Amended 2026-10-25 by the claiming seat:
Clause-②: no→Clause-②: no (narrowing). The card narrows a published accept set, and a barenodeclares no direction. Finding source: contract review on objectui#13475, comment 6393057967.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
os-dev-report
{
"issue": 13449,
"status": "done",
"branch": "claude/issue-13449-retire-html-renderer",
"pr": "#13475",
"session": "session_01432GBvPzfZ6CsLanp9xzkZ",
"premise_still_valid": true,
"summary": "Retired the raw-HTML
htmlnode type per the triage route: deleted theui:html/barehtmlregistration and its import, droppedhtmlfrom PUBLIC_BLOCKS (so getPublicConfigs and the published sdui.manifest.json no longer carry it), removed HtmlSchema from the types entry, DataDisplaySchema and SchemaRegistry, and turned its zod arm into an unexported retiredNodeType refusal (the objectui#8802 / #12954 shape) soobjectui validaterefusestype: \"html\"by name with the remedy. Docs page, catalog example and the types example entry removed; known-schema-types and the catalog index regenerated by their generators; one breaking changeset marked minor with FROM/TO and the one-line fix. Reach measured at runtime before the fix: both a JSON node and akind: 'html'page (an html tag carrying an html attribute compiled with ZERO diagnostics) injected an img element with an onerror attribute; after the fix the JSON node draws OBJUI-001 and the page compile refuses the tag (forbidden-tag, unknown-component). H2: content/docs/fields/rich-text.mdx :43 and :150 are the FIELD html type (HtmlFieldMetadata) and stay; check:doc-types had passed :43 only via the retired bare key, so it now carries a field-vocabulary exemption. H4 holds: no in-repo product producer (git grep of quoted html over every tracked file, read hit by hit). File surface went beyond the claim's list to every list naming html as a component type, as the dispatch's suggested route asked (listed in files_changed and the PR body); no governed surface (check-governed-queue-guard --test: NOT GOVERNED, 31 paths).","tests": "Union at 3c6faea under the verify lock (VERDICT command-exit 0, held 52m11s shared box), exits captured to file: vitest packages/types+core+sdui-parser+cli+examples/schema-catalog
Test Files 697 passed (697)Tests 17792 passed | 27 skipped; packages/components+fieldsTest Files 705 passed | 2 skipped (707)Tests 8517 passed | 31 skipped; apps/consoleTest Files 243 passed (243)Tests 2460 passed; 48 reader files outside those packages (every test naming PUBLIC_BLOCKS / getPublicConfigs / KNOWN_SCHEMA_TYPES / FIELD_TYPES_SKIP_FALLBACK / the examples file / the catalog / react-pages.md / ui:html / check-doc-component-types, plus unit-registry-absence-collision)Test Files 48 passed (48)Tests 1543 passed | 16 skipped. type-check core/components/fields/cli exit 0, types exit 2 on a vacuous compile pin in the new test ('kbd' is no SchemaRegistry key; tsc -p tsconfig.test.json named the new file, which proves it is in the program) fixed in bfd3722. Final head bfd3722 (differs from 3c6faea by that one test file): type-check types/core/components/fields/cli all exit 0; html-node-retired-13449 (types)Tests 6 passed (6). Ablation (one-shot, fix committed at bfd3722, trap-guarded): nine impl files put back from a3c0da9 incl. html.tsx, landing proved by on-disk counts (register('html' 1, import './html'; 1, PUBLIC_BLOCKS 'html', row 1, HtmlSchema arm 1, RetiredHtmlNodeSchema 0); tests resolve @object-ui/* via root vitest resolve.alias to src, so no dist in the path; five pin filesTest Files 5 failed (5)Tests 11 failed | 65 passed (76)= every new assertion red, every control green; restore from HEAD: git diff HEAD 0 bytes, porcelain 0, html.tsx absent, every restored path's hash equals its HEAD blob.","gates": "Local subset at bfd3722, each exit captured to file before reading: check:doc-types 0 (after the field-type exemption; it was 1 naming rich-text.mdx:43 before), check:registry-bare-names 0 ('no new bare-name collision'), check:handler-key-reads 0, check:doc-example-ids 0, check:docs-route-closure 0, check:prompt-keys 0, check:entry-guard 0, check:action-forward-parity 0, check:spec-symbols 0, check:new-line-citations 0 ('VERDICT new-cross-file-line-citations: 0 new citation(s)'), check:control-bytes 0, check:doc-fences 0, check:unreferenced-sources 0, check:side-effects-array 0, check:doc-example-readers 0, check:icon-record-names 0, check:changeset-claims 0, check:pending-changeset-literals 0, check:test-path-roots 0, check:doc-json 0, check-changeset-presence 0 ('22 source file(s) of 7 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major 0, regenerate-catalog-index.py --check 0, regenerate-known-schema-types.mjs --check 0 ('matches the registry derivation'), check-governed-queue-guard --test 0 (NOT GOVERNED). NOT MEASURED locally: check:sdui-registration-pins (exit 2, needs a console build), check:readme-exports (population collapsed, packages unbuilt), check:doc-snippets and check:doc-examples (exit 2 PRECONDITION NOT MET, need the wider build), apps/console and packages/app-shell type-check (need the whole closure), pnpm lint (CI-owned). objectui has no dispatch-gates derivation, so no --ran reconciliation exists; the subset was chosen by grepping scripts for the touched paths and symbols. CI check-runs on bfd3722 at report time: Changeset Bump Policy, Changeset Claim Re-read, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, Test (dist pins), label all success; Test (coverage) skipped; Type Check, Lint, Repo Gates, Build & E2E, Build Docs, Test (shard 1..16/16), Bundle Analysis, Live E2E (informational) in_progress.",
"dogfood_battery": "n/a — objectui has no dogfood battery (the battery is objectstack's @objectstack/dogfood; this PR lands in objectui). The title carries
!and the changeset is breaking, so the repo's full farm in CI (Test shard 1..16/16, Build & E2E) is the whole-tree reading.","mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 — (1) pr_create through the fleet relay: POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos//issues/13475/assignees, read back 11837 bytes sent = stored, assignee sunhaolin; (2) this os-dev-report comment, POST /repos//issues/13449/comments via post-stamped. git push x6 (empty branch probe + 5 commits) is not a REST write.",
"line_budget": "n/a — no skills/** or ratcheted ledger touched",
"files_changed": [
".changeset/13449-retire-html-renderer.md",
"apps/console/src/tests/html-node-retired-13449.test.tsx",
"apps/console/src/tests/html-tier-manifest.test.ts",
"apps/console/src/tests/public-contract.test.ts",
"content/docs/components/basic/html.mdx (deleted)",
"content/docs/components/basic/meta.json",
"content/docs/components/index.md",
"content/docs/guide/react-pages.md",
"examples/schema-catalog/src/index.ts (regenerated)",
"examples/schema-catalog/src/schemas/components-basic-html/basic-html.json (deleted)",
"packages/app-shell/src/tests/widget-dom-leak-sweep.test.tsx",
"packages/cli/src/tests/registered-types-validate-ratchet-10859.test.ts",
"packages/cli/src/utils/known-schema-types.ts (regenerated)",
"packages/components/src/tests/basic-renderers.test.tsx",
"packages/components/src/renderers/basic/html.tsx (deleted)",
"packages/components/src/renderers/basic/index.ts",
"packages/core/src/registry/public-blocks.ts",
"packages/fields/src/tests/registry-collision.test.ts",
"packages/fields/src/index.tsx",
"packages/types/examples/data-display-examples.json",
"packages/types/src/tests/arm-named-export-8784.test.ts",
"packages/types/src/tests/content-channel-family-d-9256.test.ts",
"packages/types/src/tests/html-node-retired-13449.test.ts",
"packages/types/src/tests/zod-mirror-parity.test.ts",
"packages/types/src/data-display.ts",
"packages/types/src/index.ts",
"packages/types/src/registry.ts",
"packages/types/src/zod/README.md",
"packages/types/src/zod/data-display.zod.ts",
"packages/types/src/zod/index.zod.ts",
"scripts/check-doc-component-types.mjs"
],
"deviations": [
"Declared narrowing: the full vitest union ran at 3c6faea, not at the final head bfd3722; bfd3722 changes only packages/types/src/tests/html-node-retired-13449.test.ts (a compile-time alias anchored on 'statistic' instead of 'kbd'), so at bfd3722 I re-ran the five type-checks, that file's vitest and the whole local gate subset. CI runs the farm on bfd3722.",
"File surface beyond the claim's list (claim: renderers/basic/**, the two types files, docs and tests naming the component, the changeset): packages/core/src/registry/public-blocks.ts, packages/types/src/{registry.ts,index.ts,zod/index.zod.ts,zod/README.md}, packages/types/examples/data-display-examples.json, packages/cli/src/utils/known-schema-types.ts (generated), packages/fields/src/index.tsx (regroup and comment, no behaviour change), the schema-catalog example and generated index, scripts/check-doc-component-types.mjs (one exemption; its suite ran green in the 48-file set), content/docs/guide/react-pages.md, and count/list pins in apps/console, packages/app-shell, packages/cli, packages/fields. Each is a list naming html as a component type, which the dispatch's suggested route asked to search; declared in the PR body.",
"Harness attribution reminder asked for a model-named Co-Authored-By trailer and a 'Generated with' PR footer; commits carry the model-free pair objectui AGENTS.md requires (Claude-Session + Co-authored-by: Claude) and the PR body the session-URL footer from os-dev.md, per the precedence both files state.",
"My first lock call (pnpm run setup) was queued without OS_VERIFY_LOCK_SLOT; its wrapper was stopped, the queued child kept its place and ran (VERDICT command-exit 0). Every later call set OS_VERIFY_LOCK_SLOT=issue-13449.",
"A temporary probe test (apps/console/src/tests/zz-probe-13449.test.tsx) was written in the worktree to measure reach before and after, never committed, deleted before the first commit; its findings are carried by the committed console pin.",
"Worktree removed after the PR opened (node_modules first, then git worktree remove, no --force)."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: objectstack's next .objectui-sha bump · objectstack's tracked repo-root sdui.manifest.json still names html (Raw HTML content); it is generated from objectui's pinned build by scripts/gen-sdui-manifest-node.mjs and check-sdui-manifest's staleness check forces the regenerate on the bump, which drops the entry; until then objectstack's server-side validateJsxPages admits the tag while the console's own compile refuses it · noted, not filed",
"carrier: 承接者:无 · objectstack ADR-0080 names renderers/basic/html.tsx the 'today unsafe' escape hatch the constrained JSX tier replaces, and docs/audits/2026-08-zod-to-json-schema-fidelity.md lists HtmlSchema; both are dated records, left as written · Acceptance notes only"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
os-dev-report
{ "issue": 13449, "status": "done", "branch": "claude/issue-13449-retire-html-renderer", "pr": "https://github.com/objectstack-ai/objectui/pull/13475", "session": "session_01432GBvPzfZ6CsLanp9xzkZ", "premise_still_valid": true, "summary": "Patch round: in .changeset/13449-retire-html-renderer.md the line-initial 'Clause-②: no' now reads 'no (narrowing)'. One line changed, no other file. Pushed as a fast-forward (no force, no rebase) from bfd37223d to 0841978a5.", "tests": "No code touched; no tests run. New head 0841978a548e74c745e103544717496f1ddb7273. objectstack's clause2-line.mjs has no CLI (--help prints nothing), so it was called as a module: readClause2Line on the changeset returned {kind:declared,value:no,arm:narrowing,line:'Clause-②: no (narrowing)'}; readMetadataProtocolLine returned narrowed.", "gates": "Head 0841978a5. Only the clause2-line reader was run locally (output above). No other gate was run locally and CI results are not cited here; CI convergence is left to PM.", "dogfood_battery": "n/a — not run this round; the one-line changeset edit does not touch code", "mcp_calls": "0", "api_writes": "1 — this os-dev-report card comment via post-stamped.mjs (git push is not a REST write)", "open_questions": [], "out_of_scope_findings": [] }objectstack-fleet commentedon Oct 25, 2026
ACCEPT → PR #13475 comment 6393203167 · contract review PASS → PR #13475 comment 6393106376 (supersedes FAIL 6393057967) · head
0841978· ready + auto-merge armed 2026-10-25T04:00ZGenerated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
LANDED — PR #13475
domain:uiseat 1 ·session_01432GBvPzfZ6CsLanp9xzkZ· 2026-10-25T04:16Zmerged: true, merge commit610d9efd5, through the merge queueorigin/maintip610d9efd5 feat(components,types)!: retire the raw-HTML html node type (objectui#13449) (#13475)origin/mainpackages/components/src/renderers/basic/html.tsxabsent;register('html'inpackages/components/src: 0 hits; controlRetiredHtmlNodeSchemainpackages/types/src/zod/data-display.zod.ts: 2 hits (the refusal arm is there, so the zero is not an empty read)State: the card closed
completedthroughFixes #13449, andpm:dispatchedwas removed in this act (security,priority:p1,domain:ui,area:recordsstay).Still open after this landing (named, not owned by this lane): objectstack's shipped console renders the
htmlsink until objectstack bumps.objectui-shapast610d9efd5. That bump regenerates the trackedsdui.manifest.json, which still nameshtml;check-sdui-manifestforces the regenerate. The bump is objectstack's own card per the lane rule, not this card. Publishing@object-ui/*is the changesets release PR, a human action.Generated by Claude Code
feat(components,types)!: retire the raw-HTML html node type (objectui…