For AI agents

Driving mmz from an agent: machine-readable state, honest exit codes, and the one mistake an agent is most likely to make with it.

Why this page exists

An agent working in a repository pays for every re-run twice — once in wall clock, once in the context the output consumes. mmz is well suited to that: its whole job is answering “has this already passed?” without producing the output again.

But the same properties that make it useful make one failure mode likely, and it is worth stating before anything else.

Do not widen strict or narrow a scope to make a red gate go green. A rule that reports fresh because its inputs stopped resolving is worse than a rule that fails: it is a check that will never run again, and nothing will say so.

Machine-readable state

Every question has a JSON answer, so nothing needs to be parsed out of a table meant for humans:

mmz --status=json                 # every rule, its inputs, their hashes
mmz --status=json-schema # the schema for the above
mmz --schema # the manifest schema

The state vocabulary is small and total: fresh, stale, never, failed, no-inputs, missing-output, expired — see the CLI reference. Reading --status=json is the right move when a gate is red and the question is which input moved:

mmz --status=json | jq '.rules[] | select(.state != "fresh") | {name, state, missing_output}'

Honest exit codes

Every code means one thing, so branching on $? is safe:

An agent should treat 4, 5, 6 and 7 as “fix the cause”, never as “retry”.

Recording a pass

mmz observes only the commands it wraps. Running just check by hand and then asserting freshness will fail, correctly, and the gate says so:

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

So the loop is: run the check under mmz to record the pass, then assert. The assertion costs a hash comparison rather than a re-run, which is what makes it usable in a hook or at the end of a task.

mmz just check            # runs, or skips if already fresh; records on success
mmz --is-fresh --tag gate # asserts, runs nothing, exits 1 if any gate is stale

Adding a rule

When you add a memoized command, the scopes are the whole decision. The rule: list every file the command could read.

Then check your work with mmz --status=json: if the rule’s resolved input list is shorter than you expected, a glob is not matching — very often because the path is git-ignored or excluded.

When in doubt, over-declare. An unnecessary re-run costs time. A missing input costs correctness, silently, and the silence is the expensive part.

Do not commit the cache

.mmz/cache is derived, throwaway, machine-local state. mmz --init writes a .mmz/.gitignore covering it. If a cache directory ever appears in a diff, the fix is to ignore it, never to commit it.