# Data2story Pro

> Use to turn a dataset into a verifiable multimedia blog (a data story / data-driven article / interactive dashboard from a dataset). Orchestrator for the Data Journalist Agent (Data2Story): a 7-team newsroom (14 agents) running detective → scout → analyst → imagineer → editor → copywriter → designer → interaction → hero → cinematographer → programmer → auditor → critic → inspector in sequence. Trigger when the user hands over a dataset (CSV/JSON/folder/path) and wants a published story, blog post, or interactive report built from it. Creates a versioned project folder per run.

- Skill: `qinghonglin/data2story-pro` (Agent Skill, multi-file: 29 files)
- Install (CLI): `npx skillmds@latest add qinghonglin/data2story-pro`
- Raw SKILL.md: https://api.skillmd.com/api/skills/qinghonglin/data2story-pro/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: QinghongLin (https://skillmd.com/u/qinghonglin)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/qinghonglin/data2story-pro

---


# 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 --check` the scripts, recompute a known model output and compare, byte-compare each inline JSON island to its `verify/` 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 this `SKILL.md` (`.../skills/data2story-pro`)
- Resolve `ARCHIVE_DIR` = the ancestor directory that contains `skills/` (two levels up from `SKILL_DIR`, i.e. `SKILL_DIR/../..`)
- Resolve `DATA2STORY_ROOT` = parent of `ARCHIVE_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 the `DATA_DIR` that **Stage 0 — Input dispatch (below)** resolves, so run Stage 0 before resolving `PROJECT_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); or `ideation`'s output folder (IDEA MODE). For a bare dataset name with no Stage-0 acquisition, fall back to `DATA2STORY_ROOT/data/{DATA_NAME}`.
- `TIMESTAMP` = current time formatted as `MMDD_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 resolved `DATA_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.py` read it; absent ⇒ `premium`.

> **Windows:** there is usually no `python3` on PATH — run the `python3 …` commands in this skill as `py` (or `py -3`). Set `PYTHONUTF8=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/`** and **`dataviz-craft/`** to live under the same `skills/` 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 skills **`find-data/`** and **`sparring-partner/`** under the same `skills/` parent: Stage 0's `ideation` sub-skill drives `sparring-partner` to converge a topic and `find-data` to 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:**

1. **Empty / whitespace** → **IDEA MODE** (open by inviting the idea: "what do you want to tell a story about?").
2. **An existing path on disk** (`$ARGUMENTS` resolves to a file or directory) → **DATA MODE**. The current behavior, unchanged: set `DATA_DIR` to that path and proceed into the pipeline.
3. **A data URL** (`$ARGUMENTS` starts with `http://` or `https://`) → **DATA MODE via find-data url-mode**. Run `Skill find-data "<url>"` — it fetches the resource and validates it against the 4 completeness gates, producing a dataset folder. Set `DATA_DIR` to that folder and continue the pipeline.
4. **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. A `story_brief.json` will sit at `<DATA_DIR>/meta/story_brief.json`. Set `DATA_DIR` and `DATA_NAME` from 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:**
1. If `$ARGUMENTS` contains **`--fast`** → `fast`; **`--premium`** → `premium`. **Strip the flag** from `$ARGUMENTS` before input dispatch. A headless/automated call with no flag defaults to `premium`.
2. 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)?"*
3. Write the choice to `PROJECT_DIR/run_config.json` as `{"run_profile": "premium"}` or `{"run_profile": "fast"}` right after `PROJECT_DIR` is created (Setup). **`validate.py` and `generate_viewer.py` read 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:

```bash
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`](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 **`premium`** flagship. The **`fast`** profile (~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. Absent `run_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 any `verify_*` error, OR Section 15 reports any `send_back_open` / `playtest_hard_unresolved`, OR `generate_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 and `verify/` artifacts are mentioned as MANDATORY" is enforced by those deterministic checks, not by prose alone. A left-`open` detected defect (a Section-15 `send_back_open` / `playtest_hard_unresolved`) blocks flagship in the same class as a `verify_*` 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_model`s), `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).** The `is_computational` flag 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: if `analyst.json` emitted any `client_model`, OR any finding whose nature is a **rate / ranking / aggregate / probability / model output** (i.e. a number the reader could in principle reproduce), **upgrade `topic_profile.is_computational` to `true`** in the resolved profile (the `detective.json`/`scout.json` `topic_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`](../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`](../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's `verify.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.py` here at Stage 6.4 (after the contract gate, before the Critic) and `generate_viewer.py` at 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.
```bash
python3 SKILL_DIR/inspector/scripts/verify.py PROJECT_DIR
```

> Back to the **Auditor team**: with `verifier.json` in 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)**:
1. Read `critic.json`. If `overall.pass` is true, proceed to the contract gate + Stage 7. **`overall.pass == true` is 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.pass` will be false and `overall.tier` will be `"competent"`, not `"flagship"`.**
2. Otherwise, for each failing dimension apply its `suggested_fix` via the `send_back_to` role 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.
3. 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`), and `verify.py` (so `verifier.json` reflects the revised page). The contract gate now also asserts the verify layer (`validate.py` Section 7): if it reports any `verify_*` 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's `resolution` (`fixed` after a clearing re-run, or `blocker_recorded`+`blocker_reason`); the contract gate asserts none is left `open` (Section 15).
4. Re-run `Skill critic PROJECT_DIR` (increment `overall.round`).
5. Stop when `overall.pass` is 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 final `critic.json` (with any residual issues) in place. A run is `flagship` **only** when `overall.pass == true` (i.e. `overall.tier == "flagship"`). If the loop exits after 2 rounds with `overall.pass == false`, read `critic.json overall.tier` and 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): status **`INCOMPLETE — 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 its `send_back_to` role + `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): status **`COMPETENT — 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_fix` most likely to reach a signature 5).
   A passing `critic.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 the `verify/` FILES exist and the panel SHELL is present (the `#verifyToggle` toggle + 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 by `generate_viewer.py` itself.

### 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:
```bash
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.py` **exits nonzero**, read its **stderr**: a complaint about the authored `verify/` payloads or the panel shell (missing/empty islands, a `data-*` id with no `verify_map` entry, a schema-invalid `verify_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-run** `generate_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)
