wanshuiyin/ARIS-Movie-Director/skills/comic-asset-ref-generator/SKILL.md
comic-asset-ref-generator
Phase-1 (S4) of a comic movie — PRODUCE the canonical reusable references the whole spiral conditions on. Per asset it bakes ONE canonical 1:1 white-bg identity ref via the agent mcp__codex__codex sidecar bake (Codex native image_gen — conditioned, never hand-pasted) OR, for a deterministic motif (clock/chart/stamp/star-map), emits a single-source parametric SVG from a python generator (asset_lib.py). Hashes + base64-encodes the bake into the asset node's output_ref (all 6 fields or it's a schem
- Source repository stars
- 54
- Declared platforms
- 1
- Static risk flags
- 2
- Last source update
- 2026-08-18
- Source checked
- 2026-08-25
Decision brief
What it does: where it fits
The missing Layer-2 of comic-author: take the storyboard's consolidated ASSETREQUESTS and produce, for every declared asset, the one canonical artifact that every downstream panel bake will condition on — so the film never grows two visual dialects and every panel of the same ch…
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Declared | Source record | Install path and trigger |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/wanshuiyin/ARIS-Movie-Director --skill "skills/comic-asset-ref-generator"Inspect the Agent Skill "comic-asset-ref-generator" from https://github.com/wanshuiyin/ARIS-Movie-Director/blob/42dc1f9ee374019ea8c75e57f3849808bd26fc48/skills/comic-asset-ref-generator/SKILL.md at commit 42dc1f9ee374019ea8c75e57f3849808bd26fc48. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
Procedure (numbered — an agent runs this top to bottom)
Read the asset node. Assert nodetype=="asset". Abort if reviewstatus=="locked" ("immutable per asset gate") or reviewstatus=="approved" unless --regenerate. Classify the asset by assetkind into its production route: character | scene | prop (with a non-deterministic appearance)…
Run the STYLE-BIBLE / banned-vocab filter (see below) over visualdescription; rewrite inline andCompose the prompt (template):Emit the request (orchestrator role). First write the composed P1r prompt body to - 02
P4 · Hand off to the review loop (NEVER self-acquit)
Single mode: trigger comic-asset-review-loop, await its verdict (approve → loop locks it / regenerate → recursive --regenerate callback, bounded by MAXREVIEWROUNDS=3). Batch mode: fire the review loop once at the end. This skill never writes reviewstatus:"locked" — locking is th…
Single mode: trigger comic-asset-review-loop, await its verdict (approve → loop locks it / regenerate → recursive --regenerate callback, bounded by MAXREVIEWROUNDS=3). Batch mode: fire the review loop once at the end. T… - 03
Constants
GENERATOR (raster route) = the agent's mcpcodexcodex sidecar bake: model: "gpt-5.5",
GENERATOR (raster route) = the agent's mcpcodexcodex sidecar bake: model: "gpt-5.5",SVG SOURCE (deterministic route) = a pure-python parametric builder living in gen/assetlib.py, emittedMAXGENRETRIES = 2 (3 attempts total) — on ok:false OR a missing / zero-byte / invalid PNG - 04
Input contract (3 modes)
This skill never invents an asset id. If the outline/storyboard did not declare it, the operator adds it at the outline layer (comic-outline-creator) — not here.
single assetid matching ^asset:[a-z0-9-]+$ — produce one asset, await its verdict.--batch-from-outline — read the human-approved outline's characterassetids[] +--regenerate --reason "" — force a re-render even if approved/rejected; prefix the - 05
What it reads / what it writes (wiki nodes & edges)
Per schemas/nodeschema.json (node/comic/3.0):
READS storyboardspec (payload.consolidatedassetrequests, payload.globalpolicies) andWRITES / MUTATES the asset node (nodeid: ^asset:[a-z0-9-]+$, nodetype:"asset"). RequiredEDGES (schemas/edgeschema.json, src/dst/type): on a regenerate,
Permission review
Static risk signals and limitations
Writes files
The documentation asks the agent to create, modify, or delete local files.
pattern — AND actually write that second node file so both endpoints resolve.)Runs scripts
The documentation asks the agent to run terminal commands or scripts.
python3 -c 'import sys, time; sys.path.insert(0, "skills/method-figure/scripts"); \Runs scripts
The documentation asks the agent to run terminal commands or scripts.
python3 -c 'import sys, json; sys.path.insert(0, "skills/method-figure/scripts"); \Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 93/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 54 | Source | Repository attention, not individual Skill quality |
| Compatibility | 1 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- wanshuiyin/ARIS-Movie-Director
- Skill path
- skills/comic-asset-ref-generator/SKILL.md
- Commit
- 42dc1f9ee374019ea8c75e57f3849808bd26fc48
- License
- MIT
- Collected
- 2026-08-25
- Default branch
- main
View the original SKILL.md
comic-asset-ref-generator — the Reference Producer (Phase 1 · S4)
The missing Layer-2 of comic-author: take the storyboard's consolidated ASSET_REQUESTS and produce, for
every declared asset, the one canonical artifact that every downstream panel bake will condition on —
so the film never grows two visual dialects and every panel of the same character/prop/motif reads as the same
thing. This is precisely the producer of comic.json's identity_refs (e.g. duo_canonical_ref_v001.png +
per-character locks like executor.hoodie #1D4684 / reviewer.beard true) that the proven comic created by
hand. It is the upstream sibling of comic-blueprint-author (which
authors per-panel content-SVGs) and feeds comic-asset-review-loop and
the comic-director spiral.
Two asset classes, two production routes — this is the load-bearing fork:
storyboard.consolidated_asset_requests
├── identity / scene / prop ──▶ RASTER ref: agent mcp__codex__codex sidecar bake, CONDITIONED on a labeled white-bg
│ (a face, a hoodie, condition + real identity refs → 1:1 (or 16:9 scene) PNG
│ an empty room) → output_ref{file_path,data_url,sha256,width,height,mime} (ALL 6)
│
└── deterministic motif ──▶ SVG SOURCE: ONE parametric builder in asset_lib.py
(clock / chart / stamp (ddl_chip / stamp / mug / curve_panel / tokyo_chip / starmap …)
/ mug / star-map) the "ref" IS the single-source SVG — NOT an image bake
→ records generator_script, owner_script, file_sha256
both routes → review_status:"pending" (NEVER "locked" here) → collision gate → asset-review-loop
The battle lesson, landed as the fork above: a clock, a chart, a verdict stamp, a star-map is deterministic
content — you do NOT bake it as a fuzzy image and hope the digits land; you build it once from a python
generator (asset_lib.py) so all 18 instances of the DDL timeline, all 7 verdict stamps, both the labeled and
the wordless star-map render from the same coordinates and never diverge. An identity (a face, a costume) is
not deterministic — that you bake once via the sidecar bake (Codex's native image tool), conditioned on a
labeled white-bg reference and the real identity refs, never a free prompt, never a hand-paste.
Constants
- GENERATOR (raster route) = the agent's
mcp__codex__codexsidecar bake:model: "gpt-5.5",config{ model_reasoning_effort: "xhigh", include_image_gen_tool: true },sandbox: "workspace-write", plusapproval-policy: "never"— an mcp-call argument, NOT a.bakereq.jsonfield (the sidecar payload carries no such key). This exact shape is the empirical v3.5 lesson: withoutworkspace-write+approval=never+include_image_gen_tool=true, Codex falls back to writing descriptive text (or an SVG renderer) instead of firing its native image tool.image_genis incompatible withminimaleffort; the shipped bake runsxhigh— the samegpt-5.5 + xhighsingle compat default pinned inrun_comic.get_bake_plan()(contractbakereq/v1, the digest the p0_proof cert binds to; a config-driven model/effort override is planned, not yet implemented). Honest scope: this Phase-1 raster ASSET bake is paid and runs PRE-P0 — the p0_proof cert gates the Phase-2/3 PANEL bakes only (no cert can exist beforecomic.jsoncompiles, and assets must lock first); the gate on THIS spend is the cross-modelcomic-asset-review-loop+ the single-source collision gate (P5), not P0. NB the Codex CLI reviewers elsewhere in the pipeline pin no model (they follow the local codex config — currentlygpt-5.6-sol) at effortxhigh; only the bake payload pinsgpt-5.5. The authoritative bake reply is the.bakestatus.jsonsidecar ({status, failure_kind, mcp_output, request_id}— P2r step 3) + the native PNG on disk; codex is never asked to emit a JSON-line self-report (that was a retired codex-exec-era contract). - SVG SOURCE (deterministic route) = a pure-python parametric builder living in
gen/asset_lib.py, emitted by a thingen/gen_core_assets.pywriter. The palette is pinned tocomic.jsonui_tokens — never re-typed per asset. This route calls no image model and spends zero credits. - MAX_GEN_RETRIES = 2 (3 attempts total) — on
ok:falseOR a missing / zero-byte / invalid PNG (file <path>check). Append"[retry {n}: previous failure was: {reason}]"to the prompt each retry. - VERSIONING = monotonic 3-digit
_v{NNN}(duo_canonical_ref_v001.png,ddl_widget_template_v1.svg). Version only grows; the prior artifact stays on disk for the audit cascade; asupersedesself-edge links the new version to the old (recordprev_sha256:<hex>+prev_version:Nintags). <refs>RESOLUTION + SUBDIR —<refs>=<project>/+movie.project.jsondirs.assets(defaultassets/); the canonical identity target resolves viamovie.project.jsonidentity_refwith fallbackassets/duo_canonical_ref_v001.png(the same resolutionrun_comic.py derive_paths()uses). Thecharacters | scenes | props | text_panelssubdir split is the RECOMMENDED layout for a new multi-asset project; the worked example predates it and keeps a FLAT layout — duo ref atassets/duo_canonical_ref_v001.png, extra cast underassets/identity/(researcher_chibi_canonical_ref_v001.png,trio_identity_sheet_v001.png). SVG sources undergen/+assets/. Placeholder key:<project>= the project dir (e.g.examples/comic_m3_audit);<refs>as above; the pickup verifier lives at the literal repo-relative pathskills/method-figure/scripts/pickup_image.py.- NEVER LOCK —
review_statusis left"pending"(nodestatus≤under_review). The executor that produces an asset is forbidden from approving it; locking belongs to the cross-modelcomic-asset-review-loop(a different model family). Settingreview_status:"locked"here is a contract violation. - ONE RUNNER PER PROJECT — each raster bake writes its own explicit
out_pathvia the.bakereq.json/.bakestatus.jsonsidecar seam, so there is no global-dir cross-pollination between concurrent bakes. There is no/tmp/aris_imagegen.lockin agent mode — the agent wrapper itself serializes themcp__codex__codexcalls. The real race surface is two runners on the same project colliding on the per-asset.bakereq.json/.bakestatus.jsonsidecars + the sharedout_path, so keep one runner per project. (The global generated-images dir + newest-after-marker pickup that could cross-pollinate is a hazard of the legacy exec path ONLY, retired for real bakes.)
Input contract (3 modes)
This skill never invents an asset id. If the outline/storyboard did not declare it, the operator adds it at
the outline layer (comic-outline-creator) — not here.
Pipeline position (the Phase-1 asset DAG — the documented contract): OUTLINE_DRAFT_VALID (the outline gate
validates narrative + continuity + safety and that every referenced asset_id is DECLARED with a complete,
generatable request — it does NOT require locked assets) → human outline approval → provisional
storyboard (structural pass; may reference draft assets) → consolidated_asset_requests → this skill (S4) +
comic-asset-review-loop (S5) generate and LOCK the assets → OUTLINE_FINAL_LOCK (cheap re-check: the locked
assets still match the approved outline) → storyboard FINAL asset-resolution validation → blueprints. The hard
locked-asset barrier sits before blueprint authoring — not at the outline gate (the old single-stage
contract deadlocked: an outline demanding locked assets that depend on a storyboard that depends on a locked
outline).
- single
asset_idmatching^asset:[a-z0-9_-]+$— produce one asset, await its verdict. --batch-from-outline <outline_id>— read the human-approved outline'scharacter_asset_ids[] + scene_asset_ids[] + prop_asset_ids[]∪ the provisional storyboard'sconsolidated_asset_requests(structural pass — the storyboard is not asset-resolved yet at this point in the DAG), keep onlyreview_status=="pending", process serially (raster bakes never overlap), fire the review loop once at the end.--regenerate <asset_id> --reason "<text>"— force a re-render even ifapproved/rejected; prefix the generation prompt with"[regen reason: ...]", bump_v{NNN}, write asupersedesself-edge.
What it reads / what it writes (wiki nodes & edges)
Per schemas/node_schema.json (node/comic/3.0):
- READS
storyboard_spec(payload.consolidated_asset_requests,payload.global_policies) andoutline_spec(payload.character_asset_ids / scene_asset_ids / prop_asset_ids,payload.global_style_bible). - WRITES / MUTATES the
assetnode (node_id: ^asset:[a-z0-9_-]+$,node_type:"asset"). Required payload fields (schemaoneOf → asset):asset_kind,name,visual_description,identity_lock,ref_requirements,review_status,version. This skill additionally writes the produced ref onto the node (see the two contracts below) and leavesreview_status:"pending", nodestatus:"under_review". - EDGES (
schemas/edge_schema.json,src/dst/type): on a regenerate, emit asupersedestrue self-edge with the barenode_idon BOTH endpoints (the node_id pattern^(...|asset|...):[a-z0-9_-]+$forbids@/{/}, and there is ONEasset:<slug>node per asset — no per-version node files — so any@v{N}endpoint would dangle and failcli/validate_wiki.pylines 165-167 (endpoint resolution; the node_id-pattern check is lines 113-114)):{src: asset:<id>, dst: asset:<id>, type:"supersedes", evidence:"regen v{N}->v{N+1}: <reason>; prev_sha256:<hex>; prev_version:N"}. The cross-version lineage lives in the artifact filename_v{NNN}+prev_sha256/prev_versionintags(peroutput-versioning) — NOT in the edge endpoints. (Only if you truly need a versioned snapshot node, encode the version IN the slug —asset:<slug>_v002, which matches the pattern — AND actually write that second node file so both endpoints resolve.) - FAILURE — on retry exhaustion write a
failure_modenode so the spiral routes around it, and exit non-zero. No asset_ref-layer example node ships in the repo (everyexamples/comic_m3_audit/wiki/nodes/fail_*.jsonis a downstream SHOT-level node — e.g.fail_s09_a01.jsoncarrieslayer:"panel_visual"+affected_shot_ids:["S09"]— do NOT mirror those for this upstream gate). Write this literal skeleton instead:
(root{ "node_id": "fail:<asset-slug>_image_gen_unavailable", "node_type": "failure_mode", "status": "active", "title": "asset_ref bake failed — <asset-slug>", "created_at": "<real UTC now>", "tags": ["image_gen_unavailable"], "payload": { "layer": "asset_ref", "affected_shot_ids": [], "active": true, "repair_pattern": "<tag-derived hint>" } }status:"active"is the runtime canon validate_wiki.py enforces for failure_mode; payload has all 3 PAYLOAD_REQUIRED fields —layer,affected_shot_ids(empty: this is the UPSTREAM asset gate, no shots yet),active.) Do not trigger the review loop on an empty file.
The two fail-closed engine contracts (honor these or the director refuses to run)
These are the same fail-closed invariants the spiral enforces — produce assets so they cannot violate them:
- Every panel needs a
content_svg. Every deterministic motif a panel will use MUST be produced as a single-source SVG inasset_lib.py, so the panel's blueprint can name a realcontent_svg. If a recurring motif has no parametric source here, the downstream blueprint has nothing to bake-condition on and the director's render step fails closed. One parametric builder per recurring motif — no exceptions. - A baked figure-panel needs
expected_literals. Any motif carrying gated text (a chart with0.71→0.66, a stamp readingREJECT, a DDL chip readingT-16:05) must render those literals deterministically so the panel can declare ascii-tokenizableexpected_literalsfor the blind token-diff gate. Conversely, a zero-text asset (the wordless constellation) must contain zero glyphs — assert it at build time (assert "<text" not in svg). Producing a fuzzy raster of a chart breaks this contract; that is exactly why charts/clocks/stamps take the SVG route.
Procedure (numbered — an agent runs this top to bottom)
P0 · Resolve the target
Read the asset node. Assert node_type=="asset". Abort if review_status=="locked" ("immutable per asset gate") or review_status=="approved" unless --regenerate. Classify the asset by asset_kind into
its production route: character | scene | prop (with a non-deterministic appearance) → raster route (P1r);
a deterministic motif (clock/chart/stamp/mug/symbol/star-map, anything whose pixels are computable) →
SVG-source route (P1s). When in doubt: if the asset has gated literals or must be byte-identical across
instances, it is deterministic → SVG route.
P1r · RASTER route — build the bake prompt
From payload: asset_kind, name, visual_description, identity_lock.must_preserve[] (traits that MUST
appear — e.g. "blue hoodie #1D4684", "beard", "silver hair"), identity_lock.must_avoid[] (→ a
image-prompt "do not add" block — e.g. "celebrity likeness", "corporate branding", "voxel 3-D blocks"),
ref_requirements.{aspect_ratio,background,pose,isolated_reference,data_url_required}.
- Run the STYLE-BIBLE / banned-vocab filter (see below) over
visual_description; rewrite inline and record each substitution into the composed prompt. - Compose the prompt (template):
Pose defaults by kind:Render a single reference image for asset {id} ({kind}: {name}). Subject: {filtered visual_description} Identity lock — preserve exactly: {must_preserve[]} Identity lock — do not add: {must_avoid[]} Reference requirements: {aspect_ratio} canvas; pure {background} fully isolated, no shadow/halo; pose = {pose}; isolated reference (no scene around the subject). No labels / watermarks / text / captions / contact-sheet grids. Output a SINGLE coherent reference image — not a collage, not a variation grid. Treat composability (this becomes a reusable identity ref) as the primary goal.character= "three-quarter front view, neutral expression, upper-body or full-body framing";scene= "establishing wide of the empty location, no characters, neutral daytime lighting";prop= "isolated centred view, slight three-quarter angle";logo_free_symbol= "centred symbol on white, no scene". Provide the project's real identity refs as read-only condition images when the asset must match an existing cast member (never invent a face).
P2r · RASTER route — bake via the .bakereq/.bakestatus sidecar (CONDITION it, never hand-paste)
This is the same fail-closed bake seam run_comic.py / run_spiral.py use (no marker, no /tmp lock, no
newest-pickup — each bake writes its OWN explicit OUTPUT_PATH). OUTPUT_PATH is a deterministic
project-relative path (<refs>/{subdir}/{name}_v{NNN}.png — in the worked example's flat layout that is
assets/{name}_v{NNN}.png / assets/identity/{name}_v{NNN}.png); canvas guidance in the prompt = 1024x1024
(1:1 default) or 1280x720 (scene 16:9), background opaque/fully white, ONE image.
ONE agent, BOTH sidecar roles. Unlike
run_comic.py(a Python orchestrator serviced by an external agent wrapper), this skill ships no runner — the SAME agent running this SKILL plays the orchestrator (emit + verify) and the wrapper (callmcp__codex__codex, write the status). The one-liners below are transitional until the sharedcli/service_bake_requests.pybroker lands (planned — it does not exist yet); they import the canonical primitives fromskills/method-figure/scripts/pickup_image.py(the single source of truth) — never hand-assemble a.bakereq.jsonany other way.<ABS_OUT>= the absoluteOUTPUT_PATH;<ABS_PROJECT>= the absolute project dir; run from the repo root.
- Emit the request (orchestrator role). First write the composed P1r prompt body to
<ABS_OUT>.promptbody.txt, then emit.emit_bake_requestmints the per-bake uuid4-hexrequest_iditself (stamps it into the payload and RETURNS it), pre-deletes any staleout_path+ status so a prior bake can't be silently reused, and writes the sidecar atomically (.tmp → os.replace). Capture the printed id:python3 -c 'import sys, time; sys.path.insert(0, "skills/method-figure/scripts"); \ from pickup_image import build_bake_prompt, emit_bake_request; \ body = open("<ABS_OUT>.promptbody.txt").read(); \ pt = build_bake_prompt(body, "<ABS_CONDITION_PNG>", "<ABS_IDENTITY_REF or empty>", "<ABS_OUT>"); \ print(emit_bake_request("<ABS_OUT>", {"prompt_text": pt, "out_path": "<ABS_OUT>", "content_png": "<ABS_CONDITION_PNG>", "identity_ref": "<ABS_IDENTITY_REF or empty>", "model": "gpt-5.5", "config": {"model_reasoning_effort": "xhigh", "include_image_gen_tool": True}, "sandbox": "workspace-write", "cwd": "<ABS_PROJECT>", "created_at": time.time(), "min_bytes": 500000, "aspect": 1.0}))'aspect= W/H as a float (1.0for 1:1,1.7778for a 16:9 scene).build_bake_promptprepends the canonical "use your native image generation tool" header and embeds the reference + output paths LITERALLY inprompt_text(ref #1 = the labeled white-bg condition, ref #2 = the identity ref — this skill's own doctrine: never a free prompt, so ref #1 always exists). Also note the epoch you passed ascreated_at. - Bake (wrapper role). Call
mcp__codex__codexwithprompt_textverbatim as the prompt andmodel/config/sandbox/cwdexactly as in the.bakereq.json, plusapproval-policy: "never"(an mcp-call argument — NOT a bakereq field); codex writes the native PNG toOUTPUT_PATH(themcp__codex__codexschema has no-iimage param — the paths insideprompt_textare the only transport). TheconfigMUST carry bothmodel_reasoning_effort:"xhigh"andinclude_image_gen_tool:true, else codex is never handed the native image tool and no bake fires. - Write the status (wrapper role). Use the Write tool to create
<ABS_OUT>.bakestatus.json={status:"ok"|"fail", failure_kind:null|"throttle"|"other", mcp_output:"<the FULL raw mcp__codex__codex reply text, pasted verbatim>", request_id:"<the id step 1 printed, VERBATIM>"}.mcp_outputis MANDATORY on BOTH ok and fail and must be the COMPLETE reply — never a summary or a truncation (the HARD-VETO scans it forimport struct/zlib.compress/<svg/matplotlib/… traces of a hand-drawn fallback; an emptymcp_outputleaves the veto inert and fail-closes). A missing/mismatchedrequest_idis a stale/foreign bake and fail-closes. - Verify the EXPLICIT
OUTPUT_PATH(orchestrator role). Optionally re-read the status through the canonical poller (returns immediately in this single-agent flow; kept for parity with the split-role seam):
Then — only onpython3 -c 'import sys, json; sys.path.insert(0, "skills/method-figure/scripts"); \ from pickup_image import await_bake_status; print(json.dumps(await_bake_status("<ABS_OUT>", 600)))'status:"ok"— run the pickup verifier (literal repo-relative path):
which checks PNG sig + IHDR dims + size strictly >python3 skills/method-figure/scripts/pickup_image.py --out-existing --out <ABS_OUT> \ --min-bytes 500000 --aspect <W/H> --created-at <epoch from step 1> \ --request-id <the id step 1 printed> --transcript <ABS_OUT>.bakestatus.jsonmin_bytes+mtime >= created_at+ a non-emptymcp_output, fail-closes on arequest_idmismatch, and HARD-VETOESstruct/zlib/PIL/<svg/matplotlibmarkers in the transcript (a clean PNG sig NEVER overrides a fallback marker). There is no newest-pickup — pickup verifies the one explicitOUTPUT_PATHthis bake wrote.
RETRY per MAX_GEN_RETRIES. After exhaustion: keep the best valid PNG if any, else write the failure_mode
node + exit non-zero (do not call the review loop on an empty file). Never patch a failed bake by
hand-pasting the missing trait — re-condition and re-bake.
Grid guard. A 2×2 / contact-sheet / variation-grid output is a failed attempt → retry (the "single coherent image, not a grid" line is the primary mitigation; a PIL aspect-vs-canvas check + an optional codex "is this a grid?" sanity reply is the backstop).
P3r · RASTER route — hash, encode, update (atomic), honor the 6-field contract
SHA256 = shasum -a 256 <png>;(W,H)via PIL;DATA_URL = "data:image/png;base64,$(base64 -b 0 <png> || base64 -w 0 <png>)"(macOS-b/ GNU-w, cross-platform).data_urlis a v4 invariant — Layer-3 multi-ref composition reads it directly; a missingdata_urlsilently breaks downstream.- Mutate the node
output_refwith ALL SIX fields{file_path, data_url, sha256, width, height, mime}. A partial write is a schema violation — reject it. Write.tmpthenmv(atomic; the wiki must validate even if interrupted). Leavereview_status:"pending", nodestatus:"under_review". Append alog.mdline.
P1s–P3s · SVG-SOURCE route — one parametric builder per motif
- Add / reuse one builder in
gen/asset_lib.pyfor the motif (ddl_chip,stamp,mug,curve_panel,tokyo_chip,mini_stamp_glyph,verdict_card, the star-map coord tables). Palette pinned tocomic.jsonui_tokens at the top of the file. Encode every cross-panel reuse contract into the docstring (e.g. S11 REJECT ↔ S16 ACCEPT instantiate the SAMEverdict_card— mirror pair; the star-map JSON is the single coordinate truth for S16b labeled + S22 wordless). - Emit the canonical sheet via
gen/gen_core_assets.py— a thinw(name, content)writer that imports only fromasset_lib. Its docstring maps each output filename → the storyboardASSET_REQUEST #. Enforce single-source inline: if a specialized generator (e.g.gen_b06) takes ownership of a richer variant, delete the generic duplicate here (don't keep two). - Coordinate-truth motifs (the star-map): write the JSON (
wiki_starmap_nodes_v1.json) as the only truth source; both the labeled SVG (S16b) and the wordless SVG (S22) derive from it programmatically —禁目测/ never re-layout by eye. The zero-text twin assertsassert "<text" not in svgat build time. - Record on the node:
generator_script,owner_script,file_sha256; paths project-relative. Leavereview_status:"pending".
P4 · Hand off to the review loop (NEVER self-acquit)
Single mode: trigger comic-asset-review-loop, await its verdict
(approve → loop locks it / regenerate → recursive --regenerate callback, bounded by
MAX_REVIEW_ROUNDS=3). Batch mode: fire the review loop once at the end. This skill never writes
review_status:"locked" — locking is the cross-model loop's exclusive right.
P5 · Single-source collision gate (CI / pre-handoff)
Run gen/check_asset_collisions.py (static scan of every gen_*.py for w("…") writes). Every output
filename must have exactly one generator owner. Two owners → exit 1 → fix (rename the specialized variant or
delete the dead duplicate) before any handoff. This is a deliberately-not-"skip-if-exists" guard (that would
hide the run-order bug).
EXACT gate — asset_generation_self_check (deterministic, ported from aris_movie)
This is the producer-side self-check the skill must pass before handing to the review loop. It is a set
of boolean predicates with vetoes — not a numeric rubric (the numeric visual rubric lives in
comic-asset-review-loop, the analog of the panel_gate). Verdict ∈ { generated | failed } — NO
approve/locked is ever emitted here.
- output_exists — the produced file exists and is non-zero bytes. Veto on fail.
- raster_six_field_complete — for a raster ref,
output_refhas ALL of{file_path, data_url, sha256, width, height, mime}. A partial write = schema violation. Veto. - sha256_matches_file — the recorded
sha256equalsshasum -a 256of the file on disk. Veto. - data_url_present —
data_urlis populated (v4 invariant for multi-ref composition). Veto on raster. - svg_path_project_relative — for an SVG/JSON source, the recorded path is project-relative (never an absolute machine path). Veto.
- single_source —
check_asset_collisions.pyexits 0: each output filename has exactly one generator owner. Veto. - zero_text_contract — a zero-text asset (the S22 constellation) contains no glyphs:
assert "<text" not in svg. Veto. - no_two_dialects — no motif has two visual dialects (e.g. two stamp families, two DDL renderers). The new
asset must match the already-baked instances (e.g. the stamp geometry must match the baked S15
WARN_corrected). Veto. - not_a_grid (raster) — PIL aspect-vs-canvas check (+ optional codex "is this a grid?" reply); a 2×2/contact-sheet is a failed attempt, not a pass → retry.
- banned_vocab_clear — refuse to call
image_genat all if any banned / off-bible term survives the filter.
Why no numeric
approvehere: the executor that produced the asset is not allowed to judge it (cross-model independence). This gate only proves the artifact is well-formed and single-source; whether it is good (identity fidelity, on-bible look) is decided bycomic-asset-review-loopwith a different model family.
STYLE-BIBLE / banned-vocab filter (mandatory before any raster bake)
The aris_movie source bans camera/lens vocab that conflicts with downstream synth and silently regresses
fidelity (8K, 4K, photorealistic, cinematic, 50mm, dolly, push-in, bokeh, rim light, masterpiece, trending on artstation, close-up, wide shot, …). For the comic, this filter reads ART_BIBLE.md and additionally
enforces the bible's own terms: the two-world warm-real / dark-cyber palette (warm = Edison/wood/window;
digital-ARIS = dark_navy_void #0A0E27), the voxel ban (禁止 voxel 立体块 — pixel-flat only), and the
per-character hex/feature locks (executor blue hoodie #1D4684 + brown hair + NO beard; reviewer green
hoodie #30582D + near-black hair + beard). A surviving banned/off-bible term ⟹ refuse to call image_gen.
Identity is BRING-YOUR-OWN: swap these locks for whatever the project's ART_BIBLE.md + identity_lock
declare; the ARIS chibi duo is only the worked example.
Worked example (copy this exact pattern)
The proven comic is examples/comic_m3_audit/. Copy its shape:
- The single-source builder library —
examples/comic_m3_audit/gen/asset_lib.py: one parameterized builder per recurring token —ddl_chip(x,y,t,state,skin,…)(the sole renderer for the whole 18-instance DDL timeline, 3 skins × {amber,red,green} + aSUBMITTEDvariant), theSTAMPStable +stamp(...)(DUP / SURVIVES / REJECT / ACCEPT / WARN_corrected / SUBMITTED / AUDIT — one stamp dialect, geometry matching the baked S15),mug(...)(theML RESEARCHcup,hot|fading|cold),curve_panel(...)(thewandb · exact_parsecurve),tokyo_chip(...)(the{"city":"Tok|yo"}broken-JSON chip, multi-scale + arepaired=Truetwin), andSTARMAP_NODES / STARMAP_EDGES / STARMAP_LOOP(the single coordinate truth)- the shared
verdict_card(...)whose docstring states S11 REJECT and S16 ACCEPT instantiate the SAME card (mirror pair). Palette constants (RED/AMBER/GREEN/VOID/…) are pinned tocomic.jsonui_tokens at the top.
- the shared
- The thin canonical-sheet emitter —
examples/comic_m3_audit/gen/gen_core_assets.py: aw(name, content)writer that imports only fromasset_lib; its docstring maps every output filename → storyboardASSET_REQUEST #. It writeswiki_starmap_nodes_v1.jsonas the only truth source, then derives bothwiki_starmap_v1.svg(S16b, labeled — only the 4 gated literals are big) andconstellation_layout_v1.svg(S22, zero text) from the same coordinates, ending with the load-bearing guardassert "<text" not in svg, "constellation must contain ZERO text". It also shows the inline ownership transfer discipline: the genericwandb_exact_parse_060_071curve was deleted here becausegen_b06_blueprints.pyowns the richer two-series version (one canonical source per token). - The single-source collision gate —
examples/comic_m3_audit/gen/check_asset_collisions.py: regex\bw\(\s*["']([^"']+\.(?:svg|json|png))["']over everygen_*.py; each filename must have exactly one writer; exit 0"✓ no asset collisions — all N generator outputs are single-source"(verified clean: 26 outputs, exit 0). Deliberately not a "skip if file exists" guard.
The raster side is grounded by the same project's hand-built duo_canonical_ref_v001.png +
comic.json.identity_refs (per-character hex/beard/silhouette locks) — the artifact this skill automates
producing. (P0 reminder from the real run: a missing researcher_chibi_canonical_ref_v001.png was a flagged
P0 blocker — every cast member needs its canonical ref before the panels that use it can bake.)
Hard do / don't (earned lessons)
- DO condition the raster bake on a labeled white-bg reference + the real identity refs — and force the
native tool with
sandbox:workspace-write+approval:never+include_image_gen_tool:true, else Codex falls back to descriptive TEXT instead of firingimage_gen. - DO build every clock / chart / stamp / mug / star-map as ONE parametric SVG in
asset_lib.py— never bake deterministic content as a fuzzy image and hope the digits land. The "ref" for a motif IS its single-source SVG. - DO populate
data_urland all 6output_reffields atomically (.tmp → mv); a partial write is illegal. - DO version monotonically (
_v{NNN}), keep the prior artifact on disk, and write asupersedesself-edge on every regenerate (prev_sha256/prev_versionintags). - DO run
check_asset_collisions.pybefore any handoff — one canonical owner per token, no two dialects. - DON'T hand-paste a missing trait onto a finished bake (reads as pasted/fake) — re-condition and re-bake.
- DON'T ever set
review_status:"locked"/approvedhere — the executor never self-acquits; locking is the cross-modelcomic-asset-review-loop's exclusive right. - DON'T invent an asset id — if the outline didn't declare it, the operator adds it at the outline layer.
- DON'T run two runners on the same project at once. Each bake writes its own explicit
out_pathvia the.bakereq/.bakestatussidecar, so there is no global-dir cross-pollination and no/tmp/aris_imagegen.lockin agent mode (the agent wrapper serializes themcp__codex__codexcalls) — but two runners on one project still collide on the per-asset sidecars + sharedout_path, so keep one runner per project. (The cross-pollinating global generated-images dir is a hazard of the legacy exec path only.) - DON'T trigger the review loop on an empty/failed file — write a
failure_modenode + exit non-zero so the spiral routes around it.
Protocols (governance contracts this skill honors)
output-versioning— refs are versioned_v{NNN}monotonically; the prior artifact stays on disk and asupersedesself-edge records the lineage (prev_sha256/prev_version).reviewer-routing— the raster BAKE payload pinsgpt-5.5+xhigh(the single compat default inrun_comic.get_bake_plan(), contractbakereq/v1; a config-driven override is planned, not yet implemented), while the pipeline's Codex CLI reviewers pin no model (they follow the local codex config — currentlygpt-5.6-sol) atxhigh; the visual judging is delegated tocomic-asset-review-loopon a different model family — never downgrade the tier.artifact-integrity— the model that produces an asset does NOT judge whether it's good; this skill emits onlygenerated | failed(well-formed + single-source), and the cross-model review loop owns acquittal — the executor never self-acquits.
Frequently asked questions
What to verify before installation and use
What does the comic-asset-ref-generator source document cover?
The missing Layer-2 of comic-author: take the storyboard's consolidated ASSETREQUESTS and produce, for every declared asset, the one canonical artifact that every downstream panel bake will condition on — so the film never grows two visual dialects and every panel of the same ch…
How do I install comic-asset-ref-generator?
The source record exposes this install command: npx skills add https://github.com/wanshuiyin/ARIS-Movie-Director --skill "skills/comic-asset-ref-generator". Inspect the command and pinned source before running it.
Which Agent platforms does the source record declare?
The pinned source record declares support for: codex.
Which permission-related actions were detected?
Static rules flagged write-files, exec-script in the source; the page lists the matching lines and excerpts.
Alternatives
Compare before choosing
gaelic-ghost/socket
python-ci-workflow
Design and maintain Python CI workflows around uv, pytest, Ruff, mypy, package build checks, dependency caching, Python version matrices, and local-command parity.
PramodDutta/qaskills
Pairwise Test Generator
Generate optimized test combinations using pairwise (all-pairs) testing algorithms to achieve maximum coverage with minimum test cases across multiple input parameters
PramodDutta/qaskills
Test Case Generator from User Stories
Automatically generate comprehensive test cases from user stories and acceptance criteria using BDD patterns, equivalence partitioning, and risk-based prioritization
PramodDutta/qaskills
Database Migration Test Generator
Generate tests for database migration safety covering schema changes, data integrity preservation, rollback verification, and zero-downtime migration validation