Skip to content

TigerMill: an AI content factory in 33 hours

Two agents built a gated webtoon factory in a weekend. The cheapest lever was not generating, and the metric that matters is the one it doesn't record yet.

Tim Urista 12 min TigerMill
Title card for Built with Agents Part 3, showing a low-poly 3D blocking thumbnail beside the finished manhwa panel it became: Ren driving a sword into a crystal crawler while Vela holds a shield behind him.
On this page
  1. What TigerMill is
  2. Five stages, and a gate after every one
  3. Two agents, 33 hours
  4. The 30-minute policy reversal
  5. Keep money out of the agent’s toolbelt
  6. What a dollar of image generation taught me
  7. Pretty is not correct
  8. Dollars per accepted panel
  9. What I’d carry into the next factory

At a glance

  • Codex scaffolded TigerMill, a Go + SQLite + Three.js webtoon factory, and Claude Opus 5 co-authored the next 11 commits. 13 commits, 33 hours.
  • At 10:13 every gate needed a human. By 10:43 agents could close gates, made safe by recording who acted plus revision checks that return 409.
  • The MCP server has 13 tools and none of them can generate an image. Paid calls are human-triggered, dry-run by default, and never retried.
  • About a dollar of fal requests proved generating from scratch loses to repairing a panel, so the pipeline flipped to repair-first.
  • The project defines the right metric, dollars per accepted panel, and doesn't store the data to compute it. That gap is the lesson.

The best-looking image in the TigerMill repo is a rejection. A party of four walks into a crystal cave. An antlered golem glows at the back, crawlers crowd the rocks, and wet light pools on the floor. You could put it on a pitch deck.

It was rejected in one line: the shot contained only the party. The model added a boss and a small army because the style reference had them in it.

That frame is the whole project in miniature. Generating an image is cheap. Generating the right image, and knowing it’s right, is where the cost lives, and that cost is not on anyone’s pricing page.

TigerMill is a factory for turning a novel into a webtoon, the vertical-scroll comic format also called manhwa. I built it over one weekend with two coding agents. The directory was created Friday at 08:18 and the thirteenth commit landed Saturday at 17:35. This is Part 3 of Built with Agents. It has the smallest invoice of the series and the biggest unit-economics lesson.

Wall clock

33h

Fri 08:18 to Sat 17:35

Commits

13

2 by Codex, 11 co-authored by Claude

MCP tools that generate

0 of 13

Spend stays out of the toolbelt

Measured fal spend

≈ $1

8 paid requests

What TigerMill is

Before computers took over, I spent years in middle school learning Renaissance painting technique: charcoal, oil copies of Rembrandt, getting lost chasing the perfect line. So a pipeline whose render stages are literally layout, then line art, then color did not need much selling.

The runtime is deliberately small. One Go process serves the HTTP API with JWT auth, a SQLite WAL queue, and exactly one worker that makes one provider call at a time with a three-minute deadline. The container has a 256 MiB memory limit and no Python, Redis or GPU. The studio runs in the browser with React and Three.js: 3D blocking with mannequins, a storyboard editor, a sequence studio that exports animatics and control packs, and a scroll reader.

Each panel moves through layout, lineart and color. Each stage gets three attempts. Approval resets the counter, rejection requires written feedback, and nothing retries a paid job automatically.

TigerMill studio dashboard with a five-stage sidebar, five stage cards labeled You or You or agent, and a counter showing 120 panels ready for review.
The production floor. Each stage card says who may act there. The footer reads 'Demo rendering · No API spend'. When I took this, 120 panels were waiting on review.

Five stages, and a gate after every one

One file, navigation.ts, defines the stages: Story & beats, World & cast, Shot & lettering, Structure check, Episode. Each ends at a gate. Review is not a sixth stage. It’s a pinned queue that every gate reports into, and the pipeline doc explains why in a line I wish I’d had at a few past jobs: “holding review until the end is the most expensive possible arrangement.”

Stage 4 is labeled “the last free stage.” Its gate checks grip, blade axis, contact point, occlusion and limb length “before one paid pixel.” Everything to its left is local geometry you can redo a hundred times for nothing. A defect you catch in a 600px outline costs zero. The same defect found in a color candidate costs a generation plus a review cycle.

Diagram of five stage cards with gates underneath. Gates for script approval and structure check are human-only. Gates for set pinning, composition and episode build can be closed by a human or an attributed agent. Below an amber line, one billed AI candidate step feeds a review, which either repairs the panel, sends it back to stage 3, or accepts it into the episode.
Free steps above the amber line, the one billed step below it. Teal gates can be closed by an agent, and the record says which one did.

Two agents, 33 hours

The git log tells a two-agent story. The first two commits have no co-author trailer, and the review files from that phase are signed “Codex, explicitly delegated by the user,” acting as creative director. Commit 1 was the whole scaffold at once: 161 files and 21,286 lines of Go backend, React studio, Dockerfile, k8s manifests, docs and character sheets. Commit 2 carries the eloquent Friday-night title “support mcp changes” and 61,913 insertions, mostly PNG and JSON.

Commits 3 through 13 are all co-authored by Claude Opus 5. Saturday morning was infrastructure: conditioning exports, attribution and claims, the fal trial, a trace bench, a handoff doc. Saturday afternoon turned to the world bible, with four passes on story questions in 18 minutes. Then came two commits where story decisions were openly shaped by production cost.

Lollipop chart of 13 commits on a time axis from Friday 08:00 to Saturday 18:00 with an overnight break. Lines changed per commit on a log scale: two large gray commits without co-author trailers, then eleven smaller teal commits co-authored by Claude. Annotations mark 10:13 'Every gate is a human' and 10:43 'Agents may close gates'.
Lines changed per commit, log scale. Gray has no co-author trailer. Teal is co-authored by Claude Opus 5. The two annotated lines are 30 minutes apart.

The 30-minute policy reversal

At 10:13 on Saturday, the pipeline doc said it flatly: “Every gate is a human.” Agents could compose shots, record feedback and assemble previews. Approval was reserved.

At 10:43, 0cc6383 Let agents close gates, and record who acted reversed it: “Agents do most of the work while a human reviews alongside, so gates are no longer human-only. What makes that safe is attribution, not restriction.”

The first rule guarded the wrong thing. The risk was never an agent pressing approve. The risk was the record lying about who looked. An approval is a claim that someone checked the work, so the fix makes the claim honest:

  • Every review record carries by. Agents are named actors, like agent:modelling and agent:rendering.
  • A final color approval by an agent “reads as asserted by that agent, never as a human reviewer attestation.” That rule lives in the tool description the model reads, not only in the docs.
  • Claims let an actor take a set of panels, 10 minutes by default and an hour at most. Each claim stores the revisions it holds, so changes made by someone else show up as stale. Presence expires after 90 seconds.
  • Expected revisions are the actual guarantee. Every write names the revision it was based on, and a mismatch gets a 409.

The handoff doc’s line on that last point is my favorite in the repo: “A 409 is information, not an error to retry.” Claims only stop two actors from spending effort on the same shot, and they expire because “a dead agent must not hold a shot forever.”

This is the same shape as the cost attribution work I did at Roku and Apple. Nobody owns a cost they can’t see, and nobody can trust an approval they can’t attribute. The control that matters isn’t “who is allowed.” It’s “is the ledger true.”

Attribution is not a substitute for looking, though. Delegated approval had already failed once. On Friday, Codex approved the original three-panel art test as director. I looked at the boss panel and found an implausible relationship between the shield, the defender and the attack. The approvals were withdrawn in writing. The fix was structural: rebuild as an editable 18-shot 3D storyboard, so the next approval would have geometry to check against. Attribution didn’t prevent the bad call. It made the bad call cheap to find and reverse.

Keep money out of the agent’s toolbelt

The MCP server grew from 6 tools to 7 to 13 across three commits. An agent can read state and assets, update a scene at a revision, request changes, get, rebuild or preview an episode, review a panel, and manage claims and presence. None of the 13 can generate an image, retry a paid job, capture the browser or import a candidate. The server’s instructions say so directly: “No paid generation tools are exposed.”

Paid generation lives in a separate script that is dry-run by default, needs --confirm to spend, and never retries. The fal notes explain the “never.” An HTTP 422 is rejected before execution and bills nothing, but any other failure may already have started, and “a timeout is not a cancellation.” A retry loop on a billed endpoint is how you pay twice for a panel you’re about to reject.

Context is a cost line too. factory_state returns the entire factory: 4 chapters, 58 panels, about 122 KB, roughly 30k tokens per read. The docs say read once per session and let 409s catch staleness, because that is “cheaper than re-reading defensively.”

What a dollar of image generation taught me

Late Saturday morning the agent ran the conditioning experiment against fal: eight paid requests, “roughly a dollar.” Warm FLUX requests came back in 5 to 12 seconds. Two cold runs on a custom Illustrious checkpoint took 128 and 243 seconds.

The first result was the most humbling. FLUX.1-dev, given the full character-lock paragraph and no reference, “produced three characters instead of four, no white forelock, no ivory mantle, a bow instead of two short blades, and the bronze shield on the wrong person. Only the cobalt/turquoise palette survived.”

Left: a polished anime lineup of four characters, Ren, Vela, Io and Pip, with distinct costumes and props. Right: a generic cartoon image of three characters in a cave with swapped props, generated from a prompt only.
Left, the cast the prompt described. Right, what FLUX.1-dev returned from the prompt alone in 11.9 seconds. Adjectives don't carry costume, headcount or props.

The rest of the findings, in the order they cost me something:

  • Img2img at strength 0.8 mostly discards the reference. The reference arm was no better than prompt-only.
  • Base model beats conditioning machinery. Illustrious produced “recognisable anime line work where FLUX produced generic cartoon,” but still one character, no cave and no arms.
  • IP-Adapter averages references into one identity. Six turnarounds at once “blended four people into pure noise.”
  • CFG doesn’t transfer across model families. 3.5 is a FLUX number, and on SDXL “it makes mush.” Use about 6.5. As the notes put it, “a ‘bad model’ result is often a parameter carried across families.”

The working conclusion: generating from scratch loses to the chat-tool output the project already had, and conditioning is “a structure tool, not a style tool.” So the pipeline flipped to repair rather than regenerate. The control pack now exports overlay.png, the pose skeleton drawn over the blocking render, described as “where the limb belongs, against where the candidate put it.” The ink studio is the bench where you trace the fix.

Structure references help when they carry volume, not just edges. One image call from a volumetric mannequin kept both hands on the hilt and the blade going down into the shell. One call from an edge-only outline came back beautiful and wrong. That outline trial was also confounded by a handedness bug: the rig named the anatomical right side left*. A test now guards it, because “a regression there would silently mirror every pose map we ever generate.”

Four panels. A grey 3D mannequin in a two-handed downward stab, then an anime render that holds the pose. A line-art outline of a mannequin standing over a crawler, then an anime render that changes the arm pose and sword direction.
Volumes in, pose kept (left pair). Outlines in, pose lost (right pair). One image call each, and one result proves nothing general. The repo says so too.

The path that actually filled an episode used the chat tool’s image generation with 3D atlas references. Each request drew a six-shot sheet, so the 36-shot episode took 8 requests: six atlases, one tall opening and one correction. Those requests aren’t metered anywhere in the repo, which matters later.

Left: a low-poly Three.js atlas of six shots with mannequins on a cave ledge. Right: the finished manhwa panel for shot 9, with Ren stabbing a crystal crawler while Vela holds a shield behind him.
Free blocking in, candidate panel out. Batching six shots per request cut requests roughly 4.5x.

Pretty is not correct

Early on, four shots went through image generation and three were rejected outright for inventing enemies. None were rejected for being ugly. The review note that followed made a rule of it: later prompts must include an explicit inventory of visible actors. Counts, identities and world positions are the contract. Camera cuts may move things on screen, but they can’t add a crawler.

Left: 3D blocking for shot 2 with four simple mannequins walking toward cave columns. Right: a lush anime illustration of the same party facing an antlered golem and dozens of crawlers.
Shot 02. The blocking has four people. The candidate has four people, a boss and an army. Rejected.

The cheapest idea of the weekend came from the story side. The world bible needed brief ghosts at the edges of shots. The expensive version is new character sheets, rigs and prompts. The shipped version is c5114ef Give brief ghosts a look, built by subtraction : ghost: true on an actor reuses the existing mannequin with every identifying part removed. There is no new geometry, sheet or rig. Ghosts are excluded from the pose map so “a corridor full of extras” can’t fight the subject for the control map. The test asserts fewer meshes, no accent color and no pose figure.

The bible then turned the cheapness into story: “a person appearing and vanishing is worth nothing here.” I’ve watched plenty of product decisions get shaped by cost. This was the first time I saw an agent make a narrative call for the same reason and put it in a commit title: “ghosts kept cheap.”

Dollars per accepted panel

Here is the part that stings, given what I do for a living.

The provider evaluation defines the metric correctly: “dollars per accepted panel — never dollars per image. An arm that costs triple per image and halves the reroll rate wins.” Then the handoff doc, item 6 of next steps: “Nothing stores provider, model revision, or spend, so cost per accepted panel — the number that actually matters — cannot be computed.”

The factory doesn’t record the number it says matters most. I’ve built attribution pipelines for $10–12M a quarter of AWS spend and across a fleet of roughly 700,000 servers, and I opened this repo to find my own weekend project with the right metric written down twice and no column to store it.

The list prices are easy to find, and they’re not the whole story. The project researched them from vendor docs:

Horizontal bar chart of list price per image: GPT Image 2 $0.28, Nano Banana Pro $0.14, Nano Banana 2 $0.08, fal flux-general about $0.055 to $0.063 at 1024 by 1536, FLUX.2 about $0.047, Seedream 5.0 $0.03. flux-general is marked as accepting a hard structural control map.
Vendor list prices researched in September 2026, not measured. The options that accept a pose or depth map are also near the cheap end, so control isn't a quality-for-cost trade.

A 9x spread per image looks decisive until you divide by acceptance rate. The table below is an illustration only. It uses the repo’s list prices, assumes one image per attempt, and makes up the acceptance rates, because nobody has measured them yet.

Acceptance rateSeedream 5.0 ($0.03)flux-general (~$0.06)Nano Banana Pro ($0.14)GPT Image 2 ($0.28)
100%$0.03$0.06$0.14$0.28
50%$0.06$0.12$0.28$0.56
25%$0.12$0.24$0.56$1.12
10%$0.30$0.60$1.40$2.80
36-panel episode at 25%$4.32$8.64$20.16$40.32

Read it diagonally. Seedream at 10% acceptance costs $0.30 per accepted panel. GPT Image 2 at 100% costs $0.28. The cheapest image isn’t the cheapest panel if you throw away nine of ten.

The fix is small. Put provider, model revision, request ID and billed amount on every candidate artifact. The review outcome is already recorded. Then divide. Until then, “roughly a dollar” is a vibe, not a unit cost.

What I’d carry into the next factory

  1. Put the paid step behind a human trigger and outside the MCP tool list. Dry-run by default, explicit confirm, no automatic retries.
  2. Let agents close gates, and make the record honest. Named actors, agent approvals labeled as agent assertions, revision checks that return 409.
  3. Spend the free rungs first. Blocking, outlines, pose maps and animatics catch structural defects for zero dollars.
  4. Record spend per artifact from the first paid call. Not as item 6 of next steps.
  5. Keep tool descriptions in sync with the docs. The model only reads one of them.
  6. Distrust one pretty image. Fix the rubric before you look at the output, and count defects, not wow.

The factory can draw a panel in twelve seconds. It still can’t tell me what an accepted one costs. That’s the next commit.

agentsgenerative-aipipelinesunit-economics

Cash-pay advisory
from $250/hr

Check fit