Prefix any command with mmz. When the matched rule’s declared inputs are byte-for-byte unchanged since that command last succeeded, mmz skips it and exits 0. Otherwise it runs the command, streams its output, and records the result on success.
It answers one question per invocation: is this rule’s work still done? The comparison page places it beside the build systems, task runners and compiler caches you might reach for instead.
# .mmz/config.yaml
scopes:
rust:
commands:
- name: cargo test
inputs:
validate: 4 orders, 2 customers OK
mmz: skipped ./bin/validate.sh (inputs unchanged)
Installation
Quickstart covers the prebuilt binaries, nix run, and scaffolding a manifest.
Usage
The trade
mmz cannot see a dependency you did not declare, so the asymmetry is the whole contract:
- Under-declaring a rule’s inputs skips a command that should have run — a false green, and dangerous.
- Over-declaring buys an unnecessary re-run — and nothing else.
Broaden the scope when in doubt. mmz fails closed everywhere else: a missing or invalid manifest always errors, and so do an unmatched command and a matched rule with no inputs, unless strict relaxes them.
Documentation
- Quickstart — Install mmz, scaffold a manifest, and watch a command skip itself.
- Concepts — The model behind mmz: rules, records, freshness, and the asymmetry that decides every design question.
- Inputs: scopes and probes — Named glob sets, the gitignore filter and how to opt one scope out of it, and probes for the inputs that are not files.
- Code inputs: matching source with AST patterns — The `ast:` selector — depending on a function, a type or an impl block instead of a whole file — what it hashes, how `capture:` narrows a match to the parts that matter, and which grammars a build carries.
- Matching and parametric rules — How an invocation is matched to a rule, how the cache identity follows from that, and how one rule can fan over a scope's files.
- Declared outputs — A producer command's record can be undone without touching an input. Declaring what a run leaves behind is how mmz notices.
- 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.
- Composing a manifest from imports — Why imports: merges files instead of overriding them, the one path rule that trips everyone the first time, and how to debug what a composed manifest actually resolved to.
- Manifest reference — Every key .mmz/config.yaml can declare, generated from the JSON Schema the binary ships.
- CLI reference — Every action mmz accepts, every exit code it returns, and the JSON it can be asked for — generated from the binary's own help text.
- Rust library — mmz ships as a library crate as well as a binary; the binary is a thin wrapper over it.
- 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.
- Comparison — Where mmz sits among build systems, task runners and compiler caches — and which of them you should reach for instead.
Contributing
See CONTRIBUTING.md. mmz memoizes its own checks, so just check is itself the worked example — and closing a task asserts those checks already passed with mmz --is-fresh --tag gate.
License
MIT.