/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). turns 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 for ARIS artifacts that have a real human reader: | Artifact | Why HTML helps | Template | |----------|----------------|----------| | | Ranked ideas + pilot signal + scores feel like a decision dashboard, not a flat list | | | (+…

, '

/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). turns 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 for ARIS artifacts that have a real human reader: | Artifact | Why HTML helps | Template | |----------|----------------|----------| | | Ranked ideas + pilot signal + scores feel like a decision dashboard, not a flat list | | | (+…

], ['\\\\\\\\(', '\\\\\\\\)']], displayMath: [['$', '$'], ['\\\\\\\\[', '\\\\\\\\]']], processEscapes: true },\n options: { skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] }\n};\n\u003c/script>\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js\" async>\u003c/script>\n\n\u003c!-- highlight.js -->\n\u003clink rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/styles/atom-one-light.min.css\">\n\u003cscript src=\"https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/highlight.min.js\">\u003c/script>\n\u003cscript>document.addEventListener('DOMContentLoaded', () => hljs.highlightAll());\u003c/script>\n\"\"\"\n\nCDN_BLOCK_OFFLINE = \"\u003c!-- offline mode: MathJax + highlight.js skipped; math and code blocks render as plain text -->\"\n\n\ndef load_template(name: str) -> str:\n path = TEMPLATES_DIR / f\"{name}.html\"\n if not path.exists():\n raise SystemExit(\n f\"error: template '{name}' not found at {path}. \"\n f\"Available: {[p.stem for p in TEMPLATES_DIR.glob('*.html')]}\"\n )\n return path.read_text(encoding=\"utf-8\")\n\n\ndef sha256_of(text: str) -> str:\n return hashlib.sha256(text.encode(\"utf-8\")).hexdigest()\n\n\ndef render_json_as_pre(json_path: Path) -> str:\n \"\"\"Render a sidecar JSON file as a collapsed \u003cdetails> block of pretty-printed JSON.\"\"\"\n try:\n obj = json.loads(json_path.read_text(encoding=\"utf-8\"))\n except json.JSONDecodeError as e:\n return f'\u003cdiv class=\"callout callout-bad\">\u003cdiv class=\"callout-title\">JSON parse error\u003c/div>\u003cp>{html_lib.escape(str(e))}\u003c/p>\u003c/div>'\n pretty = json.dumps(obj, ensure_ascii=False, indent=2, sort_keys=False)\n return (\n f'\u003cdetails>\u003csummary>Sidecar JSON: \u003ccode>{html_lib.escape(str(json_path))}\u003c/code>\u003c/summary>'\n f'\u003cpre>\u003ccode class=\"language-json\">{html_lib.escape(pretty)}\u003c/code>\u003c/pre>'\n f'\u003c/details>'\n )\n\n\ndef substitute(template: str, vars: dict) -> str:\n out = template\n for k, v in vars.items():\n out = out.replace(\"{{\" + k + \"}}\", v)\n return out\n\n\ndef json_for_script(obj: object) -> str:\n \"\"\"Serialize JSON for direct embedding inside a classic \u003cscript> block.\n\n json.dumps() alone is unsafe because a string value containing '\u003c/script>'\n will break out of the host \u003cscript> tag. Escape \u003c, >, & and line/paragraph\n separators as \\\\uXXXX so the result is always safe inside a \u003cscript>.\n \"\"\"\n return (\n json.dumps(obj, ensure_ascii=False)\n .replace(\"&\", \"\\\\u0026\")\n .replace(\"\u003c\", \"\\\\u003c\")\n .replace(\">\", \"\\\\u003e\")\n .replace(\"
\", \"\\\\u2028\")\n .replace(\"
\", \"\\\\u2029\")\n )\n\n\ndef _repo_relative(input_path: Path) -> str:\n \"\"\"Return a display-friendly repo-relative path. Avoids leaking absolute\n /Users/\u003cname>/... in the generated HTML meta + footer.\n\n Order: cwd-relative if input is under cwd; else git-root-relative if in\n a git repo; else basename only (parent dirs stripped) so we never\n surface the home directory.\n \"\"\"\n try:\n return str(input_path.relative_to(Path.cwd()))\n except ValueError:\n pass\n try:\n import subprocess\n result = subprocess.run(\n [\"git\", \"rev-parse\", \"--show-toplevel\"],\n cwd=input_path.parent,\n capture_output=True,\n text=True,\n check=False,\n )\n if result.returncode == 0:\n git_root = Path(result.stdout.strip())\n try:\n return str(input_path.relative_to(git_root))\n except ValueError:\n pass\n except Exception:\n pass\n return input_path.name\n\n\ndef main(argv: list[str] | None = None) -> int:\n ap = argparse.ArgumentParser(\n description=\"Render an ARIS Markdown artifact to single-file HTML.\",\n )\n ap.add_argument(\"input\", help=\"Path to input .md (or .json — wrapped in a \u003cpre>)\")\n ap.add_argument(\"--template\", default=\"academic\", choices=[\"academic\", \"dashboard\"])\n ap.add_argument(\"--out\", help=\"Output HTML path (default: \u003cinput>.html)\")\n ap.add_argument(\"--title\", help=\"Page title (default: first H1, or filename)\")\n ap.add_argument(\"--subtitle\", default=\"\", help=\"Optional italic subtitle line\")\n ap.add_argument(\"--eyebrow\", default=\"\", help=\"Optional uppercase eyebrow above H1\")\n ap.add_argument(\"--author\", default=\"\", help=\"Optional author byline (e.g., 'Name (姓名), Affiliation')\")\n ap.add_argument(\"--lang\", default=\"zh-CN\", help='\u003chtml lang=\"…\"> attribute (default zh-CN)')\n ap.add_argument(\"--state\", help=\"Optional sidecar state JSON to append as \u003cdetails>\")\n ap.add_argument(\"--json\", dest=\"json_sidecar\", help=\"Optional sidecar JSON to append (e.g., KILL_ARGUMENT.json)\")\n ap.add_argument(\"--offline\", action=\"store_true\", help=\"Skip MathJax / highlight.js CDN blocks\")\n ap.add_argument(\"--no-toc\", action=\"store_true\", help=\"Skip TOC sidebar (forces TOC_LABEL/TOC_HTML to empty)\")\n ap.add_argument(\n \"--papers\",\n help=\"Sidecar JSON file with paper registry for [[key]] popovers. \"\n \"Schema: {key: {title, authors, date, inst, key, arxiv}, ...}\",\n )\n ap.add_argument(\n \"--blog-mode\",\n action=\"store_true\",\n help=\"Enable blog/talk mode (adds .aris-blog body class, opt-in active-H2 highlighting)\",\n )\n ap.add_argument(\n \"--collapse-code-min\",\n type=int,\n default=30,\n help=\"Auto-collapse code blocks with >= N lines (default 30). Override per-block with ```lang {open}/{collapsed}.\",\n )\n args = ap.parse_args(argv)\n\n input_path = Path(args.input).resolve()\n if not input_path.exists():\n print(f\"error: input not found: {input_path}\", file=sys.stderr)\n return 2\n display_source_path = _repo_relative(input_path)\n\n raw = input_path.read_text(encoding=\"utf-8\")\n source_hash = sha256_of(raw)\n\n # If the input is JSON, wrap it as a single code block.\n is_json = input_path.suffix.lower() == \".json\"\n if is_json:\n try:\n obj = json.loads(raw)\n pretty = json.dumps(obj, ensure_ascii=False, indent=2, sort_keys=False)\n except json.JSONDecodeError:\n pretty = raw\n md_source = f\"# {input_path.name}\\n\\n```json\\n{pretty}\\n```\\n\"\n else:\n md_source = strip_frontmatter(raw)\n\n blocks = parse_blocks(md_source.split(\"\\n\"))\n body_html, toc = _render_blocks(blocks, collect_toc=not args.no_toc)\n\n # Title autodetection.\n title = args.title\n if not title:\n # Look for first H1 block.\n for b in blocks:\n if b.get(\"type\") == \"heading\" and b.get(\"level\") == 1:\n title = b[\"text\"]\n break\n if not title:\n title = input_path.stem.replace(\"_\", \" \").replace(\"-\", \" \").title()\n\n # Append sidecar JSON if requested.\n extra_html_blocks: list[str] = []\n for label, path_str in ((\"state\", args.state), (\"json\", args.json_sidecar)):\n if path_str:\n p = Path(path_str).resolve()\n if p.exists():\n extra_html_blocks.append(f'\u003ch2 id=\"sidecar-{label}\">Sidecar — \u003ccode>{html_lib.escape(p.name)}\u003c/code>\u003c/h2>')\n extra_html_blocks.append(render_json_as_pre(p))\n else:\n extra_html_blocks.append(\n f'\u003cdiv class=\"callout callout-warn\">\u003cdiv class=\"callout-title\">Sidecar missing\u003c/div>'\n f'\u003cp>\u003ccode>{html_lib.escape(path_str)}\u003c/code> not found.\u003c/p>\u003c/div>'\n )\n\n body_html = body_html + (\"\\n\" + \"\\n\".join(extra_html_blocks) if extra_html_blocks else \"\")\n\n template_str = load_template(args.template)\n\n toc_html = render_toc(toc) if not args.no_toc else \"\"\n toc_label = \"Contents\" if not args.no_toc else \"\"\n if args.no_toc:\n # Hide TOC entirely by emitting an empty \u003cnav> (template has \u003cnav>, we'd\n # rather just leave structure intact; CSS won't visually break).\n toc_html = \"\"\n\n eyebrow_block = f'\u003cdiv class=\"eyebrow\">{html_lib.escape(args.eyebrow)}\u003c/div>' if args.eyebrow else \"\"\n subtitle_block = f'\u003cp class=\"subtitle\">{html_lib.escape(args.subtitle)}\u003c/p>' if args.subtitle else \"\"\n byline_block = (\n f'\u003cp class=\"byline\">By \u003cstrong>{html_lib.escape(args.author)}\u003c/strong>\u003c/p>'\n if args.author else \"\"\n )\n\n generated_at = datetime.now(timezone.utc).strftime(\"%Y-%m-%d %H:%M UTC\")\n\n # Paper registry sidecar (for [[key]] popovers). Validate as JSON before\n # embedding; on parse error, log a warning and emit \"{}\" so the template\n # still works (popover JS just no-ops when key is missing).\n papers_json = \"{}\"\n if args.papers:\n p = Path(args.papers).resolve()\n if p.exists():\n try:\n obj = json.loads(p.read_text(encoding=\"utf-8\"))\n papers_json = json_for_script(obj)\n except json.JSONDecodeError as e:\n print(f\"warning: --papers JSON parse error: {e}\", file=sys.stderr)\n else:\n print(f\"warning: --papers file not found: {p}\", file=sys.stderr)\n\n vars_ = {\n \"LANG\": html_lib.escape(args.lang, quote=True),\n \"TITLE\": html_lib.escape(title),\n \"SUBTITLE_BLOCK\": subtitle_block,\n \"EYEBROW_BLOCK\": eyebrow_block,\n \"BYLINE_BLOCK\": byline_block,\n \"SOURCE_PATH\": html_lib.escape(display_source_path),\n \"SOURCE_SHA256\": source_hash,\n \"SOURCE_SHA256_SHORT\": source_hash[:12],\n \"GENERATED_AT\": generated_at,\n \"HEAD_CDN\": CDN_BLOCK_OFFLINE if args.offline else CDN_BLOCK_FULL,\n \"TOC_HTML\": toc_html,\n \"TOC_LABEL\": toc_label,\n \"BODY_HTML\": body_html,\n \"EXTRA_META\": \"\",\n \"PAPER_REGISTRY_JSON\": papers_json,\n \"COLLAPSE_CODE_MIN\": str(args.collapse_code_min),\n \"BODY_CLASS\": \"aris-blog\" if args.blog_mode else \"\",\n }\n\n rendered = substitute(template_str, vars_)\n\n out_path = Path(args.out).resolve() if args.out else input_path.with_suffix(\".html\")\n if out_path.is_dir():\n print(\n f\"error: --out points to a directory: {out_path}. \"\n f\"Specify a file path ending in .html.\",\n file=sys.stderr,\n )\n return 2\n out_path.parent.mkdir(parents=True, exist_ok=True)\n out_path.write_text(rendered, encoding=\"utf-8\")\n print(f\"wrote {out_path} ({len(rendered):,} bytes, {len(toc)} TOC entries, source sha256 {source_hash[:12]}...)\")\n return 0\n\n\nif __name__ == \"__main__\":\n sys.exit(main())\n","content_type":"text/x-python; charset=utf-8","language":"python","size":40583,"content_sha256":"9d76fe9d0d95cab750fdd3e1b8bbc5eb30e3f4e2c3c690c687a53975749579b6"},{"filename":"scripts/templates/academic.html","content":"\u003c!DOCTYPE html>\n\u003chtml lang=\"{{LANG}}\">\n\u003chead>\n\u003cmeta charset=\"UTF-8\">\n\u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\u003ctitle>{{TITLE}}\u003c/title>\n\n\u003cmeta name=\"generator\" content=\"ARIS render-html (academic, v1)\">\n\u003cmeta name=\"aris:source-path\" content=\"{{SOURCE_PATH}}\">\n\u003cmeta name=\"aris:source-sha256\" content=\"{{SOURCE_SHA256}}\">\n\u003cmeta name=\"aris:generated-at\" content=\"{{GENERATED_AT}}\">\n\n{{HEAD_CDN}}\n\n\u003cstyle>\n:root {\n --bg: #fdfcf7;\n --bg-soft: #f4f1ea;\n --bg-code: #f8f5ec;\n --ink: #1a1a1a;\n --ink-soft: #4a4a4a;\n --ink-muted: #6b6b6b;\n --primary: #1a4a8c;\n --primary-soft: #2d6cb8;\n --accent: #b8390e;\n --warn: #b45309;\n --warn-bg: #fef3c7;\n --info-bg: #dbeafe;\n --good-bg: #d1fae5;\n --good: #065f46;\n --bad-bg: #fee2e2;\n --bad: #991b1b;\n --border: #d6d0c0;\n --border-soft: #e8e3d5;\n}\n\n* { box-sizing: border-box; }\nhtml { scroll-behavior: smooth; }\n\nbody {\n font-family: \"Source Serif Pro\", \"Source Serif 4\", \"Crimson Pro\", \"Georgia\", \"Songti SC\", \"STSong\", serif;\n line-height: 1.65;\n color: var(--ink);\n background: var(--bg);\n margin: 0;\n padding: 0;\n font-size: 16px;\n}\n\n.layout {\n max-width: 1280px;\n margin: 0 auto;\n display: grid;\n grid-template-columns: 260px 1fr;\n gap: 48px;\n padding: 40px 32px;\n}\n\nnav.toc {\n position: sticky;\n top: 24px;\n align-self: start;\n font-size: 13px;\n max-height: calc(100vh - 48px);\n overflow-y: auto;\n border-right: 1px solid var(--border-soft);\n padding-right: 16px;\n}\nnav.toc h3 {\n margin: 0 0 12px;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--ink-muted);\n font-weight: 600;\n}\nnav.toc ol { list-style: none; padding: 0; margin: 0; counter-reset: toc; }\nnav.toc ol li { margin: 5px 0; counter-increment: toc; }\nnav.toc ol li::before { content: counter(toc) \". \"; color: var(--ink-muted); margin-right: 4px; }\nnav.toc a {\n color: var(--ink-soft);\n text-decoration: none;\n border-bottom: 1px dotted transparent;\n}\nnav.toc a:hover { color: var(--primary); border-bottom-color: var(--primary); }\nnav.toc ul { list-style: none; padding-left: 14px; margin: 3px 0; font-size: 12px; }\nnav.toc ul li::before { content: \"→ \"; color: var(--border); }\n\nmain { min-width: 0; }\n\nheader.hero {\n border-bottom: 3px double var(--primary);\n padding-bottom: 24px;\n margin-bottom: 32px;\n}\nheader.hero .eyebrow {\n color: var(--accent);\n font-size: 13px;\n text-transform: uppercase;\n letter-spacing: 0.12em;\n font-weight: 600;\n margin-bottom: 8px;\n}\nbody.aris-blog header.hero .eyebrow {\n font-family: inherit;\n color: var(--ink-muted);\n text-transform: none;\n letter-spacing: 0.01em;\n font-weight: 400;\n}\nheader.hero h1 {\n font-size: 32px;\n line-height: 1.2;\n margin: 0 0 12px;\n color: var(--ink);\n font-weight: 700;\n letter-spacing: -0.01em;\n}\nheader.hero .subtitle {\n font-size: 16px;\n color: var(--ink-soft);\n margin: 0 0 8px;\n font-style: italic;\n}\nheader.hero .byline {\n font-size: 14px;\n color: var(--ink-soft);\n margin: 0 0 20px;\n}\nheader.hero .byline strong {\n color: var(--ink);\n font-weight: 600;\n}\nheader.hero .meta {\n display: flex;\n gap: 20px;\n flex-wrap: wrap;\n font-size: 12px;\n color: var(--ink-muted);\n border-top: 1px solid var(--border-soft);\n padding-top: 14px;\n}\nheader.hero .meta span strong { color: var(--ink-soft); }\nheader.hero .meta code {\n font-family: \"JetBrains Mono\", \"SF Mono\", \"Menlo\", \"Consolas\", monospace;\n font-size: 11px;\n background: var(--bg-soft);\n padding: 1px 5px;\n border-radius: 3px;\n border: 1px solid var(--border-soft);\n}\n\nh2 {\n font-size: 24px;\n margin: 44px 0 14px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--border);\n color: var(--ink);\n font-weight: 700;\n}\nh2 .num { color: var(--primary); font-weight: 600; margin-right: 8px; }\nh3 { font-size: 19px; margin: 28px 0 10px; color: var(--primary); font-weight: 600; }\nh4 { font-size: 16px; margin: 20px 0 8px; color: var(--ink); font-weight: 600; }\n\np { margin: 10px 0; }\nul, ol { padding-left: 22px; margin: 10px 0; }\nul li, ol li { margin: 4px 0; }\nul li::marker { color: var(--primary); }\n\nstrong { color: var(--accent); font-weight: 600; }\nem { color: var(--ink-soft); }\n\na { color: var(--primary); }\na:hover { color: var(--accent); }\n\ncode:not(.hljs) {\n font-family: \"JetBrains Mono\", \"SF Mono\", \"Menlo\", \"Consolas\", monospace;\n font-size: 0.86em;\n background: var(--bg-code);\n padding: 1px 5px;\n border-radius: 3px;\n border: 1px solid var(--border-soft);\n color: var(--accent);\n}\n\npre {\n background: #fafaf6;\n border: 1px solid var(--border);\n border-left: 4px solid var(--primary);\n padding: 0;\n overflow-x: auto;\n border-radius: 4px;\n margin: 14px 0;\n}\npre code, pre code.hljs {\n background: transparent !important;\n display: block;\n padding: 14px 18px !important;\n font-size: 13px;\n line-height: 1.55;\n font-family: \"JetBrains Mono\", \"SF Mono\", \"Menlo\", monospace;\n color: var(--ink);\n}\npre.diagram {\n background: #f9f6ed;\n border-left: 4px solid var(--accent);\n font-size: 12.5px;\n line-height: 1.4;\n}\n\n.callout {\n margin: 16px 0;\n padding: 12px 16px;\n border-radius: 4px;\n border-left: 4px solid;\n font-size: 15px;\n}\n.callout-title {\n font-weight: 600;\n margin-bottom: 6px;\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.callout-info { background: var(--info-bg); border-left-color: var(--primary); }\n.callout-info .callout-title { color: var(--primary); }\n.callout-warn { background: var(--warn-bg); border-left-color: var(--warn); }\n.callout-warn .callout-title { color: var(--warn); }\n.callout-good { background: var(--good-bg); border-left-color: var(--good); }\n.callout-good .callout-title { color: var(--good); }\n.callout-bad { background: var(--bad-bg); border-left-color: var(--bad); }\n.callout-bad .callout-title { color: var(--bad); }\n\ntable {\n width: 100%;\n border-collapse: collapse;\n margin: 16px 0;\n font-size: 14px;\n border: 1px solid var(--border);\n border-radius: 4px;\n overflow: hidden;\n}\nthead { background: var(--primary); color: white; }\nth, td {\n text-align: left;\n padding: 9px 12px;\n border-bottom: 1px solid var(--border-soft);\n vertical-align: top;\n}\nth { font-weight: 600; font-size: 13px; letter-spacing: 0.02em; }\ntr:last-child td { border-bottom: none; }\ntbody tr:nth-child(even) { background: var(--bg-soft); }\n\ndetails.qa, details {\n background: white;\n border: 1px solid var(--border-soft);\n border-radius: 6px;\n margin: 10px 0;\n padding: 0;\n}\ndetails summary {\n cursor: pointer;\n padding: 10px 14px;\n font-weight: 600;\n font-size: 14px;\n color: var(--primary);\n list-style: none;\n user-select: none;\n}\ndetails summary::-webkit-details-marker { display: none; }\ndetails summary::before {\n content: \"▸ \";\n margin-right: 4px;\n display: inline-block;\n transition: transform 0.15s;\n}\ndetails[open] summary::before { transform: rotate(90deg); }\ndetails[open] summary { border-bottom: 1px solid var(--border-soft); }\ndetails > :not(summary) { padding: 10px 14px; }\ndetails p:first-of-type { margin-top: 8px; }\n\nmjx-container[display=\"true\"] { margin: 12px 0 !important; }\n\nfooter.aris-footer {\n margin-top: 60px;\n padding-top: 20px;\n border-top: 1px solid var(--border);\n font-size: 12px;\n color: var(--ink-muted);\n}\nfooter.aris-footer a { color: var(--ink-muted); border-bottom: 1px dotted var(--border); }\n\n@media (max-width: 900px) {\n .layout { grid-template-columns: 1fr; gap: 20px; padding: 20px 16px; }\n nav.toc {\n position: static;\n max-height: none;\n border-right: none;\n border-bottom: 1px solid var(--border-soft);\n padding-right: 0;\n padding-bottom: 14px;\n }\n header.hero h1 { font-size: 24px; }\n h2 { font-size: 20px; }\n}\n@media print {\n nav.toc { display: none; }\n .layout { grid-template-columns: 1fr; padding: 0; }\n body { background: white; }\n header.hero { border-bottom-color: var(--ink); }\n #cite-pop, dialog.lightbox { display: none !important; }\n}\n\n/* --- P0 polish: TOC scrollspy active state ---------------------------- */\nnav.toc a.active {\n color: var(--accent);\n font-weight: 600;\n border-bottom-color: var(--accent);\n}\n\n/* --- P0 polish: Long-code auto-collapse ------------------------------- */\ndetails.code-card {\n margin: 14px 0;\n background: #fafaf6;\n border: 1px solid var(--border);\n border-left: 4px solid var(--primary);\n border-radius: 4px;\n}\ndetails.code-card > summary {\n cursor: pointer;\n padding: 8px 14px;\n color: var(--ink-soft);\n font-size: 13px;\n font-family: \"JetBrains Mono\", \"SF Mono\", monospace;\n list-style: none;\n user-select: none;\n}\ndetails.code-card > summary::-webkit-details-marker { display: none; }\ndetails.code-card > summary::before { content: none; }\ndetails.code-card > pre {\n margin: 0;\n padding: 0;\n border: none;\n border-left: none;\n border-radius: 0;\n}\n@media print {\n details.code-card { border: 1px solid #ccc; background: white; }\n details.code-card > summary { display: none !important; }\n details.code-card > pre { border: 1px solid #ddd; border-radius: 4px; background: white; }\n}\n\n/* --- P0 polish: Paper popover ([[ref]] popups) ------------------------ */\n[data-ref] {\n color: var(--primary);\n border-bottom: 1px dotted var(--primary);\n cursor: pointer;\n white-space: nowrap;\n}\n[data-ref]:hover { background: rgba(26, 74, 140, 0.08); }\n#cite-pop {\n position: fixed;\n width: 360px;\n max-width: calc(100vw - 32px);\n background: white;\n border: 1px solid var(--border);\n border-radius: 6px;\n padding: 14px 16px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n font-size: 13px;\n line-height: 1.5;\n z-index: 1000;\n}\n#cite-pop[hidden] { display: none; }\n#cite-pop .cp-title {\n font-weight: 600;\n color: var(--ink);\n margin-bottom: 4px;\n padding-right: 24px;\n}\n#cite-pop .cp-meta {\n color: var(--ink-muted);\n font-size: 12px;\n margin-bottom: 6px;\n}\n#cite-pop .cp-key {\n color: var(--accent);\n font-style: italic;\n margin-bottom: 8px;\n}\n#cite-pop .cp-link {\n display: inline-block;\n color: var(--primary);\n font-weight: 500;\n}\n#cite-pop .cp-close {\n position: absolute;\n top: 6px;\n right: 10px;\n background: none;\n border: none;\n cursor: pointer;\n font-size: 16px;\n color: var(--ink-muted);\n}\n\n/* --- P0 polish: Figure lightbox (native \u003cdialog>) --------------------- */\ndialog.lightbox {\n border: none;\n padding: 0;\n background: rgba(0, 0, 0, 0.92);\n width: 96vw;\n height: 96vh;\n max-width: 96vw;\n max-height: 96vh;\n}\ndialog.lightbox::backdrop { background: rgba(0, 0, 0, 0.92); }\ndialog.lightbox .lb-inner {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 16px;\n position: relative;\n}\ndialog.lightbox img {\n max-width: 90vw;\n max-height: 84vh;\n object-fit: contain;\n cursor: zoom-out;\n}\ndialog.lightbox figcaption {\n color: rgba(255, 255, 255, 0.85);\n font-size: 14px;\n text-align: center;\n max-width: 700px;\n}\ndialog.lightbox .lb-close {\n position: absolute;\n top: 16px;\n right: 20px;\n background: none;\n border: none;\n color: white;\n font-size: 28px;\n cursor: pointer;\n}\n\n/* --- P0 polish: blog/talk mode active-H2 highlight (opt-in) ----------- */\nbody.aris-blog h2 { scroll-margin-top: 24px; }\nbody.aris-blog h2.aris-active-h2 {\n background: linear-gradient(to right, rgba(26, 74, 140, 0.06), transparent);\n border-radius: 4px;\n padding-left: 12px;\n margin-left: -12px;\n}\n\u003c/style>\n\u003c/head>\n\u003cbody class=\"{{BODY_CLASS}}\">\n\u003cdiv class=\"layout\">\n\n\u003cnav class=\"toc\">\n \u003ch3>{{TOC_LABEL}}\u003c/h3>\n {{TOC_HTML}}\n\u003c/nav>\n\n\u003cmain>\n\u003cheader class=\"hero\">\n {{EYEBROW_BLOCK}}\n \u003ch1>{{TITLE}}\u003c/h1>\n {{SUBTITLE_BLOCK}}\n {{BYLINE_BLOCK}}\n \u003cdiv class=\"meta\">\n \u003cspan>\u003cstrong>Source:\u003c/strong> \u003ccode>{{SOURCE_PATH}}\u003c/code>\u003c/span>\n \u003cspan>\u003cstrong>SHA256:\u003c/strong> \u003ccode>{{SOURCE_SHA256_SHORT}}\u003c/code>\u003c/span>\n \u003cspan>\u003cstrong>Rendered:\u003c/strong> {{GENERATED_AT}}\u003c/span>\n {{EXTRA_META}}\n \u003c/div>\n\u003c/header>\n\n{{BODY_HTML}}\n\n\u003cfooter class=\"aris-footer\">\n Generated by \u003ca href=\"https://github.com/wanshuiyin/Auto-claude-code-research-in-sleep/blob/main/skills/render-html/SKILL.md\">ARIS \u003ccode>/render-html\u003c/code>\u003c/a> ·\n source path \u003ccode>{{SOURCE_PATH}}\u003c/code> ·\n SHA256 \u003ccode>{{SOURCE_SHA256_SHORT}}\u003c/code> ·\n generated at {{GENERATED_AT}}.\n This is a generated view — edit the source Markdown, then re-render.\n\u003c/footer>\n\u003c/main>\n\n\u003c/div>\n\n\u003c!-- P0 polish: paper popover host (populated by IIFE below) -->\n\u003cdiv id=\"cite-pop\" hidden role=\"dialog\" aria-labelledby=\"cite-pop-title\">\n \u003cbutton class=\"cp-close\" type=\"button\" aria-label=\"Close\">×\u003c/button>\n \u003cdiv class=\"cp-content\">\u003c/div>\n\u003c/div>\n\n\u003c!-- P0 polish: figure lightbox (native \u003cdialog>, graceful fallback) -->\n\u003cdialog class=\"lightbox\" id=\"figure-lightbox\" aria-label=\"Figure detail\">\n \u003cdiv class=\"lb-inner\">\n \u003cbutton class=\"lb-close\" type=\"button\" aria-label=\"Close\">×\u003c/button>\n \u003cimg alt=\"\" />\n \u003cfigcaption>\u003c/figcaption>\n \u003c/div>\n\u003c/dialog>\n\n\u003c!-- P0 polish: paper registry sidecar, embedded by render_html.py (--papers) -->\n\u003cscript>window.PAPER_REGISTRY = {{PAPER_REGISTRY_JSON}};\u003c/script>\n\n\u003c!-- P0 polish: interactive enhancements (TOC scrollspy, code-card auto-collapse,\n paper popovers, figure lightbox, print-restore for \u003cdetails>, blog-mode H2). -->\n\u003cscript>\n(function () {\n \"use strict\";\n\n function escapeHtml(s) {\n return String(s).replace(/[&\u003c>\"']/g, function (c) {\n return ({\"&\":\"&\",\"\u003c\":\"<\",\">\":\">\",\"\\\"\":\""\",\"'\":\"'\"})[c];\n });\n }\n\n // --- Feature 5: Print degradation fix ---------------------------------\n // Force every \u003cdetails> open during print, restore the pre-print state after.\n var _stashOpen = [];\n var _printing = false;\n window.addEventListener(\"beforeprint\", function () {\n if (_printing) return; // guard against multi-fire (Chrome print preview)\n _printing = true;\n _stashOpen = [];\n document.querySelectorAll(\"details:not([open])\").forEach(function (d) {\n _stashOpen.push(d);\n d.open = true;\n });\n });\n window.addEventListener(\"afterprint\", function () {\n _stashOpen.forEach(function (d) { if (d.isConnected) d.open = false; });\n _stashOpen = [];\n _printing = false;\n });\n\n // --- Feature 3: TOC scrollspy -----------------------------------------\n var tocLinks = [].slice.call(document.querySelectorAll(\"nav.toc a\"));\n var headings = tocLinks.map(function (a) {\n var id = a.getAttribute(\"href\").replace(/^#/, \"\");\n return document.getElementById(id);\n }).filter(Boolean);\n if (headings.length && typeof IntersectionObserver !== \"undefined\") {\n var linkById = new Map();\n headings.forEach(function (h, i) { linkById.set(h.id, tocLinks[i]); });\n var io = new IntersectionObserver(function (entries) {\n entries.forEach(function (en) {\n if (!en.isIntersecting) return;\n tocLinks.forEach(function (l) { l.classList.remove(\"active\"); });\n var lk = linkById.get(en.target.id);\n if (lk) lk.classList.add(\"active\");\n });\n }, { rootMargin: \"-15% 0px -75% 0px\" });\n headings.forEach(function (h) { io.observe(h); });\n }\n\n // --- Feature 1: Long-code auto-collapse -------------------------------\n var COLLAPSE_THRESHOLD = {{COLLAPSE_CODE_MIN}};\n\n function wrapCodeCard(pre, openByDefault) {\n if (!pre || pre.closest(\"details.code-card\")) return;\n var code = pre.querySelector(\"code\");\n if (!code) return;\n var lines = code.textContent.replace(/\\n+$/, \"\").split(\"\\n\").length;\n var det = document.createElement(\"details\");\n det.className = \"code-card\";\n det.open = openByDefault;\n var sum = document.createElement(\"summary\");\n function updateSummary() {\n sum.textContent = (det.open ? \"▾ Collapse code · \" : \"▸ Expand code · \") + lines + \" lines\";\n }\n updateSummary();\n det.addEventListener(\"toggle\", updateSummary);\n pre.parentNode.insertBefore(det, pre);\n det.appendChild(sum);\n det.appendChild(pre);\n }\n\n // Honor explicit per-block flags first.\n document.querySelectorAll(\"pre[data-collapse]\").forEach(function (pre) {\n var flag = pre.getAttribute(\"data-collapse\");\n if (flag === \"collapsed\") wrapCodeCard(pre, false);\n // \"open\" → leave fully expanded, do not wrap.\n });\n // Then auto-wrap long unflagged blocks.\n document.querySelectorAll(\"pre > code\").forEach(function (code) {\n var pre = code.closest(\"pre\");\n if (!pre || pre.closest(\"details\") || pre.hasAttribute(\"data-collapse\")) return;\n var lines = code.textContent.replace(/\\n+$/, \"\").split(\"\\n\").length;\n if (lines >= COLLAPSE_THRESHOLD) wrapCodeCard(pre, false);\n });\n\n // --- Feature 10: Figure lightbox --------------------------------------\n var lightbox = document.getElementById(\"figure-lightbox\");\n if (lightbox) {\n var lbImg = lightbox.querySelector(\"img\");\n var lbCap = lightbox.querySelector(\"figcaption\");\n var lbClose = lightbox.querySelector(\".lb-close\");\n function openLightbox(srcImg) {\n lbImg.src = srcImg.src;\n lbImg.alt = srcImg.alt || \"\";\n var cap = srcImg.closest(\"figure\") ? srcImg.closest(\"figure\").querySelector(\"figcaption\") : null;\n lbCap.textContent = cap ? cap.textContent : \"\";\n lbCap.hidden = !cap;\n if (typeof lightbox.showModal === \"function\") lightbox.showModal();\n else lightbox.hidden = false;\n }\n function closeLightbox() {\n if (typeof lightbox.close === \"function\") lightbox.close();\n else lightbox.hidden = true;\n }\n document.querySelectorAll(\"main figure img, main > p > img\").forEach(function (img) {\n if (img.closest(\"dialog\")) return;\n img.style.cursor = \"zoom-in\";\n img.addEventListener(\"click\", function () { openLightbox(img); });\n });\n if (lbClose) lbClose.addEventListener(\"click\", closeLightbox);\n lightbox.addEventListener(\"click\", function (e) {\n // Backdrop click closes; clicks inside .lb-inner do not bubble here.\n if (e.target === lightbox) closeLightbox();\n });\n }\n\n // --- Feature 2: Paper popover -----------------------------------------\n var PAPERS = window.PAPER_REGISTRY || {};\n var pop = document.getElementById(\"cite-pop\");\n var popContent = pop ? pop.querySelector(\".cp-content\") : null;\n var popClose = pop ? pop.querySelector(\".cp-close\") : null;\n var popTrigger = null;\n\n function showPopover(el) {\n var key = el.dataset.ref;\n var info = PAPERS[key];\n if (!info || !pop || !popContent) return;\n popContent.innerHTML =\n '\u003cdiv class=\"cp-title\" id=\"cite-pop-title\">' + escapeHtml(info.title || key) + '\u003c/div>' +\n (info.authors\n ? '\u003cdiv class=\"cp-meta\">' + escapeHtml(info.authors) +\n (info.date ? ' · ' + escapeHtml(info.date) : '') +\n (info.inst ? ' · ' + escapeHtml(info.inst) : '') + '\u003c/div>'\n : '') +\n (info.key ? '\u003cdiv class=\"cp-key\">' + escapeHtml(info.key) + '\u003c/div>' : '') +\n (info.arxiv\n ? '\u003ca class=\"cp-link\" href=\"https://arxiv.org/abs/' + encodeURIComponent(info.arxiv) +\n '\" target=\"_blank\" rel=\"noopener\">arXiv ' + escapeHtml(info.arxiv) + ' ↗\u003c/a>'\n : '');\n var r = el.getBoundingClientRect();\n pop.hidden = false; // unhide first so offsetHeight is measurable\n var margin = 16;\n var popWidth = Math.min(360, Math.max(0, window.innerWidth - margin * 2));\n var popHeight = Math.min(pop.offsetHeight || 200, Math.max(0, window.innerHeight - margin * 2));\n pop.style.left = Math.max(margin, Math.min(r.left, window.innerWidth - popWidth - margin)) + \"px\";\n pop.style.top = Math.max(margin, Math.min(r.bottom + 8, window.innerHeight - popHeight - margin)) + \"px\";\n popTrigger = el;\n if (popClose) popClose.focus();\n }\n function hidePopover() {\n if (pop) pop.hidden = true;\n if (popTrigger) popTrigger.focus();\n popTrigger = null;\n }\n document.querySelectorAll(\"[data-ref]\").forEach(function (el) {\n el.setAttribute(\"tabindex\", \"0\");\n el.setAttribute(\"role\", \"button\");\n el.addEventListener(\"click\", function (e) { e.stopPropagation(); showPopover(el); });\n el.addEventListener(\"keydown\", function (e) {\n if (e.key === \"Enter\" || e.key === \" \") { e.preventDefault(); showPopover(el); }\n });\n });\n if (popClose) popClose.addEventListener(\"click\", hidePopover);\n document.addEventListener(\"click\", function (e) {\n if (pop && !pop.hidden && !pop.contains(e.target) && !e.target.matches(\"[data-ref]\")) hidePopover();\n });\n document.addEventListener(\"keydown\", function (e) {\n if (e.key === \"Escape\" && pop && !pop.hidden) hidePopover();\n });\n\n // --- Feature 7: Blog-mode active-H2 highlight (opt-in) ----------------\n if (document.body.classList.contains(\"aris-blog\")) {\n var h2s = [].slice.call(document.querySelectorAll(\"main h2\"));\n if (h2s.length && typeof IntersectionObserver !== \"undefined\") {\n var io2 = new IntersectionObserver(function (entries) {\n entries.forEach(function (en) {\n if (en.isIntersecting) {\n h2s.forEach(function (h) { h.classList.remove(\"aris-active-h2\"); });\n en.target.classList.add(\"aris-active-h2\");\n }\n });\n }, { rootMargin: \"-15% 0px -75% 0px\" });\n h2s.forEach(function (h) { io2.observe(h); });\n }\n }\n})();\n\u003c/script>\n\u003c/body>\n\u003c/html>\n","content_type":"text/html; charset=utf-8","language":"markup","size":21148,"content_sha256":"af3a2f998091edd2804b9e64eab21bf77b6caa57349fcfbbe767b1a8d16380cc"},{"filename":"scripts/templates/dashboard.html","content":"\u003c!DOCTYPE html>\n\u003chtml lang=\"{{LANG}}\">\n\u003chead>\n\u003cmeta charset=\"UTF-8\">\n\u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\u003ctitle>{{TITLE}}\u003c/title>\n\n\u003cmeta name=\"generator\" content=\"ARIS render-html (dashboard, v1)\">\n\u003cmeta name=\"aris:source-path\" content=\"{{SOURCE_PATH}}\">\n\u003cmeta name=\"aris:source-sha256\" content=\"{{SOURCE_SHA256}}\">\n\u003cmeta name=\"aris:generated-at\" content=\"{{GENERATED_AT}}\">\n\n{{HEAD_CDN}}\n\n\u003cstyle>\n:root {\n --bg: #fdfcf7;\n --bg-soft: #f4f1ea;\n --bg-card: #ffffff;\n --bg-code: #f8f5ec;\n --ink: #1a1a1a;\n --ink-soft: #4a4a4a;\n --ink-muted: #6b6b6b;\n --primary: #1a4a8c;\n --primary-soft: #2d6cb8;\n --accent: #b8390e;\n --warn: #b45309;\n --warn-bg: #fef3c7;\n --info-bg: #dbeafe;\n --good-bg: #d1fae5;\n --good: #065f46;\n --bad-bg: #fee2e2;\n --bad: #991b1b;\n --border: #d6d0c0;\n --border-soft: #e8e3d5;\n}\n\n* { box-sizing: border-box; }\nhtml { scroll-behavior: smooth; }\n\nbody {\n font-family: \"Source Serif Pro\", \"Source Serif 4\", \"Crimson Pro\", \"Georgia\", \"Songti SC\", \"STSong\", serif;\n line-height: 1.55;\n color: var(--ink);\n background: var(--bg);\n margin: 0;\n padding: 0;\n font-size: 15px;\n}\n\n.shell {\n max-width: 1440px;\n margin: 0 auto;\n padding: 32px 32px 48px;\n}\n\nheader.hero {\n border-bottom: 3px double var(--primary);\n padding-bottom: 20px;\n margin-bottom: 28px;\n}\nheader.hero .eyebrow {\n color: var(--accent);\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.12em;\n font-weight: 600;\n margin-bottom: 6px;\n}\nheader.hero h1 {\n font-size: 28px;\n line-height: 1.2;\n margin: 0 0 10px;\n color: var(--ink);\n font-weight: 700;\n letter-spacing: -0.01em;\n}\nheader.hero .subtitle {\n font-size: 15px;\n color: var(--ink-soft);\n margin: 0 0 6px;\n font-style: italic;\n}\nheader.hero .byline {\n font-size: 13px;\n color: var(--ink-soft);\n margin: 0 0 16px;\n}\nheader.hero .byline strong { color: var(--ink); font-weight: 600; }\nheader.hero .meta {\n display: flex;\n gap: 18px;\n flex-wrap: wrap;\n font-size: 12px;\n color: var(--ink-muted);\n border-top: 1px solid var(--border-soft);\n padding-top: 12px;\n}\nheader.hero .meta span strong { color: var(--ink-soft); }\nheader.hero .meta code {\n font-family: \"JetBrains Mono\", \"SF Mono\", \"Menlo\", \"Consolas\", monospace;\n font-size: 11px;\n background: var(--bg-soft);\n padding: 1px 5px;\n border-radius: 3px;\n border: 1px solid var(--border-soft);\n}\n\n.grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n margin: 16px 0 28px;\n}\n.grid.metrics { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }\n\n.card {\n background: var(--bg-card);\n border: 1px solid var(--border-soft);\n border-radius: 6px;\n padding: 16px 18px;\n box-shadow: 0 1px 2px rgba(0,0,0,0.02);\n}\n.card .card-label {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--ink-muted);\n font-weight: 600;\n margin-bottom: 6px;\n}\n.card .card-value {\n font-size: 28px;\n font-weight: 700;\n color: var(--primary);\n line-height: 1.1;\n font-family: \"JetBrains Mono\", \"SF Mono\", \"Menlo\", monospace;\n letter-spacing: -0.01em;\n}\n.card .card-sub {\n font-size: 12px;\n color: var(--ink-muted);\n margin-top: 6px;\n}\n.card.accent .card-value { color: var(--accent); }\n.card.good .card-value { color: var(--good); }\n.card.warn .card-value { color: var(--warn); }\n.card.bad .card-value { color: var(--bad); }\n\nh2 {\n font-size: 20px;\n margin: 32px 0 12px;\n padding-bottom: 6px;\n border-bottom: 1px solid var(--border);\n color: var(--ink);\n font-weight: 700;\n}\nh3 { font-size: 16px; margin: 22px 0 8px; color: var(--primary); font-weight: 600; }\nh4 { font-size: 14px; margin: 16px 0 6px; color: var(--ink); font-weight: 600; }\n\np { margin: 8px 0; }\nul, ol { padding-left: 20px; margin: 8px 0; }\nul li, ol li { margin: 3px 0; }\nul li::marker { color: var(--primary); }\n\nstrong { color: var(--accent); font-weight: 600; }\nem { color: var(--ink-soft); }\n\na { color: var(--primary); }\na:hover { color: var(--accent); }\n\ncode:not(.hljs) {\n font-family: \"JetBrains Mono\", \"SF Mono\", \"Menlo\", \"Consolas\", monospace;\n font-size: 0.86em;\n background: var(--bg-code);\n padding: 1px 5px;\n border-radius: 3px;\n border: 1px solid var(--border-soft);\n color: var(--accent);\n}\n\npre {\n background: #fafaf6;\n border: 1px solid var(--border);\n border-left: 4px solid var(--primary);\n padding: 0;\n overflow-x: auto;\n border-radius: 4px;\n margin: 12px 0;\n}\npre code, pre code.hljs {\n background: transparent !important;\n display: block;\n padding: 12px 16px !important;\n font-size: 12.5px;\n line-height: 1.5;\n font-family: \"JetBrains Mono\", \"SF Mono\", \"Menlo\", monospace;\n color: var(--ink);\n}\n\ntable {\n width: 100%;\n border-collapse: collapse;\n margin: 12px 0;\n font-size: 13px;\n border: 1px solid var(--border);\n border-radius: 4px;\n overflow: hidden;\n}\nthead { background: var(--primary); color: white; }\nth, td {\n text-align: left;\n padding: 8px 10px;\n border-bottom: 1px solid var(--border-soft);\n vertical-align: top;\n}\nth { font-weight: 600; font-size: 12px; }\ntr:last-child td { border-bottom: none; }\ntbody tr:nth-child(even) { background: var(--bg-soft); }\n\n.badge {\n display: inline-block;\n padding: 1px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n font-family: \"JetBrains Mono\", \"SF Mono\", monospace;\n letter-spacing: 0.02em;\n}\n.badge-good { background: var(--good-bg); color: var(--good); }\n.badge-warn { background: var(--warn-bg); color: var(--warn); }\n.badge-bad { background: var(--bad-bg); color: var(--bad); }\n.badge-info { background: var(--info-bg); color: var(--primary); }\n\n.callout {\n margin: 14px 0;\n padding: 10px 14px;\n border-radius: 4px;\n border-left: 4px solid;\n font-size: 14px;\n}\n.callout-title {\n font-weight: 600;\n margin-bottom: 4px;\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.callout-info { background: var(--info-bg); border-left-color: var(--primary); }\n.callout-info .callout-title { color: var(--primary); }\n.callout-warn { background: var(--warn-bg); border-left-color: var(--warn); }\n.callout-warn .callout-title { color: var(--warn); }\n.callout-good { background: var(--good-bg); border-left-color: var(--good); }\n.callout-good .callout-title { color: var(--good); }\n.callout-bad { background: var(--bad-bg); border-left-color: var(--bad); }\n.callout-bad .callout-title { color: var(--bad); }\n\ndetails {\n background: white;\n border: 1px solid var(--border-soft);\n border-radius: 6px;\n margin: 10px 0;\n}\ndetails summary {\n cursor: pointer;\n padding: 9px 13px;\n font-weight: 600;\n font-size: 13px;\n color: var(--primary);\n list-style: none;\n user-select: none;\n}\ndetails summary::-webkit-details-marker { display: none; }\ndetails summary::before {\n content: \"▸ \";\n margin-right: 4px;\n display: inline-block;\n transition: transform 0.15s;\n}\ndetails[open] summary::before { transform: rotate(90deg); }\ndetails[open] summary { border-bottom: 1px solid var(--border-soft); }\ndetails > :not(summary) { padding: 8px 13px; }\n\nfooter.aris-footer {\n margin-top: 48px;\n padding-top: 18px;\n border-top: 1px solid var(--border);\n font-size: 11px;\n color: var(--ink-muted);\n}\nfooter.aris-footer a { color: var(--ink-muted); border-bottom: 1px dotted var(--border); }\n\n@media (max-width: 600px) {\n .shell { padding: 20px 16px; }\n header.hero h1 { font-size: 22px; }\n .card .card-value { font-size: 24px; }\n}\n@media print {\n .shell { padding: 0; }\n body { background: white; }\n .card { box-shadow: none; }\n}\n\u003c/style>\n\u003c/head>\n\u003cbody>\n\u003cdiv class=\"shell\">\n\n\u003cheader class=\"hero\">\n {{EYEBROW_BLOCK}}\n \u003ch1>{{TITLE}}\u003c/h1>\n {{SUBTITLE_BLOCK}}\n {{BYLINE_BLOCK}}\n \u003cdiv class=\"meta\">\n \u003cspan>\u003cstrong>Source:\u003c/strong> \u003ccode>{{SOURCE_PATH}}\u003c/code>\u003c/span>\n \u003cspan>\u003cstrong>SHA256:\u003c/strong> \u003ccode>{{SOURCE_SHA256_SHORT}}\u003c/code>\u003c/span>\n \u003cspan>\u003cstrong>Rendered:\u003c/strong> {{GENERATED_AT}}\u003c/span>\n {{EXTRA_META}}\n \u003c/div>\n\u003c/header>\n\n{{BODY_HTML}}\n\n\u003cfooter class=\"aris-footer\">\n Generated by \u003ca href=\"https://github.com/wanshuiyin/Auto-claude-code-research-in-sleep/blob/main/skills/render-html/SKILL.md\">ARIS \u003ccode>/render-html\u003c/code>\u003c/a> ·\n source \u003ccode>{{SOURCE_PATH}}\u003c/code> ·\n SHA256 \u003ccode>{{SOURCE_SHA256_SHORT}}\u003c/code> ·\n generated at {{GENERATED_AT}}.\n Edit the source Markdown / JSON, then re-render.\n\u003c/footer>\n\n\u003c/div>\n\u003c/body>\n\u003c/html>\n","content_type":"text/html; charset=utf-8","language":"markup","size":8371,"content_sha256":"156e0825a131279debe55d1b91d7aa7c5f85e755458414932110687c9c3b3779"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"/render-html: Markdown → single-file HTML for human reading","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Markdown is for writers. HTML is for readers.","type":"text","marks":[{"type":"strong"}]},{"text":" ARIS workflow nodes write Markdown (canonical, audit-trail-friendly, machine-parseable). ","type":"text"},{"text":"/render-html","type":"text","marks":[{"type":"code_inline"}]},{"text":" turns ","type":"text"},{"text":"selected","type":"text","marks":[{"type":"em"}]},{"text":" artifacts into a polished single-file HTML view for the human who actually has to read them. The Markdown stays the source of truth.","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to use this skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text","marks":[{"type":"strong"}]},{"text":"/render-html","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" for","type":"text","marks":[{"type":"strong"}]},{"text":" ARIS artifacts that have a real human reader:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Artifact","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Why HTML helps","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Template","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"idea-stage/IDEA_REPORT.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ranked ideas + pilot signal + scores feel like a decision dashboard, not a flat list","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"academic","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"review-stage/AUTO_REVIEW.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" (+ ","type":"text"},{"text":"REVIEW_STATE.json","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Round-by-round score progression + weakness status; pass ","type":"text"},{"text":"--state","type":"text","marks":[{"type":"code_inline"}]},{"text":" to embed the JSON","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"academic","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"paper/KILL_ARGUMENT.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" (+ ","type":"text"},{"text":"KILL_ARGUMENT.json","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Per-point attack/defense with ","type":"text"},{"text":"\u003cdetails>","type":"text","marks":[{"type":"code_inline"}]},{"text":" Q&A cards + red/yellow/green callouts","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"academic","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"research-wiki/SUMMARY.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or ","type":"text"},{"text":"research-wiki/index.md","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cross-entity cockpit: papers / ideas / experiments / claims at a glance","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dashboard","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PAPER_PLAN.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" (optional)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Claims-evidence matrix renders better as a polished table than raw MD","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"academic","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"RESUBMIT_REPORT.{md,json}","type":"text","marks":[{"type":"code_inline"}]},{"text":" (optional)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"7-state failure-mode ledger","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"academic","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"dashboard","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Do NOT use","type":"text","marks":[{"type":"strong"}]},{"text":" for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"LaTeX paper output — the final reader-facing artifact is PDF, not HTML.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" files — those are internal LLM-facing protocol.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".aris/traces/*","type":"text","marks":[{"type":"code_inline"}]},{"text":" review traces — forensic debug, not human display.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Every Markdown file in your project — only artifacts that benefit from sticky TOC, callouts, math, or score progressions.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core invariants","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"MD / JSON is canonical, HTML is generated view.","type":"text","marks":[{"type":"strong"}]},{"text":" Edit the source, then re-render. Do not hand-edit the HTML.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cross-model review at the artifact boundary","type":"text","marks":[{"type":"strong"}]},{"text":" (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 ","type":"text"},{"text":"--review","type":"text","marks":[{"type":"code_inline"}]},{"text":" to force it. See § ","type":"text"},{"text":"HTML Review Gate","type":"text","marks":[{"type":"em"}]},{"text":" below.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Drift detection.","type":"text","marks":[{"type":"strong"}]},{"text":" Every rendered HTML embeds the source path, SHA256, and generation timestamp in ","type":"text"},{"text":"\u003cmeta>","type":"text","marks":[{"type":"code_inline"}]},{"text":" tags AND in the visible page header. If the HTML and source diverge, the meta tells you which version of the source produced it.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Single-file output.","type":"text","marks":[{"type":"strong"}]},{"text":" No build system, no separate CSS, no ","type":"text"},{"text":"node_modules","type":"text","marks":[{"type":"code_inline"}]},{"text":". Just one ","type":"text"},{"text":".html","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CDN-friendly default, ","type":"text","marks":[{"type":"strong"}]},{"text":"--offline","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" fallback.","type":"text","marks":[{"type":"strong"}]},{"text":" MathJax 3 and highlight.js load from ","type":"text"},{"text":"cdn.jsdelivr.net","type":"text","marks":[{"type":"code_inline"}]},{"text":" by default. Pass ","type":"text"},{"text":"--offline","type":"text","marks":[{"type":"code_inline"}]},{"text":" to skip both — math will appear as raw ","type":"text"},{"text":"$x$","type":"text","marks":[{"type":"code_inline"}]},{"text":", code blocks won't get syntax highlighting, but everything stays readable.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pure stdlib helper.","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"render_html.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" uses only ","type":"text"},{"text":"re","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"html","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"hashlib","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"datetime","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pathlib","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"argparse","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"sys","type":"text","marks":[{"type":"code_inline"}]},{"text":". No pip install required.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Defense-in-depth XSS sanitization.","type":"text","marks":[{"type":"strong"}]},{"text":" The helper strips ","type":"text"},{"text":"\u003cscript>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003cstyle>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003ciframe>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003cobject>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003cembed>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003cform>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003cinput>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003cbutton>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003clink>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003cmeta>","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"\u003cbase>","type":"text","marks":[{"type":"code_inline"}]},{"text":" tags, all ","type":"text"},{"text":"on*","type":"text","marks":[{"type":"code_inline"}]},{"text":" event-handler attributes (","type":"text"},{"text":"onclick","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"onload","type":"text","marks":[{"type":"code_inline"}]},{"text":", …), and rewrites ","type":"text"},{"text":"javascript:","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"vbscript:","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"data:","type":"text","marks":[{"type":"code_inline"}]},{"text":" href/src/action schemes to ","type":"text"},{"text":"#blocked-unsafe-url:","type":"text","marks":[{"type":"code_inline"}]},{"text":". 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.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Tool Location","type":"text"}]},{"type":"paragraph","content":[{"text":"Arch C self-contained: the canonical implementation lives at ","type":"text"},{"text":"skills/render-html/scripts/render_html.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" (this SKILL's own ","type":"text"},{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":" subdirectory), together with its templates at ","type":"text"},{"text":"skills/render-html/scripts/templates/{academic,dashboard}.html","type":"text","marks":[{"type":"code_inline"}]},{"text":". The helper is new — no legacy ","type":"text"},{"text":"tools/","type":"text","marks":[{"type":"code_inline"}]},{"text":" shim exists.","type":"text"}]},{"type":"paragraph","content":[{"text":"Resolve ","type":"text"},{"text":"$RENDER_HTML","type":"text","marks":[{"type":"code_inline"}]},{"text":" 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 ","type":"text"},{"text":"shared-references/integration-contract.md","type":"text","marks":[{"type":"link","attrs":{"href":"../shared-references/integration-contract.md","title":null}},{"type":"code_inline"}]},{"text":" §2, ","type":"text"},{"text":"Policy A — skill-local gate","type":"text","marks":[{"type":"strong"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Layer 0: self-contained (CC 1.0+ exposes $CLAUDE_SKILL_DIR).\nRENDER_HTML=\"\"\nif [ -n \"${CLAUDE_SKILL_DIR:-}\" ] && [ -f \"$CLAUDE_SKILL_DIR/scripts/render_html.py\" ]; then\n RENDER_HTML=\"$CLAUDE_SKILL_DIR/scripts/render_html.py\"\nfi\n# Layers 1-3: shared-runtime chain (non-CC hosts + manual installs).\nif [ -z \"$RENDER_HTML\" ]; then\n cd \"$(git rev-parse --show-toplevel 2>/dev/null || pwd)\" || exit 1\n if [ -z \"${ARIS_REPO:-}\" ] && [ -f .aris/installed-skills.txt ]; then\n ARIS_REPO=$(awk -F'\\t' '$1==\"repo_root\"{print $2; exit}' .aris/installed-skills.txt 2>/dev/null) || true\n fi\n RENDER_HTML=\".aris/skills/render-html/scripts/render_html.py\"\n [ -f \"$RENDER_HTML\" ] || RENDER_HTML=\"skills/render-html/scripts/render_html.py\"\n [ -f \"$RENDER_HTML\" ] || { [ -n \"${ARIS_REPO:-}\" ] && RENDER_HTML=\"$ARIS_REPO/skills/render-html/scripts/render_html.py\"; }\n [ -f \"$RENDER_HTML\" ] || RENDER_HTML=\"\"\nfi\n[ -z \"$RENDER_HTML\" ] && {\n 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\n echo \" /render-html cannot produce HTML output. Fix: rerun bash tools/install_aris.sh, or copy from \\$ARIS_REPO/skills/render-html/scripts/.\" >&2\n exit 1\n}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Invocation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Default: academic template, output to \u003cinput>.html alongside source\npython3 \"$RENDER_HTML\" idea-stage/IDEA_REPORT.md\n\n# Dashboard template for cockpit-style views\npython3 \"$RENDER_HTML\" research-wiki/SUMMARY.md --template dashboard\n\n# Custom output path + title + eyebrow\npython3 \"$RENDER_HTML\" review-stage/AUTO_REVIEW.md \\\n --out review-stage/AUTO_REVIEW.html \\\n --title \"Auto Review — overnight run\" \\\n --eyebrow \"Workflow 2\"\n\n# Embed sidecar state JSON (rendered as a folded \u003cdetails> JSON block at end)\npython3 \"$RENDER_HTML\" review-stage/AUTO_REVIEW.md \\\n --state review-stage/REVIEW_STATE.json\n\n# Embed sidecar JSON (e.g., for KILL_ARGUMENT.md + KILL_ARGUMENT.json)\npython3 \"$RENDER_HTML\" paper/KILL_ARGUMENT.md \\\n --json paper/KILL_ARGUMENT.json \\\n --title \"Kill Argument — adversarial review\"\n\n# Offline (no CDN; math + code render as plain text)\npython3 \"$RENDER_HTML\" idea-stage/IDEA_REPORT.md --offline\n\n# JSON-only input (wrapped in a \u003cpre>\u003ccode class=\"language-json\"> block)\npython3 \"$RENDER_HTML\" review-stage/REVIEW_STATE.json --template dashboard\n\n# Language attr (default zh-CN; set for English-primary artifacts)\npython3 \"$RENDER_HTML\" docs/SKILLS_CATALOG.md --lang en\n\n# Skip review (academic template otherwise reviews by default)\npython3 \"$RENDER_HTML\" idea-stage/IDEA_REPORT.md\n# … then the skill skips the mcp__codex__codex review step if --no-review\n# was on the command line. Pass --review to a dashboard render to force it.","type":"text"}]},{"type":"paragraph","content":[{"text":"The ","type":"text"},{"text":"--review","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"--no-review","type":"text","marks":[{"type":"code_inline"}]},{"text":" flags are parsed by the SKILL orchestrator (Claude Code), not by ","type":"text"},{"text":"render_html.py","type":"text","marks":[{"type":"code_inline"}]},{"text":". The helper itself stays pure stdlib and never calls MCP. See § ","type":"text"},{"text":"HTML Review Gate","type":"text","marks":[{"type":"em"}]},{"text":" below for the exact resolution and prompt.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Identify the artifact","type":"text"}]},{"type":"paragraph","content":[{"text":"From ","type":"text"},{"text":"$ARGUMENTS","type":"text","marks":[{"type":"code_inline"}]},{"text":", determine what to render. Common patterns:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"render IDEA_REPORT\" → ","type":"text"},{"text":"idea-stage/IDEA_REPORT.md","type":"text","marks":[{"type":"code_inline"}]},{"text":", academic template","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"make AUTO_REVIEW readable\" → ","type":"text"},{"text":"review-stage/AUTO_REVIEW.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"--state review-stage/REVIEW_STATE.json","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"show the kill argument as HTML\" → ","type":"text"},{"text":"paper/KILL_ARGUMENT.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"--json paper/KILL_ARGUMENT.json","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"research-wiki dashboard\" → look for ","type":"text"},{"text":"research-wiki/SUMMARY.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" or generate from raw entity counts (Phase 2 work; for Phase 1 just render ","type":"text"},{"text":"SUMMARY.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" if present, else fall back to listing top-level wiki structure)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Pick template","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"academic","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default): linear long-form. Sticky TOC sidebar + serif body + callouts + tables + math + Q&A ","type":"text"},{"text":"\u003cdetails>","type":"text","marks":[{"type":"code_inline"}]},{"text":". Use for IDEA_REPORT, AUTO_REVIEW, KILL_ARGUMENT, PAPER_PLAN.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"dashboard","type":"text","marks":[{"type":"code_inline"}]},{"text":": grid layout, smaller font, denser metrics cards, no TOC sidebar. Use for research-wiki cockpit, RESUBMIT_REPORT, multi-project overviews.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Run the helper","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the resolver above to get ","type":"text"},{"text":"$RENDER_HTML","type":"text","marks":[{"type":"code_inline"}]},{"text":", then invoke. The script writes the HTML alongside the source by default (or wherever ","type":"text"},{"text":"--out","type":"text","marks":[{"type":"code_inline"}]},{"text":" says) and prints a one-line confirmation including the source SHA256 prefix.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: HTML Review Gate (cross-model)","type":"text"}]},{"type":"paragraph","content":[{"text":"Decide whether to run review.","type":"text","marks":[{"type":"strong"}]},{"text":" 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:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"should_review = explicit --review present\n or (template == \"academic\" and --no-review NOT present)","type":"text"}]},{"type":"paragraph","content":[{"text":"So:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--template academic","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default) → ","type":"text"},{"text":"review by default","type":"text","marks":[{"type":"strong"}]},{"text":". Skip with ","type":"text"},{"text":"--no-review","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--template dashboard","type":"text","marks":[{"type":"code_inline"}]},{"text":" → no review by default. Force with ","type":"text"},{"text":"--review","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Phase 2 workflow auto-emit (activated 2026-05) selects per-skill via the RENDER_HTML hooks documented below — interim views default to ","type":"text"},{"text":"--no-review","type":"text","marks":[{"type":"code_inline"}]},{"text":", final / audit-class deliverables default to full gate.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If ","type":"text","marks":[{"type":"strong"}]},{"text":"should_review","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is true","type":"text","marks":[{"type":"strong"}]},{"text":", fire a fresh ","type":"text"},{"text":"mcp__codex__codex","type":"text","marks":[{"type":"code_inline"}]},{"text":" thread (NEVER ","type":"text"},{"text":"codex-reply","type":"text","marks":[{"type":"code_inline"}]},{"text":") with the prompt below. The reviewer reads the source MD + generated HTML directly; it does ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" see this skill's intermediate state.","type":"text"}]},{"type":"paragraph","content":[{"text":"Scope of review (narrow on purpose).","type":"text","marks":[{"type":"strong"}]},{"text":" The HTML reviewer audits ","type":"text"},{"text":"render fidelity / safety / structure only","type":"text","marks":[{"type":"strong"}]},{"text":" — not claim truthfulness. Claim audit belongs upstream (","type":"text"},{"text":"/paper-claim-audit","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/research-review","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/result-to-claim","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Specifically the reviewer checks:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Information fidelity","type":"text","marks":[{"type":"strong"}]},{"text":" — every section, claim, table, code block, list, math snippet, sidecar payload is present in the HTML (no silent drop)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Structural integrity","type":"text","marks":[{"type":"strong"}]},{"text":" — heading hierarchy / nested lists / table cells / code fences / ","type":"text"},{"text":"\u003cdetails>","type":"text","marks":[{"type":"code_inline"}]},{"text":" blocks / math delimiters survive parsing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Callout routing","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"> 🚨 …","type":"text","marks":[{"type":"code_inline"}]},{"text":" got ","type":"text"},{"text":".callout-bad","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"> 💡 …","type":"text","marks":[{"type":"code_inline"}]},{"text":" got ","type":"text"},{"text":".callout-info","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Safety / escaping","type":"text","marks":[{"type":"strong"}]},{"text":" — no raw ","type":"text"},{"text":"\u003cscript>","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"onclick=","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"javascript:","type":"text","marks":[{"type":"code_inline"}]},{"text":" / unreplaced placeholders / template-leak survives","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Expected-difference allowance","type":"text","marks":[{"type":"strong"}]},{"text":" — frontmatter strip, generated header/footer/meta, TOC insert, sanitized unsafe HTML are all expected, not flagged","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Codex prompt (mandatory shape).","type":"text","marks":[{"type":"strong"}]},{"text":" Send this as a fresh thread (","type":"text"},{"text":"mcp__codex__codex","type":"text","marks":[{"type":"code_inline"}]},{"text":", NOT ","type":"text"},{"text":"codex-reply","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"You are an independent ARIS HTML render auditor. This is a fresh review thread.\n\nRead these files directly:\n- Source artifact: \u003cABS path to source.md or source.json>\n- Generated HTML: \u003cABS path to out.html>\n- Optional sidecars: \u003cstate.json>, \u003ckill_argument.json> (if any)\n\nTask: Audit whether the generated HTML is a faithful, safe, structurally\nusable view of the source artifact. Do NOT judge whether the research\nclaims are true. Judge only rendering fidelity.\n\nChecks:\n1. Information fidelity — sections / claims / tables / code blocks /\n lists / math / sidecars not silently dropped or materially altered.\n2. Structural integrity — heading hierarchy, tables, nested lists,\n code fences, details/summary, math delimiters preserved.\n3. Callout routing — warning/critical/good/info blockquotes map to\n appropriate CSS classes when present.\n4. Safety/escaping — no unexpected raw script/style/iframe/form/\n event-handler/javascript/data URL survives from source.\n5. Placeholder/template leakage — no unreplaced {{PLACEHOLDER}} or\n parser private placeholder character appears.\n6. Expected differences — frontmatter strip, generated header/footer/\n meta, TOC insertion, sanitized unsafe HTML are EXPECTED and not a\n defect.\n\nReturn STRICT JSON first, then a short prose note:\n\n{\n \"verdict\": \"PASS|WARN|FAIL|ERROR\",\n \"checks\": {\n \"source_hash_match\": \"pass|warn|fail|unknown\",\n \"information_fidelity\": \"pass|warn|fail\",\n \"structure\": \"pass|warn|fail\",\n \"math_code_tables\": \"pass|warn|fail\",\n \"callouts\": \"pass|warn|fail|not_applicable\",\n \"safety_escaping\": \"pass|warn|fail\",\n \"placeholder_leak\": \"pass|warn|fail\"\n },\n \"blocking_issues\": [\n {\"severity\": \"fail\",\n \"source_location\": \"L\u003cn>...\",\n \"html_location\": \"\u003cselector or near-text>\",\n \"issue\": \"...\",\n \"suggested_fix\": \"...\"}\n ],\n \"warnings\": [\n {\"severity\": \"warn\", \"issue\": \"...\", \"suggested_fix\": \"...\"}\n ],\n \"summary\": \"one paragraph\"\n}\n\nVerdict rules:\n- PASS: no material fidelity/safety issue.\n- WARN: readable output with minor unsupported-Markdown or cosmetic\n degradation only.\n- FAIL: missing/altered meaningful content, broken tables/math/code/\n callouts that change interpretation, unsafe executable HTML, source\n hash mismatch, or placeholder leakage.\n- ERROR: files could not be read or audit could not complete.","type":"text"}]},{"type":"paragraph","content":[{"text":"Save outputs","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Write the JSON verdict to ","type":"text"},{"text":"\u003cout_path>.review.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (sibling to the HTML).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Save the raw codex trace to ","type":"text"},{"text":".aris/traces/render-html/\u003cYYYY-MM-DD>_run\u003cNN>/review.{txt,json}","type":"text","marks":[{"type":"code_inline"}]},{"text":" per ","type":"text"},{"text":"shared-references/review-tracing.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Print a one-line summary to the user: ","type":"text"},{"text":"verdict, N blocking, N warnings, trace: \u003cpath>","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If ","type":"text","marks":[{"type":"strong"}]},{"text":"verdict == FAIL","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":": the HTML is ","type":"text"},{"text":"NOT","type":"text","marks":[{"type":"strong"}]},{"text":" 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.","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text","marks":[{"type":"strong"}]},{"text":"verdict == WARN","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":": deliver the HTML but surface the warning list. User decides whether to fix or accept.","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text","marks":[{"type":"strong"}]},{"text":"mcp__codex__codex","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is not available","type":"text","marks":[{"type":"strong"}]},{"text":" (e.g., user runs ","type":"text"},{"text":"/render-html","type":"text","marks":[{"type":"code_inline"}]},{"text":" on a Codex-CLI-only setup where Codex MCP isn't wired): emit ","type":"text"},{"text":"verdict: REVIEW_UNAVAILABLE","type":"text","marks":[{"type":"code_inline"}]},{"text":" to the sidecar, do not fabricate ","type":"text"},{"text":"PASS","type":"text","marks":[{"type":"code_inline"}]},{"text":", and tell the user the HTML was generated but ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" independently reviewed. The user can manually invoke ","type":"text"},{"text":"/research-review","type":"text","marks":[{"type":"code_inline"}]},{"text":" on the source MD or re-run with Codex MCP available.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: (Optional) Verify in browser","type":"text"}]},{"type":"paragraph","content":[{"text":"open \u003cout.html>","type":"text","marks":[{"type":"code_inline"}]},{"text":" on macOS, ","type":"text"},{"text":"xdg-open","type":"text","marks":[{"type":"code_inline"}]},{"text":" on Linux, ","type":"text"},{"text":"start","type":"text","marks":[{"type":"code_inline"}]},{"text":" on Windows. Math + code highlighting need internet (CDN); offline mode degrades gracefully to readable text.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"What the helper supports","type":"text"}]},{"type":"paragraph","content":[{"text":"Markdown subset","type":"text","marks":[{"type":"strong"}]},{"text":" (chosen to match what ARIS workflows actually emit):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Headings ","type":"text"},{"text":"#","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"##","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"###","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"####","type":"text","marks":[{"type":"code_inline"}]},{"text":" with auto-generated IDs for TOC","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Paragraphs, bold ","type":"text"},{"text":"**x**","type":"text","marks":[{"type":"code_inline"}]},{"text":", italic ","type":"text"},{"text":"*x*","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"_x_","type":"text","marks":[{"type":"code_inline"}]},{"text":", inline code ","type":"text"},{"text":"`x`","type":"text","marks":[{"type":"code_inline"}]},{"text":", strikethrough ","type":"text"},{"text":"~~x~~","type":"text","marks":[{"type":"code_inline"}]},{"text":", links ","type":"text"},{"text":"[t](url)","type":"text","marks":[{"type":"code_inline"}]},{"text":", images ","type":"text"},{"text":"![a](url)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Unordered/ordered lists with 2-space nested indentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Code blocks with optional language (","type":"text"},{"text":"```python","type":"text","marks":[{"type":"code_inline"}]},{"text":") — gets ","type":"text"},{"text":"\u003cpre>\u003ccode class=\"language-python\">","type":"text","marks":[{"type":"code_inline"}]},{"text":" for highlight.js","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"ASCII-art code blocks (heuristic: many box-drawing chars) → ","type":"text"},{"text":"\u003cpre class=\"diagram\">","type":"text","marks":[{"type":"code_inline"}]},{"text":" with a distinct cream-yellow background","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tables with ","type":"text"},{"text":":---","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":":---:","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"---:","type":"text","marks":[{"type":"code_inline"}]},{"text":" alignment","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Blockquotes ","type":"text"},{"text":"> ...","type":"text","marks":[{"type":"code_inline"}]},{"text":" — emoji-prefix detection routes to callout variants:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"⚠️","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":".callout-warn","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Warning)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"💡","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"📝","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":".callout-info","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Tip / Note)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"✅","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"🔒","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":".callout-good","type":"text","marks":[{"type":"code_inline"}]},{"text":" (OK / Guarantee)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"❌","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"🚨","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":".callout-bad","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Blocked / Critical)","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"HTML passthrough for ","type":"text"},{"text":"\u003cdetails>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003csummary>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003cdiv>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003cfigure>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003ctable>","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"\u003csection>","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc. (block-level) — used for the existing Round-1/2/3 fix details in ","type":"text"},{"text":"KILL_ARGUMENT.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"LaTeX math: inline ","type":"text"},{"text":"$x$","type":"text","marks":[{"type":"code_inline"}]},{"text":" and display ","type":"text"},{"text":"$x$","type":"text","marks":[{"type":"code_inline"}]},{"text":" pass through verbatim to MathJax","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"What the helper does NOT support","type":"text","marks":[{"type":"strong"}]},{"text":" (intentional simplifications):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Footnotes (","type":"text"},{"text":"[^1]","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Definition lists","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reference-style links ","type":"text"},{"text":"[label][ref]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Setext-style headings (","type":"text"},{"text":"===","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"---","type":"text","marks":[{"type":"code_inline"}]},{"text":" underline form — ARIS doesn't emit these)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Frontmatter (","type":"text","marks":[{"type":"strong"}]},{"text":"--- ... ---","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":") at the very top of the file is stripped before rendering","type":"text","marks":[{"type":"strong"}]},{"text":" (SKILL.md frontmatter style); the body that follows is what gets converted.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Two-phase integration plan (Phase 1 was this skill; Phase 2 is workflow hooks, ","type":"text"},{"text":"activated 2026-05","type":"text","marks":[{"type":"strong"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"Phase 1 (originally shipped):","type":"text","marks":[{"type":"strong"}]},{"text":" Opt-in only. Users explicitly called ","type":"text"},{"text":"/render-html \u003cartifact.md>","type":"text","marks":[{"type":"code_inline"}]},{"text":" after a workflow completes.","type":"text"}]},{"type":"paragraph","content":[{"text":"Phase 2 (activated 2026-05):","type":"text","marks":[{"type":"strong"}]},{"text":" Eight key skills now auto-emit HTML at workflow termination, each guarded by a ","type":"text"},{"text":"RENDER_HTML = true","type":"text","marks":[{"type":"code_inline"}]},{"text":" constant that the user can flip off per skill (or pass ","type":"text"},{"text":"— render html: false","type":"text","marks":[{"type":"code_inline"}]},{"text":" at invocation).","type":"text"}]},{"type":"paragraph","content":[{"text":"Cost-tiered review gate","type":"text","marks":[{"type":"strong"}]},{"text":" (per artifact type):","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skill","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Artifact","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Review tier","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Why","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/idea-discovery","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"idea-stage/IDEA_REPORT.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--no-review","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Interim; source went through novelty + cross-model review already","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/auto-review-loop","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"review-stage/AUTO_REVIEW.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--no-review","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Per-round log; the loop itself is the cross-model review","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/research-pipeline","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"NARRATIVE_REPORT.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--no-review","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Internal Stage 5 handoff to ","type":"text"},{"text":"/paper-writing","type":"text","marks":[{"type":"code_inline"}]},{"text":"; not reviewer-facing — upstream Stage 4 auto-review-loop already gated the claims","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/kill-argument","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cpaper-dir>/KILL_ARGUMENT.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"full gate","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Audit-class artifact; matches skill's own cross-model invariant","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/paper-claim-audit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"paper/PAPER_CLAIM_AUDIT.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"full gate","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Audit-class artifact","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/citation-audit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"paper/CITATION_AUDIT.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"full gate","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Audit-class artifact","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/proof-checker","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PROOF_AUDIT.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"full gate","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Audit-class artifact","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/rebuttal","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"REBUTTAL_DRAFT_rich.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"full gate","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pre-submission deliverable","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/paper-writing","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(no auto-HTML)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"n/a","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"The final reader artifact is PDF","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"/research-wiki","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"research-wiki/index.html","type":"text","marks":[{"type":"code_inline"}]},{"text":" dashboard","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"future","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Phase 2.1 — dashboard template work, not in this round","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"--no-review","type":"text","marks":[{"type":"code_inline"}]},{"text":" for interim reports is intentional: the source MD has already been reviewed by the producing skill's own cross-model gate, and the HTML render only converts structure — it doesn't add new claims. Full review at every checkpoint would multiply Codex calls per pipeline by 4-6×.","type":"text"}]},{"type":"paragraph","content":[{"text":"To disable HTML output for a specific skill: set ","type":"text"},{"text":"RENDER_HTML = false","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the skill's constants block or pass ","type":"text"},{"text":"— render html: false","type":"text","marks":[{"type":"code_inline"}]},{"text":". To globally disable: skip with environment variable ","type":"text"},{"text":"ARIS_RENDER_HTML=0","type":"text","marks":[{"type":"code_inline"}]},{"text":" (planned, not yet implemented; flag per skill in the meantime).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Customizing the templates","type":"text"}]},{"type":"paragraph","content":[{"text":"The two templates live at ","type":"text"},{"text":"skills/render-html/scripts/templates/{academic,dashboard}.html","type":"text","marks":[{"type":"code_inline"}]},{"text":". Each is a single self-contained HTML file with inline CSS using ","type":"text"},{"text":"{{PLACEHOLDER}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" substitution. To customize:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Copy one of the templates to a new name, e.g., ","type":"text"},{"text":"my_brand.html","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Edit the CSS variables in ","type":"text"},{"text":":root { ... }","type":"text","marks":[{"type":"code_inline"}]},{"text":" to change colors, the font stack, or layout dimensions.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add the template name to the ","type":"text"},{"text":"--template","type":"text","marks":[{"type":"code_inline"}]},{"text":" choices in ","type":"text"},{"text":"render_html.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"argparse","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Re-run ","type":"text"},{"text":"/render-html \u003cinput> --template my_brand","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"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.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"External alternatives (for richer surfaces)","type":"text"}]},{"type":"paragraph","content":[{"text":"For deck / poster / Xiaohongshu card / tweet card / data report style outputs, point users to ","type":"text"},{"text":"html-anything","type":"text","marks":[{"type":"link","attrs":{"href":"https://github.com/nexu-io/html-anything","title":null}},{"type":"strong"}]},{"text":" (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 ","type":"text"},{"text":"not","type":"text","marks":[{"type":"em"}]},{"text":" depend on it — ","type":"text"},{"text":"/render-html","type":"text","marks":[{"type":"code_inline"}]},{"text":" covers ARIS-native artifacts; html-anything is the recommended path for richer publishing surfaces.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Key rules","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not auto-render every Markdown file.","type":"text","marks":[{"type":"strong"}]},{"text":" Only artifacts on the whitelist above. File proliferation is the main anti-pattern.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Do not hand-edit the generated HTML.","type":"text","marks":[{"type":"strong"}]},{"text":" Edit the source, then re-render. The embedded SHA256 in the HTML meta tells you if the source has changed since render.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"academic-template HTML is a reviewed artifact by default","type":"text","marks":[{"type":"strong"}]},{"text":", not raw output. Cross-model Codex review (fresh thread) gates the academic deliverables — the same way ","type":"text"},{"text":"/proof-checker","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/paper-claim-audit","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/citation-audit","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/kill-argument","type":"text","marks":[{"type":"code_inline"}]},{"text":" gate their respective products. ","type":"text"},{"text":"--no-review","type":"text","marks":[{"type":"code_inline"}]},{"text":" is appropriate for ","type":"text"},{"text":"interim auto-emits","type":"text","marks":[{"type":"strong"}]},{"text":" (e.g., ","type":"text"},{"text":"idea-stage/IDEA_REPORT.html","type":"text","marks":[{"type":"code_inline"}]},{"text":", per-round ","type":"text"},{"text":"review-stage/AUTO_REVIEW.html","type":"text","marks":[{"type":"code_inline"}]},{"text":") where the source MD has already passed an upstream cross-model gate — the HTML render is then a structural conversion, not a new claim audit. For ","type":"text"},{"text":"shipped / reviewer-facing / audit-class","type":"text","marks":[{"type":"strong"}]},{"text":" outputs, keep the full gate.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The reviewer audits rendering, not research.","type":"text","marks":[{"type":"strong"}]},{"text":" Claim truthfulness is owned upstream by ","type":"text"},{"text":"/paper-claim-audit","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/result-to-claim","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"/research-review","type":"text","marks":[{"type":"code_inline"}]},{"text":". The HTML reviewer asks: \"did the renderer faithfully + safely convert this source?\" — nothing more.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CDN dependency is opt-out, not opt-in.","type":"text","marks":[{"type":"strong"}]},{"text":" Most users have internet; ","type":"text"},{"text":"--offline","type":"text","marks":[{"type":"code_inline"}]},{"text":" is for air-gapped runs / archival.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The default style is academic-newspaper, not marketing-flashy.","type":"text","marks":[{"type":"strong"}]},{"text":" Match the existing ARIS tonal voice. If you want decks/posters/social cards, point users to html-anything.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pure stdlib only.","type":"text","marks":[{"type":"strong"}]},{"text":" Adding a ","type":"text"},{"text":"pip install","type":"text","marks":[{"type":"code_inline"}]},{"text":" dependency to ","type":"text"},{"text":"render_html.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" requires an explicit decision — the helper currently has none. MCP calls live in the skill orchestrator, never in the helper script.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"render-html","author":"@skillopedia","source":{"stars":11172,"repo_name":"auto-claude-code-research-in-sleep","origin_url":"https://github.com/wanshuiyin/auto-claude-code-research-in-sleep/blob/HEAD/skills/render-html/SKILL.md","repo_owner":"wanshuiyin","body_sha256":"d8cff028569ff741a1f51cf5784ef4d4d8f4ef63089245825d7c723215ef555d","cluster_key":"5423dd5999591546f8f172c3dd511ec737fb70e13c36befdaec9136098576569","clean_bundle":{"format":"clean-skill-bundle-v1","source":"wanshuiyin/auto-claude-code-research-in-sleep/skills/render-html/SKILL.md","attachments":[{"id":"b36f01f5-ed3e-50f4-b95d-bdc364bc6d4e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b36f01f5-ed3e-50f4-b95d-bdc364bc6d4e/attachment.py","path":"scripts/render_html.py","size":40583,"sha256":"9d76fe9d0d95cab750fdd3e1b8bbc5eb30e3f4e2c3c690c687a53975749579b6","contentType":"text/x-python; charset=utf-8"},{"id":"d41c8fbd-bd05-53b7-a1c0-0c3013b32d2c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d41c8fbd-bd05-53b7-a1c0-0c3013b32d2c/attachment.html","path":"scripts/templates/academic.html","size":21148,"sha256":"af3a2f998091edd2804b9e64eab21bf77b6caa57349fcfbbe767b1a8d16380cc","contentType":"text/html; charset=utf-8"},{"id":"a54cd51f-18f2-537a-a703-aa299283cef3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a54cd51f-18f2-537a-a703-aa299283cef3/attachment.html","path":"scripts/templates/dashboard.html","size":8371,"sha256":"156e0825a131279debe55d1b91d7aa7c5f85e755458414932110687c9c3b3779","contentType":"text/html; charset=utf-8"}],"bundle_sha256":"10bc6aca0eba821e519d48333524911be7003d0f5deba064e1d69abdc29245d0","attachment_count":3,"text_attachments":3,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/render-html/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"web-development","category_label":"Web"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"web-development","import_tag":"clean-skills-v1","description":"Render an ARIS Markdown / JSON artifact (IDEA_REPORT, AUTO_REVIEW, KILL_ARGUMENT, PAPER_PLAN, research-wiki state, etc.) into a single-file HTML view designed for human reading. Academic template outputs are gated by a fresh cross-model Codex review for render fidelity + safety (the ARIS invariant). Use when the user says \"渲染 HTML\", \"出一份 HTML 报告\", \"render html\", \"make this readable\", \"export to html\", or wants a polished web-rendered view of a Markdown artifact. Markdown/JSON stays the canonical source; HTML is a generated, reviewed view.","allowed-tools":"Bash(*), Read, Write, mcp__codex__codex","argument-hint":"\u003cinput.md> [--template academic|dashboard] [--out \u003cpath>] [--title ...] [--state \u003cstate.json>] [--json \u003csidecar.json>] [--offline] [--review|--no-review]"}},"renderedAt":1782979254192}

/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). turns 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 for ARIS artifacts that have a real human reader: | Artifact | Why HTML helps | Template | |----------|----------------|----------| | | Ranked ideas + pilot signal + scores feel like a decision dashboard, not a flat list | | | (+…