Commit Graph
1179 Commits
Author SHA1 Message Date
digital-garbageandClaude Fable 5 9b202fec2d Preview stability round 4: never serve mid-write video, signature-keyed remux/segment caches
Three holes left after the /result faststart remux (e296fe0), matching the
still-live symptoms (decode errors in console, blinking clips, playback
restarting from 0, stale previews):

1. Mid-write serving: /result hit while VHS is still writing the mp4 found no
   moov, failed the remux, and served the raw moov-less bytes anyway —
   undecodable, and the player's cache-busted retries kept blinking against it.
2. Byte-switching under one URL: once the write finished, the same URL switched
   from raw to remuxed bytes with different offsets/length. A browser mid-Range
   stream mixes offsets (PIPELINE_ERROR_DECODE) or fails If-Range revalidation
   and falls back to a full 200 — resetting the element and restarting the clip
   from the beginning.
3. Cache collisions/staleness: the remux cache was keyed by path only (a
   rewritten source kept serving the old remux) and the remux output was named
   by basename only (two sources sharing a basename overwrote each other's
   remuxed copy with -y, possibly while it was being streamed).

Fix: _is_faststart becomes _moov_position (front/end/none) so "remux me"
(moov at end = complete VHS file) is distinguished from "come back later"
(no moov = still being written or aborted save). No-moov sources now get 503 +
Retry-After from both /result and preview-segment instead of undecodable bytes;
the player retry loop already handles that. The remux cache is keyed on the
source's (mtime_ns, size) signature, the output name is derived from
path+signature (no basename collisions), the remuxed file is validated
(nonzero, moov at front) before caching, and a stable-but-unremuxable file is
remembered so ffmpeg isn't re-run per request. The preview-segment cache key
also gains the source signature so a re-rendered source at the same path can't
serve segments encoded from the old bytes.

Verified against real ffmpeg files: end/front/none discrimination on complete,
faststart, truncated-mid-write, and empty mp4s; remux of a truncated file
raises instead of caching garbage. All 160 movie_editor tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:19:01 +03:00
digital-garbageandClaude Sonnet 5 4f121ff2f5 Fix undecodable scene previews: faststart-remux raw ComfyUI renders in /result
ComfyUI's VHS_VideoCombine node writes moov at the end of the mp4 (no
+faststart). Deep-seeking that file is exactly what preview-segment already
routes multi-scene chain clips around, but a "shallow" single clip (inSec
<=0.5) or just opening a scene's own preview hits /result directly, gets
served the raw moov-at-end file, and fails to decode the moment the user
scrubs anywhere into it — reproduced live as repeated
NS_ERROR_DOM_MEDIA_DECODE_ERR/AppleVTDecoder failures on the same file even
after cache-busted retries, since the bytes on disk are permanently bad for
seeking, not transiently mid-write.

Fix at the source instead of extending the segment path: /result now
stream-copy remuxes (+faststart, no re-encode) any .mp4 it serves that
doesn't already have moov up front, caching the remuxed copy so it only
happens once per file. FunPack's own segment/export/final-render output
already sets +faststart, so this is a no-op for everything except the raw
VHS output — verified the atom-order detection and remux against real
ffmpeg-produced faststart/non-faststart files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 06:24:16 +03:00
digital-garbage 449532358f Fix stale FakeModel/FakeVAE mocks and expectations in test_scene_chain_sampler.py
model=object() lacked model_options (needed by the per-scene wrapper
snapshot/restore added for the scene-wrapper-leak fix), and FakeVAE lacked
decode() (needed since the sampler always decodes IMAGES when the caller
doesn't declare that output as disconnected) — together these silently
killed every test in the file that calls sample(). Added FakeModel and
FakeVAE.decode, then fixed the resulting return-tuple arity (sample() now
returns 6 values, these tests unpacked 4) and three genuinely stale
assertions: the per-scene report line gained a timing segment, carry_i2v_guides
now prepends its guide frame instead of appending it, and _append_i2v_guides
no longer routes through keyframe_idxs/guide_attention_entries.

Verified in isolation: all 14 tests in this file now pass. Running the full
tests/ suite still shows the same file failing — that's a separate,
pre-existing cross-file sys.modules stubbing collision (confirmed via
git stash on a clean dev checkout) affecting this file plus
test_mask_safe_attention.py and test_v2_category_alignment.py; not fixed
here since it isn't a one-line root cause.
2026-07-08 06:09:41 +03:00
digital-garbage 0c46f1f02e Carry frame overlap through anchor changes; fix identity_pin on anchor scenes
carry_overlap_through_anchor lets a scene keep the previous scene's latent
tail for background/environment continuity even when it switches to its own
i2v anchor, instead of the current hard cut. The anchor's own leading frame
stays untouched; only the frames after it carry the old tail.

Also fixes a real gap found along the way: the mixed-anchor branch never
called _apply_configured_guides, so Best-FaceID identity_transfer could
never resolve an identity_pin on an anchor-swap scene — exactly where it's
most useful. Added a standalone lookup so it resolves there too.
2026-07-08 05:57:39 +03:00
digital-garbageandClaude Sonnet 5 864d222ee5 Preview stability: route timeline filmstrip/waveform through segments
Today's preview-stability fix (4bc56e8) made the player pool fetch every
chain-shared clip through a trimmed, faststart preview segment instead of
deep-seeking the raw chain output (moov-at-end, stalls/blacks in the
browser). Two other consumers of the raw file were missed:

- appendFilmstrip built a <video preload=auto> pointed at the full shared
  chain output and seeked into it per thumbnail — the same deep-seek
  hazard the player fix eliminated, just in a different element.
- sceneAudioClip's waveform did a plain fetch()+arrayBuffer() of the
  ENTIRE shared file, once per scene sharing it, to decode a waveform.

Repro (fake 3-scene chain sharing one moov-at-end mp4): opening the
project fired 15 aborted requests fighting the player pool over Chrome's
6-per-origin cap — exactly the stale/black/reset-to-start symptoms
reported live. Both now request the same cached preview-segment URL the
player uses; post-fix, scrubbing 11/11 test positions succeeded with no
black frames or resets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 22:50:09 +03:00
digital-garbageandClaude Sonnet 5 a4388d617f Best-FaceID identity transfer: full native overlap+ArcFace port
Replaces the source-phase-RoPE-only approximation with a faithful native port of
ComfyUI-BFSNodes' LTX Identity Transfer (read upstream source directly): the reference
face is now injected as separate, non-rendered overlap tokens (clean timestep, RoPE
source-phase tag, trimmed before output) instead of blending into Continuity's identity-pin
keyframe, plus an optional ArcFace embedding -> IdentityProjector -> context-token channel
(identity_projector, source_id, phase_scale, id_strength, arcface_mode, debug_log). Stays
on Chain Sampler per the Studio/Sampler boundary law's guide-ops carve-out. New
identity_transfer.py module; insightface added as a soft pip dependency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 21:52:12 +03:00
digital-garbageandClaude Sonnet 5 05b628629b Release 3.2.1: CHANGELOG + version bump
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:59:44 +03:00
digital-garbageandClaude Sonnet 5 93dcf8dfc2 Setup-node picker collapses on select; autocomplete shows all matching shortcuts
Models & Pipeline -> +New node: picking a node now collapses the search results
into a one-line "selected" row with a Change button, instead of leaving the full
list open above the Values/Wiring panel.

Prompt autocomplete: dropped the MAX_ITEMS=8 cap on matchTriggers — a prefix
shared by many shortcuts (e.g. 15 "lazy" triggers) now shows all of them; the
menu already scrolls (max-height in CSS), so nothing was gained by truncating.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:55:04 +03:00
digital-garbageandClaude Sonnet 5 622e8ef747 Best-FaceID identity transfer: native source-phase RoPE port, no external node
Reverts the earlier ComfyUI-BFSNodes graph-splice approach (builder.py/timeline.py/
server.py scaffolding) in favor of porting the missing piece natively: FunPack's own
guide-attention path (LTXVAddGuide.add_keyframe_index + guide_attention_entries)
already places a reference image on the frame-0 grid exactly like BFSNodes' overlap
technique, and Continuity's existing "Identity pin" already applies one reference
face to every scene. The only real gap was the LoRA's trained-time source_phase RoPE
tag, added as a tag+strip patch on FunPackLTXAVSceneChainSampler scoped to the exact
guide-entry token range (never co-active mid_scene/carry/prior-scene guides sharing
the same keyframe_idxs tensor). New "Best-FaceID compatibility" + "Source-phase id"
knobs live under Engine Settings -> Experimental, alongside every other unvalidated
technique. LoRA loading itself reuses the existing generic Models-menu lora role.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:46:56 +03:00
digital-garbageandClaude Sonnet 5 79b0b5bb14 Best-FaceID identity transfer: one-checkbox LoRA + LTX Identity Transfer splice
Detects ComfyUI-BFSNodes' LTX Identity Transfer node in object_info and, when
enabled, auto-loads the Best-FaceID LoRA onto the model path and splices the
identity node between Studio and the Chain Sampler (project-wide, one
reference face per project — a run's graph can't vary it mid-chain). New
Engine settings pane under Chain Sampler picks the reference face (media
picker) and LoRA file (live dropdown), with advanced ArcFace/phase knobs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 20:08:26 +03:00
digital-garbageandClaude Fable 5 0df109480c Preview stability: async segment encodes, ghost segments, cache-keyed trims, preload budget
Four compounding defects behind stale/black previews on live instances:

1. /preview-segment ran its ffmpeg x264 re-encode synchronously inside the async
   handler — each encode froze ComfyUI's single aiohttp event loop for seconds,
   stalling every streaming /result response mid-transfer (elements error out,
   retries cache-bust and refetch, transport wedges). Worst right after a run,
   when the pool requests every clip's segment at once. Now asyncio.to_thread
   with a per-key lock (scrub storms don't spawn duplicate encodes) and
   -preset veryfast for preview trims. /export-clip wrapped the same way.

2. Ghosts were excluded from the segment system, so scrubbing a removed scene
   deep-seeked the raw moov-at-end chain MP4 — the exact stall segments were
   built to avoid. Ghosts (and lone clips with a deep in-point, e.g. the last
   surviving scene of a chain) now get segments; the trim window travels in the
   query (render_in + dur) since the scene no longer exists server-side.

3. Real staleness: segment URLs lacked the clip duration while responses are
   browser-cached for an hour — trimming a clip kept the same URL and replayed
   the hour-old segment. dur is now part of every segment URL.

4. Every pooled <video> used preload=auto: the whole timeline downloaded
   simultaneously, starving the browser's per-origin connections on tunneled
   instances and aborting the stream actually being watched. Pool now idles at
   preload=metadata; the current clip and its neighbours are promoted to auto.

Verified live on local ComfyUI with a synthetic 2-scene chain + ghost: ghost
segment 200/video-mp4, API stays responsive during an encode (17ms ping),
scrub across boundaries, seek into ghost (segment-relative time + badge),
play across the scene→ghost boundary, rapid pause/resume — all clean.
160 editor tests pass (new: ghost clip-spec parsing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:34:13 +03:00
digital-garbageandClaude Fable 5 b8422d647b Guide blur: own strength + sigma-threshold controls, independent of ALG's
alg_blur_guides no longer borrows the sampler's anchor knobs: the Scene Chain
Sampler gains alg_guide_blur_strength (default 2.0) and
alg_guide_blur_sigma_threshold (default 0.975), threaded per-chunk via
extra_options like the tail count. The sampler now precomputes one latent per
(anchor blurred?, tail blurred?) combination — the frame sets are disjoint, so
the both-blurred variant composes the two passes — and each region follows its
own sigma window per step. Anchor ALG (alg_enabled/alg_strength/
alg_sigma_threshold) is untouched and back to anchor-only semantics.

Editor: 'Guide blur strength' and 'Guide blur sigma threshold' rows appear
under Engine ▸ Experimental when the blur toggle is on (sampler_inputs bridge,
names match the new node widgets; inputs appended at the block end to keep the
builder's positional widget mapping aligned).

Verified live: object_info shows both widgets appended last; Editor rows
appear on toggle and persist to project.sampler_inputs. 11 ALG unit tests
pass (new: composed independent-strength blur); same 20 pre-existing suite
failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:17:23 +03:00
digital-garbageandClaude Fable 5 6528acf504 Shortcut revolver: no-repeat cycling through multi-replacement shortcuts
Editor ▸ Settings ▸ Editor gains a 'Shortcut revolver' toggle (+ 'Random' sub-option).
When on, each shortcut with several replacements draws from a per-shortcut queue —
sequential (first…last, then restart) or shuffled once per cycle — and nothing repeats
until the whole set has fired. Off (default) keeps the seeded random pick unchanged.

- State is a sidecar (shortcut_revolver.json) next to the shortcut DB: settings +
  per-shortcut remaining queue + replacement-set fingerprint (editing a shortcut's
  replacements restarts its cycle; changing either setting resets all cycles).
- Generation commits the draw (refine_v2 positive prompt); every preview path
  (parse_timeline, split mapping, fragments) peeks the same stored queue without
  saving, so previews show exactly what the next Generate will use.
- Batch Training variants each commit a draw: N variants sweep N consecutive
  chambers instead of seed-rolled repeats.
- FunPackStudio.IS_CHANGED returns nan while the revolver is on so fixed-seed
  re-runs aren't served from ComfyUI's node cache (the next chamber must fire).

Verified live on local ComfyUI: settings round-trip, seed-independent stable peeks,
sequential cycle REP_ONE→TWO→THREE→ONE across processes, peek-after-commit shows the
next chamber. 9 new unit tests; full suite has the same 20 pre-existing failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 05:26:21 +03:00
digital-garbageandClaude Fable 5 1112c2dccc ALG blur guides: standalone — guide/JoyAI tail blur no longer requires alg_enabled
alg_blur_guides now works with the sampler's alg_enabled off: the anchor
stays sharp and only the newly-appended guide-attention frames (mid-scene
guide, carried guides, per-scene guides, JoyAI memory) are blurred, reusing
the same alg_strength / alg_sigma_threshold parameters and sigma window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 05:11:33 +03:00
digital-garbageandClaude Fable 5 8eb6f3704a Ideas popover: habit-aware co-pilot for multi-shot prompt construction
The bulb's popover now mixes what the editor knows about YOUR prompting habits
with library browsing, in four sections:
- You've paired these before — shortcuts you historically put in the same shot
  as what's in the prompt, ranked by co-occurrence (reason chip: with «X» ×N)
- Your usual next shot — what usually follows the previous scene's shortcuts
  in timeline order (scene inspector only; reason chip: after «X» ×N)
- Continue the thread — more from categories the prompt already uses
- New directions — the existing missing-category browser (counts, +N more)

Habits are mined on demand by GET /api/library/suggestion_stats: a text scan of
all saved projects' scene texts (triggers stay verbatim in scene texts, so no
new persistent state — project files remain the only source of truth). Pairs =
same scene, follows = consecutive scenes in timeline order; editorial cut
subclips and excluded scenes are skipped. One-off co-occurrences (<2) ignored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 04:04:04 +03:00
digital-garbageandClaude Fable 5 e163f4d006 Media gallery: one-click continuity pin on image cards
📌 hover action on image cards sets/unsets continuity_settings.identity_pin_ref
(same setting as Engine settings ▸ Continuity ▸ Identity pin, just quicker).
The pinned card shows a persistent 📌 badge on its thumbnail. Pinning while
Auto continuity is explicitly off re-enables it — a pin that does nothing
would be a hidden setting. Image cards only, chain-sampler pipelines only.

fpMediabrowser now fingerprints identity_pin_ref so pin changes repaint the
gallery zone (they were dropped by the ViewBus fingerprint before).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 03:52:40 +03:00
digital-garbageandClaude Fable 5 5c48fed1ab Editor: shortcut Ideas bulb — quiet nudge for unused shortcut categories
A 💡 next to the scene prompt and Composer global prompt lights up when whole
shortcut categories (camera moves, sub-actions, …) exist in the library but are
absent from the prompt. Click-only (never auto-opens): a category-grouped
popover lists every missing category with count, random insertable samples,
+N-more browse per category, and a shuffle. Chips insert at the caret through
the field's normal input path. Toggleable via Settings > Editor > Shortcut ideas.

Guards: no popover off a hidden/detached bulb (stranded top-left window),
dismiss listeners detach on every close path, popover closes when a re-render
removes the bulb.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 03:45:48 +03:00
digital-garbageandClaude Fable 5 0c2685ce6f Release 3.2.0: CHANGELOG + version bump
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 21:06:33 +03:00
digital-garbageandClaude Fable 5 541c58e514 player: survive media resets — rapid play/pause no longer restarts from 0
Fast pause/play over the network can abort the stream fetch and reset the
<video> element (the retry path's cache-busted load() does the same): the
element rewinds to 0, the tick stomped the playhead to the clip start, and the
next play restarted from the beginning. Now 'emptied' stashes the playhead as a
pending seek, loadedmetadata restores it (and resumes when playing), and the
tick treats readyState<1 as seeking so a resetting element can't feed a bogus
currentTime. Reproduced+verified via forced mid-playback load(): position holds
while playing, mid-burst, and while paused.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 21:05:24 +03:00
digital-garbageandClaude Fable 5 d898fc9384 Engine settings: category sidebar + macOS row fields (no more card list)
Engine becomes a flush section with its own always-visible category sidebar —
Overview · FunPack Studio (Refinement / Adjustments / Sampler algorithm) ·
Chain Sampler (Continuity / Timing & Seed / Guidance / Decode / Experimental) —
replacing the collapsible-card scroll list. Fields render as macOS-style rows
(title left, control right, grouped boxes with section labels); categories show
live changed-count badges. All persistence logic (studio refiner, adjustments,
continuity/guide settings, sampler knobs with dependsOn/forced-carry, seed,
presets, pipeline-node pickers, focus-protect while typing) is unchanged.
navItem moves onto SettingsWindow as the shared inner-sidebar builder (Models
uses it too). Collapsed sidebar search loses native styling (white box).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 20:52:51 +03:00
digital-garbageandClaude Fable 5 33693cc4f5 Settings: icon-rail sidebar + one-column fields — fits tiny screens
The main Settings sidebar collapses to a 52px icon rail and expands to full
width on hover/focus, overlaying the content (no reflow). Node values render in
one column instead of two, Engine's Conditioning/Sampler pickers stack, and the
wiring/fields shrink chain got min-width:0 everywhere a long select option used
to force horizontal overflow — the node page header actions (bypass/remove) now
sit in a wrapping cluster and stay reachable at any width (live-instance bug:
remove button was unreachable, no horizontal scroll).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 20:45:01 +03:00
digital-garbageandClaude Fable 5 fad55f02a6 Editor: split-at-playhead actually cuts the clip in place
splitScene set frames on both halves but left frames_mode='project', which
sceneEffFrames (and backend eff_frames) IGNORE - so the first half kept full
project length (looked untouched) and the second half rendered as a whole
extra scene. Both halves now flip to 'timeline' mode, same as resizeScene.

Also: with a pinned custom cut order, _ensureTimelineOrder appended the new
half's id at the END of timeline_order - the 'split adds a scene to the end'
symptom. The second half is now spliced into the cut order right after its
first half.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 20:38:41 +03:00
digital-garbageandClaude Fable 5 e2e88bc455 menubar: Settings window is the ONLY door — kill duplicated menu entries
The FunPack menu and the Settings dropdown are gone; the top bar is
File / Edit / View / Help / Settings, where Settings is a plain button that
opens the unified window (keeps .menu-btn[data-menu=Settings] for the tour).
Everything they held already lives in Settings sections, plus the two
stragglers migrated: Reset Studio session (Refinement & Taste, keeps the
armed/cancel two-step + confirm and Store-driven label) and Import ComfyUI
Workflow (Models & Pipeline header action next to Refresh). Tour/FAQ texts
updated to the new paths. FunPackMaintenance/FunPackGit stay the single
implementations behind the Settings rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:39:36 +03:00
digital-garbageandClaude Fable 5 5432fd8e4f Settings: About FunPack section + Models & Pipeline sidebar redesign
About FunPack ("About This Mac" style): mark, version (new: pyproject version
in /api/git/status), commit, branch, © DigitalGarbage, Software Update deep link.

Models & Pipeline becomes a nested macOS-style pane: inner sidebar with Linked
inputs (count badge) and Pipeline at top, then '+ New node' and every configured
node with a status dot and hover pencil rename. Clicking a node opens its full
page (values, multi-wiring, sources, expose/bypass). '+ New node' opens a role
dropdown, then a Setup-node modal with search where widget values AND output
wires / input sources are set before the node is added — landing on its page.
Link mode is now a persistent bar: pick inputs across node pages, save from the
bar, instead of the old everything-expanded scrollable list. The requirements
'+ Add' buttons open the same setup flow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:32:50 +03:00
digital-garbageandClaude Fable 5 86f58ca074 Editor: unified macOS-style Settings window (sidebar, icons, grouped sections)
One Settings window replaces the four scattered modals: searchable sidebar with
colored icon chips grouped as Editor / Generation (Engine, Models & Pipeline) /
Learning (Refinement & Taste) / System (Updates & ComfyUI, Temp Files). Sections
self-register on a small shell (settings_window.js); the legacy modal facades
(EditorSettingsModal/EngineSettingsModal/ModelsModal/TempBrowserModal) deep-link
into their section so every existing entry point still works. Refinement-key and
restart/update actions get proper panels reusing the single menubar
implementations (window.FunPackMaintenance). Cmd+, opens it; tour's overlay
sweep now closes the window through its cleanup hooks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 15:16:09 +03:00
digital-garbageandClaude Fable 5 9195906c03 DynaShift intake: bank the quality-missing rating family, not just intrusions
'Awful' / 'Wrong appearance' banked on intrusion semantics; now any rating
whose profile reward marks the run a bad OUTCOME (<= -0.25: Missing
quality, Missing details+quality, Missing action+quality, Missing
details+action+quality) also feeds the negative bank - degradation is
visible in the latent, so steering off that attractor is meaningful.
Near-miss ratings with positive reward (Missing details +0.35, Missing
action +0.05) stay excluded: those latents are mostly-wanted content and
banking them would repel future runs from material the user asked for.

Decision moves into negative_memory.is_negative_profile (intrusion keys OR
reward threshold, robust to future profiles); promoted entries now carry
the rating key as provenance for severity-weighted steering later.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 06:29:46 +03:00
digital-garbageandClaude Fable 5 4a74e7476b DynaShift: negative latent memory - a negative prompt at CFG=1
Store what went WRONG, not just what was asked for. Ratings 'Awful' and
'Wrong appearance' promote that run's final video latent (fp16, batch
squeezed) + mean-pooled conditioning into a per-key negative bank
(<key>.negative_latents.pt, ring buffer of 8). The Chain Sampler writes a
pending candidate at the end of every keyed run; the rating that scores
that run promotes or discards it - same pairing as the x0 snapshot, and
the stem glob in delete_refinement_key covers both new sidecars.

Sampling (dynashift toggle + strength/threshold, off by default):
per late step each current video frame is pooled and compared against
every banked negative frame (alignment-free in time, so chain position /
guide tails don't matter; other resolutions are skipped). Frames matching
above threshold get the matched negative frame's aligned component
subtracted (projection removal, coef clamped >= 0), scaled by
above-threshold margin - steering self-releases once the unwanted feature
is gone. Negatives are weighted by prompt similarity so bad gens from
unrelated prompts steer less. No uncond pass, no extra model forward;
audio byte-identical (video span slice + cat). Wrapper rides the tagged
per-scene lifecycle (leak-strip covered) and threads through batch
training.

Editor: DynaShift knobs land in Engine Settings > Guidance same-change
(sampler_inputs pass-through, no backend change), verified rendering live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 06:03:16 +03:00
digital-garbageandClaude Fable 5 8e2ddd47a3 sampler: exception-safe wrapper lifecycle + output_guidance calibration fix
Two audit findings on the experimental feature stack:

1. Leak: an interrupt or OOM inside _sample_chunk skipped the per-feature
unwind blocks, leaking embed_guidance/score_slider/output_guidance/temporal
wrappers on the shared ModelPatcher - every later run then chained new
wrappers onto the leaked ones and double-steered (same in-process
progressive-corruption mode as the old block-hook leak).
- one base-wrapper snapshot per scene, restored in the same finally that
  removes the v2a hooks; the four conditional unwind blocks are gone
- every per-scene wrapper is tagged with its predecessor; sample() strips
  leaked tagged chains at run start (mirrors strip_funpack_block_hooks)

2. Inert feature: output_guidance applied the RAW value-fn gradient, whose
per-element magnitude is O(1/pool_window) - at video-latent sizes the
correction was ~1e-6 relative, i.e. numerically a no-op. It also fed the
full packed AV tensor to a value function trained on the video stream
alone (shifted pooling buckets + audio contamination).
- gradient now computed on the packed latent's video span only (matches
  the training domain of _save_output_value_snapshot)
- delta norm-calibrated: strength = fraction of video-stream norm per
  fully-ramped step (0.02 = 2%), same relative convention as the slider
- audio region byte-identical by construction (cat, not post-hoc mask)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 05:41:33 +03:00
digital-garbageandClaude Fable 5 79c5aa05d6 reposition project description: FunPack is a movie studio, not a node grab-bag
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 22:33:17 +03:00
digital-garbageandClaude Fable 5 6a97dedfe6 sampler: name the guide mechanism on each scene's timing line
The per-scene line now includes run_mechanisms, so a slow guide scene
attributes itself fully: which feature appended the tokens
(custom_guide_stack / carry_i2v_guides / joyai_memory / mid_scene_guide
/ soft_continuation) alongside the tail counts and sampling seconds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 19:50:55 +03:00
digital-garbageandClaude Fable 5 a4008e6a13 sampler: per-scene sampling time + appended-token context in the report
Live report: guide scenes suddenly ~3.5x slower than the anchor scene
(6-scene 768x768x305 montage at 7 min vs ~2.5 expected), on a nightly
ComfyUI. Audit of the upstream delta found nothing touching the default
guide attention path (context windows is opt-in, int8 irrelevant, the
guide-strength mask machinery dates to February) - so before theorizing
further, make each run attribute itself: every scene line now carries
its sampling seconds plus what was appended to its sequence (carried
head frames, guide tail, audio tail), printed live and in the report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 19:40:21 +03:00
digital-garbageandClaude Fable 5 b7ed4f9fb6 remove block steering + NVFP4 loader - both failed live validation
Block steering: run-averaged block activity is architecture-determined
(varies in the 4th decimal run-to-run), so no scoring scheme - pole EMAs
or reward correlation - could extract a rating signal from it. The idea
needs within-run per-step measurement to have a chance; that is a
research project, not a feature. Removed: module, sampler wiring, the
two trailing widgets (positional reference mapping unaffected), the
Refiner pairing hook, editor knobs, tests. Stale block_snapshot/
block_profile sidecars are inert and covered by the existing
delete_refinement_key sweep.

FunPackNVFP4ModelLoader: the FP4 GEMM path was real (3.35x at 16k
tokens) but end-to-end the user's runs are dominated by everything the
loader cannot touch (attention, prep, encode - the sampler node is
~23s of a ~60s run) while dynamic input quantization added a fixed
per-layer-call toll. Net observed: overhead. Users are better served
by dedicated NVFP4 loaders. Removed: module, registration, tests.

Kept (independently useful): the chain sampler's phase timing report +
quantized-layer census, the nested-AV x0 snapshot fix, and the sm_120
masked-attention work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 12:08:08 +03:00
digital-garbageandClaude Fable 5 e9aee731e7 nvfp4: static input-scale calibration (capture once, inject forever)
Justified by live data: sampler phase timing shows sampling at 89% of
the chain node, all 792 quantized layers on the fast path, and the probe
puts dynamic input quantization at ~0.30 ms/layer-call vs ~0.11 ms with
a provided scale (amax dominates) - a top-line per-call cost.

New input_scales widget on the loader:
- "auto-calibrate", first load without a sidecar: forward-pre-hooks
  record each quantized layer's running input amax (one generation is
  enough; sidecar under refinements/nvfp4_scales/<checkpoint>.json,
  per-machine and rebuilt in one run whenever absent).
- every later load: injects input_scale = amax * 1.25 / (448*6) into
  the state dict next to each quantized weight - core's loader
  registers it natively and skips the per-call amax from then on.
  Margin leaves clip headroom (NVFP4 clips above the scale).

Tests: suffix-matched injection across checkpoint prefixes, value math,
core Linear registering input_scale and forwarding, sidecar read.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:58:46 +03:00
digital-garbageandClaude Fable 5 184e897385 block-steer: correlation attribution + confidence ramp (fixes 'flat activity')
Live report: "flat activity across runs" with a working profile. Root
cause is real, not a threshold tuning issue: the fingerprint is each
block's MEAN residual activity over ~1200 steps, which is practically a
property of the architecture - run-to-run variation sits in the 4th
decimal, so raw reward-covariance is numerically dead. Lowering the
epsilon would have "fixed" it by steering on noise.

Now: per-block Pearson CORRELATION between reward and activity (scale-
free - tiny variations count when they consistently track ratings),
fingerprints stored mean-normalized so run-global scale (step counts,
schedulers) can't masquerade as block signal, and a confidence ramp
(min(1,(runs-2)/6)) so a 3-run profile steers gently instead of not at
all, reaching full strength ~8 runs. MIN_RATED_RUNS 2->3 (correlation
over 2 runs is degenerate). Status lines now carry the evidence:
"ready (N runs, peak |corr| X, confidence Y)". Older profiles are
discarded on first pairing (fingerprint semantics changed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:52:54 +03:00
digital-garbageandClaude Fable 5 fc000ef070 sampler: phase timing + quantized-path census in the chain report
"The matmuls got faster" and "the video arrives sooner" are different
claims. The run report and log now show total = sampling + VAE decode +
other with percentages, so an optimization's real ceiling is visible
(at 768x768x305 the ~22k-token attention rivals all linears combined,
and decode/prep are NVFP4-blind - a 2x on quantized GEMMs can cap out
around seconds of a one-minute run).

Also logs a quantized-layer census at run start: layers forced OFF the
quantized matmul path by weight patches (LoRA / refiner attn deltas) or
emulation dequantize on EVERY call - silently slower than bf16.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:47:11 +03:00
digital-garbageandClaude Fable 5 e3954c22c2 nvfp4: probe verdict from large-M + static-scale timing + scope advice
Live numbers from the rental (sm_120, cu130): FP4 GEMM wins 3.35x at
M=16384 (1.97x net) but loses at M=1024 to launch overhead, and input
quantization is a near-fixed ~0.2-0.3 ms per layer-call. With scope
'all blocks' (1232 layers incl. the small audio branch) that fixed toll
cancels the GEMM savings - the exact "speed didn't change" report.

The probe now: times input quantization with a precomputed static scale
too (tells whether input_scale calibration would reclaim the overhead or
it's construction/launch cost), and draws the verdict from the LARGE-M
run only, with explicit advice to prefer scope 'video blocks' when the
fast path is active but end-to-end looks flat.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:35:58 +03:00
digital-garbageandClaude Fable 5 74dd3afb61 nvfp4: decompose the GEMM probe - input quantization vs FP4 kernel
First live diagnostics run showed the whole quantized path at 0.26x of
bf16 despite a healthy cuda backend and zero emulated layers. The single
combined timing cannot say WHICH half loses: the dynamic input
quantization wrapped around every matmul (a fixed per-call cost times
1232 layers/step) or the FP4 GEMM kernel not engaging. The probe now
times them separately at M=1024 and M=16384 (realistic video token
count) and names the culprit in the verdict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:31:28 +03:00
digital-garbageandClaude Fable 5 7da139bbdb nvfp4: load-time diagnostics - prove the fast path instead of assuming it
'Loaded fine' and 'runs fast' are different claims. Every NVFP4 load now
prints a diagnostics block that settles which one you have:

- comfy_kitchen backend census (the big one: torch built for CUDA < 13
  disables ck's CUDA backend, so quantized matmuls silently fall through
  to the pure-PyTorch eager backend - correct output, ZERO speedup, with
  an explicit warning line when detected)
- torch/cuda build + device capability
- loaded-model layer census: nvfp4 layers vs layers flagged
  full-precision emulation (pre-Blackwell GPUs)
- a timed 4096x4096 GEMM probe (incl. dynamic input quantization):
  bf16 vs nvfp4 wall time with a FAST PATH ACTIVE / NO SPEEDUP verdict

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:25:49 +03:00
digital-garbageandClaude Fable 5 3d674207f5 fix: block-steer covariance attribution + AV x0 snapshot + NVFP4 metadata
Three live-run findings from the rental:

1. Block steering said "profile not ready (needs 2+ rated runs)" with 16
   paired runs. The v1 liked/disliked-EMA attribution had no contrast when
   ratings cluster on one pole (pole-vs-all-runs-mean cancels to zero) and
   dropped rewards in (0, 0.3) entirely. Attribution is now the per-block
   COVARIANCE between reward and activity over a rolling 32-run history:
   every admissible reward contributes with its actual value, contrast
   comes from reward variance, and the sampler log now states the real
   blocker ("all rated alike - rate both good AND bad runs") instead of a
   misleading run count. v1 profiles are discarded on first pairing.

2. output_guidance stayed "not ready (needs 10+)" forever on LTX-AV: the
   end-of-run x0 snapshot was gated on output samples being a plain
   torch.Tensor, but AV chain output is a NESTED tensor (video+audio) - so
   the output value function never received a single sample. The snapshot
   now extracts the video stream from nested outputs.

3. FunPackNVFP4ModelLoader hit a size-mismatch wall (9-vs-6 ada params,
   connector dims): it loaded the state dict without the safetensors
   METADATA that carries the LTX-AV architecture config, so detection
   built the wrong model variant. Metadata now flows through, mirroring
   comfy.sd.load_diffusion_model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:16:26 +03:00
digital-garbageandClaude Fable 5 d2e642ebed editor: surface the disabled-built-in-pipeline state instead of hiding it
'No, I'll use my own pipeline' in the missing-nodes prompt silently
PERSISTED disable_core on the project. From then on Engine settings…
greyed out with no reason and Generate died with ComfyUI's raw
"Prompt has no outputs" - both looked like breakage with no visible cause.

- The setup prompt button now confirms first, spelling out what it
  disables, what Generate will need, and where to re-enable it.
- Engine settings… never greys over pipeline state anymore: the modal
  itself already explains why knobs are unavailable and links to Models.
- Builder: disable_core with NO output node in the graph is now a
  BLOCKING error with the fix in the message (wire 🌐 Global video
  output, or Models → Enable built-in pipeline) instead of letting
  ComfyUI reject the queue.
- GEN_ERROR_MAP: prompt_no_outputs mapped to the same actionable text
  for custom pipelines that pass the builder but lack an output node.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 07:59:36 +03:00
digital-garbageandClaude Fable 5 439a85f469 readme: show the Cutting Room - hero screenshots + gallery section
'What it looks like' section right under the intro: a clean full-workspace
hero shot plus Composer and Engine Settings side by side, generated from
the mocked tour demo (no ComfyUI/GPU needed) by a new
tools/showcase/capture_clean.mjs (npm run capture:clean) - same scaffolding
as capture.mjs but with the tour overlay stripped, so shots are undimmed
and card-free. Images committed under docs/images/.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 07:01:27 +03:00
digital-garbageandClaude Fable 5 36826165f3 readme: unify support-button shapes + solo-maintainer notice
All three buttons are now same-shape shields.io for-the-badge badges
(Ko-fi's rounded SVG pill and Patreon's PNG clashed with the flat
Vast.ai badge), and an IMPORTANT callout right under them sets
expectations: one-person project, provided as is, bug reports welcome.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 06:52:28 +03:00
digital-garbageandClaude Fable 5 70dcc74692 sampler: block steering - rating-learned per-block residual gains (experimental)
RLHF at the model level: which REGIONS of the transformer were doing the
work when a generation came out good or bad, and steer accordingly.

1. Measure: tagged forward hooks record each block's relative video-residual
   delta per model call (near-zero cost; covered by the existing leak-strip).
2. Attribute: the end-of-run fingerprint is staged (block_snapshot.pt, same
   stage-then-pair convention as x0_snapshot) and paired with the rating
   reward on the next Refiner cycle into liked/disliked/all-runs EMA
   profiles; when output_guidance's value function is trained, an innermost
   model-function wrapper additionally credits blocks per step by
   (value delta) x (centered activity) - converges in a handful of runs.
3. Steer: vx' = vx_in + g_b*(vx_out - vx_in) on the video stream only,
   audio untouched; scores zero-mean/unit-max so emphasis is redistributed
   rather than inflated, gains hard-capped at +/-10%. Exact no-op until the
   profile has 2+ paired runs; blocks at gain 1.0 get no hook at all.

Chain Sampler toggle + strength (append-only widgets), exposed same-change
in the editor's Chain Sampler Guidance section via the sampler_inputs
bridge. New sidecars ride the defensive <key>.* sweep in
delete_refinement_key, so atomic key deletion covers them.

Tests: toy AV-block model driving the real hooks (recorder ranking, steer
video-only scaling + native path for unit gains, credit wrapper, snapshot/
profile/scores/gains roundtrip, mild-positive band, depth-mismatch reset,
leak-strip tagging). Full suite failure set identical to the pre-existing
baseline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 06:42:31 +03:00
digital-garbageandClaude Fable 5 3dd2d33f1d model: experimental NVFP4 on-the-fly patching for LTX-AV on Blackwell
FunPackNVFP4ModelLoader quantizes an existing bf16/fp16 checkpoint to NVFP4
at load time as a pure state-dict transform: selected 2D Linears are
re-serialized in ComfyUI core's quantized-checkpoint format (packed fp4
weights + fp8-e4m3 block scales + fp32 tensor scale + per-layer comfy_quant
metadata) and handed to the stock loading path. Core's MixedPrecisionOps
then does everything: real FP4 tensor-core GEMMs on sm_100+ (sm_120 incl.),
dynamic activation scales (no calibration pass), per-layer LoRA fallback,
and dequant emulation on older GPUs. No custom forward, no monkeypatching.

Zone-aware precision map for the joint AV transformer: default scope
quantizes only video-branch Linears (attn1/attn2/ff) of the middle blocks;
the audio branch, the audio<->video cross-modal bridges, the first/last N
blocks, and all non-matmul weights stay full precision. Scopes widen to
+cross-modal, all blocks, and all 2D layers for experimentation.

Tests drive the REAL comfy.quant_ops/comfy.ops via comfy_kitchen's eager
backend on CPU: scope selection, serialized-format contract, prefix
handling, refusal of pre-quantized checkpoints, and a load+forward
roundtrip through MixedPrecisionOps on both the NVFP4 and emulation paths.

The Movie Editor Models modal picks the node up automatically as a
Unet/Diffusion Model candidate (object_info-driven).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 06:16:08 +03:00
digital-garbageandClaude Fable 5 c8710597d0 editor: fix input interruption, unviewable renders, and API-wedging media connections
- Composer search: filter keystrokes rebuilt the whole window and dropped
  focus after every character; now only the list refreshes
- Global prompt: stale funpack-global-prompt-updated events (preview refresh
  pinning the LAST completed apply) rewound the Compose textarea while newer
  text was still debouncing/mid-parse; overlapping applies could also land
  out of order. Added pending-apply guard + apply sequence token
- Composer crashed the editor when opened with no project (welcome screen)
- /result now streams from disk via FileResponse (Range/seek support) instead
  of buffering whole videos through a loopback /view fetch that crawled or
  aborted while ComfyUI was saving the next run; added '..' traversal guard
- Player keeps a URL-keyed video pool: one failed load left the clip dead
  until page reload; now bounded cache-busted retries + recovery on rebuild
- Render validation deleted (and autosaved away!) sceneRenders on ANY HEAD
  failure; now only a definitive 404 drops a render
- Temp browser / media bin / filmstrip / duration probes held stalled media
  connections open, exhausting Chrome's 6-per-origin pool so /view tabs and
  even generate calls hung until reboot; thumbnails snapshot to canvas and
  every probe releases its src when done
- Inspector guide frame/apply/strength inputs lacked data-k typing protection

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 01:40:22 +03:00
digital-garbage 293dbd11d5 distilled flow: port quality_sharpness unsharp mask from Hybrid Euler 2S
The free video-only unsharp-on-x0 sharpening (temporal high-pass between
consecutive denoised predictions, no extra model eval) only existed on
Hybrid Euler 2S. Distilled Flow — the recommended sampler for this
pipeline (deterministic, clean audio) — had no equivalent, so the
"quality phase" of the actual production sampler had no sharpening path
at all.

Ports the same _apply_quality_sharpness call into
sample_funpack_distilled_flow, gated to the existing final-correction
(Heun) steps. Threaded through FunPackDistilledFlowSampler's
INPUT_TYPES/get_sampler, Studio's _build_one_sampler, and the Editor's
sampler_panel.js Distilled Flow section — verified live end-to-end
(UI -> studio_settings JSON -> backend build call).
2026-07-01 17:02:13 +03:00
digital-garbage bcbc3670d5 value_function: pairwise ranking loss + ensemble disagreement gate
Replace absolute reward regression with Bradley-Terry pairwise ranking
(DPO-style) so the value function learns relative preference between
sampled pairs rather than fitting noisy absolute reward floats.

Add a 3-member bootstrap ensemble; gradient() and ascend() scale by
inter-member agreement so guidance backs off in regions the ensemble
hasn't seen data for, instead of confidently extrapolating (the
static-character collapse this was meant to guard against).

Loosen the "Wrong but loved" exclusion in conditioning.py so a strong
loved_modifier signal is no longer filtered out by skip_value_function
- "Wrong but loved" and "Missing but loved" now train the value
function, per the stronger-signal precedence requested.

Legacy single-net checkpoints migrate transparently on load (weights
remap into all ensemble members, rating buffer preserved intact).
2026-07-01 16:35:05 +03:00
digital-garbageandClaude Sonnet 5 a0fcab3f0a editor: expose output_guidance in Chain Sampler Guidance section
The sampler-side toggle landed as a node widget only, with no Editor UI
control — caught after the fact. Editor UI takes priority over node
widgets for any new toggle going forward. No backend change needed: the
existing generic sampler_inputs bridge (builder.py:338) already forwards
any declared SAMPLER_KNOBS entry onto the Chain Sampler node.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 15:27:12 +03:00
digital-garbageandClaude Sonnet 5 d91388922e sampler: add output_guidance, a value function trained on the model's own prediction
New LatentValueFunction (value_function.py) mirrors OnlineValueFunction but is
trained on a pooled snapshot of the sampler's own predicted output (x0_hat)
instead of the input conditioning, borrowing NoiseTilt's core idea (reward
gradient from the model's own prediction) without its SDE noise-term trick,
which has no analogue in this deterministic-ODE sampler.

New output_guidance / output_guidance_strength toggle on the Chain Sampler
(off by default): per-step correction installed as the outermost
model_function_wrapper (on top of embed_guidance/score_slider), same
near-zero-forward-pass shape as embed_guidance. End of run writes a small
pooled snapshot per refinement key; the next rating cycle trains the value
function on it via a new call site in conditioning.py.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 15:10:40 +03:00
digital-garbageandClaude Sonnet 5 ce5cd9f5d4 editor: fix Models +Add buttons doing nothing on an empty project
The composer was already being pre-selected on click, but nothing scrolled
it into view or signaled the change — on a fresh project all essentials
show as missing and push the composer off-screen, so the click looked
like a no-op. Now scrolls the composer into view and flashes it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 15:10:32 +03:00