I-MATCH-PROMOTABLE
Info-level lint. An
if
chain over an enum-typed state cell is mechanically promotable
to a
<match for=Type>
block (Tier 1).
What it means
The compiler detected an
if
/
else if
chain whose branches all discriminate on the same enum-typed
state cell with clean variant predicates — exactly the
shape that lifts mechanically to a
<match for=Type>
block. The lint does not nudge; the chain is valid scrml. It
names the opportunity.
The pairing is intentional: scrml surfaces compiler signal AND mechanical execution, but keeps the dev in the loop for both. Silent rewrites (Prettier-style auto-promote on save) would defeat the deliberateness of tier-ladder promotion as a ceremony — promotions are when the dev formally tells the system “this surface is now a state machine” and gets back stronger guarantees in exchange.
Fire conditions
The lint fires when ALL of the following hold (per SPEC §56.2):
-
The chain's leading condition reads a
@cellwhose declared type resolves to an enum. -
Each branch's condition is one of:
@cell == .Variant,@cell.is(.Variant),@cell == .Variant(payload), or@cell == .Variant msg(single-field bind). - All branches reference the SAME discriminator. Mixed-discriminator chains do not fire.
-
No compound conditions
(
&&,||, negation, side-effect guards). Compound branches surface a separate advisory (§56.4).
Three message shapes
Exhaustive (clean promotion)
I-MATCH-PROMOTABLE at app.scrml:42 — this if-else exhaustively covers Phase
(.Idle, .Loading, .Error, .Success). Run `bun scrml promote --match
app.scrml:42` to convert.
Near-miss (concrete actionable)
I-MATCH-PROMOTABLE at app.scrml:42 — this if-else covers Phase partially
(.Idle, .Loading, .Error). Missing .Success. Add the missing arm, then
run `bun scrml promote --match app.scrml:42` to convert. Once promoted,
the compiler will catch any future variant-add at the <match> site
automatically.
Wrong-discriminator (folds into W-LIFECYCLE-CANDIDATE)
When the discriminator is a string-typed cell whose RHS values
are enum-tag-shaped, the lint defers to
W-LIFECYCLE-CANDIDATE
rather than firing I-MATCH-PROMOTABLE. Two-step path: lift to
enum first, then re-run lints to surface the match-promotion.
Minimal reproducer
type Phase:enum = { Idle, Loading, Error(msg: string), Success(count: int) }
<phase> = .Idle
function load() { /* ... */ }
// I-MATCH-PROMOTABLE fires here — exhaustive shape.
${
if (@phase == .Idle) { lift <button onclick=load()>Load</button> }
else if (@phase == .Loading) { lift <p>Loading...</p> }
else if (@phase == .Error msg) { lift <p class="text-rose-700">${msg}</p> }
else if (@phase == .Success count) { lift <p>Got ${count} rows</p> }
}
How to use
-
Promote to
<match>via the CLI. Runbun scrml promote --match <file>:<line>. The CLI rewrites per-branch by the table in SPEC §56.5.2; state-children carry forward verbatim. The file gains structural exhaustiveness checking (any future variant addition fails compilation at the match site). -
Preview before lifting.
bun scrml promote --dry-run --match <file>emits a unified diff with no file mutations. Useful on large files or recursive directory runs. -
Recurse a directory.
bun scrml promote --match src/walks all.scrmlfiles. Idempotent — already-promoted sites are skipped. Non-promotable sites are reported with a one-line reason and left untouched. -
Ignore the lint.
It's info-level; the chain is valid scrml. Suppress
via lint config
lint.match-promotableper SPEC §28 if the call site is intentionally pre-promotion (e.g., prototyping).
Why info, not warning
Apps don't START at the tier ladder's top; they EVOLVE toward it. A Tier-0 if-chain on an enum-typed cell is a legitimate prototype shape — punishing it with a warning (let alone an error) would discourage the very migration path scrml is designed to support. I-MATCH-PROMOTABLE names the opportunity without nudging.
Sibling shape:
W-MATCH-RULE-INERT
is the next tier's analog — warning-level when a
rule=
on a
<match>
arm is legal-but-inert, nudging up to
<engine>.
The pair maps the two rungs of the ladder.
Related
- <match> — Tier-1 element. Structural exhaustiveness over an enum.
- <engine> — Tier-2 element. Full state-machine surface.
- W-MATCH-RULE-INERT — warning surfacing the next-rung lift (match → engine).
-
E-MATCH-NOT-EXHAUSTIVE
— the error you'd hit if a promoted
<match>left a variant uncovered.
Specification
Normative text:
SPEC §56 (promotion ergonomics + CLI surface),
§56.2 (fire conditions),
§56.3 (three message shapes),
§56.5 (CLI verb shape + per-branch rewrite rules),
§18.0.1 (target
<match for=Type>
block-form),
§28 (lint suppression configs),
§34 catalog row. Spec lives at
compiler/SPEC.md
.