magus v0.4.3 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
2 min read

MGS1018: dead output glob

A project declares an output glob that matches no files, while other globs the same project declares do match. The glob is almost always inherited from a bound spell that this project never satisfies.

Why this matters, and why it hides

Declared outputs are per project, and a bound spell contributes its provides globs to every target on that project. A target that declares nothing of its own (ctx.writesFiles) inherits the whole set. So a check-only target (a test, a lint) inherits an output glob it was never going to write.

That is harmless until the target has to snapshot, which only happens on a cache miss. As long as the target keeps replaying, the code path never runs. The first thing to see the failure is a cold cache: a fresh clone, a new CI runner, or magus clean --cache. By then the declaration that caused it may be months old, and the error arrives somewhere that looks unrelated to it.

This diagnostic moves that discovery forward, to the moment the glob stops matching.

Example

A project binds the typescript spell, which provides dist/**, but emits its bundles to gen/:

import "magus";

magus\project({
    "spells": [typescript],   // contributes dist/** to every target
    "outputs": ["gen/**"],    // what this project actually writes
});

// No ctx.writesFiles, so this inherits dist/** - a glob it never produces.
export fun test(ctx: magus\Context, args: [str]) > void {
    magus\cmd("buzz", args: ["-t", "render.buzz"]);
}

Resolve it

Pick whichever is true of your project:

  • The target does produce files. Declare them, so its snapshot records the real artifact instead of an inherited glob:

    ctx.writesFiles("gen/assets/app.js");
    
  • The target produces nothing (a test, a check). Nothing to declare: the inherited glob should not have applied. magus only treats an empty result as an error when the target declared the outputs itself, so this case is already safe; the diagnostic is pointing at the project-level glob, not at your target.

  • The project never writes that glob at all. The spell binding is contributing an output shape this project does not use. Either the binding is wrong, or the spell's mgs_listProvidedGlobs does not describe how this project builds.

Why the check stays quiet on an unbuilt tree

If nothing a project declares has been produced yet, the project has not been built, and every glob matching zero is expected. Reporting then would fire on every fresh clone and teach people to ignore the check. It only speaks when some outputs exist and one glob still matches nothing, which is the case that indicates a real mistake rather than an empty tree.

See also

  • Cache model: what provides means and how a snapshot works
  • Spells: what a bound spell contributes to a project
  • MGS1005: a per-target output glob that duplicates a project-wide one
MGS1018magusfileoutputscachesnapshotdoctorspells
Last updated (353014be)
Earlier changes on this page (5)

Full history ↗ · Blame source ↗

Glossary

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. Project in full.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. Magusfile in full.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. Target in full.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). Op in full.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. Spell in full.

Ward

A coded diagnostic that inspects a resolved op and nudges or blocks an anti-pattern before it runs. Ward in full.

Buzz

The language magusfiles are written in (the .buzz engine). Buzz in full.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. Cache in full.

CI

An ordinary target you compose yourself with ctx.needs; magus does not hardcode its stages. It is the anchor magus affected ci keys off, and ci strips the rw charm. CI in full.

Snapshot

A point-in-time view of live state (the pool's occupancy or a tick of exported metrics), as opposed to accumulated history. Snapshot in full.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate. Run in full.

Conventions

This page uses none of the site's convention markers. The full set is on the conventions page.