Repository navigation
spec(react): SchemaRenderer's 37 un-cast reads off its working copy of a node block objectui#8347's index-signature removal (the interpreter's copy is renderer state; authored keys get the per-key verdict) #11349
Description
Filing-gate category: ① — named landing points: the un-cast reads that objectui#8347's premise P1 enumerated, ordered by the objectui#7927 ruling's step 1. A derived child of in-flight objectui#8347, filed with the parent's domain:spec and priority:p2.
Reader: the domain:spec @ objectui seat (objectui#10217) dispatches it, and an os-dev executes it, serial with the properties-bag rollout (see Serial below).
Parent: objectui#8347. That card stays pm:blocked until this card and its siblings land.
Why this card exists
The objectui#7927 ruling (5565351677, director seat, decision batch #68; the maintainer's 「同意」) orders the removal:
- Step 1, verbatim: 「Census first, delete never before: every read that rides the passthrough is enumerated and either declared on its schema or retired」.
- Unit: objectui#8327's sequencing ruling
5587717012(Q2 = A) sets one card per reader package.
objectui#8347's P1 (report 5926022178):
@object-ui/reactcarries 37 errors in shipped source and 10 in tests onceBaseSchema's[key: string]: anyis deleted alone.- Control: 0 errors at the same base with the signature present.
- This is the largest source population of any package. It is also the one architectural slice: the report's Q2.
The population (origin/main 5262f7dd3; files and keys, no line addresses)
All 37 are in packages/react/src/SchemaRenderer.tsx. Each read is off the interpreter's spread copy of an incoming node, whose type tsc prints as an anonymous { type: string; id?: …; name?: …; … }: the declared BaseSchema members and nothing else.
| key | reads | kind |
|---|---|---|
properties |
9 | property read |
props |
6 | property read; the annotated legacy alias of properties (the maintainer's 2026-08-18 ruling, objectui#5123) |
params |
3 | index read (TS7053) |
title |
3 | index read over "label" | "title" | "value" | "description" |
content |
3 | property read |
visibility |
3 | property read |
_hidden |
3 | the interpreter's own marker |
_disabled |
2 | the interpreter's own marker |
schema, dataSource, objectFields, responsiveStyles |
1 each | property read |
| (dynamic key) | 1 | index read with a string key |
Tests: 10 errors in 6 files (content ×3, value ×2, ariaDescribedBy ×2, role, visibility, responsiveStyles).
Re-derive it: apply objectui#8347's P1 mutation in a worktree, rebuild @object-ui/types, and run this package's type-check programs un-chained. Capture the exit codes before any pipe.
Seat ruling on the report's Q2: premise-bearing, inside the maintainer's veto window
Governing text:
- AGENTS.md §5 #0.1, contract-first: no lenient fallback in the renderer.
- The objectui#7927 ruling, which refuses "Keep the signature and document that key validity is zod's job" and orders each read to be declared on its schema or retired.
These settle the option that would change the public contract shape: option (a), declaring these keys on BaseSchema, is refused. What remains is the type of an internal value, and each authored key's verdict follows the per-key rule below, so this does not escalate.
- Ruling.
- The interpreter's working copy is renderer state, not an authoring site. Type it with a working type internal to
@object-ui/reactand not exported. It has three parts:- the declared
BaseSchemamembers; - the interpreter's own markers (
_hidden,_disabled), declared explicitly, with a docblock saying the interpreter writes them; Record<string, unknown>for everything else, so every other read must narrow before use. That is stricter than today'sany.
- the declared
- Each authored key read off the copy gets the per-key verdict (option (c)):
properties,props,content,title,params,visibility,responsiveStyles,objectFieldsandschema.- The spec declares the key on that node type: read it through the declared type.
- The spec does not: retire the read, with the producer remedy per #0.1, or report a fork with the four-axis frame if a real producer authors it.
props: an existing ruling binds. The file's own docblock quotes the maintainer's 2026-08-18 ruling (objectui#5123): 「propertieswins on BOTH channels」, withpropskept as "the annotated legacy alias" ("the alias keeps working").- Type it on the working type as that annotated alias, and name the ruling in the PR.
- Retiring the alias is a new decision this card does not take. If the producer census says it should go, report that as a fork with the four-axis frame.
- The interpreter's working copy is renderer state, not an authoring site. Type it with a working type internal to
- Named, falsifiable premise. The working type stays internal: it is not exported from
@object-ui/react, and no published.d.tssignature names it.- Re-check: build
@object-ui/react, then grep itsdistdeclarations for the working type's name. Every hit must be a non-exported declaration, with no hit in an exported signature.
- Re-check: build
- Prohibition. If the premise fails, meaning the working type would leak into a public signature, report a fork and stop.
- ⛔ Do not fall back to declaring the keys on
BaseSchema. - ⛔ Do not keep
any. - ⛔ Do not leave the reads cast to
any.
- ⛔ Do not fall back to declaring the keys on
The maintainer can veto this in the seat's round report. Until then, it is the dispatch's Zone 1.
Acceptance (executable)
- With the signature present: the package's type-check and tests are green, and
mainstays green. - With objectui#8347's P1 mutation applied locally: this package's programs report 0 errors, except any ceiling-pin probe keys, which the PR body lists as objectui#8347's flips.
- The premise re-check reads as stated, with its output in the PR body.
- Each retired authored key has its producer census (catalog, examples, docs, server-shipped documents) in the PR body. The docs agree with each verdict (AGENTS.md Add automated testing infrastructure and CI/CD workflows #2).
Serial
properties/props: the properties-bag rollout (objectui#10859 batches, objectui#11276;domain:ui) is still moving arms into the bag. This card dispatches against the merged state, and itspropertiesverdict reads the bag asmainhas it.dataSourcethrough the renderer's prop spread is objectui#11070's (its direction names the runtime-only route). This card types the read and leaves the verdict there.
Out of scope ⛔
- The removal, the compile-fail pin, and AGENTS.md §5 #0.1: those are objectui#8347's.
ComponentRendererProps' own index signature (renderer props; objectui#8347 keeps it out of scope).- The zod strict face (objectui#8345, objectui#11070).
Dedupe
Grepped locally over every objectui issue updated since 2026-09-01: 2,209 issues, open and closed, with bodies, read 2026-10-01T06:35Z.
SchemaRendererwith_hidden|responsiveStyles|objectFields|index signature|undeclared: 23 hits. The closest are:- objectui#9308 (closed: the seam's
dataSourcecarries a second meaning); - objectui#8818 (closed: an authored
objectFieldsreaching the kanban predicate layer); - objectui#9131 (closed: the forwarded
disabledprop); - objectui#8410 (closed: the "read by no renderer" method finding);
- objectui#11276 (open: flat arms refuse the spec's
properties); - objectui#11070 (open: strict-face refusals).
None asks for the working copy's type.
- objectui#9308 (closed: the seam's
responsiveStyles: 7 hits, all on other questions (objectui#8236, finding(types):BaseSchema.testId's JSDoc promises "Rendered as data-testid attribute" — nothing renders it, and it reaches the DOM as a lowercasetestid#8268, Synthesizedasideregion carriesclassName, whichPageRegionSchemarejects — a Reference Rail page has no persistable spelling #4286, finding(react,plugin-kanban): an AUTHOREDobjectFieldsstill reaches the predicate layer on thekanban-uientry — batch #70 closed thekanbanarm, not the key #8818, finding(method): "declared but read by no renderer" is inferred from the absence of aschema.KEYread — unsound wherever the renderer FORWARDS its rest-spread onto the underlying primitive #8410, types: the flat armsflex/object-grid/object-chartrefuse the spec's page-componentpropertiesbag, so the showcase's 18 producer nodes fail both faces; does triage's answer A on objectui#10872 extend to them? #11276, and a seat post).
Dedupe words: BaseSchema index signature removal SchemaRenderer working copy · interpreter spread copy undeclared reads properties props legacy alias · _hidden _disabled interpreter markers type · react un-cast read Record unknown narrowing
Activity
objectstack-fleet commentedon Oct 1, 2026
Claim: PM loop round 2 (this card as filed: the react unit of objectui#8347's preparation, with the seat's premise-bearing ruling on Q2)
Session:
session_01VhxTqosz7wn54ahqyxgERTAccount:
os-litant(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-11349-schemarenderer-working-copyWorktree:
objectui-issue-11349Domain:
domain:specSeat:
domain:spec#1(objectui#10217)File surface:
packages/react/src/SchemaRenderer.tsx, for the interpreter's internal, non-exported working type and the 37 reads off it.packages/types/src, only where an authored key's verdict is a by-reference declaration on a node type's spec row.BaseSchema, ⛔ not the removal, ⛔ notComponentRendererProps. ⛔ NotdataSource's verdict (objectui#11070), and ⛔ not thepropsalias's retirement (the maintainer's 2026-08-18 ruling binds it).Stop on breach; explain in the report.
Container & model:
M,mode:subagent,model: opus(TIER_DEFAULT; no path-derived mandate forpackages/react/**)Clause-②: no
Why no: an internal working type is not a published surface. It is stricter than today's
anyand is not exported. Typing its reads moves no accept set. If a per-key verdict retires an authored read or declares a key, the arm moves, and the PR's line then states it (no (narrowing)oryes). The at-tier contract record is owed on the landing head either way, because the ruling's premise is a published-surface claim.Thread-read: none
Serial constraints cleared: read 2026-10-01T07:29Z at objectui
origin/main17dc16793, over all open PRs' file lists and every openpm:dispatchedclaim.packages/react, and onlyhooks/useNavigationOverlay.ts. That is disjoint.ObjectGanttSchema) editspackages/typesonly. This card reads the bag asmainhas it.objectstack-fleet commentedon Oct 1, 2026
os-dev-report
{ "issue": 11349, "status": "done", "branch": "claude/issue-11349-schemarenderer-working-copy", "pr": "https://github.com/objectstack-ai/objectui/pull/11374", "session": "session_01VhxTqosz7wn54ahqyxgERT (the dispatching seat's session; this is a subagent run, and the harness stamped no separate Claude-Session for it)", "premise_still_valid": true, "summary": "SchemaRenderer's evaluation-memo copy ({ ...schema }) is now typed InterpreterWorkingCopy, which is internal to SchemaRenderer.tsx and not exported. It has four parts: the members BaseSchema declares (DeclaredMembersOf, a key-remapping mapped type that strips the index signature); the interpreter markers _hidden / _disabled, declared as interpreter-written; the two authored keys a ruling names (props, the annotated legacy alias per the maintainer's 2026-08-18 ruling on objectui#5123, typed unknown; visibility, typed by reference to the spec's deprecated PageComponentSchema.visibility); and a string-keyed unknown record for everything else. Each authored key read off the copy got its per-key verdict: properties, params, content, the text keys and responsiveStyles are declared on spec rows and already narrowed by existing guards (isConfigBag, isParamsBag, typeof string, hasResponsiveStyles). objectFields and schema are host-supplied strips. dataSource's read is typed and its verdict is left with objectui#11070. No verdict retires or declares anything, so there is no behaviour change and the published .d.ts is byte-identical. Six test suites' authoring literals are typed as the nodes they are, through identity helpers, and one fixture authoring TextSchema's retired value is re-spelled content. With objectui#8347's P1 mutation applied locally, react's two type-check programs read 0 / 0 at the landing head (37 / 10 before).", "tests": "All heavy runs went through os-verify-lock, with exit codes captured before any pipe. (1) H1 re-derived twice. At fd6f5da44 and again at d0fba91aa (fast-forwarded base), the control read src 0 / test 0 and the P1 mutation read src 37 / test-program 47 (= the same 37 + 10 in 6 test files), matching the card key for key. The mutation was applied by objectstack scripts/ablation-replace.mjs in WRAP mode (anchor 1 to 0, blob ce2bc9e31dc6 to 7fa27a8f1580). Every restore was proven: blob == HEAD ce2bc9e31dc6, git diff HEAD empty, types dist back to 2 signature sites. (2) At e22274398 (landing head): signature present, REACT_SRC_TSC_EXIT=0 and REACT_TEST_TSC_EXIT=0 (0 / 0); P1 mutation, REACT_SRC_TSC_EXIT=0 and REACT_TEST_TSC_EXIT=0 (0 / 0). (3) Premise re-check: @object-ui/react built at d0fba91aa and at 376e8d43b; the sha256 of all 70 dist .d.ts files compared gives DTS_DIFF_EXIT=0 (byte-identical); the four type names (InterpreterWorkingCopy, DeclaredMembersOf, InterpreterMarkers, WorkingCopyAuthoredKeys) hit 0 dist files; control SchemaRendererProps hits 2. (4) Reverse validation at 376e8d43b, signature present: leg A adds the probe const __probe11349: number = newSchema.undeclaredProbeKey11349. Predicted RED; observed TSC_EXIT=2 with exactly 1 error, TS2322 Type 'unknown' is not assignable to type 'number'. Leg B is the same probe with DeclaredMembersOf of BaseSchema replaced by bare BaseSchema. Predicted GREEN; observed TSC_EXIT=0. Both legs were restored and proven (blob == HEAD 27b0fe283f31). (5) pnpm --filter @object-ui/react type-check (script echoed) exit 0. pnpm exec vitest run --maxWorkers=2 packages/react/ from the repo root at e22274398: Test Files 106 passed (106), Tests 1389 passed (1389), exit 0. (6) eslint . in packages/react (the package's own lint scope) at e22274398: 176 files, 0 errors, all 7 touched files in the population. The diff adds no any token outside comments and removes one typed any. (7) The check:* gates listed in gates, all exit 0, plus check-changeset-presence exit 0 (empty frontmatter) and check-governed-queue-guard --test: NOT GOVERNED.", "mcp_calls": "0", "api_writes": "3 — each one fleet-write relay dispatch (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): pr_create, POST /repos/objectstack-ai/objectui/pulls (objectui#11374, draft forced, body read back 16000/16000 bytes identical); assignees, objectui#11374 assignee os-litant via label-write.mjs (read back MATCHES, zero label writes); comment, POST /repos/objectstack-ai/objectui/issues/11349/comments (this os-dev-report) via post-stamped.mjs. Not REST: git push x3 (the empty-branch probe, 376e8d43b, e22274398).", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · visibility (node level) has zero producers in objectui fd6f5da44 and objectstack 99398542b (census in the PR body), and the spec normalizes it to visibleWhen at parse, so the renderer's leg serves raw metadata only; retiring that read is the spec's ADR-0089 deprecation, not this card · noted, not filed", "carrier: 承接者:无 · an authored _hidden / _disabled would arrive pre-set because the copy starts as { ...schema }, and nothing refuses one; zero producers; not measured through a public door · noted, not filed", "carrier: objectui#8347 · resolveAriaProps' flat ariaDescribedBy / role are not BaseSchema members; the spec declares them only inside the nested aria bag (AriaPropsSchema), and objectui#4580 Q2-B keeps the flat reader; now typed unknown and forwarded as authored; whether the authoring face declares them is the removal card's question · noted, not filed", "carrier: 承接者:无 · propsWithoutCanonicalKeys still returns a string-keyed any record for the outgoing alias bag; it is not a read of the working copy, so it is left as is · noted, not filed" ], "gates": [ { "gate": "pnpm --filter '@object-ui/react^...' build", "sha": "d0fba91aa", "exit": 0, "note": "the diff touches no dependency of react" }, { "gate": "react tsc --noEmit + tsc -p tsconfig.test.json, signature present", "sha": "e22274398", "exit": 0, "errors": "0 / 0" }, { "gate": "react tsc --noEmit + tsc -p tsconfig.test.json, objectui#8347 P1 mutation (never committed, restore proven)", "sha": "e22274398", "exit": 0, "errors": "0 / 0 (was 37 / 10)" }, { "gate": "pnpm --filter @object-ui/react type-check", "sha": "376e8d43b", "exit": 0, "note": "last code commit; e22274398 adds only .changeset" }, { "gate": "premise re-check: react dist .d.ts sha256, base vs after", "sha": "376e8d43b vs d0fba91aa", "exit": 0, "verdict": "70/70 byte-identical; working-type names 0 dist hits; control 2" }, { "gate": "reverse validation leg A (probe, expect RED)", "sha": "376e8d43b", "exit": 2, "verdict": "1 x TS2322 unknown to number, as predicted" }, { "gate": "ablation leg B (probe + DeclaredMembersOf removed, expect GREEN)", "sha": "376e8d43b", "exit": 0, "verdict": "as predicted" }, { "gate": "pnpm exec vitest run --maxWorkers=2 packages/react/", "sha": "e22274398", "exit": 0, "verdict": "106 files / 1389 tests passed" }, { "gate": "eslint . (packages/react)", "sha": "e22274398", "exit": 0, "verdict": "176 files, 0 errors" }, { "gate": "check:new-line-citations", "sha": "e22274398", "exit": 0, "verdict": "0 new" }, { "gate": "check:control-bytes", "sha": "e22274398", "exit": 0 }, { "gate": "check:changeset-claims", "sha": "e22274398", "exit": 0, "verdict": "4 pending paragraphs name SchemaRenderer.tsx; each re-read, none made false" }, { "gate": "check:pending-changeset-literals", "sha": "e22274398", "exit": 0 }, { "gate": "check:spec-symbols", "sha": "e22274398", "exit": 0 }, { "gate": "check:handler-key-reads", "sha": "e22274398", "exit": 0 }, { "gate": "check:test-path-roots", "sha": "e22274398", "exit": 0 }, { "gate": "check:vi-mock-specifiers / -inherit / -override-shape", "sha": "e22274398", "exit": 0 }, { "gate": "check:phantom-deps", "sha": "e22274398", "exit": 0 }, { "gate": "check:published-dist", "sha": "e22274398", "exit": 0, "note": "ran a repo-wide turbo build (478s) outside the lock; see deviations" }, { "gate": "check:readme-exports", "sha": "e22274398", "exit": 0 }, { "gate": "node scripts/check-changeset-presence.mjs", "sha": "e22274398", "exit": 0, "verdict": "1 changeset, empty frontmatter" }, { "gate": "node scripts/check-governed-queue-guard.mjs --test (8 paths)", "sha": "e22274398", "exit": 0, "verdict": "NOT GOVERNED" }, { "gate": "CI", "sha": "e22274398", "verdict": "in_progress (not awaited, per the contract)" } ], "line_budget": "n/a — no skills/** or governed ledger touched; diff d0fba91aa..e22274398: 8 files, +235 / -33", "files_changed": [ "packages/react/src/SchemaRenderer.tsx", "packages/react/src/__tests__/SchemaNode.reconciliation.test.ts", "packages/react/src/__tests__/SchemaRenderer.aria.test.tsx", "packages/react/src/__tests__/SchemaRenderer.errorBoundary.test.tsx", "packages/react/src/__tests__/SchemaRenderer.expressions.test.tsx", "packages/react/src/__tests__/SchemaRenderer.scopedStyleSchemaIdentity.test.tsx", "packages/react/src/__tests__/SchemaRenderer.unevaluatedExpressionDiagnostic.test.tsx", ".changeset/11349-schemarenderer-working-copy.md" ], "deviations": [ "Fast-forwarded the branch to origin/main d0fba91aa before any edit (no merge commit; I had no commits yet). SchemaRenderer.tsx had not changed upstream, but packages/react (a new hook test), packages/types and the lockfile had, and the acceptance is 0 under the mutation at the landing head. Base and mutation were re-derived there.", "resolveAriaProps' parameter was retyped from a string-keyed any record to the working type, which is outside the census's 37: it received the whole copy through any, which the ruling's 'never keep any' covers. Runtime bytes are unchanged; the ARIA record is now typed unknown.", "check:published-dist was run outside os-verify-lock and performed a repo-wide turbo build of 45 packages (478s). It ran past the foreground cap into the background; I waited on its PID in the foreground until it exited 0. It is a repo-wide run that belonged to CI.", "The type-check script, the premise re-check and the proof legs ran at 376e8d43b. The landing head e22274398 adds only the .changeset file. The signature and mutation legs were re-run at e22274398.", "The producer census reads objectui at fd6f5da44 (the dispatch base, pre-fast-forward) and objectstack at 99398542b: a dated reading.", "Confirmed the coordinator's update 1: the test half was 10 literals carrying 11 undeclared keys. The 'all ARIA props together' literal hid role behind the reported ariaDescribedBy; the final mutation leg reads 0." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
ACCEPT: report
5929366107, PR #11374 ate22274398, ready for the at-tier contract review. From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01VhxTqosz7wn54ahqyxgERT, 2026-10-01T10:19Z.The seat's Q2 ruling is executed as written.
InterpreterWorkingCopy, internal toSchemaRenderer.tsx. It is made of:BaseSchemamembers, through a key-remapping type that drops the index signature;_hidden/_disabledmarkers;propsas the annotated alias, typedunknown, under the maintainer's 2026-08-18 ruling;visibility, by reference to the spec's deprecated row;Record<string, unknown>for everything else..d.tsfiles are byte-identical, and the four working-type names have 0 hits indist.BaseSchema, and noanyis kept. One typedanyis removed:resolveAriaProps' parameter.The per-key verdicts move nothing.
properties/params/content/ the text keys /responsiveStylesare declared on spec rows and already narrowed by existing guards.objectFields/schemaare host-supplied strips.dataSourceis typed, and its verdict stays with objectui#11070.No behaviour changes. The empty-frontmatter changeset is the no-release form.
The measurement. With objectui#8347's P1 mutation applied, react reads 0 / 0, down from 37 / 10. The reverse leg holds both ways: a planted undeclared read is red with the working type and green without it. The restore was proven every time. Nothing here waits on objectui#11364.
Carried, not carded:
visibilityhas zero producers, and its retirement is the spec's ADR-0089 deprecation;_hidden/_disabledwould arrive pre-set (zero producers);propsWithoutCanonicalKeys' outgoinganyrecord.The flat
ariaDescribedBy/role(objectui#4580 Q2-B) go to objectui#8347's last PR as an authoring-face question.objectstack-fleet commentedon Oct 1, 2026
Landed: PR objectui#11374 merged through the queue as
b5b0afea5(Fixes #11349). From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01VhxTqosz7wn54ahqyxgERT, 2026-10-01T10:49Z.Verification
git patch-id --stableequals the PR's net diff (af5d5116cc4con both sides).5929588500(PASS) namese22274398, and nothing was pushed after it.Delivered. The react unit of objectui#8347's preparation, under the seat's premise-bearing Q2 ruling, with no veto received.
SchemaRenderer's working copy of a node is typedInterpreterWorkingCopy, internal and not exported. It is made of:BaseSchemamembers;_hidden/_disabledmarkers;propsas the annotated alias (the maintainer's 2026-08-18 ruling);visibility, by reference to the spec's row;unknownfor the rest.On objectui#8347: this preparation card is discharged. Three
Blocked-by:cards remain: #11348, #11355 and #11364. #11364, the publishedschemaprop type and the public-block node types, can start now thatSchemaRenderer.tsxis free.The leftover
pm:dispatchedis stripped in the same act.fix(plugin-form): type the five form variants' child form nodes as Fo…
refactor(react): type SchemaRenderer's working copy as internal rende…