5 Commits
Author SHA1 Message Date
malkuthro 3a0f835814 fix(audio-loop): read active director card values (#195) 2026-05-31 21:25:25 +02:00
malkuthro 63f5c1db7b fix(audio-loop): capture card metadata and Director pins (#193)
* fix(audio-loop): capture card metadata and Director pins

* fix(audio-loop): address card metadata review

* fix(audio-loop): remove duplicate card import
2026-05-31 16:44:46 +02:00
malkuthro 201cdf3001 Track audio loop card setup metadata (#191)
* feat(audio): surface director flavor and prompt mode

* Track audio loop card setup metadata

* Fix audio card review issues

* Harden audio card metadata capture
2026-05-31 00:02:45 +02:00
malkuthro 031af80f43 feat(forks): upgrade Koolook Director to stable v1.3.9 (#187)
* feat(forks): upgrade Koolook Director to stable v1.3.9

* docs(forks): clarify Director upgrade compatibility
2026-05-29 18:09:03 +02:00
malkuthroandClaude Opus 4.7 26124e08d5 refactor(loop-audio): card reads only multilines + Koolook Director (#177) (#185)
* refactor(loop-audio): card reads only multilines + Koolook Director (#177)

The audio-lipsync card is now bounded to two source families: the five
tracked Text Multiline nodes and the LTX Director (Koolook v1.3.2)
node's own widgets + input wiring. SAMPLER block (BasicScheduler /
KSamplerSelect / RandomNoise / CFGGuider scrapes), FORK STATE block
(_dev_build.json + git status), and INERT warning are gone — they were
either widget scrapes outside the two source families or duplicates of
the Director's own state.

New card layout (top to bottom):

  HEADER         run NNN — {name} + date + workflow filename
  KNOB STATE     relay_overrides (the per-render knob)
  BASE · NOTES   overlay - info verbatim, blank lines preserved
  BASE · LOCKED  epsilon · Audio src · Working folder (path-wrapped)
  BASE · SCENE   Segments (N) parent, indented `1) 0 to 5 seconds`
                 child list, flat Prompt/Audio/Keyframe coverage
  POST-RENDER    overlay - feedback + parsed motion/sync/sharp

Audio src now derived structurally from a 4-state machine
(audio_vae link × use_custom_audio × audioSegments count) that mirrors
ltx_director.py:610-622 — never from substring matches on prompt text.
Per-segment Audio coverage checks audioSegments[] overlap, not the
maintainer's [Audio]: prompt formatting.

Also fixed: upstream-LTXDirector widget index bug (saved workflow keeps
legacy order even after Koolook fork reordered the schema; DIRECTOR_WIDX
matches the serialised order); loop_audio.py's .env discovery now falls
back to the main repo when run from a worktree (same pattern as
make_card.py); notes.md drops the widget-scraped Mechanical interp
section; log.md columns realigned (Audio src + Segments replace Phase 1
/ Phase 2 / Custom audio).

Three new docs alongside the module:

  reading-graph.html        visual flow (dark theme, color-coded bands)
  reading-graph.schema.yaml machine-readable mirror of the same graph
  CHEATSHEET.md             refreshed against the new design

All 231 tests pass.

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

* fix(lint): split semicolon statements + drop unused symbols in make_card_audio

Ruff (project's Lint & Smoke gate) flagged 10 issues in the refactored
card renderer:

- F401 unused imports — `typing.Optional` at the top of the file and
  `loop_audio.REPO_ROOT` in the standalone CLI's import block.
- F841 unused locals — `dur_f` and `dur_s` were left over from the
  dropped "Duration" row; segment time ranges convey the same info now.
  Kept in the state dict for notes.md; not read in the renderer.
- E702 six semicolon-joined statements in the HEADER and POST-RENDER
  sections (`draw.text(...); y += N`). Split into separate lines.

Rendering is unchanged — verified by re-running the standalone CLI
against run-002. All 249 tests still pass; ruff now clean.

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

* fix(loop-audio): resolve PR #185 multi-agent review findings

Anchored by the verified multi-agent review on PR #185 — every fix
traces to a confirmed finding (false-positive rate was 0/11).

HIGH:
- derive_audio_state now distinguishes "(no director)" from "off (no
  VAE)". Pre-fix, is_input_wired(None, ...) returned None, the
  truthiness check collapsed director-missing into VAE-unwired, and
  the card / log silently agreed on the wrong story. Added an early
  return when director_node is None. Also documented that vae_wired
  can be None (input socket absent in older schemas) and that we
  intentionally collapse it with False at the unwired branch — after
  the missing-director case has been split off.

- extract_multilines now iterates needles longest-first. Pre-fix the
  per-node `break` was order-dependent on config insertion: a future
  short substring (e.g. "relay") in the same tracked_multilines list
  would have silently shadowed "relay_overrides". Sorting by length
  descending removes that footgun without breaking the current 5-needle
  layout.

- parse_timeline now coerces per-segment numeric fields (start /
  length / trimStart) to int via int(float(...)) with try/except → 0
  fallback. The upstream Director itself does this defensively at
  ltx_director.py:232-234; without coercion a string-valued field
  silently breaks video_segment_has_audio's `<` comparisons
  (lexical) and crashes the renderer at `start / fps`. Also drops
  non-dict entries from segments / audioSegments arrays so a
  malformed JSON can't slip past json.loads + isinstance checks.

- Duplicated wrap_path + video_segment_has_audio bodies in
  make_card_audio.py are gone. Renderer now imports them at module
  level from loop_audio so the two scripts agree byte-for-byte on
  path-wrapping rules and audio-overlap geometry.

MEDIUM:
- DIRECTOR_WIDX now carries a MIRROR: comment pointing at the YAML
  schema's `widget_order:` block (the cross-link the review asked for
  as the minimum drift-prevention fix).

- duration_frames / duration_seconds removed from the state dict and
  from render_audio_card's docstring. The renderer dropped the
  Duration row earlier in the PR; notes.md reads them straight from
  director_node via director_widget, so keeping them in state was
  documenting a contract the renderer didn't honour. YAML schema now
  splits them out under a notes_only section to match.

- Log-row score formatting now uses an explicit `is not None` check
  (`_score()` helper) instead of `or '?'` — 0 was a legitimate score
  the maintainer could type that pre-fix collapsed to '?' because 0
  is falsy. Matches the card's existing `_s()` helper for parity.

- Card already surfaces the missing-Director state distinctly thanks
  to derive_audio_state returning "(no director)" — the BASE · LOCKED
  Audio src row shows that label loudly when applicable. No
  additional row needed.

LOW:
- Renderer falls back to "1) frames 0-120 (no fps)" when frame_rate
  is None instead of computing "0 to 0 seconds" silently.

Test coverage:
- New tests/scripts/test_loop_audio.py — 41 tests covering all five
  audio_src states, multiline ordering edge cases, parse_timeline
  coercion + malformed inputs, video_segment_has_audio overlap
  boundaries, parse_feedback (with explicit zero-score regression
  test), is_input_wired None vs False semantics, wrap_path
  separator-only wrapping, pick_existing_path filesystem fallback,
  autogen_label slug derivation.

Docs:
- reading-graph.html lists five audio states (was four).
- reading-graph.schema.yaml audio_state_machine adds "(no director)"
  state + director_present input; state_dict moves duration_frames /
  duration_seconds into a notes_only section; invariants block grows
  with the missing-director split, type-coercion guarantee, longest-
  needle-wins guarantee, and zero-score preservation.
- CHEATSHEET.md Audio src table grows from 4 to 5 rows.

Validation:
- 290 tests pass (was 249 — 41 new + 0 regressions).
- ruff clean on scripts/loop_audio.py, scripts/make_card_audio.py,
  tests/scripts/test_loop_audio.py.
- Standalone CLI re-renders run-002 card identically (same workflow,
  same pixels).

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 16:56:02 +02:00