The declaration this series exists to protect was itself checked for four of
sixteen bases: swapping FLUX_16 for SD3_16 in the FLUX declaration passed every
committed test and corrupted every FLUX preview at generation time. A 16-row
identity table now pins each base against the pre-refactor dispatch, and each of
the nine spaces has a hand-derived preview pixel. The smoothing-kernel test was
seeding after it drew its sample, so it had never been deterministic.
The guidance control's bounds now come from the architecture too, the way the
dimension grid already does. FLUX.2 accepts at most 20 and four architectures
reject below 1, while the input allowed 0 to 100 for every model -- so a value
the UI offered was rejected at enqueue. Pinned against the ge/le of the field
each denoise node actually validates.
Also: Ideal Size is no longer titled for SD, since it answers for every
architecture; the contributor docs describe the layout this branch created,
including the defs module whose absence refuses boot; and the docs-export test
no longer depends on pytest's working directory.
- safe_globals is assembled by conditioning_safe_globals() and pinned to its
call site by an AST test; the old test never imported dependencies at all.
- validate() enforces only first-party facets, and register() accepts an
identical re-declaration, so a custom node pack and --dev_reload no longer
kill boot blaming defs/.
- dimension_grid is variant-keyed: Wan TI2V-5B declares 32, which ideal_size
and the served table now resolve from the model's variant.
- The cross-stack graph contract records the literal inputs webv2 writes and
validates each against the invocation field it lands on.
- sd-2, z-image and anima declare the regional guidance they actually support;
the default-settings matrix is pinned per (base, variant).
- The generated architecture stub now refuses to import, as its docstring
already promised, and starter_models exports only its catalogues and types.
`scripts/new_architecture.py` writes the three files the registry made mechanical — the declaration
under `architectures/defs/`, the invocation package, and the starter-model module — and then prints
everything it cannot write.
The second half is the more useful one. That list is derived on each run, not stored: any module
naming five or more `BaseModelType` members is dispatching on base, so a new base has to be added to
it. Twelve modules qualify today. A written-down list would already be wrong — `step_callback.py`,
`dependencies.py` and, as of the previous commit, `ideal_size.py` have all dropped off it during
this series, and a maintained list would still be naming them.
The generated declaration deliberately does not work: every required facet is present with an
obviously wrong value — a one-row black projection, a single mode, a placeholder canvas — and five
TODOs saying what each one needs. The app refuses to boot until they are filled in. A stub that
booted would let a half-integrated architecture reach a user, which is the failure this structure
exists to prevent. Verified end to end: written, refused to import, refused to overwrite on a second
run, removed cleanly.
Tests pin the parts that would rot silently: that each stub parses, that the declaration carries all
five required facets, that it still looks unfinished, and that the derived list contains
`configs/main.py` while containing none of the three modules the registry has absorbed. That last
assertion is what notices a facet being bypassed later.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>