Documentation
¶
Overview ¶
Package constitution is a thin exec wrapper around the sibling adr-sourced-constitution primitive's `constitution` binary (spec-lifecycle.md §7, implementation-plan.md §2.7). The seam is a runtime CLI process boundary, never a Go import: this package never imports github.com/kentra-io/adr-sourced-constitution.
Spike §12.2 findings (recorded here, not just in the build report) ¶
`constitution deviation validate <path>` EXISTS exactly as implementation-plan.md §2.7/spec-lifecycle.md §7 item 5 describe it — confirmed by reading adr-sourced-constitution's cmd/constitution/deviation.go, internal/deviation/deviation.go, and its own cmd/constitution/testdata/script/deviation_validate.txtar (tag/commit at the time of this read). No fallback JSON re-implementation (implementation-plan.md §12 item 3's contingency) is needed. The contract, precisely:
- It is a HIDDEN subcommand (`deviation validate`, not top-level) — stable enough to depend on (it is exercised by adr-sourced- constitution's own e2e suite) but intentionally not advertised in `constitution --help`.
- Argument: exactly one positional <path> to a deviation.json file. Absolute or relative — relative is resolved against the process's CURRENT WORKING DIRECTORY, not the path's own location.
- Working-directory precondition: the process must be run with its cwd set to "a constitution project root" — a directory containing both constitution.yml and constitution/adr/. This package always sets exec.Cmd.Dir explicitly (DeviationValidate's root parameter) and always passes an ABSOLUTE path for <path>, so callers never have to reason about relative-path resolution against that directory.
- Exit codes: 0 valid (deviation.json is schema-valid, every adrId cites an ACTIVE, rule-bearing ADR, deviation ids are unique, and the summary tallies) · 1 invalid (schema/citation/tally errors, printed one per line on stderr) · 2 could not run (path unreadable, cwd is not a constitution project root, or the ADR log itself can't be read).
- Output: on success, "deviation validate: <path> is valid" on stdout. A constitutionHash that no longer matches the live constitution/constitution.md is an ADVISORY, not a failure — printed to stderr ("constitutionHash mismatch [HIGH — stale gate]...") regardless of the exit code, including on the valid (0) path. This package folds that advisory into Result.Advisories rather than discarding it.
implementation-plan.md §2.7's spec-lifecycle.md §7.5 constitutionHash recompute does NOT need to shell out at all: Hash reimplements the identical sha256("sha256:"+hex) algorithm adr-sourced-constitution's own internal/deviation.ConstitutionHash uses, over the same constitution/constitution.md path, so lifecycle's own recompute and the value a deviation.json stamps are directly, byte-for-byte comparable without a process round-trip.
Index ¶
Constants ¶
const EnvBinOverride = "LIFECYCLE_CONSTITUTION_BIN"
EnvBinOverride is the documented override mechanism for the constitution binary's location, checked when no explicit override string is passed to Locate (e.g. from a future `--constitution-bin` flag). Set it to an absolute (or PATH-relative) binary path to bypass the default PATH lookup — the mechanism the M3 testscript e2e suite uses to point at a binary built fresh from source rather than requiring the companion primitive be `go install`ed on the machine running the tests.
Variables ¶
This section is empty.
Functions ¶
func Hash ¶
Hash recomputes the sha256 of <root>/constitution/constitution.md, formatted "sha256:<hex>" — byte-identical to the algorithm adr-sourced-constitution's own internal/deviation.ConstitutionHash uses over the same file, so lifecycle's authoritative recompute (approval-state.json's constitutionHash, spec-lifecycle.md §5) and the value a deviation.json stamps (deviationConstitutionHash) are directly comparable without shelling out (doc.go's spike note).
A missing constitution.md is not an error: lifecycle can run in a project before the constitution companion primitive is initialized. Hash then returns ("", false, nil); callers decide how to treat an absent hash (spec-lifecycle.md §5's example always shows a populated constitutionHash, which presumes the companion is already set up).
func HashesEqual ¶
HashesEqual reports whether two constitutionHash values are equal, accepting either the canonical "sha256:<hex>" form or a bare 64-hex digest, case-insensitively on the hex — mirroring adr-sourced-constitution/internal/deviation's own hashMatches/ normalizeHash (unexported there; reimplemented here since the seam is a process boundary, not a Go import — doc.go).
func Locate ¶
Locate resolves the constitution binary's path. Precedence: override (a non-empty argument, reserved for a future CLI flag) wins; else the EnvBinOverride environment variable; else a PATH lookup for "constitution" (the default — the harness installs both primitives side by side, spec-lifecycle.md §7/§9.3). A value containing a path separator is stat'd directly (not looked up on PATH); a bare name is resolved via exec.LookPath so "constitution" and "./bin/constitution" both work as expected.
func MinorPin ¶
MinorPin derives a "<major>.<minor>.x" version pin from a reported version string v (e.g. "0.3.2" -> "0.3.x"; "v1.2.0-rc1 (abcdef)" -> "1.2.x"), for `lifecycle init` to seed lifecycle.yml's `constitution.version` from a freshly detected binary (implementation-plan.md §2.9 step e). ok=false when v isn't a dotted-numeric release build (e.g. a "(devel)" local build) or has fewer than two numeric components — init leaves the pin empty in that case rather than guess (an empty pin is always satisfied, §7 item 5: "presence, not version, is the only hard prerequisite when nothing is pinned").
Types ¶
type DeviationExit ¶
type DeviationExit int
DeviationExit mirrors `constitution deviation validate`'s exit-code contract (doc.go's spike note; ADR-0009 in the companion primitive).
const ( DeviationValid DeviationExit = 0 DeviationInvalid DeviationExit = 1 DeviationCouldNotRun DeviationExit = 2 )
The three exit codes `constitution deviation validate` reports.
type DeviationResult ¶
type DeviationResult struct {
ExitCode DeviationExit
Stdout string
Stderr string
}
DeviationResult is one `constitution deviation validate` invocation's outcome: the exit code plus the captured streams (Stderr carries both the advisory staleness note, printed regardless of exit code, and — on exit 1 — the schema/citation/tally error lines).
func DeviationValidate ¶
func DeviationValidate(bin, root, path string) (DeviationResult, error)
DeviationValidate runs `<bin> deviation validate <path>` with its working directory set to root. Per the verb's own contract (doc.go's spike note), root MUST be "a constitution project root" — a directory containing constitution.yml and constitution/adr/ — and path SHOULD be absolute so it resolves correctly regardless of root (callers are not required to pass an absolute path, but a relative one resolves against root, not the caller's own cwd, exactly like running the command by hand from that directory would).
A non-nil error means the process itself could not be started/run (e.g. bin is not executable) — distinct from DeviationCouldNotRun, which is the verb's OWN exit-2 "could not run" outcome (unreadable path, root isn't a constitution project root, ADR log unreadable) and is reported as a normal (err=nil) DeviationResult so callers can inspect Stderr for why.
func (DeviationResult) Valid ¶
func (r DeviationResult) Valid() bool
Valid reports whether the deviation.json passed (exit 0). A valid result MAY still carry an advisory in Stderr (e.g. a stale constitutionHash) — Valid() does not inspect that.
type Preflight ¶
type Preflight struct {
Path string
Version string
Pin string
// Compatible is only meaningful when Certain is true.
Compatible bool
// Certain is false when Version isn't a plain dotted-numeric release
// build (e.g. a "(devel)"-tagged local build) — in that case
// Compatible can't be evaluated, and the caller should treat this as
// advisory-only (spec-lifecycle.md §7: "version mismatch = warning",
// not a hard refusal — independent release cadences are expected).
Certain bool
// Warning is a human-readable, non-fatal note: either "version could
// not be confirmed" (Certain=false) or "version does not satisfy the
// pin" (Certain=true, Compatible=false). Empty when the pin is
// unset or satisfied.
Warning string
}
Preflight is the outcome of checking a resolved constitution binary against the lifecycle.yml `constitution: { version }` pin (spec-lifecycle.md §7 item 5/§10, implementation-plan.md §2.7).
func CheckVersion ¶
CheckVersion resolves bin's reported version and compares it against pin (lifecycle.yml's constitution.version, e.g. "0.1.x" — an "x" wildcard component matches any value at that position). An empty pin always yields a satisfied, non-Warning result — presence, not version, is the only hard prerequisite when nothing is pinned.