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

govern

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 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 Applier

type Applier interface {
	Apply(ctx context.Context, event string) (gsm.State, error)
	State() gsm.State
}

Applier applies a named governed event to shared state, returning the new normal form. Both the in-memory Governor and the crash-recoverable 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(ctx context.Context, entity, event string) error
	Events(ctx context.Context, entity string) ([]string, error)
}

EventLog is an append-only log of governed events per entity. Order is preserved on append but is NOT required for correctness — gsm convergence guarantees any replay order of the same event set reaches the same state. Implementations: MemEventLog (below), and any durable backend (SQLite/Postgres) with the same two methods.

type FederatedApplier

type FederatedApplier interface {
	Apply(ctx context.Context, registry, event string) (gsm.FedState, error)
	State() gsm.FedState
}

FederatedApplier is the federated analogue of Applier: it applies a named event to a named component registry of a gsm federation, returning the new federated normal form.

type FederatedGovernor

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

FederatedGovernor is a crash-recoverable governor over a gsm federation (multiple component registries linked by directed morphisms). Each applied event is durably appended to an EventLog as a (registry, event) pair; state is reconstructed by replaying the log through the federated machine.

Two guarantees carry over from the federated convergence theorem (paper §8):

  • Replay is exact and, for events the network proved independent, order-independent — so a shared durable log lets multiple processes converge on the same federated state.
  • Cross-registry conflicts resolve WITHOUT coordination via the authority argument (§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 / agent subtree, 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 and reconstructs its state by replaying the entity's event log from the initial federated state.

func (*FederatedGovernor) Apply

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

Apply validates the (registry, event) against the federation, durably records it, then advances the federated state. Validation runs before the append (via the pure ApplyNamed, which does not mutate), so a rejected event is never written to the log; if the append fails, the in-memory state is left untouched.

func (*FederatedGovernor) State

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

State returns the current federated normal-form state.

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.

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) (gsm.State, error)

Apply advances the shared state by one event (O(1) table lookup; compensation to a valid normal form is baked in). 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) error

Append records an event for the entity, preserving append order.

func (*MemEventLog) Events

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

Events returns a copy of the entity's events in append order.

type PersistentGovernor

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

PersistentGovernor is a crash-recoverable Governor: every applied event is durably appended to an EventLog, and the current state is reconstructed by REPLAYING the log through the machine on startup. Because gsm.Apply is convergent, replay is exact — and (for the events gsm proved independent) order-independent. Back it with a shared durable EventLog and multiple processes converge on the same governed state.

func NewPersistent

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

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

func (*PersistentGovernor) Apply

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

Apply durably records the event, then advances the shared state. On a later restart, NewPersistent replays the log to the same state.

func (*PersistentGovernor) State

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

State returns the current shared normal-form state.

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).

Directories

Path Synopsis
postgreslog module
redislog module
sqlitelog module

Jump to

Keyboard shortcuts

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