# Comic Asset Review Loop

> Phase-1 (S5) UPSTREAM ref-asset gate — the bounded cross-model adversarial loop that LOCKS one reusable identity-locked asset (character sheet / location plate / prop cutout / text-panel / logo-free symbol) BEFORE it can be composited into any panel. This is NOT the panel_gate (composed-output) or the assembly_gate (cross-panel) — it gates the refs that FEED both, so identity drift is caught at t…

- **Type:** Skill
- **Install:** `agentstack add skill-wanshuiyin-aris-movie-director-comic-asset-review-loop`
- **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-asset-review-loop
- **Website:** https://wanshuiyin.github.io/ARIS-Movie-Director/comic/

## Install

```sh
agentstack add skill-wanshuiyin-aris-movie-director-comic-asset-review-loop
```

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

## About

# comic-asset-review-loop — the UPSTREAM ref gate (Phase 1 · S5)

The **ref gate the comic side was missing.** `comic-director`'s `panel_gate` reviews *composed output*; the
`assembly_gate` reviews *cross-panel consistency*. Neither gates the **identity refs that feed both** — so
without this skill, identity drift is only caught *downstream*, after credits are spent on a panel bake. This
skill is the **Layer-2 gate on a SINGLE reusable reference asset** (the character sheets / location plates /
prop cutouts the `ART_BIBLE.md` points every bake at), run **before** any panel composition.

A reusable asset is a one-way door: once a hundred panels condition on `duo_canonical_ref_v001.png`, you cannot
quietly swap it. So the lock must be **earned cross-model**, never self-certified by the generator. This skill
locks an asset **only at 准×3** — Claude *and* Gemini *and* Codex each independently pass the **same** review
round — or it sends the asset back to be regenerated, or (at the round ceiling) escalates the asset
*requirement* itself back to the outline/storyboard layer. The executor (and the generation model family)
**never** acquit their own ref.

```text
  asset (review_status:"pending")  ◀── comic-asset-ref-generator (S4: generates, NEVER self-locks)
        │
        ▼
   LAYER 1 — STATIC CI GATE (deterministic, zero credits)
        check_asset_collisions.py  (one canonical owner per filename — "one visual dialect, never two")
        + build asserts: zero-text contract · literal contract · raster-ref 6-field completeness
        │  pass
        ▼
   LAYER 2 — VISUAL LOOP   (round R = 1 … MAX_REVIEW_ROUNDS=3)
        ① Claude narrative  ‖  ② Gemini visual (primary: identity/iso)  ‖  ③ Codex synth (policy/semantic)
        each reads the REAL asset file · scores 5 dims (0–5 ints) · flags failure_modes from the fixed vocab
        │
        ▼ Codex synthesises: MIN-fuse per dim (max for inverted) · threshold · per-reviewer unanimity
   准×3 ? ── no, a family HARD-FAILS a floor ─▶ comic-asset-ref-generator --regenerate (bump version) ─▶ ① R+1
        │ no, third family just ABSENT (e.g. Gemini down) ─▶ re-VOTE (re-collect the missing reviewer; NO re-bake)
        │ no (safety_ip= 3**.
- **MAX_REVIEW_ROUNDS** = 3 (hard ceiling). Even if Codex keeps saying `regenerate`, the loop STOPS at 3 and
  routes back to outline/storyboard — the *requirement* changes; the gate does not grind an image forever.
- **CROSS-MODEL ACQUITTAL** — Codex is the **synth/policy** reviewer and (in the broader pipeline) the
  generation family for raster refs. A Codex `approve` can *diagnose/veto* but is **never the sole acquitter**;
  the lock needs Gemini approve + Claude approve too (acceptance-gate.md: the loop DRIVES, it can't ACQUIT).
- **SAFETY_IP SINGLE-VOTE VETO** — `safety_ip /story/ASSET_REVIEW.md` (the human score-progression audit) + wiki nodes/edges
  under `wiki/nodes/` + per-reviewer trace under `.aris/traces/comic-asset-review-loop/`.

## Input contract / what this skill owns
The contract boundary is the **`asset` wiki node** authored by [`comic-asset-ref-generator`](../comic-asset-ref-generator/SKILL.md) (S4) at
`review_status: "pending"` — generated, never self-locked. This skill is **pure verify + lock**:
- It **never edits the asset's pixels or geometry** — on `regenerate`/`revise` it routes a ≤140-char
  `repair_instruction` back to S4, which bumps the version and emits a new file.
- It **never invents** scores or a verdict on behalf of an absent reviewer — a dim no reviewer scored is
  `not_scored` and **EXCLUDED** from the threshold, never substituted with 0 (the v1.0 bug; see Hard do/don't).
- It writes back **only** `status: "locked"` + `payload.review_status: "locked"` on a 准×3 approve, plus the
  review/decision/failure_mode trace. `locked` is the one-way door — immutable for the rest of Phase 1.

**Read** the `asset` node payload (per `schemas/node_schema.json`, `node_type: "asset"`,
`payload.required = [asset_kind, name, visual_description, identity_lock, ref_requirements, review_status,
version]`):
- `asset_kind` ∈ {character, scene, prop, text_panel, logo_free_symbol} — **routes the per-reviewer query**
  (see §"Asset-kind specialisation").
- `identity_lock.{must_preserve[], must_avoid[]}` — the per-asset rubric (e.g. from `ART_BIBLE.md` §1:
  executor = blue hoodie `#1D4684` + brown hair + **no beard**; reviewer = green hoodie `#30582D` + near-black
  hair + **beard**; "颜色/胡子/体型轮廓不可变").
- `ref_requirements.{aspect_ratio, background (white|transparent), pose, isolated_reference}`.
- `output_ref.{file_path, data_url, sha256, width, height, mime}` for a raster ref (all 6 fields, else schema
  violation) **OR** `{generator_script, owner_script, file_sha256}` for a deterministic SVG/JSON source.

## The EXACT gate (ported from `asset-review-loop`)

### Layer 1 — static CI gate (deterministic, MANDATORY, zero credits)
Run **before** any visual reviewer call (and re-run before lock). Fail-closed; correctness costs no credits.

- **single-source collision check** — `check_asset_collisions.py` static-scans every `gen_*.py` with the regex
  `\bw\(\s*["']([^"']+\.(?:svg|json|png))["']` and asserts **each output filename has EXACTLY ONE generator
  owner**. `len(writers) > 1` → `exit 1`. This is the tool that enforces **"one visual dialect, never two"**:
  two generators writing the same path with different content is a silent, run-order-dependent corruption
  (whoever runs last wins). It is deliberately **NOT** a "skip if file exists" guard — that would *hide* the
  order bug instead of surfacing it. Verdict: `exit 0` (clean) is a **precondition** for entering Layer 2;
  `exit 1` blocks the round and routes to S4 to rename/delete the duplicate. (Verified clean at 26 outputs in
  the worked example.)
- **zero-text contract** — for any asset that must contain no glyphs (e.g. the S22 constellation):
  `assert " **These are THIS skill's pre-gate routing thresholds (the asset-gate step-3 routing this skill OWNS), and
> they are written to MATCH [`comic-cross-layer-gate`](../comic-cross-layer-gate/SKILL.md)'s `--gate asset`
> LOCK predicate verbatim.** The gate's `LOCKED` condition is `identity_lock_satisfied >= 4 AND ref_quality >= 4
> AND bg_isolation >= 4 AND safety_ip >= 4` **AND** the `准 ×3` same-round cross-model unanimity rule —
> `reuse_readiness` is **ADVISORY** at the gate and does **not** block the lock. The `approve` rule below uses
> exactly those four floors; `reuse_readiness` only feeds `revise`/`regenerate` routing, never blocks the lock.

Codex **MIN-fuses** each dim across the reviewers who scored it (the gate's deterministic rule: **min** per dim,
**max** for an inverted dim, single scorer → use it, NO scorer → `not_scored` **EXCLUDE** — **never 0-substitute,
never average**: averaging `{5,3}`→4 would slip a sub-floor ref past the `>=4` lock; min=3 correctly fails). So
this inline routing can only be **stricter**, never weaker, than `--gate asset`. The legal asset verdict set is
`{approve, regenerate, locked, abandon_shot}` (gate-enforced); the internal `revise` below is an alias that
routes a `regenerate`. Then:

- **`approve`** ← `identity_lock_satisfied >= 4` **AND** `ref_quality >= 4` **AND** `bg_isolation >= 4`
  **AND** `safety_ip >= 4` (the four gate floors). **`asset_kind == "character"` override:**
  `identity_lock_satisfied == 5` is REQUIRED (a raster character ref locks only at a *perfect* identity score —
  4 is not enough for a face hundreds of panels condition on); the other three floors stay `>= 4`. (准×3
  same-round unanimity still applies on top — see the LOCK note below.)
- **`regenerate`** ← `identity_lock_satisfied = MAX_REVIEW_ROUNDS` without convergence.

> **Deterministic-SVG / JSON asset branch** (a parametric builder, NOT a raster ref → the visual 5-dim rubric
> does not apply). A deterministic-SVG source **locks iff ALL FOUR hold** (Layer-1 asserts + a 3-dim 0–5 read):
> `single_source_owner == pass` (Layer-1 `check_asset_collisions.py` exit 0 — exactly one generator owns the
> filename) **AND** `render_preview_legible >= 4` (the SVG/JSON rasterizes to a non-empty, on-spec preview PNG —
> a RAW render pre-check, not a vote) **AND** `literal_contract >= 4` (the `_contract` field is honored: literals
> are ascii-tokenizable, the JSON is the single coordinate truth, no eyeball re-layout) **AND**
> `style_family_consistency >= 4` (one visual dialect — palette/stroke/grid match the `ART_BIBLE.md` family).
> Plus the **zero-text / non-gated-text asserts**: a wordless source contains no `` (`assert " in svg`); a labeled source's glyphs are exactly the contract's literals. Same 准×3 same-round unanimity gates
> the SVG lock as the raster lock. (Gemini PRIMARY on `render_preview_legible` + `style_family_consistency`;
> Codex PRIMARY on `literal_contract`; `single_source_owner` is the Layer-1 tool, not a reviewer vote.)

> **LOCK requires 准×3 UNANIMITY, not Codex-synth-approve.** Even on a Codex `approve`, the lock fires **iff**
> `cc_approves AND gemini_approves AND codex_approves` in the **same** round (each reviewer's own 5 scores
> independently clear the per-reviewer thresholds — including the `identity_lock_satisfied == 5` floor for a
> character ref / the four-dim SVG floors for a deterministic source). A Codex `approve` with any reviewer below
> threshold is **DOWNGRADED to `revise`**. (`--lite` is **non-acquitting**: with Gemini absent it may only write
> provisional `status: under_review` + `lite_provisional: true`, and can NEVER satisfy the LOCK predicate or reach P6.) This same-round
> unanimity rule is exactly [`comic-cross-layer-gate`](../comic-cross-layer-gate/SKILL.md)'s `准 ×3` for
> `--gate asset` (this skill is the OWNER of that convention) — NOT "three consecutive rounds".

### Veto rules (hard, non-negotiable)
- **safety_ip = MAX_REVIEW_ROUNDS without 准×3 → ESCALATE the asset *requirement*** to
  [`comic-outline-creator`](../comic-outline-creator/SKILL.md) / [`comic-storyboard-creator`](../comic-storyboard-creator/SKILL.md)
  (the spec/outline must change; stop re-baking the image). Mutual recursion is bounded by this ceiling.

### Controlled failure-mode vocabulary (the FIXED set reviewers draw tags from)
`face_drift, age_drift, costume_drift, pose_off, multi_subject_leak, halo_artifact, shadow_spill,
non_white_bg, jpeg_artifact, glyph_break, unintended_text, watermark_present, contact_sheet, celeb_likeness,
copyrighted_character, brand_logo_present, collage_grid, low_resolution, melted_anatomy`. Every reject mints a
`failure_mode` wiki node `fail:_` tagged from this set (the `` must match `[a-z0-9_]+` —
strip any stray punctuation before assembling the node_id; maps to `comic-director`'s anatomy/identity vetoes) and routes
its tag into the S4 `repair_instruction`. The node MUST carry the schema-required shape (see the `fail:*`
skeleton in "Wiki node skeletons" below): root `status: "active"` + payload `{layer, affected_shot_ids, active,
repair_pattern}`. Because this is an **upstream asset gate with no shots yet**, `affected_shot_ids` is `[]`
(empty) — never invented; mirror the real `examples/comic_m3_audit/wiki/nodes/fail:*.json` shape.

## Wiki node skeletons (the EXACT shape each minted node MUST carry — validated by `cli/validate_wiki.py`)
Every node this skill writes obeys `schemas/node_schema.json`: the **root** carries `node_id, node_type, title,
status, created_at, payload` (all six required), the `node_id` matches `^(…|review|decision|fail):[a-z0-9_-]+$`,
and the `payload` carries every field in `validate_wiki.py` `PAYLOAD_REQUIRED` for that type. The canonical root
`status` for each runtime type (schema $comment; the real example nodes carry exactly these): `review →
"complete"`, `decision → "final"`, `failure_mode → "active"`. Copy these skeletons verbatim:

```jsonc
// review:*  — one per reviewer per round (P1 cc, P2 gemini, P3 codex). node_id: review:__round{R}
// PAYLOAD_REQUIRED["review"] = [target_node_id, reviewer, gate_kind]  (review_scores carries the 5 dims)
{ "node_id": "review:duo_canonical_ref_v001_cc_round1", "node_type": "review", "status": "complete",
  "title": "CC narrative asset review duo_canonical_ref v1 r1", "created_at": "",
  "payload": { "target_node_id": "asset:", "reviewer": "cc", "gate_kind": "asset",
               "review_scores": { "identity_lock_satisfied": 5, "ref_quality": 4, "bg_isolation": 4,
                                  "reuse_readiness": 4, "safety_ip": 5 },
               "failure_modes_flagged": [], "timed_out": false } }

// decision:*  — one per round after the synth+准×3 (P4). node_id: decision:asset__round{R}
// PAYLOAD_REQUIRED["decision"] = [target_node_id, verdict, gate_kind]
{ "node_id": "decision:asset_duo_canonical_ref_v001_round1", "node_type": "decision", "status": "final",
  "title": "asset_gate duo_canonical_ref v1 r1 -> locked", "created_at": "",
  "payload": { "target_node_id": "asset:", "gate_kind": "asset", "verdict": "approve",
               "reasoning": "LOCK 准×3 — identity=5 ref=4 bg=4 safety=5 reuse=4; cc&gemini&codex approve r1",
               "repair_instruction": "", "prior_lock_count": 3 } }

// fail:*  — only on a reject/abandon_shot (P0 Layer-1 fail, P2 parse failure-mode, P5 reject). node_id: fail:_
// PAYLOAD_REQUIRED["failure_mode"] = [layer, affected_shot_ids, active]   (upstream gate → affected_shot_ids: [])
{ "node_id": "fail:duo_canonical_ref_v001_costume_drift", "node_type": "failure_mode", "status": "active",
  "title": "asset_gate duo_canonical_ref costume_drift", "created_at": "",
  "payload": { "layer": "asset_ref", "affected_shot_ids": [], "active": true,
               "repair_pattern": "executor 蓝衣棕发无须 (must_preserve); 当前绿衣漂移 — 重生为 #1D4684 hoodie" } }
```

After any lock (P6), run `python3 cli/validate_wiki.py examples/` — it MUST PASS (root status enum,
node_id form, payload invariants). A node missing root `status` or `target_node_id` / `layer` /
`affected_shot_ids` / `active` is the exact failure the validator catches.

## The two fail-closed engine contracts (where they bite here)
This is an **upstream** gate, so it enforces the *preconditions* the downstream engine fail-closes on. **Do NOT
conflate the three SVG fields** (per `schemas/node_schema.json` + the engine): the wiki
`blueprint.payload.content_svg` (top-level on the blueprint payload), the `panel_spec.payload.content_blueprint`
(the panel_spec's own field — there is NO `content_svg` on a panel_spec), and the **comic.json** panel's
**runtime** `condition.content_svg` (the field the engine reads at `spiral_engine.js` `.condition.content_svg`).
1. **Every panel needs a renderable blueprint SVG.** A locked asset is only useful if a panel can condition on
   it — so when locking a deterministic SVG/JSON source, verify it rasterizes to a non-empty PNG (a `blueprint`
   node would carry it as `blueprint.payload.content_svg`), because at bake time the engine **refuses** a
   comic.json panel whose `condition.content_svg` is `null`/absent. A ref that cannot become a content-SVG
   condition is not "reuse-ready" — score `reuse_readiness` (and, for an SVG source, `render_preview_legible`)
   accordingly. **Never `condition.content_svg: null`.**
2. **A baked figure-panel needs `condition.expected_literals`.** If the asset is a **text_panel** / figure
   source whose downstream panels bake numbers (e.g. a wandb curve, a verdict stamp, a DDL widget), its lock
   MUST verify the literal contract holds (the literals are ascii-tokenizable and the JSON `_contract` forbids
   eyeball re-layout) — because `comic-director`'s gate **refuses to run** a baked figure-panel whose comic.json
   `condition.expected_literals` is empty. Locking a figure source that cannot supply gateable literals would
   strand the panel; a scene panel with no audited numbers takes `text_mode: "html"` instead.

## Numbered procedure (per round R, start R = 1, loop until 准×3 or R = 3)

**P0 — Resolve + Layer-1 gate.** Resolve the t

…

## 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:** yes

*"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-asset-review-loop
- 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%.
