Code review flagged that R1's early-exit (chosen in 1793f8d to satisfy
a len(errs)==1 assertion) would diverge from Tasks 3-6 which use
accumulate-all. Retrofit R1 + relax the test to use any()/next() so a
user fixes all continuity gaps in one validate pass. Also deepcopy the
page template in the fixture helper to avoid shared-ref leaks in later
rule tests.
Plan derived from M2 spec — breaks validator Layer 2, CLI, schema export,
skill pack, and examples into 15 bite-sized tasks. Each task lists exact
file paths, test code, implementation code, shell commands, and commit
message so a fresh engineer can execute task-by-task without context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Design for M2 milestone covering:
- Layer 2 semantic validator (5 rules, M1 feature subset)
- Standalone CLI with human + --json output modes
- schema.json as single source of truth (exported from core/schema.py)
- Skill pack for AI-as-storyboarder workflow: SKILL.md, PROMPT_TEMPLATE,
README, and 5 examples each with screenplay.md + output.json
- Canvas-size defaults (1080x1920) to anchor AI composition judgments
Layer 3 pre-generation checks, shape_group/polygon rules, and non-custom
page_template dimension tables are explicitly deferred to later milestones.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four minor improvements flagged in the M1 holistic review:
- I7: Extract core/image_ops.py with arr_to_pil / pil_to_arr / hex_to_rgb.
Removes the three duplicated copies scattered across compositor,
batch_generator, and page_composer. Sets up cleaner reuse for M2/M3
when validator and polygon mask code need the same helpers.
- I5: Move `import torch` out of module top-level in both node files,
into the methods that actually use it. Keeps the ComfyUI node scan
cheap — torch is the heaviest dependency by far.
- I3: Rename the `retries` widget to `max_attempts` (default 3, min 1).
The old name was misleading: retries=2 meant 3 total attempts.
Log lines now read "attempt N/M" for clarity.
- I2: Add manifest_version: 1 to cache manifests. manifest_matches()
now treats missing/mismatched versions as stale, so a future hash
schema change can safely invalidate old caches by bumping the
constant in core/cache_manager.py.
Tests updated to new kwargs, plus 2 new regression tests covering the
manifest-version behaviour. 74/74 pass, ruff clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Optional node that plugs between ComicScriptLoader and ComicBatchGenerator
when the user wants to experiment with cfg / steps / sampler without
editing the JSON. Any widget left at its sentinel value ((keep), 0, 0.0,
-1.0) is ignored; non-sentinel widgets overwrite every panel's sampler,
including panels that had per-panel sampler_override in the JSON.
Workflow stays JSON-authoritative by default: don't insert the node and
the JSON is the single source of truth (I4 / spec §14). Insert it only
for ad-hoc experimentation.
Panels are rebuilt via dataclasses.replace — the input SolvedScript is
never mutated, keeping C1's no-mutation invariant. Cache hashing picks
up the new sampler values automatically.
Registered as "Comic Sampler Override (experimental)" in NODE_CLASS_MAPPINGS.
5 tests cover: passthrough, single-key override, no-mutation, override
winning over per-panel sampler_override, and mixed partial override.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Schema requires default_sampler at book level to contain all 5 keys
(sampler_name, scheduler, steps, cfg, denoise), and the normalizer
always populates panel.sampler from it. The node-level widgets were
dead code — the merge `{**widget, **panel.sampler}` always collapsed
to panel.sampler.
Remove the 5 sampler widgets from INPUT_TYPES so the node stays faithful
to the design principle "schema is the single source of truth"
(spec §14). JSON's default_sampler + per-panel sampler_override are now
the only way to configure sampler params.
positive_suffix / negative_suffix widgets remain — they are explicit
run-time overrides, not duplicates of JSON config.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
C1: generate() baked widget defaults + prompt suffixes into the caller's
SolvedPanel, breaking cache invalidation on widget edits and leaking
mutated prompts into the pass-through comic_script output. Replace the
in-place mutation with dataclasses.replace into a local run_panel used
for hashing and sampler invocation; the input script is never touched.
C2: retry_then_skip exhausted retries returned a red-X placeholder but
wrote nothing to disk, so the next run re-sampled the failing panel
instead of staying skipped. Persist placeholder PNG + manifest with
{"status": "skipped"} so manifest_matches keeps the panel skipped.
Also assert sampler-returned shape == (h, w, 3) and add a defensive
shape check in page_composer before cropping.
Regression tests added: no-mutation, skip persistence across runs, and
shape-mismatch halt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Overwrite README.md with full M1 documentation (nodes table, install,
test, limitations). Restore NODE_CLASS_MAPPINGS / NODE_DISPLAY_NAME_MAPPINGS
in __init__.py now that all three node modules exist; import order fixed
for ruff I001.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add test_integration_e2e.py that exercises the full loader → generator
→ composer pipeline with GreyBackend, asserting 2 pages, 4 panels, and
cache-hit behaviour on second run (PNG round-trip tolerance atol=1/255).
Add examples/workflows/m1_basic_vertical_stack.json as a reference
ComfyUI workflow JSON wiring CheckpointLoaderSimple → ComicScriptLoader
→ ComicBatchGenerator → ComicPageComposer → SaveImage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Auto-fix all I001 (unsorted imports), UP007 (Optional → X | None),
UP035 (typing.Iterable → collections.abc), B905 (zip strict=),
and F401 (unused imports) violations across core/, nodes/, and tests/.
No behaviour change; 64 tests still pass after fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Assembles per-panel IMAGE tensors onto full page canvases via compose_page,
emits panel_bboxes_json metadata, and passes the original panel stack through.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements panel-level cache-skip (manifest hash), per-panel retry loop,
halt vs retry_then_skip failure modes, and white-padding before stacking
mixed-height panels into a single (N,H,W,3) tensor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the ComfyUI input node that loads, validates, and normalizes
comic script JSON via three modes (file, path, inline), plus conftest.py
with a folder_paths stub for CI and 6 TDD tests covering all modes and
error handling.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements solve_vertical_stack() with left/center/right alignment,
gutter support, and LayoutError for overflow and oversized panels.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>