Skip to content
MUNEEB SHAFIQ
AGENT GOVERNANCES06

Puffo Marketing Studio

Marketers ship compliant, on-brand assets from plain language, no Git, Markdown, or HTML.

  • Python 3
  • FastAPI
  • Starlette
  • uv
Status
Working MVP on an unmerged branch, last pushed 2026-09-01. Nine of twelve PRD acceptance criteria are verified and reproducible on the build machine; the three requiring four live agents and a second human account are recorded as BLOCKED. One real job ran with three operator decisions logged. The pipeline runs end to end without agents via the job builder, audit and editor. Four rule-book changes the PRD requires are written up but deliberately not applied, pending a human yes. Not merged, not deployed beyond the local machine, and no evidence in the repository that the client's marketers have used it.
Private repository

By the numbers · 7

  • 27

    automated end-to-end checks against the live service

  • 9 of 12

    PRD acceptance criteria verified and reproducible; 3 marked BLOCKED, not passed

  • ~1,700 / ~1,300

    lines of Python service and script code / lines of browser ES-module code

  • 4 agents, 2 model tiers

    roster split by decision authority, with the reviewer alone on the larger model

  • 3

    asset types with shipped template specs (poster 1080x1350, 16:9 deck, responsive landing page)

  • 15 commits over 2 days

    scope of the branch, from template contract to operator handover

  • 500ms / 1s / 180s

    autosave debounce, hard save cap, and stale-lock release

Summary

A four-agent production line, built on the Puffo agent platform, that turns a marketer's plain-language request into an HTML asset they can edit in a browser and export as PNG. A lead agent handles intake and gates; a copywriter resolves facts through the repository's existing source-of-truth registry and writes plain-text copy keyed by region id; an independent reviewer running a larger model is the only agent permitted to issue PASS; a designer builds the HTML master and runs a layout audit. Two interlocks carry the design: a blocked asset can never reach a marketer-editable state, and no editor link is ever promised without the local service answering its health check and the file existing on disk. A local FastAPI service serves one editable asset per job over localhost or LAN, with a single-writer lock, debounced autosave, text that shrinks to a template floor and then names the cut rather than clipping, and placeholder-only image and QR replacement that refuses a non-square QR with a reason instead of cropping it into something that will not scan. HTML is the sole editable master; the same Chrome instance renders the preview and the export, so they cannot diverge.

The problem

A client marketing team produced campaign assets by hand from a repository of approved course facts and compliance rules. Three frictions remained. Non-technical marketers had to touch Claude Code, Git, Markdown and HTML to get anything. Changing one date or price meant another model request, adding latency and cost to a trivial edit. And converting the high-fidelity HTML posters into an editable PPTX visibly degraded typography, spacing and shadows, leaving two masters that drifted apart. The team needed a request-to-asset path where a marketer edits in place and no factual or compliance error survives to the editing stage.

Approach

  1. Split the roster by what each agent is allowed to decide rather than by task convenience: the lead routes and never rules on compliance, the copywriter writes and never clears its own work, the reviewer judges and never drafts a replacement, the designer builds only after PASS.

  2. Made the compliance gate structural instead of advisory, `design` is unreachable until `review.md` ends in PASS, so a blocked asset has no path to a marketer's editing session.

  3. Ran the reviewer on the larger model tier while the copywriter and designer run the smaller one at high inference: the reviewer's job is to be harder to convince than the agent that produced the work, which is the one place a stronger model buys something the rest of the system cannot.

  4. Made the copy handoff JSON keyed by region id rather than HTML, so `[NEEDS CONFIRMATION]` becomes a machine-detectable value in a named field and the state machine can refuse to advance while any remain.

  5. Defined a template contract (`data-mk-*` markup plus a `template.json` of canvas, regions, placeholders and per-language budgets) so the editor understands the contract, not templates, a hand-written bespoke poster and a library template are equally editable, and the library can grow without touching the editor.

  6. Transcribed the per-region character budgets from the marketing team's existing skill file into template config rather than hard-coding them, so the team keeps ownership of the table it already maintained.

  7. Added a second interlock against the failure mode that costs most: a confident message about an asset that does not exist. The lead verifies `/healthz` and the file on disk before issuing any link.

  8. Built the whole pipeline to run end to end without a single agent, `new_job.py` to `audit.py` to editor to export, which is how the editor was developed and tested, and what let acceptance be measured before the agents were provisioned.

  9. Wrote the acceptance report to mark three of twelve criteria BLOCKED, with the reason and the operator action each needs, rather than counting a partially-satisfied criterion as a pass.

Architecture

puffo-marketing-studio · flow
A marketer posts a plain-language request in a platform channel.The lead agent asks once for whatever is genuinely missing, asset type, offering, channel, audience, language, goal, deadline, mints a job id, and writes the brief.The copywriter resolves every fact through the repository's offerings registry and emits `content.json`, plain text keyed by region id, with any unresolvable field marked and listed.The reviewer opens the actual files rather than any agent's summary, checks each concrete claim character by character against the file that owns it, applies the compliance tier the declared publishing channel demands, and writes a report ending in PASS or BLOCKED; a block returns the job to the copywriter with the findings attached, and a second block on the same asset escalates to a human.On PASS the designer builds `material.html`, filling a library template or hand-writing one in the house style, marks every marketer-touchable element, emits the matching `template.json`, writes the AI-initial snapshot, and runs the layout audit, which loads the real document in the real browser and measures overflow, section collisions, line counts and character budgets.The lead then verifies the editor service is answering and the file exists before issuing the link.The marketer edits text in place with no edit mode, drops images and QR codes into declared placeholders after a preview, and exports a PNG rendered from that same document by that same browser.Publishing stays a manual human step outside the system.
ComponentRole
marketing-lead (Sonnet tier)Intake, job id minting, the nine-state machine, both interlocks, and the operator's only inbox, no other agent may DM a human
campaign-copywriter (Sonnet tier)Resolves facts through the offerings registry and writes `content.json` keyed by region id; never clears its own compliance
fact-reviewer (Opus tier)Independent fact and compliance review against the owning file; the only agent that can write PASS, and never drafts the replacement copy
visual-designer (Sonnet tier)Builds `material.html` and `template.json`, marks editable regions, runs the layout audit, renders PNG; never produces PPTX or PDF
Local editor service (FastAPI)Serves one editable asset per job over localhost/LAN behind a per-job token; owns the write lock, autosave, uploads, snapshots and export
Editor front end (vanilla ES modules)In-place text editing, font fitting, placeholder media with preview, undo/redo, deck navigation, and a viewer-language interface independent of the asset's language
Template contract`data-mk-*` markup plus `template.json` canvas, regions, placeholders and per-language budgets, the editor understands the contract, never the template
audit.pyLoads the document in headless Chrome and measures real rendered overflow, child collisions, line counts and budgets; the designer's gate and the template CI check
selftest.py27 end-to-end checks against the live service, from token refusal through stale-lock release to export pixel dimensions
Job directoryOne folder per job: brief, status, content, review, the HTML master, template, assets, snapshots, exports, lock state and an append-only event log, gitignored, never auto-committed

Trade-offs

  • Chose

    A standalone browser page for the editor

    Over

    Embedding it in the agent platform or a Claude Design WebView

    WebView constraints on rendering, file uploads, caching, developer tooling and PNG export were the long pole; a plain browser tab gave direct control over all five and shortened the MVP path (ADR-001).

  • Chose

    HTML as the sole editable master

    Over

    Keeping the existing HTML-to-PNG publish track alongside a python-pptx editable track

    Two masters drift, and the repository's own rule book already documented that pptx cannot reproduce CSS gradients, soft shadows, fine corner radius or real icons. One master cannot disagree with itself (ADR-002). The cost is named honestly in the change proposal: anyone editing the pptx in Google Slides loses that path.

  • Chose

    Locked layout with editable content

    Over

    A free-form canvas with layers and drag-and-drop

    Text, image, QR and predefined section visibility cover the high-frequency marketer edits, while protecting brand consistency and cutting editor complexity by an order of magnitude (ADR-004). Anything without a `data-mk-id` is simply unreachable from the browser.

  • Chose

    One writable session per job with a 180-second heartbeat lock

    Over

    Real-time collaborative editing with conflict merging or CRDTs

    A single-client local deployment does not justify the complexity, and a stale lock releasing on timeout covers the actual failure (a closed browser) that would otherwise block an asset permanently (ADR-005).

  • Chose

    Driving the installed Chrome or Edge directly, reading results back via `--dump-dom`

    Over

    Playwright or a CDP client

    No browser download and no Node dependency, and the same binary renders the preview and the export, so they match rather than approximately match. The readiness check needs only uv, git and a browser.

  • Chose

    Localhost and LAN access behind an unguessable per-job token

    Over

    Building authentication and public access into the MVP

    Validating the generation and editing experience came first; identity, expiring remote tokens and hosting were deferred to a named Phase 2 rather than half-built (ADR-003).

At scale

  • Four agents across two model tiers, on one platform space with two channels

  • Nine job states, two hard interlocks, one escalation list of eight conditions

  • Three asset types: 1080x1350 poster, 1920x1080 three-slide deck, responsive landing page

  • Twelve browser ES modules and ten Python modules, roughly 3,000 lines total

  • Twelve PRD acceptance criteria, five architecture decision records, four proposed rule-book changes

  • One job directory per request holding brief, status, copy, review, master, template, assets, snapshots, exports, lock state and an append-only event log

My role

Sole author of the entire `marketing-studio-mvp` branch: the four agent briefs, the state machine and both interlocks, the local editor service and its browser front end, the template contract, the layout audit and self-test harness, the three template specs, the platform space runbook, the acceptance report, and the rule-book change proposal held back for human approval. Fifteen commits between 2026-08-31 and 2026-09-01, structured as Phases A through G plus six fixes driven by the first real job. He authored none of the default branch, the marketing SOP, the fact registry and the five business skills his agents consume are another engineer's work, and his system was deliberately built to add to them and migrate nothing.