Data Journalist Agent (Data2Story)
Turn $ARGUMENTS into a blog. Orchestrates the roles below in sequence.
Orchestration rituals
Five process rules earned from real runs. Each is one rule + why it exists; follow them across every run regardless of topic.
- (D1) Candidate-image review loop. For any hero/cover image of a named person or specific real object, generate N candidates, then run a VLM (or human) review and select one — never ship the first render. Why: text2image of named people intermittently refuses outright and quality varies shot-to-shot, so a single render is a coin-flip; a small candidate pool plus a review step is the only reliable way to land a usable, correctly-identified image.
- (D2) Audit-before-finalize. Run the read-only multi-agent audit (provenance / repro / assets / IP lenses) to find every defect FIRST, collect them, and only THEN make one editing pass to fix them. Why: interleaving find-and-fix makes agents edit the same file against a moving target and re-introduce each other's defects; separating the finding phase from the fixing phase prevents that churn.
- (D3) Single owner of one big file. Parallel agents may produce data and assets concurrently, but exactly ONE agent writes
index.html. Why: the HTML is one indivisible artifact; concurrent edits to it interleave and corrupt it, so it must have a single writer even when everything feeding it is parallel. - (D4) Headless-verify-then-defer-render. Prove everything you can without a browser —
node --checkthe scripts, recompute a known model output and compare, byte-compare each inline JSON island to itsverify/file — then hand the final visual/browser sign-off to the user. Why: deterministic checks catch the failures that don't need eyes cheaply and early, while genuine render correctness needs a real browser this harness can't drive, so the human does that last mile (see also the chart-width-0 class of bug, invisible to node shims). - (D5) Verify-coexistence-with-zero-engine-edits. New interactive features layer on top of the frozen Verify engine (the in-page Inspector panel +
verify/artifacts); never edit the engine to make a feature fit. Why: the Verify layer is the paper's coding verifier and a hard gate — editing it to accommodate a feature risks silently breaking provenance, so features adapt to it, not the reverse.
The 7 teams
The newsroom is 7 teams (the paper's 7 canonical roles), staffed by 14 agents. Two teams are a single agent; five are a small team with a lead who coordinates one or more members. The folders stay flat and every Skill <name> call resolves by the member's frontmatter name: — the teams are a coordination overlay, not a folder move. You (this orchestrator) are the top coordinator across all 7 teams.
| Team (paper role) | Lead | Members | Kind |
|---|---|---|---|
| Detective | Detective | + Scout | multi-agent |
| Analyst | Analyst | + Imagineer | multi-agent |
| Editor | Editor | + Copywriter | multi-agent |
| Designer | Designer | + Interaction + Hero + Cinematographer | multi-agent |
| Programmer | Programmer | — | single-lead |
| Auditor | Auditor | + Critic (+ Playtester step) | multi-agent |
| Inspector | Inspector | — | single-lead |
Each lead's SKILL.md carries a "Team coordination" section describing how it briefs and integrates its members; the member Skill <name> calls below are mirrored in those sections so a member is never silently skipped. The cross-team checkpoints (the two gates and the Critic revision loop) are owned by this top orchestrator, not delegated into any team.
Setup
Resolve paths before doing anything:
- Never hard-code machine-local paths and never ask the user to export path variables.
- Resolve
SKILL_DIR= the directory containing thisSKILL.md(.../skills/data2story-pro) - Resolve
ARCHIVE_DIR= the ancestor directory that containsskills/(two levels up fromSKILL_DIR, i.e.SKILL_DIR/../..) - Resolve
DATA2STORY_ROOT= parent ofARCHIVE_DIR - Commands below use symbolic placeholders such as
ARCHIVE_DIR; replace them with resolved, quoted paths before running Bash. DATA_NAME= the dataset folder name (e.g.pick_a_card). In URL / IDEA mode it is taken from theDATA_DIRthat Stage 0 — Input dispatch (below) resolves, so run Stage 0 before resolvingPROJECT_DIR.DATA_DIR= the validated dataset folder, resolved by Stage 0 — Input dispatch below. It originates from one of: the existing path in$ARGUMENTS(DATA MODE, path);find-data's output folder (DATA MODE, URL); orideation's output folder (IDEA MODE). For a bare dataset name with no Stage-0 acquisition, fall back toDATA2STORY_ROOT/data/{DATA_NAME}.TIMESTAMP= current time formatted asMMDD_HHMM(e.g.0401_1618):date +%m%d_%H%M(run in bash)PROJECT_DIR=DATA2STORY_ROOT/project/{DATA_NAME}/blog_{MODEL}_{TIMESTAMP}- Create
PROJECT_DIR/,PROJECT_DIR/assets/,PROJECT_DIR/code/— after Stage 0 has resolvedDATA_DIR/DATA_NAME(immediate in path mode; after the Stage-0 acquisition in URL / IDEA mode). - Write
PROJECT_DIR/run_config.json={"run_profile": "premium"}or{"run_profile": "fast"}from the Stage-0 Run profile decision (below).validate.py/generate_viewer.pyread it; absent ⇒premium.
Windows: there is usually no
python3on PATH — run thepython3 …commands in this skill aspy(orpy -3). SetPYTHONUTF8=1(e.g.$env:PYTHONUTF8=1) to avoid GBK console errors when scripts print Unicode.
Sibling-skill dependency. This skill requires the sibling skills
frontend-design-pro/anddataviz-craft/to live under the sameskills/parent (ARCHIVE_DIR/skills/). The Designer/Programmer/Auditor/Critic borrow their component recipes, design tokens, quality rubric, and dataviz craft from them via../frontend-design-pro/...and../dataviz-craft/...relative paths. Deploy all three together —data2story-pro/alone is incomplete; a clone missing either sibling will break the design/audit references. IDEA MODE additionally requires the sibling skillsfind-data/andsparring-partner/under the sameskills/parent: Stage 0'sideationsub-skill drivessparring-partnerto converge a topic andfind-datato acquire a real dataset. A clone missing either of these two can still run DATA MODE (a path or URL), but cannot run IDEA MODE (an empty/free-text idea).
Stage 0 — Input dispatch (data path · URL · idea)
Classify $ARGUMENTS before resolving DATA_DIR. This stage decides how DATA_DIR is obtained; every later stage (Detective → … → Inspector) is identical regardless of which branch ran. FIRST match wins:
- Empty / whitespace → IDEA MODE (open by inviting the idea: "what do you want to tell a story about?").
- An existing path on disk (
$ARGUMENTSresolves to a file or directory) → DATA MODE. The current behavior, unchanged: setDATA_DIRto that path and proceed into the pipeline. - A data URL (
$ARGUMENTSstarts withhttp://orhttps://) → DATA MODE via find-data url-mode. RunSkill find-data "<url>"— it fetches the resource and validates it against the 4 completeness gates, producing a dataset folder. SetDATA_DIRto that folder and continue the pipeline. - Otherwise (free-text prose, not a path) → IDEA MODE.
Ambiguity guard. If $ARGUMENTS LOOKS like a path (contains a slash, a drive letter, or a file extension) but does NOT exist on disk, do one quick confirmation question instead of silently treating it as an idea — e.g. "that looks like a path but I can't find it; did you mean a dataset folder, or a topic to research?" — and branch on the answer.
IDEA MODE
Hand the user's idea to the ideation sub-skill, passing the raw $ARGUMENTS text verbatim and the resolved DATA2STORY_ROOT:
Skill ideation "<raw $ARGUMENTS>" <DATA2STORY_ROOT>
ideation runs the brainstorming dialogue (reusing sparring-partner) to converge the vague idea into one concrete data-story topic, then runs find-data to acquire a real, validated dataset — with a user checkpoint after each step — and returns exactly one of:
DATA_DIR=<absolute path to the validated dataset folder>— success. Astory_brief.jsonwill sit at<DATA_DIR>/meta/story_brief.json. SetDATA_DIRandDATA_NAMEfrom this folder and proceed into the EXISTING pipeline unchanged (Detective → … → Inspector). Nothing downstream differs.IDEATION_ABORTED: <reason>— the user aborted, or no adequate real dataset could be found. Halt gracefully with an honest one-line message that relays the reason. Do NOT run the pipeline, and do NOT fabricate data or a dataset to "succeed".
Run profile — Fast vs Premium (select ONCE, then commit)
Data2Story ships two committed profiles. Resolve the profile once, here at Stage 0, and write it to PROJECT_DIR/run_config.json; do not revisit it mid-run. A profile is a self-consistent bundle (which stages run AND the matching gate set), NOT a pile of per-stage toggles.
premium(default) — the full 14-agent newsroom: verified media (Scout), interactive playgrounds (Imagineer + Interaction), an animated cover (Hero), a cinematic scroll (Cinematographer), titling (Copywriter), the Critic revision loop, and the full runnable verify layer (in-page panel + runnable Pyodide cells + a reproducible notebook). ~2h40–2h50. The flagship.fast(~15–25 min) — the 7 canonical roles only: Detective → Analyst → Editor → Designer (charts + static images, no media generation) → Programmer → Auditor → Inspector. Keeps the in-page traceability panel (click any claim → its code/source) but NOT the runnable cells / reproducible notebook. Skips Scout, Imagineer, Copywriter, Interaction, Hero, Cinematographer, and the Critic loop. The premium floors (mandatory cinematic / BGM / richness / engagement / runnable-verify / playtest) do not apply.
Resolve it:
- If
$ARGUMENTScontains--fast→fast;--premium→premium. Strip the flag from$ARGUMENTSbefore input dispatch. A headless/automated call with no flag defaults topremium. - Otherwise (interactive, no flag) → ask one
AskUserQuestion: "Fast (15–25 min — charts + static images, traceability only) or Premium (2h40–2h50 — the full cinematic/interactive flagship)?" - Write the choice to
PROJECT_DIR/run_config.jsonas{"run_profile": "premium"}or{"run_profile": "fast"}right afterPROJECT_DIRis created (Setup).validate.pyandgenerate_viewer.pyread this file; absent ⇒premium, so the default and every automated call is the full flagship.
Each stage heading below is marked [premium] when the fast profile skips it; unmarked stages run in both.
Archival
Immediately after creating PROJECT_DIR, snapshot the current skills:
mkdir -p PROJECT_DIR/archival
cp -r ARCHIVE_DIR/skills PROJECT_DIR/archival/skills
This preserves the exact skill versions used for this run.
Tools available
All media tools route through OpenRouter. Set OPENROUTER_API_KEY before any generation call.
Media generation is the Designer's job, so the media tools (text2image, text2video, image2video, text2music, embeddings) live under SKILL_DIR/designer/scripts/openrouter-*/. The full list — default models and exact python3 ... invocations — is in designer/references/tools.json; full per-tool docs are each tool's own SKILL.md under SKILL_DIR/designer/scripts/openrouter-*/. Note: text2music is for opt-in atmospheric sound-design / SFX (un-findable sounds — drones, textures, best-effort foley), NOT the front-of-blog BGM — the BGM must be a sourced real track (sourced_bgm, found by the Scout), never AI-composed.
Pipeline Overview
The pipeline is a single linear sequence that produces a traceable HTML blog from raw data:
DATA → Detective → Scout → Analyst → Imagineer → Editor → Copywriter → Designer → Interaction → Hero → Cinematographer → media-purpose + richness + cinematic-supply gates → Programmer → Auditor + Playtester → contract gate → Inspector verify.py (Stage 6.4 → verifier.json) → Critic (bounded loop, <=2 rounds; signature-move pass bar R9 = every dim >=4 AND >=1 dim >=5; re-runs Programmer + Auditor + validate.py + verify.py each round) → Inspector generate_viewer.py (Stage 7, REQUIRED terminal step — MUST exit 0) → final index.html (with the in-page Inspector panel) + verify/ artifacts
Two profiles (set once at Stage 0). The sequence above is the
premiumflagship. Thefastprofile (~15–25 min) runs only the 7 canonical roles — Detective → Analyst → Editor → Designer (charts + static images) → Programmer → Auditor → Inspector — keeping the in-page traceability panel but skipping every stage whose heading is marked [premium] (Scout, Imagineer, Copywriter, Interaction, Hero, Cinematographer, the Critic loop) plus the runnable-verify cells + notebook. Its gate set drops the premium floors (cinematic / BGM / richness / engagement / playtest / runnable-verify) to match. Absentrun_config.json⇒premium.
The Imagineer (Stage 2.5) fans out candidate interactive concepts; the Editor curates them into the hero + supporting set; the Copywriter (Stage 3.5) re-titles the masthead + every section title + every figure/photo/table caption to a titling standard that kills the AI-tell patterns (strings only — names, never edits); the Interaction Engineer (Stage 4.5) builds that whole SET (not just one centerpiece); the Hero (Stage 4.6) crafts the animated cover; the Cinematographer (Stage 4.7) consumes that cover as its first scene (
cin_00); the Playtester (a step inside the Auditor team at Stage 6) drives every built playground in a real browser before the contract gate.
A run is INCOMPLETE — do NOT present it as finished — if the contract gate (
validate.py) Section 7 reports anyverify_*error, OR Section 15 reports anysend_back_open/playtest_hard_unresolved, ORgenerate_viewer.py(Stage 7) exits nonzero. The verify layer is the paper's coding verifier; it is a hard pipeline gate, not an optional flourish, and "the panel andverify/artifacts are mentioned as MANDATORY" is enforced by those deterministic checks, not by prose alone. A left-opendetected defect (a Section-15send_back_open/playtest_hard_unresolved) blocks flagship in the same class as averify_*error.
Run each stage in order. Each stage reads the previous artifact(s) before starting. Do not proceed to the next stage until the current artifact is complete.
Model tier (optional). The mechanical stages — the Programmer's HTML build and the Inspector's scripts (
verify.py,generate_viewer.py) — are deterministic and run fine on a cheaper/faster model; the creative and analytical stages (Detective, Analyst, Editor, Designer, Interaction, Critic) benefit most from a strong model. Spend the budget where judgment matters.
The stages below are grouped under their team heading. The stage numbers, Input/Output, What, and Call lines are unchanged — run them in the same linear order regardless of grouping.
Detective team — context + verified media (lead: Detective; member: Scout)
Stage 1 — Detective
Input: DATA_DIR
Output: PROJECT_DIR/detective.json
What: Researches external context — background knowledge, domain history, related findings, why this data matters. Each finding gets a det_xx ID.
Stage 1.5 — Scout [premium]
Input: DATA_DIR, PROJECT_DIR/detective.json
Output: PROJECT_DIR/scout.json, PROJECT_DIR/assets/scout_*
What: Sources and verifies rich external media the Detective's background pass didn't cover — license-clean music for the front-of-blog BGM, high-value real photos/video of the story's key subjects, and the latest live status (timestamped, display-only). Every asset carries a checked license (permits republication) and identity (it is what the caption claims) block; each item gets an sct_xx ID. The pipeline's media verifier — what it passes downstream is legally usable and correctly identified. Skips lightly for abstract/statistical/privacy-sensitive datasets (records why).
Call: Skill scout DATA_DIR PROJECT_DIR
Analyst team — quantitative analysis + interactive ideation (lead: Analyst; member: Imagineer)
Stage 2 — Analyst
Input: DATA_DIR, PROJECT_DIR/detective.json
Output: PROJECT_DIR/code/*.py, PROJECT_DIR/analyst.json
What: Exhaustive quantitative analysis of the data, informed by detective's context. All code saved to code/ as runnable scripts. Each finding gets an ana_xx ID with calculation (file + lines + output) and data_table (chart-ready data).
Stage 2.5 — Imagineer [premium]
Input: PROJECT_DIR/analyst.json (+ client_models), PROJECT_DIR/detective.json (topic_profile), PROJECT_DIR/editor.md spine (if already present)
Output: PROJECT_DIR/imagineer.json
What: Fans out many candidate interactive concepts grounded in the data + narrative — one img_xx per concept, each binding a finding (ana_xx), an archetype, a reader-action, a purpose (INFORM/IMMERSE), and a feasibility/sketch — for the Editor to curate into the hero + supporting set. A deliberate fan-out, not a final selection. img_ ids are internal and never reach the HTML. Gates its breadth off topic_profile.is_computational/is_visual (fans out little for abstract/statistical data).
Call: Skill imagineer PROJECT_DIR
Post-Analyst re-confirm
is_computational(cheap, additive — after the Analyst team, before the Editor). Theis_computationalflag was first set by the Detective at Stage 1 from the headline's nature, before any numbers existed; the Analyst has now actually computed them, so re-check it cheaply: ifanalyst.jsonemitted anyclient_model, OR any finding whose nature is a rate / ranking / aggregate / probability / model output (i.e. a number the reader could in principle reproduce), upgradetopic_profile.is_computationaltotruein the resolved profile (thedetective.json/scout.jsontopic_profile) so downstream reads see it. This is a one-way upgrade (never downgrade) that recovers the interaction + runnable-verify flagship levers when an early Detective pass under-classified a chart-led computational topic. Do not reorder stages; this is a single boolean re-confirm between the Analyst team and the Editor.
Editor team — narrative + titling (lead: Editor; member: Copywriter)
Stage 3 — Editor
Input: PROJECT_DIR/detective.json, PROJECT_DIR/analyst.json, PROJECT_DIR/imagineer.json
Output: PROJECT_DIR/editor.md, PROJECT_DIR/editor.json
What: Editorial decisions — which findings matter, what the narrative arc is, what the blog argues. Each section gets an edt_xx ID with explicit references to ana_xx findings and det_xx context. Also curates the Imagineer's img_ concepts into editor.json.interactives (the hero + ranked supporting[]), binding each to a section + a distinct finding + a purpose (INFORM/IMMERSE) so the Interaction Engineer builds only what earns its place. No visual design.
Stage 3.5 — Copywriter [premium]
Input: PROJECT_DIR/editor.md, PROJECT_DIR/editor.json, PROJECT_DIR/analyst.json, PROJECT_DIR/detective.json (topic_profile), PROJECT_DIR/designer.json (if already present)
Output: PROJECT_DIR/copywriter.json
What: Names the piece — strings only. Re-writes the masthead (headline + standfirst + kicker), every section title, and every figure/photo/table caption to a research-driven titling standard that kills the AI-tell patterns a competent default falls into — above all the AT1 two-beat ("Flat statement. Flat counter-statement.", e.g. "Argentina is the favourite. No bookmaker agrees."). Writes copywriter.json (masthead + items{edt_xx:{title}, des_xx:{caption}}), each string backs-ed to a real ana_*. Naming, not editing: it touches no finding, no number, no data-* id, no layout, no body prose, and reuses the existing edt_/des_ ids (adds none) — so the Verify layer + provenance graph are untouched; the Programmer renders the masthead + figcaptions from copywriter.json verbatim. Runs after the Editor, before the Designer (Editor team).
Call: Skill copywriter PROJECT_DIR
Designer team — visual brief + interaction + cinematic (lead: Designer; members: Interaction, Cinematographer)
Stage 4 — Designer
Input: PROJECT_DIR/editor.md, PROJECT_DIR/editor.json, PROJECT_DIR/analyst.json
Output: PROJECT_DIR/designer.json, PROJECT_DIR/assets/*
What: Data-driven creative visual decisions — how to present each point using charts, images, video, audio, maps, interactives, stat callouts, instances, or text-only treatment when appropriate. The media mix should emerge from the dataset's properties, not from a fixed checklist. The page should be multimedia-rich by default: borrow the visual language from the shared frontend-design-pro skill and use all five channels (chart, image, video, audio, interactive/map) unless a channel's documented fallback would be fabricated or purely decorative. Each visual gets a des_xx ID with data_source pointing to ana_xx data_tables when data-driven. Generates selected assets. No HTML.
Stage 4.5 — Interaction Engineer [premium]
Input: PROJECT_DIR/editor.md, PROJECT_DIR/editor.json (+ interactives), PROJECT_DIR/analyst.json (+ client_model), PROJECT_DIR/imagineer.json, PROJECT_DIR/designer.json
Output: PROJECT_DIR/interaction.json
What: Builds the curated SET the Editor approved (editor.json.interactives) — the ONE narrative-bound hero centerpiece PLUS the ranked supporting[] playgrounds, each an explorable that makes the reader PRODUCE a distinct finding to the three-layer-number standard — plus scrollytelling/transition craft, consuming the Analyst's client_model. Builds only what the Editor curated (nothing more); every element earns its place by binding a distinct finding the reader produces/feels. May reach back into the Editor's spine to confirm the hero. See interaction/SKILL.md + the shared ../frontend-design-pro/references/interaction_playbook.json.
Call: Skill interaction PROJECT_DIR
Stage 4.6 — Hero [premium]
Input: PROJECT_DIR/editor.md, PROJECT_DIR/editor.json (+ interactives.hero), PROJECT_DIR/scout.json, PROJECT_DIR/designer.json, PROJECT_DIR/interaction.json
Output: PROJECT_DIR/hero.json, PROJECT_DIR/assets/teaser.* (.webm + _web.mp4 + .jpg poster)
What: Crafts the cover — the single most important detail. The animated hero is the default: an auto-selected source ladder (first rung that resolves) keeps the cover moving — Scout real still → image2video cinemagraph; recognizable real public figure → image2video --model kwaivgi/kling-v3.0-std (Veo/Wan refuse real faces) → ffmpeg Ken-Burns; license-clean stock clip; abstract/atmospheric text2image still then animate; and only as the LAST rung, a recorded static poster (kind:"image" with a media_blocker). Emits hero.json (id:"des_hero_video", kind, assets{video_webm,video_mp4,poster}, reduced_motion_fallback, verify.class_marker:"teaser"). Reuses the des_ prefix (no 6th provenance prefix). Runs optimize_assets.py (VP9 webm + H.264 mp4 + poster, < 3 MB web budget).
Call: Skill hero PROJECT_DIR
Stage 4.7 — Cinematographer (mandatory) [premium]
Input: PROJECT_DIR/editor.md, PROJECT_DIR/editor.json, PROJECT_DIR/designer.json, PROJECT_DIR/interaction.json, PROJECT_DIR/scout.json
Output: PROJECT_DIR/cinematographer.json
What: Mandatory — every blog ships a cinematic scroll background; there is no off/opt-out. Designs a full-bleed, scroll-driven background + global motion choreography, staging the Designer's visuals and the Interaction centerpiece as scenes. The MODE adapts to the topic, it never turns off: photographic (primary — a scroll built from the Scout's verified real imagery, each scene a cin_xx whose background media_ref points at a verified sct_/des_ asset so it stays licensed + identified), generative (AI-sourced atmosphere when real cover-able imagery is thin), or data_driven (a chart-spine background — kind:color|gradient/chart scenes carrying no media_ref — for abstract/statistical data, so a dry topic still gets a tasteful immersive layer instead of an inert column). Mandatory means always-present-AND-tasteful, never force-junk: a tonally-wrong or decorative immersion still caps visual_design at 3 (it does not satisfy the stage). See cinematographer/SKILL.md.
Call: Skill cinematographer PROJECT_DIR
Programmer team — HTML build (single-lead: Programmer)
Stage 5 — Programmer
Input: PROJECT_DIR/editor.md, PROJECT_DIR/editor.json, PROJECT_DIR/analyst.json, PROJECT_DIR/designer.json, PROJECT_DIR/interaction.json, PROJECT_DIR/cinematographer.json
Output: PROJECT_DIR/index.html
What: Implements the final blog in HTML. Applies the theme/accent recorded in designer.json page_rhythm and borrows component + token recipes from the frontend-design-pro skill. Resolves chart data from analyst.json data_tables (NO raw data access). Tags every element with data-edt, data-ana, data-det, data-des (and data-sct on scouted media, data-cin on cinematic backgrounds) attributes for traceability. The cinematic background is MANDATORY, so cinematographer.json meta.mode is always one of photographic / generative / data_driven (cinematic is the backward-compat alias for photographic); the Programmer builds the scroll-driven background layer per its scenes for every blog (see cinematographer/references/cinematic_recipes.json). The plain editorial column is built only in the impossible no-scenes pipeline-error case.
Auditor team — build/render correctness + content quality (lead: Auditor; members: Critic + Playtester step)
The two cross-team gates (the media-purpose gate and the contract gate
validate.py) and the Inspector team'sverify.py(Stage 6.4) fall between the Auditor (Stage 6) and the Critic (Stage 6.5); they are run by this top orchestrator at the canonical points below, not delegated into the Auditor team.
Stage 6 — Auditor
Input: PROJECT_DIR/index.html
Output: PROJECT_DIR/index.html (modified), PROJECT_DIR/auditor.json, PROJECT_DIR/audit/ (render_report.json + screenshots)
What: Actually renders the page in a real headless browser (auditor/scripts/render_capture.js drives the installed Chrome/Edge via puppeteer-core — no Chromium download; falls back to static analysis if a browser isn't available) to catch render-time defects that source inspection is blind to — blank/0-width charts, broken/404 media, horizontal overflow, dead interactions — measuring every element's true pixel box and capturing console errors. It also runs a mobile-viewport pass (~390×844) that re-checks horizontal overflow + screenshots the phone layout (virality is mobile, so mobile overflow is a real defect), and an asset-weight / performance check over assets/ against shared budgets (audio > 3 MB, single image > 600 KB, video > 3 MB, total payload > ~8 MB). Then 2–3 cross-validating vision agents review the screenshots against designer.json/editor.md intent (charts / layout / media+interaction lenses; ≥2-agree = high confidence). Fixes layout (incl. mobile overflow) in place; views each image to catch a wrong/garbled subject or an AI render faking a specific real object (e.g. a fake trophy); records correctness + oversized-asset problems in auditor.json.send_backs for the owning role; re-renders to confirm fixes. Runs automatically after Programmer.
Call: Skill auditor PROJECT_DIR
After the Auditor, if auditor.json.send_backs is non-empty, route each issue to its send_back_to role (Designer re-fetches a real image, regenerates, or re-encodes/compresses an oversized generated asset; Scout swaps an oversized sourced asset for a lighter still-licensed one; Programmer fixes wiring/markup or a mobile-overflow constraint), then re-run the Programmer (if the HTML changed) and the Auditor — bounded to ≤2 rounds — before the contract gate. Set each routed send-back's resolution (fixed after a clearing re-run, or blocker_recorded+blocker_reason); the contract gate asserts none is left open.
Playtester step (inside the Auditor team, Stage 6 — after the render/probe, before the contract gate). The Auditor team then drives every built playground in a real browser (the auditor/scripts/playtest_drive.js script, built and documented by the Auditor team) — for each int_ id in interaction.json (centerpiece + supporting[]) it sets the declared controls, reads the live readout, and checks the playground fires, its in-browser recompute agrees with the published ana_* figure, it degrades without JS, and it is keyboard-reachable — writing PROJECT_DIR/audit/playtest_report.json. Correctness defects join auditor.json.send_backs and route through the Auditor's ≤2-round loop (purpose/abundance defects route to the Critic loop). Every hard playtest send-back must end fixed (a re-run shows the check PASS) or blocker_recorded+reason; an open/unmatched/omitted hard playtest fail hard-fails the contract gate (Section 15) — a false-positive call is RECORDED as blocker_recorded, never silently dropped or downgraded. (Only referenced here; the step's script + checks are owned by the Auditor team.)
Inspector team — traceability verification + viewer (single-lead: Inspector)
The Inspector team's two scripts run inline at the canonical points, invoked by this top orchestrator:
verify.pyhere at Stage 6.4 (after the contract gate, before the Critic) andgenerate_viewer.pyat Stage 7 (after the Critic loop settles).
Stage 6.4 — Inspector: verify.py (sentence → evidence map)
Input: PROJECT_DIR/index.html, all JSON files
Output: PROJECT_DIR/verifier.json
What: Runs the Inspector's first step — verify.py — to produce the sentence→evidence map. Run it after the contract gate passes (see Handoff rules) and before the Critic, so verifier.json exists when the Critic reads it (the Critic scores data_method_transparency against this traceability index). This is only the sentence→evidence pass; the in-page panel injection (generate_viewer.py) stays at Stage 7.
python3 SKILL_DIR/inspector/scripts/verify.py PROJECT_DIR
Back to the Auditor team: with
verifier.jsonin hand, its content-quality member runs next.
Stage 6.5 — Critic (quality review + bounded revision loop) [premium]
Input: PROJECT_DIR/index.html + all JSON
Output: PROJECT_DIR/critic.json
What: Scores the finished article against the 5 quality-rubric dimensions and sends targeted, surgical fixes back to the responsible roles. This is the pipeline's only content-quality gate (the Auditor handles build/render correctness, the Inspector only traceability).
Call: Skill critic PROJECT_DIR
Then run the bounded revision loop (max 2 rounds):
- Read
critic.json. Ifoverall.passis true, proceed to the contract gate + Stage 7.overall.pass == trueis the signature-move bar (R9): it means every rubric dimension scored >=4 AND at least one dimension reached >=5 (a signature move). A run that is merely "all >=4 but nothing reached 5" is NOT a pass —overall.passwill be false andoverall.tierwill be"competent", not"flagship". - Otherwise, for each failing dimension apply its
suggested_fixvia thesend_back_torole as a surgical edit of that role's artifact only — Editor →editor.md/editor.json; Designer →designer.json(+regenerate only the affected asset); Analyst → the specific finding; Detective → the missing context. Do NOT regenerate the whole pipeline. - Re-run the Programmer to re-render only the affected sections into
index.html, then re-run the Auditor (Skill auditor PROJECT_DIR), the contract gate (validate.py), andverify.py(soverifier.jsonreflects the revised page). The contract gate now also asserts the verify layer (validate.pySection 7): if it reports anyverify_*error, route it to the Programmer (Step 2.5) per the contract-gate routing table below and re-run before continuing. Set each routed send-back'sresolution(fixedafter a clearing re-run, orblocker_recorded+blocker_reason); the contract gate asserts none is leftopen(Section 15). - Re-run
Skill critic PROJECT_DIR(incrementoverall.round). - Stop when
overall.passis true OR after 2 rounds (the loop never exceeds 2 rounds and never downgrades-then-passes / rounds-up-to-pass); then proceed to build regardless, leaving the finalcritic.json(with any residual issues) in place. A run isflagshiponly whenoverall.pass == true(i.e.overall.tier == "flagship"). If the loop exits after 2 rounds withoverall.pass == false, readcritic.json overall.tierand report one of two honest terminal sub-states — in neither case is the build hard-blocked (Stage 7 still runs and the deliverables are still produced), but in neither case may the run be reported as a silent "done" or called flagship:overall.tier == "sub_competent"(some dimension scored < 4): statusINCOMPLETE — quality gate not cleared(parallel to a contract-gate / verify-layer failure). Surface it explicitly in the final deliverables (see Handoff rules → Final deliverables) and in the run's closing summary, listing each failing dimension and itssend_back_torole +suggested_fix.overall.tier == "competent"(every dimension scored >= 4 but none reached >= 5 — the signature-move bar R9 was not met within the bounded rounds): statusCOMPETENT — NOT flagship-verified (no signature move reached 5 in N rounds)(N =overall.round). This is a sound competent blog, not a failure: the deliverables still ship and Stage 7 still runs with NO hard block. But it must NOT be called flagship, and it must be surfaced in the closing summary, naming the highest-leverage lift the Critic recorded (the dimension +suggested_fixmost likely to reach a signature 5). A passingcritic.json(overall.pass == true,tier == "flagship") is required before a run may be called flagship-quality.
The Critic runs BEFORE Stage 7 fills the panel islands. The Critic's PIT-29 verify-layer check (
frontend-design-pro/references/pitfalls.json) therefore confirms only that theverify/FILES exist and the panel SHELL is present (the#verifyToggletoggle + the two island<script>tags, which are still EMPTY at this point) — it must NOT require the islands to be filled. The deterministic island-filling is checked at Stage 7 bygenerate_viewer.pyitself.
Stage 7 — Inspector: generate_viewer.py (panel injection + redirect)
Input: PROJECT_DIR/index.html, the Programmer-authored PROJECT_DIR/verify/ payloads (verify_map.json, run_cells.json, the reproducible notebook), PROJECT_DIR/verifier.json (from Stage 6.4)
Output: PROJECT_DIR/index.html (with the in-page Inspector panel filled), PROJECT_DIR/verify/cell_registry.json (derived)
What: Stands up the in-page Inspector panel — a click-any-claim drawer embedded in index.html whose evidence is byte-identical to the verify/ artifacts, backed by a reproducible notebook that re-executes the headline numbers from raw data (the notebook is the paper's from-raw-data coding verifier; the panel makes its reproductions one-click in the page). This whole layer is MANDATORY on every blog. verifier.json was already produced at Stage 6.4; this stage first runs inspector/scripts/relocate_unreferenced.py (relocating any unreferenced heavy master from assets/ to a non-shipping provenance/, writing the provenance/_relocated.json sentinel), then runs the panel + redirect emitter:
python3 SKILL_DIR/inspector/scripts/generate_viewer.py PROJECT_DIR
It fills the panel's two inline JSON islands (verifyMap/runCells, byte-identical to verify/verify_map.json + verify/run_cells.json — both authored by the Programmer, not by this script), derives verify/cell_registry.json, wires the reproducible notebook (NB_PATH — the Download-notebook target the reader runs locally; no Colab). The Programmer copies the panel UI verbatim (see the Programmer's inspector_panel family) and authors the verify/ payloads; the runnable computation snippets re-execute in-browser via lazy Pyodide. See inspector/SKILL.md for details.
Stage 7 is a REQUIRED terminal step — it MUST exit 0. It is not optional and the run is not finished until it has run successfully:
- If
generate_viewer.pyexits nonzero, read its stderr: a complaint about the authoredverify/payloads or the panel shell (missing/empty islands, adata-*id with noverify_mapentry, a schema-invalidverify_map.json/run_cells.json) routes to the Programmer (Step 2.5) to fix the authored layer; a script-internal failure routes to the Inspector. Then re-rungenerate_viewer.py. - Loop fix → re-run until it exits 0. Do NOT present the run as finished while Stage 7 has a nonzero exit (or while the contract gate's
verify_*Section-7 errors are unresolved).
Stage 7 is the Inspector team's second script, invoked by the orchestrator once the Critic's bounded revision loop has settled. It closes the linear sequence — and the run is incomplete until it exits 0.
Traceability: ID flow through the pipeline
det_01 ──┐
det_02 ──┤
sct_01 ──┤ (scouted media: verified photo / video / music / live-status, each with license + identity)
├──▶ ana_01 (based_on: [det_02]) ──┐
│ ana_02 (based_on: []) ├──▶ edt_01 (findings: [ana_01, ana_02], context: [det_01, sct_01]) ──▶ des_01 (section: edt_01, data_source: ana_01, media: sct_01)
│ ana_03 (based_on: [det_01]) │ edt_02 (findings: [ana_03], context: [det_02]) ──▶ des_02 (section: edt_02, data_source: ana_03)
└────────────────────────────────────┘
All role outputs key their items as a dict {id: {...}} (not arrays); detective sources are {url, title, facts} dicts — validate.py/verify.py enforce this.
Every value in the final HTML can be traced: HTML data-des="des_01" → designer.json des_01.data_source="ana_01" → analyst.json ana_01.calculation.code → verifiable. Likewise every external media element: HTML data-sct="sct_01" → scout.json sct_01.license + sct_01.identity → a checked license + verified subject. And every cinematic background scene: HTML data-cin="cin_01" → cinematographer.json cin_01.background.media_ref → the verified sct_/des_ asset behind it. The Hero cover is a des_ item (hero.json id="des_hero_video", reusing data-des — no 6th prefix): it surfaces as a SINGLE provenance hit on the .teaser overlay (data-des="des_hero_video"), and the Cinematographer consumes it as cin_00 (its continuous backdrop), so the cover routes through the existing des_/cin_ graph with no new tuple. Each interactive (hero + supporting) traces the same way: HTML data-int="int_NN" → interaction.json centerpiece (the hero, a single object) / supporting[] (the curated set) → the Analyst client_model / ana_* it recomputes. The Imagineer's img_ ids are internal (concept
…(truncated)