| id | 94 |
|---|---|
| title | Lint-once for `<?include?>` and `<?catalog?>` embeds |
| status | ✅ |
| model | sonnet |
| summary | Diagnose embedded content only against its source file, never against the host that pulled it in via `<?include?>` or `<?catalog?>`. Avoids duplicate findings and contradictory rule settings. |
When <?include?> or <?catalog?> pulls Markdown into
a host file, lint each byte of that content once —
in its source file, under its source file's effective
config. The host file should not re-diagnose the
embedded bytes.
Today, content embedded into a host file via
<?include?> or <?catalog?> is parsed as part of the
host. If a fragment includes a heading or a paragraph,
that heading/paragraph runs through the host's rules
and may be flagged twice — once when the fragment is
linted on its own, once as part of the host. The two
files can also have different effective configs (e.g.
different kinds in plan 92), which produces
inconsistent diagnostics for the same bytes.
The fix is lint-once. The host's rules apply to the host's own bytes (everything outside generated sections). The fragment is responsible for its own bytes — checked when the fragment itself is linted.
The engine already knows which spans of a host's
parsed AST came from a generated section (the
<?include?> / <?catalog?> body). It must mark each
diagnostic emitted by a rule with the source file
that owned the byte range. For host-owned spans, the
source is the host. For embedded spans, the source is
the fragment.
When a rule emits a diagnostic, the engine drops any diagnostic whose source file is not the file currently being linted. The fragment, when linted on its own, emits its own diagnostics directly.
<?catalog?> produces row content from front matter of
many files. Each row's source attribution is the file
the front matter came from, not the catalog host.
Errors in a row's data show up against that source
file when it's linted, not against the catalog host.
Per-file metrics (metrics rank) follow the same
authored-only model: a host file's reported metrics
count only the host's authored bytes, not the
embedded content from <?include?> or <?catalog?>.
The fragment's own metrics are reported against the
fragment when it's measured directly. This matches
the lint-once attribution and avoids double counting
across a host plus its sources. Worded as a behavior
change: today the host's metric counts include the
generated bytes; this plan switches them to authored
bytes only.
Today mdsmith fix already regenerates <?include?>
and <?catalog?> bodies from source. After this plan,
the check path matches: embedded bytes are no
longer the host's responsibility either to diagnose
or to fix.
- Extend the AST/diagnostic data model to attribute
each span to its source file. (
lint.LineRange,lint.File.GeneratedRanges,gensection.FindAllGeneratedRanges) - At diagnostic emit time, drop diagnostics whose
source file is not the file under lint.
(
checker.FilterGeneratedDiags, called fromCheckRulesbeforeAdjustDiagnostics;GeneratedRangespopulated inrunner.Run/runner.RunSource) - Audit rules that walk the AST: ensure none of them short-circuit on host-owned vs embedded spans in a way that produces missing diagnostics on the source file. (No rule changes required — filtering is applied at the engine layer after rules emit.)
- Add fixtures: a host with
<?include?>of a fragment that contains a known violation; lint the host (no diagnostic from the embedded bytes) and the fragment (diagnostic appears). (engine.TestLintOnce_IncludeHost) - Same for
<?catalog?>— front-matter validation errors in a row's source surface against the source, not the catalog host. (engine.TestLintOnce_CatalogHost) - Update
metrics rankto count authored bytes only (skip generated-section content). Document the change indocs/reference/cli.md. (gensection.AuthoredSourceapplied inmetrics.Collect;metrics.TestCollect_AuthoredMetrics)
- A host file with
<?include?>of a fragment containing a known violation produces no diagnostic for that violation when the host is linted in isolation. - The same fragment, linted on its own, produces the diagnostic.
- A host with
<?catalog?>does not surface front-matter errors that originate in catalog row sources; those errors surface against the source file (covered by test). - No rule short-circuits on host-owned vs embedded spans in a way that suppresses a diagnostic on the source file (covered by test).
- Existing fix behavior for
<?include?>and<?catalog?>is unchanged (regression test). -
metrics rankreports authored-only metrics: a host with included content has the same metric values as the same host with the generated section emptied (covered by test). - All tests pass:
go test ./... -
go tool golangci-lint runreports no issues