Mill is a local-first software factory that turns approved product intent into bounded, tested, locally reviewed draft pull requests.
It is designed for a founder or small team that wants coding-agent leverage without handing one agent an open-ended ticket, production credentials, and the power to judge its own work. Product truth stays in the repository. Codex writes inside a disposable worktree. Native tests and scenarios verify the committed candidate. A separate read-only pass reviews that exact commit. Only the attended shipper can use your GitHub identity. Draft-only is the default; repositories may explicitly enable a separately approved, exact-plan merge.
Mill 0.3.1 is the current qualified public alpha and the latest release on
GitHub and npm. The
release includes provenance, an SBOM, independent exact-artifact qualification,
and verified registry and GitHub downloads. GitHub's Latest label does not
expand the public-alpha support limits. See the
release record for exact evidence and recovery
history. The v0.1.5 genesis release remains the historical trust root;
v0.2.0 was held before publication and its tag is preserved.
Coding is only one part of software delivery. The difficult failure modes live between a PRD and a merged change: ambiguous intent, changing architecture, weak acceptance tests, context drift, cumulative regressions, credential leakage, interrupted side effects, noisy review, and artifacts that cannot be reconstructed later.
Mill makes those boundaries explicit:
- approved outcomes, invariants, scenarios, impacts, and tasks are versioned in Git;
- each run is bound to an exact base, authority closure, worker profile, budget, candidate commit, validation result, and review result;
- product code remains independently buildable and testable without Mill;
- the builder cannot push, merge, deploy, or rewrite the oracle that certifies its own candidate;
- GitHub mutations are separately planned, approved, journaled, and reconciled;
- one complete review is repaired systemically instead of creating micro-PR churn;
- longitudinal qualification proves that accepted behavior survives a sequence of dependent changes, not just one isolated demo;
- releases preserve and publish one independently reproduced tarball rather than rebuilding at publication time.
This is the differentiator: Mill is not another chat UI or general coding harness. It is the small, inspectable delivery control plane around the coding agent you already use.
For its one qualified shape, Mill can:
- inspect a PRD, source manifest, structured product proposal, and repository without executing repository code;
- freeze an approved product contract, stable invariants, scenarios, and per-change impact;
- create a repository from the bundled web recipe or plan a compatible adoption without overwriting existing truth;
- prepare exact npm dependencies as a separate attended network effect;
- run one approved task through Codex build, OCI validation, exact-candidate review, and one bounded repair generation;
- plan and open one draft GitHub PR through the operator's own
ghsession; - observe exact-head CI and review, optionally execute a separately approved attended merge, then verify resulting-main closure;
- back up, restore, purge, reconcile, cancel, detach, and export a redacted support bundle through explicit commands;
- audit the exact repository candidate and validate a public-alpha qualification record;
- derive bounded, source-revision-bound TypeScript repository evidence from a clean Git root without executing its code;
- compile an operator-supplied follow-up change request and approved impacts into dependency-checked task packets and an outcome plan.
- project a durable run into a versioned, read-only continuation packet that names the next attended safe action without taking it;
- report the built-in builder's trusted-host boundary and reject an unqualified request for isolated execution rather than silently claiming containment.
Mill does not autonomously research the web or invent a product specification in this alpha. The operator supplies the structured proposal that Mill assesses and freezes.
The first recipe is intentionally exact:
- Node.js 24.18.1 and npm 11.16.0 inside the verifier image;
- TypeScript 6.0.3;
- Next.js 16.3.4 and React 19.2.8;
- Playwright 1.62.1;
- GitHub as the only forge;
- Codex CLI with the operator's existing login;
- Docker-compatible OCI verification;
- macOS arm64 as the first candidate host tuple.
Mill itself is developed with Node.js 24.20.0 and npm 11.19.0. Exact support is
published in release qualification evidence, not inferred from nearby versions.
The 0.3.0 host tuple was exercised on September 5, 2026 and expires on October
5, 2026 at 15:35:52 UTC; its attached qualification lists every exact version
and digest. All other stacks, operating systems, architectures, forges, models,
and worker harnesses are experimental or unsupported until independently
qualified. Experimental native Node ESM/npm adoption is separate from this
qualified web recipe.
Install the qualified public alpha at its exact version with lifecycle scripts disabled:
npm install --save-dev --ignore-scripts @davidahmann/mill@0.3.1
npx --no-install millctl --versionTo develop Mill itself from a clean source checkout:
git clone https://github.com/davidahmann/mill.git
cd mill
asdf install
node_bin_dir=$(dirname "$(asdf which node)")
PATH="$node_bin_dir:$PATH" "$node_bin_dir/npm" ci --ignore-scripts
PATH="$node_bin_dir:$PATH" "$node_bin_dir/npm" run check
PATH="$node_bin_dir:$PATH" "$node_bin_dir/npm" run build
node dist/cli.js --versionThe alpha and latest npm tags both resolve to 0.3.1. Downstream
repositories should still pin the exact version so a later release cannot alter
their delivery machinery implicitly. Updating an existing Mill installation is
deliberate: preserve state and repository snapshots, inspect the release notes,
and requalify the changed toolchain. There is no automatic upgrade or proven
general downgrade path for operational state.
Start with read-only readiness. These commands do not execute repository code:
npx --no-install millctl doctor --mode inspect
npx --no-install millctl inspect --prd product/PRD.md
npx --no-install millctl adopt --scan-onlyThe remaining examples use millctl as shorthand for the installed executable;
use npx --no-install millctl when it is installed locally. Supply your actual
PRD and approved authority paths; installing the CLI does not create them.
Static discovery is a separate read-only operation. It requires a clean Git
repository root and rejects sensitive paths, symbolic links, unsafe Git
configuration, and incomplete source traversal. It parses local TypeScript and
JavaScript imports only after each regular file's physical bytes match its blob
in the captured HEAD tree. The same check protects parsed package metadata. It
reports source locations, inventories test files, and gives conservative
importer leads for named paths. Nonliteral module loads and option-bearing test
commands remain explicit unknowns. It never installs dependencies, runs the
target's code, or claims executed coverage or delivery authority.
millctl --json discover /absolute/path/to/repository --changed src/service.tsThe first increment is a deterministic TypeScript extractor, not arbitrary-stack
support, a graph database, a watcher, or an AI-generated knowledge graph. Read
product/brownfield-discovery.md and
docs/canaries/brownfield-discovery.md
for the exact boundary and real-fixture evidence.
For a source-backed specification, supply the PRD, source manifest, and structured proposal:
millctl --json plan specification \
--prd product/PRD.md \
--sources product/sources.yaml \
--proposal product/proposal.yamlReview the returned contradictions, assumptions, questions, and exact proposal digest. Approval freezes that proposal; it does not grant repository writes.
Preview the exact file plan first, then apply the same plan attended:
millctl --json new my-product --dry-run \
--prd product/PRD.md --sources product/sources.yaml \
--proposal product/proposal.yaml --approve-product sha256:<product> \
--repository-id <uuid> --approved-by <identity> \
--approved-at <iso-time> --author-name <name> --author-email <email>
millctl --json new my-product --apply --attended \
--prd product/PRD.md --sources product/sources.yaml \
--proposal product/proposal.yaml --approve-product sha256:<product> \
--approve-plan sha256:<integration-plan> --repository-id <uuid> \
--approved-by <identity> --approved-at <iso-time> \
--author-name <name> --author-email <email>Greenfield apply stages and runs the complete native recipe gate before the target becomes a Git repository. It never replaces an existing path.
Use the same two-step boundary with adopt --plan and then
adopt --apply --attended. Adoption supports only the exact recipe-compatible
Node/Next.js shape. It keeps the operator checkout unchanged, writes an isolated
branch, and blocks on conflicting product truth, drifted native oracle files,
unsafe Git state, symlinks, credential-like files, or incompatible versions.
Prepare dependencies separately because it is the only recipe step that needs registry network access:
millctl --json dependencies prepare --attendedLater verification has no network and receives read-only source.
The downstream repository owns mill.yaml, product/contract.yaml,
quality/scenarios.yaml, an approved impact manifest, and a version 2 task.
Qualify the unchanged base, then use the returned digest once:
millctl --json qualify --baseline --task product/tasks/TASK.yaml
millctl --json run --task product/tasks/TASK.yaml \
--approve sha256:<baseline-approval> --attended
millctl --json verify --task product/tasks/TASK.yaml --run <run-id>
millctl --json review --task product/tasks/TASK.yaml --run <run-id>The shorter resumable path is:
millctl --json start --prd product/PRD.md --attendedmillctl start selects exactly one approved ready outcome or resumes its sole
existing lifecycle. It checks authority before dependency or model spend.
Raise trustCeiling to propose only after configuring the exact GitHub
repository node ID, branch, allowed operator and merger, checks, review policy,
and approval TTL in mill.yaml.
millctl --json pr plan --task product/tasks/TASK.yaml --run <run-id>
millctl --json pr open --task product/tasks/TASK.yaml --run <run-id> \
--approve sha256:<delivery-plan> --attended
millctl --json pr observe --task product/tasks/TASK.yaml --run <run-id>
# By default, a human marks ready and merges in GitHub.
millctl --json pr finalize --task product/tasks/TASK.yaml --run <run-id>Or use millctl ship --draft twice: first to return the proposal, then with its
exact digest and --attended to perform it. Draft delivery never implicitly
authorizes readiness or merge.
With propose.attendedMerge: true, producer-bound required checks, strict
up-to-date protection enforced for administrators, and no bypass-role grants,
the attending operator can inspect and approve:
millctl --json pr merge-plan --task product/tasks/TASK.yaml --run <run-id> --method squash
millctl --json pr merge --task product/tasks/TASK.yaml --run <run-id> \
--approve sha256:<merge-plan> --attended
millctl --json pr finalize --task product/tasks/TASK.yaml --run <run-id>The plan binds PR/head/base, exact tree, actor, method, policy and expiry. A chat host can submit the operator's approval through this CLI; Mill does not authenticate arbitrary chat messages or let the builder approve its own work. See attended approvals and recovery.
Mill checks classic branch-protection enforcement and exact check-producer bindings. The operator must separately inspect bypass-role/ruleset grants; do not infer a complete permissions audit from a successful merge preflight.
plan tasks --request product/change.yaml deterministically compiles an
operator-supplied PRD/plan/bug/review change request and approved impact
manifests into version-2 tasks and a dependency-checked outcome plan. Its
separate --apply --approve sha256:<plan> --attended step writes an isolated
worktree. It does not infer approval or acceptance tests from prose. See
planning.
adopt-native --config adoption.yaml offers experimental Node ESM/npm adoption
that adds only mill.yaml and mill.lock, preserving existing code and native
scripts. Apply requires the exact digest and attendance; dependency preparation,
baseline qualification and task execution remain separate. See
brownfield scope and qualification.
The source implementation keeps propose.requiredChecks as the complete gate
for the exact pull-request head. Optional propose.postMergeRequiredChecks
selects a nonempty subset of those names for resulting-main readback only;
omitting it retains the full requiredChecks list for both phases. New delivery
plans approval-bind and persist both effective lists. Missing or pending
required checks prevent completion; skipped or failed required checks fail the
phase that requires them.
In this repository, dependency-review runs only for pull requests, while
validate and codeql also run on pushes to main. The maintainer-prepared
mill.yaml requires all three at the exact PR head and explicitly selects
[validate, codeql] for resulting-main readback.
New delivery records identify the policy source as configured or
implicit_default. A historical record with a full defaulted list and no
provenance can bind the configured subset once only if its exact reviewed
candidate proves omission of the optional policy and authoritative merge
readback establishes every other delivery identity. That compatibility path
cannot relax a new delivery. See
repository settings and the
migration record for the
conditions and outstanding live evidence for delivery
01801a1b-58f9-480f-8cee-54ea2bbeabb2. Human readiness, merge authority, and
exact candidate/tree checks remain required.
Mill separates four principals:
| Principal | May do | Cannot do |
|---|---|---|
| Builder | Edit approved paths in a disposable worktree | Push, merge, deploy, change authority or oracles |
| Verifier | Run declared commands in bounded no-network OCI | Write candidate source or use forge credentials |
| Reviewer | Read the exact committed candidate | Execute or edit code |
| Attended shipper | Push/open a draft; separately approved opt-in readiness/merge | Change the candidate, self-approve, bypass protection, or deploy |
Codex uses your existing Codex CLI session and therefore your own provider
billing. GitHub operations use your existing gh session. Another maintainer
can clone Mill and use their own Codex and GitHub accounts after the downstream
repo explicitly allows their identity. Mill stores neither credential.
The Codex worker runs on the trusted host with a workspace-write sandbox. This is not containment against hostile code, host files, keychains, processes, or network access. Native candidate verification is the stronger boundary: a pre-pulled digest-pinned OCI image, no network, read-only source/root, dropped capabilities, deadlines, bounded output and resources, and explicit cleanup. Do not use this alpha with hostile repositories or sensitive source.
millctl isolation --request trusted-host reports the exact built-in boundary.
millctl isolation --request isolated fails closed because no isolated builder
adapter has been qualified; run and resume accept the same --isolation
option and will not silently fall back. See the
reliability boundary.
Every run has durable state and an append-only event history. If a controller is interrupted, inspect before acting:
millctl --json status --run <run-id>
millctl --json continuation --run <run-id>
millctl --json resume --task product/tasks/TASK.yaml --run <run-id>
millctl --json cancel --run <run-id>
millctl --json pr reconcile --task product/tasks/TASK.yaml --run <run-id>Mill never signals a process solely from a stored PID and never retries an uncertain external effect without authoritative readback. Use explicit local recovery for state and diagnostics:
millctl --json state backup
millctl --json state restore --from /absolute/path/returned-backup.sqlite3
millctl --json state purge --confirm <repository-uuid>
millctl --json support-bundle --run <run-id>
millctl --json detach planstatus remains compact: it does not reveal the worktree, raw worker context,
validation/review data, logs, private commit trailers, or credentials. Its
continuation projection binds task/base/candidate/configuration identities,
observed interruption/effect uncertainty, measured resource fields, and the next
attended action without performing it. It reports provider-measured input,
output, and cache-input tokens when present, partial fields when a completed
call omitted them, and unavailable currency cost rather than an estimate.
An unresolved push, PR, readiness or merge blocks repair, new delivery and state
purge/restore even if the enclosing run says blocked or cancelled.
Cancellation records intent without discarding the receipt. Use
pr merge-reconcile for readiness/merge; a confirmed merge then requires
pr finalize and green main checks before cleanup. See
approval recovery.
Before any remote attempt, stale review scope can use
review --task product/tasks/TASK.yaml --run <run-id> --refresh --base <exact-provider-commit> --attended.
This preserves the candidate and remaining review budget, invalidates the
unexecuted delivery plan and does not move frozen Git refs. Plan delivery again
after the fresh review passes.
For generated authority, state reconcile-plans verifies the exact committed
files. A failed plan can instead be explicitly discontinued with
state abandon-plan --approve <original-plan-digest> --attended, after
preserving partial output in a clean commit on its recorded branch. Abandonment
retains evidence and does not certify successful apply. See
plan recovery.
Restore validates the database before atomic replacement and quarantines newer unreferenced worktrees. Detach is plan-only; the operator performs the reviewed removal. A generated/adopted repo must continue to build and test natively after Mill is removed.
Run purge from a surviving original checkout, never a worktree scheduled for deletion. Preserve an external state backup and committed candidate branches first. The known purge-from-a-deletable-worktree P2 can leave cleanup incomplete; it does not grant permission to discard unresolved effects or foreign files.
millctl audit is a bounded, read-only milestone check for Mill's selected
recipe and release path. It requires a clean exact Git candidate and reports
product, code, UX, accessibility, security, dependency, architecture,
operations, and release checks in a schema-valid JSON envelope. These checks are
labelled assurance: structural: they inspect contracts and hooks, not executed
security, accessibility or business behavior. Native command results, realistic
scenarios and release canaries are separate evidence.
millctl --json --cwd . audit
millctl --json --cwd . qualify public-alpha \
--file /absolute/path/qualification.jsonPublic-alpha qualification requires at least five dependent accepted changes, item-level new-behavior and preservation evidence, a rejected and recovered seeded-fault branch, a current exact support tuple, every required packed and integration canary, and all nine audits. A later success cannot conceal an earlier unresolved preservation failure.
Every qualified release also requires two independent clean builds from the exact annotated tag, canonical content equality, a preserved tarball, SBOM, trusted npm OIDC publication, provenance, registry reinstallation, and GitHub Release readback. See the release runbook.
WRONG_MILL_VERSION: run the exact package version inmill.lock; Mill does not silently delegate to another version.BASE_REF_DRIFTor context drift: stop, review the new repository state, and requalify. Never reuse the old approval digest.- missing OCI image: pull the exact digest explicitly outside Mill, then rerun readiness. Mill never pulls implicitly.
- provider login failure: run
codex loginas the operator; do not pass a token through the task or repository. - GitHub identity or destination mismatch: correct
mill.yamlor log in with the explicitly allowedghidentity. Do not weaken the binding. effect_unknown: run read-only reconciliation. Do not retry push or PR creation until absence or success is authoritative.- active-run conflict: resume or safely terminalize the existing run; do not start a second writer.
Use GitHub Issues for reproducible defects and private vulnerability reporting for security issues. Support is best-effort with no SLA.
- local and attended only;
- one repository, outcome, and writer at a time;
- one exact web recipe and compatible adoption shape;
- operator-supplied structured proposal, not autonomous planning research;
- Codex and GitHub through the operator's existing sessions;
- no hostile-host containment for the coding agent;
- no daemon, hosted control plane, scheduler, fleet, or parallel agents;
- no automatic readiness, merge, deployment, repository provisioning, or issue synchronization;
- no general migration engine, automatic upgrade/rollback, or automatic detach;
- no self-improvement loop or model-authored acceptance authority.
For system detail, read the product requirements, architecture, development guide, workflow, and agent operating contract.