* feat(automations): workflow JSON validator + LTX 2.3 stage-2-only findings Adds scripts/validate_workflow.py — a stdlib-only CLI that checks a ComfyUI workflow JSON for internal consistency: link-table declarations vs node-IO references, slot index bounds and types, endpoint cross- checks, last_node_id/last_link_id bounds, and duplicate IDs. Catches the class of bugs that come from hand-editing workflow JSONs (e.g. when an agent re-wires a graph to build a slim variant from a Director workflow). Comfy's loader silently drops or mis-wires bad parts, so the cost of an undetected drift is "graph looks fine but behaves wrong" — exactly the failure mode this script blocks. Exit codes: 0 clean, 1 problems on stderr, 2 file missing or bad JSON. Docs: - docs/automations/CONVENTIONS.md §11 — when and how to run the validator, full list of checks performed. - docs/automations/LTX-2.3/findings.md — two new sections covering the spatial upscaler's architectural facts (ltx-2.3-spatial-upscaler-x2 is hardcoded x2, the brief enters only via Director > guide_data > DirectorGuide, attention scales (W * H * T)^2) plus three open hypotheses for a stage-2-only refine path. - CHANGELOG.md [Unreleased] entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(scripts): make validate_workflow.py robust against malformed JSON Addresses PR #170 review (Codex GPT-5.3) — the validator crashed with AttributeError on malformed-but-valid JSON, defeating its goal as a safe preflight tool. Two repros from the review: {"nodes":[1],"links":[],"last_node_id":0,"last_link_id":0} -> AttributeError: 'int' object has no attribute 'get' [1,2] -> AttributeError: 'list' object has no attribute 'get' Both confirmed locally. Now every shape assumption is guarded: - Top-level must be a JSON object; non-object short-circuits with one clear problem line. - 'nodes' and 'links' must be lists; non-list reported and treated as empty so subsequent checks still run. - Each node entry must be a dict; bad entries reported and dropped. - Each input/output entry must be a dict (extracted into a _sanitize_slots helper, cached per node so duplicates aren't reported when the same node is touched by multiple links). - Each link entry must be a 6-element list; malformed link entries reported and skipped without crashing later phases. - node_by_id only keys on int node IDs (non-int IDs can't match a link's int src/dst, so we don't mask bad data by accepting them). Adds tests/scripts/test_validate_workflow.py — 24 tests covering both review repros, every new guard, and a sampling of the existing consistency checks (link-to-missing-node, type mismatch, endpoint desync, duplicate IDs, last_node_id bound, CLI exit codes). Full suite: 168 passed in 1.59s. CHANGELOG entry updated to reflect the defensive behavior + test coverage. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.4 KiB
Conventions
Cross-cutting rules that every loop under docs/automations/<model>/ shares. Edit here when something changes globally; per-model docs reference these.
1. The working folder
One folder = one project = everything for one iteration.
Structure:
<working folder>/
<workflow>.json ← user content (ComfyUI export)
<workflow>.mp4 ← user content (rendered video)
_AI/ ← AI-managed — safe to keep / commit separately
card.png ← tracking card overlay
iterations.md ← append-only log
_AI/subfolder holds everything the agent writes. Wiping it never destroys user content; wiping the parent never touches the AI's history. Either side can be cleaned independently.- Path lives in two places, kept in sync:
- Inside the workflow — a Text Multiline node titled
Working_Folder_PATH(also matchesOUT_working_folder,Work_Folder, etc. — case-insensitive title contains "working_folder"). - In
.env(repo root, gitignored) —KOLOOK_AUTOMATIONS_WORK_DIR=<path>. See.env.examplefor the template.
- Inside the workflow — a Text Multiline node titled
- The
Working_Folder_PATHnode wins if both are present; the env var is the fallback used by/make-cardto find the latest JSON when invoked without an argument. - When switching projects: update the node, save the workflow into the new folder, update
KOLOOK_AUTOMATIONS_WORK_DIRin.env. No script or doc edits.
2. The Text Multiline node contract
Three named nodes drive the card. Naming is matched by title containing (case-insensitive); exact strings below are recommended.
Working_Folder_PATH
- Body: one line, the absolute working folder path (forward slashes preferred).
- Read by:
scripts/make_card.pyto determine where to write_AI/card.pngand_AI/iterations.md.
OVERLAY - INFO
- Body: a free-form summary of this run, plus a section marked
BASE (notes):orBASE:whose content captures Δ from baseline. - Example body:
Phase 1 Format: 2K Mult: 1 Scheduler: linear_quadratic Steps: 8 Denoise: 1 BASE (notes): Changed Audio for each segment to match the duration - The card extracts the
BASE (notes):block as the "Δ from base" text. The other lines are informational — already captured from the actual workflow nodes.
OVERLAY - FEEDBACK
- Body: post-render observations + score lines.
- Score syntax:
motion: 4/5,sync 5/5,sharp: 3,sharpness: 4— case-insensitive,/5optional, integer 0–5. - Non-score lines are kept as the feedback text on the card.
- Example body:
Teeth are missing in some frames Very subtle loss of quality on Hi-speed movements motion: 4/5 sync: 5/5 sharp: 4/5
3. The card
- Filename:
_AI/card.pnginside the working folder. Stable — always overwrites. NLE points at it once. - Width: 540 px. Vertical, sits beside a 16:9 video.
- Sections: Header → Phase 1 → Base · notes → Base · locked → Base · scene → Post-render (render time + output + feedback + outcome scores).
- Palette: mirrors
docs/designs/snapshot-dialogs.html(panel#151515, panel-soft#1a1a1f, amber#ffb84d, sky#6db4ff, green#7bcf80).
Layout + extraction lives in scripts/make_card.py. Iterate on that file, not on per-model docs.
4. Render duration
Sourced from ComfyUI's own log (<comfyui-root>/user/comfyui.log), not from disk timestamps. The script pairs the latest saving images: 100% line with the next Prompt executed in X and shows that duration — wall-time for the actual render that wrote frames. Disk-based fallbacks (EXR sequence span, MP4 mtime delta) are used only if the log is unavailable.
Log path resolution:
KOLOOK_COMFYUI_LOGenv var if set (explicit override).- Inferred from
KOLOOK_COMFYUI_DEV_PATH→<comfyui-root>/user/comfyui.log.
5. The iterations log
- File:
_AI/iterations.mdinside the working folder. - Append-only Markdown table. Newest at bottom.
- One row per call to
/make-card. Dedupe key:(json_filename, json_mtime)— re-running on the same iteration won't double-log. - Hidden HTML-comment fingerprint at end of each row enables the dedupe.
- Columns:
# · When · Run · Format · Denoise · JSON · Video · Δ from base · Feedback.
6. The /make-card skill
- Trigger:
/make-card,card, ormake card. - Reads
KOLOOK_AUTOMATIONS_WORK_DIRfrom.env(repo root) for the working folder. - Globs newest
*.jsonin that folder; runsscripts/make_card.pyagainst it. - Writes
_AI/card.png+ appends_AI/iterations.mdrow. - Returns the absolute path of the PNG + shows it inline.
7. The watcher (optional)
scripts/watch_cards.py— leave running in a terminal during a session.- Polls the working folder every 2 s.
- Re-renders
_AI/card.pngautomatically whenever the JSON in the folder is modified. - For hands-free operation alongside ComfyUI's "auto-save workflow on queue" setting (pythongosssss custom-scripts).
8. The loop filename marker
Files whose basename contains loop (case-insensitive) are treated as post-card outputs — the composited card+video that ComfyUI writes back to the working folder after /make-card has produced the card. Auto-discovery and the watcher both skip these:
LTX-Director_loop_00001.mp4,LTX-Director_loop_00001.json→ ignored.LTX-Director_00002.mp4,LTX-Director_00002.json→ picked up normally.
Reason: without the skip, the script's "newest JSON in the folder" picks up its own downstream output, generating a stale card pointing at the previous iteration. Naming the post-card pass with loop in the basename keeps the auto-discovery clean.
If you need to force-render a card for a loop-named JSON, invoke the script explicitly: python scripts/make_card.py <wf_loop.json>.
9. Run identification
The card title's "Run" label is auto-derived from the JSON stem:
v01/v04style wins if present — givesRun v04.- Otherwise falls back to a 5-digit sequence number like
_00007_— givesRun #7. - Otherwise
Run ?.
ComfyUI's metadata bundle JSONs (<name>_<seq>.json) work natively with #2.
10. Tools — where they live (and don't move)
| Tool | Path | Why here |
|---|---|---|
| Card renderer | scripts/make_card.py |
All project scripts under scripts/. |
| File watcher | scripts/watch_cards.py |
Same. |
| Workflow validator | scripts/validate_workflow.py |
Same. See §11. |
| Skill | .claude/skills/make-card/ |
Claude Code expects skills under .claude/skills/. |
| Working folder env var | .env → KOLOOK_AUTOMATIONS_WORK_DIR |
Project convention; see .env.example. |
| Per-project artifacts | <working folder>/ |
One path for everything. |
| Loop docs | docs/automations/<model>/ |
This folder. |
11. Hand-editing workflow JSONs — validate before reload
Workflow JSONs are large and link-heavy. Adding or removing a node by hand (or via an agent edit) is easy to get subtly wrong: a link can be declared in the top-level links table but missing from one endpoint's IO; a slot index can drift; a slot type can mismatch. ComfyUI's loader will sometimes silently drop or mis-wire the bad parts, producing a graph that looks fine but behaves wrong.
Run scripts/validate_workflow.py after any non-trivial hand edit:
python scripts/validate_workflow.py <workflow.json>
# OK: <name>.json — 22 nodes, 26 links, 5 groups
Checks performed:
- The file parses as JSON.
- Every link referenced by a node input/output is declared in the top-level
linkstable. - Every entry in the
linkstable is referenced by both endpoints (no orphans). - For each link, the source/destination nodes and slots exist and the slot type matches the declared link type.
last_node_id >= max(node ids)andlast_link_id >= max(link ids)(so the next added node/link won't collide).- No duplicate node IDs or link IDs.
Exit codes: 0 clean, 1 problems printed to stderr, 2 file missing or not valid JSON. Use --quiet to suppress the OK summary in scripts.
When to run it:
- After any agent edit that adds, removes, or re-wires nodes in a workflow JSON (e.g. building a slim variant from a Director workflow).
- Before opening a hand-edited workflow in ComfyUI for the first time.
- In CI, against any workflow JSON checked into the repo (none today — workflow files normally live in
<working folder>/, not the repo).
stdlib only — no install step.