Documentation
¶
Overview ¶
Package execution implements the PR-11 guarded Model-A execution path — the runtime.ExecutionProvider that turns the decision-only Gateway into a bounded, rollout-mode-gated executor. It is wired ONLY for the Gateway capability and ONLY when rollout distribution arms it (disabled by default). It composes the existing engines without weakening any of them: rollout (mode/scope/hard-failure), the PR-6 policy decision (already computed), PR-7 inspection/DLP for the response, the PR-4 credential broker (materialization with no token passthrough), the PR-8 events manager (commit-before-side-effect), and the PR-11 upstream client.
Index ¶
- func ReconcileOrphan(ctx context.Context, w Witness, orphan RecoveredAttempt, ...) (model.ReconciliationEvidence, error)
- func ReconcileTransitionAllowed(from, to model.ReconciliationResult) bool
- func SetIngestGuard(fn func(ingest func() error) error)
- func SetReconcileHook(fn func())
- type AttemptState
- type CanarySafety
- type Config
- type CredentialPlanner
- type Discovery
- type EvidenceReader
- type Executor
- func (e *Executor) Execute(ctx context.Context, in runtime.ExecInput, res rollout.Resolution) runtime.ExecOutput
- func (e *Executor) KillActive() bool
- func (e *Executor) ReconcileAndReport(ctx context.Context, w Witness, orphan RecoveredAttempt, ...) (model.ReconciliationEvidence, error)
- func (e *Executor) Resolve(in runtime.ExecInput) rollout.Resolution
- type LiveExecutionGate
- type LiveGateDecision
- type LiveGateInput
- type Metrics
- type RecoveredAttempt
- type RecoveryReport
- type ReservationBreach
- type ShadowConfig
- type ShadowDecision
- type ShadowEvaluator
- type ShadowOutcome
- type UpstreamCaller
- type Witness
- type WitnessObservation
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ReconcileOrphan ¶ added in v1.0.218
func ReconcileOrphan(ctx context.Context, w Witness, orphan RecoveredAttempt, expectServer, expectMethod string, now int64) (model.ReconciliationEvidence, error)
ReconcileOrphan derives authoritative knowledge about one recovered orphan.
It never manufactures certainty. A witness that is unavailable, times out, returns malformed data, or cannot prove completeness leaves the attempt at reconciliation_required — the resting state — rather than producing a verdict the evidence does not support.
func ReconcileTransitionAllowed ¶ added in v1.0.218
func ReconcileTransitionAllowed(from, to model.ReconciliationResult) bool
ReconcileTransitionAllowed pins the state machine (§9). Reconciliation only ever moves an attempt from reconciliation_required to a knowledge state, and there is NO transition back to executable — the recovery/reconciliation surfaces expose no execution capability at all.
func SetIngestGuard ¶ added in v1.0.218
SetIngestGuard installs the default ingest-serialization guard used by NewDiscovery. The composition root calls it once at startup; nil clears it (tests). It keeps this package decoupled from tool trust — it only ever runs the ingest it is handed inside whatever critical section the composition root wraps it in, and that gating can never widen usability.
func SetReconcileHook ¶ added in v1.0.218
func SetReconcileHook(fn func())
SetReconcileHook installs the default post-ingest reconcile callback used by NewDiscovery. The composition root calls it once at startup; nil clears it (tests). It keeps this package decoupled from tool trust — it only ever invokes the func it is handed, and that func can only withdraw-or-re-affirm trust, never widen usability.
Types ¶
type AttemptState ¶ added in v1.0.218
type AttemptState string
AttemptState is the derived disposition of one attempt as the durable ledger describes it.
const ( // AttemptReconciliationRequired — an intent with no valid terminal outcome. Only // an authoritative independent witness can resolve it. AttemptReconciliationRequired AttemptState = "reconciliation_required" // AttemptSettled — an intent with exactly one valid, consistent terminal outcome. AttemptSettled AttemptState = "settled" )
type CanarySafety ¶ added in v1.0.218
type CanarySafety interface {
// Breach reports that an authoritative whole-Canary breach OCCURRED. code is a
// canary.AbortConditions() taxonomy code; an unrecognised code fails closed to a
// whole-Canary latch at the controller, so a typo can only ever stop the experiment, never
// silently continue it.
Breach(capability string, gen uint64, code string)
// AttemptSettled reports ONE settled post-admission attempt so the composition layer's
// population detectors (elevated error rate, latency pathology) can judge. failed is an
// ordinary execution failure; latency is the observed attempt duration.
//
// The engine reports only ADMITTED attempts that settled. It does NOT report request-scoped
// denials — a policy deny, a scope refusal, an allowance already consumed — because those are
// what a healthy Canary does all day, and counting them would let a Canary abort itself for
// correctly refusing requests. Conditions carrying their own immediate whole-Canary
// classification are likewise not laundered through a rate: they trip directly.
AttemptSettled(capability string, gen uint64, failed bool, latency time.Duration)
}
CanarySafety receives authoritative safety facts from the execution engine.
type Config ¶
type Config struct {
// State is the capability-local rollout state (Gateway). Required.
State *rollout.State
// Broker materializes the approved-server credential (nil ⇒ no credential is
// attached; the upstream call carries no Authorization).
Broker *broker.Broker
// Events is the PR-8 durable-event manager. Required for the commit-before-
// side-effect guarantee; a nil Events fails every execution closed.
Events *events.Manager
// Upstream is the bounded upstream MCP client. Required.
Upstream UpstreamCaller
// ResponseProfile is the PR-7 inspection profile used to inspect + DLP the
// upstream response before returning it to the client.
ResponseProfile inspection.Profile
// Metrics is the optional rollout telemetry sink.
Metrics Metrics
// Clock is injected for tests; nil ⇒ time.Now.
Clock func() time.Time
// Actor labels events emitted by this executor.
Actor string
// LiveGate is the OPTIONAL composition-layer side-effect gate consulted at the boundary
// BEFORE the executor's own tool-freshness + emergency-kill re-check, so the kill re-read
// stays the LAST authoritative check before Upstream.Call (PREREQ-MCP-KILL-1). It owns the
// gates that live OUTSIDE this package — Canary blast-radius budget reservation, runtime
// live-execution trust revalidation, and read-first enforcement. nil ⇒ the executor is
// byte-identical to the pre-gate path (the ShadowEvaluator and any non-live composition
// never set it). See livegate.go.
LiveGate LiveExecutionGate
// Safety is the OPTIONAL narrow whole-Canary breach seam (blocker #7). Nil means no Canary is
// composed; it is replaced by a no-op so call sites never branch. It is deliberately separate
// from Metrics — see safety.go for why observability and control must not share a sink.
Safety CanarySafety
}
Config wires an Executor for the Gateway capability.
type CredentialPlanner ¶ added in v1.0.218
type CredentialPlanner interface {
Plan(broker.PlanInput) (broker.CredentialPlan, error)
}
CredentialPlanner is the NARROW, plan-only credential capability a caller supplies to a Shadow evaluator. Plan is metadata-only: no provider call, no cache decrypt, no secret; it deliberately does NOT expose Materialize. *broker.Broker satisfies it. NOTE: this interface is the CALLER-FACING config type only — a ShadowEvaluator does NOT retain the planner as an interface value (which would keep the concrete broker recoverable by a type assertion). NewShadowEvaluator extracts the bound `Plan` METHOD VALUE and drops the interface (see the `plan` field + `NewShadowEvaluator`), so the materialize-capable concrete value is genuinely unreachable from the stored evaluator, not merely un-called (SEC — Codex P2 on PR #1226).
type Discovery ¶
type Discovery struct {
Registry *registry.Registry
Catalog *catalog.Catalog
Upstream UpstreamCaller
// OnIngest, when set, is invoked after a SUCCESSFUL catalog ingest (a new snapshot was
// published). Ingestion can only ever land a tool Quarantined/ReviewRequired — it never
// produces catalog.Usable — so a re-discovered tool that exactly matches an active trust
// approval would otherwise stay non-Usable until the next inventory read, Shadow
// preflight, or the periodic 30s reconcile tick, contaminating an in-flight Shadow
// experiment. The composition root wires this to the tool-trust reconcile hook so the
// approval's projection is re-materialized immediately. Optional and nil-safe; it must
// only WITHDRAW-or-re-affirm trust (it can never widen usability), so calling it here is
// safe even though this package holds no trust authority.
OnIngest func()
// IngestGuard, when set, runs the catalog ingest (its snapshot PUBLISH) inside the
// tool-trust reconcile critical section so a catalog revision advance is MUTUALLY
// EXCLUSIVE with an in-flight trust approval. Without it, ingestion advances the live
// catalog revision without holding the coordinator's derive lock, so an approval that
// captured the pre-advance revision between its target load and its durable commit would
// validate a stale copy — an identical rediscovery (same fingerprint, bumped revision) or
// an F1→F2→F1 flap in that window would be approved instead of returning the required
// stale-target conflict (ADR-0034 optimistic concurrency, PR round-15). Optional and
// nil-safe; when nil the ingest runs directly. Like OnIngest it only ever gates WHEN a
// publish lands, never what a publish produces, so carrying it grants this package no
// trust authority.
IngestGuard func(ingest func() error) error
}
Discovery performs a real upstream tools/list against a registered server and feeds the result through the PR-2 catalog ingestion path (fingerprints → drift classification → quarantine of unknown/expanded tools). It reuses the PR-1 kernel on the upstream leg (via the upstream client's strict decode) and never auto-approves a new or changed fingerprint. A discovery failure returns a classified error and leaves the previous known-good catalog snapshot UNCHANGED.
func NewDiscovery ¶
func NewDiscovery(reg *registry.Registry, cat *catalog.Catalog, up UpstreamCaller) (*Discovery, error)
NewDiscovery constructs a Discovery. It fails closed on missing collaborators and installs the default post-ingest reconcile hook (SetReconcileHook) plus the ingest-serialization guard (SetIngestGuard) so the ingest path reconciles trust and serializes its publish with in-flight approvals without the caller having to wire OnIngest / IngestGuard itself.
func (*Discovery) Discover ¶
Discover runs the ordered discovery sequence for one registered server:
- resolve + validate the server registration + trusted identity;
- fetch a bounded tools/list via the upstream client (strict shared-kernel decode of the response);
- feed the exact result bytes into the PR-2 catalog ingestion path;
- drift classification + quarantine happen inside Ingest (unknown/expanded fingerprints land Quarantined and never auto-clear);
- on ANY failure, the previous catalog snapshot is retained unchanged.
It returns the ingestion Report (safe drift/quarantine evidence) on success.
type EvidenceReader ¶ added in v1.0.218
type EvidenceReader interface {
CommittedForExport(part model.Partition, afterSeq uint64, maxRecords int) ([]model.Event, []uint64, uint64, error)
}
EvidenceReader is the narrow read seam over the authoritative committed event stream. *spool.Spool satisfies it; tests supply deterministic fixtures.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor implements runtime.ExecutionProvider. It is the LIVE object: it possesses the upstream client and the materialize-capable broker and is composed ONLY for Canary/Production (both prohibited today). Its Shadow-fallback disposition (out-of-scope Canary → Shadow) is delegated to a distinct, capability-reduced *ShadowEvaluator so the shadow path never touches this object's live capabilities.
func (*Executor) Execute ¶
func (e *Executor) Execute(ctx context.Context, in runtime.ExecInput, res rollout.Resolution) runtime.ExecOutput
Execute is the runtime.ExecutionProvider entry. It acts on the PRE-RESOLVED mode/scope disposition (it never re-resolves mode or scope — F7 single resolution, Codex P2 #1234) and dispatches record-only / block / execute.
It re-reads ONLY the emergency kill: the kill switch is an immediate admission stop, so a kill engaged AFTER Resolve but before the irreversible upstream call must still stop it. This is orthogonal to single-resolution — it reads only the monotonic kill flag and can only make the outcome MORE restrictive (an emergency block), so it cannot reopen the routing TOCTOU F7 closed. Fail-closed here matters most on the LIVE path: it stops an upstream side effect that Resolve had cleared microseconds before the operator hit kill.
func (*Executor) KillActive ¶ added in v1.0.218
KillActive implements runtime.ExecutionProvider: it reports whether this capability's emergency kill switch is engaged, for the runtime's record-only fall-through re-check.
func (*Executor) ReconcileAndReport ¶ added in v1.0.218
func (e *Executor) ReconcileAndReport(ctx context.Context, w Witness, orphan RecoveredAttempt, expectServer, expectMethod string, capability string, now int64, ) (model.ReconciliationEvidence, error)
ReconcileAndReport runs ReconcileOrphan and routes an AUTHORITATIVE physical-effect contradiction to the whole-Canary breach seam (First Controlled Canary review, blocker #7 §10).
This is the load-bearing path the review requires: when reconciliation eventually says the world disagrees with Culvert's own record of what it did — a duplicated attempt, a reservation reused, a binding that does not match the intent — that verdict must already be able to stop the experiment, not merely be written down. All three of those shapes arrive here as ReconConflict; the #1306 derivation folds a >1 observation count and a mismatched binding into exactly that verdict, so conflict is the single signal to act on and there is no second classification to keep in sync.
It does NOT create a production witness. With no witness wired, ReconcileOrphan returns reconciliation_required and nothing trips — the shipped posture, and blocker #1/#8's to change. What this closes is the gap where the signal existed and had nowhere to go.
func (*Executor) Resolve ¶ added in v1.0.218
func (e *Executor) Resolve(in runtime.ExecInput) rollout.Resolution
Resolve implements runtime.ExecutionProvider: it resolves the effective rollout disposition for this request EXACTLY ONCE (no side effect), so routing and execution use the same snapshot. A killed capability resolves to an emergency block.
type LiveExecutionGate ¶ added in v1.0.218
type LiveExecutionGate interface {
// AdmitSideEffect decides whether this request may cross the irreversible upstream
// boundary. It performs NO upstream call and NO credential materialization.
AdmitSideEffect(in LiveGateInput) LiveGateDecision
}
LiveExecutionGate is the OPTIONAL composition-layer gate the live Executor consults at the side-effect boundary. It exists so the gates that must live OUTSIDE this package — Canary blast-radius BUDGET reservation, runtime live-execution TRUST revalidation, and READ-FIRST operation enforcement — can run immediately before the irreversible upstream call WITHOUT this package importing the composition layer (rollout-runtime / tooltrust / canary singletons).
PLACEMENT IS A SAFETY INVARIANT. The executor invokes the gate at the TOP of callUpstream, BEFORE preCallGuard's tool-freshness + emergency-kill re-check, so the kill re-read remains the LAST authoritative check before Upstream.Call (PREREQ-MCP-KILL-1): nothing the gate does (which may block on a durable budget persist) sits between the kill re-read and the side effect. A gate DENIAL therefore fails the execution closed and Upstream.Call is never reached; a gate ADMIT returns a Release the executor runs exactly once after the upstream leg (success, failure, or a later boundary refusal), so a reserved concurrency slot is never leaked — including the §11 case where a subsequent kill/freshness abort occurs AFTER the gate admitted (and after any credential materialization).
The gate is nil in every non-live composition (the ShadowEvaluator has no LiveGate, and the disabled-by-default build composes no live executor), so the executor is byte-identical to the pre-gate path when it is unset.
type LiveGateDecision ¶ added in v1.0.218
type LiveGateDecision struct {
Admit bool
Reason mcperr.Reason
// Revalidate (non-nil only when Admit) is a FINAL-BOUNDARY re-check the executor runs inside
// preCallGuard, immediately before the emergency-kill re-read. It returns false when the
// activation this request was admitted under is no longer current — e.g. a concurrent Canary
// demotion invalidated the reserved generation AFTER admission but BEFORE the irreversible call.
// Because the admission-time reservation cannot see a later demotion, and preCallGuard's kill
// re-read does not consult the Canary generation, WITHOUT this an already-admitted request could
// still reach the upstream after a leaving-live transition returned success (Codex P1 round-8,
// PR #1290). It is a composition-layer concern (the generation lives in the canary runtime), so it
// enters this package only as an injected predicate — the executor stays generic and byte-identical
// when the gate (or Revalidate) is nil.
Revalidate func() bool
Release func()
// ReservationID (set only when Admit) names the budget slot this side effect was
// authorized against. It binds a physical attempt to the reservation that paid
// for it, so an effect can never be attributed to an unauthorized slot and an
// orphan can be traced back to the exact grant. An empty value is tolerated:
// gates that do not meter (nil/legacy) keep the executor byte-identical.
ReservationID string
// ActivationGeneration (set only when Admit) is the Canary activation generation
// in force at admission. It is recorded on the attempt so an orphan from a
// superseded generation stays recognizable after a restart and can never be
// mistaken for fresh execution allowance. Zero when the gate does not meter.
ActivationGeneration uint64
}
LiveGateDecision is the gate's verdict. Admit==false fails closed with Reason and Upstream.Call is never reached. Release (non-nil only when Admit) is run exactly once after the upstream leg.
type LiveGateInput ¶ added in v1.0.218
type LiveGateInput struct {
Capability protocol.Capability
// Operation is the policy-engine operation class (read-first is decided from THIS, never
// the server-provided readOnlyHint).
Operation policy.OperationClass
// Tenant / Principal identify the authenticated subject for the blast-radius ceilings.
Tenant string
Principal string
// ServerID / ToolName / Fingerprint are the exact reviewed target the live-trust
// revalidation binds against (Fingerprint is the hex composite fingerprint the decision
// was computed against; tool freshness separately proves it still matches the live tool).
ServerID string
ToolName string
Fingerprint string
Now time.Time
}
LiveGateInput carries the authoritative, already-resolved facts the composition-layer gate needs. Every field derives from the pre-resolved decision (never a request-supplied claim): the executor builds it from runtime.ExecInput at the boundary.
type Metrics ¶
type Metrics interface {
ObserveResolution(capability string, res rollout.Resolution)
ObserveBlock(capability string, reason mcperr.Reason)
ObserveExecution(capability string, ok bool)
ObserveUpstream(capability string, outcome string)
ObserveDLPBlock(capability string, response bool)
// ObserveOutcomeEvidenceLoss records that a post-execution outcome event could
// not be committed. The side effect already happened, so this is the archive
// losing its record of it — best-effort must mean "does not block the response",
// never "fails invisibly".
ObserveOutcomeEvidenceLoss(capability string)
// ObserveShadowOutcome records ONE non-executing Shadow evaluation and its formal
// Model-1 verdict. outcome is a ShadowOutcome value (a bounded, low-cardinality enum:
// would_execute / would_block / would_require_* / would_fail_*) — never a tenant,
// subject, tool, or argument. It is emitted only by the ShadowEvaluator; the live
// executor never calls it.
ObserveShadowOutcome(capability string, outcome string)
}
Metrics is the optional bounded, low-cardinality rollout telemetry sink. All labels are bounded enums (capability, disposition, reason code, hard class) — never a tenant, subject, tool argument, URL, or token.
type RecoveredAttempt ¶ added in v1.0.218
type RecoveredAttempt struct {
AttemptID string
ReservationID string
ActivationGeneration uint64
State AttemptState
// TerminalSendState is the committed physical-send state when State is
// AttemptSettled, and the zero value otherwise. It is never synthesized for an
// orphan.
TerminalSendState model.PhysicalSendState
// Reconciliation is the DERIVED knowledge from append-only witness evidence. It
// is ReconRequired when no witness has answered — for an orphan AND for a settled
// attempt, since a settled attempt whose send state is may_have_been_sent still
// has an open question. It never changes execution authority — only what is known.
Reconciliation model.ReconciliationResult
}
RecoveredAttempt is the reconstructed identity of one attempt. Every field comes from the durable intent; recovery NEVER mints a new AttemptID, because a new identity would be unmatchable against a witness that recorded the original.
func (RecoveredAttempt) NeedsReconciliation ¶ added in v1.0.218
func (a RecoveredAttempt) NeedsReconciliation() bool
NeedsReconciliation reports whether an independent witness could still change what is known about this attempt.
It is deliberately NOT "State == AttemptReconciliationRequired". A terminal outcome makes an attempt SETTLED as to execution authority, but says nothing about whether its physical fate is known: an upstream POST that ended without a response settles as may_have_been_sent, which is the single most important case a witness exists to resolve, and gating on State alone made it permanently unreconcilable (Codex round 8, P1).
The converse matters just as much. Once a witness has RESOLVED an attempt, asking again can only move knowledge backwards: a witness outage would answer reconciliation_required, and the append-only ledger correctly refuses that downgrade — so the query itself would turn a healthy resolved attempt into a recovery failure. Resolved knowledge is therefore final here.
type RecoveryReport ¶ added in v1.0.218
type RecoveryReport struct {
// Orphans require reconciliation. They consume their original allowance
// permanently and are never re-executed.
Orphans []RecoveredAttempt
// Settled attempts reached a valid terminal outcome.
Settled []RecoveredAttempt
// ReservationBreaches names every slot bound to more than one attempt. A
// non-empty slice means N accepted reservations produced more than N potential
// physical invocations.
//
// Surfacing this was a red-team finding. Recovery previously listed such
// attempts individually — VISIBLE, but not the same as DETECTED: nothing
// distinguished "two attempts" from "two attempts that one slot paid for".
ReservationBreaches []ReservationBreach
}
RecoveryReport is the result of one derivation over the durable stream.
func RecoverAttempts ¶ added in v1.0.218
func RecoverAttempts(r EvidenceReader) (RecoveryReport, error)
RecoverAttempts derives every attempt's disposition from the durable event stream.
It FAILS CLOSED on any ambiguity rather than choosing the newest record. A ledger that describes one attempt two different ways is not a ledger to pick a winner from — the ambiguity is itself unsafe evidence, and silently resolving it is how a duplicate physical effect would disappear from the record.
func (RecoveryReport) HasReservationBreach ¶ added in v1.0.218
func (r RecoveryReport) HasReservationBreach() bool
HasReservationBreach reports whether any slot authorized more than one attempt.
type ReservationBreach ¶ added in v1.0.218
ReservationBreach names one budget slot that authorized MORE THAN ONE physical attempt. It is the review-blocker-#6 invariant breach expressed in the ledger:
one accepted execution reservation => at most one physical tool invocation
It is REPORTED rather than raised as an error, deliberately. Failing the whole derivation closed would leave the operator with no report at all — including no account of the very attempts that need reconciling — so the breach is named, carried alongside a usable report, and impossible to overlook.
type ShadowConfig ¶ added in v1.0.218
type ShadowConfig struct {
// State is the capability-local rollout state (Gateway). Required.
State *rollout.State
// Planner is the OPTIONAL plan-only credential capability. nil ⇒ credential
// readiness is reported as not-evaluated (a Shadow evaluator is constructible with
// no credential capability at all).
Planner CredentialPlanner
// Events is the durable-event manager. Required: evidence-before-report is
// mandatory, so a nil Events fails every evaluation closed.
Events *events.Manager
// Metrics is the optional rollout telemetry sink.
Metrics Metrics
// Clock is injected for tests; nil ⇒ time.Now.
Clock func() time.Time
// Actor labels events emitted by this evaluator.
Actor string
}
ShadowConfig wires a ShadowEvaluator. It CANNOT carry an UpstreamCaller or a materialize-capable *broker.Broker — those fields do not exist on this struct. That absence is the Layer-B capability-security invariant.
type ShadowDecision ¶ added in v1.0.218
type ShadowDecision struct {
EvaluatedAction string // the raw policy action (never softened)
Outcome ShadowOutcome // Model-1 enforcement prediction
ShadowOverride bool // policy itself is restrictive (non-allow-class)
CredentialPlan string // credential_plan_valid / _invalid / no_credential_profile
MaterializeReady string // always not_evaluated (§12 — Shadow never materializes)
RequestInspection string // would_pass / would_fail (from the pre-executor hard-fail source)
ResponseInspection string // always not_evaluated (§13 — no upstream response exists)
}
ShadowDecision is the structured, truthful verdict recorded for a Shadow evaluation. It preserves the policy verdict (EvaluatedAction) SEPARATELY from the enforcement prediction (Outcome), so a DENY / REQUIRE_APPROVAL / REQUIRE_CONFIRMATION is never laundered into a plain WOULD_EXECUTE.
type ShadowEvaluator ¶ added in v1.0.218
type ShadowEvaluator struct {
// contains filtered or unexported fields
}
ShadowEvaluator is the non-executing Shadow capability object. It implements runtime.ExecutionProvider, but holds no upstream client and no materialize-capable broker, so it cannot perform an upstream call or materialize a credential. A request that resolves to EffectExecute (impossible in Shadow mode) fails CLOSED here.
func NewShadowEvaluator ¶ added in v1.0.218
func NewShadowEvaluator(cfg ShadowConfig) (*ShadowEvaluator, error)
NewShadowEvaluator constructs a Shadow evaluator. Note what it does NOT require: no upstream client, no materializing broker. A Shadow evaluator is fully constructible with neither. It NARROWS the supplied planner to its Plan method value and drops the interface, so no materialize-capable concrete value is retained (Codex P2).
func (*ShadowEvaluator) Execute ¶ added in v1.0.218
func (s *ShadowEvaluator) Execute(ctx context.Context, in runtime.ExecInput, res rollout.Resolution) runtime.ExecOutput
Execute is the runtime.ExecutionProvider entry for a Shadow-only runtime. It acts on the PRE-RESOLVED mode/scope disposition — it never re-resolves mode or scope (F7 single resolution, Codex P2 #1234) — and dispatches. It has no execute path.
The one thing it DOES re-read is the emergency kill: the kill switch is an immediate admission stop (admin surface + runbook contract), so a kill engaged AFTER Resolve but before this evaluation commits must still stop it — otherwise the evaluator would commit durable evidence and return a would_* verdict AFTER the operator's emergency stop. This is orthogonal to single-resolution: it reads only the monotonic kill flag and can only make the outcome MORE restrictive (an emergency block), never turn a record-only into an evaluation or an evaluation into an execute, so it cannot reopen the routing TOCTOU that F7 closed (Codex P2, PR #1234).
func (*ShadowEvaluator) KillActive ¶ added in v1.0.218
func (s *ShadowEvaluator) KillActive() bool
KillActive implements runtime.ExecutionProvider: it reports whether this capability's emergency kill switch is engaged, for the runtime's record-only fall-through re-check.
func (*ShadowEvaluator) Resolve ¶ added in v1.0.218
func (s *ShadowEvaluator) Resolve(in runtime.ExecInput) rollout.Resolution
Resolve implements runtime.ExecutionProvider: it resolves the rollout disposition for this request EXACTLY ONCE (no side effect) so the runtime can route on it and hand the SAME resolution back to Execute. A killed capability resolves to an emergency block (never record-only), so the runtime routes it to Execute rather than its inline path.
type ShadowOutcome ¶ added in v1.0.218
type ShadowOutcome string
ShadowOutcome is the formal Model-1 Shadow verdict: what a fully-enforcing mode (Canary/Production) WOULD do with this request, computed without executing it. It is never a permissive-passthrough label — a policy DENY is WOULD_BLOCK, never WOULD_EXECUTE (SH-INV, §8 of the phase brief).
const ( ShadowWouldExecute ShadowOutcome = "would_execute" ShadowWouldBlock ShadowOutcome = "would_block" ShadowWouldRequireApproval ShadowOutcome = "would_require_approval" ShadowWouldRequireConfirmation ShadowOutcome = "would_require_confirmation" ShadowWouldFailCredentialReadiness ShadowOutcome = "would_fail_credential_readiness" ShadowWouldFailInspection ShadowOutcome = "would_fail_inspection" ShadowWouldFailStaleDecision ShadowOutcome = "would_fail_stale_decision" ShadowWouldFailHardControl ShadowOutcome = "would_fail_hard_control" )
The bounded set of Model-1 Shadow outcomes. Each names what a fully-enforcing mode would do at the pre-side-effect boundary; the differential equivalence test pins them to the live executor's decision.
type UpstreamCaller ¶
type UpstreamCaller interface {
Call(ctx context.Context, target upstreamclient.Target, method string, params json.RawMessage, opts upstreamclient.CallOptions) (*upstreamclient.Response, error)
}
UpstreamCaller is the injected upstream client (interface for testability).
type Witness ¶ added in v1.0.218
type Witness interface {
LookupAttempt(ctx context.Context, attemptID string) (WitnessObservation, error)
}
Witness is the narrow authoritative lookup seam. The PRODUCTION implementation belongs to the controlled-upstream work (review blocker #1) and is deliberately unwired here; this PR establishes the contract and proves it against controlled infrastructure.
type WitnessObservation ¶ added in v1.0.218
type WitnessObservation struct {
// Count is the number of physical invocations the witness recorded for this
// AttemptID. Retained verbatim: >1 is a physical-effect breach, not a duplicate
// to be normalized away.
Count int
// Complete reports whether the witness can PROVE its observation set is complete
// for the relevant interval. Absence from an incomplete log proves nothing.
Complete bool
// CompletenessWatermark is the evidence backing Complete — a durable monotonic
// watermark, closed window, or authoritative sequence boundary. Required for a
// definitive "not received".
CompletenessWatermark string
// Binding metadata the witness observed, used to confirm the observation really
// belongs to this attempt rather than merely sharing its id.
ServerID string
Method string
ReservationID string
// Source identifies the witness, so an audit can tell whose evidence this was.
Source string
// WindowStartUnixNano / WindowEndUnixNano bound the observed interval.
WindowStartUnixNano int64
WindowEndUnixNano int64
ObservedAtUnixNano int64
// EvidenceDigest is a bounded, non-reversible reference to the underlying record.
EvidenceDigest string
}
WitnessObservation is what an independently controlled witness REPORTS. It is facts only: how many matching invocations it saw, what it can prove about the completeness of its own view, and what binding metadata it recorded. It carries no verdict, because a verdict from the witness would be an assertion this engine must not simply trust.