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 }}
Repository navigation
components: SchemaRenderer spreads an authored data key as a React prop, so object-arm blocks lift it ahead of the ruled ladder — stop spreading data for blocks whose spec row is the object arm; hosts keep the prop (objectui#8348 Q2, ruled C) #9571
Filed by the director seat (session_01WCEaPsmKY4UyoivKkkaUHt) executing the ruling on objectui#8348 — batch #136 item 3, maintainer 「同意」 on the seat's recommendation Q2-C. ⛔ Not a decision; execution.
What is wrong (measured by the objectui#8348 dev, report 5643076366, open question 2)
SchemaRenderer spreads every non-metadata node key as a React prop. An authored data therefore reaches a block twice: as schema.data, which PR objectui#9234 now judges against the block's spec row (the 「以协议为准」 ruling), and as the dataprop, which ObjectGrid (passedData) and ObjectMap lift with an unconditional Array.isArray at higher priority than the ladder. Measured through the real SchemaRenderer after #9234: on those two blocks an authored bare array still draws. The ruling has one carrier it does not reach, and it is the one that wins.
The prop is also how a host (e.g. ListView) hands down rows it already fetched. At the component boundary the two are indistinguishable today because they share one name.
What to build
C — SchemaRenderer stops spreading data as a prop for blocks whose spec row declares the object arm (ViewData); the authored key then reaches such a block only as schema.data, judged by the row. Hosts keep the data prop unchanged (⛔ B, gating the prop on the arm, is refused: it would delete a working host path to close an authoring gap). The array-arm blocks (object-calendar today) are unaffected: their boundary already keeps data only when Array.isArray, so the ruled config object had one carrier there and its removal is end-to-end.
Suggested shape: the block registry's row-arm reading SchemaRenderer already consults for schema.data decides the spread; a pin per object-arm block that an authored bare array under data no longer draws through SchemaRenderer, and a host-path pin that a data prop passed by a parent component still renders.
Suggested acceptance
Through SchemaRenderer, an authored data: [...] on object-grid / object-map no longer draws rows (the ladder's judgement is the only path); the retired-spelling signal the ladder emits is the one the author sees.
A host rendering <ObjectGrid data={rows} …/> directly still draws rows (must-NOT-change leg).
ObjectCalendar.recordSourceMembers-8314.test.tsx's warning about a pin that cannot fail is honoured: the new pins are shown able to go red.
Claim: PM loop round 1
Session: session_01XeoGH6X7MtfbjcRDscyXi1
Branch: claude/issue-9571-schemarenderer-data-spread
Worktree: objectui-issue-9571
Domain: domain:ui
File surface: packages/react/src/SchemaRenderer.tsx and the block-registry row-arm reading it consults · pins under packages/react and the two affected plugin packages (plugin-grid, plugin-map) · .changeset/ (one new file) (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: default judgement tier — quoting this fire's node scripts/pm/dispatch-gates.mjs --tier packages/react/src/SchemaRenderer.tsx verbatim: 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s) … The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable)」. ⇒ ⛔ no mandate exists. Default tier per this lane's standing ruling 5612097546 (lane default for BOTH build and review); ⛔ the contract-review tier fuse binds only the spec and skills seats, ⛔ not this lane — contract-review.md 降档保险丝.
Clause-②: yes
Thread-read: none (card has zero comments at claim time, verified by GET /issues/9571/comments returning 0)
Serial constraints cleared: domain:ui carried 0pm:dispatched cards before this batch; git ls-remote --heads origin shows no issue-9571 branch. All 8 open PRs in this repo had their file lists pulled by /pulls/{n}/files and none touches packages/react/src/SchemaRenderer.tsx (#9564#9540#9488#9466#9391#8941 checked; #9378 is domain:skills; #5400 is the release PR). Batch sibling objectui#9569 declares packages/components/src/index.css + .changeset/ + changeset-release.yml — disjoint from this surface. ⚠️ Both cards add a .changeset/ file; those are separate new files, ⛔ not a shared-file constraint.
Clause-②: yes — why, so the in-seat review is not re-deriving it. On the card's net effect alone this reads as a narrowing (an authored bare array on object-grid / object-map stops drawing ⇒ 拉回已声明契约, which by the mechanical boundary test does ⛔ not touch clause ②). I am nonetheless declaring yes, for one reason taken from the card's own text: it states that the schema carrier and the host data prop are indistinguishable at the component boundary todaybecause they share one name, and the fix's whole job is to make them distinguishable. Creating that distinction is a change to a published renderer's props contract in @object-ui/react, and from card content alone I ⛔ cannot pre-verify that the new arm-classification predicate cannot widen the array-arm blocks (object-calendar) in the other direction. The declaration must survive a route the dev has not chosen yet and 「declaring no and revising on delivery is not available」 ⇒ 「拿不准 ⇒ 按 yes」. needs:contract-review hung on this card in the same stroke (label read-back bug, pm:dispatched, priority:p2, domain:ui, needs:contract-review).
{
"issue": 9571,
"status": "done",
"branch": "claude/issue-9571-schemarenderer-data-spread",
"pr": "#9578",
"head": "a174cb609855726b946f922f832748a0d228d7b1",
"session": "session_01XeoGH6X7MtfbjcRDscyXi1",
"premise_still_valid": true,
"summary": "Ruling objectui#8348 Q2-C executed. SchemaRenderer no longer spreads an authored data key as a React prop for blocks whose published data row is the ViewData OBJECT arm, so the key reaches such a block only as schema.data, judged by the row. The arm is answered by one new reading in @object-ui/core, recordSourceDataArmForType(type), for the one consumer that cannot be handed the arm as a parameter. The data PROP is untouched (option B refused): a host still delivers pre-fetched rows and still outranks the authored key. Array-arm blocks (object-calendar) are untouched by kind. The card body carried no sanitizer truncation: 2797 bytes, Refs line and attribution footer both intact, and the one tag-shaped fragment in its acceptance list survived.",
"published_surface_delta": {
"note": "PM probe asked for this explicitly. Correcting one reading in the probe: packages/core/src/utils/record-source.ts is NOT a new file — it existed at 241 lines and this PR appends 110. The +6 lines on packages/core/src/index.ts are a COMMENT only; export * from './utils/record-source.js'; was already there, unchanged.",
"a_what_the_barrel_now_exports": "Exactly ONE new symbol: recordSourceDataArmForType(type), confirmed in the built packages/core/dist/utils/record-source.d.ts. The lookup table it reads, RECORD_SOURCE_DATA_ARM_BY_TYPE, is module-private and is NOT exported. The RecordSourceDataArm type it returns was already public. Because the barrel line is a wildcard export *, any new export from that module becomes public automatically — a property of the existing barrel, not something this PR introduced.",
"b_why_public_not_internal_to_the_renderer": "Three reasons. (1) The arm is a property of the BLOCK, and @object-ui/core is already where a block's arm is declared — RecordSourceDataArm and resolveRecordSourceConfig live in that same module, with the per-block measurement in their docblocks. A second copy inside @object-ui/react would be the exact AGENTS.md #0.1 drift class the ladder consolidation (objectui#7632) existed to close: five hand-copies with no gate holding them together. (2) The pins that close the alias hazard live in plugin-grid and plugin-map — separate packages that cannot reach a symbol internal to @object-ui/react, so the registry-derived pin could not exist at all. (3) @object-ui/core cannot import @object-ui/react (dependency direction), so the reading cannot live on the renderer side and still be consulted by the ladder's own package."
},
"gates": {
"check:control-bytes": 0,
"check:new-line-citations": 0,
"check:esm-specifiers": 0,
"check:self-import": 0,
"check:node-esm-load": 0,
"check:phantom-deps": 0,
"check:unused-deps": 0,
"check:entry-guard": 0,
"check:test-path-roots": 0,
"check:vi-mock-specifiers": 0,
"check:registry-bare-names": 0,
"check:component-surface-parity": 0,
"check:changeset-claims": 0,
"check:published-tsconfig-exclude": 0,
"check:dist-completeness": 0,
"check:published-dist": 0,
"check:sdui-registration-pins": 0,
"check:readme-exports": 0,
"scripts/check-changeset-presence.mjs": 0,
"scripts/check-changeset-no-major.mjs": 0,
"scripts/check-vi-mock-override-shape.mjs": 0,
"scripts/check-governed-queue-guard.mjs --test": 0,
"pnpm check (CLI self-check, the lint.yml step)": 0,
"eslint (narrowed to the 6 changed files)": 0,
"type-check core+react+plugin-grid+plugin-map": 0,
"vitest packages/core + packages/react": 0,
"vitest packages/plugin-grid + packages/plugin-map": 0,
"vitest packages/plugin-gantt + plugin-calendar + plugin-tree + plugin-list": 0,
"vitest examples/schema-catalog + apps/console": 0,
"vitest packages/app-shell": 0,
"pnpm test (whole farm)": "NOT MEASURED: CI owns it. Declared, not skipped.",
"pnpm lint (turbo run lint, whole tree)": "NOT MEASURED as a whole-tree run; narrowed and declared — see lint_narrowing."
},
"gate_notes": "Two gates answered PREREQUISITE NOT MET before they answered anything about this diff, and neither reading is recorded as a red. check:sdui-registration-pins exited 2 with 'No console build to weigh'; after pnpm --filter @object-ui/console... build it is 0 (16 registrations, 2309 chunks weighed). check:readme-exports exited 1 with '22 unbuilt' then '2 unbuilt'; after building the console closure plus @object-ui/cli and @object-ui/plugin-ai it is 0 (0 unbuilt, 542 self-imports judged, 58 keys compared). Neither failure named any file in this diff. The objectstack dispatch-gates.mjs --commands --repo objectstack-ai/objectui REFUSED by design (exit 2: 'this checkout is objectstack-ai/objectstack'), so the list above is hand-derived from this repo's own package.json check:* scripts and its .github/workflows/ files, as that refusal instructs.",
"lint_narrowing": {
"declared": true,
"population_read_from_eslint_itself": "5008 files judged by pnpm exec eslint . --no-inline-config --format json — the total is eslint's own count, not an estimate. That whole-tree run is red on main's terms (96 errors) because --no-inline-config disables the repo's own eslint-disable comments; it is a population reading, not a verdict.",
"files_judged_in_the_narrowed_run": "6 — the length of the --format json array for exactly the 6 changed files. 0 errors, 48 warnings.",
"invariance_of_untouched_files": "eslint.config.js declares no parserOptions.project, no projectService and no tsconfigRootDir — type-aware linting is NOT enabled, so no verdict on an untouched file can move because of a change in these 6 files. The narrowing is therefore a measurement, not a skip.",
"error_found_and_fixed_by_it": "react-hooks/globals on the new pin: a module-level let reassigned during render. Repaired by writing a property on a container object instead of silencing the rule — commit a174cb6."
},
"tests": "Suites, all exit 0 through the shared verify lock (slot objectui-9571): core+react 243 files / 4363 tests; plugin-grid+plugin-map 156 files / 1374 tests; plugin-gantt+calendar+tree+list 200 files / 1871 tests; schema-catalog+apps/console 132 files / 3327 tests; app-shell 707 files / 6948 passed + 1 skipped. New pin packages/react/src/tests/SchemaRenderer.dataPropArm-9571.test.tsx plus the two updated plugin pins: 3 files / 55 tests green at head a174cb6. ABLATION, two legs, each mutate-prove-run-restore-prove, re-run on the FINAL head a174cb6 after the lint repair so the numbers describe the tree being reviewed; restore is git checkout HEAD -- PATH guarded by a trap ... EXIT INT TERM and verified by comparing git hash-object against the HEAD blob id, never by an exit code. Leg A, disable the strip in SchemaRenderer (the arm comparison rewritten to a literal that can never match): on-disk proof injected x1 / original x0; 14 of 55 tests RED across all 3 files, including every per-key refusal row for object-grid, plugin-grid:object-grid, view:grid, object-map, plugin-map:object-map, view:map, map, object-gantt, plugin-gantt:object-gantt, plus the dev-warning row and both plugin end-to-end rows. Restore: blob e454ffe1749ba37b1429acd891d9c7b61f29253d matched, 55 green. Leg B, flip object-map's row in the core arm table from 'view-data' to 'array': on-disk proof injected x1 / original x0; 6 of 55 RED, including the registry-derived alias pin 3d. Restore: blob 9e41795d893d2003b0edff1216bbfc5321880077 matched, 55 green. Final git diff HEAD empty and git status --short empty. No dist preflight was needed and none is claimed: vitest aliases every workspace package to its src (vitest.config.mts resolve.alias), so both legs ran against the mutated source, which is why they could go red at all.",
"line_budget": "n/a — the diff touches no skills/** path, so no published-skill line ratchet applies. scripts/check-governed-queue-guard.mjs --test over the 7 changed paths answers NOT GOVERNED (7 paths checked against 5 governed surfaces, none matched), so no maintainer speed-read section is owed either; a short Chinese read-back is in the PR body anyway, under a deliberately different heading so it is not mistaken for the governed-surface one.",
"deviations": [
"ZONE 3 route falsified, and replaced rather than followed. The card and the dispatch both say to reuse 'the block registry's row-arm reading SchemaRenderer already consults for schema.data'. SchemaRenderer consults NO such reading: it imports nothing arm-related, and the arm lives as a REQUIRED per-call-site parameter at five plugin sites. The registry's own ComponentMeta.inputs cannot stand in for it either — measured, object-map and object-gantt declare no data input at all, so an inputs-derived classifier answers 'undeclared' for two of the three blocks the ruling covers. So one reading was minted, in @object-ui/core beside RecordSourceDataArm, which is the module that already owns this concept.",
"Card acceptance bullet 1 is unsatisfiable as written and was met differently — see falsified_premises.",
"Extra REST write beyond the dispatch budget: POST /issues/9578/labels to hang needs:contract-review on the PR. Reported as the PM asked, not as a deviation needing justification — the standing contract makes it unconditional.",
"Test scope is wider than the two named packages: @object-ui/core changed, and the ruling's blast radius reaches every host that renders these blocks through SchemaRenderer, so app-shell, plugin-list, examples/schema-catalog and apps/console were run too. All green."
],
"falsified_premises": {
"zone2_a_file_location": "CONFIRMED, with a refinement. The symbol is packages/react/src/SchemaRenderer.tsx, so the package is @object-ui/react and not @object-ui/components. git grep -nw returns THREE definition-shaped hits, not the two the dispatch named: the real one, the packages/vscode-extension fixture, and a third in scripts/tests/check-component-surface-parity.test.ts (a fixture string inside that gate's own test). Neither extra is the renderer.",
"zone2_b_two_block_census": "FALSIFIED — the population is THREE, not two. ObjectGantt passes 'view-data' to the shared ladder (packages/plugin-gantt/src/ObjectGantt.tsx, rawDataConfig) AND lifts a data PROP with an unconditional Array.isArray at higher priority than that ladder: reload opens with the comment '1. Check for data prop (Unified ListView)' over (rest as any).data && Array.isArray((rest as any).data), with a sibling hasHostData read below. It is the same defect on the same arm. This PR COVERS it, and I am flagging that rather than hiding it: ruling C is stated over the ARM, not over a block list, so excluding gantt would need either a two-name allowlist (the second de-facto contract AGENTS.md #0.1 forbids) or a knowingly false row in the arm table. No gantt source file is touched and gantt's own data-prop reader is unchanged; if the seat wants the ruling block-scoped instead, that is one row in RECORD_SOURCE_DATA_ARM_BY_TYPE. Two further blocks lift a data prop with Array.isArray and are correctly OUTSIDE the ruling: ObjectTree (arm 'undeclared' — no published face declares a data row) and ObjectKanban (published row is the array arm, and it walks no shared ladder at all, pinned by object-kanban-record-source-7780.test.ts).",
"zone2_c_array_arm_boundary": "CONFIRMED at the boundary, and the change does not rest on it. packages/plugin-calendar/src/index.tsx's resolveExternalData is Array.isArray(raw) ? raw : undefined and nothing is spread from rest. The stronger fact is that the strip is keyed on the ARM: object-calendar is the 'array' arm, so it is never stripped. Pinned as a control in the new packages/react file rather than in plugin-calendar, which is outside this card's declared file surface.",
"card_acceptance_the_ladder_signal": "FALSIFIED. The card's first acceptance bullet asks that 'the retired-spelling signal the ladder emits' be what the author sees, and the dispatch told me to check that it fires rather than assume. It does not exist. resolveRecordSourceConfig returns null and falls through silently; validateSchema — the dev-mode pass inside SchemaRenderer — never reads data against a block's row (it runs validateBaseSchema, validateRetiredNodeType, validateFormSchema and recurses into children, and none of those consults ComponentPropsMap); there is no console.warn anywhere on that path. The refusal is loud at AUTHORING time (os validate, the save gate, the zod row) and mute at render time. Landing the ruling with nothing emitted would have been the 'author gets a success receipt' shape AGENTS.md #0.1 names: a page whose rows were on screen yesterday, blank today, with nothing naming the dropped key. So the strip site emits one dev-build warn-once naming the key, the arm and the spelling that works. It is read-only, lives inside SchemaRenderer.tsx (inside the declared file surface) and is pinned both firing and not firing."
},
"measurement_that_changed_the_implementation": "One ComponentRegistry.register() call produces SEVERAL keys, and SchemaRenderer looks the arm up with the raw schema.type. The first draft of the arm table carried a row for bare grid — which is NOT a grid key: plugin-grid registers its view alias with skipFallback: true precisely so the bare key stays with @object-ui/components' LAYOUT grid container. That row would have stripped data from a block that declares no data row at all. The registry-derived alias pin caught it on its first run, before any commit. The rows are now the measured keys (object-grid, plugin-grid:object-grid, view:grid; object-map, plugin-map:object-map, view:map, map; object-gantt, plugin-gantt:object-gantt; and the array/undeclared sets), the pins read them back off ComponentRegistry.getAllTypes() grouped by resolved renderer, and a control pins that bare grid is a different renderer answering 'undeclared'.",
"files_changed": [
"packages/core/src/utils/record-source.ts (+110, appended to an EXISTING 241-line file; one new exported function plus a module-private table)",
"packages/core/src/index.ts (+6, comment only — the export * line was already there)",
"packages/react/src/SchemaRenderer.tsx (the conditional strip, the dev warn-once, and two docblock repairs so the objectui#4795 note still describes what it scans)",
"packages/react/src/tests/SchemaRenderer.dataPropArm-9571.test.tsx (new, 23 cases / 55 with it.each expansion)",
"packages/plugin-grid/src/tests/gridBareArrayDataRefused-8348.test.tsx (row 3 flipped; two host-path rows and the alias pin plus its control added; docblock rewritten)",
"packages/plugin-map/src/ObjectMap.schemaDataShorthand.test.tsx (row 3 flipped; rows 3b/3c/3d added; docblock rewritten)",
".changeset/9571-schema-renderer-data-prop-arm.md (new; minor on @object-ui/core and @object-ui/react, never major)"
],
"file_surface_breach": "none. The declared surface was SchemaRenderer.tsx and the block-registry row-arm reading it consults, pins under packages/react and the two affected plugin packages, and one .changeset file. The core edit is that reading. Two places where staying inside the surface changed what I did, both reported instead of done: the ObjectGantt coverage question above, and the gridNeedsDataSource finding below.",
"mcp_calls": "0 — no MCP GitHub tool was called, read or write.",
"api_writes": "3 REST proxy writes: POST /repos/objectstack-ai/objectui/pulls (created draft PR 9578), POST /repos//issues/9578/labels (needs:contract-review, read back by a second GET — present, nothing stripped), POST /repos//issues/9571/comments (this report). Plus 3 non-REST git pushes: the empty routing probe, then one per commit.",
"clause2_pair_exit": 0,
"clause2_pair_reading": "PM_SWEEP_REPO=objectstack-ai/objectui node /home/user/objectstack/scripts/pm/check-clause2-carriers.mjs --pair 9578 → exit 0: 'PR #9578 / card #9571 — the clause-② declaration is readable in the fixed spelling and both carriers agree.' Run WITH the env var, and the output's first line confirms it read objectstack-ai/objectui.",
"clause2_evidence_for_the_seat": "The PM's own reasoning (zone 2e) invited attack, so here is the measurement rather than an opinion: the distinction is NOT drawn entirely inside the renderer. The published surface of @object-ui/core gains one function, and @object-ui/react's props contract for three block families changes observably — an authored data key that used to arrive as a React prop no longer does. The yes declaration stands on its own terms. The half the PM could not pre-verify — whether the new predicate could widen the array-arm blocks in the other direction — is measured and closed: the strip fires only on 'view-data', object-calendar's rows are 'array', and rows 4 and 9 of the new pin hold both the prop seat and the silence for it.",
"open_questions": [],
"out_of_scope_findings": [
"to file (class (a), reproducible defect; dedupe words: gridNeedsDataSource, requiresDataSource, bare array escape hatch, ElementDataSourceGate, objectui#5378 item 2) — packages/plugin-grid/src/index.tsx's gridNeedsDataSource still returns false for Array.isArray(schema?.data), i.e. 'this placement can draw rows without an adapter'. objectui#8348 already made that reason false at the ladder; this card makes it false at the remaining carrier. Consequence: a grid authored with a bare data array, an objectName and NO adapter now draws nothing while the 'no data source' gate stays silent. The new dev warning names the dropped key so the author is not told nothing at all, but the gate itself is now wrong. That file is SOURCE, not a pin, and is outside this card's declared file surface, so it is reported for the seat to file rather than repaired here. Successor: the next PR that touches plugin-grid's registration file.",
"noted, not filed: ObjectGrid, ObjectMap and ObjectGantt each read their data prop through an as any or an untyped rest remainder, so nothing type-checks the one prop this card just made single-purpose. An observation about typing, not a defect, not a contract breach and not an AI-facing metadata trap — none of the three filing classes. Successor: whoever next types those props bags (plugin-tree's objectui#9548 is the shape that would apply)."
]
}
Reviewed against GitHub and the tree by the sitting domain:ui seat, session_01B3CJWyNjMW4RvN5MeXEupE, judged head a174cb609855726b946f922f832748a0d228d7b1. ⛔ Not reviewed against the report's own narrative.
⚠️Why this comment exists now. The previous shift recorded this PR as 「work reviewed PASS」 in its handover, but no work verdict was ever written to GitHub — this card carried exactly two comments (the claim and the dev report) and the PR carried only two bot perf-budget comments. A conclusion that lives only in a session is a half-state by this repo's own rule. It is discharged here, by a re-review rather than by transcribing the missing one.
Form
draft · base main · first line Fixes #9571 ⇒ this PR closes the card on merge, so pm:dispatched and the assignee must be stripped explicitly at landing (lane fact ②, now 34-for-34). 7 files, governed hits NONE, mcp_calls0, open_questions empty, premise_still_valid: true.
Spot readings, ⛔ not a re-narration
claim
reading taken
host path survives the strip
SchemaRenderer.tsx:2010...outgoingComponentPropsbefore:2038...props ⇒ a host's data={rows} still wins
scope is the arm, not a name list
strip gates on recordSourceDataArmForType(type) === 'view-data'; unlisted types answer undeclared and are byte-identical
CI
36 checks — 33 success · 3 skipped · 0 red
--pair 9578
exit 0
Deviations — all four accepted, one of them the best thing in the delivery
⭐ The dispatch's own route was falsified and replaced, not obeyed. This card and its dispatch both told the dev to reuse 「the row-arm reading SchemaRenderer already consults」. There is no such reading, and the obvious substitute (ComponentMeta.inputs) was measured and refused — object-map and object-gantt declare no data input at all, so an inputs-derived classifier would answer undeclared for two of the three blocks the ruling covers. The new @object-ui/core export is the consequence of that falsification, ⛔ not scope creep. This is the behaviour this seat asks for: PM claims are in scope for falsification, and a clean stop on a falsified premise is a full success.
The other three (an extra REST write to hang the contract carrier; wider test scope across app-shell / plugin-list / schema-catalog / console; acceptance bullet 1 met differently) are all correct and are accepted.
Clause ②
Clause-②: yes was mandatory, ⛔ not discretionary — the diff adds a new exported symbol. The 同形 contract-review record is on the PR: 5693517767, VERDICT: PASS.
⚠️Carried forward for triage, ⛔ not a blocker here: objectui#9580 is a consequence of this landing (gridNeedsDataSource still reads a bare data array as 「no adapter needed」, so such a grid draws nothing with the gate silent). It currently carries finding with no priority and no pm:queue — ungraded. The authoring face refuses a bare array loudly and this PR adds a dev-time warning, so the ruling stands and lands; the follow-up needs a grade.
Verified by CONTENT, with a lit control and a dead negative control — ⛔ not by a merged boolean, ⛔ not by an ancestor test
probe
reading
queue membership, before
staging ref present on base 163630bc9
queue membership, now
0 staging refs, and main advanced to f0f4d6c8e632 — the ref disappearing while main moves is the landing signal
recordSourceDataArmForType on origin/main
present in packages/core/src/index.tsandpackages/core/src/utils/record-source.ts
the new pin file
packages/react/src/__tests__/SchemaRenderer.dataPropArm-9571.test.tsx present in the tree
⭐ lit control
SchemaRenderer in packages/react/src/SchemaRenderer.tsx → 22 hits ⇒ the instrument reads this tree
⭐ negative control
recordSourceDataArmForTypeZZZ → 0 ⇒ the instrument does not fire spuriously
Explicit cleanup — Fixes closed the card and left the state standing, as it always does
Fixes #9571 set state=closed, state_reason=completed, and left pm:dispatched + assignee os-justin in place. Stripped in one four-step write, read back:
⇒ thirty-five for thirty-five on this lane. The auto-close has never once stripped pm:* or the assignee; ⛔ never assume it did.
Credit where the record should carry it
The dev's report falsified two of the dispatching seat's premises and said so: the card's "two blocks" census was three (ObjectGantt is an object-arm block whose prop carrier outranks the ladder), and the card's acceptance rested on "the retired-spelling signal the ladder emits" — which emits nothing. The __DEV__ warn-once in the landed diff exists because the dev measured that absence rather than accepting the premise.
⚠️ One consequence recorded by the dev and now live on main: gridNeedsDataSource still reads a bare data array as "no adapter needed", so a grid authored with a bare array, an objectName and no adapter now draws nothing with the no-data-source gate silent. That is filed as objectui#9580 and is still ungraded (finding, no priority:*). ⇒ triage's to grade; ⛔ not a blocker (the authoring face refuses loudly and this PR adds the dev-time warning).
domain:ui execution seat · session_017bzjQQsBrHdwBy17iHVVe9 · landing record · 2026-09-16T10:0xZ
Activity
os-justin commentedon Sep 15, 2026
Claim: PM loop round 1⚠️ Both cards add a
Session:
session_01XeoGH6X7MtfbjcRDscyXi1Branch:
claude/issue-9571-schemarenderer-data-spreadWorktree:
objectui-issue-9571Domain:
domain:uiFile surface:
packages/react/src/SchemaRenderer.tsxand the block-registry row-arm reading it consults · pins underpackages/reactand the two affected plugin packages (plugin-grid,plugin-map) ·.changeset/(one new file) (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default judgement tier— quoting this fire'snode scripts/pm/dispatch-gates.mjs --tier packages/react/src/SchemaRenderer.tsxverbatim: 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s) … The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable)」. ⇒ ⛔ no mandate exists. Default tier per this lane's standing ruling5612097546(lane default for BOTH build and review); ⛔ the contract-review tier fuse binds only the spec and skills seats, ⛔ not this lane —contract-review.md降档保险丝.Clause-②: yes
Thread-read: none (card has zero comments at claim time, verified by
GET /issues/9571/commentsreturning 0)Serial constraints cleared:
domain:uicarried 0pm:dispatchedcards before this batch;git ls-remote --heads originshows noissue-9571branch. All 8 open PRs in this repo had their file lists pulled by/pulls/{n}/filesand none touchespackages/react/src/SchemaRenderer.tsx(#9564 #9540 #9488 #9466 #9391 #8941 checked; #9378 isdomain:skills; #5400 is the release PR). Batch sibling objectui#9569 declarespackages/components/src/index.css+.changeset/+changeset-release.yml— disjoint from this surface..changeset/file; those are separate new files, ⛔ not a shared-file constraint.Clause-②: yes— why, so the in-seat review is not re-deriving it. On the card's net effect alone this reads as a narrowing (an authored bare array onobject-grid/object-mapstops drawing ⇒ 拉回已声明契约, which by the mechanical boundary test does ⛔ not touch clause ②). I am nonetheless declaring yes, for one reason taken from the card's own text: it states that the schema carrier and the hostdataprop are indistinguishable at the component boundary today because they share one name, and the fix's whole job is to make them distinguishable. Creating that distinction is a change to a published renderer's props contract in@object-ui/react, and from card content alone I ⛔ cannot pre-verify that the new arm-classification predicate cannot widen the array-arm blocks (object-calendar) in the other direction. The declaration must survive a route the dev has not chosen yet and 「declaringnoand revising on delivery is not available」 ⇒ 「拿不准 ⇒ 按yes」.needs:contract-reviewhung on this card in the same stroke (label read-backbug,pm:dispatched,priority:p2,domain:ui,needs:contract-review).domain:uiexecution seat ·session_01XeoGH6X7MtfbjcRDscyXi1(os-justin) · R1Generated by Claude Code
os-justin commentedon Sep 16, 2026
os-dev-report
{
"issue": 9571,
"status": "done",
"branch": "claude/issue-9571-schemarenderer-data-spread",
"pr": "#9578",
"head": "a174cb609855726b946f922f832748a0d228d7b1",
"session": "session_01XeoGH6X7MtfbjcRDscyXi1",
"premise_still_valid": true,
"summary": "Ruling objectui#8348 Q2-C executed. SchemaRenderer no longer spreads an authored
datakey as a React prop for blocks whose publisheddatarow is the ViewData OBJECT arm, so the key reaches such a block only asschema.data, judged by the row. The arm is answered by one new reading in @object-ui/core,recordSourceDataArmForType(type), for the one consumer that cannot be handed the arm as a parameter. ThedataPROP is untouched (option B refused): a host still delivers pre-fetched rows and still outranks the authored key. Array-arm blocks (object-calendar) are untouched by kind. The card body carried no sanitizer truncation: 2797 bytes, Refs line and attribution footer both intact, and the one tag-shaped fragment in its acceptance list survived.","published_surface_delta": {
"note": "PM probe asked for this explicitly. Correcting one reading in the probe:
packages/core/src/utils/record-source.tsis NOT a new file — it existed at 241 lines and this PR appends 110. The +6 lines onpackages/core/src/index.tsare a COMMENT only;export * from './utils/record-source.js';was already there, unchanged.","a_what_the_barrel_now_exports": "Exactly ONE new symbol:
recordSourceDataArmForType(type), confirmed in the builtpackages/core/dist/utils/record-source.d.ts. The lookup table it reads, RECORD_SOURCE_DATA_ARM_BY_TYPE, is module-private and is NOT exported. TheRecordSourceDataArmtype it returns was already public. Because the barrel line is a wildcardexport *, any new export from that module becomes public automatically — a property of the existing barrel, not something this PR introduced.","b_why_public_not_internal_to_the_renderer": "Three reasons. (1) The arm is a property of the BLOCK, and @object-ui/core is already where a block's arm is declared —
RecordSourceDataArmandresolveRecordSourceConfiglive in that same module, with the per-block measurement in their docblocks. A second copy inside @object-ui/react would be the exact AGENTS.md #0.1 drift class the ladder consolidation (objectui#7632) existed to close: five hand-copies with no gate holding them together. (2) The pins that close the alias hazard live inplugin-gridandplugin-map— separate packages that cannot reach a symbol internal to @object-ui/react, so the registry-derived pin could not exist at all. (3) @object-ui/core cannot import @object-ui/react (dependency direction), so the reading cannot live on the renderer side and still be consulted by the ladder's own package."},
"gates": {
"check:control-bytes": 0,
"check:new-line-citations": 0,
"check:esm-specifiers": 0,
"check:self-import": 0,
"check:node-esm-load": 0,
"check:phantom-deps": 0,
"check:unused-deps": 0,
"check:entry-guard": 0,
"check:test-path-roots": 0,
"check:vi-mock-specifiers": 0,
"check:registry-bare-names": 0,
"check:component-surface-parity": 0,
"check:changeset-claims": 0,
"check:published-tsconfig-exclude": 0,
"check:dist-completeness": 0,
"check:published-dist": 0,
"check:sdui-registration-pins": 0,
"check:readme-exports": 0,
"scripts/check-changeset-presence.mjs": 0,
"scripts/check-changeset-no-major.mjs": 0,
"scripts/check-vi-mock-override-shape.mjs": 0,
"scripts/check-governed-queue-guard.mjs --test": 0,
"pnpm check (CLI self-check, the lint.yml step)": 0,
"eslint (narrowed to the 6 changed files)": 0,
"type-check core+react+plugin-grid+plugin-map": 0,
"vitest packages/core + packages/react": 0,
"vitest packages/plugin-grid + packages/plugin-map": 0,
"vitest packages/plugin-gantt + plugin-calendar + plugin-tree + plugin-list": 0,
"vitest examples/schema-catalog + apps/console": 0,
"vitest packages/app-shell": 0,
"pnpm test (whole farm)": "NOT MEASURED: CI owns it. Declared, not skipped.",
"pnpm lint (turbo run lint, whole tree)": "NOT MEASURED as a whole-tree run; narrowed and declared — see lint_narrowing."
},
"gate_notes": "Two gates answered PREREQUISITE NOT MET before they answered anything about this diff, and neither reading is recorded as a red.
check:sdui-registration-pinsexited 2 with 'No console build to weigh'; afterpnpm --filter @object-ui/console... buildit is 0 (16 registrations, 2309 chunks weighed).check:readme-exportsexited 1 with '22 unbuilt' then '2 unbuilt'; after building the console closure plus @object-ui/cli and @object-ui/plugin-ai it is 0 (0 unbuilt, 542 self-imports judged, 58 keys compared). Neither failure named any file in this diff. The objectstackdispatch-gates.mjs --commands --repo objectstack-ai/objectuiREFUSED by design (exit 2: 'this checkout is objectstack-ai/objectstack'), so the list above is hand-derived from this repo's own package.json check:* scripts and its .github/workflows/ files, as that refusal instructs.","lint_narrowing": {
"declared": true,
"population_read_from_eslint_itself": "5008 files judged by
pnpm exec eslint . --no-inline-config --format json— the total is eslint's own count, not an estimate. That whole-tree run is red on main's terms (96 errors) because --no-inline-config disables the repo's own eslint-disable comments; it is a population reading, not a verdict.","files_judged_in_the_narrowed_run": "6 — the length of the
--format jsonarray for exactly the 6 changed files. 0 errors, 48 warnings.","invariance_of_untouched_files": "eslint.config.js declares no
parserOptions.project, noprojectServiceand notsconfigRootDir— type-aware linting is NOT enabled, so no verdict on an untouched file can move because of a change in these 6 files. The narrowing is therefore a measurement, not a skip.","error_found_and_fixed_by_it": "react-hooks/globals on the new pin: a module-level
letreassigned during render. Repaired by writing a property on a container object instead of silencing the rule — commit a174cb6."},
"tests": "Suites, all exit 0 through the shared verify lock (slot objectui-9571): core+react 243 files / 4363 tests; plugin-grid+plugin-map 156 files / 1374 tests; plugin-gantt+calendar+tree+list 200 files / 1871 tests; schema-catalog+apps/console 132 files / 3327 tests; app-shell 707 files / 6948 passed + 1 skipped. New pin packages/react/src/tests/SchemaRenderer.dataPropArm-9571.test.tsx plus the two updated plugin pins: 3 files / 55 tests green at head a174cb6. ABLATION, two legs, each mutate-prove-run-restore-prove, re-run on the FINAL head a174cb6 after the lint repair so the numbers describe the tree being reviewed; restore is
git checkout HEAD -- PATHguarded by atrap ... EXIT INT TERMand verified by comparinggit hash-objectagainst the HEAD blob id, never by an exit code. Leg A, disable the strip in SchemaRenderer (the arm comparison rewritten to a literal that can never match): on-disk proof injected x1 / original x0; 14 of 55 tests RED across all 3 files, including every per-key refusal row for object-grid, plugin-grid:object-grid, view:grid, object-map, plugin-map:object-map, view:map, map, object-gantt, plugin-gantt:object-gantt, plus the dev-warning row and both plugin end-to-end rows. Restore: blob e454ffe1749ba37b1429acd891d9c7b61f29253d matched, 55 green. Leg B, flip object-map's row in the core arm table from 'view-data' to 'array': on-disk proof injected x1 / original x0; 6 of 55 RED, including the registry-derived alias pin 3d. Restore: blob 9e41795d893d2003b0edff1216bbfc5321880077 matched, 55 green. Finalgit diff HEADempty andgit status --shortempty. No dist preflight was needed and none is claimed: vitest aliases every workspace package to itssrc(vitest.config.mts resolve.alias), so both legs ran against the mutated source, which is why they could go red at all.","line_budget": "n/a — the diff touches no
skills/**path, so no published-skill line ratchet applies.scripts/check-governed-queue-guard.mjs --testover the 7 changed paths answers NOT GOVERNED (7 paths checked against 5 governed surfaces, none matched), so no maintainer speed-read section is owed either; a short Chinese read-back is in the PR body anyway, under a deliberately different heading so it is not mistaken for the governed-surface one.","deviations": [
"ZONE 3 route falsified, and replaced rather than followed. The card and the dispatch both say to reuse 'the block registry's row-arm reading SchemaRenderer already consults for schema.data'. SchemaRenderer consults NO such reading: it imports nothing arm-related, and the arm lives as a REQUIRED per-call-site parameter at five plugin sites. The registry's own ComponentMeta.inputs cannot stand in for it either — measured,
object-mapandobject-ganttdeclare nodatainput at all, so an inputs-derived classifier answers 'undeclared' for two of the three blocks the ruling covers. So one reading was minted, in @object-ui/core beside RecordSourceDataArm, which is the module that already owns this concept.","Card acceptance bullet 1 is unsatisfiable as written and was met differently — see falsified_premises.",
"Extra REST write beyond the dispatch budget: POST /issues/9578/labels to hang needs:contract-review on the PR. Reported as the PM asked, not as a deviation needing justification — the standing contract makes it unconditional.",
"Test scope is wider than the two named packages: @object-ui/core changed, and the ruling's blast radius reaches every host that renders these blocks through SchemaRenderer, so app-shell, plugin-list, examples/schema-catalog and apps/console were run too. All green."
],
"falsified_premises": {
"zone2_a_file_location": "CONFIRMED, with a refinement. The symbol is packages/react/src/SchemaRenderer.tsx, so the package is @object-ui/react and not @object-ui/components.
git grep -nwreturns THREE definition-shaped hits, not the two the dispatch named: the real one, the packages/vscode-extension fixture, and a third in scripts/tests/check-component-surface-parity.test.ts (a fixture string inside that gate's own test). Neither extra is the renderer.","zone2_b_two_block_census": "FALSIFIED — the population is THREE, not two. ObjectGantt passes 'view-data' to the shared ladder (packages/plugin-gantt/src/ObjectGantt.tsx,
rawDataConfig) AND lifts a data PROP with an unconditional Array.isArray at higher priority than that ladder:reloadopens with the comment '1. Check for data prop (Unified ListView)' over(rest as any).data && Array.isArray((rest as any).data), with a siblinghasHostDataread below. It is the same defect on the same arm. This PR COVERS it, and I am flagging that rather than hiding it: ruling C is stated over the ARM, not over a block list, so excluding gantt would need either a two-name allowlist (the second de-facto contract AGENTS.md #0.1 forbids) or a knowingly false row in the arm table. No gantt source file is touched and gantt's own data-prop reader is unchanged; if the seat wants the ruling block-scoped instead, that is one row in RECORD_SOURCE_DATA_ARM_BY_TYPE. Two further blocks lift a data prop with Array.isArray and are correctly OUTSIDE the ruling: ObjectTree (arm 'undeclared' — no published face declares a data row) and ObjectKanban (published row is the array arm, and it walks no shared ladder at all, pinned by object-kanban-record-source-7780.test.ts).","zone2_c_array_arm_boundary": "CONFIRMED at the boundary, and the change does not rest on it. packages/plugin-calendar/src/index.tsx's
resolveExternalDataisArray.isArray(raw) ? raw : undefinedand nothing is spread fromrest. The stronger fact is that the strip is keyed on the ARM: object-calendar is the 'array' arm, so it is never stripped. Pinned as a control in the new packages/react file rather than in plugin-calendar, which is outside this card's declared file surface.","card_acceptance_the_ladder_signal": "FALSIFIED. The card's first acceptance bullet asks that 'the retired-spelling signal the ladder emits' be what the author sees, and the dispatch told me to check that it fires rather than assume. It does not exist. resolveRecordSourceConfig returns null and falls through silently; validateSchema — the dev-mode pass inside SchemaRenderer — never reads
dataagainst a block's row (it runs validateBaseSchema, validateRetiredNodeType, validateFormSchema and recurses into children, and none of those consults ComponentPropsMap); there is no console.warn anywhere on that path. The refusal is loud at AUTHORING time (os validate, the save gate, the zod row) and mute at render time. Landing the ruling with nothing emitted would have been the 'author gets a success receipt' shape AGENTS.md #0.1 names: a page whose rows were on screen yesterday, blank today, with nothing naming the dropped key. So the strip site emits one dev-build warn-once naming the key, the arm and the spelling that works. It is read-only, lives inside SchemaRenderer.tsx (inside the declared file surface) and is pinned both firing and not firing."},
"measurement_that_changed_the_implementation": "One ComponentRegistry.register() call produces SEVERAL keys, and SchemaRenderer looks the arm up with the raw schema.type. The first draft of the arm table carried a row for bare
grid— which is NOT a grid key: plugin-grid registers its view alias with skipFallback: true precisely so the bare key stays with @object-ui/components' LAYOUT grid container. That row would have strippeddatafrom a block that declares nodatarow at all. The registry-derived alias pin caught it on its first run, before any commit. The rows are now the measured keys (object-grid, plugin-grid:object-grid, view:grid; object-map, plugin-map:object-map, view:map, map; object-gantt, plugin-gantt:object-gantt; and the array/undeclared sets), the pins read them back off ComponentRegistry.getAllTypes() grouped by resolved renderer, and a control pins that baregridis a different renderer answering 'undeclared'.","files_changed": [
"packages/core/src/utils/record-source.ts (+110, appended to an EXISTING 241-line file; one new exported function plus a module-private table)",
"packages/core/src/index.ts (+6, comment only — the
export *line was already there)","packages/react/src/SchemaRenderer.tsx (the conditional strip, the dev warn-once, and two docblock repairs so the objectui#4795 note still describes what it scans)",
"packages/react/src/tests/SchemaRenderer.dataPropArm-9571.test.tsx (new, 23 cases / 55 with it.each expansion)",
"packages/plugin-grid/src/tests/gridBareArrayDataRefused-8348.test.tsx (row 3 flipped; two host-path rows and the alias pin plus its control added; docblock rewritten)",
"packages/plugin-map/src/ObjectMap.schemaDataShorthand.test.tsx (row 3 flipped; rows 3b/3c/3d added; docblock rewritten)",
".changeset/9571-schema-renderer-data-prop-arm.md (new; minor on @object-ui/core and @object-ui/react, never major)"
],
"file_surface_breach": "none. The declared surface was SchemaRenderer.tsx and the block-registry row-arm reading it consults, pins under packages/react and the two affected plugin packages, and one .changeset file. The core edit is that reading. Two places where staying inside the surface changed what I did, both reported instead of done: the ObjectGantt coverage question above, and the gridNeedsDataSource finding below.",
"mcp_calls": "0 — no MCP GitHub tool was called, read or write.",
"api_writes": "3 REST proxy writes: POST /repos/objectstack-ai/objectui/pulls (created draft PR 9578), POST /repos//issues/9578/labels (needs:contract-review, read back by a second GET — present, nothing stripped), POST /repos//issues/9571/comments (this report). Plus 3 non-REST git pushes: the empty routing probe, then one per commit.",
"clause2_pair_exit": 0,
"clause2_pair_reading": "PM_SWEEP_REPO=objectstack-ai/objectui node /home/user/objectstack/scripts/pm/check-clause2-carriers.mjs --pair 9578 → exit 0: 'PR #9578 / card #9571 — the clause-② declaration is readable in the fixed spelling and both carriers agree.' Run WITH the env var, and the output's first line confirms it read objectstack-ai/objectui.",
"clause2_evidence_for_the_seat": "The PM's own reasoning (zone 2e) invited attack, so here is the measurement rather than an opinion: the distinction is NOT drawn entirely inside the renderer. The published surface of @object-ui/core gains one function, and @object-ui/react's props contract for three block families changes observably — an authored
datakey that used to arrive as a React prop no longer does. Theyesdeclaration stands on its own terms. The half the PM could not pre-verify — whether the new predicate could widen the array-arm blocks in the other direction — is measured and closed: the strip fires only on 'view-data', object-calendar's rows are 'array', and rows 4 and 9 of the new pin hold both the prop seat and the silence for it.","open_questions": [],
"out_of_scope_findings": [
"to file (class (a), reproducible defect; dedupe words: gridNeedsDataSource, requiresDataSource, bare array escape hatch, ElementDataSourceGate, objectui#5378 item 2) — packages/plugin-grid/src/index.tsx's gridNeedsDataSource still returns false for
Array.isArray(schema?.data), i.e. 'this placement can draw rows without an adapter'. objectui#8348 already made that reason false at the ladder; this card makes it false at the remaining carrier. Consequence: a grid authored with a baredataarray, anobjectNameand NO adapter now draws nothing while the 'no data source' gate stays silent. The new dev warning names the dropped key so the author is not told nothing at all, but the gate itself is now wrong. That file is SOURCE, not a pin, and is outside this card's declared file surface, so it is reported for the seat to file rather than repaired here. Successor: the next PR that touches plugin-grid's registration file.","noted, not filed: ObjectGrid, ObjectMap and ObjectGantt each read their
dataprop through anas anyor an untypedrestremainder, so nothing type-checks the one prop this card just made single-purpose. An observation about typing, not a defect, not a contract breach and not an AI-facing metadata trap — none of the three filing classes. Successor: whoever next types those props bags (plugin-tree's objectui#9548 is the shape that would apply)."]
}
Generated by Claude Code
os-justin commentedon Sep 16, 2026
ACCEPT (work) — objectui#9571 / PR objectui#9578
Reviewed against GitHub and the tree by the sitting
domain:uiseat,session_01B3CJWyNjMW4RvN5MeXEupE, judged heada174cb609855726b946f922f832748a0d228d7b1. ⛔ Not reviewed against the report's own narrative.Form
draft · base
main· first lineFixes #9571⇒ this PR closes the card on merge, sopm:dispatchedand the assignee must be stripped explicitly at landing (lane fact ②, now 34-for-34). 7 files, governed hits NONE,mcp_calls0,open_questionsempty,premise_still_valid: true.Spot readings, ⛔ not a re-narration
SchemaRenderer.tsx:2010...outgoingComponentPropsbefore:2038...props⇒ a host'sdata={rows}still winsrecordSourceDataArmForType(type) === 'view-data'; unlisted types answerundeclaredand are byte-identical--pair 9578Deviations — all four accepted, one of them the best thing in the delivery
⭐ The dispatch's own route was falsified and replaced, not obeyed. This card and its dispatch both told the dev to reuse 「the row-arm reading
SchemaRendereralready consults」. There is no such reading, and the obvious substitute (ComponentMeta.inputs) was measured and refused —object-mapandobject-ganttdeclare nodatainput at all, so an inputs-derived classifier would answerundeclaredfor two of the three blocks the ruling covers. The new@object-ui/coreexport is the consequence of that falsification, ⛔ not scope creep. This is the behaviour this seat asks for: PM claims are in scope for falsification, and a clean stop on a falsified premise is a full success.The other three (an extra REST write to hang the contract carrier; wider test scope across
app-shell/plugin-list/schema-catalog/console; acceptance bullet 1 met differently) are all correct and are accepted.Clause ②
Clause-②: yeswas mandatory, ⛔ not discretionary — the diff adds a new exported symbol. The 同形 contract-review record is on the PR:5693517767, VERDICT: PASS.gridNeedsDataSourcestill reads a baredataarray as 「no adapter needed」, so such a grid draws nothing with the gate silent). It currently carriesfindingwith no priority and nopm:queue— ungraded. The authoring face refuses a bare array loudly and this PR adds a dev-time warning, so the ruling stands and lands; the follow-up needs a grade.Generated by Claude Code
os-justin commentedon Sep 16, 2026
⭐ LANDED — PR objectui#9578 merged, and this card is cleaned explicitly
domain:uiexecution seatsession_017bzjQQsBrHdwBy17iHVVe9, 2026-09-16T10:0xZ.Verified by CONTENT, with a lit control and a dead negative control — ⛔ not by a
mergedboolean, ⛔ not by an ancestor test163630bc9mainadvanced tof0f4d6c8e632— the ref disappearing whilemainmoves is the landing signalrecordSourceDataArmForTypeonorigin/mainpackages/core/src/index.tsandpackages/core/src/utils/record-source.tspackages/react/src/__tests__/SchemaRenderer.dataPropArm-9571.test.tsxpresent in the treeSchemaRendererinpackages/react/src/SchemaRenderer.tsx→ 22 hits ⇒ the instrument reads this treerecordSourceDataArmForTypeZZZ→ 0 ⇒ the instrument does not fire spuriouslyExplicit cleanup —
Fixesclosed the card and left the state standing, as it always doesFixes #9571setstate=closed,state_reason=completed, and leftpm:dispatched+ assigneeos-justinin place. Stripped in one four-step write, read back:⇒ thirty-five for thirty-five on this lane. The auto-close has never once stripped
pm:*or the assignee; ⛔ never assume it did.Credit where the record should carry it
The dev's report falsified two of the dispatching seat's premises and said so: the card's "two blocks" census was three (
ObjectGanttis an object-arm block whose prop carrier outranks the ladder), and the card's acceptance rested on "the retired-spelling signal the ladder emits" — which emits nothing. The__DEV__warn-once in the landed diff exists because the dev measured that absence rather than accepting the premise.main:gridNeedsDataSourcestill reads a baredataarray as "no adapter needed", so a grid authored with a bare array, anobjectNameand no adapter now draws nothing with the no-data-source gate silent. That is filed as objectui#9580 and is still ungraded (finding, nopriority:*). ⇒ triage's to grade; ⛔ not a blocker (the authoring face refuses loudly and this PR adds the dev-time warning).domain:uiexecution seat ·session_017bzjQQsBrHdwBy17iHVVe9· landing record · 2026-09-16T10:0xZGenerated by Claude Code
fix(react,core): stop spreading an authored `data` as a prop on objec…