Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[P3] objectui ActionDef's [key: string]: any absorbs every retired and misspelled action key — removing a spec key produces no compile error #4046

Closed

Description

@os-zhuang

Migrated from objectstack-ai/objectstack#4075 under the file-at-destination ruling (objectstack-ai/objectstack#7167, maintainer 2026-08-10). Originally filed 2026-07-30T08:22:35Z. The full prior thread — including triage rulings and hold/restart conditions — remains on the source issue and MUST be read before acting on this card.


⚠️ 状态头(2026-08-06 更新——派发者读这里,不要按下文的 staged plan 派单)

  • Step 1(构造点清查 + dev-mode 未识别键警告)✅ 已完成:objectui#3032(2026-07-30 合并,@object-ui/core 17.1.0;actionKeys.ts + AST 再派生 pin 测试)
  • Step 2(合法键位提升为显式字段)✅ 已完成:objectui#3190(2026-08-02 合并,17.2.0;18 个 spec 键全部提升)
  • Step 3(删除索引签名)🟡 已裁决、待上游:维护者 2026-08-06 裁定 visible / disabled 统一收敛为 boolean | string(CEL) | {dialect,source},spec 侧先行(spec: ActionSchema 的 visible / disabled 统一为 boolean | string(CEL) | {dialect,source}(#4075 step 3 前置,维护者 2026-08-06 已裁决) objectstack#5970);合并后 objectui 按 spec 派生类型收敛两键、删索引签名、删 DeclaredActionsBar.tsx 的 (action as any).disabled 强转(裁决全文见 2026-08-06 PM 裁决评论,分析见验收评论 5156971051)。
  • 本单曾两次被按下文 staged plan 误派 step 1(2026-08-02、2026-08-06,均由 premise 检查拦下)——下文正文是立单时的计划,不再是现状,以本状态头为准。

Blocked-by: objectstack-ai/objectstack#5970


Split out of objectstack-ai/objectstack#3856 / objectui#2990, which hit this directly and documented it rather than working around it silently.

The mechanism

ActionDef (packages/core/src/actions/ActionRunner.ts:277, interface at :78) ends with:

  /** Any additional properties */
  [key: string]: any;

at ActionRunner.ts:185. So ActionDef accepts any key of any type. Concretely, in objectui#2990:

  • deleting ActionDef.execute produced zero compile errors, even though the field had just been removed from the interface;
  • stale metadata still authoring execute: 'markDone' continues to type-check today;
  • the same deletion on @object-ui/types' ActionSchema — which has no index signature — correctly produced TS2353 at the authoring site.

That asymmetry is the whole issue: one of the two readers is capable of catching a retired key, the other is structurally incapable.

Because the compiler couldn't help, objectui#2990 had to add a runtime branch to keep the removal audible — executeScript now returns the rename prescription when it finds only the retired key, rather than a bare "No script provided". That branch exists solely to compensate for this index signature. Binding no handler silently is the objectstack-ai/objectstack#2169 "Mark Done does nothing" shape, so the compensation was necessary — but it is compensation.

Not the same as ActionContext's

ActionContext has an index signature too (ActionRunner.ts:53) and it should keep it: it is a runtime data bag whose keys are genuinely open (data, record, pageVariables, user, plus whatever a host passes). ActionDef is the opposite — it is a declared metadata contract that mirrors @objectstack/spec's ActionSchema. An open key set on a contract is what lets a typo (targt, exectue) and a tombstoned key (execute) both sail through to a runner that then silently does nothing.

Why it can't just be deleted

The runner deliberately accepts shapes wider than current spec, and ActionDef already enumerates a lot of them explicitly (actionType "legacy action type field", api as string | ApiConfig, onClick "legacy", navigate?: any). Some callers likely pass keys not declared anywhere. A bare removal would surface as a wall of TS2353 across components / plugin-* / host apps, some legitimate.

Staged narrowing is the realistic path:

  1. Inventory what is actually passed — grep the repo's ActionDef construction sites and any action[...] dynamic reads, and add a dev-mode warning on unrecognized keys (the "declared, warned, lintable shim" form AGENTS.md PD [WIP] Enhance UI components for forms and layouts #12 asks for, instead of a bare open type).
  2. Promote every legitimate key found into an explicit optional field, keeping the legacy ones marked @deprecated.
  3. Remove the index signature. At that point tsc catches both typos and retired spec keys, and Security: attachment blob download has no record-level RLS — bytes served by fileId with only a committed-status check (IDOR) objectstack#2990's runtime prescription branch can eventually retire with it.

Step 1 is independently useful and non-breaking: it turns an invisible failure into a warning without changing types.

Relation to objectstack-ai/objectstack#3903

objectstack-ai/objectstack#3903 established that the spec contract stops at authored source — stored sys_metadata rows are rehydrated unparsed. This is the same "declared ≠ enforced" pattern on the type channel rather than the parse channel: @object-ui/core is the reader that consumes those unparsed rows (runner(action)), and its type accepts anything they contain. The two issues bound the same gap from opposite ends; fixing objectstack-ai/objectstack#3903 alone would still leave a reader that cannot describe what it accepts.

Refs objectstack-ai/objectstack#3856 (objectui#2990), objectstack-ai/objectstack#3903, objectstack-ai/objectstack#2169, objectstack-ai/objectstack#3855, objectstack-ai/objectstack#3883, AGENTS.md PD #12.

Activity

  1. self-assigned this
    on Aug 11, 2026
  2. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Contributor

    CLAIM — step 3, unblocked (objectui whole-repo seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3) — dispatching now. State reconciliation per the status header: steps 1+2 landed (#3032, #3190); the Blocked-by: objectstack#5970 restart condition has FIRED — that card closed completed 2026-08-07 (PR objectstack#6450, merged pre-rc.6), so the installed spec already carries the unified boolean | string(CEL) | {dialect,source} shape for both keys. This dispatch executes the 2026-08-06 maintainer ruling's objectui half.


    Generated by Claude Code

  3. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Contributor

    ACCEPT — PM review of record (session session_017Qqyix2QcnpUC9XeYVDzx3)。

    PR #4285 执行了裁定的 step 3,验收要点:

    范围外发现处置:#4281 入队(它如实界定了本卡自身主张的边界——4 个转发点中 2 个因 spread 关闭了多余属性检查,守卫只在一半生效);#4282 留 finding 池。

    Flipping ready + auto-merge。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions