Repository navigation
Description
Found while implementing #5633. Filed unassigned, observation-class (finding, no pm:queue): no shipped behaviour is wrong — this is test fixtures describing a container the schema rejects, so the coverage they advertise is weaker than it reads.
What
packages/lint/src/lint-flow-patterns.test.ts binds the loop item with itemVar in three pre-existing fixtures:
loopBodyFlow()— the sharedloop-body helper introduced by flow lint rules never descend into aloopbody — the whole family is blind to nested nodes (8 real inert conditions shipped pastflow-inert-node-condition) #5383, used by most of that PR's nested cases;- the nested-loop case inside the
#5383block; - the nested-loop case inside the
#5482(flow-multi-write-unfiltered) block.
LoopConfigSchema (packages/spec/src/automation/control-flow.zod.ts) is a strictObject, and the declared key is iteratorVariable. itemVar is neither declared nor aliased, so it is reported as an unrecognized_key rather than dropped (#4001):
REJECTED itemVar unrecognized_keys=[["itemVar"]] (the only issue)
PARSES iteratorVariable unrecognized_keys=[]
REJECTED itemVariable unrecognized_keys=[["itemVariable"]] (declared alias -> guidance)
(itemVariable is deliberately registered as an alias so the edit-distance suggester cannot send an author to indexVariable — see the comment above LoopConfigSchema's aliases. itemVar is not in that set.)
Why the tests do not notice
Region collection reads config.body. collectFlowGraphs / regionSlotsOf never look at the item-binding key, so a fixture spelling itemVar descends and gets scanned exactly like a correct one — every assertion in those cases passes for the right reason about the rule, while the fixture describes a loop an author cannot write. Nothing in the suite parses these fixtures, so nothing goes red.
That is the same shape this very file already documents for a trigger descriptor — the #4966 note above the time-relative case, which added expect(TimeRelativeTriggerSchema.safeParse(...).success).toBe(true) precisely because "a fixture the runtime would refuse teaches the wrong shape to every reader of this file". This is that finding one container down.
Scope, and what is already fixed
Fixtures added by #5633 (PR #5695) are already correct: re-spelled to iteratorVariable, given the label that FlowNodeSchema requires on the body nodes, and pinned against LoopConfigSchema in both directions (fixture parses green; the near-miss spelling really is rejected, so the pin has teeth). That PR deliberately did not touch the three pre-existing fixtures — they are not exposed by its change, and re-spelling someone else's fixtures is scope creep on a build-gating PR.
So what remains is exactly those three call sites.
Fix
Mechanical: itemVar to iteratorVariable at the three sites, plus the label the body nodes need if the fixture is to parse fully, plus a LoopConfigSchema pin on the shared loopBodyFlow helper so the whole family cannot rot back. Worth checking in the same pass whether other container fixtures in the flow-lint tests spell undeclared keys on parallel branches or try_catch regions — the audit is one safeParse per fixture and is the durable half of the fix.
Note the flow-level label requirement is a separate and much broader convention in this file: essentially none of its fixtures are full-FlowSchema-green (FlowSchema requires label on the flow and on every node), because the suite passes hand-written raw literals rather than parsed stacks. Bringing the whole file to full-flow-parse-green is a different, larger decision and is not proposed here — the narrow claim is that a key a fixture writes should be a key the schema accepts.
Related
flow-runas-unscoped(severity error) still searches only top-level nodes — a scheduled flow whose data ops all live in aloopbody passes the build and is refused at run time #5633 / PR fix(lint):flow-runas-unscoped跨区域搜索它的数据节点证据 (#5633) #5695 — where this surfaced; its own fixtures are already correct and pinned.lint-flow-patterns.test.ts的 fixture 教了一个永远绑不上的timeRelative描述符(#4001 第一类发现的第八例) #4966 — the same finding class on a trigger descriptor, in the same test file.- 未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001 — why an undeclared container key is now reported instead of silently dropped.
- flow lint rules never descend into a
loopbody — the whole family is blind to nested nodes (8 real inert conditions shipped pastflow-inert-node-condition) #5383 /multi: true且filter为空的 delete_record / update_record 是「按声明清空整个对象」,authoring 期零诊断 —— #3810 的守卫按「条件被抹掉」判定,不按「条件为空」判定 #5482 — the PRs that introduced the three fixtures.
Activity
os-zhuang commentedon Aug 6, 2026
发现分诊(finding 轮):晋级 —— 摘
finding,入pm:queue,域domain:devx。packages/lint/src/lint-flow-patterns.test.ts三处 fixture(loopBodyFlow()helper、flow lint rules never descend into aloopbody — the whole family is blind to nested nodes (8 real inert conditions shipped pastflow-inert-node-condition) #5383 嵌套用例、multi: true且filter为空的 delete_record / update_record 是「按声明清空整个对象」,authoring 期零诊断 —— #3810 的守卫按「条件被抹掉」判定,不按「条件为空」判定 #5482 嵌套用例)itemVar→iteratorVariable+ body 节点补label+ 给共享 helper 上LoopConfigSchemapin —— test-only 机械修,lint-flow-patterns.test.ts的 fixture 教了一个永远绑不上的timeRelative描述符(#4001 第一类发现的第八例) #4966 同类先例已在同一文件。packages/lint→domain:devx。parallel/try_catchfixture 键位审计(每 fixture 一次safeParse)属本单的 durable 半边,做;⛔ 全文件 full-FlowSchema-green 是另一个更大的决定,本单不做。flow-runas-unscoped(severity error) still searches only top-level nodes — a scheduled flow whose data ops all live in aloopbody passes the build and is refused at run time #5633 / PR fix(lint):flow-runas-unscoped跨区域搜索它的数据节点证据 (#5633) #5695 已合入(origin/main HEADeb26126),其自带 fixture 已正确并 pin,余量恰为正文点名的三处 —— 前提新鲜。flow-runas-unscoped's "the defaultrunAs:'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(同轮晋级,lint-flow-patterns家族的 rule 源 + 同一测试文件)同车道,由 devx 车道跨轮串行,不挂 Blocked-by。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
hotlong commentedon Aug 7, 2026
⛔ 认领(claim)
session_01BDmDsu2575gDxeMCxXhDE3(devx 车道执行座位 PM)claude/issue-5700-loop-fixture-iterator-variablepackages/lint/src/lint-flow-patterns.test.ts(test-only)—— 与在飞 跑pnpm objectui:refresh并落 console pin —— pin 落后 objectui main 98 个 commit,两条 v17 破坏性变更与 102 条待发 changeset 声明目前进不了 v17 #6089(console pin 族)、check-override-consistency 增加「override 有无消费者」体检(报告而非 red)—— #5835 裁 A 的配套正交项 #6046→PR feat(scripts): check-override-consistency 增加 override 消费者体检与自失效上界报告 #6093(check-override-consistency)不相交。flow-runas-unscoped's "the defaultrunAs:'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 同文件家族)已解除 ——flow-runas-unscoped's "the defaultrunAs:'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→PR fix(lint):flow-runas-unscoped用一句两读皆真的措辞命名有效身份 (#5693) #6060 于 2026-08-07 00:53Z MERGED。origin/main@be59695,已含 fix(lint):flow-runas-unscoped用一句两读皆真的措辞命名有效身份 (#5693) #6060):三处itemVarfixture 仍在(lint-flow-patterns.test.ts:1103/1163/1518,即 loopBodyFlow helper、flow lint rules never descend into aloopbody — the whole family is blind to nested nodes (8 real inert conditions shipped pastflow-inert-node-condition) #5383 嵌套用例、multi: true且filter为空的 delete_record / update_record 是「按声明清空整个对象」,authoring 期零诊断 —— #3810 的守卫按「条件被抹掉」判定,不按「条件为空」判定 #5482 嵌套用例);fix(lint):flow-runas-unscoped跨区域搜索它的数据节点证据 (#5633) #5695 自带的 pin/near-miss(:518/:533)不在本单面内。前提新鲜。label+ 共享 helper 上LoopConfigSchemapin +parallel/try_catchfixture 键位审计(每 fixture 一次safeParse);⛔ 全文件 full-FlowSchema-green 不做。派 1 名 os-dev 落地;test-only 走
skip-changeset。Generated by Claude Code