# Comic Blueprint Author

> Phase-1 (S7 of the comic-author suite) — turn ONE locked panel_spec into a deterministic content-SVG blueprint that becomes the codex image_gen condition, by WRITING A PYTHON GENERATOR (never raw SVG in chat — LLMs botch coordinates). The HEADLINE comic-pivot rule: the image bakes NO bubbles at all — draw only characters + scene + leave negative-space SAFE ZONES; HTML/CSS owns the entire bubble.…

- **Type:** Skill
- **Install:** `agentstack add skill-wanshuiyin-aris-movie-director-comic-blueprint-author`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [wanshuiyin](https://agentstack.voostack.com/s/wanshuiyin)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [wanshuiyin](https://github.com/wanshuiyin)
- **Source:** https://github.com/wanshuiyin/ARIS-Movie-Director/tree/main/skills/comic-blueprint-author
- **Website:** https://wanshuiyin.github.io/ARIS-Movie-Director/comic/

## Install

```sh
agentstack add skill-wanshuiyin-aris-movie-director-comic-blueprint-author
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# comic-blueprint-author — one panel_spec → a deterministic content-SVG blueprint (S7)

The **per-panel condition author** of the [`comic-author`](../comic-author/SKILL.md) Phase-1 suite. It sits
between the storyboard ([`comic-storyboard-creator`](../comic-storyboard-creator/SKILL.md)) and the bake
([`comic-director`](../comic-director/SKILL.md)): for ONE locked `panel_spec` it produces ONE deterministic
`content_svg` — the answer-key composition image that the spiral feeds to `codex image_gen` as `-i` ref #1.
Unlike video, **the panel IS the deliverable** — there is no i2v start-frame residue, no motion, no temporal
chaining. The whole job is to LOCK what the panel must contain (numbers, layout, where the empty bubble
real-estate lives) so the image model has a precise composition to finalize and the `blueprint` gate has a
ground truth to diff against. You author this **by writing a Python generator script** — mirroring
[`examples/comic_m3_audit/gen/asset_lib.py`](../../examples/comic_m3_audit/gen/asset_lib.py) +
`gen_*_blueprints.py` — because hand-writing raw SVG coordinates in chat is exactly how LLMs botch a figure
(misplaced rects, off-canvas text, collisions). The script is the artifact; the SVG is its deterministic,
re-runnable output.

```text
  locked panel_spec ─▶ ⓪ PREFLIGHT  (assert refs status=locked + output_ref present; build input_refs; non-empty)
                            ▼
                       ① TEXT-MODE BIFURCATION  (baked → expected_literals verbatim · scene/no-numbers → html)
                            ▼
                       ② WRITE A PYTHON GENERATOR  (import only from asset_lib · palette pinned to ui_tokens)
                            │   draws characters+scene+figures · leaves negative-space SAFE ZONES · NO bubbles
                            ▼
                       ③ EMIT content_svg  + run check_asset_collisions.py (single-source, mandatory)
                            │   + zero-text assert for no-text panels (constellation etc.)
                            ▼
                       ④ WRITE the blueprint node  (content_svg, expected_literals, safe_zones, html_bubbles,
                            │   crop, negative_space_policy, generator_script, file_sha256)
                            ▼
                       ⑤a PRE-GATE FAN-OUT  (raster→PNG · CC render/lint ‖ Codex xhigh ‖ Gemini → write
                            │   review:* nodes + `reviews` edges → blueprint   ·never call the gate cold·)
                            ▼
                       ⑤b comic-cross-layer-gate  --gate blueprint   (FUSES the reviews)
                            │   refs_present≥4 ∧ spatial_correctness≥4 ∧ blueprint_renders≥4 (+ text_preservation
                            │   if baked) → approve · safezone_quality_blueprints.py` per panel (or
  per beat-group), importing the single-source builders from
  [`asset_lib.py`](../../examples/comic_m3_audit/gen/asset_lib.py). **Never** paste SVG coordinates into chat
  and never hand-edit the emitted `.svg` — re-run the script.
- **PALETTE PINNED** to `comic.json` `ui_tokens` (and the per-character hexes in `ART_BIBLE.md` §1 / §0.5),
  imported as constants from `asset_lib` (`VOID`, `BOARD`, `INK`, `GREEN`, `AMBER`, `RED`, …) **so the film
  never grows two visual dialects.**
- **THE IMAGE BAKES NO BUBBLES** — see the Headline rule below. The blueprint draws characters + scene +
  diegetic figures and leaves the bubble real-estate EMPTY (low-detail safe zones). `html_bubbles[]` live in
  the JSON view only.
- **NODE = `blueprint`** — read the upstream `panel_spec` node; write a `blueprint` node (`blueprint:`).
- **GATE = `comic-cross-layer-gate  --gate blueprint`** — the SOLE gate authority; verdicts
  `{approve, revise, fallback}`, ADVANCE iff `refs_present ≥ 4 ∧ spatial_correctness ≥ 4 ∧ blueprint_renders ≥ 4`
  (+ `text_preservation ≥ 4` when baked text), `safezone_quality 1
  generator) before the gate runs.
- **SCOPE** — one panel per invocation. You author the *condition*; you do NOT bake the frame (that's
  [`comic-director`](../comic-director/SKILL.md)) and you do NOT compose the final text prompt (that's
  [`comic-panel-prompt-builder`](../comic-panel-prompt-builder/SKILL.md)).

## Headline rule (the comic-pivot, COMIC_PIVOT_DESIGN §"two headline authoring rules")
> **The image generates NO bubbles at all — not even empty ones.** It draws only characters + scene + diegetic
> figures, and leaves negative-space **SAFE ZONES** where text will go. HTML/CSS owns the **entire** bubble
> (background + border + tail + text), auto-sizing to fluid bilingual text. The earlier "bake empty bubbles +
> overlay" plan was **rejected** — a dual system that drifts and produces garbled pseudo-glyphs (risk R2).

Concretely, in every blueprint you author:
- The generator draws **zero** speech bubbles, tails, captions, narration, headers, or labels-as-dialogue.
- It draws diegetic, in-world signage that **is the art** (a terminal `$ aris run …`, a wandb curve label, a
  `REJECT`/`ACCEPT` stamp, a DDL chip, a MacBook screen UI) — these are *figure literals*, not bubbles, and
  for a `baked` panel they go into `expected_literals` verbatim.
- It reserves the bubble real-estate as **low-detail negative space** (a soft gradient / empty desk surface /
  sparse sky band) and records those rectangles as `safe_zones[]` (normalized `{id,x,y,w,h}`, 0..1) so the
  `html` bubble layer has a clean budget to anchor into.
- **`text_mode:"html"` that bakes a bubble inside the SVG = HARD FAIL.** **`content_svg:null` = HARD FAIL.**

"If a human might edit / translate / reposition it, it's HTML; if it's a pixel object in the world, it's art."
(COMIC_PIVOT §3.5 TEXT SPLIT RULE.) Whole bubbles + captions + narration + headers → 100% HTML; only diegetic
signage may be baked.

## Two fail-closed engine contracts (author to these or the run is refused)
1. **Every panel needs a `condition.content_svg`** — a deterministic blueprint, *figure OR layout*. A
   dialogue/scene panel with no audited numbers still needs a **layout blueprint** (the scene composition with
   the safe zones drawn as low-detail regions). Never `content_svg:null`; the engine rejects it.
2. **A `baked` figure-panel MUST declare `condition.expected_literals`** — the exact numbers/keys baked into
   the figure, **verbatim, ASCII-tokenizable** (`["REJECT","+6.2","T-16:05"]`). These are what the gate's
   blind reviewers transcribe and a token-diff checks. A scene panel with **no** audited numbers →
   `text_mode:"html"` with `expected_literals:[]`. (Decorative non-ASCII like a `✓` is **out of contract** —
   gate only the ASCII tokens; e.g. S20 gates `"SUBMITTED"`, the `✓` is decorative.)

## Procedure (an agent can execute this step by step)

### ⓪ Preflight — resolve the panel + assert its refs are locked
1. Load the upstream `panel_spec` node (`node_type=="panel_spec"`); read `panel_id`, `page_id`, `world`,
   `text_mode`, `expected_literals`, `content_blueprint` (the storyboard's blueprint *plan*: which asset to
   instantiate + the literals layout), `asset_ids[]`, `bubbles`, `side_narration`, `motifs`.
2. Build `input_refs = dedup([scene] + characters[] + props[] + text_refs[])` from `asset_ids[]`; **assert
   non-empty** ("must reference ≥1 scene/character"). For each ref load its `asset` node and **HARD-FAIL if
   `status != locked` or `output_ref` (file_path/data_url) is missing** — a blueprint may only compose
   *locked, immutable* assets (ported from frame-condition-builder Phase 0; refs are never mutated). The
   `identity_ref` (the canonical duo/trio sheet) is the identity authority and is **always** present, never
   dropped.
3. Read `ART_BIBLE.md` (§0.5 two-world warm/dark palette, §1 per-character hex iron-law, §6.5 density target)
   and `comic.json` `ui_tokens` — these define the palette your generator imports and the rubric the gate
   scores against. The bible is the convergence target; without it the gate emits "style drift" forever.

### ① Text-mode bifurcation — decide what the gate will check
4. If the panel carries audited numbers/keys baked as diegetic figure literals → **`text_mode:"baked"`**;
   populate `expected_literals` **verbatim** from the storyboard's `content_blueprint` (the salient *large*
   tokens only — the blind-transcribe threshold). A concrete authoring trick ported from
   frame-condition-builder: extract the text-whitelist by matching `content_blueprint` keys
   (`"text:"`, `"label:"`, `"title:"`, quoted strings) into the explicit literal list.
5. If the panel is a pure scene / dialogue beat with **no** audited numbers → **`text_mode:"html"`**,
   `expected_literals:[]`; the generator draws scene + characters + safe zones only.
6. **Sub-threshold exclusions are explicit:** a literal that is sized below the blind-transcribe threshold
   (e.g. the S09 `Tok|yo` chip at ≤1.5% frame height, "foreshadow, not gated") is drawn but is **NOT** in
   `expected_literals`. Skill-name tags (`novelty-check`, `experiment-bridge`, …) **never** appear in-frame
   (the 铁律) — they live only in side-narration metadata.

### ② Write the Python generator (the load-bearing step)
7. Create / extend `gen__blueprints.py` next to `asset_lib.py`. Import **only** from `asset_lib`
   (single source per recurring token) + the pinned palette constants. Define the local `w(name, content)`
   emitter (`open(.../assets/name,"w").write(content); print("wrote", name)`) — exactly the
   [`gen_b06_blueprints.py`](../../examples/comic_m3_audit/gen/gen_b06_blueprints.py) shape.
8. Build the panel body as a Python list `o = [...]` of element strings (rects, text, paths, imported
   builders like `ddl_chip(...)`, `stamp(...)`, `curve_panel(...)`, `tokyo_chip(...)`, `verdict_card(...)`).
   Compute coordinates **in code** (loops, fractions of `W`/`H`) so they are deterministic and reproducible —
   never eyeball pixel positions. Wrap with `svg_doc(W, H, "".join(o))`.
9. **Draw NO bubbles** (Headline rule). Draw the diegetic figure(s) carrying the `expected_literals`. Reserve
   the bubble real-estate as low-detail negative space and note the rectangles for `safe_zones`.
10. **Reuse the canonical builder** for any cross-panel motif (mirror-pair verdict cards, the one star-map
    coordinate source, the DDL chip family) — do not re-implement it locally; that is what would split the
    visual dialect. (E.g. S11 `REJECT` and S16 `ACCEPT` BOTH call `verdict_card(...)` so they are a precise
    geometric mirror; S16b labeled star-map and S22 wordless constellation BOTH derive from
    `STARMAP_NODES` — "禁目测 / no eyeballing".)
11. Run the generator. Inspect the emitted `.svg` (and, if rasterizing to PNG to eyeball, do it via headless
    Chrome — never re-hand-edit the SVG; fix the *script*).

### ③ Single-source + zero-text gates (deterministic, pre-gate)
12. **MANDATORY: run `check_asset_collisions.py`** — it statically scans every `gen_*.py` for `w("…")` writes
    and fails if any output filename has >1 owner. Must exit 0 (`✓ no asset collisions — all N generator
    outputs are single-source`). If a specialized generator now owns a richer version of a shared asset,
    **delete the dead duplicate** (the inline ownership-transfer discipline) rather than letting two writers
    race (run-order corruption — "skip if file exists" is explicitly rejected because it hides the bug).
13. For a **no-text** panel (constellation, wordless geometric twin) add a build-time assert in the emitter:
    `assert "`, `status: under_review`). Payload **must** carry the
    9 required fields (`schemas/node_schema.json` → `blueprint`):
    `source_panel_id` · `content_svg` (relative path to the emitted SVG) · `expected_literals` (verbatim;
    `[]` for html) · `safe_zones[]` (`{id,x,y,w,h}` 0..1) · `html_bubbles[]` (the JSON-view bubbles, anchored
    to a named safe-zone, each with `text{zh,en}` — these are **NOT** baked) · `crop` (`shape ∈
    {hero16x9|wide|tall|square}` + window) · `negative_space_policy` (where the empty bubble real-estate is
    and why it is low-detail) · `generator_script` (the `gen_*.py` path — provenance, so the SVG is
    reproducible) · `file_sha256` (of the emitted SVG). Add edges (`type` ∈ `cli/validate_wiki.py` EDGE_TYPES):
    `derived_from` (`blueprint:` → `panel:`, the panel_spec node's real id; `source_panel_id` payload = that same `panel:`) and one `uses_asset` (`blueprint:` →
    `asset:`) per locked asset ref. (`composes_from` is NOT a legal edge type — it is a video-era leftover
    that survives only in `docs/history/legacy-video-arch.md`; the live author-layer asset-composition verb is
    `uses_asset`. Writing `composes_from` makes `python3 cli/validate_wiki.py` exit 1.)
15. Mirror the authored condition into `comic.json` under `panels..condition`: `content_svg`,
    `expected_literals`, `world`, `identity_ref`, `identity_desc` (the §1 hex iron-law restated),
    `characters`, `scene` (the §0.5 / §6.5 rubric restated per panel) — and `panels..safe_zones[]` +
    `panels..text_mode`. (See the worked `condition` blocks in
    [`examples/comic_m3_audit/comic.json`](../../examples/comic_m3_audit/comic.json), e.g. S01, S02, S08, S22.)

### ⑤a Pre-gate fan-out — raster + reviewers + `review` nodes (NEVER call the gate cold)
`comic-cross-layer-gate` is a **score-FUSER**: it reads pre-existing `review:*` nodes via `reviews` edges and
**HARD-FAILS if zero reviews are attached** (`no reviews — gate is a score-fuser, not a reviewer`). So you must
fan out the reviewers and persist them FIRST — the gate collects, it does not run them:
15a. **Rasterize the emitted SVG to a non-empty PNG** via headless Chrome (this also pre-confirms the
    `blueprint_renders` RAW check). Run the deterministic pre-gate checks once more: `check_asset_collisions.py`
    exit 0; zero-text assert for no-text panels.
15b. **Fan out the independent reviewers** on the blueprint dims (`refs_present`, `spatial_correctness`,
    `blueprint_renders`, `text_preservation` [only when baked text], `safezone_quality`), file paths only,
    behind an `=== EXTERNAL CONTEXT (advisory) ===` fence, NEVER the author's `expected_literals` list:
    **Claude render/lint ‖ Codex `gpt-5.5` `xhigh` ‖ Gemini `auto-gemini-3`**
    ([`reviewer-routing`](../../protocols/reviewer-routing.md)).
15c. **Write one `review` node per reviewer** (`node_id: review:`, `node_type:"review"`). Payload **must**
    carry the 3 required fields from `cli/validate_wiki.py` `PAYLOAD_REQUIRED["review"]` — `target_node_id`
    (the `blueprint:`), `reviewer`, `gate_kind:"blueprint"` — plus `review_scores:{dim:score,...}` (the
    per-dim numbers the fuser reads). Append a **`reviews` edge `review:` → `blueprint:`** (the
    direction the gate walks — NOT `reviewed_by`, which points the other way and the fuser would miss).

### ⑤b Cross-layer gate (fuse + adjudicate)
16. **Only now** run `comic-cross-layer-gate  --gate blueprint`. It fuses the `review:*` scores
    (min per dim), runs the Codex adjudicator on scores + paths + a verbatim slice + the verbatim rubric, and
    emits `VERDICT= GATE=blueprint TARGET=`. Route on the gate's THREE real verdicts:
    - **`approve`** → the gate flips the `blueprint` node to `status: locked` (the cross-layer hand-off token).
    - **`revise`** → **edit the generator script** and re-emit (never hand-patch the SVG), redo ⑤a (new
      reviews), re-gate. Counts against `MAX_BLUEPRINT_REGEN = 3`; on the cap, escalate to **rewrite the
      `panel_spec`** (the gate's own escalation target), not infinite regen.
    - **`fallback`** (`safezone_quality `, `node_type:"blueprint"`,
  `status: under_review → locked`). **Required payload (9 fields, verbatim from `cli/validate_wiki.py`
  `PAYLOAD_REQUIRED["blueprint"]`):** `source_panel_id`, `content_svg`, `expected_literals`, `safe_zones`,
  `html_bubbles`, `crop`, `negative_space_policy`, `generator_script`, `file_sha256`. **Edges (every `type`
  ∈ `validate_wiki.py` EDGE_TYPES):** `derived_

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [wanshuiyin](https://github.com/wanshuiyin)
- **Source:** [wanshuiyin/ARIS-Movie-Director](https://github.com/wanshuiyin/ARIS-Movie-Director)
- **License:** MIT
- **Homepage:** https://wanshuiyin.github.io/ARIS-Movie-Director/comic/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-wanshuiyin-aris-movie-director-comic-blueprint-author
- Seller: https://agentstack.voostack.com/s/wanshuiyin
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
