Help us shape the future of Go! Take the 2026 Go Developer Survey

model

package
v0.21.0 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: AGPL-3.0 Imports: 6 Imported by: 0

Documentation

Overview

Package model defines the records that flow through Atlas and their on-disk binary encoding.

Everything the engine processes is a keyed Record discriminated by a (ValueType, Intent) pair. Commands are intentions and are never persisted; events are facts and are appended to the write-ahead log. State is the fold of all events. See docs/architecture/data-model.md for the full reference.

The encoding here is the internal log format: a hand-written binary layout behind an explicit version byte (ADR-0009). It is deliberately reflection- free so that encoding an event on the processor hot path does not allocate (invariant I1); callers reuse a single buffer across records.

Index

Constants

View Source
const EncipheredMarker = `{"atlas:personal":`

EncipheredMarker opens the JSON envelope a personal value is stored as (ADR-0314).

It lives here, beside the variable's own encoding, because it is part of what a stored variable can *be* — and because the engine has to recognise one without being able to open it. The sealing and opening live in the vault, which is the only place that may hold a key; this const is what keeps the two from drifting, since the vault builds and matches its envelopes against it.

View Source
const HeaderSize = 1 +
	8 +
	8 +
	8 +
	8 +
	1 +
	1 +
	1 +

	2 // PartitionId

HeaderSize is the encoded size of the version byte plus the fixed header.

View Source
const MaxIndexedValueBytes = 256

MaxIndexedValueBytes bounds what the variable value index stores per entry. The index exists to find an instance by a business value — an identity, an order number, a case reference — and those are short. The bound is what keeps one pathological write from dominating the index, and it is checked rather than truncated: a truncated key would answer an exact-match query with a wrong row.

View Source
const MaxMigrationMappings = 4096

MaxMigrationMappings bounds the element mapping a single migration event may carry. The mapping covers the element indices an instance's *live* records reference — a handful for an ordinary instance, more for a wide multi-instance activity — never the size of either graph, so this is far above anything real and exists to keep a corrupt or hostile record from asking the decoder for an unbounded allocation.

Variables

View Source
var (
	// ErrShortBuffer is returned when a buffer is too small to hold the record
	// or payload being read.
	ErrShortBuffer = errors.New("model: buffer too short")
	// ErrUnknownVersion is returned when a record's version byte is not one this
	// build can decode.
	ErrUnknownVersion = errors.New("model: unknown codec version")
)

Functions

func AppendRecord

func AppendRecord(dst []byte, r *Record) []byte

AppendRecord encodes r and appends it to dst, returning the extended slice. Passing a reused buffer (e.g. buf[:0]) makes encoding allocation-free, which the processor hot path relies on (invariant I1).

func AppendValue

func AppendValue(dst []byte, v Value) []byte

AppendValue appends v's payload bytes (no record header) to dst and returns the extended slice. The state store uses this to persist materialized values; the log uses AppendRecord, which frames a full header around the payload.

func CounterOf

func CounterOf(key uint64) uint64

CounterOf returns the per-partition counter component of key.

func DecodeValueInto

func DecodeValueInto(v Value, src []byte) error

DecodeValueInto decodes a payload from src into the caller-provided v. Unlike DecodeValue it allocates nothing, so a hot-path reader can decode into a reused struct (invariant I1). v must be the concrete type matching the bytes.

func EncipheredSubject added in v0.10.0

func EncipheredSubject(text string) (string, bool)

EncipheredSubject returns the data subject a sealed personal value was enciphered for, read from the envelope's clear header (ADR-0314: the subject's id is a reference, and travels in the clear on purpose). It needs no key. ok is false when text is not an envelope with a subject.

The engine uses it to refuse a sealed value copied into a scope whose data subject is somebody else: the value would still open, under its own subject's key, but an erasure of the instance's subject would leave it readable.

func NewKey

func NewKey(partition uint16, counter uint64) uint64

NewKey composes a globally unique 64-bit key from a partition id and a per-partition monotonic counter.

func PartitionOf

func PartitionOf(key uint64) uint16

PartitionOf returns the partition that owns key.

Types

type ActionApprovalValue added in v0.20.0

type ActionApprovalValue struct {
	ProcessInstanceKey uint64
	ApprovalKey        uint64 // the request's key; every later fact names it
	JobKey             uint64 // the job whose decision asked
	DecisionKey        uint64 // the decision that asked
	At                 int64  // when this fact happened, frozen by the caller
	ExpiresAt          int64  // until when an answer may be given (requested)
	Kind               ApprovalKind
	Outcome            ApprovalOutcome // decided
	Approvals          uint8           // how many distinct approvers the obligation needs
	PolicyVersion      uint32

	ActionDigest  string
	Worker        string
	Operation     string
	Resource      string
	RequestedBy   string // the principal the action would run for
	ApproverGroup string
	PolicyID      string
	ObligationID  string
	// Principal is who answered or withdrew; empty on a request.
	Principal string
	Reason    string
}

ActionApprovalValue is one fact about an approval of one action (ADR-0475 §9): that it was requested, that an approver answered, or that an answer was withdrawn. Every fact is its own event, keyed under its instance and approval, so the approval's history is the sequence of them and nothing is ever overwritten.

An approval is of ActionDigest and nothing else. Who may give it — ApproverGroup, Approvals, the separation-of-duties rules — is checked by the server when the answer is given and again by the policy when the action is authorized; the record says what happened, not whether it still counts.

func (*ActionApprovalValue) ValueType added in v0.20.0

func (*ActionApprovalValue) ValueType() ValueType

type ActionOutcomeValue added in v0.9.0

type ActionOutcomeValue struct {
	InstanceKey uint64 // the instance that carried the action out, when one did
	At          int64  // when the outcome was recorded, Unix nanoseconds

	OrderID   string
	Position  string // the order position (ADR-0384): the item id, or item#variant
	CommandID string
	Source    string // who reported it: the order itself, the REST route, a send task
	Action    string // the action's key
	Effect    string // provision | deprovision | change | service
	Outcome   string // completed | rejected | failed
	EventType string // what it is published as: declared, or <message>.<outcome>
	Principal string // whose right it is, by id
	ItemID    string
	VariantID string
	Result    string // a JSON object of scalars, bounded by whoever reports it
}

ActionOutcomeValue is how one action asked of an order position ended (ADR-0429 §3): completed, rejected or failed, under the event type it is published as beyond Atlas.

It is keyed by the order, the position and the command id, because that is what makes a report idempotent: the process that carries out an action reports at least once, and a second identical report must not be a second fact. A report of a different outcome for the same command is refused before it is written, so the family never holds two endings of one command.

Like an entitlement it outlives the order it came from, and the same open question about erasure applies to it (ADR-0346): it names the principal by id and nothing else, and Result is bounded and holds scalars an action declares — a rule for the product's author, because the log cannot enforce it (ADR-0314).

At is read by the caller at command time and frozen into the event, so replay reproduces it rather than re-reading a clock (I4/I6).

func (*ActionOutcomeValue) Valid added in v0.9.0

func (v *ActionOutcomeValue) Valid() bool

Valid reports whether this outcome can be written down: it names the command it ends and how it ended.

func (*ActionOutcomeValue) ValueType added in v0.9.0

func (*ActionOutcomeValue) ValueType() ValueType

type ApprovalKind added in v0.20.0

type ApprovalKind uint8

ApprovalKind says which fact about an approval an ActionApprovalValue is.

const (
	// ApprovalRequested opens an approval: a decision found an approval obligation
	// unmet and asked for one, of one action digest.
	ApprovalRequested ApprovalKind = 1
	// ApprovalDecided is one approver's answer.
	ApprovalDecided ApprovalKind = 2
	// ApprovalRevoked withdraws an approver's earlier answer.
	ApprovalRevoked ApprovalKind = 3
)

func (ApprovalKind) String added in v0.20.0

func (k ApprovalKind) String() string

type ApprovalOutcome added in v0.20.0

type ApprovalOutcome uint8

ApprovalOutcome is an approver's answer.

const (
	ApprovalOutcomeGranted  ApprovalOutcome = 1
	ApprovalOutcomeRejected ApprovalOutcome = 2
)

func (ApprovalOutcome) String added in v0.20.0

func (o ApprovalOutcome) String() string

type AuthorizationValue added in v0.20.0

type AuthorizationValue struct {
	ProcessInstanceKey uint64
	ElementInstanceKey uint64
	JobKey             uint64
	// LeaseEpoch is the lease the decision was made under. A decision is a decision
	// for that lease only: the next lease needs its own.
	LeaseEpoch uint64
	// DecisionKey is the event's own key, minted on the command path.
	DecisionKey uint64
	// DecidedAt is the instant the request was evaluated at, read by the enforcement
	// point from its clock.
	DecidedAt int64
	Effect    AuthzEffect
	// Enforced is false for a shadow decision: recorded, labeled, never blocking.
	Enforced      bool
	PolicyVersion uint32

	ReasonCode     string
	PolicyID       string
	Worker         string // the configured Worker
	WorkerInstance string // the lease holder's self-asserted label
	WorkerType     string
	Operation      string
	Resource       string
	ActionDigest   string
	InitiatedBy    string // the instance's StartedBy
	ExecutedFor    string // the Atlas principal the action ran for
	ExecutedAs     string // the external identity key, or service:<worker>
	ConnectionID   string
	IdentityMode   string
	Detail         string
}

AuthorizationValue is one policy decision about one external Worker action, the evidence an enforcement point writes before it releases anything the action needs (ADR-0475 §12).

Everything in it was decided on the command path and is frozen here: applyToState stores it and nothing else, so replay rebuilds the same history without evaluating a policy, and a decision already in the log is never recomputed (I4/I6).

It names people by stable principal id and never by name or address, and it never holds a token, a secret or a credential reference. Detail is a bounded JSON object — obligations, approvals used, the trace — written by the enforcement point.

func (*AuthorizationValue) ValueType added in v0.20.0

func (*AuthorizationValue) ValueType() ValueType

type AuthzEffect added in v0.20.0

type AuthzEffect uint8

AuthzEffect is a policy decision's effect as the log records it. The numbers are authz.Effect's and are pinned by a test, because a decision decoded years later must read back as what it was.

const (
	AuthzIndeterminate AuthzEffect = 0
	AuthzPermit        AuthzEffect = 1
	AuthzDeny          AuthzEffect = 2
	AuthzNotApplicable AuthzEffect = 3
)

func (AuthzEffect) String added in v0.20.0

func (e AuthzEffect) String() string

type CompensableValue

type CompensableValue struct {
	ProcessInstanceKey uint64
	ProcessDefKey      uint64
	ScopeKey           uint64 // FlowScopeKey the compensable activity lived in
	ElementInstanceKey uint64 // the completed activity's element-instance key
	Seq                uint64 // the record's key sequence (log position), set on consume
	ElementId          int32  // the compensable activity's compiled node id
	HandlerNode        int32  // the compensation handler's compiled node id
}

CompensableValue is one completed compensable activity: an activity that bore a compensation boundary and finished successfully, retained so a later compensation throw can run its handler (ADR-0103). It is keyed under ScopeKey in completion order (by the event's log position), so a reverse scan yields reverse completion order. ElementId identifies the compensated activity (for activityRef matching); HandlerNode is the compensation handler to activate; Seq carries the record's key sequence back on the consume event so its index entry can be deleted. All fields are fixed-width.

func (*CompensableValue) ValueType

func (*CompensableValue) ValueType() ValueType

type DataObjectValue

type DataObjectValue struct {
	ScopeKey uint64 // owning scope (process instance key today)
	Name     string
	State    string // BPMN data state; "" when the object declares none
	Kind     VarKind
	Bool     bool
	Text     string // number canonical string, string contents, or canonical JSON; empty otherwise
	// ProducerKey names the element instance whose processing wrote this value: the
	// activity whose data output association produced it (ADR-0058/0060). It is the
	// fact that says *who wrote this*, which no diff of two data-object snapshots can
	// recover — on two parallel branches both branches see both writes, so both appear
	// to have made them. Data objects take the same answer variables took (ADR-0219).
	//
	// 0 means no element wrote it: the seeding at instance creation, or a record
	// written before attribution existed. Stamped at command time and frozen into the
	// event, never recomputed on replay (I6). Append-compatible: an old record ends
	// after Text and decodes to 0.
	ProducerKey uint64
}

DataObjectValue is a BPMN data object owned by a scope (the process instance root today). It is variable-shaped — a name and a typed value reusing the same VarKind machinery, so a data object can hold a structured VarJSON payload (ADR-0037) — plus a State: the BPMN data state (e.g. "received", "approved"). Its encoding mirrors VariableValue with the extra State string between the name and the kind byte; like a variable it carries genuine runtime data, so it is length-prefixed rather than fixed-size (ADR-0053).

func (*DataObjectValue) ValueType

func (*DataObjectValue) ValueType() ValueType

type DecisionEvaluationValue

type DecisionEvaluationValue struct {
	ProcessInstanceKey uint64 // owning instance (the scope this record is keyed under)
	ElementInstanceKey uint64 // the business rule task instance that evaluated
	ProcessDefKey      uint64 // the process definition, to map ElementId onto the diagram
	ElementId          int32  // interned diagram node id of the business rule task
	DecisionId         string // the evaluated DMN decision id
	InputsJSON         string // canonical JSON object: the decision's input context
	OutputsJSON        string // canonical JSON object: the decision's outputs (name → value)
	TraceJSON          string // temis trace JSON (which rules fired); "" when none
	// DecisionKey is the deployment whose model answered: a decision deployment, or
	// the process's own key for a bundled model. It is what makes the version that
	// ran a fact in the log once latest is resolved when the job is worked
	// (ADR-0423). An appended field: a
	// record written before it ends after TraceJSON and reads 0, "not recorded".
	DecisionKey uint64
}

DecisionEvaluationValue is one business rule task's DMN decision evaluation, retained for debugging (ADR-0066). It freezes what the worker computed off the processor goroutine: the input context the decision was given, the outputs it produced, and the temis trace explaining which rules fired. The three payloads are canonical JSON text (InputsJSON and OutputsJSON are objects; TraceJSON is the temis trace tree, or "" when the evaluator produced none — e.g. a literal- expression decision or a remote decision whose worker returned no trace).

It is keyed under its owning ProcessInstanceKey as append-only history — one record per evaluation, never overwritten — so an operator can inspect after the fact exactly how a decision was made. ElementInstanceKey, ProcessDefKey and ElementId locate the business rule task on its instance and diagram. Like a variable it carries genuine runtime data, so its encoding is length-prefixed.

func (*DecisionEvaluationValue) ValueType

func (*DecisionEvaluationValue) ValueType() ValueType

type DispatchChannel added in v0.20.0

type DispatchChannel uint8

DispatchChannel says whether a dispatch was a signal or a message.

const (
	// DispatchSignal is a signal broadcast (ADR-0088).
	DispatchSignal DispatchChannel = 1
	// DispatchMessage is a message published for correlation (ADR-0020).
	DispatchMessage DispatchChannel = 2
)

type DispatchSource added in v0.20.0

type DispatchSource uint8

DispatchSource says what sent a dispatch.

const (
	// DispatchFromProcess is a running instance: a throw event, an end event, or a
	// send task naming a message.
	DispatchFromProcess DispatchSource = 1
	// DispatchFromPublish is a publish with no sending instance: POST /api/v1/messages,
	// or atlas's own order and fulfilment publishes.
	DispatchFromPublish DispatchSource = 2
	// DispatchFromInbound is a Worker's inbound watch (ADR-0075).
	DispatchFromInbound DispatchSource = 3
	// DispatchDirected is a directed delivery to one running instance (ADR-0428).
	DispatchDirected DispatchSource = 4
)

type DispatchValue added in v0.20.0

type DispatchValue struct {
	Channel        DispatchChannel
	Source         DispatchSource
	Name           string
	CorrelationKey string // a message's; empty for a signal
	// The sending instance, its definition and the element that sent: set for
	// DispatchFromProcess, zero otherwise.
	SenderProcessInstanceKey uint64
	SenderProcessDefKey      uint64
	SenderElementId          int32
	// The instance a directed delivery was addressed to and its definition: set for
	// DispatchDirected, zero otherwise.
	TargetProcessInstanceKey uint64
	TargetProcessDefKey      uint64
	Reached                  uint32
	Started                  uint32
}

DispatchValue is the fact that a signal was broadcast or a message published, by whom, and how far it reached (ADR-0472 §1). It is written once per send, on the command path and after the deliveries, so the counts are what the send did: Reached the subscriptions whose element still waited, Started the instances it started. A send that reached nobody is written with both zero.

It carries no payload: what a signal or a message carries is every variable of the sending instance, and the log that is folded from this records names and moments, never values.

func (*DispatchValue) ValueType added in v0.20.0

func (*DispatchValue) ValueType() ValueType

type ElementInstanceValue

type ElementInstanceValue struct {
	ProcessInstanceKey uint64
	ProcessDefKey      uint64
	ElementId          int32  // INDEX into the compiled graph, not a string
	FlowScopeKey       uint64 // parent scope (subprocess instance), 0 = root
	BpmnElementType    uint8  // for fast dispatch
	// AttachedToKey links a boundary event's element instance to the host activity
	// instance it is attached to (0 for every non-boundary element). It lets an
	// interrupting boundary find and terminate its host, and a completing host find
	// and disarm its boundary events (ADR-0040).
	AttachedToKey uint64
	TokenID       uint64
	ParentTokenID uint64
	SourceFlowId  int32
	// MultiInstance marks an element instance's role in a multi-instance activity
	// (ADR-0077): 0 = not multi-instance, 1 = the body (the scope that seeds the
	// iterations), 2 = an inner iteration (running the node's real behavior, scoped
	// under the body). Append-compatible: an old record without it decodes to 0.
	MultiInstance uint8
	// EventGatewayKey labels a catch event armed by an event-based gateway with the
	// gateway's element-instance key — its race group (ADR-0110). The first armed catch to
	// fire cancels every other live instance sharing this key. 0 for every element not armed
	// by an event gateway. Append-compatible: an old record without it decodes to 0.
	EventGatewayKey uint64
}

ElementInstanceValue is the token-carrying state of one active BPMN element.

func (*ElementInstanceValue) ValueType

func (*ElementInstanceValue) ValueType() ValueType

type ElementMapping added in v0.3.0

type ElementMapping struct {
	From int32
	To   int32
}

ElementMapping is one element index in the source definition's compiled graph and the index that means the same element in the target's. Indices, never ids: element ids are interned at compile time and never written to the log as text (invariant I5), so the API resolves an operator's element-id overrides into indices before the command is ever submitted.

type EntitlementHistoryValue added in v0.7.0

type EntitlementHistoryValue struct {
	// Principal, ItemID, VariantID and OrderID are copied from the hold as it
	// stood. Principal ids and nothing else about a person, exactly as the
	// inventory holds them (ADR-0314): an append-only
	// family cannot forget what it was given in the clear, and this one is meant
	// to be read years later.
	Principal string
	ItemID    string
	VariantID string
	// OrderID names the order behind the hold. It is kept knowing the order itself
	// is probably gone — an id that resolves to nothing still says the hold came
	// through an order and names which, and an auditor with an exported log can
	// find it there.
	OrderID string
	// Since is when the hold began and EndedAt when it stopped being recorded,
	// both in unix nanoseconds. Since is copied from the hold; EndedAt is read at
	// command time and frozen into the event, so replay reproduces it rather than
	// re-reading a clock (invariants I4/I6).
	Since   int64
	EndedAt int64
	// Until is the end the hold was granted with, or zero. Kept beside EndedAt
	// rather than collapsed into it because the pair is the finding: a hold whose
	// EndedAt is far past its Until was overdue for that long, and after the row
	// is written that is the only place the fact survives.
	Until int64
	// Origin is where the knowledge came from, copied from the hold.
	Origin      EntitlementOrigin
	EndedReason HoldEnd
	// EndedBy is who decided, as a principal id, or empty where nothing recorded
	// it. A return driven by a modelled process names the principal the process
	// authenticated as, which is the honest answer — a process is who its token
	// says it is.
	EndedBy string
	// ApprovedBy is who approved the order line behind the hold, copied from it:
	// the other end of the story EndedBy closes. Empty where no approval was
	// recorded.
	ApprovedBy string
}

EntitlementHistoryValue is one hold that has ended.

It copies the hold rather than referring to it, because there is nothing left to refer to: that is the whole point. The entitlement row is deleted in the same transaction that writes this one, and the order was very likely deleted years earlier.

func (*EntitlementHistoryValue) CoveredAt added in v0.7.0

func (v *EntitlementHistoryValue) CoveredAt(at int64) bool

CoveredAt reports whether this row says the principal was recorded as holding the item at that moment.

Recorded, not "had": what a corrected row covers is a period Atlas claimed, and HoldEnd.Held is what separates the two. The half-open interval is deliberate — a hold that ended at T did not cover T, so a right returned and re-granted in the same nanosecond is not counted twice.

func (*EntitlementHistoryValue) Overdue added in v0.7.0

func (v *EntitlementHistoryValue) Overdue() int64

Overdue reports how long past its promised end the hold was still recorded, in nanoseconds, and zero for one that had no end or ended on time.

func (*EntitlementHistoryValue) Valid added in v0.7.0

func (v *EntitlementHistoryValue) Valid() bool

Valid reports whether this row names both halves of "who held what". A row missing either is a fact about nothing.

func (*EntitlementHistoryValue) ValueType added in v0.7.0

func (*EntitlementHistoryValue) ValueType() ValueType

type EntitlementOrigin added in v0.7.0

type EntitlementOrigin uint8

EntitlementOrigin says where the knowledge that somebody holds something came from. It is the difference between evidence and an assumption, and it costs one byte now against a migration of an append-only column family later.

const (
	// OriginOrdered is the ordinary one: an order was placed, approved and
	// provisioned, and Atlas watched it happen.
	OriginOrdered EntitlementOrigin = 0
	// OriginAdopted is a privilege reconciliation found in a target system and
	// somebody accepted into the inventory. Atlas did not grant it and says so.
	OriginAdopted EntitlementOrigin = 1
	// OriginLegacy is what the estate already held when the portal was
	// commissioned.
	//
	// On the first day the inventory is empty and reality is full. Without this,
	// the first reconciliation reports every existing privilege as a discrepancy,
	// and each of those carries an executable "remove in the target system" — which
	// is how a new access-governance tool locks out a company on its first run.
	OriginLegacy EntitlementOrigin = 2
)

func (EntitlementOrigin) String added in v0.7.0

func (o EntitlementOrigin) String() string

type EntitlementValue added in v0.7.0

type EntitlementValue struct {
	// Principal is who holds it, as a principal id and nothing else. Never a name,
	// an address or a department: those resolve from the account when a screen is
	// rendered (ADR-0314), and an append-only log cannot
	// forget what it was given in the clear.
	Principal string
	// ItemID is the catalogue item, as the release that granted it named it. A
	// product renamed since resolves through the item, not through the text.
	ItemID string
	// VariantID is the variant held, empty where the item has none.
	VariantID string
	// OrderID names the order that produced it. Empty for an adopted or legacy
	// entitlement, which is the honest answer: nothing here produced it.
	OrderID string
	// Since is when the hold began, in unix nanoseconds. Read at command time and
	// frozen into the event, so replay reproduces it rather than re-reading a clock
	// (invariants I4/I6).
	Since  int64
	Origin EntitlementOrigin

	// Until is when this right was meant to end, in unix nanoseconds, or zero for
	// one with no end. Set only for a right the portal granted, from the ceiling
	// the release's product declared (ADR-0344).
	//
	// **It is a promise about when access should end, not a statement that it
	// has.** An entitlement past its Until is still held: the target system still
	// has the membership, and nothing has run. Removing the record when a clock
	// passes would make Atlas assert that somebody does not have access they
	// demonstrably do — which is the direction of wrongness ADR-0334 calls the one
	// that corrupts the evidence. Decaying into it is bad; manufacturing it on a
	// timer would be worse.
	//
	// Never set for an adopted or legacy right, whatever its product declares. A
	// commissioning load records Since as when the right was *found*, so a ceiling
	// measured from it would schedule the expiry of a whole estate on the
	// anniversary of the day somebody switched the portal on.
	Until int64

	// ApprovedBy is who approved the order line that granted this right, as a
	// principal id, or empty where no approval was recorded: a right that needed
	// none, one adopted or loaded rather than ordered, and one granted before
	// approvals were recorded. It is kept here and not only on the order because
	// the order is deleted by retention long before the right ends, and "who let
	// this person have it" is the question an access review asks of a right still
	// held.
	ApprovedBy string
}

EntitlementValue is one thing one principal holds.

func (*EntitlementValue) Expired added in v0.7.0

func (v *EntitlementValue) Expired(now int64) bool

Expired reports whether this right's end has passed. A right with no end never expires, which is every right an installation holds until a product declares a ceiling.

func (*EntitlementValue) Valid added in v0.7.0

func (v *EntitlementValue) Valid() bool

Valid reports whether this entitlement can be written down. Both halves of "who holds what" are required: an entitlement naming nobody is a fact about nothing, and one naming no item is a person holding the unspecified.

func (*EntitlementValue) ValueType added in v0.7.0

func (*EntitlementValue) ValueType() ValueType

type FeedRetentionValue added in v0.9.0

type FeedRetentionValue struct {
	Through uint64
}

FeedRetentionValue is the cut the event feed's retention makes (ADR-0429 §5): the feed drops every row of the partition at or before Through, a log position.

The sweep picks the position from the rows' own times and freezes it into the command, so replay prunes exactly what the live run pruned rather than re-reading a clock (I4/I6).

func (*FeedRetentionValue) ValueType added in v0.9.0

func (*FeedRetentionValue) ValueType() ValueType

type HoldEnd added in v0.7.0

type HoldEnd uint8

HoldEnd says why a hold stopped being recorded — and, because the two reasons differ in what they assert, what the row containing it means.

This is not a label on an otherwise uniform row. A returned hold is evidence that the person *had* the access until that moment. A corrected hold is evidence that Atlas *claimed* they did, and reconciliation found the target system disagreed. Writing the second as though it were the first would put in a record kept for years an assertion that somebody had access nobody can show they had — which is the direction of wrongness ADR-0334 calls the one that corrupts the evidence.

const (
	// EndReturned is a right given back: the order line was returned, the
	// deprovisioning process ran, the access went away. The ordinary ending, and
	// the only one that is evidence of a held period.
	EndReturned HoldEnd = 0
	// EndCorrected is a right reconciliation found the target system did not have,
	// and somebody accepted the correction.
	//
	// The access did not end here. As far as anybody can tell it was never there,
	// or went away at a moment nobody observed. What ended is Atlas's claim, and
	// the row says only that.
	EndCorrected HoldEnd = 1
)

func (HoldEnd) Held added in v0.7.0

func (e HoldEnd) Held() bool

Held reports whether a row that ended this way is evidence the access existed for the period it names.

The distinction every reader of this family has to make, given a name so that no reader has to rediscover it. A report that answers "what did this person hold in March" must show a corrected row differently from a returned one, or it asserts on Atlas's behalf something Atlas explicitly declined to decide.

func (HoldEnd) String added in v0.7.0

func (e HoldEnd) String() string

type InboundDeliveryValue

type InboundDeliveryValue struct {
	SourceID  string
	SourceSeq uint64
}

InboundDeliveryValue advances an external event source's inbound high-water mark (ADR-0075). SourceID is an opaque per-source identifier (e.g. a clio worker + watched subject) the engine never interprets; SourceSeq is that source's monotonic sequence up to which delivery has been applied. Folding these into a per-source high-water mark lets a replayed at-least-once publish be skipped.

func (*InboundDeliveryValue) ValueType

func (*InboundDeliveryValue) ValueType() ValueType

type IncidentReason added in v0.5.0

type IncidentReason uint8

IncidentReason classifies why an incident was raised, for the resolve path and for an operator reading a list of them.

const (
	// IncidentUnclassified is "not recorded": an older record, or a source whose
	// resolution the element's node type still decides.
	IncidentUnclassified IncidentReason = 0
	// IncidentOverBudget marks an element that was activated but never ran, because
	// its token had used up the execution budget for this run
	// (ADR-0272). Resolving it runs the behavior that never ran.
	IncidentOverBudget IncidentReason = 1
	// IncidentTooManyIterations marks a multi-instance activity that asked for more
	// iterations than the instance budget allows (ADR-0276). Like
	// IncidentOverBudget it names an element that never ran, so resolving it runs the
	// behavior — which re-evaluates the count, and parks again if it is still too
	// large.
	IncidentTooManyIterations IncidentReason = 2
	// IncidentVariableTooLarge marks an element whose write was refused because the
	// value was past the budget for a variable, or for a multi-instance activity's
	// output collection (ADR-0294). Unlike the two above it
	// names an element that *did* run: the work happened and its result is the thing
	// that will not fit. Resolving it does not write the value again: nothing re-runs
	// the element's write, so the incident goes and the element stays where it stopped
	// (#1123).
	IncidentVariableTooLarge IncidentReason = 3
	// IncidentPersonalInTheClear marks an element whose write was refused because it
	// carried a value the process declared personal (ADR-0314) and carried it in the
	// clear. Such a value is enciphered where it enters Atlas — a form, a worker's
	// result, an operator's write — and the engine cannot do it: it holds no key, by
	// the same invariant that keeps applyToState free of side effects (I4).
	//
	// So a write that arrives readable came from a path with no edge to seal at, and
	// the honest outcome is to refuse it: writing it would put a value in the log that
	// no erasure could ever reach, while the model said the opposite. Resolving retries
	// the write, so re-delivering the value through a path that does seal lets it
	// through.
	IncidentPersonalInTheClear IncidentReason = 4
	// IncidentDataSubjectMoved marks an element whose write was refused because it
	// would have changed the instance's data subject after values were already sealed
	// under the previous one (ADR-0314).
	//
	// Allowing it would split one person's data across two keys, and then erasing
	// either of them would leave the other half readable — silently, which is the
	// failure the whole mechanism exists to prevent. Before anything is sealed the
	// change is harmless and goes through; afterwards the honest remedy is a new
	// instance with the right subject, so this does not become a retry somebody can
	// resolve their way out of.
	IncidentDataSubjectMoved IncidentReason = 5
	// IncidentPersonalOfAnotherSubject marks an element whose write was refused because it
	// carried a sealed personal value enciphered for a different data subject than the one
	// the target scope names (ADR-0314). A value only reaches a scope sealed for someone
	// else by being copied there — an output mapping handing a value out of a subprocess
	// that shadowed the subject, say. It would still open under its own subject's key, so
	// nothing would look wrong, but erasing the instance's subject would leave it readable.
	// The same reason refuses the other order: the data subject's first write into a scope
	// that already holds such a copy, which a call activity seeding its child produces.
	IncidentPersonalOfAnotherSubject IncidentReason = 6
	// IncidentUnhandledError marks a BPMN error that no handler caught (ADR-0089): a
	// worker's thrown error, an error end event, a mockup task's simulated one. The
	// element did not finish what it was doing, so resolving retries the thing that
	// threw — the job, which an uncaught worker error leaves in place with its retries
	// spent, or the throw itself — and an error still uncaught parks again on a fresh
	// incident rather than leaving the token parked behind none.
	IncidentUnhandledError IncidentReason = 7
	// IncidentAuthorizationDenied marks a job whose external action a policy refused
	// (ADR-0475 §5): the decision is recorded beside it
	// and its message names the reason and the decision. Retrying would only be refused
	// again, so the job parks here with its retries spent; resolving it re-offers the
	// job, and the next lease is decided afresh — under whatever the policies, the
	// account and the connection say by then.
	IncidentAuthorizationDenied IncidentReason = 8
)

type IncidentValue

type IncidentValue struct {
	ProcessInstanceKey uint64
	ElementInstanceKey uint64
	JobKey             uint64
	ElementId          int32 // the compiled-graph element the token is stuck on (maps to a BPMN element id for the operator)
	RaisedAt           int64 // unix nanoseconds the incident was raised; read at command time and frozen into the event (I6)
	Message            string
	// Reason says what raised the incident, where the message says it in words. It
	// exists because resolving one is not one act: an incident the element never got
	// to run past needs its behavior run, where a failed job needs the job re-created.
	// The engine used to infer that from the element's node type, which works only as
	// long as each node type has one way of getting stuck.
	//
	// Zero is IncidentUnclassified and means exactly that — nothing was recorded —
	// which is what every incident written before this field existed decodes as, and
	// what the sources still resolved by node type write today.
	Reason IncidentReason
}

IncidentValue is a durable fault attached to the element instance where progress stalled — today, a job whose retries were exhausted (ADR-0061). It is keyed by ElementInstanceKey (one activity holds at most one job, so at most one incident) and points at the job, so resolving the incident can re-activate it. Message is the worker-reported failure reason; it is genuine runtime data, so the value is length-prefixed rather than fixed-size.

func (*IncidentValue) ValueType

func (*IncidentValue) ValueType() ValueType

type Intent

type Intent uint8

Intent is the verb of a record: what transition it represents. The element- instance lifecycle (Activating → Activated → Completing → Completed, plus Terminating → Terminated) is the heart of the model.

const (
	// ElementInstance lifecycle (every BPMN element goes through this).
	IntentActivating Intent = iota
	IntentActivated
	IntentCompleting
	IntentCompleted
	IntentTerminating
	IntentTerminated

	// SequenceFlow.
	IntentSequenceFlowTaken

	// Job.
	IntentJobCreated
	IntentJobActivated // picked up by a worker
	IntentJobCompleted
	IntentJobFailed
	IntentJobTimedOut
	IntentJobAssigned // user-task assignee set/changed/cleared (claim/unclaim, ADR-0042)

	// Timer.
	IntentTimerCreated
	IntentTimerTriggered

	// Message / Signal.
	IntentSubscriptionCreated
	IntentSubscriptionCorrelated
	IntentMessagePublished

	// Variable.
	IntentVariableCreated
	IntentVariableUpdated

	// Incident.
	IntentIncidentCreated
	IntentIncidentResolved

	// IntentJobCanceled retires a job whose element was interrupted (e.g. by an
	// interrupting boundary event) rather than completed by a worker. It applies
	// like JobCompleted/JobFailed (the job is deleted), and is appended here rather
	// than beside the other job intents so the existing intents keep their numeric
	// values on the log.
	IntentJobCanceled

	// IntentTimerCanceled retires a timer without firing it — used when a new
	// version of a process supersedes a prior version's timer start event, so the
	// old schedule stops (ADR-0051). It applies like TimerTriggered (the timer is
	// deleted from the due-date index) but, unlike it, drives no side effect: no
	// instance is created. Appended at the end so existing intents keep their log
	// values.
	IntentTimerCanceled

	// IntentTimerStartArm is a command-only intent (never persisted as an event):
	// it directs the processor to arm a freshly deployed definition's timer start
	// events, creating their durable timers and retiring any that a prior version
	// left armed (ADR-0051). Because commands are not replayed (invariant I6), its
	// numeric value never reaches the log.
	IntentTimerStartArm

	// DataObject. Appended after the existing intents so every prior intent keeps
	// its numeric value on the log (ADR-0053). Created seeds a data object under a
	// scope with its declared initial data state; StateChanged transitions the data
	// state (and, later, the value) as an activity writes to it.
	IntentDataObjectCreated
	IntentDataObjectStateChanged

	// IntentDecisionEvaluated records that a business rule task's DMN decision was
	// evaluated (ADR-0066). Appended after the existing intents so every prior
	// intent keeps its numeric value on the log. It is a pure history event: the
	// worker has already evaluated the decision off the processor goroutine, and the
	// resulting inputs/outputs/trace are frozen into the event so replay re-applies
	// them without re-evaluating (invariant I6).
	IntentDecisionEvaluated

	// IntentVariableDeleted removes a variable from its scope. It applies like the
	// inverse of VariableCreated (the variable is deleted rather than put), and is
	// appended at the end so every prior intent keeps its numeric value on the log.
	// Its use is dropping an activity-local variable scope when the activity
	// completes, after I/O output mappings have promoted the values that escape
	// (ADR-0068): the drop is emitted as one VariableDeleted per local variable, so
	// replay reproduces it exactly (invariant I6) rather than recomputing it.
	IntentVariableDeleted

	// IntentInboundDeliveryApplied advances an external source's inbound high-water
	// mark (ADR-0075). It applies by upserting the source's last-applied sequence,
	// and is appended at the end so every prior intent keeps its numeric value on the
	// log. It rides in the same batch as the message publish it guards, so the
	// dedup mark and the correlate/start effects it authorizes commit atomically.
	IntentInboundDeliveryApplied

	// IntentVariableModify is a command-only intent (never persisted as an event),
	// like IntentTimerStartArm: it directs the processor to set or overwrite
	// variables on a running instance's scope from outside the model — an operator
	// correction to a live instance (ADR-0095). The handler validates the target
	// scope, then emits a VariableCreated event for a new name or a VariableUpdated
	// event for an existing one, so the change is a durable, replayable fact recorded
	// in the instance's variable timeline (the audit trail), not a raw store write.
	// Because commands are not replayed (invariant I6), its numeric value never
	// reaches the log, so it is appended at the end without disturbing the persisted
	// intents' values.
	IntentVariableModify

	// IntentVariableAudited records that an external actor set a variable on a running
	// instance (ADR-0098). It is a pure history event, like IntentDecisionEvaluated:
	// emitted alongside the VariableCreated/VariableUpdated the override produces, it
	// freezes who made the change into the log so replay rebuilds the identical audit
	// trail without re-running the command (invariant I6). Appended at the end so every
	// prior intent keeps its numeric value on the log.
	IntentVariableAudited

	// IntentJobErrorThrown is a command-only intent (never persisted as an event): a
	// worker reports that its job threw a BPMN error code (ADR-0089). Its handler cancels
	// the job and propagates the error from the job's element to the nearest matching error
	// handler — so, unlike IntentJobFailed, it is control flow, not a retry/incident.
	// Because commands are not replayed (invariant I6), its numeric value never reaches the
	// log. Appended at the end so every prior intent keeps its numeric value.
	IntentJobErrorThrown

	// IntentCompensableRecorded records that a compensable activity (one bearing a
	// compensation boundary) completed successfully, so a later compensation throw can
	// run its handler (ADR-0103). It is a pure state event, keyed under the activity's
	// scope in completion order: applyToState writes it into the compensable index off
	// the completion event, so recovery rebuilds the identical index (invariant I6).
	// Appended at the end so every prior intent keeps its numeric value on the log.
	IntentCompensableRecorded
	// IntentCompensableConsumed removes a compensable record once its activity has been
	// compensated (the handler was activated), so it is compensated at most once (ADR-0103).
	// It carries the record's scope and sequence; applyToState deletes that index entry.
	IntentCompensableConsumed

	// IntentOperatorActed records that an operator intervened on a running instance —
	// completing a parked job by hand, say (ADR-0159). Like IntentVariableAudited it is a
	// pure history event emitted alongside the events the intervention produces, freezing
	// who acted and why into the log so replay rebuilds the identical audit trail without
	// re-running the command (invariant I6). Appended at the end so every prior intent
	// keeps its numeric value on the log.
	IntentOperatorActed

	// IntentPurging is a command-only intent (never persisted as an event), like
	// IntentTimerStartArm: the retention sweep directs the processor to hard-delete a
	// finished instance's history (ADR-0115). Its handler emits IntentPurged for the
	// instance it carries. Because commands are not replayed (invariant I6), its numeric
	// value never reaches the log. Appended at the end so every prior intent keeps its
	// numeric value.
	IntentPurging
	// IntentPurged removes a finished process instance's history from the state store —
	// the terminal record and every per-instance family (ADR-0115). It is the durable
	// delete: applyToState folds it into the removals, so recovery reproduces the purge
	// (invariants I4/I6). Appended at the end so every prior intent keeps its numeric
	// value on the log.
	IntentPurged

	// IntentConditionRecheck is a command-only intent (never persisted as an event), like
	// IntentJobErrorThrown: after a batch writes one or more variables in an instance, the
	// processor schedules a re-check of that instance's armed conditional events (ADR-0137).
	// Its handler evaluates each armed conditional's FEEL condition over its scope chain and
	// drives the ones now true to Completing. Because commands are not replayed (invariant
	// I6) — the fire is the persisted Completing→Completed chain — its numeric value never
	// reaches the log. Appended at the end so every prior intent keeps its numeric value.
	IntentConditionRecheck

	// IntentMigrating is a command-only intent (never persisted as an event), like
	// IntentPurging: an operator directs the processor to rebind a running instance to
	// another deployed version of its process (ADR-0162). Its handler re-checks what the
	// API validated, then emits IntentMigrated for the instance it carries. Because
	// commands are not replayed (invariant I6), its numeric value never reaches the log.
	// Appended at the end so every prior intent keeps its numeric value.
	IntentMigrating
	// IntentMigrated rebinds a running instance to another deployed version: the
	// durable fact applyToState folds, carrying both definition keys and the element
	// mapping it rewrites the instance's live records through (ADR-0162). Appended at
	// the end so every prior intent keeps its numeric value on the log.
	IntentMigrated

	// IntentVariableReindex is a command-only intent (never persisted as an event), like
	// IntentMigrating: an operator asks that one instance's variable-index membership be
	// brought back in line with what its process declares searchable (ADR-0244). Its
	// handler compares the instance's root-scope variables against the declaration and
	// emits IntentVariableIndexed for each one whose membership differs — none, on an
	// instance already in step. Because commands are not replayed (invariant I6), its
	// numeric value never reaches the log. Appended at the end so every prior intent
	// keeps its numeric value.
	IntentVariableReindex
	// IntentVariableIndexed changes one variable's membership in the value index without
	// touching its value: the durable fact applyToState folds when a migration or a
	// reindex moves an instance to a declaration that differs from the one its values
	// were stamped under. Appended at the end so every prior intent keeps its numeric
	// value on the log.
	IntentVariableIndexed

	// IntentVariableElementSet sets one element of a list variable, naming the element
	// rather than carrying the whole list. It is what a multi-instance activity emits
	// per finished iteration, and it exists because the alternative grows with the
	// square of the iteration count: writing the collection each round put the growing
	// list into the log and into the variable timeline once per round
	// (ADR-0296).
	//
	// It folds by reading the collection, setting the element and writing it back —
	// deterministic, a function of the batch's own state, so replay reaches the same
	// list (I4). It records no snapshot: a loop's half-filled collection is scratch at
	// the body scope until the loop promotes it, the same reasoning that keeps the
	// dropping of an activity-local scope out of the timeline. Appended at the end so
	// every prior intent keeps its numeric value on the log.
	IntentVariableElementSet

	// IntentForking is a command-only intent (never persisted as an event), like
	// IntentMigrating: an operator directs the processor to end a running instance and
	// continue its work in a *new* instance of another deployed version, at resume
	// points they named (ADR-0389). It is the answer to the
	// migration ADR-0162's validator has to refuse — a token whose element the target
	// version no longer has cannot be rebound, but the instance's data and the decision
	// about where to pick it up again can still cross.
	//
	// Its handler re-checks what the API validated and then emits only events that
	// already exist: the successor's IntentActivated (carrying PredecessorInstanceKey),
	// the predecessor's IntentTerminated (carrying SuccessorInstanceKey), and an
	// operator action on each. Because commands are not replayed (invariant I6), its
	// numeric value never reaches the log. Appended at the end so every prior intent
	// keeps its numeric value.
	IntentForking

	// IntentEntitlementGranted and IntentEntitlementRevoked record that somebody
	// started or stopped holding a catalogue item
	// (ADR-0312).
	//
	// They are the inventory's only two events, and there is deliberately no third
	// for editing one. An entitlement is observed or derived, never authored: the
	// day somebody can edit one directly is the day the inventory stops being
	// evidence and becomes an opinion. A correction is a revoke and a grant, both of
	// which say when they happened.
	//
	// Appended at the end so every prior intent keeps its numeric value on the log.
	IntentEntitlementGranted
	IntentEntitlementRevoked

	// IntentTriggering is a command-only intent (never persisted, like IntentForking):
	// a directed trigger asks to start one deployed process at one of its message
	// start events (ADR-0425). Its handler answers from a receipt when the sender
	// already delivered this trigger, and otherwise creates the instance and records
	// IntentTriggerReceived in the same batch, so the instance and the fact that it
	// answered this trigger are durable together or not at all (I2).
	IntentTriggering
	// IntentTriggerReceived records a trigger's receipt: sender, trigger id, the
	// instance that answered and when.
	IntentTriggerReceived
	// IntentTriggerReceiptsPruning is a command-only intent: drop the receipts older
	// than a cutoff. IntentTriggerReceiptsPruned is its event, carrying that cutoff, so
	// replay drops exactly what was dropped live (I6). Appended at the end so every
	// prior intent keeps its numeric value on the log.
	IntentTriggerReceiptsPruning
	IntentTriggerReceiptsPruned
	// IntentDelivering is a command-only intent: a directed delivery asks to hand one
	// message to one running instance — the instance a product's lifecycle runs in
	// for one order position (ADR-0428).
	// Its handler correlates the instance's open subscriptions for that message and
	// records IntentTriggerReceived in the same batch, so a retry answers from the
	// receipt instead of delivering twice. Appended at the end so every prior intent
	// keeps its numeric value on the log.
	IntentDelivering
	// IntentActionReporting is a command-only intent: somebody reports how an action
	// asked of a position ended (ADR-0429 §3). Its handler answers a repeated
	// identical report from state and refuses a different outcome for the same
	// command, so only a first report becomes IntentActionCompleted.
	IntentActionReporting
	// IntentActionCompleted is the fact that an action ended, with its outcome.
	IntentActionCompleted
	// IntentFeedPruning is a command-only intent: the retention sweep asks the event
	// feed to drop its rows through a log position (ADR-0429 §5).
	IntentFeedPruning
	// IntentFeedPruned is the fact that it did, with the position it was pruned
	// through, which a feed reader is answered 410 below.
	IntentFeedPruned
	// IntentMessageBuffered is the fact that a message published with a time to live
	// correlated nothing and is kept, payload and all, for the subscription that opens
	// for it later (ADR-0370). Appended at the end so every prior intent keeps its
	// numeric value on the log.
	IntentMessageBuffered
	// IntentMessageIdRecorded is the fact that a message carrying an id was delivered
	// at once: the id is remembered until the message would have expired, so a repeat
	// of it is recognised as the same message (ADR-0370 idempotency).
	IntentMessageIdRecorded
	// IntentMessageConsumed is the fact that a buffered message was delivered to a
	// subscription that opened after it was published.
	IntentMessageConsumed
	// IntentMessageExpiring is a command-only intent: the timer sweep asks that a
	// message whose time to live has passed be dropped.
	IntentMessageExpiring
	// IntentMessageExpired is the fact that it was: the buffered message, if it still
	// waited, and its id are gone.
	IntentMessageExpired
	// IntentDispatched is the fact that a signal was broadcast or a message published
	// (VTDispatch, ADR-0472 §1). Appended at the end so every prior intent
	// keeps its numeric value on the log.
	IntentDispatched
	// IntentSignalStarted is the fact that a broadcast started an instance of a
	// definition with a matching signal start event: the definition, its start
	// element, the signal and the sender (VTSignal, ADR-0472 §2). It changes
	// no subscription; the event log folds a row from it.
	IntentSignalStarted
	// IntentEventLogPruning is a command-only intent: the retention sweep asks the event
	// log to drop its rows through a log position (ADR-0472 §4).
	IntentEventLogPruning
	// IntentEventLogPruned is the fact that it did, with the position it was pruned
	// through (VTFeedRetention).
	IntentEventLogPruned

	// IntentAuthorizationDeciding is a command-only intent: an enforcement point asks
	// that a decision it made be recorded for a job it holds a lease on
	// (ADR-0475 §5). Its handler re-checks the lease,
	// records IntentAuthorizationDecided and, for an enforced refusal, fails the job or
	// asks for approval in the same batch, so the decision and its consequence are
	// durable together (I2). Appended at the end so every prior intent keeps its value.
	IntentAuthorizationDeciding
	// IntentAuthorizationDecided is the decision, as a fact.
	IntentAuthorizationDecided
	// IntentApprovalDeciding is a command-only intent: an approver answers an approval,
	// or withdraws an answer. Its handler checks the approval is open and records
	// IntentApprovalRecorded.
	IntentApprovalDeciding
	// IntentApprovalRecorded is one fact about an approval — requested, answered or
	// withdrawn; the value's Kind says which.
	IntentApprovalRecorded
)

func (Intent) String

func (i Intent) String() string

type JobTimerKind added in v0.3.0

type JobTimerKind int32

JobTimerKind distinguishes the holds a job timer releases.

const (
	// JobTimerRetry releases a retry backoff (ADR-0111). Zero so pre-existing records,
	// which are all retry timers, decode correctly.
	JobTimerRetry JobTimerKind = 0
	// JobTimerLease releases a worker's lease when it expires (ADR-0007).
	JobTimerLease JobTimerKind = 1
)

type JobValue

type JobValue struct {
	ProcessInstanceKey uint64
	ElementInstanceKey uint64
	JobType            int32 // interned string → index
	Retries            int32
	// Deadline is the *user task* due date (ADR-0032) — when the work is due, not
	// anything about a worker. It is displayed and sorted on, and a job carrying one is
	// as pullable as any other. The worker lease lives in LeaseExpiresAt below; the two
	// were nearly conflated, and a user task with a due date would then have been
	// invisible to every worker.
	Deadline int64
	Assignee string
	// RetryDueDate is the unix-nano instant a failed-but-retryable job may be handed to a
	// worker again — a retry backoff (ADR-0111). While it is non-zero and in the future the
	// job is held OFF the activatable index; a retry timer clears it when the backoff elapses.
	// 0 means "pullable now" (no backoff), which is every job's steady state. Append-compatible:
	// an old record without it decodes to 0.
	RetryDueDate int64
	// LeaseExpiresAt is the unix-nano instant an external worker's claim on this job runs
	// out (ADR-0007). While it is non-zero the job is held OFF the activatable index and
	// Assignee names the holder; a lease timer clears it when the deadline passes, and the
	// job is offered again. 0 means unheld, which is every job's steady state.
	// Append-compatible: an old record without it decodes to 0.
	LeaseExpiresAt int64
	// LeaseEpoch counts how many times this job has been leased, and is the fencing
	// token a worker presents when it reports an outcome (ADR-0007's third open item).
	// Assignee alone does not fence: two instances of one worker deployment share a
	// name, so a completion from a holder whose lease expired would be accepted while
	// the second instance still holds the job. The epoch differs per lease, so a
	// stale report presents a number the job has moved past and is refused.
	//
	// It is incremented at command time and written into the JobActivated event, never
	// recomputed on replay (I6). Append-compatible: an old record decodes to 0, which
	// reads correctly as "never leased".
	LeaseEpoch uint64
	// CandidateGroups is the resolved group assignment, comma-separated, seeded from
	// the model when the task activates — the same way Assignee is. It lives on the
	// job because a model may name its groups with an expression, and what that
	// expression evaluated to is a fact about *this* instance rather than about the
	// definition (ADR-0318). Append-compatible: a
	// record written before it decodes to "", and a reader falls back to the model's
	// own value for those.
	CandidateGroups string
	// CreatedAt is the unix-nano instant the job was created — when a user task was
	// opened, which the inbox shows on every row. It is the JobCreated event's header
	// timestamp, stamped by applyToState and carried by every later re-put of the job,
	// so replay rebuilds it identically (I4/I6). Append-compatible: a record written
	// before it decodes to 0, read as "not recorded".
	CreatedAt int64
}

JobValue is service-task work waiting for an external worker. Variables are referenced via the element/instance scope, not embedded here. Assignee is the user-task assignee (ADR-0042): empty for a service-task job, and for a user task it starts at the model's default and is rewritten by claim/unclaim. It is the one variable-length field; for a service job it encodes as a 4-byte zero length and decodes to "" with no allocation, keeping the hot path clean (I1).

func (*JobValue) ValueType

func (*JobValue) ValueType() ValueType

type MessageFlowValue

type MessageFlowValue struct {
	SenderProcessInstanceKey   uint64
	ReceiverProcessInstanceKey uint64
	ReceiverProcessDefKey      uint64
	ReceiverElementId          int32 // INDEX into the receiver definition's graph
	MessageName                string
	CorrelationKey             string
}

MessageFlowValue is one delivered message flow, retained as history so the collaboration replay can show which message crossed to which receiving element and when (ADR-0038). It is produced when a message correlates a catch event or instantiates a message-start process. The receiving element identifies the message-flow edge on the diagram; the sender/receiver instance keys tie the two pools' instances together. ReceiverProcessInstanceKey is 0 when the message created the receiver via a message start event (no instance existed yet).

func (*MessageFlowValue) ValueType

func (*MessageFlowValue) ValueType() ValueType

type MessageSubscriptionValue

type MessageSubscriptionValue struct {
	ProcessInstanceKey uint64
	ElementInstanceKey uint64
	MessageName        string
	CorrelationKey     string // FEEL correlation key, evaluated at subscribe time
	// ProcessDefKey and ElementId identify the waiting catch event on its diagram.
	// They are carried so that when the subscription correlates, the retained
	// message-flow history record can name the receiving element without a lookup
	// (ADR-0038); they are set at subscribe time from the element instance.
	ProcessDefKey uint64
	ElementId     int32
}

MessageSubscriptionValue is an open subscription: an element instance (a message intermediate catch event) waiting for a named message whose correlation key matches. Like a variable it carries genuine runtime data (the message name and the evaluated correlation key), so its encoding is length-prefixed rather than fixed-size. The (MessageName, CorrelationKey) pair is the match key a publish scans for; see ADR-0020.

func (*MessageSubscriptionValue) ValueType

func (*MessageSubscriptionValue) ValueType() ValueType

type MessageValue added in v0.12.0

type MessageValue struct {
	MessageName    string
	CorrelationKey string
	MessageId      string
	ExpiresAt      int64 // Unix nanoseconds
	Variables      []VariableValue
	// BufferedAt is when the message was published, read from the same clock reading
	// its expiry was computed from, so an operator sees how long it has waited. It is
	// the last field of the encoding, and a record written without it reads zero.
	BufferedAt int64
}

MessageValue is a published message the engine keeps (ADR-0370): one that correlated nothing when it was published with a time to live, and waits in the buffer for the subscription that opens for it later.

It rides VTMessage, the value type reserved for an incoming message since the first record shape; until the buffer, only the publish command used it, and no event did.

ExpiresAt is computed once, from the processor's clock and the publish's time to live, and written into the event, so replay keeps the message exactly as long as the live run did (invariant I6). MessageId is the sender's own id, "" when the sender gave none; while the message lives, a second publish of the same name, key and id is the same message and is dropped.

On IntentMessageBuffered the record carries the payload. On IntentMessageIdRecorded, IntentMessageConsumed and IntentMessageExpired it does not: those name the entry to keep or drop, and the payload already reached its instance or never will.

func (*MessageValue) ValueType added in v0.12.0

func (*MessageValue) ValueType() ValueType

func (MessageValue) WithoutPayload added in v0.12.0

func (v MessageValue) WithoutPayload() MessageValue

WithoutPayload returns the message with its variables dropped: the shape every record but the buffering one carries.

type OperatorActionKind added in v0.3.0

type OperatorActionKind uint8

OperatorActionKind is the verb of an operator intervention. It is a small closed vocabulary encoded as a byte rather than free text, so the log never carries a model string (invariant I5) and the record stays fixed-width apart from its two genuinely free-form fields.

const (
	// OperatorActionCompleteJob is an operator completing a parked job by hand, the
	// job a worker would otherwise have completed (ADR-0159).
	OperatorActionCompleteJob OperatorActionKind = iota + 1
	// OperatorActionMigrate is an operator rebinding a running instance to another
	// deployed version of its process (ADR-0162). It is the audit half of the
	// migration: the rebinding itself is VTProcessMigration, and this record is what
	// makes it visible — who, why, and at which log position the instance stopped being
	// the old version and started being the new one, which is what lets the replay
	// resolve each step through the definition that was in force at it.
	OperatorActionMigrate
	// OperatorActionForkedTo is written on the instance an operator forked *away from*:
	// its tokens could not be rebound onto the target version, so it was terminated and
	// its work continued in a new instance of that version
	// (ADR-0389). Which instance that is, is on the record —
	// SuccessorInstanceKey — so this kind carries only who ended it and why.
	OperatorActionForkedTo
	// OperatorActionForkedFrom is the same fork seen from the successor: the instance
	// did not begin at a start event but continued another instance's work at resume
	// points an operator named. Its predecessor is on its record; this is what makes the
	// successor's timeline open with the reason it exists rather than with a token
	// appearing in the middle of the diagram.
	OperatorActionForkedFrom
	// OperatorActionThrowJobError is an operator throwing a BPMN error from a parked job
	// by hand, the error a worker would otherwise have thrown (ADR-0089, ADR-0159). Like a
	// completion by hand it forces a path the engine did not take on its own — here the
	// error flow — so it is recorded with who and why, and with the code it threw.
	OperatorActionThrowJobError
)

func (OperatorActionKind) String added in v0.3.0

func (k OperatorActionKind) String() string

type OperatorActionValue added in v0.3.0

type OperatorActionValue struct {
	ProcessInstanceKey uint64 // owning instance (the scope this record is keyed under)
	ElementInstanceKey uint64 // the element the action was applied to; 0 when instance-wide
	JobKey             uint64 // the job that was acted on; 0 when the action is not job-scoped
	Kind               OperatorActionKind
	Actor              string // who performed it; "" when auth is off / unidentified
	Reason             string // why — free text supplied by the operator
	// FromProcessDefKey is the definition the instance was on *before* the action, set
	// only by OperatorActionMigrate and 0 for every other kind (ADR-0162). The instance
	// record names the definition it is on now; each migration record names the one
	// before it, so a chain of migrations reads back as a chain of log-position ranges
	// — which is how the replay knows to resolve a step recorded under the old version
	// through the old version's compiled graph. Append-compatible: a record written
	// before this field decodes to 0, which is what every non-migration record means.
	FromProcessDefKey uint64
	// ErrorCode is the BPMN error code an OperatorActionThrowJobError threw, and "" for
	// every other kind (ADR-0089). It is written only when set, after FromProcessDefKey,
	// so a record of any other kind keeps the layout it always had and an older reader
	// stops where it always stopped.
	ErrorCode string
}

OperatorActionValue records one operator intervention on a running instance for audit (ADR-0159): who forced what, on which element, and why. ADR-0098 made an operator's variable corrections durable and replayable; this is its counterpart for the act itself — completing a parked job by hand — so a step the engine did not drive on its own is never indistinguishable from one it did. It is keyed under its owning ProcessInstanceKey as append-only history, folds into the same instance timeline at its log position, and survives the instance finishing.

The element is referenced by ElementInstanceKey, never by its id: element ids are interned at compile time and never written to the log as text (invariant I5), so a reader resolves the id from the element instance exactly as the timeline already does. Actor is the acting principal's username, or "" when auth is off (single-user) or the caller is unidentified; Reason is the operator's justification, required by the surfaces that mint these records. Both are genuine runtime data, so — like a variable — the encoding is length-prefixed.

func (*OperatorActionValue) ValueType added in v0.3.0

func (*OperatorActionValue) ValueType() ValueType

type ProcessInstanceState

type ProcessInstanceState uint8

ProcessInstanceState marks where an instance is in its lifecycle. The zero value is Active; the terminal states are only ever stored in the history index (an active instance's record always carries Active). See ADR-0017.

const (
	PIActive     ProcessInstanceState = iota // running
	PICompleted                              // reached its end normally
	PITerminated                             // ended by termination
)

func (ProcessInstanceState) String

func (s ProcessInstanceState) String() string

type ProcessInstanceValue

type ProcessInstanceValue struct {
	ProcessDefKey  uint64
	State          ProcessInstanceState
	CompletedAt    int64  // unix nano when it reached a terminal state; 0 while active
	CreatedAt      int64  // unix nano when the instance was activated
	CorrelationKey string // message correlation key a message-start instance began with; "" otherwise
	// ParentElementInstanceKey is the call-activity element instance that started
	// this instance as its child, 0 for a root instance (API/message/timer start).
	// A completing child resumes its caller through it (ADR-0076).
	ParentElementInstanceKey uint64
	// ExpiryDueDate is the due date (unix nano) of this instance's TTL expiry timer,
	// 0 when the definition has no TTL (ADR-0085). Stored so completion/termination can
	// cancel that timer by key (the instance key) without scanning the timer index.
	ExpiryDueDate int64
	// CompletedPosition is the log position of the instance's terminal event, set only
	// on the history record (0 while active, and 0 on records written before this field,
	// ADR-0115). Since the terminal event is an instance's last, it is the instance's
	// highest position — so history retention can prove every event is exported
	// (CompletedPosition <= exported position) before hard-deleting the instance.
	CompletedPosition uint64
	// PurgeDueDate is when history retention is scheduled to hard-delete this finished
	// instance: CompletedAt + the definition's atlas:historyTtl, 0 when it declares none
	// (ADR-0146). Frozen on the terminal event so applyToState can index the instance by
	// it — and read back from the purge event to drop that index entry — without either
	// fold reading a clock or a definition.
	PurgeDueDate int64
	// PredecessorInstanceKey is the instance this one continues: a fork migration ends
	// an instance whose tokens cannot be rebound onto another version and starts this
	// one in that version at operator-named resume points, carrying the data across
	// (ADR-0389). 0 for every instance that began on its own.
	//
	// It is the successor's half of a link both records carry, so either instance
	// answers "where did this come from" and "where did this go" without a scan.
	PredecessorInstanceKey uint64
	// SuccessorInstanceKey is the instance that continues this one: set on the terminal
	// event of a forked-away instance, so the history record still names where the work
	// went. 0 for every instance that ended on its own account — which is what keeps a
	// forked predecessor distinguishable from an ordinary cancellation, both of which
	// are PITerminated (ADR-0389).
	SuccessorInstanceKey uint64
	// PublicPassHash is the SHA-256, hex, of the pass a public start link handed the
	// visitor who started this instance, when its process holds a public wizard
	// (ADR-0449). The pass is what lets that visitor, who has no account, fill the
	// steps of the instance's public wizards and nothing else. It rides in on the
	// creation command and is frozen into the activation event, so it is durable in the
	// same fsync as the instance it unlocks and read back on replay rather than
	// recomputed (I6). Empty for every other instance.
	//
	// Kept out of every JSON rendering of the record (the event export among them): it
	// cannot be turned back into the pass, but a verifier of a credential has no
	// reader outside the one route that checks it.
	PublicPassHash string `json:"-"`
	// StartedBy is the stable principal id ("usr_…") of whoever created the instance
	// through an authenticated API call, frozen into the activation event from the
	// request's principal (ADR-0475 §3). It is the
	// one trusted answer to "who initiated this": a variable naming the starter can be
	// rewritten by any later step, this cannot. Empty when nobody authenticated did —
	// a timer, a message, an open server — and on every record written before it.
	StartedBy string
}

ProcessInstanceValue is the running instance as a whole — the root scope a process's element instances live under. CreatedAt is set at activation (the activation event's timestamp, so it replays identically — invariant I6); CorrelationKey records the message key a message-start instance was created with ("" for API/timer/none starts, ADR-0035). State and CompletedAt are set only on the history record written when an instance ends (ADR-0017); while live, they carry their zero values (Active, 0).

func (*ProcessInstanceValue) ValueType

func (*ProcessInstanceValue) ValueType() ValueType

type ProcessMigrationValue added in v0.3.0

type ProcessMigrationValue struct {
	ProcessInstanceKey uint64
	FromProcessDefKey  uint64
	ToProcessDefKey    uint64
	// Mapping is ordered by From and carries no duplicate From, so a fold can binary
	// search it and a reader can compare two migrations without normalizing first.
	// NewProcessMigration establishes both.
	Mapping []ElementMapping
}

ProcessMigrationValue rebinds a running instance from one deployed version of its process to another (ADR-0162). It carries the two definition keys and the fully materialized element mapping — every index the instance's live records reference — because applyToState must reproduce the rebinding from the event alone.

That is the whole design constraint. A fold that *derived* the mapping from the two compiled processes would depend on the matching algorithm's code, so an algorithm improved in a later release would replay an old log into a different state, and two builds of Atlas could disagree about where a token is (invariants I4/I6). The matching runs once, at command time, where it can also be refused.

Element instance keys are *not* in the mapping and never change: a migration rewrites bindings, it does not terminate and recreate, which is what lets variables, data objects, jobs and the whole scope tree ride through untouched — they are keyed by element instance key, not by element index.

func NewProcessMigration added in v0.3.0

func NewProcessMigration(piKey, fromDefKey, toDefKey uint64, mapping map[int32]int32) ProcessMigrationValue

NewProcessMigration builds a migration value from a from→to index map, ordering the mapping by source index so the encoding of a given migration is byte-identical whatever order the caller discovered it in. A map has no order, and an event whose bytes depend on Go's map iteration is an event whose log is not reproducible.

func (*ProcessMigrationValue) Target added in v0.3.0

func (v *ProcessMigrationValue) Target(from int32) (int32, bool)

Target returns the index the given source element index maps to, and whether the mapping covers it. An uncovered index is not an error here — the fold leaves such a record alone — because validation at command time is what guarantees no *live* record carries one (ADR-0162).

func (*ProcessMigrationValue) ValueType added in v0.3.0

func (*ProcessMigrationValue) ValueType() ValueType

type Record

type Record struct {
	Header RecordHeader
	Value  Value
}

Record bundles a header with its typed payload. Value is nil for records whose ValueType carries no payload yet.

func ReadRecord

func ReadRecord(src []byte) (Record, error)

ReadRecord decodes a single record from the front of src. The payload (if any) is chosen by the header's ValueType.

type RecordHeader

type RecordHeader struct {
	Position    uint64 // monotonic log position (sequence number)
	SourcePos   uint64 // position of the record that caused this one (causality)
	Key         uint64 // entity key
	Timestamp   int64  // unix nano
	RecordType  RecordType
	ValueType   ValueType
	Intent      Intent
	PartitionId uint16
}

RecordHeader is the fixed-size metadata every record carries. SourcePos threads a causal chain through the log: every record points at the record that produced it.

func ReadHeader added in v0.7.0

func ReadHeader(src []byte) (RecordHeader, error)

ReadHeader decodes only the fixed header from the front of src, leaving the payload untouched. A reader that asks *what* a record is rather than what it carries — its position, kind and intent — wants this: ReadHeader allocates nothing, where ReadRecord allocates one value per record for a caller that is about to discard it. Over a log of a hundred million records that difference is the whole cost of the pass.

type RecordType

type RecordType uint8

RecordType distinguishes the three kinds of record. Only events are written to the log; commands and rejections are processed but never persisted.

const (
	// RecordCommand is an intention submitted to the processor. It may be
	// rejected and is never persisted.
	RecordCommand RecordType = iota
	// RecordEvent is a fact that already happened. It is appended to the log
	// and is the only record type that is persisted.
	RecordEvent
	// RecordCommandRejection records that a command was refused. It is not
	// persisted; it is surfaced to the submitter.
	RecordCommandRejection
)

func (RecordType) String

func (t RecordType) String() string

type SignalSubscriptionValue

type SignalSubscriptionValue struct {
	ProcessInstanceKey uint64
	ElementInstanceKey uint64
	SignalName         string
	// ProcessDefKey and ElementId identify the waiting catch on its diagram; set at
	// subscribe time from the element instance and carried so the record locates its
	// own state index entry (invariant I4), mirroring MessageSubscriptionValue.
	ProcessDefKey uint64
	ElementId     int32
	// SenderProcessInstanceKey is the instance whose throw a correlation delivered,
	// set on SubscriptionCorrelated and on IntentSignalStarted (ADR-0472 §2).
	// Stale marks a correlation that only cleared a subscription whose element no
	// longer waited: no delivery, so no row in the event log. Both are appended
	// fields: a record written before them ends after ElementId and decodes them as
	// zero, and a version before them stops reading where they begin.
	SenderProcessInstanceKey uint64
	Stale                    bool
}

SignalSubscriptionValue is an open subscription to a broadcast signal: an element instance (a signal intermediate catch event, later a signal boundary or event subprocess) waiting for a named signal. It is the MessageSubscriptionValue shape (ADR-0020) minus the correlation key — a signal matches by name alone and fans out 1:n, so there is nothing to correlate on (ADR-0088). The SignalName is the sole match key a broadcast scans for.

func (*SignalSubscriptionValue) ValueType

func (*SignalSubscriptionValue) ValueType() ValueType

type TimerValue

type TimerValue struct {
	ProcessInstanceKey uint64
	ElementInstanceKey uint64
	TargetElementId    int32
	DueDate            int64
	Repetitions        int32 // remaining fires after this one; -1 = infinite (timer cycle), 0 = fire once
	// ProcessDefKey names the definition a *start* timer instantiates when it
	// fires (ADR-0051). It is 0 for an instance-owned timer (catch/boundary),
	// which is identified instead by ProcessInstanceKey/ElementInstanceKey. A
	// start timer is precisely one with ProcessInstanceKey == 0 and
	// ProcessDefKey != 0; TargetElementId then names its timer-start element.
	ProcessDefKey uint64
	// JobKey marks a job timer: non-zero means this timer, when due, acts on the job with
	// that key rather than firing an element. 0 for every ordinary (catch/boundary/start/
	// TTL) timer. Append-compatible: an old record decodes to 0.
	JobKey uint64
	// JobKind says *which* hold on the job this timer releases, and is only meaningful
	// with JobKey set. Two holds can sit on one job at once — a worker leases it and then
	// fails it with a backoff (ADR-0007 and ADR-0111) — and each timer must release only
	// its own, or the lease expiry would hand the job out early and defeat the backoff.
	// Append-compatible: a record written before this field decodes to JobTimerRetry,
	// which is what every job timer was.
	JobKind JobTimerKind
}

TimerValue is a timer-event subscription. The due-date index makes "which timers are due now" a range scan; see data-model.md.

func (*TimerValue) ValueType

func (*TimerValue) ValueType() ValueType

type ToolCall added in v0.5.0

type ToolCall struct {
	Tool      string          // the tool activity's BPMN id, as the model was told it
	CallId    string          // the model's own id for this call, carried into the activation
	Arguments []VariableValue // written into the activated activity's own scope
}

ToolCall is one tool an agent chose for the next round of an agent-driven ad-hoc subprocess (ADR-0253): the BPMN id of the contained activity to activate, and the arguments it supplies for that activity's declared parameters.

Unlike everything else in this file it is **not a persisted record** — it has no ValueType and no codec. It rides on a job-completion command and on the Completion a worker hands back, and what becomes durable is the activation the engine derives from it: a replay re-activates exactly the same activities without asking the model again (invariant I6). It lives here rather than in engine because the job protocol carries it too, and job must not depend on the engine's implementation.

type TriggerReceiptValue added in v0.8.0

type TriggerReceiptValue struct {
	Source      string
	TriggerID   string
	InstanceKey uint64
	At          int64 // when the trigger was received, Unix nanoseconds
	Cutoff      int64 // prune events only: receipts received before this go
}

TriggerReceiptValue records that a directed trigger was applied (ADR-0425): which sender asked, under which of its own trigger ids, and the instance that answered.

It exists because the senders of a trigger deliver at least once. An HR system that reports a leaver and times out waiting for the answer reports the leaver again, and a second deprovisioning is a second conversation with a target system Atlas does not control. The receipt is what makes the second delivery answer with the first instance instead of creating another.

It is a set, not a high-water mark like InboundDeliveryValue (ADR-0075): a sender's trigger ids are its own and are not ordered, so "everything up to N was applied" is not a statement anybody could make about them.

On IntentTriggerReceiptsPruned only Cutoff is meaningful: every receipt received before it is dropped. The cutoff is written into the event, so replay drops exactly what was dropped live (invariant I6).

func (*TriggerReceiptValue) ValueType added in v0.8.0

func (*TriggerReceiptValue) ValueType() ValueType

type Value

type Value interface {
	// ValueType reports the discriminator a header should carry for this payload.
	ValueType() ValueType
	// contains filtered or unexported methods
}

Value is the typed payload of a record. Each implementation owns a fixed binary layout. encode appends to a caller-owned buffer (no allocation when the buffer has spare capacity, satisfying invariant I1); decode reads a payload back, returning ErrShortBuffer if src is truncated.

The methods are unexported on purpose: the set of value types is closed to this package, which keeps encode/decode and the newValue dispatch in lockstep.

func DecodeValue

func DecodeValue(vt ValueType, src []byte) (Value, error)

DecodeValue decodes a payload of the given value type from src into a freshly allocated Value. It returns an error if vt has no payload type or src is too short for it.

type ValueType

type ValueType uint8

ValueType identifies which entity a record concerns and therefore which payload struct its bytes carry. The set mirrors docs/architecture/data-model.md.

const (
	VTProcessInstance     ValueType = iota // the running instance as a whole
	VTElementInstance                      // a single active BPMN element instance (token carrier)
	VTJob                                  // service-task work for external workers
	VTTimer                                // timer-event subscription
	VTMessageSubscription                  // waiting for a message (receive task, message event)
	VTMessage                              // an incoming message (buffered)
	VTVariable                             // a process variable
	VTIncident                             // a fault state (job failed, expression error)
	VTSignal
	VTError             // error-event propagation
	VTProcessDefinition // a deployed definition
	// VTMessageFlow is a delivered message flow retained for history: one record
	// per message that correlated to a catch event or instantiated a message-start
	// process. Unlike VTMessageSubscription (an open wait, deleted on correlation)
	// it is never deleted, so the Operations collaboration view can replay the
	// exchange between pools after the fact (ADR-0038).
	VTMessageFlow
	// VTDataObject is a BPMN data object: a typed, named, scope-owned datum with a
	// declared lifecycle state. Unlike a plain variable it carries a data state
	// (order [received] → [approved]) whose every transition is a durable event,
	// so its state history and provenance rebuild from the log (ADR-0053).
	VTDataObject
	// VTDecisionEvaluation is one business rule task's DMN decision evaluation
	// retained for debugging: the input context it was given, the outputs it
	// produced, and the temis trace explaining which rules fired. Like VTMessageFlow
	// it is append-only history (one record per evaluation, never deleted), so an
	// operator can inspect after the fact exactly how a decision was made (ADR-0066).
	VTDecisionEvaluation
	// VTInboundDelivery is a per-source high-water mark for an at-least-once inbound
	// event bridge (ADR-0075): it records that an external source's deliveries up to
	// a sequence have been applied, so a replayed publish is skipped rather than
	// re-correlated (which would double-start a message-start process). It is generic
	// — the engine never interprets the opaque source id — and appended last so every
	// prior value type keeps its numeric value on the log.
	VTInboundDelivery
	// VTVariableAudit is one external variable override retained for audit (ADR-0098):
	// who set which variable, to what value, on which scope, keyed under its process
	// instance. Like VTMessageFlow and VTDecisionEvaluation it is append-only history
	// (one record per variable an operator sets, never deleted), so the "who changed
	// it" trail survives the instance and rebuilds from the log. Appended last so every
	// prior value type keeps its numeric value on the log.
	VTVariableAudit
	// VTCompensable is one completed compensable activity retained so a later
	// compensation throw can run its handler (ADR-0103). Written on the activity's
	// successful completion, keyed under its scope in completion order, and deleted when
	// compensated or when its scope tears down. Appended last so every prior value type
	// keeps its numeric value on the log.
	VTCompensable
	// VTOperatorAction is one operator intervention on a running instance retained for
	// audit (ADR-0159): who completed a parked job by hand, on which element, and why.
	// Like VTVariableAudit it is append-only history keyed under its process instance —
	// never deleted — so a step a person forced is always distinguishable from one the
	// engine drove, and the trail rebuilds from the log. Appended last so every prior
	// value type keeps its numeric value on the log.
	VTOperatorAction

	// VTProcessMigration is a running instance being rebound from one deployed version
	// of its process to another (ADR-0162). It is the only value that rewrites live
	// state in bulk rather than describing one entity's transition: the instance's
	// definition key and every element index its live records carry are translated
	// through the mapping the value holds. That mapping is *in the event* — never
	// derived during the fold — because a fold that recomputed it would depend on the
	// matching algorithm's code, so improving that algorithm would silently replay an
	// old log into a different state (invariants I4/I6). Appended last so every prior
	// value type keeps its numeric value on the log.
	VTProcessMigration

	// VTVariableIndex is one variable's membership in the value index changing after
	// the fact: the instance's process now declares that name searchable, or no longer
	// does (ADR-0244, ADR-0295). It carries no
	// value — the variable holds that — only the name and the membership, because the
	// point is that the value did not change. It exists because a variable's membership
	// is stamped by the version that wrote it (I6), and an instance can change version
	// under an operator's hand: a migration (ADR-0162) or an explicit reindex. Appended
	// last so every prior value type keeps its numeric value on the log.
	VTVariableIndex

	// VTEntitlement is what somebody holds: a principal, a catalogue item, since
	// when, and where the knowledge came from
	// (ADR-0312).
	//
	// It is engine state rather than a sidecar record for one reason that decides
	// it: an entitlement outlives the process instance that produced it, by years.
	// The instance is eligible for retention deletion long before somebody stops
	// holding their VPN access, so the fact cannot live in the instance's history —
	// and a sidecar file would put an access record outside the log that every
	// other engine fact rebuilds from.
	//
	// It carries a principal *reference* and nothing else about a person. That is
	// what keeps applyToState free of any key access, and it is what makes erasure
	// possible at all: an append-only log cannot forget what it was given in the
	// clear. Appended last so every prior value type keeps its numeric value on the
	// log.
	VTEntitlement

	// VTEntitlementHistory is a hold that has ended: the inventory row as it stood,
	// plus when and why it stopped being recorded
	// (ADR-0346).
	//
	// A separate type rather than a flag on VTEntitlement, because the two answer
	// different questions and a value that means one thing on one event and another
	// on another is one nobody can read. It is also the only value type in the
	// portal that is written and never updated: a hold ends once.
	//
	// Appended last so every prior value type keeps its numeric value on the log.
	VTEntitlementHistory

	// VTTriggerReceipt records that a directed trigger was applied: the sender, its
	// trigger id and the instance that answered (ADR-0425). It is what lets an
	// at-least-once sender retry without starting a second instance. Receipts are
	// dropped by an explicit prune event once they are older than the retention, so
	// the set stays bounded. Appended last so every prior value type keeps its
	// numeric value on the log.
	VTTriggerReceipt

	// VTActionOutcome records how one action asked of an order position ended —
	// completed, rejected or failed — with the event type it is published under
	// (ADR-0429 §3). It outlives the order it came from, as an entitlement does.
	// Appended last so every prior value type keeps its numeric value on the log.
	VTActionOutcome

	// VTFeedRetention records how far a table folded from facts has been pruned: the
	// event feed (ADR-0429 §5) with IntentFeedPruned, the event log (ADR-0472)
	// with IntentEventLogPruned. Every row of the partition at or before Through is
	// gone. Such a table is shortened by a fact of its own rather than by a delete
	// nobody replays (I4). Appended last so every prior value type keeps its numeric
	// value on the log.
	VTFeedRetention

	// VTDispatch is the fact that a signal was broadcast or a message published: by
	// whom, under which name, and how far it reached (ADR-0472 §1). One
	// record per send, a send that reached nobody included. It carries no payload.
	// Appended last so every prior value type keeps its numeric value on the log; a
	// version before it decodes it to no value and skips it on replay.
	VTDispatch
	// VTAuthorization is one policy decision about one external Worker action — the
	// evidence written before anything the action needs is released
	// (ADR-0475 §12). Append-only history keyed under
	// its process instance, like VTOperatorAction. Appended last so every prior value
	// type keeps its numeric value on the log.
	VTAuthorization
	// VTActionApproval is one fact about the approval of one action digest: requested,
	// answered, or withdrawn (§9). A separate type from VTAuthorization because the two
	// answer different questions — what was decided, and who agreed to what. Appended
	// last so every prior value type keeps its numeric value on the log.
	VTActionApproval
)

func (ValueType) String

func (t ValueType) String() string

type VarKind

type VarKind uint8

VarKind tags the FEEL values Atlas persists for a variable: the scalars, plus VarJSON for the structured values (objects and arrays) an author or a script produces.

const (
	VarNull   VarKind = iota // no value
	VarBool                  // Bool is meaningful
	VarNumber                // Text is the canonical decimal string
	VarString                // Text is the string contents
	// VarJSON is a structured value (object or array). Text is its canonical
	// JSON encoding; it is re-parsed into a FEEL context/list when bound into an
	// evaluation. Kept as text so the durable record format is unchanged — a new
	// kind byte over the same length-prefixed Text (ADR-0009, ADR-0037).
	VarJSON
)

type VariableAuditValue

type VariableAuditValue struct {
	ProcessInstanceKey uint64 // owning instance (the scope this record is keyed under)
	ScopeKey           uint64 // the scope the variable was written to (root or a sub-scope)
	Actor              string // who performed the override; "" when auth is off / unidentified
	Name               string // the variable that was set
	Kind               VarKind
	Bool               bool
	Text               string // number canonical string, string contents, or canonical JSON; empty otherwise
}

VariableAuditValue records one external variable override for audit (ADR-0098): who set which variable, to what value, on which scope. It is keyed under its owning ProcessInstanceKey as append-only history — one record per variable an operator sets — so the "who changed it" trail folds into the same instance timeline as the variable snapshot at the same log position, and survives the instance finishing. Actor is the acting principal's username, or "" when auth is off (single-user) or the caller is unidentified. Name/Kind/Bool/Text mirror the VariableValue that was written, so the audit row is self-contained. Like a variable it carries genuine runtime data, so its encoding is length-prefixed.

func (*VariableAuditValue) ValueType

func (*VariableAuditValue) ValueType() ValueType

type VariableIndexValue added in v0.7.0

type VariableIndexValue struct {
	ProcessInstanceKey uint64 // the root scope the variable lives in: only that scope is indexed
	Name               string
	Indexed            bool
}

VariableIndexValue is one variable's membership in the value index, changed after the fact. It carries no value, deliberately: the variable holds that, and the point of this record is that the value did not change — only whether the index answers for it (ADR-0244).

It exists because membership is stamped by the version that wrote the value (I6), while an instance can be moved to another version under an operator's hand. A migration onto a version that declares more names, or fewer, would otherwise leave the instance missing from the index its new version's search reads, or listed in it under a version that never promised to be searchable by that name. The comparison is made at command time against the compiled process, and one of these is emitted per variable whose membership differs — so the fold, which cannot ask a compiled process anything, is handed the answer.

func (*VariableIndexValue) ValueType added in v0.7.0

func (*VariableIndexValue) ValueType() ValueType

type VariableValue

type VariableValue struct {
	ScopeKey uint64 // owning scope (process instance key today)
	Name     string
	Kind     VarKind
	Bool     bool
	Text     string // number canonical string or string contents; empty otherwise
	// ProducerKey names the element instance whose processing wrote this value: the
	// task whose job returned it, the activity whose io-mapping or script produced it,
	// the catch event the message payload arrived on. It is the fact that says *who
	// wrote this*, which no diff of two variable snapshots can recover — on two
	// parallel branches both branches see both writes, so both appear to have made
	// them (ADR-0219).
	//
	// 0 means no element wrote it: the instance's start variables, an operator's
	// override (ADR-0098 records that one), or a record written before attribution
	// existed. Stamped at command time and frozen into the event, never recomputed on
	// replay (I6). Append-compatible: an old record ends after Text and decodes to 0.
	ProducerKey uint64
	// Index is which element of a list variable this write sets, or -1 for a write of
	// the whole value — which is what every write outside a multi-instance activity's
	// output collection is.
	//
	// A loop collects one result per iteration into one list. Writing the whole list
	// each time put the growing collection into the log and into the variable timeline
	// once per iteration, so the bytes written grew with the *square* of the iteration
	// count: a hundred thousand results of a kilobyte each cost a hundred gigabytes to
	// record a hundred megabytes of answer. Naming the element instead makes the record
	// the size of one result (ADR-0296).
	Index int32

	// Indexed says this write belongs in the variable value index: its process
	// declared the name searchable (atlas:searchable) and the write is at the
	// instance's root scope.
	//
	// It is decided at command time and frozen into the event, never recomputed on
	// replay (I6) — and it has to be, because applyToState holds the record and
	// nothing else: it cannot ask a compiled process whether a name is searchable.
	// Append-compatible: a record written before the index existed ends after the
	// producer key and decodes to false, which is honest — such a write is not in the
	// index.
	Indexed bool
}

VariableValue is a process variable: a named value owned by a scope (the process instance root for now). Unlike the graph-derived payloads, a variable carries genuine runtime data (its name and contents), so its encoding is length-prefixed rather than fixed-size.

func (*VariableValue) IndexText added in v0.5.0

func (v *VariableValue) IndexText() (string, bool)

IndexText is the value's canonical text for the variable value index, and whether it can be indexed at all.

The index is an ordered key-value index, so it answers equality and prefix over a byte string and nothing else. That admits the scalars and excludes the rest: a structured value is not something anyone searches for by its exact encoding, a NUL byte would break the terminator the exact match relies on, and a value past MaxIndexedValueBytes is refused rather than cut short.

The search side calls this on the query's value, so a query and a write agree on the bytes by construction rather than by two implementations happening to match.

func (*VariableValue) IsEnciphered added in v0.7.0

func (v *VariableValue) IsEnciphered() bool

IsEnciphered reports whether this value holds a sealed personal value rather than a readable one. It needs no key, which is the point: the single writer uses it to refuse a write of a declared personal variable that arrived in the clear — the one check that makes the rule hold on every path into an instance, including the ones no edge can seal because the target instance is only known once a message correlates.

func (*VariableValue) ValueType

func (*VariableValue) ValueType() ValueType

Jump to

Keyboard shortcuts

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