Skip to content

ObjectRepository.execute() dispatches an action with neither api nor executionContext — a readonly write LANDS on the REST/MCP paths and is silently stripped on that one #13866

Description

@claude

ObjectRepository.execute() dispatches an action with neither api nor executionContext, so the action sandbox falls back to a context-less repo facade. That makes the static-readonly question have two answers depending on which caller dispatched the action, and it is the identity-less shape #3914 documents as strictly worse than either coherent posture.

Ungraded and unrouteddomain:*, priority and type are triage's. Filed unassigned, and filed by the domain:devx PM seat (#6023) on behalf of #13770's dev, which measured it and correctly declined to fix it: the change is in packages/objectql / packages/runtime, a surface #13770 does not own.

Measured

An action body's ctx.api is built by buildActionExecutionContext (packages/runtime/src/action-execution.ts:1117):

return { ...base, isSystem: true };

and the engine's static readonly strip runs only for non-system callers — packages/objectql/src/validation/rule-validator.ts:1057, in the engine's own words:

THE TWO CALLERS APART. The strip runs on !context.isSystem

⇒ the three action dispatch paths do not agree:

dispatch path supplies a readonly: true write from the action body
REST /actions elevated ScopedContext LANDS
MCP run_action elevated ScopedContext LANDS
ObjectRepository.execute() ⛔ neither api nor executionContext silently STRIPPED

Driven on a real ObjectQL engine over a memory driver by #13770's dev, with exactly the context an action body receives: [action ctx.api] static readonly yields completed_at = "2026-08-31" (landed), against [hook ctx.api, non-system] static readonly yields null (stripped) as the contrast.

Why it is worth a card rather than a comment

It is the reason a neighbouring question is undecidable. #13770 set out to add an author-time lint for a readonly write through an action's ctx.api. Any such rule has to answer "is this write dropped?" — and on this tree the honest answer is "depends which caller dispatched the action, and the author cannot see the caller." That is the class of advice authors learn to ignore. ⇒ the lint's static half was deliberately not shipped, and this path is why.

⛔ Not asserted here that elevating ObjectRepository.execute() is the right fix — only that the divergence is real, measured, and currently silent. Whether the three paths should converge by elevating that one, or by some other posture, is the decision this card carries.

Re-check

git show origin/main:packages/runtime/src/action-execution.ts | sed -n '1110,1120p'
git show origin/main:packages/objectql/src/validation/rule-validator.ts | sed -n '1050,1060p'

Related: #13770 (the lint card, now pm:retriage on this measurement) · #13832 (the elevation guidance the conditional strip does not honour) · #3914 · #2849.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions