Invalid Combination Linter¶
A running tool — instantiates Grammar-Guided Structure Recovery
A running tool that scans a recovered structure for forbidden co-occurrences — elements each legal alone but illegal together — and dispatches a handled violation when it finds one.
The Invalid Combination Linter is the runtime that catches the errors no single node-local check can see: forbidden co-occurrences — two or more elements that are each perfectly well-formed on their own but must not appear together. Where a single validation rule inspects one node, this tool inspects relationships — mutual exclusions, required-together pairs, ordering and cardinality constraints across the structure — and it owns what happens when one fires. Its defining move is twofold: it operates on the class of errors that live only in the interaction of parts, and, as the running engine, it turns a detected conflict into a deterministic consequence rather than a passive note.
Example¶
A CI/CD pipeline config is written in YAML and has already parsed into a clean tree. A job specifies both image: and machine:. Each key is individually valid — plenty of jobs use one or the other — but together they are contradictory: you cannot run on a prebuilt image and a raw machine at once. A per-key rule sees nothing wrong; the Invalid Combination Linter, scanning the parsed structure, detects the forbidden pair within one job and dispatches a structured violation — which two keys, which job, why they conflict — and fails the run. That last part is the point: this is an error, not a style nit, so the linter's handling policy raises rather than warns.
The same tool enforces the mirror constraint elsewhere: cache: requires steps:, so a job with caching but no steps is flagged as an incomplete combination. In both cases the conflict exists only in the co-occurrence, and the linter is the thing that both finds it and decides the consequence.
How it works¶
The linter runs over the recovered structure — not the raw text — and evaluates cross-element constraints a node-local rule cannot express: mutual exclusion, required-together, ordering, and cardinality across nodes. On a hit it constructs a violation carrying the offending elements, their location, and a rationale, then dispatches it through a handling policy: raise as an exception and fail, block the operation, downgrade to a warning, or apply a mechanical fix. What distinguishes it from a single validation rule is both halves of that description — it finds combinations, and it owns the consequence of finding one.
Tuning parameters¶
- Combination arity — pairwise conflicts vs. n-way constraints spanning several co-occurring elements. Wider constraints catch subtler conflicts at higher cost.
- Handling policy — raise/fail vs. warn vs. auto-resolve — the exception behavior on a violation, and the tool's most consequential dial.
- Locality window — evaluate within a node, a subtree, or the whole document. Wider windows catch cross-cutting conflicts but cost more to check.
- Precedence — which constraint wins when several fire on overlapping elements.
- Fail-fast vs. collect-all — stop at the first violation, or gather every conflict before reporting.
When it helps, and when it misleads¶
Its strength is that it catches the invalid-combination class that node-local validation structurally cannot see, and — by owning the handling policy — converts a detected conflict into a deterministic outcome: a failed build, a blocked publish, a rejected config. Its failure modes cluster where combinations are subtle. False positives bite hardest here, because a flagged "conflict" is sometimes a valid advanced usage the rule author didn't anticipate; an over-eager fail-fast policy can mask later conflicts behind the first; and exhaustive n-way checking gets expensive. The classic misuse is encoding a team's current convention as a hard combination-error, so any novel-but-legitimate structure is rejected as malformed. The discipline that guards against it is to reserve raised exceptions for true contradictions, downgrade merely-stylistic co-occurrence preferences to warnings, and test every combination rule against real, valid inputs before it can fail a run.[1]
How it implements the components¶
The Invalid Combination Linter realizes the consequence-handling component of the archetype — deciding and dispatching what happens when a recovered structure is malformed by combination:
exception_handling— on a detected forbidden co-occurrence it constructs a structured violation and dispatches it through a handling policy (raise, block, warn, or fix), owning the deterministic response to a malformed structure.
It does not author the atomic checks it runs (those are individual Linting or Validation Rules), does not validate a structure against a versioned semantic schema (that is Semantic Schema Validation), and does not repair malformed input so parsing can continue (that is Error-Tolerant Parsing).
Related¶
- Instantiates: Grammar-Guided Structure Recovery — the linter is the archetype's runtime for combination constraints and their consequences.
- Consumes: Linting or Validation Rule supplies the constraints the linter evaluates and enforces.
- Sibling mechanisms: Linting or Validation Rule · Semantic Schema Validation · Error-Tolerant Parsing · Grammar Rule Set · Chart Parsing · Ambiguity Register
Notes¶
The exception handling here is the consequence of a validation failure on an already-recovered structure — raise, block, or warn — and is a different thing from parse-error recovery, which keeps a parser running through malformed input. Error-Tolerant Parsing owns the second; this linter owns the first. Conflating them leads to the trap of trying to "recover" from a genuine contradiction that should simply have failed the run.
References¶
[1] A mutual-exclusivity constraint ("at most one of these may be present") and its partner the co-requirement ("if one, then the other") are the canonical forbidden-combination shapes. They cannot be expressed as a property of any single node — only of a set of nodes — which is exactly why they need a combination linter rather than an atomic validation rule. ↩