Skip to content

ADR-009: over lint is a separate, tolerant, pre-deserialization static-analysis pass

Status: accepted

Context

over lint could have been implemented as "try to construct every overlay via Overlay::new and report the errors" — reusing the same path apply and Repository::overlays() already use. That path is not built for diagnostics: Repository::overlays() silently warns and drops a malformed overlay, and config/serde error messages are technical ("expected one of name, root, ...", raw "caused by:" chains) rather than actionable.

Decision

lint_repository never touches a target directory and never calls Overlay ::apply. It re-implements overlay discovery so it can continue past a parse failure — reporting, for instance, "overlay X uses overlay Y, which failed to parse" instead of silently treating Y as nonexistent. Before deserializing at all, prevalidate_descriptor parses the raw TOML/YAML as a generic table and checks top-level keys against a hand-maintained VALID_ OVERLAY_KEYS list, offering a Levenshtein-distance typo suggestion (targt → "did you mean target?") instead of letting serde's error surface. Diagnostics carry a Severity (error/warning) with a defined sort order, which apply's plain anyhow::Error propagation has no equivalent of.

Consequences

Lint messages stay readable and specific — confirmed by tests that assert no raw serde/config error text leaks through. The cost is duplication: VALID_OVERLAY_KEYS must be kept in sync by hand with Overlay's actual #[derive(Deserialize)] fields — nothing generates it from the struct — so a new field added without updating the constant makes lint misreport it as an unknown-key typo. discover_overlay_dirs also duplicates Repository ::overlays()'s glob-walk logic rather than sharing it, which can drift if one changes without the other.