# @evolv/flow-lint Lint Node-RED flow JSON against the EVOLV flow-layout rule (`.claude/rules/node-red-flow-layout.md`). ## Usage ```bash # every examples/*.flow.json across all nodes node tools/flow-lint/bin/flow-lint.js # one flow node tools/flow-lint/bin/flow-lint.js nodes/rotatingMachine/examples/01-Basic.flow.json # CI / JSON output (one line per flow) node tools/flow-lint/bin/flow-lint.js --json ``` Exit code `1` on any `error`-severity finding; `0` otherwise (warnings do not fail). ## Rules | Rule | Severity | Catches | |---|---|---| | `UI_XY` | error | `ui-*` nodes with both `x` and `y` at 0 — pile up at canvas origin. | | `UI_CHART_PROP` | error | `ui-chart` missing one of the required-or-it-renders-blank properties (interpolation, yAxisProperty, …). | | `UI_CHART_TYPE` | warn | `width` / `height` as string instead of number. | | `UI_CHART_PALETTE` | warn | `colors` array with fewer than 3 entries. | | `INJECT_JSON_PROPS` | error | `inject` with `payloadType:"json"` but missing `props[]` — silently treated as string. | | `LINK_CHANNEL_NAME` | warn | `link out` / `link in` without a `cmd:` / `evt:` / `setup:` prefix. | | `LINK_BROKEN` | error | `link out` / `link in` referencing a non-existent node id. | | `LINK_PAIR_TYPE` | error | `link out` paired with anything other than `link in`, or vice versa. | | `LINK_PAIR_ASYMMETRIC` | error | One side references the other but the partner does not link back. | | `DEBUG_LOG_IN_FLOW` | warn | `enableLog:"debug"` shipped in a flow — fills container log. | | `SELF_LOOP` | error | Any node wired to itself. | | `GROUP_WIDTH_SUM` | warn | `ui-group` widths on a `ui-page` summing to non-multiple of 12 — leaves grid gaps. | ## Why use it Every rule corresponds to a bug we've actually hit: - `UI_XY` and `UI_CHART_PROP` — the dashboard pile-up + blank-chart bugs. - `INJECT_JSON_PROPS` — the silent-string inject bug. - `LINK_PAIR_ASYMMETRIC` — drift between named channels. - `SELF_LOOP` — the 250k-msg/s `replace_all` incident. Run before committing any flow change.