# Artifact contracts

Workflow graph version is 3. JSON artifacts use version: 2 unless a type names schemaVersion 1 or the storyboard uses version 3. Compute hashes with the portable evaluator command supplied in CURRENT.md: use --hash RELATIVE_PATH, repeating --hash for multiple paths. It calls the harness's authoritative `fingerprint(root, paths)` from `src/agent/artifacts.ts`; never invent hashes. Types in that module are the authoritative field contract. Read only the relevant type. Paths below are relative to the client site.

- Business: `onboarding/business.json`. offer, audience, trigger, outcome, includes[], engagement, positioning; facts[{id,claim,evidence:[evidenceId]}]; evidence[{id,source,excerpt}]; uncertainties[{question,blocking,resolution?}]. Sources are captured copy paths or explicit public URLs. Quoted excerpts must actually support the claim.
- Media: `onboarding/media-inventory.json`. harvestHash over [`onboarding/brief.json`,`media/harvest`]; assets[{id,path,sources[],description,role,disposition,reason,inspected,placements[],sha256?,duplicateOf?}]. disposition is use/retain/duplicate/exclude/unresolved. Paths start /media/. Account for every local harvest asset; inspect every selected image. Every added `/media/recovered/` or `/media/stock/` file, including retained files, needs local SHA-256, matching origin, source evidence and a unique inventory mapping. Video posters use `poster` plus `posterSha256` or an existing content-addressed filename. Register recovered customer bytes with `media:register`; hashing does not prove inspection or identity.
- Direction: `onboarding/direction.json` version 2 plus `designContractVersion` 1. businessHash over [`onboarding/business.json`]; request (exact operator theme when one exists); candidates[{id,rationale,typography,composition,mediaTreatment}]; selected; rationale; system{personality,typography,colorRoles,density,rhythm,mediaTreatment,surfaces,navigation,motifs,interiors,avoid[]}; studies[{candidate,path}]. Decision trail: centralIdea, opportunityIds, decisions[{id,kind,choice,rationale,scope,tradeoff}], rejectedAlternatives (empty only when the operator prescribed the direction), identityInvariants, flexibleDecisions. The system fields stay authoritative for tokens. Studies are private sketches. Assigned mode elaborates one provided brief.
- Narrative: `onboarding/narrative-brief.json` schemaVersion 2. schemaVersion 1 remains readable and fails the current gate instead of receiving invented opportunities. Fields: businessHash; mediaHash; customer; problem; offer; serviceScope; proof{statement,status,evidenceIds[]}; conversion; objections[]; unsupportedClaims[]; facts[]; visitorQuestions[]; assetNotes[]; toneConstraints[]; recordedUserGoals[]; designOpportunities[{id,visitorNeed,businessRelevance,evidenceIds,mediaIds,presentationOpportunity,constraints,basis,uncertainty?}]. `basis` is `evidence-backed` or `design-hypothesis`. A hypothesis must name its uncertainty. A populated opportunity is not an aesthetic score.
- Reference analysis: `onboarding/reference-analysis.json` schemaVersion 1. packVersion; packHash; narrativeHash; applicable[]; avoided[]; plannedApplication; evidencePaths[] confined to the calibration pack; teachingPrinciples[{id,version,contentHash,reason}] selected from `design-system/knowledge/v1`. Teaching ids are not calibration screenshots. The calibration pack is not a preference dataset. An unselected lesson changing does not stale a selection whose content hashes still match.
- Design-quality review: `agent/reviews/representative-quality.json` schemaVersion 1 kind design-quality. Anchored 0-4 dimensions with page/viewport/section evidence. visualInspection{available,probeHash,observedToken,captureHashes}. The runner archives the representative-quality adapter log and writes `agent/reviews/representative-quality.receipt.json` binding logHash, current probe file hash, and representative screenshot hashes. Validation reads that archived log, not toolReads inside the review JSON. Missing probe file hash, mismatched screenshot hashes, missing PNG image-read events, or a missing dataBlobId/image payload cannot pass. Generic adapters without verifiable image-read events fail closed. Private sketches cannot pass. Quality repair budget is per candidate: initial review plus two author repairs; exhaustion is rejected/blocked.
- Parent diversity review: `agent/design-runs/<run-id>/direction-review.json`. schemaVersion 1; kind `direction-diversity`; unique candidate rows with substantive observations for every brief ID; axes with non-identical evidence. Pass cannot carry repair or unresolvedSimilarity. Duplicate rows cannot substitute for a missing candidate. Critic allowlist is the review file plus invocation logs/receipt. Malformed reviews cannot enter repair.
- Usage receipts: `agent/runs/<iteration>-<node>.usage.json` and design-run `*.usage.json`. Persist adapter/model/timing. Provider cache fields stay separate. Interrupted or unmetered calls mark usage.unknown. Author and reviewer adapters are configured separately (`agent/adapter.json` and optional `agent/adapter.reviewer.json`).
- Design-run feedback: `agent/design-runs/<run-id>/feedback/<id>.json` schemaVersion 1 with immutable comparisonHash, baseCandidateId, themeCandidateId, and choices[{operation,source,target,facets,preserve,priority}]. Operations are replace/insert/remove/preserve/avoid. Structured choices outrank inferred notes. Display labels A–E resolve to immutable candidate IDs.
- Synthesis plan: `onboarding/synthesis-plan.json` on the final root. Identifies base/theme sources, tokens, route/section order, selected facets, and acceptance criteria before implementation.
- Storyboard: `onboarding/storyboard.json` version 3 plus `designContractVersion` 1. businessHash; directionHash; routes[{source,disposition,target?,reason}]; pages[{path,purpose,role,visitorOutcome,representative,sections:[{id,question,message,role,purpose,opportunityIds,decisionIds,facts,media,action,composition,emphasis,emphasisReason,layoutFamily,density,mediaProminence,interaction,repetitionRationale,mobileTransformation,transition}]}]; globalMedia[{assetId,placement:"header"|"footer",routes:["/",...],reason}]. Every use asset needs section or shared placement. For each chosen slot, `media-selection.json` names route, placement, and sectionId for a section. Shared placements are limited to inspected customer brand assets and are checked in the visible rendered region on every declared route. Legacy version 2 section plans remain readable under their original rules; migrate a recovery copy explicitly before claiming the new contract. Layout names are a vocabulary with a custom description, not a required sequence. Equal emphasis across unrelated jobs needs a repetition rationale. Homepage and one interior are representative.
- Copy: `onboarding/copy-plan.json`. storyboardHash over [`onboarding/storyboard.json`]; pages[{path,sections:[{id,heading,body,factIds:[]}]}]. Match every storyboard section. Actual copy, not instructions to write it later.
- Review: `agent/reviews/<node>.json`. subjectHash from `npm run eval -- --node <node> --subject`; reviewer; verdict pass/fail/unknown; evidence:[existing paths]; checks[{criterion,verdict,evidence}]; findings[{id,page,section,evidence,severity,problem,repair,target,verify}]. Severity blocker/major/minor. List only unresolved findings. Unknown never passes. A major finding always fails even if the overall verdict says pass.
- Parent diversity review: `agent/design-runs/<run-id>/direction-review.json`. schemaVersion 1; kind `direction-diversity`; runId; reviewer; verdict pass/fail/unknown; subjectHash over content identities, not filenames; identities{directions,studies,assets,renders,viewports,refs} from `fingerprint` of those artifacts plus viewport/ref versions; evidence:[existing paths, including captured PNGs]; axes[{name,evidence}] covering typography, color, density, media-role, composition; candidates[{id,findings[]}] for every brief; findings[]; optional repair{directionId,reason,targets[]}. Paths are metadata. Reuse only when every identity matches current bytes. Unknown and fail never expand candidates. A critic may write only this review file; mutating briefs, studies, or captures invalidates the review. Render/tool failures are `tool_failure` states, not aesthetic passes. Exhausted planning repairs keep the rejected result and fail explicitly.

Review criteria by node:
- business-review: offer, audience, engagement, evidence, contradictions
- narrative-review: customer, offer, proof, conversion, unsupported-claims
- direction-review: business-fit, distinct-options, composition, media-fit, theme-coherence
- copy-review: accuracy, clarity, specificity, continuity, cta-behavior
- representative-review: business-fit, hierarchy, rhythm, media-treatment, mobile, interior
- representative-quality: business-specificity, composition, media-integration, typography, copy-design-integration, brand-coherence, responsive-craft
- visual-review: business-fit, hierarchy, rhythm, media-treatment, mobile, interior, theme-coherence, organizing-idea
- content-review: accuracy, clarity, specificity, continuity, cta-behavior, coverage, audit-leakage

Keep unsupported claims out of public copy. You may infer positioning but label it as interpretation. Missing critical facts are a blocking question; stylistic choices are yours to resolve.

Media also records unavailable:[{source,reason,resolution,evidence}] for source images absent locally. Resolution is unresolved/unavailable/excluded. Every source URL needs an asset mapping or a resolved, evidenced disposition; a silent failed download never passes. Asset placements are destination route paths.
