You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
ActionRunner's non-array params reads are dead vocabulary — the url interpolation scope and params.newTab were retired by the spec ruling on objectstack#6828 (finding) #4097
Filed unassigned per contract-first: the contract half landed in objectstack, this is the consumer half. Not a live defect — a removal that is now authorized and was not before.
A key with three meanings and no authorized spelling for the third is the de-facto-contract shape AGENTS.md #0.1 forbids; the schema already refuses it and there is no live victim, so retirement costs nothing. Interpolation scope + newTab on a type:'url' inline action either gets its own properly-named key via a spec proposal (if someone demonstrates pull) or stays out of the vocabulary. Document the refusal in the error guidance so authors reaching for it learn the sanctioned spellings.
So object-form params is not getting a spelling on a type:'url' action. It never had one — packages/spec/src/ui/action.zod.ts has always declared params: z.array(ActionParamSchema), so the shape fails the props parse — but until this ruling the question "should it get a key?" was open. It is now closed the other way.
What that makes dead in this repo
Both reads are in packages/core/src/actions/ActionRunner.ts:
interpolateTarget — the non-array params interpolation scope. It takes a non-array params as the ${param.X} substitution scope for the target URL (around :1091 as measured on objectui main for objectstack#6828 on 2026-08-09 — re-anchor by content, not line). The ${param.X} mechanism itself is live and stays: it resolves values the params dialog collected. What is dead is the branch that accepts a statically authored object as that scope — no author can produce one, because the spec refuses the shape at parse time.
executeUrl — the legacy params.newTab escape hatch (around :1133-1136, same caveat), read below openIn in priority. openIn: 'self' | 'new-tab' is the declared key and already wins; the fallback can only fire on a shape that cannot be authored. Superseded in practice by URL action 无法声明式"新标签页打开";params 字段语义重载导致 newTab 被当成用户输入项弹框 #2043's fix, and now formally retired rather than merely outranked.
Why it is safe to remove
Nothing authors it. objectstack#6828 grepped origin/main in all three repos for inline-action sites (objectstack examples, cloud service-tenant pages, objectui). Every type:'url' inline action is target-only; the only object-form params anywhere was the showcase's api submit button, which objectstack#5777 migrated to bodyExtra.
The spec refuses it and now says so. The refusal message used to prescribe bodyExtra for every action type, which for a url action was a wrong instruction (an api request-body key is neither an interpolation scope nor a new-tab control). The objectstack PR for fix(data-objectstack): lower rule-shaped filter arrays on aggregate()'s analytics path #6828 branches that guidance: api ⇒ bodyExtra; url ⇒ put static values in the target string, and use openIn: 'new-tab' — naming both retired readings as retired, not renamed.
Removal cannot regress a validated stack, because a stack carrying either shape never validated.
What a fix looks like
Drop the non-array branch from interpolateTarget, keeping the dialog-collected ${param.X} scope and ${ctx.X} untouched.
Drop the params.newTab fallback from executeUrl, leaving openIn plus the existing external/relative-URL default.
Pin the sanctioned spellings so the removal cannot quietly come back: openIn: 'new-tab' opens a new tab, and a url action whose target carries ${param.X} interpolates from the collected dialog values.
Worth measuring before deleting: whether any objectui-internal caller synthesizes a non-array params itself (a renderer or console shim constructing an action object rather than parsing an authored one). The corpus grep above covered authored metadata; a synthesized caller inside this repo would not appear in it, and would have to move to the sanctioned spelling in the same change.
Related: objectstack-ai/objectstack#6828 (this card's contract half — the ruling and the error guidance), objectstack-ai/objectstack#5777 (the type:'api' half, ruled 2026-08-06 → bodyExtra), #2043 (the original newTab-in-params overload that produced openIn), #2997 (element:button's inline action shape).
The contract half is ruled and closed (objectstack#6828 maintainer ruling quoted verbatim in the body); this card is the authorized consumer-side removal — executing a made ruling, so queue, not the decision box.
Anchors verified on objectui origin/main @ 1592b21: packages/core/src/actions/ActionRunner.ts — interpolateTarget call at :1128, legacy params.newTab fallback at :1170-1176. The body's caveat (measure objectui-internal synthesized non-array params callers before deleting) belongs in the dev run.
Scope = the card's fix shape: drop interpolateTarget's non-array-params scope branch (the dialog-collected ${param.X} scope and ${ctx.X} stay), drop executeUrl's legacy params.newTab fallback (openIn + the existing default stay), pin the sanctioned spellings so the removal cannot quietly return. The card's own measurement clause is binding: before deleting, sweep for objectui-INTERNAL synthesized non-array params producers (the corpus grep covered authored metadata only) — any found must move to the sanctioned spelling in the same change or the removal stops.
Delegated ruling on the open question: B — the objectui half is discharged (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3; veto window open) — and ACCEPT of PR #4262 as the card's shipped half.
The measurement clause did exactly what it was for: the sweep found eight internal producers and proved the card's premise HALF-false — interpolateTarget's non-array scope is not dead authored vocabulary but the live read point of an internal runtime value-bag channel (_selectedIds, _rowRecord, dialog-collected values), confirmed three independent ways including the spec's own refusal message naming the mechanism as surviving. The #6828 ruling's target was the AUTHORING vocabulary — "a key with three meanings and no authorized spelling for the third" — and that is now fully handled: the spec refuses the third meaning at parse with guidance (upstream, shipped), and PR #4262 removes the newTab runtime read (with the bonus of closing the dialog-field-named-newTab navigation-hijack hazard). What remains is a channel no author can reach or observe; option A would spend a four-package runtime-contract migration to remove an already-unreachable shape — plumbing expansion with zero pull. Option C's heuristic can misfire on legitimate collected bags; declined.
Residual scope (this card stays open, re-scoped to one S-rider): the in-code documentation that makes ruling B discoverable — a comment block at interpolateTarget's non-array branch naming it an INTERNAL runtime value-bag channel (not an authoring surface), citing #6828 + this ruling, and flagging the one hypothetical A named (a future spec loosening would silently re-admit an authored scope — the upstream refusal pin and the two local pins are the guards). It rides the NEXT card touching ActionRunner.ts (#4046 is the queued candidate) rather than minting a comment-only PR. PR #4262 lands now as Part of #4097.
Activity
claude commentedon Aug 10, 2026
Triage:
pm:queue.origin/main@1592b21:packages/core/src/actions/ActionRunner.ts—interpolateTargetcall at :1128, legacyparams.newTabfallback at :1170-1176. The body's caveat (measure objectui-internal synthesized non-arrayparamscallers before deleting) belongs in the dev run.element:buttonexecutes anactionprop the spec does not declare #2997 are historical context, not open shadows.domain:*label — whole-repo seat, routing labels do not apply here.)本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
yinlianghui commentedon Aug 11, 2026
CLAIM (objectui whole-repo seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3) — executing the objectstack#6828 ruling's consumer half; dispatching now.claude/issue-4097-retire-params-reads, worktreeobjectui-issue-4097(S/M container)interpolateTarget's non-array-paramsscope branch (the dialog-collected${param.X}scope and${ctx.X}stay), dropexecuteUrl's legacyparams.newTabfallback (openIn+ the existing default stay), pin the sanctioned spellings so the removal cannot quietly return. The card's own measurement clause is binding: before deleting, sweep for objectui-INTERNAL synthesized non-arrayparamsproducers (the corpus grep covered authored metadata only) — any found must move to the sanctioned spelling in the same change or the removal stops.Generated by Claude Code
yinlianghui commentedon Aug 11, 2026
Delegated ruling on the open question: B — the objectui half is discharged (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3; veto window open) — and ACCEPT of PR #4262 as the card's shipped half.The measurement clause did exactly what it was for: the sweep found eight internal producers and proved the card's premise HALF-false —
interpolateTarget's non-array scope is not dead authored vocabulary but the live read point of an internal runtime value-bag channel (_selectedIds,_rowRecord, dialog-collected values), confirmed three independent ways including the spec's own refusal message naming the mechanism as surviving. The #6828 ruling's target was the AUTHORING vocabulary — "a key with three meanings and no authorized spelling for the third" — and that is now fully handled: the spec refuses the third meaning at parse with guidance (upstream, shipped), and PR #4262 removes thenewTabruntime read (with the bonus of closing the dialog-field-named-newTabnavigation-hijack hazard). What remains is a channel no author can reach or observe; option A would spend a four-package runtime-contract migration to remove an already-unreachable shape — plumbing expansion with zero pull. Option C's heuristic can misfire on legitimate collected bags; declined.Residual scope (this card stays open, re-scoped to one S-rider): the in-code documentation that makes ruling B discoverable — a comment block at
interpolateTarget's non-array branch naming it an INTERNAL runtime value-bag channel (not an authoring surface), citing #6828 + this ruling, and flagging the one hypothetical A named (a future spec loosening would silently re-admit an authored scope — the upstream refusal pin and the two local pins are the guards). It rides the NEXT card touchingActionRunner.ts(#4046 is the queued candidate) rather than minting a comment-only PR. PR #4262 lands now asPart of #4097.Generated by Claude Code
yinlianghui commentedon Aug 11, 2026
CLOSED — 全部两半均已落地 (session
session_017Qqyix2QcnpUC9XeYVDzx3)。params.newTabfallback 删除 — PR retire(core): drop the legacyparams.newTabfallback on url actions (#4097) #4262,已落(d6aa1726a)。interpolateTarget非数组分支的内部 value-bag 信道说明 + objectstack#6828 引用 + 上游拒绝 pin + 两个本地守卫 — 随 [P3] objectuiActionDef's[key: string]: anyabsorbs every retired and misspelled action key — removing a spec key produces no compile error #4046 的 PR fix(core,app-shell): close ActionDef — delete the index signature, converge visible/disabled on the spec (#4046) #4285 落地(ee66e2ebf),注释性 rider,零行为变化。Ruling B 全文与否决窗口记录在本卡此前的裁决评论;spec 侧词汇状态归 objectstack#6828 线,不在本卡追踪。
Generated by Claude Code
retire(core): drop the legacy `params.newTab` fallback on url actions (…
fix(core,app-shell): close ActionDef — delete the index signature, co…