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

govern

package
v0.6.0 Latest Latest
Warning

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

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

Documentation

Overview

Package govern is the Tier-2 (distributed / concurrent) compensating-agent layer: it integrates the gsm "Governed State Machine" library so that MULTIPLE concurrent agents mutating SHARED state converge to the same valid state regardless of interleaving — PROVABLY, via gsm's build-time normalization-confluence verification (WFC + CC).

This is the fundamentally different tier from the sequential/hierarchical saga in the core package: there is no single causal order to reverse, so correctness can't be "reverse-the-log." Instead each agent's action is a gsm Event, shared business state is gsm's finite-domain state, business rules are Invariants, and compensations are Repairs. gsm proves at build time that every interleaving reaches the same normal form.

It lives OUTSIDE the core (which imports no gsm) — an edge integration, so the lean hexagonal core is untouched.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AttestedEventTool

func AttestedEventTool(gov Applier, name, description, event, policyDigest string, safety agent.Safety) agent.Tool

AttestedEventTool is EventTool that also records, in the tool's durable result, WHICH governed policy admitted the action: it embeds policyDigest (a stable identifier of the combinator policy, e.g. gsm.Registry.PolicyDigest) alongside the event. Because the result is part of the journaled record, any audit over the journal (a signed tree head, or a ProofBundle from ProveToolCall) then commits cryptographically to the policy the action ran under, not merely that the action happened. An auditor recomputes the digest from the published policy bytes and runs the external verified oracle on them (see `bide-audit verify-governance`), tying the cryptographic root (the log) to the mathematical root (the proof) over one artifact.

policyDigest is treated as an opaque string on purpose: the SDK does not depend on gsm's serialization format, it only records the identifier the policy's owner published.

func EventTool

func EventTool(gov Applier, name, description, event string, safety agent.Safety) agent.Tool

EventTool gives an agent a GOVERNED action: when the agent's LLM calls it, `event` is applied to the shared governed state (convergent + thread-safe). Multiple agents that share one governor converge regardless of how their governed calls interleave — the tool-boundary bridge from real agent loops to gsm's proven convergence. gsm Guards handle preconditions (a guarded-out event is a no-op).

This is the "governor middleware": the point where an agent's tool call becomes a verified event on shared state, rather than an unchecked side effect. Works with either Governor (in-memory) or PersistentGovernor (crash-recoverable).

func FederatedEventTool

func FederatedEventTool(gov FederatedApplier, name, description, registry, event string, safety agent.Safety) agent.Tool

FederatedEventTool gives an agent a GOVERNED federated action: when the agent's LLM calls it, `event` is applied to component `registry` of the shared federation. Multiple agents (each owning a different registry) converge regardless of interleaving, with cross-registry conflicts resolved by the authority argument — no locking. This is the federated twin of EventTool: the point where an agent tool call becomes a verified event on shared, cross-organizational governed state.

Types

type Applied added in v0.5.0

type Applied struct {
	// State is the shared normal form after the event.
	State gsm.State
	// Position is the event's place in the order State is a replay of: its position in the shared
	// EventLog for a log-backed governor, or in the governor's own sequence for the in-memory
	// Governor. Replaying the events at positions [0, Position] from the initial state through the
	// same policy reproduces State exactly, which is what lets an auditor check a recorded state.
	Position int64
}

Applied is the outcome of applying one governed event.

type Applier

type Applier interface {
	Apply(ctx context.Context, event string) (Applied, error)
	// State returns the governor's current view of the shared state. For a log-backed governor
	// it is as of the last Apply or Sync; other processes' later appends are folded in by the
	// next one.
	State() gsm.State
}

Applier applies a named governed event to shared state. Both the in-memory Governor and the log-backed PersistentGovernor satisfy it, so EventTool works with either.

type ConfluenceCertificate

type ConfluenceCertificate struct {
	Machine      string `json:"machine"`       // name of the gsm machine the certificate is about
	PolicyDigest string `json:"policy_digest"` // stable identifier of the policy this certificate certifies

	// Converges is the headline: WFC && CC. True means every interleaving of events reaches the
	// same normal form, proven exhaustively at build time.
	Converges bool `json:"converges"`

	// WFC (well-founded compensation): every repair chain terminates; MaxRepairLen is the longest
	// one found. CC (compensation commutativity): independent event pairs commute.
	WFC          bool `json:"wfc"`
	CC           bool `json:"cc"`
	MaxRepairLen int  `json:"max_repair_len"`

	// The CC evidence: how many independent pairs were checked, split into those discharged by
	// footprint-disjointness and those checked by brute force over all valid states.
	PairsTotal    int `json:"pairs_total"`
	PairsDisjoint int `json:"pairs_disjoint"`
	PairsBrute    int `json:"pairs_brute"`

	// States is the size of the state space the check ran over exhaustively.
	States int `json:"states"`

	// CompensationFree marks the CRDT fragment. CRDT.v (sibling proof repo) establishes that
	// CRDTs are exactly the compensation-free fragment of this theory: operations that always
	// preserve every invariant, so normalization never needs to repair. MaxRepairLen == 0 means
	// no state in the enumerated space ever triggered a repair, so the machine sits in that
	// fragment: it is a CRDT, needs no coordinator, and its convergent state can merge with other
	// CRDT infrastructure. A positive MaxRepairLen means the policy uses compensation and is
	// therefore strictly more expressive than any CRDT (the reason to run it here at all). The
	// classification is conservative: MaxRepairLen == 0 is a sound witness of the CRDT fragment;
	// a positive value means compensation is defined and may fire.
	CompensationFree bool `json:"compensation_free"`
}

ConfluenceCertificate is the machine-checkable evidence that a governed policy converges, carried out of gsm's build-time verification and into the SDK so it can be surfaced to a user and anchored in the audit trail. gsm.Registry.Build runs the normalization-confluence check EXHAUSTIVELY over the enumerated state space (not sampled): WFC proves every compensation chain terminates, and CC proves every independent event pair commutes to the same normal form. By Newman's lemma those two local facts give global confluence, so applying a set of events in ANY order reaches the same valid state. This certificate is that result made portable.

Without it the guarantee lived only inside gsm's build step and was discarded at the Bide boundary; a run could prove which policy was used (audit) but not that the policy was provably convergent. Anchoring the certificate next to the policy leaf (see audit.RecordConvergence) closes that: an auditor verifies not merely "policy digest X was used" but "policy digest X is provably convergent, checked over N states".

func CertifyConvergence

func CertifyConvergence(rep *gsm.Report, policyDigest string) ConfluenceCertificate

CertifyConvergence translates a gsm build Report and the policy's digest into a portable certificate. Pass the Report returned by gsm.Registry.Build and the digest from Registry.PolicyDigest. A Report whose machine failed to build (WFC or CC false) still produces a certificate; it simply reports Converges == false, which a caller should refuse to deploy.

func (ConfluenceCertificate) Classification

func (c ConfluenceCertificate) Classification() string

Classification names the fragment the machine lives in, for a human-facing report.

func (ConfluenceCertificate) Marshal

func (c ConfluenceCertificate) Marshal() ([]byte, error)

Marshal serializes the certificate for anchoring as an audit leaf (see audit.RecordConvergence).

func (ConfluenceCertificate) String

func (c ConfluenceCertificate) String() string

String renders the certificate for a build log or CLI.

type EventLog

type EventLog interface {
	// Append records event at the end of entity's log and returns its position: the number of
	// the entity's events recorded before it. It must be safe to call concurrently from many
	// processes, which must never be assigned the same position.
	Append(ctx context.Context, entity, event string) (int64, error)
	// Events returns entity's events at positions from onward, in log order.
	Events(ctx context.Context, entity string, from int64) ([]string, error)
}

EventLog is an append-only, totally ordered log of governed events per entity: the durable shared record several processes' governors act on (MemEventLog below; govern/sqlitelog, govern/postgreslog, and govern/redislog for durable backends).

Positions are dense and never change. The first event appended for an entity is at position 0, and once a reader has seen the events at positions [0, n), every later read returns those same events first. Governors rely on this to fold other processes' events into their state in exactly the order an auditor will replay them.

type FedApplied added in v0.5.0

type FedApplied struct {
	State    gsm.FedState
	Position int64
}

FedApplied is the outcome of applying one federated event: the federated normal form after it, and the event's position in the shared EventLog. Replaying the log's events at positions [0, Position] from the initial federated state reproduces State.

type FederatedApplier

type FederatedApplier interface {
	Apply(ctx context.Context, registry, event string) (FedApplied, error)
	// State returns the governor's view of the federated state as of its last Apply or Sync.
	State() gsm.FedState
}

FederatedApplier is the federated analogue of Applier: it applies a named event to a named component registry of a gsm federation.

type FederatedGovernor

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

FederatedGovernor is a governor over a gsm federation (multiple component registries linked by directed morphisms), backed by a shared, durable EventLog. Each applied event is appended to the log as a (registry, event) pair, and the governor's state is the log replayed through the federated machine: before answering, Apply folds in every event the log holds up to and including its own, in log order, including other processes' events. So any number of processes sharing the log act on the same federated state, and a recorded state matches an auditor's replay of the log.

Cross-registry conflicts resolve WITHOUT coordination via the authority argument (paper §8.3): a source registry deterministically fixes its targets' shared components, so a target-side event that collides with its source is overwritten on the next ρ_Fed. This is the "distributed compensating agents across ownership boundaries" tier: each component registry is an organization or agent subtree, and morphisms are the cross-org constraints.

func NewFederated

func NewFederated(ctx context.Context, m *gsm.FedMachine, log EventLog, entity string, initial gsm.FedState) (*FederatedGovernor, error)

NewFederated constructs a federated governor over the entity's log and reconstructs its state by replaying the log from the initial federated state.

func (*FederatedGovernor) Apply

func (fg *FederatedGovernor) Apply(ctx context.Context, registry, event string) (FedApplied, error)

Apply validates the (registry, event) against the federation, appends it to the shared log, then folds the log into the state up to and including its position. Validation runs before the append (via the pure ApplyNamed, which does not mutate), so an unknown registry or event is never written to the log. If the append succeeds but reading the log fails, the event is still recorded; the next Apply or Sync folds it in.

func (*FederatedGovernor) State

func (fg *FederatedGovernor) State() gsm.FedState

State returns the governor's view of the federated state as of its last Apply or Sync.

func (*FederatedGovernor) Sync added in v0.5.0

Sync folds every entry the log currently holds into the state and returns it, so the governor reflects other processes' events without applying one of its own.

type Governor

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

Governor applies agents' events to one in-memory, gsm-governed state. Apply is convergent and thread-safe: because the machine was verified at Build time (WFC + CC), any order of the same event set lands on the same valid normal form. Its state lives only in this process; use a PersistentGovernor over a shared EventLog when several processes act on the same state.

func New

func New(m *gsm.Machine, initial gsm.State) *Governor

New wraps a built, verified gsm.Machine with an initial state.

func (*Governor) Apply

func (g *Governor) Apply(_ context.Context, event string) (Applied, error)

Apply advances the shared state by one event (O(1) table lookup; compensation to a valid normal form is baked in). Position is the event's place in this governor's sequence. The error is always nil for the in-memory Governor; it exists to satisfy Applier alongside PersistentGovernor.

func (*Governor) State

func (g *Governor) State() gsm.State

State returns the current shared normal-form state.

type MemEventLog

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

MemEventLog is an in-memory EventLog for tests/local dev.

func NewMemEventLog

func NewMemEventLog() *MemEventLog

NewMemEventLog returns an empty in-memory EventLog.

func (*MemEventLog) Append

func (l *MemEventLog) Append(_ context.Context, entity, event string) (int64, error)

Append records an event for the entity and returns its position.

func (*MemEventLog) Events

func (l *MemEventLog) Events(_ context.Context, entity string, from int64) ([]string, error)

Events returns a copy of the entity's events at positions from onward.

type PersistentGovernor

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

PersistentGovernor is a governor over a shared, durable EventLog. Every event is appended to the log, and the governor's state is the log replayed through the machine, so any number of processes sharing the log act on the same state: before answering, Apply folds in every event the log holds up to and including its own, in log order, including other processes' events. The state an Apply returns is therefore exactly what an auditor gets by replaying the shared log through that event's position.

func NewPersistent

func NewPersistent(ctx context.Context, m *gsm.Machine, log EventLog, entity string, initial gsm.State) (*PersistentGovernor, error)

NewPersistent constructs a governor over the entity's log and reconstructs its state by replaying the log from the initial state.

func (*PersistentGovernor) Apply

func (pg *PersistentGovernor) Apply(ctx context.Context, event string) (Applied, error)

Apply appends the event to the shared log, then folds the log into the state up to and including the event's position (so every earlier event, from any process, is applied first, in log order), and returns that state and position. Events appended after it by other processes are folded in by the next Apply or Sync. If the append succeeds but reading the log fails, the event is still recorded; the next Apply or Sync folds it in.

func (*PersistentGovernor) State

func (pg *PersistentGovernor) State() gsm.State

State returns the governor's view of the shared state as of its last Apply or Sync.

func (*PersistentGovernor) Sync added in v0.5.0

func (pg *PersistentGovernor) Sync(ctx context.Context) (gsm.State, error)

Sync folds every event the log currently holds into the state and returns it, so the governor reflects other processes' events without applying one of its own.

type QuorumResult

type QuorumResult struct {
	Decision string `json:"decision"`  // the plurality decision (the label the most voters chose)
	VotesFor int    `json:"votes_for"` // how many voters chose Decision
	Total    int    `json:"total"`     // number of voters that successfully voted
	Agreed   bool   `json:"agreed"`    // whether VotesFor >= k (statistical signal, not a proof)
	Votes    []Vote `json:"votes"`     // every recorded vote, in voter order
}

QuorumResult is the tally over the votes. Decision is the plurality value (the decision the most voters chose); VotesFor is how many voters chose it; Total is the number of voters that voted; Agreed reports whether VotesFor >= k. Votes carries every recorded vote in voter order.

Agreed is the statistical signal, not a proof. The provable object is the gate a caller builds from VotesFor: seed votes_for into a gsm invariant that admits the commit only when votes_for >= k (see examples/quorum), and the k-of-n requirement is then machine-checked over every possible count. Whether the agreed Decision is correct is not certified here: correlated model errors mean agreement is not statistical independence, so a quorum lowers single-model risk without certifying the answer.

func Quorum

func Quorum(ctx context.Context, store agent.Durable, runID string, k int, voters ...Voter) (QuorumResult, error)

Quorum fans the voters out concurrently and durably, tallies their normalized decisions, and returns the plurality decision with its count. It is a composition helper over existing seams, not a new agent type: the fan-out is agent.Parallel (each vote is a journaled Step, so it is at-most-once and replayable), each vote and the final tally are recorded as durable Step values (provable one by one via audit.ProveStep), and the k-of-n gate itself is left to the caller to express as a gsm invariant over VotesFor (see examples/quorum). Keeping the model call inside each Voter.Decide keeps this helper model-agnostic and the votes normalized.

Durability: the votes and the tally are memoized under runID by name. Re-running Quorum with the same runID returns the recorded votes without re-invoking any Voter.Decide, so a resumed run neither re-queries the models nor re-tallies. Voter names must therefore be unique within one call. The tally is recorded under the step name "quorum/tally".

Tally: votes are grouped by decision value; the plurality value wins (ties broken by decision string, deterministically, so the recorded tally is stable across replays). Agreed is VotesFor >= k. A voter that errors is not recorded, so on a later resume it re-runs while the voters that already voted are memoized; the joined error is returned and the tally reflects only the votes that succeeded (Total is the count of successful votes). k <= 0 is treated as no gate (Agreed is true whenever at least one voter voted).

The boundary, stated plainly: this returns a tally. The tally makes the k-of-n gate provable once a caller wires VotesFor into an invariant; the agreement itself is statistical and never a guarantee of correctness. Do not blur the two.

type Vote

type Vote struct {
	Voter    string `json:"voter"`    // the voter that cast this decision
	Decision string `json:"decision"` // the normalized decision label the voter chose
}

Vote is one voter's recorded decision. It is the provenance the audit trail commits to.

type Voter

type Voter struct {
	Name   string                                // identifies who voted; also the durable step key, so it must be unique per Quorum call
	Decide func(context.Context) (string, error) // produces this voter's normalized decision label
}

Voter is one named unit that produces a normalized decision. Name identifies who voted (a model, a provider/version, or any principal), so the recorded vote shows not just the tally but who voted how; it doubles as the durable step key, so it must be unique across the voters in one Quorum call. Decide returns a discrete decision string; the caller keeps the model call and its parsing inside Decide, so Quorum stays model-agnostic and every vote is already a comparable, normalized value. Free-form prose cannot be quorumed: Decide must map to a small set of labels (an enum, a yes/no, a chosen action). Decide must have no side effects: a vote not yet recorded when the process dies is cast again on resume.

Directories

Path Synopsis
Package eventlogtest checks that an implementation satisfies the govern.EventLog contract, so every adapter (in-memory, SQLite, Postgres, Redis, or your own) is held to the same guarantees a governor relies on: dense, unique positions under concurrent appends from separate processes, and a log that only ever grows at the end.
Package eventlogtest checks that an implementation satisfies the govern.EventLog contract, so every adapter (in-memory, SQLite, Postgres, Redis, or your own) is held to the same guarantees a governor relies on: dense, unique positions under concurrent appends from separate processes, and a log that only ever grows at the end.
postgreslog module
redislog module
sqlitelog module

Jump to

Keyboard shortcuts

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