Install
$ agentstack add skill-wanshuiyin-aris-movie-director-comic-continuity-audit ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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 No
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.
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 assemblygate'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/spiralengine.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.
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_ledgernode (itscolumns/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).
- MOTIFTABLECOLUMNS (verbatim, the dimensions the audit reads) =
Panel | Mug(ML RESEARCH motif 杯)| DDL | Tok|yo | exact_parse | claim_delta | bounce.
- FACT-EXTRACTOR = Gemini
auto-gemini-3viamcp__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.
- MAXCONSTRAINTSTO_CHECK = 20 (cap; archived/superseded
continuity_constraintnodes skipped) ·
GEMINI_MODEL = auto-gemini-3 (never downgrade, [reviewer-routing](../../protocols/reviewer-routing.md)).
- OUTPUT = a
reviewnode (always) +story/CONTINUITY_TRACE.md+observed_state.jsonper 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 assemblygate's drift set). It does NOT feed a bake-time panel_gate KEEP — verified: [spiral_engine.js](../../packages/core/spiralengine.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_ledgernode (node_type: "motif_ledger", idmotif:) — payloadcolumns,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_attemptnode under audit (node_type: "panel_attempt", idattempt:) — payload
source_panel_id, image_path (the single baked panel PNG, or a page of panels), attempt_index.
- the MOST RECENT accepted
panel_attempton 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_constraintnodes — filter on the payload booleanpayload.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
reviewnode (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
decisionnode (node_type: "decision", iddecision: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_ofedge (failure_mode → attempt). Norollback_ofedge — 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_constraintcandidate nodes (institutional memory; idcont:, 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 byadditionalProperties: 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:
- Every panel needs a
content_svg. The baked panel must trace to ablueprintnode 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.)
- A baked panel carrying a
content_svgneedsexpected_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 theML RESEARCHmotif 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_parsevsclaim_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
- Source: 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.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.