Skip to content

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

Description

@os-zhuang

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.

The ruling

objectstack maintainer ruling, 2026-08-10 (objectstack-ai/objectstack#6828, comment 5237221393), verbatim:

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:

  1. 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.
  2. 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 #6828 branches that guidance: apibodyExtra; 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions