13 — Workflow
The working agreement: how a session runs, how a client is onboarded, how work moves from a client's ask to a delivered file, and who approves what.
00-CHARTER.md defines the vocabulary and the non-negotiables. This file defines the procedure. Where they disagree, the charter wins.
The first images of a lane carry the most weight
Justin, 2026-08-18: "the first images that are created for any client should come under the most scrutiny, given that they do set the tone stylistically for the rest of the site or app."
This is now a rule, not a preference, and it has a mechanism.
The first accepted pieces in a lane do not merely ship — they become exemplars/, which the compiler reads as canon on every subsequent run. An early yes propagates silently into hundreds of later assets, and unlike a bad individual render it never announces itself. The cost of being wrong is asymmetric, so the scrutiny should be too.
What follows from it:
- A lane's look is chosen from options, not accepted from the first attempt. The forge produces two or three genuinely different directions, rendered at the size the lane is actually judged at, and a human picks one. Presenting one good option beside two broken ones is not a choice — if the alternatives fail the stated non-negotiables, say so and regenerate rather than staging a decision that has already been made.
- Nothing enters
exemplars/without a human verdict. Not during the forge, not as a convenience. Seeding exemplars by hand happened on 2026-08-18 and was wrong: it put work in the approved gallery that nobody had approved. - A style stays
draftuntil its direction is chosen.activemeans a human said yes to the look, not that the pipeline produced something. - Spend more per image early. Three plate attempts to get a usable comparison is cheap against eighteen collectibles rendered in the wrong register — and far cheaper than a whole catalogue that has to be redone.
1. Naming, settled
Two words caused confusion and are now pinned.
"Project" is never used for a client. WootBuild lives inside a Claude project, each client is a Claude project, and the word therefore points at three things at once. The studio word is client. clients/<id>/client.yaml carries root:, which is the path to that Claude project — the link is explicit, so the duplication is a join key, not a redundancy.
"Category" is a lane. A lane is a distinct visual track inside one client: docade's badge work and its prize work are two lanes. Lanes are where style, spec, autonomy, and delivery path all get decided, which is exactly the level the word "category" was reaching for.
Two terms are added to the charter vernacular by this document. Nothing else changes, and no synonyms are introduced — these are table names.
| Term | Meaning |
|---|---|
| Request | What a client asks for, filed through the bridge (§5) before WootBuild has costed or specced it. Free text, a lane, a count, a deadline. Not yet a brief. |
| House style | A parent style sheet that a client's lanes inherit from, carrying what is common across the whole client. Lanes override with thin deltas. |
The distinction between a request and a brief is the one that earns its keep: a request is the client's words, and it can be vague, mispriced, or impossible. A brief is WootBuild's compiled, specced, priced version — the thing a run executes. Collapsing them would put an unreviewed, unestimated ask directly in front of a paid model, which the second non-negotiable forbids.
The hierarchy, end to end
client docade a Claude project that consumes imagery
└─ lane avatar a visual track; the unit of style + spec
├─ style docade-house@1 inherited, versioned, immutable once used
│ └─ delta docade-avatar@1 thin per-lane override
├─ spec avatar-tile 512×512 png transparent — mechanical only
└─ requests "60 kid avatars" from the client, via the bridge
└─ brief compiled + priced → run → candidates → verdicts → assets
→ delivery back into the client repo
2. House styles, and why lanes now inherit
The charter says lanes do not share style sheets. That holds for salon-structure, whose diagram and human lanes share nothing but a client id. It breaks for docade, whose avatars, prizes, and badges must read as one illustrator's work while differing in framing, sizing, and subject matter.
Forging three unrelated style sheets for one visual world is how drift starts: three independent probe→converge cycles, three sets of lessons, and no mechanism that keeps them agreeing a month later.
So a client may declare a house style, and lanes inherit it:
house_style: docade-house@1
lanes:
avatar:
style: docade-avatar@1 # inherits docade-house@1, overrides only deltas
Rules, so inheritance does not become a second source of drift:
- A delta may narrow, never contradict. A lane may add subject framing or tighten a constraint. It may not restate the palette or swap the model binding — that is a different house, and should be one.
- Immutability composes. Editing a used house style forks a new version; inheriting lanes stay pinned to the version they were built against and are migrated deliberately, never implicitly.
- Lessons flow up, not down. A lesson learned in one lane is proposed to the house style, where a human confirms it and every lane inherits it. This is the whole point of the mechanism.
- A house style is optional.
salon-structurewill not have one, and forcing it to would be worse than the problem being solved.
3. Client onboarding
Five stages and a half. Stages 1–3 complete before a single image is generated — that ordering is the whole point, because every expensive mistake in this business is made by producing before deciding what the thing should look like.
Stage 4.5 is the exception that proves it: a deliberate sandbox where images are generated, cheaply, precisely so that nothing is locked in ignorance of what the models actually do.
Stage 0 — The working agreement
A gate, and what advances it is the client's own words. WootBuild guides this; it does not hand over a paragraph and call it agreed. A client who writes it themselves has adopted it. A client who pastes ours has filed it.
The generated contract page (clients/<id>/bridge.md, from lib/bridge.mjs) carries a draft. It is a starting point for their version, not the version.
This stage exists because RecHero wrote it before we asked. Client #2, on their own initiative, produced a working agreement sharper than the one WootBuild generates — and Justin's instruction was to make it part of onboarding for everyone. Most of what follows is theirs, and it is better than what it replaced.
The division of labour, written sharply
The client owns the requirements. WootBuild owns the execution. RecHero's formulation, and the useful half is the second sentence:
A deliverable that misses because our brief was vague is our failure.
That makes writing a spec precise enough to be missed the client's actual work, rather than something they can wave at. It also earns WootBuild the right to be held to the other half without argument.
The circumvention list
The failure mode arrives quietly, dressed as pragmatism. Each of these feels efficient on its own:
- Generating or hand-rolling an asset inline because it is "just a quick one"
- Lowering a visual requirement because a delivery is pending
- Letting an untracked placeholder become permanent
- Deciding the studio "probably can't do this" without ever asking
Their sum is a studio that never gets exercised, and a product whose visuals drift back to whatever was fastest — which is the exact outcome commissioning a studio was meant to avoid. Naming them individually is the point: as a slogan ("don't circumvent the studio") the rule does not survive contact with a deadline.
The challenge list
The obligations that run the other way, and they are the more demanding half:
- A spec precise enough that a miss is unambiguous
- Rejecting a deliverable with specifics rather than a vibe
- Asking for something harder once the easier thing is proven
- Handing the studio problems the client does not know how to solve
- Feeding intake friction back as a finding rather than working around it
Placeholders — the honest version, not a ban
The UI has to be built against something. So a placeholder is allowed, and:
- must be visibly a placeholder
- must be logged in the request drop-box
- is never shipped to an end user
An unlogged placeholder is circumvention with extra steps.
And what is not catalogue art
The agreement is guidance, not a gate on every image. A diagram, a chart, an internal mock, a throwaway — made in the client's own repo, and authored SVG usually beats a diffusion model for anything carrying real text. Routing a two-minute job through a studio is worse than doing it locally. What must not happen is shipping product art made by handing a screenshot to an image model.
Early clients carry a responsibility
Being client #2 is a responsibility, not a perk — RecHero's framing, recorded because the instinct when a process is rough is to quietly work around it. docade came in midstream and taught the studio a great deal; the process is still refining. Friction hit at intake is data the studio needs. A client who absorbs it silently is withholding the most valuable thing they have.
Stage 1 — Understand what the assets do
Not what they look like. Where they appear in the product, what job they perform, and what they sit beside. Appearance decisions fall out of function, and a lane scoped without it produces art that is individually attractive and collectively useless.
docade's task icons are the worked example: 32 glyphs at 24px whose real requirement is that laundry stays distinguishable from fold-clothes in a list, at thumbnail size, with no colour. That requirement is invisible from the subject line and obvious from the placement.
- Function map — per lane: where it appears, what it must accomplish, what it sits next to, and how it fails.
- Foreign keys — does the client's code address these assets by filename? If so, filenames are a schema, not a naming convention, and WootBuild must take them from the client rather than invent them.
- Cost to change each constraint. Record what every inherited constraint — ground colour, palette, type, aspect, format — would cost the client to alter, beside what it costs WootBuild to work within. Where a constraint is cheap for them and expensive for us, that asymmetry is an advisory finding, not a parameter.
00-CHARTER.md§ The studio advises, question 4. - Which constraints are real yet? A token in a stylesheet is not a decision if the surface using it has not been built. Check the client's code before treating a value as settled — an unbuilt surface is a free choice, and it is the cheapest possible moment to influence it.
Stage 2 — Volume profile, and the capability gap
| Question | Why it decides something |
|---|---|
| Immediate volume | Sets the first batch, the budget, and the review cadence. |
| Ongoing volume | Decides training vs reference conditioning. A one-off batch of 30 never justifies a LoRA; 150/year does. |
| Phase order | What ships first is rarely what is most interesting to make. |
| What does the best version require? | The required question. See below. |
This stage asks what the ideal deliverable is, not what we can currently make. Per lane: given what the assets have to do (Stage 1), what is the best possible artifact for that job? Decide that first, with the roster ignored.
Only then compare it to what WootBuild can produce today — checking 14-CAPABILITY-RADAR.md, which records what the studio could adopt and what would trigger it — and write the difference down as a staffing gap — the capability, why the work needs it, a rough cost, and who can close it. A gap is a hiring decision, never a reason to narrow the ask. It is also never left implicit: an unnamed gap becomes a category that quietly ships short while every dashboard reads green.
And challenge the list itself. Stage 2 is where the studio asks whether the requested work is the right work: is it sequenced by value, does the plan contradict itself, and is any of it unnecessary? Findings go in clients/<id>/advisory-<n>.md before production starts, with the client's own evidence cited. See 00-CHARTER.md § The studio advises. Rendering a bad list beautifully costs the same as rendering a good one.
Worked example, docade crane: 14 pieces the product uses as the reveal moment of its core loop. The best artifact is animation, so the gap is animation production — not "deliver stills because that satisfies check-assets." Delivering the still is how a studio ships a hollow category and calls it done.
Stage 3 — Art direction, before anything is generated
The deliverable is a visual mission — templates/visual-mission.md, stored at clients/<id>/visual-mission.md. It answers "what should this look like, and why that" for the whole client, and it carries both halves:
- Methodology — the principles and the decision rules. Written so a future session can resolve a new case that nobody anticipated.
- Examples — what the principles actually look like. References first; probe renders once the forge runs, promoted back in.
This document governs the house style (§2). Lanes inherit from it, and a lane that cannot be derived from it is a signal the mission is wrong or incomplete — not a licence to improvise.
Gate: a human approves the visual mission before any lane is forged. It is cheap to argue about a document and expensive to argue about 200 images.
Stage 4 — Configure the client
| # | Step | Output |
|---|---|---|
| 1 | art client new <id> --root <path> | clients/<id>/client.yaml |
| 2 | Lane inventory — group by production method, not by the client's delivery categories | lanes: keys |
| 3 | House style — derived from the visual mission (§2) | house_style: or nothing |
| 4 | Mandate — how much latitude do we actually have? Declared, never assumed | mandate: |
| 5 | Budget — per-client monthly cap | month_ceiling: |
| 6 | Delivery constraints — live repo? branch-only? filename foreign keys? | client.yaml comments |
| 7 | Request source — a manifest the client already maintains, or the bridge (§5) | recorded |
Lanes group by how a thing is made, not by where it lands. docade delivers into 19 directories but has roughly 8 production methods; forging 19 style sheets would mean 19 canary sets and 19 chances to drift.
Stage 4.5 — Calibrate
Prove the machine and learn the models before anything is locked. A half stage on purpose: it sits between configuration and the forge, it runs once per client, and nothing it produces is canon.
The charter says that at volume the spec flows from what can actually be produced. Calibration is where that is learned. Skipping it means the first thing anyone discovers about a model's real behaviour is discovered inside a committed style version, which is the expensive place to find it.
What it is for
- Prove one brief runs end-to-end: compile → route → generate → candidate → provenance → ledger.
- Find out what each routed model actually does for this client's registers — a plush on
#262c4ais not a generic prompt, and a 24px monochrome glyph is not a picture. - Shake out the process: what the review loop is missing, where the harness fails, what the estimate got wrong.
Rules, so a sandbox stays a sandbox
- No style version is created. Calibration precedes the forge; it does not quietly become one.
- No canary, no delivery, no exemplar promotion. Output is disposable by construction, and cannot be laundered into the library later.
- Cheap by default — draft tier, small counts, its own ceiling. The point is information per dollar, not quality.
- Findings are the deliverable. They land in
docs/corpus/models/<key>.mdper12-TOOL-CORPUS-SOP.md, and in the notes the lane's style sheet will inherit. Images are a by-product; what was learned is the output. - It is allowed to fail. A calibration run that produces nothing usable but explains why is a success. This is the one stage where being wrong is the expected outcome rather than a defect.
Exit: one brief has run end-to-end and landed a candidate with full provenance and a ledger entry, and the calibration log can say what each routed model does for this client's registers.
Output: clients/<id>/calibration.md — template in templates/.
Stage 5 — Per lane, every time
| # | Step | Output |
|---|---|---|
| 1 | Capability, not model — vector, diagram, photoreal, stylized-volume, typographic, chart, edit, motion | capability: |
| 2 | art style forge — intake, probe, converge, validate | styles/<style>@1/ |
| 3 | Fix the canary subjects — six, and never edited after | canary_subjects |
| 4 | Spec — dimensions, format, transparency, DPI, safe areas | specs: block |
| 5 | Naming and deliver_to — from the client's schema if it has one | naming:, deliver_to: |
| 6 | QA contract — including the failure mode found in Stage 1 | in style.yaml |
| 7 | Autonomy — starts at 0. Always. | autonomy: 0 |
The gate: a lane is not onboarded until its canary set renders and a human has looked at it. A style sheet that has never produced an image is a guess.
Templates live in templates/:
| Template | Filled | Stage |
|---|---|---|
templates/client-intake.md | once per client | 1–2 |
templates/visual-mission.md | once per client, revised deliberately | 3 |
templates/calibration.md | once per client | 4.5 |
templates/lane-request.md | every request | ongoing |
A client that already maintains a machine-readable manifest should not fill out lane-request.md — consume their artifact instead. Two sources of truth for the same list is how a 204-piece catalog silently becomes two 204-piece catalogs that disagree.
4. The work loop
05-PIPELINE.md is the mechanical description. This is the human one.
request → triage → brief → estimate → run → QA → review → asset → delivery
- Request arrives through the bridge, or is typed directly.
- Triage — is there a lane for this? If not, stop: onboarding a lane (§3) is the work, not squeezing the ask into a lane that nearly fits.
- Brief —
art briefor theart-briefskill. A vague ask gets a clarifying question, never an improvised prompt. - Estimate — printed before any spend, always. Over
WOOT_RUN_CEILING, it stops and asks. This is non-negotiable #2 and has no exceptions. - Draft pass first for anything exploratory. Finals are for converged work.
- Run → candidates.
- QA — automated, mechanical then perceptual. Nothing reaches a human that fails its own spec.
- Review — contact sheet, keystroke verdicts, critique captured.
06-REVIEW-UX.md. The critique is the part that compounds; a bare reject teaches nothing. - Asset — approved candidates only, normalized, named, provenance intact.
- Delivery — into the client repo at
deliver_to. Never without a verdict unless the lane is at autonomy 3 and a human put it there.
Review structure — who approves what
| Gate | Who | Question |
|---|---|---|
| Mechanical QA | code | Does it meet the spec? |
| Perceptual QA | Claude vision | Does it meet the style's QA contract? |
| Verdict | Justin | Is it right? — the only gate that authorizes delivery |
| Corpus write-back | proposed by Claude, confirmed by Justin | Is this lesson durable? |
There is no separate client-side approval step. The verdict is the approval; delivery is what happens after it. Adding a second sign-off inside the client project would mean images sitting in two review queues with no one owning either.
5. The bridge — how clients ask, and collect
The bridge has two halves and they have different problems. The local half is declared and running; the hosted half is designed and not built.
5a. Local clients — lib/bridge.mjs, art intake <client>
When a client is a checkout on this machine there is no transport problem: the filesystem is the transport. What was missing was a contract — until 2026-08-19, docade's request source was a comment in client.yaml and a default parameter in lib/manifest.mjs. Both correct for docade, neither a declaration, so client #2 had nowhere to say it differed.
Each client declares, in client.yaml, every path WootBuild takes as input and what role it plays, because "the brief changed" and "the work-list changed" are not the same event:
| Role | What it is | A change means |
|---|---|---|
worklist | the machine-readable piece list — the work itself. Exactly one | pieces added, removed or respecced; lanes may not cover them |
brief | prose the client wrote about intent | a person reads it; nothing derives automatically |
style | their brand guide or identity package | a style sheet, or a copy we hold, may be out of date |
art intake <client> hashes each declared read, reports what moved since last time, re-reads the work-list without overwriting the snapshot — the diff is the product — and assesses whether new pieces land in an existing lane or need a new one. It is free, offline, and writes nothing without --accept.
It proposes and never acts. It creates no lane, and it never writes into a client repo. A new lane is forged by a person, and its first phase is field — the bake-off — which is a gate. That is deliberate: Justin, 2026-08-19, asked for notification and assessment, "if it's a new lane, enter into the process once the user is present" — not an automated loop.
Why it was built during the easy client. Four clients are waiting and docade's needs are straightforward. A stale snapshot against a simple manifest is a cheap lesson; the same bug against a complex client, while that client waits, is not. It paid for itself on the first run: Number('vector') is NaN, JSON.stringify writes NaN as null, and fourteen of docade's declared sizes had been silently discarded — og-image at 1200x630 among them, which would have been produced square. Same class as advisory 04's room-treatments-at-half-size, and found the same way: by diffing rather than overwriting.
Delivery is declared, not yet built. bridge.delivers.receipt names where an index of delivered work goes in the client repo, so the path is agreed before the first delivery rather than invented at delivery time. When art deliver writes it, it goes under the same human verdict gate as the assets — never unattended.
5b. Hosted clients — Supabase
Decision, 2026-08-17: Supabase, reversing the Phase 0 refusal.
01-ARCHITECTURE.md declined Supabase, and recorded the condition for revisiting it: evidence, specifically a hosted review UI or a second machine. The evidence arrived in a different form than expected, and is stronger:
The client Claude projects are hosted on claude.ai and cannot read this disk. The original refusal assumed both sides were local — that docade's manifest was a file WootBuild could simply read, which is true only for the Claude Code side of docade. A hosted Claude project with a Supabase connector can write a request row; it cannot write a file into ~/Projects/wootbuild. Files-are-truth was never the thing in question; reachability was, and it was assessed wrong.
What this does not change: non-negotiable #1 still holds without amendment.
Supabase is a transport, not a source of truth. A request lands in Postgres, and WootBuild immediately materializes it as a file under
requests/. The file is truth. Everything in Supabase must be reconstructible from the repo, andart reindexmust never need it.
Sequenced the same way — an inbox and an outbox, not a shared database:
| Direction | Carries | Truth lives |
|---|---|---|
| Client → WootBuild | requests: lane, free text, count, deadline | materialized to requests/ on arrival |
| WootBuild → Client | delivered assets: URL, filename, spec, verdict, provenance | the library + the client repo |
Isolation is the whole risk here, and it is the exact collision the charter warns about. The global SUPABASE_* on this machine points at salon-structure production. WootBuild gets its own Supabase project and its own WOOT_SUPABASE_* credentials. guard-env.sh already denies any command copying a global into WOOT_ scope, and that guard stays exactly as it is.
Known blocker, not yet fixed: assertIsolation() in lib/config.mjs flags a leak when a global SUPABASE_URL exists and WOOT_SUPABASE_URL is set — comparing presence rather than value. A dedicated project trips it as a false positive. The fix is to compare values, which preserves the real protection (catching an actual copy) and must land before the bridge is wired.
Not building yet: the browsable gallery and approval UI. Deferred until docade has assets, because a gallery designed against an empty library is a guess. When it is built, it reads from R2 — already provisioned and verified writable — and Cloudflare Pages, not Vercel. One credential relationship instead of two.
6. Session protocol
Start
- The SessionStart hook injects handoff, git state, and the last session's heading before the first prompt. No action needed.
art doctor— always, in any session that will generate.art models --verifybefore the first spend of a session that generates. Free, read-only, and it has caught six phantom model ids in one pass.
During
- Git authority is standing: push, merge, rebase,
gh,dopplerrun without prompts. Every push is gated onnpm run checkby.claude/hooks/pre-push.sh. Force-push tomainis refused. - Never spend without a printed estimate.
- When a model's behavior turns out to be durable knowledge, write it to
docs/corpus/models/<router-key>.mdper12-TOOL-CORPUS-SOP.md. - Never infer a model slug from a sibling. Read it off the vendor's page, then verify it by calling the API.
Close — the standing expectation, not something to be asked for.
npm run checkgreen; committed; pushed;mainsynced; CI green.docs/SESSION-LOG.mdupdated with decisions and anything that was wrong and got corrected. The corrections are the most valuable entries in this repo.docs/HANDOFF.mdupdated if the blocked list changed; LinearATLmatched to reality.- Project memory updated if a durable fact changed.
- No plaintext credentials on disk;
.env.localnever committed.
7. What this document does not decide
Recorded so they are not mistaken for oversights:
- jud-tools lane structure. Held deliberately until the client is ready. Known: a
diagramlane, and ascene-recreationlane that will need geospatially grounded references (Google Maps / Street View as conditioning input). No capability in the router covers that today. curl-referencegrading. salon-structure needs imagery that is accurate to a taxonomy — a 3B must not be a 2C — which is a different success criterion from every other lane, where correctness is aesthetic. It needs a reference exemplar set per curl type and a QA contract that grades against it. Designed when the lane is built, not before.- Whether docade's lanes need one LoRA or several. ATL-3, and it is decided against real reference-conditioning limits (FLUX.2 takes 10 references, the Gemini family 14), not against the understated numbers that framed it.