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

cistate

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: AGPL-3.0, AGPL-3.0-only Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const StatePath = paths.Root + "/pipeline.json"

StatePath is the workspace-relative path where pipeline state is persisted.

View Source
const SubsystemsDir = paths.Root + "/subsystems"

SubsystemsDir holds one fragment per subsystem outcome (subsystems/build.json, subsystems/security.json, …). These fragments — NOT pipeline.json — are the single carrier of subsystem state ACROSS jobs.

Why: CI jobs are isolated workspaces whose outputs are recombined by an artifact system with no merge — only file union, where same-path files clobber. A single shared pipeline.json therefore cannot accumulate across jobs: when a downstream job (publish) downloads two upstreams that each wrote pipeline.json, the last-written copy overwrites the other, silently dropping the subsystems only one job recorded. That order-dependent clobber is what made publish's authorization gate see "security did not run". Per-name fragments never collide across jobs (perform→build.json, review→security.json), so ReadState UNIONs them regardless of download order. pipeline.json remains each job's LOCAL merged view (the read model); it is not forwarded as a contested cross-job ledger.

Variables

View Source
var BareFacts = []string{
	"status", "status_icon", "status_verb",
	"project", "modality", "ref", "pipeline_url", "commit_title",
	"duration", "sha", "version",
}

BareFacts are the always-resolvable identity and status facts served directly by Fact()'s switch — usable as {name} in any composed body.

View Source
var DomainFacts = map[string][]string{
	"failure":   {"subsystem", "reason"},
	"retention": {"pruned"},
	"publish":   {"tags", "registries"},
	"tests":     {"total", "passed", "coverage"},
	"security":  {"blocking", "critical", "high", "medium", "low", "total", "sbom", "blocking_list"},
	"changelog": {"count", "range"},
	"reconcile": {"total", "succeeded", "failed", "backend", "units", "cluster", "declined", "failures"},
	"ansible":   {"total", "converged", "changed", "unreachable", "failed"},
}

DomainFacts maps each fact domain to the metric keys its subsystem records — usable as {domain.key}. Every domain ALSO serves {domain.outcome} and {domain.reason} (the universal subsystem keys), and every metric elides when its domain recorded nothing. The special domains failure and retention are served by Fact() itself rather than a subsystem's Results.

Functions

func UpdateState

func UpdateState(rootDir string, fn func(*State)) error

UpdateState does read-modify-write. The caller mutates individual fields only — never rebuild nested structs wholesale to avoid clobbering prior state written by other subsystems.

func WriteState

func WriteState(rootDir string, st *State) error

WriteState writes pipeline state atomically (tmp + fsync + rename). Normalizes Version to 1 on write and stamps StartedAt on the run's first write (the {duration} fact's zero point — recording time is impure by nature; the RENDER of the recorded value stays deterministic).

Types

type BuildState

type BuildState struct {
	ProducedImages bool   `json:"produced_images"`
	PublishedCount int    `json:"published_count"`
	ManifestPath   string `json:"manifest_path,omitempty"`
}

BuildState holds build-specific domain metadata. Lifecycle tracking (attempted/completed/outcome) is in Subsystems.

type CIState

type CIState struct {
	Provider   string `json:"provider"`
	PipelineID string `json:"pipeline_id"`
	Ref        string `json:"ref,omitempty"`
	Branch     string `json:"branch,omitempty"`
	Tag        string `json:"tag,omitempty"`
	SHA        string `json:"sha"`

	Project     string `json:"project,omitempty"`      // repo/project name ({project})
	Modality    string `json:"modality,omitempty"`     // lifecycle mode ({modality})
	PipelineURL string `json:"pipeline_url,omitempty"` // forge pipeline URL ({pipeline_url})
	CommitTitle string `json:"commit_title,omitempty"` // HEAD commit subject ({commit_title})
	Version     string `json:"version,omitempty"`      // detected version string ({version})

	// StartedAt is stamped (unix seconds) on the run's FIRST state write — the
	// audition, in practice — and rides the state artifact across jobs. narrate
	// derives DurationSecs from it at gather time; {duration} renders from the
	// recorded value, so the render itself stays deterministic.
	StartedAt    int64 `json:"started_at,omitempty"`
	DurationSecs int   `json:"duration_secs,omitempty"`
}

CIState captures the CI environment for this pipeline run, plus the run's identity facts ({project}/{modality}/{pipeline_url}/{commit_title}) recorded by the phases that can compute them (best-effort fill; empty stays empty).

func InitFromCI

func InitFromCI(ciCtx *ci.CIContext) CIState

InitFromCI populates a CIState from a ci.CIContext.

type ExternalRetentionRecord added in v0.5.0

type ExternalRetentionRecord struct {
	Registry string   `json:"registry"`
	Prefix   string   `json:"prefix"`
	Total    int      `json:"total"`
	Pruned   int      `json:"pruned"`
	Kept     int      `json:"kept"`
	Errors   []string `json:"errors,omitempty"`
}

ExternalRetentionRecord records external cache retention results.

type LocalRetentionRecord added in v0.5.0

type LocalRetentionRecord struct {
	Dir           string `json:"dir"`
	EntriesBefore int    `json:"entries_before"`
	Pruned        int    `json:"pruned"`
	PrunedBytes   int64  `json:"pruned_bytes"`
}

LocalRetentionRecord records local cache retention results.

type ReleaseState

type ReleaseState struct {
	Eligible bool `json:"eligible"`
}

ReleaseState holds release-specific domain metadata. Lifecycle tracking is in Subsystems.

type RetentionState added in v0.5.0

type RetentionState struct {
	Local    *LocalRetentionRecord    `json:"local,omitempty"`
	External *ExternalRetentionRecord `json:"external,omitempty"`
}

RetentionState records cache retention enforcement results. Authoritative — governance and diagnostics can inspect this.

type SecurityState

type SecurityState struct{}

SecurityState holds security-specific domain metadata. Lifecycle tracking is in Subsystems.

type State

type State struct {
	Version    int                    `json:"version"`
	CI         CIState                `json:"ci"`
	Runner     runner.ExecutionReport `json:"runner,omitempty"`
	Build      BuildState             `json:"build"`
	Security   SecurityState          `json:"security"`
	Release    ReleaseState           `json:"release"`
	Subsystems []SubsystemState       `json:"subsystems,omitempty"`
	Retention  RetentionState         `json:"retention,omitempty"`
}

State is the per-run ledger for the current pipeline workspace. Each subsystem records what it did; downstream stages read the ledger instead of probing files.

func ReadState

func ReadState(rootDir string) (*State, error)

ReadState reads pipeline state from the workspace. Returns a zero State (Version: 1) on missing file — missing state is normal when the first subsystem hasn't run yet. Only errors on I/O or parse failures for an existing file.

func (*State) Fact added in v0.8.0

func (st *State) Fact(name string) (string, bool)

Fact resolves a stencil fact token against the run's recorded truth. Facts are the deterministic data half of the stencil language (the author owns the framing):

  • Bare status facts — {status}, {status_icon}, {status_verb} — derive from PipelineStatus and are status-CARRYING: they resolve differently on pass/fail, which is how bodies alternate without conditionals.
  • Dotted facts — {<domain>.<key>} — read a subsystem's recorded Results ({tests.passed}, {security.blocking}, {publish.tags}), plus the lifecycle fields every subsystem carries ({security.outcome}, {security.reason}). {failure.subsystem}/{failure.reason} derive from the first hard-failed subsystem; {retention.pruned} from the retention records.

Resolution semantics honor the naming split: a DOTTED token is fact namespace by construction, so it always resolves (ok=true) — to "" when the domain recorded nothing, which is what makes presence-elision work ("no data → the line vanishes"). A BARE token that isn't a status fact returns ok=false so unknown bare tokens stay visibly literal (typo, or a token for the downstream gitver leaf-pass).

func (*State) GetSubsystem added in v0.5.0

func (st *State) GetSubsystem(name string) *SubsystemState

GetSubsystem returns the subsystem entry by name, or nil if not found.

func (*State) MergeSubsystemResults added in v0.8.0

func (st *State) MergeSubsystemResults(name string, results map[string]string)

MergeSubsystemResults merges fact key→value pairs into a subsystem's Results, creating the entry if the subsystem has no record yet and leaving every lifecycle field untouched. This is the write path for recorded facts ({domain.key} tokens).

func (*State) PipelineStatus added in v0.5.0

func (st *State) PipelineStatus() string

PipelineStatus derives the aggregate pipeline outcome from all subsystems. States: passing, warning, failing, unknown.

Resolution rules (platform-agnostic, policy-aware):

  • Any required subsystem with outcome "failed" → failing
  • Any non-required subsystem with outcome "failed" + allow_failure → warning
  • Any subsystem with outcome "warning" → warning
  • Nothing attempted → unknown
  • Otherwise → passing

func (*State) RecordSubsystem added in v0.5.0

func (st *State) RecordSubsystem(s SubsystemState)

RecordSubsystem upserts a subsystem entry by name. A record carrying no Results preserves the existing entry's Results — lifecycle records (attempted/outcome) and fact records (MergeSubsystemResults) are written by different call sites, and an outcome upsert must not erase the facts a subsystem already recorded.

type SubsystemState added in v0.5.0

type SubsystemState struct {
	Name         string `json:"name"`
	Attempted    bool   `json:"attempted"`
	Completed    bool   `json:"completed"`
	Skipped      bool   `json:"skipped"`
	AllowFailure bool   `json:"allow_failure"` // true = non-vital; failure produces warning, not fail
	Required     bool   `json:"required"`      // true = failure is a hard pipeline fail
	Outcome      string `json:"outcome"`       // success | failed | skipped | warning | not_applicable | cancelled
	Reason       string `json:"reason,omitempty"`
	// Blocking is the CONTROL truth about this subsystem's subject: may that subject
	// continue producing distributable artifacts? A phase that consumes this NEVER
	// branches on anything else. It is deliberately separate from Outcome (what happened)
	// and Required (a forge-status policy). A remediated source is Blocking (the fix lives
	// in Replacement, not in this subject), so it must never build.
	Blocking bool `json:"blocking,omitempty"`
	// Replacement is LINEAGE, never control: the commit that supersedes this subject when a
	// fix was produced (the remediation candidate). Consumed by narrate / publish / the forge
	// status renderer / webhooks — never by the orchestrator's build decision.
	Replacement string `json:"replacement,omitempty"`
	// Contract marks a verdict-about-the-candidate record (e.g. the audition
	// contract) as opposed to work a job performed. Job-end Outcomes rendering
	// excludes contracts — the job's exit already states its verdict.
	Contract bool `json:"contract,omitempty"`
	// Results is the subsystem's recorded FACTS — flat key→value pairs a subsystem
	// computed during the run (test: passed/failed/total/coverage; security:
	// blocking/low/…; publish: tags/registries). Consumed by the stencil fact
	// resolver as {<domain>.<key>} tokens. Riding the subsystem shard keeps facts
	// union-safe across forwarded job artifacts (same guarantee as Outcome). Generic
	// by design: any subsystem (any modality) records facts without a schema change.
	Results map[string]string `json:"results,omitempty"`
}

SubsystemState is the generic lifecycle phase record. All subsystems register here regardless of mode. The resolver uses this list — never hardcoded field names.

func SessionSubsystems added in v0.9.0

func SessionSubsystems() []SubsystemState

SessionSubsystems returns the subsystems recorded by this process.

Jump to

Keyboard shortcuts

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