ADR 0109 — Lint health is read together
Status: accepted · Date: 2026-11-07
This amends the roadmap point of ADR 0105 that painted rest findings on the Docs and config sheets. Each artifact’s coverage stays the one in ADR 0107. Risk does not change: it still reads scopeHealth for each code file (ADR 0108).
ADR 0110 amends decision 3: code can be complete when each tool declares its universe, without waiting for the artifact to carry an enumeration that was never written.
Context
Section titled “Context”Code is stored in lint.repmod.json and documents in lint-rest.repmod.json. Each has its own meta.tree. The Code card read only the first, so an empty 1.2 legacy file came out as unknown coverage and the doc/broken-link findings ended up in Docs and config, mixed with the catalog.
Adding the two in the interface double-counts as soon as someone adds them again. One adapter is enough.
Decision
Section titled “Decision”- The sum lives in a pure function. It does not write artifacts, it does not change
domain, and it does not store per-folder health.
function scopeHealthMerged( artifacts: readonly (LintArtifact | null)[], scope: HealthScope, snapshot?: HealthSnapshot): MergedScopeHealth- Findings merge by canonical path. Each one keeps
domain,toolandruleId. If the code artifact does not say which tool wrote the message —eslint and oxlint share the file—, the tool is inferred from theruleIdagainstmeta.systemsand the finding isinferred. If both could have written it, it is not duplicated:tooljoins the candidates.anchornames the map (codeorrest) and the path, so the UI can open the node. - Coverage is per origin and per path. An enumerated document is
enumeratedon itsmeasuredPaths. A legacy origin islegacy/unknownon its own, with reasonlegacy-version(“measured with an earlier version”). A 1.0–1.2summary.measured > 0does not becomecomplete. A 1.3 code artifact with ESLint enumerated andeligiblePaths: null, and oxlintunknown, stayspartial: it is not forced tocomplete. - The aggregate is clean only when every domain applicable to the selection is
complete, withfresh === trueand zero findings. An applicable legacy orunknownis not clean. A domain the canonical path leaves out does not count. - At the project root, documents are not cut by the code snapshot’s path list. In a folder and a file, the canonical path decides.
meta.treeis not merged. Each origin keeps its own and its freshness. If the snapshot is the other origin’s tree, this origin’s freshness staysnull: it is not called stale and the two trees are not pretended to be one.- Risk does not call
scopeHealthMerged. It still reads the code artifact. - Docs and config reads the catalog, not the findings.
systemActivityderivesactiveRulesandcountState(known-complete,partialorunknown) frommeta.systems. They are not written in the file. Oxlint with the note “no se enumeran” and no list isunknown, never zero. - The
codarchsystem’s rules come fromcodarchActiveRulesinpackages/lint/src/rest.ts: broken links always, and orphans, frontmatter and twins only when thedocs:block turns them on. They do not come from the messages.buildRestLintwrites them inmeta.systems[].rules, a field that already existed.
Checksum
Section titled “Checksum”The artifact checksum is still the hash of { domain, tools, systems, files, coverage }. countState, activeRules, inferred and anchor are not in it: they are computed on read.
Writing rules on the codarch system does change the checksum of a newly built lint-rest.repmod.json, because systems was already part of the input. An artifact already on disk is not recomputed: readLintArtifact keeps the file’s checksum. A system without rules still reads; if the list is missing, the catalog shows codarchRuleCatalog() and not the findings.
Consequences
Section titled “Consequences”- The Code card shows health and findings from both domains, filtered by the selection. Docs and config does not show finding counts or affected files.
- A
.mdfile is not a node of the code map: the analysis extensions do not include it. It is on the rest map. The finding’sanchorpoints at that map. If the code card does not have the node, the button cannot select it there. - Document freshness is not checked against the code checksum. With the two trees different, the aggregate is not called clean.
Alternatives discarded
Section titled “Alternatives discarded”- Rewrite both artifacts into one. One
meta.treecannot sign two trees. - Add them in each panel. The second sum does not match the first.
- Treat a legacy file with
measured > 0as verified. The number does not say which paths were looked at. - Force 1.3 code to
complete. ESLint leaves the universenulland oxlint did not enumerate: it waspartialon purpose. ADR 0110 changes the premise: both tools can now declare their universe. - Store
countStatein the file. That would add a new checksum field for a fact that comes from the note and fromrules.
Roadmap
Section titled “Roadmap”No new phase.
- ✅
scopeHealthMerged, the catalog withcountState, and thecodarchrules in the producer. - ✅ The Code card reads both origins. Docs and config stays on the catalog.
The record of what was done lives in the guide feed.