governance

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 7, 2026 License: MIT Imports: 34 Imported by: 0

Documentation

Overview

Package governance implements validation rules for GoCell metadata. It checks referential integrity, topological legality, verify closure, format compliance, and advisory warnings across the parsed ProjectMeta.

governance is invoked by `gocell validate` (cmd/gocell/app) and by CI; it is a pure validation tool — no runtime / adapter side effects, no goroutine ownership, no I/O beyond reading filesystem-resident YAML during the parse phase.

Rule numbering: see kernel/governance/CLAUDE.md for the REF / TOPO / VERIFY / FMT / ADV / OUTGUARD series and the ValidationResult schema.

Package governance hosts repository-level audit and validation rules. The helpers in this file expose a single source of truth for "what does git HEAD know about this path" so generatedverify and metricschema cannot drift into divergent definitions of "tracked".

Package governance — select-targets impact analysis. Given a set of changed file paths, TargetSelector computes which slices, cells, contracts, and journeys are potentially affected. This is ADVISORY level — not a completeness guarantee.

Package governance implements validation rules for GoCell metadata. It checks referential integrity, topological legality, verify closure, format compliance, and advisory warnings across the parsed ProjectMeta.

Design ref: kubernetes apimachinery field/errors.go — typed error classification and error accumulation pattern; diverges by using simple string field paths instead of K8s field.Path linked lists.

Index

Constants

View Source
const GoGeneratedPrefix = "// Code generated by gocell generate "

GoGeneratedPrefix is the canonical "// Code generated by gocell generate " prefix that gocell-generated Go files start with. The exact phrasing is the boundary contract enforced by writeGeneratedFile (cmd/gocell/app generate.go) on the producer side and by generatedverify reverse enumeration on the verifier side; both must read this constant rather than re-spelling the literal.

View Source
const YAMLGeneratedPrefix = "# Generated by gocell generate "

YAMLGeneratedPrefix is the YAML-comment counterpart used for boundary.yaml, metrics-schema.yaml, and any future generated YAML artifact.

Variables

This section is empty.

Functions

func CommittedInHEAD

func CommittedInHEAD(ctx context.Context, root, rel string) (bool, error)

CommittedInHEAD reports whether rel is committed in HEAD at root. Files that are only `git add`-ed (in the index but not in HEAD) return false so every committed-in-HEAD audit gate uses a uniform predicate.

rel must be a forward-slash repo-relative path. ExitErrors are interpreted as "not committed" (cat-file -e exits non-zero for unknown refs); other errors propagate so the caller fails closed. ctx.Err() takes precedence over the ExitError mapping so a canceled subprocess never gets folded into "not committed".

func EvalExistingPrefix

func EvalExistingPrefix(p string) string

EvalExistingPrefix resolves symlinks on the longest existing ancestor of p, then appends the non-existent suffix. This handles platforms where intermediate directories are symlinks (e.g., macOS /tmp → /private/tmp).

func HasErrors

func HasErrors(results []ValidationResult) bool

HasErrors returns true if any result has SeverityError.

func HasGitMetadata

func HasGitMetadata(root string) bool

HasGitMetadata reports whether root looks like a git work tree (has a .git entry). Test fixtures that operate on plain temp directories return false so callers can degrade gracefully to content-only checks.

func IsGoCellGenerated

func IsGoCellGenerated(content []byte) bool

IsGoCellGenerated reports whether content carries either of the gocell generator header sentinels. Callers pass the leading bytes of a candidate file.

func IsWithinRoot

func IsWithinRoot(root, target string) bool

IsWithinRoot checks that target resolves to a path inside root. Both sides are normalized to absolute paths, and symlinks are resolved when possible, to prevent both relative-path and symlink-based bypasses.

Exported so cmd/gocell and other callers share a single implementation rather than carrying a duplicate with a hand-maintained `// SYNC:` note.

kernel/metadata.isWithinRoot is a forced duplicate (layering forbids kernel/metadata importing kernel/governance); keep both in sync until the shared-helper extraction tracked in #1255 lands.

func ListGeneratedInHEAD

func ListGeneratedInHEAD(ctx context.Context, root string) ([]string, error)

ListGeneratedInHEAD returns repo-relative paths of every committed file at root whose first line carries a gocell generator header sentinel. Result is filtered to the file extensions generators currently emit (.go, .yml, .yaml); any future generator producing a different extension must extend this filter alongside the generator change so reverse enumeration stays closed.

Implementation uses `git grep` so the entire HEAD tree is scanned in a single subprocess; per-file probing through `git show` would not scale for large work trees.

An empty repository (HEAD does not yet resolve) or "no matches" returns (nil, nil) without error.

Types

type AffectedTargets

type AffectedTargets struct {
	Slices    []string // affected slice IDs ("cellID/sliceID" format)
	Cells     []string // affected cell IDs
	Journeys  []string // affected journey IDs
	Contracts []string // affected contract IDs
}

AffectedTargets holds the impact analysis result.

type Edge

type Edge struct {
	From string
	To   string
}

Edge is a directed dependency between two cells: From depends on To.

type Graph

type Graph struct {
	Nodes []string
	Edges []Edge
}

Graph is the cell-level directed dependency graph produced by Graph(). Nodes and Edges are deterministically sorted (Nodes alphabetically; Edges by From then To) so callers can byte-compare two Graph values.

type IssueType

type IssueType string

IssueType classifies the kind of validation issue. ref: kubernetes apimachinery field/errors.go — typed error classification

const (
	IssueRequired    IssueType = "required"
	IssueInvalid     IssueType = "invalid"
	IssueRefNotFound IssueType = "referenceNotFound"
	IssueMismatch    IssueType = "mismatch"
	IssueForbidden   IssueType = "forbidden"
	IssueDuplicate   IssueType = "duplicate"
)

type Phase

type Phase int

Phase selects which command surface a rule participates in. It replaces the former four separate dispatch lists with one declarative attribute, so the strict/base split is a rule property rather than two rule sets (ADR §6.3 root cause: "FMT strict-only 切两套规则集 ... 被错做规则集合分裂").

const (
	// PhaseBase rules run on every `gocell validate` invocation.
	PhaseBase Phase = iota
	// PhaseStrict rules run only with `gocell validate --strict` (VERIFY-06,
	// FMT-16/17/19, DOC-NAME-01).
	PhaseStrict
	// PhaseDep rules are the cell dependency-graph checks (DEP-01..03). They
	// run alongside base rules on `gocell validate` but are grouped separately
	// because their detect functions consult the cell/contract registries.
	PhaseDep
	// PhaseHealth rules are the contract-health invariants (CH-01..06) run by
	// `gocell check`.
	PhaseHealth
)

type Rule

type Rule struct {
	Code   RuleCode
	Phase  Phase
	Detect func(v *Validator) []ValidationResult
}

Rule is one governance rule expressed as data: classification metadata plus a compiler-checked detect function value. The allRules slice (rules_registry.go) is the sole source of truth; run() iterates it. Detect returns the finished findings (built via the locator constructors). See package doc for the open-source analogs this mirrors.

type RuleCode

type RuleCode string

RuleCode is a named string type that identifies a single governance rule. Exported so cmd/ and tools/ can reference the type when constructing ValidationResult values.

func StrictRuleCodes

func StrictRuleCodes() []RuleCode

StrictRuleCodes returns the codes of the PhaseStrict rules in declaration order — the rules that run only under `gocell validate --strict`. The validate CLI derives its --strict flag help from this, so the help text is a pure projection of the registry and can never drift from the rules that run.

type Severity

type Severity string

Severity of a validation result.

const (
	SeverityError   Severity = "error"   // blocking
	SeverityWarning Severity = "warning" // advisory
)

type TargetSelector

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

TargetSelector computes affected targets from file changes. This is ADVISORY level — not a completeness guarantee.

func NewTargetSelector

func NewTargetSelector(project *metadata.ProjectMeta) *TargetSelector

NewTargetSelector creates a TargetSelector for the given project metadata.

func (*TargetSelector) SelectFromFiles

func (ts *TargetSelector) SelectFromFiles(files []string) *AffectedTargets

SelectFromFiles takes changed file paths (relative to project root) and returns affected targets.

Mapping logic:

  1. File path -> slice (via parsed cell/slice source directories)
  2. Slice -> cell (via belongsToCell)
  3. Slice -> contracts (via contractUsages)
  4. Cell -> journeys (via journey.cells)
  5. journeys/J-*.yaml -> journey -> cells -> slices + contracts
  6. assemblies/{id}/assembly.yaml -> assembly -> cells -> slices

ref: K8s kubectl diff — impact analysis across all resource types

func (*TargetSelector) SelectFromSlice

func (ts *TargetSelector) SelectFromSlice(sliceKey string) *AffectedTargets

SelectFromSlice takes a slice key ("cellID/sliceID") and expands to all affected targets.

type ValidationResult

type ValidationResult struct {
	Code      RuleCode // e.g., codeREF01, codeTOPO03
	Severity  Severity
	IssueType IssueType
	File      string // YAML file path; empty when Scope is set
	Scope     string // virtual scope name; empty when File is set
	Field     string // field path within YAML, e.g. "contractUsages[0].role"
	Message   string
	// Fix is the remediation guidance for a finding ("how to make this rule
	// pass"). It is REQUIRED and non-empty for all findings regardless of
	// severity: both error and warning results carry structured remediation
	// guidance in this typed field. Splitting Fix out of Message replaces the
	// old "; fix:" Message-substring convention (INV-3 Soft anchor) with a
	// typed field: all findings are constructed exclusively through newError /
	// newWarning / newErrorAt / newScopedError, each of which takes fix as a
	// mandatory positional argument (GOVERNANCE-RULE-ERROR-FIX-FIELD-01).
	// Renderers surface it as a distinct "fix:" line / JSON field, never
	// re-concatenated into Message.
	Fix string
	// Line and Column locate the offending value inside File. They are 1-based
	// (matching yaml.v3) and zero when the position is unknown — e.g. the
	// ProjectMeta was constructed without FileNodes, or the field path cannot
	// be resolved (array index out of range, typo in rule code, etc.). They
	// are always zero when Scope is set.
	Line   int
	Column int
}

ValidationResult represents a single validation finding.

File and Scope are mutually exclusive:

  • File identifies a real YAML file; the finding points at a concrete location (File plus Line/Column) that an IDE can open.
  • Scope names a virtual domain ("project", "cross-file", ...) used by checks that inspect relationships across multiple files and therefore cannot pin the issue to a single file position. CLI renderers must avoid showing Scope with a "file:line:col" prefix because doing so would invite users to try (and fail) to jump to it.

func FilterErrors

func FilterErrors(results []ValidationResult) []ValidationResult

FilterErrors returns only error-severity results.

func FilterWarnings

func FilterWarnings(results []ValidationResult) []ValidationResult

FilterWarnings returns only warning-severity results.

type Validator

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

Validator runs all validation rules against a parsed project. It embeds locator to share locate + the typed constructors (newError/newWarning/newScopedError) and to promote the project field so rule code reads v.project.* directly.

Validator is not safe for concurrent ValidateStrict calls. Build one Validator per concurrent caller.

func NewValidator

func NewValidator(project *metadata.ProjectMeta, root string, clk clock.Clock) *Validator

NewValidator creates a Validator for the given parsed project metadata. If project is nil, an empty ProjectMeta is used to avoid nil-pointer panics.

func (*Validator) CheckHealth

func (v *Validator) CheckHealth(ctx context.Context) ([]ValidationResult, error)

CheckHealth runs all PhaseHealth rules (CH-01 through CH-06) against the project loaded into v and returns the combined findings. It is the single entry point for `gocell check contract-health`.

The error return mirrors run(): it is non-nil only when ctx is canceled mid-run, carrying the partial findings so the caller can distinguish a clean run from an interrupted one. Callers must propagate it rather than discard it, so a signal-aware ctx (Ctrl-C during a slow handler-file scan) actually stops the check.

func (*Validator) Graph

func (v *Validator) Graph() (Graph, []ValidationResult)

Graph builds the cell dependency graph from the project metadata and returns it together with any resolution errors encountered during construction. The returned Graph is always fully sorted (Nodes and Edges) for determinism. If resolution errors are present the graph may be incomplete, but it still contains all nodes for cells that were resolved cleanly.

func (*Validator) ValidateStrict

func (v *Validator) ValidateStrict(ctx context.Context, strict, failFast bool) ([]ValidationResult, error)

ValidateStrict is the single entry point for governance validation. strict and failFast are orthogonal flags forming a 2x2 matrix:

  • strict=false, failFast=false → run all base rules, collect every result
  • strict=false, failFast=true → run base rules, stop at the first error
  • strict=true, failFast=false → run base + strict-only rules, collect all
  • strict=true, failFast=true → run base + strict rules, stop on error

The rule pipeline is driven by allRules (rules_registry.go), filtered by phase: base runs PhaseBase+PhaseDep; strict additionally includes PhaseStrict. The engine loop (run) handles ctx cancellation and implements the fail-fast bailout on the first SeverityError. See engine.go for the loop body.

The error return is non-nil only when ctx.Err() != nil at the time the loop is interrupted; it carries the partial findings collected so far so callers can distinguish "clean run" from "interrupted run".

Validator is not safe for concurrent ValidateStrict calls. Build one Validator per concurrent caller — same expectation as the underlying locator and the verifyJourneyRef closure.

ValidateStrict drives the `gocell validate` surface (PhaseBase/Dep/Strict). The contract-health surface (`gocell check`) is a separate entry point, CheckHealth (contracthealth.go), which runs PhaseHealth through the same engine loop.

Jump to

Keyboard shortcuts

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