ContractRefine Configuration

ContractRefine (Architecture → ContractRefine) exposes one operator-facing config knob today. The C05 PR (#1025) ships the Go-level ServerOptions field; the runtime config-file binding lands in a downstream wiring PR.

runtime.contract_refine.mode

Controls the operational stance of the ContractRefine HTTP endpoints (POST /v1/preflight, GET /v1/preflight/{id}) and — once C08 lands — the /v1/tool binding- consumption surface.

Value

/v1/preflight

/v1/tool binding-mismatch

Default

disabled

503

header ignored

explicit opt-out only

advisory

returns decision

emits autonomy.decision deny WAL frame; request continues to the policy layer (no 403)

yes (post-C12 #1032)

enforce

returns decision

mismatch denies with contract: preflight_binding_mismatch

GA after future soak

The post-C12 default is advisory: an upgraded runtime that has not explicitly set the field gets binding-layer signals in its WAL but no wire-level enforcement. Operators who want the pre-C12 silent posture set mode: disabled explicitly — the value is preserved when set; only the unset/zero case changed. See INV-CR-1 backward-compat.

Typo handling. A non-empty but unrecognized value (e.g. advsiory, enfroce) falls back to disabled (safe — never silently enable the /v1/preflight surface on garbage input) and the runtime logs a startup warning naming the unrecognized value. This mirrors the attestation ladder’s ParseEnforcementMode shape so operators see one stable pattern across runtime config knobs.

Go-level field

The runtime API surface is runtime.ServerOptions.ContractRefineMode (type runtime.ContractRefineMode). The constants are:

const (
    ContractRefineDisabled ContractRefineMode = "disabled"
    ContractRefineAdvisory ContractRefineMode = "advisory"
    ContractRefineEnforce  ContractRefineMode = "enforce"
)

Empty string is treated as ContractRefineAdvisory (post-C12 #1032 default flip; the unset case is the post-soak default). A non-empty unrecognized value falls back to ContractRefineDisabled and the runtime logs a startup warning — typos do not silently enable a new HTTP surface.

Verifier wiring

The mode knob is necessary but not sufficient. Operators that set mode to advisory or enforce MUST also wire a ContractVerifier on ServerOptions:

runtime.ServerOptions{
    ContractRefineMode: runtime.ContractRefineAdvisory,
    ContractVerifier: &contract.DefaultVerifier{
        // resolvers per deployment...
    },
}

A nil ContractVerifier with mode set to advisory or enforce returns 503 on every /v1/preflight call AND skips the /v1/tool binding ladder (INV-CR-2 fail-closed). The two surfaces are jointly gated on verifier presence so a partial wiring never emits contract: preflight_required denies operators cannot satisfy.

Default introspectors (#1054 F1)

autonomy runtime start wires a DefaultVerifier whose PlanIntrospector matches what the runtime can actually execute, so the published plan_shape contract isn’t quietly weakened:

&contract.DefaultVerifier{
    Plan:      runtime.KindPlan{},      // delegates to runtime.IsKnownToolKind
    Zone:      contract.PermissiveZone{},      // no zone schema in Phase 1
    PolicyRef: policyRef,
}

Type

Where

Behavior

Production-suitable?

runtime.KindPlan{}

runtime

Delegates to runtime.IsKnownToolKind, which mirrors executeTool’s dispatch switch. Hallucinated or empty kinds fail plan_shape.

Yes — this is the production default.

contract.PermissiveZone{}

runtime/contract

Accepts every (capability, zone) pair. Truthful default for Phase 1 (no zone schema). ROSOrin (#793) substitutes a concrete ZoneIntrospector later.

Yes — until a zone schema exists, this is the honest default.

contract.StaticPlan{Kinds: ...}

runtime/contract

Operator-supplied allowlist. Nil/empty Kinds map rejects every kind (fail-closed default — INV-CR-2 posture).

Yes — for deployments that want a static catalog over the dispatch table.

contract.PermissivePlan{}

runtime/contract

Accepts every kind including "" and arbitrary unknown strings.

No — DO NOT WIRE in production. Use only in tests or scenarios where kind enforcement is intentionally disabled. The original #1054 F1 commit wired this on runtime start and was reverted under #1055 review fix1.

Operators who want stricter introspection construct their own ContractVerifier and pass it via ServerOptions.ContractVerifier, which the runtime honors over the default.

Verifier-side knobs

The contract.DefaultVerifier struct exposes further knobs the operator can tune per deployment:

  • BindingWindow — lifetime of ExecutionEligibility.ExpiresAt. Zero defaults to DefaultBindingWindow = 5 * time.Minute.

  • RepairAttempts — bounded-attempts ceiling (INV-CR-6). Zero defaults to DefaultRepairAttempts = 1. Clamped to MaxRepairAttempts = 3.

  • SessionsSessionStore for the bounded-attempts counter. Nil means the verifier is stateless (every call counts as attempt 1); wire an InMemorySessionStore{} (or a future persistent store) to enforce the counter across calls.

See runtime/contract/ for the full surface.