Architecture decision records¶
Nygard-style records (Status / Context / Decision / Consequences) for comfy-env. Numbers are chronological-ish by subsystem, not by date; most decisions were made implicitly during development and are recorded here retroactively as of v0.4.12 (August 2026).
Where a status line cites "adversarial review": these are structured LLM review panels (two independently-briefed reviewer personas that investigate the ADRs and code separately, then debate each other's findings), run by the maintainer in August 2026. Their claims were verified against the working tree before being incorporated; treat the citations as "the argument survived cross-examination", not as external human endorsement.
| ADR | Decision | One-liner |
|---|---|---|
| 0001 | Process isolation via persistent subprocess workers | Conflicting node deps get their own interpreter; ComfyUI talks to proxies. |
| 0002 | pixi as environment manager | conda-forge + PyPI in one manifest with a real lockfile; uv speed underneath. |
| 0003 | Two config files, two roles | comfy-env-root.toml never touches the Python env; comfy-env.toml means full isolation. |
| 0004 | Prebuilt CUDA wheel index | Kill the ABI x torch x CUDA x OS x arch build matrix for end users. |
| 0005 | Tiered tensor serialization | Six strategies, best-available-first, zero-copy where the platform allows. |
| 0006 | comfy-env is never installed into worker envs | The worker script crosses as source text -- materialized as a file, never imported; duplication is deliberate. |
| 0007 | Machine-wide workspace, per-env manifests | One shared store per machine; one pixi.toml per env; stamps guard staleness. |
| 0008 | Graceful degradation everywhere | Every failure path ends in "ComfyUI still boots". |
| 0009 | Platform strategy | Windows/macOS/Linux each get targeted workarounds, not lowest-common-denominator. |
| 0010 | Wire protocol and transport | Hand-rolled framing stays; the v2 direction versions the wire and unifies the stack. |
| 0011 | Isolation before sandboxing | v1 ships dependency isolation only; security is deferred with the path mapped, and isolation is never sold as a security boundary. |
| 0012 | Unavailable nodes: menu-hidden, never unregistered | Workflows must load; menus must not show dead nodes. DEPRECATED separates the two. |
| 0013 | Env-file config: honest passthrough | Forward everything pixi owns; comfy-env keeps 4 denied keys, 1 rewritten family, 1 merged table. Amends ADR-0003. |
| 0014 | Pack-extensible serializer registry | Packs register their own wire types; payloads decompose into schema + tensors, never pickle; unknown tags pass through opaque. |
| 0015 | Declared wire types | One [types] table per pack; serialization.py only for hero types; identity tags make shared types interop; failed serialization errors loudly. |
| 0016 | Node pack dependencies | [node_packs] auto-install stays for headless testing -- but pinned (git refs only; registry untrusted) and comfy-envved only; test-workflow utilities move to comfy-test config. |
| 0017 | Pre-1.0: no backward compatibility, by decision | Break freely while all consumers are the author's; the era ends at the slow-rollout tripwire, which starts the compat and security clocks. |
| 0018 | Worker call timeout | 600 s default becomes per-env call_timeout; expiry keeps kill-the-worker semantics; the mid-call heartbeat is the named successor. |
| 0019 | Worker lifecycle | Workers are disposable, replacement is invisible; generations, the stale-patcher invariant, consumed-ack, and the idle reaper decided. |
| 0020 | Concurrency and env granularity | The pack is the unit of concurrency and fate: one worker + one lock per env; disk may dedupe by content, processes never merge. |
| 0021 | The three-call contract | install/setup_env/register_nodes as one-liners; caller inference via stack frames; the file layout is the configuration. |
| 0022 | comfy-env's placement in the host env | The one exception to the host-env principle, kept for pre-1.0; the split-and-relocate alternative recorded for the rollout tripwire. |
| 0023 | Metadata scan and proxy synthesis | The main process interviews packs instead of importing them; scan subprocess + hash-keyed cache + generated V1 proxies. |
| 0024 | The upstream interface contract | The loan book: every ComfyUI internal we touch, stamped, with the upstream ask that retires it; the posture for the day core ships isolation. |
| 0025 | VRAM co-management across processes | ComfyUI stays the single VRAM authority; workers hold on lease; WDDM is best-effort by decision; device fields reserved for multi-GPU. |
| 0026 | Trust and supply chain | What users trust today, enumerated; wheel hashes and farm qualification move to now; bus factor named; pickle flips at the sandbox. |
| 0027 | Testing and verification strategy | CI proves transport and compilers; the canary IS the GPU test; conformance, benchmark harness, and the HEAD-canary lane get owners. |
| 0028 | Workspace disk lifecycle | Envs are caches evicted with consent: gc categories, a banner nudge, and the refcount design content-addressing is blocked on. |
| 0029 | Parent as switchboard | All inter-worker data flows through the parent, which owns what it holds; alternatives rejected at 1-2%; revisit trigger named. |
| 0030 | GPU platform floors | Pinned-memory D2H is the next GPU investment; pool IPC demoted to experimental pending an ownership contract; floors probed, never assumed. |
| 0031 | Frontend JavaScript isolation (deferred) | Pack JS shares one browser origin -- backend isolation buys zero here; deferred like 0011, with a comfy-test collision gate as the one build-now item. |
| 0032 | Shared-memory lifetime: consumed-ack | The reader frees blocks by acking call_id, not by a timer; the TTL is a crash fallback; materialize-on-receipt is the sibling rule. |
| 0033 | Local IPC authentication | Per-spawn authkey as the worker's first frame + SO_PEERCRED on Linux; addr/key via env not argv; the honest Windows same-user gap and the not-a-sandbox line. |
| 0034 | Admission by arithmetic, never mem_get_info |
mem_get_info is per-process on WDDM (measured: 13 GiB invisible). Mechanism superseded by 0038: the target is upstream's own expression again, and the ledger correction applies only where the reading really is process-local. |
| 0035 | The model proxy is a duck-type | 18 declared members instead of ~120 inherited; unknown access raises naming the attribute. Deprecation reversed, 2026-09-05: reclaim depends on this object entirely, and the observer meant to replace it was deleted. |
| 0036 | Mirroring ComfyUI's memory manager | Superseded by 0038. Its Part 1 remains the reference description of upstream's memory manager; the mirroring decision is not what shipped. |
| 0037 | No non-isolated paths | One execution model: isolated. The off-flags removed (nobody set them; off-states never worked); degradation stays evidence-triggered; value-sensitive tombstones. |
| 0038 | The memory floor | comfy-env reads, publishes one number, and asks; it does not patch the host. Reclaim of worker VRAM is dropped for idle release. Supersedes the mechanisms of 0034 and 0036. It also deprecated 0035's proxy and dropped reclaim; both were reversed on 2026-09-05 and the proxy is current. |
| 0039 | The env directory seam | <env-name>_<abi-tag>, underscore seam, version dots kept. One dash used to mean four things and could not be read back. Older spellings are adopted in place, never renamed. |