report-render

A governed artifact in the stable bundle. Replacing this file on your machine makes your next attestation come back Drifted, naming this path.

DescriptionRender a STANDALONE visual artifact to the visual-bar standard — interactive HTML (Plotly charts + vis-network graphs) PLUS a branded PNG companion, phone-readable by default. Use when the deliverable is chart/graph/visual-shaped: 'visualize X', 'show me X as a chart/heatmap/graph', 'plot this data', 'dependency graph of X', 'render/build the HTML+PNG', 'the visual-bar companion' — explicit HTML+PNG wording NOT required. PNG is NOT optional, Mermaid is REJECTED, phone-first layout is the default. NOT for PM planning/roadmaps/boards, NOT for web-app dashboard pages (that is app work), NOT for prose reports (docx), decks (pptx), or spreadsheets (xlsx).
Argumentsnone
Tools it may use[Read, Write, Edit, Bash]
Installs atskills/report-render/SKILL.md
Mode0644 — never executable
Size10595 bytes
SHA-256aab06185407efe01f669eb51e0ec84b30204c4e02f52fbfb471bc01b31919fb7
fetch the raw bytes

The complete file, verbatim

This is the entire SKILL.md, including its YAML frontmatter: byte-identical to what installs, not rendered and not reformatted. Hash exactly what is below and you get the digest above. The table is only a reading aid; this block is the artifact.

---
name: report-render
description: Render a STANDALONE visual artifact to the visual-bar standard — interactive HTML (Plotly charts + vis-network graphs) PLUS a branded PNG companion, phone-readable by default. Use when the deliverable is chart/graph/visual-shaped: 'visualize X', 'show me X as a chart/heatmap/graph', 'plot this data', 'dependency graph of X', 'render/build the HTML+PNG', 'the visual-bar companion' — explicit HTML+PNG wording NOT required. PNG is NOT optional, Mermaid is REJECTED, phone-first layout is the default. NOT for PM planning/roadmaps/boards, NOT for web-app dashboard pages (that is app work), NOT for prose reports (docx), decks (pptx), or spreadsheets (xlsx).
allowed-tools: [Read, Write, Edit, Bash]
---

# report-render — the visual-bar engine

Turns the visual-bar standard from prose-rebuilt-by-hand-every-time into one repeatable
render.

**Why it exists (measured):** an audit of our own session corpus found **73** separate
report-shaped deliverables, each one re-deriving the palette, re-inventing the dual-output
step, and each one slightly different from the last. The variance was the cost, not the
effort. **This skill is the engine that makes the visual-quality gate cheap for every other
skill** — once it exists, no other workflow has to relitigate what "a good report" means.

---

## Phone-first is the DEFAULT layout

Assume your reader opens the deliverable on a phone. Every artifact, unless the requester
names a desktop-only context:

- single-column flow
- base font >= 15px
- tap targets >= 40px
- charts full-width with `responsive: true`
- tables that reflow, or scroll inside their own container (never force the page to scroll
  sideways)
- no hover-only affordances — every tooltip needs a tap equivalent
- the PNG companion legible at 400px wide

**If the desktop layout and the phone layout fight, the phone wins.** We learned this the
expensive way: this preference was re-stated roughly fifteen times across the corpus before
anyone wrote it down as a default. Every restatement was a deliverable that had to be redone.

---

## The standard — non-negotiable

1. **Two artifacts, always.** An **interactive HTML** (Plotly for charts; vis-network for
   entity / dependency graphs) **AND** a **static branded PNG** companion. The PNG is **not
   optional** — a deliverable without it fails the bar. Rationale: the HTML is for
   exploration, the PNG is for the places a deliverable actually gets consumed — chat, a
   message thread, a slide, a phone lock screen. An HTML-only artifact is invisible in half
   its destinations.
2. **Mermaid is rejected.** Network and relationship views use vis-network. Mermaid's layout
   is not controllable enough to hit the bar, and its output is not brandable.
3. **One branded palette** on both artifacts (see below). Same colors, same semantics,
   every time.
4. **Single-file HTML with CDN dependencies** — data and layout inline, libraries from CDN.
   Opens by double-click **when online**. Be honest that this is **not** truly
   self-contained: no network, no charts. If the artifact must work offline, vendor the JS
   locally; otherwise **state the CDN dependency in the handoff** rather than letting the
   recipient discover it on a plane.

---

## Palette

This is our dark palette. If you are adopting this skill at another organization, swap these
values for your own brand — the *rule that matters* is that there is exactly one palette and
every artifact uses it.

```
BG     #0f1420   PANEL  #1a2233   INK   #eceef5   MUTE  #8e8e93
CYAN   #00e5ff   BLUE   #448aff   GREEN #00e676   AMBER #ffd740
ORANGE #ff6e40   RED    #ff1744

CYAN_SCALE  [0]#10151f [.15]#143049 [.4]#1f5fae [.65]#2f8fd6 [.85]#00c2e0 [1]#aef2ff
AMBER_SCALE [0]#1a1410 [.2]#3d2a12 [.5]#9c5410 [.75]#ff8c00 [.9]#ffb300 [1]#ffe27a
```

Two sequential scales, not one: CYAN for "more is neutral or good" (volume, coverage,
throughput), AMBER for "more is heat" (cost, risk, load). Picking the scale is a semantic
decision, not a decorative one.

---

## The reference pattern — mirror it, don't reinvent

Keep **one** working generator in your repo and mirror its structure for every new report.
Ours is a heatmap generator; the shape generalizes. Write a small generator that emits both
artifacts from the same data structure in one run:

```python
# report_gen.py — the shape every report generator should have
import plotly.graph_objects as go
import matplotlib
matplotlib.use("Agg")            # headless: no display server needed
import matplotlib.pyplot as plt

PALETTE = {...}                  # the block above, one source of truth

def load() -> dict:
    """Read the data. Keep this the only I/O-shaped function."""

def normalize(data: dict) -> dict:
    """Per-row normalization so one hot row doesn't flatten every other row."""

def render_html(data, out_path: str) -> None:
    fig = go.Figure(...)
    fig.update_layout(
        paper_bgcolor=PALETTE["BG"], plot_bgcolor=PALETTE["PANEL"],
        font=dict(color=PALETTE["INK"], size=15),
        margin=dict(l=8, r=8, t=48, b=8),       # phone-first: minimal gutters
    )
    fig.write_html(out_path, include_plotlyjs="cdn",
                   config={"responsive": True, "displayModeBar": False})

def render_png(data, out_path: str) -> None:
    """A separate render, NOT a screenshot of the HTML."""
    fig, ax = plt.subplots(figsize=(8, 4.5), dpi=160)
    fig.patch.set_facecolor(PALETTE["BG"])
    ...
    fig.savefig(out_path, facecolor=PALETTE["BG"], bbox_inches="tight")
```

**Per-row normalization matters.** A single dominant row will otherwise wash every other row
out into the same near-black cell, and the chart ends up saying nothing.

**PNG export: prefer matplotlib.** Plotly + kaleido also works if installed, but kaleido
pulls a headless-Chrome dependency that fails silently in containers and on locked-down
build hosts. matplotlib has no such dependency. Choosing matplotlib removed an entire class
of "the file exists but is 0 bytes" failures for us.

---

## Build flow

1. **Lead with the ONE headline.** Before any chart: what is the single insight this report
   exists to surface? Put it at the top, big. The visuals support the headline; they are not
   a substitute for having one. A report that opens with a chart and no claim makes the
   reader do the analysis you were supposed to do.
2. **Render the two artifacts.** Write the generator, run it, produce `<name>.html` +
   `<name>.png`. If your environment has an artifact-publishing hook or a standard place
   these are meant to land, register the output with it as the last step.
3. **Low cognitive load.** One screen = one idea. Don't cram; split into sections.
4. **Surface the result, don't bury it.** Print the paths prominently, or attach the PNG
   directly. The visual is the payoff — burying it three paragraphs into a wall of prose
   defeats the entire exercise.

---

## Honest output

Report the **exact paths** of both artifacts.

If the PNG export **fails** (missing export backend, plotting error, empty frame), say
**"PNG: FAILED — <reason>"** and do **not** claim the bar is met. HTML-only is an incomplete
deliverable and gets reported as such — never silently.

**PNG visual QA:** after export, actually **open and inspect** the PNG. Confirm it rendered —
not blank, not clipped, not garbled, axis labels not overlapping into mush. Confirming that
*a file exists* is not confirming that *an image is correct*; those are different claims, and
only one of them is what the reader cares about. If visual QA was not run, report
**"PNG QA: NOT-RUN"**. A check that did not run is NOT-RUN — never a silent skip, and never
downgraded to a pass.

---

## NOT this skill

- Word doc -> a document-authoring skill · slides -> a deck skill · spreadsheet -> a
  spreadsheet skill
- Generative / algorithmic art -> an art skill
- Just *reading* a chart someone else made -> native file read
- Roadmaps, boards, and PM planning artifacts -> your PM planning skill
- Pages inside a live web app -> that is application work, not artifact rendering

---

## Definition-of-Done self-check (honest)

This is the shape of the DoD we hold skills to. It is reported as-is, including what did not
run — the honesty is the point, and a self-check that only ever reports MET is not a check.

1. **Trigger vocabulary** — **MET** (narrowed after review). Scoped vocab (render HTML+PNG,
   chart / graph / heatmap, plot data, visual-bar companion) plus explicit negatives (PM
   planning, app/dashboard build, docx/pptx/xlsx, art, review-only).
2. **allowed-tools scoped** — **MET.** `Read` (data + the reference generator), `Write`/`Edit`
   (generator + artifacts), `Bash` (run the render). No MCP access, no blanket extras.
3. **Cites its reason** — **MET.** 73 report-shaped deliverables in the corpus; the standing
   visual-bar rule in team memory; it is the enabler for the visual-quality gate in every
   other skill.
4. **Grounded facts** — **MET** (verified). The palette and the dual-output
   Plotly-HTML + matplotlib-PNG approach are lifted from a generator that actually runs, not
   invented for the write-up.
5. **Honest reporting** — **MET.** PNG-FAILED is reported explicitly; HTML-only never passes
   the bar silently; PNG QA has an explicit NOT-RUN state.
6. **Trigger-fire validation** — **NOT-RUN** (needs live routing in a fresh session). Prompts
   defined: (1) "visualize this cost breakdown" -> should fire; (2) "render the sprint board
   as a report" -> should fire; (3) "make me a dependency graph of these services" -> should
   fire, vis-network path. Negatives that must NOT fire: "make a slide deck of this",
   "put this in a spreadsheet". **Run before shipping.**
7. **Visual bar** — **DESIGNED; NOT-RUN.** It *is* the visual bar by design, but no sample
   artifact has been generated and QA'd. The fire-test must produce one small sample
   HTML+PNG and visually QA the PNG before this flips to MET.
8. **Reader fit** — **MET.** Designed for a time-poor reader who is context-switching and
   usually on a phone: leads with the one headline insight, one-screen-one-idea, honest on
   failure, and the visual itself is the reward for reading. Litmus test: a well-branded
   report should surface the next step at a glance — it should make the reader lean in, not
   close the laptop.

**Open item:** trigger-fire validation. Run the three fire prompts above, generate one small
sample HTML+PNG, and eyeball the PNG before you rely on this skill in your own project.

All skills · Back to the overview