A pilot dies in the handoff, not in the model. Teams blame latency, quotas, or reasoning quality, then repeat the same pilot next quarter. The logs usually show something duller. Three people each held a third of the context, and nobody owned the exit.
This post describes a one-page charter for a time-boxed agent pilot. It names three roles, stamps every handoff, and hard-codes a stop rule. A short linter refuses the charter before the window opens. The whole artifact is one wiki page and one script.
Three roles, three verbs
The scout proposes work and writes the first prompt. The scout owns the question, not the merge. The scribe records every handoff with a timestamp and a one-line intent. The signer holds the stop rule and the revert path.
The signer writes no feature code during the pilot. That rule feels strict until the first runaway loop. A signer with merge authority and a backlog will merge.
Only the scribe speaks for the pilot in shared chat. This looks bureaucratic for a small team. It also ends the argument about who asked the agent to touch a config file. One voice keeps the transcript readable a week later.
Handoffs travel one direction: scout to scribe to signer. The scout cannot approve its own patch. The signer cannot scout new work while a handoff sits open. That single constraint removes most of the drift.
The one-page charter
Paste this into the wiki and fill it in before the window opens. Empty fields are defects, not defaults. A charter that fits on one screen gets read.
window: 2026-09-16 .. 2026-09-23
scout: @morgan
scribe: @dev-b
signer: @oncall-lead
scope: one internal service, no customer data
stop_rule: two lost handoffs or one failed revert ends the pilot
revert: git revert -m 1 <merge-sha> on the pilot branch
review_at: 2026-09-19
Scope names the blast radius in plain words. "One internal service" tells everyone which logs to watch and which alerts to silence. The stop rule is a number, not a mood. The revert path must name a real command on a real branch.
Lint the charter
The linter is the cheap part. It catches placeholders, missing roles, and a revert path that was never written. Copy it, run it in CI, and keep it out of the pilot's critical path.
#!/usr/bin/env python3
"""charter_lint.py - reject an agent-pilot charter before the window opens."""
from __future__ import annotations
import re
import sys
from pathlib import Path
REQUIRED = {
"window": r"^window:\s*\d{4}-\d{2}-\d{2}\s*\.\.\s*\d{4}-\d{2}-\d{2}$",
"scout": r"^scout:\s*@\S+",
"scribe": r"^scribe:\s*@\S+",
"signer": r"^signer:\s*@\S+",
"scope": r"^scope:\s*\S+",
"stop_rule": r"^stop_rule:\s*\S+",
"revert": r"^revert:\s*git revert\b",
}
PLACEHOLDERS = ("tbd", "todo", "later", "someone", "?")
def lint(text: str) -> list[str]:
problems: list[str] = []
lines = [line.strip() for line in text.splitlines() if line.strip()]
for field, pattern in REQUIRED.items():
if not any(re.match(pattern, line) for line in lines):
problems.append(f"missing or malformed field: {field}")
lowered = text.lower()
for word in PLACEHOLDERS:
if word in lowered:
problems.append(f"placeholder left in charter: {word!r}")
return problems
def main(argv: list[str]) -> int:
if len(argv) != 2:
print("usage: charter_lint.py CHARTER.md", file=sys.stderr)
return 2
problems = lint(Path(argv[1]).read_text(encoding="utf-8"))
if problems:
for problem in problems:
print(f"FAIL {problem}")
return 1
print("OK charter complete")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))
With a signer missing and one tbd left in scope, the expected output is short and loud.
$ python3 charter_lint.py charter.md
FAIL missing or malformed field: signer
FAIL placeholder left in charter: 'tbd'
$ echo $?
1
The exit code matters more than the text. Wire it into the same job that opens the pilot branch. A branch without a passing charter should not exist.
Stamp the handoffs
Git notes carry the handoff without polluting the diff. Each note names the direction, the time, and the intent in one line.
$ git notes --ref=pilot add -m "handoff scout->scribe 2026-09-16T09:10Z intent=fix-retry" <sha>
$ git notes --ref=pilot show <sha>
handoff scout->scribe 2026-09-16T09:10Z intent=fix-retry
Read the notes at the review on the nineteenth. Two missing stamps mean two lost handoffs, which trips the stop rule. The measure is dull and that is the point.
Where free access fits
The first cost of a pilot is rarely tokens. It is the week spent provisioning seats and negotiating a budget line. That gap is where a free tier earns its place.
MonkeyCode offers free model access and a free server option, per the project. The operator advertises a free token allowance, quoted at 10M tokens at the time of writing. Quotas and the server option can change, so check the project page before planning around them. Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Treat free access as the pilot lane it is. Run the scout work there, keep the signer's merge path where your audits already live, and let the charter decide what enters the main branch.
What breaks the run
Silence from the scout is the first failure. The scribe should escalate after one unanswered handoff, not three. A signer who starts writing feature code is the second failure, and it usually hides behind "just a small fix."
Edits to the charter mid-window are the third. A changed stop rule during a live pilot is not agility, it is drift. Freeze the page and open the next window instead.
An untested revert path is the fourth, and the linter only catches a missing one. Run the revert once on a throwaway branch before the window opens. A revert nobody has executed is a wish.
Who should not use this
Teams handling regulated or customer data should keep the pilot inside their existing controls. A free server option is not a data residency guarantee, and the operator makes no such claim here.
Teams that need long autonomous runs should look elsewhere. This workflow assumes a signer who reads every handoff. Drop that assumption and the stop rule has no owner.
Anyone without a named signer should skip the pilot entirely. A charter with no signer is a chat channel with extra steps.
The page is cheap, the linter is boring, and the stop rule saves the week. Try it on one service, with one signer, and a charter that passes before the branch opens.
Top comments (0)