inspector

$npx mdskill add QinghongLin/data2story-skill/inspector

Runs sentence-level traceability verification and builds in-page Inspector panel.

  • Links each blog sentence to evidence in role JSONs.
  • Depends on verify.py, generate_viewer.py, and Bash tools.
  • Parses HTML and role JSONs to create traceability map.
  • Emits verify/ artifacts and embeds runnable verifier in index.html.

SKILL.md

.github/skills/inspectorView on GitHub ↗
---
name: inspector
description: "Run sentence-level traceability verification on a Data2Story blog (verify.py -> verifier.json), then emit the in-page Inspector panel (the reader-facing runnable verifier) + the verify/ artifacts (verify_map.json, run_cells.json, the reproducible notebook, cell_registry.json). Mostly Python; the runnable layer recomputes each computation in-browser from its inlined data and grades it against the published output, while the bundled notebook re-executes from raw data. Use verify.py at Stage 6.4 (after validate.py, before the Critic) and generate_viewer.py at Stage 7 after the Programmer authors the verify/ files and pastes the panel shell. Triggers: a built index.html plus the role JSONs exist, or you need the traceability map / the in-page runnable verifier."
argument-hint: [PROJECT_DIR]
allowed-tools: Bash(*), Read, Write
---

# Inspector

Your job is **traceability verification** plus standing up the **runnable verify layer** — a reader can recompute each computation in-browser (from its inlined data), and the bundled notebook re-executes the headline numbers from raw data (that notebook is the paper's coding verifier). You parse the blog HTML, link each visible sentence back to its evidence in the role JSONs (`verify.py → verifier.json`), and then build the **in-page Inspector panel**: a click-any-claim drawer embedded in `index.html` whose evidence is byte-identical to the on-disk `verify/` artifacts, backed by a reproducible notebook that re-runs the headline numbers from raw data.

**This whole layer is MANDATORY on every blog.** A blog without a working Inspector panel, the `verify/` artifacts, and a clean reproducible notebook is incomplete.

## Setup

- `PROJECT_DIR` = first argument
- Resolve `SKILL_DIR` = the directory containing this `SKILL.md` (`.../skills/data2story-pro/inspector`). Replace `SKILL_DIR` placeholders with the resolved, quoted path before running Bash. Do not hard-code machine-local paths.
- Required files in PROJECT_DIR: `index.html`, `analyst.json`, `detective.json`, `designer.json`, `editor.json`

## Step 1: Run verify.py (sentence → evidence map) — early, at Stage 6.4

```bash
python3 SKILL_DIR/scripts/verify.py PROJECT_DIR
```

Produces `PROJECT_DIR/verifier.json` (the sentence→evidence mapping — the upstream traceability index). The output shape (format v3: `stats`, `sentences`, `unused_ids`) is in **[`references/inspector_schema.json`](references/inspector_schema.json)**.

**Ordering (important):** `verify.py` runs **early** — at **Stage 6.4**, after the contract gate (`validate.py`) and **before the Critic** — so `verifier.json` already exists when the Critic reads it. Re-run `verify.py` inside the Critic loop each round. The panel-injection (Step 2, `generate_viewer.py`) happens later, at **Stage 7**.

## Step 2: Emit the Inspector panel + verify/ artifacts

The Inspector no longer ships a standalone reader-viewer. It now emits the **in-page verify panel** (the reader-facing front-end) plus the auditable `verify/` artifacts that back it — including the reproducible notebook, which is the paper's from-raw-data coding verifier:

1. **The in-page Inspector panel (lives in `index.html`).** The Programmer copies `references/inspector_panel_reference.html` **VERBATIM** into `index.html` (the `.v-*` `<style>`, the `#verifyToggle` button, the drawer/backdrop/toast markup, and the `window.__verify` IIFE — see the programmer's `inspector_panel` family). The `.v-*` CSS must stay **inside a `<style>` element in `<head>`** — if it lands after the document's own `</style>` the rules render as visible text (conspicuous on mobile). The Inspector fills the panel's two inline JSON islands:
   - `<script type="application/json" id="verifyMap">` — per-element provenance, **byte-identical** to `verify/verify_map.json`
   - `<script type="application/json" id="runCells">` — in-browser-runnable snippets, **byte-identical** to `verify/run_cells.json`
   Clicking any element carrying a `data-{ana,det,des,sct,cin}` id opens the drawer for that id and renders the matching kind. A `computation` whose snippet is runnable gets a **Run** button that re-executes the statement in-browser (lazy Pyodide) and grades stdout against `expected_stdout` — recomputing the figure from the snippet's inlined data (the reproducible notebook performs the paper's from-raw-data re-execution; the panel makes that reproduction one-click for the reader).

2. **The `verify/` artifacts (on-disk source of truth).**
   - `verify/verify_map.json` — the per-element provenance map (the same bytes inlined as `verifyMap`). Schema: **[`references/verify_map.schema.json`](references/verify_map.schema.json)**.
   - `verify/run_cells.json` — the parallel runnable-snippet map (the same bytes inlined as `runCells`). Schema: **[`references/run_cells.schema.json`](references/run_cells.schema.json)**.
   - `verify/<topic>_forecast.ipynb` — the **reproducible notebook** that re-runs the pipeline from raw data and `assert`s the reproduced figures match the published ones (the notebook is a *proof*). Recipe: **[`references/reproducible_notebook.md`](references/reproducible_notebook.md)** (+ `references/notebook_template.ipynb` skeleton).
   - `verify/cell_registry.json` — maps each notebook `cell_id` to the atomic claim ids it backs (the inverse of `verify_map[id].cell_id`). Schema: **[`references/cell_registry.schema.json`](references/cell_registry.schema.json)**.
   - `verify/data/` — the dataset inputs the notebook reads (bundled at publish; see `reproducible_notebook.md` §7).

```bash
python3 SKILL_DIR/scripts/generate_viewer.py PROJECT_DIR
```

`generate_viewer.py` is the **deterministic panel + redirect emitter** — pure Python, no LLM. It runs at **Stage 7** and **requires** that the upstream authoring is already in place: the Programmer must have authored `verify/verify_map.json` + `verify/run_cells.json` (per their schemas) and copied `references/inspector_panel_reference.html` **VERBATIM** into `index.html` (the two **empty** `<script type="application/json" id="verifyMap">` and `id="runCells">` islands plus the IIFE's `var NB_PATH`). Given that, the script:

1. **Validates** the authored `verify/*.json` against the schemas, and checks every page `data-*` id resolves to a `verify_map` key (and vice-versa).
2. **Injects** the two payloads byte-identically into the empty islands in `index.html` — the rule is `inline == file_text.rstrip("\n")`, raw UTF-8, escaping a literal `</script>` only if present (never re-serialised). The on-disk `verify/*.json` files stay the source of truth; the inline copies are what the panel reads at runtime.
3. **Derives** `verify/cell_registry.json` — the mechanical inverse of `verify_map[id].cell_id` (plus the analyst `calculation.file → cell_<stem>` tag) — written only if absent, or refreshed with `--refresh` (so hand-attached `backs[]` are preserved).
4. **Wires** `NB_PATH` in the IIFE (the Download-notebook target — run locally; there is no Colab branch).

You run the script; you do not reimplement it. Its behaviour — the no-reflow overlay drawer, the kinds→renderers, the byte-identical-inline rule, the `data-*` contract, the lazy-Pyodide grading, and the publish wiring — is documented in the LIVE internals doc **[`references/inspector_panel_internals.json`](references/inspector_panel_internals.json)**; the verbatim UI build it copies is **[`references/inspector_panel_reference.html`](references/inspector_panel_reference.html)**. (An earlier margin-shift evidence viewer was retired and **superseded** by this layer — do not follow that old design.)

### Kinds the panel renders

`verify_map[id].kind` selects the drawer renderer (matching `verify_map.schema.json`):

- **computation** — claim + code slice (`code_file` + `code_lines`) + `data_preview` + `expected_output` + a Run panel. Runnable snippets execute in-browser and grade; stochastic ones run a reduced-N sample graded "≈ within noise" (never a hard PASS); `needs_network:true ⇒ runnable:false` (shown read-only, pointing at the notebook cell in `full_ref`).
- **media** — real (fetched) asset preview + `source_url` + `license` + `author` + `identity`; multi-asset elements render a gallery (`flags || assets || photos`, first non-empty wins).
- **generated** — AI-asset preview + `tool`/`model`/`prompt`/`disclosure` + the `real_default` publish-safe fallback (the real asset is the default; the AI asset is opt-in).
- **fact** — claim + category + categorised `sources[]`, each with its own `facts[]`.
- **credits** — a fact-shaped license/source aggregator (e.g. a card-deck's data + photo licences) rendered with a **Credits** badge (same renderer as `fact`).

### Byte-identical inline rule (hard)

The `verifyMap` / `runCells` islands inlined into `index.html` MUST be byte-identical to `verify/verify_map.json` / `verify/run_cells.json`. The on-disk files are the auditable source of truth and the publish artifacts (they are also the inputs the downloaded notebook reproduces from); the inline copies are what the panel reads at runtime. If they drift, the audited provenance no longer matches what readers see. When inlining, escape any literal `</script>` as `<\/script>` and serialise with `ensure_ascii` (see `inspector_panel_internals.json` → `inline_byte_identical`).

### data-* contract (no new attribute)

Elements bind to provenance via `data-{ana,det,des,sct,cin}` — one per producing role; the value is the atomic id (e.g. `data-ana="ana_01"`). Reuse these five only; do **not** introduce a new `data-*` attribute. Decorative wrappers carry no id and are skipped; the innermost tagged element wins. Every `data-*` id on the page MUST exist as a key in `verify_map.json` (a dangling id is an auditor failure; a `verify_map` id with no element is an unused-entry report, not fatal).

## Scripts

| Script | Role |
|---|---|
| `scripts/verify.py` | sentence→evidence pass → `verifier.json`. Runs **early, at Stage 6.4** (before the Critic). |
| `scripts/generate_viewer.py` | the deterministic **panel + redirect emitter** (Stage 7): validates the authored `verify/verify_map.json` + `verify/run_cells.json` against the schemas + page `data-*` ids, injects them byte-identically into the panel shell already in `index.html`, derives `verify/cell_registry.json` (write-if-absent / `--refresh`), wires `NB_PATH` (the Download-notebook target — run locally). |
| `scripts/validate.py` | the contract gate (run before `verify.py`; not modified here). |

Where the `verify/` payloads come from: `verify/verify_map.json`, `verify/run_cells.json`, and the reproducible notebook are **authored upstream by the Programmer** (the `claim` is the Editor's published sentence; `data_preview` is curated; the `run_cells` snippets are self-contained, pre-tested Pyodide code with real `expected_stdout`). They are not producible by a pure `analyst.json → islands` transform, so they are not emitted by the Inspector. `generate_viewer.py` only **validates, injects, derives, and wires** what the Programmer authored — it never invents provenance, which is why no LLM enters the Inspector. `cell_registry.json` is the one artifact the script derives (mechanical inversion of `verify_map[id].cell_id`).

## Running the steps

```bash
python3 SKILL_DIR/scripts/verify.py PROJECT_DIR
python3 SKILL_DIR/scripts/generate_viewer.py PROJECT_DIR
```

## Output

- `PROJECT_DIR/verifier.json` — full traceability data (with `raw_evidence`) — the upstream sentence→evidence map.
- `PROJECT_DIR/index.html` — now carrying the **in-page Inspector panel** (verbatim UI + the two byte-identical JSON islands).
- `PROJECT_DIR/verify/verify_map.json`, `PROJECT_DIR/verify/run_cells.json`, `PROJECT_DIR/verify/<topic>_forecast.ipynb`, `PROJECT_DIR/verify/cell_registry.json` (+ `verify/data/` at publish) — the auditable coding-verifier artifacts.

Done when: `index.html` opens directly in a browser (no server) and the 🔍 Inspector toggle opens a no-reflow drawer for every traced `data-*` element; a `computation` with a runnable snippet executes in-browser and grades against `expected_stdout`; the inline `verifyMap`/`runCells` islands are byte-identical to the `verify/` files; the reproducible notebook runs clean (every cell's `assert` passes).

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.