Skip to content

finding(plugin-detail): DetailSection renders section.icon, but detail-section's authoring surface declares no icon — one affordance the renderer honours and no author can reach #9529

Description

@os-tesla

Dedup keywords: detail-section · DETAIL_SECTION_NODE_INPUTS · section.icon · DetailViewSection · undeclared-input

Filed by the domain:ui execution seat while re-measuring objectui#8626 before closing it. ⛔ Unassigned and ungraded — grading is triage's. This is not objectui#8626's defect (that one was the flat-versus-section shape mismatch, repaired by PR objectui#9221) and it is not objectui#9218 (showBorder inert in the collapsible branch). It is the gap objectui#9221's acceptance notes recorded as "Noted, not filed"; this card is the filing, with the measurement redone.

The measurement

At origin/main bbc9dc3.

DetailViewSection (packages/types/src/views.ts) carries eleven members. DETAIL_SECTION_NODE_INPUTS (packages/plugin-detail/src/DetailSectionNode.tsx) declares eight — and the registration at packages/plugin-detail/src/index.tsx:352 publishes exactly those eight as the node's inputs. Undeclared: name, icon, visible.

Reads counted off the section object in packages/plugin-detail/src/DetailSection.tsx, with every declared member carried as a lit control in the same pass:

member section.<m> reads declared?
title 4 yes
description 4 yes
fields 8 yes
collapsible 2 yes
defaultCollapsed 1 yes
columns 1 yes
showBorder 3 yes
headerColor 2 yes
icon 2 ⛔ no
name 0 no
visible 0 no

⇒ eight non-zero controls in the same grep pass, so the two zeros are readings, not a dead pattern.

⭐ So the gap is exactly ONE member, not three

PR objectui#9221's note named all three undeclared members together. Re-measured, only icon is both read by the renderer and unreachable by an author. name and visible are undeclared and unread here, so they carry no authoring gap at this block — whatever else may be true of them, they are not this finding.

Why it matters

The inputs array is not documentation. packages/components/src/renderers/layout/page.tsx builds the JSX-page compiler's manifest from getKnownTypes() plus these same inputs, and sdui-parser's validateTree judges an authored page against it. ⇒ the platform's own validator tells an author the eight names are the correct and complete way to write this block, on the one surface where the answer is machine-read — while the renderer honours a ninth.

⚠️ The failure direction is the quiet one: an author who writes icon is warned, not silently dropped (per objectui#9221's reading), so nothing breaks and nothing renders. The affordance simply cannot be reached.

⛔ Not pre-judged

This is a decision to WIDEN a published authoring surface, which is a product question and ⛔ not this seat's:

  1. Declare it — add { name: 'icon', type: 'string' } to the registration and to DETAIL_SECTION_NODE_INPUTS, making the renderer's affordance reachable.
  2. Stop reading it — delete the two section.icon reads, making the declaration complete instead. ⚠️ In-repo callers use DetailSection directly (not through the node), so they may be authoring icon today; measure that before choosing this.
  3. Leave it — accept that the node surface is deliberately narrower than the component's, and say so in the registration so the next reader does not re-file this.

⇒ option 2 is a behaviour change for direct callers and option 1 is new published surface (clause ②). Neither is mechanical.

Reproduction

  1. git show origin/main:packages/plugin-detail/src/DetailSectionNode.tsx | grep -A10 DETAIL_SECTION_NODE_INPUTS — the eight.
  2. git show origin/main:packages/types/src/views.ts | awk '/interface DetailViewSection[ <{]/,/^}/' — the eleven.
  3. git show origin/main:packages/plugin-detail/src/DetailSection.tsx | grep -c 'section\.icon\b' — 2. Run the same count for the eight declared names as controls.

Related

  • objectui#8626 — the card this came out of; its own defect is repaired and it is being closed.
  • objectui#9218 — DetailSection's collapsible branch never reads section.showBorder. Different member, different mechanism, also filed from objectui#9221.
  • objectui#6955 — narrowed headerColor's value vocabulary; same eight lines, different question.

Filed by the domain:ui execution seat · session session_011QreXiyMEqKLN4U5daMPVa — attribution as prose because a footer block is stripped from issue bodies on creation.


Generated by Claude Code

Activity

  1. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 14, 2026
  2. hotlong commented on Sep 18, 2026

    @hotlong
    Contributor

    Ruling: batch #152 item 6 · letter 1 (declare icon on detail-section's node inputs — { name: 'icon', type: 'string' } in DETAIL_SECTION_NODE_INPUTS and the registration; the renderer already honours it) · maintainer 「其他同意」 2026-09-18T03:59Z

    Director seat, summon #24, session_01Wj1HUjzyeiBQ8atRf1ZhaL. Presented in detail with the recommendation 1 (fallback 3); the maintainer agreed. Facts (this card; triage 5707817020; seat re-check): DetailViewSection carries eleven members, the node's inputs publish eight; section.icon is read twice by DetailSection.tsx and reachable by no author (the validator warns), while name and visible are undeclared and unread — the gap is exactly one member. DetailView.tsx writes icon itself in three places through direct DetailSection use, so the capability is implemented and in use; the node face is the only place it is missing. inputs is not documentation: validateTree judges an authored page against it, so the platform's own validator tells authors the eight names are complete while the renderer honours a ninth.

    Ruling — 1

    • DETAIL_SECTION_NODE_INPUTS and the registration at packages/plugin-detail/src/index.tsx gain { name: 'icon', type: 'string' } with a one-line description; sdui-intrinsics and the generated manifest follow by regeneration. Pin: an authored icon on a detail-section node validates and renders (red on the current head).
    • Authoring surface widens by one key that already works ⇒ Clause-②: yes in the changeset (minor); domain:ui — no at-tier review outside the spec/skills lanes (batch Add automated display issue detection for component renderers #151 item 3).
    • ⛔ 2 — breaks the view's own direct use; ⛔ 3 — keeps a declaration that calls itself complete while the renderer disagrees.

    Four-facet reading: ① a section icon is a standard layout affordance the platform already implements; ② declaration equals implementation; ③ the validator stops steering an author's AI away from a key the renderer honours; ④ one line.

    Execution

    needs-user-decision → pm:queue; domain:ui, priority:p3, plugin stay.


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 — domain:ui execution seat
    Session: session_01BA3nKVUwKQJf8DBxrSVtNC
    Branch: claude/issue-9529-detail-section-declares-icon
    Worktree: objectui-issue-9529
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: packages/plugin-detail/src/DetailSectionNode.tsx (DETAIL_SECTION_NODE_INPUTS gains icon), the detail-section registration in packages/plugin-detail/src/index.tsx, a pin beside the existing packages/plugin-detail/src/__tests__/detailSectionAuthoredNode-8626.test.tsx, any checked-in text reader that pins the node's input list (update it, ⛔ never weaken it), and one .changeset/9529-…md (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (default judgement tier) — priority:p3: a renderer-honoured key the node's declared inputs omit, so the validator steers authors away from it
    Clause-②: yes
    Thread-read: 5724939941
    Serial constraints cleared: open-PR file lists read 2026-09-25T03:58Z ⇒ none touches DetailSectionNode.tsx or plugin-detail/src/index.tsx. Live pm:dispatched claims read at the same time ⇒ none names them or plugin-detail.

    Scope

    The director ruling 5724939941 (batch #152 item 6, letter 1; maintainer 「其他同意」):

    • DETAIL_SECTION_NODE_INPUTS and the registration at packages/plugin-detail/src/index.tsx gain { name: 'icon', type: 'string' } with a one-line description. sdui-intrinsics and the generated manifest follow by regeneration (they are build outputs of packages/sdui-parser/scripts/gen-manifest.ts; commit one only if it is checked in).
    • Pin: an authored icon on a detail-section node validates and renders. It is red on the current head.
    • The authoring surface widens by one key that already works ⇒ Clause-②: yes, and the changeset is minor. Per the ruling, no at-tier review is owed in domain:ui (batch Add automated display issue detection for component renderers #151 item 3); the seat reviews.
    • ⛔ name and visible stay undeclared: they are unread.

    ⇒ Fixes #9529.

    domain:ui seat #1 · session_01BA3nKVUwKQJf8DBxrSVtNC · claim · 2026-09-25T04:00Z

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 9529,
      "status": "done",
      "branch": "claude/issue-9529-detail-section-declares-icon",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10484",
      "session": "session_01BA3nKVUwKQJf8DBxrSVtNC — subagent, so the parent PM session; the harness-stamped Claude-Session line on both commits names it",
      "premise_still_valid": true,
      "summary": "Implemented ruling 5724939941 letter 1: the detail-section registration in packages/plugin-detail/src/index.tsx and DETAIL_SECTION_NODE_INPUTS each gain icon ({ name: 'icon', type: 'string' } with a one-line description on the registration), so the validator stops warning on an authored icon and DetailSectionNode folds it into the section DetailSection draws; changeset is minor on @object-ui/plugin-detail with the Clause-② yes line. Mechanism assumptions measured: #1 held (the registration RESTATES the list, because registration entries carry type/required/enum/description while the constant is the name list the fold iterates; the existing #8626 fold-parity row pins the two in both directions). #2 held on the node path. #3 held for icon but was FALSIFIED for the proposed control: visible is in the validator's BASE_PROPS and draws nothing on base or head, so the lit control is name. #4: no generated artifact is checked in, and detail-section is not in getPublicConfigs(), so there was nothing to regenerate. #5: no text reader outside plugin-detail enumerates detail-section; none was edited or weakened. The card's 'exactly one member' premise has since moved: objectui#8603 restored section.hideEmpty, which DetailSection now reads and the node does not declare (a finding below). The ruling's own action is unaffected, so premise_still_valid stays true.",
      "tests": "All at final head 6b100e89a, from the repo root through os-verify-lock unless noted. New pin packages/plugin-detail/src/__tests__/detailSectionAuthoredIcon-9529.test.tsx, 5 rows: at base 274e14af4 'Tests 4 failed | 1 passed (5)' (validate x2 red on unknown-prop; render x2 red; the name control green); at head 'Tests 5 passed (5)'. pnpm exec vitest run packages/plugin-detail/ plus the console readers registry-inputs-spec-parity, ga-honoured-inputs-author-reach, public-contract and component-input-union-specimens: 'Test Files 208 passed | 1 skipped (209)', 'Tests 2270 passed | 8 skipped (2278)', VERDICT command-exit 0. Dependency closure: turbo run build --filter='@object-ui/plugin-detail^...' --concurrency=2 gave 'Tasks: 11 successful, 11 total' (at 37d66e3de; the later commit edits only test comments). pnpm --filter @object-ui/plugin-detail type-check: VERDICT command-exit 0, and tsc -p tsconfig.test.json --listFiles lists the new pin once. pnpm check:control-bytes OK (8544 files); pnpm check:new-line-citations '0 new citation(s) -> exit 0'; node scripts/check-changeset-presence.mjs pass (3 source files, 1 package, 1 changeset); check-changeset-no-major pass; pnpm check:changeset-claims exit 0 and report-only. It names two pending changesets that describe index.tsx (9280 record:highlights fields, 4998 record:alert action); both were read and are still true. check:doc-types, check:registry-bare-names, check:icon-record-names, check:test-path-roots and check:pending-changeset-literals all exit 0. check:component-surface-parity exits 0 and is report-only: it adds a detail-section.icon input-outside-keyset row beside seven existing detail-section rows, for the same reason (it sees no named read through the DetailSectionNode fold). NOT MEASURED: check:sdui-registration-pins, reason: exit 2 PREREQUISITE NOT MET (needs a console build), left to CI. ESLint narrowed and declared: eslint --no-inline-config --format json over the 3 touched files gives 3 linted, 0 errors. Per-rule counts equal base on both edited files (DetailSectionNode.tsx 1 react-refresh/only-export-components; index.tsx 29 react-refresh/only-export-components + 2 @typescript-eslint/no-explicit-any; base read with git show piped to eslint --stdin), and the new pin has 0 messages. Type-aware linting is off (tseslint.configs.recommended, no parserOptions.project or projectService), and the package has no own eslint config. Ablation used objectstack's scripts/ablation-replace.mjs --delete in WRAP mode, inside a wrapper with its own trap EXIT INT TERM that restores with git checkout HEAD -- on the absolute path and verifies blob equality plus an empty git diff HEAD. It ran on the committed implementation with no build in the path: the pin imports ../index relatively, and vitest aliases core/react/sdui-parser to src. Leg A (the dispatch's) deleted the icon entry from the registration inputs: anchor 1->0, blob f9a01c588->10eddd5d7. Both validate rows and #8626 fold parity went red; both render rows, the name control and the other six #8626 rows stayed green ('Tests 3 failed | 9 passed (12)'). Restored: blob f9a01c588 == HEAD, diff empty. Leg B deleted 'icon' from DETAIL_SECTION_NODE_INPUTS: anchor 1->0, blob 2612f9a8c->3e0dd79e3. Both render rows and fold parity went red; both validate rows and the control stayed green (3 failed | 9 passed). Restored: blob 2612f9a8c == HEAD, diff empty.",
      "mcp_calls": "0 — no MCP GitHub tool was called (reads went through the REST proxy)",
      "api_writes": "2 relay writes as objectstack-fleet[bot], each one POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, which became POST /repos/objectstack-ai/objectui/pulls with draft=true (PR #10484; run 36095429932 success; body read back byte-identical at 11070 bytes with 0 angle brackets); (2) this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/9529/comments via post-stamped --repo=objectstack-ai/objectui. Not REST: 3 git pushes (empty-branch probe, implementation, comment fix). Zero label writes. The PR body was written once and never PATCHed.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: c · evidence: at base 274e14af4, DetailSection reads section.hideEmpty === true (the all-empty hide, restored by objectui#8603 after this card measured at bbc9dc3, where the read was absent). The detail-section registration and DETAIL_SECTION_NODE_INPUTS omit hideEmpty. Probe against a page.tsx-style live manifest: an authored hideEmpty on the node draws warning unknown-prop, and DetailSectionNode passes it as a host prop DetailSection ignores, so it is dropped at render. Producer: an authored JSX/SDUI page typed against DetailViewSection, which declares hideEmpty. It is the same class as objectui#9529, which this PR closes, so file it as a sibling card on the same director-ruling route: widening is a product decision, and the omitted default differs between record:details and detail-view. Not touched here; the ruling names icon only. · dedupe words: detail-section · hideEmpty · DETAIL_SECTION_NODE_INPUTS · section.hideEmpty · undeclared-input",
        "carrier: 承接者:无 · .changeset/8626-detail-section-authored-node.md (pending) says in present tense that the registration 'declares eight FLAT inputs'. It publishes in the same release as this PR's changeset, which states the widening. Outside the claim's file surface, so not edited. Noted in PR #10484 Acceptance notes, not filed.",
        "carrier: 承接者:无 · ruling 5724939941 cites DetailView.tsx writing icon in three places through direct DetailSection use. Those three icon literals are action icons (share-2, square-pen, trash-2). The direct-path section icon comes from buildDefaultPageSchema passing a field group's icon to its section descriptors. The outcome is unaffected. Noted in the PR Acceptance notes, not filed."
      ],
      "deviations": "(1) The harness attribution reminder asks for a Co-Authored-By model trailer; the dispatch and os-dev contract say model-free trailers only. Followed the contract: commits carry only Claude-Session. (2) One tsc -p tsconfig.test.json --listFiles ran outside the verify lock (light; it only re-listed the test program). (3) One locked run chained type-check && listFiles | grep and printed VERDICT batch-last-exit. The chain reached the grep, so type-check had exited 0, and type-check was re-run alone on the final head: command-exit 0. (4) Dispatch mechanism assumption #3's control (visible) was falsified, and name replaced it."
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT — PR objectui#10484 at 6b100e89a · entering the merge queue once CI is green

    domain:ui seat #1, session_01BA3nKVUwKQJf8DBxrSVtNC. The seat read the dev report and the diff against the ruling 5724939941 and the claim 5826465177. Clause-②: yes, as the ruling states: the detail-section authoring surface widens by one key, icon, which the renderer already honoured. Per the same ruling, 「domain:ui — no at-tier review outside the spec/skills lanes (batch #151 item 3)」, so the seat's review is the record.

    Implemented-by:  claude/issue-9529-detail-section-declares-icon
    Reviewed-by:     session_01BA3nKVUwKQJf8DBxrSVtNC
    
    item reading
    shape draft · base main · Fixes #9529 · Clause-②: yes at line start
    the ruled change, exactly The registration's inputs gain { name: 'icon', type: 'string' } with a one-line description, and DETAIL_SECTION_NODE_INPUTS gains 'icon'. The registration restates the list rather than spreading the constant, because its entries carry type, required, enum and description while the constant is the name list the fold iterates. The existing objectui#8626 fold-parity row pins the two in both directions. ⛔ name and visible stay undeclared
    regeneration Nothing to regenerate. No generated manifest is checked in, and detail-section is outside the public block tier, so sdui.manifest.json and sdui-intrinsics.d.ts never carried it
    pin + ablation detailSectionAuthoredIcon-9529.test.tsx: an authored icon validates (no unknown-prop) and renders on the node path, both red at base (4 failed / 1 passed). The lit control is name, which still warns. The dispatch's control was visible, which the validator's base props accept, so the dev replaced it. Leg A, icon deleted from the registration: the validate rows and fold parity go red, and render and control stay green. Leg B, icon deleted from the node inputs: the render rows and fold parity go red, and validate and control stay green. Both legs restored by blob
    tests + gates plugin-detail plus the console registry-input readers: 208 files, 2270 passed. The closure build is 11/11 and the plugin-detail type-check is clean. eslint per-rule counts are equal to base. The changeset family passes. check:component-surface-parity is report-only: it adds one detail-section.icon row beside seven existing rows of the same kind. check:sdui-registration-pins needs a console build and is left to CI
    changeset minor on @object-ui/plugin-detail, stating the one widened key and that no exported symbol moves

    Dev findings — each one routed

    Maintainer veto window (named in the round report; landing is not held): an authored detail-section node may now carry icon, which is validated as a string and drawn before the section title.

    domain:ui seat #1 · review · 2026-09-25T04:54Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpluginpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions