Skip to content

symfonic.kernel.compiler

compiler

The invocation compiler: one plan, compiled once, before any effect (IPL-1).

The compile sequence is fixed and total โ€” accept, validate, resolve, order, freeze, diagnose โ€” and every failure in it raises ConfigurationError with nothing executed. That guarantee is the reason this module imports no I/O: an effect-free compiler is what makes "no effect has executed" a fact rather than an intention, and what makes a plan compilable in a test without a live world.

There is exactly one public compile entry point. A second one would be IPL-2's forbidden second pipeline, however reasonable its motivation looked.

CompileRequest dataclass

CompileRequest(config_digest: str, scope: RequestScope = RequestScope(), model: ModelResolution = ModelResolution(), instructions: str | None = None, tools: Sequence[ToolDescriptor] = (), stages: Sequence[StageDescriptor] = (), bindings: ServiceBindings = ServiceBindings(), effect_grants: frozenset[str] = frozenset(), limits: PlanLimits = PlanLimits(), event_program: EventProgram = EventProgram(), capabilities: Sequence[str] = ())

The compiler's only accepted input (IPL-1 step 1).

It is a normalized value, never raw adopter configuration: re-parsing legacy input inside the compiler is how a second, subtly different interpretation of a config file gets born.

compile_invocation_plan

compile_invocation_plan(request: CompileRequest) -> InvocationPlan

Compile one immutable, stateless plan. Performs no effect of any kind.

Source code in src/symfonic/kernel/compiler.py
def compile_invocation_plan(request: CompileRequest) -> InvocationPlan:
    """Compile one immutable, stateless plan. Performs no effect of any kind."""
    records: list[DiagnosticRecord] = list(_input_records(request))

    # The third gate. A stage's declaration and a capability's grant are each
    # checked at construction; this is where the *invocation's* grants land --
    # today the facade's baseline, and the seam an adopter-supplied grant will
    # arrive through. Checked here so an unknown family cannot enter a plan at
    # all, rather than being caught only if some stage happens to want it.
    require_known_families(request.effect_grants, subject="the invocation")
    validate_event_program(request.event_program)
    validate_limits(request.limits)
    validate_bindings(request.bindings)
    model = _resolve_model(request.model)
    tool_manifest = _resolve_tools(request.tools, records)
    stage_program, stage_records = _resolve_stages(request, records)
    records.extend(stage_records)
    records.extend(_binding_records(request.bindings))
    records.extend(_grant_records(request))

    return InvocationPlan(
        identity=PlanIdentity(
            plan_id=uuid.uuid4().hex,
            config_digest=request.config_digest,
            compiled_at=time.time(),
            schema_version=SCHEMA_VERSION,
        ),
        scope=request.scope,
        model=model,
        tool_manifest=tool_manifest,
        stage_program=stage_program,
        bindings=request.bindings,
        effect_grants=frozenset(request.effect_grants),
        limits=request.limits,
        event_program=request.event_program,
        diagnostics=PlanDiagnostics(records=tuple(records)),
    )

derive_child_plan

derive_child_plan(parent: InvocationPlan, request: CompileRequest) -> InvocationPlan

Compile a child plan by narrowing the parent (IPL-6).

Every widening attempt is rejected here rather than at the point of use. A sub-agent that could add a tool, a grant, a tenant or a token budget its parent did not have would make the parent's plan a suggestion.

Source code in src/symfonic/kernel/compiler.py
def derive_child_plan(parent: InvocationPlan, request: CompileRequest) -> InvocationPlan:
    """Compile a child plan by narrowing the parent (IPL-6).

    Every widening attempt is rejected here rather than at the point of use.
    A sub-agent that could add a tool, a grant, a tenant or a token budget its
    parent did not have would make the parent's plan a suggestion.
    """
    child = compile_invocation_plan(request)
    _require_narrowing(parent, child)
    return InvocationPlan(
        identity=PlanIdentity(
            plan_id=child.identity.plan_id,
            config_digest=child.identity.config_digest,
            compiled_at=child.identity.compiled_at,
            schema_version=child.identity.schema_version,
            parent_plan_id=parent.identity.plan_id,
        ),
        scope=child.scope,
        model=child.model,
        tool_manifest=child.tool_manifest,
        stage_program=child.stage_program,
        bindings=child.bindings,
        effect_grants=child.effect_grants,
        limits=child.limits,
        event_program=child.event_program,
        diagnostics=child.diagnostics,
    )