Comic blueprint author
Skill wanshuiyin/ARIS-Movie-Director/skills/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 bake condition (reference #1 of the agent mcp__codex__codex sidecar bake), 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. Every panel gets a content_svg (a figure OR a layout blueprint); a baked figure-panel must declare expected_literals verbatim. Single-source collision check is mandatory. Gated by comic-cross-layer-gate --gate blueprint. Use when the storyboard is locked and you need each panel's deterministic generation condition; do NOT use to bake the panel (that is comic-director) or to write the verdict-stamp/curve assets (that is comic-asset-ref-generator).From its SKILL.md
npx -y skills add wanshuiyin/ARIS-Movie-Director --skill comic-blueprint-authorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- runs commandsInstructs the agent to run 2 commands, including `check_asset_collisions.py` and 1 more.
SKILL.md
31.7 KB, ~8.2k tokens by cl100k_base, as published. Nobody here has run it
comic-blueprint-author — one panel_spec → a deterministic content-SVG blueprint (S7)
The per-panel condition author of the comic-author Phase-1 suite. It sits
between the storyboard (comic-storyboard-creator) and the bake
(comic-director): for ONE locked panel_spec it produces ONE deterministic
content_svg — the answer-key composition image the bake conditions on. Concretely (the live seam,
run_comic.py + pickup_image.build_bake_prompt): the engine renders the SVG to a PNG via headless Chrome,
and the agent-side mcp__codex__codex sidecar bake embeds that render as the FIRST of exactly two
absolute-path reference images written literally into the bake prompt (ref #1 = the blueprint render,
ref #2 = the canonical identity sheet — the mcp schema has NO -i image param, so paths ride in the text),
sandbox: workspace-write, and the returned PNG is verified by pickup_image.py --out-existing.
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 +
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 <blueprint_id> --gate blueprint (FUSES the reviews)
│ refs_present≥4 ∧ spatial_correctness≥4 ∧ blueprint_renders≥4 (+ text_preservation
│ if baked) → approve · safezone_quality<3 → fallback(→HTML) · MAX_REGEN=3
│ hard veto: image-bubble text · content_svg:null
verdict? ─ revise ─▶ ② (fix the generator, re-emit) ─ fallback ─▶ route text→HTML, re-emit
│ approve ─▶ gate flips status=locked
Constants
- AUTHORING MEDIUM = a Python generator script, not raw SVG. One
gen_<panel>_blueprints.pyper panel (or per beat-group), importing the single-source builders fromasset_lib.py. Never paste SVG coordinates into chat and never hand-edit the emitted.svg— re-run the script. - PALETTE PINNED to
comic.jsonui_tokens(and the per-character hexes inART_BIBLE.md§1 / §0.5), imported as constants fromasset_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 upstreampanel_specnode; write ablueprintnode (blueprint:<slug>). - GATE =
comic-cross-layer-gate <blueprint_id> --gate blueprint— the SOLE gate authority; verdicts{approve, revise, fallback}, ADVANCE iffrefs_present ≥ 4 ∧ spatial_correctness ≥ 4 ∧ blueprint_renders ≥ 4(+text_preservation ≥ 4when baked text),safezone_quality < 3 → fallback, capMAX_BLUEPRINT_REGEN = 3(see EXACT gate). The gate is a score-FUSER — you must FIRST fan out the reviewers (Claude render/lint ‖ Codex atxhigh(no model pin — the CLI follows the local codex config) ‖ Geminiauto-gemini-3) and write theirreview:*nodes +reviewsedges (⑤a), never call it cold. The author never self-acquits. - SINGLE-SOURCE = mandatory —
check_asset_collisions.pymust exit 0 (no filename written by >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) and you do NOT compose the final text prompt (that'scomic-panel-prompt-builder).
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, aREJECT/ACCEPTstamp, a DDL chip, a MacBook screen UI) — these are figure literals, not bubbles, and for abakedpanel they go intoexpected_literalsverbatim. - 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 thehtmlbubble 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)
- 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). Nevercontent_svg:null; the engine rejects it. - A
bakedfigure-panel MUST declarecondition.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"withexpected_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
- Load the upstream
panel_specnode (node_type=="panel_spec"); readpanel_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. - Build
input_refs = dedup([scene] + characters[] + props[] + text_refs[])fromasset_ids[]; assert non-empty ("must reference ≥1 scene/character"). For each ref load itsassetnode and HARD-FAIL ifstatus != lockedoroutput_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). Theidentity_ref(the canonical duo/trio sheet) is the identity authority and is always present, never dropped. - Read
ART_BIBLE.md(§0.5 two-world warm/dark palette, §1 per-character hex iron-law, §6.5 density target) andcomic.jsonui_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
- If the panel carries audited numbers/keys baked as diegetic figure literals →
text_mode:"baked"; populateexpected_literalsverbatim from the storyboard'scontent_blueprint(the salient large tokens only — the blind-transcribe threshold). A concrete authoring trick ported from frame-condition-builder: extract the text-whitelist by matchingcontent_blueprintkeys ("text:","label:","title:", quoted strings) into the explicit literal list. - 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. - Sub-threshold exclusions are explicit: a literal that is sized below the blind-transcribe threshold
(e.g. the S09
Tok|yochip at ≤1.5% frame height, "foreshadow, not gated") is drawn but is NOT inexpected_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)
- Create / extend
gen_<panel>_blueprints.pynext toasset_lib.py. Import only fromasset_lib(single source per recurring token) + the pinned palette constants. Define the localw(name, content)emitter (open(.../assets/name,"w").write(content); print("wrote", name)) — exactly thegen_b06_blueprints.pyshape. - Build the panel body as a Python list
o = [...]of element strings (rects, text, paths, imported builders likeddl_chip(...),stamp(...),curve_panel(...),tokyo_chip(...),verdict_card(...)). Compute coordinates in code (loops, fractions ofW/H) so they are deterministic and reproducible — never eyeball pixel positions. Wrap withsvg_doc(W, H, "".join(o)). - 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 forsafe_zones. - 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
REJECTand S16ACCEPTBOTH callverdict_card(...)so they are a precise geometric mirror; S16b labeled star-map and S22 wordless constellation BOTH derive fromSTARMAP_NODES— "禁目测 / no eyeballing".) - 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)
- MANDATORY: run
check_asset_collisions.py— it statically scans everygen_*.pyforw("…")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). - For a no-text panel (constellation, wordless geometric twin) add a build-time assert in the emitter:
assert "<text" not in svg, "constellation must contain ZERO text"— the S22 zero-text contract. (Also forbid re-layout-by-eye on shared coordinate JSON via a_contractfield.)
④ Write the blueprint node
- Write a
blueprintnode (node_id: blueprint:<slug>,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 withtext{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(thegen_*.pypath — provenance, so the SVG is reproducible) ·file_sha256(of the emitted SVG). Add edges (type∈cli/validate_wiki.pyEDGE_TYPES):derived_from(blueprint:<slug>→panel:<slug>, the panel_spec node's real id;source_panel_idpayload = that samepanel:<slug>) and oneuses_asset(blueprint:<slug>→asset:<id>) per locked asset ref. (composes_fromis NOT a legal edge type — it is a video-era leftover that survives only indocs/history/legacy-video-arch.md; the live author-layer asset-composition verb isuses_asset. Writingcomposes_frommakespython3 cli/validate_wiki.pyexit 1.) - Mirror the authored condition into
comic.jsonunderpanels.<id>.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) — andpanels.<id>.safe_zones[]+panels.<id>.text_mode. (See the workedconditionblocks inexamples/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 at xhigh (no model pin — follows the local codex config) ‖ Gemini
auto-gemini-3 (reviewer-routing).
15c. Write one review node per reviewer (node_id: review:<slug>, node_type:"review"). Payload must
carry the 3 required fields from cli/validate_wiki.py PAYLOAD_REQUIRED["review"] — target_node_id
(the blueprint:<slug>), reviewer, gate_kind:"blueprint" — plus review_scores:{dim:score,...} (the
per-dim numbers the fuser reads). Append a reviews edge review:<slug> → blueprint:<slug> (the
direction the gate walks — NOT reviewed_by, which points the other way and the fuser would miss).
⑤b Cross-layer gate (fuse + adjudicate)
- Only now run
comic-cross-layer-gate <blueprint_id> --gate blueprint. It fuses thereview:*scores (min per dim), runs the Codex adjudicator on scores + paths + a verbatim slice + the verbatim rubric, and emitsVERDICT=<v> GATE=blueprint TARGET=<id>. Route on the gate's THREE real verdicts:approve→ the gate flips theblueprintnode tostatus: 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 againstMAX_BLUEPRINT_REGEN = 3; on the cap, escalate to rewrite thepanel_spec(the gate's own escalation target), not infinite regen.fallback(safezone_quality < 3, floor otherwise passing) → a route switch, NOT a regen: set the paneltext_mode:"html",expected_literals:[], drop any whitelisted baked text, re-emit the layout-blueprint with clean safe zones, then redo ⑤a and re-gate.- Hard veto (image-bubble text /
content_svg:null, computed in the gate's ⓪ pre-check) → the run is refused; remove the baked bubble or supply a real blueprint, then redo ⑤a. Escalate to human after repeated same-root failure (acceptance-gate: the loop can drive but cannot acquit).
EXACT gate (--gate blueprint) — the BINDING contract is owned by comic-cross-layer-gate
The single source of truth for --gate blueprint is
comic-cross-layer-gate (§--gate blueprint). This skill MUST NOT invent
a parallel rubric — it quotes the gate's real contract verbatim and routes on the gate's real verdicts.
Verdicts: {approve, revise, fallback} (the IMAGE analog of aris_movie's frame_condition gate; the
motion dims are dropped). The gate is a score-FUSER over reviews-edge review:* nodes (see ⑤a below); it does
not re-run reviewers.
The 5 real dims (0–5 scale) and the ADVANCE logic (mirror comic-cross-layer-gate exactly):
refs_present≥ 4 — every composed ref resolves and wasstatus: locked.spatial_correctness≥ 4 — subject / eyeline / figure / safe-zone geometry is laid out correctly (the composition is buildable, nothing cramped or colliding, reading order sane).blueprint_renders≥ 4 — the SVG rasterizes to a non-empty PNG (a RAW pre-check the gate computes, not a vote).text_preservation— required ONLY when the panel has whitelisted baked text; for anhtmlpanel it is N/A (null). When present: eachexpected_literalis drawn verbatim and ASCII-tokenizable.safezone_quality(html panels) — a clean low-detail region exists where eachhtmlbubble anchors.
ADVANCE (approve) iff refs_present ≥ 4 AND spatial_correctness ≥ 4 AND blueprint_renders ≥ 4 (with
text_preservation ≥ 4 additionally required when baked text is whitelisted). The advance rule is ASYMMETRIC —
those three (plus conditional text_preservation) gate; the rest ride into the audit.
fallback verdict: safezone_quality < 3 while the floor otherwise passes ⇒ fallback = route the
panel's text to the HTML overlay (a route switch, NOT a regen): set the panel text_mode:"html",
expected_literals:[], drop any whitelisted baked text, and re-emit the layout-blueprint with clean safe zones.
Handle this in step ⑤ — do not treat fallback as a hard veto or as a normal regen.
Cap: MAX_BLUEPRINT_REGEN = 3 regenerations → escalate to rewrite the panel_spec (not infinite regen).
Structural hard vetoes the gate's ⓪ pre-check computes (RAW, not a vote — any one → revise/refuse):
- Any image-bubble text in the blueprint — a speech bubble / tail / caption / narration / dialogue-label
drawn into the SVG (
text_mode:"html"with a baked bubble is the canonical violation). The image bakes NO bubbles. content_svg:null(or missing) — every panel must have a blueprint.- A
bakedpanel with empty/absentexpected_literals; a stray non-diegetic glyph in a declared no-text panel (constellation must be zero-text).
(Advisory, NON-binding) what each real dim is checking on this static condition — this is explanatory prose, not a separate rubric; the binding floors are the 5 dims above:
- content_authority → folded into
spatial_correctness+ (baked)text_preservation: the blueprint pins the diegetic figure(s), composition/camera/layout, and everyexpected_literalverbatim. - literal_contract →
text_preservation:baked⇒ non-empty ASCII literals matching drawn figure literals;html⇒expected_literals:[]and no audited numbers drawn. - negative_space_overlay_safety →
safezone_quality:safe_zones[]present, non-overlapping, over genuinely low-detail pixels (not over a face / busy figure). - layout_readability →
spatial_correctness: one-glance legibility; no motion to clarify a static panel. - deterministic_asset_integrity →
refs_present+ the deterministic pre-gatecheck_asset_collisions.py: every recurring motif renders from its single canonicalasset_libbuilder (mirror-pairs identical, star-map from one coordinate source). - style_bible_alignment → rides into the audit (scored against
ART_BIBLE.md§0.5 / §1 / §6.5, not a vibe): palette pinned toui_tokens; warm/dark split is by-design, not drift — only a mis-colored world flags.
Reviewers (cross-model, independent) — the upstream fan-out in ⑤a, NOT the gate itself: Claude render/lint
(does the SVG render? do the literals appear? are safe zones over low-detail pixels? any baked bubble?) ‖ Gemini
visual (auto-gemini-3) ‖ Codex at xhigh (no model pin — follows the local codex config; only the BAKE pins
a model, gpt-5.5 + xhigh via run_comic.get_bake_plan()'s bakereq/v1 compat default — a config-driven
override is planned, not shipped). Each writes a review:* node; the gate COLLECTS them via
reviews edges and hard-fails if none exist. The author (this agent) never self-acquits; ACCEPT needs the
deterministic checks clean and the fused cross-model verdict to advance.
Node read / written (per schemas/node_schema.json)
- Reads: the upstream
panel_specnode (node_type:"panel_spec") — required payload incl.source_storyboard_id, page_id, panel_id, sequence_index, page_type, world, asset_ids, text_mode, expected_literals, content_blueprint, bubbles, side_narration, motifs; plus each referencedassetnode (must bestatus:locked,output_refpresent) and themotif_ledger/continuity_constraintnodes that pin this panel's required values. - Writes: ONE
blueprintnode (node_id: blueprint:<slug>,node_type:"blueprint",status: under_review → locked). Required payload (9 fields, verbatim fromcli/validate_wiki.pyPAYLOAD_REQUIRED["blueprint"]):source_panel_id,content_svg,expected_literals,safe_zones,html_bubbles,crop,negative_space_policy,generator_script,file_sha256. Edges (everytype∈validate_wiki.pyEDGE_TYPES):derived_from(blueprint:<slug>→panel:<slug>, the panel_spec node's real id;source_panel_idpayload = that samepanel:<slug>); oneuses_asset(blueprint:<slug>→asset:<id>) per lockedasset. (On regenerate: mark the prior blueprintstatus: supersededand add asupersedesedge (newblueprint:<slug>→ priorblueprint:<slug>); record the reason into afailure_modenode and add afailure_ofedge (fail:<slug>→ the priorblueprint:<slug>) — the positive-invariant memory.composes_fromandrepairs_failure_modeare NOT legal edge types and will failvalidate_wiki.py;uses_asset/supersedes/failure_ofare their real-vocabulary replacements.)
Worked example
Ground every blueprint in the real ARIS-Movie-Director comic
(examples/comic_m3_audit/) — copy these exact patterns:
-
The single-source builder library —
gen/asset_lib.py. ONE parameterized function per recurring token, palette pinned at the top as module constants:MONO = "JetBrains Mono, Menlo, monospace" RED = "#FF3366"; AMBER = "#FFB000"; GREEN = "#00C896"; VOID = "#0A0E27"; INK = "#E4E9F7"; ... def ddl_chip(x, y, t, state="amber", skin="corner", ...): ... # the SINGLE timeline authority def stamp(x, y, label, scale=1.0, tilt=0): ... # geometry matches the baked S15 stamp def verdict_card(x, y, header, body_rows, stamp_label, ...): ...# S11 REJECT & S16 ACCEPT = SAME card (mirror) def tokyo_chip(x, y, scale=1.0, repaired=False): ... # one source, multi-scale STARMAP_NODES = [...] # ONE coord truth for S16b + S22Cross-panel reuse contracts live in the docstrings ("S11 REJECT ↔ S16 ACCEPT = same card mirror pair"; "star-map JSON is the one truth source for S16b + S22"). Copy this: never re-implement a motif locally — import the builder so the film keeps one visual dialect.
-
A per-panel blueprint generator —
gen/gen_b06_blueprints.py. The exact shape to mirror:sys.path.insert(0, …)→from asset_lib import (svg_doc, text, rect, ddl_chip, tokyo_chip, …)→ a localw(name, content)emitter → buildo = [...]with coordinates computed in code (the GPU grid is afor i in range(8)loop overcol,row), call imported builders for the shared chips,w("…_v1.svg", svg_doc(W, H, "".join(o))). Note the deterministic seed for the log-wall (_r.seed(7)) and the sub-gateTok|yomicro chip drawn atscale=0.27with no callout, no arrow — it is the foreshadow, deliberately below the transcription threshold, so it is NOT inexpected_literals. -
The mandatory single-source gate —
gen/check_asset_collisions.py. Regex\bw\(\s*["']([^"']+\.(?:svg|json|png))["']over everygen_*.py; exit 1 if any filename has >1 writer. Verified clean on the real comic: 26 outputs, all single-source. Run this before the gate, every time. -
The blueprint → gate join in the IR —
comic.json. Thepanels.<id>.conditionblock is the contract surface this skill writes. Two canonical shapes to copy:bakedfigure-panel (S08, line ~519):"content_svg": "assets/handoff_terminal_v1.svg","expected_literals": ["T-24:00:00","ARIS ONLINE","$ aris run"],world,identity_desc(§1 hex iron-law restated),characters,scene(§0.5 seam + §6.5 density restated). The bubbles (这一轮交给 ARIS 了/交给我!/我盯着。) are NOT in the condition — they arehtml_bubbles.htmlno-text panel (S22 constellation, line ~752):"content_svg": "assets/constellation_layout_v1.svg","expected_literals": [],"characters": "NONE. No characters, no figures, no text anywhere in the image", plus asafe_zonesband for the HTML tagline. The blueprint bakes zero glyphs (build-timeassert "<text" not in svg).
Hard do / don't (earned lessons)
- DO author every blueprint by writing a Python generator that imports from
asset_lib— LLMs botch raw SVG coordinates; the script is deterministic and re-runnable, the SVG is its output. - DO draw NO bubbles in the image — only characters + scene + diegetic figures + low-detail safe
zones. HTML/CSS owns the entire bubble. (
text_mode:"html"with a baked bubble = hard fail.) - DO give every panel a
content_svg(figure OR layout). Nevercontent_svg:null. - DO declare
expected_literalsverbatim, ASCII-tokenizable for everybakedfigure-panel; a scene with no audited numbers →text_mode:"html"with[]. - DO run
check_asset_collisions.py(exit 0) before the gate — single canonical owner per token; delete dead duplicates rather than letting two generators race. - DO reuse the one canonical builder for every cross-panel motif (mirror-pair cards, the single star-map coordinate source) — that is the only defence against discrete style scatter (risk R13).
- DON'T hand-edit the emitted
.svgto "fix" a coordinate — fix the generator script and re-emit; a hand-patched SVG is no longer reproducible from itsgenerator_script. - DON'T bake skill-name tags, sub-threshold chips, or decorative
✓intoexpected_literals(only the salient large ASCII tokens are gated; skill names never appear in-frame at all). - DON'T flag the warm/dark world split as drift — it is by-design (§0.5); only a mis-colored world is a violation (design-aware gate).
- DON'T compose an unlocked / mutated asset ref — a blueprint may only compose
status=lockedassets, and never mutates an asset node. - DON'T self-acquit — the blueprint gate is cross-model (CC ‖ Gemini ‖ Codex); the author drives the loop but cannot ACCEPT its own blueprint.
Protocols (governance contracts this skill honors)
reviewer-independence— the upstream pre-gate reviewers (⑤a: Claude ‖ Codex ‖ Gemini, whosereview:*nodes the gate later fuses) get the SVG / PNG + an=== EXTERNAL CONTEXT (advisory) ===fence and blind-transcribe the literals; they never see the author'sexpected_literalslist. The gate fuses scores + paths only — never reviewer prose.acceptance-gate— the loop can DRIVE but can't ACQUIT: alockedblueprint needs the deterministic checks clean (collision-free, literals coherent, zero-text where required) and the cross-model panel to pass; the author never self-acquits.artifact-integrity— the agent that authors a blueprint does not judge its own blueprint's correctness; numbers are originated by the storyboard and verified by the blind diff, never invented at blueprint time.reviewer-routing— Codex atxhigh(no model pin — the reviewer follows the local codex CLI config); Geminiauto-gemini-3; never downgrade the effort tier.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.