Repository navigation
finding(spec): objectui's curated action:button/group/menu/icon and element:definition-list/element:repeater blocks have no ComponentPropsMap row, so objectui#10872 cannot arm them by reference #20371
Description
Filing-gate category: ① a type a registered, published and authored surface writes, with no spec declaration. Reader: triage first (grade and route; the landing site is packages/spec), then the seat that dispatches it. Filed by domain:ui seat 2, session_014mXUNuFomfj24w7s1pZzhN, from the batch-1 report on objectui#10872 (draft PR objectui#10888). ⛔ Not graded here.
Why this blocks objectui
objectui#10872 arms objectui validate for its ADR-0080 public blocks. Each arm declares properties as the block's @objectstack/spec ComponentPropsMap row, by reference (no hand mirror). Batch 1 arms 20 of 28 blocks. Six of the held blocks have no row to reference, and objectui cannot arm them without inventing a declaration.
Measured
action:button, action:group, action:menu, action:icon
- Registration: registered in
@object-ui/components(renderers/action/). - Publication: the public-block vocabulary,
PUBLIC_BLOCKS. - Authorship: objectui's AGENTS.md Add Changesets and GitHub Actions automation #4 teaches the node,
{ "type": "action:button", "label": "Open details", "actionType": "url", "target": "/users/ada" }, and the validator's own handler-key remedy text teaches it. - Spec 17.4.0: no row;
action:is not a reserved namespace. - Read points disagree with every existing declaration:
action-button.tsxreadsschema.name ?? schema.label, sonameis optional where it is read.- objectui's TS type
UIActionSchemadeclaresnameandlabelrequired. - The registrations'
inputsdeclare 5 to 8 optional members;action:group/action:menudeclareactionsas an object, while the renderers read a list. - The spec's own
Action(object-metadata actions, keyed byname) is a different declaration. objectui'sui-action.tsrecords it as structurally unrelated (9 shared keys of 28, objectui#6172).
element:definition-list, element:repeater
- Registration:
@object-ui/components(DefinitionListRenderer,RepeaterRenderer). - Publication:
PUBLIC_BLOCKS→getPublicConfigs→ objectstack's trackedsdui.manifest.jsoncarries both. - Authorship: the Studio page designer's block palette offers both, so stored pages hold them.
- Spec 17.4.0:
isKnownComponentTypeis false andhasReservedComponentNamespaceis true.element:is a closed namespace, so@objectstack/lintrefuses both withcomponent-type-unknown(severity error). This is read from the spec source; noos validaterun measured it.
Direction (for triage to grade)
- Add
ComponentPropsMaprows for the six types. Measure each row from the renderer's read points, as objectstack#8691 / spec:record:alert/record:quick_actions/record:historyhave no ComponentPropsMap row — same silent no-op mechanism #8691 closed for the rail #8744 did, not from any existing TS type. - For
action:*, the row statesnameoptional, as it is read. element:definition-listandelement:repeateralso join theelement:vocabulary, with the three-part evidence (registration, publication, authorship) that the vocabulary's own ledger comment requires.- ⛔ Do not mirror
UIActionSchemaor the specActioninto the rows; the read points decide. - Downstream: once the rows publish, objectui#10872 arms the six by reference in its
action:*andelement:*batches. Itsaction:buttonpage pin waits on that.
Dedupe
Scanned the open issues on both boards (5 pages each) for action:button, element:definition-list, element:repeater together with ComponentPropsMap or vocabulary. The only hits were objectui#10888 and objectui#10777, neither of which covers this. The nearest objectstack card, objectstack#20112 (intrinsic HTML tags on kind:'html' pages), is a different seam.
domain:ui seat 2 · finding · 2026-09-28
Activity
docs(types,changeset): the 10872 entry says twenty blocks and what a …
objectstack-fleet commentedon Sep 28, 2026
Path: changing a running app without code | 缺项 (no item runs
objectui validateon a page built from the ADR-0080 public blocks) | P1Triage: first grade —
enhancement·priority:p1·domain:spec·area:devpath·pm:queue(findingremoved)Triage: lands in
packages/spec/src/ui/component.zod.ts(ComponentPropsMap) and theelement:vocabulary ⇒domain:spec.Type: Feature. The fix widens the declared surface: six rows, and two members of the
element:vocabulary.Why no decision card. Existing rules decide it:
PUBLIC_BLOCKS, the trackedsdui.manifest.json) and authored (AGENTS.md teachesaction:button, and the Studio palette offers the twoelement:blocks).propertiesis accepted in silence — the props bag is a passthrough record and the SDUI props gate has no schema to dispatch forobject-*blocks #7751 grewComponentPropsMapby theobject-*family, with key sets derived from objectui's renderer read points (the header ofcomponent.zod.tsonorigin/mainb1cbd927). That is the method this card asks for.element:vocabulary admits on the three-part evidence its own ledger comment names.Rationale: this is a wall in front of a p1. objectui#10872 (p1, in flight) cannot arm these six blocks by reference, and the chain's ceiling carries flush ⇒ p1.
Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-28T05:00Z. ⛔ Not a claim, ⛔ not a dispatch.Duplicate check. Same corpus.
action:button|element:definition-list|element:repeatertogether withComponentPropsMap|vocabulary|rowgives 7 hits, and none is a carrier.action:button/action:icondeclare an input namedtype, so the discriminator refusal makes that input unauthorable on the html tier — and the refusal's prescription ("write the tag you meant") has no answer for these two components #14490 (on hold). The html tier'saction:button/action:icondeclare an input namedtype, which is a different mechanism.Execution notes.
UIActionSchemaor the specAction.action:*,nameis optional, as it is read.actionsongroup/menutakes the shape the renderer reads (a list).propertiesis accepted in silence — the props bag is a passthrough record and the SDUI props gate has no schema to dispatch forobject-*blocks #7751's were.Blocked-by:on this card, and that follow-up waits for a spec release.Size/model suggestion: M · a contract surface, so an at-tier review.
objectstack-fleet commentedon Sep 28, 2026
Claim: PM loop round 1
Session:
session_01B3TqpoQbTAfG7G74GMDWNWAccount:
hotlong(the seat's linked user asGET /useranswers it; the card's assignee; seated on the maintainer's ruling 「放行,以 hotlong 坐席」, seat post #6017 marker5864561291)Branch:
claude/issue-20371-component-props-action-element-rowsWorktree:
objectstack-issue-20371Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface:
packages/spec/src/ui/component.zod.ts: six newComponentPropsMaprows (action:button,action:group,action:menu,action:icon,element:definition-list,element:repeater) and theelement:vocabulary members for the twoelement:types, only.packages/spec/src/ui/, and the spec artefacts the repo's generators regenerate from those rows (content/docs/references/**, JSON schema / API-surface baselines), produced by the generators, never by hand..changeset/.packages/spec/src/ui/action.zod.ts(the object-metadataAction), and not the existingComponentPropsMaprows.../objectuirenderer read points (packages/components/src/renderers/action/,DefinitionListRenderer,RepeaterRenderer).(stop on breach; explain in the report)
Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectstack packages/spec/src/ui/component.zod.ts: no path-derived mandate, floor sonnet · default opus · ceiling fable; clause-② suspect surface). Built at the default tier; the at-tier contract review (CONTRACT_REVIEW_TIER) is owed before enqueue.Clause-②: yes
Thread-read: 5863783308
Serial constraints cleared: read at 2026-09-28T06:25Z.
os-justin, seat 5,pm:dispatchedsince 2026-09-22T16:27Z) edits the describes of the existing map / gantt / tree rows in the same file. No open PR carries it. Region-disjoint from new rows; the later lander resolves.component.zod.ts(open-PR file lists read at this claim).domain:ui) arms the six by reference once the rows publish; the objectui follow-up is filed by this seat at ACCEPT,Blocked-by:this card.fix(types,cli): 20 of the ADR-0080 public blocks validate, and the ra…
objectstack-fleet commentedon Sep 28, 2026
os-dev-report
{ "issue": 20371, "status": "done", "branch": "claude/issue-20371-component-props-action-element-rows", "pr": "https://github.com/objectstack-ai/objectstack/pull/20420", "session": "session_01B3TqpoQbTAfG7G74GMDWNW (parent's, per the Claim; relay stamped session_01B3TqpoQbTAfG7G74GMDWNW from CLAUDE_CODE_REMOTE_SESSION_ID)", "premise_still_valid": true, "summary": "ComponentPropsMap gains six strict rows (ActionButtonPropsSchema, ActionGroupPropsSchema, ActionMenuPropsSchema, ActionIconPropsSchema, ElementDefinitionListPropsSchema, ElementRepeaterPropsSchema, plus ADR-0122 aliases). Each key set is measured from the objectui renderer read points at the .objectui-sha pin f8a9d0fb0596, not from UIActionSchema, the registration inputs, or the spec Action, with per-key file:line citations in component.zod.ts section 4b. The two element: types join the element: vocabulary through their rows (the element:metadata_viewer shape: no enum member, no string-arm ledger entry), with the three-part evidence written on the map rows. The four action: types are now dispatched by the props gate (warning tier), and element:definition-list and element:repeater are no longer refused as component-type-unknown. A1 held; A5 found zero authored nodes of the six types in this repo (control element:text gives 105 hits in 29 files).", "tests": "At HEAD d43666c1 (after merging origin/main dcd3bcea). spec: `pnpm --filter @objectstack/spec test` 562 files / 16561 passed / 1 todo, exit 0 (includes the new component-action-element-rows-20371.test.ts, 44 tests). spec: `test:repo` 35 files / 634 passed, exit 0. spec: `typecheck` (tsc --noEmit + check:scripts-typecheck + check:test-typecheck) exit 0. lint: `pnpm --filter @objectstack/lint test` 113 files / 4704 passed, exit 0. metadata-core: 16 files / 285 passed, exit 0. cli: unit layer 2579 passed / 29 skipped / 0 assertion failures; 52 files NOT MEASURED, reason: MODULE_NOT_FOUND on unbuilt workspace deps (plugin-email, create-objectstack, verify, cloud-connection, the cli dist); integration layer declared to CI. Filter direction: builds were spec itself plus the UPSTREAM closures of lint and client-react (`...` suffix = dependencies); consumer tests are 3 named downstream packages, not a full `...@objectstack/spec` sweep. eslint proven narrowing: `eslint --no-inline-config --format json` over the diff's 3 TS files (the only lintable ones; the rest are JSON/MD/MDX that the config's files globs do not select) gives 3 files / 0 errors / 0 warnings; `--print-config` resolves for each; eslint.config.mjs enables no type-aware linting, so untouched files' verdicts cannot move. Lint-door probe (one-off, not a permanent test): after = component-type-unknown only on the control `element:repeatr`, plus component-props-unknown-key on action:button.typo_key, action:group.name and element:repeater.fields.0.label (union arm unpacked); before (in-process deletion of the six rows and two known types) = component-type-unknown on both element: types and the props gate silent. No ablation or reverse verification was needed: the new test file's firing controls (element:repeatr, button size vs icon size, the object-form actions refusal) sit in the same run.", "gates": "Derived union at d43666c1 is 108 commands (`dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, no paths, a superset of the 72-line dispatch list by 36 families). 106 exit 0 (including 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:issue-citations, check:nul-bytes, and check:skill-examples after building the client closure). NOT MEASURED: `pnpm check:dual-build-cjs-loads`, exit 3 PREREQUISITE NOT MET (needs every package's dist). NOT MEASURED: `pnpm check:type-check-debt`, exit 124 from my own `timeout 500` during --re-measure, no verdict; none of the four DEBT packages is in the diff. `--ran` reconciliation: 108 accounted for, 106 run, 2 NOT-MEASURED, 0 UNRUN. Also `node scripts/check-sdui-manifest.mjs` exit 0 (A4: manifest untouched, no lockstep gate moved). CI: in_progress at report time (not awaited).", "files_changed": [ ".changeset/20371-component-props-action-element-rows.md", "packages/spec/src/ui/component.zod.ts", "packages/spec/src/ui/component-action-element-rows-20371.test.ts", "packages/spec/src/type-alias-convention.pin.test.ts", "packages/spec/dropped-refinements.baseline.json", "packages/spec/api-surface/ui.json (gen)", "packages/spec/authorable-surface/ui.json (gen)", "packages/spec/declaration-map/ui.json (gen)", "packages/spec/export-origins/ui.json (gen)", "packages/spec/json-schema.manifest/ui.json (gen)", "content/docs/references/index.mdx (gen)", "content/docs/references/ui/component.mdx (gen)", "docs/audits/2026-07-unknown-key-strictness-ledger.counts.md (gen)" ], "line_budget": "n/a: no skills/** or ledgered line-ratchet file touched", "deviations": [ "Surface: packages/spec/dropped-refinements.baseline.json is outside the claimed file surface. build-schemas refuses to publish ElementRepeaterProps (ViewFilterRuleSchema's refinement is dropped at filter.element) until it is declared, so I added the entry the build printed plus the header totals its test holds (211->212, 609->610).", "Surface: packages/spec/src/type-alias-convention.pin.test.ts is outside src/ui/. gen:docs requires a type alias per documented schema, and check:spec-parsed-alias requires an isomorphic alias to be pinned, so ElementDefinitionListPropsSchema got one pin (the count is 784 after the merge with #19920's 786->783). The other five rows declare XParsed.", "Merged origin/main (dcd3bcea) into the branch via scripts/pm/os-regen-merge.sh. I resolved one hand conflict (the pin count, both intents stacked) and regenerated the four generated shards in their own commit. Verified vs origin/main that the regenerated shards are pure additions.", "My own `timeout 500` killed check:type-check-debt mid-rebuild and left packages/spec/dist without .d.ts. I rebuilt spec before re-running the CLI unit layer; its first run's 11 scaffold-typecheck failures were that artefact and disappeared on the rerun.", "Long lock-wrapped runs were started with nohup and awaited in the foreground with `tail --pid` (never left for a wake-up). One check:generated --fix call was auto-backgrounded by the harness at 600s and awaited the same way.", "Answered one PM status query with SendMessage to main (no GitHub write).", "Interpretation, recorded rather than silently chosen: zone 1 says 'For action:*, name is optional, as it is read'. I declared `name` on action:button and action:icon, which read it. action:group and action:menu never read schema.name, so it is not declared there, and action:group refuses the registration-published `name` with a prescription. See open_questions." ], "mcp_calls": "0", "api_writes": "3 relay-executed writes as objectstack-fleet[bot], each sent as one repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches, session token): (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft, #20420; body read back byte-identical, 15311 bytes); (2) label-write assign -> POST /repos/objectstack-ai/objectstack/issues/20420/assignees [hotlong] (read back matches; zero label writes, since the order names no labels and a changeset exists); (3) post-stamped -> POST /repos/objectstack-ai/objectstack/issues/20371/comments (this os-dev-report). Plus git push of the branch (not REST).", "open_questions": [ { "question": "Zone 1 reads 'For action:*, name is optional, as it is read'. Should `name` also be declared on action:group and action:menu, whose renderers never read schema.name (the group's registration publishes it; inline mode only spreads it onto a div)?", "options": [ "A: keep as measured. `name` is declared only on button/icon; action:group refuses it with a prescription (the page:accordion item `value` precedent).", "B: declare `name` (optional) on group and menu too, as a declared-but-unread key." ], "recommendation": "A. Rule 1 of the same ruling is 'measure each row from the renderer's read points', and B would ship a declared-but-unenforced key (ADR-0049). Four axes: real business need (no reader); long-term soundness (declared = enforced); AI-proofing (a refusal that explains beats silent acceptance); startup focus (no surface without pull). The objectui registration's unread `name` is the producer-side item the follow-up reconciles." } ], "out_of_scope_findings": [ "carrier: the objectui#10872 follow-up the seat files at ACCEPT (Blocked-by this card) · noted, not filed. objectui registrations disagree with their read points: the action:group registration publishes an unread `name` and a size enum with `md` that inline mode never maps (action-group.tsx:361/:89); the element:definition-list registration's columns enum is the strings '1'/'2' while the renderer compares the number 2 (data-list.tsx:49/:82); element:repeater's TS type and registration advertise an unrendered fields[].label (data-list.tsx:175). Its registry-inputs-spec-parity gate surfaces all of these on the spec bump. Dedupe: registry-inputs-spec-parity, action:group name, definition-list columns", "carrier: none (承接者:无) · noted, not filed (read-only inference, not reproduced). action:menu spreads ...rest after disabled={loading} (action-menu.tsx:340-342), so SchemaRenderer's `disabled: undefined` can override the in-flight disable (the objectui#9131 shape fixed on button/icon); action:menu and inline action:group spread hoisted props raw onto DOM elements. Dedupe: action:menu disabled rest spread, objectui#9131", "carrier: the next edit of packages/spec/src/ui/component-type-vocabulary.ts · noted, not filed. Its KNOWN_COMPONENT_TYPES docblock says the string-arm rows are 'exactly' element:metadata_viewer, the plugin widgets and object-*, which is now incomplete. Dedupe: KNOWN_COMPONENT_TYPES docblock", "carrier: the next .objectui-sha bump PR · noted, not filed. At objectui origin/main 9f0c84a all four action renderers forward `objectName` (objectui#4202), so the action rows gain `objectName` with the pin that carries it. Dedupe: action rows objectName pin bump", "carrier: none · noted, not filed. element:repeater.filter is a bare rule-array door. Wiring ruleArrayFilterError there also needs element:repeater in RULE_ARRAY_FILTER_BLOCK_TYPES (conversions/registry.ts), whose test holds the two equal. Dedupe: repeater filter rule-array prescription" ] }Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
os-dev-report delta
PR #20420. New head is
29fd4591e, pushed without force. The previous head wasd43666c1.1. Merge (the
dirtystate is cleared). I mergedorigin/main50e273fdwithscripts/pm/os-regen-merge.sh:2e86a7cd, then regeneration commitfa5631fb, which discharged the os-regen deferral.packages/spec/src/type-alias-convention.pin.test.ts. I resolved it by stacking both intents: main's783 -> 780connector-retirement receipt, then this card's re-based780 -> 781. The count was recomputed from the merged file: 781export type Iso_lines = main's 780 +Iso_ui_component__ElementDefinitionListPropsSchema.content/docs/references/index.mdxand the strictness-ledger counts were regenerated, not hand-edited.origin/main, the four generated shards (api-surface,authorable-surface,export-origins,declaration-mapui.json) differ by additions only (+124/-0). The reference index differs only by this card's +6 schemas (1516 -> 1522).2. CI red: Dogfood
expression-conformance. The six predicate surfaces were UNCLASSIFIED. I reproduced the failure locally (1 failed / 6 passed) and added three ADR-0060 rows topackages/qa/dogfood/test/expression-conformance.ledger.ts. They are split by fault face; I read the renderer andSchemaRenderersource at pinf8a9d0fb0596but did not run them. Each surface is evaluated by the renderer and also bySchemaRenderer's node gate on the hoisted key.cel-action-block-visible-closedcoversActionButtonPropsSchema.visibleandActionMenuPropsSchema.visible. It is fail-closed: the renderer evaluates withuseCondition(..., { throwOnError: true }), so a faulting predicate hides the block.cel-action-block-visible-softcoversActionIconPropsSchema.visibleandActionGroupPropsSchema.visible. It is fail-soft-log: neither leg throws, both answertrue(shown), and the node gate is the leg that reports the fault.cel-action-block-disabledcoversActionButtonPropsSchema.disabledandActionIconPropsSchema.disabled. It is fail-closed: both legs answertrueon a fault, and on an un-negated enablement leg that greys the button out.cel-action-disabledrow. That row cites an empty-disabledfix, not a faulting predicate; it is not changed here.All six surfaces are really evaluated, so no row key needed to change. The first version of the rows carried objectui tracker ids inside strings, and
check:doc-authoringwent red on it. I moved those ids to//comments; the baseline is untouched.3. Readings.
fa5631fb(spec sources are identical at29fd4591):pnpm --filter @objectstack/spec typecheckpnpm --filter @objectstack/spec testpnpm --filter @objectstack/spec test:repo29fd4591:pnpm --filter @objectstack/spec run check:generatedpnpm check:spec-parsed-aliaspnpm --filter @objectstack/spec run check:api-surfacepnpm --filter @objectstack/spec run check:authorable-surfacepnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-component-props.test.ts src/validate-component-types.test.ts src/page-envelope-audit.test.tspnpm exec vitest run --maxWorkers=2 test/expression-conformance.test.ts, run inpackages/qa/dogfood(after building the@objectstack/verifyclosure)pnpm check:doc-authoringpnpm check:nul-bytespnpm check:issue-citationspnpm check:cross-package-test-inputsapi-surfacegrep, run atfa5631fb:ui.json:XPropsSchema (const)= 1 andXProps (type)= 1 for each, plus the 5XPropsParsedaliases = 1 each.data.json: all 11CrossField*/CROSS_FIELD_*/crossField*entries = 1 each.integration.jsonconnector-resilience exports = 0.ObjectTimelinePropsSchema= 1,ElementRepeatrPropsSchema= 0,ConnectorSchema(integration) = 1.4. For the seat: PR body text, not written by me. My role file forbids patching a PR body after creation, so the round's two writes are the push and this comment. Please add this bullet under "Surface beyond the claim, and why":
Writes this round:
git pushof2e86a7cd/fa5631fb/29fd4591, plus this comment through the relay. MCP calls: 0.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Review: ACCEPT · PR #20420 at head
29fd4591e4· 2026-09-28T11:04Zdomain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW), reviewer of record under claim5864633609. Dev report5867890885and patch-round delta5868569396. The seat took these readings on GitHub or onorigin/main.main. The first body line isFixes #20371, the only closing keyword. The merge conflict is cleared:mergeable_statereadsblocked(draft), notdirty.component.zod.tsand their test file. The rest is generated (api-surface, authorable-surface, declaration-map, export-origins, json-schema manifest, references, strictness counts) plus three gate-forced companions, each named in the PR body's "Surface beyond the claim": thedropped-refinementsbaseline entry (the build refuses to publishElementRepeaterPropswithout it); the one isomorphic alias pin (count 780→781 after the stacked merge); and three ADR-0060 rows inpackages/qa/dogfood/test/expression-conformance.ledger.ts(see CI). The seat amended the PR body's section with the third, from the delta.@objectstack/specminor, carryingClause-②: yes(six new rows and two newelement:members widen the accepted surface).check-governed-merges.mjs --pr 20420: not governed; 1,367 changed lines.fa5631fb,Dogfood Regression Gate (3/3)failed onexpression-conformance.test.ts: the six newvisible/disabledpositions were unclassified. That failure belongs to this PR. The patch adds three rows split by fault face, read at the objectui pin: button/menuvisiblefail-closed, icon/groupvisiblefail-soft-log, button/icondisabledfail-closed. The dev reproduced the failure locally and re-ran it green. CI on29fd4591is running; the landing window judges it.Flags carried to the at-tier review (③):
open_questions[0]:nameonaction:group/action:menu. The dev chose A: declare it only where it is read (button and icon), and haveaction:grouprefuse the registration-publishednamewith a prescription. The seat agrees under triage's execution note 1 ("measure each row from the renderer's read points").disabledrow reads fail-closed, opposite to the existingcel-action-disabledledger row. The dev records the reason (that row cites an empty-disabledfix, not a faulting predicate) and leaves it unchanged.content/docs/protocol/objectui/layout-dsl.mdxnamesComponentPropsMap. The reviewer judges whether that page's claim about the map now needs the six rows.Out of scope (report
out_of_scope_findings):action:grouppublishes an unreadnameand an unmappedmdsize; thedefinition-listcolumnsenum is strings while the renderer compares a number;element:repeateradvertises an unrenderedfields[].label). Carrier: the objectui follow-up the seat files at landing,Blocked-by: #20371.KNOWN_COMPONENT_TYPESdocblock is now incomplete (it still says the string-arm rows are "exactly"element:metadata_viewer, the plugin widgets andobject-*). Acceptance notes, for the next edit ofcomponent-type-vocabulary.ts.objectNamewith the next pin bump. Acceptance notes.action:menu's rest-spread order. Read-only inference in objectui, not reproduced: Acceptance notes.filterprescription. Acceptance notes.Next:
needs:contract-reviewgoes on PR #20420 in this act, and the isolated at-tier review starts now.objectstack-fleet commentedon Sep 28, 2026
os-dev-report delta
PR #20420 is merged with the latest
mainand the reference docs are regenerated. New head is28681fc5b, pushed without force. The previous head was29fd4591. This round changed no source file.1. Merge and regeneration.
bash scripts/pm/os-regen-merge.shmergedorigin/maine4d3f2ca6, which includes docs(spec): generated reference pages follow the docs title rule, sidebar labels kept via navTitle #20401. The merge commit isded6f2066. Git completed the merge itself, with no conflict in any hand-written file.content/docs/references/index.mdxandcontent/docs/references/ui/component.mdx.MERGE_HEAD), withpnpm --filter @objectstack/spec check:generated --fixunder the verify lock. It built spec, found 1 of 15 artifacts stale (check:docs), rangen:docs, and re-checked it green. The regeneration commit is28681fc5b; the os-regen marker is cleared.29fd4591to28681fc5also moved onmain. The branch still differs fromorigin/mainin the same 14 files as before.2. #20401's title rule is kept, and the six rows are present. Checked by grep:
ui/component.mdxfrontmatter readstitle: Component schema — UI Protocol referenceandnavTitle: Component, the same asorigin/main. Before docs(spec): generated reference pages follow the docs title rule, sidebar labels kept via navTitle #20401,e4d3f2ca6^, it readtitle: Component, so the new rule is what survived. Only thedescriptioncount differs: 37 more on main, 43 more on the branch.index.mdxfrontmatter readstitle: Protocol reference — every schema by moduleandnavTitle: Protocol Reference, the same asorigin/main. Its only diff from main is this card's +6 schemas: 1516 -> 1522 in total, and 159 -> 165 under UI.ui/component.mdx:## ActionButtonProps,ActionGroupProps,ActionIconProps,ActionMenuProps,ElementDefinitionListPropsandElementRepeaterPropseach appear once. Controls:## ObjectTimelinePropsappears once, and## ElementRepeatrPropsdoes not appear.git diff origin/main --stat -- content/docs/references/shows two files:index.mdx(10 lines) andui/component.mdx(+176/-8, the six sections plus the import and description lines).3. Readings at
28681fc5.pnpm --filter @objectstack/spec run check:generateddistthis round's--fixbuilt from the merged treepnpm --filter @objectstack/spec run check:docspnpm --filter @objectstack/spec run check:api-surfacepnpm --filter @objectstack/spec run check:authorable-surfacepnpm --filter @objectstack/spec typecheck, under the lockpnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/ui/component-action-element-rows-20371.test.tspnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/expression-conformance.test.ts@objectstack/verifyclosure under the lockWrites this round:
git pushofded6f2066and28681fc5b, plus this comment through the relay. MCP calls: 0.Generated by Claude Code
fix(spec): re-measure the six #20371 rows at objectui dd3f7e1be and d…
objectstack-fleet commentedon Sep 28, 2026
os-dev-report
{ "issue": 20371, "status": "done", "round": "patch round 4: pin-citation re-measurement at objectui dd3f7e1be", "branch": "claude/issue-20371-component-props-action-element-rows", "pr": "https://github.com/objectstack-ai/objectstack/pull/20420", "session": "session_01B3TqpoQbTAfG7G74GMDWNW (parent's, per the claim)", "premise_still_valid": true, "head_before": "28681fc5baabf169602c2de284b3ab99ebeeba1d (confirmed before any work)", "head_after": "5e50899a481dce659bc12ad2576b4fc534cd3893 (git ls-remote reads the same; PR still draft, auto_merge null, per a REST read)", "commits": [ "abf69011a Merge origin/main (3cf644938, the pin bump) via scripts/pm/os-regen-merge.sh; no conflict in any hand-written file", "c628e1d02 chore(spec): regenerate authorable-surface on the merged tree", "3ac9125cf fix(spec): re-measure the six rows at objectui dd3f7e1be and declare objectName", "5e50899a4 chore(spec): regenerate the authorable surface and reference docs for objectName" ], "summary": "I did not replace the sha by itself. Every read point the six rows cite was re-derived from the files at objectui dd3f7e1be3561d63267d7162f3fc0ac52e72834d (scratch clone), and each anchor and the sha were updated together. The two asserting citations (component.zod.ts:2686 section 4b, :5398 map rows) now read `.objectui-sha` = `dd3f7e1be`; section 4b also keeps the first measurement in the historical form (`.objectui-sha` pin `f8a9d0fb0`). One read point changed meaning: the new pin forwards `objectName` to the runner, so action:button and action:icon now declare `objectName: z.string().optional()`. That is the ActionDef scalar (`SpecActionInput['objectName']`, ActionRunner.ts:406), and it matches every sibling row's object-name keys. action:group and action:menu forward `objectName` only per MEMBER, so it rides the member object (a record-typed list the rows do not judge) and the container rows gain no key. `params` keeps its meaning on the page path, so its value schema is unchanged. No read point died. The merge moved neither the type-alias pin count (781) nor the dropped-refinements totals (212/610). The changeset stays minor, and its text now covers objectName.", "key_changes": [ "ADDED action:button.objectName (z.string().optional()): forced by action-button.tsx:307 `objectName: (schema as any).objectName` in the `forwarded: ActionDef` literal (:201-308). The comment at :295-306 says the console resolves its dispatch target as action.objectName, falling back to the page object.", "ADDED action:icon.objectName (z.string().optional()): forced by action-icon.tsx:195, the same forward in its literal (:134-196).", "NOT ADDED on action:group / action:menu: action-group.tsx:323 and action-menu.tsx:313 read `(action as any).objectName` off each MEMBER in handleExecute, never `schema.objectName`. The row test pins this: a container-level objectName is refused, and one on a member parses.", "UNCHANGED params (z.unknown()), with its reading re-derived: action-button.tsx:176-179 / action-icon.tsx:130-133 forward an array `schema.params` as actionParams, and take static values from `readStaticParamValues` (static-params.ts:91-101), which reads `properties.params` when it is an object (isConfigBag excludes arrays). Because the row IS the properties bag and SchemaRenderer hoists it (the same object, so no warning), a page-authored array is still the input list and a page-authored object is still the static values. Only a node-level object outside `properties` changed: it is now ignored, with a development warning. The row never judged that position.", "UNCHANGED element:repeater.filter: data-list.tsx:119-120 now passes the rules through `useResolvedFilter` (objectui#10666), which resolves context tokens such as `{current_user_id}` before `$filter` (:152). The value is still the ViewFilterRule array, and a string `value` already admits a token. Noted in the docblock; no schema change.", "UNCHANGED key sets otherwise: the container keys of group and menu, and the definition-list and repeater key sets, are identical at the new pin. The node-gate evaluators (SchemaRenderer evaluateVisibilityPredicate / evaluateEnablementPredicate / isDisabled) are code-identical across the hop, and useExpression.ts changed in comments only.", "TEST: component-action-element-rows-20371.test.ts adds objectName to FORWARDED and one new pin (objectName on button/icon; refused at container level; accepted on a member), so it now has 45 tests (was 44)." ], "citation_deltas": { "action:button (components/src/renderers/action/action-button.tsx)": [ "name :118 -> :119 (diagnostic label), forward :188 -> :212", "label :346 -> :383 (React child); also :118 -> :119, :192 -> :216", "icon :133 -> :134", "actionType :187 -> :211", "variant / size :136 / :137 -> :137 / :138", "visible :116 + :298 -> :117 + :335", "disabled :129 + :334 -> :130 + :371", "params :153-155 -> :176-179 (now via readStaticParamValues, static-params.ts:91-101)", "forward block :187-270 -> :211-307", "registration inputs :358-379 -> :395-416", "objectName: new, :307" ], "action:icon (action-icon.tsx)": [ "size-not-read :106 -> :107", "no undoable/recordIdField, forward literal :119-174 -> :134-196", "label :137 -> :152, :243 -> :265, :252 -> :274, :258 / :264 -> :280 / :286", "description tooltip :264 -> :286, forward :138 -> :153", "visible :96 + :207 -> :97 + :229", "disabled :101 + :236 -> :102 + :258", "variant :105 -> :106", "forwards :132-173 -> :147-195", "params: new citation :130-133", "objectName: new, :195" ], "action:group (action-group.tsx)": [ "actions :243 -> :248; location filter :244 -> :249; members inline :79-132 -> :81-134, dropdown :164-202 -> :166-204; forward :248-287 -> :274-324 (static values :274-280, member objectName :323)", "display :309 -> :346", "label / icon :328 / :313 -> :365 / :350", "variant :319, :360 -> :356, :397", "size :320, :361, :89 -> :357, :398, :91", "visible :233 + :306 -> :238 + :343", "unread name, registration :378 -> :415" ], "action:menu (action-menu.tsx)": [ "actions :299 -> :328; members :78 -> :80, :106-145 -> :108-147, :360 -> :389; ActionAutoTrigger :320-327 -> :349-356; forward :237-285 -> :253-314 (static values :253-259, member objectName :313)", "label :341 -> :370, :350-351 -> :379-380", "icon :224 -> :229", "variant / size :225 / :226 -> :230 / :231", "visible :219 + :293 -> :224 + :322", "registration inputs :381-390 -> :410-419" ], "element:definition-list (basic/data-list.tsx)": [ "every renderer anchor unchanged (readProps :42-47, items :48, columns :49, empty :51-53, inline :63, term :66, description :68, registration enum :82)", "designer previews/block-config.ts columns :306 -> :307" ], "element:repeater (basic/data-list.tsx)": [ "readProps :97-107 unchanged", "object :122, :134 -> :143, :155; never-queries :122-125 -> :143-146", "filter :113, :131 -> :120, :152 (resolved through useResolvedFilter, :119-120)", "sort :132 -> :153", "limit :133 -> :154", "emptyText :160 -> :181", "divided :165 -> :186", "titleField :170-171 -> :191-192", "fields :116, :173-175 -> :128, :194-196; field-only read :175 -> :196", "adapter data-objectstack/src/index.ts :4793-4804 -> :4782-4793 and serializeOrderBy :759-785 -> :760-786 (text byte-identical)" ], "section 4b and map-row evidence": [ "public-blocks.ts :109-114 -> :117-122; element pair :109-110 -> :117-118; action quartet :111-114 -> :119-122", "registration data-list.tsx :75 unchanged, :184 -> :205", "palette block-types.ts :129-130 -> :136-137", "inspector block-config.ts :282-316 -> :283-317", "ui/button.tsx :19-35 and auto-trigger.ts :18-19 unchanged", "the section 4b bullet 'objectName: not read at the pin' is replaced by a paragraph that declares it with the four anchors above", "actionMemberList docblock: members now also carry objectName and properties.params static values (readMemberStaticParamValues, static-params.ts:142-148)" ], "dogfood ledger (packages/qa/dogfood/test/expression-conformance.ledger.ts, 3 rows; historical form now `.objectui-sha` pin `dd3f7e1be356`, first read at f8a9d0fb0596)": [ "action-button.tsx:116-119 -> :117-120, :298 -> :335, :129 + :333-339 -> :130 + :370-376", "action-menu.tsx:219-222 -> :224-227, :293 -> :322", "action-icon.tsx:96 -> :97, :197-199 -> :219-221, :101 + :235-241 -> :102 + :257-263", "action-group.tsx:233 -> :238, truthiness :306 -> :343", "SchemaRenderer.tsx evaluateVisibilityPredicate :1048 -> :1157, evaluateEnablementPredicate :1138 -> :1247, :1138-1165 -> :1247-1274, :1149-1156 -> :1258-1265, isDisabled :1772-1784 -> :1879-1891 (ranges diffed identical); :469 unchanged", "useExpression.ts:200-244 / :215-238 and ExpressionEvaluator.ts:387-408 unchanged", "failPolicy of all three rows unchanged: the evaluators are code-identical across the hop" ] }, "tests": "All exit codes were written to disk before any output was read, at HEAD 5e50899a4 (source identical to 3ac9125cf). Under scripts/pm/os-verify-lock.sh: (1) build of the @objectstack/verify closure minus spec, exit 0; (2) `pnpm --filter @objectstack/spec test` (local): 565 files, 16684 passed, 1 todo, exit 0; (3) spec `test:repo`: 37 files, 684 passed, exit 0; (4) spec `typecheck`: exit 0 (tsc, scripts-typecheck, test-typecheck: 251 errors / 138 pinned signatures held); (5) lint suite (`exec vitest run --maxWorkers=2`): 115 files, 5331 passed, exit 0; (6) metadata-core suite: 16 files, 289 passed, exit 0; (7) dogfood `test/expression-conformance.test.ts`: 7 passed, exit 0; (8) the row test alone: 45 passed, exit 0. Pin gate: `check:objectui-pin-citations` exit 0 ('49 asserting objectui pin citation(s) match .objectui-sha (dd3f7e1be), 61 historical'); `--verify-anchors` with OBJECTUI_ROOT at the dd3f7e1be scratch clone exit 0 ('7 anchor content assertion(s) verified against objectui at dd3f7e1be'). My anchors carry no quoted first line, so that mode checks their sha label, not their content; the content was re-read by hand, file by file. `check:generated --fix` (built spec in 276s) rewrote exactly two artifacts: authorable-surface/ui.json (+2 lines, ActionButtonProps:objectName and ActionIconProps:objectName) and content/docs/references/ui/component.mdx (+2). The re-check then read 'All 15 generated artifacts are up to date'. `node scripts/check-sdui-manifest.mjs` exit 0 ('recorded at the live objectui pin dd3f7e1be356'). Type-alias pin count 781 and dropped-refinements 212/610 are unchanged, and spec test holds both. No eslint run this round: component.zod.ts / the row test / the ledger are CI's `pnpm lint` population, and the round-1 narrowing proof still applies (no type-aware linting).", "gates": "dispatch-gates at 5e50899a4 (no paths, merge base 3cf644938): 108 derived commands. I ran 104 unlocked and 4 locked, with a runner that records each exit code before its output is read. Result: 107 exit 0, including check:generated, check:api-surface, check:authorable-surface, check:docs, check:objectui-pin-citations, check:spec-parsed-alias, check:doc-authoring, check:issue-citations, check:nul-bytes, check:strictness-ledger, check:liveness, check:dts-closure and check:type-check-debt (this time to a verdict: '4 ledger entr(ies) re-measured in 590.5s … none above its recorded number'). check:skill-examples first exited 3 (PREREQUISITE NOT MET: no client-react .d.ts). After building the client-react closure minus spec, it exited 0. NOT MEASURED: `pnpm check:dual-build-cjs-loads`, exit 3 PREREQUISITE NOT MET (it reads every workspace package's dist/, a whole-repo build; this diff changes only @objectstack/spec's output). `--ran` verdict: '108 derived famil(ies) accounted for — 107 run, 1 NOT-MEASURED'. CI convergence is the seat's to read. origin/main has since moved 10 commits past the merge base; none of them touches any file in this diff.", "pr_body_delta": [ "Heading 'Read points: measured at the pin (`.objectui-sha` `f8a9d0fb0596`)' becomes 'Read points: measured at the pin (`.objectui-sha` `dd3f7e1be356`)'. Add one sentence under it: 'First measured at `f8a9d0fb0596`; after #20436 moved the pin, every read point was re-derived at `dd3f7e1be356` (2026-09-28) and re-anchored with the sha.'", "Read-points table, action:button row: in 'Declared keys', '+ 20 keys forwarded' becomes '+ 21 keys forwarded' and gains `objectName`. Anchors: `action-button.tsx:118` -> `:119`, `(:136-137)` -> `(:137-138)`.", "Read-points table, action:icon row: `:106` -> `:107`, `(:119-174)` -> `(:134-196)`. Declared keys still 'same as the button, minus size, undoable, recordIdField', so it now includes objectName.", "Read-points table, action:group row: `(:243)` -> `(:248)`, `(:378)` -> `(:415)`, `(:320)` -> `(:357)`, `(:361, :89)` -> `(:398, :91)`.", "Read-points table, action:menu row: `(:225-226)` -> `(:230-231)`. Definition-list `data-list.tsx:49` is unchanged.", "Replace the paragraph 'Pin vs objectui origin/main (9f0c84a)' with: '**objectName, carried by the new pin.** At `dd3f7e1be356`, action:button and action:icon forward `objectName` to the runner (`action-button.tsx:307`, `action-icon.tsx:195`), and the console dispatches to that object instead of the page object. Both rows declare `objectName` as the ActionDef string scalar. action:group and action:menu forward 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 from `properties.params` (objectui#10289, `static-params.ts:91-101`). On a page node that is the row itself, so `params` keeps its meaning: an array is the input list and an object is the static values. Its value schema is unchanged.'", "Acceptance notes: delete the bullet '`objectName` on the four action rows is to be added with the next `.objectui-sha` bump…', which this round discharged. The other bullets stand.", "Tests: replace the lead line with 'The readings below are at HEAD `5e50899a4` (after merging `origin/main` at `3cf644938`, the pin bump)'. Update the readings: spec test **565 files, 16684 passed**, 1 todo; the row test **45 tests** (was 44); test:repo **37 files, 684 passed**; typecheck exit 0; lint **115 files, 5331 passed**; metadata-core **16 files, 289 passed**; dogfood expression-conformance **7 passed**. Mark the cli unit-layer bullet 'not re-run this round (round-1 reading)'. The eslint bullet stands (round-1 narrowing, no type-aware linting).", "Gates: 'The derived union at `5e50899a4` is 108 commands.' '**107 exit 0**', adding check:dts-closure and check:type-check-debt (this time to a verdict: 4 ledger entries re-measured, none above its number). check:dual-build-cjs-loads stays NOT MEASURED (exit 3), and the check:type-check-debt NOT MEASURED bullet is deleted. `--ran`: '108 derived famil(ies) accounted for — 107 run, 1 NOT-MEASURED'. The sdui-manifest line reads 'recorded at pin `dd3f7e1be356`'. Add: '`check:objectui-pin-citations` exit 0 (49 asserting citations match `dd3f7e1be`); `--verify-anchors` against a `dd3f7e1be` clone exit 0.'", "Surface beyond the claim, dogfood bullet: append 'Re-anchored at `dd3f7e1be356` in round 4; the fault faces are unchanged because the evaluators are code-identical across the hop.'", "Downstream: the read-but-unpublished counts become 22 on action:button (was 21) and 20 on action:icon (was 19), both because of objectName.", "Changeset section: unchanged ('minor'). The changeset file itself now names objectName on the button and icon and states that group and menu carry it per member." ], "mcp_calls": "0", "api_writes": "1 relay write: post-stamped -> POST /repos/objectstack-ai/objectstack/issues/20371/comments (this report), through the fleet-write relay. Plus git push (not REST): two pushes, c628e1d02 and then 5e50899a4, neither forced. Reads only otherwise: one unauthenticated GET /repos/objectstack-ai/objectstack/pulls/20420 for the live body and head. PR body, labels, draft state and auto-merge were not touched.", "deviations": [ "The order says 'declare objectName on the four action rows'. The renderer read declares it on two: group and menu forward `objectName` only per member (anchors above), so a container-level key would be read by nothing. The row test pins the refusal.", "No quoted first lines were added to my anchors, and ASSERTED_ANCHOR_FLOOR is unchanged. Raising coverage would mean editing the gate script and running its own suite, which is outside this round. This remains an option for the seat." ], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed. Read-only inference from the renderer, not reproduced: action:group/action:menu call readActionEntryParamValues for a member whose params is an object. For type 'api' that still returns the object as the request payload until 18 (static-params.ts:162-182). No change is needed to this row, because members are not judged here." ], "cleanup": "Done: rm -rf the worktree's node_modules, then `git worktree remove /home/user/objectstack-issue-20371` (not forced), exit 0, after git status read clean and the remote head read 5e50899a4. The scratch objectui clones (f8a9d0f, dd3f7e1) were deleted too. No background process of mine is left running; every locked run had its VERDICT read." }Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Close-out by the
domain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW) at 2026-09-28T16:25Z.Delivered. PR #20420 merged as
75b216924through the merge queue. This card closedcompletedon the PR'sFixesline.pm:dispatchedis removed.ComponentPropsMapgains strict rows foraction:button,action:group,action:menu,action:icon,element:definition-listandelement:repeater. The twoelement:types are admitted to the vocabulary through their rows..objectui-shadd3f7e1be356after PR chore(objectui): bump the console pin to dd3f7e1be356 (carries the injected-client boot fix) with the showcase div→box and trash-icon follow-through #20436 moved the pin mid-queue (round 4; audit comment5871523278).objectNameis declared onaction:buttonandaction:icon.5873778605PASS at5e50899a.What remains, and who carries it.
domain:ui). It waits for an installable@objectstack/specrelease carrying these rows. The landing note, with the producer-side findings re-read at the new pin, is posted there.cel-action-disabledmay be mis-measured: [finding] the dogfood ledger rowcel-action-disabledmay be mis-measured:action:group/action:menumembers evaluate a registered action'sdisabledwithoutthrowOnErrorand grey out on a fault #20470, filed from the at-tier records.--verify-anchorschecks none of their content: [finding] the sixComponentPropsMaprows #20371 added cite about a hundred objectui anchors by line number alone: no quoted first line, so--verify-anchorschecks none of their content #20471.component-type-vocabulary.ts'sKNOWN_COMPONENT_TYPESdocblock still says "exactly" the string-arm rows. This is prose drift, and its carrier is the next edit of that file. Noted here, not filed.Generated by Claude Code
feat(spec): ComponentPropsMap rows for action:button/group/menu/icon …
fix(types,cli): element:number validates, with the spec's dataSource …