Lints
lintRules([SEC_01])Returns warnings. They never fail a build, the CLI prints them to stderr, and they never affect its exit code — a warning is advice, and advice that can break your pipeline stops being advice.
| Code | Meaning |
|---|---|
extreme-bound |
A window above 10,000 events. Obligation state is O(k), so a very wide window is usually a mistake rather than an intention. |
unknown-event-kind |
A matcher naming a kind outside the current event model. |
unknown-event-kind is deliberately a warning rather than an error. A rule set written against a newer adapter must
still load on an older engine, matching nothing rather than failing to parse — which is the same forward-compatibility
stance propositions takes.
Is this rule about anything?
Section titled “Is this rule about anything?”lintRules reads a rule set alone, so it cannot know whether restartService is a tool you have. lintNames can,
given the tools and steps that actually exist:
lintNames(SPEC, { tools })Whatever you already hold counts as a tool set — a list of ids, a list of the tools themselves, the two mixed, or the
record you handed the guard. A record is read the way the guard reads it, tool.id ?? key, so the lint cannot disagree
with the thing it is checking.
lintNames(SPEC, { tools: ['queryMetrics', restartService], steps: workflow.steps })| Code | Meaning |
|---|---|
unknown-tool |
A rule or check names a tool nothing provides. |
unknown-step |
A rule names a step the workflow does not have. |
This is the drift the type system cannot reach. Passing the tool itself makes a rename a compile error — but a rule set loaded from JSON has only names, and tools built per run by a factory have no symbol to point at. In both cases a renamed tool leaves a rule matching something nothing emits, and such a rule does not fail. It goes quietly satisfied forever, which reads exactly like a rule that is working.
A near-miss is named, because most of these are typos:
no tool called 'restartServcie' is available, so this rule can never fire — did you mean 'restartService'?What it will not guess at
Section titled “What it will not guess at”Only the dimension you supply. Pass tools alone and steps are left alone entirely — a tool guard knows nothing
about steps, and flagging every step would make the lint useless in the place it is easiest to run.
Nothing without a name. P.toolCall() is about every call, and a custom predicate carries code rather than a name.
Neither can go stale, so neither is checked.
A warning, never an error — for the same reason the others are. A rule about a tool you deliberately do not guard in
this run is a legitimate thing to have, and a check that refused it would be wrong more often than right. Where you want
it to bite, assert on it: expect(lintNames(SPEC, { tools })).toEqual([]) in the test suite is
the place it belongs.
For the questions a linter cannot answer — whether a rule can ever fire, whether two rules can hold at once, what the shortest trace that breaks one looks like — see static analysis, which reasons with a solver rather than by inspection.