feat(spec): ComponentPropsMap rows for action:button/group/menu/icon and element:definition-list/repeater - #20420
Conversation
…and element:definition-list/repeater Six curated objectui public blocks had no row: the props gate skipped the four action:* types and component-type-unknown refused the two element:* lists. Each row is strict from birth, with its key set measured from the renderer read points at the objectui pin. Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
… dropped filter refinement Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…s for the six rows Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…sion Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…esolved citation Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…ent-rows Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…authorable-surface, export-origins, declaration-map) Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…ction-element-rows Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…nts on the merged tree Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 137 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin eac80982bafa158af1fff732c3b7505b54162e85 && git checkout eac80982bafa158af1fff732c3b7505b54162e85
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3cf64493899458632f87e661fff1b130bd3a8273 5e50899a481dce659bc12ad2576b4fc534cd3893 && git checkout -B drift-repro 3cf64493899458632f87e661fff1b130bd3a8273 && git merge --no-ff 5e50899a481dce659bc12ad2576b4fc534cd3893
node scripts/docs-audit/affected-docs.mjs --json 3cf64493899458632f87e661fff1b130bd3a8273
|
…ession surfaces Three ADR-0060 conformance rows for the six predicate positions the new ComponentPropsMap rows declare, split by the fault face the objectui renderers and SchemaRenderer's node gate compose to at the pin. Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #20371 (body, all five comments; triage notes ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each tested against the pin:
② Semver level
③ Boundary flags
CI on this head: 46 check-runs, none in progress at render time: 39 success, 7 skipped (opt-in and label jobs), 0 failures. The previous head's red ( Implemented-by: VERDICT: PASS Generated by Claude Code |
…mponent-props-action-element-rows
… merged tree (keeps the docs title rule) Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
|
Regen-provenance: 5868826831 ·
What the hop is: the dev's base-merge round What the seat measured on the committed trees:
This line is a pointer; the queue guard re-runs the content test itself. |
|
Pulled from the merge queue by the Signature. The merge group Why this is not a flake, and whose it is. It is a semantic conflict with a PR ahead in the queue, and it is deterministic. Every merge of this head after PR #20436 fails the same way. The rows' read points were measured at the old pin, so a citation refresh alone would be the "confident lie" the gate names. Fix (patch round, after PR #20436 lands).
Pulling it early lets PR #20414 and PR #20423 regroup without it. Generated by Claude Code |
…mponent-props-action-element-rows
Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…eclare objectName The objectui pin moved from f8a9d0fb0596 to dd3f7e1be356. Every read point the action:* and element:* rows cite was re-derived at the new pin and re-anchored; the asserting citations now name dd3f7e1be. The new pin forwards `objectName` to the action runner from action:button and action:icon, so both rows declare it (the ActionDef string scalar, per the #7751 value posture). action:group and action:menu forward it per member, so the container rows gain no key. `params` keeps its meaning on the page path (array = input list, object = static values via properties.params). The dogfood expression-conformance rows for these keys are re-anchored at the same pin; their fault policies are unchanged across the hop. Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
… objectName Output of `pnpm --filter @objectstack/spec check:generated --fix` after action:button and action:icon gained `objectName`. Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #20371 (body and all seven comments, the round-4 report ① Derived judgmentsEvery public-surface change the diff implies, re-tested at the new pin:
② Semver level
③ Boundary flags
CI on this head: 42 check-runs, none in progress after re-polling ( Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #20371
Clause-②: yes
What this does
ComponentPropsMapgains six rows for the curated objectui public blocks that had none:action:button,action:group,action:menu,action:icon,element:definition-list,element:repeater. Each row is astrictObjectfrom birth, and each key set is measured from the renderer's read points in objectui, not transcribed fromUIActionSchema, from the registrations'inputs, or from this package's object-metadataActionSchema(triage execution note 1).Before this change the six failed in two ways:
action:*types sit outside every namespace thePageComponentTypeenum populates, so the props gate skipped them. Any key insidepropertiesparsed, was stored, and was ignored by the renderer.element:*types sit inside the reservedelement:namespace with no enum member and no row, socomponent-type-unknownrefused the whole node (severity error), although objectui registers, publishes and offers both.A row closes both.
component-type-vocabulary.tsderives the known set fromObject.keys(ComponentPropsMap), so the twoelement:*types join theelement:vocabulary through their rows. This is theelement:metadata_viewershape: no enum member and no string-arm ledger entry (the vocabulary test forbids a ledger entry for a type the map declares). The three-part evidence the ledger comment asks for (registration, publication, authorship) is written on the map rows, with pin citations.Findings stay at the props gate's existing warning tier.
PageComponentSchemaparsing is unchanged, because the opentypearm already admitted all six.Read points: measured at the pin (
.objectui-shadd3f7e1be356)First measured at
f8a9d0fb0596. After #20436 moved the pin, every read point was re-derived atdd3f7e1be356(2026-09-28) and re-anchored together with the sha.Per-key citations are in
component.zod.tssection 4b and in each schema's docblock. The decisions the measurement made:action:buttonname,label,icon,actionType,variant,size,visible,disabled+ 21 keys forwarded to the runner (params,target,openIn,endpoint,method,bodyExtra,bodyShape,operation,patch,confirmText,successMessage,errorMessage,refreshAfter,undoable,recordIdField,locations,toast,resultDialog,onSuccess,description,objectName)nameis optional, because the renderer readsschema.name ?? schema.label(action-button.tsx:119).variantacceptsprimaryandsizeacceptsmdbecause the renderer maps both (:137-138).typeis refused with a rename toactionType.enabled(the legacy fallback) andautoTrigger(a host transport flag, "NOT persisted metadata") are refused with a prescription.action:iconsize,undoable,recordIdField:107pins the icon size, so there is nosize.undoableandrecordIdFieldare not in its forward (:134-196).action:groupactions,display,location,label,icon,variant,size,visibleactionsis a list of action objects (:248); the registration publishestype: 'object'. There is no group-levelname: the registration publishes it (:415), nothing reads it, and it is refused with a prescription.sizetakes the primitive's four values:mdis mapped only on the dropdown trigger (:357) and reaches the Button primitive unmapped in the default inline mode (:398,:91).action:menuactions,label,icon,variant,size,visiblevariantandsizego to the Button primitive unmapped (:230-231), so there is noprimaryand nomd.element:definition-listitems(strict{ term, description? }),columns,inlinecolumnsis the number1 | 2, because the renderer compares=== 2(data-list.tsx:49). The registration's enum publishes the strings'1'/'2', and the string'2'is refused with a prescription.itemsis optional: absent and empty both render "No details".termis required.element:repeaterobject(required),titleField,fields,filter,sort,limit,emptyText,dividedfilterandsortuse the family's one orthography,ViewFilterRule[]andSortItem[]. Both reach the query:ObjectStackAdapter.findlowers rule arrays and serializes sort items.fieldstakes a name or{ field }; thelabelthat the TS type advertises is never rendered and is refused.The value posture follows #7751. A key the renderer interprets itself gets a value schema. A key it only forwards to the action runner gets the scalar that
ActionDefdeclares for it, orz.unknown()whereActionDefuses a spec-derived block.objectName, carried by the new pin. Atdd3f7e1be356,action:buttonandaction:iconforwardobjectNameto the runner (action-button.tsx:307,action-icon.tsx:195), and the console dispatches to that object instead of the page object. Both rows declareobjectNameas theActionDefstring scalar.action:groupandaction:menuforward it per member (action-group.tsx:323,action-menu.tsx:313), so it rides each member object and the container rows gain no key. The same pin reads static values fromproperties.params(objectui#10289,static-params.ts:91-101). On a page node that is the row itself, soparamskeeps its meaning: an array is the input list and an object is the static values. Its value schema is unchanged.Surface beyond the claim, and why
The claim lists
component.zod.ts, tests insrc/ui/, generator output and.changeset/. Three gates required three more files, two inpackages/specand one inpackages/qa/dogfood. All three edits follow from the new rows, and none was stopped on:dropped-refinements.baseline.json:ElementRepeaterPropspublishesViewFilterRuleSchema, whose refinement the JSON Schema projection drops. The build refuses to publish until the site is declared. I added the entry the build printed, plus the header totals its test holds (211→212 schemas, 609→610 sites).type-alias-convention.pin.test.ts:gen:docsrequires a type alias for every documented schema, andcheck:spec-parsed-aliasrequires an isomorphic alias to be pinned.ElementDefinitionListPropsis the only isomorphic one of the six, so it gets one pin. The count is 780→781 after the merges with [finding] four more exported spec types resolve tounknownwhile their TSDoc promises a shape —ViewMetadataParsed,InlineAction,AssembledViewArtifact,JoinedReportBlock(the #19871 class, other sites) #19920's 786→783 and main's connector-retirement 783→780 (both intents stacked). The other five declareXParsed.packages/qa/dogfood/test/expression-conformance.ledger.ts: gate-forced by theDogfood Regression Gate(expression-conformance.test.ts, ADR-0060 checkLedger). The sixvisible/disabledpredicate positions the new action rows declare needed a classification. It has three rows, split by fault face as the objectui renderers andSchemaRenderer's node gate compose at the pin: button/menuvisiblefail-closed, icon/groupvisiblefail-soft-log, button/icondisabledfail-closed. Round 4 re-anchored the three rows atdd3f7e1be356; their fault faces are unchanged, because the evaluators are code-identical across the pin hop.component-type-vocabulary.tswas not edited. ItsKNOWN_COMPONENT_TYPESdocblock lists the string-arm rows "exactly" (element:metadata_viewer, the plugin widgets,object-*), and that list no longer covers the six new rows. It is noted below, not fixed here.element:repeater'sfilteris the barez.array(ViewFilterRuleSchema)door thatrecord:related_listdeclares, not aruleArrayFilterErrordoor. That prescription speaks to a door that used to take the record form, and wiring it would pull the repeater into the reach of the stored-row conversionpage-component-filter-record-to-rule-array(conversions/registry.ts, whose test holds the two equal). That registry is outside this card.Premise checks (order zone 2)
ab6fb027,git grepfor each quoted type inpackages/spec/src: the fouraction:*hit onlyaction.zod.ts:983-986(theAction.componentenum) plusexpression-bindable-text-keys.test.ts:126. The twoelement:*types hit 0. Control:'element:text'hitscomponent.zod.ts:4609(the row).origin/mainare listed above. The pin was not bumped.propertiesis accepted in silence — the props bag is a passthrough record and the SDUI props gate has no schema to dispatch forobject-*blocks #7751 / spec:record:reference_railhas no ComponentPropsMap row — an entryfilterparses, typechecks, validates, ships, and silently does nothing #8691 / spec:record:alert/record:quick_actions/record:historyhave no ComponentPropsMap row — same silent no-op mechanism #8691 closed for the rail #8744 method is followed. The three-part evidence is on the twoelement:*rows.node scripts/check-sdui-manifest.mjs: see the gate table. The manifest was not touched.['"]action:(button|group|menu|icon)['"]|['"]element:(definition-list|repeater)['"]plus thetype:YAML form, excludingnode_modules/dist. The hits aresdui.manifest.json,action.zod.ts'scomponentenum, three generated reference pages (a type union) and one test. Control with the same shape:'element:text'gives 105 occurrences in 29 files, includingexamples/**andplatform-objects. No shipped metadata is refused by the new rows.Through the lint door
A one-off probe (not a permanent test) runs
validateComponentTypesandvalidateComponentPropsfrompackages/lint/srcagainst the built spec, using one stack with a planted typo on each row pluselement:repeatras a control:component-type-unknownfires only onelement:repeatr.component-props-unknown-key(warning) fires onaction:button.typo_key, onaction:group.name, and onelement:repeater.fields.0.label, which the lone union arm unpacks.component-type-unknownfires on bothelement:*types and onelement:repeatr, and the props gate reports nothing.Tests
The readings below are at HEAD
5e50899a4(after mergingorigin/mainat3cf644938, the pin bump):pnpm --filter @objectstack/spec test(thelocalproject): 565 files, 16684 passed, 1 todo, exit 0. This includes the newsrc/ui/component-action-element-rows-20371.test.ts(45 tests):element:repeatrcontrol.pnpm --filter @objectstack/spec test:repo: 37 files, 684 passed, exit 0.pnpm --filter @objectstack/spec typecheck(tsc --noEmit,check:scripts-typecheck,check:test-typecheck): exit 0....@objectstack/specsweep: the three the order names):pnpm --filter @objectstack/lint test: 115 files, 5331 passed, exit 0.pnpm --filter @objectstack/metadata-core test: 16 files, 289 passed, exit 0.packages/qa/dogfoodtest/expression-conformance.test.ts: 7 passed, exit 0.@objectstack/cliunit layer (exec vitest run --project unit; not re-run this round, this is the round-1 reading): 2579 passed, 29 skipped, 0 assertion failures. 52 files are NOT MEASURED: they fail to load onMODULE_NOT_FOUNDfor workspace dependencies not built here (@objectstack/plugin-email,create-objectstack,@objectstack/verify,@objectstack/cloud-connection, the cli's owndist). The integration layer is declared to CI, since the diff touches no spawn entry.@objectstack/specitself;--filter @objectstack/formula --filter @objectstack/sdui-parser --filter @objectstack/lint);--filter "@objectstack/client-react..." --filter "!@objectstack/spec") forcheck:skill-examples.pnpm lint(that one is CI's):eslint --no-inline-config --format jsonover the diff's three TS files (the only lintable files in it; the rest are JSON, MD and MDX, which the config'sfilesglobs do not select): 3 files, 0 errors, 0 warnings.--print-configresolves a config for each of the three files.eslint.config.mjsenables no type-aware linting (noparserOptions.project, as its own comment near line 327 states), so this diff cannot move any untouched file's verdict.7bd546c1fixed it.Gates
The derived union at
5e50899a4is 108 commands (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths, off the merge base). It is a superset of the 72-line dispatch list, adding 36 families for the changeset, the docs and the pin test. Every exit code was written to disk before its output was read.check:type-check-debt(to a verdict this time: 4 ledger entries re-measured, none above its recorded number),check:dts-closure,check:generated,check:api-surface,check:authorable-surface,check:docs,check:strictness-ledger,check:objectui-pin-citations,check:spec-parsed-alias,check:yaml-examples,check:liveness,check:issue-citations,check:nul-bytesandcheck:skill-examples(after building the client closure).pnpm check:dual-build-cjs-loads. It exited 3 (PREREQUISITE NOT MET) because it reads every workspace package'sdist, which needs a whole-repo build. This diff changes only@objectstack/spec's build output.dispatch-gates --ran: "108 derived famil(ies) accounted for — 107 run, 1 NOT-MEASURED".node scripts/check-sdui-manifest.mjs: exit 0. The manifest is untouched by this branch and recorded at pindd3f7e1be356(A4: no lockstep gate moved).check:objectui-pin-citations: exit 0 (49 asserting citations matchdd3f7e1be, 61 historical).--verify-anchorsagainst add3f7e1beclone: exit 0.Acceptance notes (not filed; the seat decides)
registry-inputs-spec-paritygate will surface each of these on the spec bump):action:groupregistration publishesname, which nothing reads, and asizeenum withmd, which inline mode does not map;element:definition-listregistration'scolumnsenum is the strings'1'/'2'(the designer writes numbers);element:repeater's TS type and registration description advertisefields[].label, which is never rendered.action:menuspreads...restonto its trigger afterdisabled={loading}, soSchemaRenderer'sdisabled: undefinedcan override the in-flight disable (the objectui#9131 shape it fixed on button/icon);action:menuand inlineaction:groupspread hoisted props (actions,label, …) raw onto DOM elements.component-type-vocabulary.ts'sKNOWN_COMPONENT_TYPESdocblock enumerates the string-arm rows as "exactly"element:metadata_viewer, the plugin widgets andobject-*; the six new rows are not in that list. This is prose drift and not edited, because the file is outside the claimed surface (carrier: the next edit of that file).element:repeater.filteris a bare rule-array door. Wiring it toruleArrayFilterErrormeans addingelement:repeatertoRULE_ARRAY_FILTER_BLOCK_TYPESinconversions/registry.tsin the same change (carrier: none).Changeset
@objectstack/specminor: six new public rows, two types admitted to theelement:vocabulary, and nothing that a declared row accepted is refused.Downstream
objectui#10872 can now arm the six by reference. When it bumps
@objectstack/spec, itsregistry-inputs-spec-paritygate will judge the six in both directions:nameonaction:groupand the'1'/'2'string enum oncolumns.action:button, 20 onaction:icon,location/visibleon the group, andsize/visibleon the menu.That reconciliation belongs to objectui.
The
Surface beyond the claimsection was amended by thedomain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW) after the patch round, from the dev's delta5868569396.Round 4 (after #20436 moved
.objectui-shatodd3f7e1be356): the read-points heading, the table anchors, theobjectNameparagraph, Tests, Gates, Acceptance notes and Downstream were amended by the same seat from the dev’s delta (report5873527479).