# Comic Continuity Audit

> The authoring-side DIEGETIC continuity AUDIT — it produces the world-state evidence that comic-cross-layer-gate `--gate continuity` adjudicates against the storyboard's MOTIF STATE TABLE (motif_ledger). It checks each baked panel STRICTLY against the pre-committed table row, then emits a continuity review-node the gate fuses (it does NOT own a bake-time KEEP). Gemini auto-gemini-3 is the per-pane…

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

## Install

```sh
agentstack add skill-wanshuiyin-aris-movie-director-comic-continuity-audit
```

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

## About

# comic-continuity-audit — the Motif-State-Table Audit (Phase 1, step S11)

The **diegetic-continuity AUDIT step of the [`comic-author`](../comic-author/SKILL.md) suite**: verify that a
baked panel obeys the **WORLD-LOGIC** the comic committed to — time / prop-state / motif-arc / metric-value /
causality across cuts — *distinct from* identity/style (`comic-director`'s `panel_gate` already covers "does
the character LOOK the same"; this covers "does the WORLD stay consistent"). The audit is **not vibes**: it
reads the pre-committed **MOTIF STATE TABLE** authored by [`comic-storyboard-creator`](../comic-storyboard-creator/SKILL.md)
(the `motif_ledger` node) and checks each baked panel STRICTLY against its own row + the film's global
invariants. **This skill is the AUDIT producer, not the gate**: it emits a continuity `review` node (world-state
evidence) that [`comic-cross-layer-gate`](../comic-cross-layer-gate/SKILL.md) `--gate continuity` collects and
adjudicates — *the loop can DRIVE but it cannot ACQUIT* ([`acceptance-gate`](../../protocols/acceptance-gate.md)).
On a same-panel miss it requests a `retry_panel`; a cross-frame motif break is handed to the page
**assembly_gate's drift set** (seed-anchored comic panels are independent — there is **no cross-panel
rollback** in this image pipeline, [`spiral_engine.js:54`](../../packages/core/spiral_engine.js)). It ports the
real procedure of aris_movie's `continuity-audit` (Gemini-as-fact-extractor → Claude-as-deterministic-judge →
violations → routed repair) onto stills, but anchors it to our **pre-committed per-dimension-per-panel ledger**
instead of asking a holistic judge to infer intent.

> **Cardinal lesson baked in as a predicate, not prose:** *the audit IS the table.* The audit never invents a
> rule — it reads the `motif_ledger` and flags any panel whose observed value contradicts that panel's row. And it is
> **cast-aware**: a panel that simply does not contain a character (the duo are absent in S01/S03/S06/S18/S20/S22
> by design) is **design, NEVER a drift penalty (absence≠drift).** This is the concrete fix for the B03
> cross-frame **FALSE-drift**: *IDENTICAL `min=0` scores across re-bakes were the fingerprint of a broken,
> cast-blind RUBRIC — not a broken artifact.* When scores don't move across repairs, audit the JUDGE (is it
> reading the table? is it penalizing intended absence?), do not keep regenerating the panel.

```text
  motif_ledger (the TABLE) ─▶ ① LOAD  attempt + prior-accepted + this panel's table row + active continuity_constraints
                                  ▼
                            ② EXTRACT  Gemini auto-gemini-3, ONE analyzeFile per panel image, FACT-ONLY (no scoring, no comparison)
                                  ▼
                            ③ JUDGE  Claude deterministic: observed ⊖ table-row  AND  global invariants
                                  │       (DDL monotonic · bounce S02=ONLY MAX · metric columns never co-mingle · MIRROR LOCKS)
                                  ▼
                            ④ CAST-AWARE FILTER  intended-absence ≠ drift; design variation ≠ drift  (read the row's design intent)
                                  ▼
                            ⑤ EMIT  one continuity review node (always) + 0-N continuity_constraint candidates + observed_state patch
                                  ▼
                       contradiction? ─ yes ─▶ same-panel miss: request retry_panel (failure_mode.repair_pattern = positive invariant)
                                  │            cross-frame break: add panel id to the page assembly_gate drift set (NO cross-panel rollback)
                                  │ no
                                  ▼
                            ⑥ the continuity review node is COLLECTED by comic-cross-layer-gate --gate continuity (the gate ACQUITS, not this skill)
```

## Constants
- **THE GATE = THE TABLE.** The audit reads the `motif_ledger` node (its `columns` / `rows` / `invariants` /
  `mirror_locks`) and checks each panel against its own row. It **does not invent rules**; an un-tabled claim
  is out of scope (escalate to the storyboard layer, do not adjudicate it).
- **MOTIF_TABLE_COLUMNS** (verbatim, the dimensions the audit reads) =
  `Panel | Mug（ML RESEARCH motif 杯）| DDL | Tok|yo | exact_parse | claim_delta | bounce`.
- **FACT-EXTRACTOR** = Gemini `auto-gemini-3` via `mcp__gemini__analyzeFile` — **ONE call PER panel image**
  (`analyzeFile` takes ONE `filePath`, never a batched array). Prompt is **FACT-ONLY**: *"Do NOT score quality.
  Do NOT compare to anything. Just extract what you SEE in THIS ONE panel."* For a multi-panel **page**, sample
  the salient panels (first + last + the dominant `big-frame` panel), one `analyzeFile` each.
- **JUDGE** = Claude (this agent), **deterministic predicate evaluator** over the observed JSON vs the table row
  + the global invariants. The judge is logic, not per-pixel taste.
- **CROSS-MODEL INDEPENDENCE** — the executor that **baked** the panel (Codex `image_gen`) is NOT a judge here;
  it never self-acquits ([`reviewer-independence`](../../protocols/reviewer-independence.md)). Gemini extracts,
  Claude judges; the generator family is excluded from this audit.
- **GEMINI_FALLBACK** — a frame Gemini fails: retry once; if it still fails, **mark that dimension
  `indeterminate`, never default to satisfied/violated**. A false negative wastes a re-bake; surface ambiguity.
- **MAX_CONSTRAINTS_TO_CHECK = 20** (cap; archived/superseded `continuity_constraint` nodes skipped) ·
  **GEMINI_MODEL = auto-gemini-3** (never downgrade, [`reviewer-routing`](../../protocols/reviewer-routing.md)).
- **OUTPUT** = a `review` node (always) + `story/CONTINUITY_TRACE.md` + `observed_state.json` per panel +
  0-N `continuity_constraint` candidate nodes + (on mismatch) a `decision` + a `failure_mode` + edges.

## Input contract — what it reads, what it writes (the wiki node it touches)
This skill is **pure audit** — it produces the world-state **evidence** (a continuity `review` node) that
[`comic-cross-layer-gate`](../comic-cross-layer-gate/SKILL.md) `--gate continuity` later COLLECTS (via the
`reviews` edge) and fuses against the `motif_ledger`. **The continuity verdict belongs to that gate** (it scores
the gate's four dims `ledger_row_complete / invariants_hold / mirror_locks_paired / design_aware`); this skill
only writes the review + (on a contradiction) requests the engine's bake-time repair (`retry_panel`, or a
cross-frame add to the page assembly_gate's drift set). It does **NOT** feed a bake-time `panel_gate` KEEP —
verified: [`spiral_engine.js`](../../packages/core/spiral_engine.js) `panelVerdict` computes `narr` from
`cc.narrative_beat_fidelity` / `cc.composition_story` and **never reads a `plot_continuity` field**, so this
skill never claims MIN-fusion influence over the bake-time KEEP.

**Reads** (per `schemas/node_schema.json`, `node/comic/3.0`):
- the **`motif_ledger`** node (`node_type: "motif_ledger"`, id `motif:`) — payload `columns`, **`rows`**
  (the per-panel ground truth), **`invariants`** (DDL monotonic; bounce-uniqueness; metric-column separation),
  **`mirror_locks`** (the non-adjacent paired constraints). *This is THE spec the audit checks against (and the
  gate then adjudicates).*
- the **`panel_attempt`** node under audit (`node_type: "panel_attempt"`, id `attempt:`) — payload
  `source_panel_id`, **`image_path`** (the single baked panel PNG, or a page of panels), `attempt_index`.
- the **MOST RECENT accepted** `panel_attempt` on the same lineage = `PRIOR_ACCEPTED` (the implicit reference
  for monotonicity); if none → bootstrap-only pass (vacuous-satisfied, still emit a review node).
- all **live `continuity_constraint`** nodes — filter on the **payload boolean `payload.active == true`** (the
  designed institutional-memory "is this constraint live" flag, NOT the node `status`; skip nodes whose `status`
  is `superseded`/`rejected`). Per `validate_wiki.py` `PAYLOAD_REQUIRED["continuity_constraint"]` the payload
  carries exactly `family, constraint_text, applies_to_panel_ids, source_node_id, active` — read all five.

**Writes** (every node-id matches the schema pattern `^(…|review|decision|fail):[a-z0-9_-]+$`; every payload
carries exactly the `validate_wiki.py` `PAYLOAD_REQUIRED` fields for its type):
- **always** one **`review`** node (`node_type: "review"`). **Node-id and reviewer are NAMESPACED to avoid a
  byte-collision** with the bake-time `panel_gate` CC review: id **`review:continuity___cc`**
  (NOT `review:_` — that is byte-identical to the existing `review:panel_s01_a01_cc` and would
  OVERWRITE it), reviewer **`continuity_cc`** (a *logic* judgment, not the bake-time per-pixel `cc`),
  **`gate_kind: "continuity"`**. **The four continuity-gate dims go under `payload.review_scores`** — that map
  is the ONLY copy [`comic-cross-layer-gate`](../comic-cross-layer-gate/SKILL.md) `--gate continuity` reads (its
  §① loads each review's `payload.review_scores`): `review_scores = {ledger_row_complete, invariants_hold,
  mirror_locks_paired, design_aware}` (each 0–5, the EXACT dims it fuses) + a `timed_out` bool + `notes` at
  payload top-level. The 3 schema-required keys are `target_node_id` (the **`motif_ledger`** = `motif:`,
  the node `--gate continuity` is passed + adjudicates), `reviewer`, `gate_kind`; `status: "complete"`. Append a
  **`reviews`** edge `review:continuity___* → motif:` (the `motif_ledger` is the exact node
  the continuity gate collects reviews INTO; the audited attempt is named in the review node_id + carried in
  `notes` — never target `attempt:*` (the continuity gate does not adjudicate the attempt), never `reviewed_by`,
  the opposite direction). *(The bake-time `panel_gate` reviews use flat top-level dim keys for the engine's
  reader; this AUTHOR-layer continuity review uses `review_scores` because that is the map the author gate reads.)*
- **on a contradiction** a **`decision`** node (`node_type: "decision"`, id `decision:continuity__`,
  payload schema-required `target_node_id, verdict, gate_kind` — `gate_kind: "continuity"`, `status: "final"`)
  whose **`verdict` is `retry_panel`** (same-panel mug/tokyo/metric miss) **or `assembly_drift`** (a cross-frame
  motif break — routed to the page assembly_gate's `drift_panels`, NOT a per-panel rollback) + a
  **`failure_mode`** node (`node_type: "failure_mode"`, id `fail:continuity__`, payload
  schema-required **`layer: "continuity"`**, **`affected_shot_ids`**, **`active: true`**, status `active`) whose
  **`repair_pattern`** is the **POSITIVE INVARIANT** for the next bake — e.g. *"S06 DDL reads exactly `T-18:40`
  so `18:40 > S07's 18:30` holds the countdown"*, never the ban. **(Dataflow, exact:** the engine sources the
  next bake's invariant from the panel-gate reviewer's in-memory `failure_mode_positive_invariant` →
  `verdict.invariant` → `pending[pid]` → the next `generate_panel`; the persisted
  `failure_mode.payload.repair_pattern` is the audit SINK written FROM that same invariant, NOT read back by the
  engine — so author `repair_pattern` as the identical positive invariant the reviewer emits.) Append a **`decides`** edge (`decision → attempt`, verdict on the edge)
  + a **`failure_of`** edge (`failure_mode → attempt`). **No `rollback_of` edge** — that edge is for the
  page-level assembly rollback the engine actually performs, not a (non-existent) per-panel chained rewind.
  Edge vocab used here, all in `validate_wiki.py` EDGE_TYPES: `reviews / decides / failure_of` (+ the existing
  `attempt_of` minted upstream).
- **0-N `continuity_constraint`** candidate nodes (institutional memory; id `cont:`, payload
  `family, constraint_text, applies_to_panel_ids, source_node_id, active`) — an implicit check that **fires
  twice on the same lineage** gets promoted to an explicit constraint node so the next run checks it cheaply.
- **patches** the attempt payload with `observed_state` (allowed by `additionalProperties: true`) — *this* is the
  progressive chain: the NEXT audit has a verified reference instead of re-inferring it.

## Two fail-closed engine contracts (refuse, do not paper over)
The audit runs only on artifacts the pipeline guarantees; if a precondition is missing it **fails closed** and
points at the offending node rather than silently auditing a malformed unit:
1. **Every panel needs a `content_svg`.** The baked panel must trace to a `blueprint` node whose payload carries
   a non-empty `content_svg` (the deterministic SVG blueprint that was the generation condition). No
   `content_svg` → the panel was free-baked, the table-row comparison has no anchor → **REFUSE**, route back to
   [`comic-blueprint-author`](../comic-blueprint-author/SKILL.md). (You audit what was *committed*, not a vibe.)
2. **A baked panel carrying a `content_svg` needs `expected_literals`.** For any panel whose effective
   `text_mode` is `baked` AND that carries a `content_svg` (cfg_usable's exact trigger), the blueprint's
   `expected_literals` (the ascii-tokenizable numbers/labels: e.g. `["REJECT","37","T-16:05"]`) MUST be present;
   they are the literal values the metric/DDL columns are checked against. Missing/empty `expected_literals` on a
   baked figure panel → **REFUSE** (the fail-closed gate cannot verify `exact_parse`/`claim_delta`/`DDL` without
   them). `text_mode: "html"` (pure-scene) panels carry no literals — they are gated on `stray_text_absence`, and
   their continuity is judged on the scene-state columns (mug / bounce / cast presence) only.

## THE AUDIT PREDICATES — what this skill checks and reports to the `continuity` gate
This is the **pre-gate routing** (the audit's own predicates) — NOT the gate's APPROVE condition. The gate's
APPROVE predicate is owned by [`comic-cross-layer-gate`](../comic-cross-layer-gate/SKILL.md) `--gate continuity`
(`ledger_row_complete ≥ 4 AND invariants_hold ≥ 4 AND mirror_locks_paired ≥ 4 AND design_aware ≥ 4`, with a
structural HARD VETO on any invariant violation). This skill computes the per-column / per-invariant findings
below, folds them into those four dims, and writes them to the review node the gate fuses.

**Audit columns** (the six table columns; each is a machine-checkable predicate the audit verifies from the
table alone — they roll up into the gate's `invariants_hold` + `ledger_row_complete` dims):

- **`mug`** — tracks ONLY the `ML RESEARCH` motif mug. States: `hot` (two stable steam wisps = the film's
  *temperature origin*) / `fading` (one thin wisp) / `cold` (no steam + milk skin). **DESIGN-AWARE
  DISAMBIGUATION:** the `` mug is an **environment prop**, tagged `env`, and **ignored** — a panel showing a
  `` mug is NOT a motif-mug appearance and must NOT be flagged. The B01↔S20 bookend `cold` is *"the hardest
  continuity constraint"* (S20 must read stone-cold + milk-skin, same mug as S01's hot origin).
- **`ddl`** — global invariant **"DDL 全片单调递减，永不回拨"** (monotonic non-increasing across the ENTIRE
  film, never rewinds). Exact 16-value spine:
  `T-24:00:00 → 19:42 → 19:42 → 18:55 → 18:40 → 18:30 → 17:46 → 17:02 → 16:21 → 16:05 → 05:12 → 02:58 → 02:41 → 01:57 → 00:42 → 00:27(submitted)`.
  Two consecutive `19:42` (S03/S04, same-instant rhyme) are allowed *because non-increasing*; `S06=18:40`
  exists SOLELY so `18:40 > S07's 18:30` holds the never-rewind rule.
- **`tokyo`** — the broken-JSON chip `{"city":"Tok|yo"}` lifecycle:
  `absent → first-micro (S09, sub-threshold, NOT gated) → hero/gated center (S10) → evidence thumbnail (S11) →
  tiny as-prior (S12a) → REPAIRED {"city":"Tokyo"} ✓ parsed (S17) → single unlabeled dark star (S22)`.
- **`exact_parse`** vs **`claim_delta`** — the two metric columns that **"永不混写" / never co-mingle**:
  `exact_parse ∈ {0.60, 0.71, 0.66, 0.78, 0.89}`, `claim_delta ∈ {+6.2, +1.4}`. A value written into the wrong
  column is a violation (a `+6.2` appearing where an `exact_parse` should be, or vice-versa).
- **`bounce`** — uniqueness invariant **"S02 是

…

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

*"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-continuity-audit
- 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%.
