Repository navigation
fix(plugin-form): record:line_items stops requiring childObject, so the page compile accepts a dataSource-bound node (objectui#11569) - #11604
Conversation
…he page compile accepts a dataSource-bound node (objectui#11569) The spec row leaves `childObject` optional because the node's `dataSource` binding can supply it, and `RECORD_LINE_ITEMS_DATA_SOURCE` lands `dataSource.object` on `childObject` before the panel reads the node. The registration still declared `required: true`, so the page compile refused a bound node with `missing-required-prop` at error severity. The manifest has no "one of these is required" form (`ComponentInput` and `ManifestInput` carry a boolean `required` only), so the input drops `required` and gains a description that names the binding. A node with neither keeps the panel's runtime configuration hint. New pin: apps/console line-items-bound-child-object-11569, judged against the shipped sdui.manifest.json. Claude-Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2 Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Read-only, against: the card (objectui#11569 body, triage ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL Written 2026-10-04T09:24Z. FAIL on ② alone, the changeset level. ① and ③ hold. A patch round that regrades |
…r, with the Clause-② widening banner The PR declares `Clause-②: yes`: the page compile's accept set widens for a `record:line_items` node bound through `dataSource` (and for one with neither). A `yes` declaration takes at least `minor`, as objectui#10909's `element:number` landing shipped. The widening paragraph now opens with the `**Clause-②: yes (widening)**` banner. Nothing else moves. Claude-Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2 Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Re-review after the FAIL record What ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Written 2026-10-04T09:51Z. The FAIL on ② is cleared on this head by the changeset alone; ① and ③ hold as on |
|
Generated by Claude Code |
Fixes #11569
Clause-②: yes
What changes
The
record:line_itemsregistration in@object-ui/plugin-formno longer declareschildObjectwithrequired: true. The input now publishes adescription: the node'sdataSourcebinding can name the object instead (dataSource.objectlands on this key and outranks an authored value), and a node with neither shows the panel's configuration hint and loads nothing.Why:
@objectstack/spec17.6.0'sComponentPropsMap['record:line_items']leaveschildObjectoptional "because the component-leveldataSourcebinding can supply the object instead", and the renderer agrees (RECORD_LINE_ITEMS_DATA_SOURCEmaps the binding'sobjectontochildObject). The page compile (compile()in@object-ui/sdui-parser, whoseokis the save gate) reads the registration, so it refused a bound node that the row, the zod faces and the runtime all accept.The vocabulary question, measured
The manifest has no "one of these is required" form, so per the triage ruling
required: trueis dropped and nothing is added:ComponentInput(@object-ui/types,base.ts) declaresname,type,of,required(a boolean),enumanddescription, plus retirement tombstones.ManifestInput(@object-ui/sdui-parser,types.ts) adds the framework-setbinding: 'object'marker. It says an input NAMES an object and carries no requiredness.validateTree's only requiredness check isinput.required && !(input.name in node).packages/typesis not edited. The same shape already landed in this repo:element:number'sobject(objectui#10909), and theobjectNameinputs ofobject-calendar,object-gantt,object-mapandobject-timeline, each droppedrequiredand gained a description that names the binding.Accept-set changes (for the contract review)
Measured against the manifest the console ships (
buildArtifactsoverdev/manifest-registry.ts). "Before" is this head withrequired: trueput back throughablation-replace.mjs; "after" is this head.record:line_itemswithdataSource.object,relationshipField,columns, nochildObjectok: false: one error,missing-required-propnamingchildObjectok: true, no diagnostics. ThedataSourcebinding site is recorded in both runsrecord:line_itemswithrelationshipFieldandcolumns, and neitherchildObjectnordataSourceok: false: the same errorok: true, no diagnostics. The ruling leaves this node to the runtime: the panel shows the configuration hint namingchildObjectand fetches nothing.LineItemsPanel.childObjectDecline.test.tsxpins the hint, unchanged and greenrecord:line_itemswithchildObject,relationshipFieldandcolumnsok: trueok: true(control)Generated artifacts that move with it (same generator, same runs):
sdui.manifest.json, entryrecord:line_items, inputchildObject:required: trueremoved,descriptionadded.sdui-intrinsics.d.ts(the typed JSX face):childObject: stringbecomeschildObject?: string.sdui-blocks.md, the required-props column forrecord:line_items:childObject, relationshipField, columnsbecomesrelationshipField, columns.Unchanged:
relationshipFieldandcolumnsstay required. The zod faces (objectui validate), the spec row and the renderer are not touched.registry-inputs-spec-parity
apps/console/src/__tests__/registry-inputs-spec-parity.test.tsdoes not compare requiredness, so nothing in it records this difference and the file is unchanged. Evidence: the file never reads an input'srequired, and it does readinput.ofandinput.type(the control). It is green on this head.Pins
New:
apps/console/src/__tests__/line-items-bound-child-object-11569.test.ts. It judges against the shippedsdui.manifest.json(written byemitSduiManifestand read back):childObjectas not required, with a description that namesdataSourceanddataSource.object.relationshipFieldandcolumnsstay required.okwith no diagnostic, and the binding is recorded.childObjectnode compilesok.childObjectbut has norelationshipFieldis refused with exactly onemissing-required-prop. This row reads the same before and after the fix.childObject.Reproduced first: before the fix, rows 1, 2 and 5 were red, each with
missing-required-propnamingchildObjectat error severity.Ablation, run on the committed fix:
ablation-replace.mjsputrequired: trueback (anchor hit once, blobe9b208727dd7became2591aef44b45). Rows 1, 2 and 5 turned red (3 failed, 2 passed). The restore is proven by blob hash:e9b208727dd7equals HEAD andgit diff HEADis empty. On resolution: the console project aliases@object-ui/plugin-formtopackages/plugin-form/src, so the mutation reaches the test without a build. One first attempt was refused by the tool before any test ran, because its replacement contained its own anchor. It restored to HEAD and measured nothing.Other edits
LineItemsPanel.tsxandLineItemsPanel.childObjectDecline.test.tsx: comment-only. Two comments saidchildObjectwas declaredrequired: trueon the registry entry and that the block had no spec schema. They now state the current facts.content/docs/guide/data-source.md: one prose sentence, no code fence. A boundrecord:line_itemsnode needs nochildObjectand the page compile accepts it. A node with neither shows the configuration hint..changeset/11569-line-items-child-object-optional.md:@object-ui/plugin-formminor. AClause-②: yeswidening takes at leastminor, which matches the objectui#10909 landing (PR objectui#10944). It was first gradedpatchand regraded at84ee3beafter the at-tier contract review5978497549. Its widening paragraph opens with the**Clause-②: yes (widening)**banner.Verification (head
84ee3be)84ee3bechanges only the changeset (the grade and the banner). The test, build and type-check runs below were taken at448bac5, whose code is identical. The changeset gates were re-run at84ee3be:check-changeset-presence,check-changeset-no-major,check-changeset-overwrite,check-changeset-fixed,check:changeset-claims,check:pending-changeset-literals,check:control-bytesandcheck:new-line-citationseach exit 0.pnpm --filter '@object-ui/plugin-form^...' build: exit 0 (12 of 47 projects).pnpm --filter @object-ui/plugin-form type-check: exit 0 (tsc --noEmit && tsc -p tsconfig.test.json).pnpm exec vitest run packages/plugin-form/: exit 0.Test Files 163 passed (163),Tests 1889 passed | 1 skipped (1890).Test Files 21 passed (21),Tests 614 passed (614). They include the new pin,registry-inputs-spec-parity,sdui-manifest-build-11403,html-tier-manifest,sdui-intrinsics-compile-11075,public-contract,record-block-record-reach,element-data-source-objectname-waiver-11117(readsdata-source.md) andregistered-types-validate-ratchet-10859.check-changeset-presence,check-changeset-no-major,check-changeset-overwrite,check:changeset-claims,check:pending-changeset-literals,check:control-bytes,check:new-line-citations(0 new citation(s)),check:handler-key-reads,docs:check-links,check:doc-types,check:doc-fences,check:doc-example-ids,check:element-data-source-declaration,check:test-path-rootsandcheck-vi-mock-override-shape: each exit 0.check-governed-queue-guard --testover the six paths:NOT GOVERNED.--no-inline-config --format json): 4 files, 0 errors. The warning count per file is equal at base and head (22, 18, 4). The config has no type-aware linting (noproject/projectService), and no custom rule ineslint-rules/reads the disk, so this diff cannot change the result for an untouched file.check:doc-snippetsandcheck:doc-examplesstopped atPRECONDITION NOT MET(exit 2) because the packages are unbuilt. The docs edit adds no code fence.@object-ui/consoletype-check needs the whole console closure built. A scopedtscover the new test file (console tsconfig) shows the file in the program with 0 errors in it. All 36 errors it printed are module-resolution errors (TS2307 / TS2882) for unbuilt packages, indev/manifest-registry.tsandsrc/register-plugins.ts.Acceptance notes
LineItemsPanelSchema.childObject(the panel component's TypeScript prop type) is stillstring, not optional. It is a React prop type, not stored metadata: the gate fills it before the panel reads it, and the panel guards theundefinedcase. Recorded only. Carrier: none.Implemented by the os-dev run under session
https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2, for the claim on the card.Generated by Claude Code