Developer docs
Quality

The agent flow

What happens between pressing Run now and a draft post existing — with the gates, the state machine and the gaps.

docs/AGENT_FLOW.md

Six agents do jobs a person would otherwise do by hand. None of them changes anything on their own: each one works, stops, and asks. This is what happens between pressing Run now and a draft post existing.

The rule underneath all of it is the third non-negotiable in PROJECT_BRIEF.md — nothing publishes without a recorded approval. Every gate below exists to make that structurally true rather than a convention.


The whole path

Two things worth reading off that diagram.

The proposal is written before anybody decides. An agent's whole output lands in the run row while its status is awaiting_approval. Nothing about that touches your content, your budget or a platform — so a rejected run costs model tokens and nothing else.

apply.ts is a separate step from execute.ts. Running the agent and applying its result are different code paths, reached by different requests, minutes or days apart. That separation is the gate: there is no branch inside the agent where it could write early.


The state machine

state.ts is the only writer of status, and it refuses an illegal move rather than recording one.

The important edge is the one that is not there: running → applied. An auto-apply mode is one array entry away in TRANSITIONS, which is exactly why its absence is written down in the code rather than left to be inferred.


The six agents

AgentKindReadsApproving it does
Scoutmarketing_scoutpublished posts + engagement, and the unpublished pipelinerecords the read, unlocks the Ideator
Ideatormarketing_ideatean approved Scout runrecords the ideas, unlocks the Drafter
Draftercontent_draftan approved Ideator runcreates draft posts
Optimisercampaign_optimisationad campaigns + their metric historyfiles a change into a second review
Plannerplan_generationconnected accounts, KPI snapshots, existing budgetsproposes a budget/cadence
Site auditsite_auditcrawled pagesrecords findings; can reach your site

Only the Drafter creates content. The others record, propose, or unlock — which is why the card for each one states what approving it does, rather than leaving everybody to learn it once by doing it.


The content chain

Three of the six are a sequence. Each needs the previous one approved, not merely run.

gateState decides whether a step can start by looking for an approved parent run. The other three agents depend on nothing and can run at any time.

The gate is why a freshly seeded database cannot demonstrate the chain in one click: you have to approve a Scout to see the Ideator light up. That is the flow.


Who can do what

ActionGate
Start a runorganisation on Growth or above; project visible to the caller; rate limit 30/window
See a runthe project's own scope — another tenant's run is a 404, not a 403
Approve or rejectpermission agents:approve
Cancelany caller who can see the run

Starting a run is billed (incrementUsage(org, "agent_runs", 1)) and deliberately not role-gated beyond project visibility — see the gaps below.


Where it runs

npm run dev starts only the API and the web app. The worker is a separate process, so on a default dev machine every run sits in queued forever and the page shows In progress with nothing behind it. Start it explicitly:

docker compose up -d                      # postgres, rabbitmq, minio
cd apps/api && npx tsx --env-file-if-exists=../../.env src/worker.ts

GET /api/v1/health reports queue and worker; both must read up.


Trying it locally

npm run seed                # org + owner
npm run seed:agents         # history for every agent to read

seed:agents gives each agent something deliberately uneven to find — a standout post, a decaying campaign, two broken pages — because a clean fixture produces a correct run with nothing in it, which proves the agent ran but not that it works. It is deterministic and tagged [fixture], so re-running replaces its own rows and never touches hand-made ones.

The Ideator and Drafter are not seeded. Approve a Scout to unlock the Ideator, approve that to unlock the Drafter — the gate is the part worth testing.


Known gaps

Written down because a flow document that only describes the happy path is how the holes survive.

A run whose dispatch fails is stuck forever. domain.create publishes to the queue and swallows the error, leaving the row queued on the grounds that it "stays visible as such". It does not: queued looks identical to a run about to be picked up, nothing retries it, and nothing ages it out. The publisher solved the same problem with reapStalePublishing; the agents have no equivalent.

Starting a run has no role check. domain.create verifies the plan and that the project is visible, and nothing else — so a client or viewer on a Growth organisation can start runs and spend the billed quota. Approving is properly gated (agents:approve); starting is not.

Scraped pages are treated as trusted. The Site audit reads content from somebody else's website and feeds it to a model with no taint flag on it.

No per-run budget. Runs cost model tokens with no ceiling beyond the rate limit.