tropo

Rung 10 · The Agentic Builder Series

In Tropo: Every Session Ends


You read why the ending is the overlooked work — that if it isn't in a file, it didn't survive. Here you prove it to yourself the only way that lands: you stop a real build on purpose, mid-extension, have the agent write its own handoff, close the session cold — and then open a fresh one that has never seen your conversation and watch it pick the work back up from your files alone. You end holding a tool that outlived the agent that built it. About fifteen minutes.

This is the one rung where the point is that the agent goes away. So the walkthrough has you deliberately kill continuity and rebuild it from disk. If it works, you'll never again be hostage to one chat you're afraid to close.


Part 1 — The Spec File

Here's the spec for the tool you'll be ending well: a client-intake tool. Someone submits a project request through a form; the tool rejects the bad ones with a clear reason and files the good ones into a durable record. You're not authoring this — you're borrowing a finished spec so the whole rung can be about the ending, not the building.

Read it. Notice it names what "done" means, gives one worked example, and fences off the non-goals — same shape as every spec in this series.

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. (Many agents show it at the top of the window, too.) Everything in this walkthrough lives in that one folder, plus a versions/ subfolder the agent will make for you.

Getting the spec into a file — the easy way. Don't hand-craft a .md file (on a Mac, TextEdit will fight you — it saves .rtf and hides extensions). Let the agent do it. Paste the whole spec below into your agent and say:

Save the text below as a file named intake.spec.md in the folder
you're working in, and add a uid line to its frontmatter — generate
it with `openssl rand -hex 4` (a random value, never a word). Don't
build anything yet.

<paste the entire spec here>

That's it — the spec is now a real file in your folder, spelled correctly, no extension surprises. (If you'd rather make the file yourself and you're comfortable doing it, go ahead — same result.)

Here's the spec to paste:

---
type: spec
tool: client-intake
version: 1.3.0
---
<!-- your agent adds a random `uid:` here on save (openssl rand -hex 4) -->

# Client Intake Tool — Spec

## Intent
Receive client project-request submissions from a web form, reject the bad ones
with a clear reason, and file the good ones into a durable record. This is the
grown-up version of the personal outreach tracker: instead of me typing rows by
hand, other people submit and the tool catches bad input before it reaches the record.

## What "done" means
- A single self-contained `intake.html` opens in any browser, no server, no network.
- The form collects four fields: **name**, **email**, **company**, **request**.
- On submit the tool VALIDATES against `rules.md`:
 - name: non-empty, <= 80 chars
 - email: matches `something@something.tld`
 - company: non-empty, <= 80 chars
 - request: non-empty, >= 12 chars, <= 500 chars
- A rejected submission shows the exact failed rule(s) inline and is NOT filed.
- An accepted submission is appended to the record with a timestamp and a status
 of `accepted`, and the on-screen record table updates immediately.
- The record is exportable as a log line the reader can paste into `submissions.log`.
- Every accepted row shows: timestamp, name, email, company, request, status.

## One worked example
Input: name `Dana Reyes`, email `dana@northwind.co`, company `Northwind`,
request `Need a landing page rebuilt before our September launch.`
→ passes all four rules → filed as:
`2026-07-04T18:22Z | accepted | Dana Reyes | dana@northwind.co | Northwind | Need a landing page rebuilt before our September launch.`

Counter-example: email `dana@` → fails the email rule → shown inline as
"email: must look like name@domain.tld" → NOT filed.

## Non-goals
- No server, no database, no auth — a single HTML file is the whole tool.
- No spam scoring or CAPTCHA (rules.md catches structural bad input only).
- No editing or deleting filed rows in this version (append-only record).
- No multi-page or pagination — one screen.

That's the whole spec, and notice it says version: 1.3.0 at the top. This tool has history — it climbed the earlier rungs to get here, and you're joining it at v1.3.0. In this rung you'll take it to v1.4.0. That version line is going to matter, because a spec that records its own version is a spec that survives a handoff: the next agent knows exactly which state it's looking at.

A note on governance — kept true and light. Look at the frontmatter: that uid: line at the top is what makes a file governed — it gives the file a stable identity your studio can track, and when your studio next rebuilds its index, the file gets picked up. Governing a file is not a registry you hand-edit; it's give the file a uid and let your studio track it. Your agent generates the uid itself (it runs openssl rand -hex 4 — a random value, different every time), so you never type one in. Some of the files below are governed this way; some — a plain .log, a loose export — are just captured records living in your folder. Both survive the session. Only the ones with a uid are governed. That distinction is the whole subject of this rung.


Part 2 — The Build Walkthrough

Three steps. Step 1 stands the tool up. Step 2 starts an extension and then deliberately stops halfway — writing the handoff at the exact moment you'd normally push through. Step 3 is the payoff: close the session, open a cold one, and watch it finish the work from your files alone.

A note on what you'll see: the conversation is text — copy the prompts straight out. The tool itself is the one thing you'll see as an image, because its whole point is that it's a real screen you use. 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 that the named files appear. Tropo runs the same way across tools; the words matter, the chrome doesn't.

Step 1 — Stand up the tool, its rules, and its record

The spec is now saved as intake.spec.md. The tool validates against a separate rules file and files rows into a log, so you'll create those alongside the build. Do it in one hand-off — you're directing the what, not writing the how.

Paste this:

I dropped a spec in this folder called intake.spec.md. Build exactly
what it describes, and set up its two companion files:

1. Create rules.md — a small table of the four validation rules from
 the spec (name, email, company, request), each with its reject
 message. The tool's JS should mirror this file.
2. Build intake.html — one self-contained file I can double-click:
 the four-field form, inline rejection with the exact failed rule,
 an append-only record table, and an export box that emits a log
 line per accepted row.
3. Create submissions.log with a header comment and two seed rows for
 Dana Reyes and Priya Anand, so I can see the log-line format.

Don't ask me questions, just build it to the spec.

Roughly what comes back:

Done. Three files in your folder:

· rules.md — the four rules as a table, each with its reject message.
 I mirrored these exactly in the intake.html RULES object and left a
 comment so the two stay in sync by hand.
· intake.html — one self-contained file. Four-field form (Name, Email,
 Company, Request), inline rejection showing the exact failed rule,
 an append-only record table with six columns (When · Name · Email ·
 Company · Request · Status), and an export box that emits one log
 line per accepted row. Empty state reads "No submissions filed yet."
· submissions.log — header comment plus two seed rows (Dana Reyes,
 Priya Anand) in the pipe-delimited format.

Open it with: open intake.html

Now open it. Find intake.html in your folder and double-click it — it opens in your browser. Type a good submission (name, a real-looking email, company, a request of at least a dozen characters) and hit Submit; a row files. Try a bad email like dana@ and it rejects inline instead. That's the tool working.

[screenshot: your working intake tool] A calm bone-and-white card. A title "Client Intake" and a subtitle "v1.3.0 · receives submissions, rejects bad ones per rules.md, files the good ones." A four-field form — Name, Email, Company, Request — with a blue "Submit request" button. Below it, a "Filed record" table with six column headers (When (UTC), Name, Email, Company, Request, Status) reading "No submissions filed yet," and an "Export for submissions.log" box beneath.

You built the whole tool with one hand-off. Now you'll capture a version snapshot — because the next thing you do is edit it, and this rung is about what survives that edit.

Paste this:

Make a versions/ subfolder and save a copy of intake.html exactly as
it is right now — before any changes — as versions/intake.v1.3.0.html.
This is the clean v1.3.0 snapshot, my rollback point. Don't change
intake.html itself.

Roughly what comes back:

Created versions/ and saved versions/intake.v1.3.0.html — a byte-for-byte
copy of the current intake.html (four fields, six columns, colspan="6"
on the empty-state row). intake.html is untouched. That's your
last-good v1.3.0 snapshot to roll back to.

You now have a clean v1.3.0 on disk. That snapshot is deliberate: it's taken before the edit, so it's a true rollback point. The moment you start changing intake.html, versions/intake.v1.3.0.html is the fixed thing you can always return to.

Step 2 — Start an extension, then stop on purpose and write the handoff

Here's the move this rung exists for. You're going to begin adding one optional field — a budget field — get it half in, and then stop before it's finished and have the agent write the handoff note. This is the deliberately-interrupted build. In real life the interruption is your day ending; here you cause it on purpose so you can watch the recovery.

First, start the extension — but only the visible part of it. Paste this:

I want to add an OPTIONAL budget field to the intake tool — a v1.4.0
change. For now, do ONLY the first slice: add a "Budget (optional)"
text input to the form, right before the Request field. Do NOT wire
it up yet — don't read it on submit, don't add it to the table, don't
add it to the export, don't touch the rules. Just the form input.
Leave everything else exactly as-is.

Roughly what comes back:

Added just the form input:
 <label>Budget (optional) <input id="budget" placeholder="$5,000"></label>
It sits right before the Request field. Nothing else changed —
the budget value isn't read on submit, it's not in the table, not in
the export, and rules.md is untouched. The field shows in the form but
does nothing yet. This is a half-done extension.

The tool is now mid-change: a budget input sits in the form but is wired to nothing. This is exactly the state you never want to lose in your head. So capture it. This is the Power Play from the essay — don't write the handoff yourself; make the agent write its own, while the whole session is fresh in its view.

Paste this:

We're stopping here mid-extension. Before this session ends, write the
handoff the next agent will need to finish this work. Assume they have
NONE of our conversation — only the files in this folder. Save it as
handoff.md. Cover:
- what we're building and why
- the decisions we made and the reasons (and point them at decisions.log)
- what's done (v1.3.0 is complete; versions/intake.v1.3.0.html is the rollback)
- what's next: the budget field is HALF DONE — only the form input
 exists. List the exact remaining steps to finish it, and that budget
 is OPTIONAL so it must NOT go in the rules.
- one or two warnings you'd give them.

Also create decisions.log if it doesn't exist — an append-only file,
one line per decision (date | decision | reason), and record the key
decisions behind this tool: single self-contained HTML file, rules
live in rules.md and the JS mirrors it, the record is append-only,
timestamps are UTC ISO, and snapshot to versions/ before risky edits.

Roughly what comes back:

Created both files.

decisions.log — append-only, one line per decision, with the five
you named (self-contained HTML; rules.md is the source of truth and the
JS RULES object mirrors it by hand; append-only record; UTC ISO
timestamps; snapshot to versions/ before risky edits).

handoff.md — written for an agent with zero conversation memory:
· What & why: the client-intake tool, grown-up outreach tracker.
· Decisions & reasons: points at decisions.log for the full trail.
· Done: v1.3.0 complete and verified; versions/intake.v1.3.0.html is the
 rollback point.
· Next (in-progress): the optional budget field is HALF DONE — only
 the form input exists. Remaining steps listed: read budget on submit,
 store it on the row, add a Budget column (a <th> after Company AND a
 <td> in the row template), add it to the export line, then bump the
 spec to v1.4.0, add a decisions.log entry, and snapshot intake.v1.4.0.html.
· Warnings: (1) rules.md and the JS RULES object are synced by hand —
 budget is optional so it does NOT go in RULES; (2) the header and the
 empty-state row BOTH hardcode the column count as colspan="6" — when
 you add the Budget column you must bump BOTH to 7. Grep for colspan
 before calling it done.

Open handoff.md and read it once. That warning about colspan in two places is the kind of thing that lives only in the agent's head at the moment of the edit — and it's now on disk. So is the fact that budget is optional and must stay out of the rules. That's the whole bet of this rung: the load-bearing knowledge is now in files, not in a session that's about to end.

Step 3 — Close the session cold, open a fresh one, and watch it resume

Now do the thing the essay promised. End this session completely — close the chat, or start an entirely new conversation with a blank agent that has never seen a word of what you just did. This is not a soft reset; you want a genuine stranger.

Then, into that fresh cold agent, paste exactly this — nothing else, no backstory:

You're picking up an in-progress project in this folder with no memory
of prior work. Read intake.spec.md, decisions.log, and handoff.md — in
that order — then continue exactly where the last agent stopped. Don't
ask me to re-explain; the files are the source of truth. When you know
what's half-done, finish it.

Roughly what comes back:

Read the three files. Here's the state, from the files alone:

We're building a client-intake tool. v1.3.0 is complete; the last agent
started a v1.4.0 extension — an OPTIONAL budget field — and stopped with
only the form input in place. handoff.md lists the remaining steps, so
I'll finish them:

 ✓ Read budget on submit and store it on the row object.
 ✓ Left rules.md and RULES untouched — budget is optional, per the
 handoff, so it stays out of validation.
 ✓ Added a Budget column: a <th> after Company in the header AND a
 <td> in the row template — kept them aligned.
 ✓ Bumped BOTH colspan="6" to colspan="7" (the header empty-state row
 and the JS empty-state string) — the handoff warned there were two.
 Grepped colspan to confirm no others.
 ✓ Added budget to the export log line.
 ✓ Bumped intake.spec.md to version: 1.4.0.
 ✓ Appended a decisions.log entry: resumed cold from handoff, finished
 the optional budget field, colspan bumped 6→7.
 ✓ Snapshotted versions/intake.v1.4.0.html.

The budget field is now fully wired. Reload intake.html to see it.

Reload intake.html in your browser. There's the new Budget column, right after Company, and a new optional input in the form.

[screenshot: your intake tool, now finished by a cold agent] The same card, now titled with a Budget (optional) input added to the form before Request, and the record table showing seven column headers — When (UTC), Name, Email, Company, Budget, Request, Status. A submission with a budget filed cleanly; one with the budget left blank files with a "—" in the Budget cell.

Stop and feel what just happened. A brand-new agent, with zero memory of your session, walked into a half-finished build and completed it correctly — including catching the two-site colspan trap — because the knowledge it needed was in handoff.md, decisions.log, and the spec. The agent that started the work is gone. The work continued anyway. That's the whole lifecycle, closed.

(If your cold agent bumped only one colspan, or slipped budget into the rules, or missed the Budget cell in the row template — that's not the method failing, that's exactly what Part 3 catches. The handoff named the traps; verification confirms the agent honored them.)


Part 3 — The Verification Checklist

Directing the ending is half the job. Deciding whether the resume actually worked is the harder half — and on this rung it's the whole point, because "the agent said it finished" is precisely the thing you can no longer take on faith: the agent that made the claim is a stranger reading your files. Don't read this as checkmarks to admire. Perform it.

Go to the reloaded intake.html and to your files, and do this now, before you call it done:

  1. Reload the page and file a submission with a budget. Fill every field including Budget (say $5,000) and submit. A row files, and the Budget column shows $5,000. Then file one with Budget left blank — it must still file (budget is optional) with a in the Budget cell. If a blank budget rejects the submission, the cold agent wrongly added budget to the rules — send it back.
  2. Count the table columns, then check the empty state. The header must read seven columns, Budget sitting right after Company. Now the trap: with no rows filed, the "No submissions filed yet" row must span the full width of all seven columns. If it stops short under six, one of the two colspan sites didn't get bumped. Have the agent grep: "Grep this folder for colspan and show me every match — they should all read 7." Two matches, both 7, or it's not done.
  3. Check the row template got the new cell. File a row and confirm its Budget cell lines up under the Budget header — not shoved under Request or missing. A header column with no matching row cell is the classic half-applied multi-touch edit.
  4. Open the files and read the record, not the agent's summary. intake.spec.md must now say version: 1.4.0. decisions.log must carry a new bottom line about resuming from the handoff. versions/intake.v1.4.0.html must exist beside intake.v1.3.0.html. If the spec still says version: 1.3.0, the resume didn't actually finish — it built the field but skipped closing the loop.
  5. The real test of the whole rung: diff old against new. Open versions/intake.v1.3.0.html and the current intake.html side by side (or ask: "Show me what changed between versions/intake.v1.3.0.html and intake.html."). The v1.3.0 has six columns and no budget; the current one has seven and a wired budget field. That gap was produced by an agent that never saw your session. That diff is the receipt for "if it isn't in a file, it didn't survive" — the survival is right there in the two files.

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

What was askedStand up a client-intake tool (form + rules.md + record + export), snapshot a clean v1.3.0, start an optional budget field and stop halfway, have the agent write its own handoff, then close the session and resume from files alone with a cold agent — finishing the budget field to v1.4.0.
What you can now do yourselfEnd a build cleanly mid-stream, direct an agent to write its own handoff while the work is fresh, and hand a half-finished project to a brand-new agent that resumes it correctly from your files. The load-bearing skill: making the work outlive the session that produced it.
What the record showsintake.spec.md now reads version: 1.4.0. handoff.md and decisions.log sit in your folder; versions/intake.v1.3.0.html (six columns) and versions/intake.v1.4.0.html (seven) both exist. The diff between them was produced by an agent with no memory of the original session — that's the evidence that the files carried the knowledge, not the chat.
What's not done yetThe tool still doesn't validate budget when present (that's a possible v6 — optional-but-validated). The record is append-only by design. And intake.spec.md and handoff.md are governed — they carry a uid, so your studio tracks them once its index rebuilds; but decisions.log and submissions.log are captured records, not governed vault entries. Knowing which is which is the literacy this rung leaves you with. The full governed build with real data is rung eleven.

If a blank budget rejected, if the empty state stopped short of seven columns, if the row cell was missing, or if the spec still said v1.3.0 — you caught it, which is exactly the job, and it's a job the agent literally could not do for you, because the agent doing the resume is the one whose work you're checking. Send it back:

The resume isn't finished. Grep this folder for colspan — every match
must read 7 — and confirm intake.spec.md says version: 1.4.0 with a matching
decisions.log entry. Budget is OPTIONAL: a blank budget must file, not
reject, and it must never be added to rules.md. Fix whatever's off and
re-snapshot versions/intake.v1.4.0.html.

Directing includes rejecting — and this time the thing you might reject was written by an agent you'll never talk to again. That's not the loop failing; that's the loop working across the seam where one agent ends and the next begins.


Part 4 — What You Now Have in Your Studio

You started with a working tool and one agent that knew everything about it. You end with the agent gone — and the tool finished by a stranger, correctly, from your files.

In your folder, right now:

  • A tool that outlived its authorintake.html, taken from v1.3.0 to v1.4.0 by an agent that never saw the session that started the work. Not a demo. It receives real submissions, rejects bad ones, and files good ones — a tool you can hand someone today.
  • The memory that made that possiblehandoff.md, decisions.log, the versioned spec, and the versions/ snapshots. This is the project's memory living outside any one session. handoff.md and the spec are governed — your studio tracks them by their uid once it rebuilds. The logs are captured records beside them. Every one of them survived a session that ended.
  • Your first cross-session receipt — the checklist above, filled with what you actually saw, capped by the v1.3.0-versus-v1.4.0 diff. That diff is the proof, and the habit behind it — end deliberately, capture the handoff, verify the resume — is the one that finally lets you close a chat without fear.

It's doable. It's not automatic — you had to cause the interruption, make the agent write the handoff, and check the cold resume yourself. But you did all three, and a brand-new agent picked your work up cold and finished it right. The agent that ran the loop with you is gone, and the work is still here. That's the whole lifecycle, closed — and now you know the ending was never the loss it looked like. If it isn't in a file, it didn't survive — so you put it in the file, and it did.