model

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package model holds the kind-agnostic CORE contract: the kind-identity primitives (Kind, KindVersion, ResourceRef, ResourceManifest), the reaction request/response and composition value types the control plane builds from a DB row and applies (Resource, ReactionRequest, Outcome, ChildSpec, DepEdge, …), the author-facing Trigger/Transition enums, the config-merge helpers, the per-stage wire DTOs, the CRD-style KindManifest with its validation lattice, and the StageDispatcher seam. It is the CORE-SIDE twin of the worker SDK's own types (sdk-go/converge) — both describe the same worker proto, neither imports the other, and the two meet ONLY at the proto (the broker's internal/wire converter ⇄ the SDK's convert.go). It imports no other private package and touches no database.

It is INTERNAL: a worker author does NOT import it — they implement converge.Provider and speak converge.Resource/Outcome/ReactionRequest (sdk-go/converge). This package is what the ENGINE speaks on its side of the wire; a change here that alters the wire shape must be mirrored in the SDK's copy + the proto.

Concrete per-kind Spec/Status types live next to each provider. A kind comes online when its KindManifest is applied to kind_manifest (operator- or boot-seeded); this package does not enumerate them.

Index

Constants

View Source
const (
	// TypeReady is the health/availability axis (Crossplane "Ready").
	// A reaction that returns TypeReady=False sets the resource's
	// health_ok=false, demoting is_ready even when fully synced. A
	// reaction that emits no Ready condition is treated as healthy
	// (health_ok stays true) — the common case.
	TypeReady = "Ready"
	// TypeSynced is the spec-reconciled axis (Crossplane "Synced"). It
	// is DERIVED from synced_gen vs generation and synthesized by the
	// API; providers do not normally emit it. The core writes a
	// Synced=False row on reconcile failure so the failure reason is
	// queryable as current truth.
	TypeSynced = "Synced"
)

Standard condition types. Providers may also emit their own custom axes (e.g. "Bound", "Healthy"); only TypeReady feeds the health_ok scalar that gates is_ready.

Variables

This section is empty.

Functions

func IsTerminal

func IsTerminal(err error) bool

IsTerminal reports whether err (or anything it wraps) was marked terminal via Terminal(). Used by the dispatcher's failure path.

func Terminal

func Terminal(err error) error

Terminal wraps err to signal a non-retryable (terminal) failure. The dispatcher records phase=Failed with failure_terminal=true so the scheduler stops re-queuing it. nil in → nil out.

func ValidateManifest

func ValidateManifest(m KindManifest) error

ValidateManifest enforces the CLOSED legality lattice on a manifest's reactions — the SDK half of the DB validate_kind_manifest() CHECK; both must agree. The reaction rules reject a mis-declared manifest at AUTHORING/APPLY time, before a task ever runs against bad data. Returns a descriptive error on the first violation, nil if legal.

Types

type ChildSpec

type ChildSpec struct {
	Kind Kind
	// KindVersion is the web-API version the child is emitted at. A versioned composer
	// (classicbom/v2) emits its children at the new kindVersion; because the (kind,
	// name) address is stable, ApplyComposeResult ADOPTS the existing child BY ID
	// and flips its kindVersion in place — a safe in-place upgrade, never a
	// delete-and-recreate of live infra. REQUIRED and explicit (>= 1): there is no
	// implicit v1 default — a composer MUST emit each child with the version it
	// wants (a 0 fails the DB NOT-NULL/CHECK at ApplyComposeResult, never silently v1).
	KindVersion int
	Name        string
	Spec        any
	Labels      map[string]string
}

ChildSpec describes one child a composer wants to exist under a parent. Spec is the typed Go value (e.g. AccountSpec); the core JSON-marshals at the DB boundary.

type ComposePipelineResult

type ComposePipelineResult struct {
	Desired     []ChildSpec
	Edges       []DepEdge
	Configs     []ProviderConfigSpec
	Status      json.RawMessage
	Conditions  []Condition
	FailedStage int
	Err         error
}

ComposePipelineResult is the compose wire output: desired children/edges/configs + status/conditions (and an error with the failing stage index). It is the only *PipelineResult still carried on the wire — work/rollup/delete/operate results ride back as the bare Outcome fields (status/conditions/output) in StageComplete, so they need no dedicated result DTO.

type ComposeRequest

type ComposeRequest struct {
	Root     Resource
	Observed []Resource
	Desired  []ChildSpec
	Edges    []DepEdge
	Configs  []ProviderConfigSpec
	Env      *Env
}

ComposeRequest is the compose wire input: the root + its observed children (Desired/Edges/Configs are accumulator fields the wire carries; the single-handler model leaves them empty on input and fills the Outcome instead).

type Condition

type Condition struct {
	Type    string          // "Ready", "Synced", or a custom axis
	Status  ConditionStatus // True | False | Unknown
	Reason  string          // CamelCase machine code, e.g. "Available"
	Message string          // human-readable detail
}

Condition is one axis of a resource's status — K8s/Crossplane shape. Returned by a reaction in its Outcome's Conditions slice; the core upserts them into resource_conditions on transition and, for Type==TypeReady, folds Status into the health_ok scalar.

type ConditionStatus

type ConditionStatus string

ConditionStatus mirrors metav1.ConditionStatus.

const (
	ConditionTrue    ConditionStatus = "True"
	ConditionFalse   ConditionStatus = "False"
	ConditionUnknown ConditionStatus = "Unknown"
)

type DeleteRequest

type DeleteRequest struct {
	Resource Resource
	Env      *Env
}

DeleteRequest is the delete wire input.

type DepEdge

type DepEdge struct {
	From   ResourceRef // dependent
	To     ResourceRef // dependency
	Values []ValueFlow
}

DepEdge: dependent depends on dependency. Persisted to resource_deps. An edge optionally carries value flows that fill the dependent's spec from the upstream's status when the upstream becomes ready.

type Env

type Env struct {
	Logger         *slog.Logger
	ProviderConfig json.RawMessage
	// ProviderBundle is the per-resource CUSTOM bundle override (the clone of the
	// referenced custom config's `data` — the opaque artifact, e.g. a zip of
	// Starlark .star files), or nil when the resource carries no custom bundle.
	// Unlike ProviderConfig it does NOT field-merge: a handler computes its
	// EFFECTIVE bundle by taking this override whole when non-empty, else the kind
	// DEFAULT bundle (from the SDK's OnConfig/startup prime) — opaque bytes can't
	// deep-merge. Use the SDK's converge.EffectiveBundle helper.
	ProviderBundle []byte
}

Env is intentionally narrow: a handler describes what it wants done (children, status, output) and lets the core persist it. Handlers do not — and must not — issue raw SQL. Logger is scoped slog with task-specific fields (kind, resource id, generation).

ProviderConfig is the per-task CUSTOM config override: the clone of the resource's referenced providerconfig (work_queue.provider_config), or nil when the resource carries no custom override. It is pure read-only data (not a DB handle), so it fits Env's "per-call environment" role without widening what a handler can DO. Every handler of a task sees the same value. The kind DEFAULT config reaches the provider through the SDK (its OnConfig hook, primed at startup); a handler computes its EFFECTIVE config by overlaying this override onto that default (an object merge — the custom override wins per field, the default fills the rest) via the SDK's converge.EffectiveConfig. Spec is a separate axis and never participates.

type Kind

type Kind string

Kind names a resource kind. String-aliased so call sites can use untyped string literals or typed constants interchangeably.

type KindManifest

type KindManifest struct {
	Kind Kind `json:"kind"`
	// KindVersion is the web-API-style user-facing version (vpc/v1, vpc/v2, … — no
	// minor/micro; 1–32767, the smallint ceiling of the resources/work_queue
	// columns it is denormalized onto). A manifest is published PER (kind,
	// kindVersion); the core routes work by (kind, kindVersion) exact equality. The
	// kind string stays bare `vpc`; the kindVersion is this orthogonal field, never
	// encoded into Kind. REQUIRED and explicit (>= 1): there is no implicit v1
	// default — an un-versioned manifest is rejected at publish, never silently
	// treated as v1 (no `omitempty`, so it always serializes).
	KindVersion int    `json:"kind_version"`
	Description string `json:"description,omitempty"`

	// JSON Schemas (RFC draft 2020-12) authored for the kind, so the runtime stores
	// schema bytes, never a reflect.Type. A nil schema means "untyped" — the kind
	// opts out of validation for that axis.
	SpecSchema   json.RawMessage `json:"spec_schema,omitempty"`
	StatusSchema json.RawMessage `json:"status_schema,omitempty"`
	ConfigSchema json.RawMessage `json:"config_schema,omitempty"`

	// Reactions is the declared set of (Trigger, Emits) reactions. The core
	// dispatches purely off these (it never reads a kind name, only the masks).
	Reactions []ReactionDecl `json:"reactions"`

	// FinalizerName, when set, makes deletes a two-phase teardown (the reaper
	// soft-deletes and waits for the DeleteRequested reaction to strip it). Empty
	// = immediate hard-delete.
	FinalizerName string `json:"finalizer_name,omitempty"`

	// Operational policy (today's kind_config knobs). Seeded insert-if-absent on
	// apply so an operator's live /api/kinds/{kind}/config edits are never
	// clobbered by a re-apply.
	MaxInflight        int  `json:"max_inflight,omitempty"`
	TaskDeadlineSecs   int  `json:"task_deadline_secs,omitempty"`
	ResyncIntervalSecs int  `json:"resync_interval_secs,omitempty"`
	ResyncRecomposes   bool `json:"resync_recomposes,omitempty"`
	OrphanGraceSecs    int  `json:"orphan_grace_secs,omitempty"`
	// MaxTransientAttempts caps consecutive TRANSIENT reconcile failures before the
	// failure is escalated to terminal (dead-lettered), so a persistently-broken
	// handler stops retrying forever. 0 (the default) = unbounded — set it to opt a
	// kind into the poison-pill.
	MaxTransientAttempts int `json:"max_transient_attempts,omitempty"`

	// Retired SUNSETS this web-API version: a publish declaring it true (or an
	// operator toggling it via /api/kinds/{kind}/config) freezes NEW creates/flips
	// onto (kind, KindVersion) while EXISTING resources keep reconciling so they can
	// drain or be migrated off — never a forced teardown. Seeded from the manifest
	// but operator-editable at runtime, so it is preserved across a re-apply (the
	// sync trigger never un-retires). Off the claim hot path.
	Retired bool `json:"retired,omitempty"`
}

KindManifest is the CONTROL-PLANE half of a kind's definition: the CRD-style document an operator applies (its schemas, declared reactions, and lifecycle policy), validated by the legality lattice below and dispatched off by the outcome-mask vocabulary. A worker author never declares schemas, reactions, or policy — the manifest is applied out of band into the kind_manifest table, so none of this is on the public SDK surface.

The KindManifest IS the kind definition: the operator applies it as a CRD into the kind_manifest table and that row is the sole source of a kind's schemas, reactions, and policy. Its reactions are a single list of ReactionDecl whose (Trigger, Emits) pair encodes each reaction's behaviour.

KindManifest is the serializable CRD definition of a kind. It round-trips to the kind_manifest DB row (schemas as JSONB, reactions as JSONB) and to the API/UI manifest editor. JSON tags match the DB column names and the wire contract.

func (KindManifest) ComposerReaction

func (m KindManifest) ComposerReaction() (ReactionDecl, bool)

ComposerReaction returns the (single, by the lattice) reaction that emits children on a spec change, and whether one exists. The core uses this to know a kind is a composer without reading its name.

func (KindManifest) DeleteReaction

func (m KindManifest) DeleteReaction() (ReactionDecl, bool)

DeleteReaction returns the deleteRequested (finalizer teardown) reaction, and whether one exists — i.e. whether this kind runs a teardown on delete.

func (KindManifest) IsReactor

func (m KindManifest) IsReactor() bool

IsReactor reports whether this kind is a REACTOR — a side-effect handler a binding SUBSCRIBES to other kinds' transitions (e.g. a status-upload sink), not a directly-creatable resource. A reactor declares ONLY reactor reactions: at least one TriggerReactor reaction and no specChange/childrenSettled/ deleteRequested/operation reaction. The API uses it to keep reactors out of the creatable-resource gate while still serving their config schema.

func (KindManifest) OperationReaction

func (m KindManifest) OperationReaction(verb string) (ReactionDecl, bool)

OperationReaction returns the operation reaction bound to verb, and whether one exists — i.e. whether this kind exposes that subresource verb.

func (KindManifest) ReactorReaction

func (m KindManifest) ReactorReaction() (ReactionDecl, bool)

ReactorReaction returns the (single) reactor reaction and whether one exists. A reactor CRD declares exactly one TriggerReactor reaction; its name is the handle the worker keys its handler by (the reaction the dispatcher ships when a binding to this reactor fires).

func (KindManifest) RollupReaction

func (m KindManifest) RollupReaction() (ReactionDecl, bool)

RollupReaction returns the (single, by the lattice) childrenSettled+status reaction, and whether one exists — i.e. whether this kind is a rollup-er.

func (KindManifest) WorkerReaction

func (m KindManifest) WorkerReaction() (ReactionDecl, bool)

WorkerReaction returns the specChange reaction that emits status WITHOUT children (a plain leaf worker), and whether one exists.

type KindVersion

type KindVersion struct {
	Kind    Kind
	Version int
}

KindVersion is a (kind, web-API version) pair — the addressable unit of everything versioned: what a provider serves, what a worker advertises, what the broker routes, what a config-cache slot holds. Version is EXPLICIT (>= 1); there is no implicit v1. This is the ONE pair type across the SDK — providers, the worker client, and the remote-worker transport all use it, so no layer re-defines or converts the concept.

type OperateRequest

type OperateRequest struct {
	Resource  Resource
	Operation Operation
	Env       *Env
}

OperateRequest is the operate wire input: the resource + the operation row.

type Operation

type Operation struct {
	// No ID field: the resource_operations row id is a pure internal DB key the
	// broker uses to correlate the result; a provider never needs it. ResourceID
	// (the resource's public uuid) identifies what the verb runs against.
	ResourceID  uuid.UUID
	Verb        string
	Input       json.RawMessage
	Attempts    int
	RequestedBy string
}

Operation is the provider-side view of a resource_operations row, passed to an operation reaction's handler.

type Outcome

type Outcome struct {
	Status     json.RawMessage      // work/compose/rollup status
	Conditions []Condition          // health/custom axes
	Children   []ChildSpec          // composer: desired children
	Edges      []DepEdge            // composer: dep edges (+ value flows)
	Configs    []ProviderConfigSpec // composer: provider configs it owns

	OperationOutput json.RawMessage // operate: verb output
	SideEffectDone  bool            // lifecycle: the side effect completed (informational)
}

Outcome carries everything a reaction may produce. A handler fills only the parts its reaction declared in Emits; the core applies exactly those (a part set without the matching Emits bit is ignored, and the manifest legality lattice forbids the dangerous combinations). Status is last-writer-wins; Conditions accumulate.

type OutcomeBit

type OutcomeBit string

OutcomeBit names one applicable Outcome part.

const (
	OutcomeChildren        OutcomeBit = "children"        // ApplyComposeResult (graph upsert/prune)
	OutcomeEdges           OutcomeBit = "edges"           // dep edges (with value-flow)
	OutcomeConfigs         OutcomeBit = "configs"         // provider configs the composer owns
	OutcomeStatus          OutcomeBit = "status"          // resources.status
	OutcomeConditions      OutcomeBit = "conditions"      // resource_conditions / health_ok
	OutcomeFinalizer       OutcomeBit = "finalizer"       // strip the finalizer (delete path)
	OutcomeOperationOutput OutcomeBit = "operationOutput" // resource_operations.output
	OutcomeSideEffect      OutcomeBit = "sideEffect"      // external side effect only; ack the delivery
)

type OutcomeMask

type OutcomeMask []OutcomeBit

OutcomeMask is the set of Outcome parts the core will APPLY for a reaction — the SECOND half of a reaction's identity. A reaction author declares it so the core knows which fields of the returned Outcome are meaningful (and validates legality, e.g. only a Children-emitting reaction can mutate the graph). It serializes as a JSON array of OutcomeBit strings.

func (OutcomeMask) Has

func (m OutcomeMask) Has(bit OutcomeBit) bool

Has reports whether the mask includes bit.

type ProviderConfigSpec

type ProviderConfigSpec struct {
	Name string
	Kind Kind
	// KindVersion is the web-API version of the consumer Kind this config is for. A
	// composer configuring a v2 child emits its config at KindVersion 2 so the config's
	// (kind, kindVersion) matches the child it attaches to (the resource-apply guard
	// enforces the match). REQUIRED and explicit (>= 1): there is no implicit v1
	// default — a composer MUST set the version its config is for.
	KindVersion int
	IsDefault   bool
	Spec        any
}

ProviderConfigSpec is one provider config a composer emits, alongside its children and edges. The handler chain threads accumulated configs (like Children/Edges) and ApplyComposeResult diffs them against the configs this root already OWNS — upserting the changed/new delta and pruning what vanished, all owned by the root so they GC with it (a composer's emitted configs never outlive the composer root). Spec is the config document; its shape is the consumer kind's manifest config_schema.

  • IsDefault=true → the consumer kind's single live-reconfigurable DEFAULT (editing it pushes to every worker; one per kind globally, enforced by uq_providerconfigs_default_per_kind).
  • IsDefault=false → a named CUSTOM config a resource references by Name via provider_config_ref.

Names are a GLOBAL handle, so a composer must name its configs uniquely (e.g. prefixed by the root) exactly like children — a name already owned by a different root is left untouched (the owner-guarded upsert no-ops).

type ReactRequest

type ReactRequest struct {
	Resource   Resource
	Transition Transition
	DedupToken string
	Env        *Env
}

ReactRequest is the lifecycle-reactor wire input: the resource that crossed a transition, the transition name, and the dedup token (the stable per-binding key an idempotent reactor uses to no-op a redelivery). Status rides in the Resource; the sink config rides in Env.ProviderConfig (like every other stage).

type ReactionDecl

type ReactionDecl struct {
	Name    string      `json:"name"`
	Trigger Trigger     `json:"trigger"`
	Emits   OutcomeMask `json:"emits"`

	// Verb refines TriggerOperation: the subresource verb this reaction handles.
	Verb string `json:"verb,omitempty"`
	// Finalizer refines TriggerDeleteRequested: the finalizer string this teardown
	// strips (defaults to the manifest's FinalizerName when empty).
	Finalizer string `json:"finalizer,omitempty"`
}

ReactionDecl is one declared reaction. name is the stable handle the worker keys its handler by (and the lifecycle_outbox binding name). trigger + emits drive the core's dispatch and apply. The optional fields refine specific triggers. Trigger is the author-facing Trigger enum; Emits is the control-plane OutcomeMask.

type ReactionRequest

type ReactionRequest struct {
	Reaction    string  // the ReactionDecl.Name — selects the handler
	KindVersion int     // the resource's web-API version (>= 1) — lets a Work serving several versions of one kind route (vpc/v1 vs vpc/v2)
	Trigger     Trigger // why it fired

	Resource    Resource        // the subject resource, with its current Spec/Status
	Status      json.RawMessage // accumulated status (work/compose/rollup); nil for the first
	Observed    []Resource      // composer: current owned children + statuses
	Descendants []Resource      // rollup: the whole settled subtree
	Operation   *Operation      // operate: the resource_operations row
	Transition  Transition      // reactor: the lifecycle edge that fired (a TransitionX constant)
	Generation  int64           // the generation that fired (pin reads/keys to it)
	DedupToken  string          // lifecycle: stable "<id>:<transition>:<gen>:<reaction>" for non-idempotent sinks
	Env         *Env            // logger + this resource's per-resource config override (merge with the default in the handler)
}

ReactionRequest carries everything a reaction may need. The core fills only the fields a given reaction needs (driven by its Trigger): SpecChange/Work gets Resource+Status; a composer also reads Observed; a rollup gets Descendants; operate gets Operation; lifecycle gets Transition+DedupToken. Reaction + Trigger tell the worker which handler to run and why.

type Resource

type Resource struct {
	ID                  uuid.UUID
	Kind                Kind
	Name                string
	OwnerID             *uuid.UUID
	RootID              *uuid.UUID
	Spec                json.RawMessage
	Status              json.RawMessage
	Generation          int64
	SyncedGen           int64
	IsReady             bool
	Finalizers          []string
	DeletionRequestedAt *time.Time
	// FrozenUntil and Quarantined are the two faces of the single resources
	// frozen_until column (orphaned = a finite future instant; quarantined =
	// 'infinity'), split here so providers read each unambiguously:
	//
	//   FrozenUntil — non-nil means the composer dropped this child and it is in
	//   the orphan-grace window (phase=Orphaned), awaiting a re-emit (which clears
	//   it) or the reaper's sweep. It is the finite teardown deadline.
	//
	//   Quarantined — true when an operator set this failed resource ASIDE
	//   (phase=Quarantined, frozen_until='infinity').
	//
	// A rollup MUST treat either as "ignore this child" (use IsFrozen): an
	// orphaned child has left the composition, and a quarantined child is
	// deliberately excluded so one bad child can't block the root.
	FrozenUntil *time.Time
	Quarantined bool
	Labels      map[string]string
}

Resource is what a reaction handler receives. JSON columns are json.RawMessage so handlers can re-marshal without conversion.

State is observed via IsReady (GENERATED from synced_gen >= generation AND health_ok AND no pending deletion) — the Synced and Ready axes folded into one queryable flag. There is no conditions array on the row and no separate observed_generation; synced_gen carries the synced axis (plus composed_gen for the compose-skip gate).

func (Resource) IsFrozen

func (r Resource) IsFrozen() bool

IsFrozen reports whether the resource is set aside from scheduling and the rollup — orphaned (in the grace window) OR quarantined. Both faces of the resources frozen_until column; a rollup must skip a frozen child.

type ResourceManifest

type ResourceManifest struct {
	Kind   Kind              `json:"kind"`
	Name   string            `json:"name"`
	Labels map[string]string `json:"labels"`
	Spec   json.RawMessage   `json:"spec"`
}

ResourceManifest is the self-describing document for a single resource: its identity (Kind + Name), Labels, and Spec bundled into one object. It is the canonical shape both on disk (the resource JSON fixtures) and on the wire (the Apply request body and the download/synthesize response). Carrying kind/name/labels alongside the spec means a manifest applies on its own, with all of its identity in the one document.

{
  "kind":   "some-kind",
  "name":   "example-1",
  "labels": {"team": "core"},
  "spec":   { ... }
}

Spec is kept as raw JSON so a manifest round-trips an arbitrary per-kind spec without this package knowing its shape, and so large specs (tens of MB) are never re-marshaled. Labels is a flat string→string map, mirroring resource_meta.labels (JSONB object); it has no omitempty so an empty label set serializes as {} (the canonical on-disk shape) rather than vanishing — and it must be a non-nil map at marshal time for that to hold.

type ResourceRef

type ResourceRef struct {
	Kind Kind
	Name string
}

ResourceRef identifies a sibling resource by (Kind, Name) — the natural key under a single owner. Composers and the provider framework use ResourceRef in DepEdge and ChildSpec; the on-disk row uses a UUID id, not a ref.

func (ResourceRef) String

func (r ResourceRef) String() string

type RollupRequest

type RollupRequest struct {
	Root        Resource
	Descendants []Resource
	Status      json.RawMessage
	Env         *Env
}

RollupRequest is the rollup wire input: the root + its settled subtree.

type StageDispatcher

type StageDispatcher interface {
	// kindVersion is the resource's web-API version (work_queue.kindVersion, carried from the
	// claim) and is ALWAYS explicit (>= 1) — there is no implicit v1 default. A
	// dispatcher that routes to versioned handlers (in-process registry, or a
	// remote worker serving several kindVersions) uses it to select the
	// (kind, kindVersion)'s handler; a 0/negative value has no handler and is a hard
	// lookup miss (a real failure, never silently v1).
	DispatchStage(ctx context.Context, kind Kind, kindVersion int, rx ReactionDecl, req ReactionRequest) (Outcome, int, error)
}

───────────────────────────────────────────────────────────────────────── StageDispatcher — the dispatch seam between the reconcile engine and wherever a reaction's handler actually RUNS.

The core's reconcile engine (internal/runtime) builds a ReactionRequest from a claimed DB row and calls DispatchStage; the IMPLEMENTATION decides WHERE the handler runs and returns its Outcome:

  • remote (the broker's fanout, the ONLY production impl): the dispatcher does NOT execute — it ships the stage to a dumb worker over Connect and awaits the worker's result. Routing/leases/heartbeats are the broker's job; running the handler is the worker's.
  • in-process (test only, test/internal/inproc): encode the stage to a proto StageTask and run it directly against the registered providers — the zero-broker path the integration suite drives.

Either way the ENGINE does all the DB reads/writes AROUND the stage (the gate reads, ApplyComposeResult, AppendOutbox) and applies the returned Outcome; the dispatcher only gets the handler run and hands the Outcome back. The single manifest-driven call means the engine never threads typed per-kind funcs.

It takes a ReactionDecl (the manifest-declared reaction being dispatched) but speaks the author-facing ReactionRequest/Outcome — a worker author never implements or calls it; only the broker/engine/test executors do.

Returns the Outcome, the failing handler index (for the error label), and the error (nil on success). A stage whose handler is not present is an error.

type Transition

type Transition string

Transition is a resource lifecycle edge a reactor_bindings subscription may watch — the set a `reactor` reaction fires on. It is delivered to the reactor handler as data (ReactionRequest.Transition). The constants are the ONLY legal values; they mirror the DB CHECK on reactor_bindings.transition / lifecycle_outbox.transition (both must agree), and the huma enum tag on the reactor-binding apply body validates an incoming value against this same set at the API edge. Use these — never a string literal — so a typo is a compile error, not a binding that silently never fires.

const (
	// TransitionCreated fires when a resource is first applied (from the apply tx).
	TransitionCreated Transition = "created"
	// TransitionSynced fires when a resource reaches synced_gen >= generation (the
	// durable "rolled up / reconciled" edge — the cascade computes it).
	TransitionSynced Transition = "synced"
	// TransitionDegraded fires when a resource's health flips true→false.
	TransitionDegraded Transition = "degraded"
	// TransitionFailed fires when a resource newly hard-fails (failure_gen == generation).
	TransitionFailed Transition = "failed"
	// TransitionDeleted fires when a resource is torn down.
	TransitionDeleted Transition = "deleted"
)

type Trigger

type Trigger string

Trigger is the condition that fires a reaction — the FIRST half of a reaction's identity; the SECOND half is the Emits mask (OutcomeMask). The core matches a claimed task / lifecycle delivery to the reaction(s) whose Trigger applies, ships the reaction, and applies the Outcome ∩ Emits.

const (
	// TriggerSpecChange fires when generation bumps (a spec edit / first apply).
	// Emits=Children → compose; Emits=Status → work. A kind can declare both.
	TriggerSpecChange Trigger = "specChange"
	// TriggerChildrenSettled fires when a root is re-pended after its whole
	// subtree settled (the cascade edge). Emits=Status → rollup. The ONLY trigger
	// for which the core loads the descendant subtree.
	TriggerChildrenSettled Trigger = "childrenSettled"
	// TriggerDeleteRequested fires when a finalizer-bearing resource is being torn
	// down. Emits=Finalizer → the core strips the finalizer string on success.
	TriggerDeleteRequested Trigger = "deleteRequested"
	// TriggerOperation fires when a resource_operations row is enqueued for a Verb.
	// Emits=OperationOutput → the core writes the verb output.
	TriggerOperation Trigger = "operation"
	// TriggerReactor marks a REACTOR reaction: a pure side effect that a
	// reactor_bindings SUBSCRIPTION delivers when a WATCHED kind crosses a
	// transition (created|synced|degraded|failed|deleted). Emits=SideEffect only.
	// The reactor CRD declares just this reaction (name + emits) — NO transition,
	// kind, or label; those live on the binding. At delivery the transition that
	// fired is passed to the handler as DATA (ReactionRequest.Transition), not
	// baked into the reaction's identity. The core acks the lifecycle_outbox row
	// on success (at-least-once).
	TriggerReactor Trigger = "reactor"
	// TriggerResync fires on a drift tick (no generation bump). Re-runs the
	// SpecChange-class reactions to re-observe health; not a distinct handler.
	TriggerResync Trigger = "resync"
)

type ValueFlow

type ValueFlow struct {
	DependentField string
	SourceField    string
}

ValueFlow declares one field-level flow on a DepEdge: the dependent's spec[DependentField] is filled from the upstream's status[SourceField] when the upstream's status becomes available.

Both fields are RFC 6901 JSON Pointers ("/account_id"); a bare key without a leading slash is treated as a single-segment pointer. The flow is applied as a JSON-pointer COPY (status[SourceField] → spec[DependentField]), guarded by present-and-non-null — an absent source field simply does not fire (no error), and the copied value is trusted as-is (it is the upstream's own status). The fields are NOT individually schema-checked; the guarantee is the DEPENDENT's own spec_schema, which the store validates when the composer emits the child (with DependentField relaxed, since the flow fills it later). So a flow into a kind with a closed spec_schema is bounded by that schema (minus the deferred field); a flow into an opaque (additionalProperties:true) kind is trusted by design.

type WorkRequest

type WorkRequest struct {
	Resource Resource
	Status   json.RawMessage
	Env      *Env
}

WorkRequest is the work wire input: the resource + accumulated status.

Jump to

Keyboard shortcuts

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