Skip to content

flow-runas-unscoped's "the default runAs:'user'" wording is unreachable on the CLI surface — the parse materializes the default, so an author who declared nothing is told they declared it #5693

Closed

Description

@os-zhuang

Found while implementing #5633. Filed unassigned, observation-class (no pm:queue): nothing is blocked and the remedy in the hint is correct either way — the defect is that a deliberate distinction in the diagnostic is dead on the surface that matters.

What

packages/lint/src/lint-flow-patterns.ts branches the message on whether runAs was authored or defaulted:

const runAs = typeof flow.runAs === 'string' ? flow.runAs : 'user';
...
const declared = typeof flow.runAs === 'string' ? `\`runAs:'${runAs}'\`` : `the default \`runAs:'user'\``;

The rule is wired input: 'parsed' (authoring-rules.ts), and the flow schema supplies 'user' as runAs's default — so by the time the rule runs on the CLI, flow.runAs is the string 'user' whether the author wrote it or not. The 'the default …' arm is therefore never taken on os validate / os build / os lint.

Measured

A real example flow (examples/app-todo/src/flows/task.flow.ts, overdue_escalation) with its runAs line deleted entirely — the author declared nothing — through os validate:

  x Author-time rules failed (1 issue)
  - flow 'overdue_escalation' - runAs: schedule-triggered flow runs as `runAs:'user'`, but a
    schedule run has no trigger user - so its data node 'update_priority' (update_record), in
    loop 'loop_overdue' body, has no identity to scope to and will be REFUSED at run time.
      rule: flow-runas-unscoped

runs as runAs:'user' — the explicit arm, for a flow with no runAs key. The unit tests take the other arm (the default runAs:'user') only because they pass raw, unparsed stack literals straight to lintFlowPatterns, so the test suite proves the branch works while no shipped surface can reach it.

Why it is worth recording rather than shrugging at

It reads as an accusation the author can disprove by looking at their file. Someone who wrote no runAs is told their flow "runs as runAs:'user'", which invites the reading "I never wrote that — the tool is confused", exactly when the tool is right and the fix is one line away. The author-vs-default distinction is real and useful (it separates "you wrote something incoherent" from "you inherited a default that does not fit a user-less trigger"), and it is currently invisible where it would help.

It is also a small instance of a shape this repo has paid for before: a branch that is exercised only by tests whose input shape differs from production's. Cf. #4984 (fixtures spelling rejected aliases kept tests green while the rule was dead) — same mechanism, much lower stakes.

Possible directions (a maintainer's call — not proposed here)

  1. Read the authored value. Have the rule consult the pre-parse stack for runAs presence, so the distinction survives. Costs a second input to a rule that is otherwise cleanly parsed.
  2. Drop the distinction. Delete the dead arm and always print runAs:'user', keeping the message honest about what it can actually know. Cheapest; loses a genuinely useful nuance.
  3. Re-word so it is true either way — e.g. "runs under runAs:'user' (the default when none is declared)" — one sentence, no new input, no dead branch.

(3) looks like the best value for the size of the problem, but this is a diagnostic-wording contract and the person who wrote the distinction may have wanted (1).

Not caused by #5633

Pre-existing: #5633 only widened where this rule looks for its evidence node and left the declared branch untouched. The behaviour above reproduces identically on origin/main at 414395b9a. Confirmed with real os validate output in both directions.

Related

Activity

  1. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    发现分诊:持有(finding 保留,补 domain:devx)。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    发现分诊(finding 轮):晋级 —— 摘 finding,入 pm:queue(域 domain:devx 已在)。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. self-assigned this
    on Aug 6, 2026
  4. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    认领:PM 循环第 3 轮(devx 车道)
    会话:session_01BDmDsu2575gDxeMCxXhDE3
    分支:claude/issue-5693-runas-wording-two-way-true
    Worktree:objectstack-issue-5693
    域:domain:devx
    文件面:packages/lint/src/lint-flow-patterns.ts 及其测试文件(越界即停,报告说明)

    按分诊预裁方向 (3):措辞改为两读皆真 + 死支删除/收编 + 用 parsed 形状测试钉住「explicit 支为唯一可达支」;⛔ 方向 (1)(增读 pre-parse)若 dev 判定才是对的,STOP 升级不径做。⚠️ 与 #5700 同文件家族,#5700 本轮不派、跨轮串行。


    Generated by Claude Code

  5. hotlong commented on Aug 6, 2026

    @hotlong
    Contributor

    验收(devx 车道,会话 session_01BDmDsu2575gDxeMCxXhDE3):ACCEPT → PR #6060,已转 ready 并挂 auto-merge(required 集绿即自动入队;ESLint 属平台级不可运行态 —— main 自身同 job 同窗口起不来的对照证明在案 —— 真绿前不会落地,闸门自恰)。

    交付摘要(裁方向 3):


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions