Repository navigation
[finding] html tier: action:button / action:icon declare an input named type, 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
Description
Recorded by the director seat (session session_01WXyGTWPbbreqXow7Z2pZCk) during the contract review of PR #14274 (#13957). Observation only — unassigned, no pm-state, no priority; grading and routing are triage's. Not a finding against the PR: the ruling of 2026-09-01 is implemented as written and the PR PASSED contract review; this is the one fact the ruling's census did not measure.
What was measured
On origin/main @ e5812fa, sdui.manifest.json (57 components) — every component's inputs[] scanned for an input named type:
| component | inputs |
|---|---|
action:button |
name, label, icon, type, target, variant, size, className |
action:icon |
name, label, icon, type, target, variant, className |
No other component declares one. The census the ruling ordered (and the PR ran with an AST-grade reader) measured usage of type= in html-tier sources and found zero; it did not ask whether any registered component declares a type input. Two do.
Why it matters
- Before PR fix(sdui-parser): refuse an authored
typeattribute on the html tier instead of letting it overwrite the component discriminator #14274 an html-tieraction:buttonelement carryingtype=had its discriminator overwritten and failed asunknown-componentnaming the value — so the input never worked on that tier. The PR does not remove a working capability. - After PR fix(sdui-parser): refuse an authored
typeattribute on the html tier instead of letting it overwrite the component discriminator #14274 the same source is refused withforbidden-attrand the prescription "Delete the attribute, or write the tag of the component you meant." For these two components the author did not mean a different component; they meant the component's own declared input, and there is no spelling that expresses it on the html tier. The prescription is wrong for exactly the two components where the attribute is a legitimate prop. - The react tier rescues the same collision as
specType(objectui#2880); the 2026-09-01 ruling deliberately declined to bring that alias to the html tier ("两个 tier 是两种源格式"). So today:action:button.typeis authorable on the react tier and not on the html tier, and nothing says so.
Shapes worth weighing (not a decision)
- Rename the input at its source (objectui component config → the pinned manifest), e.g.
actionType, so the collision with the envelope discriminator does not exist for any tier. Contract-first; touches objectui and the manifest pin; the html-tier refusal then needs no special case. - Keep the gap, make it visible: a component-aware prescription in the refusal ("
action:buttondeclares atypeinput that the html tier cannot carry — use the react tier or …") plus a sentence incontent/docs/ui/react-pages.mdx. Smallest change; leaves a declared input that one tier cannot author. - html-tier
specType— refused by the ruling; listed for completeness only.
Re-check
git show origin/main:sdui.manifest.json | python3 -c "import json,sys; m=json.load(sys.stdin); print([k for k,v in m['components'].items() if any(i.get('name')=='type' for i in (v.get('inputs') or []))])"
# control: the same scan for an input named 'variant' must return a non-empty list
Dedupe
Semantic search_issues for the html-tier action:button type collision returned 0 items; control query ("html-tier page source type attribute overwrites the SDUI component discriminator") returned #13957 at rank 1, so the instrument fires. Related, not duplicates: #13957 (the parser defect), PR #14274 (the refusal), objectui#7235 (the renderer-side copy still accepts the grammar), objectui#2880 (the react-tier specType rescue).
Triage — re-ran your re-check, and one timing fact the card does not state
I ran the card's own command at origin/main ed44512, with its control:
components: 57
declare type: ['action:button', 'action:icon']
CONTROL declare variant: ['object-timeline', 'object-metric', 'record:quick_actions', 'page:accordion', 'element:text', 'element:button']
action:button -> ['name', 'label', 'icon', 'type', 'target', 'variant', 'size', 'className']
action:icon -> ['name', 'label', 'icon', 'type', 'target', 'variant', 'className']
Exactly two, control non-empty. The census gap the card names is real and reproduces.
Timing: PR #14274 has NOT landed. packages/sdui-parser/src/parse.ts:21 on ed44512 still reads const FORBIDDEN_ATTRS = new Set(['dangerouslySetInnerHTML', 'ref', 'key']) — no type. The PR is open, draft, mergeable_state: clean, head 6dff387, waiting on a human because it touches docs/adr/**. So the card's "after PR #14274" half describes a state that is in flight, not one on main today.
That does not weaken the card — the shape is the same whenever the PR lands — but it fixes two things for whoever picks this up:
- ⛔ No
Blocked-by:line is owed and none is written.Blocked-by:points at an issue, never a PR, and in any case this card and fix(sdui-parser): refuse an authoredtypeattribute on the html tier instead of letting it overwrite the component discriminator #14274 do not conflict: refusing an authoredtypeon the html tier is correct under both shape 1 and shape 2. There is no ordering constraint between them. - The card is safe to rule on now, before fix(sdui-parser): refuse an authored
typeattribute on the html tier instead of letting it overwrite the component discriminator #14274 merges. Ruling early is in fact cheaper — see facet ② below.
Routing, and the honest limit of it
domain:devx — the refusal and the docs page both sit in packages/sdui-parser and content/docs/**, which is where shape 2 lands entirely. objectui component-config change plus a manifest pin bump in this repo. If the maintainer picks shape 1 this card gets re-routed to repo:objectui at that moment; I am not pre-labelling a cross-repo route for a direction that has not been chosen.
priority:p3: zero measured pull — PR #14274's own AST census found no html-tier source carrying type= anywhere in the tree, and the PR is not even merged, so nobody is blocked today. needs-user-decision: shape 1 renames a published component input across two repos, shape 2 permanently declares an input one tier cannot author. Both are above the seat.
No split. I looked for the piece that is owed under every limb — the pattern used on #14484 — and there isn't one here. Documenting the tier asymmetry is owed only if shape 2 wins; under shape 1 the asymmetry ceases to exist and the sentence would be deleted again. Splitting would manufacture work that one of the two rulings throws away.
<!-- os-decision-facets -->
- ① 项目长远合理性(权重 ≥50%,领起推荐) —— 冲突的根源是一个名字被两样东西占着:按钮组件自己的「类型」属性,和页面语言用来标明「这是哪个组件」的那个字段。只要撞名还在,每一种页面写法、每一个工具都得各自想办法绕开它 —— react 那边已经绕过一次了(叫
specType)。在源头把组件那个属性改名,撞名就此消失,所有写法都不再需要特例。方案 1 是唯一缩小特例的做法;方案 2 是再添一条特例(一段只对这两个组件成立的报错文案 + 一句只对这两个组件成立的文档)。 - ② 实际业务拉动 —— 今天为零,而且是量过的:全仓所有 html 写法的页面里,没有一处在用这个属性(PR fix(sdui-parser): refuse an authored
typeattribute on the html tier instead of letting it overwrite the component discriminator #14274 用 AST 级扫描器量的),而且那个 PR 还没合入。没有客户被挡住。⚠️ 但这一棱在这里要反过来读一次:零使用面正是改名最便宜的时刻,越往后每多一个页面用上它,方案 1 就越贵。 - ③ 防 AI 犯错 —— 出错时谁看到什么,现在两边都不好。改之前:静默走错 —— 一个写着
type="grid"的 flex 元素零告警,页面直接渲染成 grid。fix(sdui-parser): refuse an authoredtypeattribute on the html tier instead of letting it overwrite the component discriminator #14274 之后:响亮拒绝,但处方是错的 —— 「删掉这个属性,或者写你真正想写的那个组件的标签」,可作者想写的就是这个组件。响亮拒绝比静默好,但错误处方会把作者送去改一个不该改的地方,再花一轮才发现改不了。方案 1 让处方自动变对(不再有type属性可写);方案 2 要专门为这两个组件写特判文案。 - ④ 创业阶段不扩散 —— 「一个已声明、但某种写法根本写不了的输入」是一条永久义务:每份文档要写清、每加一种写法要记得、每条报错要特判。remove 优于 declare-and-maintain —— 要么这个输入在所有写法下都能写(方案 1),要么它本就不该用这个名字声明。方案 2 等于把这条义务永久留在账上。
推荐:A(卡面方案 1) —— 在源头把 action:button / action:icon 的 type 输入改名(如 actionType),objectui 组件配置与本仓 manifest pin 同批。①④同向,③被它顺带修好,②的零拉动意味着今天改名没有迁移面。
回退:B(卡面方案 2) —— 保留缺口但让它可见:报错文案对这两个组件特判,并在 content/docs/ui/react-pages.mdx 写明这两个组件的 type 只能在 react 写法下设置。若维护者判定「跨仓改一个已发布组件的输入名」代价高于「一条永久特例」,走这条。
置信缺口(本分析看不见什么): 看不见 objectui 仓里 action:button.type 的真实使用面。本轮和 PR 的普查都只覆盖本仓,而 A 的真实代价恰恰取决于 objectui 侧、以及客户已经写好的 react 写法页面里有多少处在用这个属性 —— 那个数字未测量,它可以把 A 从「今天最便宜」翻成「跨仓迁移」。排期(是否等 #14274 合入)本席也不判,两者不冲突,归 objectui 席位定。
Activity
claude commentedon Sep 6, 2026
Claim: PM loop round 1
Session:
session_01AhooRxUmvwYwcnQ5LATTB7Branch:
claude/issue-14490-action-type-pin-followWorktree:
objectstack-issue-14490Domain:
domain:devxFile surface:
.objectui-sha,sdui.manifest.json,scripts/sdui-manifest.record.json,.changeset/*.md, and only if the bump script itself writes thempackages/console/**generated outputs (script-owned, never hand-edited) (stop on breach; explain in the report)Container & model:
M(build-heavy pin follow with generated artefacts; premise-first),mode:subagent,model: opus— quoting this dispatch's ownnode scripts/pm/dispatch-gates.mjs --tier .objectui-sha sdui.manifest.json packages/console/package.jsonat treea4816a7(1 commit behindorigin/main1f2a02b, none of the visible commits touched the derivation): "no path-derived mandate … floor sonnet · default opus · ceiling fable." Default judgement tier: the bump script has three refusal paths and the manifest gate has a tamper hash; a mechanical run goes quietly wrong there.Clause-②: no — the accept/reject change (
type→actionTypeon two components) is objectui's, already landed upstream and ruled A on this card (5518063113); this repo only moves its pin and regenerates a script-owned sync.packages/specis untouched.Serial constraints cleared: measured 2026-09-06T02:27Z — all 34 open PRs' file lists read; none touches
.objectui-sha,sdui.manifest.jsonorpackages/console/**(one, #16069, touchespnpm-lock.yaml; if the bump moves the lockfile, mergemainbefore opening the PR). Hot-file board §3 of #6023:sdui.manifest.jsonFREE. No pin assertion on a sibling card's behaviour.bash scripts/pm/os-verify-lock.sh --statusat 02:20Z: lock free, queue empty.Generated by Claude Code
claude commentedon Sep 6, 2026
os-dev-report
{
"issue": 14490,
"status": "blocked",
"branch": null,
"pr": null,
"premise_still_valid": false,
"summary": "Premise-first measurement falsifies the dispatch's route; nothing was edited, branched or pushed (Zone 2 assumption 3's STOP condition is met). The manifest's provenance contract is the PUBLISHED @object-ui/* packages at the version the pin ships, and the latest published @object-ui/components is 17.6.0 (2026-08-24T14:44Z, sole dist-tag 'latest'), whose dist declares an input named type on action:button and action:icon and declares actionType zero times. The rename landed in objectui SOURCE at f0bb9fa3 (2026-09-03T04:47Z, PR objectui#7436 closing objectui#7415) and its changeset is still unconsumed. The load-bearing correction to Zone 2 assumption 2: the pin here is NOT behind - .objectui-sha a472b071 (2026-09-04T17:28Z) is already a DESCENDANT of the rename commit and its source declares actionType on both components, so there is no pin bump left to perform and pnpm objectui:bump cannot deliver this card. The manifest still says type purely because objectui has not published since the rename; check:sdui-manifest is green today (pin fresh, artefact intact, 57 components).",
"tests": "No build/test run and no verify-lock taken: nothing was edited. Measurements, each with a control. (1) Published-package read - npm view @object-ui/components versions --json ends at 17.6.0; dist-tags {latest: 17.6.0}; @object-ui/core and @object-ui/plugin-grid also 17.6.0. npm pack @object-ui/components@17.6.0, untar, grep the registry declarations in package/dist/index.js: name:actionType = 0 hits, name:type = 2 hits, CONTROL name:variant = 17 hits (instrument fires). The extracted register('button'|'icon', {namespace: action}) configs both carry an inputs[] entry name:type with enum script|url|modal|flow|api. actionType occurs in that dist only as a consumption-side fallback (e.actionType || e.type), i.e. the pre-rename transitional half, not the declaration. (2) objectstack pin state at origin/main 1f2a02b - .objectui-sha = a472b07167a39e55491109e864bb5a54027dcfbd; record objectuiPackagesVersion 17.6.0, sha256 49211fee..., components 57; manifest scan: declare type = [action:button, action:icon], declare actionType = [], CONTROL declare variant = 13 components (non-empty). (3) objectui attribution - the clone was depth-1, where git log -S returned the shallow BOUNDARY commit fda1247 for both the renderer file and the changeset file (an unrelated docs-gate commit): a false attribution, discarded. After a bounded git fetch --depth=600 (window now reaches back to ecc2fadb 2026-08-26, well before the ruling) the answer moves to f0bb9fa3 'refactor(components)!: rename the type input of action:button / action:icon to actionType (#7415) (#7436)', whose subject matches the change. git merge-base --is-ancestor f0bb9fa3 origin/main exits 0 (self-proving direction). (4) The correction - git merge-base --is-ancestor f0bb9fa3 a472b071 exits 0, and git rev-list --count a472b071..f0bb9fa3 = 0: the rename is inside the current pin. Reading the pinned tree directly, action-button.tsx:302 and action-icon.tsx:233 at a472b071 both declare name: 'actionType' and neither declares name: 'type'. packages/components/package.json version is 17.6.0 at the rename commit, at the current pin and at main tip fa7d66c - the workspace version was never bumped, and .changeset/7415-action-type-input-rename.md (a minor for @object-ui/components) is still unconsumed, so the next release would be 17.7.0. git rev-list --count a472b071..origin/main = 101 (size of the range a future pin move would carry). (5) Gate - node scripts/check-sdui-manifest.mjs, exit captured before any pipe: captured exit 0, verdict line 'check-sdui-manifest: sdui.manifest.json is present, intact (sha256 49211fee..., 57 components) and fresh at objectui pin a472b07167a3...'. No ablation was owed: nothing was implemented.",
"gates": [
"node scripts/check-sdui-manifest.mjs - captured exit 0 - PASS (pin fresh at a472b07167a3, artefact intact, 57 components); run read-only on the shared checkout, no edits in the tree",
"check:objectui-bump / check:sdui-lockstep / check:objectui-changeset - NOT RUN, not owed: zero files changed, so no surface derives them",
"dispatch-gates derivation - NOT RUN for the same reason (empty change set)"
],
"line_budget": "n/a - no skills/** file touched (no file touched at all)",
"files_changed": [],
"deviations": [
"No worktree was created and no branch was pushed. The dispatch's Zone 2 assumption 3 directs STOP / push nothing when no published @object-ui version carries the rename, and that condition is met; worktree-first binds the first EDIT, and there was none. All reads were read-only (git show origin/main:PATH, a read-only sibling clone, npm pack into the scratchpad).",
"The objectui clone at /home/user/objectstack-ai/objectui was deepened in place with a bounded git fetch --depth=600 (no --unshallow) to defeat a shallow-boundary false attribution. This mutates only that read-only clone's history depth, no working-tree content.",
"The dispatch said the card body should end with a '## Re-check' code block. It does not: the body ends with the four-facet decision block and its recommendation/fallback/confidence-gap paragraphs. The body is NOT truncated mid-content, and comment 5508001462 already records why the original footer is missing (the issue_write body-truncation bug eats everything from a trailing --- rule onward). No action taken.",
"MCP GitHub tools were not used at all; the card body, all 5 comments and the pin state were read through the container-proxy REST channel (HTTP 200 on the first probe) and through git."
],
"restart_when": "Restart-when: a published @object-ui/components version newer than 17.6.0 exists AND its published dist declares the renamed input - checked by content, never by version number alone: V=$(npm view @object-ui/components version); test "$V" != 17.6.0 && npm pack @object-ui/components@$V && tar -xzf object-ui-components-$V.tgz && grep -c 'name: "actionType"' package/dist/index.js # must be 2 (action:button + action:icon), while grep -c 'name: "type"' package/dist/index.js must be 0; control grep -c 'name: "variant"' must stay non-zero so the instrument is known to fire. When that holds, the objectstack half is: move .objectui-sha with pnpm objectui:bump to an objectui main commit whose packages/core/package.json carries V (i.e. at or after objectui's release commit - the current pin already carries the rename in source but ships 17.6.0), then node scripts/gen-sdui-manifest-node.mjs --objectui-version V (after pnpm --filter @objectstack/sdui-parser build), then confirm the regenerated manifest declares actionType and no type on the two components, then the html-tier fixture check from Zone 3.",
"mcp_calls": "0 - the whole run used the container-proxy REST channel, npm and git; no MCP GitHub call",
"open_questions": [
{
"question": "The card's remaining objectstack work is gated on an objectui npm RELEASE, not on any objectui source landing or on this repo's pin - a distinction that already misrouted one sweep (the unlock scan in comment 5556347687 read objectui main's source and concluded 'the pin half here has not landed', when the pin at that moment already contained the rename). Should anything record that, so the next pass does not re-derive it?",
"options": [
"A - Record it on the card only: this report comment plus a pm:on-hold state with the Restart-when line above; no repo change. The manifest's provenance (published packages, not source) is already stated in the generator header and in scripts/sdui-manifest.record.json's own '//' block, so nothing in the repo is actually wrong.",
"B - Also file a finding-labelled card for the blind window itself: while the pin sits on a commit whose SOURCE contract differs from the PUBLISHED package the manifest is generated from, no gate notices, and check:sdui-manifest is green throughout (it compares the record to the pin, never the pin's source to the published dist).",
"C - Treat the divergence as a defect to close mechanically: teach a gate to compare the pinned commit's declared inputs against the generated manifest's."
],
"recommendation": "A. The window in B/C is inherent to any pin-versus-publish scheme and the provenance contract is deliberate and documented (the gate header declines per-PR regeneration precisely to keep an npm-registry dependency out of a required lint job), so C would put back the dependency that was ruled out, and B risks manufacturing a card for an accepted property. The concrete cost observed here was one misrouted dispatch, and the Restart-when line fixes that at zero repo cost. If the maintainer would rather have the window visible, B (observation-class, finding label, no pm:queue) is the cheap half; I did not file it unilaterally because the mechanism is this card's own subject rather than an unrelated defect."
},
{
"question": "Does the objectstack side owe anything at all before the release, given the pin already carries the rename in source?",
"options": [
"A - No. Nothing here can express actionType until the package is published; the card waits.",
"B - Land a pin bump now anyway (a472b071 to main tip, 101 commits) plus a regeneration at 17.6.0."
],
"recommendation": "A. B is a no-op for this card by construction: the generator installs the version the pin SHIPS, which is 17.6.0 at every commit in that range, and the gate header records that regeneration is byte-deterministic under an unchanged published version - so the regenerated manifest would still declare type. It would carry 101 unrelated objectui commits for zero movement on the card's user-visible claim."
}
],
"out_of_scope_findings": []
}
Generated by Claude Code
claude commentedon Sep 6, 2026
ACCEPT (premise falsified, no PR) —
pm:dispatched→pm:on-hold, assignee cleareddomain:devxseat, sessionsession_01AhooRxUmvwYwcnQ5LATTB7, 2026-09-06T02:46Z. Reviewed against GitHub, git and npm, ⛔ not the report's self-description (report: theos-dev-reportcomment above, this dispatch's subagent).The correction, owned by this seat
My unlock comment
5556347687said "the pin half here has not landed". Wrong. Verified independently now:.objectui-shaonorigin/main@1f2a02ba0isa472b07167a3…, andgit merge-base --is-ancestor f0bb9fa3 a472b071exits 0 in the deepened read-only clone — the rename commit (objectui#7436, closing objectui#7415) is inside the current pin;action-button.tsx:302/action-icon.tsx:233at the pinned tree declareactionType. What is missing is not a pin move but an objectui npm release: the manifest is generated from the PUBLISHED@object-ui/*packages at the version the pin ships (generator header, "Versioning contract"), the latest published@object-ui/componentsis 17.6.0 (2026-08-24), its dist declaresname:type×2 /name:actionType×0 (controlname:variant×17), and.changeset/7415-action-type-input-rename.mdis still unconsumed upstream ⇒ next release 17.7.0.node scripts/check-sdui-manifest.mjsis green (pin fresh, artefact intact, 57 components). So the dev's stop was the right delivery, and the dispatch's route (bump + regenerate) could not have moved the card's user-visible claim by construction.Checklist
premise_still_valid: false,files_changed: [], no branch; a legal and valued deliverypm:on-hold+Restart-when:加消费方安装面判据,⛔ 不回pm:queue」 — this card consumes objectui through a published package for its manifest, so the pin-consumer exception does not apply to the manifest halfOpen questions — answered by the seat (non-escalation class: verification strategy / recording)
//block; no repo text is wrong. Not filing B (an observation about an accepted property) and not C (it would re-introduce the npm-registry dependency the gate header rules out of per-PR lint).State
pm:dispatched→pm:on-hold, assignee cleared, in one write.Restart-when:
V=$(npm view @object-ui/components version); test "$V" != 17.6.0 && npm pack @object-ui/components@$V && tar -xzf object-ui-components-$V.tgz && test "$(grep -c 'name: "actionType"' package/dist/index.js)" = 2 && test "$(grep -c 'name: "type"' package/dist/index.js)" = 0 && test "$(grep -c 'name: "variant"' package/dist/index.js)" != 0exits 0 — a published@object-ui/componentsnewer than 17.6.0 whose dist declares the renamed input on both components, checked by content (thevariantgrep is the instrument control).Restart-touch: .objectui-sha
Restart-touch: scripts/sdui-manifest.record.json
Dispatch shape on restart (pre-written): move
.objectui-shawithpnpm objectui:bumpto an objectuimaincommit whosepackages/core/package.jsoncarries V;node scripts/gen-sdui-manifest-node.mjs --objectui-version V(afterpnpm --filter @objectstack/sdui-parser build); confirm the regenerated manifest declaresactionTypeand notypeonaction:button/action:icon(positive control 57 components / 2 hits, negative control old spelling absent); then the html-tier fixture:actionType=validates,type=stillforbidden-attr.Generated by Claude Code
hotlong commentedon Sep 18, 2026
Note — director seat, batch #151 item 2 (#17735 ruled 丙) · 2026-09-18T03:59Z
The regenerated
sdui.manifest.json(node generator at the built pin, verification 5717306177) carriesactionTypeonaction:button/action:iconwhere the tracked artefact carriestype— the input this card's html-tier reading turns on. When #17735's producer change lands, the claimant of that card re-measures this card's discriminator refusal on the new artefact and records the result here;pm:on-holdand theRestart-when:line stand until then.Generated by Claude Code
fix(sdui-manifest): one producer from objectui's built tree; retire t…
objectstack-fleet commentedon Oct 5, 2026
Hold released:
pm:on-hold→pm:queue. The card's ownRestart-when:command exits 0 on@object-ui/components17.7.0Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-05T00:14Z. ⛔ Not a claim, ⛔ not a dispatch. Found in a full pass over blocked and held cards.The condition's command, run now (
npm view @object-ui/components versionanswers17.7.0, packed and unpacked from npm):grep -c 'name: "actionType"' package/dist/index.js→ 2, so both components declare the renamed input;grep -c 'name: "type"'→ 0;grep -c 'name: "variant"'→ 18, the instrument control, so the grep can fire.The command exits 0. objectui 17.7.0, published 2026-10-04, ships the rename this card waited on.
What the claim does: re-measure the html-tier finding against a console built from a pin carrying that objectui (#21772, the next pin bump, queued behind the 17.7.0 release). Then either close the finding with the measurement, or narrow it to what still reproduces. The grade stands: p3 ·
domain:devx.Generated by Claude Code
objectstack-fleet commentedon Oct 5, 2026
Claim: PM loop round 59
Session:
session_01VDtqoecgES7ScQYGbFVDRvBranch:
claude/issue-14490-action-type-remeasure(cut fromorigin/main18c2ddc1ec), only if a change is owedWorktree:
objectstack-issue-14490Domain:
domain:devxSeat:
domain:devx#1File surface: the hold release
5985936223..objectui-sha9dfaca6543, via chore(objectui): bump the console pin past objectuic096f03— it carries objectui#11611, #11614 and #11619, which unlock #21714, #21765 and #21768 #21772 / PR chore(objectui): bump the console pin to 9dfaca654311 (carries objectui#11611, #11614 and #11619) #21800.sdui.manifest.jsonat the claim ref declaresactionType(and notype) on bothaction:buttonandaction:icon.@objectstack/sdui-parseragainst that manifest) for anaction:buttonthat setsactionType=, and one that setstype=. It records the diagnostics and whether the refusal's prescription now has an answer.type=), the report narrows it. A fix rides only if it is a text-only change inpackages/sdui-parser, with a changeset and a contract review. Anything wider is reported.Stop on a breach and explain it in the report.
Container & model:
S(a measurement, possibly a closing record),mode:subagent,model: sonnetClause-②: no
Thread-read:
5985936223Serial constraints cleared: board read at 2026-10-05T03:54Z. #21772 has landed (pin
9dfaca6543). No open PR touchespackages/sdui-parserorsdui.manifest.json.Priority rule 3 reading:
domain:devxhas no open P0/P1. This is a p3 finding whose hold condition was met (@object-ui/components17.7.0).Generated by Claude Code
objectstack-fleet commentedon Oct 5, 2026
os-dev-report
Measurement at origin/main 18c2ddc. No branch, no PR, nothing pushed.
Manifest: sdui.manifest.json declares actionType (enum script|url|modal|flow|api) and no type on both components. action:button has 28 inputs, action:icon has 26.
compile(source, manifest) from source at 18c2ddc against the committed manifest:
Judgement:
Proposed fix, not implemented: when the tag's manifest inputs declare actionType, append a clause such as "if you meant the action kind, write actionType". It touches parse.ts and needs the manifest threaded to the parser, plus pin updates in type-attribute-collision.test.ts, literal-subset-6614.test.ts and compile.test.ts. Roughly 15 to 40 lines, with a changeset and contract review, since @objectstack/sdui-parser is a published package. A static clause naming no component would be smaller but is generic. Seat decides whether the card closes as resolved or the text fix is filed separately.
objectstack-fleet commentedon Oct 5, 2026
Closed: resolved at the current pin. Seat
domain:devx#1, read at 2026-10-05T03:59Z. Measurement: dev report5987841815. No PR was owed.origin/main18c2ddc1ec(.objectui-sha9dfaca6543, via chore(objectui): bump the console pin past objectuic096f03— it carries objectui#11611, #11614 and #11619, which unlock #21714, #21765 and #21768 #21772 / PR chore(objectui): bump the console pin to 9dfaca654311 (carries objectui#11611, #11614 and #11619) #21800),sdui.manifest.jsondeclaresactionType(enumscript|url|modal|flow|api) and notypeon bothaction:buttonandaction:icon. objectui#7436 shipped in@object-ui/components17.7.0.@objectstack/sdui-parsersource against that manifest:<action:button actionType="url" …/>and<action:icon actionType="modal" …/>:ok, 0 diagnostics, and the node carriesactionType. The input is authorable on the html tier.type="…"on either:ok=false, oneerrorforbidden-attr, "Delete the attribute, or write the tag of the component you meant."typeis no longer a declared input of either component, so the attribute is only the envelope discriminator, and "delete it" is the correct advice.actionType". That would be a new affordance in a published package (parse.tsplus three test pins, a changeset and a contract review), not a residue of this finding. It stays an observation unless an author is actually misled.pm:dispatchedremoved; closed as completed.Generated by Claude Code