From data2story-pro
Orchestrates a 14-agent newsroom pipeline to turn a dataset (CSV, JSON, folder, or URL) into a verifiable multimedia blog post with images, interactive charts, and audit artifacts.
How this skill is triggered — by the user, by Claude, or both
Slash command
/data2story-pro:data2story-proThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Turn **$ARGUMENTS** into a blog. Orchestrates the roles below in sequence.
README.mdTEAMS.mdanalyst/references/data_table_rules.jsonanalyst/references/field_rules.jsonanalyst/references/paper_mode.jsonanalyst/references/schema.jsonauditor/references/checks.jsonauditor/references/fix_patterns.jsonauditor/references/flagship_contract.jsonauditor/references/report_types.jsonauditor/scripts/playtest_drive.jsauditor/scripts/render_capture.jscinematographer/references/cinematic_recipes.jsoncinematographer/references/example_cinematic_scroll.htmlcinematographer/references/schema.jsoncopywriter/references/schema.jsoncritic/references/rubric.jsondesigner/references/audio_rules.jsondesigner/references/diversity_rules.jsondesigner/references/field_rules.jsonTurn $ARGUMENTS into a blog. Orchestrates the roles below in sequence.
Five process rules earned from real runs. Each is one rule + why it exists; follow them across every run regardless of topic.
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.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).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 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.
Resolve paths before doing anything:
SKILL_DIR = the directory containing this SKILL.md (.../skills/data2story-pro)ARCHIVE_DIR = the ancestor directory that contains skills/ (two levels up from SKILL_DIR, i.e. SKILL_DIR/../..)DATA2STORY_ROOT = parent of ARCHIVE_DIRARCHIVE_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}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).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
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).
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:
$ARGUMENTS resolves to a file or directory) → DATA MODE. The current behavior, unchanged: set DATA_DIR to that path and proceed into the pipeline.$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.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.
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".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:
$ARGUMENTS contains --fast → fast; --premium → premium. Strip the flag from $ARGUMENTS before input dispatch. A headless/automated call with no flag defaults to premium.AskUserQuestion: "Fast (~15–25 min — charts + static images, traceability only) or Premium (~2h40–2h50 — the full cinematic/interactive flagship)?"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.
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.
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.
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.
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.
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
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).
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.
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.
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
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.
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
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
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
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.
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.
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.)
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).
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.
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):
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".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.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).Skill critic PROJECT_DIR (increment overall.round).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 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.
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:
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.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.
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 ideation only) and never reach the HTML — they are added to NO data-* tuple/regex; the Editor maps the chosen img_ concepts onto the int_ ids the Interaction Engineer builds. data-play-out="int_NN" is a non-provenance hook (a Playtester readout selector, like data-backs) — it is NOT a provenance prefix and is invisible to validate.py/verify.py.
Provenance prefix convention (authoritative). Each producing role tags its atomic ids with a fixed prefix, surfaced in the HTML as data-<prefix>="<prefix>_NN": det_ Detective · sct_ Scout · ana_ Analyst · edt_ Editor · des_ Designer · int_ Interaction (every playground — hero centerpiece + each supporting[] — via data-int) · cin_ Cinematographer. The Imagineer's img_ is internal-only (never surfaced in HTML, in no tuple/regex), and data-play-out is a non-provenance hook (not a prefix). This is the single source of truth for the prefix↔role mapping used throughout the pipeline (validate.py/verify.py, the verify-layer schemas, and each role's SKILL.md).
designer.json must declare a purpose — INFORM (carries data/information the prose can't) or IMMERSE (sets mood/aids reading); an item serving neither is decoration and must be cut. Aim for breadth across the five channels (chart, image, video, audio, interactive_or_map), but a channel is used only when an asset in it earns a purpose — for any channel marked used:false, confirm a data-grounded reason is recorded in meta.media_decisions. Send back to the Designer (before the Programmer) any unpurposed/decorative asset, or a channel skipped for convenience rather than genuine lack of purpose.
interaction.json — the centerpiece (hero) and each supporting[] entry. Any playground that declares no purpose (INFORM/IMMERSE) or binds a fake/non-existent finding (ana_*) is decoration and is CUT here — before the Programmer builds it, so an unearned widget is never coded in the first place. This pre-build cut is the single most important guard against abundance degrading quality: every supporting playground must independently justify its existence, or it does not survive to Stage 5. Send the cut back to the Interaction Engineer (drop or re-bind the playground) before the Programmer runs.topic_profile.is_visual == true OR is_computational == true (UNION — a genuinely abstract/small/privacy_sensitive dataset trips none, each sub-check being capability-gated). On a rich topic, confirm the page is not under-delivered: a dynamic hero (hero.json kind=="video", not a bare static still) on a visual topic; a license-clean topic-asset floor (≥3 distinct verified images, ≥5 cover-able for cinematic) on a visual non-privacy topic; substantive verify (no hollow cell_registry cells) on a computational topic. On a topic the classifier marks visual/computational these floors BLOCK (validate.py Section 12 emits the matching richness_* ERRORs — richness_static_hero, richness_asset_floor, richness_hollow_verify_cells — and the contract gate routes the nonzero validate.py back to the owning role: hero/scout/programmer), unless a legitimate blocker is recorded — a privacy_sensitive topic, or a genuinely-failed source ladder / an honest fallback. Route each blocking error back to the named role to enrich, then re-run BEFORE the Programmer re-runs. Anti-slop: each required-rich element still earns its place via the media-purpose gate — the floor forbids impoverishment, it NEVER forces a fabricated/decorative asset (which itself caps visual_design at 3). It is NOT correct to say this gate "never hard-blocks": on a rich topic with no recorded legitimate blocker, an unmet floor blocks (mirror of, and must not collide with, the abstract-topic missing_engagement_floor).cinematographer.json meta.mode is one of photographic / generative / data_driven — never off/opt-out) AND opens with a fitting real-sourced BGM. Two floor checks: (a) cinematic missing — meta.mode is absent / not one of the on-modes (no scroll background produced) → send back to the Cinematographer to build the immersive layer (photographic if ≥5 cover-able verified images exist, else generative AI atmosphere, else a data_driven chart-spine); (b) BGM missing or tonally wrong — no fitting front-of-blog soundtrack on any topic (a missing BGM, or a present-but-tonally-WRONG track — e.g. a celebratory anthem on a sober topic) → send back to the Scout to source a fitting, license-clean (or demo-gated) real track. The two floors differ in how they block. BGM is a HARD ERROR on EVERY blog — there is NO privacy exemption and NO audio.used == false escape hatch: the Scout's classical-recording floor (rung C) is the GUARANTEED terminal rung of the audio source ladder, so a license-clean BGM always exists (a privacy-sensitive topic gets a quiet classical recording, an abstract topic a restrained pensive/ambient track), and validate.py emits the bgm_missing ERROR on every topic whenever no BGM presence signal is found — "ship no BGM" is never a valid outcome. The legitimate-blocker escape survives ONLY for the cinematic background: cinematic_missing ERRORs on a visual/computational topic (else warn), and cinematic may fall back to a thin spine or be reduced under under-supply / on a privacy_sensitive topic. The contract gate routes the nonzero validate.py back to the owning role — Cinematographer / Scout. Mandatory means always-present-AND-tasteful, never force-junk: never satisfy these by bolting on a tonally-wrong/decorative immersion (that still caps visual_design at 3).topic_profile.is_visual == true) AND cinematic is ON but the immersion fell back to a non-photographic mode for under-supply of real cover-able imagery (1 <= verified license-clean cover-able image count < 5), send the run back — to the Scout to source ≥5 (ideally a dozen+) license-clean cover-able backgrounds of the story's subjects, then to the Designer to register each as a real des_/sct_ item, BEFORE the Cinematographer re-runs so the immersion can UPGRADE to a photographic scroll. The count is the primary signal; meta.mode_reason is only a secondary confirm that the non-photographic mode was supply-driven (not honest restraint). validate.py Section 12 front-runs this as the richness_cinematic_undersupplied warning (a WARN, not an error). Surface an unmet supply gate in the closing summary as "richness floor not met". This send-back never hard-blocks the build (the run still finishes); the ≥5 threshold itself is unchanged.meta.mode ∈ photographic/generative/data_driven/cinematic), assert that every cin_* scene's background.media_ref (for kind image/video) already resolves to a registered sct_ (scout) or des_ (designer) item — the cinematic background must be a registered media item BEFORE the Cinematographer references it, not just an entry in a bypass manifest. (A data_driven chart-spine scene with kind color/gradient carries no media_ref and is exempt from this resolution check — it is still a real built scroll layer.) Any image/video cin_* whose media_ref is missing or dangles (not a current sct_/des_ id) is sent back: to the Designer/Scout to register the background as a real des_/sct_ item, then to the Cinematographer to point media_ref at it. Resolve all cin_* references before invoking the Programmer (this front-runs the same check validate.py Section 6 enforces, so the dangling reference never reaches the build).python3 SKILL_DIR/inspector/scripts/validate.py PROJECT_DIR. If it exits nonzero, read PROJECT_DIR/validation.json and send each violation back to its owning role to fix (ana_* → Analyst, edt_* → Editor, des_*/instance → Designer, sct_*/media_* (license/identity) → Scout, int_/data-int → Interaction (centerpiece spec) or Programmer (its wiring), cin_* → Cinematographer, HTML data-* → Programmer), then re-run until it reports 0 errors. Do not start the Inspector until validate.py passes. (Unlike the media-richness gate above, this is a deterministic hard pass/fail.) The contract gate now ALSO asserts (Section 15) that every auditor.json/playtest send-back is resolved — each auditor.json.send_backs[] entry must end fixed or blocker_recorded+blocker_reason, and every hard audit/playtest_report.json fail must match an auditor resolution — so a left-open send-back or an unmatched hard playtest fail is a contract-gate ERROR; and (Section 16) that assets/ ships no unreferenced heavy master.
topic_profile_unresolved → Detective (resolve the topic_profile with explicit is_visual/is_computational booleans), then Scout (which writes one into scout.json if the Detective's is still absent).verify_map_kind_keys_missing / verify_map_unknown_kind / verify_map_entry_malformed → Programmer (Step 2.5) (fix the authored verify/verify_map.json).cinematic_data_driven_not_built → Cinematographer when the data_driven mode emitted no real scene; Programmer when the scenes exist but the data-cin token was never mounted.des_data_source_scout_dangling → Designer (point data_source at a real scout.<sct_id> / scout.live_status) with the Scout (register the sct_ item or supply the live_status entry).missing_engagement_floor → Imagineer (propose the engagement-floor concept) / Interaction (build it) — or record the honest engagement_blocker. It now BLOCKS on a resolved descriptive topic (is_visual:false AND is_computational:false) unless an honest engagement_blocker is recorded (privacy_sensitive topics are auto-exempt) — it is not advisory/soft.send_back_open / send_back_blocker_no_reason / send_back_fixed_but_still_failing (Section 15) → the entry's OWN send_back_to role (the Auditor sets it) — resolve it (fixed after a clearing re-run, or blocker_recorded+blocker_reason); a left-open, a blocker_recorded with no reason, or a "fixed" the re-run still fails is the contract-gate ERROR.playtest_hard_unresolved (Section 15) → Programmer (or the hard-flagged playground's owner) — a hard audit/playtest_report.json fail with no matching auditor.json resolution; fix it (then mark the matching send-back fixed) or record blocker_recorded+reason.asset_unreferenced_heavy (Section 16) → Inspector (re-run the Stage-7 relocate_unreferenced.py so the heavy master moves to provenance/) / Programmer (if the asset SHOULD be referenced, wire it into index.html).validate.py Section-7 kinds → Programmer (Step 2.5) (author/repair the verify layer per programmer/SKILL.md): verify_dir_missing, verify_map_missing, verify_map_empty, verify_map_missing_entry, run_cells_missing, run_cells_no_runnable, notebook_missing, panel_shell_missing. After the Programmer fixes the layer, re-run validate.py (and the Programmer if index.html changed) until 0 errors. (Section 7 asserts only that the verify/ files exist + the panel shell is pasted; it does not assert the islands are filled — that is Stage 7's generate_viewer.py job.)verify.py at Stage 6.4, generate_viewer.py at Stage 7); the orchestrator invokes them inline at those canonical points. This keeps each single-lead team self-contained and the cross-team sequence in one hand.PROJECT_DIR/assets/ only. Superseded/unreferenced masters are relocated to provenance/ at finalize (kept for traceability, never shipped); assets/ ships only what the page loads.PROJECT_DIR/index.html (with the in-page Inspector panel), PROJECT_DIR/detective.json, PROJECT_DIR/scout.json, PROJECT_DIR/analyst.json, PROJECT_DIR/code/*.py, PROJECT_DIR/imagineer.json, PROJECT_DIR/editor.md, PROJECT_DIR/editor.json, PROJECT_DIR/copywriter.json (the re-titled masthead + section titles + figure/photo/table captions; strings only — names, never edits), PROJECT_DIR/designer.json, PROJECT_DIR/interaction.json (now the hero centerpiece + the curated supporting[] set), PROJECT_DIR/hero.json (the animated cover; kind:"image" only when every animation rung degraded, with a recorded media_blocker), PROJECT_DIR/cinematographer.json (mandatory — meta.mode is one of photographic/generative/data_driven; never off), PROJECT_DIR/auditor.json, PROJECT_DIR/audit/playtest_report.json (the Playtester's real-browser drive of every playground), PROJECT_DIR/critic.json, PROJECT_DIR/validation.json, PROJECT_DIR/verifier.json, the PROJECT_DIR/verify/ artifacts (verify_map.json, run_cells.json, the reproducible notebook, cell_registry.json). The Inspector panel + verify/ artifacts are MANDATORY on every blog — a run that reaches "done" with the contract gate's verify_* (Section 7) errors unresolved, or with generate_viewer.py (Stage 7) at a nonzero exit, is INCOMPLETE and must not be presented as finished. Likewise, a run whose critic.json still has overall.pass == false after the bounded 2-round loop must carry one of two explicit terminal statuses (read from critic.json overall.tier) in its closing summary: tier == "sub_competent" → INCOMPLETE — quality gate not cleared (naming the still-failing dimension[s]); tier == "competent" → COMPETENT — NOT flagship-verified (every dimension >= 4 but no signature move reached 5 in the bounded rounds, naming the highest-leverage lift the Critic recorded). In both cases the deliverables are produced and Stage 7 still runs (the quality gate does not hard-block the build), but the run must NOT be reported as a silent "done" or called flagship-quality. Only a run with all hard gates clear — including validate.py Section 15 reporting no send_back_open / playtest_hard_unresolved — AND critic.json overall.pass == true (tier == "flagship") is a finished flagship.npx claudepluginhub qinghonglin/data2story-skill --plugin data2storyOrchestrates a multi-agent pipeline (detective→analyst→editor→designer→programmer→auditor→inspector) to transform a dataset into a versioned HTML blog.
Manages full-lifecycle blog content with 30 sub-skills for writing, rewriting, analysis, SEO, schema, images, repurposing, and multilingual publishing. Optimized for Google rankings and AI citations.
Drafts three journalist-grade deliverables from verified findings: findings-report.md, report.html (from template), and evidence-map.json. For use after Gate 1 approval in Phase 5.