Skip the work that is already done.
mmz is a memoized command runner. Prefix any command with it and it runs — then skips, for as long as the inputs you declared are byte-for-byte unchanged since that command last succeeded.
# yaml-language-server: $schema=../../../schema/mmz.schema.json
# The docs fixture's manifest. www/generate.sh runs the real `mmz` against a
# throwaway copy of this project and captures the output verbatim, so every
# transcript on the docs site is this file's actual behaviour rather than a
# hand-typed illustration.
#
# The two commands are deliberately fast and dependency-free (`sh` and `jq`,
# both already on the docs build's PATH): a fixture whose checks took a minute
# would make every docs build take a minute, and a fixture needing a toolchain
# would make the site unbuildable wherever that toolchain is missing. Between
# them they exercise all four things a rule can declare — scopes, a probe,
# declared outputs, and tags.
scopes:
# The pipeline's real inputs: the data it reads and the scripts that read it.
data: ["data/*.json"]
bin: ["bin/*.sh"]
probes:
# Both scripts shell out to jq, so a jq upgrade must bust both rules. jq is
# not a file in this project, which is exactly the gap a probe closes.
jq-version:
run: jq --version
on_hit: "mmz: skipped {cache:command} (inputs unchanged)"
commands:
# A verdict command: it passes or it fails, and leaves nothing behind.
- name: ./bin/validate.sh
inputs: [data, bin, jq-version]
tags: [gate]
# A producer command: its record is void once the artifact it promised is
# gone, however unchanged the inputs are.
- name: ./bin/report.sh
inputs: [data, bin, jq-version]
outputs:
- out/report.txt
tags: [gate, report]validate: 4 orders, 2 customers OK
mmz: skipped ./bin/validate.sh (inputs unchanged)RULE STATE AGE
./bin/validate.sh fresh 0s ago
./bin/report.sh fresh 0s agoOne rule, three moments: what you declared, what it cost, what it is worth now.
One question per run
Is this rule’s work still done?
That is the whole of it. A rule declares a command and the inputs that command depends on. mmz hashes those inputs, compares the digest against the record the last successful run left behind, and either skips or runs. There is no task graph to satisfy, no artifact to replay, no daemon to keep alive.
Built for gates
mmz --is-fresh inverts the wrapper: instead of running a stale command, it refuses one. A pre-push hook can require that an expensive check was already run and memoized, without paying to run it at the least convenient moment — see Gating with tags. mmz gates its own repository that way.
Fail closed
The failure this design fears is the silent one: a command skipped that should have run. So the asymmetry is baked in.
- Under-declaring a rule’s inputs skips work that should have happened — a false green, and dangerous.
- Over-declaring buys an unnecessary re-run — and nothing else.
Every ambiguous case therefore errors rather than guessing. A missing or invalid manifest is an error; a command matching no rule is an error; a rule whose inputs resolve to zero files is an error. Relax those last two per project with strict when you mean to — never by default. Concepts has the full contract.
What it is not
mmz is not a build system, and the boundary is deliberate — see Comparison for the whole rubric.
- No orchestration. No execution order, no dependency graph. Your task runner already does that;
mmzis a prefix on one line of it. - No output replay. Only the exit code is cached — never stdout, stderr, or artifacts. A declared output is checked for existence, never stored or restored.
- No dependency tracing. Nothing is inferred by watching syscalls. Scopes are declared, and being wrong about them is your risk to manage.
- No remote cache. State is local and throwaway. Delete it and the worst case is one honest re-run.