Data Journalist Agent (Data2Story)

SkillDatabases & data

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.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Data Journalist Agent (Data2Story) skill

What this skill tells your AI

The instructions your AI receives, as published by qinghonglin/data2story-skill in skills/data2story-pro/SKILL.md and read by ahel’s review.

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)LeadMembersKind
DetectiveDetective+ Scoutmulti-agent
AnalystAnalyst+ Imagineermulti-agent
EditorEditor+ Copywritermulti-agent
DesignerDesigner+ Interaction + Hero + Cinematographermulti-agent
ProgrammerProgrammersingle-lead
AuditorAuditor+ Critic (+ Playtester step)multi-agent
InspectorInspectorsingle-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 togetherdata2story-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 / whitespaceIDEA 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 --fastfast; --premiumpremium. 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:

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 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.jsonpremium.

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_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). 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 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

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
155
Forks
22
Last commit
Jul 2026
Advanced
Catalog kind
skill
Gateway key
data2story-pro
Source
github.com/qinghonglin/data2story-skill