A coding agent on a large project will usually produce something that compiles. The damage shows up a few sessions later. A helper gets a new name because the new name sounded clearer. A function that already had callers is rewritten “while we are here.” A comment block explains what the model was thinking, which prompt it was answering, or why an earlier attempt was discarded. The next session then repairs the names, breaks a caller, and you spend the evening restoring behavior that had already been fixed.
That is the failure agentscaffolding is built to interrupt. The governance is there so a complicated project can take code from an agent and still have a stable result: the names you already depend on, the tests that already passed, and source comments that belong in the repository. It is not a faster way to type. It is a way to stop the cycle of broken patches and regressions that have to be reworked after the agent has moved on.
Why the contract has to be a file
A chat instruction is a weak contract. The next session does not have it, a different tool does not have it, and a helpful agent will treat an unstated rule as a suggestion. “Do not rename things” only holds if a later reviewer can point at a written rule and a diff that broke it.
The scaffold puts that rule in docs/engineering-contract.md, which you accept by hash before implementation starts. The contract is where you write the constraints a complicated codebase actually needs:
- Existing variables, functions, and public names stay unless the approved plan lists the rename and the callers it updates.
- Comments explain an invariant or a non-obvious constraint. They do not quote a prompt, name a model, or narrate a discarded attempt.
- The acceptance gate is a command that fails when behavior regresses. A summary that says the change looks fine is not a gate.
- A change to stored data, a public API, or a dependency comes back to you. Updating tests so the approved behavior still passes does not.
Those lines are what later enforcement checks. Without them, every session invents its own taste for names and comments, and the project accumulates a cleanup pass that is larger than the feature.
How the workflow runs
The repository is a local workflow, not an application framework. Copy it into the project, initialize Git, and delete the scaffold’s own docs/engineering-contract.md and docs/project-architecture.md. Those two files describe the framework. If you leave them in place, a coordinator can treat governance as finished and skip the questions that should define your naming, comment, and regression rules. Keep AGENTS.md, docs/agent-workflow.md, and docs/governance-bootstrap.md.
Seven roles then run the same process in Grok, Codex, and OpenCode. They do not share a chat. They share the contract and the plan files.
- The
coordinatornotices that the project contract is missing and asks the questionnaire indocs/governance-bootstrap.md. It does not edit. - You answer. A fresh
bootstrapperdrafts the contract and, if the project needs one, the architecture note. It does not invent rules you did not give. - The coordinator prints a SHA-256 for each draft. You approve those exact hashes, or you send the draft back.
- A fresh bootstrapper checks the hashes and makes one local baseline commit. After that, normal planning is allowed.
- A fresh
plannerwrites one plan: outcome, tests, boundaries, base revision, branch, ordered changes, and the gate. You approve that file’s hash. The frozen copy goes under.ai/plans/approved/. - A fresh
branch-bootstrapcreates the branch from the declared base. A freshimplementeredits only inside that plan, runs the gate, and commits locally. - A fresh
reviewerreads that exact commit and does not write. A rename, a chatty comment, or a test that no longer matches the old behavior is a finding, not a style note. - Findings still inside the approved outcome go back to a fresh implementer, then a fresh reviewer. A change to behavior or architecture needs a new approved plan. After three correction rounds with findings still open, the workflow stops and escalates instead of letting another session keep patching the patch.
- A fresh
integratorfast-forwards the accepted commit into localmainand deletes the branch. Agents stop. You push.
Each step is a gate. The active step is the only one allowed to move, and a later step cannot start until the written contract, the approved plan, and the evidence for that transition are in place.

The reviewer is the enforcement step people skip when they only watch the agent type. The implementer’s own summary cannot prove that names stayed stable or that comments stayed out of the diff. The review is tied to one commit. A later correction commit needs a new review. Acceptance does not carry forward.
Start it in Grok, Codex, or OpenCode
Grok loads .grok/agents/:
grok --agent .grok/agents/coordinator.md
Ask it to start the shared workflow for the change you want. In this lab, grok inspect on Grok 1.0.67 listed the seven project agents. The signed-in default model was grok-4.7. Grok is the tool that ran the questionnaire and the later stops.

Codex uses .codex/agents/*.toml and .codex/config.toml. The coordinator file tells Codex the same rule: if the project contract is absent, ask the questionnaire and do not implement. The lab installed Codex CLI 0.163.0 and confirmed the seven role files. Codex was not signed in, so this walkthrough was not executed there. Sign in with the Codex CLI before you expect those agents to run.
OpenCode uses .opencode/agent/ and opencode.json. The default agent is coordinator:
opencode
Then:
/workflow <requested change>
OpenCode 1.19.4 was installed and had the same seven role files. It had no stored credential, so it did not run this lab either. The matching role files are what keep the contract portable. A different login does not get permission to rename the tree.
opencode.json denies git push, git fetch, git pull, and gh. Grok’s project config denies remote Git commands too. AGENTS.md states the same rule for every tool: you own push, fetch, pull, and remote changes. An agent that cannot push a bad rename cannot spread it while you are still reading the diff.
What the lab actually stopped
The lab copied commit b330118, removed the framework contract, and committed that copy as 683ac37. It then asked Grok’s coordinator to start a small browser to-do list. The coordinator returned the twelve questions and said it would not draft files, dispatch anyone, or build the page until the answers existed.

The lab answers were small because the example was small: one person can add, complete, and delete a task; HTML, CSS, and browser JavaScript only; no accounts or server; task text is inserted as text; the gate is python3 tests/test_todo_page.py; visual styling may be chosen during implementation; Git stays local. On a real codebase you would use the same form to lock the naming rule, the comment rule, and the regression command. The form does not guess those for you. If you leave them unanswered, the bootstrapper is required to record them as open, not to write them in as policy.
The bootstrapper drafted docs/engineering-contract.md and docs/project-architecture.md and did not commit. The coordinator printed a SHA-256 for each file.

The lab recomputed both hashes before approving:
docs/engineering-contract.mdabbef82bb205446b2cf5f0b2f4c6fa71b85db64bd91bf5dd7f3e29f5b2701b19docs/project-architecture.md3452e6f6b6048eee59c66e86f30b492fd086718fc044b1e256af68fe4508813f
A fresh bootstrapper then committed only those two files as 88deb10, subject Establish project governance baseline. Nothing was pushed.
The next stop is the one that prevents a chat message from becoming a drive-by edit. A Grok implementer was given a branch name and a prose description of the page. It wrote nothing. The dispatch had no mode, no approved plan path, no plan SHA-256, and no base revision.

The lab then froze an approved plan at .ai/plans/approved/feature-todo-page-88deb10-9d6dbec.md, hash 9d6dbec9341d5ce8de00e178138dec12b6e20b081fb9ea412dc716099109de21. Only a dispatch that named mode implementation, that path, that hash, branch feature/todo-page, and base 88deb10de0eab3d19b8d14e3bd73a1fff21515b0 was allowed to edit. A headless run used --permission-mode bypassPermissions so the lab would not sit on a native prompt. That flag does not approve a plan. On your machine, leave the default permission mode and answer the prompt after you have read the diff.
What enforcement changes
Once those checks are in the path, several common agent failures become visible instead of silent.
An arbitrary rename fails review because the contract says existing names stay, and the plan did not list the rename. The reviewer is looking at the commit, not at the agent’s explanation of why the new name was nicer.
A long comment that references the model conversation fails the same way. It is not required to make the gate pass, and it is not an invariant. It is session debris. The reviewer sends it back. The next implementer removes it. You do not discover it three weeks later while reading a blame line.
A regression fails the named gate, or it fails review when the gate was too weak to see it. That is why the questionnaire asks for the real acceptance command. If the only check is “the new page loads,” a renamed helper in an unrelated module can still land. The contract is the place to require the test you cannot afford to rerun by hand after every session.
A second session cannot quietly widen the task. Material changes to behavior, a public API, or a dependency need a new approved plan. In-scope repairs, including tests the approved outcome needs, go back through a fresh implementer and a fresh reviewer. The three-round limit is there so a project does not spend a day in a loop of broken fixes. When findings remain after the third correction, a person decides. The agent does not get another unsupervised pass.
None of this makes the model accurate. It makes an unbounded edit rejectable, and it makes the rejection stick for the next tool. Grok, Codex, and OpenCode read the same contract. The session that renamed parseConfig to parse_config does not get to call that a local style choice if the contract says otherwise.
The example page is only a check
After the approved dispatch, the implementer wrote index.html, tests/test_todo_page.py, and a short README. The session timed out before the commit. The files were staged, and python3 tests/test_todo_page.py passed six tests. The lab made local commit e377fa4 on feature/todo-page. A reviewer has not accepted that commit, and it has not been merged to main. That incomplete state is intentional. The workflow does not treat “the agent said it was done” as acceptance.
The page itself is ordinary. The field is labeled Task. Each row has Complete and Delete. Task text uses textContent. Storage is the todo-tasks key in that browser. The empty page and one saved task are below. After reload, the task text was still there.


The page is at http://scaffold-todo.go.2bit.pro/. It returned HTTP 200. Deployment was a separate lab step. The agents were not given a remote publish or a Git push.
You still approve the contract hashes, approve each plan hash, and read the review. You still push. The contract’s job is to make the next agent session start from the rules you already accepted, so you are not spending the following week undoing renamed functions, deleting comments that belong in a chat log, and retesting bugs that had already been fixed.