mirror of
https://github.com/Comfy-Org/ComfyUI.git
synced 2026-08-25 10:22:12 +08:00
AGENTS.md had grown to 355 lines. Move the detailed architecture, model
and node sections into docs/agents/ and keep the root file to the rules
that should steer every change. All existing rules are preserved.
Add CLAUDE.md as a regular file containing an @AGENTS.md import so Claude
Code reads the same instructions. The previous attempt used a symlink,
which broke updates and was reverted in 7cf4e78.
4.1 KiB
4.1 KiB
Architecture and Interfaces
Detailed rules referenced from AGENTS.md.
Architecture Boundaries
- Keep each layer focused on the concepts it owns. Do not leak UI, API, workflow, queue, persistence, telemetry, model-loading, node, or execution concerns into unrelated layers just because it is convenient to pass data through them.
- Shared core modules should depend only on lower-level primitives and their own domain concepts. Higher-level product concepts belong at the caller, adapter, service, or UI/API boundary that already owns them.
- Pass the narrowest data needed across a boundary. Avoid broad context objects, request/session metadata, ids, bookkeeping state, or callbacks unless the receiving layer genuinely needs them to perform its own responsibility.
- Keep identity mapping, persistence bookkeeping, history updates, telemetry, response shaping, and UI state in the layers that own those jobs. Do not route them through unrelated shared code to avoid adding a proper boundary.
- Treat
execution.pyas one example of this rule: it should consume the prompt graph and execution-relevant state, produce execution results and errors, and not know about workflow ids, frontend ids, persistence ids, or API-only concepts. - Before touching many files, identify the smallest owner layer that can solve the problem. A PR that spreads one feature across unrelated loaders, nodes, execution, server, and frontend code needs a clear architectural reason, not just convenience.
- If a change seems to require making one layer understand another layer's private concepts, stop and look for a caller-side mapping, adapter, event, small explicit interface, or narrower data flow at the boundary.
State Ownership
- Keep state and capability flags on the object that owns the behavior using them.
- Avoid probing child objects with
getattr(child, "...", default)to decide parent-level control flow. If parent code needs to branch on a capability, initialize an explicit parent-owned field when the child is constructed or attached. - Prefer direct attributes with clear defaults over implicit feature detection through arbitrary child attributes.
- Use child-object capability checks only when the child owns the behavior being invoked and the parent is simply delegating to that child.
Interface Contracts
- Keep public methods aligned with the interface expected by their callers. Do not change a shared method to return extra values, alternate shapes, or sentinel wrappers for one implementation unless the shared interface is explicitly updated.
- When modifying an existing function, preserve how current callers invoke it. Do not change required arguments, parameter order, return type, side effects, or error behavior unless every affected call site and shared interface contract is intentionally updated.
- Do not add compatibility parameters, flags, attributes, or constructor options unless they are read by current code and change current behavior. Remove pass-through or stored-but-unused values instead of preserving upstream or deprecated API baggage.
- Do not add a model-specific option to a shared helper when only one caller needs it. Keep one-off behavior at the model integration boundary, or extend the shared helper only when the option is a coherent reusable capability.
- Implementations of shared model interfaces should accept the standard caller contract without model-specific rejection branches for optional capabilities they do not consume. Let supported behavior be determined by implementation paths that actually use those inputs.
- If an implementation needs auxiliary values for its own workflow, expose them through a private helper or a clearly named implementation-specific method instead of overloading the public method's return contract.
- Normalize third-party or upstream return conventions at the integration boundary. Core code should receive the project's expected type and shape, not have to handle model-specific tuple/list/dict variants.
- Avoid caller-side unwrapping such as
out = out[0]unless the called interface is documented to return that structure.