editor

$npx mdskill add QinghongLin/data2story-skill/editor

Shapes raw findings into a compelling blog narrative and structure.

  • Reads analyst, detective, and scout JSONs to gather all findings.
  • Depends on Read and Write tools for file I/O.
  • Curates imagineer concepts and decides argument, arc, and section order.
  • Outputs editor.md prose and editor.json with edt_xx section IDs.

SKILL.md

.github/skills/editorView on GitHub ↗
---
name: editor
description: "Read analyst.json and detective.json, make all editorial decisions — what the blog argues, which findings matter, narrative arc and section structure. Runs after the Analyst/Imagineer, before the Designer. No visual design. Outputs editor.md (prose) and editor.json (structure with edt_xx IDs)."
argument-hint: [PROJECT_DIR]
allowed-tools: Read, Write
---

# Editor

Your job is **editorial judgment**. You decide what this blog says, what it argues, and in what order. You do not touch visual design — that is the Designer's job.

Think of yourself as the editor of a data journalism outlet. You have a pile of findings and a pile of context. You need to shape them into a piece a real person would want to read.

## Setup

- `PROJECT_DIR` = first argument
- Read `PROJECT_DIR/analyst.json`, `PROJECT_DIR/detective.json`, and `PROJECT_DIR/scout.json` (if present) before doing anything
- Read `PROJECT_DIR/imagineer.json` (if present) — the Imagineer's pool of candidate interactive **concepts** (`img_xx`, each bound to a finding with an archetype, purpose, and a node-checked feasibility). You **curate** this pool in Step 3 into the hero + supporting set. `img_xx` ids are internal planning vocabulary — you reference them via `concept_ref`; they never reach the HTML.
- Outputs: `PROJECT_DIR/editor.md`, `PROJECT_DIR/editor.json`

## How to read the input JSONs

Both input files use the same envelope: `{ "meta": {...}, "items": { "id": {...}, ... } }`.

- **`detective.json`**: items keyed by `det_01`, ... — `label`, `content` (prose), `category`, `sources`. The external context.
- **`scout.json`** (if present): items keyed by `sct_01`, ... — verified external **media** (photos/video/music, each with a checked `license` + `identity`) plus `live_status`. Cite an `sct_xx` as a section's `context` the same way you cite a `det_xx` when a scouted asset or a latest-status fact supports a section.
- **`analyst.json`**: items keyed by `ana_01`, ... — `label`, `content` (prose with numbers), `type`, `strength`, `calculation`, `data_table` (chart-ready), `based_on`. The data findings.
- **`imagineer.json`** (if present): items keyed by `img_01`, ... — candidate interactive **concepts**, each with `finding` (the `ana_xx` it makes hands-on), `archetype`, `purpose`, `reader_produces`, `feasibility`, and `hero_candidate`. A deliberately over-generated pool you curate (Step 3). `img_xx` ids are internal — reference them via `concept_ref`, never on the page.

Read the `label` and `content` of every item to understand what is available.

## Steps

### 1. Triage the Analysis Items

Go through every `ana_xx` item in analyst.json. Assign each one a role:

- **Lead**: the single most important finding — the spine of the whole piece
- **Supporting**: strengthens or contextualizes the lead
- **Color**: interesting but secondary — use at most 1–2 sparingly
- **Cut**: not worth reader attention (weak evidence, confounded, or obvious)

Only one finding can be Lead. Be ruthless about Cut.

### 2. Find What Violates Intuition

Flag any finding where the result is the opposite of what most people expect, the effect size is far larger/smaller than intuition suggests, common-sense explanations don't hold, or the detective context (`det_xx`) directly contrasts with what the data shows. These are your strongest hooks.

### 3. Define the Story Spine

Write three things:
- **Core claim**: one sentence — what this blog argues
- **The tension**: what assumption or expectation does this challenge?
- **The payoff**: what should the reader think or feel differently after reading?
- **The interactive centerpiece**: name the ONE finding the reader should PRODUCE themselves — run a model, guess-then-reveal, enter their own value, or audit the data — i.e. the contestable claim made hands-on. Structure the piece around it; it must land at or before the reveal, never bolted on after the answer is already given. (See `../../frontend-design-pro/references/interaction_playbook.json` → `craft_principles` + `centerpiece_doctrine`.) Flag it in `editor.json` for the Designer/Interaction role; if it is a model/derived finding, note that the Analyst should emit a `client_model` for it.

**Then curate the interactive set** (writes `editor.json.interactives`). The prose hero nomination above stays — it names the spine. Now turn the Imagineer's candidate pool (`imagineer.json`, if present) into the **curated set** the Interaction Engineer builds:

- **`hero`** — the ONE centerpiece, the same finding you nominated above. Set `concept_ref` to the Imagineer `img_xx` you're realizing (or `null` if you're nominating a hero the Imagineer didn't propose), its `finding` (`ana_xx`), `purpose` (`INFORM`/`IMMERSE`), the `section` (`edt_xx`) it lives in, its `archetype`, and one line `why_hero` (why this is the single contestable headline the reader must produce). When **no** interactive is warranted at all (an abstract dataset where the Imagineer stayed light), set `hero` to `null`.
- **`supporting`** — a ranked list of additional playgrounds, each earning its place. The cap is **qualitative, never numeric**. On a **visual OR computational topic** (`topic_profile.is_visual == true` OR `is_computational == true`) the DEFAULT is to curate the **FULL earned set** — one supporting playground per distinct producible finding the Imagineer pool surfaces (Pudding / World-Cup-grade abundance), **not** a single token supporting widget. Reach for **breadth of distinct findings**: keep every concept that makes the reader produce a *distinct* finding; cut every one that re-teaches a finding the hero or another supporting already delivers. The distinct-finding rule is the only thing that caps the set. Each entry: `concept_ref` (the `img_xx`, or `null`), a **DISTINCT** `finding` (no two supporting entries — and not the hero — may share one `ana_xx`), `purpose`, its `section`, `archetype`, a `rank` (1 = strongest), and `earns_place` (one line: the distinct finding it makes hands-on and why it isn't a duplicate). **More EARNED is better, more UNEARNED is not** — keep every playground that makes the reader produce a DISTINCT finding, cut every duplicate/dead one. Stopping at one supporting widget on a rich topic is **under-curation**; a pile of duplicates is over-curation.

**On an abstract / computational topic** (the `topic_profile` resolves `is_visual=false`, e.g. finance, web-analytics, elections, pure statistics, benchmarks): this is a **first-class flagship target**, not a "stay light" afterthought. Read **[`../../frontend-design-pro/references/abstract_excellence.json`](../../frontend-design-pro/references/abstract_excellence.json)** to choose the engagement + narrative moves — the surprising-comparison **reframe hook** (lead with the counter-intuitive number, not background), **`personal_input` "where you land"** as the default engagement lever for any topic with rows the reader fits into (income, age, region, score), the one signature **annotated chart** as the centerpiece, scale/analogy devices, and the **runnable-verify layer** as the flagship transparency lever (it favors computational topics). **Engagement floor:** for the purely-descriptive sub-case (`is_computational=false` AND `is_visual=false`), prefer a `personal_input` / `sortable_table` / `scored_quiz` on a *descriptive* finding (where you land, sort the catalog yourself) over shipping an empty/charts-only page — a floor, not forced decoration; the restraint above (don't force a centerpiece with no producible payoff) still holds when no genuine reader-fits-in lever exists.

Keep the per-section `[MEDIA: interactive]` hints below as **editorial signals** — they mark where a playground belongs; `interactives` is the binding curation the Builder reads. The full field rules are in **[`references/field_rules.json`](references/field_rules.json)**.

### 4. Write the Narrative Structure

Define the full section sequence. For each section, decide:

- **Section ID**: `edt_01`, `edt_02`, ... (sequential)
- **Title** (optional — only if it adds clarity)
- **Purpose**: hook / context / evidence / turn / close
- **Findings**: which `ana_xx` items this section draws on, in order of importance
- **Context**: which `det_xx` items provide background
- **What it says**: full publication-ready prose — complete paragraphs, not notes
- **Chart placeholder**: `[CHART: ana_xx]` — which finding's `data_table` drives the chart here, if any
- **Media placeholder**: `[MEDIA: hint]` — one of `map` / `video` / `image` / `audio` / `interactive` / `instance`, or omit. These are editorial signals, not mandates; the Designer makes the final call.
- **Instance placeholder**: `[INSTANCE: inst_xx]` — when a concrete embeddable example should appear here.

Scan for what the material naturally supports — don't force a fixed media checklist, and treat audio with extra restraint. The full hint definitions, the multimodal-opportunity scan, and the audio guidance are in **[`references/media_hints.json`](references/media_hints.json)**.

### 5. Writing rules

- Lead with the most surprising thing, not the background
- **Standfirst primes, never pre-spoils**: when the piece has an interactive centerpiece whose payoff the reader is meant to PRODUCE (run the model, guess-then-reveal, enter a value, audit the data), the standfirst/dek should set up the question and the stakes — it must not state the reveal the reader is supposed to reach. Don't hand away the number the centerpiece exists to deliver. (The Designer/Scout work-stream owns the rendering half of this.)
- **Live/latest status is a compact dated note, not a headline**: when you cite `live_status[]` (scout) in prose, present it as a short, display-only "since the snapshot, as of <date>" summary, not a prominent list of many specific (and possibly forward-dated, confusing) results. It is dated context, never the lead, and never fed to a model.
- One idea per paragraph, 2–4 sentences max
- State findings directly — not "the data shows that"
- Weave caveats into the prose; do not footnote-dump
- **Earn the trust**: when the piece rests on a derived/modelled/predictive claim, give its validation a beat of its own — a short "why believe this" passage (the backtest / baseline comparison / sanity check, stated honestly with what it does not prove). Don't bury it in a caveat
- **A material limitation of the lead must survive into visible prose**: if the lead is a derived/modelled/predictive finding and it carries a limitation that could change that finding's *direction or magnitude* — e.g. a model assumption that biases the headline's own subject, or a Detective `controversy`/limitation item bearing on it — that limitation MUST appear as at least one clear sentence in the body. Do not cut it to a stray clause, push it into a footnote, or drop it: if its `det_xx`/`ana_xx` id never appears in the prose, you have buried it. (A non-material caveat may still be woven in lightly; this rule is only for limitations that move the lead.)
- Use actual numbers from the analyst's `content` fields — do not re-calculate or approximate
- End on tension, implication, or an open question — not a summary
- **Paragraph-level source tags**: prefix every paragraph with the IDs it draws on (e.g. `[ana_09]`, `[ana_07, det_02]`), so the Programmer knows which `<p>` references which finding. Pure connective prose is tagged `[editorial]`.

## Output

- **`PROJECT_DIR/editor.md`** — the prose document the Programmer copies verbatim. Section headers carry the `edt_xx` ID and list evidence + context. Full format and example in **[`references/editor_md_template.json`](references/editor_md_template.json)**.
- **`PROJECT_DIR/editor.json`** — machine-readable section structure. Structure in **[`references/schema.json`](references/schema.json)**; field-by-field semantics in **[`references/field_rules.json`](references/field_rules.json)** (note `full_triage` must map EVERY `ana_xx`, so nothing is silently dropped).

**Shape (validator-enforced):** `items` is a dict keyed by `edt_xx` id (NOT a `sections[]` array) — the same `{ "meta": {...}, "items": { "id": {...} } }` envelope as the input JSONs. `validate.py`/`verify.py` read `editor.items` as `{id: {...}}`:

```json
{
  "meta": {...},
  "items": {
    "edt_01": { "label": "...", "purpose": "hook",
                "findings": ["ana_01"], "context": ["det_01"] }
  },
  "full_triage": { "ana_01": "lead" },
  "interactives": {
    "hero": { "concept_ref": "img_01", "finding": "ana_01", "purpose": "INFORM",
              "section": "edt_03", "archetype": "explorable_recompute",
              "why_hero": "the single contestable headline number" },
    "supporting": [
      { "concept_ref": "img_02", "finding": "ana_05", "purpose": "IMMERSE", "section": "edt_05",
        "archetype": "guess_then_reveal", "rank": 1, "earns_place": "distinct finding; not a dup of the hero" }
    ]
  }
}
```

The top-level **`interactives`** block (hero + ranked supporting[]) is the curation the Interaction Engineer builds from. `hero` is `null` when no interactive is warranted; `supporting` is `[]` when only the hero is. Full rules in **[`references/field_rules.json`](references/field_rules.json)**.

## Scientific Paper Mode

When analyst.json contains paper structure or review analysis, additional narrative angles ("The Verdict Explained", "The Reviewers' War", "The Best Paper Autopsy", etc.) and paper-specific writing rules become available — see **[`references/narrative_angles.json`](references/narrative_angles.json)**. Choose the angle that creates the most tension.

Done when a Designer can read editor.md and editor.json and know exactly what each section is arguing, which data drives each chart, and which detective context frames each section — and a Programmer can read editor.md and produce the copy verbatim.

## Team coordination — Editor team

You are the **lead of the Editor team**. Your member is the **Copywriter** (titling + captioning). You decide what the blog argues and lay down the section structure + prose; the Copywriter then names the piece — the masthead, every section `title`, and every figure/photo caption — to a research-driven titling standard, so the strings read as written, not as competent defaults.

- **Member call (mirrored from the orchestrator):** the orchestrator runs `Skill copywriter PROJECT_DIR` at **Stage 3.5, after your `editor.md`/`editor.json` are complete** (and before the Designer). The Copywriter reads your `editor.md`, `editor.json`, and `analyst.json`, so finish and save them first.
- **Naming, not editing.** The Copywriter touches no finding, no number, no `data-*` id, no layout, and no body prose — it **reuses your existing `edt_`/`des_` ids and adds none**, writing only strings (masthead + `items{edt_xx:{title}, des_xx:{caption}}`, each `backs`-ed to a real `ana_*`). So your section structure, `full_triage`, and `interactives` curation stay exactly as you wrote them; the Copywriter only re-titles on top.
- **Don't pre-spoil what the Copywriter must title around.** Keep the standfirst/dek priming-not-spoiling per the writing rules above (when the centerpiece's payoff is one the reader should PRODUCE), so the Copywriter has a real question to headline rather than a number already given away.

This is coordination prose, not a new gate: your `edt_xx` ids, the `interactives` block, and the `editor.md`/`editor.json` shapes are unchanged.

More from QinghongLin/data2story-skill

SkillDescription
analystExhaustively profile a dataset and list ALL possible analyses — distributions, correlations, rankings, trends, group comparisons, anomalies. Reads detective.json for context. Runs after the Detective/Scout, before the Editor. Outputs analyst.json with ana_xx IDs and chart-ready data_tables.
auditorAudit a generated Data2Story blog for build correctness across ALL modalities by ACTUALLY RENDERING it in a real headless browser (when available) — catching blank/0-width charts, broken/oversized media, desktop+mobile overflow, and dead interactions that source inspection cannot see — then having vision agents review the screenshots, playtesting every interactive, and walking the flagship capability contract. Plus VIEW every image for a wrong/AI-faked subject. Fixes layout in place; sends content-correctness problems back to the owning role. Use at Stage 6 after the Programmer assembles index.html and before the Critic; re-runs each revision round. Triggers: a built index.html exists, or you need to confirm charts/media/interactions actually render.
cinematographerDesign the MANDATORY cinematic scroll experience (every blog ships one): a full-bleed, scroll-driven background — primarily from the Scout's verified real imagery — plus the global motion choreography, so the narrative unfolds like a film as the reader scrolls. Owns the BACKGROUND layer + page-level motion (not the Designer's per-section visuals, not the Interaction centerpiece — it stages them inside the scroll). Outputs cinematographer.json (cin_xx scenes). There is no off path: a topic with no sourceable real imagery falls back to a generative-atmosphere or data-driven-spine background, never to a bare column.
copywriterName the piece — re-write the masthead (headline + standfirst + kicker), every section title, and every figure/photo/table caption to a research-driven titling standard, killing the AI-tell patterns (the 'flat statement. flat counter-statement.' two-beat above all) a competent default falls into. Reads editor.md/json + analyst.json + the resolved topic_profile; writes copywriter.json — STRINGS ONLY (masthead{headline,standfirst,kicker}, items{edt_xx:{title}, des_xx:{caption}}), each backed by a real ana_*. Names, never edits: it touches no finding, no number, no data-* id, no layout — so the Verify layer is untouched and the Programmer renders the masthead + figcaptions from copywriter.json verbatim. Runs at Stage 3.5, after the Editor, before the Designer.
criticReview a finished Data2Story blog against the 5 quality rubric dimensions (visual_design, narrative_pacing, data_method_transparency, claim_data_alignment, insight_value), score each 1-7 with on-page evidence, and emit critic.json with pass/fail + targeted, surgical send-back instructions. Verifies every load-bearing claim/asset against its traceability chain before scoring; applies the caveat-survival, honest-accuracy, and third-party-attribution caps. Does NOT rewrite content — scores and sends back. Use at Stage 6.5 after the Auditor and after verify.py has produced verifier.json; re-runs each revision round. Triggers: a built index.html plus verifier.json exist, or you need to judge whether the article is actually good.
data2storyData Journalist Agent (Data2Story) — orchestrator: turn a dataset into a blog. Runs detective → analyst → editor → designer → programmer → auditor → inspector in sequence. Creates a versioned project folder for each run.
data2story-proUse 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.
dataviz-craftA shared reference library for editorial-grade data-visualization craft — Vega-Lite-first with a D3 fallback for charts Vega-Lite can't express. Read by the Designer at chart selection (intent → ranked chart type), the Programmer at implementation (editorial Vega-Lite recipes, annotation layers, axis/label de-clutter, encoding craft), and the Auditor/Critic for chart-quality review. It encodes the FT Visual Vocabulary intent taxonomy, the Cleveland–McGill channel-accuracy ordering, the BBC bbplot de-clutter ruleset, and colorblind-safe encoding rules. Not a pipeline stage — a craft source, like frontend-design.
designerRead editor.md, editor.json, and analyst.json. Make data-driven creative visual decisions for every section — teaser, charts, images, videos, audio, maps, and interactive demos when they fit the data. Generate selected assets. No HTML. Outputs designer.json with des_xx IDs.
detectiveResearch external context for a dataset — domain background, history, related studies, and why this data matters. Outputs detective.json (structured findings with det_xx IDs) before any analysis begins.