AgentStack
SKILL verified MIT Self-run

Comic Asset Review Loop

skill-wanshuiyin-aris-movie-director-comic-asset-review-loop · by wanshuiyin

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…

No reviews yet
0 installs
10 views
0.0% view→install

Install

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

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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 Used

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.

Are you the author of Comic Asset Review Loop? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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.

  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 panelspec's own field — there is NO content_svg on a panelspec), 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.

  1. 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.

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

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.