Documentation
¶
Overview ¶
Package agent provides the Scope Agent Framework execution kernel. Definition owns immutable behavior and creates serializable Execution values. Engine owns Process lifecycle, Signal delivery, Effect dispatch, child composition, resource bounds, observation, and tree recovery. Strategy payloads remain opaque to the kernel.
Execution and ownership ¶
A root tree is the consistency, commit, and recovery unit. A Process isolates lifecycle and Strategy state; a Step is the concurrency unit. Sibling Steps may run concurrently, but one tree owner revalidates and adopts their results. Independent commit throughput requires separate root trees.
Execution.Step is cancellable, discardable candidate computation. It must not perform external I/O, hide unbounded work, or start unowned goroutines. External work is declared as an Effect and performed by a Dispatcher. These are cooperation contracts, not a sandbox: cancellation cannot forcibly terminate a goroutine, and implementations must return in bounded time. Expired Step attempts are discarded, including their errors; Execution is rebuilt from the last adopted state. Before adoption, the Engine validates that the Deployment can restore the candidate's Snapshot. Definitions own state equivalence and deterministic continuation, verified by conformance cases.
Continue permits further private candidate computation. Checkpoint requires acknowledgment before another Step or Effect in that Process runs. Pending commits and freezes block work that cannot cross their boundary; inspection never drives execution. The owner services ready work in bounded scheduling turns, while the Host owns storage deadlines.
One commit protocol ¶
Every Engine requires an explicit TreeCommitter. NewMemoryTreeCommitter supplies the same compare-and-swap, writer fencing, and deduplication protocol with retention limited to that store instance's lifetime. Persistent adapters provide their own storage transactions. No backend bypasses acknowledgment. Checkpoints and Effects share an incarnation-scoped commit sequence, allocated by the Engine and atomically retained with the stored head. Equal snapshot content can recur; it never identifies a commit or permits historical replay. EffectBoundary, TreeCheckpoint, and TreeActivation derive the canonical commit Identity and ContentDigest used by both memory and persistent stores. A Host stores these Scope facts alongside its own business write set, within the same atomic head comparison. The TreeCommitter example also binds the real storage call to a Host deadline and shutdown signal.
Start commits the initial head and writer identity before publishing the Process, and every TreeSnapshot carries that identity. Engine.RestoreTree activates a new writer against the supplied head; missing heads, stale digests, and superseded writers fail.
The runtime may hold a newer private candidate than its acknowledged head. Signal admission, child publication, committed Events, terminal results, and inspection snapshots become visible only after their tree commit succeeds.
A failed acknowledgment stops the writer with a RuntimeError, without inventing a logical termination or overwriting an already published result. The error carries its last acknowledged digest and uncertain Effect identities. Recovery must load the store's authoritative head: a lost response or another writer may have advanced storage beyond this runtime's knowledge.
External Effects and uncertainty ¶
Dispatcher Effects advance in declaration order through planned, pending, and settled. The pending boundary commits the complete prospective tree before dispatch starts. Settlement commits before continuation or publication. A returned error means the external outcome is unknown; it is not definite failure. A planned Effect that never dispatched cannot become unknown. Direct-child controls perform no external I/O: recipient changes and their definite settlement share one atomic tree checkpoint.
An interrupted pending attempt may replay only when its ReplayPolicy establishes the same logical operation under the original identity. A settled Unknown requires explicit Host reconciliation or Process.ReplayUnknownEffect. The existing Unknown remains authoritative throughout replay until a definite resolution commits. Cancellation still collects already started external work. Terminal snapshots retain actual settlements, unstarted tails, and unresolved identities; restoration never silently turns uncertainty into success.
Signals, lifecycle, and bounds ¶
Signal is the only runtime input into Execution. Process.DeliverSignals atomically admits an ordered batch and charges each identity once. Reusing an identity with different immutable content conflicts. SignalReceipt exposes acknowledged admission and consumption for delivery reconciliation. Waits and child waits carry explicit identities; stale continuation input is rejected. Candidate failure cannot permanently consume an input.
Terminal intent still joins started work and required acknowledgments; a late successful child initialization joins under that intent before running a Step. Host cancellation never abandons a required acknowledgment. Terminal Unknown settlements remain evidence of remote uncertainty.
Each Process carries its own cumulative Budget; one TreeLimits per root tree owns mailbox, snapshot, and structural capacity for every member. Captured quotas survive restoration without new Engine defaults. Unlimited execution still retains history and checks numeric identity overflow; the Host chooses retention and resource policy. Default depth and active-child bounds do not bound retained tree size or lifetime work. Hosts requiring bounded lifetime growth must choose finite Steps, Effects, Signals, Process snapshot bytes, total children, tree Processes, and tree snapshot bytes for their workload. See the bounded configuration in the Definition example. These quotas do not cap the committer ledger across trees or repeated control checkpoints; storage retention has its own Host-owned lifetime.
Snapshot admission reserves bounded control and termination metadata with worst-case JSON escaping. Admission of one cut does not authorize future growth. An external result that cannot fit stops the runtime with its unresolved identity; the Host reconciles the outcome from the acknowledged stored head.
Recovery and observation ¶
TreeSnapshot is the sole recovery representation, with one strict current schema and no version envelopes, migration dispatch, or compatibility reads. ProcessSnapshot is diagnostic state, not an independent recovery unit. ExecutionState contains opaque Strategy-owned state; Hosts resolve an exact DeploymentRef rather than interpreting Strategy kinds or control flow.
Events describe attempts or committed facts; Delta is best-effort observation. Neither substitutes for an acknowledged head. Event sequences restart with each activation and do not impose ordering across writers. Wall timestamps are diagnostic, not causal proofs. EventListener and DeltaListener own their callback bounds and reentrancy rules.
Strategies ¶
Built-in strategies live under strategy/. Each concrete package implements the public execution protocol; the directory itself defines no package or runtime contract. Host-defined strategies use the same protocol.
github.com/Tangerg/scope/agent/strategy/interaction implements ReAct-style model and tool loops with working context, delegates, and artifacts. github.com/Tangerg/scope/agent/strategy/planning owns goal-driven planning; its goap subpackage supplies a quota-controlled search implementation selected by the Host. github.com/Tangerg/scope/agent/strategy/workflow implements ordered deterministic stages over a closed vocabulary, composing real child Processes. github.com/Tangerg/scope/agent/strategy/coordination composes bounded input gates, absolute deadlines, and first-success competition through the same child and wait contracts. github.com/Tangerg/scope/agent/strategy/collaboration runs coordinator turns beside background workers. Decisions choose whether to continue while workers run, wait for drained results, or complete. The Strategy retains explicit working state and its Deployment binds the coordinator and workers; the Engine starts the children from those bindings. Workflow child failures and collaboration coordinator failures preserve their original Failure kind, code, and diagnostic.
github.com/Tangerg/scope/agent/messaging delivers intermediate input through a narrow Host-authorized port, retaining the original recipient and Effect-derived Signal identity. Its sender and recipient acknowledge separately; it does not provide the direct-child control's single tree commit.
The Engine never imports or type-switches a concrete strategy. A new strategy is admitted by implementing the waist, state codec, and safe consumption boundary, and binding a dispatcher when it declares external Effects.
Host boundaries ¶
Hosts own product identity, transports, storage transactions, retention, permissions, billing, deployment catalogs, and provider selection. The kernel owns execution identities, state transitions, resource accounting, child ownership, and the commit protocol. Successive bounded root trees require a Host transaction for successor admission and input cutover; equal Start input alone does not make admission idempotent.
Database adapters belong to the consuming application or an independently owned adapter. Package agenttest supplies Definition and TreeCommitter conformance suites. Chat, tools, embeddings, history, and telemetry remain in their own modules.
Index ¶
- Constants
- Variables
- func NormalizeDiagnostic(message string) string
- func RequireContext(ctx context.Context) context.Context
- func ValidDescription(description string) bool
- func ValidDiagnostic(message string) bool
- func ValidQualifiedName(name string) bool
- type Budget
- type CallbackPanicError
- type Capability
- type CapabilitySet
- func (c CapabilitySet) Allows(requested CapabilitySet) bool
- func (c CapabilitySet) Contains(capability Capability) bool
- func (CapabilitySet) JSONSchemaAlias() any
- func (c CapabilitySet) MarshalJSON() ([]byte, error)
- func (c *CapabilitySet) UnmarshalJSON(data []byte) error
- func (c CapabilitySet) Valid() bool
- func (c CapabilitySet) Values() []Capability
- type ChildControlResult
- type ChildKey
- type ChildOutcome
- func (ChildOutcome) JSONSchemaAlias() any
- func (c ChildOutcome) MarshalJSON() ([]byte, error)
- func (c ChildOutcome) Result() Result
- func (c ChildOutcome) SubtreeResolved() bool
- func (c ChildOutcome) SubtreeUnresolvedEffects() ([]UnresolvedEffect, bool)
- func (c *ChildOutcome) UnmarshalJSON(data []byte) error
- func (c ChildOutcome) Valid() bool
- type ChildSpec
- type ChildStartResult
- func (c ChildStartResult) Failure() (Failure, bool)
- func (ChildStartResult) JSONSchemaAlias() any
- func (c ChildStartResult) MarshalJSON() ([]byte, error)
- func (c ChildStartResult) ProcessID() (ProcessID, bool)
- func (c *ChildStartResult) UnmarshalJSON(data []byte) error
- func (c ChildStartResult) Valid() bool
- type ChildWaitBoundary
- type ChildWaitCondition
- type ChildWaitOpened
- type ChildWaitSatisfied
- type ChildWaitSpec
- type ClassifiedError
- type Definition
- type Delta
- func (d Delta) AttemptID() EffectAttemptID
- func (d Delta) EffectID() EffectID
- func (d Delta) EffectSequence() uint64
- func (d Delta) EmittedAt() time.Time
- func (d Delta) MarshalJSON() ([]byte, error)
- func (d Delta) Payload() json.RawMessage
- func (d Delta) ProcessID() ProcessID
- func (d Delta) TreeIncarnationID() (TreeIncarnationID, bool)
- func (d *Delta) UnmarshalJSON(data []byte) error
- func (d Delta) Valid() bool
- type DeltaDropped
- type DeltaEmitter
- type DeltaListener
- type DeltaListenerFunc
- type Deployment
- type DeploymentConfig
- type DeploymentRef
- func (d DeploymentRef) BindingsDigest() Digest
- func (d DeploymentRef) ConfigurationDigest() Digest
- func (d DeploymentRef) ContractDigest() Digest
- func (d DeploymentRef) Digest() Digest
- func (d DeploymentRef) ImplementationDigest() Digest
- func (DeploymentRef) JSONSchemaAlias() any
- func (d DeploymentRef) MarshalJSON() ([]byte, error)
- func (d DeploymentRef) Name() string
- func (d DeploymentRef) String() string
- func (d *DeploymentRef) UnmarshalJSON(data []byte) error
- func (d DeploymentRef) Valid() bool
- type DeploymentResolver
- type Descriptor
- func (d Descriptor) DecodeOutput[T any](output Payload) (T, error)
- func (d Descriptor) Description() string
- func (d Descriptor) Digest() Digest
- func (d Descriptor) EncodeInput[T any](value T) (Payload, error)
- func (d Descriptor) InputSchema() Schema
- func (Descriptor) JSONSchemaAlias() any
- func (d Descriptor) MarshalJSON() ([]byte, error)
- func (d Descriptor) Name() string
- func (d Descriptor) OutputSchema() Schema
- func (d Descriptor) SignalSchema() Schema
- func (d *Descriptor) UnmarshalJSON(data []byte) error
- func (d Descriptor) Valid() bool
- func (d Descriptor) ValidateInput(input Payload) error
- func (d Descriptor) ValidateOutput(output Payload) error
- func (d Descriptor) ValidateSignal(input Payload) error
- type DescriptorConfig
- type Digest
- type Dispatcher
- type Effect
- func NewChildCancelEffect(childID ProcessID, reason string) (Effect, error)
- func NewChildSignalEffect(childID ProcessID, signal SignalRequest) (Effect, error)
- func NewChildStartEffect(spec ChildSpec) (Effect, error)
- func NewChildWaitEffect(spec ChildWaitSpec) (Effect, error)
- func NewDispatcherEffect(payload json.RawMessage, required ...Capability) (Effect, error)
- func NewWaitEffect(key WaitKey) (Effect, error)
- type EffectAttemptID
- type EffectBoundary
- func (e EffectBoundary) ContentDigest() (Digest, error)
- func (e EffectBoundary) Identity() string
- func (e EffectBoundary) Kind() EffectBoundaryKind
- func (e EffectBoundary) PreviousTreeDigest() Digest
- func (e EffectBoundary) Request() EffectRequest
- func (e EffectBoundary) Sequence() uint64
- func (e EffectBoundary) Settlement() (Settlement, bool)
- func (e EffectBoundary) TreeSnapshot() TreeSnapshot
- func (e EffectBoundary) Valid() bool
- type EffectBoundaryKind
- type EffectFinished
- func (e EffectFinished) AttemptID() EffectAttemptID
- func (e EffectFinished) Duration() time.Duration
- func (e EffectFinished) Failure() (FailureClassification, bool)
- func (e EffectFinished) SettlementStatus() SettlementStatus
- func (e EffectFinished) Target() EffectTarget
- func (e EffectFinished) Valid() bool
- type EffectID
- type EffectRequest
- func (e EffectRequest) AttemptID() (EffectAttemptID, bool)
- func (e EffectRequest) BatchIndex() uint32
- func (e EffectRequest) DeploymentRef() DeploymentRef
- func (e EffectRequest) Effect() Effect
- func (e EffectRequest) ID() EffectID
- func (e EffectRequest) ProcessID() ProcessID
- func (e EffectRequest) Relation() ProcessRelation
- func (e EffectRequest) StepSequence() uint64
- func (e EffectRequest) TreeIncarnationID() (TreeIncarnationID, bool)
- func (e EffectRequest) Valid() bool
- type EffectResolved
- type EffectStarted
- type EffectTarget
- type Engine
- func (e *Engine) CaptureTree(ctx context.Context, rootID ProcessID) (TreeSnapshot, error)
- func (e *Engine) Close(ctx context.Context) error
- func (e *Engine) FlushDeltas(ctx context.Context) error
- func (e *Engine) InspectTree(ctx context.Context, rootID ProcessID) (TreeInspection, error)
- func (e *Engine) ObservationFailures() ObservationFailures
- func (e *Engine) Process(id ProcessID) (*Process, bool)
- func (e *Engine) ReleaseTree(ctx context.Context, rootID ProcessID) error
- func (e *Engine) RestoreTree(ctx context.Context, rootDeployment Deployment, snapshot TreeSnapshot) (*Process, error)
- func (e *Engine) Run(ctx context.Context, deployment Deployment, input Payload) (Result, error)
- func (e *Engine) Start(ctx context.Context, deployment Deployment, input Payload) (*Process, error)
- func (e *Engine) ValidateRestorableTree(ctx context.Context, rootDeployment Deployment, snapshot TreeSnapshot) error
- type EngineConfig
- type Event
- func (e Event) DeltaDropped() (DeltaDropped, bool)
- func (e Event) DeploymentRef() DeploymentRef
- func (e Event) EffectFinished() (EffectFinished, bool)
- func (e Event) EffectID() (EffectID, bool)
- func (e Event) EffectResolved() (EffectResolved, bool)
- func (e Event) EffectStarted() (EffectStarted, bool)
- func (e Event) MarshalJSON() ([]byte, error)
- func (e Event) Name() string
- func (e Event) OccurredAt() time.Time
- func (e Event) Payload() json.RawMessage
- func (e Event) Phase() EventPhase
- func (e Event) ProcessFinished() (ProcessFinished, bool)
- func (e Event) ProcessID() ProcessID
- func (e Event) ProcessSequence() uint64
- func (e Event) Relation() ProcessRelation
- func (e Event) RuntimeStopped() (RuntimeStopped, bool)
- func (e Event) SignalAccepted() (SignalAccepted, bool)
- func (e Event) StepCommitted() (StepCommitted, bool)
- func (e Event) StepFinished() (StepFinished, bool)
- func (e Event) StepSequence() (uint64, bool)
- func (e Event) TreeIncarnationID() (TreeIncarnationID, bool)
- func (e *Event) UnmarshalJSON(data []byte) error
- func (e Event) Valid() bool
- type EventListener
- type EventListenerFunc
- type EventPhase
- type Execution
- type ExecutionState
- type Failure
- func (f Failure) Classification() FailureClassification
- func (f Failure) Code() string
- func (Failure) JSONSchemaAlias() any
- func (f Failure) Kind() FailureKind
- func (f Failure) MarshalJSON() ([]byte, error)
- func (f Failure) Message() string
- func (f *Failure) UnmarshalJSON(data []byte) error
- func (f Failure) Valid() bool
- type FailureClassification
- type FailureKind
- type ListenerPanic
- type MemoryTreeCommitter
- func (m *MemoryTreeCommitter) ActivateTree(_ context.Context, activation TreeActivation) error
- func (m *MemoryTreeCommitter) CommitCheckpoint(_ context.Context, checkpoint TreeCheckpoint) error
- func (m *MemoryTreeCommitter) CommitEffect(_ context.Context, boundary EffectBoundary) error
- func (m *MemoryTreeCommitter) LoadTree(_ context.Context, rootID ProcessID) (TreeSnapshot, bool, error)
- type ObservationFailures
- func (o ObservationFailures) DeltaListenerPanics() uint64
- func (o ObservationFailures) DroppedEvents() uint64
- func (o ObservationFailures) EventListenerPanics() uint64
- func (o ObservationFailures) LastDeltaPanic() (ListenerPanic, bool)
- func (o ObservationFailures) LastEventPanic() (ListenerPanic, bool)
- type Payload
- type Process
- func (p *Process) Await(ctx context.Context) (Result, error)
- func (p *Process) Budget() Budget
- func (p *Process) Capabilities() CapabilitySet
- func (p *Process) DeliverSignals(ctx context.Context, requests ...SignalRequest) (accepted bool, err error)
- func (p *Process) DeploymentRef() DeploymentRef
- func (p *Process) ID() ProcessID
- func (p *Process) Join(ctx context.Context) error
- func (p *Process) Kill(ctx context.Context, reason string) error
- func (p *Process) Pause(ctx context.Context, reason string) error
- func (p *Process) Relation() ProcessRelation
- func (p *Process) ReplayUnknownEffect(ctx context.Context, effectID EffectID) error
- func (p *Process) RequestCancellation(ctx context.Context, reason string) error
- func (p *Process) ResolveUnknownEffect(ctx context.Context, settlement Settlement) error
- func (p *Process) Resume(ctx context.Context) error
- func (p *Process) StartedAt() time.Time
- type ProcessAdmission
- type ProcessAdmitter
- type ProcessAdmitterFunc
- type ProcessFinished
- type ProcessID
- type ProcessInitializationAcknowledger
- type ProcessInitializationAcknowledgerFunc
- type ProcessInitializationOutcome
- func (p ProcessInitializationOutcome) Admission() ProcessAdmission
- func (p ProcessInitializationOutcome) Failure() (Failure, bool)
- func (p ProcessInitializationOutcome) StartedAt() (time.Time, bool)
- func (p ProcessInitializationOutcome) Status() ProcessInitializationOutcomeStatus
- func (p ProcessInitializationOutcome) Valid() bool
- type ProcessInitializationOutcomeStatus
- type ProcessInspection
- type ProcessRelation
- func (p ProcessRelation) ChildKey() (ChildKey, bool)
- func (p ProcessRelation) Depth() uint32
- func (p ProcessRelation) IsRoot() bool
- func (p ProcessRelation) ParentID() (ProcessID, bool)
- func (p ProcessRelation) ProcessID() ProcessID
- func (p ProcessRelation) RootID() ProcessID
- func (p ProcessRelation) Valid() bool
- type ProcessSnapshot
- func (p ProcessSnapshot) Budget() Budget
- func (p ProcessSnapshot) Capabilities() CapabilitySet
- func (p ProcessSnapshot) CommittedExecutionState() ExecutionState
- func (p ProcessSnapshot) DeploymentRef() DeploymentRef
- func (p ProcessSnapshot) EffectDiagnostic(id EffectID) (Failure, bool)
- func (p ProcessSnapshot) JSON() json.RawMessage
- func (p ProcessSnapshot) MarshalJSON() ([]byte, error)
- func (p ProcessSnapshot) ProcessID() ProcessID
- func (p ProcessSnapshot) Relation() ProcessRelation
- func (p ProcessSnapshot) Result() (Result, bool)
- func (p ProcessSnapshot) Settlements() []Settlement
- func (p ProcessSnapshot) SignalReceipts() []SignalReceipt
- func (p ProcessSnapshot) Status() Status
- func (p ProcessSnapshot) UnknownEffectIDs() []EffectID
- func (p ProcessSnapshot) Usage() Usage
- func (p ProcessSnapshot) Valid() bool
- func (p ProcessSnapshot) WaitID() (WaitID, bool)
- func (p ProcessSnapshot) WaitKind() (WaitKind, bool)
- type ProcessWork
- type Quota
- type ReplayPolicy
- type Result
- type RuntimeError
- type RuntimeStopped
- type Schema
- type Settlement
- type SettlementStatus
- type Signal
- func (s Signal) EngineOwned() bool
- func (s Signal) ID() SignalID
- func (Signal) JSONSchemaAlias() any
- func (s Signal) MarshalJSON() ([]byte, error)
- func (s Signal) Payload() json.RawMessage
- func (s Signal) Settles(effectID EffectID) bool
- func (s *Signal) UnmarshalJSON(data []byte) error
- func (s Signal) Valid() bool
- func (s Signal) WaitID() (WaitID, bool)
- type SignalAccepted
- type SignalID
- type SignalReceipt
- func (s SignalReceipt) ArrivalSequence() uint64
- func (s SignalReceipt) Consumed() bool
- func (s SignalReceipt) ID() SignalID
- func (s SignalReceipt) Matches(request SignalRequest) bool
- func (s SignalReceipt) PayloadDigest() Digest
- func (s SignalReceipt) PendingSignal() (Signal, bool)
- func (s SignalReceipt) WaitID() (WaitID, bool)
- type SignalRequest
- func (s SignalRequest) ID() SignalID
- func (SignalRequest) JSONSchemaAlias() any
- func (s SignalRequest) MarshalJSON() ([]byte, error)
- func (s SignalRequest) Payload() json.RawMessage
- func (s *SignalRequest) UnmarshalJSON(data []byte) error
- func (s SignalRequest) Valid() bool
- func (s SignalRequest) WaitID() (WaitID, bool)
- type Status
- type StepCommitted
- type StepFinished
- type StepStatus
- type Termination
- func (t Termination) Cause() TerminationCause
- func (t Termination) Failure() (Failure, bool)
- func (Termination) JSONSchemaAlias() any
- func (t Termination) MarshalJSON() ([]byte, error)
- func (t Termination) Reason() string
- func (t Termination) Status() Status
- func (t *Termination) UnmarshalJSON(data []byte) error
- func (t Termination) UnresolvedEffectIDs() []EffectID
- func (t Termination) Valid() bool
- type TerminationCause
- type Transition
- func Checkpoint(consumedSignals uint32) (Transition, error)
- func Complete(consumedSignals uint32, output Payload) (Transition, error)
- func Continue(consumedSignals uint32, effects ...Effect) (Transition, error)
- func Fail(consumedSignals uint32, failure Failure) (Transition, error)
- func Pause(consumedSignals uint32, reason string) (Transition, error)
- func Wait(consumedSignals uint32, waitID WaitID) (Transition, error)
- func (t Transition) ConsumedSignals() uint32
- func (t Transition) Effects() []Effect
- func (t Transition) Failure() (Failure, bool)
- func (t Transition) Kind() TransitionKind
- func (t Transition) MarshalJSON() ([]byte, error)
- func (t Transition) Output() (Payload, bool)
- func (t Transition) Reason() (string, bool)
- func (t *Transition) UnmarshalJSON(data []byte) error
- func (t Transition) Valid() bool
- func (t Transition) WaitID() (WaitID, bool)
- type TransitionKind
- type TreeActivation
- func (t TreeActivation) ContentDigest() (Digest, error)
- func (t TreeActivation) Identity() string
- func (t TreeActivation) IncarnationID() TreeIncarnationID
- func (t TreeActivation) PreviousIncarnationID() TreeIncarnationID
- func (t TreeActivation) PreviousTreeDigest() Digest
- func (t TreeActivation) TreeSnapshot() TreeSnapshot
- func (t TreeActivation) Valid() bool
- type TreeCheckpoint
- func (t TreeCheckpoint) ContentDigest() (Digest, error)
- func (t TreeCheckpoint) Identity() string
- func (t TreeCheckpoint) Kind() TreeCheckpointKind
- func (t TreeCheckpoint) PreviousTreeDigest() Digest
- func (t TreeCheckpoint) Sequence() uint64
- func (t TreeCheckpoint) TreeSnapshot() TreeSnapshot
- func (t TreeCheckpoint) Valid() bool
- type TreeCheckpointKind
- type TreeCommitter
- type TreeFreezePhase
- type TreeIncarnationID
- type TreeInspection
- type TreeLimits
- type TreeSnapshot
- func (t TreeSnapshot) Digest() Digest
- func (t TreeSnapshot) EffectRequest(processID ProcessID, id EffectID) (EffectRequest, bool)
- func (t TreeSnapshot) EncodedSize() int
- func (t TreeSnapshot) IncarnationID() TreeIncarnationID
- func (t TreeSnapshot) JSON() json.RawMessage
- func (t TreeSnapshot) MarshalJSON() ([]byte, error)
- func (t TreeSnapshot) ProcessSnapshots() []ProcessSnapshot
- func (t TreeSnapshot) RootID() ProcessID
- func (t *TreeSnapshot) UnmarshalJSON(data []byte) error
- func (t TreeSnapshot) Valid() bool
- type UnresolvedEffect
- type Usage
- type WaitID
- type WaitKey
- type WaitKind
Examples ¶
Constants ¶
const ( EventProcessStarted = "agent.process.started" EventProcessRestored = "agent.process.restored" EventProcessPaused = "agent.process.paused" EventProcessResumed = "agent.process.resumed" EventProcessFinished = "agent.process.finished" // EventRuntimeStopped reports loss of an active instance without a committed // logical terminal result. It never changes the durable Process lifecycle. EventRuntimeStopped = "agent.runtime.stopped" EventSignalAccepted = "agent.signal.accepted" EventStepStarted = "agent.step.started" EventStepFinished = "agent.step.finished" EventStepPrepared = "agent.step.prepared" EventStepCommitted = "agent.step.committed" EventEffectStarted = "agent.effect.started" EventEffectFinished = "agent.effect.finished" // EventEffectResolved reports an Unknown settlement replaced by a definite // result, through host adjudication or an explicitly requested replay. EventEffectResolved = "agent.effect.resolved" EventDeltaDropped = "agent.delta.dropped" )
const MaxDescriptionBytes = 4096
MaxDescriptionBytes bounds descriptions of advertised behavior.
const MaxDiagnosticBytes = 4096
MaxDiagnosticBytes bounds persistable failure messages and strategy diagnostics.
const MaxPayloadBytes = 64 << 20
MaxPayloadBytes is the maximum encoded JSON size of an individual Agent input, output, Effect, Signal, or settlement payload. Both the supplied bytes and the canonical encoding must fit. Canonical payloads use RFC 7493 JSON, omit whitespace, sort object names by UTF-16 code units (RFC 8785 section 3.2.3), preserve number literals verbatim, and escape <, >, &, U+2028 and U+2029. Strings otherwise use their shortest JSON encoding. Digests over payloads and persisted payload bytes use this form; this is not RFC 8785 number canonicalization.
Variables ¶
var ( ErrInvalidEngineConfig = errors.New("agent: invalid engine configuration") ErrEngineClosed = errors.New("agent: engine is closed") ErrEngineHasActiveProcesses = errors.New("agent: engine has active processes") ErrProcessAlreadyExists = errors.New("agent: process identity already exists") // ErrTreeNotFound reports that this Engine has no registered root tree with // the requested identity, including after ReleaseTree removed it. ErrTreeNotFound = errors.New("agent: tree not found") )
var ( ErrSignalRejected = errors.New("agent: signal rejected") // ErrSignalConflict reports a repeated SignalID within one batch or reuse // of an accepted identity with different immutable content or WaitID. ErrSignalConflict = errors.New("agent: conflicting signal identity") )
var ( ErrProcessFinished = errors.New("agent: process has finished") ErrEffectNotPending = errors.New("agent: effect does not require resolution") ErrEffectReplayForbidden = errors.New("agent: effect cannot be replayed under the same identity") ErrEffectOutcomeUnknown = errors.New("agent: effect outcome remains unknown") ErrInvalidProcessControl = errors.New("agent: invalid process control request") ErrNilContext = errors.New("agent: nil Context") )
var ( ErrInvalidSchema = errors.New("agent: invalid schema") ErrSchemaValidation = errors.New("agent: schema validation failed") )
var ( ErrCommitConflict = errors.New("agent: committer boundary conflicts with committed content") ErrTreeIncarnationConflict = errors.New("agent: tree incarnation conflict") )
var ErrCounterExhausted = NewClassifiedError( FailureKindExecution, failureCodeEngineCounterExhausted, "agent: counter exhausted", )
ErrCounterExhausted reports exhausted numeric identity or accounting space, independently of the Host's quota policy. Work stops before a counter wraps. Strategies exhausting their own counters report this same sentinel.
var ErrInvalidCapability = errors.New("agent: invalid capability")
var ErrInvalidChildControl = errors.New("agent: invalid child control")
var ErrInvalidChildStart = errors.New("agent: invalid child process start")
var ErrInvalidChildWait = errors.New("agent: invalid child wait")
var ErrInvalidDelta = errors.New("agent: invalid delta")
var ErrInvalidDeployment = errors.New("agent: invalid deployment")
var ErrInvalidDeploymentRef = errors.New("agent: invalid deployment reference")
var ErrInvalidDescriptor = errors.New("agent: invalid descriptor")
var ErrInvalidDigest = errors.New("agent: invalid digest")
var ErrInvalidEffect = errors.New("agent: invalid effect")
var ErrInvalidEffectAttemptID = errors.New("agent: invalid Effect attempt identity")
var ErrInvalidEvent = errors.New("agent: invalid event")
var ErrInvalidExecutionState = errors.New("agent: invalid execution state")
var ErrInvalidFailure = errors.New("agent: invalid failure")
var ErrInvalidIdentity = errors.New("agent: invalid identity")
var ErrInvalidPayload = errors.New("agent: invalid payload")
var ErrInvalidProcessRelation = errors.New("agent: invalid process relation")
var ErrInvalidSettlement = errors.New("agent: invalid effect settlement")
var ErrInvalidSignal = errors.New("agent: invalid signal")
var ErrInvalidSignalRequest = errors.New("agent: invalid signal request")
var ErrInvalidSnapshot = errors.New("agent: invalid process snapshot")
var ErrInvalidStatus = errors.New("agent: invalid status")
var ErrInvalidTransition = errors.New("agent: invalid transition")
var ErrInvalidTreeIncarnationID = errors.New("agent: invalid tree incarnation identity")
var ErrInvalidTreeSnapshot = errors.New("agent: invalid process tree snapshot")
var ErrListenerReentrancy = errors.New("agent: operation would wait for its active listener")
ErrListenerReentrancy reports an operation that would wait for the active listener carrying its context: an EventListener's tree owner or a DeltaListener's delivery worker. Derived contexts retain this restriction until the callback returns. Replacing the context does not remove the listener's obligation to avoid waiting for itself.
var ErrProcessAdmissionRejected = errors.New("agent: process admission rejected")
ErrProcessAdmissionRejected marks failure at the policy boundary before execution starts.
var ErrResourceLimitExceeded = errors.New("agent: resource limit exceeded")
ErrResourceLimitExceeded reports a designed execution bound, not an Engine defect.
Functions ¶
func NormalizeDiagnostic ¶ added in v0.26.0
NormalizeDiagnostic makes arbitrary external text non-empty, trimmed UTF-8 within MaxDiagnosticBytes. Callers remain responsible for excluding secrets.
func RequireContext ¶ added in v0.26.0
RequireContext enforces the non-nil Context contract shared by Agent boundaries. It panics with ErrNilContext for a programming error and otherwise returns ctx.
func ValidDescription ¶ added in v0.26.0
ValidDescription checks descriptive text without repairing or reinterpreting it.
func ValidDiagnostic ¶ added in v0.26.0
func ValidQualifiedName ¶ added in v0.26.0
ValidQualifiedName checks a 1–128 byte name beginning with a lowercase ASCII letter and followed only by lowercase letters, digits, dots, underscores or hyphens.
Types ¶
type Budget ¶
type Budget struct {
// Steps bounds committed Steps. A finite zero forbids new computation.
Steps Quota `json:"steps"`
// Effects bounds stable Effect identities prepared across all Steps. A
// finite zero permits pure Steps but forbids Effects.
Effects Quota `json:"effects"`
// Signals bounds accepted external and Engine-generated Signals. A finite
// zero permits computations that need no runtime input or settlements.
Signals Quota `json:"signals"`
}
Budget grants cumulative work authority. Zero quotas are unlimited. A finite parent permanently charges child grants, including unused child work. An unlimited parent grants either kind without a finite debit. Each dimension is independent; a finite parent cannot grant an unlimited child quota.
func (*Budget) UnmarshalJSON ¶ added in v0.27.0
type CallbackPanicError ¶ added in v0.28.0
CallbackPanicError identifies a panic contained at a Host callback boundary. Operation names the callback; Value is the recovered value, which Unwrap exposes when it is an error. The boundary still owns the outcome: execution failures use FailureKindPanic, uncertain dispatch remains unknown, and storage errors stop the writer through RuntimeError. It is an in-memory diagnostic and must not be serialized as a result.
func (*CallbackPanicError) Error ¶ added in v0.28.0
func (c *CallbackPanicError) Error() string
func (*CallbackPanicError) Unwrap ¶ added in v0.28.0
func (c *CallbackPanicError) Unwrap() error
type Capability ¶
type Capability struct {
// contains filtered or unexported fields
}
Capability is one stable qualified authority name understood by a Deployment's dispatcher or another external boundary. The Framework only enforces possession and attenuation; it does not assign product meaning.
func ParseCapability ¶
func ParseCapability(name string) (Capability, error)
ParseCapability validates a lowercase qualified capability name.
func (Capability) JSONSchemaAlias ¶ added in v0.17.0
func (Capability) JSONSchemaAlias() any
func (Capability) MarshalText ¶
func (c Capability) MarshalText() ([]byte, error)
func (Capability) String ¶
func (c Capability) String() string
func (*Capability) UnmarshalText ¶
func (c *Capability) UnmarshalText(text []byte) error
func (Capability) Valid ¶
func (c Capability) Valid() bool
type CapabilitySet ¶
type CapabilitySet struct {
// contains filtered or unexported fields
}
CapabilitySet is an immutable, sorted set of authority names. Its zero value is the valid empty set, encoded as an empty JSON array. JSON null is invalid.
func NewCapabilitySet ¶
func NewCapabilitySet(capabilities ...Capability) (CapabilitySet, error)
func (CapabilitySet) Allows ¶
func (c CapabilitySet) Allows(requested CapabilitySet) bool
func (CapabilitySet) Contains ¶
func (c CapabilitySet) Contains(capability Capability) bool
func (CapabilitySet) JSONSchemaAlias ¶ added in v0.17.0
func (CapabilitySet) JSONSchemaAlias() any
func (CapabilitySet) MarshalJSON ¶
func (c CapabilitySet) MarshalJSON() ([]byte, error)
func (*CapabilitySet) UnmarshalJSON ¶
func (c *CapabilitySet) UnmarshalJSON(data []byte) error
UnmarshalJSON rejects duplicate grants and null; ordering is normalized. Construction accepts duplicates because its arguments describe a set.
func (CapabilitySet) Valid ¶
func (c CapabilitySet) Valid() bool
func (CapabilitySet) Values ¶
func (c CapabilitySet) Values() []Capability
Values returns an independently owned, sorted capability slice.
type ChildControlResult ¶ added in v0.18.0
type ChildControlResult struct {
// contains filtered or unexported fields
}
ChildControlResult is a definite tree-local admission result. Failure preserves rejected authority, wait, or resource admission without failing the sending Strategy implicitly. The declaring Effect owns the recipient and any delivered SignalID and the control operation; the result carries only whether admission failed. The zero value confirms successful admission.
func ParseChildControlResult ¶ added in v0.18.0
func ParseChildControlResult(signal Signal) (ChildControlResult, error)
ParseChildControlResult decodes an unaddressed Framework control settlement carrying an Engine-owned Signal identity.
func (ChildControlResult) Failure ¶ added in v0.18.0
func (c ChildControlResult) Failure() (Failure, bool)
func (ChildControlResult) JSONSchemaAlias ¶ added in v0.18.0
func (ChildControlResult) JSONSchemaAlias() any
func (ChildControlResult) MarshalJSON ¶ added in v0.18.0
func (c ChildControlResult) MarshalJSON() ([]byte, error)
func (*ChildControlResult) UnmarshalJSON ¶ added in v0.18.0
func (c *ChildControlResult) UnmarshalJSON(data []byte) error
func (ChildControlResult) Valid ¶ added in v0.18.0
func (c ChildControlResult) Valid() bool
type ChildKey ¶
type ChildKey struct {
// contains filtered or unexported fields
}
ChildKey is an Execution-owned stable identity for one logical child start. The Engine combines it with the parent Process identity and prepared Effect identity to make retries and restoration idempotent.
func ParseChildKey ¶
func (ChildKey) JSONSchemaAlias ¶ added in v0.17.0
func (ChildKey) JSONSchemaAlias() any
func (ChildKey) MarshalText ¶
func (*ChildKey) UnmarshalText ¶
func (ChildKey) Valid ¶
func (i ChildKey) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
type ChildOutcome ¶
type ChildOutcome struct {
// contains filtered or unexported fields
}
ChildOutcome is one child's immutable terminal Result and, when its wait drained the child's subtree, the Effects that subtree left unresolved. The wait that produced it owns the child's key and boundary.
func (ChildOutcome) JSONSchemaAlias ¶ added in v0.17.0
func (ChildOutcome) JSONSchemaAlias() any
func (ChildOutcome) MarshalJSON ¶ added in v0.17.0
func (c ChildOutcome) MarshalJSON() ([]byte, error)
func (ChildOutcome) Result ¶
func (c ChildOutcome) Result() Result
func (ChildOutcome) SubtreeResolved ¶ added in v0.41.0
func (c ChildOutcome) SubtreeResolved() bool
SubtreeResolved reports whether this outcome proves that the child and all of its descendants drained without retained Unknown settlements. A terminal-result outcome proves nothing about the subtree and reports false.
func (ChildOutcome) SubtreeUnresolvedEffects ¶ added in v0.22.0
func (c ChildOutcome) SubtreeUnresolvedEffects() ([]UnresolvedEffect, bool)
SubtreeUnresolvedEffects returns an independent, ProcessID/EffectID-ordered projection including this child and all descendants. The boolean is true only for a drained subtree; false must not be interpreted as an empty subtree.
func (*ChildOutcome) UnmarshalJSON ¶ added in v0.17.0
func (c *ChildOutcome) UnmarshalJSON(data []byte) error
func (ChildOutcome) Valid ¶
func (c ChildOutcome) Valid() bool
type ChildSpec ¶
type ChildSpec struct {
// Key is the parent-scoped logical identity of this child start.
Key ChildKey `json:"key"`
DeploymentRef DeploymentRef `json:"deployment_ref"`
Input Payload `json:"input"`
// Budget is permanently allocated from the parent to this child.
Budget Budget `json:"budget"`
// Capabilities is the attenuated authority granted to this child.
Capabilities CapabilitySet `json:"capabilities"`
}
ChildSpec is the complete Strategy-declared intent for one child Process. Input is validated by the target Deployment before any Process is created.
func ParseChildStartEffect ¶ added in v0.43.0
ParseChildStartEffect returns the ChildSpec a NewChildStartEffect Effect declares, so a settlement can be read against the request that owns its key and Deployment.
type ChildStartResult ¶
type ChildStartResult struct {
// contains filtered or unexported fields
}
ChildStartResult is the definite result of one NewChildStartEffect Effect. Success contains the Engine-created child ProcessID; failure contains a stable Framework Failure and never masquerades as an unknown external outcome. The requesting ChildSpec owns the key and Deployment; the result carries only what the start established.
func ParseChildStartResult ¶
func ParseChildStartResult(signal Signal) (ChildStartResult, error)
ParseChildStartResult decodes a Framework-owned child-start settlement Signal. The Signal must carry an Engine-owned identity and must not address a wait.
func (ChildStartResult) Failure ¶
func (c ChildStartResult) Failure() (Failure, bool)
Failure returns the definite start failure and true when no child was created.
func (ChildStartResult) JSONSchemaAlias ¶ added in v0.17.0
func (ChildStartResult) JSONSchemaAlias() any
func (ChildStartResult) MarshalJSON ¶ added in v0.17.0
func (c ChildStartResult) MarshalJSON() ([]byte, error)
func (ChildStartResult) ProcessID ¶
func (c ChildStartResult) ProcessID() (ProcessID, bool)
func (*ChildStartResult) UnmarshalJSON ¶ added in v0.17.0
func (c *ChildStartResult) UnmarshalJSON(data []byte) error
func (ChildStartResult) Valid ¶
func (c ChildStartResult) Valid() bool
type ChildWaitBoundary ¶ added in v0.17.0
type ChildWaitBoundary string
ChildWaitBoundary selects the lifecycle fact counted by a child wait.
const ( ChildWaitBoundaryInvalid ChildWaitBoundary = "" ChildWaitBoundaryResult ChildWaitBoundary = "terminal_result" ChildWaitBoundaryDrained ChildWaitBoundary = "subtree_drained" )
func (ChildWaitBoundary) String ¶ added in v0.17.0
func (c ChildWaitBoundary) String() string
func (ChildWaitBoundary) Valid ¶ added in v0.17.0
func (c ChildWaitBoundary) Valid() bool
type ChildWaitCondition ¶
type ChildWaitCondition struct {
// contains filtered or unexported fields
}
ChildWaitCondition identifies when a set of child Processes releases its parent. It counts children at the requested boundary; it does not cancel unfinished children or reinterpret child terminal statuses.
func AllChildren ¶
func AllChildren() ChildWaitCondition
func AnyChild ¶
func AnyChild() ChildWaitCondition
func ChildQuorum ¶
func ChildQuorum(count uint32) (ChildWaitCondition, error)
func (ChildWaitCondition) Valid ¶
func (c ChildWaitCondition) Valid() bool
type ChildWaitOpened ¶
type ChildWaitOpened struct {
// contains filtered or unexported fields
}
ChildWaitOpened is the definite acknowledgement that the Engine opened a child wait and minted its WaitID. The declaring Effect owns the spec; the acknowledgement carries only the identity the Engine minted.
func ParseChildWaitOpened ¶
func ParseChildWaitOpened(signal Signal) (ChildWaitOpened, error)
ParseChildWaitOpened decodes the settlement Signal produced by NewChildWaitEffect and verifies its Engine-owned Signal identity and attached WaitID.
func (ChildWaitOpened) Valid ¶
func (c ChildWaitOpened) Valid() bool
func (ChildWaitOpened) WaitID ¶
func (c ChildWaitOpened) WaitID() WaitID
type ChildWaitSatisfied ¶ added in v0.17.0
type ChildWaitSatisfied struct {
// contains filtered or unexported fields
}
ChildWaitSatisfied is one condition-satisfying, request-ordered child result set. For any or quorum it includes every child at the requested boundary at the atomic satisfaction check, without canceling or omitting based on status. The wait it answers owns the key and boundary.
func ParseChildWaitSatisfied ¶ added in v0.17.0
func ParseChildWaitSatisfied(signal Signal) (ChildWaitSatisfied, error)
ParseChildWaitSatisfied decodes an Engine-generated, WaitID-addressed child wait-satisfaction Signal. Caller-owned Signal identities are rejected.
func (ChildWaitSatisfied) Matches ¶ added in v0.26.0
func (c ChildWaitSatisfied) Matches(id WaitID, spec ChildWaitSpec) bool
Matches correlates a satisfaction with the entire active wait, including its boundary, required count, and request order.
func (ChildWaitSatisfied) Outcomes ¶ added in v0.17.0
func (c ChildWaitSatisfied) Outcomes() []ChildOutcome
Outcomes returns terminal children in the original ChildWaitSpec order.
func (ChildWaitSatisfied) Valid ¶ added in v0.17.0
func (c ChildWaitSatisfied) Valid() bool
func (ChildWaitSatisfied) WaitID ¶ added in v0.17.0
func (c ChildWaitSatisfied) WaitID() WaitID
type ChildWaitSpec ¶
type ChildWaitSpec struct {
// Key is the Execution-owned logical identity of this wait request.
Key WaitKey
// Children lists direct child identities in result order.
Children []ProcessID
// Boundary explicitly selects terminal results or joined subtrees. A joined
// subtree has completed its owned local work and required acknowledgments in
// this runtime, as defined by Process.Join; remote uncertainty may remain.
Boundary ChildWaitBoundary
Condition ChildWaitCondition
}
func (ChildWaitSpec) Valid ¶
func (c ChildWaitSpec) Valid() bool
type ClassifiedError ¶ added in v0.36.0
type ClassifiedError struct {
// contains filtered or unexported fields
}
ClassifiedError is a sentinel that owns the Failure kind and code persisted when an error wrapping it leaves Execution.Step, so no separate mapping can disagree with the declaration.
func NewClassifiedError ¶ added in v0.36.0
func NewClassifiedError(kind FailureKind, code, message string) *ClassifiedError
NewClassifiedError declares one sentinel together with the Failure the Engine persists when Execution.Step returns an error wrapping it; the persisted message is the complete wrapped diagnostic. An invalid kind, code, or message is a programming error and panics.
func (*ClassifiedError) Error ¶ added in v0.36.0
func (c *ClassifiedError) Error() string
type Definition ¶
type Definition interface {
// Descriptor returns the immutable, portable contract shared by every
// Execution created from this definition. Repeated and concurrent calls must
// return an equivalent value; runtime configuration and mutable state do not
// belong in the descriptor.
Descriptor() Descriptor
// ChildDeployments returns every Deployment this definition binds in its
// own configuration to start as a child. The Deployment owns these
// bindings: NewDeployment folds their references into the DeploymentRef,
// so a definition bound to different children is a different Deployment,
// and the Engine starts and restores these children from the bindings
// themselves. Only children named by runtime input are left to the
// DeploymentResolver. Repeated and concurrent calls must return the same
// set; a leaf definition returns none.
ChildDeployments() []Deployment
// Start validates input against Descriptor and creates a fresh, isolated
// Execution without performing external I/O. It takes no context because it
// only initializes local state and must return promptly.
Start(input Payload) (Execution, error)
// Restore reconstructs one Execution from a state previously produced by
// Snapshot for this exact definition; the Engine enforces the pairing through
// the snapshot's DeploymentRef. Restore validates state and strategy
// invariants without replaying external work, and runs on every Step, so it
// must honor ctx. The Engine's ctx carries cancellation but no Host values,
// which must not become unrecorded inputs.
Restore(ctx context.Context, state ExecutionState) (Execution, error)
}
Definition is an immutable Agent behavior definition. Its methods may be called concurrently for different Processes. Implementations create a fresh Execution from validated input or restore one from their own opaque ExecutionState. Definition methods must not depend on Host product identities, storage protocols, or mutable global registration.
Example ¶
An independently authored Definition binds through the same Deployment and Engine contracts as built-in strategies. TestExternalPackageCanComposeAndRunDefinition checks this implementation with agenttest.RunDefinitionConformance.
package main
import (
"context"
jsonv2 "encoding/json/v2"
"fmt"
"sync/atomic"
"github.com/Tangerg/scope/agent"
)
type echoInput struct {
Value string `json:"value"`
}
type echoOutput struct {
Value string `json:"value"`
}
type echoDefinition struct {
descriptor agent.Descriptor
}
type echoState struct {
Phase string `json:"phase"`
Value string `json:"value"`
}
func (e echoDefinition) Descriptor() agent.Descriptor { return e.descriptor }
func (echoDefinition) ChildDeployments() []agent.Deployment { return nil }
func (e echoDefinition) Start(input agent.Payload) (agent.Execution, error) {
if err := e.descriptor.ValidateInput(input); err != nil {
return nil, err
}
value, err := input.Decode[echoInput]()
if err != nil {
return nil, err
}
return &echoExecution{state: echoState{Phase: "ready", Value: value.Value}}, nil
}
func (echoDefinition) Restore(ctx context.Context, state agent.ExecutionState) (agent.Execution, error) {
value, err := state.Decode[echoState]("example.echo")
if err != nil {
return nil, err
}
switch value.Phase {
case "ready", "awaiting_echo", "completed":
return &echoExecution{state: value}, nil
default:
return nil, agent.ErrInvalidExecutionState
}
}
type echoExecution struct {
state echoState
}
func (e *echoExecution) Step(ctx context.Context, signals []agent.Signal) (agent.Transition, error) {
if err := ctx.Err(); err != nil {
return agent.Transition{}, err
}
switch e.state.Phase {
case "ready":
if len(signals) != 0 {
return agent.Transition{}, agent.ErrInvalidSignal
}
payload, err := jsonv2.Marshal(echoInput{Value: e.state.Value})
if err != nil {
return agent.Transition{}, err
}
effect, err := agent.NewDispatcherEffect(payload)
if err != nil {
return agent.Transition{}, err
}
e.state.Phase = "awaiting_echo"
return agent.Continue(0, effect)
case "awaiting_echo":
if len(signals) != 1 {
return agent.Transition{}, agent.ErrInvalidSignal
}
var result echoOutput
if err := jsonv2.Unmarshal(signals[0].Payload(), &result, jsonv2.RejectUnknownMembers(true)); err != nil {
return agent.Transition{}, err
}
output, err := agent.EncodePayload(result)
if err != nil {
return agent.Transition{}, err
}
e.state.Phase = "completed"
return agent.Complete(1, output)
default:
return agent.Transition{}, agent.ErrInvalidExecutionState
}
}
func (e *echoExecution) Snapshot() (agent.ExecutionState, error) {
payload, err := jsonv2.Marshal(e.state)
if err != nil {
return agent.ExecutionState{}, err
}
return agent.ParseExecutionState("example.echo", payload)
}
func newEchoDefinition() (echoDefinition, error) {
inputSchema, err := agent.SchemaFor[echoInput]()
if err != nil {
return echoDefinition{}, err
}
outputSchema, err := agent.SchemaFor[echoOutput]()
if err != nil {
return echoDefinition{}, err
}
descriptor, err := agent.NewDescriptor(agent.DescriptorConfig{
Name: "example.echo", Description: "Echoes a value through an Engine-managed Effect.",
InputSchema: inputSchema, OutputSchema: outputSchema,
})
if err != nil {
return echoDefinition{}, err
}
return echoDefinition{descriptor: descriptor}, nil
}
// echoDispatcher has no external mutation, so repeating an identity is safe.
type echoDispatcher struct{}
func (echoDispatcher) Dispatch(ctx context.Context, request agent.EffectRequest, emit agent.DeltaEmitter) (agent.Settlement, error) {
if err := ctx.Err(); err != nil {
return agent.Settlement{}, err
}
payload := request.Effect().Payload()
if emit != nil {
emit(payload)
}
return agent.NewSettlement(request.ID(), agent.SettlementStatusSucceeded, payload)
}
func (echoDispatcher) ReplayPolicy(agent.Effect) agent.ReplayPolicy {
return agent.ReplayPolicySameIdentity
}
// countingDispatcher counts attempts, including replay, rather than logical operations.
type countingDispatcher struct {
next agent.Dispatcher
attempts atomic.Int64
}
func (c *countingDispatcher) Dispatch(ctx context.Context, request agent.EffectRequest, emit agent.DeltaEmitter) (agent.Settlement, error) {
c.attempts.Add(1)
return c.next.Dispatch(ctx, request, emit)
}
func (c *countingDispatcher) ReplayPolicy(effect agent.Effect) agent.ReplayPolicy {
return c.next.ReplayPolicy(effect)
}
// An independently authored Definition binds through the same Deployment and
// Engine contracts as built-in strategies. TestExternalPackageCanComposeAndRunDefinition
// checks this implementation with agenttest.RunDefinitionConformance.
func main() {
ctx := context.Background()
definition, err := newEchoDefinition()
if err != nil {
panic(err)
}
dispatcher := &countingDispatcher{next: echoDispatcher{}}
deployment, err := agent.NewDeployment(agent.DeploymentConfig{
Definition: definition, Dispatcher: dispatcher,
ImplementationDigest: agent.ComputeDigest([]byte("example-echo-implementation")),
ConfigurationDigest: agent.ComputeDigest([]byte("example-echo-configuration")),
})
if err != nil {
panic(err)
}
// These are Host-selected bounds for this small episode, not universal defaults.
// The ledger lifetime is this invocation; ReleaseTree does not erase it.
engine, err := agent.NewEngine(agent.EngineConfig{
TreeCommitter: agent.NewMemoryTreeCommitter(),
Budget: agent.Budget{Steps: agent.NewQuota(100), Effects: agent.NewQuota(100), Signals: agent.NewQuota(200)},
TreeLimits: agent.TreeLimits{
MaxDepth: 4, MaxActiveChildren: 4, MaxChildren: agent.NewQuota(16),
MaxTreeProcesses: agent.NewQuota(32), MaxSnapshotBytes: agent.NewQuota(32 << 20),
MaxProcessSnapshotBytes: agent.NewQuota(1 << 20), MaxPendingSignals: 64,
},
})
if err != nil {
panic(err)
}
defer func() {
if closeErr := engine.Close(ctx); closeErr != nil {
panic(closeErr)
}
}()
input, err := definition.Descriptor().EncodeInput(echoInput{Value: "hello"})
if err != nil {
panic(err)
}
process, err := engine.Start(ctx, deployment, input)
if err != nil {
panic(err)
}
result, err := process.Await(ctx)
if err != nil {
panic(err)
}
if joinErr := process.Join(ctx); joinErr != nil {
panic(joinErr)
}
output, ok := result.Output()
if !ok {
panic("completed Result has no Output")
}
value, err := definition.Descriptor().DecodeOutput[echoOutput](output)
if err != nil {
panic(err)
}
inspection, err := engine.InspectTree(ctx, result.ProcessID())
if err != nil {
panic(err)
}
report, found := inspection.Process(result.ProcessID())
if !found {
panic("completed Process is absent from its tree")
}
if err := engine.ReleaseTree(context.Background(), result.ProcessID()); err != nil {
panic(err)
}
fmt.Println(result.Status(), value.Value, dispatcher.attempts.Load())
fmt.Println("inspected:", report.Snapshot.Status(), report.Snapshot.Usage().PreparedEffects)
}
Output: completed hello 1 inspected: completed 1
type Delta ¶
type Delta struct {
// contains filtered or unexported fields
}
Delta is a bounded, best-effort stream increment from one Effect attempt. EffectSequence preserves emitter admission order. Delta is never replayed from a snapshot and never contributes to the authoritative final Output.
func (Delta) AttemptID ¶ added in v0.32.0
func (d Delta) AttemptID() EffectAttemptID
AttemptID correlates this increment with EffectStarted, EffectFinished and DeltaDropped facts. Sequence numbers are scoped to this attempt.
func (Delta) EffectID ¶
EffectID identifies the logical Effect and remains stable across replay attempts.
func (Delta) EffectSequence ¶
EffectSequence returns the one-based emitter admission order within the Effect attempt. Delivered sequences increase; dropped payloads leave gaps. Exhausting the sequence space drops further increments from that attempt.
func (Delta) MarshalJSON ¶
func (Delta) Payload ¶
func (d Delta) Payload() json.RawMessage
Payload returns an independently owned Strategy-defined increment.
func (Delta) TreeIncarnationID ¶
func (d Delta) TreeIncarnationID() (TreeIncarnationID, bool)
TreeIncarnationID returns the active writer that emitted this delta.
func (*Delta) UnmarshalJSON ¶
type DeltaDropped ¶ added in v0.41.0
type DeltaDropped struct {
// contains filtered or unexported fields
}
DeltaDropped reports the number of increments rejected during one Effect attempt because validation failed, the bounded observation queue was full, or the sequence space was exhausted. Count saturates at math.MaxUint64.
func (DeltaDropped) AttemptID ¶ added in v0.41.0
func (d DeltaDropped) AttemptID() EffectAttemptID
func (DeltaDropped) Count ¶ added in v0.41.0
func (d DeltaDropped) Count() uint64
func (DeltaDropped) Valid ¶ added in v0.41.0
func (d DeltaDropped) Valid() bool
type DeltaEmitter ¶
type DeltaEmitter func(payload json.RawMessage)
DeltaEmitter accepts Strategy-owned streaming payloads while Dispatch is active. The Engine validates, orders, bounds, and publishes each payload as a best-effort Delta; concurrent calls are serialized, and delivered EffectSequence values increase with gaps for dropped payloads. It returns no observer error. With no DeltaListener the emitter is nil, so Dispatchers must guard emission with emit != nil. A Dispatcher must join concurrent emissions before returning and must not retain or call emit afterward.
type DeltaListener ¶
type DeltaListener interface {
// OnDelta receives an accepted increment in queue order, sequentially per
// listener and possibly lagging execution. Engine.Close and FlushDeltas wait
// for accepted delivery, so calling either with this context, or a derived
// one, returns [ErrListenerReentrancy] while the invocation is active.
OnDelta(ctx context.Context, delta Delta)
}
DeltaListener observes best-effort Strategy streaming increments. Panics are isolated; all listeners and trees share one Engine queue and delivery worker, so a slow callback delays the others and can cause bounded queue drops. Implementations must return in bounded time. Hosts put network or disk exporters behind their own bounded queue and expose its drops.
type DeltaListenerFunc ¶
type Deployment ¶
type Deployment struct {
// contains filtered or unexported fields
}
Deployment is an immutable binding of one Definition, its optional external Dispatcher, and an exact value reference used by Process snapshots.
func NewDeployment ¶
func NewDeployment(config DeploymentConfig) (Deployment, error)
NewDeployment freezes a Definition and Dispatcher under explicit implementation and configuration digests, so recovery resolves the exact behavior a snapshot names rather than whatever now shares its name.
func (Deployment) Definition ¶
func (d Deployment) Definition() Definition
func (Deployment) DeploymentRef ¶
func (d Deployment) DeploymentRef() DeploymentRef
func (Deployment) Descriptor ¶
func (d Deployment) Descriptor() Descriptor
func (Deployment) Valid ¶
func (d Deployment) Valid() bool
Valid checks the frozen binding without invoking user code. The Engine checks the live Definition contract at startup and restoration boundaries.
type DeploymentConfig ¶
type DeploymentConfig struct {
Definition Definition
// Dispatcher interprets this Definition's external Effects. Nil binds a
// Definition that uses only Framework Effects; a dispatcher-targeted Effect
// then fails admission before any Effect in its Step can execute.
Dispatcher Dispatcher
ImplementationDigest Digest
// ConfigurationDigest identifies all frozen behavior-affecting Definition
// and Dispatcher configuration, including each finite or unlimited Strategy
// quota. Quota JSON preserves that distinction for configuration hashing.
// Child bindings are not restated here: NewDeployment folds the ones the
// Definition reports through ChildDeployments into the DeploymentRef.
ConfigurationDigest Digest
}
DeploymentConfig contains the complete behavior binding of one Deployment. The digests must cover the exact code artifact and all frozen dispatcher or Strategy configuration that can affect execution or restoration.
type DeploymentRef ¶
type DeploymentRef struct {
// contains filtered or unexported fields
}
DeploymentRef is the immutable value identity of one exact Definition implementation and frozen execution configuration. It contains no registry pointer and is sufficient to reject restore against a different Deployment.
func (DeploymentRef) BindingsDigest ¶ added in v0.41.0
func (d DeploymentRef) BindingsDigest() Digest
BindingsDigest identifies the child Deployments the definition binds, as reported by Definition.ChildDeployments.
func (DeploymentRef) ConfigurationDigest ¶
func (d DeploymentRef) ConfigurationDigest() Digest
ConfigurationDigest returns the frozen behavior-affecting configuration identity, including dispatcher configuration.
func (DeploymentRef) ContractDigest ¶
func (d DeploymentRef) ContractDigest() Digest
func (DeploymentRef) Digest ¶
func (d DeploymentRef) Digest() Digest
func (DeploymentRef) ImplementationDigest ¶
func (d DeploymentRef) ImplementationDigest() Digest
func (DeploymentRef) JSONSchemaAlias ¶ added in v0.17.0
func (DeploymentRef) JSONSchemaAlias() any
func (DeploymentRef) MarshalJSON ¶
func (d DeploymentRef) MarshalJSON() ([]byte, error)
func (DeploymentRef) Name ¶
func (d DeploymentRef) Name() string
func (DeploymentRef) String ¶
func (d DeploymentRef) String() string
func (*DeploymentRef) UnmarshalJSON ¶
func (d *DeploymentRef) UnmarshalJSON(data []byte) error
func (DeploymentRef) Valid ¶
func (d DeploymentRef) Valid() bool
type DeploymentResolver ¶
type DeploymentResolver interface {
// Resolve returns the immutable binding for exactly reference, without
// falling back by name. Missing and mismatched bindings are errors.
Resolve(reference DeploymentRef) (Deployment, error)
}
DeploymentResolver performs one bounded, deterministic, context-free lookup of an exact immutable Deployment for a child named by runtime input; the Engine never consults it for a Deployment's own static bindings. The Engine accepts only a result whose reference exactly matches the requested reference. Implementations must be safe for concurrent use, must not perform remote I/O, and must not re-enter any Process. Routing and caller-specific selection happen before an exact DeploymentRef reaches this contract.
type Descriptor ¶
type Descriptor struct {
// contains filtered or unexported fields
}
Descriptor is an immutable Definition contract. It contains no executable behavior or Deployment configuration.
func NewDescriptor ¶
func NewDescriptor(config DescriptorConfig) (Descriptor, error)
Example ¶
package main
import (
"fmt"
"github.com/Tangerg/scope/agent"
)
func main() {
schema, err := agent.SchemaFor[string]()
if err != nil {
panic(err)
}
descriptor, err := agent.NewDescriptor(agent.DescriptorConfig{
Name: "example.echo",
Description: "Returns the supplied text.",
InputSchema: schema,
OutputSchema: schema,
})
if err != nil {
panic(err)
}
fmt.Println(descriptor.Name(), descriptor.Valid())
}
Output: example.echo true
func (Descriptor) DecodeOutput ¶
func (d Descriptor) DecodeOutput[T any](output Payload) (T, error)
DecodeOutput validates output against this Descriptor's authoritative output schema and strictly decodes it into T.
func (Descriptor) Description ¶
func (d Descriptor) Description() string
func (Descriptor) Digest ¶
func (d Descriptor) Digest() Digest
func (Descriptor) EncodeInput ¶
func (d Descriptor) EncodeInput[T any](value T) (Payload, error)
EncodeInput converts value into a Payload and validates it against this Descriptor's authoritative input schema.
func (Descriptor) InputSchema ¶
func (d Descriptor) InputSchema() Schema
func (Descriptor) JSONSchemaAlias ¶ added in v0.18.0
func (Descriptor) JSONSchemaAlias() any
func (Descriptor) MarshalJSON ¶
func (d Descriptor) MarshalJSON() ([]byte, error)
func (Descriptor) Name ¶
func (d Descriptor) Name() string
func (Descriptor) OutputSchema ¶
func (d Descriptor) OutputSchema() Schema
func (Descriptor) SignalSchema ¶ added in v0.29.0
func (d Descriptor) SignalSchema() Schema
SignalSchema returns the unaddressed input contract; false rejects all input.
func (*Descriptor) UnmarshalJSON ¶
func (d *Descriptor) UnmarshalJSON(data []byte) error
func (Descriptor) Valid ¶
func (d Descriptor) Valid() bool
func (Descriptor) ValidateInput ¶
func (d Descriptor) ValidateInput(input Payload) error
func (Descriptor) ValidateOutput ¶
func (d Descriptor) ValidateOutput(output Payload) error
func (Descriptor) ValidateSignal ¶ added in v0.29.0
func (d Descriptor) ValidateSignal(input Payload) error
ValidateSignal checks unaddressed Host input before mailbox admission.
type DescriptorConfig ¶
type DescriptorConfig struct {
// Name is a stable lowercase qualified Definition name.
Name string
// Description states the Definition's behavior for human and model-facing
// discovery without execution-specific state.
Description string
InputSchema Schema
OutputSchema Schema
// SignalSchema declares unaddressed Host input. The zero value rejects all
// unaddressed Signals. Addressed replies belong to their external wait;
// Engine-owned protocol frames never pass through this schema.
SignalSchema Schema
}
DescriptorConfig contains the complete static contract of a Definition. Executable implementation and frozen configuration identity belong to a Deployment, not this contract.
type Digest ¶
type Digest struct {
// contains filtered or unexported fields
}
Digest is a canonical SHA-256 content identity. Its zero value is invalid.
func ComputeDigest ¶
ComputeDigest returns the canonical SHA-256 identity of data. Callers that assemble a Deployment use it for reproducible implementation artifacts and canonical frozen configuration bytes.
Digests identify exact bytes, not semantic JSON equality. Payload constructors normalize open JSON; typed protocol owners hash their deterministic encoding (struct field order and normalized embedded values). TreeSnapshot retains that encoding and its digest together, so durable stores use Digest without rehashing.
func ParseDigest ¶
ParseDigest validates a canonical sha256:<lowercase-hex> identity.
func (Digest) JSONSchemaAlias ¶ added in v0.17.0
func (Digest) JSONSchemaAlias() any
func (Digest) MarshalText ¶
func (*Digest) UnmarshalText ¶
func (Digest) Valid ¶
func (i Digest) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
type Dispatcher ¶
type Dispatcher interface {
// Dispatch performs one frozen Effect. The Settlement must address
// request.ID; a non-nil error means the external outcome is unknown, not
// definitely failed. emit is valid only during this call. The runtime
// cancels ctx, which keeps Host values, when terminal intent reaches this
// Process or an ancestor, and still collects the returned settlement. Panics
// in Dispatch or in interpreting its error are isolated as unknown outcomes.
// Explicit replay errors keep the original cause; Await, durable state, and
// events retain only outcome classifications and bounded diagnostics.
Dispatch(ctx context.Context, request EffectRequest, emit DeltaEmitter) (Settlement, error)
// ReplayPolicy declares, without I/O or mutable side effects, whether this
// exact Effect can be repeated under its original EffectID when restoring
// a pending attempt or through Process.ReplayUnknownEffect. A settled Unknown
// requires an explicit host request; terminal intent forbids starting replay.
// The answer is deterministic for equivalent Effects.
ReplayPolicy(effect Effect) ReplayPolicy
}
Dispatcher executes Strategy-owned Effects outside Execution.Step. The Engine supplies valid requests and non-nil contexts; direct callers must do the same. Rejecting a valid request before external work starts is a definite Failed settlement. Implementations may serve Processes concurrently, must return in bounded time, and must not mutate an Execution or start unowned goroutines.
A transparent decorator preserves the context, request identity, emitter, Settlement, and error of its wrapped Dispatcher. Forwarding a SameIdentity ReplayPolicy is valid only when the added work is safe to repeat under the original identity. Observation counters count dispatch attempts, including replay, not distinct logical operations; the Definition example shows one.
type Effect ¶
type Effect struct {
// contains filtered or unexported fields
}
Effect is an immutable request for an operation outside Execution.Step. Payload is frozen before dispatch and interpreted only by Target's owner. EffectID is deliberately absent because the Engine assigns it during prepare.
func NewChildCancelEffect ¶ added in v0.26.0
NewChildCancelEffect declares cancellation of an exact direct child's subtree. The receipt confirms the recorded intent, not termination or resource release; use a drained child wait before reusing exclusive resources. An already terminal direct child succeeds without changing its result.
func NewChildSignalEffect ¶ added in v0.26.0
func NewChildSignalEffect(childID ProcessID, signal SignalRequest) (Effect, error)
NewChildSignalEffect declares delivery through the child's ordinary mailbox contract. It cannot release an unrelated wait or preempt in-flight external work. Delivery and the parent Effect settlement share one tree acknowledgment. The exact SignalRequest retains its caller-chosen deduplication identity. Cross-tree delivery remains a Host-authorized messaging operation.
func NewChildStartEffect ¶ added in v0.26.0
NewChildStartEffect creates a Framework-owned Effect requesting one independently managed child Process. The Engine derives the child ProcessID; Execution code cannot construct or start the Process directly.
func NewChildWaitEffect ¶ added in v0.26.0
func NewChildWaitEffect(spec ChildWaitSpec) (Effect, error)
NewChildWaitEffect creates a Framework Effect that opens an Engine-owned wait over direct children. Dispatch returns immediately with a WaitID; child work never blocks Execution.Step or holds a prepared Step open.
func NewDispatcherEffect ¶
func NewDispatcherEffect(payload json.RawMessage, required ...Capability) (Effect, error)
NewDispatcherEffect freezes an opaque request and its required capabilities. Insufficient authority rejects the entire Step before dispatch.
func NewWaitEffect ¶ added in v0.26.0
NewWaitEffect creates the Framework Effect that asks the Engine to mint one WaitID for key. ParseWaitOpened reads the minted WaitID from the Signal that acknowledges it; the Execution keeps whatever the wait is about.
func (Effect) MarshalJSON ¶
func (Effect) Payload ¶
func (e Effect) Payload() json.RawMessage
Payload returns an independently owned copy of the operation intent.
func (Effect) RequiredCapabilities ¶
func (e Effect) RequiredCapabilities() CapabilitySet
RequiredCapabilities returns the immutable authority set the Process must possess before this Dispatcher Effect may be prepared.
func (Effect) Target ¶
func (e Effect) Target() EffectTarget
func (*Effect) UnmarshalJSON ¶
type EffectAttemptID ¶ added in v0.32.0
type EffectAttemptID struct {
// contains filtered or unexported fields
}
EffectAttemptID identifies one actual Effect invocation, including replay and invocations after restore. It is observation metadata, not recovery state.
func (EffectAttemptID) JSONSchemaAlias ¶ added in v0.32.0
func (EffectAttemptID) JSONSchemaAlias() any
func (EffectAttemptID) MarshalText ¶ added in v0.32.0
func (e EffectAttemptID) MarshalText() ([]byte, error)
func (*EffectAttemptID) UnmarshalText ¶ added in v0.32.0
func (e *EffectAttemptID) UnmarshalText(text []byte) error
func (EffectAttemptID) Valid ¶ added in v0.32.0
func (i EffectAttemptID) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
type EffectBoundary ¶
type EffectBoundary struct {
// contains filtered or unexported fields
}
EffectBoundary binds an Effect fact to its prospective tree so a Host cannot acknowledge dispatch or settlement independently of recovery state. External dispatch uses a pending permission followed by settlement. Tree-local child controls settle directly, atomically with the recipient's mailbox or intent; they perform no external I/O requiring a pending dispatch permission.
func (EffectBoundary) ContentDigest ¶ added in v0.40.0
func (e EffectBoundary) ContentDigest() (Digest, error)
ContentDigest covers the complete Scope boundary, including its predecessor, sequence, request coordinates, settlement, and prospective recovery cut. It excludes physical dispatch attempts and Host-owned business write sets. Equal identity and content do not authorize historical replay: the store must still atomically check the current writer, head, and sequence.
func (EffectBoundary) Identity ¶ added in v0.40.0
func (e EffectBoundary) Identity() string
Identity is the opaque storage key for this root, Effect, and boundary kind. It survives writer activation; attempts, incarnation, and sequence do not create a second permission or settlement for the same logical Effect stage. Invalid boundaries have an empty identity. Persist the returned key verbatim.
func (EffectBoundary) Kind ¶
func (e EffectBoundary) Kind() EffectBoundaryKind
func (EffectBoundary) PreviousTreeDigest ¶
func (e EffectBoundary) PreviousTreeDigest() Digest
func (EffectBoundary) Request ¶
func (e EffectBoundary) Request() EffectRequest
func (EffectBoundary) Sequence ¶ added in v0.32.0
func (e EffectBoundary) Sequence() uint64
Sequence identifies this commit within its tree incarnation. Retries retain it.
func (EffectBoundary) Settlement ¶
func (e EffectBoundary) Settlement() (Settlement, bool)
func (EffectBoundary) TreeSnapshot ¶
func (e EffectBoundary) TreeSnapshot() TreeSnapshot
func (EffectBoundary) Valid ¶
func (e EffectBoundary) Valid() bool
type EffectBoundaryKind ¶
type EffectBoundaryKind string
EffectBoundaryKind is closed because recovery needs a defined continuation for every acknowledged external Effect boundary.
const ( EffectBoundaryKindInvalid EffectBoundaryKind = "" EffectBoundaryKindPending EffectBoundaryKind = "pending" EffectBoundaryKindSettled EffectBoundaryKind = "settled" EffectBoundaryKindResolved EffectBoundaryKind = "resolved" )
func (EffectBoundaryKind) String ¶
func (e EffectBoundaryKind) String() string
func (EffectBoundaryKind) Valid ¶
func (e EffectBoundaryKind) Valid() bool
type EffectFinished ¶ added in v0.41.0
type EffectFinished struct {
// contains filtered or unexported fields
}
EffectFinished is the immutable settlement observation for one Effect attempt. It does not replace the durable Effect boundary.
func (EffectFinished) AttemptID ¶ added in v0.41.0
func (e EffectFinished) AttemptID() EffectAttemptID
func (EffectFinished) Duration ¶ added in v0.41.0
func (e EffectFinished) Duration() time.Duration
func (EffectFinished) Failure ¶ added in v0.41.0
func (e EffectFinished) Failure() (FailureClassification, bool)
Failure classifies the Dispatcher error that made the outcome Unknown, without diagnostic text. An Unknown returned directly by the Dispatcher has no classification.
func (EffectFinished) SettlementStatus ¶ added in v0.41.0
func (e EffectFinished) SettlementStatus() SettlementStatus
func (EffectFinished) Target ¶ added in v0.41.0
func (e EffectFinished) Target() EffectTarget
func (EffectFinished) Valid ¶ added in v0.41.0
func (e EffectFinished) Valid() bool
type EffectID ¶
type EffectID struct {
// contains filtered or unexported fields
}
EffectID identifies one Effect at a stable Process, Step, and batch index.
func ParseEffectID ¶
func (EffectID) JSONSchemaAlias ¶ added in v0.17.0
func (EffectID) JSONSchemaAlias() any
func (EffectID) MarshalText ¶
func (*EffectID) UnmarshalText ¶
func (EffectID) Valid ¶
func (i EffectID) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
type EffectRequest ¶
type EffectRequest struct {
// contains filtered or unexported fields
}
EffectRequest is the immutable dispatch context prepared by the Engine.
func (EffectRequest) AttemptID ¶ added in v0.38.0
func (e EffectRequest) AttemptID() (EffectAttemptID, bool)
AttemptID identifies this physical Dispatch invocation. Captured requests and durability boundaries describe logical Effects without an active invocation; they return false. Replay keeps ID and receives a fresh AttemptID.
func (EffectRequest) BatchIndex ¶
func (e EffectRequest) BatchIndex() uint32
BatchIndex returns the zero-based declaration order within the Step Effect batch.
func (EffectRequest) DeploymentRef ¶
func (e EffectRequest) DeploymentRef() DeploymentRef
func (EffectRequest) Effect ¶
func (e EffectRequest) Effect() Effect
Effect returns an independently owned copy of the frozen intent.
func (EffectRequest) ID ¶
func (e EffectRequest) ID() EffectID
func (EffectRequest) ProcessID ¶
func (e EffectRequest) ProcessID() ProcessID
func (EffectRequest) Relation ¶
func (e EffectRequest) Relation() ProcessRelation
func (EffectRequest) StepSequence ¶
func (e EffectRequest) StepSequence() uint64
StepSequence returns the one-based Step sequence that declared the Effect.
func (EffectRequest) TreeIncarnationID ¶ added in v0.16.0
func (e EffectRequest) TreeIncarnationID() (TreeIncarnationID, bool)
TreeIncarnationID identifies the active durable writer for observation and correlation. It is not part of the Effect's idempotency identity, which remains ID across restoration.
func (EffectRequest) Valid ¶
func (e EffectRequest) Valid() bool
type EffectResolved ¶ added in v0.41.0
type EffectResolved struct {
// contains filtered or unexported fields
}
EffectResolved records the definite result that replaced an Unknown settlement. It has no duration: adjudication is not an execution attempt.
func (EffectResolved) SettlementStatus ¶ added in v0.41.0
func (e EffectResolved) SettlementStatus() SettlementStatus
func (EffectResolved) Target ¶ added in v0.41.0
func (e EffectResolved) Target() EffectTarget
func (EffectResolved) Valid ¶ added in v0.41.0
func (e EffectResolved) Valid() bool
type EffectStarted ¶ added in v0.41.0
type EffectStarted struct {
// contains filtered or unexported fields
}
func (EffectStarted) AttemptID ¶ added in v0.41.0
func (e EffectStarted) AttemptID() EffectAttemptID
func (EffectStarted) Target ¶ added in v0.41.0
func (e EffectStarted) Target() EffectTarget
func (EffectStarted) Valid ¶ added in v0.41.0
func (e EffectStarted) Valid() bool
type EffectTarget ¶
type EffectTarget string
EffectTarget identifies which of the two execution boundaries owns an Effect. Framework Effects are interpreted by the Engine; Dispatcher Effects remain opaque to the Engine and are interpreted by the Deployment-bound dispatcher.
const ( EffectTargetInvalid EffectTarget = "" EffectTargetFramework EffectTarget = "framework" EffectTargetDispatcher EffectTarget = "dispatcher" )
func (EffectTarget) String ¶
func (e EffectTarget) String() string
func (EffectTarget) Valid ¶
func (e EffectTarget) Valid() bool
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine keeps admission, publication, and execution under one owner because resource reservations and recoverable tree state must describe the same lifecycle. Construct it with NewEngine; its zero value is not usable. An Engine must not be copied because doing so shares its registries while duplicating their synchronization. Operations on a nil Engine report ErrInvalidEngineConfig; read accessors return zero values.
func NewEngine ¶
func NewEngine(config EngineConfig) (*Engine, error)
func (*Engine) CaptureTree ¶
CaptureTree quiesces the tree at Strategy-safe boundaries, commits the cut, and returns that acknowledged snapshot. In-flight Effects settle before the barrier completes. After a runtime failure, read the authoritative head from the Host's commit store; unacknowledged runtime state is never recoverable.
func (*Engine) Close ¶
Close rejects pending starts or restorations, Processes whose result publication or parent/child bookkeeping is incomplete, and trees that still own asynchronous work or a freeze. Process.Join or Run establishes subtree completion; callers must finish every tree and release any freeze before closing the Engine. Once closing begins, the Engine drains accepted Delta delivery and stops observation workers. Canceling ctx stops only this caller's wait; it does not interrupt that owned shutdown, which later and concurrent callers join. Existing handles retain results and RuntimeErrors for later reads.
func (*Engine) FlushDeltas ¶
FlushDeltas provides the ordering barrier needed before publishing a final value that must not overtake accepted streaming observations. Dropped Deltas remain lost because flushing cannot strengthen best-effort delivery. Closing the Engine rejects flushes whose admission check observes closure. A flush already admitted may race with Close, even with no listeners configured.
func (*Engine) InspectTree ¶ added in v0.16.0
InspectTree samples a registered root tree without freezing it or waiting for storage acknowledgment. It remains available during a commit, while a freeze is acquired or held, and after the owner stops or Engine.Close returns. ReleaseTree removes the lookup; an overlapping inspection may return its earlier valid sample. ctx bounds both admission and response waiting. The owner never calls user execution code to construct a report. Dependencies must still return in bounded time: synchronous EventListeners must not query or control their own tree. Query failures are returned as errors; runtime failures are reported through ProcessInspection.RuntimeError.
func (*Engine) ObservationFailures ¶
func (e *Engine) ObservationFailures() ObservationFailures
ObservationFailures returns consistent panic counts and the latest bounded diagnostic for each listener kind. Listener failures cannot veto execution.
func (*Engine) ReleaseTree ¶ added in v0.15.0
ReleaseTree waits for the complete root tree to settle and removes its in-memory registry entries and execution state. It does not cancel work or delete Host persistence. Capture any required TreeSnapshot before releasing. Existing Process handles retain their Result or RuntimeError; Engine.Process no longer finds the released identities, and tree operations return ErrTreeNotFound. Canceling ctx before settlement leaves the tree registered and usable.
func (*Engine) RestoreTree ¶
func (e *Engine) RestoreTree( ctx context.Context, rootDeployment Deployment, snapshot TreeSnapshot, ) (*Process, error)
RestoreTree recreates a complete Process tree from one strict TreeSnapshot. rootDeployment must exactly bind the captured root. Children bound by the root's Deployment, transitively through their own bindings, reuse those bindings; only other exact references are resolved through EngineConfig's DeploymentResolver. Registration is all-or-nothing within this Engine. The Engine reserves every captured identity before resolving children or restoring Execution state. Any failure releases the entire reservation. Committed states and nonterminal prepared candidates must restore through their exact Definition before registration, activation, or Effect dispatch. Interrupted terminal candidates remain inert evidence and are not restored. Both completed and prepared completion outputs must satisfy that Definition's output schema before admission.
Snapshot capabilities, limits, budgets, and usage remain authoritative. Current EngineConfig start defaults do not revoke or rewrite captured grants. The Host must authorize the snapshot before calling RestoreTree; ProcessAdmitter and initialization acknowledgment are not repeated for captured Processes. Captured capacity must fit lifecycle reservations for live Processes and the encoded size of terminal Processes. Restoration rejects insufficient capacity before activating a writer or publishing handles. Later growth requires new capacity admission.
func (*Engine) Run ¶
Run starts one root Process and joins its entire subtree before returning the root's terminal Result. It keeps waiting after ctx cancellation because owned work and required acknowledgments must finish. A runtime failure anywhere in the subtree returns a RuntimeError and no Result, even when the root already completed; its acknowledged result remains available through Process.Await. Ordinary execution failure returns the root's valid Result and nil error.
func (*Engine) Start ¶
Start keeps ctx attached to the resulting tree so Host cancellation and deadlines reach accepted work; execution that outlives a request needs a longer-lived context. An already-canceled context never reserves an identity or invokes Host admission. Insufficient Process or tree snapshot capacity, including mandatory lifecycle growth, returns ErrResourceLimitExceeded before the initial head commits. Later Steps, dispatch permissions, and settlements each require their own capacity admission.
Example (SuccessiveEpisodes) ¶
This example keeps continuation in the embedding Host. Its teaching store atomically links successor admission to the initial recoverable tree. A real database must perform those writes in one transaction. Engine.Start itself remains a fresh start; ambiguous creation is reconciled through that record.
package main
import (
"context"
"errors"
"fmt"
"github.com/Tangerg/scope/agent"
"github.com/Tangerg/scope/agent/strategy/workflow"
)
type episodeState struct {
Revision uint32 `json:"revision"`
Summary string `json:"summary"`
}
func episodeDeployment() (agent.Deployment, error) {
stage, err := workflow.Transform("revise", func(_ context.Context, state episodeState) (episodeState, error) {
if state.Revision >= 3 {
return episodeState{}, errors.New("episode revision bound reached")
}
state.Revision++
return state, nil
})
if err != nil {
return agent.Deployment{}, err
}
definition, err := workflow.NewDefinition(workflow.DefinitionConfig{
Name: "example.episode", Description: "Advance one bounded revision.", Stages: []workflow.Stage{stage},
})
if err != nil {
return agent.Deployment{}, err
}
return episodeBinding(definition)
}
func episodeBinding(definition agent.Definition) (agent.Deployment, error) {
return agent.NewDeployment(agent.DeploymentConfig{
Definition: definition,
ImplementationDigest: agent.ComputeDigest([]byte("example-episode-implementation")),
ConfigurationDigest: agent.ComputeDigest([]byte(definition.Descriptor().Name() + "/revision-bound-3")),
})
}
// The Host chooses an explicit new allocation; unused predecessor budget is
// never refunded. Request equality freezes state, behavior, limits, and authority.
type successorRequest struct {
Predecessor agent.ProcessID `json:"predecessor"`
DeploymentRef agent.DeploymentRef `json:"deployment_ref"`
Input agent.Payload `json:"input"`
Budget agent.Budget `json:"budget"`
TreeLimits agent.TreeLimits `json:"tree_limits"`
Capabilities agent.CapabilitySet `json:"capabilities"`
}
func (s successorRequest) identity() (agent.Digest, error) {
if !s.Predecessor.Valid() || !s.DeploymentRef.Valid() || !s.Input.Valid() || !s.TreeLimits.Valid() || !s.Capabilities.Valid() {
return agent.Digest{}, errors.New("invalid successor request")
}
encoded, err := agent.EncodePayload(s)
if err != nil {
return agent.Digest{}, err
}
return agent.ComputeDigest(encoded.JSON()), nil
}
// This example keeps continuation in the embedding Host. Its teaching store
// atomically links successor admission to the initial recoverable tree. A real
// database must perform those writes in one transaction. Engine.Start itself
// remains a fresh start; ambiguous creation is reconciled through that record.
func main() {
ctx := context.Background()
store := newEpisodeStore()
deployment, err := episodeDeployment()
if err != nil {
panic(err)
}
engine, err := agent.NewEngine(agent.EngineConfig{TreeCommitter: store.trees})
if err != nil {
panic(err)
}
defer func() {
if closeErr := engine.Close(ctx); closeErr != nil {
panic(closeErr)
}
}()
initial, err := agent.EncodePayload(episodeState{Summary: "reviewed plan"})
if err != nil {
panic(err)
}
previous, err := engine.Start(ctx, deployment, initial)
if err != nil {
panic(err)
}
result, err := store.sealEpisode(ctx, previous)
if err != nil {
panic(err)
}
output, present := result.Output()
if !present {
panic("completed episode has no output")
}
state, err := output.Decode[episodeState]()
if err != nil {
panic(err)
}
transfer, err := agent.EncodePayload(state)
if err != nil {
panic(err)
}
treeLimits := agent.DefaultTreeLimits()
treeLimits.MaxPendingSignals = 8
request := successorRequest{
Predecessor: previous.ID(), DeploymentRef: deployment.DeploymentRef(), Input: transfer,
Budget: agent.Budget{Steps: agent.NewQuota(8), Effects: agent.NewQuota(4), Signals: agent.NewQuota(8)},
TreeLimits: treeLimits,
}
host := &episodeHost{store: store}
defer func() {
if closeErr := host.close(ctx); closeErr != nil {
panic(closeErr)
}
}()
next, err := host.start(ctx, deployment, request)
if err != nil {
panic(err)
}
duplicate, err := host.start(ctx, deployment, request)
if err != nil {
panic(err)
}
final, err := next.Await(ctx)
if err != nil {
panic(err)
}
if joinErr := next.Join(ctx); joinErr != nil {
panic(joinErr)
}
finalOutput, present := final.Output()
if !present {
panic("successor has no output")
}
nextState, err := finalOutput.Decode[episodeState]()
if err != nil {
panic(err)
}
fmt.Println("revision:", state.Revision, "->", nextState.Revision)
fmt.Println("same successor:", next.ID() == duplicate.ID())
fmt.Println("new allocations:", store.allocations)
}
// Each Host owns its runtime instances. A replacement Host has no live handles
// and restores the already admitted tree; it does not create another successor.
type episodeHost struct {
store *episodeStore
engines []*agent.Engine
}
func (e *episodeHost) start(ctx context.Context, deployment agent.Deployment, request successorRequest) (*agent.Process, error) {
if deployment.DeploymentRef() != request.DeploymentRef {
return nil, agent.ErrInvalidDeployment
}
if err := deployment.Descriptor().ValidateInput(request.Input); err != nil {
return nil, err
}
attempt, existing, err := e.store.claim(request)
if err != nil {
return nil, err
}
if existing.Valid() {
for _, engine := range e.engines {
if process, present := engine.Process(existing); present {
return process, nil
}
}
}
return e.activate(ctx, deployment, request, attempt, existing)
}
func (e *episodeHost) activate(ctx context.Context, deployment agent.Deployment, request successorRequest, attempt *episodeAttempt, existing agent.ProcessID) (*agent.Process, error) {
engine, err := agent.NewEngine(agent.EngineConfig{
TreeCommitter: attempt, ProcessAdmitter: attempt,
Budget: request.Budget, TreeLimits: request.TreeLimits, Capabilities: request.Capabilities,
})
if err != nil {
return nil, err
}
e.engines = append(e.engines, engine)
if !existing.Valid() {
return engine.Start(ctx, deployment, request.Input)
}
head, present, err := e.store.trees.LoadTree(ctx, existing)
if err != nil {
return nil, err
}
if !present {
return nil, errors.New("successor record has no committed tree")
}
return engine.RestoreTree(ctx, deployment, head)
}
func (e *episodeHost) close(ctx context.Context) error {
var failures []error
for _, engine := range e.engines {
failures = append(failures, engine.Close(ctx))
}
return errors.Join(failures...)
}
Output: revision: 1 -> 2 same successor: true new allocations: 1
func (*Engine) ValidateRestorableTree ¶ added in v0.36.0
func (e *Engine) ValidateRestorableTree( ctx context.Context, rootDeployment Deployment, snapshot TreeSnapshot, ) error
ValidateRestorableTree reports whether this Engine can rebuild snapshot under rootDeployment, using the same prepare pass as RestoreTree: exact Deployment bindings, committed and prepared Execution states, mailbox and wait history, Signal and output schemas, and captured snapshot capacity.
It skips admission, reserves no identity, and activates no writer, so it neither fences the previous writer nor promises that a later RestoreTree succeeds. Retained Unknown settlements are restorable facts; whether to reconcile, replay, or refuse them stays a Host decision.
type EngineConfig ¶
type EngineConfig struct {
// TreeCommitter is required. Publication follows acknowledgment of the
// authoritative tree head, including when the Host selects volatile storage.
TreeCommitter TreeCommitter
// ProcessInitializationAcknowledger optionally accepts initialization outcomes
// before publication. Its acknowledgment is separate from TreeCommitter;
// nil omits this Host acceptance step.
ProcessInitializationAcknowledger ProcessInitializationAcknowledger
// DeploymentResolver binds the exact child references a Deployment does
// not own: children named by runtime input. Same-Deployment recursion and
// children a Deployment binds through Definition.ChildDeployments need no
// resolver. Nil rejects every other child reference.
DeploymentResolver DeploymentResolver
// ProcessAdmitter optionally applies Host policy before root or child
// initialization. Nil admits every start that satisfies Engine constraints.
ProcessAdmitter ProcessAdmitter
// EventListeners receive synchronous Framework facts. An empty slice disables
// delivery. Callbacks must be bounded and must not query or control their tree.
EventListeners []EventListener
// DeltaListeners receive queued, best-effort Strategy increments. An empty
// slice disables delivery. Callbacks run serially and must return so Engine
// shutdown can drain the queue and join its delivery worker.
DeltaListeners []DeltaListener
// A bounded queue prevents slow listeners from retaining unlimited Deltas.
// Zero selects the library default; negative capacities are invalid.
DeltaBufferCapacity int
// Budget grants cumulative work authority to each new root; zero quotas are
// unlimited.
Budget Budget
// TreeLimits is the capacity policy of each new root tree. Lifetime quotas
// and snapshot bytes default to unlimited; depth, active-child, and
// pending-Signal capacity inherit finite defaults.
TreeLimits TreeLimits
// Capabilities grants authority to new roots. Children receive only subsets
// of their parent's captured authority, including after restoration.
Capabilities CapabilitySet
}
EngineConfig keeps scheduling and authority policy outside Deployments so a strategy cannot change its constraints through its behavior binding. Budget, TreeLimits, and Capabilities apply to newly started root trees. RestoreTree retains their captured values; the Host authorizes snapshots before recovery.
type Event ¶
type Event struct {
// contains filtered or unexported fields
}
Event is an immutable, ordered fact published by the Framework. Observers may project or instrument it, but observer failure never changes Process state. Payload is descriptive data, never a Signal or state mutation command.
func (Event) DeltaDropped ¶
func (e Event) DeltaDropped() (DeltaDropped, bool)
func (Event) DeploymentRef ¶
func (e Event) DeploymentRef() DeploymentRef
func (Event) EffectFinished ¶
func (e Event) EffectFinished() (EffectFinished, bool)
func (Event) EffectResolved ¶ added in v0.26.0
func (e Event) EffectResolved() (EffectResolved, bool)
func (Event) EffectStarted ¶
func (e Event) EffectStarted() (EffectStarted, bool)
func (Event) MarshalJSON ¶
func (Event) OccurredAt ¶
func (Event) Payload ¶
func (e Event) Payload() json.RawMessage
Payload returns an independently owned descriptive payload.
func (Event) Phase ¶
func (e Event) Phase() EventPhase
func (Event) ProcessFinished ¶
func (e Event) ProcessFinished() (ProcessFinished, bool)
func (Event) ProcessSequence ¶
ProcessSequence returns the Process-local publication order within one tree runtime activation, starting at one. Restoration starts a new sequence; publication progress is observation state and is not part of a TreeSnapshot.
func (Event) Relation ¶
func (e Event) Relation() ProcessRelation
func (Event) RuntimeStopped ¶ added in v0.16.0
func (e Event) RuntimeStopped() (RuntimeStopped, bool)
func (Event) SignalAccepted ¶
func (e Event) SignalAccepted() (SignalAccepted, bool)
func (Event) StepCommitted ¶
func (e Event) StepCommitted() (StepCommitted, bool)
func (Event) StepFinished ¶
func (e Event) StepFinished() (StepFinished, bool)
func (Event) StepSequence ¶
StepSequence returns the one-based Step sequence and true, or zero and false for a Process fact outside a Step.
func (Event) TreeIncarnationID ¶
func (e Event) TreeIncarnationID() (TreeIncarnationID, bool)
TreeIncarnationID returns the active writer that emitted this event.
func (*Event) UnmarshalJSON ¶
type EventListener ¶
type EventListener interface {
// OnEvent receives one committed or attempted Framework fact in increasing
// ProcessSequence for its Process within one tree runtime activation; a
// restored nonterminal Process starts with EventProcessRestored. Different
// tree runtimes may call the listener concurrently. It runs synchronously on
// the observed tree's owner, so a blocked callback prevents that tree's
// control and shutdown. Querying or controlling that tree with this context,
// or a derived one, returns [ErrListenerReentrancy] until the invocation
// returns. Calls to other trees must still be bounded; distinct owners do not
// prevent cyclic waits between callbacks. Network and disk exporters belong
// behind a Host-owned bounded queue.
OnEvent(ctx context.Context, event Event)
}
EventListener observes ordered Framework facts. Panics are isolated from Process execution and never alter committed state. Implementations must return in bounded time and must not query or control the observed tree.
type EventListenerFunc ¶
EventListenerFunc adapts a plain function to EventListener.
type EventPhase ¶
type EventPhase string
EventPhase distinguishes runtime observations from facts supported by authoritative Process state. Engines publish committed facts only after TreeCommitter acknowledges the resulting tree state. Attempt facts do not assert a committed Process state change.
const ( EventPhaseInvalid EventPhase = "" EventPhaseAttempt EventPhase = "attempt" EventPhaseCommitted EventPhase = "committed" )
func (EventPhase) String ¶
func (e EventPhase) String() string
func (EventPhase) Valid ¶
func (e EventPhase) Valid() bool
type Execution ¶
type Execution interface {
// Step reduces the current private state and the supplied ordered Signal
// prefix into one candidate Transition. It must honor ctx for bounded CPU
// work, perform no I/O, consume no hidden input, and never retain signals.
// An error wrapping a sentinel declared with NewClassifiedError discards
// the candidate and records that sentinel's kind and code with the complete
// error as diagnostic; any other error discards it with
// execution.step.failed. Fail instead commits the candidate and consumption.
Step(ctx context.Context, signals []Signal) (Transition, error)
// Snapshot returns a complete, independently owned state from
// which Definition.Restore can reproduce the current Execution exactly. It
// must fail rather than omit state required for deterministic continuation.
Snapshot() (ExecutionState, error)
}
Execution is the single Strategy-owned state machine inside one Process. Step must be a bounded, deterministic state reduction over the current state and the supplied Signal prefix. It must not perform external I/O, read clock, random or global state, or start ownerless goroutines. External operations are returned as Effects. Snapshot must fail rather than return partial state.
Execution methods require the initialized instance Start or Restore returned. The Engine is the sole caller, supplies a non-nil context, and never invokes Step concurrently for the same Execution. If Step or Snapshot fails, the instance is discarded and rebuilt from the committed ExecutionState. A panic while interpreting a returned error is an execution panic.
type ExecutionState ¶
type ExecutionState struct {
// contains filtered or unexported fields
}
ExecutionState is an immutable envelope owned by one Execution Strategy. The Engine persists and returns Payload without interpreting it. Its zero value is invalid.
func EncodeExecutionState ¶ added in v0.26.0
func EncodeExecutionState[T any](kind string, value T) (ExecutionState, error)
EncodeExecutionState strictly encodes a typed strategy state and seals it with ParseExecutionState. Invalid UTF-8 and duplicate JSON names are rejected.
func ParseExecutionState ¶ added in v0.36.0
func ParseExecutionState(kind string, payload json.RawMessage) (ExecutionState, error)
ParseExecutionState freezes an opaque payload under its owning Strategy kind.
func (ExecutionState) Decode ¶ added in v0.26.0
func (e ExecutionState) Decode[T any](kind string) (T, error)
Decode checks the Strategy kind before decoding its payload into T. Unknown object members are rejected; the Strategy still validates its domain state.
func (ExecutionState) Kind ¶
func (e ExecutionState) Kind() string
func (ExecutionState) MarshalJSON ¶
func (e ExecutionState) MarshalJSON() ([]byte, error)
func (ExecutionState) Payload ¶
func (e ExecutionState) Payload() json.RawMessage
Payload returns an independently owned copy of the opaque Strategy state.
func (*ExecutionState) UnmarshalJSON ¶
func (e *ExecutionState) UnmarshalJSON(data []byte) error
func (ExecutionState) Valid ¶
func (e ExecutionState) Valid() bool
type Failure ¶
type Failure struct {
// contains filtered or unexported fields
}
Failure separates stable codes from diagnostic text so wording changes cannot alter control flow. Its bounded UTF-8 message must survive snapshot JSON and must exclude secrets.
func NewFailure ¶
func NewFailure(kind FailureKind, code, message string) (Failure, error)
NewFailure requires a trimmed UTF-8 message within MaxDiagnosticBytes.
func StepFailure ¶ added in v0.41.0
StepFailure reports the Failure the Engine persists when Execution.Step returns an error wrapping a ClassifiedError. Its message includes the complete wrapped diagnostic. It reports false for an error the Engine records as execution.step.failed, and for cancellation and contained panics, whose classifications the Engine owns.
func (Failure) Classification ¶ added in v0.41.0
func (f Failure) Classification() FailureClassification
func (Failure) JSONSchemaAlias ¶ added in v0.17.0
func (Failure) Kind ¶
func (f Failure) Kind() FailureKind
func (Failure) MarshalJSON ¶
func (*Failure) UnmarshalJSON ¶
type FailureClassification ¶ added in v0.41.0
type FailureClassification struct {
// contains filtered or unexported fields
}
FailureClassification is the stable, diagnostic-free part of a Failure. Observations carry only this part, so they can be recorded and aggregated without exposing diagnostic text.
func (FailureClassification) Code ¶ added in v0.41.0
func (f FailureClassification) Code() string
func (FailureClassification) Kind ¶ added in v0.41.0
func (f FailureClassification) Kind() FailureKind
func (FailureClassification) Valid ¶ added in v0.41.0
func (f FailureClassification) Valid() bool
type FailureKind ¶
type FailureKind string
FailureKind stays independent of retryability so classifying a failure cannot silently select a recovery or business policy.
const ( FailureKindInvalid FailureKind = "" // FailureKindExecution reports a Strategy error or an exhausted execution bound. FailureKindExecution FailureKind = "execution" // FailureKindContract reports an invalid protocol value or authority violation. FailureKindContract FailureKind = "contract" // FailureKindExternal reports a Host boundary error without a contained panic. FailureKindExternal FailureKind = "external" // FailureKindPanic reports a contained callback panic when that boundary produces // a Failure. Storage and API errors retain CallbackPanicError instead; isolated // observation panics remain in ObservationFailures. This kind does not establish // an external operation's outcome or authorize replay. FailureKindPanic FailureKind = "panic" )
func (FailureKind) String ¶
func (f FailureKind) String() string
func (FailureKind) Valid ¶
func (f FailureKind) Valid() bool
type ListenerPanic ¶ added in v0.18.0
type ListenerPanic struct {
ListenerIndex int
ListenerType string
ProcessID ProcessID
Message string
Stack string
}
ListenerPanic is a detached report of one isolated listener failure. ListenerIndex is the zero-based position in EngineConfig.EventListeners or DeltaListeners, and ListenerType is its Go type. Message retains at most 4 KiB of the formatted panic value; Stack retains at most 64 KiB.
type MemoryTreeCommitter ¶ added in v0.32.0
type MemoryTreeCommitter struct {
// contains filtered or unexported fields
}
MemoryTreeCommitter atomically accepts tree commits in volatile memory; an acknowledgment survives only as long as this instance. Share the instance when restoring trees into another Engine. Heads and replay-protection facts are retained for the store lifetime, including after Engine.ReleaseTree, so retention grows with commit count. Discard the store only after all its writers and callers have stopped. Construct it with NewMemoryTreeCommitter.
func NewMemoryTreeCommitter ¶ added in v0.32.0
func NewMemoryTreeCommitter() *MemoryTreeCommitter
NewMemoryTreeCommitter starts a separate volatile ledger; it cannot fence writers or reconcile acknowledgments from an earlier instance.
func (*MemoryTreeCommitter) ActivateTree ¶ added in v0.32.0
func (m *MemoryTreeCommitter) ActivateTree( _ context.Context, activation TreeActivation, ) error
func (*MemoryTreeCommitter) CommitCheckpoint ¶ added in v0.32.0
func (m *MemoryTreeCommitter) CommitCheckpoint( _ context.Context, checkpoint TreeCheckpoint, ) error
func (*MemoryTreeCommitter) CommitEffect ¶ added in v0.32.0
func (m *MemoryTreeCommitter) CommitEffect( _ context.Context, boundary EffectBoundary, ) error
func (*MemoryTreeCommitter) LoadTree ¶ added in v0.32.0
func (m *MemoryTreeCommitter) LoadTree( _ context.Context, rootID ProcessID, ) (TreeSnapshot, bool, error)
type ObservationFailures ¶ added in v0.18.0
type ObservationFailures struct {
// contains filtered or unexported fields
}
ObservationFailures is an immutable snapshot of event loss and listener panics isolated by one Engine. Counts are monotonic and saturate at math.MaxUint64; only the latest panic of each listener kind is retained.
func (ObservationFailures) DeltaListenerPanics ¶ added in v0.18.0
func (o ObservationFailures) DeltaListenerPanics() uint64
func (ObservationFailures) DroppedEvents ¶ added in v0.26.0
func (o ObservationFailures) DroppedEvents() uint64
DroppedEvents counts events omitted because their Process publication sequence exhausted uint64. It never wraps and does not change Process execution.
func (ObservationFailures) EventListenerPanics ¶ added in v0.18.0
func (o ObservationFailures) EventListenerPanics() uint64
func (ObservationFailures) LastDeltaPanic ¶ added in v0.18.0
func (o ObservationFailures) LastDeltaPanic() (ListenerPanic, bool)
func (ObservationFailures) LastEventPanic ¶ added in v0.18.0
func (o ObservationFailures) LastEventPanic() (ListenerPanic, bool)
type Payload ¶ added in v0.26.0
type Payload struct {
// contains filtered or unexported fields
}
Payload is an immutable canonical JSON value. Its zero value is invalid. ParsePayload and EncodePayload copy and normalize their input. Descriptor schemas define its role as Process input or final output; streamed Deltas never become final output implicitly.
func EncodePayload ¶ added in v0.26.0
EncodePayload strictly encodes a typed value into an independently owned Payload. Invalid UTF-8 and duplicate JSON names are rejected, including custom codec output. Custom codecs are responsible for preserving their source values.
Example ¶
package main
import (
"fmt"
"github.com/Tangerg/scope/agent"
)
func main() {
type request struct {
Topic string `json:"topic"`
}
input, err := agent.EncodePayload(request{Topic: "agent runtimes"})
if err != nil {
panic(err)
}
decoded, err := input.Decode[request]()
if err != nil {
panic(err)
}
fmt.Println(decoded.Topic, input.Valid())
}
Output: agent runtimes true
func ParsePayload ¶ added in v0.26.0
func ParsePayload(data json.RawMessage) (Payload, error)
ParsePayload validates one JSON value and returns an independently owned Payload.
func (Payload) Decode ¶ added in v0.26.0
Decode strictly decodes p into a typed value. Unknown object fields are rejected when T is a struct.
func (Payload) IsZero ¶ added in v0.32.0
IsZero distinguishes an absent optional payload from an explicit JSON null.
func (Payload) JSON ¶ added in v0.26.0
func (p Payload) JSON() json.RawMessage
JSON returns an independently owned JSON representation.
func (Payload) JSONSchemaAlias ¶ added in v0.26.0
func (Payload) MarshalJSON ¶ added in v0.26.0
func (*Payload) UnmarshalJSON ¶ added in v0.26.0
type Process ¶
type Process struct {
// contains filtered or unexported fields
}
Process is an Engine-issued handle to one managed execution. Its fields and construction remain private so a caller cannot create a second lifecycle owner. Identity and allocation are immutable; Engine.InspectTree owns live inspection. The zero value and nil receiver are invalid; methods require an Engine-issued handle and may panic otherwise. A finished Process remains a valid handle. Control methods submit requests to the owning tree runtime. Except for RequestCancellation, ctx bounds both submission and response waiting. Once a command enters the runtime queue, canceling ctx does not revoke it. A context already canceled before submission never admits a command. If termination or runtime failure races a queued command, an error may replace its response even after the command took effect. Reconcile delivery through SignalReceipts and effect resolution through the Host's authoritative committer records. Retained snapshot Settlements expose current evidence, not a historical journal.
func (*Process) Await ¶
Await waits for the immutable terminal result and the Engine's immediate parent/child bookkeeping for that termination. Canceling ctx stops only the wait; Process cancellation is explicit or follows the context passed to Start. A runtime failure stops this instance and returns a RuntimeError with no Result. A failed logical execution returns a valid Result and nil error. Descendants may still be settling after this Process's result is ready.
func (*Process) Capabilities ¶
func (p *Process) Capabilities() CapabilitySet
func (*Process) DeliverSignals ¶
func (p *Process) DeliverSignals(ctx context.Context, requests ...SignalRequest) (accepted bool, err error)
DeliverSignals submits one or more immutable Strategy inputs as an ordered, atomic batch: either every new identity is appended in request order or the mailbox remains unchanged. Accepted input queues for the next Strategy-safe Step, including while Paused or waiting for child completion.
Unaddressed input must satisfy Descriptor.SignalSchema and never releases a wait or pause. A current external wait accepts only its answer until satisfied, including while Paused and after an earlier answer in the same batch; any other wait answer returns ErrSignalRejected. Reusing a SignalID with different normalized payload bytes or WaitID, or repeating it within the batch, returns ErrSignalConflict. Identical historical identities are retained without another budget charge or acceptance event. Exceeding mailbox, work-budget, Process snapshot, or tree snapshot capacity returns ErrResourceLimitExceeded.
With nil error, accepted reports whether any new input committed to the authoritative tree head; false means every identity was already accepted. A caller timeout does not revoke an admitted command; retry the identical batch to reconcile uncertain delivery, or inspect SignalReceipts once the Process has terminated.
func (*Process) DeploymentRef ¶
func (p *Process) DeploymentRef() DeploymentRef
func (*Process) Join ¶ added in v0.17.0
Join waits for this Process and every descendant to finish their owned work and required acknowledgments in this runtime. It neither cancels execution nor releases registry entries, and canceling ctx stops only this wait. Execution failure and terminal Unknown settlements still join successfully. A runtime failure in this subtree returns a RuntimeError after its local jobs return, even when this Process published its result first. Join makes no claim that a remote operation or a previous writer has stopped; Strategies wait on children through NewChildWaitEffect instead.
func (*Process) Kill ¶
Kill records the Engine control plane's highest-priority terminal intent. It signals owned work throughout the subtree and collects in-flight Effects before termination. It cannot force a goroutine or a remote operation to stop. A nil error acknowledges the local intent. Await establishes whether the resulting termination committed or this instance stopped with a RuntimeError.
func (*Process) Pause ¶
Pause requests a scheduling pause for a Running or Waiting Process at the next safe Step boundary; a Paused Process returns ErrInvalidProcessControl. An in-flight Effect settles before the pause becomes visible. A committed wait keeps its WaitID and may queue answers, but only Resume releases the pause. An accepted pause discards an unadopted Wait candidate without consuming its Signals. A nil error acknowledges the local intent; Engine.InspectTree reports StatusPaused only after a tree commit acknowledges it.
func (*Process) Relation ¶
func (p *Process) Relation() ProcessRelation
Relation returns the immutable parent/root/depth location assigned by the Engine. It is a root relation for Processes created through Engine.Start.
func (*Process) ReplayUnknownEffect ¶ added in v0.24.0
ReplayUnknownEffect explicitly repeats an uncertain Dispatcher Effect under its original identity and immutable intent when its ReplayPolicy is ReplayPolicySameIdentity. The Dispatcher must reconcile or idempotently repeat the external operation; replay does not claim the earlier attempt did no work.
The Unknown remains authoritative until a definite settlement commits, including across crashes and cancellation. A nil error confirms that settlement; an uncertain attempt returns ErrEffectOutcomeUnknown. Every replay publishes its own attempt facts, and a committed definite result also publishes EventEffectResolved. Canceling ctx stops only the caller's wait. Terminal intent rejects new attempts, and concurrent replay or resolution returns ErrEffectNotPending.
func (*Process) RequestCancellation ¶
RequestCancellation submits a caller-owned cancellation intent. A nil error means the request entered the owning tree runtime's queue, not that the Process reached a safe boundary; ctx cancellation cannot revoke it. The first committed cancellation maps to StatusCanceled with a host-cancellation cause. An in-flight tree commit finishes before the intent applies, so storage latency also delays cancellation. Applying it cancels owned Step, Dispatch, and child-admission contexts throughout the subtree; started external work still settles, planned Effects do not start, and required acknowledgments keep independent contexts. A surviving parent receives the ordinary child-completion Signal.
func (*Process) ResolveUnknownEffect ¶
func (p *Process) ResolveUnknownEffect(ctx context.Context, settlement Settlement) error
ResolveUnknownEffect supplies a definite result after an Effect attempt became unknown. The Engine never converts unknown into retry or success implicitly. Acknowledged resolution publishes EventEffectResolved, not an attempt event. A definite result exceeding Process or tree snapshot capacity returns ErrResourceLimitExceeded without changing the Unknown record or durable head; the caller can then supply a smaller result. Terminal intent or a committed terminal result returns ErrProcessFinished; retained interrupted-batch evidence cannot resume a terminated execution.
func (*Process) Resume ¶
Resume releases an explicit pause. An unanswered current wait returns to Waiting, since Resume satisfies no wait; otherwise the Process becomes Running. A nonterminal Process that is not Paused returns ErrInvalidProcessControl. A nil error acknowledges local resumption; later durable boundaries publish the resumed state.
type ProcessAdmission ¶
type ProcessAdmission struct {
// contains filtered or unexported fields
}
ProcessAdmission is the immutable Framework-owned information supplied to a ProcessAdmitter immediately before one root or child Process starts. It does not expose Input, Execution, Dispatcher, product identity, or Host state.
func (ProcessAdmission) Budget ¶
func (p ProcessAdmission) Budget() Budget
func (ProcessAdmission) Capabilities ¶
func (p ProcessAdmission) Capabilities() CapabilitySet
Capabilities returns the prospective Process's immutable authority set.
func (ProcessAdmission) DeploymentRef ¶
func (p ProcessAdmission) DeploymentRef() DeploymentRef
func (ProcessAdmission) Descriptor ¶
func (p ProcessAdmission) Descriptor() Descriptor
func (ProcessAdmission) Relation ¶
func (p ProcessAdmission) Relation() ProcessRelation
func (ProcessAdmission) Valid ¶
func (p ProcessAdmission) Valid() bool
type ProcessAdmitter ¶
type ProcessAdmitter interface {
// Admit returns nil to accept only the supplied identity and resources, or
// an error to reject this prospective Process before initialization. It
// cannot enlarge Budget or Capabilities.
Admit(ctx context.Context, admission ProcessAdmission) error
}
ProcessAdmitter decides whether one prospective root or child Process may initialize. Implementations may coordinate caller-owned external admission work but must not create a Process, mutate the admission, allocate Framework resources, or re-enter the Engine. They must respect ctx, return in bounded time, and be safe for concurrent calls. Persistence, charging, and business idempotency are implementation responsibilities; a prepared Step may replay the same child admission with the same prospective identity after recovery.
The runtime cancels an active child admission when its parent terminates. Every accepted admission concludes with exactly one ProcessInitializationOutcome when an acknowledger is configured; a child that initializes anyway is published and then terminated with its parent. Restore repeats neither admission nor its outcome for a captured Process.
type ProcessAdmitterFunc ¶
type ProcessAdmitterFunc func(ctx context.Context, admission ProcessAdmission) error
func (ProcessAdmitterFunc) Admit ¶
func (p ProcessAdmitterFunc) Admit(ctx context.Context, admission ProcessAdmission) error
type ProcessFinished ¶ added in v0.41.0
type ProcessFinished struct {
// contains filtered or unexported fields
}
ProcessFinished is the immutable terminal fact carried by a finished Process Event. Usage is the authoritative Framework-owned terminal usage.
func (ProcessFinished) Cause ¶ added in v0.41.0
func (p ProcessFinished) Cause() TerminationCause
func (ProcessFinished) Failure ¶ added in v0.41.0
func (p ProcessFinished) Failure() (FailureClassification, bool)
Failure is present exactly when the Process failed.
func (ProcessFinished) Status ¶ added in v0.41.0
func (p ProcessFinished) Status() Status
func (ProcessFinished) Usage ¶ added in v0.41.0
func (p ProcessFinished) Usage() Usage
func (ProcessFinished) Valid ¶ added in v0.41.0
func (p ProcessFinished) Valid() bool
type ProcessID ¶
type ProcessID struct {
// contains filtered or unexported fields
}
ProcessID is the stable identity of one Engine-owned Process.
func ParseProcessID ¶
func (ProcessID) JSONSchemaAlias ¶ added in v0.17.0
func (ProcessID) JSONSchemaAlias() any
func (ProcessID) MarshalText ¶
func (*ProcessID) UnmarshalText ¶
func (ProcessID) Valid ¶
func (i ProcessID) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
type ProcessInitializationAcknowledger ¶ added in v0.41.0
type ProcessInitializationAcknowledger interface {
// Acknowledge runs before publication, so an
// error rejects initialization without exposing a usable Process.
Acknowledge(ctx context.Context, outcome ProcessInitializationOutcome) error
}
ProcessInitializationAcknowledger lets a Host close each accepted admission, even when initialization fails before a Process exists. Rejecting an initialized outcome prevents publication; accepting it does not guarantee later persistence. Initialization waits for this call, so implementations must be bounded, concurrency-safe, idempotent by admission identity, and must not re-enter the Engine. ctx keeps Host values without cancellation so an accepted admission can finish while its parent terminates; the Host applies its own deadline and reconciles an uncertain acknowledgment by admission identity. Restore produces no outcome.
type ProcessInitializationAcknowledgerFunc ¶ added in v0.41.0
type ProcessInitializationAcknowledgerFunc func( ctx context.Context, outcome ProcessInitializationOutcome, ) error
func (ProcessInitializationAcknowledgerFunc) Acknowledge ¶ added in v0.41.0
func (p ProcessInitializationAcknowledgerFunc) Acknowledge( ctx context.Context, outcome ProcessInitializationOutcome, ) error
type ProcessInitializationOutcome ¶ added in v0.19.0
type ProcessInitializationOutcome struct {
// contains filtered or unexported fields
}
ProcessInitializationOutcome separates initialization acceptance from publication: persistence can still fail after an initialized outcome is accepted.
func (ProcessInitializationOutcome) Admission ¶ added in v0.19.0
func (p ProcessInitializationOutcome) Admission() ProcessAdmission
func (ProcessInitializationOutcome) Failure ¶ added in v0.19.0
func (p ProcessInitializationOutcome) Failure() (Failure, bool)
func (ProcessInitializationOutcome) StartedAt ¶ added in v0.19.0
func (p ProcessInitializationOutcome) StartedAt() (time.Time, bool)
StartedAt returns the observed UTC lifecycle start time recorded before initialization. It is available only after successful initialization and does not identify the later Process publication boundary.
func (ProcessInitializationOutcome) Status ¶ added in v0.19.0
func (p ProcessInitializationOutcome) Status() ProcessInitializationOutcomeStatus
Status is determined by which fact the outcome carries: a failure or the time initialization succeeded.
func (ProcessInitializationOutcome) Valid ¶ added in v0.19.0
func (p ProcessInitializationOutcome) Valid() bool
type ProcessInitializationOutcomeStatus ¶ added in v0.19.0
type ProcessInitializationOutcomeStatus string
const ( ProcessInitializationOutcomeStatusInvalid ProcessInitializationOutcomeStatus = "" ProcessInitializationOutcomeStatusInitialized ProcessInitializationOutcomeStatus = "initialized" ProcessInitializationOutcomeStatusFailed ProcessInitializationOutcomeStatus = "failed" )
func (ProcessInitializationOutcomeStatus) String ¶ added in v0.19.0
func (p ProcessInitializationOutcomeStatus) String() string
func (ProcessInitializationOutcomeStatus) Valid ¶ added in v0.19.0
func (p ProcessInitializationOutcomeStatus) Valid() bool
type ProcessInspection ¶ added in v0.16.0
type ProcessInspection struct {
Snapshot ProcessSnapshot
Work ProcessWork
Stale bool
EffectID EffectID
RuntimeError *RuntimeError
}
ProcessInspection combines an existing execution capture with current work. A stale job is still draining but cannot publish its result. RuntimeError reports an instance failure; its unresolved Effects may exceed the Unknown settlements already present in Snapshot.
type ProcessRelation ¶
type ProcessRelation struct {
// contains filtered or unexported fields
}
ProcessRelation is the immutable location of one Process in an Engine-owned tree. Roots identify themselves as RootID at depth zero. Children have one parent and one stable ChildKey; a Process never has multiple parents.
func (ProcessRelation) ChildKey ¶
func (p ProcessRelation) ChildKey() (ChildKey, bool)
ChildKey returns the parent-scoped logical child identity and true for a child, or zero and false for a root.
func (ProcessRelation) Depth ¶
func (p ProcessRelation) Depth() uint32
Depth returns zero for a root and parent depth plus one for every child.
func (ProcessRelation) IsRoot ¶
func (p ProcessRelation) IsRoot() bool
func (ProcessRelation) ParentID ¶
func (p ProcessRelation) ParentID() (ProcessID, bool)
ParentID returns the direct parent and true for a child, or zero and false for a root.
func (ProcessRelation) ProcessID ¶
func (p ProcessRelation) ProcessID() ProcessID
func (ProcessRelation) RootID ¶
func (p ProcessRelation) RootID() ProcessID
func (ProcessRelation) Valid ¶
func (p ProcessRelation) Valid() bool
type ProcessSnapshot ¶
type ProcessSnapshot struct {
// contains filtered or unexported fields
}
ProcessSnapshot is an immutable diagnostic capture of one Engine-owned Process. Strategy state and Effect payloads remain opaque. A ProcessSnapshot is not a recovery unit; only a complete TreeSnapshot can be restored. An interrupted terminal Process retains its prepared batch as evidence: settled operations keep their actual results, planned operations never run, and the candidate state and input cursor were not adopted. Its JSON keeps only the parent link of its relation, so it decodes only within the TreeSnapshot that supplies the root and depth, and that tree owns the TreeLimits it must satisfy. Engine.InspectTree identifies the acknowledged head of its durable captures.
func (ProcessSnapshot) Budget ¶
func (p ProcessSnapshot) Budget() Budget
func (ProcessSnapshot) Capabilities ¶
func (p ProcessSnapshot) Capabilities() CapabilitySet
func (ProcessSnapshot) CommittedExecutionState ¶
func (p ProcessSnapshot) CommittedExecutionState() ExecutionState
CommittedExecutionState returns the latest committed opaque Strategy state. A prepared candidate, when present, remains an uncommitted Engine detail. Only the owning Definition or its typed inspection helpers may interpret the returned state's payload.
func (ProcessSnapshot) DeploymentRef ¶
func (p ProcessSnapshot) DeploymentRef() DeploymentRef
func (ProcessSnapshot) EffectDiagnostic ¶ added in v0.22.0
func (p ProcessSnapshot) EffectDiagnostic(id EffectID) (Failure, bool)
EffectDiagnostic returns the bounded diagnostic retained for an uncertain dispatch attempt while its prepared Step remains captured, including after restoration or explicit resolution. It does not establish failure of the external operation or authorize replay.
func (ProcessSnapshot) JSON ¶
func (p ProcessSnapshot) JSON() json.RawMessage
JSON returns an independently owned snapshot representation.
func (ProcessSnapshot) MarshalJSON ¶
func (p ProcessSnapshot) MarshalJSON() ([]byte, error)
func (ProcessSnapshot) ProcessID ¶
func (p ProcessSnapshot) ProcessID() ProcessID
func (ProcessSnapshot) Relation ¶
func (p ProcessSnapshot) Relation() ProcessRelation
func (ProcessSnapshot) Result ¶ added in v0.28.0
func (p ProcessSnapshot) Result() (Result, bool)
Result returns the captured immutable terminal outcome, when present. Its persistence authority is that of the enclosing TreeSnapshot transaction.
func (ProcessSnapshot) Settlements ¶ added in v0.28.0
func (p ProcessSnapshot) Settlements() []Settlement
Settlements returns retained Effect outcomes in declaration order. These are execution facts even when cancellation prevented candidate-state adoption. Adopted outcomes move into SignalReceipts until consumed by the Strategy; this is not a historical journal and absence does not imply non-execution.
func (ProcessSnapshot) SignalReceipts ¶ added in v0.17.0
func (p ProcessSnapshot) SignalReceipts() []SignalReceipt
SignalReceipts returns admitted Signal facts in mailbox arrival order. The committed cursor distinguishes consumption from admission, including inputs accepted after a final Step obtained its Signal window. These facts have the same acknowledgment boundary as this snapshot; absence in an older capture does not prove rejection. No mailbox or consumption authority is transferred.
func (ProcessSnapshot) Status ¶
func (p ProcessSnapshot) Status() Status
func (ProcessSnapshot) UnknownEffectIDs ¶ added in v0.16.0
func (p ProcessSnapshot) UnknownEffectIDs() []EffectID
UnknownEffectIDs returns Effects whose captured settlement requires explicit resolution. RuntimeError separately owns outcomes an instance could not confirm. A terminal capture retains unresolved evidence; its Process cannot resume or accept further resolution commands.
func (ProcessSnapshot) Usage ¶ added in v0.16.0
func (p ProcessSnapshot) Usage() Usage
func (ProcessSnapshot) Valid ¶
func (p ProcessSnapshot) Valid() bool
func (ProcessSnapshot) WaitID ¶
func (p ProcessSnapshot) WaitID() (WaitID, bool)
WaitID returns the current unanswered Engine-minted wait identity, including while the captured Process is Paused.
func (ProcessSnapshot) WaitKind ¶ added in v0.16.0
func (p ProcessSnapshot) WaitKind() (WaitKind, bool)
WaitKind distinguishes Host input from Framework child completion while the captured Process has an unanswered wait, including while Paused.
type ProcessWork ¶ added in v0.16.0
type ProcessWork string
ProcessWork describes work owned by the current runtime, independently of the lifecycle state in its last acknowledged snapshot. Queued means waiting for an owner turn; a commit or freeze can still block it. Idle means no queued work or job, not a terminal Process or a successful execution.
const ( ProcessWorkInvalid ProcessWork = "" ProcessWorkIdle ProcessWork = "idle" ProcessWorkQueued ProcessWork = "queued" ProcessWorkStep ProcessWork = "step" ProcessWorkRestore ProcessWork = "restore" ProcessWorkDispatch ProcessWork = "dispatch" ProcessWorkChildStart ProcessWork = "child_start" )
func (ProcessWork) String ¶ added in v0.26.0
func (p ProcessWork) String() string
func (ProcessWork) Valid ¶ added in v0.26.0
func (p ProcessWork) Valid() bool
type Quota ¶ added in v0.27.0
type Quota struct {
// contains filtered or unexported fields
}
Quota is an immutable optional upper bound. Its zero value is unlimited; NewQuota constructs a finite bound, including zero. A Quota is policy, never a usage counter or a reservation. Its JSON object preserves this distinction: {"maximum":null} is unlimited and {"maximum":0} permits no work.
Example ¶
unlimited := Quota{}
finite := NewQuota(3)
zero := NewQuota(0)
fmt.Println(unlimited.Allows(10001), finite.Allows(2, 1), finite.Allows(3, 1), zero.Allows(1))
Output: true true false false
func (Quota) Allows ¶ added in v0.27.0
Allows reports whether all quantities fit together, without unsigned overflow. Unlimited quotas impose no bound; callers still own counter and identity overflow.
func (Quota) JSONSchemaAlias ¶ added in v0.27.0
func (Quota) MarshalJSON ¶ added in v0.27.0
func (Quota) Maximum ¶ added in v0.27.0
Maximum returns the finite upper bound, or false for an unlimited quota.
func (*Quota) UnmarshalJSON ¶ added in v0.27.0
type ReplayPolicy ¶
type ReplayPolicy string
ReplayPolicy states whether a Dispatcher can prove that repeating an Effect with the same EffectID is the same logical external operation. It does not claim transactionality or allow replay under a different identity.
const ( ReplayPolicyInvalid ReplayPolicy = "" // ReplayPolicyNever forbids both restored pending replay and explicit // replay of an Unknown. A host can still supply a definite settlement. ReplayPolicyNever ReplayPolicy = "never" ReplayPolicySameIdentity ReplayPolicy = "same_identity" )
func (ReplayPolicy) String ¶
func (r ReplayPolicy) String() string
func (ReplayPolicy) Valid ¶
func (r ReplayPolicy) Valid() bool
type Result ¶
type Result struct {
// contains filtered or unexported fields
}
Result is the immutable terminal outcome of one Process. A failed or canceled execution is represented by Termination, not by Await's error.
func (Result) FinishedAt ¶
FinishedAt returns the observed UTC time of committed termination. Wall-clock adjustments and restoration on another writer can make it earlier than StartedAt.
func (Result) Termination ¶
func (r Result) Termination() Termination
type RuntimeError ¶ added in v0.16.0
type RuntimeError struct {
// contains filtered or unexported fields
}
RuntimeError reports that the active runtime stopped without establishing the requested Process result or subtree completion. It establishes neither logical termination nor permission to replay an uncertain Effect; a result acknowledged before a descendant failed remains available through Process.Await. The Host may reconcile storage and restore its authoritative tree head in another Engine. Only the Engine constructs these errors, and each accessor returns an independent value.
func (*RuntimeError) Error ¶ added in v0.16.0
func (r *RuntimeError) Error() string
func (*RuntimeError) HeadDigest ¶ added in v0.16.0
func (r *RuntimeError) HeadDigest() Digest
HeadDigest identifies the last tree head acknowledged to this instance. The store may have advanced further if a commit response was lost or a new writer acquired ownership; recovery must read the store's authoritative head.
func (*RuntimeError) IncarnationID ¶ added in v0.16.0
func (r *RuntimeError) IncarnationID() TreeIncarnationID
IncarnationID identifies the writer that stopped.
func (*RuntimeError) ProcessID ¶ added in v0.16.0
func (r *RuntimeError) ProcessID() ProcessID
ProcessID identifies the affected Process handle in the stopped instance.
func (*RuntimeError) UnresolvedEffectIDs ¶ added in v0.16.0
func (r *RuntimeError) UnresolvedEffectIDs() []EffectID
UnresolvedEffectIDs returns the sorted, distinct identities whose external outcomes this instance could not adopt or establish durably. Pending Effects that were never dispatched by this instance are not added solely for a failed pending-boundary acknowledgment.
func (*RuntimeError) Unwrap ¶ added in v0.16.0
func (r *RuntimeError) Unwrap() error
Unwrap preserves the capacity or committer failure, including ownership and content conflicts, for errors.Is and errors.As.
type RuntimeStopped ¶ added in v0.41.0
type RuntimeStopped struct {
// contains filtered or unexported fields
}
RuntimeStopped describes an instance failure, not a logical Process termination. It contains only the failure classification, never storage error messages or application payloads. Event carries the Process and incarnation.
func (RuntimeStopped) Failure ¶ added in v0.41.0
func (r RuntimeStopped) Failure() FailureClassification
func (RuntimeStopped) Valid ¶ added in v0.41.0
func (r RuntimeStopped) Valid() bool
type Schema ¶
type Schema struct {
// contains filtered or unexported fields
}
Schema is an immutable, resolved JSON Schema used by Framework input and output contracts. Its zero value is invalid.
func ParseSchema ¶
func ParseSchema(data json.RawMessage) (Schema, error)
ParseSchema validates and resolves one JSON Schema.
func SchemaFor ¶
SchemaFor derives and resolves a JSON Schema for T. Named types contribute package-qualified schema names, so relocating them can change Descriptor digests and exact Deployment bindings even when their JSON fields stay equal.
func (Schema) JSON ¶
func (s Schema) JSON() json.RawMessage
JSON returns an independently owned JSON representation.
func (Schema) MarshalJSON ¶
func (*Schema) UnmarshalJSON ¶
type Settlement ¶
type Settlement struct {
// contains filtered or unexported fields
}
Settlement is the immutable final fact for one EffectID. Payload is owned by the Effect target and becomes opaque Signal data for the next Step. The Engine uses Status only to preserve definite versus unknown execution facts.
func NewSettlement ¶
func NewSettlement(effectID EffectID, status SettlementStatus, payload json.RawMessage) (Settlement, error)
func (Settlement) EffectID ¶
func (s Settlement) EffectID() EffectID
func (Settlement) MarshalJSON ¶
func (s Settlement) MarshalJSON() ([]byte, error)
func (Settlement) Payload ¶
func (s Settlement) Payload() json.RawMessage
Payload returns an independently owned owner-defined result.
func (Settlement) Status ¶
func (s Settlement) Status() SettlementStatus
func (*Settlement) UnmarshalJSON ¶
func (s *Settlement) UnmarshalJSON(data []byte) error
func (Settlement) Valid ¶
func (s Settlement) Valid() bool
type SettlementStatus ¶
type SettlementStatus string
SettlementStatus records whether an Effect definitely succeeded, definitely failed, or has an unknown external result. Unknown never implies safe retry.
const ( SettlementStatusInvalid SettlementStatus = "" SettlementStatusSucceeded SettlementStatus = "succeeded" SettlementStatusFailed SettlementStatus = "failed" SettlementStatusUnknown SettlementStatus = "unknown" )
func (SettlementStatus) String ¶
func (s SettlementStatus) String() string
func (SettlementStatus) Valid ¶
func (s SettlementStatus) Valid() bool
type Signal ¶
type Signal struct {
// contains filtered or unexported fields
}
Signal is the immutable input envelope delivered by the Engine to an Execution. Dispatcher and ordinary wait payloads belong exclusively to the Strategy; Framework composition payloads are decoded only through their public typed helpers. SignalID identifies delivery, while an optional WaitID identifies the Engine-created wait target.
func NewSignal ¶ added in v0.36.0
NewSignal builds one delivery envelope with a canonical payload for Definition tests and conformance cases. Constructing one does not admit or deliver it: at runtime the Engine mints every Signal, and Process.DeliverSignals accepts only a SignalRequest.
func (Signal) EngineOwned ¶ added in v0.22.0
EngineOwned reports whether the Engine produced this Signal as execution evidence. Ordinary delivery cannot use this authority. Like all restored execution facts, a decoded Signal is trustworthy only from trusted storage.
func (Signal) JSONSchemaAlias ¶ added in v0.17.0
func (Signal) MarshalJSON ¶
func (Signal) Payload ¶
func (s Signal) Payload() json.RawMessage
Payload returns an independently owned copy. Strategy-owned payloads are interpreted only by their Strategy; Framework-owned payloads should be read through the corresponding typed parser rather than decoded ad hoc.
func (Signal) Settles ¶ added in v0.24.0
Settles reports whether the Engine minted this delivery for effectID's settlement, without exposing the private delivery identity. A wait opening is identified by the WaitID it addresses instead. As with EngineOwned, decoded Signals must come from trusted storage.
func (*Signal) UnmarshalJSON ¶
type SignalAccepted ¶ added in v0.41.0
type SignalAccepted struct {
// contains filtered or unexported fields
}
SignalAccepted is the immutable delivery identity carried by an accepted Signal Event. WaitID is present only for a wait-addressed Signal.
func (SignalAccepted) SignalID ¶ added in v0.41.0
func (s SignalAccepted) SignalID() SignalID
func (SignalAccepted) Valid ¶ added in v0.41.0
func (s SignalAccepted) Valid() bool
func (SignalAccepted) WaitID ¶ added in v0.41.0
func (s SignalAccepted) WaitID() (WaitID, bool)
type SignalID ¶
type SignalID struct {
// contains filtered or unexported fields
}
SignalID is the stable identity used to deduplicate one Signal delivery.
func ParseSignalID ¶
ParseSignalID validates an externally supplied Signal delivery identity. Parsing does not accept or deliver the Signal. The signal:engine: namespace is reserved for Engine-generated delivery and cannot be used in SignalRequest.
func (SignalID) JSONSchemaAlias ¶ added in v0.17.0
func (SignalID) JSONSchemaAlias() any
func (SignalID) MarshalText ¶
func (*SignalID) UnmarshalText ¶
func (SignalID) Valid ¶
func (i SignalID) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
type SignalReceipt ¶ added in v0.17.0
type SignalReceipt struct {
// contains filtered or unexported fields
}
SignalReceipt is an immutable admission and consumption fact from a ProcessSnapshot. Consumed payload bytes are released while their normalized digest and recipient WaitID remain available for duplicate reconciliation. Its Process owner and committer boundary are supplied by that snapshot.
func (SignalReceipt) ArrivalSequence ¶ added in v0.17.0
func (s SignalReceipt) ArrivalSequence() uint64
func (SignalReceipt) Consumed ¶ added in v0.17.0
func (s SignalReceipt) Consumed() bool
Consumed reports committed consumption, not delivery to a candidate Step.
func (SignalReceipt) ID ¶ added in v0.17.0
func (s SignalReceipt) ID() SignalID
func (SignalReceipt) Matches ¶ added in v0.17.0
func (s SignalReceipt) Matches(request SignalRequest) bool
Matches reports whether this receipt proves admission of the external request. Internal wait-opening and child-wait settlement Signals cannot prove external admission. A different WaitID or normalized payload remains an identity conflict.
func (SignalReceipt) PayloadDigest ¶ added in v0.17.0
func (s SignalReceipt) PayloadDigest() Digest
func (SignalReceipt) PendingSignal ¶ added in v0.17.0
func (s SignalReceipt) PendingSignal() (Signal, bool)
PendingSignal returns the retained input only while it remains unconsumed.
func (SignalReceipt) WaitID ¶ added in v0.17.0
func (s SignalReceipt) WaitID() (WaitID, bool)
type SignalRequest ¶
type SignalRequest struct {
// contains filtered or unexported fields
}
SignalRequest is an immutable request to deliver Strategy-owned input to a Process. ID supplies caller-stable deduplication. WaitID is zero for ordinary next-boundary input and Engine-minted for an external wait answer.
func NewSignalRequest ¶
func NewSignalRequest(id SignalID, waitID WaitID, payload json.RawMessage) (SignalRequest, error)
NewSignalRequest requires a caller-chosen SignalID outside the reserved signal:engine: namespace, so a Host retry after an ambiguous failure stays one logical delivery rather than a second answer.
func (SignalRequest) ID ¶
func (s SignalRequest) ID() SignalID
ID returns the stable delivery and deduplication identity.
func (SignalRequest) JSONSchemaAlias ¶ added in v0.18.0
func (SignalRequest) JSONSchemaAlias() any
func (SignalRequest) MarshalJSON ¶ added in v0.18.0
func (s SignalRequest) MarshalJSON() ([]byte, error)
func (SignalRequest) Payload ¶
func (s SignalRequest) Payload() json.RawMessage
Payload returns an independently owned Strategy-defined value.
func (*SignalRequest) UnmarshalJSON ¶ added in v0.18.0
func (s *SignalRequest) UnmarshalJSON(data []byte) error
func (SignalRequest) Valid ¶
func (s SignalRequest) Valid() bool
func (SignalRequest) WaitID ¶
func (s SignalRequest) WaitID() (WaitID, bool)
type Status ¶
type Status string
Status is the complete common lifecycle state of a Process. Strategy-specific conditions such as a Planning no-plan result do not add common statuses.
const ( StatusInvalid Status = "" StatusRunning Status = "running" // StatusWaiting identifies a Process awaiting a WaitID-addressed Signal // without an explicit pause. StatusPaused may retain the same unanswered wait. StatusWaiting Status = "waiting" StatusPaused Status = "paused" StatusCompleted Status = "completed" StatusFailed Status = "failed" StatusCanceled Status = "canceled" StatusTimedOut Status = "timed_out" StatusKilled Status = "killed" )
func (Status) MarshalText ¶
func (*Status) UnmarshalText ¶
type StepCommitted ¶ added in v0.41.0
type StepCommitted struct {
// contains filtered or unexported fields
}
func (StepCommitted) Status ¶ added in v0.41.0
func (s StepCommitted) Status() Status
func (StepCommitted) Valid ¶ added in v0.41.0
func (s StepCommitted) Valid() bool
type StepFinished ¶ added in v0.41.0
type StepFinished struct {
// contains filtered or unexported fields
}
StepFinished closes one physical attempt, including discarded candidates. WorkDuration covers Step, Snapshot, and Restore in the worker. AdoptionDelay covers completion delivery and waiting for the tree owner, including barriers. Both use monotonic elapsed time, independent of lifecycle wall-clock stamps. Attempts with the same logical StepSequence are paired in activation-local event order; the previous attempt finishes before another starts.
func (StepFinished) AdoptionDelay ¶ added in v0.41.0
func (s StepFinished) AdoptionDelay() time.Duration
func (StepFinished) Status ¶ added in v0.41.0
func (s StepFinished) Status() StepStatus
func (StepFinished) Valid ¶ added in v0.41.0
func (s StepFinished) Valid() bool
func (StepFinished) WorkDuration ¶ added in v0.41.0
func (s StepFinished) WorkDuration() time.Duration
type StepStatus ¶
type StepStatus string
StepStatus reports whether one Step reduction succeeded, and is deliberately narrower than Status: a failed Step does not by itself terminate a Process, because the terminal decision also depends on recorded control intent.
const ( StepStatusInvalid StepStatus = "" StepStatusSucceeded StepStatus = "succeeded" StepStatusFailed StepStatus = "failed" StepStatusDiscarded StepStatus = "discarded" )
func (StepStatus) String ¶
func (s StepStatus) String() string
func (StepStatus) Valid ¶
func (s StepStatus) Valid() bool
type Termination ¶
type Termination struct {
// contains filtered or unexported fields
}
Termination is the immutable result of applying the terminal priority matrix. Only the Engine creates terminal facts from validated control intents and Step outcomes; callers obtain this observation from Result or decode it. Failure owns failed causes and diagnostics; other terminal causes own their control reason. Status is always derived from the cause.
func (Termination) Cause ¶
func (t Termination) Cause() TerminationCause
func (Termination) Failure ¶
func (t Termination) Failure() (Failure, bool)
Failure returns the classified failure for StatusFailed.
func (Termination) JSONSchemaAlias ¶ added in v0.17.0
func (Termination) JSONSchemaAlias() any
func (Termination) MarshalJSON ¶
func (t Termination) MarshalJSON() ([]byte, error)
func (Termination) Reason ¶
func (t Termination) Reason() string
Reason returns a bounded diagnostic reason. Completion has an empty reason.
func (Termination) Status ¶
func (t Termination) Status() Status
func (*Termination) UnmarshalJSON ¶
func (t *Termination) UnmarshalJSON(data []byte) error
func (Termination) UnresolvedEffectIDs ¶
func (t Termination) UnresolvedEffectIDs() []EffectID
UnresolvedEffectIDs returns the canonical identities of external operations that may have occurred but were not durably resolved when the tree stopped.
func (Termination) Valid ¶
func (t Termination) Valid() bool
type TerminationCause ¶
type TerminationCause string
const ( TerminationCauseInvalid TerminationCause = "" TerminationCauseCompletion TerminationCause = "completion" TerminationCauseEngineKill TerminationCause = "engine_kill" TerminationCauseParentDeadline TerminationCause = "parent_deadline" TerminationCauseHostDeadline TerminationCause = "host_deadline" TerminationCauseParentCancellation TerminationCause = "parent_cancellation" TerminationCauseHostCancellation TerminationCause = "host_cancellation" TerminationCauseExecutionFailure TerminationCause = "execution_failure" TerminationCauseContractFailure TerminationCause = "contract_failure" TerminationCauseExternalFailure TerminationCause = "external_failure" TerminationCausePanic TerminationCause = "panic" )
func (TerminationCause) String ¶
func (t TerminationCause) String() string
func (TerminationCause) Valid ¶
func (t TerminationCause) Valid() bool
type Transition ¶
type Transition struct {
// contains filtered or unexported fields
}
Transition is an immutable candidate lifecycle intent. The Engine validates ConsumedSignals against the delivered Signal window, captures the candidate ExecutionState, and assigns EffectID values before committing anything.
func Checkpoint ¶ added in v0.28.0
func Checkpoint(consumedSignals uint32) (Transition, error)
Checkpoint commits the consumed Signal prefix and candidate state; the TreeCommitter must acknowledge the complete tree before any further Step or Effect in this Process runs. It creates no settlement Signal.
func Complete ¶
func Complete(consumedSignals uint32, output Payload) (Transition, error)
Complete supplies the final semantic Output. The Engine must validate it against the Definition Descriptor before committing Completed.
func Continue ¶
func Continue(consumedSignals uint32, effects ...Effect) (Transition, error)
Continue keeps the Process schedulable after consuming the stated Signal prefix. Effects are dispatched only after the Engine prepares the Step.
func Fail ¶
func Fail(consumedSignals uint32, failure Failure) (Transition, error)
Fail supplies a stable Strategy-declared failure without making the Execution instance untrusted. A Step error follows the separate discard path.
func Wait ¶
func Wait(consumedSignals uint32, waitID WaitID) (Transition, error)
Wait moves the Process to Waiting for an Engine-minted WaitID already stored in the candidate ExecutionState.
func (Transition) ConsumedSignals ¶
func (t Transition) ConsumedSignals() uint32
func (Transition) Effects ¶
func (t Transition) Effects() []Effect
Effects returns independently owned operation intents in declaration order.
func (Transition) Failure ¶
func (t Transition) Failure() (Failure, bool)
func (Transition) Kind ¶
func (t Transition) Kind() TransitionKind
func (Transition) MarshalJSON ¶
func (t Transition) MarshalJSON() ([]byte, error)
func (Transition) Output ¶
func (t Transition) Output() (Payload, bool)
func (Transition) Reason ¶
func (t Transition) Reason() (string, bool)
func (*Transition) UnmarshalJSON ¶
func (t *Transition) UnmarshalJSON(data []byte) error
func (Transition) Valid ¶
func (t Transition) Valid() bool
Valid requires each kind to carry exactly its own payload field.
func (Transition) WaitID ¶
func (t Transition) WaitID() (WaitID, bool)
type TransitionKind ¶
type TransitionKind string
const ( TransitionKindInvalid TransitionKind = "" TransitionKindContinue TransitionKind = "continue" TransitionKindCheckpoint TransitionKind = "checkpoint" TransitionKindWait TransitionKind = "wait" TransitionKindPause TransitionKind = "pause" TransitionKindComplete TransitionKind = "complete" TransitionKindFail TransitionKind = "fail" )
func (TransitionKind) String ¶
func (t TransitionKind) String() string
func (TransitionKind) Valid ¶
func (t TransitionKind) Valid() bool
type TreeActivation ¶
type TreeActivation struct {
// contains filtered or unexported fields
}
TreeActivation changes writer identity and recovery state together so the previous Engine cannot continue committing after restoration takes ownership.
func (TreeActivation) ContentDigest ¶ added in v0.40.0
func (t TreeActivation) ContentDigest() (Digest, error)
ContentDigest includes both the expected previous writer and head as well as the proposed snapshot. Changing a precondition under a retained identity is a content conflict even when the proposed writer and snapshot are unchanged.
func (TreeActivation) Identity ¶ added in v0.40.0
func (t TreeActivation) Identity() string
Identity is the opaque storage key for this root and proposed writer. Invalid activations have an empty identity. Persist the returned key verbatim.
func (TreeActivation) IncarnationID ¶
func (t TreeActivation) IncarnationID() TreeIncarnationID
func (TreeActivation) PreviousIncarnationID ¶
func (t TreeActivation) PreviousIncarnationID() TreeIncarnationID
func (TreeActivation) PreviousTreeDigest ¶
func (t TreeActivation) PreviousTreeDigest() Digest
func (TreeActivation) TreeSnapshot ¶
func (t TreeActivation) TreeSnapshot() TreeSnapshot
func (TreeActivation) Valid ¶
func (t TreeActivation) Valid() bool
type TreeCheckpoint ¶
type TreeCheckpoint struct {
// contains filtered or unexported fields
}
TreeCheckpoint keeps child publication, input acceptance, and execution progress on the same head so recovery cannot observe partially accepted work. Input, child, and progress cuts can coexist with sibling jobs because those jobs expose only committed Execution state or already recorded Effect intent.
func (TreeCheckpoint) ContentDigest ¶ added in v0.40.0
func (t TreeCheckpoint) ContentDigest() (Digest, error)
ContentDigest covers the Scope checkpoint kind, predecessor, and prospective recovery cut. The sequence belongs to Identity. Hosts retain their business write-set digest separately and check both within the same transaction.
func (TreeCheckpoint) Identity ¶ added in v0.40.0
func (t TreeCheckpoint) Identity() string
Identity is the opaque storage key for this root, writer, and sequence. Repeated snapshot content cannot identify a checkpoint because execution may return to an earlier recovery cut. Invalid checkpoints have an empty identity. Persist the returned key verbatim.
func (TreeCheckpoint) Kind ¶
func (t TreeCheckpoint) Kind() TreeCheckpointKind
func (TreeCheckpoint) PreviousTreeDigest ¶
func (t TreeCheckpoint) PreviousTreeDigest() Digest
func (TreeCheckpoint) Sequence ¶ added in v0.32.0
func (t TreeCheckpoint) Sequence() uint64
Sequence identifies this commit within its tree incarnation. Retries retain it.
func (TreeCheckpoint) TreeSnapshot ¶
func (t TreeCheckpoint) TreeSnapshot() TreeSnapshot
func (TreeCheckpoint) Valid ¶
func (t TreeCheckpoint) Valid() bool
type TreeCheckpointKind ¶
type TreeCheckpointKind string
TreeCheckpointKind distinguishes absent-head creation from writer-fenced updates and classifies the saved recovery cut, not live runtime work. Parked means every nonterminal snapshot is waiting, paused, or blocked on an Unknown settlement. A requested replay may be running against that unchanged cut; TreeInspection reports that live work separately.
const ( TreeCheckpointKindInvalid TreeCheckpointKind = "" TreeCheckpointKindStart TreeCheckpointKind = "start" TreeCheckpointKindChildStart TreeCheckpointKind = "child_start" TreeCheckpointKindSignals TreeCheckpointKind = "signals" TreeCheckpointKindProgress TreeCheckpointKind = "progress" TreeCheckpointKindParked TreeCheckpointKind = "parked" TreeCheckpointKindTerminal TreeCheckpointKind = "terminal" )
func (TreeCheckpointKind) String ¶
func (t TreeCheckpointKind) String() string
func (TreeCheckpointKind) Valid ¶
func (t TreeCheckpointKind) Valid() bool
type TreeCommitter ¶ added in v0.32.0
type TreeCommitter interface {
// ActivateTree must fence the previous writer before restored work can run.
ActivateTree(ctx context.Context, activation TreeActivation) error
// CommitEffect must keep the Effect fact and tree head atomic so recovery
// cannot disagree with the dispatch or settlement that was acknowledged.
CommitEffect(ctx context.Context, boundary EffectBoundary) error
// CommitCheckpoint must compare an absent head for start, or the current
// head otherwise, to prevent publication from overwriting another writer.
CommitCheckpoint(ctx context.Context, checkpoint TreeCheckpoint) error
}
TreeCommitter keeps all recoverable state on one authoritative head so a restored writer cannot race its predecessor. Every commit must atomically compare and advance that head; accepting a duplicate requires identical content and a head that still matches the proposal and its sequence. Hosts own storage, deadlines, and reconciliation when a commit response is lost. The supplied context retains Host values but removes cancellation and deadlines. Hosts must apply an independent bounded storage deadline and a host-owned shutdown signal. Canceling an Await, Join or ReleaseTree wait does not abort a transaction. Hosts release blocked storage through that shutdown signal, return its error, and then Join the stopped runtime. A timeout does not prove that the authoritative head was unchanged and requires reconciliation.
Acknowledgment guarantees installation under this protocol. Survival across store closure or process restart depends on the chosen backend; the Engine never changes its publication or recovery rules for volatile storage.
A start checkpoint requires an absent head and a zero PreviousTreeDigest. The Engine assigns one consecutive Sequence shared by checkpoints and Effects within each incarnation. Start installs sequence 1; activation installs sequence 0 under the new incarnation. Every subsequent commit requires the current incarnation, previous digest, and exactly the next sequence. Hosts atomically retain the sequence with the head and deduplication facts, separately from the portable snapshot and backend revisions. An identical retry succeeds only while its sequence is still current; historical replay must fail even if content cycles. A checkpoint is identified by root, incarnation, and sequence, never by digest. An Effect commit is identified by root, Effect identity, and boundary kind. Each Effect contributes at most one pending, one settled, and one resolved commit for the lifetime of its tree, including across incarnations, so its deduplication fact needs neither the incarnation nor the sequence. A definite replay result uses the resolved boundary; it never commits a second settled boundary over a retained Unknown. Activation replaces writer and head and resets the sequence atomically before restoration can publish a Process. Initialization acknowledgment is separate because failed root initialization has no execution tree to persist.
EffectBoundary, TreeCheckpoint, and TreeActivation own Identity and ContentDigest. All implementations must use those canonical projections for Scope facts; they do not replace storage CAS or a Host business write set.
Example ¶
ctx := context.Background()
shutdown, stop := context.WithCancel(ctx)
defer stop()
committer := &hostTreeCommitter{store: newHostTreeStore(), shutdown: shutdown, timeout: time.Second}
definition, err := newEchoDefinition()
if err != nil {
panic(err)
}
deployment, err := agent.NewDeployment(agent.DeploymentConfig{
Definition: definition, Dispatcher: echoDispatcher{},
ImplementationDigest: agent.ComputeDigest([]byte("host-commit-example")),
ConfigurationDigest: agent.ComputeDigest([]byte("host-commit-config")),
})
if err != nil {
panic(err)
}
engine, err := agent.NewEngine(agent.EngineConfig{TreeCommitter: committer})
if err != nil {
panic(err)
}
input, err := agent.EncodePayload(echoInput{Value: "durable"})
if err != nil {
panic(err)
}
result, err := engine.Run(ctx, deployment, input)
if err != nil {
panic(err)
}
head, exists, err := committer.LoadTree(ctx, result.ProcessID())
if err != nil {
panic(err)
}
fmt.Println(result.Status(), exists && head.Valid())
if err := engine.Close(ctx); err != nil {
panic(err)
}
Output: completed true
type TreeFreezePhase ¶ added in v0.16.0
type TreeFreezePhase string
TreeFreezePhase describes the current scheduling barrier. Acquiring blocks scheduling while in-flight Effects settle. Held also stops completion adoption; an in-flight Step may still be computing against its isolated state.
const ( TreeFreezePhaseInvalid TreeFreezePhase = "" TreeFreezePhaseNone TreeFreezePhase = "none" TreeFreezePhaseAcquiring TreeFreezePhase = "acquiring" TreeFreezePhaseHeld TreeFreezePhase = "held" )
func (TreeFreezePhase) String ¶ added in v0.26.0
func (t TreeFreezePhase) String() string
func (TreeFreezePhase) Valid ¶ added in v0.26.0
func (t TreeFreezePhase) Valid() bool
type TreeIncarnationID ¶
type TreeIncarnationID struct {
// contains filtered or unexported fields
}
TreeIncarnationID identifies the one active writer generation of a durable Process tree. Its zero value is invalid.
func (TreeIncarnationID) JSONSchemaAlias ¶ added in v0.26.0
func (TreeIncarnationID) JSONSchemaAlias() any
func (TreeIncarnationID) MarshalText ¶
func (t TreeIncarnationID) MarshalText() ([]byte, error)
func (*TreeIncarnationID) UnmarshalText ¶
func (t *TreeIncarnationID) UnmarshalText(text []byte) error
func (TreeIncarnationID) Valid ¶
func (i TreeIncarnationID) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
type TreeInspection ¶ added in v0.16.0
type TreeInspection struct {
RootID ProcessID
IncarnationID TreeIncarnationID
HeadDigest Digest
CommitPending bool
Freeze TreeFreezePhase
Stopped bool
Processes []ProcessInspection
}
TreeInspection is a caller-owned report from one runtime owner turn. Snapshots and HeadDigest come from the last head this instance acknowledged; storage may have advanced further, so recovery must load the authoritative stored tree. Work and barriers describe the sampling turn and may be newer than the snapshots. Stopped means the owner has exited after draining its work. Reports carry no recovery or scheduling authority and are not a persistence schema.
func (TreeInspection) Process ¶ added in v0.16.0
func (t TreeInspection) Process(processID ProcessID) (ProcessInspection, bool)
type TreeLimits ¶
type TreeLimits struct {
// MaxSnapshotBytes bounds the encoded tree, including completed descendants.
// Admission includes each live Process's lifecycle and Framework reservations.
// The per-Process overhead described by MaxProcessSnapshotBytes accumulates
// across live members; terminal members contribute only their encoded size.
// A finite zero denies every new tree snapshot admission.
MaxSnapshotBytes Quota `json:"max_snapshot_bytes"`
// MaxProcessSnapshotBytes bounds each encoded Process snapshot, including
// retained history. Admission of a live Process also reserves worst-case
// control, termination, diagnostic, and Framework settlement growth; control
// strings alone reserve about 144 KiB after JSON escaping. Terminal Processes
// need only their encoded size. A finite zero denies every new admission.
MaxProcessSnapshotBytes Quota `json:"max_process_snapshot_bytes"`
// MaxPendingSignals bounds each Process's unconsumed mailbox suffix and the
// suffix after its prepared Step consumes inputs and appends settlements.
// Every arriving Signal preserves both bounds regardless of its source.
// Capacity must fit the uint32 consumed-Signal count in one Transition.
MaxPendingSignals uint64 `json:"max_pending_signals"`
// MaxDepth bounds the root-relative depth of any Process.
MaxDepth uint32 `json:"max_depth"`
// MaxChildren bounds the lifetime child count of one Process. A finite
// zero confines new execution to the root.
MaxChildren Quota `json:"max_children"`
// MaxActiveChildren bounds concurrent non-terminal children of one Process.
MaxActiveChildren uint32 `json:"max_active_children"`
// MaxTreeProcesses bounds the lifetime Process count of one tree. A finite
// zero is rejected because a tree includes its root.
MaxTreeProcesses Quota `json:"max_tree_processes"`
}
TreeLimits is the capacity policy shared by every Process of one root tree. Cumulative counts and snapshot bytes default to unlimited; zero depth, active-child capacity, and pending-Signal capacity inherit DefaultTreeLimits. A tree snapshot carries exactly one TreeLimits, so recovery cannot give members different bounds.
func DefaultTreeLimits ¶
func DefaultTreeLimits() TreeLimits
DefaultTreeLimits returns conservative structured-concurrency and mailbox bounds; cumulative counts and snapshot bytes remain unlimited.
func (*TreeLimits) UnmarshalJSON ¶ added in v0.27.0
func (t *TreeLimits) UnmarshalJSON(data []byte) error
func (TreeLimits) Valid ¶
func (t TreeLimits) Valid() bool
type TreeSnapshot ¶
type TreeSnapshot struct {
// contains filtered or unexported fields
}
TreeSnapshot is an immutable, portable capture of one complete Process tree. It carries Framework execution facts, a canonical content digest, and a writer identity. A snapshot may be a prospective commit or an acknowledged head; the operation supplying it defines that guarantee. Validation does not establish storage acknowledgment or current writer ownership. Persistence, transactions, revisions, and cleanup policy remain Host responsibilities.
func ParseTreeSnapshot ¶
func ParseTreeSnapshot(data json.RawMessage) (TreeSnapshot, error)
ParseTreeSnapshot strictly validates the current wire shape and domain constraints of one complete Process tree, in canonical order regardless of input array order. Every open child wait must observe direct children of the Process whose mailbox opened it, pending satisfaction Signals render from the children they answered with, and a retained successful child-start settlement must identify a captured child matching the complete request.
func (TreeSnapshot) Digest ¶
func (t TreeSnapshot) Digest() Digest
func (TreeSnapshot) EffectRequest ¶ added in v0.32.0
func (t TreeSnapshot) EffectRequest(processID ProcessID, id EffectID) (EffectRequest, bool)
EffectRequest returns a frozen request retained in a captured prepared Step, carrying this capture's writer identity and no AttemptID. It can supply typed settlement helpers after a restart but authorizes neither dispatch nor replay. Adopted Steps are not retained, so absence does not prove non-execution.
func (TreeSnapshot) EncodedSize ¶ added in v0.37.0
func (t TreeSnapshot) EncodedSize() int
EncodedSize returns the canonical JSON byte length without copying the snapshot. The zero value has size zero.
func (TreeSnapshot) IncarnationID ¶
func (t TreeSnapshot) IncarnationID() TreeIncarnationID
func (TreeSnapshot) JSON ¶
func (t TreeSnapshot) JSON() json.RawMessage
JSON returns an independently owned tree snapshot representation.
func (TreeSnapshot) MarshalJSON ¶
func (t TreeSnapshot) MarshalJSON() ([]byte, error)
func (TreeSnapshot) ProcessSnapshots ¶
func (t TreeSnapshot) ProcessSnapshots() []ProcessSnapshot
ProcessSnapshots returns immutable captures ordered by depth and ProcessID.
func (TreeSnapshot) RootID ¶
func (t TreeSnapshot) RootID() ProcessID
func (*TreeSnapshot) UnmarshalJSON ¶
func (t *TreeSnapshot) UnmarshalJSON(data []byte) error
func (TreeSnapshot) Valid ¶
func (t TreeSnapshot) Valid() bool
type UnresolvedEffect ¶ added in v0.22.0
type UnresolvedEffect struct {
ProcessID ProcessID `json:"process_id"`
EffectID EffectID `json:"effect_id"`
}
UnresolvedEffect identifies an unsettled external effect at a drained subtree boundary. It is an observation; only the owning Process can settle the Effect.
func (UnresolvedEffect) Valid ¶ added in v0.22.0
func (u UnresolvedEffect) Valid() bool
type Usage ¶
type Usage struct {
// CommittedSteps counts finalized Steps.
CommittedSteps uint64 `json:"committed_steps"`
// PreparedEffects counts stable logical Effect identities, not replay attempts.
PreparedEffects uint64 `json:"prepared_effects"`
// AcceptedSignals counts external and Engine-generated mailbox entries.
AcceptedSignals uint64 `json:"accepted_signals"`
// DroppedDeltas counts increments rejected by validation, the bounded queue,
// or exhausted sequence space. It saturates at math.MaxUint64.
DroppedDeltas uint64 `json:"dropped_deltas"`
}
Usage contains monotonic Framework-owned counters. It deliberately excludes provider pricing and Strategy-specific concepts such as tokens or tool calls. JSON decoding requires every counter, including explicit zeros; partial updates are not execution facts.
func (*Usage) UnmarshalJSON ¶ added in v0.42.0
type WaitID ¶
type WaitID struct {
// contains filtered or unexported fields
}
WaitID identifies one Engine-created wait target owned by a Process. Parsing a WaitID does not create a wait; the Engine rejects identities it did not mint.
func ParseWaitID ¶
func ParseWaitOpened ¶ added in v0.43.0
ParseWaitOpened returns the WaitID that a NewWaitEffect settlement Signal acknowledges, verifying its Engine-owned identity and fixed payload.
func (WaitID) JSONSchemaAlias ¶ added in v0.17.0
func (WaitID) JSONSchemaAlias() any
func (WaitID) MarshalText ¶
func (*WaitID) UnmarshalText ¶
func (WaitID) Valid ¶
func (i WaitID) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
type WaitKey ¶
type WaitKey struct {
// contains filtered or unexported fields
}
WaitKey is an Execution-owned logical key used to associate a requested wait with the WaitID later minted by the Engine.
func ParseWaitKey ¶
func (WaitKey) JSONSchemaAlias ¶ added in v0.17.0
func (WaitKey) JSONSchemaAlias() any
func (WaitKey) MarshalText ¶
func (*WaitKey) UnmarshalText ¶
func (WaitKey) Valid ¶
func (i WaitKey) Valid() bool
Valid reports whether a value satisfies its invariant. For immutable values, constructors and decoders establish content invariants; Valid distinguishes their successful result from the invalid zero value without revalidating text. Caller-mutable values and enums must check their current content instead.
Source Files
¶
- callback_error.go
- callback_panic.go
- capability.go
- child.go
- child_control.go
- child_start.go
- child_wait.go
- definition.go
- delta.go
- delta_stream.go
- deployment.go
- deployment_ref.go
- descriptor.go
- diagnostic.go
- digest.go
- dispatcher.go
- doc.go
- effect.go
- effect_attempt.go
- effect_dispatch.go
- effect_phase.go
- engine.go
- event.go
- event_payload.go
- event_recorder.go
- execution_boundary.go
- execution_state.go
- failure.go
- framework_operation.go
- freeze_barrier.go
- head_writer.go
- identity.go
- job_table.go
- mailbox.go
- memory_tree_committer.go
- name.go
- observation.go
- observation_failure.go
- observation_reentrancy.go
- pause.go
- prepared_step.go
- process.go
- process_admission.go
- process_handle.go
- process_initialization_outcome.go
- process_relation.go
- process_restore.go
- process_snapshot.go
- process_state.go
- publication_ledger.go
- quota.go
- resource.go
- restore_context.go
- run_queue.go
- runtime_error.go
- schema.go
- settlement.go
- signal.go
- signal_receipt.go
- signal_request.go
- snapshot_text_reservation.go
- status.go
- step_commit.go
- step_error.go
- subtree_facts.go
- termination.go
- transition.go
- tree_commit.go
- tree_incarnation.go
- tree_inspection.go
- tree_members.go
- tree_operation.go
- tree_restore.go
- tree_runtime.go
- tree_snapshot.go
- wire.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package agenttest provides deterministic consumer-side fixtures and reusable conformance suites for the Agent Framework's public execution boundaries.
|
Package agenttest provides deterministic consumer-side fixtures and reusable conformance suites for the Agent Framework's public execution boundaries. |
|
examples
|
|
|
autonomous
command
Command autonomous demonstrates an Interaction in which the model chooses a Tool from environment feedback and decides when to stop.
|
Command autonomous demonstrates an Interaction in which the model chooses a Tool from environment feedback and decides when to stop. |
|
composition
command
Command composition demonstrates that direct Engine embedding and a cross-Strategy composed Agent use the same Definition/Execution/Process contracts.
|
Command composition demonstrates that direct Engine embedding and a cross-Strategy composed Agent use the same Definition/Execution/Process contracts. |
|
direct_vs_managed
command
Command direct_vs_managed contrasts a direct model call with the same model capability managed as a recoverable agent Process.
|
Command direct_vs_managed contrasts a direct model call with the same model capability managed as a recoverable agent Process. |
|
evaluator_optimizer
command
Command evaluator_optimizer demonstrates bounded evaluator-optimizer composition with exact managed child Processes.
|
Command evaluator_optimizer demonstrates bounded evaluator-optimizer composition with exact managed child Processes. |
|
orchestrator_workers
command
Command orchestrator_workers demonstrates model-directed task decomposition, deterministic managed worker fan-out, and model synthesis without a collaboration Strategy.
|
Command orchestrator_workers demonstrates model-directed task decomposition, deterministic managed worker fan-out, and model synthesis without a collaboration Strategy. |
|
workflow
command
Command workflow demonstrates an ordered managed Workflow whose Call and Fork Stages create independently recoverable child Processes.
|
Command workflow demonstrates an ordered managed Workflow whose Call and Fork Stages create independently recoverable child Processes. |
|
workflow_patterns
command
Command workflow_patterns demonstrates prompt chaining, routing, parallel sectioning, and parallel voting through one managed Workflow.
|
Command workflow_patterns demonstrates prompt chaining, routing, parallel sectioning, and parallel voting through one managed Workflow. |
|
internal
|
|
|
conformancetest
Package conformancetest captures real Engine boundaries for built-in Strategy tests.
|
Package conformancetest captures real Engine boundaries for built-in Strategy tests. |
|
jsonwire
Package jsonwire owns strict decoding for Agent protocols and persisted state.
|
Package jsonwire owns strict decoding for Agent protocols and persisted state. |
|
panicinfo
Package panicinfo bounds retained diagnostics for observational callbacks.
|
Package panicinfo bounds retained diagnostics for observational callbacks. |
|
Package messaging delivers intermediate input through ordinary Dispatcher Effects.
|
Package messaging delivers intermediate input through ordinary Dispatcher Effects. |
|
run
|
|
|
postgres
module
|
|
|
strategy
|
|
|
collaboration
Package collaboration provides decision-driven multi-agent work.
|
Package collaboration provides decision-driven multi-agent work. |
|
coordination
Package coordination provides bounded coordination through ordinary Agent Definitions.
|
Package coordination provides bounded coordination through ordinary Agent Definitions. |
|
interaction
Package interaction provides the model-directed execution Strategy for the Agent Framework.
|
Package interaction provides the model-directed execution Strategy for the Agent Framework. |
|
internal/childcall
Package childcall owns recoverable child handshake progress.
|
Package childcall owns recoverable child handshake progress. |
|
internal/restore
Package restore owns the Restore protocol shared by built-in strategies: strict state decoding, Definition-consistency validation, and cooperative cancellation around that validation.
|
Package restore owns the Restore protocol shared by built-in strategies: strict state decoding, Definition-consistency validation, and cooperative cancellation around that validation. |
|
internal/stepfail
Package stepfail builds the classified failures built-in strategies end their Steps with, so every strategy normalizes diagnostic text the same way.
|
Package stepfail builds the classified failures built-in strategies end their Steps with, so every strategy normalizes diagnostic text the same way. |
|
planning
Package planning provides goal-directed state planning as an Agent execution strategy.
|
Package planning provides goal-directed state planning as an Agent execution strategy. |
|
planning/goap
Package goap provides deterministic goal-oriented action planning over immutable planning.WorldState values.
|
Package goap provides deterministic goal-oriented action planning over immutable planning.WorldState values. |
|
workflow
Package workflow provides deterministic orchestration of Framework-managed child Processes.
|
Package workflow provides deterministic orchestration of Framework-managed child Processes. |