release: 3.5.1 "Auspicious Asparagus"

Release codenames, the way Ubuntu does it: an adjective and a vegetable
sharing an initial, advancing alphabetically, one per MAJOR version — so all
of 3.x is "Auspicious Asparagus" and only a new major earns a new letter.

CODENAMES lives beside funpack_version() because the two are read and shipped
together, and rides out on the same git-status payload the About panel already
fetches. A major with no entry renders as absence, not as an empty pair of
quotes. The test pins the naming rule itself (shared initial, ascending) so a
future entry that breaks the pattern fails rather than quietly shipping.

Version 3.5.1 in pyproject; CHANGELOG entry covering the whole cycle (the
Unreleased section only documented the first few commits of it); README gains
a Versions preamble explaining the scheme plus notes for 3.5.0 and 3.5.1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
digital-garbage
2026-08-11 23:55:53 +03:00
co-authored by Claude Opus 5
parent 98d48e78e3
commit 06c3f75080
7 changed files with 194 additions and 3 deletions
+80
View File
@@ -2,7 +2,73 @@
## [Unreleased]
## [3.5.1] "Auspicious Asparagus" - 2026-08-11
FunPack picks up release codenames, Ubuntu-style: an adjective and a vegetable sharing an
initial, one per major version. All of 3.x is **Auspicious Asparagus**.
Otherwise this is a compatibility-and-polish release on top of 3.5.0's second model family.
LTX-2.5 landed upstream mid-cycle and turned out to need almost nothing — but the two places
it *would* have broken silently are fixed, and several long-standing rough edges went with
them.
### Added
- **Release codenames.** Shown under the name in Settings ▸ About, carried on the same
payload as the version. A major with no name yet simply shows nothing.
- **About reports the machine ComfyUI runs on.** Chip, memory, GPU (name, VRAM and compute
capability), free disk, OS, ComfyUI, Python, torch and CUDA — plus which fast-attention
backend is actually importable, which answers "is SageAttention really installed on this
box" without reading the launch arguments. On a rental the host and the browser are
different machines and the host is the interesting one, so the panel says which it means.
Every probe degrades to a dash on its own: About is the screen that should still render
when the install is broken.
- **A schedule for every sampler.** Steps and scheduler belong to the pass, not to the
KSampler branch, so any sampler can now be given a computed schedule instead of only a
hand-typed sigma list. The frames field became usable in the same pass.
- **ALG on any sampler.** The i2v anchor blur used to be locked to FunPack's Distilled Flow
sampler. It now runs on whatever sampler is wired — a stock KSampler, Hybrid Euler 2S, a
two-evals-per-step sampler like `heun` — by lifting the guidance out of the sampler loop
and onto a denoiser proxy driven by the step's sigma.
- **H3 audio clock.** MiniMax H3 denoises video and audio on two different flow schedules
but hands the sampler one sigma grid, so the DiT reconciles them with a start-of-step
slope that badly overshoots on a few-step schedule — heard as distortion. `h3_audio_clock`
swaps that for the chord actually spanning the step. One scalar multiply, no extra model
call. It works with stock ComfyUI samplers too, and stands itself down with a console note
on samplers that evaluate twice per step rather than guessing.
- **Projects remember your Editor settings**, so a fresh rental does not reset your
preferences and shortcut revolver.
- **Every Engine setting says what it does, what it costs and what it needs**, in one line.
### Fixed
- **LTX-2.5 compatibility.** 2.5 reuses the same model classes behind new config flags, so
nearly everything binds unchanged — but two places assumed 2.3 specifics and would have
failed *silently*. The video/audio conditioning split was a hardcoded width table; from
2.5 on ComfyUI reads those widths off the checkpoint, and an unrecognised pair made the
split return the full width, steering the audio text context along with the video. The
only symptom would have been degraded audio. The split is now measured off the live model,
with the table kept as a fallback. Separately, decode-time noise reached only the conv
decoder: 2.5's diffusion decoder takes no timestep and seeds itself, but is an `nn.Module`,
so the settings were accepted and then ignored — a knob that read as live and did nothing.
It is now capability-tested and says so when it cannot apply.
- **`cut_opening_frames` no longer leaves a noisy first frame.** The cut was made in latent
space, and the LTX video VAE is causal: latent frame 0 is the temporal origin, while every
later frame was generated as a continuation. Slicing the front off the latent promoted a
continuation frame to position 0, which then decoded with origin handling it was never
generated for. The cut now happens on decoded pixels, after a decode that saw every frame
in the context it was sampled in — the way MiniMax H3 already did it. The count was wrong
too: a latent cut could only remove whole latent frames and shortened the clip by a
different amount than the span it removed. It is exact now, N means N. Video comes from the
IMAGES output on a cut run (the Editor already wires it that way); the latent's audio
stream is cropped by the same amount of time so sound and picture still start together.
- **Guide keyframes are dropped when `second_pass_op` changes the resolution.** They are
recorded as token indices into pass 1's grid, so `upscale_2x` left them addressing the
wrong tokens — which recent ComfyUI rejects outright and older builds mis-placed silently.
Pass 1 still uses every guide; only pass 2 loses them, and the scene report says so.
- **One ALG control instead of two.** The Distilled Flow panel had its own switch while the
chain sampler's anchor blur did the same thing on any sampler and already drove it — two
controls for one behaviour, with precedence depending on which you had touched. The Editor
now shows one. Projects carrying the old switch are migrated with their strength and
threshold, and told so.
- **H3 image conditioning actually reaches the model.** Two independent breaks, both silent,
both ending with a generation that ignored the input image entirely and left the prompt to
carry the whole shot. The Movie Editor always splits a run into per-scene conditionings,
@@ -16,12 +82,26 @@
because the sampler's positive comes from Studio. New optional **Chain Sampler ·
h3_keyframes** input (auto-wired for H3 projects) salvages the pins — first frame onto the
opening scene, last frame re-indexed onto the closing scene's own final frame.
- **Spare H3 token tags are trimmed, not thrown away.** An image prompt comes back one tag
long, and treating the surplus as corruption discarded the whole vector — which on exactly
those prompts left the DiT modulating the picture as if it were text.
- **Projects remember which model family they are for.** Every project on disk had no family
recorded while the global default said MiniMax H3, so the builder read them as LTXAV and
routed the audio VAE at a node H3's graph never builds. That is the reported "phantom Audio
VAE port": one root cause, four separate bugs, all fixed.
- **A wired `positive_conditioning` satisfies Studio's CLIP requirement**, and an
fl2va/ref2va node satisfies H3's AV latent requirement — neither should have been blocking
generation.
- **Warn only when Studio has no conditioning source at all**, rather than whenever one
particular input is empty.
### Changed
- **Guide strengths span the full 0..1 range.** Mid-scene guide, identity pin, prior-scene
guide, the per-guide strength field, and the JoyAI memory floor were clamped to 0.25–0.5.
That band is the measured audio-safe sweet spot, not a physical limit; it stays in the
tooltips and out of the code.
- **Project texts reach nodes that encode on their own**, expanded — so a custom encoder in
the graph sees the same prompt the pipeline does.
- The H3 `v2a_grad_scale` warning chip is gone. The knob does nothing without JoyAI audio
memory on any model, so flagging it as an H3 limitation blamed the wrong thing.
+23 -1
View File
@@ -44,7 +44,13 @@ Use only `main` if you want the most stable version of FunPack. Bug reports base
From time to time other short-lived branches may appear for the most advanced yet most breaking changes and features (e.g. a large rework in progress). I do not recommend using any non-`main` branch unless it's strictly necessary for your workflow — any sort of stability is not guaranteed, and such branches can be force-pushed or deleted at any time.
## Version 3.0 — Cutting Room and development direction
## Versions
Since **3.5.1**, each major version carries a release codename — an adjective and a vegetable
sharing an initial, advancing alphabetically, in the tradition Ubuntu made familiar. All of
3.x is **"Auspicious Asparagus"**. It is shown in Settings ▸ About.
### Version 3.0 — Cutting Room and development direction
FunPack **3.0** introduces the **Cutting Room** — a dedicated montage editor at `/funpack/movie` inside ComfyUI. It is the primary surface for building multi-scene projects, previewing on a real timeline, and driving FunPack Studio + the LTXAV Scene Chain Sampler without wiring a graph by hand.
@@ -82,6 +88,22 @@ the model's comfortable window, and a progress readout that says which scene and
running. Context windows, which never actually worked before, are fixed — see the
[CHANGELOG](CHANGELOG.md) for the full list.
**3.5.0** adds a **second model family**: MiniMax H3 alongside LTX-2 / LTXAV. A project picks a
family, and everything downstream follows from that choice — which nodes the graph emits, which
model files the setup asks for, which sampler settings are even applicable, and what counts as a
valid scene length. Nothing about LTX-2 changes.
**3.5.1 "Auspicious Asparagus"** is the first release with a codename, and a
compatibility-and-polish pass on top of 3.5.0. **LTX-2.5** works — it reuses the same model
classes behind new config flags, so almost everything binds unchanged, and the two places that
would have broken *silently* are fixed. **Settings ▸ About** now reports the machine ComfyUI
runs on (chip, memory, GPU and VRAM, disk, OS, Python, torch, and which fast-attention backend
is actually installed), which is the thing you want at a glance on a fresh rental. **ALG** runs
on any sampler rather than only FunPack's Distilled Flow, and its two duplicate switches became
one. **`cut_opening_frames`** no longer leaves a noisy first frame — it cuts decoded pixels
instead of latents, and the count is now exact. See the [CHANGELOG](CHANGELOG.md) for the full
list.
See [`docs/MovieEditor.md`](docs/MovieEditor.md) for complete Cutting Room documentation.
## Installation
+22 -1
View File
@@ -39,6 +39,25 @@ def funpack_version() -> str:
return m.group(1) if m else ""
# Release codenames, the way Ubuntu does it: an adjective and a vegetable sharing an
# initial, advancing alphabetically. Keyed by MAJOR version, so every 3.x release ships
# under the same name and only a new major earns a new letter.
CODENAMES = {
"3": "Auspicious Asparagus",
}
def funpack_codename(version: str = "") -> str:
"""Codename for `version` (default: the installed one), or "" when its major has none.
Kept beside funpack_version because they are read together and shipped together; a
major with no entry simply has no codename yet, which the UI renders as absence rather
than as a blank line.
"""
major = (version or funpack_version()).split(".", 1)[0].strip()
return CODENAMES.get(major, "")
def _current_branch() -> str:
proc = _run_git("rev-parse", "--abbrev-ref", "HEAD")
if proc.returncode != 0:
@@ -115,6 +134,7 @@ def status() -> dict:
return {
"ok": True,
"version": funpack_version(),
"codename": funpack_codename(),
"branch": branch,
"commit": commit,
"dirty": dirty,
@@ -125,7 +145,8 @@ def status() -> dict:
"repo": str(REPO_ROOT),
}
except GitUpdateError as e:
return {"ok": False, "version": funpack_version(), "detail": str(e)}
return {"ok": False, "version": funpack_version(),
"codename": funpack_codename(), "detail": str(e)}
def pull(branch: str | None = None) -> dict:
+9
View File
@@ -246,6 +246,15 @@
const git = G ? G.get() : null;
wrap.append(el("div", "sw-about-mark", "◉"));
wrap.append(el("div", "sw-about-name", "FunPack"));
// Ubuntu-style release codename, carried on the git status alongside the version.
// Keyed by major, so it reads "3 “Auspicious Asparagus”". Omitted entirely when the
// backend is older than the field or the major has no name yet — an empty quoted
// string would look like a bug.
if (git?.codename) {
const major = String(git.version || "").split(".")[0];
wrap.append(el("div", "sw-about-codename",
(major ? major + " " : "") + "“" + git.codename + "”"));
}
// window.FunPackAppName lets a different frontend sharing this file (e.g. Easy
// Gen) relabel the app name without forking the whole section; Editor leaves it
// unset and keeps "Cutting Room".
+2
View File
@@ -1723,6 +1723,8 @@ body.tour-mode { overflow: hidden; }
.sw-about-mark { font-size: 56px; line-height: 1; color: var(--amber);
text-shadow: 0 0 24px rgba(243,169,60,.45); margin-bottom: 10px; }
.sw-about-name { font-family: var(--font-display); font-weight: 700; font-size: 28px; letter-spacing: -.02em; }
.sw-about-codename { font-family: var(--font-display); font-size: 14px; font-style: italic;
color: var(--amber); letter-spacing: .01em; margin-top: 2px; }
.sw-about-sub { font-size: 13px; color: var(--muted); margin-bottom: 18px; }
/* Two-column grid rather than centred rows: values now run from short ("3.14") to long
("NVIDIA RTX PRO 6000 Blackwell · 95.5 GB · sm_120"), and only a shared column keeps
@@ -0,0 +1,55 @@
"""Version and release codename, as shown in Settings ▸ About.
The codename is cosmetic, so the only things worth pinning are the ones that would make it
look broken: a major with no name must render as ABSENCE (the panel omits the line), never
as an empty pair of quotes, and the pyproject version must stay parseable — About reads it
through the same helper the update check does.
"""
import pytest
from movie_editor.backend import git_update
def test_installed_version_is_parseable():
version = git_update.funpack_version()
assert version, "About and the update check both read this"
parts = version.split(".")
assert len(parts) >= 2 and all(p.isdigit() for p in parts[:2]), version
def test_current_major_has_a_codename():
assert git_update.funpack_codename() == "Auspicious Asparagus"
def test_codename_is_keyed_by_major_not_by_release():
"""Every 3.x release ships under one name; only a new major earns a new letter."""
for version in ("3.0.0", "3.5.1", "3.99.12"):
assert git_update.funpack_codename(version) == "Auspicious Asparagus"
def test_unknown_major_has_no_codename():
"""Absence, not an empty string in quotes — the panel omits the line entirely."""
assert git_update.funpack_codename("9.0.0") == ""
assert git_update.funpack_codename("") != None # noqa: E711 - never None, always str
@pytest.mark.parametrize("version", ["3", "3.5", "3.5.1", " 3.5.1 "])
def test_codename_tolerates_short_and_padded_versions(version):
assert git_update.funpack_codename(version) == "Auspicious Asparagus"
def test_codenames_advance_alphabetically():
"""The Ubuntu rule this follows: adjective and vegetable share an initial, and the
initial advances with the major. A future entry that breaks it should fail here."""
for major, name in sorted(git_update.CODENAMES.items()):
adjective, _, noun = name.partition(" ")
assert noun, f"{name!r} should be two words"
assert adjective[0].lower() == noun[0].lower(), f"{name!r} initials differ"
majors = sorted(git_update.CODENAMES, key=int)
initials = [git_update.CODENAMES[m][0].lower() for m in majors]
assert initials == sorted(initials), f"codename initials must ascend: {initials}"
def test_status_carries_the_codename():
"""The About panel reads it off the git status payload, not a separate call."""
assert "codename" in git_update.status()
+3 -1
View File
@@ -2,7 +2,9 @@
[project]
name = "ComfyUI-FunPack" # Unique identifier for your node. Immutable after creation.
description = "An AI movie studio inside ComfyUI: the Cutting Room browser-based video editor with timeline, per-scene prompts and one-click montage generation, plus a self-improving engine that learns from your ratings. Includes the full node stack (Studio, Refiner V2, LTXAV Scene Chain Sampler) for classic graph workflows."
version = "3.5.0" # Custom Node version. Must be semantically versioned.
version = "3.5.1" # Custom Node version. Must be semantically versioned.
# Release codename ("Auspicious Asparagus" for all of 3.x) lives in
# movie_editor/backend/git_update.py:CODENAMES — keyed by major, shown in Settings ▸ About.
license = { text = "GNU GPL v3" }
dependencies = [] # Filled in from requirements.txt