commands

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package commands executes explicitly registered, model-bound Arc commands. Register composes typed adapters without reflective invocation or a required container. Build freezes metadata and extensions; Execute owns resources and completion, while Validate runs authorization and input validation only. Failed results never publish a response. Cleanup is not proof of rollback.

Index

Examples

Constants

View Source
const ResolvedKey = "resolvedKey"

ResolvedKey is the provider-neutral command key entry.

Variables

View Source
var (
	// ErrInvalidRegistration identifies invalid configuration or input.
	ErrInvalidRegistration = errors.New("invalid command registration")
	// ErrDuplicate identifies duplicate types, identities, options or extensions.
	ErrDuplicate = errors.New("duplicate command registration")
	// ErrFrozen identifies mutation after a successful Build.
	ErrFrozen = errors.New("command registry frozen")
	// ErrMissingHandler identifies an absent supported handler.
	ErrMissingHandler = errors.New("command handler missing")
	// ErrResponseType identifies an incompatible or unknown response contract.
	ErrResponseType = errors.New("incompatible command response type")
	// ErrMultipleResponses identifies ambiguous unconsumed return values.
	ErrMultipleResponses = errors.New("multiple command responses")
	// ErrCommitNotConfirmed identifies a completion that cannot report successful persistence.
	ErrCommitNotConfirmed = errors.New("command commit not confirmed")
	// ErrNilReturn identifies a typed nil claimed as a server-consumed return.
	ErrNilReturn = errors.New("nil server-consumed command return")
	// ErrReadModelProvider identifies a command dependency without a registered provider.
	ErrReadModelProvider = errors.New("command read-model provider missing")
	// ErrUnhandledEffect identifies an effect without a consumer.
	ErrUnhandledEffect = errors.New("unhandled command effect")
	// ErrInvalidPreparation identifies a zero preparation or missing payload.
	ErrInvalidPreparation = errors.New("invalid command preparation")
	// ErrNoContext identifies a missing command callback context.
	ErrNoContext = errors.New("command context unavailable")
	// ErrExecutionClosed identifies an expired executor or invocation.
	ErrExecutionClosed = errors.New("command execution closed")
	// ErrConcurrentExecution identifies overlapping use of a bound executor.
	ErrConcurrentExecution = errors.New("concurrent command execution")
	// ErrExecutionFailed identifies a recorded failure in this command or an ancestor.
	ErrExecutionFailed = errors.New("command execution has recorded failures")
	// ErrExecutionMismatch identifies an incompatible frame or validation-only use.
	ErrExecutionMismatch = errors.New("command execution mismatch")
)
View Source
var ErrDecisionProfile = errors.New("arc: protected decision profile refused")

ErrDecisionProfile identifies a command, registry or invocation that does not satisfy the protected decision-read profile. It is returned by Register, Build and, joined with ErrDecisionRead, by ReadDecision.

View Source
var ErrDecisionRead = errors.New("arc: decision read refused")

ErrDecisionRead identifies a refused decision read: an inadmissible profile, an uncertified provider, a failed provider stage, or a read that was not issued to the current invocation. The original cause is retained for errors.Is/As. It is always an infrastructure failure, never an advisory validation finding, so no allowed validation severity can turn a refused read into success.

View Source
var ErrInvalidOperation = errors.New("invalid command operation")

ErrInvalidOperation identifies an unsupported declaration or execution boundary.

Functions

func FrameState

func FrameState[T any](ctx context.Context, inv *Invocation, key StateKey[T]) (T, bool, error)

FrameState reads a slot shared across one command's callbacks, never siblings or children. Membership ends with that command. Values follow RootState ownership.

func ReadModelOrNil

func ReadModelOrNil[M any](ctx context.Context, inv *Invocation) (*M, error)

ReadModelOrNil returns nil only for a valid key with no instance. The returned pointer owns a shallow value copy; reference-valued model fields remain borrowed.

func Register

func Register[C any](r Registrar, options ...Option[C]) error

Register composes one handler and ordered typed options. Without a handler it supports exactly Handle(context.Context) error through Go's method set.

Example
package main

import (
	"context"
	"fmt"

	"github.com/cratis/arc.go/commands"
)

type ClearLocalCache struct{}

func (ClearLocalCache) Handle(context.Context) error { return nil }
func main() {
	var registry commands.Registry
	if err := commands.Register[ClearLocalCache](&registry); err != nil {
		panic(err)
	}
	pipeline, err := registry.Build(commands.PipelineOptions{})
	if err != nil {
		panic(err)
	}
	result, err := pipeline.Execute(context.Background(), ClearLocalCache{})
	if err != nil {
		panic(err)
	}
	_, present := result.Response()
	fmt.Println(result.IsSuccess(), present)
}
Output:
true false

func RegisterOperation

func RegisterOperation[O ExecutableOperation[D], D any](r *Registry, dependencies Factory[D], keys ...di.Key) error

RegisterOperation registers exact O without activating services. Pointer/value types are distinct. dependencies resolves the COMPLETE execution/compensation bundle before any consumer or operation runs. Nil bundles are invalid. Empty struct bundles support no-dependency operations; a factory is still required. Optional DI keys are copied and checked against the existing catalog at Build. Business methods are invoked through typed calls, never reflection. A present but malformed Compensate method is a registration error, not an absent reversal.

Example
package main

import (
	"context"
	"fmt"

	"github.com/cratis/arc.go/commands"
	"github.com/cratis/arc.go/execution"
)

type seatReservations interface {
	Reserve(context.Context, string, string) error
	CancelOwned(context.Context, string) error
}

type reserveSeat struct{ ReservationID, SeatID string }

func (reserveSeat) CommandOperation() {}
func (o reserveSeat) Execute(ctx context.Context, service seatReservations) error {
	return service.Reserve(ctx, o.ReservationID, o.SeatID)
}
func (o reserveSeat) Compensate(ctx context.Context, service seatReservations, _ commands.OperationFailure) error {
	return service.CancelOwned(ctx, o.ReservationID)
}

type bookSeat struct{ ReservationID, SeatID string }

func (c bookSeat) Handle(context.Context) (commands.Outcome[string], error) {
	return commands.Respond(c.ReservationID, reserveSeat(c)), nil
}

type reservationService struct{ calls int }

func (s *reservationService) Reserve(context.Context, string, string) error { s.calls++; return nil }
func (*reservationService) CancelOwned(context.Context, string) error       { return nil }

func main() {
	service := &reservationService{} // Application-owned, borrowed by this pipeline.
	var registry commands.Registry
	if err := commands.RegisterOperation[reserveSeat, seatReservations](&registry,
		func(context.Context, *execution.Scope) (seatReservations, error) { return service, nil }); err != nil {
		panic(err)
	}
	if err := commands.Register[bookSeat](&registry, commands.Handle(bookSeat.Handle), commands.WithOperations[bookSeat]()); err != nil {
		panic(err)
	}
	pipeline, err := registry.Build(commands.PipelineOptions{})
	if err != nil {
		panic(err)
	}
	command := bookSeat{"reservation-1", "seat-2"}
	_, _ = command.Handle(context.Background()) // Declares work only.
	fmt.Println("before pipeline:", service.calls)
	result, err := commands.Execute[string](context.Background(), pipeline, command)
	if err != nil {
		panic(err)
	}
	response, _ := result.Response()
	recovery, _ := result.Recovery()
	fmt.Println(response, result.IsSuccess(), service.calls, recovery.StartedCount)
}
Output:
before pipeline: 0
reservation-1 true 1 1

func RegisterReadModelProvider

func RegisterReadModelProvider[M any](r *Registry, name string, ownership ReadModelOwnership, resolve func(context.Context, *Invocation, string) (ReadModelInstance[M], error)) error

RegisterReadModelProvider binds the exact non-pointer struct M for command dependencies, not public queries. Declared owners beat fallbacks; explicit overrides beat both. Duplicate claims at any one level fail even if overridden. The resolver is borrowed and must support concurrent independent executions. It may use FrameState for provider-specific caches; the core does not cache or infer coordinates/read modes. ResolveReadModel always validates the command key.

Example
package main

import (
	"context"
	"fmt"

	"github.com/cratis/arc.go/commands"
)

type commandModel struct{ Name string }
type keyedCommand struct {
	ID string `arc:"key"`
}

func main() {
	var registry commands.Registry
	if err := commands.RegisterReadModelProvider[commandModel](&registry, "authors", commands.DeclaredReadModel,
		func(ctx context.Context, inv *commands.Invocation, key string) (commands.ReadModelInstance[commandModel], error) {
			return commands.ReadModelInstance[commandModel]{Value: commandModel{Name: "Ada"}, Exists: key == "ada"}, nil
		}); err != nil {
		panic(err)
	}
	if err := commands.Register[keyedCommand](&registry, commands.Invoke(func(ctx context.Context, inv *commands.Invocation, _ keyedCommand) (string, error) {
		model, err := commands.RequireReadModel[commandModel](ctx, inv)
		return model.Name, err
	})); err != nil {
		panic(err)
	}
	pipeline, err := registry.Build(commands.PipelineOptions{})
	if err != nil {
		panic(err)
	}
	result, err := commands.Execute[string](context.Background(), pipeline, keyedCommand{ID: "ada"})
	if err != nil {
		panic(err)
	}
	name, _ := result.Response()
	fmt.Println(name)
}
Output:
Ada

func RegisterResponseValueHandler

func RegisterResponseValueHandler[T any](r *Registry, name string, factory Factory[ResponseValueHandler], keys ...di.Key) error

RegisterResponseValueHandler limits a consumer to exact concrete T or values implementing interface T, retaining its dynamic predicate. The marker does not establish unconditional no-response metadata.

func ReportCommit

func ReportCommit(ctx context.Context, inv *Invocation, report CompletionReport) error

ReportCommit retains an early persistence observation on the command owner. It checks callback/security continuity and refuses validation-only execution. Later failures or no-work completion cannot erase confirmed/unknown outcomes. This records facts only; it neither commits nor grants commit authority.

func RequireReadModel

func RequireReadModel[M any](ctx context.Context, inv *Invocation) (M, error)

RequireReadModel resolves M and reports absent instances as dependencyUnavailable. It never manufactures a successful zero-valued dependency after a failed read.

func RootState

func RootState[T any](ctx context.Context, inv *Invocation, key StateKey[T]) (T, bool, error)

RootState reads a slot shared only by this root's bound nested command tree. Values are borrowed, not cloned: their owner must guard mutable contents and recheck Invocation.Execution before using a retained capability. Membership is discarded at execution end. Every access checks callback/security continuity.

func SetFrameState

func SetFrameState[T any](ctx context.Context, inv *Invocation, key StateKey[T], value T) error

SetFrameState replaces a frame-local slot after checking callback admission.

func SetRootState

func SetRootState[T any](ctx context.Context, inv *Invocation, key StateKey[T], value T) error

SetRootState replaces a root slot. No initializer or user callback runs under the state lock. It does not confer completion authority on an operation scope.

func VerifyDecision

func VerifyDecision(ctx context.Context, inv *Invocation, read *DecisionRead) (err error)

VerifyDecision refuses a nil, zero, foreign or expired read: one not issued to this command frame, issued to another command or a validation-only run, or whose evidence the provider no longer accepts. It re-checks callback and security continuity before and after the provider check. Checking only a provider token's type or progress does not prove provenance. Every failure wraps ErrDecisionRead.

Types

type AuthorizationFilter

type AuthorizationFilter interface {
	Filter
	IsAuthorizationFilter()
}

AuthorizationFilter runs ahead of ordinary filters, never instead of declarations.

func AsAuthorizationFilter

func AsAuthorizationFilter(filter Filter) AuthorizationFilter

AsAuthorizationFilter categorizes a filter without activating it.

type BuildView

type BuildView struct {
	// contains filtered or unexported fields
}

BuildView is an immutable snapshot of exact registered command types. Its zero value contains no commands; it is safe to retain and read concurrently.

func (BuildView) ContainsCommandType

func (v BuildView) ContainsCommandType(typ reflect.Type) bool

ContainsCommandType reports exact registration membership. Pointer and value types are distinct; nil is never registered.

type CommandContext

type CommandContext struct {
	// contains filtered or unexported fields
}

CommandContext is an immutable metadata snapshot. Command, response and value objects remain borrowed; callers must not mutate or retain them beyond execution.

func ContextFrom

func ContextFrom(ctx context.Context) (CommandContext, bool)

ContextFrom reads the snapshot installed for the current application callback.

func (CommandContext) AllowedSeverity

func (c CommandContext) AllowedSeverity() (validation.Severity, bool)

AllowedSeverity returns the explicit caller allowance, if supplied.

func (CommandContext) Command

func (c CommandContext) Command() any

Command returns the decoded command receiver.

func (CommandContext) CorrelationID

func (c CommandContext) CorrelationID() correlation.ID

CorrelationID returns the inherited or generated correlation.

func (CommandContext) Descriptor

func (c CommandContext) Descriptor() metadata.Command

Descriptor returns a copy-isolated descriptor.

func (CommandContext) IsValidationOnly

func (c CommandContext) IsValidationOnly() bool

IsValidationOnly distinguishes advisory validation from execution.

func (CommandContext) Principal

func (c CommandContext) Principal() identity.Principal

Principal returns immutable identity metadata.

func (CommandContext) ReceivedAt

func (c CommandContext) ReceivedAt() time.Time

ReceivedAt returns this frame's distinct UTC receipt.

func (CommandContext) ResolvedKey

func (c CommandContext) ResolvedKey() (string, bool)

ResolvedKey preserves an authoritative empty key's presence.

func (CommandContext) Response

func (c CommandContext) Response() (any, bool)

Response returns the selected response and its presence.

func (CommandContext) Tenant

func (c CommandContext) Tenant() tenancy.ID

Tenant returns the selected tenant, not evidence of membership.

func (CommandContext) Values

func (c CommandContext) Values() ContextValues

Values returns copied case-insensitive membership.

type CommitDisposition

type CommitDisposition uint8

CommitDisposition describes observed persistence, not command success. A failed command can have committed. No disposition authorizes an automatic retry.

const (
	// NoPersistedWork means no events/writes persisted (including successful checks only).
	NoPersistedWork CommitDisposition = iota
	// NotCommitted means the provider established that work did not commit.
	NotCommitted
	// Committed means the provider confirmed persistence.
	Committed
	// OutcomeUnknown requires reconciliation; persistence may have occurred.
	OutcomeUnknown
	// MixedCommit means observations include both confirmed persistence and other outcomes.
	MixedCommit
)

type CompensatingOperation

type CompensatingOperation[D any] interface {
	Compensate(context.Context, D, OperationFailure) error
}

CompensatingOperation optionally reverses attempted work. D must contain every execution AND compensation dependency; registration preflights the whole bundle. Providers own partial-write, lost-acknowledgment, isolation and idempotency safety.

type CompletionError

type CompletionError struct {
	// Report records persistence independently of Cause.
	Report CompletionReport
	// Cause is the original failure, possibly a joined error.
	Cause error
}

CompletionError preserves a failed operation's cause and persistence report, including failures after confirmed persistence. Unwrap retains errors.Is/As.

func (*CompletionError) Error

func (e *CompletionError) Error() string

Error describes the failure without claiming a rollback.

func (*CompletionError) Unwrap

func (e *CompletionError) Unwrap() error

Unwrap exposes the original error.

type CompletionReport

type CompletionReport struct {
	// Disposition is the provider's observed outcome, never inferred from IsSuccess.
	Disposition CommitDisposition
}

CompletionReport is server-side evidence retained independently of the HTTP envelope and command success. Zero means no persisted work. MixedCommit can represent immediate and deferred effects; Arc does not coordinate providers.

type ContextValues

type ContextValues struct {
	// contains filtered or unexported fields
}

ContextValues owns copied, case-insensitive membership; contained values remain borrowed.

func NewContextValues

func NewContextValues(entries map[string]any) (ContextValues, error)

NewContextValues copies membership, rejecting empty or case-folded duplicate keys.

func (ContextValues) Entries

func (v ContextValues) Entries() map[string]any

Entries returns copied membership, with normalized lower-case keys.

func (ContextValues) Get

func (v ContextValues) Get(name string) (any, bool)

Get performs case-insensitive lookup, preserving empty and nil values.

type ContextValuesProvider

type ContextValuesProvider interface {
	Provide(context.Context, *Invocation) (ContextValues, error)
}

ContextValuesProvider supplies trusted preauthorization metadata. Later providers overwrite earlier membership. Factories must be cheap and honor cancellation.

type DecisionEvidence

type DecisionEvidence interface{ DecisionReads() []*DecisionRead }

DecisionEvidence is implemented by values that carry issued decision reads. A protected command's Provide payload that implements it is verified before Handle runs; *DecisionRead implements it. Arc does not inspect other payload shapes, so a provider-typed decision value should implement this interface. A protected command's evidence must carry at least one issued read. An unmarked or unprotected command may return evidence that carries no reads as an advisory snapshot; any read it does carry is refused, because it was never issued.

type DecisionProfile

type DecisionProfile uint8

DecisionProfile is a command's frozen decision-read profile. The zero value is DecisionsUnmarked, so existing commands keep their behavior.

const (
	// DecisionsUnmarked is the default profile. ReadDecision refuses; a provider
	// should also refuse its decision-read API, as C# Arc does for unmarked
	// commands, while ordinary read-model injection stays advisory.
	DecisionsUnmarked DecisionProfile = iota
	// DecisionsProtected admits ReadDecision: reads are invocation-owned, enrolled
	// with the provider's completion owner and verified before Handle.
	DecisionsProtected
	// DecisionsUnprotected acknowledges advisory, unguarded reads. ReadDecision
	// refuses; a provider serves its detached snapshot instead.
	DecisionsUnprotected
)

func CurrentDecisionProfile

func CurrentDecisionProfile(ctx context.Context, inv *Invocation) (profile DecisionProfile, err error)

CurrentDecisionProfile reports the decision profile of the command that owns this invocation, after checking callback and security continuity. Providers use it to refuse decision reads for unmarked commands and to serve advisory snapshots for unprotected ones.

type DecisionProvider

type DecisionProvider struct {
	// contains filtered or unexported fields
}

DecisionProvider is the opaque identity of one decision-read provider, such as one Chronicle client. Construct it with NewDecisionProvider; the zero value and nil are invalid. Identity is by pointer: two providers never share reads even when their store and namespace names match. It carries no capability itself; it is certified only for a registry it was added to.

func NewDecisionProvider

func NewDecisionProvider() *DecisionProvider

NewDecisionProvider allocates an independent provider identity.

type DecisionRead

type DecisionRead struct {
	// contains filtered or unexported fields
}

DecisionRead is an issued read. Only ReadDecision issues one; a zero or copied value is never accepted. It is borrowed, invocation-owned evidence: verify it with VerifyDecision before relying on it in another callback.

func ReadDecision

func ReadDecision(ctx context.Context, inv *Invocation, target DecisionTarget, source DecisionSource) (read *DecisionRead, err error)

ReadDecision issues a decision read for target within a protected command's invocation. It refuses, before any provider stage, an unmarked or unprotected command, an uncertified provider or a missing completion owner.

One acquisition is shared per command frame and target, including concurrent callers; the first caller's context owns it and canceled waiters do not cancel it. Results, including failures, are cached for the frame and never retried. Every call re-checks the evidence and, outside validation-only execution, re-enrolls it with source's current owner. Validation-only execution uses a separate cache and never enrolls. Every failure wraps ErrDecisionRead.

func (*DecisionRead) DecisionReads

func (r *DecisionRead) DecisionReads() []*DecisionRead

DecisionReads implements DecisionEvidence. A nil read yields one nil entry, which verification refuses.

func (*DecisionRead) Value

func (r *DecisionRead) Value() any

Value returns the provider's opaque evidence. It is not a proof by itself.

type DecisionSource

type DecisionSource interface {
	Admit(context.Context) error
	Acquire(context.Context) (any, error)
	Check(context.Context, any) error
	Enroll(context.Context, any) error
}

DecisionSource is the provider-neutral seam a decision-read provider supplies for one target. Arc calls the stages in order, re-checking callback and security continuity between them, and never holds a lock while calling them. A stage that panics is converted to an error.

Admit refuses unsupported models (for example classified ones) before any acquisition. Acquire returns the provider's opaque read evidence; it must not be nil and must not be manufactured by the application. Check re-validates that evidence (lifetime, target) on every issue and verification. Enroll registers the evidence with the provider's current completion owner so a competing write rejects the whole batch; Arc calls it for every protected issue, including cached ones, and never in validation-only execution.

type DecisionTarget

type DecisionTarget struct {
	// Provider must be certified with Registry.AddDecisionProvider.
	Provider *DecisionProvider
	// Model is the read-model type.
	Model reflect.Type
	// Store, Namespace and Key select the instance within the provider.
	Store, Namespace, Key string
}

DecisionTarget identifies one read. Every field takes part in the invocation cache key, so the same model and key from another provider, store or namespace is a separate read. All fields are required.

type DecodeError

type DecodeError struct{ Cause error }

DecodeError exposes parser causes locally while publishing only safe findings.

func (*DecodeError) Error

func (e *DecodeError) Error() string

Error returns safe malformed-input text.

func (*DecodeError) Unwrap

func (e *DecodeError) Unwrap() error

Unwrap retains inspectable parser/conversion errors.

func (*DecodeError) ValidationResults

func (e *DecodeError) ValidationResults() []validation.Result

ValidationResults returns one safe malformedRequest finding.

type DeferredCommitParticipant

type DeferredCommitParticipant interface {
	Begin(context.Context, *Invocation) error
	Complete(context.Context, *Invocation, Result[any]) (CompletionReport, error)
}

DeferredCommitParticipant reserves ownership before ordinary ExecutionScope Begin callbacks. Complete runs once, after all ordinary Complete callbacks and before resource disposal. It receives their merged outcome and a live bounded cleanup context, even after a failed Begin. Validate never activates it.

Begin must not connect or activate tenant resources before authorization; use lazy binding in the provider. Complete must roll back open work on failure, report the provider's actual disposition even when returning an error, and never retry an ambiguous commit. Domain rejections use validation-bearing errors. Use ReportCommit for early completion so later callback failures retain evidence.

type Details

type Details struct {
	// CorrelationID is echoed unchanged; transport normalization happens separately.
	CorrelationID concepts.UUID
	// Completion is server-side persistence evidence, never serialized in the envelope.
	Completion CompletionReport `json:"-"`

	// Authorized states the final authorization decision.
	Authorized bool
	// ValidationResults are retained findings after severity filtering.
	ValidationResults []validation.Result
	// ExceptionMessages are client-safe messages; nonempty means exception failure.
	ExceptionMessages []string
	// ExceptionStackTrace is empty outside explicit development exposure.
	ExceptionStackTrace string
	// AuthorizationFailureReason is safe client-visible denial feedback.
	AuthorizationFailureReason string
	// contains filtered or unexported fields
}

Details configures a finalized result. Zero denies authorization. Use Success or WithResponse for success. Arrays are copied; validation State remains borrowed.

type ExecutableOperation

type ExecutableOperation[D any] interface {
	Operation
	Execute(context.Context, D) error
}

ExecutableOperation declares one typed, synchronous, error-only business call. Dependencies must be usable concurrently across commands. Arc never retries it.

type ExecuteOptions

type ExecuteOptions struct{ AllowedSeverity *validation.Severity }

ExecuteOptions supplies zero or one caller allowance; command floors still apply.

type Execution

type Execution struct {
	// contains filtered or unexported fields
}

Execution is a callback-scoped view of synchronous command ownership. It is not a transaction or commit verdict. It launches no goroutines and rejects expiry.

func (*Execution) Check

func (e *Execution) Check(ctx context.Context) error

Check verifies callback admission, current frame and security continuity.

func (*Execution) CheckExplicitCommit

func (e *Execution) CheckExplicitCommit(ctx context.Context) error

CheckExplicitCommit refuses early persistence in an operation-capable frame. Refusal is sticky even when ignored. It observes existing callback/security ownership; it grants neither authorization nor commit authority otherwise.

func (*Execution) CheckRecordedFailures

func (e *Execution) CheckRecordedFailures(ctx context.Context) error

CheckRecordedFailures checks callback/security continuity and fails if this frame or an ancestor has already recorded a failure, including an ignored nested Execute result. Advisory nested Validate remains advisory during Execute. This read-only guard is useful before explicit early persistence. It is not an authorization grant or a promise that subsequent callbacks will succeed.

func (*Execution) IsRoot

func (e *Execution) IsRoot() bool

IsRoot reports whether this frame owns root completion.

type ExecutionScope

type ExecutionScope interface {
	Begin(context.Context, *Invocation) error
	Complete(context.Context, *Invocation, Result[any]) (Result[NoResponse], error)
}

ExecutionScope participates once per root Execute. Begin runs in registration order before authorization; Complete runs in reverse order for every entered Begin, including a failing Begin. Completion uses a live bounded cleanup context but observes the already-failed forward result. This is not a rollback contract.

type Factory

type Factory[T any] func(context.Context, *execution.Scope) (T, error)

Factory constructs a borrowed stage-local extension. Resources own disposal.

type Filter

type Filter interface {
	OnExecution(context.Context, *Invocation) (Result[NoResponse], error)
}

Filter contributes a verdict after mandatory authorization. Zero fragments deny.

type FilterFunc

type FilterFunc func(context.Context, *Invocation) (Result[NoResponse], error)

FilterFunc adapts a synchronous filter callback.

func (FilterFunc) OnExecution

func (f FilterFunc) OnExecution(ctx context.Context, inv *Invocation) (Result[NoResponse], error)

OnExecution calls f.

type Handler

type Handler[C, O any] struct {
	// contains filtered or unexported fields
}

Handler contains immutable, separately staged command callbacks. Callbacks and captured dependencies must support concurrent calls. O is the raw return type.

func Handle

func Handle[C, O any](call func(C, context.Context) (O, error)) Handler[C, O]

Handle adapts a model method expression or an ordinary typed callback.

Example
package main

import (
	"context"
	"fmt"

	"github.com/cratis/arc.go/commands"
)

type Greet struct {
	Name string `json:"name"`
}

func (c Greet) Handle(context.Context) (string, error) { return "Hello, " + c.Name, nil }
func main() {
	var registry commands.Registry
	if err := commands.Register(&registry, commands.Handle(Greet.Handle)); err != nil {
		panic(err)
	}
	pipeline, err := registry.Build(commands.PipelineOptions{})
	if err != nil {
		panic(err)
	}
	result, err := commands.Execute[string](context.Background(), pipeline, Greet{Name: "Ada"})
	if err != nil {
		panic(err)
	}
	response, present := result.Response()
	fmt.Println(result.IsSuccess(), present, response)
}
Output:
true true Hello, Ada
Example (Dependencies)
package main

import (
	"context"
	"fmt"

	"github.com/cratis/arc.go/commands"
)

type Multiplier interface{ Multiply(int) int }
type multiplier int

func (m multiplier) Multiply(value int) int { return int(m) * value }

type Calculate struct {
	Number int `json:"number"`
}

func (c Calculate) Handle(_ context.Context, service Multiplier) (int, error) {
	return service.Multiply(c.Number), nil
}
func main() {
	service := multiplier(2)
	var registry commands.Registry
	if err := commands.Register(&registry, commands.Handle(func(c Calculate, ctx context.Context) (int, error) { return c.Handle(ctx, service) })); err != nil {
		panic(err)
	}
	pipeline, err := registry.Build(commands.PipelineOptions{})
	if err != nil {
		panic(err)
	}
	result, err := commands.Execute[int](context.Background(), pipeline, Calculate{Number: 21})
	if err != nil {
		panic(err)
	}
	response, _ := result.Response()
	fmt.Println(response)
}
Output:
42

func Invoke

func Invoke[C, O any](call func(context.Context, *Invocation, C) (O, error)) Handler[C, O]

Invoke supplies an expiring callback frame for generated and handwritten adapters.

func Prepare

func Prepare[C, P, O any](provide func(context.Context, *Invocation, C) (Preparation[P], error), handle func(context.Context, *Invocation, C, P) (O, error)) Handler[C, O]

Prepare is the generator seam: dependencies resolve independently in each stage.

func Scoped

func Scoped[C, O any, S execution.Resources](factory func(context.Context, S) (Handler[C, O], error)) Handler[C, O]

Scoped lazily constructs an adapter from an application resource holder after authorization and validation. It preserves the adapter's preparation stage.

Example
package main

import (
	"context"
	"fmt"

	"github.com/cratis/arc.go/commands"
	"github.com/cratis/arc.go/execution"
)

type Multiplier interface{ Multiply(int) int }
type multiplier int

func (m multiplier) Multiply(value int) int { return int(m) * value }

type Calculate struct {
	Number int `json:"number"`
}

func (c Calculate) Handle(_ context.Context, service Multiplier) (int, error) {
	return service.Multiply(c.Number), nil
}

type CalculationResources struct {
	Service Multiplier
	Closed  bool
}

func (r *CalculationResources) Close(context.Context) error { r.Closed = true; return nil }
func main() {
	resources := &CalculationResources{Service: multiplier(2)}
	var registry commands.Registry
	if err := commands.Register(&registry, commands.Scoped(func(_ context.Context, r *CalculationResources) (commands.Handler[Calculate, int], error) {
		return commands.Handle(func(c Calculate, ctx context.Context) (int, error) { return c.Handle(ctx, r.Service) }), nil
	})); err != nil {
		panic(err)
	}
	pipeline, err := registry.Build(commands.PipelineOptions{OpenResources: func(context.Context) (execution.Resources, error) { return resources, nil }})
	if err != nil {
		panic(err)
	}
	result, err := commands.Execute[int](context.Background(), pipeline, Calculate{Number: 21})
	if err != nil {
		panic(err)
	}
	response, _ := result.Response()
	fmt.Println(response, resources.Closed)
}
Output:
42 true

func Void

func Void[C any](call func(C, context.Context) error) Handler[C, NoResponse]

Void adapts an error-only method expression.

func WithPreparation

func WithPreparation[C, P, O any](provide func(C, context.Context) (Preparation[P], error), handle func(C, context.Context, P) (O, error)) Handler[C, O]

WithPreparation supports explicit preparation controls and early stop.

func WithProvide

func WithProvide[C, P, O any](provide func(C, context.Context) (P, error), handle func(C, context.Context, P) (O, error)) Handler[C, O]

WithProvide separates typed preparation from handling. A failed Provide never fabricates a usable payload. Neither callback is called by Validate.

Example
package main

import (
	"context"
	"fmt"

	"github.com/cratis/arc.go/commands"
)

type Double struct {
	Number int `json:"number"`
}

func (c Double) Provide(context.Context) (int, error)           { return c.Number * 2, nil }
func (Double) Handle(_ context.Context, value int) (int, error) { return value, nil }
func main() {
	var registry commands.Registry
	if err := commands.Register(&registry, commands.WithProvide(Double.Provide, Double.Handle)); err != nil {
		panic(err)
	}
	pipeline, err := registry.Build(commands.PipelineOptions{})
	if err != nil {
		panic(err)
	}
	result, err := commands.Execute[int](context.Background(), pipeline, Double{Number: 21})
	if err != nil {
		panic(err)
	}
	response, _ := result.Response()
	fmt.Println(response)
}
Output:
42

type Invocation

type Invocation struct {
	// contains filtered or unexported fields
}

Invocation is an expiring callback frame, not a context-stored service locator. Accessors return nil facilities after expiry. Retained executors reject work.

func (*Invocation) CommandContext

func (i *Invocation) CommandContext() CommandContext

CommandContext returns a copied snapshot including values set during this callback.

func (*Invocation) Execution

func (i *Invocation) Execution() *Execution

Execution returns the current owner's callback view, or nil after expiry.

func (*Invocation) ParentCommandContext

func (i *Invocation) ParentCommandContext(ctx context.Context) (value CommandContext, present bool, err error)

ParentCommandContext returns the parent's metadata at nested admission, if present. Membership and descriptors are copied; command/value objects remain borrowed immutable data. It exposes no parent invocation or completion owner.

func (*Invocation) Pipeline

func (i *Invocation) Pipeline() Pipeline

Pipeline returns an executor joining this command owner. Retained use fails.

func (*Invocation) Scope

func (i *Invocation) Scope() *execution.Scope

Scope returns a non-closing callback-scoped operation view, or nil after expiry.

func (*Invocation) SetValue

func (i *Invocation) SetValue(name string, value any) error

SetValue updates copied membership only; it cannot replace security or ownership.

type KeyProvider

type KeyProvider interface{ GetKey() string }

KeyProvider explicitly supplies a provider-neutral command key.

type KeyResolver

type KeyResolver interface {
	Resolve(context.Context, *Invocation) (string, bool, error)
}

KeyResolver supplies trusted keys before filters; empty keys do not win.

type NoResponse

type NoResponse struct{}

NoResponse is the type argument for commands without a client response.

type Operation

type Operation interface{ CommandOperation() }

Operation marks immutable business data describing immediate inline work. Payloads remain borrowed, including pointer declarations; do not mutate them during execution. Services belong in Execute/Compensate parameters, not payloads.

type OperationCommitParticipant

type OperationCommitParticipant interface {
	DeferredCommitParticipant
	ObserveCommit(context.Context, *Invocation) (CompletionReport, error)
}

OperationCommitParticipant is the existing sole terminal slot plus a read-only observation hook. ObserveCommit reports provider facts before operation entry; it must not connect, commit, retry or activate business services. Pending work can report NotCommitted. Complete owns the authoritative final outcome. Recovery dependencies must remain usable after completion; a completed deferred transaction itself is not a usable recovery resource.

type OperationCompensation

type OperationCompensation uint8

OperationCompensation describes one started invocation's recovery observation.

const (
	// CompensationNotNeeded means recovery was not required.
	CompensationNotNeeded OperationCompensation = iota
	// CompensationCompleted means the callback returned successfully.
	CompensationCompleted
	// CompensationFailed means the callback failed, panicked or exceeded its budget.
	CompensationFailed
	// CompensationNotAvailable means no reversal was declared.
	CompensationNotAvailable
	// CompensationBudgetExpired means the callback was not entered before expiry.
	CompensationBudgetExpired
	// CompensationSuppressed means persistence facts prohibited reversal.
	CompensationSuppressed
)

type OperationFailure

type OperationFailure struct {
	InvocationIndex                          int
	InvocationCompleted, IsFailingInvocation bool
	Source                                   OperationFailureSource
	Completion                               CompletionReport
	Original                                 Result[NoResponse]
	Cause                                    error
}

OperationFailure supplies original failure facts to a compensator. Original is an immutable response-free snapshot; Completion is the sole persistence report. Cause preserves the original error identity, never later compensation errors.

type OperationFailureSource

type OperationFailureSource uint8

OperationFailureSource identifies the first failed pipeline phase.

const (
	// FailurePlanning is declaration or dependency preflight failure.
	FailurePlanning OperationFailureSource = iota
	// FailureResponseHandling is return classification/control/consumer failure.
	FailureResponseHandling
	// FailureExecution is a business invocation failure.
	FailureExecution
	// FailureCancellation is cancellation of forward execution.
	FailureCancellation
	// FailureScopeCompletion is ordinary or terminal completion failure.
	FailureScopeCompletion
)

type OperationOptions

type OperationOptions struct {
	// CompensationTimeout defaults to 30 seconds when zero; negative is invalid.
	// A callback ignoring cancellation is still synchronously joined. After expiry
	// no further compensator starts; Arc never restarts a timed-out callback.
	CompensationTimeout time.Duration
}

OperationOptions configures one cooperative, detached budget shared by recovery.

type OperationOutcome

type OperationOutcome struct {
	InvocationIndex     int
	OperationType       reflect.Type
	ExecutionCompleted  bool
	Compensation        OperationCompensation
	CompensationFailure error
}

OperationOutcome is a backend-only observation with no declaration payload. Error objects remain borrowed and must not be mutated after returning.

type Operations

type Operations struct {
	// contains filtered or unexported fields
}

Operations is an ordered batch with copied membership and borrowed payloads. Zero is empty. It is safe for concurrent reads if payloads are not mutated.

func NewOperations

func NewOperations(values ...Operation) (Operations, error)

NewOperations copies membership once and rejects nil members, including typed nils.

func (Operations) Values

func (o Operations) Values() []Operation

Values returns isolated ordered membership; declaration payloads remain borrowed.

type Option

type Option[C any] interface {
	// contains filtered or unexported methods
}

Option is a sealed, typed registration option. Singular options cannot repeat.

func WithAuthorization

func WithAuthorization[C any](a metadata.Authorization) Option[C]

WithAuthorization replaces the declaration rather than merging it.

func WithBlockOnValidationSeverity

func WithBlockOnValidationSeverity[C any](s validation.Severity) Option[C]

WithBlockOnValidationSeverity supplies an inclusive, non-loosenable floor.

func WithDescriptor

func WithDescriptor[C any](d metadata.Command) Option[C]

WithDescriptor pins the complete descriptor before individual overrides.

func WithExcludeFromDiscovery

func WithExcludeFromDiscovery[C any](exclude bool) Option[C]

WithExcludeFromDiscovery hides discovery without changing routes or authorization.

func WithHandlingDependencies

func WithHandlingDependencies[C any](keys ...di.Key) Option[C]

WithHandlingDependencies declares Handle-only DI keys without eager activation.

func WithKey

func WithKey[C any](key func(C) (string, bool)) Option[C]

WithKey supplies an explicit fallback command key selector.

func WithName

func WithName[C any](name string) Option[C]

WithName overrides the logical command name while retaining namespace defaults.

func WithNamespace

func WithNamespace[C any](s string) Option[C]

WithNamespace overrides the public namespace, never a Go import path.

func WithNoResponse

func WithNoResponse[C any]() Option[C]

WithNoResponse requires every returned value to be consumed.

func WithOperations

func WithOperations[C any]() Option[C]

WithOperations declares participation when Outcome effects erase operation types. Raw any returns are unsupported. Even empty/absent outputs require a compatible boundary. Validate never prepares, executes or compensates operations.

func WithPath

func WithPath[C any](s string) Option[C]

WithPath overrides the entire route.

func WithPreparationDependencies

func WithPreparationDependencies[C any](keys ...di.Key) Option[C]

WithPreparationDependencies declares keys for static catalog checks, not resolution.

func WithProtectedDecisions

func WithProtectedDecisions[C any]() Option[C]

WithProtectedDecisions opts a command into protected decision reads, the Go counterpart of C# [ProtectedDecision]. The profile is fixed at registration, before any filter, validator or handler can run.

This initial profile cannot certify arbitrary validation code. Register refuses it unless WithoutModelValidation is also supplied, and it refuses any WithValidator or WithScopedValidator option and any command operation. Build refuses it unless the registry has a decision provider (AddDecisionProvider) and a completion owner (AddDeferredCommitParticipant). It cannot be combined with WithUnprotectedDecisions (ErrDuplicate).

Example
package main

import (
	"context"
	"fmt"
	"reflect"

	"github.com/cratis/arc.go/commands"
	"github.com/cratis/arc.go/execution"
)

type BookRoom struct{ Room string }

type Room struct{ Booked bool }

// roomSource stands in for a provider integration such as Chronicle's. Real
// evidence is a provider token; Enroll registers it with the completion owner.
type roomSource struct{}

func (roomSource) Admit(context.Context) error          { return nil }
func (roomSource) Acquire(context.Context) (any, error) { return &Room{}, nil }
func (roomSource) Check(context.Context, any) error     { return nil }
func (roomSource) Enroll(context.Context, any) error    { return nil }

// noOwner is a completion owner that persists nothing.
type noOwner struct{}

func (noOwner) Begin(context.Context, *commands.Invocation) error { return nil }
func (noOwner) Complete(context.Context, *commands.Invocation, commands.Result[any]) (commands.CompletionReport, error) {
	return commands.CompletionReport{Disposition: commands.NoPersistedWork}, nil
}

func main() {
	var registry commands.Registry
	provider := commands.NewDecisionProvider()
	err := commands.Register(&registry,
		commands.WithProtectedDecisions[BookRoom](),
		commands.WithoutModelValidation[BookRoom](),
		commands.Prepare(func(ctx context.Context, inv *commands.Invocation, c BookRoom) (commands.Preparation[*commands.DecisionRead], error) {
			target := commands.DecisionTarget{Provider: provider, Model: reflect.TypeFor[Room](), Store: "hotel", Namespace: "default", Key: c.Room}
			read, err := commands.ReadDecision(ctx, inv, target, roomSource{})
			if err != nil {
				return commands.Preparation[*commands.DecisionRead]{}, err
			}
			return commands.Provided(read), nil
		}, func(_ context.Context, _ *commands.Invocation, c BookRoom, read *commands.DecisionRead) (commands.NoResponse, error) {
			// Arc verified read before Handle; decide from it.
			fmt.Println("booking", c.Room, "booked:", read.Value().(*Room).Booked)
			return commands.NoResponse{}, nil
		}))
	if err == nil {
		err = registry.AddDecisionProvider(provider)
	}
	if err == nil {
		err = registry.AddDeferredCommitParticipant("owner", func(context.Context, *execution.Scope) (commands.DeferredCommitParticipant, error) {
			return noOwner{}, nil
		})
	}
	if err != nil {
		fmt.Println(err)
		return
	}
	pipeline, err := registry.Build(commands.PipelineOptions{})
	if err != nil {
		fmt.Println(err)
		return
	}
	result, err := pipeline.Execute(context.Background(), BookRoom{Room: "101"})
	fmt.Println(result.IsSuccess(), err)
}
Output:
booking 101 booked: false
true <nil>

func WithResponseType

func WithResponseType[C, R any]() Option[C]

WithResponseType declares an enforced client response contract.

func WithScopedValidator

func WithScopedValidator[C any](factory Factory[validation.Validator[C]], keys ...di.Key) Option[C]

WithScopedValidator constructs a validator only in validation, after authorization.

func WithUnprotectedDecisions

func WithUnprotectedDecisions[C any]() Option[C]

WithUnprotectedDecisions marks a command as using advisory, unguarded reads, the Go counterpart of C# [Unprotected]. It adds no runtime guarantee; it lets a provider serve detached snapshots and records the choice for tooling. It cannot be combined with WithProtectedDecisions (ErrDuplicate).

func WithValidator

func WithValidator[C any](v validation.Validator[C]) Option[C]

WithValidator appends an ordered, shared validator.

func WithoutModelValidation

func WithoutModelValidation[C any]() Option[C]

WithoutModelValidation explicitly disables graph methods, registered rules and tags.

type Outcome

type Outcome[R any] struct {
	// contains filtered or unexported fields
}

Outcome is an explicitly classified return graph. Membership is copied; values remain borrowed. Zero means successful void output. Collections are single leaves.

func Control

func Control[R any](control Result[NoResponse]) Outcome[R]

Control explicitly adopts an infrastructure result rather than serializing it.

func Effects

func Effects[R any](effects ...any) Outcome[R]

Effects requires every nonnil leaf to be consumed by controls or response handlers.

func Respond

func Respond[R any](response R, effects ...any) Outcome[R]

Respond reserves one response and requires consumption of every effect leaf.

Example
package main

import (
	"context"
	"fmt"

	"github.com/cratis/arc.go/commands"
	"github.com/cratis/arc.go/execution"
)

type ItemAdded struct {
	Name string `json:"name"`
}
type AddNamedItem struct {
	Name string `json:"name"`
}

func (c AddNamedItem) Handle(context.Context) (commands.Outcome[string], error) {
	return commands.Respond("receipt", ItemAdded(c)), nil
}

type eventRecorder struct{ Events []ItemAdded }

func (*eventRecorder) CanHandle(_ commands.CommandContext, value any) bool {
	_, ok := value.(ItemAdded)
	return ok
}
func (r *eventRecorder) Handle(_ context.Context, inv *commands.Invocation, value any) (commands.Result[commands.NoResponse], error) {
	r.Events = append(r.Events, value.(ItemAdded))
	return commands.Success(inv.CommandContext().CorrelationID()), nil
}
func main() {
	// This is an in-memory extension, not Chronicle append or a transaction.
	recorder := &eventRecorder{}
	var registry commands.Registry
	if err := commands.Register(&registry, commands.Handle(AddNamedItem.Handle)); err != nil {
		panic(err)
	}
	if err := commands.RegisterResponseValueHandler[ItemAdded](&registry, "events", func(context.Context, *execution.Scope) (commands.ResponseValueHandler, error) { return recorder, nil }); err != nil {
		panic(err)
	}
	pipeline, err := registry.Build(commands.PipelineOptions{})
	if err != nil {
		panic(err)
	}
	result, err := commands.Execute[string](context.Background(), pipeline, AddNamedItem{Name: "Book"})
	if err != nil {
		panic(err)
	}
	response, _ := result.Response()
	fmt.Println(response, recorder.Events[0].Name)
}
Output:
receipt Book

func Values

func Values[R any](values ...any) Outcome[R]

Values permits at most one unconsumed response after context-updater processing.

type Pipeline

type Pipeline interface {
	Lookup(string) (Registration, bool)
	LookupCommand(any) (Registration, error)
	Execute(context.Context, any, ...ExecuteOptions) (Result[any], error)
	Validate(context.Context, any, ...ExecuteOptions) (Result[NoResponse], error)
	ExecuteScoped(context.Context, *execution.Scope, any, ...ExecuteOptions) (Result[any], error)
	ValidateScoped(context.Context, *execution.Scope, any, ...ExecuteOptions) (Result[NoResponse], error)
}

Pipeline is the public substitution boundary for backend command execution.

type PipelineOptions

type PipelineOptions struct {
	// Diagnostics is borrowed, bounded backend recording; nil disables it.
	Diagnostics            *observability.Recorder
	OpenResources          execution.OpenResources
	ScopeFactory           di.ScopeFactory
	DependencyCatalog      di.Catalog
	Authorization          *authorization.Evaluator
	Validation             *validation.Graph
	Membership             tenancy.Membership
	RequireTenant          bool
	Clock                  func() time.Time
	CleanupTimeout         time.Duration
	Operations             OperationOptions
	ExposeExceptionDetails bool
	Logger                 *slog.Logger
}

PipelineOptions configures borrowed shared collaborators. Nil authorization compiles declarations (never bypasses them); nil validation enables model/tag rules. OpenResources and ScopeFactory are mutually exclusive. Cleanup is cooperative.

type Preparation

type Preparation[P any] struct {
	// contains filtered or unexported fields
}

Preparation distinguishes usable typed payloads from explicit early stop. Zero is invalid; nil payloads supplied by Provided are still usable typed values.

func Provided

func Provided[P any](value P) Preparation[P]

Provided supplies a payload with successful preparation controls.

func ProvidedWith

func ProvidedWith[P any](value P, control Result[NoResponse]) Preparation[P]

ProvidedWith supplies a payload and controls subject to input-stage severity.

func StopProviding

func StopProviding[P any](control Result[NoResponse]) Preparation[P]

StopProviding explicitly skips Handle even when control indicates success.

type ReadModelInstance

type ReadModelInstance[M any] struct {
	// Value is meaningful only when Exists is true.
	Value M
	// Exists distinguishes an absent model from a present zero value.
	Exists bool
}

ReadModelInstance preserves absence independently of a zero-valued model. Value is borrowed from the provider for this frame. Providers own materialization, release, caching and mutable-value isolation; a release failure must be an error.

func ResolveReadModel

func ResolveReadModel[M any](ctx context.Context, inv *Invocation) (ReadModelInstance[M], error)

ResolveReadModel reads M through its selected owner with presence intact. Missing/blank keys are malformedRequest, even for optional reads. A missing provider is ErrReadModelProvider, not absence. Reader/release errors remain inspectable and are never cached or converted into a missing instance.

type ReadModelOwnership

type ReadModelOwnership uint8

ReadModelOwnership controls exact-type provider precedence independently of registration order. Providers at the same level cannot claim the same model.

const (
	// FallbackReadModel supplies models with no declared owner or application override.
	FallbackReadModel ReadModelOwnership = iota
	// DeclaredReadModel claims models backed by the provider's declared producer.
	DeclaredReadModel
	// OverrideReadModel is an explicit application replacement, above declared ownership.
	OverrideReadModel
)

type RecoveryStatus

type RecoveryStatus uint8

RecoveryStatus describes observed callback recovery, never atomic rollback.

const (
	// RecoveryNotNeeded means no failed started work requires reversal.
	RecoveryNotNeeded RecoveryStatus = iota
	// RecoveryCompleted means every eligible compensator returned successfully.
	RecoveryCompleted
	// RecoveryIncomplete means one or more reversals were unavailable or failed.
	RecoveryIncomplete
	// RecoverySuppressed means confirmed persistence prohibits automatic reversal.
	RecoverySuppressed
	// RecoveryIndeterminate means unknown/mixed persistence prohibits reversal.
	RecoveryIndeterminate
)

type RecoverySummary

type RecoverySummary struct {
	Status                                         RecoveryStatus
	StartedCount, CompletedCount, CompensatedCount int
	FailedCompensationCount, UncompensatedCount    int
}

RecoverySummary is a backend-only immutable value snapshot. Completion() on the Result remains the sole persistence report. Completed is not a retry guarantee.

type Registrar

type Registrar interface{ RegisterCommand(Registration) error }

Registrar accepts frozen registrations; generated adapters use this seam.

type Registration

type Registration struct {
	// contains filtered or unexported fields
}

Registration is an immutable typed command adapter and descriptor.

func (Registration) CommandType

func (r Registration) CommandType() reflect.Type

CommandType returns the exact pointer or value registration type.

func (Registration) DecisionProfile

func (r Registration) DecisionProfile() DecisionProfile

DecisionProfile returns the frozen decision-read profile.

func (Registration) Decode

func (r Registration) Decode(body []byte) (any, error)

Decode allocates fresh input and uses Arc scalar/field serialization.

func (Registration) Descriptor

func (r Registration) Descriptor() metadata.Command

Descriptor returns a copy-isolated declaration.

func (Registration) ResponseKind

func (r Registration) ResponseKind() ResponseKind

ResponseKind returns the frozen client contract classification.

func (Registration) ResponseType

func (r Registration) ResponseType() (reflect.Type, bool)

ResponseType returns a known response type, never the raw type as a guess.

func (Registration) ReturnType

func (r Registration) ReturnType() reflect.Type

ReturnType returns Handle's raw declared result type.

type RegistrationError

type RegistrationError struct {
	Identity string
	Kind     error
}

RegistrationError retains the affected identity and inspectable category.

func (*RegistrationError) Error

func (e *RegistrationError) Error() string

Error describes a registration failure.

func (*RegistrationError) Unwrap

func (e *RegistrationError) Unwrap() error

Unwrap exposes the category.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry is a single-owner builder. Zero uses the global namespace. Successful Build freezes it; failed Build leaves it editable. It never activates factories.

func NewRegistry

func NewRegistry(options RegistryOptions) (*Registry, error)

NewRegistry validates namespace configuration without application callbacks.

func (*Registry) AddAuthorizationFilter

func (r *Registry) AddAuthorizationFilter(name string, factory Factory[AuthorizationFilter], keys ...di.Key) error

AddAuthorizationFilter appends a lazy authorization filter.

func (*Registry) AddBuildCheck

func (r *Registry) AddBuildCheck(check func(BuildView) error) error

AddBuildCheck registers an integration configuration check. Build calls checks in insertion order after declaration validation, before freezing or activating factories, and returns the first error unchanged. Failed builds remain editable and retry all checks on the next Build. Checks may be added before commands; they must not mutate the registry or activate application services. Nil checks or receivers return ErrInvalidRegistration; frozen registries return ErrFrozen.

func (*Registry) AddContextValuesProvider

func (r *Registry) AddContextValuesProvider(name string, factory Factory[ContextValuesProvider], keys ...di.Key) error

AddContextValuesProvider registers an ordered, lazy metadata provider.

func (*Registry) AddDecisionProvider

func (r *Registry) AddDecisionProvider(provider *DecisionProvider) error

AddDecisionProvider certifies a provider identity for protected commands built from this registry. Registration is composition, not a runtime boolean: a read whose target names any other provider is refused. Nil returns ErrInvalidRegistration (as does a zero provider), a repeated provider ErrDuplicate and a frozen registry ErrFrozen. It activates nothing.

func (*Registry) AddDeferredCommitParticipant

func (r *Registry) AddDeferredCommitParticipant(name string, factory Factory[DeferredCommitParticipant], keys ...di.Key) error

AddDeferredCommitParticipant registers the sole terminal participant. A second registration is rejected irrespective of its name; cross-provider atomicity is not supported. The borrowed factory is lazy, and Build performs no I/O.

func (*Registry) AddExecutionScope

func (r *Registry) AddExecutionScope(name string, factory Factory[ExecutionScope], keys ...di.Key) error

AddExecutionScope registers a lazy borrowed root-completion participant.

func (*Registry) AddFilter

func (r *Registry) AddFilter(name string, factory Factory[Filter], keys ...di.Key) error

AddFilter appends an ordinary lazy filter in stable registration order.

func (*Registry) AddKeyResolver

func (r *Registry) AddKeyResolver(name string, factory Factory[KeyResolver], keys ...di.Key) error

AddKeyResolver registers a custom resolver before the command's own fallback.

func (*Registry) AddOperationCommitParticipant

func (r *Registry) AddOperationCommitParticipant(name string, factory Factory[OperationCommitParticipant], keys ...di.Key) error

AddOperationCommitParticipant explicitly classifies the sole terminal slot. Unclassified terminal participants remain supported for non-operation commands.

func (*Registry) AddOperationExecutionScope

func (r *Registry) AddOperationExecutionScope(name string, factory Factory[ExecutionScope], keys ...di.Key) error

AddOperationExecutionScope explicitly classifies a noncommitting scope. Begin must not commit; Complete must not persist business changes. Recovery services remain usable until the originating resource owner disposes them. This promise is static: Arc never activates factories merely to discover compatibility.

func (*Registry) AddResponseValueHandler

func (r *Registry) AddResponseValueHandler(name string, factory Factory[ResponseValueHandler], keys ...di.Key) error

AddResponseValueHandler registers a lazy dynamic consumer.

func (*Registry) AddReturnAdmission

func (r *Registry) AddReturnAdmission(name string, admission ReturnAdmission) error

AddReturnAdmission registers a named admission policy. Static claims compile raw return metadata to ResponseNone unless the command explicitly overrides it. Dynamic admission retains unknown response metadata and requires runtime checks.

func (*Registry) Build

func (r *Registry) Build(options PipelineOptions) (Pipeline, error)

Build validates catalogs and shapes, without resolving services, and freezes the registry only on success. Returned pipelines are concurrently callable.

func (*Registry) Catalog

func (r *Registry) Catalog() metadata.Catalog

Catalog returns copy-isolated declaration metadata in registration order.

func (*Registry) ContainsCommandType

func (r *Registry) ContainsCommandType(typ reflect.Type) bool

ContainsCommandType reports exact registration membership without building or freezing the registry. Pointer and value types are distinct; nil receivers and nil types return false. Like registration, access requires a single owner.

func (*Registry) RegisterCommand

func (r *Registry) RegisterCommand(value Registration) error

RegisterCommand accepts only valid immutable registrations and exact ownership.

type RegistryOptions

type RegistryOptions struct{ Namespace string }

RegistryOptions configures public identity defaults.

type ResponseKind

type ResponseKind uint8

ResponseKind distinguishes an unresolved contract from guaranteed absence.

const (
	// ResponseUnknown requires an explicit contract for typed execution.
	ResponseUnknown ResponseKind = iota
	// ResponseNone guarantees no client response.
	ResponseNone
	// ResponseValue permits a response of the declared type, or absence.
	ResponseValue
)

type ResponseValueContextUpdater

type ResponseValueContextUpdater interface {
	ResponseValueHandler
	UpdateContext(context.Context, *Invocation, any) error
}

ResponseValueContextUpdater participates before response selection. It may set context values, never identity, tenant, correlation, resources or ownership.

type ResponseValueHandler

type ResponseValueHandler interface {
	CanHandle(CommandContext, any) bool
	Handle(context.Context, *Invocation, any) (Result[NoResponse], error)
}

ResponseValueHandler is an additive server consumer. Every matching handler runs in registration order. CanHandle may depend on the selected response.

type Result

type Result[R any] struct {
	// contains filtered or unexported fields
}

Result is a finalized command outcome with computed flags and explicit response presence. Its zero value denies authorization. It is safe for concurrent reads if borrowed response and State values are not mutated. It is an output envelope, not a JSON decoder; use ordinary DTOs to consume remote responses.

func Execute

func Execute[R any](ctx context.Context, p Pipeline, command any, options ...ExecuteOptions) (output Result[R], outputErr error)

Execute checks a known response contract before application code. Unknown contracts are checked against the actual response after execution; a mismatch returns ErrResponseType and a failed envelope with no response.

func FromError

func FromError[R any](id correlation.ID, err error) Result[R]

FromError classifies joined failures and always redacts production exceptions. It retains no response. The caller still owns and may inspect the original error.

func InvalidBody

func InvalidBody(id correlation.ID) Result[NoResponse]

InvalidBody produces a safe malformedRequest finding.

func Merge

func Merge[R any](result Result[R], fragments ...Result[NoResponse]) Result[R]

Merge ANDs authorization, appends diagnostics and retains the outer correlation and first nonempty denial reason. Any failure removes response presence.

func NewResult

func NewResult[R any](details Details, response serialization.Optional[R]) Result[R]

NewResult finalizes details and copies their arrays. Any failure discards response presence, including scalar zeros. Application response values remain borrowed.

func Success

func Success(id concepts.UUID) Result[NoResponse]

Success returns a successful command without a response.

func Unauthorized

func Unauthorized(id correlation.ID, reason string) Result[NoResponse]

Unauthorized produces a denied fragment with caller-authored safe feedback.

func WithResponse

func WithResponse[R any](id concepts.UUID, response R) Result[R]

WithResponse returns success with an explicitly present response, including zero.

func WithValidationResults

func WithValidationResults(id correlation.ID, findings ...validation.Result) Result[NoResponse]

WithValidationResults produces an authorized validation fragment.

func (Result[R]) Completion

func (r Result[R]) Completion() CompletionReport

Completion returns persistence evidence, including after command/cleanup failure.

func (Result[R]) Details

func (r Result[R]) Details() Details

Details returns copy-isolated arrays; application State remains borrowed.

func (Result[R]) HasExceptions

func (r Result[R]) HasExceptions() bool

HasExceptions reports whether exceptionMessages is nonempty.

func (Result[R]) IsAuthorized

func (r Result[R]) IsAuthorized() bool

IsAuthorized reports the final authorization decision.

func (Result[R]) IsSuccess

func (r Result[R]) IsSuccess() bool

IsSuccess combines authorization, validity and absence of exceptions.

func (Result[R]) IsValid

func (r Result[R]) IsValid() bool

IsValid reports whether the retained validation list is empty.

func (Result[R]) MarshalJSON

func (r Result[R]) MarshalJSON() ([]byte, error)

MarshalJSON emits the Arc CommandResult envelope with required arrays and strings.

func (Result[R]) MarshalJSONWith

func (r Result[R]) MarshalJSONWith(encode func(any) ([]byte, error)) (body []byte, err error)

MarshalJSONWith encodes nested values using the supplied Arc traversal. The callback is synchronous and is not retained; callers normally use MarshalJSON.

func (Result[R]) OperationOutcomes

func (r Result[R]) OperationOutcomes() []OperationOutcome

OperationOutcomes returns copied backend-only observations, never payloads.

func (Result[R]) Recovery

func (r Result[R]) Recovery() (RecoverySummary, bool)

Recovery returns backend-only recovery observations, if processing participated.

func (Result[R]) Response

func (r Result[R]) Response() (R, bool)

Response distinguishes absence from a legitimate zero value. Nil payloads are omitted on the wire, even if present locally, matching C# null omission.

func (Result[R]) StatusCode

func (r Result[R]) StatusCode() int

StatusCode selects 200, 403, 400 or 500 in Arc precedence order.

type ReturnAdmission

type ReturnAdmission struct {
	// Types is the frozen set of exact server-consumed types; empty means dynamic.
	Types []reflect.Type
	// Check validates/classifies a borrowed return value, including typed nils.
	Check func(context.Context, *Invocation, any) (ReturnClassification, error)
}

ReturnAdmission supplies immutable catalog metadata and optional runtime classification. Types contains exact concrete server-consumed types (pointer and value are distinct); it is copied on registration. A matching typed nil is rejected. With no Types, Check is required and classifies dynamic values.

Check, when supplied, runs before any consumer/updater and may reject a value. For a listed type its OrdinaryReturn cannot undo the static claim. It must not perform external effects. It runs outside locks and must be concurrent-safe. Explicit Respond values and builtin controls bypass this extension entirely. No callback runs during Build. This never classifies a slice by its elements.

type ReturnClassification

type ReturnClassification uint8

ReturnClassification is opt-in admission before nil flattening and effects.

const (
	// OrdinaryReturn leaves the existing response/consumer rules unchanged.
	OrdinaryReturn ReturnClassification = iota
	// ServerConsumedReturn requires a consumer and never selects a client response.
	ServerConsumedReturn
)

type StateKey

type StateKey[T any] struct {
	// contains filtered or unexported fields
}

StateKey identifies extension-owned state. Construct it with NewStateKey; zero is invalid. Copies identify the same slot. Keep ownership-capability keys private.

func NewStateKey

func NewStateKey[T any]() StateKey[T]

NewStateKey allocates an independent typed slot, without process-wide storage.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL