Gating with tags

Use --is-fresh to require that an expensive check already passed, and tags to decide which rules a gate is allowed to ask about.

The inverse of wrapping

mmz <command> runs a stale command. mmz --is-fresh -- <command> refuses it:

With no command, mmz --is-fresh gates every rule in the manifest at once.

That is what a hook wants. A pre-push check can require that an expensive suite was already run and memoized, without paying to run it at the least convenient moment:

# pre-push: refuse the push if the VM checks were not run, but never run them here
mmz --is-fresh -- just check-vm || {
echo "stale: run 'just check-vm' first" >&2
exit 1
}

A non-fresh gate prints one line per offender, naming the rule and why it would re-run:

mmz: `./bin/validate.sh` is stale (inputs changed since it last passed)
mmz: `./bin/report.sh` is stale (inputs changed since it last passed)
mmz: re-run each listed command under mmz (e.g. `mmz ./bin/validate.sh`) to record a pass; a standalone run is not tracked

The trailing hint matters more than it looks. mmz only observes commands it wraps, so running the check by hand records nothing and leaves the rule exactly as stale as it found it. The gate says so rather than letting someone loop.

Tags

A rule can carry tags:, and --is-fresh --tag <tag> (or --status --tag) narrows to rules carrying every listed tag:

commands:
- name: cargo test
inputs: [rust]
tags: [gate]
- name: cargo bench
inputs: [rust]
tags: [bench]

mmz --is-fresh --tag gate here checks only cargo test. A bare mmz --is-fresh still checks both.

RULE             STATE  AGE
./bin/report.sh fresh 0s ago

Repeat --tag/-t to require more than one; repeats AND together. There is no OR — call mmz --is-fresh once per tag for that. A rule with no tags: never matches a --tag filter, and combining --tag with a targeted command is a usage error, since a command already resolves to exactly one rule.

Tags are case-faithful and trimmed, blank entries are dropped, and declaring the same tag twice on one rule is a manifest error.

A gate over nothing is refused

mmz --is-fresh --tag gats — one letter off — selects no rule, and a gate over no rule is vacuously true. That is a pass nobody earned, so it is an error (exit 7) naming the tags it filtered on and listing the ones the manifest actually declares:

mmz: no rule carries tag `gats`, so this gate would pass without checking anything; the manifest declares `gate`, `report`

The same refusal covers the other two ways a selection empties out: a manifest that declares no commands: at all, and a selected rule that fans over a scope resolving to no files. Each of them would otherwise exit 0 — the answer of a gate that checked nothing, spelled exactly like the answer of one that checked everything and found it fresh.

The code is its own, not 1, because the two are different questions for whoever branches on $?: 1 says the build is stale, 7 says the gate is pointed at nothing. A gate tag dropped by one bad merge is the second, and it must not read as the first.

--status over the same empty selection still exits 0 — a report asserts nothing, so an empty one is a fine answer — but it says which emptiness it is rather than claiming no rules are defined:

no rule in ./.mmz/config.yaml carries tag `gats`; the manifest declares `gate`, `report`
This is why the tag a project gates on is worth grepping for after a rename. mmz --status --tag <tag> is the cheap check: it shows the selection the gate would assert over, without asserting anything.

Why this is a tag and not a second manifest

One manifest can now hold a gating subset alongside memoized commands a gate should ignore. Without tags the alternatives are both worse: split .mmz/config.yaml per concern and maintain two files that share scopes, or let the gate assert freshness of rules it has no business blocking on.

Tag the rules a gate is allowed to fail on, not everything you memoize. A benchmark that nobody must run before pushing has no business turning a push red.

mmz gates itself

This repository is the worked example. .mmz/config.yaml tags every just check arm gate, and closing a task in the MindTape backlog runs:

mmz --is-fresh --tag gate

which passes only when every gate-tagged rule last succeeded with its inputs unchanged. So “done” means the build actually passed against this worktree — asserted, not claimed — and asserting it costs nothing, because the assertion is a hash comparison rather than a re-run. Run just check to record a pass, then flip; --force waives the gate and records the waiver.

Where to go next