Skip to content

[P2] action execute vs target: spec and objectui resolve the precedence in OPPOSITE directions #3713

Description

@os-zhuang

Found while re-verifying the liveness ledger's action.execute entry (#3711). Not a ledger problem — a real behavioural divergence.

The divergence

execute is the deprecated alias of target. Both sides handle "author declared both" — in opposite directions.

Spec keeps targetpackages/spec/src/ui/action.zod.ts:576-581:

}).transform((data) => {
  // Auto-migrate deprecated `execute` → `target` for backward compatibility
  if (data.execute && !data.target) {
    return { ...data, target: data.execute };
  }
  return data;
})

Pinned by a test — packages/spec/src/ui/action.test.ts:1032, "should preserve target over execute when both are set" → asserts result.target === 'preferredHandler'.

objectui keeps executepackages/core/src/actions/ActionRunner.ts:704, inside executeScript():

const script = action.execute || action.target;

Consequence

An action declaring both:

defineAction({ type: 'script', target: 'preferredHandler', execute: 'legacyHandler',})

runs preferredHandler server-side (the parsed metadata carries target) and legacyHandler client-side (ActionRunner prefers execute). Two different scripts for one action, with no error anywhere.

Low frequency — it needs an author to set both, which only happens mid-migration or by copy-paste — but it is silent and the failure is "the wrong code ran", which is the worst kind to debug.

Suggested fix

Align objectui on the spec's rule (target || execute), since the spec's direction is the documented one (execute is the deprecated alias) and is test-pinned. Then either:

  • keep both readers permanently, or
  • have the spec transform drop execute from its output once it has been lowered, so the ambiguity cannot reach a renderer at all — the same "canonical wins, alias disappears" shape used for agent.knowledge.topicssources ([P2] Naming drift → silent no-ops (spec key ≠ consumed key) #1891), which asserts 'topics' in parsed === false.

The second is stronger: it makes the divergence unrepresentable rather than merely agreed-upon.

Verification notes

Both sides read directly, not via the agent report that surfaced this. The ledger entry for action.execute (corrected in #3711) now cites both readers and records this divergence.

Refs #3711, #3686.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions