74 lines
4.1 KiB
Markdown
74 lines
4.1 KiB
Markdown
# Architecture and Interfaces
|
|
|
|
Detailed rules referenced from [AGENTS.md](../../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.py` as 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.
|