tropo

Rung 11 · The Agentic Builder Series

In Tropo: Your First Governed Run


You read why a governed build holds — that the trail is the credential, and that running a build too big to watch is the same four-turn loop you already run, just let out to full length. Here you do it. You drop in a governed intake tool and its whole trail, run a real fourteen-row batch through a checkpoint schedule instead of watching every row, and end holding the thing rung eleven is about: a spec, a decision log, receipts, and a one-page credential you could slide across a table. You don't build the tool from scratch — it's the same tool the series has been growing since rung one, handed to you at full stakes. About twenty-five minutes.


Part 1 — The Spec File (and the trail that travels with it)

By this rung a spec no longer travels alone. A governed build is a spec plus its trail — the rules it points to, the decision log behind it, the receipt you run to prove it's sound, the checkpoint schedule that says where you show up. So Part 1 hands you the whole set at once, and the rest of the walkthrough is you running it.

Which folder does this go in? The one your agent is already working in. If you're not sure which that is, just ask it: "What folder are you working in right now?" — and use the folder it names. Everything in this walkthrough lives in that one folder (plus a versions/ subfolder the starter kit makes for you).

Getting the whole kit into files — the easy way. You don't hand-craft eight files. You let the agent do it, exactly as you did at rung one — the only difference is there's a trail this time, not a single spec. Paste the block below into your agent and let it write every file. Read the spec inside it first, though: this is the contract the whole run is checked against, and a governed run is only as honest as the spec it's measured by.

Create the following files in the folder you're working in, exactly
as given — don't build or change anything yet, just write each file
with the exact contents shown. Make a versions/ subfolder for the
last file. Confirm the file list back to me when you're done.

===== FILE: intake.spec.md =====
# intake.spec.md

version: 1.4.0
status: governed
owner: you (the operator)
last-checkpoint: 2026-07-04

---

## Intent

A client-intake tool that receives other people's submissions, catches the
bad ones before they enter the record, and files the good ones into an
append-only log. One operator runs it; submitters never touch it directly.
The tool must be honest about *why* it rejected something, because the reject
reason is what the operator quotes back to the submitter.

This is the same tool that started life as a personal outreach tracker
(rung 1). It grew into a team intake tool at rung 6 and has been governed
ever since. This spec is versioned; every change to it is a checkpoint.

## What "done" means

A submission is a row with four fields: `name`, `email`, `org`, `request`.

The tool is DONE for a given batch when, for every submission in the batch:

1. Each row is validated against the rules in `rules.md`.
2. Every VALID row is appended to `submissions.log` with a timestamp and a
 monotonic `id`. No valid row is dropped. No row is written twice.
3. Every INVALID row is shown to the operator with the *specific* rule it
 broke — never a generic "invalid". The operator can see all rejects at
 once, in one place, without scrolling past the accepted ones.
4. The counts reconcile: `accepted + rejected == total submitted`. The tool
 displays this reconciliation. If it does not balance, the run is NOT done.
5. The run is reproducible: loading the same batch twice produces the same
 accept/reject verdict for every row (validation is pure — no randomness,
 no clock-dependent verdicts).

## Validation rules (authoritative list lives in rules.md; summarized here)

- `name`: non-empty after trim.
- `email`: contains exactly one `@`, at least one `.` after the `@`, no spaces.
- `org`: non-empty after trim.
- `request`: non-empty after trim, and 280 characters or fewer.
- Duplicate `email` **within the same batch** → reject the later row as a dup.

## One worked example

Input batch (3 rows):

| name | email | org | request |
|-------------|--------------------|------------|----------------------------|
| Dana Reyes | dana@reyes.co | Reyes Co | Need a quote for Q3 |
| (blank) | boba@shop.io | Boba Shop | Catering for 40 |
| Sam Okafor | sam[at]okafor.com | Okafor LLC | Partnership inquiry |

Expected output:

- ACCEPTED (1): `Dana Reyes` → appended to `submissions.log` as `id:1`.
- REJECTED (2):
 - Row 2 → `name is empty`
 - Row 3 → `email must contain exactly one @`
- Reconciliation: `1 accepted + 2 rejected == 3 submitted` ✓

## Non-goals

- No submitter-facing UI. The operator pastes/loads the batch; submitters
 are not users of this tool.
- No email sending. The tool produces reject reasons; the operator relays them.
- No database. `submissions.log` is the record of truth; it is append-only text.
- No auto-fixing of bad rows. A bad row is rejected with a reason, never
 silently corrected. (Corrupting-then-recovering was rung 8's lesson: the
 tool must never quietly mutate data.)
- No de-dup against prior batches (only within-batch). Cross-batch dedup is
 a future spec version, not this one.

## Checkpoint schedule (rung-11 addendum — see below)

This section is the shipped artifact of rung 11. See
`checkpoint-schedule.md` for where a real run pauses for the operator.

===== FILE: rules.md =====
# rules.md — authoritative validation rules for intake

Each rule has a stable `code` so reject reasons are quotable and greppable.

| code | field | rule | reject reason shown |
|-------------|---------|---------------------------------------------------|--------------------------------|
| NAME_EMPTY | name | non-empty after trim | name is empty |
| EMAIL_AT | email | exactly one `@` | email must contain exactly one @ |
| EMAIL_DOT | email | at least one `.` after the `@` | email needs a domain dot after @ |
| EMAIL_SPACE | email | no whitespace anywhere | email must not contain spaces |
| ORG_EMPTY | org | non-empty after trim | org is empty |
| REQ_EMPTY | request | non-empty after trim | request is empty |
| REQ_LONG | request | 280 characters or fewer | request exceeds 280 characters |
| EMAIL_DUP | email | not a duplicate of an earlier row in this batch | duplicate email in this batch |

Rules are checked in table order. The FIRST failing rule is the reported
reason (one reason per rejected row, deterministically the topmost failure).

===== FILE: receipt.template.md =====
# receipt.template.md — reusable proof-of-soundness run (from rung 9)

Run this checklist against any intake run. A receipt with all ✓ is what you
"slide across the table" when someone asks "how do you know this is sound?"

| # | Check (against intake.spec.md "what done means") | Pass? | Evidence |
|---|--------------------------------------------------------|-------|----------|
| 1 | Every valid row appended to submissions.log | | |
| 2 | No valid row dropped; no row written twice | | |
| 3 | Every reject shows the SPECIFIC rule code + reason | | |
| 4 | Counts reconcile: accepted + rejected == submitted | | |
| 5 | Re-running same batch yields identical verdicts (pure) | | |
| 6 | No row was auto-corrected (non-goal honored) | | |

Sign-off: ____________________ Spec version at run: ______ Date: ______

===== FILE: checkpoint-schedule.md =====
# checkpoint-schedule.md — rung-11 addendum to intake.spec.md

You do NOT watch every row go through. You show up at scheduled checkpoints,
verify against the spec, and log the call. Between checkpoints, the tool runs.

For a real batch, the run pauses for the operator at exactly these points:

| CP | Trigger (when the run pauses) | What you verify (against intake.spec.md) | What you log |
|----|-------------------------------------------------|------------------------------------------|--------------|
| A | Batch loaded, before validation | Row count matches what the submitter sent | note total in decisions.log |
| B | Validation done, before appending to log | Reconciliation balances (accepted+rejected==total); spot-check 2 reject reasons are the *right* rule | note counts |
| C | After append to submissions.log | No id gaps, no duplicate id, count of new ids == accepted | confirm log integrity |
| D | End of run | Run `receipt.template.md` over the whole batch; all 6 checks ✓ | file the signed receipt |

Rule: if any checkpoint fails, you STOP. You do not "fix and continue" without
a decisions.log entry saying what you decided and why. A silent fix is a
governance break (that was rung 8's lesson).

The governed files listed in `trail-index.md` — spec, rules, decisions,
receipt, handoff — plus this schedule ARE the credential. The trail is what
you slide across the table.

===== FILE: decisions.log =====
# decisions.log — one line per governed decision. Append-only.
# Format: <date> <checkpoint> <decision> — <rationale> [spec-version]

2026-06-20 CP-6 Shipped intake tool end-to-end from tracker lineage — receives/validates/files. [v1.0.0]
2026-06-24 CP-7 Chose FLAG-FOR-REVIEW owner routing — tool suggests an owner from request keywords and holds each accepted row for operator Confirm before filing, rather than auto-routing. [v1.1.0]
2026-06-27 CP-8 Made validation PURE and forbade auto-fixing after a corrupt-and-recover drill — tool had silently trimmed a request and lost data; recovered from versions/v1.1.0. [v1.2.0]
2026-07-01 CP-9 Adopted receipt.template.md as the reusable proof-of-soundness run; wired reconciliation display (accepted+rejected==total). [v1.3.0]
2026-07-03 CP-10 Paused mid-extension, wrote handoff.md, resumed cold in a fresh session — confirmed the trail alone was enough to continue. [v1.3.0]

===== FILE: handoff.md =====
# handoff.md — cold-resume note (from rung 10)

**State as of 2026-07-04:** intake tool at spec v1.4.0, governed. Nothing is
mid-edit. Ready for a real-batch run against the checkpoint schedule.

**To resume cold:**
1. Read `intake.spec.md` (intent + what "done" means + checkpoint schedule).
2. Rules of truth live in `rules.md`. The tool is `intake.html` (open in a browser).
3. The record of truth is `submissions.log` (append-only — never edit in place).
4. Last good version snapshot is in `versions/`.
5. Every decision is in `decisions.log`. Every soundness run uses `receipt.template.md`.

**Open thread (next checkpoint):** cross-batch de-dup is a non-goal in v1.4.0.
If a second real batch arrives, decide whether to bump the spec to add it —
that decision goes in decisions.log as CP-12.

===== FILE: versions/intake.spec.v1.4.0.md =====
(save a byte-for-byte copy of intake.spec.md above into this path — this is the
last-good snapshot the trail can roll back to)

That's the trail, on disk, in one paste. Nine files, and you wrote none of their contents — you directed. Before the block ships you the tool itself, one more file: the intake tool is a single self-contained HTML file, and it's long, so hand it over in its own turn. Paste this:

Now create one more file, intake.html, in the same folder — a single
self-contained HTML tool that implements intake.spec.md and rules.md
exactly. Requirements, all from the spec:

- A textarea where I paste a batch, one submission per line, fields
 separated by a pipe: name | email | org | request.
- A "Run validation" button and a "Load worked-example batch" button
 that fills in the 3-row worked example from the spec.
- Validation is PURE and mirrors rules.md in table order; the FIRST
 failing rule is the one reason shown. Add EMAIL_DUP for a duplicate
 email within the same batch.
- Two columns of results: Accepted, and Rejected with each reject
 showing its rule CODE and reason. All rejects visible at once.
- A reconciliation line that shows accepted + rejected == total and
 turns green only when it balances.
- Valid rows append to an on-screen submissions.log panel with a
 monotonic id, newest kept; nothing is auto-corrected.

No server, no install — I double-click it and it opens in a browser.

Two things worth naming before you run any of it. First, governing a file here doesn't mean hand-editing some master registry — there isn't one to edit, and a fresh folder wouldn't have it. A file becomes governed when it carries a uid: in its own frontmatter (your agent generates one with openssl rand -hex 4 — it's random, so it'll differ from anyone else's) and your studio picks it up in its index the next time it rebuilds. So intake.spec.md is the contract; the way you make your studio track it is the paste below. Second, the submissions.log panel inside the tool is a live preview — the real append-only record on disk is the submissions.log file the agent writes when you tell it to file a run. The tool shows you the record; the file is the record.

To let your studio track the spec, paste this:

Give intake.spec.md a uid in its YAML frontmatter (generate it with
openssl rand -hex 4 — don't reuse an old one) and set type: document.
Then rebuild the studio index so it picks the file up. Tell me the
uid you generated and confirm the index now tracks it.

Roughly what comes back:

Added frontmatter to intake.spec.md — uid: <uid>, type: document —
and rebuilt the index. Your studio now tracks the spec by that uid;
it'll show up in the vault index (vault/00-index.jsonl) as a governed
document. The trail files it points to travel with it.

That's the honest version of "governed": a uid in the file, and your studio tracking it — not a ledger you hand-edit.


Part 2 — The Build Walkthrough

Three turns, and none of them is "build the tool" — the tool already exists. This rung's turns are the governed run: load a real batch and verify it (checkpoints A and B), file it and confirm the record (checkpoint C), and close the run with a receipt and a decision-log entry (checkpoint D). That's what running a build too big to watch actually feels like — you show up at the checkpoints, not at every row.

A note on what you'll see: the conversation is text — copy the prompts straight out. The intake tool is the one thing you'll see as an image, because the whole point is that you're reading its verdicts at a glance instead of watching them happen. Your agent's exact wording will differ every time — the reply blocks below are roughly what you'll see, not a transcript to match. What matters is the numbers reconcile and the trail files get written.

Step 1 — Load a real batch and verify it (checkpoints A and B)

A real batch isn't the clean worked example — it's messy, which is the point. Paste this to create one:

Create a file real-batch.txt in this folder with exactly these 14
lines, verbatim (some are intentionally malformed — that's the test):

Dana Reyes | dana@reyes.co | Reyes Co | Need a quote for Q3 catering
 | boba@shop.io | Boba Shop | Catering for 40 people
Sam Okafor | sam[at]okafor.com | Okafor LLC | Partnership inquiry
Priya Nair | priya@nair.dev | Nair Design | Website refresh, 5 pages
Tomás Vega | tomas@vega.io | | Just checking pricing
Lee Chen | lee@chen.co | Chen Studio | 
Dana Reyes | dana@reyes.co | Reyes Co | Follow-up on my earlier note
Marcus Hall | marcus@@hall.com | Hall Group | Bulk order question
Wen Li | wen@li.com | Li & Co | Need onboarding help for our team of twelve going into the new fiscal year and we would like a full walkthrough of the platform including data migration from our legacy spreadsheet system which currently holds about four thousand rows across nine tabs and we are hoping to get this done before the end of the quarter so timing matters a great deal to us
Ava Brooks | ava brooks@brooks.co | Brooks Ltd | Quick call?
Noah Kim | noah@kim.net | Kim Ventures | Interested in enterprise tier
Ivy Song | ivy@song | Song Media | Rebrand project
Ravi Patel | ravi@patel.org | Patel Foods | Wholesale inquiry for 200 units
Zoe Adams | zoe@adams.co | Adams Co | Referral from Priya Nair

Now open intake.html — find it in your folder and double-click it; it opens in your browser. Open real-batch.txt, copy all fourteen lines, paste them into the batch box, and click Run validation.

This is checkpoint A and checkpoint B, back to back. Checkpoint A: the submitter sent fourteen rows — does the tool show fourteen? Checkpoint B: does the reconciliation line balance, and are two of the reject reasons the right rule?

[screenshot: the intake tool after running the 14-row batch] A calm white panel headed "Client Intake · spec v1.4.0 · governed." A green reconciliation line reading "5 accepted + 9 rejected == 14 submitted." Two result columns: Accepted (Dana Reyes, Priya Nair, Noah Kim, Ravi Patel, Zoe Adams) and Rejected — nine rows, each stamped with its rule code: NAME_EMPTY, EMAIL_AT (Sam), ORG_EMPTY (Tomás), REQ_EMPTY (Lee), EMAIL_DUP (the second Dana), EMAIL_AT (Marcus's double @), REQ_LONG (Wen), EMAIL_SPACE (Ava), EMAIL_DOT (Ivy). Below, the on-screen submissions.log preview showing five ids.

Read it, don't trust it. Five accepted, nine rejected, fourteen total — it reconciles, and the line is green. Spot-check two rejects against rules.md: the second Dana Reyes is EMAIL_DUP (same email as row 1 — right), and Ivy Song's ivy@song is EMAIL_DOT (no dot after the @ — right). Checkpoints A and B pass. That's the move: you verified against the spec at a scheduled point, you didn't watch every row.

Step 2 — File the batch and confirm the record (checkpoint C)

The tool's on-screen log is a preview. The record is the submissions.log file. This turn files the five accepted rows to disk and confirms the log's integrity — checkpoint C: no id gaps, no duplicate id, and the count of new ids equals the count accepted.

Paste this:

The 14-row batch validated: 5 accepted, 9 rejected, reconciles to 14.
Write the 5 accepted rows to submissions.log in this folder, append-only,
as id:1 through id:5 with a timestamp — same format the tool's log panel
shows. Don't touch the rejected rows; don't auto-fix anything. Then read
submissions.log back and confirm: ids 1..5, contiguous, no gaps, no
duplicate, exactly 5 new ids for 5 accepted rows.

Roughly what comes back:

Wrote 5 rows to submissions.log (append-only), id:1 through id:5:
 id:1 Dana Reyes dana@reyes.co
 id:2 Priya Nair priya@nair.dev
 id:3 Noah Kim noah@kim.net
 id:4 Ravi Patel ravi@patel.org
 id:5 Zoe Adams zoe@adams.co
Checkpoint C: ids are 1..5, contiguous, no gaps, no duplicate id.
5 new ids for 5 accepted rows — matches. Nothing was auto-corrected;
the 9 rejected rows were not written.

Checkpoint C passes: the record on disk matches what the tool accepted, and the log is intact. You'll re-confirm this yourself in Part 3 — the agent saying "no gaps" is not you seeing no gaps.

Step 3 — Close the run: receipt + decision + credential (checkpoint D)

This is checkpoint D, the end of the run, and it's the beat the whole rung points at. You run the reusable receipt over the batch, log the decision, and generate the one-page trail-index — the thing you slide across the table.

One thing to know up front, because it's the difference between a governed run and a pile of files: filing the run is a deliberate instruction. The agent doesn't log the decision or write the receipt on its own — you close the run, the same way you sign off a review. This prompt does all three closes at once.

Paste this:

Close out this run, checkpoint D. Do three things:

1. Fill in a copy of receipt.template.md as receipt.2026-07-04.md for
 this 14-row batch — all 6 checks, with evidence (5 accepted → 5 ids;
 ids 1..5 no gaps; 9 rejects each with a code; 5+9==14 reconciles;
 re-running the same batch gives identical verdicts; nothing
 auto-corrected). Sign it "operator (dogfood run)", spec v1.4.0,
 dated 2026-07-04.
2. Append ONE line to decisions.log as CP-11: ran the tool on a real
 14-row batch across the checkpoint schedule; 5 accepted / 9 rejected,
 reconciled; ran the receipt over the whole run. [v1.4.0]
3. Write trail-index.md — a one-page credential listing each governed
 file (spec, rules, decisions, receipt, handoff, checkpoint-schedule,
 submissions.log, versions/) and what each one proves, ending with the
 headline: batch of 14 → 5 accepted / 9 rejected → reconciles → receipt
 6/6. The trail is the credential.

Roughly what comes back:

Closed the run — checkpoint D complete:

1. receipt.2026-07-04.md — 6/6 ✓. Evidence filled in for each check;
 signed "operator (dogfood run)", spec v1.4.0, 2026-07-04.
2. decisions.log — appended CP-11 (2026-07-04): real 14-row batch,
 5 accepted / 9 rejected, reconciled, receipt run. [v1.4.0]
 The log now runs CP-6 → CP-11.
3. trail-index.md — one page, seven governed files, each with what it
 proves. Headline: 14 → 5/9 → reconciles → receipt 6/6. The trail is
 the credential.

Open trail-index.md and read it. That page is the answer to "how do you know this build is sound?" — and you didn't write "trust me" anywhere in it.

One line of intent closed a two-day-shaped run into a credential. You verified at four checkpoints, filed the record, ran the receipt, logged the call — and every one of those is a file someone else can open and check. That's the thing rung eleven exists to show you: you didn't supervise the build, you governed it, and the trail proves it held.


Part 3 — The Verification Checklist

Directing a governed run is half the job. This is the harder half — deciding whether the run is sound, not just whether it finished. "It reconciled on screen" is not "the record on disk is intact and the trail is honest." So don't read this as a row of checkmarks to admire. Perform it. You sign off other people's work all the time; you know the difference between glancing at a green light and actually checking the record behind it.

Do this now, before you call the run governed:

  1. Re-run the same batch and compare verdicts. Reload intake.html, paste the same fourteen lines, click Run validation again. The five accepted and nine rejected must be identical — same rows, same rule codes. If a verdict changes between runs, validation isn't pure and check 5 of the receipt is a lie. (This is the one the spec cares most about: same input, same answer, every time.)
  2. Open submissions.log and count the ids yourself. There must be exactly five lines, id:1 through id:5, contiguous — no gap, no repeat. If there are six lines or a missing id, the append wasn't clean and the record isn't trustworthy. Don't take the agent's "no gaps" for it; look.
  3. Confirm no rejected row leaked into the log. None of the nine rejects — not the blank-name row, not the duplicate Dana, not Ava's spaced email — may appear in submissions.log. If a rejected row is in the record, the tool auto-admitted bad data, which the spec forbids.
  4. Open receipt.2026-07-04.md and check the reconciliation cell reads 5 + 9 == 14. Then confirm decisions.log ends with a CP-11 line and now runs CP-6 through CP-11. A receipt that doesn't reconcile, or a run with no logged decision, is a build with no credential — finished-looking, not governed.

Now fill in the receipt — with what you saw, not what you hoped:

What was askedRun a real 14-row client-intake batch through a governed tool against a checkpoint schedule: verify the reconciliation, file only the valid rows to an append-only log, then close the run with a signed receipt, a decision-log entry, and a one-page credential.
What you can now do yourselfPoint a governed tool at messy real input, show up at scheduled checkpoints instead of watching every row, verify the record on disk matches the verdicts on screen, and — the load-bearing one — hand someone the trail that proves the run is sound instead of asking them to trust you.
What the record showssubmissions.log holds ids 1–5, contiguous, no rejected row among them. receipt.2026-07-04.md reconciles 5 + 9 == 14 at 6/6. decisions.log runs CP-6 → CP-11. trail-index.md names every governed file and what it proves. The batch, the rejects, and the receipt are the evidence — nobody has to take your word for it.
What's not done yetCross-batch de-dup is still a non-goal in v1.4.0 — a second real batch would need a CP-12 decision and a spec bump (that open thread is written in handoff.md, on purpose). And filing the run was a deliberate close, not automatic — you had to run the receipt and log the call yourself. That's the whole discipline: the trail exists because you chose to leave it.

If the re-run changed a verdict, or submissions.log had a gap or a stray reject, or the receipt didn't reconcile — you caught it, which is exactly the job. Send it back:

The re-run doesn't match the first run [or: submissions.log has a gap /
a rejected row leaked in / the receipt doesn't reconcile]. That breaks
the spec's "reproducible, append-only, no auto-fix" contract. Find what's
wrong, fix it, and re-run the receipt — and log what you changed in
decisions.log so the trail stays honest.

Directing a governed build includes rejecting a run and making the fix leave its own trail. That's not the loop failing; that's the loop working at full stakes.


Part 4 — What You Now Have in Your Studio

You started by dropping in a tool the series had already grown to full stakes. You end holding something you couldn't have held at rung one: a run too big to watch, that you can prove is sound.

In your folder, right now:

  • A governed tool and its contractintake.html and intake.spec.md, the spec now carrying a uid your studio tracks. Not a demo — a tool that takes other people's messy input and refuses to let the bad rows into the record.
  • The record of a real runsubmissions.log with five clean ids, and real-batch.txt with the fourteen rows that produced them. The five that got in and the nine that didn't are both accounted for.
  • The trail — your credentialdecisions.log running CP-6 → CP-11, receipt.2026-07-04.md at 6/6, checkpoint-schedule.md, handoff.md, rules.md, the versions/ snapshot, and trail-index.md tying them into one page. That page is the answer to "how do you know this is sound?" — and it's made of files anyone can open.

It's doable. It's not automatic — you had to show up at four checkpoints, verify the record yourself, and choose to file the receipt and log the call. But you did all of it, and the run held — not because you watched it, but because you governed it. That's the top of this ladder: you didn't supervise the build, you governed it, and the trail is the proof. The door out is right there — Becoming an Agentic Animal. You've already got the harness on.