Commit Graph
1100 Commits
Author SHA1 Message Date
digital-garbageandClaude Sonnet 4.6 6009d79ad4 sampler: experimental ALG anchor de-staticking on Distilled Flow
Adaptive Low-Pass Guidance (arXiv:2506.08456): blurs the i2v anchor frame
while sigma is in the near-pure-noise region, then swaps back to the sharp
anchor, to stop the model shortcutting to a near-static video that just
matches the reference image. Opt-in, off by default, no-op without an i2v
anchor. Swaps model.latent_image per step (the same attribute ComfyUI's
inpaint compositing already re-pastes every step) instead of monkeypatching
comfy internals.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 21:41:20 +03:00
digital-garbageandClaude Sonnet 4.6 dff6a3f604 Release 3.1.1: CHANGELOG, README, version bump
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 20:01:06 +03:00
digital-garbageandClaude Opus 4.8 e11a4e109f editor: prompt $variables + global-prompt templates (Composer)
Project-scoped $name variables resolved DEAD LAST in refine_v2 — after
shortcut-expand and the transition split — so a value containing a comma or
trigger word can never move a scene cut (order: shortcuts -> splits -> vars).
Recursive (a value may reference other vars), cycle-safe (self-reference left
literal, no infinite loop), undefined -> literal $name.

- templates.resolve_variables: recursive, depth-guarded, cycle-safe resolver
- refine_v2: resolve base prompt (single-scene) + per-scene texts (post-split);
  variables threaded via studio_settings.refiner (same channel as scenes)
- builder/server/timeline: carry project.variables into the build; persist
  variables + prompt_templates on the project
- Composer: templates dropdown + Save above the prompt; collapsible Variables
  editor (Add/Remove rows) below it, with live undeclared/cycle hint

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 12:11:44 +03:00
digital-garbageandClaude Opus 4.8 7e053e9d60 fix: atomic refinement-key deletion + surface keyless Absolute store
Deleting a refinement key was non-atomic. A key's state spans <key>.json plus
sidecars (<key>.value_fn.pt, <key>.sampler_ctx.json, attn_maps/blessed maps +
K/V banks + creativity latent + velocity store), and there was no delete route —
users removed the visible <key>.json by hand, orphaning the rest. The orphans are
still applied: _v2_ascend_conditioning loads <key>.value_fn.pt purely by name
(not gated on the json), and blessed K/V inject at a 0.04 floor even at reward 0.
So a "cleared" key kept steering future generations, surviving both a ComfyUI
restart and the manual delete — the persistent half of the long-run quality drift
(the per-session half was the hook leak in the previous commit).

- templates.py: delete_refinement_key() sweeps the canonical json + every <key>.*
  sidecar + clear_refinement_data (attn_maps banks, velocity disk+memory).
  absolute_store_info()/clear_absolute_store() for the keyless Absolute taste store
  (__funpack_absolute_global__): it learns from every rated gen across all prompts,
  is invisible in the key list, and was otherwise only wiped by Session Reset.
  Routes: POST refinement_keys/delete, GET/POST refinement_keys/absolute|clear_absolute.
- Refinement Key Loader node: "Delete Key" + "Clear Global Taste" buttons.
- Movie Editor: api deleteRefinementKey/absoluteStoreInfo/clearAbsoluteStore +
  FunPack menu "Delete refinement key…" / "Clear global taste store…".
- tests: atomic-delete sweep + empty-key guard (real store, unique key, cleanup).
  Pre-existing 16 failures unchanged (stale mocks); +2 new passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:53:08 +03:00
digital-garbageandClaude Opus 4.8 9fa0d3d2ea fix: strip leaked Technique-5 block hooks across runs (quality drift)
build_enhancements registered forward/forward-pre hooks directly on the
shared diffusion_model blocks but only removed them on scene-transition
sigma jumps, never at end-of-sampling. Single-scene runs (and the final
scene of multi-scene runs) thus left hooks installed; because ModelPatcher
clones share the underlying blocks, every subsequent generation stacked
another set, each lerp-ing block output toward a previous run's stale
reference tensor -> progressive corruption that survived Session Reset and
deleting refinements/ (the leak is in-process, not on disk) and only
cleared on a full restart.

Tag every installed hook (_FUNPACK_HOOK_TAG) and add strip_funpack_block_hooks
to pop only tagged entries from each block's hook stores (idempotent, leaves
foreign hooks intact). Strip at the top of build_enhancements and defensively
at FunPackLTXAVSceneChainSampler.sample() so accumulation is bounded to zero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:37:58 +03:00
digital-garbageandClaude Opus 4.8 593267daa8 editor: JoyAI-Echo paired audio memory + v2a coupling (phase 2)
Extend the JoyAI cross-shot memory bank from video-only to paired audio-video,
the part that distinguishes JoyAI-Echo from a generic video-consistency trick.

- _JoyAIMemoryBank now stores (video, audio) pairs under the same pin/roll policy;
  frames()/audio() stay 1:1. Audio half is None until joyai_audio_memory is on, so
  the phase-1 video path is byte-identical.
- _append_joyai_audio_memory pins each banked prior-shot audio latent into the
  chunk's audio stream as a fully-protected (mask=0) tail, cropped back off after
  sampling (_crop_audio_tail). The audio analogue of the video guide bank; rides
  native audio self-attention since LTX has no audio guide-attention API.
- _harvest_joyai_audio harvests the paired audio frame, position-matched to video.
- v2a_grad_scale scales the model's trained video->audio cross-attention via
  reversible forward hooks on each AV block's video_to_audio_attn submodule
  (JoyAI uses 2.0). 1.0 installs no hook -> native behavior, zero overhead.
- joyai_audio_memory + v2a_grad_scale wired through sample(), batch training,
  refinement context, run_mechanisms, INPUT_TYPES (append-only), and editor knobs.

Deliberately breaks the audio pass-through invariant, so both are opt-in and
off/native by default. 3 new bank tests; zero regressions (same 12 pre-existing
stale-mock failures before/after).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:04:32 +03:00
digital-garbageandClaude Opus 4.8 e5976272dd fix: move JoyAI memory inputs to end of sampler INPUT_TYPES (widget desync)
The inputs were inserted mid-list (after mid_scene_guide_strength). The builder seeds
sampler widget values from the reference workflow's POSITIONAL widgets_values array
(extract_widgets maps it onto INPUT_TYPES order), so inserting widgets shifted every
following widget — landing a numeric on the joyai_frame_select combo ("0 not in
['center','first','random']") and corrupting the seeded defaults for embed_guidance /
score_slider / decode_*. Same class of bug the existing forceInput-ordering comment warns about.

Append-only fix: joyai widgets now sit at the END of the widget sequence (after
embed_guidance_source, before the forceInput sockets). They fall beyond the reference
array so extract_widgets leaves the object_info default ('center') in place, and all
pre-existing widgets realign. No logic change; behavior identical once on.

Requires a ComfyUI restart to reload object_info.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:28:19 +03:00
digital-garbageandClaude Opus 4.8 349367e1b3 editor: surface JoyAI-Echo memory toggle in Engine Settings
The phase-1 sampler inputs existed only on the raw node; the editor's sampler panel is a
hardcoded knob list, not object_info-driven, so they weren't reachable in the normal flow.
Add joyai_memory + its size/anchors/frame-select/strength knobs to SAMPLER_KNOBS and the
Experimental group (beside mid_scene_guide). They flow to the sampler node automatically via
builder.py's generic sampler_inputs pass-through — no backend change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:20:10 +03:00
digital-garbageandClaude Opus 4.8 44d12c3203 sampler: JoyAI-Echo memory mode (phase 1 — video bank)
Diagnostic conclusion from the JoyAI-Echo engine: conditioning is fed to the sampler
unchanged (gemma+connector, which Studio already produces), so the ONLY missing sampler
feature is generate_BidirectionalMemory's cross-shot paired AV memory bank. This lands the
VIDEO half: a managed set of clean prior-shot frames injected into each scene so identity
carries across the whole chain — JoyAI's story-level consistency.

Generalizes the single-anchor mid_scene_guide to N frames, reusing the same LTX
guide-attention primitive (_append_guide_latent). _JoyAIMemoryBank holds the policy
(num_fix opening shots pinned as a global anchor + rolling most-recent window capped at
max_size, matching configs/inference.yaml: max_size 7, num_fix_frames 3, center select).
Harvest one clean frame per finished scene; inject the bank before each continuation scene;
supersedes mid_scene_guide when on. Honors the 0.25 audio-safe strength floor.

Off by default, so the audio protect/pass-through invariant is unchanged. Paired audio
memory + v2a_grad_scale coupling is the next phase (needs the ltx_core v2a term).

Inputs: joyai_memory, joyai_memory_size, joyai_fix_frames, joyai_frame_select,
joyai_memory_strength. Threaded through batch-training; logged in run_mechanisms + the
refinement sampler-context. New test_joyai_memory.py covers the bank policy (6 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:16:30 +03:00
digital-garbageandClaude Opus 4.8 716e21b562 editor: clip length adopts the real encoded file duration, not the plan estimate
A scene's frozen render.durationSec was _planDurationSec (snapped frames/fps), but the
actual encoded file can be longer (LTXAV muxes audio; container fps rounding), so the
timeline drew the clip SHORTER than the real 10s media. sceneDurationSec already routes
render.durationSec for project-mode scenes, so the fix is to store the real length there.

Probe the decoded file's duration (mirrors _probeVideoClipDuration) and adopt it for
STANDALONE renders only — multi-scene chains share one file and partition it via
inSec/durationSec slices, which must keep their plan-based lengths. Runs on generation
(immediate) and on project load via _validateSceneRenders (retroactively fixes existing
clips once their file is confirmed on disk). Idempotent.

A separated-audio track that was auto-pinned to the old plan length is carried along to
the new duration; a hand-trimmed track (length no longer matching) is left untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 17:33:42 +03:00
digital-garbageandClaude Opus 4.8 8725b9d517 sampler: contrastive-pair FreeSliders — minus pole from learned bad_dir
The score_slider synthesized a symmetric +/-liked pair, so its axis was
"liked vs anti-liked mirror" — meaningless as an away-from-bad signal. When
3+ disliked/awful gens have populated the existing bad_dir slot in the same
taste store, build the slider's minus pole from that real disliked direction
instead. The (eps_+ - eps_-) axis becomes good-vs-bad, actively steering the
noise prediction away from the conditioning region that produced rated-bad
gens. Falls back to the symmetric mirror when no bad_dir exists.

Reuses existing plumbing: bad_dir is already accumulated identically to
liked_dir (running-averaged unit-norm delta from session mean). Generalized
the direction loader to _load_taste_direction(slot); load + pass bad_dir at
the call site; log/run-mechanism note which pole is active.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 17:14:25 +03:00
digital-garbageandClaude Opus 4.8 e7601182e6 editor: scene length honors frames_mode so project-mode scenes track project length
Scene.eff_frames / eff_fps returned the per-scene value whenever frames/fps was
non-None, ignoring frames_mode. So a scene in "project" (inherit) mode with a
leftover per-scene frames value kept that stale length and ignored project length
changes — exactly the "scene ignores project length" symptom. Now only
timeline/custom modes use the per-scene value; project mode always inherits.

The frontend already respected the mode (sceneEffFrames/sceneEffFps); this aligns
the backend (per-scene duration, chain layout, merged-cut total) with it.

Tests: project-mode scene ignores stale frames/fps and follows project length
changes; timeline/custom keep their per-scene length. Updated the subclip-collapse
fixture to mark split clips timeline-mode (how the split UI actually creates them).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 22:19:13 +03:00
digital-garbageandClaude Opus 4.8 0a5be39238 editor: cycle-guard + smart auto-wire so full control stops fighting manual wiring
Full control let you wire freely, but build() still auto-wired and honored role-
default wires with no cycle-safety — so inserting an upscale node (decoded frames
-> upscale -> export) could close a loop through Studio·source_image and hand
ComfyUI a graph it rejects with the opaque "Dependency cycle detected".

- auto-wire is now cycle-aware: never picks a source that already depends on the
  target (reports it for explicit wiring instead of looping).
- _break_cycles runs before validation: a looping auto/role-default wire is
  dropped with a clear note; an all-explicit loop is reported as blocking with the
  exact node path. Core-internal links and explicit user/override/input-source
  wires are protected and never dropped.
- Decoded frames (sampler output 1) feed a custom node via core:sampler:1, and a
  loader with no explicit output wire still auto-feeds a custom node's input — so
  UpscaleModelLoader -> ImageUpscaleWithModel -> VHS images wires cleanly.

Tests: upscale-before-export wires acyclically + loader auto-feeds; looping
role-default source_image wire is dropped; all-explicit cycle is reported.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 18:43:30 +03:00
digital-garbageandClaude Opus 4.8 c70d046a14 editor: show postfix in the prompt preview
The per-scene prompt preview mirrors what Studio encodes, and the postfix is
appended to every scene at generation — so it belongs there. Surface it once
after the scenes as a 'postfix' badge, symmetric to the 'anchor' badge shown
once above them.

- bridge.expand_prompt_fragment: expands a standalone fragment (anchor/postfix)
  the same way generation does, so the previewed text matches exactly.
- server preview: result["postfix"] = expanded effective_postfix(p).
- inspector: postfix badge line; _promptPreviewKey includes postfix so editing
  it refreshes the preview live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:28:01 +03:00
digital-garbageandClaude Opus 4.8 c5ead80ae8 editor: fix scrambled timeline clip order after global-prompt edits
The cut order (timeline_order) is a list of scene ids. Editing the global
prompt rebuilds the plan and reassigns scene content to ids positionally, but
_distributeGlobalPrompt left timeline_order untouched — so a stale id sequence
scrambled the live clips against their plan badges (e.g. 01,02,04,03,...),
even though ghost re-layout (plan-anchored) stayed correct.

Fix: the cut order tracks the plan until the user explicitly pins a custom
order via the ◀ ▶ arrows. New Project.timeline_manually_ordered flag, set by
moveTimelineClip; _ensureTimelineOrder follows plan order when it's unset and
heals (survivors keep their manual order, new appended) when set. Reconcile on
global-prompt apply too. Existing drifted projects self-heal on load.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:15:55 +03:00
digital-garbageandClaude Opus 4.8 dc70e2b9c6 test: lock in postfix shortcut expansion + key fold-in
A shortcut in the postfix is expanded at the generation hit (same split_scenes
path as the anchor) and its refinement key folds into every scene. The raw
postfix is preserved on the project for round-trip; expansion is generation-time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:04:19 +03:00
digital-garbageandClaude Opus 4.8 6e6b156c4a editor: scene postfix — shared text appended to every scene, toggleable
Symmetric to the anchor (prepended): a project-level postfix appended to
each scene's conditioning. Lives next to the negative prompt in the project
inspector with its own enable toggle; never part of the verbatim global prompt.

- timeline: Project.postfix + postfix_enabled, effective_postfix(), carried in
  build_generation_scene_segments and folded into the run fingerprint.
- conditioning: split_scenes_from_segments expands the postfix (keys fold into
  every scene); the structural fold appends it after anchor+scene text.
- inspector: Postfix field with inline enable toggle after Negative prompt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 17:01:35 +03:00
digital-garbageandClaude Opus 4.8 8e04f9592f editor: corrupt/incompatible project opens with a clear error, not a 500
Reliability sweep finding. list_projects() lists a project as long as its JSON
parses, but _project_or_404 -> projects.get() did Project.from_dict(json.loads(...))
with no guard. A corrupt or schema-incompatible project file therefore appeared in
the list but 500'd with no detail when opened (an un-openable dead end).

- server._project_or_404: catch the load failure and raise HTTPUnprocessableEntity
  with a single-line, header-safe reason instead of a bare 500.
- frontend loadProject: wrap API.getProject so a failed open keeps the current view
  and shows a notice with the reason, instead of silently half-loading.
- Tests: corrupt project -> clean 4xx (reason single-line); missing project -> 404.

Sweep also confirmed the rest is solid: generation poll gives up after 3 pending
checks with a clear message, queue_prompt raises on node_errors, project/media/nle
JSON parses are guarded, saves are atomic, commit() prunes selection by id, failed
saves flip the 'unsaved' chip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 01:30:04 +03:00
digital-garbageandClaude Opus 4.8 2c9755f6c8 editor: pass scene boundaries structurally — remove 'scene N' injection entirely
Root cause the user pushed on: the editor flattened all scenes into ONE prompt
string and Studio re-derived boundaries by parsing text, so a carry scene with no
transition needed an injected 'scene N' delimiter. The editor already KNOWS every
boundary (each generative unit is a scene) — it never needed to smuggle markers
into the prompt.

- timeline.build_combined_prompt: no longer injects anything (verbatim anchor +
  scene texts). New build_generation_scene_segments(project) lists the boundaries
  the editor already has (each unit's raw text, prefixed with its REAL transition
  so effects survive; carry = plain text).
- conditioning.split_scenes_from_segments: canonical split from an explicit scene
  list (reuses split_scenes per item for expansion/keys/effect, folds the anchor),
  same output shape as split_scenes. refine_v2 gains scene_segments and uses it
  when present (count-preserving: a text-less i2v scene keeps its slot, no anchor
  shift); falls back to text-splitting only for raw node-pack use.
- server/builder: editor sends scene_segments -> studio_settings.refiner.scenes.

Net: the generation prompt Studio encodes is now exactly what the user typed —
no 'scene N' anywhere, in the prompt or the conditioning. Tests updated to assert
the verbatim prompt + structural segments; new tests for split_scenes_from_segments
(boundaries, empty-scene preservation, anchor/key fold). Fallback path unchanged
(stash-verified zero regression). NOTE: montage generation needs GPU verification.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:50:11 +03:00
digital-garbageandClaude Opus 4.8 8dc163bca6 fix: generic 'scene N' split markers must never be encoded
Multi-scene bug: the Movie Editor injects a generic 'scene N' separator before
any scene that lacks a leading transition trigger (build_combined_prompt), purely
to force a boundary. split_scenes was splitting on those markers with placement
'start', which KEEPS the marker word in the new scene's text -> 'scene 1',
'scene 2', ... got encoded into every scene's conditioning. With a text-less
leading i2v scene this also produced a label-only first scene (renders as the
anchor with no prompt) and shifted each scene's content down by one.

Fix in the one canonical splitter: a generic 'scene <N>' label (not a real
custom trigger in the transition DB) now cuts SILENTLY -> boundary only, dropped
from both expanded (generation) and raw (editing) text. Real user/DB triggers
keep their configured placement, so 'cut'/'qcut' etc. are unaffected.

Regression test asserts the markers split but never appear in any scene's
expanded/raw text, and that a real trigger still leads its scene.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:25:46 +03:00
digital-garbageandClaude Opus 4.8 f513d485a9 opencut_ui: surface score_slider (FreeSliders) in Engine Settings
The Chain Sampler's Guidance section already exposes embed_guidance via the
sampler-knob path; add its score-space sibling the same way. Two new knobs in
SAMPLER_KNOBS (score_slider bool, score_slider_strength eta 0-3, dependsOn the
toggle) listed in the chain_guid section beside embed_guidance. Flows through the
existing sampler_inputs path (setSamplerInput -> _run_sampler_inputs -> builder ->
Chain Sampler node); refinement_key_input is already wired, so no backend change.
BachVid + KV-Lock need no UI (automatic, ride the refinement key).

Test: score_slider sampler_inputs pass through _run_sampler_inputs untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:05:35 +03:00
digital-garbageandClaude Opus 4.8 c25a49fb01 FreeSliders: score-space taste slider on Scene Chain sampler
Training-free concept sliders (arxiv 2511.00103) in noise-prediction space,
sourced from the learned taste (user's choice). New score_slider + strength(eta)
inputs on the Scene Chain sampler, additive/non-disruptive, reusing the same
learned liked direction + embed_guidance_source + refinement_key_input.

Mechanism: a per-scene model_function_wrapper synthesizes a +/- concept pair from
the base conditioning and the liked direction (no concept prompts), runs 3 forward
passes on quality-phase steps and combines eps_mod = eps_base + eta*(eps_+ - eps_-).
High-sigma steps stay single-pass = the paper's k-step base-only warmup, keeping the
2x cost off early steps. Video-only: +/- conds are audio-protected and the eps blend
is masked to the packed video stream. Composes with embed_guidance (its nudge cancels
in the +/- difference). Installed/torn down per scene in reverse order; threaded
through batch-training re-dispatch. Fully guarded -> falls back to base prediction.

Tests: tests/test_score_slider.py (5) — combine, warmup, eta=0 no-op, eta linearity.
Pre-existing 16 suite failures unchanged (stale-mock/splitter set); zero new.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 17:49:44 +03:00
digital-garbageandClaude Opus 4.8 56dac0a0ef KV-Lock Phase 2: variance-gated scheduler drives BachVid injection
Inversion-free adaptation of 'When to Lock Attention' (arxiv 2603.09657).
Track x0 prediction instability per step (relative step-change of the predicted
clean latent, video-masked = a cheap proxy for the paper's variance-of-x0
hallucination metric over a sliding window). High instability => the model is
still drifting => raise the shared kvlock_scale so BachVid holds the blessed
identity K/V harder; as x0 settles the multiplier relaxes toward base strength.

No DDIM inversion (LTXAV inverts poorly) — locks against the blessed K/V bank.
No-op unless a BachVid bank installed the shared scale list, so the two compose
cleanly. Wired into all three FunPack sampler loops after each model() call;
fully guarded, cannot break sampling. Tests cover capture/inject/scale + the
schedule's stable-vs-jumpy behavior and the no-bank no-op path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 17:14:06 +03:00
digital-garbageandClaude Opus 4.8 83822f3909 BachVid Phase 1: per-key raw K/V identity bank (capture + inject)
Training-free character carry across scenes/sessions (arxiv 2510.21696),
built on the existing LTXAV attn-hook infra. Capture raw K/V at IDENTITY_BLOCKS
during the mid-sigma window into a per-refinement-key temp bank; promote to a
blessed bank on liked ratings (EMA-merge, alongside bless_attn_weights). On
later runs with a blessed bank, lerp the running gen's K/V toward the blessed
identity at matched token positions (packed-/RoPE-safe analogue of the paper's
concat-attend). Wholesale inject at identity blocks (no spatial mask yet).

Exposes a shared kvlock_scale multiplier in transformer_options for the Phase 2
KV-Lock variance scheduler to drive injection strength per step (1.0 = off now).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 17:09:33 +03:00
digital-garbageandClaude Opus 4.8 9fb0ee267b path-planner Phase 2d: conditioning-variant routing
Route the conditioning variant from per-config outcome history, via the one
charter-safe surface: the INTERNAL value-function ascend+search (not user knobs,
so no WYSIWYG / hidden-settings violation). Sampler-owned visual ops stay on the
sampler (boundary law kept).

Directly serves the original complaint (Perfect not locking, Awful reproducing):
- _v2_path_conditioning_plan: matching-arm Beta posteriors -> liked-rate mean ->
  "lock" (>=0.6: hold the liked conditioning, skip the stochastic search),
  "explore" (<=0.4: widen the search to flee a disliked region), else "normal"
- value_function.search gains eps_scale to widen/tighten the search radius
- threaded conditioning_plan through ascend -> apply_scene_refinement_keys ->
  finalize; computed once per run from path_outcomes + prompt concepts; logged

Only active once the per-key VF is ready, since it rides the existing ascend path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 16:26:16 +03:00
digital-garbageandClaude Opus 4.8 b6f17f2b3b path-planner Phase 2c: Thompson/UCB explore-exploit seed routing
Upgrade the seed explore/exploit gate from a flat reuse chance to a proper
bandit over the path-outcome arms:
- _v2_path_arm_beta: Beta posterior over an arm's liked-rate (liked>=0.3 success,
  <0.0 failure, neutral ignored; Laplace prior)
- _v2_path_explore_decision: Thompson-sample theta per concept-matching arm vs a
  fresh Beta(1,1) explore arm; good arms exploit, dislike-dominated arms route to
  a fresh seed ("do something else")
- wired into _v2_choose_successful_seed (falls back to the flat gate with no data)
- shared _v2_path_matching_arms helper; _V2_KEY_REWARD + liked/disliked consts

5 tests incl. a statistical one: a strongly-liked arm rarely explores, a hated
arm almost always does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:48:06 +03:00
digital-garbageandClaude Opus 4.8 2bd9318bb3 path-planner Phase 2: seed-lever avoidance routing
Make path_outcomes ACT via the one Studio->sampler lever wired today: the seed
(FunPackStudio.run -> _v2_choose_successful_seed -> seed output -> sampler). No
new data paths invented.

The existing seed chooser only exploited liked seeds with zero avoidance — the
headline gap. Now:
- recorder tags each seed sample avoid = reward<0 and not skip_value_function
  (Wrong-* excluded: the seed was fine, the words/identity were off)
- _v2_path_disliked_seeds: seeds that gave a low-quality result for matching concepts
- _v2_choose_successful_seed redraws a disliked fallback, drops liked-then-later-
  disliked seeds from reuse, and reports how many it avoided

Broader config levers (guidance/lora) need new wiring and are deferred pending
graph-topology confirmation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:14:40 +03:00
digital-garbageandClaude Opus 4.8 5731315b4b refiner: '-Just forget it-' truly leaves no trace
Audited every learning sink in the V2 rating flow. Rating-learning and the
Absolute store already honored skip_learning, but two feedback sinks ran gated
only by feedback_prompt — so a 'Just ignore it' run with typed feedback still
wrote advisor_feedback_history + intent_expansion_memory.

Add _v2_rating_is_discard (forget rating or auto-discarded refusal) and gate
both feedback sinks on it, without touching the active continue/fresh_prompt
modes that legitimately keep feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:10:12 +03:00
digital-garbageandClaude Opus 4.8 99afd9cef7 path-planner Phase 1: per-scene path-outcome memory
Reframe ratings from local steering into global routing: every rating —
not just successful ones — is recorded as a labeled outcome on the per-scene
config "arm" that produced it, so a later planner can route away from arms
that keep generating disliked results.

- _v2_path_signature: arm = family + concepts + guidance config, seed EXCLUDED
  (seed is the random draw within an arm); md5 arm_id collapses identical configs
- _v2_update_path_outcomes: learns from EVERY rating, decaying counts so recent
  behavior dominates; records seed-level samples for later seed routing
- hooked at the shared per-scene learning hub + single-run path; stored in
  per-key state under "path_outcomes"

Pure instrumentation — no generation behavior change yet (Phase 2 = Studio planner).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 05:59:49 +03:00
digital-garbageandClaude Opus 4.8 89945bdb5e tools: showcase generator — auto gallery + walkthrough video from the tour
Drives the editor's built-in tour (?mode=tour, fully mocked — no backend/models/
GPU) with Playwright: serves the static frontend, walks window.TourGuide, and
screenshots each step (annotated with its spotlight + caption) while recording a
walkthrough video. Emits NN-*.png stills, an index.md gallery, and
walkthrough.webm/.mp4. Self-maintaining: new tour steps appear automatically.

No more screenshotting each button by hand. node_modules/ and out/ are gitignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:39:04 +03:00
digital-garbageandClaude Opus 4.8 e4e2f90484 tests: _FakeRoutes mock covers put/patch so the full suite can collect
The fake routes object installed into sys.modules as `server` only defined
get/post/delete; server.py also registers put (models) and patch (media), so
importing the real server.py during collection (e.g. test_server.py) raised
AttributeError and aborted the whole run. Add put/patch to the mock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:28:25 +03:00
digital-garbageandClaude Opus 4.8 fa3a884528 Merge dev into main — Release 3.1.0
Brings the OpenCut Cutting Room rebuild, Anchor-as-guide, Mixed self-image
guide, autocomplete, and the Scene Builder / character / NormalizingSampler
removals onto main. Tree matches dev exactly (discards the obsolete
cutting_edge README doc link).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v3.1.0
2026-06-21 04:17:13 +03:00
digital-garbageandClaude Opus 4.8 cdc3085ac1 Release 3.1.0
OpenCut-style Cutting Room rebuild, Anchor-as-guide source, Mixed self-image
guide, prompt autocomplete, Composer floating window + file management,
canonical scene splitter, Scene Builder / character / NormalizingSampler
removals. See CHANGELOG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:16:24 +03:00
digital-garbageandClaude Opus 4.8 573e5f26eb README: rename 'Dev and Cutting Edge branches' → 'Dev and other non-main branches'
The cutting_edge branch is gone; generalize the section to cover short-lived
non-main branches without the 'cutting edge' branding, same warning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:14:37 +03:00
digital-garbageandClaude Opus 4.8 0624341995 opencut_ui: show the anchor-image picker for Mixed scenes in the inspector
Mixed scenes never rendered an inspector image picker — renderSourceField only
called renderImageSource for eff === "image", even though renderImageSource has a
dedicated mixed branch ("Anchor image (mixed i2v)"). So a Mixed anchor could only
be set by dragging onto the timeline clip, unlike image/anchor_guide. Render the
picker for eff === "mixed" too, giving Mixed the same on-timeline + inspector image
visibility as the other anchor modes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:08:00 +03:00
digital-garbageandClaude Opus 4.8 3b1eeee130 opencut_ui: fix autocomplete Tab (trailing space) + orphaned menu in the corner
Two bugs from the Tab update:

1. Tab didn't finish the trigger at end-of-text. The skip-space regex included `$`,
   so when the caret was at the end of the field (the common case) NO trailing space
   was added — the menu then re-opened on the just-accepted trigger (it matches
   itself) and appeared stuck. Drop `$`: always add the space unless a space/delimiter
   already follows.

2. Orphaned menu stranded in the top-left with no way to dismiss it. The menu lives in
   document.body; when a panel re-render removes its host textarea, removal doesn't fire
   blur, so the menu was never cleaned up and a later scroll repositioned it against a
   detached element (rect 0,0 → corner). Now: open() sweeps stray .ac-menu (single menu
   app-wide) and bails if the textarea is detached; refresh()/position() close on a
   detached textarea; and a MutationObserver closes + disconnects when the textarea
   leaves the DOM, so it hides on its own.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:08:00 +03:00
digital-garbageandClaude Opus 4.8 c6e5e5f2a5 opencut_ui: Anchor-as-guide keeps the image as a real anchor (timeline + pipeline)
The first cut withheld the anchor_guide image (set_primary=False) to get an
empty latent "for free". That broke real (custom) pipelines: the image vanished
from the timeline and never reached the graph, so a downstream Image Transform
node — which derives width/height from the input image even when i2v is
bypassed — had no input and generation failed.

Corrected model: the image flows into the pipeline like any anchor (copied as
primary -> source_image / image_processing), shows on the timeline, and is
required. The EMPTY LATENT comes from bypassing the i2v node (the value-only
node_overrides), not from withholding the image. On the built-in pipeline there
is no bypassable i2v node, so it runs as a normal i2v anchor + frame-0 guide;
the inspector warns when no i2v node is declared.

- timeline: anchorMediaRef / SRC_ICON / appendSrcBadge show the anchor_guide
  thumbnail + a "guide" badge.
- store: _anchorAvailable / _continuesChainRun treat it as an anchor; drop keeps
  guide mode instead of forcing plain image.
- backend: _prepare_media / scene_anchor_media_refs / _anchor_media_ref /
  source_needs_anchor_media include anchor_guide; removed the now-dead
  anchor_guide_media_refs guide-only copy path (image is a normal anchor now).
- inspector: relabel to "Anchor image (guide mode)", warn when no i2v node set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 04:00:19 +03:00
digital-garbageandClaude Opus 4.8 2f55814b6c opencut_ui: node search/filter available in every Add-Model mode
The "Filter nodes…" box only showed for "Any node" mode; role-based modes
filled the dropdown directly with no way to search. Now the filter is shown
for any selected model type and applies to role candidates and Any-node alike
(applyFilter drives both paths).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:47:42 +03:00
digital-garbageandClaude Opus 4.8 4f153cee99 opencut_ui: Mixed mode also attaches the scene's own image as a frame-0 guide
Mixed scenes carried prior-scene guides but the FIRST scene (nothing prior to
carry) got no guide attention at all — only the i2v anchor. Generalize the
anchor_guide self-image guide path to also cover mixed: each mixed scene's own
image is added as a reinforcing frame-0 guide (strength 0.35), merged on top of
the carried prior guides. Renamed build_anchor_guide_guides ->
build_self_image_guides. Mixed images are already copied as anchors, so guide
refs resolve via funpack_scene_media_refs with no extra media prep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:46:14 +03:00
digital-garbageandClaude Opus 4.8 1a2b4636eb opencut_ui: add "Anchor as guide" scene source (image steers, empty latent)
New scene Source mode: the image is applied as a frame-0 guide via the
existing guide-attention path while the latent fed to the sampler stays empty
(text-to-video). The image is never the i2v anchor, so frame 0 is not locked.

Backend (timeline.py / server.py):
- SceneSource.guide_strength (0..1, per-scene; default 0.35).
- build_anchor_guide_guides() emits a frame-0 self-image guide per anchor_guide
  scene; merge_scene_guide_payloads() overlays it onto continuity/manual stacks.
- anchor_guide images are copied guide-only (set_primary=False) so source_image
  is never wired -> empty latent comes free in the built-in pipeline.
- _apply_node_overrides(): force widget inputs on built-graph nodes from a
  client-supplied node_overrides list (the optional custom-i2v bypass).

Frontend:
- inspector: Source option + guide image picker + strength slider.
- editor_settings: per-browser "Anchor as guide" section — declare a custom
  i2v node and the input/state to force on those runs (value-only). Built-in
  pipeline needs none of this.
- store: sends node_overrides only for runs containing an anchor_guide scene;
  pipeline_caps: anchor_guide is chain-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:43:29 +03:00
digital-garbageandClaude Opus 4.8 ac0e4edd37 opencut_ui: roll-up Composer minimize in place + autocomplete finishes on Tab
Minimize now collapses a FloatingWindow to its title bar where it sits (a
window-shade), instead of sending a pill to a bottom-left dock. The button
toggles +/—; double-click the bar also expands. Removed the dock/pill code
and CSS.

Autocomplete accept (Tab/Enter/click) now appends a trailing space so the
trigger is "finished" and the menu doesn't re-open on the just-completed
trigger (it matched itself); skips the space when a space/delimiter follows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:43:12 +03:00
digital-garbageandClaude Opus 4.8 886a2bb3a9 opencut_ui: fix styled text-overlay crash on newer Pillow (float bbox -> int)
multiline_textbbox() returns float coords on Pillow >= 10, so tw/th flowed into
Image.new() as floats -> "TypeError: 'float' object cannot be interpreted as an
integer" the first time a styled text overlay was exported. Round the bbox coords
to int before using them for sizes and offsets. Surfaced by running the (previously
PIL-skipped) test_render_text_overlay_styled in the ComfyUI venv; all overlay tests
now pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 17:04:50 +03:00
digital-garbageandClaude Opus 4.8 9d69f4bef8 opencut_ui: split_scenes cuts on expansion-keyed transitions (scene-drop fix)
The canonical generation/preview/key splitter (split_scenes) only treated a
shortcut as a scene cut when the shortcut's TRIGGER word was a key in the
transition DB. But split markers are commonly keyed by the EXPANSION phrase: a
"cut" shortcut expands to "Scene cut.", and the split marker is "scene cut.".
So the lookup `custom_map.get(_norm(raw))` missed, the shortcut was treated as
plain content, and NO cut was made — split_scenes returned one scene for
"A cut B cut C" while the verbatim editor split returned three. That disagreement
collapsed the timeline's middle scenes (and would have generated one scene).

Fix: when the trigger word isn't in the DB, also match the shortcut's EXPANSION
(`custom_map.get(_norm(exp))`). The generic "scene <N>" label is still NOT matched
on expansions, so an incidental "scene 2" inside a content shortcut never cuts.
Verified against the user's on-disk cut/qcut defs: split_scenes now agrees with
the verbatim split (2 scenes for cut x2 / qcut x2 / mixed). Added regression tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 17:03:28 +03:00
digital-garbageandClaude Opus 4.8 7ed0717f73 opencut_ui: fix shortcut autocomplete never suggesting in prose prompts
The query was the whole token since the last , ; \n delimiter, and a trigger only
matched if that entire token was a substring of it. Typing a normal multi-word
prompt grew the token past any short trigger, so suggestions never appeared.

Now match TRAILING WORD-RUNS of the token, longest first ("golden ho" before
"ho"), keeping substring matching so "@golden" still matches "golden". The longest
run that hits any trigger wins; its span is what acceptance replaces, so completing
a multi-word trigger can't duplicate words. Verified the matching across start-of-
field, mid-prose partials, post-comma, and no-match prose cases.

Also lifted .ac-menu z-index (9000 -> 99999) so it can never render behind the
Composer floating window when completing in the Compose tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 11:22:33 +03:00
digital-garbageandClaude Opus 4.8 247ab84e34 opencut_ui: inline clip reorder controls on the timeline
Reordering was Edit-menu-only (Move Clip Left/Right). Added ◀ ▶ buttons to each
clip's head bar (next to Remove) that call the same moveTimelineClip, disabled at
the ends. Works for both generative and video clips. (Add was already on the
timeline toolbar via + Add → Clip.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 11:12:05 +03:00
digital-garbageandClaude Opus 4.8 a534f8bb6f opencut_ui: hard-remove the character feature (no UI surface remained)
The rebuilt editor never exposed character creation/assignment, so the feature
was vestigial: dead frontend plumbing plus generation branches that only fired
for legacy/imported projects. Removed it wholesale per decision.

Backend:
- Deleted movie_editor/backend/characters.py and the /library/characters routes.
- timeline.py: dropped normalize_character_bible / build_characters_anchor /
  _load_character_map / _scene_character_prefix / character_media_refs_for_project
  and friends. effective_anchor/effective_negative_prompt now return the project's
  manual text; resolve_scene_identity_pin(cs) returns the project-level identity
  pin only. Removed Scene.character_ids and Project.character_bible fields and the
  character_ids fingerprint key.
- Continuity auto-guides keep working off continuity_settings.identity_pin_ref.

Frontend:
- store.js: removed state.characters, the character preview-key plumbing,
  load/save/deleteCharacter, sceneCharacterIds/toggleSceneCharacter, character_ids
  in patch/archive paths and exports.
- timeline.js: dropped clip-char badge rendering; api.js: dropped character routes;
  view_bus.js: dropped chars fingerprint; tour.js: dropped demo characters + copy;
  styles.css: dropped .clip-chars/.clip-char.

Tests: removed test_characters.py and the character-bible timeline tests; added
manual-anchor / manual-negative / project-identity-pin tests. 133 passed, 4 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 11:08:48 +03:00
digital-garbageandClaude Opus 4.8 41e0cc48d5 opencut_ui: drop dead .char-* / .plan-* CSS
Both class families had zero JS references in the rebuilt UI. Removed all of
.char-* (character inspector/chips — no surface in the new editor) and the
.plan-* plan-strip styling (plan UI is gone). Kept the two still-live classes
that share neither prefix's full set: .clip-chars/.clip-char (clip badges) and
.clip.off-plan (removed_from_plan marker).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 10:44:48 +03:00
digital-garbageandClaude Opus 4.8 a9001123bd opencut_ui: Composer file management — Replace/Merge import, Delete-all, Files tab
Composer pt.2 backlog (#6/#7/#8):
- Import now asks Merge (overlay, name-overwrite) vs Replace all (wipe first),
  for both shortcuts and split markers. Backend import_* gain a replace flag;
  routes read ?mode=replace.
- Delete-all buttons clear every shortcut+category / every split marker, via new
  clear_shortcuts / clear_transitions bridge fns + /clear routes.
- New Composer "Files" tab: on-disk file manager over the prompt-library JSONs and
  the refinement-key store (keys + sidecars + value/latent .pt) with size, per-file
  delete, and per-group delete-all. Path-traversal-safe (basename-only, root-checked).
  Deleting library JSONs reloads Shortcuts/Splits so no stale entries linger.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 10:41:32 +03:00
digital-garbageandClaude Opus 4.8 0dbeadec74 opencut_ui: richer text overlay styling (bold/italic/align/outline/shadow/box)
Centralized text styling in OverlayUI (applyTextCss + textStyleControls) shared by
the program monitor, the Add-text modal, and the inspector. Pillow export renderer
honors every prop (real bold/italic faces or faux stroke/shear).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 01:00:38 +03:00
digital-garbageandClaude Opus 4.8 df3ac62e46 opencut_ui: shortcut autocomplete, Add-shortcut picker, anchor toggle + Editor settings
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 22:57:06 +03:00