Skip to content

Decision: should escalate_case declare runAs: 'system'? It would let the three escalation flags become readonly and delete a guard exemption — at the cost of raising a user-facing screen flow's privilege #1434

Description

@os-trump

Routed by the PM from #1429 (PR #1433). Human floor: this changes a permission boundary. Not dispatchable; recording the measurement so the ruling has it attached.

What #1429 measured

The readonly strip on the UPDATE path is one branch — if (!opCtx.context?.isSystem) — applied to caller-supplied keys only. So it is about the caller's context, not about which kind of writer you are:

  • a beforeUpdate hook's own ctx.input.data stamp survives in every context;
  • INSERT is exempt entirely;
  • a flow write survives exactly when that flow's effective runAs is 'system' — and the engine default is runAs = flow.runAs ?? 'user'.

is_escalated has three writers, and they disagree:

writer runAs write to a readonly field
case_escalation 'system' survives
case_sla_monitor 'system' survives
escalate_case (src/flows/case-actions.flow.ts) none declared ⇒ 'user' stripped

The least-privileged writer decides. One flow declaring no runAs is the entire reason is_escalated cannot be declared readonly, and therefore the entire reason PR #1427's STAMPED_NOT_TYPED guard exemption exists.

In-repo corroboration that the mechanism is already understood and relied on: close_case already declares runAs: 'system' precisely because it writes is_closed, which is readonly: true.

The decision

Adding runAs: 'system' to escalate_case would:

That last point is why this is not a mechanical fix. close_case is the precedent, but a precedent for one flow is not a policy for all of them, and "make it system so the declaration works" is a pattern that gets copied.

四象限

escalate_caserunAs: 'system' 不加
该加而加 三个标志可以诚实地声明为 readonly,门禁豁免连同反向 pin 一起删掉。close_case 已经是同样的先例、同样的理由。声明与执行重新对齐,读代码的人不再需要读一条注释才知道为什么这个字段可写。
该加而未加 豁免长期留着,理由正确但绕。每个新来的人都要重新理解「为什么这三个字段明明没人手输却不是 readonly」,而答案藏在另一个文件的一个缺失声明里。
不该加而加 一个用户点出来的界面流程从此以系统身份运行全部写入,不只是这三个标志。下游任何读取调用者身份的东西都拿不到真实用户。而且这个模式会被复制 —— 下一个人遇到 readonly 被剥离时,学到的解法是「把流程改成 system」。
不该加而未加 现状。代价是一条有据可查的豁免,收益是界面动作仍以发起人的身份运行。

My input, as input only

I lean against it as stated, and toward a narrower third option nobody has costed: leave escalate_case as 'user' and ask upstream whether a flow can declare per-node elevation, so the one update_record that stamps the flags runs elevated while the rest of the flow keeps the acting user. That keeps both properties. But I have not measured whether the platform offers it, and I am not going to guess — if this direction interests you, say so and I will have it established before anything is built.

⛔ Nothing is being changed in the meantime. The exemption stands with the corrected reason PR #1433 landed, which is accurate under the current declarations.

Refs #1429 · PR #1433 · PR #1427 · objectstack-ai/objectstack#9107 · #3425

Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

Labels

metadataDeclarative metadata — schema, security posture, UI surfaces

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions