symfonic.kernel.contracts.stages¶
stages ¶
The eight-phase ladder and the stage descriptors that populate it (STG).
A capability declares what it contributes and what it must sit beside; it never declares a position. Position is computed, once, by the compiler — which is what keeps two capabilities from fighting over an ordering neither of them can see.
CompiledStage
dataclass
¶
CompiledStage(stage_id: str, phase: Phase, capability: str, priority: int, effects: frozenset[str] = frozenset(), emits: frozenset[str] = frozenset(), kind: StageKind = StageKind.COMPILATION, config: Mapping[str, Any] = (lambda: MappingProxyType({}))(), tie_break: str = 'first-in-phase')
A stage with its position decided and the reason recorded (STG-12).
Coerces kind for the same reason :class:StageDescriptor does, and it
matters more here: this is the type the dispatcher actually reads, and its
identity comparison is only valid because of a guarantee this class has to
make itself. ordering.py copies an already-coerced descriptor, so the
shipped path was safe -- but CompiledStage and StageProgram are
public, and any other construction site got the vanishing-stage defect back
with no refusal anywhere.
Phase ¶
Bases: StrEnum
The eight phases, in the only order they ever run (STG-1).
StageDescriptor
dataclass
¶
StageDescriptor(stage_id: str, phase: Phase, capability: str = _KERNEL_CAPABILITY, priority: int = 0, after: tuple[str, ...] = (), before: tuple[str, ...] = (), optional_after: tuple[str, ...] = (), optional_before: tuple[str, ...] = (), effects: frozenset[str] = frozenset(), emits: frozenset[str] = frozenset(), kind: StageKind = StageKind.COMPILATION, config: Mapping[str, Any] = (lambda: MappingProxyType({}))())
One capability's declaration of a stage it contributes (CON-C-1).
validate ¶
Shape validation, performed the moment a capability registers (STG-4).
Resolution of the constraints against the full stage set happens later, at compile time, when the whole set is finally knowable.
Source code in src/symfonic/kernel/contracts/stages.py
StageKind ¶
Bases: StrEnum
Which half of a phase a stage belongs to (STG-7, as reformulated).
Only prompt-assembly is split today, and the split is what lets an
effectful retrieval inform a prompt without the compiler losing its purity:
- resolution — may perform I/O, once per invocation, under an STG-8 grant. Produces entries in the turn's resolved-input snapshot and nothing else; it never writes the assembly.
- compilation — a pure function of
(plan, request, snapshot). No I/O, no port that performs an effect, no mutation of the snapshot.
The default is COMPILATION because a stage that declares no effect is
pure, and because every stage that existed before this distinction was one.
Declaring RESOLUTION is how a capability says "I am the one that reaches
outside", and it is checked: a compilation stage may not declare effects.