Commit b51793c
Suppress diagnostics inside generated sections during fix (#215)
* Suppress diagnostics inside generated sections during fix
The Fixer's pre-fix and post-fix CheckRules calls were running on
*lint.File values whose GeneratedRanges had never been populated, so
filterGeneratedDiags was a no-op. As a result, `mdsmith fix` surfaced
diagnostics inside <?catalog?> / <?include?> bodies that `mdsmith
check` correctly hid — the same source bytes produced different
results depending on which command ran them, which broke the merge
queue's pre-merge-commit hook.
Why: the runner sets GeneratedRanges before linting (runner.go:108,
:201); the Fixer didn't, leaving range-based filtering inert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Propagate parse-time fields onto post-fix lint.File
Address PR #215 review: the post-fix CheckRules call also needs
StripFrontMatter and MaxInputBytes from the pre-fix lf, not just
FrontMatter/LineOffset. Without them, rules that read secondary files
(catalog, include, requiredstructure, crossfilereferenceintegrity) or
align cross-file coordinates (duplicatedcontent) silently behave
differently between the pre-fix and post-fix passes — the same kind of
runner/fixer divergence the GeneratedRanges propagation addressed.
Extract the post-fix file construction into buildPostFixFile so all
parse-time and resolution context lives in one place, and so the next
field added to lint.File doesn't get forgotten by one of the call
sites.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Wire GitignoreFunc into Fixer for runner parity
Address PR #215 review: the catalog rule's resolveGitignore calls
f.GetGitignore() to filter glob hits, but the Fixer never set
GitignoreFunc on its lint.File. So a catalog directive whose glob
matched gitignored files would silently include them when fix
regenerated the body, but exclude them when check ran on the same
bytes — the same flavor of fix/check divergence the GeneratedRanges
and parse-time-field propagations addressed.
Mirror engine.Runner: per-dir cached GitignoreMatcher, closure
captured on lf in prepareFile, propagated to finalFile in
buildPostFixFile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Hydrate per-file context inside applyFixPasses
Address PR #215 review (thread on internal/fix/fix.go:200): the
parsedFile that fixable rules see inside applyFixPasses was only
getting FS/RootDir wired up, so catalog.Fix (calls f.GetGitignore)
and include.Fix (uses f.MaxInputBytes) silently produced different
post-fix bytes than `mdsmith check` would have validated against.
Extract a hydrateLintFile helper that copies onto a freshly-parsed
*lint.File the full per-file context the engine.Runner sets:
FS / RootFS / RootDir / FrontMatter / LineOffset /
StripFrontMatter / MaxInputBytes / GitignoreFunc / GeneratedRanges.
Use it in both applyFixPasses (for parsedFile) and buildPostFixFile
(for finalFile) so all three lint passes — pre-fix, fix-pass,
post-fix — see the same File contract.
Also add a test exercising the prepareFile branch where Fixer.RootDir
is set, which the GitignoreFunc-wiring tests had been skipping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Drop defensive error branches in new fix-path code
User feedback: don't write error checks for branches that can't be
reached. Mirror the pattern at internal/archetype/gensection/ranges.go:59
(\"NewFile never errors with current implementation\").
- buildPostFixFile: drop the lint.NewFile error return; signature
becomes plain *lint.File. Caller no longer needs to handle a
parse error after fix.
- cachedGitignore: drop the filepath.Abs fallback. On the rare error
case Abs returns the input string unchanged, which is still a
usable cache key for the inputs the fix pipeline passes
(filepath.Dir(path) or f.RootDir).
Coverage on the new functions is now 100%.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Fix cachedGitignore cache-key correctness
Address PR #215 review (thread on internal/fix/fix.go:65): the
previous "drop the Abs error fallback" simplification was wrong —
filepath.Abs returns "" on failure (not the input string), so on
the rare error path every relative dir would collide on the empty
cache key and share one matcher across unrelated directories. My
preceding comment also misdescribed Abs's error semantics.
Fix the right way: don't normalize the cache key at all.
lint.NewGitignoreMatcher does its own filepath.Abs internally to
root the matcher, so the cache key only needs to be deterministic
across calls within a Fix run, which prepareFile already
guarantees by passing the same form (filepath.Dir(path) or
f.RootDir). Use the dir string verbatim and update the comment to
match what the code actually does.
Add TestFixer_CachedGitignore_DistinctKeys documenting the cache
contract: distinct inputs yield distinct matchers, repeated input
hits the cache, empty-string input is its own entry rather than
aliasing with everything else.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Make fix-pass probe actually exercise Fix
Address PR #215 review (threads on test lines 258 and 309): the
fixPassProbeRule was returning a diagnostic on Check call #1, which
is the pre-fix engine.CheckRules pass — applyFixPasses then saw an
empty diagnostic list on call #2 and never invoked Fix. So the
\"validates hydration during applyFixPasses\" assertion was actually
just validating hydration during pre/post-fix CheckRules, which the
other tests in this file already cover.
Rework:
- Track Check and Fix snapshots separately.
- Trigger the diagnostic on Check call #2 (the applyFixPasses pass)
so Fix actually fires, then assert exactly 1 Fix snapshot.
- Pin exact phase counts: 3 Check calls, 1 Fix call.
- Assert the Fix call's lint.File also has the per-file context
hydrated, not just the Check calls — that's the real regression
guard for catalog/include rules whose Fix paths consult these
fields.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Use filepath.Clean to normalize cachedGitignore key
Address PR #215 review (thread on internal/fix/fix.go:64): the
raw-key impl created separate cache entries for equivalent forms
like "sub" vs "./sub" vs "sub/", undermining the cache when the
caller didn't pre-normalize. Switch the cache key to
filepath.Clean(dir): it's total (no error path, so no defensive
fallback needed), idempotent, and collapses all the syntactic
forms filepath.Clean considers equivalent.
Don't use filepath.Abs (which is what Runner does for cross-cwd
normalization) because Abs has a stdlib error contract that would
require a defensive fallback. The Fixer's prepareFile passes the
same form for all files in a Fix() call, so the only normalization
that matters in practice is the syntactic-equivalence collapse
that Clean already provides — and lint.NewGitignoreMatcher does
its own filepath.Abs internally to root the matcher correctly.
Extend TestFixer_CachedGitignore (now ...KeyContract) to pin the
new equivalence-collapse contract.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 43c6548 commit b51793c
2 files changed
Lines changed: 505 additions & 17 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
35 | 68 | | |
36 | 69 | | |
37 | 70 | | |
| |||
118 | 151 | | |
119 | 152 | | |
120 | 153 | | |
| 154 | + | |
121 | 155 | | |
122 | 156 | | |
123 | 157 | | |
124 | | - | |
| 158 | + | |
125 | 159 | | |
126 | 160 | | |
127 | 161 | | |
| |||
133 | 167 | | |
134 | 168 | | |
135 | 169 | | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
| 170 | + | |
146 | 171 | | |
147 | 172 | | |
148 | 173 | | |
| |||
152 | 177 | | |
153 | 178 | | |
154 | 179 | | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
155 | 212 | | |
156 | 213 | | |
157 | | - | |
| 214 | + | |
158 | 215 | | |
159 | 216 | | |
160 | 217 | | |
| |||
167 | 224 | | |
168 | 225 | | |
169 | 226 | | |
170 | | - | |
171 | | - | |
172 | | - | |
173 | | - | |
| 227 | + | |
174 | 228 | | |
175 | 229 | | |
176 | 230 | | |
| |||
220 | 274 | | |
221 | 275 | | |
222 | 276 | | |
223 | | - | |
| 277 | + | |
| 278 | + | |
224 | 279 | | |
| 280 | + | |
225 | 281 | | |
226 | 282 | | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
227 | 288 | | |
228 | 289 | | |
229 | 290 | | |
| |||
0 commit comments