Install
$ agentstack add skill-wanshuiyin-aris-movie-director-comic-blueprint-author ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
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/comicm3audit/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.
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.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.