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
- Variables
- func AppendRecord(dst []byte, r *Record) []byte
- func AppendValue(dst []byte, v Value) []byte
- func CounterOf(key uint64) uint64
- func DecodeValueInto(v Value, src []byte) error
- func EncipheredSubject(text string) (string, bool)
- func NewKey(partition uint16, counter uint64) uint64
- func PartitionOf(key uint64) uint16
- type ActionApprovalValue
- type ActionOutcomeValue
- type ApprovalKind
- type ApprovalOutcome
- type AuthorizationValue
- type AuthzEffect
- type CompensableValue
- type DataObjectValue
- type DecisionEvaluationValue
- type DispatchChannel
- type DispatchSource
- type DispatchValue
- type ElementInstanceValue
- type ElementMapping
- type EntitlementHistoryValue
- type EntitlementOrigin
- type EntitlementValue
- type FeedRetentionValue
- type HoldEnd
- type InboundDeliveryValue
- type IncidentReason
- type IncidentValue
- type Intent
- type JobTimerKind
- type JobValue
- type MessageFlowValue
- type MessageSubscriptionValue
- type MessageValue
- type OperatorActionKind
- type OperatorActionValue
- type ProcessInstanceState
- type ProcessInstanceValue
- type ProcessMigrationValue
- type Record
- type RecordHeader
- type RecordType
- type SignalSubscriptionValue
- type TimerValue
- type ToolCall
- type TriggerReceiptValue
- type Value
- type ValueType
- type VarKind
- type VariableAuditValue
- type VariableIndexValue
- type VariableValue
Constants ¶
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.
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.
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.
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 ¶
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 ¶
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 ¶
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 DecodeValueInto ¶
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
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 ¶
NewKey composes a globally unique 64-bit key from a partition id and a per-partition monotonic counter.
func PartitionOf ¶
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
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
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.
type InboundDeliveryValue ¶
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 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 )
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).
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 ¶
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.
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 )
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