Skip to main content
Meerkat uses executable machine definitions for state that must have one semantic owner. The goal is simple: for any important lifecycle question, there should be one authoritative answer to what state exists, which transitions are legal, and what effects follow.

Coordination Boundaries

  • WorkGraph is the durable shared-work ledger. It owns dependency readiness, work claims and leases, evidence, and terminal work verdicts.
  • Schedule is the time authority. It owns trigger calculation, recurrence, occurrence claims, delivery attempts, and occurrence outcomes. A scheduled wake does not decide whether WorkGraph work remains ready or complete.
  • Detached Jobs own durable background execution, while Runtime Delivery owns delivery identity, sequence, replay, and application progress. A job terminal receipt does not by itself prove that the target runtime applied the result.
  • Peer delivery accelerates discovery of durable work and wakes disposable execution shells. A peer send or handoff receipt is not a WorkGraph claim, Schedule occurrence verdict, or proof that shared work completed.
  • Durable claims are authorized only by their owning machine and store contract. WorkGraph authorizes work claims, Schedule authorizes occurrence claims, and peer FIFO removal requires the exact runtime admission receipt bound to its claim. Queue snapshots, counters, ages, correlations, and projections are observability facts and cannot authorize mutation.

Canonical Machines

The canonical registry is canonical_machine_schemas() in meerkat-machine-schema/src/catalog/mod.rs. MeerkatMachine and MobMachine are the two runtime kernels. The other canonical machines protect narrower domain facts: auth and approval, schedules and occurrences, detached jobs and runtime delivery, session document and turn admission, and WorkGraph lifecycle, attention, and execution. WorkGraph is an optional subsystem authority for durable agent commitments, dependency-aware claim state, and goal attention bindings. WorkExecutionLifecycleMachine owns the cross-system obligation for one execution attempt without absorbing either WorkGraph item truth or Mob Flow run truth. Its effects are realized mechanically by the Mob composition facade, and every launch, evidence, or closure outcome returns through a typed machine input before the binding advances. Two scoped authorities sit outside the canonical registry. SessionPersistenceVersionAuthority owns admission of released session envelope and metadata schema versions. Its catalog DSL lives at meerkat-machine-schema/src/catalog/dsl/session_persistence_version_authority.rs, and its production expansion lives at meerkat-core/src/generated/session_persistence_version_authority.rs. It is a small restore-time authority, not a persistence-currentness or recovery authority, and it has no entry in canonical_machine_schemas(). MobHostBindingAuthority (catalog DSL source meerkat-machine-schema/src/catalog/dsl/mob_host_binding_authority.rs, production expansion meerkat-mob/src/machines/mob_host_binding_authority.rs). It is the member host’s process-scoped, mob-keyed admission and dedup authority for host-addressed mob commands: supervisor bind/rebind/revoke, host-command admission, materialize admission/preflight with success-only dedup memory, release admission with recorded-disposal replay, and the remote turn-outcome journal. It follows the session_persistence_version_authority precedent — one shared DSL body expanded into both crates, pinned by a dedicated production-schema parity test (meerkat-mob/tests/mob_host_binding_authority.rs) and by kernel tests instead of TLC — so it carries seam-inventory dispositions like any machine but has no poster, no generated kernel, and no entry in canonical_machine_schemas(). Principal control-scope grants (multi-host mobs §8) follow the ToolExecutionPolicy split. The MobMachine owns the grant lifecycle — the operator_grant_scopes / operator_grant_expiries facts, the GrantOperatorScopes / RevokeOperatorScopes transitions, and the in-machine revalidation of caller-proposed revoke partitions — while scope resolution lives shell-side in the sealed ResolvedControlPolicy (meerkat-mob/src/control_policy.rs): private shape, one resolve() mint path, no serde, fail-closed to the empty scope set. Expiry is data: the machine never reads a clock; the enforcement chokepoint reads the wall clock once per decision and passes now_ms in as a parameter, so a restored mob’s expired grants stay expired with zero persisted derived state. Grant durability is a runtime-metadata record (MobOperatorGrantRecord) written only under GrantRecorded/GrantRevoked transition witnesses and replayed on resume through the machine’s own GrantOperatorScopes input — never a second enforcement source.

Consumed Checkpoint Completion Recovery

A live steer checkpoint can durably consume an input before its completion receipt is finalized. Its CompletedWithoutResult candidate is not a new RunResult, and a pending completion does not make the input abandoned. MeerkatMachine owns checkpoint correlation through the consumed input’s existing run association, independently of the latest run-result slot. ClassifyTerminalCompletionCorrelation identifies the exact checkpoint shape; ResolveCheckpointCompletionResult emits authority bound to its session, run, owner input, candidate digest, complete recipient set and digest, and checkpoint requirement. Every recipient must be consumed by that exact run. A same-boundary Steer batch can have multiple recipients while retaining one canonical owner. Neither command replaces a newer active run or changes its terminal facts. The boundary is the committed receipt’s boundary, not merely the input’s requested steer mode. Live realization records it with RecordBoundarySeq; cold recovery rehydrates it through RecoverInputCompletionBoundary only after checking the existing receipt’s run, sequence, and contributors against the consumed input. This uses the existing durable format. An idle steer request can execute a full turn at either RunStart or RunCheckpoint. Its RunResult remains an ordinary run completion: the apply boundary is not a terminal verdict. Only the exact consumed CompletedWithoutResult shape uses independent checkpoint completion authority. The runtime validates the complete durable owner/recipient batch before requesting that authority, then finalizes the genuine receipt through the existing atomic input-row compare-and-swap. A stale row, changed candidate, wrong owner/run, or inconsistent recipient set refuses without rewriting the evidence. Multiple checkpoint batches in one run remain distinguished by their canonical owner inputs. The owner-held candidate and its digest are the durable candidate authority. The boundary receipt does not independently attest the candidate variant; replacing both a candidate and its digest coherently before recovery cannot be detected as historical tampering from that receipt alone. Existing row fences still reject changes made after a completion witness was captured. Only a successful receipt commit retires the pending obligation. The runtime_pending_terminal_owners table is an index over those obligations, not an operator lock table. Clearing it, rewriting an input as abandoned, or fabricating a publication receipt is not a recovery operation. Ordinary run results and directed interaction publication retain their separate generated authority and exact EventStore receipt requirements.

Durable-Tail Recovery Ownership

A Session is domain state, not a portable proof of persistence. It carries the conversation, metadata, usage, and compact transcript-rewrite graph, but no embedded persistence authority. Physical currentness belongs to the store:
  • WholeBlob authority is the exact {session_id, store_revision, blob_sha256} issued with the serialized row.
  • HeadCanonical authority is the exact {session_id, store_revision, boundary_head, committed_head_token}. The small boundary head binds its message-row, rewrite, graph, component, and metadata prefixes.
The intra-turn persistence hook may write a physical successor before the runtime boundary transaction. Each successful write returns an explicit RunCheckpointReceipt naming the store profile, committed base, run, exact candidate identity, and a contiguous candidate_sequence. A later write in the same run must present the preceding receipt; a retry must match it exactly. The final boundary promotes the latest exact receipt rather than serializing or applying the accumulated state again. A shutdown race can still leave the latest candidate durable while its runtime boundary is uncommitted. Durable is not the same as runtime-committed, and recovery of that tail is machine-owned end to end. Ownership splits three ways:
  • The runtime store retains; SessionDocumentMachine classifies. Store-issued committed authority and provisional-tail authority decide what a reader may be served; a recovery candidate is never returned as an ordinary session. ClassifyDurableTail consumes the mechanically extracted tail shape (head relation, run-id cardinality, terminal stop reason, dangling/orphan tool counts) and emits DurableTailClassified with a DurableTailRecoveryClass: CompletedCandidate, InterruptedRepairableCandidate, or Ambiguous. The classification transitions are total and disjoint over the observation. A tail carrying any dangling tool call classifies Ambiguous: the call proves intent, not execution, so the tail is held for reconciliation rather than repaired. The candidate id binds the exact store-issued base and candidate identities, run id, and candidate sequence, so classifying one physical tail can never authorize mutating a later one.
  • MeerkatMachine authorizes. AuthorizeDurableTailRecovery consumes the exact classified candidate together with typed projections of the durable evidence: the persisted machine-lifecycle row and its current-run fact (DurableRecoveryObservedLifecycle, DurableRecoveryObservedRun — a cold recovery drives a freshly registered authority whose in-process facts are vacuously quiescent, so the persisted row is the real evidence), the highest durably committed receipt compared against the candidate’s own content (DurableRecoveryPriorCommit — a candidate the receipts already cover refuses instead of committing a phantom duplicate boundary), and the attributability of the input rows the commit would terminalize (DurableRecoveryInputEvidence). Recovery is admissible only when both the in-process and the persisted facts are quiescent (Idle, Retired, or a missing row) with no current run and no recorded terminal for the candidate’s run; every other shape refuses. Commit verdicts arrive as DurableTailRecoveryCommitAuthorized (CommitCompleted, RepairAndCommitInterrupted, or CommitCompletedRetainInputs), carry the machine-minted boundary sequence — one past the last committed receipt for the run — and record the candidate run as the turn terminal. Hold and refusal verdicts arrive as DurableTailRecoveryAuthorized (HoldIntact, RefuseRecovery) and mutate nothing. CommitCompletedRetainInputs lets a completed candidate commit while retaining a separate unbound content input for ordinary redelivery. Only inputs durably proven bound to the recovered run are terminalized, including under this disposition. Unbound content inputs still hold interrupted candidates; unfenceable input evidence holds regardless of completed/interrupted shape. These holds and the ambiguous-classification hold are machine-minted: no shell predicate may downgrade a commit authorization to a hold. Authorization never changes the lifecycle phase.
  • RuntimeStore realizes. Only the machine’s commit-authorizing effect may drive the recovery commit. Recovery seals a profile-aware PreparedRuntimeSessionCommit and submits it through RuntimeStore::commit_prepared_session_boundary as one boundary: the exact store candidate is promoted, the run-boundary receipt, input lifecycle transitions, terminal outcome, catalog projection, and outbox rows commit together. WholeBlob promotion reuses the candidate bytes already written; HeadCanonical promotion advances the committed authority to the exact physical head. Both are fenced on the observed store, lifecycle-row, and input-row identities, so a concurrent writer fails the whole boundary typed instead of being overwritten. No shell promotes or discards the tail. recover_durable_tail(&dyn RuntimeStore, &SessionId) is the only public preparation seam. The store loads an opaque PreparedDurableTailRecoverySource; recovery derives the classification, receipt facts, candidate identity, and physical CAS before sealing crate-owned PreparedRecoveryEvidence. There is no public request constructor or caller-mintable recovery capability.
The recovery rule is never-discard: every store-proven durable descendant is preserved. Recovery commits a completed tail as a recovered run boundary, closes an interrupted tail as interrupted (content preserved, a typed recovery notice appended, the original run terminalized — never requeued), or holds ambiguous evidence intact with autonomous execution blocked. Current stores expose only store-issued committed or provisional-tail authority; the former embedded-checkpoint projection-conflict vocabulary is not part of the live authority path.

Canonical Compositions

The canonical registry is canonical_composition_schemas() in meerkat-machine-schema/src/catalog/mod.rs. These compositions are not marketing concepts or public APIs. They are contributor-facing guardrails for the runtime implementation.

Generated Artifacts

Generated and checked artifacts live in:

Contribution Rules

When a change affects lifecycle, routing, admission, credential state, mob membership, or scheduling, treat it as a machine-authority change until proven otherwise. Use this checklist:
  1. Identify the semantic owner.
  2. Add or update the catalog DSL if the legal states or transitions changed.
  3. Regenerate machine artifacts.
  4. Update production bridge code to call the generated authority path.
  5. Run the machine verification gates.
Do not add a side map, status enum, or handwritten reducer that decides the same fact in parallel with a machine.

Validation

Use the Make surface:
make agent-gate and CI run the relevant gates for normal development. Use the direct targets when you are touching the catalog, generated kernels, composition routes, or runtime bridge code.

See Also