writing/agent graphs ·

The model cannot own the money

Dozen is a twelve-node marketplace between brands and 1k–50k TikTok and Instagram creators. The model writes captions. It does not accept a seat, post a disclosure, or release a payout. This is what that constraint actually forced.

Every number below comes from production-imported tests in the private product repository, measured 19 August 2026. The public artifact is the case study. This page does not claim live Stripe volume, Connect transfers, or GMV.

01 / the hole

The work after “I found someone” is still a spreadsheet

Micro-creators with 1k–50k followers already make $50–$200 posts. They find that work in DMs, in Discord, or not at all. Brands who want authentic reach either overpay one macro or hire a coordinator to wrangle a dozen micros by hand.

GRIN and Aspire sell the coordinator an $11k–$100k/year cockpit. Collabstr and #paid sell a directory and leave the DMs to you. The hole is not discovery. The hole is brief, draft, FTC, revise, post, pay — the work that happens after a name is on a list.

That is a graph, not a chat. A one-pass agent demo papers over it by marking accept, post, and pay passed so a single handler can look finished. I built that version first. It took about forty-five minutes and it was a product lie. The captions were fine. The money edges were fiction.

02 / why the obvious designs fail

Three ways to fake a marketplace, and one way not to

Let the model close the irreversible edges

Accept spends a creator’s reputation. Post spends a disclosure. Pay spends someone else’s money. If the model owns those nodes, the demo finishes and the product is a caption generator with a progress bar. A failed verifier has to be a stop, not a suggestion the model can talk past.

Charge for the cockpit instead of the work

Enterprise influencer SaaS sells seats to the coordinator. That is a reasonable business if you are selling software to agencies. It is the wrong shape if the product is supposed to be the coordinator. Dozen takes 18% when a swarm pays (15% on priority or on a $2,500+ budget). No seat license. The platform is paid when money moves.

Ship a finished campaign as if it were live GMV

A public /demo that looks paid is the fastest way to lie. Dozen’s demo is labeled a closed sample against the seeded bench. Views are derived from baseline engagement, not a TikTok API. The live graph leaves accept, post, and pay open. I would rather a visitor see an unfinished node than a fabricated payout.

03 / the graph

Twelve nodes. The model owns one of them.

Draft is the only node whose closer is the model. Everything else is a script, a policy, or a human. Irreversible nodes — safety, accept, post, pay — cannot be owned by the model. A companion graph linter fails the topology if they are.

NodeCloserDone when
intakescriptBudget ≥ $200, niche + platform present
safety· irrev.policyFail-closed banned verticals
assemblescript6–16 micros, fee math, fake-risk < 70 bps
offerscriptRate band + brand escrow funded
accept· irrev.human≥60% accept or explicit bench refill
draftmodelDrafts exist only for accepted seats
verifyscriptFTC disclosure + must-say + avoid
revisescript≤2 selective retries on this subgraph only
reviewhumanBrand sees only the batch
post· irrev.humanDisclosure still present on every accepted slot
measurescriptER not anomalous vs baseline
pay· irrev.policyEscrow funded + posted + no open dispute

The inner loop is draft → verify → revise, max two attempts, and it does not restart intake or assemble. The outer loop is accept, which stays running until 60% of offered seats accept and at least six seats fill — or the brand explicitly refills from the bench. Rejected seats stay in the offered denominator. Six accepts of sixteen is not enough.

04 / escrow

Accept stays idle until the budget is on the platform

A marketplace that lets creators accept before the brand’s money has landed is asking them to spend reputation on a promise. The first version did that. Offers went out the moment assemble passed. That was the second product lie.

Launch now stops in funding. Offers are written. Accept is idle. The brand opens Stripe Checkout for the full budget. Confirm reads the session back from Stripe: payment_status must be paid, the metadata must match the brand and the campaign, and amount_total must be at least the budget. One cent short stays fail-closed. A missing secret key never creates a session.

Only then does accept open. Pay has a third gate on top of “every accepted seat posted” and “measure is clean”: escrow must already be funded. Anomalous engagement — more than 3.2× the creator’s baseline ER, or under 0.15× — holds that seat and holds the escrow. The campaign does not flip to paid so the graph can look finished.

What pay does write, on a clean run, is a platform ledger row. The detail field says the quiet part: creator bank transfers wait on Stripe Connect. Connect is not onboarded. Queuing a transfer I cannot send is honest. Marking the creator paid in the world is not.

05 / results

What the tests own, and what they do not

The tests import the live matching, loop, verifier, escrow, and Stripe modules. They are not a second implementation. As of 19 August 2026: 43 product tests and 5 graph-linter tests, 0 failures.

PropertyMeasuredTarget
Production-imported tests43 passImport live modules, not stubs
Graph linter5 passModel cannot close an irreversible edge
Accept without funded escrowclosedFail-closed until Checkout is paid
Paid amount below budgetrejectedUnderpay cannot open accept
Missing Stripe secret keythrowsCheckout and priority stay off
Anomalous ER after postthat seat heldMeasure can fail without closing pay
Clean measure + funded escrowledger releasedBank transfers wait on Connect
Production GMVnot claimedNo live money, no live posts
the thing this page is not

A launch essay is usually a traffic event. This one is a constraint dump. There is no live campaign, no creator who has been paid, and no brand budget that has moved through Stripe in production. The 43 tests prove the graph refuses to pretend otherwise. That is the result.

06 / what is still scaffolding

The parts I will not dress up

01

Stripe Checkout is wired. Live production money is not.

The product talks to Stripe’s Checkout Sessions API the same way Cite already does: form-urlencoded REST, no SDK, a secret key or a thrown error. If the key is missing, priority stays off and escrow stays unfunded. That is the correct failure. It is not the same thing as a live marketplace moving brand money.

02

Connect transfers are a ledger row

When pay passes, Dozen writes a dozen_payouts row with status queued and the sentence “Connect transfers are not live.” The 18% take is computed. The creator’s rate is recorded. No bank account is credited. Claiming payouts here would be the same class of lie as auto-closing accept.

03

Creators still tap “mark posted”

Post re-checks #ad fail-closed. It does not fetch a TikTok or Instagram URL, and it does not confirm the live caption. The public /demo is labeled a closed sample against the seed bench. Views on that page are derived from each creator’s baseline ER, not a platform API.

04

The repository stays private

The public artifacts are this essay and the case study. Shipping a public repo before Connect and live post confirmation would invite people to treat a graph that is honest about its holes as a finished marketplace.

07 / the actual product

Agent products are interesting where the model is not allowed to finish

Most agent demos optimize for a complete-looking trace. The trace is the wrong object. The object is the set of edges a model is forbidden to close — accept, post, pay, publish, wire money — and the verifier that owns each one when the model wants to keep going.

Dozen is that constraint applied to a two-sided market I actually want to exist: brands in a city, micros who already post, a coordinator no one wants to hire. The captions were never the hard part. The hard part is refusing to mark the swarm paid until the money is real.