render-html¶
paper-drafting/render-html: Markdown → single-file HTML for human reading¶
Markdown is for writers. HTML is for readers. ARIS workflow nodes write Markdown (canonical, audit-trail-friendly, machine-parseable).
/render-htmlturns selected artifacts into a polished single-file HTML view for the human who actually has to read them. The Markdown stays the source of truth.
When to use this skill¶
Use /render-html for ARIS artifacts that have a real human reader:
| Artifact | Why HTML helps | Template |
|---|---|---|
idea-stage/IDEA_REPORT.md |
Ranked ideas + pilot signal + scores feel like a decision dashboard, not a flat list | academic |
review-stage/AUTO_REVIEW.md (+ REVIEW_STATE.json) |
Round-by-round score progression + weakness status; pass --state to embed the JSON |
academic |
paper/KILL_ARGUMENT.md (+ KILL_ARGUMENT.json) |
Per-point attack/defense with <details> Q&A cards + red/yellow/green callouts |
academic |
research-wiki/SUMMARY.md (or research-wiki/index.md) |
Cross-entity cockpit: papers / ideas / experiments / claims at a glance | dashboard |
PAPER_PLAN.md (optional) |
Claims-evidence matrix renders better as a polished table than raw MD | academic |
RESUBMIT_REPORT.{md,json} (optional) |
7-state failure-mode ledger | academic or dashboard |
Do NOT use for:
- LaTeX paper output — the final reader-facing artifact is PDF, not HTML.
- SKILL.md files — those are internal LLM-facing protocol.
- .aris/traces/* review traces — forensic debug, not human display.
- Every Markdown file in your project — only artifacts that benefit from sticky TOC, callouts, math, or score progressions.
Core invariants¶
- MD / JSON is canonical, HTML is generated view. Edit the source, then re-render. Do not hand-edit the HTML.
- Cross-model review at the artifact boundary (ARIS invariant). Academic-template HTML — used for the artifacts humans actually read (IDEA_REPORT, AUTO_REVIEW, KILL_ARGUMENT, PAPER_PLAN) — is reviewed by a fresh cross-family Codex thread before being claimed as a finished view. Dashboard-template HTML (cockpit / debug views) skips review by default but accepts
--reviewto force it. See § HTML Review Gate below. - Drift detection. Every rendered HTML embeds the source path, SHA256, and generation timestamp in
<meta>tags AND in the visible page header. If the HTML and source diverge, the meta tells you which version of the source produced it. - Single-file output. No build system, no separate CSS, no
node_modules. Just one.html. - CDN-friendly default,
--offlinefallback. MathJax 3 and highlight.js load fromcdn.jsdelivr.netby default. Pass--offlineto skip both — math will appear as raw$x$, code blocks won't get syntax highlighting, but everything stays readable. - Pure stdlib helper.
render_html.pyuses onlyre,html,hashlib,json,datetime,pathlib,argparse,sys. No pip install required. - Defense-in-depth XSS sanitization. The helper strips
<script>/<style>/<iframe>/<object>/<embed>/<form>/<input>/<button>/<link>/<meta>/<base>tags, allon*event-handler attributes (onclick,onload, …), and rewritesjavascript:/vbscript:/data:href/src/action schemes to#blocked-unsafe-url:. ARIS workflow artifacts should not contain these in the first place, but the sanitizer is the safety net in case an LLM hallucinates one. Markdown text content is HTML-escaped separately and never reaches the sanitizer.
Tool Location¶
Arch C self-contained: the canonical implementation lives at skills/render-html/scripts/render_html.py (this SKILL's own scripts/ subdirectory), together with its templates at skills/render-html/scripts/templates/{academic,dashboard}.html. The helper is new — no legacy tools/ shim exists.
Resolve $RENDER_HTML with the hybrid chain (Layer 0 prefers the self-contained location for the owning SKILL; Layers 1-3 are the shared-runtime chain documented in shared-references/integration-contract.md §2, Policy A — skill-local gate):
## Layer 0: self-contained (CC 1.0+ exposes $CLAUDE_SKILL_DIR).
RENDER_HTML=""
if [ -n "${CLAUDE_SKILL_DIR:-}" ] && [ -f "$CLAUDE_SKILL_DIR/scripts/render_html.py" ]; then
RENDER_HTML="$CLAUDE_SKILL_DIR/scripts/render_html.py"
fi
## Layers 1-3: shared-runtime chain (non-CC hosts + manual installs).
if [ -z "$RENDER_HTML" ]; then
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)" || exit 1
if [ -z "${ARIS_REPO:-}" ] && [ -f .aris/installed-skills.txt ]; then
ARIS_REPO=$(awk -F'\t' '$1=="repo_root"{print $2; exit}' .aris/installed-skills.txt 2>/dev/null) || true
fi
RENDER_HTML=".aris/skills/render-html/scripts/render_html.py"
[ -f "$RENDER_HTML" ] || RENDER_HTML="skills/render-html/scripts/render_html.py"
[ -f "$RENDER_HTML" ] || { [ -n "${ARIS_REPO:-}" ] && RENDER_HTML="$ARIS_REPO/skills/render-html/scripts/render_html.py"; }
[ -f "$RENDER_HTML" ] || RENDER_HTML=""
fi
[ -z "$RENDER_HTML" ] && {
echo "ERROR: render_html.py not resolved (layer 0: \$CLAUDE_SKILL_DIR/scripts/; layers 1-3: .aris/skills/render-html/scripts/, skills/render-html/scripts/, \$ARIS_REPO/skills/render-html/scripts/)." >&2
echo " /render-html cannot produce HTML output. Fix: rerun bash tools/install_aris.sh, or copy from \$ARIS_REPO/skills/render-html/scripts/." >&2
exit 1
}
Invocation¶
## Default: academic template, output to <input>.html alongside source
python3 "$RENDER_HTML" idea-stage/IDEA_REPORT.md
## Dashboard template for cockpit-style views
python3 "$RENDER_HTML" research-wiki/SUMMARY.md --template dashboard
## Custom output path + title + eyebrow
python3 "$RENDER_HTML" review-stage/AUTO_REVIEW.md \
--out review-stage/AUTO_REVIEW.html \
--title "Auto Review — overnight run" \
--eyebrow "Workflow 2"
## Embed sidecar state JSON (rendered as a folded <details> JSON block at end)
python3 "$RENDER_HTML" review-stage/AUTO_REVIEW.md \
--state review-stage/REVIEW_STATE.json
## Embed sidecar JSON (e.g., for KILL_ARGUMENT.md + KILL_ARGUMENT.json)
python3 "$RENDER_HTML" paper/KILL_ARGUMENT.md \
--json paper/KILL_ARGUMENT.json \
--title "Kill Argument — adversarial review"
## Offline (no CDN; math + code render as plain text)
python3 "$RENDER_HTML" idea-stage/IDEA_REPORT.md --offline
## JSON-only input (wrapped in a <pre><code class="language-json"> block)
python3 "$RENDER_HTML" review-stage/REVIEW_STATE.json --template dashboard
## Language attr (default zh-CN; set for English-primary artifacts)
python3 "$RENDER_HTML" docs/SKILLS_CATALOG.md --lang en
## Skip review (academic template otherwise reviews by default)
python3 "$RENDER_HTML" idea-stage/IDEA_REPORT.md
## … then the skill skips the mcp__codex__codex review step if --no-review
## was on the command line. Pass --review to a dashboard render to force it.
The --review / --no-review flags are parsed by the SKILL orchestrator
(Claude Code), not by render_html.py. The helper itself stays pure
stdlib and never calls MCP. See § HTML Review Gate below for the
exact resolution and prompt.
Workflow¶
Step 1: Identify the artifact¶
From $ARGUMENTS, determine what to render. Common patterns:
- "render IDEA_REPORT" →
idea-stage/IDEA_REPORT.md, academic template - "make AUTO_REVIEW readable" →
review-stage/AUTO_REVIEW.md+--state review-stage/REVIEW_STATE.json - "show the kill argument as HTML" →
paper/KILL_ARGUMENT.md+--json paper/KILL_ARGUMENT.json - "research-wiki dashboard" → look for
research-wiki/SUMMARY.mdor generate from raw entity counts (Phase 2 work; for Phase 1 just renderSUMMARY.mdif present, else fall back to listing top-level wiki structure)
Step 2: Pick template¶
academic(default): linear long-form. Sticky TOC sidebar + serif body + callouts + tables + math + Q&A<details>. Use for IDEA_REPORT, AUTO_REVIEW, KILL_ARGUMENT, PAPER_PLAN.dashboard: grid layout, smaller font, denser metrics cards, no TOC sidebar. Use for research-wiki cockpit, RESUBMIT_REPORT, multi-project overviews.
Step 3: Run the helper¶
Use the resolver above to get $RENDER_HTML, then invoke. The script writes the HTML alongside the source by default (or wherever --out says) and prints a one-line confirmation including the source SHA256 prefix.
Step 4: HTML Review Gate (cross-model)¶
Decide whether to run review. Per ARIS invariant "executor must not judge its own output", the academic-template HTML is reviewed by a fresh cross-family Codex thread before being claimed as a delivered view. Resolution:
should_review = explicit --review present
or (template == "academic" and --no-review NOT present)
So:
--template academic(default) → review by default. Skip with--no-review.--template dashboard→ no review by default. Force with--review.- Phase 2 workflow auto-emit (planned) follows the same rule.
If should_review is true, fire a fresh mcp__codex__codex thread (NEVER codex-reply) with the prompt below. The reviewer reads the source MD + generated HTML directly; it does not see this skill's intermediate state.
Scope of review (narrow on purpose). The HTML reviewer audits render fidelity / safety / structure only — not claim truthfulness. Claim audit belongs upstream (/paper-claim-audit, /research-review, /result-to-claim). Specifically the reviewer checks:
- Information fidelity — every section, claim, table, code block, list, math snippet, sidecar payload is present in the HTML (no silent drop)
- Structural integrity — heading hierarchy / nested lists / table cells / code fences /
<details>blocks / math delimiters survive parsing - Callout routing —
> 🚨 …got.callout-bad,> 💡 …got.callout-info, etc. - Safety / escaping — no raw
<script>/onclick=/javascript:/ unreplaced placeholders / template-leak survives - Expected-difference allowance — frontmatter strip, generated header/footer/meta, TOC insert, sanitized unsafe HTML are all expected, not flagged
Codex prompt (mandatory shape). Send this as a fresh thread (mcp__codex__codex, NOT codex-reply):
You are an independent ARIS HTML render auditor. This is a fresh review thread.
Read these files directly:
- Source artifact: <ABS path to source.md or source.json>
- Generated HTML: <ABS path to out.html>
- Optional sidecars: <state.json>, <kill_argument.json> (if any)
Task: Audit whether the generated HTML is a faithful, safe, structurally
usable view of the source artifact. Do NOT judge whether the research
claims are true. Judge only rendering fidelity.
Checks:
1. Information fidelity — sections / claims / tables / code blocks /
lists / math / sidecars not silently dropped or materially altered.
2. Structural integrity — heading hierarchy, tables, nested lists,
code fences, details/summary, math delimiters preserved.
3. Callout routing — warning/critical/good/info blockquotes map to
appropriate CSS classes when present.
4. Safety/escaping — no unexpected raw script/style/iframe/form/
event-handler/javascript/data URL survives from source.
5. Placeholder/template leakage — no unreplaced {{PLACEHOLDER}} or
parser private placeholder character appears.
6. Expected differences — frontmatter strip, generated header/footer/
meta, TOC insertion, sanitized unsafe HTML are EXPECTED and not a
defect.
Return STRICT JSON first, then a short prose note:
{
"verdict": "PASS|WARN|FAIL|ERROR",
"checks": {
"source_hash_match": "pass|warn|fail|unknown",
"information_fidelity": "pass|warn|fail",
"structure": "pass|warn|fail",
"math_code_tables": "pass|warn|fail",
"callouts": "pass|warn|fail|not_applicable",
"safety_escaping": "pass|warn|fail",
"placeholder_leak": "pass|warn|fail"
},
"blocking_issues": [
{"severity": "fail",
"source_location": "L<n>...",
"html_location": "<selector or near-text>",
"issue": "...",
"suggested_fix": "..."}
],
"warnings": [
{"severity": "warn", "issue": "...", "suggested_fix": "..."}
],
"summary": "one paragraph"
}
Verdict rules:
- PASS: no material fidelity/safety issue.
- WARN: readable output with minor unsupported-Markdown or cosmetic
degradation only.
- FAIL: missing/altered meaningful content, broken tables/math/code/
callouts that change interpretation, unsafe executable HTML, source
hash mismatch, or placeholder leakage.
- ERROR: files could not be read or audit could not complete.
Save outputs:
- Write the JSON verdict to
<out_path>.review.json(sibling to the HTML). - Save the raw codex trace to
.aris/traces/render-html/<YYYY-MM-DD>_run<NN>/review.{txt,json}pershared-references/review-tracing.md. - Print a one-line summary to the user:
verdict, N blocking, N warnings, trace: <path>.
If verdict == FAIL: the HTML is NOT a delivered review-passed view. Tell the user the blocking issues, point them at the source (fix MD or template, not the HTML), and re-render. Do not silently overwrite or mark as complete.
If verdict == WARN: deliver the HTML but surface the warning list. User decides whether to fix or accept.
If mcp__codex__codex is not available (e.g., user runs /render-html on a Codex-CLI-only setup where Codex MCP isn't wired): emit verdict: REVIEW_UNAVAILABLE to the sidecar, do not fabricate PASS, and tell the user the HTML was generated but not independently reviewed. The user can manually invoke /research-review on the source MD or re-run with Codex MCP available.
Step 5: (Optional) Verify in browser¶
open <out.html> on macOS, xdg-open on Linux, start on Windows. Math + code highlighting need internet (CDN); offline mode degrades gracefully to readable text.
What the helper supports¶
Markdown subset (chosen to match what ARIS workflows actually emit):
- Headings
#/##/###/####with auto-generated IDs for TOC - Paragraphs, bold
**x**, italic*x*/_x_, inline code`x`, strikethrough~~x~~, linkst, images!a - Unordered/ordered lists with 2-space nested indentation
- Code blocks with optional language (
```python) — gets<pre><code class="language-python">for highlight.js - ASCII-art code blocks (heuristic: many box-drawing chars) →
<pre class="diagram">with a distinct cream-yellow background - Tables with
:---/:---:/---:alignment - Blockquotes
> ...— emoji-prefix detection routes to callout variants: ⚠️→.callout-warn(Warning)💡/📝→.callout-info(Tip / Note)✅/🔒→.callout-good(OK / Guarantee)❌/🚨→.callout-bad(Blocked / Critical)- HTML passthrough for
<details>,<summary>,<div>,<figure>,<table>,<section>, etc. (block-level) — used for the existing Round-1/2/3 fix details inKILL_ARGUMENT.md - LaTeX math: inline
$x$and display$$x$$pass through verbatim to MathJax
What the helper does NOT support (intentional simplifications):
- Footnotes (
[^1]) - Definition lists
- Reference-style links
[label][ref] - Setext-style headings (
===/---underline form — ARIS doesn't emit these)
Frontmatter (--- ... ---) at the very top of the file is stripped before rendering (SKILL.md frontmatter style); the body that follows is what gets converted.
Two-phase integration plan (Phase 1 is this skill; Phase 2 is workflow hooks)¶
Phase 1 (this skill, currently shipped): Opt-in only. Users explicitly call /render-html <artifact.md> after a workflow completes. No existing skill changes. Lets us validate which HTML views are actually useful before automating.
Phase 2 (later, planned): Selectively auto-emit HTML at workflow termination:
/idea-discoverycompletes → also writesidea-stage/IDEA_REPORT.html(academic)/auto-review-loopterminates → also writesreview-stage/AUTO_REVIEW.html(academic, with--state)/kill-argumentalready writes.md + .json→ naturally extends to+ .html/research-pipelinefinal report links to the HTML files above (doesn't re-render)/research-wiki rendersubcommand generatesresearch-wiki/index.htmldashboard/paper-writingdoes not auto-emit HTML (the final reader artifact is PDF)
Phase 2 will be guarded by a — html: true flag in each affected skill, defaulting to false until we have empirical evidence the HTML views are read.
Customizing the templates¶
The two templates live at skills/render-html/scripts/templates/{academic,dashboard}.html. Each is a single self-contained HTML file with inline CSS using {{PLACEHOLDER}} substitution. To customize:
- Copy one of the templates to a new name, e.g.,
my_brand.html. - Edit the CSS variables in
:root { ... }to change colors, the font stack, or layout dimensions. - Add the template name to the
--templatechoices inrender_html.pyargparse. - Re-run
/render-html <input> --template my_brand.
The default templates are derived from the user's own academic-newspaper tutorial style (Source Serif Pro + Songti SC, 3-color palette, sticky TOC, low-flash). Stay close to that idiom for ARIS artifacts unless you have a specific reason to break the visual language.
External alternatives (for richer surfaces)¶
For deck / poster / Xiaohongshu card / tweet card / data report style outputs, point users to html-anything (Apache-2.0, 3000⭐ at time of writing). It ships 75 SKILL.md templates across 9 surfaces and detects 8 coding-agent CLIs (including Claude Code, Codex, Copilot). ARIS does not depend on it — /render-html covers ARIS-native artifacts; html-anything is the recommended path for richer publishing surfaces.
Key rules¶
- Do not auto-render every Markdown file. Only artifacts on the whitelist above. File proliferation is the main anti-pattern.
- Do not hand-edit the generated HTML. Edit the source, then re-render. The embedded SHA256 in the HTML meta tells you if the source has changed since render.
- academic-template HTML is a reviewed artifact, not raw output. Cross-model Codex review (fresh thread) gates the academic deliverables — the same way
/proof-checker,/paper-claim-audit,/citation-audit,/kill-argumentgate their respective products.--no-reviewexists for fast iteration but should not be the way you ship. - The reviewer audits rendering, not research. Claim truthfulness is owned upstream by
/paper-claim-audit,/result-to-claim,/research-review. The HTML reviewer asks: "did the renderer faithfully + safely convert this source?" — nothing more. - CDN dependency is opt-out, not opt-in. Most users have internet;
--offlineis for air-gapped runs / archival. - The default style is academic-newspaper, not marketing-flashy. Match the existing ARIS tonal voice. If you want decks/posters/social cards, point users to html-anything.
- Pure stdlib only. Adding a
pip installdependency torender_html.pyrequires an explicit decision — the helper currently has none. MCP calls live in the skill orchestrator, never in the helper script.