The test asserts on the stub that records the progress calls. With
KEEP_NO_COMFY_MOCKS=1 the node uses the real comfy_api.latest API, whose
execution recorder has no calls attribute, so the test failed in the
mocks-off configuration (17 passed, 1 failed). It now skips there, with the
reason naming the configuration. The gate and the headless tier are
unchanged and green.
ComfyUI runs a V3 node's validate_inputs during the prompt validation, where
the links are not resolved yet: a connected pack arrives as None (after the
list unwrapping) or as the (None,) marker of a missing link. Both nodes
rejected that unresolved pack, so every prompt failed with the same error
once per input.
- validate_inputs now accepts an unresolved pack and only refuses a value
that is present but of the wrong type.
- execute raises the actionable error for a pack that is really wrong
(the plan already requires execute to raise instead of returning None).
- the shared message lives in src/keep_model_loader.keep_model_pack_error.
- tests cover the unresolved pack (None and (None,)) and the raising execute.
Verified against the real ComfyUI validation path: execution.get_input_data
with a linked pack returns (None,) and validate_inputs now returns True.
The five review findings of 2026-10-23 for phase P5:
1. The P5.5 fallback branch (a checkpoint without flownet.* keys) died with
a bare RuntimeError at the strict load: the arch always owns the flownet
(keep_arch.py:882), so the state dict of the net always carries the
flownet.* keys - and the GMFlow weights the arch had just loaded were
discarded. The flownet tensors of the built net (the trained ones - the
arch was built with flownet_path) are now merged into the checkpoint
state dict before load_state_dict(..., strict=True). The load stays
strict (an unexpected key still raises) and the trained flow weights
survive. The stub arches of the tests behave like nn.Module now - a
no-op recorder had hidden the defect.
2. P5.4's acceptance is no longer unmet: the GPU acceptance run was
executed (real weights, real ComfyUI modules, RTX 4070 Ti SUPER) and is
green (5 passed, exit code 0; the run log is in the audit doc section 5).
KEEP_MODEL_DIR now really decides where the weights come from - it is the
models root of the run (folder_paths.models_dir), the harness fixture
no longer isolates the gpu_acceptance tier, and the run needs
KEEP_NO_COMFY_MOCKS=1 so the real ComfyUI modules are in charge.
3. The "the flow weights are not random" assertion of the acceptance test
could not distinguish random weights from trained ones. It now compares
the flownet tensors with the tensors of their source (the KEEP checkpoint
or the GMFlow file) bit for bit.
4. The superseded download helper load_file_from_url_comfy is gone
(src/keep_paths.py only resolves paths; the download lives in
src/keep_download.py since P5.2). The stale docstrings, the stale
comment in the vendored facelib and the two tests that called the dead
helper are fixed.
5. The risk list mentioned a pending deletion of assets/keep-preview.png
that does not exist - the asset is committed and present (93279 bytes).
The rule in the implementation workflow now states the truth.
Checks: the gate (pytest -q -m "unit or mock_integration") is green (234
passed, 1 skipped, 1 xfailed); the opt-in acceptance run is green (5 passed);
the acceptance tier still skips cleanly without its environment variables.