Repository navigation
fix(app-shell): the generic metadata editor renders a view's served schema (a top-level anyOf), and a scalar-arm repeater draws scalar rows (objectui#11251, objectui#10239) - #11277
Conversation
…el union, and a scalar-arm repeater draws scalar rows SchemaFormBody read only top-level `properties`, so the served `view` schema (a union over the stored shapes) rendered zero controls in the MetadataDetailDrawer editor. It now resolves a top-level anyOf/oneOf against the value with resolveUnionBranch. The resolver scores a bare nested union member by member and ranks members the value contradicts below those it does not, so each stored view lands on its own member. RepeaterField draws one scalar control per row when its items resolve to a scalar arm and rows exist, so a string-array columns stays strings. Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec Co-authored-by: Claude <noreply@anthropic.com>
… what they measure The ranking comment marks its one-time scan as not re-derived, the changeset states the tie rule exactly, and the container pin is named for what it observes (controls render, none of the list overlay's). Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ 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: ① Derived judgmentsInputs and gate verdicts. Card #11251 (body; triage The declared deviation (dev report deviation 1) — judged first. Right, with the moves named.
Each stored shape lands on the member that keeps it. Right. Against
Top-level resolution only when the schema has no Scalar-row guard. Right. Pins and the four legs. 8 tests in Public surface and accept set. Nothing moves. Declared narrowing. Verified: ② Semver level
Changeset-claim-re-read (
③ Boundary flagsDev report
Out-of-scope notes, against the filing classes:
PR body at the head: Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #11251
Fixes #10239
Clause-②: no
What was wrong
Studio → object → Related → Views → a row opens
MetadataDetailDrawer, which mountsMetadataResourceEditPageembedded; with no canvas there, the page renders the genericSchemaFormover the servedviewschema. That schema is the server's derivation ofViewMetadataSchema: a top-levelanyOfover the stored shapes (ViewItem record,defineViewcontainer, list overlay, form overlay) with no top-levelproperties.SchemaFormBodyread onlyeffectiveSchema.properties ?? {}, so every stored view opened an editor with zero controls.The fix
SchemaFormBodyresolves a top-levelanyOf/oneOfagainst the value with the existingresolveUnionBranch(the resolverrepeaterItemSchemaandresolveFieldFacealready use). No second resolver. A schema that declares its ownpropertiesbeside a union is left alone.resolveUnionBranch, because the obvious call lands the stored views on the wrong member. Measured on the installed spec 17.5.0 with the unextended scorer: a ViewItem resolved to the list overlay, a flattened list overlay carryingtype: 'grid'resolved to the FORM overlay, and a container resolved to the list overlay. The ViewItem case is the harmful one: its form would show the columns underconfigas an empty top-levelcolumns, andViewMetadataSchema.safeParseof that ViewItem plus a top-levelcolumns/typeedit answerssuccess: truewith branchviewItemand those keys stripped, so the edit would be dropped silently. The refinements:unionMembers: a member that is itself a bare union (notype, noproperties) is scored member by member. The ViewItem arm is{ oneOf: [list ViewItem, form ViewItem] }and scored zero as one member.valueContradictions: among members that fit the value's kind, the one the value contradicts least wins: a key a closed member does not declare, a primitive outside aconst/enum, arequiredkey the value lacks. An empty value contradicts nothing (create mode still lands on the first member). Where contradictions tie, ranking is the old score in the old order.RepeaterFielddraws one scalar control per row when its items resolve to a scalar arm and the value has rows; Add appends a scalar. Before, a string-arraycolumnsrow got the form's 14 object sub-fields, the card summary readString.prototype.link, and editing wrote{"0":"n","1":"a","2":"m","3":"e","field":"title"}. With no rows the first arm wins by order alone, so the empty value keeps its object-row first Add.The curated designer (
VIEW_CURATED_FIELDS,FORM_FIELDS_OWNED_ELSEWHERE,ViewVariantInspector.tsx) is untouched.Measured through the real page
jsdom, the real
MetadataDetailDrawer→MetadataResourceEditPage(embedded), the mocked client servingz.toJSONSchema(ViewMetadataSchema, { unrepresentable: 'any' })+METADATA_FORM_REGISTRY.view. Not run in a browser. Before = base54a78308a, after =4a9380847, same probe, form controls counted by scopedmdf-drawer-metadata.id after clicking Edit:name,label)columnsrowsname,label)ListViewSchemaservedPins —
SchemaForm.topLevelUnion-11251.test.tsxviewschema through the real drawer: each of the three stored shapes renders controls, and not the wrong member's (ViewItem: no top-leveltype/columns/searchableFields; list overlay:searchableFieldsand thecolumnsgroup, which the form overlay lacks; container: none of the list overlay's).ListViewSchemaserved renders its list form with two object column rows.SchemaFormoverListViewSchema(top-levelproperties, so independent of the union step): a string-arraycolumnsstays stringsListViewSchemaaccepts, the row control is named by the host label plus its ordinal, Add appends''; object rows keep their declared fields; the empty value's first Add gives an object row.Ablations (committed HEAD
2082cb00b, mutations throughscripts/ablation-replace.mjswith anchor hit and blob change proven, each restore provenblob == HEADandgit diff HEADempty; the test imports./SchemaFormfrom source, so no build leg)Gates
pnpm exec vitest runover all 368 test files underpackages/app-shell/src/views/metadata-admin/, in 7 chunks at4a9380847: 368 files passed, 4346 tests passed, 1 skipped. SchemaForm consumers outside it (studio-design panels, consoleFormPage.sectionGroup): 6 files, 20 tests passed.2082cb00b(it differs from4a9380847by comment text, one test name and changeset prose): the pin file 8/8; the 43SchemaForm.*/ResourceEditPage.*/EmbeddedItemEditor.*files, 413 tests passed.pnpm --filter @object-ui/app-shell type-check(closure built first): exit 0 at4a9380847;tsconfig.test.json --listFilesOnlylists the new pin file.2082cb00b:pnpm exec eslinton both changed files 0 errors (16 warnings, all on unchanged lines);check-changeset-presence✅;check-control-bytes✅;check:new-line-citations0 new.Acceptance notes
viewFormdescribes the flattened list shape only. A ViewItem or a container therefore renders its identity controls (name,label) plus the form's "declared in form layout but missing from schema" notice for the rest (8 each in the drawer). The ViewItem's list config underconfigis not editable through this door. Observation; the form registry is objectstack's. Carrier: none.z.undefined()refusals ofconfigand the container slots derive to{}(accepts anything), so the served document cannot tell those refusals from free keys. The resolver separates container from overlay through the overlays' requiredobject+viewKindpair instead. Observation only.view: across the installed spec's served derivations (34 derived documents, both io modes), bare nested unions occur only at theviewtop level and at thevisible/disabledpredicate slots ofactionand object actions (input derivation), which render through the registered condition widget. No union member lackstypewhile carryingrequired. One-time scan; nothing re-derives it.columnsarray whose FIRST row is an object still draws object rows for every row. No valid stored view carries one: both arms refuse a mixed array at save.Implemented by the
domain:uiseat 2 dev run, sessionhttps://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec.Generated by Claude Code