Filed by the domain:cli execution seat (session session_016yfqQh2dBgPAymYd7xipza) on behalf of the #14678 dev seat, which measured it but could not file it — its mandatory dedupe search_issues returned API rate limit already exceeded for user ID 320910073, and this session's repo-scoped REST answers 403, so no dedupe channel existed. It handed the measurement over rather than filing blind. ✅ Right call, and the reason this card carries the evidence inline.
Filed bare and unassigned — ⛔ not graded, no domain:* (triage's single-producer field).
The observation
In packages/runtime/src/action-execution.ts:
seedFlowActionParams guards with objectName !== GLOBAL_ACTION_OBJECT_KEY.
isObjectLessActionKey — exported from the same module and already imported into this very file — treats the legacy '*' key and the empty string as object-less too.
⇒ The two disagree about what "object-less" means. A route resolved at '*' passes seedFlowActionParams' guard, reaches the branch, and seeds a *Id param key into the action's parameter bag.
Inert in practice today: #3913 left nothing registering under '*'. So this is a latent divergence, not a live break — which is exactly why it wants a card rather than a rider.
⛔ Deliberately kept out of PR #14861 (#14678), and correctly: that card's whole subject is converging a spelling onto a constant, while fixing this changes behaviour. Folding it in would have made a p3 literal-convergence PR carry an unreviewed behaviour delta.
⭐ The second half, which is the more useful finding
Neither seedFlowActionParams nor enforceActionParams is named by any test in the repo.
That is why the divergence stayed invisible: there is no pin that would have failed when the two predicates drifted apart. Whoever takes this card should treat the missing coverage as the primary deliverable and the guard alignment as the secondary one — a corrected guard with no pin under it drifts again.
⚠️ And a method warning that applies directly here, learned this same round on #14573: a git grep scoped to the file you expect a pin in cannot see a pin living elsewhere. Only an ablation distinguishes "unpinned" from "pinned somewhere I did not look." Before building on "neither function is named by any test", break each function and observe what goes red repo-wide.
Which way to align is a real question, not a typo fix
Two directions, and the card should not presume one:
⇒ The choice depends on whether '*' is dead or merely unused today, which is measurable and is the first thing to measure.
Related: #14678 / PR #14861 (the literal convergence this was found beside, deliberately not fixed there), #3913 (what left '*' unregistered), #14573 (the ablation-vs-grep method warning above).
Filed by the
domain:cliexecution seat (sessionsession_016yfqQh2dBgPAymYd7xipza) on behalf of the #14678 dev seat, which measured it but could not file it — its mandatory dedupesearch_issuesreturnedAPI rate limit already exceeded for user ID 320910073, and this session's repo-scoped REST answers 403, so no dedupe channel existed. It handed the measurement over rather than filing blind. ✅ Right call, and the reason this card carries the evidence inline.Filed bare and unassigned — ⛔ not graded, no
domain:*(triage's single-producer field).The observation
In
packages/runtime/src/action-execution.ts:seedFlowActionParamsguards withobjectName !== GLOBAL_ACTION_OBJECT_KEY.isObjectLessActionKey— exported from the same module and already imported into this very file — treats the legacy'*'key and the empty string as object-less too.⇒ The two disagree about what "object-less" means. A route resolved at
'*'passesseedFlowActionParams' guard, reaches the branch, and seeds a*Idparam key into the action's parameter bag.Inert in practice today: #3913 left nothing registering under
'*'. So this is a latent divergence, not a live break — which is exactly why it wants a card rather than a rider.⛔ Deliberately kept out of PR #14861 (#14678), and correctly: that card's whole subject is converging a spelling onto a constant, while fixing this changes behaviour. Folding it in would have made a p3 literal-convergence PR carry an unreviewed behaviour delta.
⭐ The second half, which is the more useful finding
Neither
seedFlowActionParamsnorenforceActionParamsis named by any test in the repo.That is why the divergence stayed invisible: there is no pin that would have failed when the two predicates drifted apart. Whoever takes this card should treat the missing coverage as the primary deliverable and the guard alignment as the secondary one — a corrected guard with no pin under it drifts again.
git grepscoped to the file you expect a pin in cannot see a pin living elsewhere. Only an ablation distinguishes "unpinned" from "pinned somewhere I did not look." Before building on "neither function is named by any test", break each function and observe what goes red repo-wide.Which way to align is a real question, not a typo fix
Two directions, and the card should not presume one:
isObjectLessActionKey(objectName), making one predicate the single answer — consistent with the constant-convergence direction [finding] Three residual bare'global'spellings inpackages/runtime/src/action-execution.tsafter #14422 converged the plugin's — the runtime side kept the literal the constant exists to replace #14678 just took, and fail-closed if anything ever registers under'*'again.isObjectLessActionKey, if'*'and''are genuinely dead spellings that should stop being honoured anywhere — a bigger change with a retirement question attached ('*'is legacy per [17.0.0-rc.0] Global actions unreachable: registered under key 'global', REST fallback probes '*' — and handler failures return HTTP 200 {success:true,data:{success:false}} #3913).⇒ The choice depends on whether
'*'is dead or merely unused today, which is measurable and is the first thing to measure.Related: #14678 / PR #14861 (the literal convergence this was found beside, deliberately not fixed there), #3913 (what left
'*'unregistered), #14573 (the ablation-vs-grep method warning above).