paper-poster-html skill (ARIS)

From Public Agent Wiki
Contents
  1. Install
  2. SKILL.md (verbatim)
  3. Why this skill exists (the failure it prevents)
  4. Mental model
  5. Constants
  6. Workflow
  7. Phase 0 — Resume, dependencies, venue spec
  8. Phase 0.5 — Design discovery (one AskUserQuestion batch)
  9. Phase 1 — Paper ingest, content plan, claim audit
  10. Phase 2 — Real paper figures (provenance-gated)
  11. Phase 3 — Scaffold + tokens
  12. Phase 4 — Layout hard loop
  13. Phase 5 — Claude visual review (gated aesthetics)
  14. Phase 6 — Codex final review (fresh thread, cross-model)
  15. Phase 7 — Final verification + report
  16. State persistence
  17. Key rules
  18. Review tracing
  19. Output contract
  20. When NOT to use
  21. Other files in this skill
  22. DESIGNFINAL.md (verbatim)
  23. 0. 定位
  24. 1. 目录布局
  25. 2. 设计 token 纪律
  26. 3. stylecheck.py 源门规则(codex 定稿,逐条实现)
  27. 4. assetcheck.py 真图门
  28. 5. 公式门(半硬)
  29. 6. MathJax 本地化
  30. 7. rungates.py + GATEREPORT.json
  31. 8. Workflow phases(SKILL.md 主结构)
  32. 9. Claude 视觉 rubric(Phase 5)
  33. 10. Fix 词汇表(反补丁循环核心)
  34. 11. COMPONENTS.md 契约
  35. 12. 已知失败模式防御(codex round 1 §7)
  36. 12.5 Round-3 ACK nits(已采纳)
  37. 13. 验收
  38. IMPLEMENTATIONCONVENTIONS.md (verbatim)
  39. A. CSS Token 契约(templates + tokens/.json + stylecheck 三方共享)
  40. B. HTML 属性契约
  41. C. CLI 契约(scripts/)
  42. stylecheck.py
  43. assetcheck.py
  44. rungates.py
  45. extractpdffigures.py
  46. preprocessfigures.py
  47. D. FIGUREMANIFEST.json schema
  48. E. 模板改造配方(posterly → ARIS fork)
  49. F. tokens/.json schema
  50. G. 测试基线
  51. templates/README.md (verbatim)
  52. Picking a template
  53. Scaffolds, not finished posters
  54. Applying a token pack (tokens/.json → :root)
  55. Retargeting the canvas
  56. Zero inline-style + utility-class policy
  57. Adding a new template

What it does. DEFAULT poster pipeline — build an academic conference poster (ICML/NeurIPS/ICLR/CVPR/...) as a single HTML/CSS file with measurement-driven hard gates, real paper figures, a two-hue design-token system, and print-ready PDF via headless Chromium. Use when the user says "做海报", "poster", "conference poster", "paper poster", or asks to design/redo a research poster. Supersedes the retired LaTeX /paper-poster. Part of ARIS: Auto-claude-code-research-in-sleep (wanshuiyin/Auto-claude-code-research-in-sleep).

Upstream wanshuiyin/Auto-claude-code-research-in-sleep
Skill file skills/paper-poster-html/SKILL.md
License MIT
Author wanshuiyin
Fetched 2026-09-10

Install

  • Clone the repo and run bash tools/install_aris.sh, or copy skills/paper-poster-html/ into ~/.claude/skills/paper-poster-html/; npx skills add wanshuiyin/Auto-claude-code-research-in-sleep --skill paper-poster-html also works.
  • Raw file: curl -sL https://raw.githubusercontent.com/wanshuiyin/Auto-claude-code-research-in-sleep/HEAD/skills/paper-poster-html/SKILL.md

SKILL.md (verbatim)

name: paper-poster-html
description: "DEFAULT poster pipeline — build an academic conference poster (ICML/NeurIPS/ICLR/CVPR/...) as a single HTML/CSS file with measurement-driven hard gates, real paper figures, a two-hue design-token system, and print-ready PDF via headless Chromium. Use when the user says \"做海报\", \"poster\", \"conference poster\", \"paper poster\", or asks to design/redo a research poster. Supersedes the retired LaTeX /paper-poster."
argument-hint: "[paper-dir-or-pdf] [— venue: ICLR, canvas: 185x90cm landscape, venue-colors: true]"
allowed-tools: Bash(*), Read, Write, Edit, Grep, Glob, WebFetch, WebSearch, AskUserQuestion, mcp__codex__codex

Paper Poster (HTML): measurement-gated poster generation

One HTML file styled for an exact print canvas (@page { size: W H }), rendered to PDF via Playwright print emulation. Iterate by measuring, not eyeballing — the screen preview lies; only print emulation at the correct viewport tells the truth. Core gate machinery is adapted from posterly (MIT, © 2026 Ruishuo Chen — see NOTICE.md and LICENSES/posterly-MIT.txt); ARIS adds style discipline gates, figure-provenance gates, the cross-model review loop, and the anti-patch-loop fix vocabulary.

Why this skill exists (the failure it prevents)

A predecessor pipeline produced a poster with 30+ colors, zero real paper figures, a screen-pixel canvas, and tiny formulas floating in oversized boxes, then spent 12+ review rounds making it worse — each round added a new badge color or bespoke SVG patch. The cure is structural, not exhortative:

  1. Hard gates run before any aesthetic opinion (alignment, style, assets must PASS first — a reviewer never sees an unmeasured poster).
  2. A closed fix vocabulary — visual-review fixes can only touch design tokens, whole catalogued components, content rebalance, assets, or canvas choice. New inline styles / new hex values / bespoke decorations are structurally forbidden.
  3. Two-hue discipline as a machine check, not a style suggestion.
  4. Real paper figures with provenance manifest, or the gate fails.

Mental model

paper (.tex / PDF) ──► content plan + claim→evidence audit (codex, fresh)
                              │
   figures extracted ─────────┤  FIGURE_MANIFEST.json (provenance, sha256)
   (real paper figures ONLY)  ▼
   template scaffold ──► fill ──► run_gates.py            ◄─── HARD, loop here
                                  preflight → style → asset → measure → polish
                              │ all hard gates PASS
                              ▼
                    Claude visual review (≤3 issues × ≤3 rounds, fix-vocabulary only)
                              │ score ≥ 9
                              ▼
                    codex final cross-model review (fresh thread, full HTML+PDF)
                              │ pass
                              ▼
                    verify-final → poster.pdf + GATE_REPORT.json

Constants

  • SKILL_SCRIPTS = ${CLAUDE_SKILL_DIR}/scripts — all helpers are single-owner and ship inside this skill (Arch C). If the directory is missing the install is broken: abort and tell the user to re-install the skill (Policy A — the gates ARE the skill; never improvise replacements).
  • REVIEWER_MODEL = gpt-6-astra, reasoning xhigh, fresh thread per review call (mcp__codex__codex, never codex-reply across review boundaries).
  • CANVAS — from the venue's official spec, looked up live in Phase 0. Never assume. (Known anchor: ICLR 2026 main = 185×90 cm landscape per its official printing service; ICML/NeurIPS commonly 60×36 in landscape; workshop posters often 61×91 cm portrait. Specs change yearly — verify.)
  • PALETTE — default = templates/tokens/generic.json (slate-blue #2D5F8B accent
    • gold #C9A24A highlight + neutrals) for all venues. Venue packs are opt-in via — venue-colors: true. Purple-dominant accents (hue 250–285) are banned unless the user passes — allow-purple: true.
  • AUTO_PROCEED = false — wait for explicit confirmation at every 🚦 checkpoint.
  • OUTPUT_DIR = poster_html/ in the working directory.

Workflow

Phase 0 — Resume, dependencies, venue spec

  1. Resume: if poster_html/POSTER_STATE.json exists with status: in_progress (< 24 h), resume from the saved phase.
  2. Dependencies (degradation chain, in order):
    • Playwright + bundled Chromium → if missing, python3 -m playwright install chromium → if install fails but system Chrome exists, scripts fall back to channel="chrome" → if all fail: you may produce the content plan and scaffold only, label everything "not print verified", and must NOT emit a final PDF.
    • pdfinfo missing → PyMuPDF reads PDF dimensions. At least one of pdftoppm / PyMuPDF must exist for PNG review renders.
    • MathJax: download tex-svg.js once into poster_html/assets/mathjax/ and reference it locally in the HTML. CDN is acceptable only for drafts; the measure gate hard-fails on unrendered MathJax either way.
  3. Venue spec lookup (live): consult the venue's official poster-instructions page (search + fetch). Extract dimensions, orientation, font floor, logo policy, anonymity rules, file format. Record {spec, source_url, retrieved} into POSTER_STATE.json — specs change yearly; never reuse a cached spec silently.

🚦 Checkpoint: echo the venue spec table (canvas, orientation, source URL) and the chosen template. Wait.

Phase 0.5 — Design discovery (one AskUserQuestion batch)

Ask once, ≤4 questions: layout template (from templates/README.md), palette (default generic pack / venue pack / custom within constraints), logos + venue mark (paths or "none" — never fabricate; check the venue's logo policy), QR target (paper / code / project page / none — generate offline with qrencode or python-qrcode; never a remote QR-service URL). Persist answers in POSTER_STATE.json as design_decisions — re-read before any later "improvement" so deliberate choices are never reverted.

Phase 1 — Paper ingest, content plan, claim audit

  1. Read the paper source (.tex ideal; PDF otherwise). Extract: title/authors/affils, the 3–5 headline numbers, core method (equations verbatim), main results (tables/figures and what they show), takeaways. Build poster_html/POSTER_CONTENT_PLAN.md — what goes in which column, word budget per card. Target density (excluding table cells, captions, author line, footer): standard poster 550–850 words; dense theory+empirical poster 750–1050 words, allowed only when ≥2 compact components are used (eqn-anatomy, flow-strip, derived-col, claim-pills, keybox--4). Warn yourself below 500 words on a 4-column landscape (it will read as sparse next to professionally dense posters) unless the template is hero/visual-first; warn above 1100 unless the user asked for dense mode. Bullets ≤ 8 words when possible — density comes from structure, not long prose. Prefer compact structure over prose: if the paper contains an explicit objective, algorithm, theorem mechanism, or baseline comparison, extract at least two of: (1) empirical objective / loss stack; (2) term-by-term equation anatomy; (3) a method-flow strip grounded in paper variables; (4) a derived-Δ column for method-vs-baseline rows; (5) a 4-up implementation/theory keybox; (6) a claim/evidence pill table for numeric-heavy posters. Do not invent an algorithm. If the paper has only an objective, label the component "objective flow" or "loss anatomy", never "algorithm".
  2. Cross-model content audit (fresh codex thread, xhigh): give it the content plan path + paper source path(s) — paths only, no summaries — and ask for a claim→evidence table: | claim on poster | paper file:line | paper says (verbatim) | match? | with match ∈ {OK, NUMERIC-MISMATCH, OVERCLAIM, MISSING-PRECONDITION, NOT-IN-PAPER, SCOPE-NARROWED}. Save to poster_html/CLAIM_EVIDENCE.md.
  3. Fix every non-OK row or record it as a user-acknowledged tradeoff.

🚦 Checkpoint: content plan + audit summary. Wait.

Phase 2 — Real paper figures (provenance-gated)

Source preference chain:

  1. Paper source figures/ (vector SVG/PDF → convert to SVG via inkscape/pdf2svg if available, else rasterize ≥ 2× rendered px).
  2. PDF-only: extract_pdf_figures.py contact-sheet + auto to list candidate regions → pick crops (🚦 human confirms crop choices) → crop at 300–450 DPI.
  3. Last resort: user supplies explicit page,x0,y0,x1,y1 bboxes.

Then preprocess_figures.py --autocrop every asset. Every paper-derived image gets a FIGURE_MANIFEST.json entry (source hash, page, bbox, dpi, sha256, natural_px) and is embedded as <img data-source="paper" data-asset-id="...">.

Hard rule: ≥ 2 paper-derived visuals or the asset gate fails. Theory-only papers may waive the total-area rule (--waive-total-area) at a human checkpoint — never silently. Never draw bespoke decorative SVG "figures" as substitutes.

Figure-area bands (asset gate, fractions of body): total target 14–22 % (warn < 12 % / > 24 %, hard < 10 % / > 28 %); per ordinary figure target 4–8 % (warn

10 %, hard > 13 %); figure--duo combined 8–12 %. Hero templates pass --hero (centerpiece may take 30–40 %). The failure mode is symmetric: too small reads as decoration, too big crowds out content. Sibling figures that share axes or tell a before→after story belong in one figure--duo card, not two cards.

Phase 3 — Scaffold + tokens

cp templates/<chosen>.html poster_html/poster.html; retarget @page + .poster dims to the venue canvas (two edits, same values); apply the chosen token pack onto the :root DESIGN TOKENS block; fill content per the plan; embed manifest figures. Run preflight + style_check — both must PASS before any layout iteration. (A fresh scaffold is expected to fail measure — that gate judges a filled poster.)

Phase 4 — Layout hard loop

After every layout change:

python3 "$SKILL_SCRIPTS/run_gates.py" poster_html/poster.html \
    --tokens <pack.json> --manifest poster_html/FIGURE_MANIFEST.json \
    --report poster_html/GATE_REPORT.json

Canonical order: preflight → style → asset → measure → polish. Targets: column-bottom spread < 5 px (aim < 3), footer gap ∈ [30, 50] px, intercard gap ∈ [12, 50] px, canvas-fill ∈ [95, 101] %, poster bbox aligned to page within ±2 px. Fix guidance for each failure mode lives in the gate output and templates/COMPONENTS.md. Do not proceed while any hard gate fails. Do not let a reviewer see an unmeasured poster. Balance under-filled columns with content from the paper (Gate C), never with whitespace, space-between, or stretched cards.

Phase 5 — Claude visual review (gated aesthetics)

Render and read the result yourself:

python3 "$SKILL_SCRIPTS/render_preview.py" poster_html/poster.html
pdftoppm -r 100 poster_html/poster_preview.pdf poster_html/review_full -png -f 1 -l 1
# plus 2-4 region crops at higher res (header / one column / equations) via PIL

Calibrate first (../shared-references/taste-calibration.md): if human-curated references/good/ + references/bad/ exist under this skill dir (or the project supplies its own pair), score those 3+3 reference posters on the axes below BEFORE the target, anchoring the scale. Never select, search for, or generate anchors yourself; if no reference sets exist, proceed uncalibrated and mark CALIBRATION: none — never fabricate anchor scores. Axes (weights sum 1.0): Design 0.35 · Craft 0.30 · Functionality 0.20 · Originality 0.15. Mapping: SCORE = min(round(1 + 9 × COMPOSITE), lowest triggered cap) — caps apply AFTER the mapping, and the loop's Score ≥ 9 threshold below always reads this final capped SCORE, never the raw composite.

Score strictly 1–10. Critical caps (hard floors — a calibrated composite never overrides them): < 2 real paper figures → ≤ 3; broken canvas / clipped content / unreadable math → ≤ 4; ≥ 4 visible hue families or gradient-heavy header → ≤ 4; large blank cards or columns → ≤ 5; fabricated visual claim → ≤ 3. Checks: posterly-showcase gestalt (would this hang next to a professionally designed poster without looking like a patched dashboard?), single-accent discipline, real figures readable and central, print hierarchy (title → headline stats → figures → detail), column fill, equation prominence (no tiny math in oversized boxes), serif-body/sans-display pairing, no gradient kitsch, component consistency, 60-second narrative. Output format:

SCORE: N/10            (= min(round(1 + 9 × COMPOSITE), lowest cap); drives the loop)
COMPOSITE: 0.xx        (weighted; list the four per-axis scores)
CALIBRATION: anchored | none
GAP: <which reference poster the target falls short of / exceeds, on which axis, and why — one paragraph; omit only when CALIBRATION: none>
CAPS_TRIGGERED: ...
TOP_ISSUES: (max 3)
ALLOWED_FIX_TYPE per issue: token | component | rebalance | asset | template/canvas
PATCH_LOOP_RISK: low | medium | high

Loop: fix (fix vocabulary below) → re-run Phase 4 gates → re-score. ≤ 3 issues per round, ≤ 3 rounds. Score ≥ 9 → Phase 6. Still < 9 after 3 rounds → STOP patching; escalate to template / canvas / content re-choice (back to Phase 3) or a human decision. Never enter round 4 of cosmetic patching.

Fix vocabulary (closed set — the anti-patch-loop core)

Allowed: (a) edit a :root token value; (b) swap/remove/add a whole component instance from templates/COMPONENTS.md; (c) content rebalance (move a card across columns, trim/grow text from the paper, resize a figure within its AR band); (d) template/canvas re-choice; (e) global edits to an existing component's CSS that reference only tokens; (f) switching predefined variants (.eqn--large, .card--compact, .figure--wide, .nowrap, …); (g) asset fixes (re-crop, swap for a clearer figure from the same paper, re-preprocess).

Forbidden: new inline styles, new hex values anywhere, bespoke decorative SVG, per-element font-size overrides. A new component may not be born inside the visual loop — stop, get a human checkpoint, add it to COMPONENTS.md, re-run from Phase 3.

Phase 6 — Codex final review (fresh thread, cross-model)

All hard gates PASS + polish warnings zero-or-waived + visual ≥ 9 first. Then a fresh codex thread (xhigh) reviews the final artifacts (not the content plan): poster.html, the rendered PDF/PNG, the paper source, GATE_REPORT.json, CLAIM_EVIDENCE.md — paths only, no executor framing. It checks: (1) fidelity & overclaims re-checked on final text (polish introduces new claims), (2) residue (\ref{, TODO, raw < in math, missing images, remote URLs), (3) visual rhetoric (headline numbers prominent, banner readable from 2 m), (4) gate-log coherence. The reviewer recommends; it does not edit. Any fix → back through Phase 4/5 gates — never straight to re-review.

Phase 7 — Final verification + report

python3 "$SKILL_SCRIPTS/poster_check.py" verify-final poster_html/poster_preview.pdf \
    --from-html poster_html/poster.html --max-size-mb 20

Page count 1, dimensions match @page, size ≤ 20 MB, no TODO/residue, no remote assets. Report: PDF path, final spread px, footer-gap range, gate summary table, unresolved waivers, codex verdict. Update POSTER_STATE.jsondone.

State persistence

poster_html/POSTER_STATE.json: {phase, venue, canvas{w,h,orientation,source_url, retrieved}, template, token_pack, design_decisions{...}, figures_selected[], visual_rounds, codex_threads{audit, final}, status, timestamp} — written after every phase; enables compact-recovery resume.

Key rules

  • Measure, don't eyeball. No layout claim without run_gates.py output.
  • Gates before aesthetics. Claude/codex review only ever sees a poster whose hard gates PASS. This ordering is what kills the patch-loop death spiral.
  • Never invent paper numbers or figures. Numbers come from the paper source; visuals carry manifest provenance. Fabrication = critical cap ≤ 3.
  • Two hues, one system. Accent + gold + neutrals. The style gate enforces it; don't negotiate with the gate.
  • Real figures are the poster. A poster without the paper's own figures is a dashboard, not a poster.
  • Fix vocabulary is closed. If a fix isn't expressible as token / component / rebalance / asset / canvas, it's the wrong fix.
  • Cross-model verdicts. Claude drives the loop and scores visuals; acceptance of content fidelity comes from the fresh codex thread (a loop can drive, never acquit).
  • Preserve user decisions. Re-read design_decisions before "improving" anything.
  • Vendor boundary. poster_check.py, render_preview.py, _posterly/ are vendored from posterly — keep diffs minimal; ARIS-side logic goes in the new scripts, not in vendored files.

Review tracing

Save every codex reviewer call's trace per shared-references/review-tracing.md to .aris/traces/paper-poster-html/<date>_run<NN>/ (audit + final threads, raw responses).

Output contract

poster_html/
├── poster.html              # single-file source of truth
├── poster_preview.pdf       # print-emulated, verify-final-checked
├── poster_preview.png       # thumbnail
├── POSTER_STATE.json        # resume state
├── GATE_REPORT.json         # canonical gate ledger (schema v1)
├── POSTER_CONTENT_PLAN.md   # what-goes-where + word budgets
├── CLAIM_EVIDENCE.md        # codex claim→evidence audit
├── FIGURE_MANIFEST.json     # figure provenance (sha256, page, bbox, dpi)
└── assets/{paper_figures,logos,qr,mathjax}/

When NOT to use

  • Slides, not a poster → /paper-talk / /slides-polish.
  • The paper's headline isn't stable yet — fix the paper first; a poster amplifies whatever story it's given.

Other files in this skill

DESIGN_FINAL.md (verbatim)

paper-poster-html — 收敛后的最终设计(codex 3 轮讨论产物)

日期:2026-06-05。讨论 thread:019e96dc-8350-70f0-a595-c1ad15919aa8(gpt-5.5 xhigh)。 状态:Round 2 全点收敛,本文档为实现规格。

0. 定位

  • HTML+CSS 是新的默认 poster 路径;旧 /paper-poster(LaTeX tcbposter)已退役为 重定向 stub(2026-06-07 落库决定,较本规格的 legacy 共存方案更进一步; 旧实现仅存于 git history)。
  • posterly(MIT, github.com/Chenruishuo/posterly)的 tools vendor 进 skill,不外部依赖 不重写;LICENSES/posterly-MIT.txt + NOTICE.md 注明来源与 ARIS 修改。

1. 目录布局

skills/paper-poster-html/
├── SKILL.md
├── LICENSES/posterly-MIT.txt
├── NOTICE.md
├── templates/
│   ├── README.md
│   ├── COMPONENTS.md          # 组件契约目录(Q6)
│   ├── landscape_4col.html    # fork 自 posterly,de-gradient + token 化
│   ├── landscape_hero.html
│   ├── portrait_2col.html
│   └── tokens/
│       ├── generic.json       # 默认:slate-blue #2D5F8B + gold #C9A24A
│       ├── iclr.json … cvpr.json   # opt-in venue 包
└── scripts/
    ├── poster_check.py        # vendored(measure/preflight/polish/verify-final)
    ├── render_preview.py      # vendored(Playwright print render)
    ├── _posterly/…            # vendored 内部模块
    ├── style_check.py         # 新:风格硬门(12 条规则)
    ├── asset_check.py         # 新:真图溯源门
    ├── run_gates.py           # 新:canonical 顺序跑全门,写 GATE_REPORT.json
    ├── extract_pdf_figures.py # 新:PDF→contact sheet→候选裁剪
    └── preprocess_figures.py  # 新:autocrop/转格式/分辨率检查

工作目录输出:

poster_html/
├── poster.html / poster.pdf / poster_preview.png
├── POSTER_STATE.json / GATE_REPORT.json
├── CLAIM_EVIDENCE.md / FIGURE_MANIFEST.json
└── assets/{paper_figures,logos,qr,mathjax}/

2. 设计 token 纪律

  • 默认色卡 = generic(所有 venue):accent #2D5F8B 族 + gold #C9A24A 族 + 中性色。 venue 色卡 opt-in(— venue-colors: true),约束:accent S≤0.55、L∈[0.25,0.45]; gold 族固定 H∈[38,48]、S≤0.65、L∈[0.42,0.65];主 accent 禁紫(H 250–285), 除非 — allow-purple: true。venue identity 默认文字 badge。
  • 字号必须走 --fs-* token scale(≤9 档,超出 warn)。
  • serif 正文(Charter/Source Serif Pro/Georgia/Times New Roman)+ sans 标题(Inter/Aptos/Helvetica Neue/Arial);mono 仅代码(Menlo/Consolas)。

3. style_check.py 源门规则(codex 定稿,逐条实现)

# 严重度 规则
1 HARD 颜色字面量只许出现在 token 文件 / :root token block;例外:data-color-exempt="logo" 的 SVG 内部
2 HARD 禁 inline style 含颜色/字体/字号/布局关键值(豁免同上 + paper asset 内部)
3 HARD 组件 CSS 颜色必须 var(--…)
4 HARD 渲染后非中性色相聚类 ≤2(聚类半径 18°,须落在 accent/gold hue ±22°;非中性=alpha≥0.10 且 S≥0.18;豁免 <img>、logo、data-source="paper"、QR)
5 HARD linear-gradient;radial-gradient 仅许 .poster 背景且所有 color stop alpha≤0.06
6 HARD 字体配对:正文 serif 栈、标题/表头 sans 栈
7 HARD 字体白名单(§2)
8 HARD 字号必须用 --fs-* token 或组件 class,禁任意 px 漂移
9 WARN 字号 token >9 档
10 HARD 契约属性:论文图必须 data-source="paper" + data-asset-id;logo 豁免必须显式标注
11 HARD 禁自造装饰 SVG;inline SVG 仅许 logo / QR fallback / COMPONENTS.md 已收录的结构图
12 WARN 大面积深色(L<0.18 且 >8% poster 面积)→ 土嗨预警

4. asset_check.py 真图门

  • ≥2 张 data-source="paper" 图;每张面积 ≥ poster 1.5%;paper-image 总面积 ≥ body 12%。
  • raster natural size ≥ rendered size 1.5×(目标 2×)。
  • FIGURE_MANIFEST.json 必填:source PDF hash、page、bbox、crop dpi、asset sha256、是否来自论文。
  • 真图获取链:论文源 figures/(SVG/PDF→SVG 转换优先)→ PDF-only 时 PyMuPDF 300–450 DPI 渲染 contact sheet → 自动候选 + 人工选 → 用户给 page,x0,y0,x1,y1 bbox → 不足 2 张硬失败 (除非 human checkpoint 显式 waiver)。

5. 公式门(半硬)

  • EQN/BROKEN(MathJax 没渲染出来)= HARD(vendored measure 已有)。
  • EQN/UNDERSIZED:.eqn inner box >80px 高且 math bbox 面积 <15% → HARD; <25% 或底部空白 >35% → WARN(final 前必须修复或记录 waiver)。

6. MathJax 本地化

Phase 0 下载 tex-svg.js 到 poster_html/assets/mathjax/(缓存复用),HTML 引本地路径; 下载失败 → 询问后 CDN 仅供草稿;final 的 measure 门对 MathJax 失败保持硬失败。

7. run_gates.py + GATE_REPORT.json

  • canonical order:preflight → style_check → asset_check → measure → polish
  • 默认 accumulate(一次给全修复面),--fail-fast 可选。
  • style/asset 保持独立 CLI(vendor diff 干净),run_gates.py 做编排。
  • GATE_REPORT.json schema:schema_version/skill/timestamp/poster_html/canvas{source,width_cm, height_cm,orientation,source_url}/overall/hard_failures/warnings/gates[{name,severity,status, command,summary,artifacts}]。
  • polish 的 WARN 在 Phase 6 前必须清零或显式 waiver。

8. Workflow phases(SKILL.md 主结构)

Phase 内容 Checkpoint
0 resume + deps(Playwright 链)+ venue spec 实时调研(WebSearch/WebFetch 官方页,URL+date 入 state) 🚦确认 venue/canvas
0.5 设计问卷(layout/palette/logo/QR/source 一轮 AskUserQuestion) 🚦确认设计输入
1 paper ingest + content plan + claim→evidence 表 codex fresh xhigh 内容审计 🚦全 claim OK 或用户接受 tradeoff
2 真图提取/预处理 asset_check + FIGURE_MANIFEST 🚦PDF-only 时人工选裁剪
3 scaffold + token patch preflight + style_check
4 布局硬循环 preflight+style+asset+measure(spread<5 aim<3;footer gap 30–50;intercard 12–50;fill 95–101%;position≤2px)
5 渲染 + Claude 视觉审(rubric §9) ≤3 issue×≤3 轮;fix 限定词汇表 §10
6 codex 终审(fresh xhigh,审 final HTML+PDF 不是 plan;fidelity/overclaim/residue/叙事/gate logs;不直接改文件) 任何 fix 回 Phase 4/5
7 verify-final + 报告 PDF 1 页/尺寸/≤20MB/无 TODO/无 remote asset 完成

Playwright 降级链:bundled Chromium → python -m playwright install chromiumchannel="chrome" → 仍失败则只产 content plan/scaffold,标注 "not print verified", 不许产出最终 PDF。pdfinfo 缺 → PyMuPDF 读尺寸;pdftoppm/PyMuPDF 至少一个用于 PNG。

9. Claude 视觉 rubric(Phase 5)

1–10 分;critical cap:无真图或 <2 张 →≤3;画布坏/裁切/公式不可读 →≤4; ≥4 个色相家族或重渐变 header →≤4;大空白卡/列 →≤5;捏造视觉 claim →≤3。 检查项:posterly-showcase gestalt / 单 accent 纪律 / 真图居中可读 / 打印层级 (title→headline→figures→details)/ 列底对齐无半空卡 / 公式占框 / serif+sans 配对 / 无渐变 kitsch / 组件一体感 / 60 秒叙事。 输出格式:SCORE: N/10CAPS_TRIGGEREDTOP_ISSUES(≤3)ALLOWED_FIX_TYPE: token|component|rebalance|asset|template/canvasPATCH_LOOP_RISK。 校准:旧 poster ≤3 分;posterly showcase ≥9 分。

10. Fix 词汇表(反补丁循环核心)

视觉审循环内只允许: (a) 改 :root token 值; (b) 整组件实例的换/删/加(组件集来自 COMPONENTS.md); (c) 内容再平衡(卡片跨列移动 / 从论文取材增删文字 / AR 门带宽内调图); (d) 画布/模板重选(升级路径); (e) 组件 stylesheet 的全局改动(只许引用 token,禁新 hex); (f) 预定义 variant 切换(.figure--wide.card--compact.eqn--large.nowrap 等, 必须已录入 COMPONENTS.md); (g) asset fix(重裁剪/换同论文更清晰图/重跑 preprocess)。 禁止:新 inline style、新 hex、自造装饰 SVG、单元素字号 override。 新组件禁止在视觉循环内诞生——需要新组件 → 停,human checkpoint 录入 COMPONENTS.md, 从 Phase 3 重跑。

11. COMPONENTS.md 契约

每组件:purpose / allowed variants / required data attributes / token usage / which gates inspect it / allowed fix operations / anti-patterns。 首发组件:card、numbered-card、figure-card、hero-figure、eqn、result-table、 claim-evidence、keybox、takeaways、qr-block、venue-badge、footer。

12. 已知失败模式防御(codex round 1 §7)

remote 资源 networkidle 假死→本地化;logo 豁免走私颜色→显式 data-color-exempt; 低清裁剪→1.5×/2× 检查;截图导致 PDF 爆体积→verify-final 20MB; 视觉审诱发新组件/新色→fix 词汇表;venue 规格过期→每次实时查+记录 URL/date; 改写引入新 claim→Phase 6 审 final HTML 不是 content plan。

12.5 Round-3 ACK nits(已采纳)

  1. style_check 规则 8:calc(var(--fs-*) * …) 仅许预定义组件 variant 使用,否则成漏洞。
  2. asset_check 的"paper-image 总面积 ≥ body 12%"对纯理论论文可 waiver——但首个验收案例不许 waiver。
  3. Phase 0 的 venue 调研在 skill 文案里写成泛化的 "official venue page lookup"(跨工具栈映射,codex 镜像兼容)。

13. 验收

首个验收案例:为一篇公开的 ICLR 2026 OpenReview 论文(理论+实验混合型)重做 poster: 画布 185×90cm 横版(ICLR 官方打印服务规格),generic 色卡, 真图来自 OpenReview PDF,目标执行方视觉 rubric ≥9 + 跨模型终审通过。 (已达成:全 gate PASS、列底 spread <1px、两轮跨模型终审 PRINT-READY。)

IMPLEMENTATION_CONVENTIONS.md (verbatim)

paper-poster-html 实现约定(所有实现 agent 必读)

配合 DESIGN_FINAL.md(规格)使用。本文档定死跨文件契约——实现时逐字遵守, 有疑问按本文档,不要自由发挥。

A. CSS Token 契约(templates + tokens/*.json + style_check 三方共享)

:root 中的 token block 必须被注释 /* ===== DESIGN TOKENS ===== *//* ===== END DESIGN TOKENS ===== */ 包围(style_check 靠这对注释定位 token block)。

颜色 token(只有这些地方允许出现颜色字面量):

--accent: #2D5F8B;  --accent-deep: #1F4566;  --accent-light: #E8F1F8;  --accent-soft: #D7E5F0;
--gold: #C9A24A;    --gold-soft: #FFF7E0;
--text-primary: #1A1A1A;  --text-secondary: #555555;  --text-muted: #888888;
--bg-page: #F6F2F0;  --bg-card: #FFFFFF;  --bg-card-tint: #FAFAFB;  --bg-emphasis: var(--accent-light);
--border-soft: #D8D8D8;  --border-strong: var(--accent);

字号 scale(9 档,模板内所有 font-size 必须引用之一;calc(var(--fs-N) * k) 仅许 COMPONENTS.md 预定义 variant 使用):

--fs-1: calc(9 * var(--u));   /* 微标签 */      --fs-2: calc(10 * var(--u));  /* 小 caption */
--fs-3: calc(11 * var(--u));  /* caption/表格 */ --fs-4: calc(12 * var(--u));  /* 正文 */
--fs-5: calc(13 * var(--u));  /* 公式/强调 */    --fs-6: calc(15 * var(--u));  /* 副标题 */
--fs-7: calc(16 * var(--u));  /* 节标题 */       --fs-8: calc(22 * var(--u));  /* banner 数字 */
--fs-9: calc(32 * var(--u));  /* 主标题 */

单位:--u: 1.6px(screen)/ @media print { :root { --u: 1mm } }。其余尺寸一律 calc(N * var(--u)),hairline 可用裸 px(≤2px)。

B. HTML 属性契约

属性 用途 谁检查
`data-measure-role="poster header banner
data-source="paper" + data-asset-id="<manifest id>" 标记来自论文的图 asset_check + style_check 豁免
data-color-exempt="logo" logo/印章 SVG 的调色豁免 style_check
data-fig-layout="beside-text" 图文并排的 AR 门 opt-out(vendored) poster_check polish

模板和最终 poster 中禁止任何 style= 属性(零容忍,style_check 规则 2 实现成这样, 简单可靠)。例外:data-color-exempt="logo" 元素的内部 SVG 标记、data-source="paper"<img> 上仅允许 style="width: NN%"(AR 调宽)。 为此模板必须自带 utility classes(替代 posterly 模板里的 inline style):

.fs-1 … .fs-9        /* font-size: var(--fs-N) */
.mt-1 … .mt-6        /* margin-top: calc(N * var(--u)) */
.mb-1 … .mb-4        /* margin-bottom */
.w-45 .w-50 … .w-100 /* 图宽 45%…100%,步长 5 */
.text-secondary .text-muted .nowrap .text-center

C. CLI 契约(scripts/)

全部 Python 3.10+,只用 stdlib + 已确认可用的 PyMuPDF(fitz)/PIL/playwright(lazy import, 缺失时给可读错误+降级指引)。每个脚本 --help 完整。exit code:0=pass,1=hard fail,2=用法/环境错误。

style_check.py

python3 style_check.py POSTER.html [--tokens TOKENS.json] [--json OUT.json]
                       [--no-render]  # 跳过渲染门(规则4、12 标 SKIPPED)

实现 DESIGN_FINAL §3 的 12 条规则 + §12.5 nit 1。源门(规则 1-3,5-11)纯静态解析 (html.parser + 正则提 CSS);渲染门(规则 4、12)用 playwright 取 computed style。 色相聚类:rgba→HSL;非中性= alpha≥0.10 且 S≥0.18;greedy 聚类半径 18°(色环距离); 聚类数 ≤2 且每类中心落在 tokens 的 accent/gold hue ±22°(hue_centers 来自 --tokens JSON, 缺省从 :root 解析 --accent/--gold 算)。 JSON 输出:{"gate":"style","status":"PASS|FAIL|WARN","rules":[{"id":1,"severity":"hard","status":"PASS","detail":"..."}]}

asset_check.py

python3 asset_check.py POSTER.html --manifest FIGURE_MANIFEST.json [--json OUT.json]
                       [--min-paper-figs 2] [--min-fig-area 0.015] [--min-total-area 0.12]
                       [--waive-total-area]   # 纯理论论文 waiver(DESIGN_FINAL §12.5 nit 2)
                       [--no-render]          # 面积检查降级为 natural-size 估算

检查:≥N 张 data-source="paper" 且 manifest 里 from_paper=true;每张渲染面积 ≥ poster 1.5%; 总面积 ≥ body 12%(可 waive);natural_px ≥ rendered px 1.5×(WARN 在 <2×); manifest 必填字段齐全(见 D);文件存在且 sha256 匹配。

run_gates.py

python3 run_gates.py POSTER.html [--report GATE_REPORT.json] [--fail-fast]
                     [--strict-polish] [--tokens TOKENS.json] [--manifest FIGURE_MANIFEST.json]
                     [--waive-total-area] [--no-render]

canonical order:preflight → style → asset → measure → polish。默认 accumulate。 子门以 subprocess 调同目录脚本(sys.executable;poster_check.py 子命令用其 CLI)。 GATE_REPORT.json 严格按 DESIGN_FINAL §7 schema(canvas 信息从 POSTER_STATE.json 读, 读不到则从 @page 解析,source 标 "page-rule")。汇总 overall=PASS/FAIL + hard_failures + warnings。

extract_pdf_figures.py

python3 extract_pdf_figures.py PAPER.pdf --out DIR [--dpi 350]
        contact-sheet                      # 整页缩略 contact sheet + 自动候选框
        crop --page P --bbox x0,y0,x1,y1 --name ID [--caption-hint "..."]
        auto                               # 自动检测大图块候选(图/表),输出候选列表

bbox 单位 = PDF points(72dpi 坐标,fitz 默认)。crop 模式渲染该区域至 --dpi,写 PNG 到 DIR,并 upsert FIGURE_MANIFEST.json(同目录上级)。contact-sheet 写 DIR/contact_sheet_pNN.png。

preprocess_figures.py

python3 preprocess_figures.py IMG... [--autocrop] [--pad 6] [--min-px 1200 700] [--manifest M.json]

PIL autocrop 白边(ImageChops.difference vs 白底,留 --pad px),报告 natural size, 低于 --min-px 给 WARN;改动后同步更新 manifest 的 natural_px/sha256。

D. FIGURE_MANIFEST.json schema

{
  "schema_version": 1,
  "source_pdf": {"path": "…", "sha256": "…"},
  "figures": [
    {"asset_id": "fig_method", "file": "assets/paper_figures/fig_method.png",
     "from_paper": true, "page": 3, "bbox": [72.0, 100.0, 520.0, 380.0], "dpi": 350,
     "sha256": "…", "natural_px": [2178, 1362], "caption_hint": "Figure 2: …"}
  ]
}

E. 模板改造配方(posterly → ARIS fork)

对 3 个模板各做(以上游 posterly 仓库的 templates/*.html 为底):

  1. 文件头注释:保留原说明,追加 "Adapted from posterly (MIT, © 2026 Ruishuo Chen) — see LICENSES/ & NOTICE.md; ARIS modifications: flat de-gradient, --fs token scale, zero-inline-style utilities, data-source/data-color-exempt contracts."
  2. 去渐变:.poster::before 顶条 → 纯色 var(--accent);.framework-banner.takeaways-strip 背景 → 纯 var(--bg-emphasis);.callout.gold → 纯 var(--gold).poster 的 radial tint(alpha≤0.06)保留。
  3. token block:按 §A 注释包围;加 --fs-1..9;所有 font-size 改 var(--fs-N)。
  4. 消灭 inline style:模板正文里所有 style="…" 换成 §B utility classes(在 CSS 段新增)。
  5. 图组件:.figure img 的 TODO 注释里写明契约:<img src="assets/paper_figures/x.png" data-source="paper" data-asset-id="x" class="w-95">
  6. logo 槽注释写明 data-color-exempt="logo"
  7. data-measure-role 一律保留。
  8. @page 默认值保留(60×36in / 24×36in),在头注释加"画布重定位:同步改 @page 与 .poster 的 width/height(各一处),ICLR 2026 main = 185cm 90cm landscape 示例"。
  9. eqn 组件加 variant .eqn--large(font-size: calc(var(--fs-5) * 1.25),预定义 calc 豁免)。
  10. 自检:改完后模板里 grep 不到 linear-gradientstyle="(除 §B 两个例外注释示例)、 裸 #hex(token block 与 logo SVG 例外)。

F. tokens/*.json schema

{"name": "generic",
 "accent": {"base": "#2D5F8B", "deep": "#1F4566", "light": "#E8F1F8", "soft": "#D7E5F0"},
 "gold": {"base": "#C9A24A", "soft": "#FFF7E0"},
 "neutrals": {"text_primary": "#1A1A1A", "text_secondary": "#555555", "text_muted": "#888888",
              "bg_page": "#F6F2F0", "bg_card": "#FFFFFF", "bg_card_tint": "#FAFAFB",
              "border_soft": "#D8D8D8"},
 "hue_centers": {"accent": 210, "gold": 43}}

venue 包约束:accent S≤0.55、L∈[0.25,0.45]、禁 H 250–285;gold 全包固定 generic 值; light/soft 从 base 推(同 hue 低饱和高亮度)。bg_page 可随 accent 微调暖/冷但 ΔE 要小。

G. 测试基线

上游 posterly 仓库的 examples/hello_world/poster.html 是全门 PASS 的参照(vendored 四门)。 新脚本写完后:style_check 对 hello_world 允许 FAIL(它有 inline style——posterly 原版风格), 但对我们改造后的模板(填充前)源门必须 PASS;run_gates 对脚手架预期 measure FAIL(未填充), 这是正常的(模板=脚手架)。

templates/README.md (verbatim)

Template gallery — paper-poster-html

Three neutral HTML scaffolds, forked from the posterly templates (MIT, © 2026 Ruishuo Chen — see ../NOTICE.md) and adapted for ARIS: flat de-gradient, --fs-* token scale, zero inline-style utility classes, and the data-source / data-color-exempt contracts. Class names are unchanged, so COMPONENTS.md (the component contract catalog) applies to all three.

Each template is self-contained and neutral: no lab branding, no paper content — only TODO placeholders. The authoring loop is: copy one to your working dir as poster.html → apply a token pack → fill TODOs with paper content + real figures → run the gates (run_gates.py) and balance until they pass.

Every layout-critical element carries data-measure-role so the measurement gate can locate columns / hero / footer regions across templates. Do not remove these attributes — the measure gate depends on them.

Picking a template

Template Canvas Layout Use when
landscape_4col.html 60 × 36 in landscape header → optional banner → 4 columns → optional takeaways → footer The default. Standard ML conference poster (ICML / NeurIPS / generic landscape) with ~3–5 content cards per column; mix of figures, equations, and tables.
landscape_hero.html 60 × 36 in landscape header → hero panel (~60%) + supporting column (~40%) → takeaways → footer ONE figure / table / system diagram is the main message. One big illustration left, 3–4 short cards right. No framework banner — the hero is the banner.
portrait_2col.html 24 × 36 in portrait header → 2 columns → footer (no banner, no takeaways strip) Portrait venues / sub-A0 sizes. Vertical space is precious, so banner + takeaways are dropped; the final card in the right column carries the conclusion/takeaways.

(File names follow DESIGN_FINAL §1: the ARIS forks drop the posterly _neutral suffix.)

Scaffolds, not finished posters

A template is a scaffold, not a poster. Figures are commented out and copy is TODO stubs, so each column only fills the top of the canvas. That has gate consequences you should expect — do not "fix" them on a fresh scaffold:

  • preflight passes out of the box. It checks structure (valid data-measure-role values, no LaTeX residue, no bare < inside $…$ math, the root data-measure-role="poster"), which the scaffold already satisfies.
  • style_check source rules (1–3, 5–11) pass on the forked scaffold. The ARIS fork has no inline styles, no linear-gradient, no stray hex outside the token block — so the static source gate is green on the empty template. (The upstream posterly originals keep inline styles and would FAIL style_check — that is expected; the fork exists precisely to fix that.)
  • measure and polish are gates for your finished poster. They check that columns bottom-align to within 5 px, that the gap to the footer sits in a tight band (30–50), that intercard gaps stay in [12, 50], and that the canvas fills 95–101% — properties only a filled poster can have. An unfilled scaffold is expected to fail them (huge column-bottom spread, a large gap to the footer). That is the gate telling you the poster is not finished, not a bug in the template.
  • asset_check fails until you embed ≥2 real paper figures. A scaffold has none.

So the loop is: copy → apply token pack → fill content + drop in real figures → run run_gates.py and balance until measure/polish/asset go green. See DESIGN_FINAL §8 for the full phase structure and the worked ICLR 2026 acceptance case (§13).

Applying a token pack (tokens/*.json:root)

The palette lives in two synchronized places: the tokens/*.json packs (machine-readable, what style_check/run_gates read for the accent/gold hue_centers) and the :root DESIGN TOKENS block in each template's <style> (what the browser renders). generic.json is the default for every venue. Venue packs are opt-in (— venue-colors: true).

To apply a pack, manually copy its values into the template's :root token block (the block fenced by /* ===== DESIGN TOKENS ===== *//* ===== END DESIGN TOKENS ===== */style_check locates the token block by exactly this comment pair, so keep it intact). The JSON-to-CSS field mapping:

JSON path CSS token
accent.base --accent
accent.deep --accent-deep
accent.light --accent-light
accent.soft --accent-soft
gold.base --gold
gold.soft --gold-soft
neutrals.text_primary / text_secondary / text_muted --text-primary / --text-secondary / --text-muted
neutrals.bg_page / bg_card / bg_card_tint --bg-page / --bg-card / --bg-card-tint
neutrals.border_soft --border-soft

--bg-emphasis: var(--accent-light) and --border-strong: var(--accent) are derived tokens — leave them as var(--…) references; they follow the accent automatically.

When you pass a pack to the gates (run_gates.py --tokens tokens/<venue>.json, style_check.py --tokens …), the hue_centers in the JSON are the source of truth for the hue-cluster check (style rule 4): the two allowed non-neutral hue families are hue_centers.accent ± 22° and hue_centers.gold ± 22°. If you copied the JSON values into :root correctly, the rendered hues will land inside those windows. (If you omit --tokens, style_check derives the centers from the :root --accent / --gold instead.)

Available packs (all share the generic gold family #C9A24A / #FFF7E0; all venue accents satisfy S ≤ 0.55, L ∈ [0.25, 0.45], hue ∉ [250, 285]):

Pack Accent identity accent hue center
generic.json slate-blue #2D5F8B (the default for every venue) 210
iclr.json deep green #2E6048 151
icml.json deep maroon #8B3A4A 348
neurips.json steel/slate blue #3A5A7A 210
cvpr.json deep azure/indigo #27407A 222
acl.json deep teal #256E72 183

Venue packs are opt-in identity, not a license to deviate from the discipline: a single accent family + the shared gold, deep and desaturated, never purple. The default text venue-badge (COMPONENTS.md) is the primary venue cue; the color pack is secondary.

Retargeting the canvas

A template ships with a default @page size and a matching .poster size. To change the canvas you must edit both in the template (each in exactly one place), keeping them identical — measure's canvas-fill / position-align gate compares the rendered .poster bounding box against the @page viewport, so a mismatch fails the gate.

  1. @page in the <style> block — e.g. @page { size: 185cm 90cm; margin: 0; }.
  2. .poster print dimensions in @media print { .poster { … width: 185cm; height: 90cm; } }. (The screen .poster uses calc(N * var(--u)) with --u: 1.6px; print uses --u: 1mm, so the screen preview scales automatically — you only hardcode the print width/height.)

The canvas parser accepts in / mm / cm / pt units. ICLR 2026 main conference example (the §13 acceptance case): the official print service spec is 185 × 90 cm landscape, so start from landscape_4col.html and set both @page and the print .poster to 185cm 90cm / width: 185cm; height: 90cm;.

Safe-area / margin design belongs as internal padding on a full-bleed .poster, never as a smaller poster — a smaller poster fails the position-align gate (the .poster bbox must align to (0,0)(viewport_w, viewport_h)).

Zero inline-style + utility-class policy

The templates and any finished poster carry no style= attribute (zero tolerance — style_check rule 2; IMPLEMENTATION_CONVENTIONS §B). To make that possible the templates ship their own utility classes in the <style> block, replacing every inline style the posterly originals used:

.fs-1 … .fs-9        font-size: var(--fs-N)
.mt-1 … .mt-6        margin-top: calc(N * var(--u))
.mb-1 … .mb-4        margin-bottom: calc(N * var(--u))
.w-45 .w-50 … .w-100 figure <img> width 45%…100% (5% steps)
.text-secondary .text-muted .nowrap .text-center

The only sanctioned style= survivors:

  • the internal markup of a data-color-exempt="logo" element (a logo / seal SVG), and
  • style="width: NN%" on a data-source="paper" <img> for aspect-ratio width tuning when the value is off the 5% grid (prefer the .w-NN class when it lands on a step).

Likewise: no linear-gradient anywhere; the only gradient allowed is the .poster background radial-gradient tint with all color stops at alpha ≤ 0.06 (style rule 5). All font-sizes go through the --fs-* scale; calc(var(--fs-N) * k) is allowed only for the predefined component variants catalogued in COMPONENTS.md (e.g. .eqn--large). All colors are tokens — no hex literal outside the :root token block (and the exempted logo SVG).

Adding a new template

A new template MUST:

  1. Set @page { size: <W> <H> } (in / mm / cm / pt) inside a <style> block — the canvas parser fails if absent — and a matching print .poster size.
  2. Carry data-measure-role="poster" on the root poster element.
  3. Use the roles consistently: header, banner (optional), body, column, card, hero (mutually exclusive with banner), footer-strip (optional), footer.
  4. Use the --u unit system (1.6px screen, 1mm print) for ALL sizing via calc(N * var(--u)) — never bare px except hairlines (≤ 2px).
  5. Carry the :root DESIGN TOKENS block fenced by the /* ===== DESIGN TOKENS ===== *//* ===== END DESIGN TOKENS ===== */ comment pair, the full --fs-1 … --fs-9 scale, and the §B utility classes.
  6. Use only components from COMPONENTS.md; no inline style=, no linear-gradient, no hex outside the token block.
  7. Keep all paper-specific content as TODO placeholders — neutral templates only.

Then add a row to the gallery table above, a catalog note if it introduces any new component (which requires the COMPONENTS.md new-component checkpoint), and link it in SKILL.md Phase 3.

Back to ARIS: Auto-claude-code-research-in-sleep or Agent skills.