Phase 3 of the modernization plan: move the framework-free math out of
the comfy-coupled modules so Tier-0 tests can run on plain Linux without
ComfyUI, coremltools, or python_coreml_stable_diffusion.
New pure-core package (no comfy / coreml / mps imports):
- coreml_suite.core.latents: chunk_batch, merge_chunks
- coreml_suite.core.controlnet: expand_inputs, no_control,
extract_residual_kwargs, chunk_control
- coreml_suite.core.inputs: CoreMLInputs (chunks + coreml_kwargs)
- coreml_suite.core.sdxl: is_sdxl / is_sdxl_base / is_sdxl_refiner,
build_sdxl_time_ids (base len 6, refiner len 5), build_sdxl_text_embeds,
sdxl_model_function_wrapper
- coreml_suite.core.naming: compose_out_name, lora_names_from_params
Thin adapters keep the public import paths:
- coreml_suite.latents / coreml_suite.controlnet: re-export from core
- coreml_suite.models: CoreMLModelWrapper, CoreMLModelWrapperLCM,
add_sdxl_model_options (now uses the pure builders from core.sdxl),
get_latent_image, get_model_patcher remain framework-coupled
- coreml_suite.nodes: CoreMLConverter.convert now delegates the out_name
composition to core.naming.compose_out_name
Test infra:
- tests/unit/* re-pointed at coreml_suite.core.*
- test_chunks.py dropped `from comfy.model_management import ...` and
the dead `model_config` fixture (Phase 1 left it broken; Phase 3
removes it entirely)
- test_characterization_sdxl_options now targets the pure builders
directly via inspect.getclosurevars on the wrapper closure
- test_characterization_out_name now calls compose_out_name without the
heavy CoreMLConverter monkey-patching that Phase 2 needed
- tests/unit/test_tier0_purity.py: new gate that fails if comfy /
coremltools / etc leak into sys.modules during a pure `-m unit` run
(skipped in mixed runs where m2 / integration legitimately import them)
- tests/__init__.py + top-level conftest.py + pyproject addopts
`--import-mode=importlib --confcutdir=tests` together stop pytest from
importing the repo-root `__init__.py` (the ComfyUI custom-node entry
pulls in comfy)
- tests/conftest.py adds tier-aware collect_ignore so `-m unit` skips
tests/m2 + tests/integration at collection time
Verification:
- `pytest -m unit tests/` → 88 passed in ~2s; deterministic across runs
- Tier-0 purity gate confirms no comfy/coreml/etc in sys.modules
- m2 golden image (Phase 2 anchor) still hashes identical → refactor
produced bit-for-bit unchanged output
- `git diff main -- __init__.py coreml_suite/nodes.py` shows zero churn
to NODE_CLASS_MAPPINGS keys or INPUT_TYPES field names (public
workflow contract intact)
Phase 2 of the modernization plan: lock the current behavior of the pure
math so the Phase 3 refactor cannot silently change it.
Unit characterization tests (Tier 0, 64 new):
- test_characterization_latents.py: chunk_batch / merge_chunks padding,
truncation, and identity contracts.
- test_characterization_controlnet.py: expand_inputs / no_control /
extract_residual_kwargs / chunk_control shape, dtype, and zero-fill
behavior, including the [None]*target contract.
- test_characterization_inputs.py: CoreMLInputs.chunks / coreml_kwargs
for SD1.5, SDXL base (time_ids len 6), SDXL refiner (time_ids len 5),
and LCM (timestep_cond).
- test_characterization_sdxl_options.py: add_sdxl_model_options time_ids
/ text_embeds assembly via a SimpleNamespace fake ModelPatcher and
inspect.getclosurevars on the returned model_function_wrapper.
- test_characterization_out_name.py: CoreMLConverter out_name encoding
for attn_impl suffix, batch/size, ControlNet, LoRA (sorted), SDXL.
M2 [Tier 2] golden image anchor (1 new):
- test_golden_image.py: posts the SD1.5+CoreML workflow to a local
ComfyUI server (auto-skips if unreachable), asserts SHA256 of the
generated PNG against tests/m2/goldens/sd15_seed42.sha256; falls back
to PSNR >= 40 dB if the hash drifts.
Test infra:
- pyproject.toml [tool.pytest.ini_options]: unit / m2 / smoke markers,
testpaths=tests; rootdir is now this package (was ComfyUI's pytest.ini).
- tests/conftest.py: bootstraps sys.path for comfy imports, auto-marks
tests by directory, and ignores the maintainer's WIP scaffolds
(test_experiments / test_unet_conversion / standalone_test) so they
don't break collection.
All 85 collected tests pass; two consecutive runs produced identical
results (run1: 3.55s, run2: 3.40s).
Phase 1 reference numbers, captured against commit ef2a18c on macOS 26.1
with the pinned toolchain (torch 2.0.1, coremltools 8.2, numpy 1.23.5,
python_coreml_stable_diffusion@e5d960c4).
- bench/env/baseline-ef2a18c.txt: full pip freeze + ComfyUI sha + macOS +
resolved ml-stable-diffusion git metadata.
- bench/env/pytest-unit-ef2a18c.txt: pytest tests/unit (test_chunks +
test_controlnet) 20/20 passing.
- bench/results/ef2a18c.{json,md}: SD1.5 1x512x512 SPLIT_EINSUM UNet
forward latency on the Apple Neural Engine and CPU+GPU. NE median 197 ms
/ GPU median 270 ms; a second run reproduced both within 0.3% (noise).
- bench/results/smoke/ef2a18c/: end-to-end Core ML image (E2E-1.5-CoreML
workflow, seed=42) saved by smoke_image.py. The MPS reference branch of
the original workflow is omitted because torch 2.0.1's MPS backend on
macOS 26.1 trips a BFloat16 conversion error in VAEDecode and an
mps.add element-type mismatch in KSampler — both go away with newer
torch and are tracked for the Phase 5 toolchain bump. The server was
started with --cpu-vae to route the VAE through CPU; this is a runtime
flag, not a pin change.
Phase 1 of the modernization plan: freeze the currently-working environment
so later refactors have a measured reference point.
- Pin python-coreml-stable-diffusion to commit e5d960c4 (the one already
installed in the maintainer's apple_env), plus torch==2.0.1, coremltools==8.2
and numpy<1.25 to match the only env that loads ComfyUI successfully
(Comfy's checkpoint-safe-loading branch in utils.py is gated on torch>=2.4,
so newer torch + numpy 1.23 breaks at import).
- Mirror the same pins in requirements.txt and commit uv.lock for
reproducible installs.
- Add requires-comfyui pinning ComfyUI to ab541335 (the validated commit).
- Fix tests/unit/test_chunks.py fixture: get_model_config() now takes a
ModelVersion argument; pass ModelVersion.SD15 (the previously-broken test
was the only Phase 1 production-code change required).
- Add the Phase 1 baseline harness: bench/run.py (direct Core ML UNet
latency, deterministic), bench/scripts/convert_sd15.py (one-command
conversion bypassing the node graph), bench/scripts/smoke_image.py (POSTs
the existing e2e workflow to a local ComfyUI server and saves the Core ML
image), bench/env/capture.sh (env snapshot), bench/prompts.json (fixed
prompt set).
- Ignore apple_env/, comfy_env/, and bench/scripts/*.log.
Tests: 20/20 unit pass (test_chunks + test_controlnet).
- Added permissions for issue writing
- Updated action version to v1 for publish-node-action
- Added condition to run job only for 'aszc-dev' repository owner