ADR 0076: The engine commits discern machinery it scaffolds
Amendments.
- Vocabulary: current spellings are
mapwhere it names the command, config, or tree (formerlydocs),discern, the gate, or the bar for the retired product-category wording, known/customjob(formerly gatecapability/ customcheck), andShared file(formerlyCo-managed seed/ co-managed file); the decisions below are unchanged.- ADR 0203 — commit attribution: the setup-wiring and setup-completion commits now pass through the shared discern commit boundary. They keep the invoking user's author and committer identity and add the co-author trailer by default; the co-author identity is
discern <[email protected]>(formerlydiscern-bot). Agent-authored commits remain outside that boundary. The ownership, fail-open, retry, and attribution semantics below stand.- Retry-evidence (2026-07-28): before the first setup-wiring commit attempt, discern records the setup branch, HEAD, whole index tree, and every machinery candidate's stage-0 mode and blob ID in worktree Git-admin state. A resumed
beginnever re-derives or re-stages that set. It retries only when the branch, HEAD, staged set, index tree, and every candidate's worktree and index bytes still match the record. The commit consumes those staged bytes. After hooks and signing run, discern compares the actual commit tree with the recorded tree. If a hook staged extra bytes, a compare-and-swap ref update removes the commit while leaving the index and worktree intact. A matching commit clears the record. Any drift skips safely and leaves the files for the agent to commit.- Message ownership (2026-09-04): the setup caller selects the typed
scaffoldWiringsite and supplies no prose. The shared registry renders the settled imperative subjectScaffold discern wiringand the exact co-author trailer; caller/registry parity is structural.
Status: accepted
Context
§discern setup begin scaffolds discern's own wiring into the project: the discern.toml, the shared .gitignore block, and — per configured agent — the MCP-server and session-hook files (.mcp.json, .claude/settings.json, .codex/config.toml, .gemini/settings.json, …). On a fresh install it does this on a dedicated, throwaway discern-setup branch created off a clean tree, then hands the coding agent a brief to author the rest (jobs, guidance, the map). Historically the engine wrote those machinery files but left committing them to the agent — consistent with the setup interaction model, where the agent narrates its work and makes a per-stage atomic commit.
That split breaks in a real cold run. A coding agent's safety classifier refuses to commit .mcp.json / .claude/settings.json: pre-approving an MCP server (and committing pre-approved hooks) is a permission-widening change agents are correctly trained to be cautious about. So the agent punts to the human or strands the files, and discern setup done ends on a dirty tree with discern's own essential wiring uncommitted — the opposite of the seamless, promise-keeping setup ADR 0036 and ADR 0065 set out to deliver.
The root cause is that discern relied on the agent to commit discern's own output. There was already a precedent for the engine owning such a commit: setup done auto-commits the [meta].bootstrapped marker it writes (best-effort, fail-open, pathspec-limited to discern.toml after proving that file's HEAD diff is exactly the marker line). Unrelated tracked, staged, or untracked local work neither blocks that marker commit nor gets swept into it. The pressure was to extend that ownership backward to begin.
Decision
§The engine commits discern machinery it scaffolds. After begin scaffolds and records provenance, and before it prints the brief, it commits exactly the machinery — the config, managed Git blocks available at that phase, per-provider MCP and hook files, provider rules, and any app-managed worktree-lifecycle config — as one Scaffold discern wiring commit on the discern-setup branch. The committed set is derived from the scaffold outcome, minus the authored-content seeds. The caller selects the typed registry site; it cannot provide a subject or body.
The explicit nos:
- It commits only discern's machinery — never
git add -A. The authored instruction source, Map skeletons,TODO.md, and optionalbrief.mdremain the agent's to write and commit. Compiled agent files are tracked Generated artifacts refreshed from the instruction authority; they are never hand-edited. - It runs only when
begincreated the isolateddiscern-setupbranch in a Git repository. An explicitly allowed current-branch setup does not claim an isolated machinery commit; non-Git setup refuses before effects. - It is best-effort and fail-open: a commit failure (e.g. commit signing) never fails
begin; the agent can still commit by hand. The outcome surfaces asmachinery_committedin the JSON envelope and a line in the human handoff, mirroring howdonereportsmarker_committed.
If the first machinery commit fails, its staged evidence is the only authority for an automatic retry. A later begin does not infer ownership from a file's path: user edits, staged changes, deletion, an unrelated staged path, a different HEAD, or malformed/missing evidence all make the retry a no-op. An unchanged retry commits the recorded staged blobs and removes the evidence.
This is a deliberate carve-out from "the agent makes the commits": discern commits its own wiring, which the agent's classifier won't; the agent commits the content it authors.
Consequences
§- A coding agent driving a cold setup — especially a cautious auto-mode one — never has to commit a permission-widening config file, because discern's own wiring is already committed when the brief is handed over. Setup no longer ends on a dirty tree of discern's essentials.
- The machinery lands in one reviewable, revertible commit, isolated on the throwaway
discern-setupbranch and separate from the agent's authored-content commits — clean history, easy rollback. - One consistent rule now spans the setup lifecycle: the engine commits its own output (
begin's machinery,done's marker); the agent commits what it authors. Both paths are best-effort and fail-open. The marker is pathspec-limited; a resumed machinery commit is staged-index-limited by persisted blob evidence. - discern now writes to the user's git history during
begin. The cost is bounded: it is one commit, on a branch built for exactly this, gated on the clean-tree precondition that branch already requires, and trivially reverted. - "Machinery" versus "authored content" is now a contract. A new scaffolded machinery file is committed automatically once it flows through the scaffold outcome; a new authored seed must be added to the exclusion set or it would be swept into the commit. A guard test pins the committed set to exactly the machinery and asserts the authored seeds stay uncommitted, so the contract can't drift silently.
Alternatives considered
§- Leave the commit to the agent (the status quo). Fails precisely for the cautious classifiers that most need setup to "just work" — the permission-widening commit is the one they refuse.
- Instruct the agent harder, in the brief, to commit the MCP files. A brief can't reliably override a safety classifier, and shouldn't try: the caution is correct in general. The fix is to not ask the agent to make that commit at all.
- Tell the user to commit it manually. Reintroduces the manual step zero-config setup exists to remove, and a dirty-tree handoff is exactly what ADR 0065 set out to end.
- Commit everything (
git add -A) atbegin. Would sweep the agent's authored canvas (instructions, Map, and TODO ledger) into discern's commit, conflating discern's wiring with the user's content and pre-empting the agent's own atomic commits. Rejected: discern commits only what discern owns.