Files
malkuthro-ComfyUI-Koolook/docs
malkuthroandClaude Opus 4.7 9f063345f7 feat(investigations): LTX23 audio-file lip-sync scaffold (#177) (#178)
* feat(investigations): scaffold #177 LTX23 audio-file lip-sync investigation

Reorganize docs/investigations/ into numbered topic folders, each
self-contained with narrative + upstream pin + patches + per-run snapshots.
Add a generic sync helper that mirrors the dev-sync pattern for any
investigation's third-party node modifications.

Structure (new):
  docs/investigations/
    00_LTX23-base-1-step/README.md             (rename of ltx-director-4k-transitions.md)
    01_LTX23-audio-file-lipsync/
      README.md            -- problem, mechanism, patches, reversion, promotion
      upstream.json        -- repo + version + pinned commit + sync target env + files
      patches/             -- source of truth for modified upstream files
        ltx_director.py    -- adds `relay_overrides` multiline JSON input
        prompt_relay.py    -- per-segment sigma per Prompt-Relay paper formula
      runs/
        LOOP.md            -- iteration protocol
        log.md             -- rolling table (empty header for now)

Sync helper:
  scripts/sync_investigation_patches.py <NN>_<topic> [--dry-run]
  - Reads upstream.json for target env var + file list
  - Backs up the target's current state once per day (<file>.bak.<YYYYMMDD>)
  - Worktree-aware: falls back to MAIN's .env via git rev-parse --git-common-dir
  - Prints a two-line report matching scripts/sync_to_dev.py shape
  - Per-investigation trigger phrase via `trigger:` key (sync-audio for #177)

Env:
  .env.example -- documents new KOLOOK_WHATDREAMSCOST_PATH
  .env         -- local path set

Glossary updates (docs/reference/glossary.md):
  + Investigation folder
  + JSON file folder
  + Loop
  + Run
  + Investigation patches
  + sync_investigation_patches.py

Closes the workflow gap from #177: MAIN is now the source of truth for
investigation patches; the live install is a sync destination only. No
manual file copying between iterations — `sync-audio` does it.

* chore(ruff): exclude docs/investigations from lint

The investigation patches under docs/investigations/*/patches/ are
vendored upstream source (WhatDreamsCost-ComfyUI v1.3.2) carried so
sync_investigation_patches.py can mirror them into the live install
and so iterations can be diff'd against the pinned upstream commit.
Linting them would force divergence from upstream and defeat that
purpose. Mirrors the existing forks/ exclude precedent.

Fixes the CI Lint & Smoke failure on PR #178.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor: per-task automation modules + WhatDreamsCost Koolook fork

Pivot docs/automations/ from "one folder per model" to "one folder per
automation module" so the same diffusion model can host several
independent tasks side by side. Replace the parallel docs/investigations/
tree with a fork of WhatDreamsCost-ComfyUI under forks/.

Automation modules (new layout)
- docs/automations/LTX-2.3/base-1step/         the existing single-stage
                                                automation, moved from
                                                docs/automations/LTX-2.3/
                                                with its README, findings,
                                                and handoff-checklist
                                                rescoped to the task. The
                                                4k-transitions backstory
                                                that produced the module
                                                moves here as
                                                backstory/4k-transitions.md.
                                                The earlier
                                                docs/investigations/experiments/
                                                card PNGs/HTML land in
                                                example-cards/.
- docs/automations/LTX-2.3/audio-lipsync/      the audio-file lip-sync
                                                module previously living
                                                under docs/investigations/01_…/.
                                                README, handoff, findings
                                                stub, backstory/, and
                                                runs/{LOOP,log}.md, all
                                                referencing the new fork.
- docs/automations/README.md + CHEATSHEET.md   updated to describe the
                                                module-per-task pivot and
                                                the new tree shape.
- docs/automations/CONVENTIONS.md               unchanged (cross-cutting
                                                loop contract still holds).

WhatDreamsCost Koolook fork (new)
- forks/whatdreamscost_koolook/versions/v1_3_2/
    UPSTREAM_PIN.yaml                          pinned to upstream e81223a
                                                (v1.3.2), GPL-3.0
                                                verified 2026-05-28.
    ltx_director.py                            MODIFIED — adds the
                                                relay_overrides multiline
                                                JSON widget on LTXDirector.
                                                Moved from
                                                docs/investigations/01_…/patches/.
    prompt_relay.py                            MODIFIED — per-segment σ per
                                                the Prompt-Relay paper
                                                formula σ = (L − w_eff) /
                                                (2 · √ln(1/ε)). Moved from
                                                docs/investigations/01_…/patches/.
    patches.py                                 UNMODIFIED — vendored
                                                verbatim from upstream
                                                because ltx_director.py
                                                imports detect_model_type
                                                and apply_patches from it
                                                (same precedent as
                                                radiance_koolook's
                                                color_helpers.py).
- Registered as LTXDirector__koolook_v1_3_2 ("LTX Director (Koolook v1.3.2)")
  so it coexists with an installed copy of upstream WhatDreamsCost-ComfyUI.
  Wired into the root __init__.py NODE_CLASS_MAPPINGS merge.
- forks/forks_manifest.yaml + forks/THIRD_PARTY.md updated with the new
  entry (attribution, change log, license verified date, exposed node IDs).

Removed
- docs/investigations/                          entire tree retired; its
                                                contents either moved to
                                                automation modules or to
                                                forks/.
- scripts/sync_investigation_patches.py         no longer needed — fork
                                                code lives in the Koolook
                                                tree and is synced via the
                                                standard dev-sync flow.
- KOLOOK_WHATDREAMSCOST_PATH in .env.example    same reason.
- docs/investigations in pyproject.toml's ruff
  extend-exclude                                added in 96bf8ad to silence
                                                lint on vendored patches;
                                                no longer needed now that
                                                vendored code is under
                                                forks/ (already excluded).

Docs
- docs/reference/glossary.md                    Investigation folder,
                                                Investigation patches,
                                                JSON file folder, and the
                                                sync_investigation_patches.py
                                                entries replaced by
                                                Automation module, Backstory,
                                                Working folder, and Card.
                                                Loop and Run rewritten to
                                                point at the new structure.
- CHANGELOG.md [Unreleased]                     describes the restructure +
                                                the new fork.

Verification
- ruff check . → all checks passed
- tools/preflight_release.py --skip manager-meta → 42 files parsed; 11
  unique node IDs collected including LTXDirector; VAE dispatch branches
  all OK; fixture workflows still reference all registered Koolook IDs.
- pytest → 173 passed, 0 failed.

Workflow JSON heads-up
The active audio-lipsync workflow LTX-23-audio_tests_v01.json continues to
reference the upstream LTXDirector ID. To pick up the new fork's behaviour,
swap that node to "LTX Director (Koolook v1.3.2)" (registered ID
LTXDirector__koolook_v1_3_2) — sockets and wiring are preserved. The
relay_overrides widget appears at the bottom of the new node. Documented
in the audio-lipsync README's "Workflow JSON — Koolook node ID" section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(scripts): add dev-sync-audio — scoped sync for audio-lipsync iteration

`dev-sync-audio` is a scoped wrapper around `dev-sync` for the
[`docs/automations/LTX-2.3/audio-lipsync/`](docs/automations/LTX-2.3/audio-lipsync/)
iteration loop. It copies *only* the paths that module touches — the
`forks/whatdreamscost_koolook/` subtree and the root `__init__.py` —
instead of `rmtree`-ing the whole dest `forks/` and re-copying every
Koolook fork on every render. Same `KOLOOK_COMFYUI_DEV_PATH` target,
same ComfyUI-Manager restart, same two-line chat-report shape.

Changes
- `scripts/sync_to_dev_audio.py` (new). Thin wrapper that reuses
  `sync_to_dev`'s env loader, target validator, restart helper, and
  chat-report builder, supplying its own `AUDIO_PATHS` tuple. Defaults
  the `--scope` tag to `"audio-lipsync fork edit"` so the build
  identifier names this module even when invoked without an explicit
  scope.
- `scripts/sync_to_dev.py` — `sync()` gains an optional `paths` kwarg
  (defaults to the existing `RUNTIME_PATHS`). Backwards-compatible with
  every existing call site.
- `docs/automations/LTX-2.3/audio-lipsync/README.md` and
  `runs/LOOP.md` updated to point at `dev-sync-audio` instead of plain
  `dev-sync` for the iteration step.
- Project `CLAUDE.md` — new "Per-module scoped variants" subsection
  under the existing `dev-sync` section documents the
  `dev-sync-<module>` pattern, the trigger-name → script-name mapping,
  and reiterates that the user-initiated-only rule applies verbatim to
  every `dev-sync-*` variant.

Verification
- ruff check . → all passed
- python scripts/sync_to_dev_audio.py --dry-run -v →
    would copy: __init__.py
    would copy: forks/whatdreamscost_koolook
    e7f3ab4 - beautiful-mahavira-ae6da6
    would sync 2 entries -> <KOLOOK_COMFYUI_DEV_PATH>  (dev-sync-audio)
  (Confirms scope is correct and the existing dev-sync chat-report
  shape is preserved.)

User-initiated only — never automatic. Trigger phrase: `dev-sync-audio`
in chat (also: "copy audio fork", "sync audio fork"). Same rule as
`dev-sync` — see project CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(audio-lipsync): only snapshot runs when explicitly kept

Every render adds a row to runs/log.md (always). The heavier
run-NNN_<label>/ snapshot folders (workflow JSON copy + relay_overrides
+ patch state + notes) are now created only when the maintainer says
"keep", "save run", "save this", or "snapshot" in chat. Without that
trigger, the render is just a log row.

Why: an active knob sweep can do 10+ renders/hour. Saving a folder
per render bloats runs/ fast and most of those folders are noise the
moment the next render lands. Cheap log row everywhere, expensive
snapshot only for runs worth pinning (knob landed, surprising failure,
something heading for findings.md).

Changes
- docs/automations/LTX-2.3/audio-lipsync/runs/LOOP.md
    Step 5 split into "always log" + "snapshot only on request".
    Trigger-detection table updated. Run-labels section reframed
    (NNN matches log.md row number). Added a one-paragraph "why this
    retention policy" rationale.
- docs/automations/LTX-2.3/audio-lipsync/README.md
    Folder-map flags run-NNN_<label>/ as conditional. Iteration loop
    step 5 names the keep / save-run / save-this triggers explicitly.

Not a code change. No script edits — the retention rule lives in the
agent protocol, not in dev-sync-audio (which stays scoped to code
sync, not run management).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(review): harden relay_overrides parser + polish (PR #178 review)

Addresses verified findings from the /review-pr pass on PR #178.

H1 — relay_overrides values are now type-checked
- Allowlist of recognised knobs (`video_strength`, `video_window_scale`,
  `audio_strength`, `audio_window_scale`, `audio_epsilon`) with explicit
  float-coercion. Numeric strings ("10") coerce silently; non-numeric
  values (e.g. "ten") now raise a ValueError naming the offending knob
  and showing a minimal correct example — instead of a TypeError deep
  inside `build_segments` arithmetic, or a SUCCESS render at upstream
  defaults that polluted the A/B iteration log.

H2 — malformed JSON no longer silently falls back to defaults
- `json.JSONDecodeError`, non-dict JSON (arrays, strings, bare numbers,
  null), and non-numeric values for recognised knobs all now raise
  `ValueError` with operator-actionable messages. The only silent
  fallback is the explicit empty/whitespace case — "use upstream
  defaults" is the operator's intent there.
- Typo'd keys (e.g. `viedo_strength`) log a warning naming the unknown
  keys + the recognised set, then drop the typo. Previously they were
  silently retained in the cleaned dict but never queried by
  `build_segments`, indistinguishable from a default render.

Architectural side-effect — shrinks the diff vs vendored upstream
- `_parse_relay_overrides` extracted into a stdlib-only sibling module
  `_relay_overrides.py`. `ltx_director.py` shrinks closer to upstream
  baseline (one import + one alias instead of a 30-line function body)
  and is easier to diff against `e81223a`. The parser is also now
  unit-testable without ComfyUI's `comfy_api` runtime.

L5 — tests added (31 cases, all passing)
- New `tests/forks/__init__.py` + `tests/forks/test_whatdreamscost_v1_3_2_relay_overrides.py`.
- Covers every confirmed failure mode (H1 + H2), the happy path, the
  comment-key convention, the unknown-key warning, and an allowlist-
  consistency assertion that fails loudly if `build_segments` ever
  grows a knob without the parser being updated.
- Test module uses `importlib.util.spec_from_file_location` so it can
  load the parser without walking through the package hierarchy (which
  would trigger the `comfy_api` import inside `ltx_director.py`).

L3 — `_build_line` promoted to public `build_line`
- `scripts/sync_to_dev.py` renames the private helper. The underscore
  alias is kept for one release cycle as a deprecation slot.
- `scripts/sync_to_dev_audio.py` switches to `_dev.build_line()`. No
  behaviour change; chat-report shape is bit-identical to before.

L2 — `source_ref` clarified in pin metadata
- `forks/whatdreamscost_koolook/versions/v1_3_2/UPSTREAM_PIN.yaml` and
  `forks/forks_manifest.yaml` both now spell out that `e81223a` is the
  post-v1.3.2 README-only commit on upstream's `main`, not the v1.3.2
  release tag itself. Code is byte-identical to the tagged release; the
  pin differs only in trailing docs.

L1, L4 are explicitly left alone (L1 is dead upstream cruft, kept for
minimum-diff; L4 is helper duplication tracked for a separate follow-up
issue when a third fork lands).

Verification
- ruff check . → clean
- pytest → 204 passed (173 prior + 31 new parser tests)
- tools/preflight_release.py --skip manager-meta → all PASS (static AST
  extraction still picks up LTXDirector from the fork)
- scripts/sync_to_dev_audio.py --dry-run → two-line chat report still
  emits via the renamed `build_line` helper

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(audio-lipsync): config-driven loop-audio script + module cheat sheet

End-to-end automation for one render cycle of the audio-lipsync
iteration loop. The chat phrase `loop-audio` invokes a single script
that does the whole chain — no manual artifact-shuffling.

Scripts (cross-cutting infrastructure at the repo root, like make_card
and sync_to_dev; they get lifted into the module folder in the
`docs/automations/` → `automations/` move queued for the follow-up PR)

- scripts/loop_audio.py
    Reads its settings from loop_audio.config.json (alongside),
    auto-discovers the most recent workflow JSON in the ComfyUI user
    workflows dir (derived from KOLOOK_COMFYUI_DEV_PATH or overridden
    via KOLOOK_COMFYUI_WORKFLOWS_DIR), extracts the five tracked Text
    Multilines + LTX Director state + sampler chain, captures
    fork-state (HEAD SHA + last dev-sync-audio sync info + fork-dir
    git status), writes the run-NNN_<auto-label>/ snapshot, appends a
    row to runs/log.md, and chains the card renderer in. Flags
    mirror sync_to_dev: --dry-run, --label, --no-log, --no-card,
    --workflow, --config.

- scripts/loop_audio.config.json
    Single source of truth for the customisable settings (job_name,
    module_path, workflow_pattern, comfyui_workflows_subpath,
    skip_filename_substring, tracked_multilines, fork_to_track,
    render_card). Underscore-prefixed keys are inline documentation
    and ignored by the loader. When the convention shifts — new
    multiline title, new fork path, different filename pattern —
    edit the JSON, not the Python.

- scripts/make_card_audio.py
    Audio-lipsync-specific card renderer. Five sections (KNOB STATE,
    FORK STATE, SAMPLER, BASE notes, OUTCOME) plus an INERT warning
    band when the workflow's Director is upstream `LTXDirector` with
    a non-empty relay_overrides. Same palette + font fallback chain
    as scripts/make_card.py so the base-1step and audio-lipsync card
    families read as a set. PIL/Pillow added to the `test` optional
    deps so CI doesn't hit ImportError if any future test touches
    this script.

Docs

- docs/automations/LTX-2.3/audio-lipsync/CHEATSHEET.md
    Module-scoped operating instructions per the cheat-sheet pattern:
    what the module is for, the chat triggers, the required workflow
    shape (5 multilines + the Koolook Director variant), the render
    cycle steps, what lands in each run-NNN_ folder, findings-
    promotion rule, where every customisable setting lives, and a
    card-section legend.

- docs/reference/glossary.md
    `Card` rewritten to describe the per-module renderer fan-out
    (`make_card.py` for base-1step, `make_card_audio.py` for
    audio-lipsync) — cards no longer live at a single stable
    `_AI/card.png` path. New `Loop config (per-module)` entry
    documenting the `<script>.config.json` convention + the
    required-keys list.

- docs/automations/LTX-2.3/audio-lipsync/runs/log.md
    Header rewritten with column set matching the actual log-row
    output (Director / relay_overrides / Custom audio / Phase 1 /
    Phase 2 / Scores / Notes).

First real run captured

- docs/automations/LTX-2.3/audio-lipsync/runs/run-001_bear_3x-fr_w.audio_upstream_audio-off_vstr10.0/
    Baseline reference render — `Bear_3x-FR_w.Audio`, upstream
    `LTXDirector` still wired into the workflow, use_custom_audio
    off. Motion 4/5 · Sync 5/5 · Sharp 4/5 per maintainer feedback.
    The relay_overrides `{"video_strength": 10.0}` is set but inert
    (Director not yet swapped to the Koolook variant); the card
    surfaces this with the amber INERT band at the bottom. Captures:
    workflow.json (185KB), relay_overrides.txt (with INERT note),
    patch_state.txt (3ffb7b7 / 15d5688 / clean), notes.md (feedback
    + scores + mechanical interp), card.png (61KB).

Verification

- ruff check . → clean
- pytest -q → 204 passed, 0 failed
- tools/preflight_release.py --skip manager-meta → all PASS
- scripts/loop_audio.py --dry-run → identifies the latest workflow,
  emits the two-line chat-report header, doesn't touch disk
- scripts/loop_audio.py (real) → wrote the run-001 folder + card +
  log row in a single invocation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(audio-lipsync card): match base-1step visual style 1:1

The first cut of the audio-lipsync card used solid colored header bands
and block section panels — visually heavier than base-1step's renderer.
Refactored to copy make_card.py's primitives directly:

  - small uppercase accent-colored section labels (no header bars)
  - subtle BG_SECTION panel outlines instead of full-width fills
  - left-accented note boxes (4px coloured bar + dark BG + label inside)
    for Base · notes, Feedback (video), and the conditional Director
    note warning
  - same font fallback chain + sizes as make_card.py (F_SECTION 17pt,
    F_MONO 19pt, F_NOTE italic 18pt)
  - oversized canvas + post-render crop matches make_card.py's pattern
    so cards size to actual content
  - palette imported in lockstep (BG_OUTER/CARD/SECTION, BORDER, the
    three accent triples plus the new ACCENT_FORK violet)

Two tuning fixes spotted during re-render:

  - _wrap_text gained a keep_blank_lines flag so prose blocks can
    collapse the empty separator lines maintainer canvases often have
  - max_lines bumped on the prose blocks: Base · notes 18 (was 6,
    truncated the OVERLAY - INFO body), Feedback 6 (was 5), Director
    note 8 (was 4, truncated mid-sentence)
  - make_card_audio.py's standalone CLI now loads .env via
    loop_audio.load_dotenv before rebuilding state, so re-renders show
    the same FORK STATE lines (MAIN sha + sync sha + scope) that the
    live loop_audio run captures

Visual reference: docs/automations/LTX-2.3/base-1step/example-cards/
run-001-card.png is the layout target. The audio-lipsync card now
reads as a sibling of that family — KNOB STATE in amber, FORK STATE in
violet, SAMPLER + Base · notes in sky, FEEDBACK + OUTCOME in green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(fork): ship LTX Director timeline-editor JS to the Koolook variant

The Koolook variant of LTXDirector was loading as a bare schema — no
image strip, no audio strip, no canvas timeline editor — because the
upstream timeline-editor JS bundle never came across with the fork.
The widget upstream paints onto the node lives entirely in
`js/ltx_director.js`; the Python class only declares the inputs.
Compounding it, the JS's binding check is hard-coded
``if (nodeData.name === "LTXDirector")``, so even if the bundle had
been loaded, it would have skipped our suffixed registry ID
(`LTXDirector__koolook_v1_3_2`).

Fix: vendor the JS into the fork and rebind it.

- web/whatdreamscost_koolook_v1_3_2/ltx_director.js
    Verbatim copy of upstream's
    custom_nodes/WhatDreamsCost-ComfyUI/js/ltx_director.js at the
    pinned commit (e81223a). Two string changes vs upstream:
      * `name: "LTXDirector"` -> `"LTXDirector_Koolook_v1_3_2"`
        so the registered ComfyUI extension doesn't collide with the
        upstream extension (both load in parallel).
      * `if (nodeData.name === "LTXDirector")` ->
        `if (nodeData.name === "LTXDirector__koolook_v1_3_2")`
        so the canvas widget mounts on the Koolook variant only;
        the upstream extension keeps owning the upstream node.
    A short comment block at the top documents both deltas.

- scripts/sync_to_dev_audio.py
    AUDIO_PATHS gains `web/whatdreamscost_koolook_v1_3_2` so
    dev-sync-audio carries the JS into the live install alongside the
    fork's Python files + root __init__.py.

After dev-sync-audio + ComfyUI restart, the Koolook variant mounts the
same timeline editor as upstream: image/audio strips, segment dragging,
the visual preview row, the [Audio] tag handling, the
img_compression/divisible_by widgets. The `relay_overrides` field
declared in the Python schema renders at the bottom of the node
alongside everything else.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 01:56:12 +02:00
..

ComfyUI-Koolook documentation

Three audience buckets — pick where to look based on what you're trying to do.

If you want to … Go to One-line description
Use a node in your ComfyUI workflow user_guide/ Per-node guides, screenshots, recipes
Look something up — a term, a node ID, a list reference/ Glossary, node inventory, spec sheets
Cut a release / fork a node / debug CI / hit the Comfy Registry API maintainers/ Releasing, Registry API, Node versioning rules
Compare UI design proposals before implementation designs/ Focused mockups and screenshots for in-progress UX decisions
Run AI-assisted model evaluation loops automations/ Visual loop overview, setup contract, per-model findings, and /make-card workflow

Files that intentionally stay at the repo root

File Why it's not in docs/
../README.md GitHub repo home + Comfy Registry pulls description from here
../LICENSE pyproject.toml references it; GitHub recognizes the license badge from this path only
../CHANGELOG.md Changelog parsers and release tooling expect it at root
../CLAUDE.md Claude Code reads project-level agent instructions from <repo>/CLAUDE.md