reconcile

package
v0.44.1 Latest Latest
Warning

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

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

Documentation

Overview

Package reconcile is the pure functional core for the server's gate/grant and execution-lifecycle state. It performs no I/O: Step takes an observed World plus a Signal and returns the new ChangeSet plus the minimal set of Actions the imperative shell must execute. See docs/superpowers/specs/2026-06-15-reconciler-core-design.md.

Index

Constants

View Source
const (
	RunKindPlan  = "plan"
	RunKindApply = "apply"
)

Run kinds — the two CI run flavors serve can trigger.

View Source
const RunExecutionIDPrefix = "run-"

RunExecutionIDPrefix marks serve-minted run execution ids (see runExecutionID). The store's stuck-run query and the queued check render key off it to distinguish serve-initiated runs from runner-created executions.

Variables

This section is empty.

Functions

func IsRunExecutionID added in v0.22.0

func IsRunExecutionID(id string) bool

IsRunExecutionID reports whether id was minted by runExecutionID.

func MarshalEvent added in v0.17.0

func MarshalEvent(e Event) (string, []byte, error)

MarshalEvent serializes a domain Event to a neutral (typeTag, JSON) pair. The tag is the variant's Go type name; the data is its JSON. Neutral types only — no store dependency. Keep this switch and UnmarshalEvent in sync with the Event variants in event.go (the round-trip-every-variant test enforces it).

func MarshalSnapshot added in v0.17.0

func MarshalSnapshot(cs ChangeSet) ([]byte, error)

MarshalSnapshot serializes a folded ChangeSet (the GateState sum type is encoded with a discriminator). Neutral []byte — no store dependency.

func Requester

func Requester(g GateState) string

Requester returns the leased requester identity for the gate ("" if none).

Types

type Action

type Action interface {
	// contains filtered or unexported methods
}

Action is an effect the pure core wants performed but cannot do itself.

func React added in v0.17.0

func React(state ChangeSet, evs []Event) []Action

React projects the effects (Actions) the shell must perform from the domain facts. Presentation (RenderCheckRun/PublishSSE) is derived here, never stored as an Event. Action ordering must match step_table_test.go exactly.

type AdminCheckOverridden added in v0.28.0

type AdminCheckOverridden struct {
	PR         int    `json:"pr"`
	Env        string `json:"env"`
	CheckName  string `json:"check_name"`
	Conclusion string `json:"conclusion"`
	Actor      string `json:"actor"`
	Reason     string `json:"reason"`
}

type AdminExecutionCancelled added in v0.28.0

type AdminExecutionCancelled struct {
	Kind        string `json:"kind"`
	ExecutionID string `json:"execution_id"`
	Actor       string `json:"actor"`
	Reason      string `json:"reason"`
}

type AdminGateSatisfied added in v0.28.0

type AdminGateSatisfied struct {
	PR          int    `json:"pr"`
	Environment string `json:"environment"`
	Class       string `json:"class"`
	Target      string `json:"target"`
	Actor       string `json:"actor"`
	Reason      string `json:"reason"`
}

type AdminGrantReleased added in v0.28.0

type AdminGrantReleased struct {
	PR          int    `json:"pr"`
	Environment string `json:"environment"`
	Class       string `json:"class"`
	Target      string `json:"target"`
	Actor       string `json:"actor"`
	Reason      string `json:"reason"`
}

type AdoptRun added in v0.24.0

type AdoptRun struct {
	Kind        string
	ExecutionID string
	SHA         string
	BuildRef    string
}

AdoptRun materializes the execution row + check run for a run bound to a build serve did NOT launch (RunAdopted), pointing them at the existing BuildRef. Unlike StartRun it never calls the executor — the build already exists. No result.

type ApplySucceeded

type ApplySucceeded struct{}

type BlockReason

type BlockReason string
const (
	ReasonDenied       BlockReason = "denied"
	ReasonExpired      BlockReason = "expired"
	ReasonRevoked      BlockReason = "revoked"
	ReasonSlotForeign  BlockReason = "slot_foreign_open" // another open PR holds the slot (⑥)
	ReasonSlotSelf     BlockReason = "slot_self"         // same PR, another env, holds the slot (⑥)
	ReasonBackendError BlockReason = "backend_error"
)

type Blocked

type Blocked struct {
	Targets []Target
	Lease   Lease
	By      Blocker
}

type Blocker

type Blocker struct {
	Reason BlockReason
	ByPR   int    // slot blocker's PR (0 if N/A)
	ByEnv  string // slot blocker's environment
}

Blocker explains why a gate is Blocked.

type CancelRun added in v0.22.0

type CancelRun struct {
	Kind           string
	OldExecutionID string
	OldBuildRef    string
	NewExecutionID string
}

CancelRun supersedes an in-flight run: the shell marks OldExecutionID superseded by NewExecutionID in the store and best-effort cancels the old build when a BuildRef exists. Idempotent; no result.

type ChangeSet

type ChangeSet struct {
	PR            int
	Environment   string
	Gate          GateState
	CheckOverride *CheckOverride
	// Runs tracks serve-initiated CI runs, keyed by kind (plan/apply). Only
	// the run-start lifecycle lives here (webhook → build started); once the
	// runner reports, the internal/execution aggregate (stream "run:<execID>")
	// carries the execution facts.
	Runs map[string]Run
}

ChangeSet is the reconciliation unit: one PR's work in one environment.

func Evolve added in v0.17.0

func Evolve(cs ChangeSet, e Event) ChangeSet

Evolve applies a single domain Event to a ChangeSet, returning the new state. It is total (never panics on any state/event combination), pure (no I/O), and deterministic. It is the fold half of the Decider pattern: Decide emits Events, Evolve reduces them into state.

func UnmarshalSnapshot added in v0.17.0

func UnmarshalSnapshot(b []byte) (ChangeSet, error)

UnmarshalSnapshot reverses MarshalSnapshot.

type CheckOverride added in v0.28.0

type CheckOverride struct {
	CheckName  string `json:"check_name"`
	Conclusion string `json:"conclusion"`
	Actor      string `json:"actor"`
	Reason     string `json:"reason"`
}

type ClaimReleased added in v0.17.0

type ClaimReleased struct {
	PR          int
	Environment string
}

type Classified added in v0.17.0

type Classified struct{ Gates []events.GateTarget }

Classified establishes/replaces the gate target set. Evolve carries forward still-live prior grants (gap ② anti-clobber); Decide has already resolved the effective set (plan-authoritative vs apply-recovery union).

type Clean

type Clean struct{} // classified, zero gates → apply passes

type Collision

type Collision struct {
	ByPR          int
	ByEnv         string
	BySelf        bool // ByPR == this changeset's PR (different env)
	ByPRAbandoned bool // shell looked up the blocker PR's GitHub state (closed && !merged)
}

Collision describes a slot blocker the shell already resolved against GitHub.

type Event added in v0.17.0

type Event interface {
	// contains filtered or unexported methods
}

Event is a past-tense domain fact produced by Decide and folded by Evolve. Events are domain facts ONLY — presentation (check run / SSE) is never an Event; React projects that from the event stream. See the Phase 3 design.

func Decide added in v0.17.0

func Decide(state ChangeSet, s Signal) []Event

Decide computes the past-tense domain facts that result from applying Signal s to the prior state. All business logic lives here; Evolve only folds.

func UnmarshalEvent added in v0.17.0

func UnmarshalEvent(tag string, data []byte) (Event, error)

UnmarshalEvent reverses MarshalEvent.

type GateBlocked added in v0.17.0

type GateBlocked struct {
	Reason BlockReason
	ByPR   int
	ByEnv  string
}

type GatePassed added in v0.17.0

type GatePassed struct{}

GatePassed and GateReleased both fold the gate to Clean. They are distinct facts so React can present them differently: GatePassed (plan-clean / nothing to gate) renders success; GateReleased (post-apply cleanup) renders nothing.

type GateReleased added in v0.17.0

type GateReleased struct{}

type GateSatisfied added in v0.17.0

type GateSatisfied struct{}

type GateState

type GateState interface {
	// contains filtered or unexported methods
}

GateState is a sum type: a gate is EXACTLY one of these.

type GateTargetRequested added in v0.17.0

type GateTargetRequested struct{ Class, Target, Requester string } // Requester pins the lease (step.go:140)

type GateTick

type GateTick struct{ Grants []ObservedGrant }

GateTick is the periodic backstop re-observation; Grants is a full re-list of the changeset's targets. Processed identically to GrantsObserved.

type GrantCleared added in v0.17.0

type GrantCleared struct{ Class, Target string }

type GrantObserved added in v0.17.0

type GrantObserved struct {
	Class, Target, Name string
	State               approval.GrantState
	Requester           string
}

type GrantsObserved

type GrantsObserved struct{ Grants []ObservedGrant }

GrantsObserved carries grant observations for THIS changeset: either the results of RequestGrant actions (fixpoint feedback) or a re-list. Grants not mentioned are left unchanged.

type InboundBuild added in v0.24.0

type InboundBuild struct {
	Kind     string // RunKindPlan | RunKindApply
	SHA      string // resolved commit sha of the build
	BuildRef string // Cloud Build build id
}

InboundBuild is an observed Cloud Build lifecycle event for a build serve may NOT have launched — a native-check Re-run or a console rebuild. The shell has already correlated it to this ChangeSet's (pr, env) and derived the run Kind (from the trigger) + the resolved commit SHA. Decide reconciles it onto the PR's stuck run; the fail-safe backstop for the adopted build stays the watchdog.

type Lease

type Lease struct{ Requester string }

Lease is the pooled requester identity leased once per ChangeSet. Empty Requester means undecided.

type NotClassified

type NotClassified struct{} // never planned → apply fails closed

type ObservedGrant

type ObservedGrant struct {
	Class     string
	Target    string
	Name      string              // backend-assigned id ("" if none)
	State     approval.GrantState // "" == no grant present for this changeset
	Requester string              // leased identity ("" if N/A)
	Collision *Collision          // set when a RequestGrant hit a SlotCollisionError
}

ObservedGrant is one grant fact the shell gathered for a (class,target).

type PRClosed

type PRClosed struct{}

type PRClosedRecorded added in v0.17.0

type PRClosedRecorded struct{}

PRClosedRecorded marks a PR-closure teardown so React renders SSE-only (no check run), distinct from an externally-observed revocation. Named *Recorded to avoid colliding with the PRClosed Signal.

type Pending

type Pending struct {
	Targets []Target
	Lease   Lease
}

type PostCommitStatus

type PostCommitStatus struct {
	State       string // "success" | "failure" | "pending"
	Description string
}

PostCommitStatus posts the commit status. Pure output; no result.

type PublishSSE

type PublishSSE struct{}

PublishSSE notifies the live-page hub that state changed. Pure output.

type ReleaseClaim added in v0.16.2

type ReleaseClaim struct {
	PR          int
	Environment string
}

ReleaseClaim releases the merge-lock stack claim a PR holds in an environment. Emitted post-apply (the apply is done, so the claim it held is no longer needed). The shell deletes the claims and re-evaluates the env's held apply-lock checks (cross-PR I/O). Idempotent.

type RenderCheckRun

type RenderCheckRun struct {
	Terminal   bool
	Conclusion string // "success" | "action_required" | "failure" | "" (in-progress)
}

RenderCheckRun re-renders the GitHub check run from current state. Pure output; no result.

type RequestGrant

type RequestGrant struct {
	Class     string
	Target    string
	Requester string
}

RequestGrant asks the backend to ensure an open grant. It YIELDS a result (grant name + leased requester, or a Collision) the shell feeds back as a GrantsObserved signal. Requester pins the lease; "" lets the backend lease.

type RevokeGrant

type RevokeGrant struct {
	Class       string
	Target      string
	PR          int
	Environment string
}

RevokeGrant revokes the open grant for (class,target,PR,environment). Idempotent; produces no result. PR/Environment default to the changeset's when zero/empty but are set explicitly when revoking a foreign slot blocker.

type Run added in v0.22.0

type Run struct {
	ExecutionID string
	Kind        string // RunKindPlan | RunKindApply
	SHA         string
	Branch      string
	Attempt     int      // bumps on rerun / retry-after-failure (part of the deterministic execution id)
	BuildRef    string   // executor backend reference ("" until started)
	Phase       RunPhase //
}

Run is the serve-initiated lifecycle of one CI run: requested by a webhook, queued (execution + check run exist), started (build accepted by the executor backend), or terminally start_failed / superseded.

func (Run) Live added in v0.22.0

func (r Run) Live() bool

Live reports whether the run is still in flight (may be superseded/cancelled).

type RunAdopted added in v0.24.0

type RunAdopted struct {
	Kind        string
	ExecutionID string
	SHA         string
	Branch      string
	Attempt     int
	BuildRef    string
}

RunAdopted records serve binding a run to a build it did NOT launch (a rerun created outside triggers.run). Unlike RunQueued it carries an existing BuildRef and does NOT trigger StartRun — the build already exists. Evolve folds it to a Started-phase Run; React materializes the execution row + check via AdoptRun. The watchdog then tracks BuildRef and fails it if it finishes without the runner ever reporting (fail-safe backstop).

type RunCompleted added in v0.22.0

type RunCompleted struct {
	Kind        string
	ExecutionID string
}

RunCompleted records the runner taking over and finalizing: the run-start lifecycle is over. Also emitted when a "start-failed" run finalizes anyway (a client-side start timeout whose build actually ran) — the finalize proves the start happened, and folding this stops the failure projection.

type RunPhase added in v0.22.0

type RunPhase string

RunPhase is the run-start lifecycle phase.

const (
	RunPhaseQueued      RunPhase = "queued"       // execution created, StartRun pending/issued
	RunPhaseStarted     RunPhase = "started"      // executor accepted the build
	RunPhaseStartFailed RunPhase = "start_failed" // executor refused / start errored
	RunPhaseSuperseded  RunPhase = "superseded"   // a newer SHA replaced this run
	RunPhaseCompleted   RunPhase = "completed"    // the runner took over and finalized
)

type RunQueued added in v0.22.0

type RunQueued struct {
	Kind        string
	SHA         string
	Branch      string
	ExecutionID string
	Attempt     int
}

RunQueued records that a run request was accepted: the shell creates the execution row + a "queued" check run and issues StartRun. ExecutionID is minted deterministically by Decide (pr/env/kind/sha/attempt — the pure core cannot use randomness).

type RunRequested added in v0.22.0

type RunRequested struct {
	Kind   string // RunKindPlan | RunKindApply
	SHA    string
	Branch string
	Rerun  bool
}

RunRequested asks for a CI run: pull_request opened/synchronize → plan for the head SHA; push to main → apply for the merge commit; check_run rerequested → the same kind again with Rerun set (forces a new attempt even for an already-seen SHA).

type RunStartFailed added in v0.22.0

type RunStartFailed struct {
	Kind        string
	ExecutionID string
	Reason      string
}

RunStartFailed records the executor refusing / erroring — surfaced as a terminal check failure (vs the pre-driver era's silent nothing).

type RunStartResult added in v0.22.0

type RunStartResult struct {
	Kind        string
	ExecutionID string
	BuildRef    string
	Err         string
}

RunStartResult is the fixpoint feedback from a StartRun action (like GrantsObserved for RequestGrant): the executor either accepted the build (BuildRef) or failed (Err).

type RunStarted added in v0.22.0

type RunStarted struct {
	Kind        string
	ExecutionID string
	BuildRef    string
}

RunStarted records the executor accepting the build.

type RunSuperseded added in v0.22.0

type RunSuperseded struct {
	Kind           string
	OldExecutionID string
	OldBuildRef    string
	NewExecutionID string
	NewSHA         string
}

RunSuperseded records a newer SHA replacing an in-flight run (plan only — applies are never superseded). The shell cancels the old build (when a BuildRef exists) and marks the old execution superseded by the new one.

type RunnerFinalize

type RunnerFinalize struct {
	Gates []events.GateTarget // (class,target) pairs needing approval
	// ApplyContext marks a finalize from the post-merge apply (apply/<env>) rather
	// than the plan gate. An apply finalize is a RECOVERY signal, not an authority:
	// it may add/refresh gate targets but must never weaken a gate the plan already
	// established (issue #103) — so decideFinalize unions the prior targets and never
	// prunes when this is set. A plan finalize (false) stays authoritative.
	ApplyContext bool
}

RunnerFinalize is the gate-lifecycle signal derived from a runner Finalize report: execution-lifecycle facts (init/phase/tick/terminal/annotate) are handled by the internal/execution aggregate; this signal carries only what decideFinalize reads to establish/advance the gate.

type Satisfied

type Satisfied struct {
	Targets []Target
	Lease   Lease
}

type Signal

type Signal interface {
	// contains filtered or unexported methods
}

Signal is the union of everything that can drive a ChangeSet.

type StartRun added in v0.22.0

type StartRun struct {
	Kind        string // RunKindPlan | RunKindApply
	SHA         string
	Branch      string
	ExecutionID string
}

StartRun asks the executor backend to start a CI build for this changeset. It YIELDS a result (build ref or error) the shell feeds back as a RunStartResult signal. The shell also creates the execution row + "queued" check run before starting, so feedback appears within the webhook turnaround.

type Target

type Target struct {
	Class     string
	Target    string
	GrantName string              // backend-assigned id; "" until created
	Grant     approval.GrantState // observed grant lifecycle; "" == no grant/absent
}

Target is one (class,target) gate and its observed grant.

type TargetRevoked added in v0.17.0

type TargetRevoked struct {
	Class, Target string
	PR            int
	Env           string
}

type World

type World struct {
	Prior   ChangeSet
	Version int
}

World is the scoped, immutable snapshot the shell hands to Step/Decide. Prior is the current persisted state for (PR, Environment); Version is the event stream's current version (0 for an empty stream), used for optimistic- concurrency append. Observations ride on the Signal. The core performs no I/O.

Jump to

Keyboard shortcuts

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