Found during contract review of PR #14263 (#14089). Non-blocking for that PR — it implements the maintainer's option-C ruling exactly as worded ("flow node/edge condition") — but the mechanism the warning describes is not confined to those two slots, and the gap is worth a card so it is not lost.
The gap
PR #14263's warnShadowedFieldReads runs on exactly two expression positions per flow graph: the node condition and the edge condition. The descriptor-declared expression slots that #4027 added (resolveFlowNodeExpressions — the per-node-type expression positions such as a filter or a computed input) are checked by the existing check(...) in the same traversal but are not passed through the shadowing pass.
If those slots are evaluated in the same flattened scope seedRunVariables builds — the variable map is one per run, so a declared variable that shadows a field shadows it in every expression that scope reaches — then a bare name that is BOTH a declared flow variable AND a field on the bound object is silent there for the same reason it was silent on condition before #14263, and the author reads the variable when they meant the field.
What needs measuring first
Whether the #4027 descriptor slots are evaluated against the run's flattened variable map, or against a narrower per-node scope. If the former, the fix is one more call site of warnShadowedFieldReads inside the descriptor-slot loop in validate-expressions.ts, reusing the same declaredVariables set already collected once per flow. If the latter, this card closes as not-a-gap with that measurement.
⛔ Within option C's letter either way: the ruling excludes A/B/D (any judgement of a bare identifier for being bare); extending the shadowing-only warning to more expression positions moves no accept set and adds no error.
Refs: #14089 (the ruling, director batch #23) · #14263 (the landing) · #4027 (the descriptor slots).
Unassigned and untriaged, per the single-producer rule for domain:*.
Found during contract review of PR #14263 (#14089). Non-blocking for that PR — it implements the maintainer's option-C ruling exactly as worded ("flow node/edge condition") — but the mechanism the warning describes is not confined to those two slots, and the gap is worth a card so it is not lost.
The gap
PR #14263's
warnShadowedFieldReadsruns on exactly two expression positions per flow graph: the nodeconditionand the edgecondition. The descriptor-declared expression slots that #4027 added (resolveFlowNodeExpressions— the per-node-type expression positions such as a filter or a computed input) are checked by the existingcheck(...)in the same traversal but are not passed through the shadowing pass.If those slots are evaluated in the same flattened scope
seedRunVariablesbuilds — the variable map is one per run, so a declared variable that shadows a field shadows it in every expression that scope reaches — then a bare name that is BOTH a declared flow variable AND a field on the bound object is silent there for the same reason it was silent onconditionbefore #14263, and the author reads the variable when they meant the field.What needs measuring first
Whether the #4027 descriptor slots are evaluated against the run's flattened variable map, or against a narrower per-node scope. If the former, the fix is one more call site of
warnShadowedFieldReadsinside the descriptor-slot loop invalidate-expressions.ts, reusing the samedeclaredVariablesset already collected once per flow. If the latter, this card closes as not-a-gap with that measurement.⛔ Within option C's letter either way: the ruling excludes A/B/D (any judgement of a bare identifier for being bare); extending the shadowing-only warning to more expression positions moves no accept set and adds no error.
Refs: #14089 (the ruling, director batch #23) · #14263 (the landing) · #4027 (the descriptor slots).
Unassigned and untriaged, per the single-producer rule for
domain:*.