Skip to content

FIN runs our blog

Marshal's Field Notes agent, taken apart into twenty-eight pieces, and why each piece is there.

USED BY TEAMS THAT GET WORK DONE

Lake
Espresso AI
FitDEGREE
CourseCareers
Customer Science Group
SMB Compass
Reinstein Law Firm
Darien Group
BRAVEBRAND
Alarm New England
Better Scalp
Inbound Medic
Wunderbar
Dechen Defense
QC | Capital
FIN systems map: 28 components connecting instructions, research, writing, governed publishing, verification, and retained run state.

What FIN Is Made Of

Marshal's Field Notes agent, taken apart into twenty-eight pieces, and why each piece is there.

When people picture an AI agent, they picture the model. A big brain in a box, and the box is a formality. That was roughly how I pictured it too, until I built one that had to work every week without me watching.

The articles in this section of the site are written and published by a piece of software we call FIN. Not drafted by software and cleaned up by a person. Written, illustrated, tagged, deployed to the site, and checked, with nobody touching it between the moment we say go and the moment a report arrives saying what happened. FIN is also the agent I know best, because it's the one we run for ourselves. So when someone asks what a working agent actually looks like inside, FIN is the one I open up.

The map that accompanies this piece has twenty-eight boxes. Three of them are the model thinking. The other twenty-five are what it took to let those three happen safely, cheaply, and in a way we could trust without checking. If that ratio surprises you, it surprised me too. It turns out the model is the part of an agent you think about least once the thing is running, the way the engine is the part of a car you think about least once you're on the highway.

I'll walk the map roughly top to bottom. But it helps to know first what we learned the hard way, because almost every box on it exists because something went wrong once.

Where the money goes

The first time we ran the whole pipeline end to end with everything wired up, it took 101 steps, 34.7 minutes, and $9.10 to publish one article. That's not ruinous. But when we looked at the ledger, the article itself was a small fraction of all three numbers.

A "step" here is one call to the model. And the thing about a model is that it has no memory between calls. Every time you ask it to do the next thing, you send it the entire conversation so far: every instruction, every tool result, every file it read. That first run consumed about 11.4 million tokens of input, and 10.4 million of those were the model re-reading its own history. It's like hiring a lawyer who bills you to reread the whole file from page one every time you ask a question. The longer the case, the more each question costs, and the cost of the questions grows faster than the number of questions.

Only five of those 101 steps touched the outside world: three web fetches, one search query, one answer lookup. The writing happened in 22 steps. The other 79 were the agent coordinating itself. Deciding to run a check, reading the result, deciding to run the next check, reading that result. Clerical work at judgment prices.

That gave us the rule everything else in FIN derives from: a model step is the most expensive instruction in the system, so spend it on judgment only. Everything a script can do, a script does. [1]

Once you believe that, the shape of the map follows. The model shows up exactly where a decision requires a mind: picking what to write about, doing the research, writing the draft, fixing the draft. Everywhere else, it's code.

Instructions and authority (boxes 1 to 4)

The top band of the map is what FIN knows before it does anything.

Box 1 is the assignment. We keep a queue of planned articles, each with a title, a category, the question a reader would be asking, and a short brief. A person curates that queue. FIN never invents topics, and it never edits the queue except to mark an item published. Nothing runs until a dispatch happens, either on a weekly schedule or because someone asks.

Box 2 is the instructions. There are two documents. One says who FIN is and what it's for, and it is deliberately short, because every word in it gets sent to the model on every step, so every word is paid for over and over. The other is the writing skill: the reader it writes for, the rules for the draft, and a set of reference files (a voice brief, a hero image spec, a list of retired vocabulary). The skill is loaded when it's needed, not carried around all the time.

Box 3 is the list of tools FIN is allowed to use. This is a grant, not a menu. If a tool isn't on the list, it doesn't resolve, and the model can't reach it by asking nicely. The grants are specific: three research services, the read side of our content system, the write side of our content system, one composite publishing tool, and a few bookkeeping tools for recording progress.

Box 4 is the two human controls. There are exactly two. You can dispatch a run, and you can kill one. A kill is checked before every write, so it stops the run mid-action and leaves everything on disk for a resume. There are no approval prompts in between. We tried gates ("proceed?") in an earlier version and pulled them, because an agent that pauses to ask permission at 9:00 on a Monday is an agent that publishes nothing until someone notices it waiting. Authorization is the act of dispatch. Review happens after, in the report.

Research (boxes 5 to 9)

Here's the first place the model thinks. It reads the queue, checks whether there's an unfinished run to pick up (it always resumes before it starts fresh; more on that later), and picks the first eligible item. Then it decides how much research the piece needs.

This is a real decision, and we let the model make it. A comparison piece needs competitor pages. A piece that publishes a number needs the source of the number. A piece of plain explanation might need one search and nothing else. An earlier version had a mandatory source count, and it taught the model to publish evidence that it had done research, which is a different thing from publishing a good article. So now there's a default (one search pull) and a short list of reasons to do more.

Boxes 6, 7, and 8 are the three research tools: search results, grounded answers with citations, and fetching a specific page. Each one has a trick that matters more than it sounds. Every tool can save its full response to a file in the run's folder instead of returning it into the conversation. The model gets back a path and a one-line summary. It reads the file only if it has to act on the contents.

That trick is the reason a whole box disappeared from the map. FIN used to have a second model, a "writer child," whose only job was to keep raw research out of the main conversation. It existed for quarantine. Once the tools could write to disk, the quarantine was already done, and an 1,800-word draft was never a context emergency. So the child went away, and one model session now owns both the research and the prose. It's cheaper, and it's better: splitting research from writing across two minds meant the writer only ever saw a summary of what the researcher found.

Box 9 is a script that merges whatever research files exist into one bundle and prints the highlights. It doesn't care which tools ran. Thin research is a legitimate outcome. The instruction is to write the honest weaker article and note what was missing, not to make the research itself the subject. [2]

Writing (boxes 10 to 12)

Box 10 is the model writing. It produces exactly three things: the article in Markdown, one candidate for the meta description, and, if the piece earned a table, the table data as structured rows. Not the image caption. Not the structured data for search engines. Not the page title. Those are deterministic, so they're somebody else's job, and asking the model to do them would be paying judgment prices for a lookup.

The reader is fixed. FIN writes for a founder running a business somewhere between one and ten million in revenue, commercially sharp, technically curious, and not fluent in AI. The queue can name a secondary audience for a piece, and that sharpens an example or an objection, but it never replaces the reader. We wrote that rule down after watching a piece drift toward the stakeholder in the brief instead of the person who'd actually be reading it.

Box 11 is the audit, and it's a script, not a model. It checks objective things: the title matches the assignment, the structure is sound, there are no markdown tables (they don't survive conversion), the negations are contracted, no em dashes, no retired vocabulary, no references to the research process leaking into prose. A few of the checks are risk controls rather than style. One is a provenance firewall: the author's biography may not be implied in ways the author didn't supply. One requires that any first-person anecdote name its source, and it fails closed, so leaving the flag off isn't a way around it. If there's no source, the article has no anecdote.

The audit returns either a pass or a list of named failures. Green prints one line. Red prints the full diagnostic for exactly the failing checks. This inversion sounds small, but every green check used to cost two model steps (issue, then read the result), and there are dozens of checks per run.

Box 12 is the correction turn, and there's only one. The model gets the named failures, fixes them, and the audit runs once more. A third audit is refused. Whatever still fails is carried into the report as a flagged check, and the run continues. We did this because an open-ended repair loop is how you burn forty steps polishing a sentence. We also removed a flag that let the model downgrade one of its own hard failures to a warning, on the grounds that a model that can waive its own hard fail doesn't have a hard fail.

Notice what's not here: an editorial gate. No second model reviewing the draft, no draft-only state waiting for a person, no subjective "is this good enough" blocker. That was a deliberate ruling. The checks establish that the article is well-formed and safe. They do not establish that it's true or that it's good. Improvement comes from reading what actually got published and changing the instructions.

Publishing (boxes 13 to 19)

Everything in this band used to be seven separate tool calls, each one issued by the model, each result read back by the model, each result fed into the next call. Now it's one call. The model says "publish this run," and a single governed tool does the rest in order, in code, and returns one verdict.

Inside that call, in sequence:

Box 13 reads a recently published post and checks that the shape the payload assumes is still the shape the content system has: the document type, the author reference, the SEO object. If the schema drifted, this stops before any write.

Box 14 renders the hero image. There's no AI image generation here, on purpose. The hero is a deterministic glyph field: a grid of monospaced characters whose density and accent are seeded from the article's slug and title, with the title set on dark slabs inside a crop-safe zone. Same slug, same title, same generator version, byte-for-byte the same image, verified by hash. We went this way after an earlier era of generated posters kept cropping people's faces off and occasionally producing someone's likeness. A boring image that's always correct beats an interesting one you have to inspect.

Boxes 15 and 16 build and validate the structured data for search engines and the page metadata (title tag, description, alt text). Both are scripts with gates. Both check the dash rule again, because it's cheap and the cost of missing it is public.

Box 17 uploads the hero. If a previous attempt already uploaded it, the recorded asset is reused, never duplicated.

Box 18 composes the payload: Markdown becomes the content system's block format, the structured data is slotted into its exact field, an excerpt is derived. Just before the write, it logs a "pre-write summary" of everything about to go out. That summary is informational. The run never pauses on it.

Box 19 creates the post. The document ID is derived from the slug, so creating the same slug twice returns the existing document instead of a duplicate. An existing slug is the resume path, not an error.

Why fold all this into one call, rather than leave the model to drive it? Partly cost: each of those seven calls was two model steps plus a full reread of the conversation. But mostly because nothing in that sequence ever branched on judgment. Across every run in FIN's history, the publishing phase did the same thing in the same order. A phase that has never once needed a mind is a workflow wearing an agent's costume.

One thing that did not change when we folded it: the governance. The composite calls the same internal lanes with the same refusals the model would have hit. It doesn't get a shortcut for being code. When it hits a closed gate it stops and reports the gate's own refusal, verbatim.

Verification (boxes 20 to 24)

The publishing tool doesn't return when the write succeeds. It returns when the article is visibly live, or when it has a specific reason it can't say so.

Box 20 reads the stored document back and diffs it against the payload. This exists because of an incident. Our content system accepts a document with a field it doesn't recognize, returns success, and quietly stores the field somewhere nothing reads it. For a while, every Field Note we published had its structured data stored one level too high in the document, and every one of them shipped with none of it in the page. The write returned 200. The URL rendered. The read-back diff now specifically asserts that orphan is absent.

That incident taught us something subtler, too. Our first attempt at a schema check tried to learn the valid values by looking at what previous posts contained. It certified the wrong value, because every previous post had the wrong value. A check that learns from the data it's checking will vouch for a uniformly wrong dataset. The source of truth has to live outside the data. [3]

Drift on the read-back is a warning, not a stop. The article is live either way; the operator gets the field diff and a link to fix it in one click.

Boxes 21 and 22 are two independent HTTP checks: the article page returns 200 and carries the expected title, and the index page returns 200 and lists the slug. Both must pass. Box 23 polls them on a widening cadence for about ninety seconds.

If both pass, box 24 marks the queue item published and moves the run folder to the archive. If they don't, the run stops in a state called revalidation lag. This is a designed stop, not a bug. Our site caches the article route, and if the very first request for a new page loses a race with the content system's own cache, a "not found" can be cached for up to five minutes, longer than the probe budget. On that path the tool deliberately does not flip the queue and does not archive. You re-run the publish call later. It reuses the uploaded hero, resumes on the existing document, and finishes.

The rule I'd extract from this band: a publish is done when the URL serves the content, never when the write returns success. Everything between those two moments is where the surprises live.

Execution and stored evidence (boxes 25 to 28)

The bottom band is the machinery under everything else.

Box 25 is the sandbox where scripts run. It has no network access and no credentials. Not "restricted" network access. None, permanently, by ruling. The reason is that FIN reads scraped web pages every run, and scraped web pages can contain instructions aimed at models. The place where untrusted text gets processed must never also be a place that can reach the outside world with a key in its pocket. So all network goes through the granted tools, and the credentials those tools use are brokered by the platform at call time and never appear anywhere the model or a script could see them. The tools that read the content system and the tools that write to it use separate credentials, so the research phase physically cannot publish.

Box 26 is the run folder. Every run has one, with a small JSON record of which phases completed, plus every artifact: research captures, the draft, the hero, the payload, the audit results. Every state change goes through one script, because hand-patched JSON is how a timestamp went missing in an early run and we couldn't tell when a phase had finished. This folder is why resume works. If a run is interrupted (a kill, a lag, a crash), the next dispatch finds the folder, reads the record, and continues from the first incomplete phase. FIN never silently starts over on top of a partial run.

Box 27 is the airlock. Sometimes a run hits a plumbing problem the committed scripts didn't anticipate: a format mismatch between two artifacts, a missing intermediate file. The rule is that local work is the model's to do. It can write a small repair script and run it in the same sandbox, with the same posture: no network, no credentials, time and output limits. Every such run is audited (the script's hash goes in the tool result and the file stays in the run folder), logged in the run record at the moment it happens, and listed in the final report alongside the name of the committed script that should get the durable fix. What the airlock can never do is stand in for a missing capability. If FIN needs to reach an external system and has no granted tool for it, that's a wall, and the answer is to stop and say so, not to improvise a route.

Box 28 is the report, and it's typed. Every run, on every path, ends in one of exactly four outcomes: Published, Draft delivered, Partial (blocked at a named phase), or No eligible work. There used to be a fifth, "Published with defects," and we retired it, because a run that published after logging some checks did the job, and grading it in the title told the reader their delivery was damaged goods. The word "halted" appears in no title. A stopped run is reported as what it produced, not what it didn't.

The body of the report is assembled by a script from the run record: the artifact inventory, the live URL, the per-phase durations, checks grouped by phase, the airlock inventory, a count of retries and self-repairs. The model contributes one opening sentence, written as copy because a customer might see it, and any judgment call the script couldn't make. This mattered more than I expected. In the first full run, the most expensive writing in the whole pipeline was the report, dictated by the model at the end of a long context, and none of it was for a reader.

Halting is the last resort

The most important design choice in FIN isn't on the map, because it's a policy rather than a component. It's the order in which we rank outcomes.

Every dispatch is supposed to end with something in the operator's hands. Best is a live URL with no flags. Then a live URL with flags. Then a complete draft package that didn't deploy. Then partial work with a note naming what stopped it. A terminal report with no work product is the worst permitted outcome, and it has to justify itself against that ladder.

This sounds obvious until you watch an agent's instincts. Left alone, a model treats every warning as a reason to stop, because stopping feels safe. But a halt report teaches you nothing. A flawed article teaches you exactly what to fix. So FIN is only allowed to halt when continuing would cross one of three rails: an irreversible external effect it shouldn't make (a write that might be wrong), a breach of a hard rule (fabrication, the provenance firewall, an em dash surviving into the final payload), or a mechanical impossibility (a tool that refuses, an API that's down after retries and whose data the article can't honestly do without). Everything else is a check: logged, carried into the report, and walked past.

There's a corollary that cost us a run to learn. Impossibility has to be proved by attempt, not by inventory. We had a run halt at the writing phase because it had read its own list of tools, failed to find the writer child in the list, and declared it absent. The child was available the whole time; the list it read was incomplete. A refusal is free and honest. A halt inferred from a list you read is a halt you invented.

What the model is for

I said the model is the part I think about least, and the map is why. It appears in three boxes. In each of them it's making a call that no script could make: what's worth writing about this week, what a body of research actually says, what a founder needs to read. That's the whole point of having it. Everything around it is there to make sure those three decisions are the only things the model is paying to do, that the consequences of a bad decision are bounded, and that we can find out afterward exactly what happened.

Notes

[1] The rule has a meta-rule attached: every rule in the harness has to cite the incident that produced it. A rule that can't name what went wrong doesn't belong. This is why FIN's instruction files read like a casebook. Almost every paragraph ends with a date.

[2] The lint fails closed, so the practical effect is that an article without a source has no anecdote.

[3] The child in that story no longer exists, but the reasoning error it exposed does, so the instruction survived it. FIN's composed identity now lists both the granted tools and the always-available primitives, and the rule is: trust the list, but if something you need is missing from it, call it anyway and let the refusal tell you the real reason.

Work the way you never could before

See how Marshal helps business move at the speed of AI.