Agent Skills: research-dossier

|

UncategorizedID: ekson73/multi-agent-os/research-dossier

Install this agent skill to your local

pnpm dlx add-skill https://github.com/ekson73/multi-agent-os/tree/HEAD/skills/research-dossier

Skill Files

Browse the full folder contents for research-dossier.

Download Skill

Loading file tree…

skills/research-dossier/SKILL.md

Skill Metadata

Name
research-dossier
Description
|

research-dossier

Research emits prose. Renderers want data. Nothing bridged the two, so every dashboard was hand-rolled — no provenance, and no reuse of the ~110 templates and ~150 design systems already installed.

This skill is that bridge, and it is deliberately thin. The lever is not the HTML; it is the IR. Once research becomes JSON with provenance attached to each claim, the existing renderers are reachable for free. Everything here composes; nothing here duplicates.

What makes this different from a report generator

Two deterministic gates can fail the build. Neither is a model judgement — same input, same verdict, every time. A gate a model can talk its way past is decoration.

Gate 1 — provenance. Fails when the evidential chain is broken: a claim missing source / as_of / confidence; a chart or recommendation citing a claim id that does not exist; a recommendation with no owner or no eta; an empty not_checked[]; or a magnitude axis truncated without declaring it. That last check is the point of the whole gate — it carries the text-level faithfulness discipline into the visual layer. A bar chart starting at 60 exaggerates a small difference into a landslide whether or not anyone intended it. Declaring the truncation passes, and the rationale is then printed on the face of the chart.

Gate 2 — palette. Runs the bundled dataviz skill's own validate_palette.js over the resolved palette in both light and dark: lightness band, chroma floor, CVD separation (ΔE OKLab per protan/deutan/tritan), normal-vision floor, surface contrast. Colour is not reimplemented here — dataviz owns it, we cite it.

What the gates check — and what they do not

An independent red-team broke the first version of gate 1, so this section is written from evidence rather than intent. Its finding: the gate verified that citations resolve and never that they agree. Both are now checked — a chart point contradicting the claim it cites, a display string contradicting its own value, stacked parts that miss their cited total, a verdict that is not the argmax of its declared weights, a blank scorecard cell whose evidence exists but went unused, evidence years staler than the dossier: all fail the build.

Declared form vs. drawn form. The schema takes 13 chart.form values; the renderer draws exactly one geometry — horizontal proportional bars. A chart declaring form:"line" therefore renders as bars, and until v0.2.0 nothing said so: accepted, ignored, and silently substituted. FORM_NOT_RENDERED now names that gap. It is a WARN, not a FAIL — the evidence is still sourced and still truncation-checked, so only the label overpromises; --strict escalates it for CI. Two alternatives were rejected on measurement: shrinking the enum is a breaking change (it is enum-constrained with additionalProperties:false, so an existing IR carrying form:"line" would stop validating) and would delete vocabulary dataviz teaches; building 13 geometries is YAGNI while every fixture chart in the suite declares only bar. Note this adds no exemption to anything — truncation stays form-agnostic.

Two things remain outside deterministic reach, and are stated here rather than implied away:

  • Summary vs. claims. Prose can contradict the evidence beneath it. A numeral in the summary that appears in no claim raises a warning; a purely qualitative inversion ("the pilot succeeded") does not.
  • A recommendation citing the claims that refute it. Citations are checked for existence and agreement-on-value, not for whether they support the sentence.

Both are semantic judgements. Do not read a green build as a claim that the argument is sound — only that its numbers are consistent with its evidence. A weighting can also decide an outcome on its own; when one criterion outweighs all others combined the build warns, because arithmetic cannot refute a rigged weight, only disclosure can.

Use it / don't

Use when research is finished and needs to become decidable: a tool comparison, a market scan, a satisfaction survey, a performance review, a post-mortem.

Don't use to do the research (/deep-research, last30days, exa do that), for a single chart with no argument around it (dataviz directly), or for prose reformatting with no evidential claims (content-recast).

Flags

| Flag | Default | Meaning | |---|---|---| | --corpus <path> | — | the finished research to read | | --ir <path> | — | skip extraction, render an existing IR | | --audience | team | exec · engineer · team · client · public | | --formats | html,md,json | + pdf · pptx · xlsx (hand-off manifests) | | --design-system | auto | auto resolves from audience | | --stakes | low | high tightens the gate and expands gaps | | --out | out | output directory | | --strict | off | escalates advisory findings to failures: a missing dataviz validator, and FORM_NOT_RENDERED |

Requirements

| Need | Why | If absent | |---|---|---| | Node ≥ 18 | the renderer and both gates | hard requirement — nothing runs | | dataviz (bundled) | gate 2's validate_palette.js | loud WARN; --strict makes it fail | | od daemon (optional) | open-design templates | falls back to templates/dossier.html | | python3 (optional) | test-suite IR mutations only | those blocks self-skip |

Zero npm dependencies, by design: a decision artifact whose renderer needs a lockfile ages badly. The only third-party code involved is dataviz's validator, which ships with the CLI and is invoked, never vendored.

The pipeline

1 — Extract the IR (probabilistic; this is the judgement work)

Read the corpus and write an IR against templates/ir.schema.json. The schema is the contract; read it before writing one.

The part that takes discipline is claims[]. Each needs id, text, source, as_of, confidence — and everything downstream cites those ids. A number that appears in a chart but in no claim is unsourced by construction, which is exactly what gate 1 catches.

Rules worth internalising while extracting:

  • not_checked[] is required and non-empty. Not a formality — the blind spots are what make the rest credible. If the scope genuinely was exhaustive, say so with a reason. An empty array is a claim of omniscience and the gate rejects it.
  • Confidence is per claim, three bands. Uniform high across a large corpus earns a warning, because a real body of evidence has softer and harder parts.
  • A recommendation needs an owner and an eta. Without both it is a wish, and this format refuses to render wishes as decisions.
  • Don't fill the scorecard grid by inference. A missing cell renders as an explicit gap, which is honest; a cell invented to complete the matrix renders as evidence, which is not. → references/scorecard.md
  • Contested claims stay marked. Averaging disagreeing sources into one number is a lie of omission.

2 — Choose the form (delegate to dataviz)

Invoke the dataviz skill. Do not reinvent its guidance here — it is the authority on form and colour, and it ships with this CLI.

The two things it will tell you that are easiest to get wrong: sometimes the right form is not a chart (three numbers are a KPI row, not a bar chart), and colour comes last, after form, marks, and interaction. Its non-negotiables apply in full — never a dual axis; colour follows the entity and never its rank; categorical hues in fixed order, never cycled; a ninth series folds into "Other" rather than generating a ninth hue.

3 — Resolve the theme

--audience selects a design system and a density; the design system supplies the eight parameters dataviz consumes. → references/audience-map.md

Audience changes presentation only. It never changes the claims, the scorecard, or not_checked[]. An exec dossier and an engineer dossier from one IR contain the same evidence and pass the same gates.

4 — Render (deterministic; the gates run here)

node bin/research-dossier-render.mjs --ir <path> --formats html,md,json --out out

Exit 0 renders, 1 means a gate failed and nothing was written, 2 is usage or I/O. Both gates run before any file is created — a failed dossier produces no output at all, rather than a plausible-looking one.

pdf, pptx, and xlsx emit hand-off manifests rather than files: those formats belong to make-pdf and document-skills, and reimplementing them here would be exactly the duplication this skill exists to avoid.

Composition — what this delegates

| Concern | Owner | |---|---| | chart form, colour, marks, palette validation | the bundled dataviz skill | | html templates + design systems | open-design (~110 / ~150) | | pdf | make-pdf, or print-CSS from the rendered html | | pptx / xlsx | document-skills (fallbacks: marp-cli, csv) | | prose recasting | content-recast | | the research itself | /deep-research, last30days, exa |

What is genuinely new here is only the IR contract, the scorecard primitive, the two gates, and the audience map. Everything else is routing.

Degradation

Each of these is a tested path, not an aspiration:

  • No od daemon → the built-in templates/dossier.html fallback.
  • No bundled dataviz validator (it lives in a version-and-hash-keyed temp dir that moves on every CLI upgrade) → the path is discovered at runtime; when absent the gate degrades to a loud WARN, never a silent pass. --strict makes it a failure for CI. DATAVIZ_VALIDATOR overrides the path.
  • JavaScript off → the dossier still renders completely. The body is written server-side; JS only reveals the theme toggle. An archival decision artifact that needs a script to show its evidence is not archival.

The output

Single file, opens over file://, no network. Inline SVG charts — nothing to bundle and no canvas to hide from a screen reader. Every chart carries role="img", an aria-label describing the actual values, and a real <table> of the same numbers (per dataviz, that table is the accessible rendering, not a courtesy). Dark mode is selected rather than flipped: its own validated steps under both the OS query and the data-theme toggle, toggle winning either way. @media print and prefers-reduced-motion present. Citations are anchors into the evidence table.

Verify

node bin/research-dossier-render.mjs --ir skills/research-dossier/examples/ir-valid.json --gates-only   # 0
for f in missing-source truncated-axis dangling-refs bad-palette wish-not-decision; do
  node bin/research-dossier-render.mjs --ir "skills/research-dossier/examples/ir-$f.json" --gates-only >/dev/null 2>&1
  echo "$f -> $?"   # each must be 1
done
DATAVIZ_VALIDATOR=/nonexistent node bin/research-dossier-render.mjs --ir skills/research-dossier/examples/ir-valid.json --gates-only 2>&1 | grep WARN

Capture exit codes directly. A pipe returns the exit of the last command in it, so cmd | grep silently reports the grep's status and a failing build reads as green.

Not built (measured, not implied)

Stated here because a skill that quietly lacks a capability its callers assume is the same failure FORM_NOT_RENDERED exists to catch. Each was probed, not guessed.

| Gap | State | Why not yet | |---|---|---| | drill-down | absent | Needs dataviz references/interaction.md first, and a design that does not create a second evidence store — a drilled-into detail must be the same claim ids, gated once. | | filter controls | declared, inert | references/audience-map.md lists it as dataviz parameter #8; nothing implements it. Interaction must degrade, never gate: with JS off the dossier already renders every claim server-side, and that property is not negotiable for an archival artifact. | | burn-down | absent | No consumer. Deferred deliberately rather than built on spec. | | stat-tile demotion (<4 points) | declared, inert | references/audience-map.md self-declares it "Not yet implemented". | | yaml output | absent | --formats takes html,md,json (+ pdf/pptx/xlsx hand-offs). yaml is neither rendered nor schema-known; an unknown format is a logged UNKNOWN_FORMAT warning, not a silent skip. |

Responsive is implemented as of v0.2.0: width breakpoints at 760px and 420px (header stacks, data tables scroll rather than the page, claim ids wrap), declared before @media print so print keeps the last word.

Anti-patterns

  • Rendering before gating. The gates exist to stop output, not to annotate it.
  • Filling not_checked[] with boilerplate to satisfy the gate. It is load-bearing.
  • Truncating an axis and declaring it reflexively. The declaration is a cost, not a bypass — if there is no real rationale, don't truncate.
  • Copying dataviz guidance into this skill. Cite it; it ships with the CLI and it will be updated without us.
  • Adding a format renderer instead of a hand-off manifest.
  • Softening a dossier for an executive audience by dropping gaps or contested markers. Audience changes density, never evidence.
  • Inventing scorecard cells so the grid looks complete.

Files

| Path | What | |---|---| | templates/ir.schema.json | the IR contract | | templates/dossier.html | fallback template (server-rendered shell) | | references/scorecard.md | the comparison primitive's discipline | | references/audience-map.md | audience → design system → the 8 dataviz parameters | | examples/ir-valid.json | working fixture (dogfood: the chart-library comparison) | | examples/ir-*.json (5) | negative fixtures — each must fail the gate | | bin/research-dossier-render.mjs | renderer + both gates |