# AI Output Runtime

> Use when YOU are producing a structured, scannable artifact for humans rather than a paragraph of prose. Covers weekly / monthly / quarterly / daily reports (周报/月报/季报/日报/汇报/business review), dashboards & status sync (状态同步/盘点/概览/总结/小结/scorecard), postmortems & retros (复盘), code reviews & audits (代码审查/安全审计/审查/审计), KPI / OKR attainment (达成率/进度), financial analysis (财务分析/经营分析/P&L bridge/waterfall/variance), sales funnels & conversion (销售漏斗/转化分析), inventory / logistics / operations / customer / cohort analytics (库存/物流/运营/客户/复购分析), trend & period-over-period analysis (趋势分析/同比/环比/WoW/MoM/YoY), comparisons & selection (对比/方案选型/竞品比较/vendor evaluation/RFP), risk assessment & 2×2 matrices (风险评估/风险矩阵/RICE/BCG/likelihood × impact), heatmaps (热力图), action items & next steps (行动项/待办), and explicit mentions of AIO / AI Output Runtime / aio:name@major — plus validating or rendering AIO Markdown via the CLI. Skip for one-paragraph chat replies, code snippets, debugging help, or when the user explicitly asks for plain prose. Co

- Skill: `wxkingstar/ai-output-runtime` (Agent Skill, multi-file: 13 files)
- Install (CLI): `npx skillmds@latest add wxkingstar/ai-output-runtime`
- Raw SKILL.md: https://api.skillmd.com/api/skills/wxkingstar/ai-output-runtime/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: wxkingstar (https://skillmd.com/u/wxkingstar)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/wxkingstar/ai-output-runtime

---


# AI Output Runtime

Emit AIO Markdown whenever you produce **structured content for human consumption** — reports, comparisons, conclusions, status panels, audit findings — not just when the user names AIO explicitly. Markdown carries the prose; schema-validated JSON blocks carry the structure; a safe runtime renders it all into a polished single-file HTML report. You never write HTML, CSS, or JavaScript.

## Output Policy

**Default to AIO blocks for these shapes:**

- Conclusion / recommendation / verdict → `aio:callout@1`
- Comparison of options, vendors, or scenarios → `aio:table@1` or `aio:comparison@1`
- Key metrics / status indicators → `aio:metric-cards@1` (static) or `aio:trend-card@1` (when change matters)
- Risk / finding / issue lists → `aio:callout@1` items or `aio:table@1`
- Trend / proportion → `aio:chart@1` · KPI attainment → `aio:gauge@1` · conversion → `aio:funnel@1` · P&L bridge → `aio:waterfall@1` · 2D density → `aio:heatmap@1` · 2×2 prioritization → `aio:matrix@1` · chronology → `aio:timeline@1` · follow-ups → `aio:action-items@1` · per-unit state → `aio:status-grid@1`

**Plain Markdown only when** the reply is conversational Q&A, a code snippet / debugging help, or the user asked for plain prose. In chat/TTY surfaces that may not render AIO, still emit AIO for structured shapes — it reads as a JSON code block and renders perfectly when later piped through `aio render`.

**Report skeleton** for a structured business report: frontmatter (or `report-header@1`) → 1–2 framing sentences → `trend-card@1` headline metrics → `status-grid@1` if sub-units have separate state → detail blocks (`chart` / `table` / `heatmap` / `funnel` / `waterfall` / `matrix`) → `timeline@1` for what happened → `comparison@1` for decisions → `action-items@1` → closing `callout@1` verdict.

A good AIO report is **prose + structured slots**, not raw JSON walls: never emit a block without surrounding prose, and if you have more than two blocks in a row with no prose between them, restructure.

### Emission economy (saves tokens; rendered output is identical)

- **Emit block JSON compact**: no indentation, no space after `:` or `,`, at most one `items[]` / `rows[]` / `series[]` element per line — that last rule keeps raw JSON scannable on surfaces that show it. Formatting never survives `JSON.parse`.
- **Prose carries the why — cause, decision, anomaly — never restate figures the adjacent block already shows.**
- **The runtime auto-inserts a collapsible Executive Summary card** at the top, built from the FIRST `aio:callout@1` in the document plus the first 4 metric/trend values. Never hand-write a summary section; make your first callout carry the report's takeaway instead. Disable with frontmatter `summary: off`.
- **Omit every field the runtime defaults or derives**: `trend-card` delta `direction` when it matches the sign of `delta.value`; `gauge` `min` / `max` / `target` at their defaults and `tone` unless overriding the auto tone; `format:"number"` anywhere (it is the default); `heatmap` `tone:"accent"`; `matrix` bounds at 0/10; funnel step/overall conversion % (auto-computed — don't put them in `note`); `waterfall` `kind` on start/up/down bars and `value` on explicit `subtotal`/`end` bars. Tones on `status-grid` / `timeline` / `matrix` items are data, not decoration — keep those.
- **Pure data tables can use the CSV body** (`aio:table@1:csv`, ~20–30% fewer bytes than minified JSON) when no `title` / `subtitle` / `caption` / `filterable` is needed. Prefer the JSON body when the rendering toolchain may predate v0.6.0 — older runtimes degrade `:csv` silently to a plain code block.

## Hard Rules

- Info strings: `aio:name@major` (JSON body), plus `aio:table@1:csv` (CSV body, table only).
- JSON bodies must be valid JSON — no comments, no trailing commas.
- No HTML, CSS, JavaScript, iframes, style attributes, event handlers, template expressions, or custom components. No `<` or `>` inside AIO string fields.
- Components in scope — **stable**: `table@1` / `metric-cards@1` / `callout@1`; **candidate**: `chart@1` / `trend-card@1` / `status-grid@1` / `report-header@1` / `timeline@1` / `action-items@1` / `comparison@1` / `gauge@1` / `funnel@1` / `waterfall@1` / `heatmap@1` / `matrix@1`. Never invent component names; content that fits nothing (diagrams, code diffs, …) stays plain Markdown.

## Components

The stable trio, in exactly the emission style to imitate:

```aio:metric-cards@1
{"title":"Release health","items":[
{"label":"Recommendation","value":"Adopt","note":"Fits stable scope","tone":"good"},
{"label":"Blockers","value":"0","tone":"neutral"}]}
```

```aio:callout@1
{"tone":"success","title":"Final recommendation","body":"Adopt AIO for structured reports.","items":["Ship v1 next sprint"]}
```

`callout` needs `title` plus `body` and/or `items` — `body` may be omitted when `items` is non-empty. `tone`: info / success / warning / danger.

```aio:table@1
{"title":"Options","columns":["Option","Benefit","Risk"],"rows":[
["Markdown","Readable","Weak interaction"],
["AIO","Validated structure","Requires runtime"]]}
```

CSV body for pure data tables — header row becomes `columns`; quote cells containing `,` or `"` (escape embedded quotes by doubling); unquoted `true` / `false` / number tokens are typed; raw newlines inside cells are forbidden:

```aio:table@1:csv
Option,Score,Verdict
Markdown,7,Readable
"AIO, validated",9,Recommended
```

**Frontmatter shortcut**: a YAML frontmatter block (`title` / `subtitle` / `period` / `author` / `status` / `data-as-of` / `classification` / `badges`) at the very top of the document is automatically turned into an `aio:report-header@1` — don't also write the block.

### Candidate components — key reference

The CLI **rejects any key not listed here** (`?` = optional). For edge-case semantics read `SKILL_DIR/specs/<name>-v1.md` before emitting anything beyond this table.

| Component | Use for | Keys |
|---|---|---|
| `chart@1` | line / bar / area / pie / donut | `type`, `title?`, `subtitle?`, `caption?`, `xLabel?`, `yLabel?`; line/bar/area: `x` (≤50) + `series` (≤6) of `{name?,data,tone?}` where every `data.length == x.length`; pie/donut: `slices` (≤12) of `{label,value,tone?}`, values ≥ 0. Numbers must be finite. No stacked/scatter/log — fall back to a table. Omitted `name` falls back to the chart title (single series) |
| `trend-card@1` | metrics + delta + sparkline | `title?`, `asOf?`, `items` (≤8) of `{label,value,format?,delta?,spark?,tone?,note?}`; `delta`: `{value,direction?,format?,label?}`; `spark`: 2–60 finite numbers |
| `status-grid@1` | green/yellow/red state per unit | `title?`, `asOf?`, `items` (≤12) of `{label,status,value?,note?}`; `status`: good / warn / bad / neutral / info |
| `report-header@1` | formal report hero | `title`, `subtitle?`, `period?`, `author?`, `status?` (draft/review/final/archived), `dataAsOf?`, `classification?` (public/internal/confidential/restricted), `badges?` (≤6) of `{label,tone?}` (tone: info/good/warn/bad/neutral) |
| `timeline@1` | chronological events | `title?`, `asOf?`, `items` (≤30) of `{time,title,body?,tone?}`; rendered in source order, never sorted |
| `action-items@1` | owned follow-ups | `title?`, `asOf?`, `items` (≤30) of `{task,owner?,due?,status?,priority?}`; `status`: todo/doing/done/blocked; `priority`: P0–P3; overdue items auto-highlight |
| `comparison@1` | options × criteria decision matrix | `title?`, `subtitle?`, `asOf?`, `options` (2–6), `recommended?` (must match one option), `criteria` (≤14) of `{label,values,weight?,tone?}`; every `values.length == options.length` |
| `gauge@1` | KPI / OKR attainment arcs | `title?`, `asOf?`, `items` (≤8) of `{label,value,target?,min?,max?,format?,tone?,note?}`; defaults: min 0, max 100 (1 when `format:"percent"`), target = max, tone auto from value/target |
| `funnel@1` | sequential conversion | `title?`, `subtitle?`, `asOf?`, `format?`, `stages` (2–8) of `{label,value,tone?,note?}`; values monotonic non-increasing; step + overall % auto-computed |
| `waterfall@1` | P&L bridge / variance / attribution | `title?`, `subtitle?`, `asOf?`, `format?`, `bars` (2–14) of `{label,value?,kind?,note?}`; omit `kind` on start/up/down bars (derived from position and sign), omit `value` only on explicit `kind:"subtotal"` / `"end"` bars (derived running total); never omit both |
| `heatmap@1` | 2D density grid | `title?`, `subtitle?`, `asOf?`, `format?`, `tone?`, `xLabels` / `yLabels` (≤32 each), `rows` of finite numbers matching y×x |
| `matrix@1` | 2×2 quadrant plot | `title?`, `subtitle?`, `asOf?`, `xLabel?`, `yLabel?`, `xMin?` / `xMax?` / `yMin?` / `yMax?` (default 0–10), `quadrants?` (exactly 4), `items` (≤24) of `{label,x,y,tone?,note?}` |

`format` fields accept `raw` / `number` / `percent` / `compact` / `currency:CCY` (ISO 4217).

## Rendering behavior worth knowing

- Numbers in number-bearing components are locale-formatted via `Intl.NumberFormat` from the document `lang` — with no `format`, numbers get grouped thousands separators (`13,620`, not `13620`). Pass the right `--lang` (`en` / `zh-CN` / `ja`) so currency separators and decimal marks render correctly. Use `format:"raw"` only when you explicitly want no formatting.
- The visual system is business-grade out of the box — 1800px wide-screen shell, phone reflow for every component, sticky TOC, per-block CSV export, strict Light/Dark toggle. Nothing to configure.
- Fenced code blocks get dark chrome, a Copy button, and syntax highlighting automatically: `json` / `bash` / `js` / `ts` / `python` / `yaml` / `diff` are built in, 24 more languages (go, rust, java, sql, …) lazy-load from CDN. `--inline-runtime` artifacts stay offline-friendly — the CLI inlines every language module the markdown actually uses. Unknown languages fall back to plain monospace, never an error.

## Local Tools

Validate, then render:

```bash
node SKILL_DIR/scripts/aio.mjs validate report.md
node SKILL_DIR/scripts/aio.mjs render report.md --lang zh-CN
```

By default the rendered HTML references the runtime via jsDelivr CDN, so the file is portable:

```html
<script src="https://cdn.jsdelivr.net/gh/wxkingstar/ai-output-runtime@v0.6.0/assets/ai-output-runtime.js"></script>
```

Useful render flags:

- `--inline-runtime` — offline / `file://`-friendly artifact (runtime + used language modules inlined).
- `--out <file.html>` — output path (defaults next to the source).
- `--theme dark` — start in Dark (default is Light).
- `--expire-hours 72` — sticky countdown banner for artifacts delivered as time-limited download links.
- `--runtime <url|path>` — custom CDN or local runtime override.

Prefer the CDN or `--inline-runtime` over copying `SKILL_DIR/assets/ai-output-runtime.js` by hand.

