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

MGS1037: a tool's observation is keyed as its version

A spell declares the same command as a tool's probe and its observe, and declares no key to narrow the version half:

[fail] observation keyed as version: 1 tool(s) declare one command as both version and
observation probe without narrowing the version half, so a value moving on the feed's
clock invalidates targets that never run the tool
    spell "go" tool "govulncheck" declares `govulncheck -version` as both probe and
    observe with no key, so everything that command prints keys every target in every
    project binding the spell, not just the targets driving the tool

The two probes differ in reach, not in what they run

That is the whole of it, and it is easy to miss because the command can be identical.

An observation is scoped. It reaches the targets whose ops drive the binary, worked out from the same static op list magus describe target prints. A target that never composes the op carries no obs: line.

A version probe is not scoped. It runs for every project that binds the spell and lands in the key of every target in that project, because a toolchain upgrade changes what every target produces. That is correct for a version and wrong for anything else.

Declare one command as both and narrow neither, and the output's observation-class half travels down the unscoped channel. Whatever moves on the feed's clock then invalidates targets that never run the tool.

What it cost when it happened

The go spell declared govulncheck -version as both. That command prints four lines, and the last is the vulnerability database's publication date:

Go: go1.26.6
Scanner: govulncheck@v1.3.0
DB: https://vuln.go.dev
DB updated: 2026-09-16 18:00:43 +0000 UTC

With no key, the whole output keyed the cache, so build in a project that never scanned anything carried the database's publication date. Every release of that database invalidated every build, test and lint entry in every Go project. The scoped observe probe was already declared and already correct; the version probe was a second path around it.

Resolve it

Which of the two applies depends on whether the command can report the tool's own version separately from the feed's.

Drop the version probe, keep the observation. Right when it cannot. govulncheck -version prints the Go version first, so extracting a semver takes the wrong tool's version and throws the database date away. The observation still carries all four lines to the targets that drive the op, so nothing that should invalidate stops invalidating. Check first whether the tool declares supported bounds: dropping the probe drops the version window with it, and a tool with no bounds was feeding no gate.

Declare a VersionKey that extracts the version alone. Right when it can. A genuine tool upgrade then keys every target, as it should, while the feed keys only its drivers.

Keying on a command's whole output stays legal. The zero VersionKey is deliberately conservative, because it is the only setting that cannot silently discard something the tool considers part of its identity. This check does not forbid that; it requires that you meant it, in the one case where conservative is the wrong default.

Why it is declared, not measured

The hazard is visible in the spell's tool table, so nothing has to run to find it. That also means the check cannot tell you whether a probe's output actually contains something that moves. It reports the shape that makes the leak possible, and the two fixes above are how an author says which they intended.

See also

  • MGS1033 names the other half of this pairing: a cacheable target composing a reads-external op with no probe and no skip_cache.
  • Cache on observations versus versions, and the invariant a probed observation has to satisfy.
MGS1037magusfilecacheobservationsversionsscannersspells
Last updated (b4f68ff3)
Glossary

Project

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

Magusfile

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

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. See targets.

Op

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

Spell

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

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

Window

The terminal a command runs in. It keys fire-once notices for a caller no host gave a session, and is never recorded as a session.

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.

Lease

The grant a holder takes on a job: the write and read paths that job declared, enforced in the checkout that took it with magus job exec. A job is the piece of work; a lease is permission over it.

Conventions

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