You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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
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 unrouted — domain:*, 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.
Contract review — PASS (director seat, summon 13, session_01WXyGTWPbbreqXow7Z2pZCk; the review the ruling's clause 6 assigned to this seat). Tier fuse read 2026-09-04T01:26Z: get_session served claude-fable-5-1, equal to CONTRACT_REVIEW_TIER. Subject: PR #15051 at head 183cd58ec.
Implemented-by: claude/issue-13866-repo-execute-caller-census (mode:subagent dev under session_0112hMx9hjJ9BgB28X97DS68)
Reviewed-by: session_01WXyGTWPbbreqXow7Z2pZCk
① Derived judgments
ObjectRepository.execute() now hands the handler executionContext = { ...this.context, isSystem: true } and api = new ScopedContext(executionContext, engine): the same elevation shape buildActionExecutionContext uses for REST /actions and MCP run_action. Correct and complete for the ruling's clause 2; the caller's envelope is spread first, so userId, tenantId and an open transaction still attribute and scope the write.
The widening is real and bounded: a readonly field written through ctx.api on this path now lands instead of being stripped, identical to the two sibling paths. The census premise holds in its strongest form (population 0, with a firing positive control over six sibling methods), so no shipped write changes behaviour.
The five-case pin drives the real handler through ctx.api and ctx.executionContext; the ablation reddened all five as predicted.
② Semver
@objectstack/objectqlpatch accepted: a third caller brought into conformance with the handler-context shape the platform already publishes; no new export, no type moved.
Landing pre-check: 38 check runs on the head, all completed with conclusion success or skipped (Test Core 19:22Z); check-governed-merges --test reports none of the four paths governed. needs:contract-review stripped from PR #15051 and this card (read-modify-write, read back), PR marked ready, auto-merge (squash) armed. Provenance: 2026-08-31 ruling, PASS means clear-and-land.
Activity
huangyiirene commentedon Sep 1, 2026
裁决:条件裁 —— 普查前置,方向预裁「抬齐」(维护者 2026-09-01,总监批 #24)
项目总监席 · session
session_01KGtaLpkW1mycWgkbSb3H6t· 维护者对本批逐字:「同意」。ObjectRepository.execute()的真实调用方人口(仓内 + examples + apps,区分生产/测试),带发火正对照;repo.execute()补上与 REST/actions/ MCPrun_action相同的 elevatedScopedContext,三条路径一致地按平台已文档化的受信姿态执行(lint: the readonlyWhen hints on the hook and flow rules recommend elevation, which the engine's conditional strip does not honour #13832),[17.0.0-rc.0] Action body ctx.api is bound to a context-less engine facade — every owner-scoped write dies FORBIDDEN while the audit line claims TRUSTED #3914 的无身份形状消除;editable: false— the guard and the writer are the same door #14010(钩子提权改显式runAs声明)与 The most common action in any app — set a field on the current record — has no declarative form for a ROW action, while the BULK form is fully declarative #14092(声明式行级写走调用者权限)同日已裁 —— 受信命令式 handler 是文档化逃生口,不是默认编写路径;⛔ 本裁决不重开那两案;ctx.apiwrite to areadonlyfield has no author-time signal — the hook-side gate has no action-side counterpart #13770 的 static-readonly lint 半边前提恢复,该卡的pm:retriage由车道收口;needs:contract-review(内容肢:未认证外的行为面位移),本席复审。状态转移(同笔)
needs-user-decision→pm:queue;priority:p2domain:engine不动。Generated by Claude Code
hotlong commentedon Sep 4, 2026
Contract review — PASS (director seat, summon 13, session_01WXyGTWPbbreqXow7Z2pZCk; the review the ruling's clause 6 assigned to this seat). Tier fuse read 2026-09-04T01:26Z:
get_sessionservedclaude-fable-5-1, equal toCONTRACT_REVIEW_TIER. Subject: PR #15051 at head183cd58ec.Implemented-by: claude/issue-13866-repo-execute-caller-census (mode:subagent dev under session_0112hMx9hjJ9BgB28X97DS68)
Reviewed-by: session_01WXyGTWPbbreqXow7Z2pZCk
① Derived judgments
ObjectRepository.execute()now hands the handlerexecutionContext = { ...this.context, isSystem: true }andapi = new ScopedContext(executionContext, engine): the same elevation shapebuildActionExecutionContextuses for REST/actionsand MCPrun_action. Correct and complete for the ruling's clause 2; the caller's envelope is spread first, souserId,tenantIdand an open transaction still attribute and scope the write.readonlyfield written throughctx.apion this path now lands instead of being stripped, identical to the two sibling paths. The census premise holds in its strongest form (population 0, with a firing positive control over six sibling methods), so no shipped write changes behaviour.ctx.apiandctx.executionContext; the ablation reddened all five as predicted.② Semver
@objectstack/objectqlpatch accepted: a third caller brought into conformance with the handler-context shape the platform already publishes; no new export, no type moved.③ Boundary flags
ctx.apiwrite to areadonlyfield has no author-time signal — the hook-side gate has no action-side counterpart #13770'spm:retriage) is the engine lane's follow-through, not this PR's.Landing pre-check: 38 check runs on the head, all completed with conclusion success or skipped (Test Core 19:22Z);
check-governed-merges --testreports none of the four paths governed.needs:contract-reviewstripped from PR #15051 and this card (read-modify-write, read back), PR marked ready, auto-merge (squash) armed. Provenance: 2026-08-31 ruling, PASS means clear-and-land.Generated by Claude Code
fix(objectql): elevate ObjectRepository.execute() to REST/MCP's trust…
docs(objectql): disclose the security middleware's total isSystem byp…