MGS1036: a narrowed footprint dropped the files its own ops read
A target declares its own footprint with ctx.readsFiles(...) and then calls a spell op
that reads a kind of file the footprint never names. Editing those files does not move the
key, so the op replays rather than runs.
[fail] footprint-drops-op-globs: 1 target(s) replace their cache footprint and then run an
op over files that footprint never names, so an edit to those files replays the op
instead of running it
libs/testlayout: format declares its own footprint and calls go[go-fmt], whose spell
reads **/*.go **/*.txtar **/*.s **/*.S **/*.c **/*.h go.mod go.sum go.work go.work.sum;
no declared glob names any of them
Why this matters
ctx.readsFiles is an ownership boundary, not an extra margin. The moment a target
declares one, buildStep resets that target's sources to the magusfiles and folds back
only the refs the body declared plus the spell sources specific to that target. The
project-wide baseline is gone, and so is every glob a spell contributes project-wide.
Most spells contribute project-wide. The go spell's mgs_listRequiredGlobs takes no
target argument, so every glob it has is project-wide, and a target that narrows its
footprint keeps none of them unless it says so.
What makes this worth a code rather than a comment is that the failure is green. The
op is skipped, not failed. A sibling target that kept the baseline still re-runs, so a
ci composing both passes while the formatter or the suite never saw the change:
- A
formatnarrowed to**/*.mdstill callsgo-fmt. A pure-Go edit replays it, so nothing is reformatted, andlint,buildandtestall re-run and pass. - A
testnarrowed to four console files drops**/*.go. Measured in this repository: about 8,400 new lines of*_test.golanded andmagus run test .replayed the cached verdict, coverage profile untouched, until--no-cache.
What it does not report
Narrowing on purpose. A footprint naming one *.go path has said what it reads; the
check cannot know whether that path is the right one, and does not guess. It fires only
when the footprint names no file of any kind the spell reads, which is the difference
between a claim an author made and one they forgot.
ctx.modifiesExistingFiles. Those refs fold into the target's sources exactly as
inputs do, so a target that declares what it edits has keyed those files. That is the
correct declaration for gofmt and dprint, which amend authored files rather than
producing them, and reporting it would be reporting the fix.
A target with no footprint of its own. Without ctx.readsFiles the project baseline
keys the target and there is nothing to drop.
A spell that declares globs per target. Those survive the reset (WithTargetSources),
so such a spell loses nothing.
Resolve it
Name what the ops read, in the declaration that matches the relationship:
export fun format(ctx: magus\Context, args: [str]) > void {
ctx.readsFiles("dprint.json");
// gofmt and dprint AMEND authored files rather than producing them, so these are
// modifiesExistingFiles: the bytes key the target without joining the snapshot.
ctx.modifiesExistingFiles("**/*.md", "**/*.go", "go.mod", "go.sum");
go["go-fmt"](ctx);
markdown["dprint"](ctx);
}
Declaring the files project-wide does not fix it. A project-wide sources entry
reaches only the targets that kept the baseline, which are the ones that do not read the
file; the target that does read it replaced its footprint and never sees the entry. That
is also why MGS1028 can go quiet on a declaration that keys nothing: its
check unions project sources with per-target refs, and a file declared in the wrong half
still counts as declared.
If the target genuinely does not read those files, the op does not belong in it.
See also
- Cache model - what a per-target footprint replaces, and the rule for where a glob belongs
- MGS1004 - a footprint declaration the loader never reaches
- MGS1028 - a changed file that seeds a project it does not key
- MGS1029 - a source glob rooted inside a pruned directory
- MGS1034 - a target reading a file it declares as its own output