Documentation
¶
Overview ¶
Package evaluation is the deterministic Evaluation stage (SPEC-ADDITIONS §19.2): it adjudicates the cold critic's findings by running the repository artifact each one names, then disposes of the arc. A finding is never trusted on the critic's text — a confirmed one (its artifact ran and failed) returns the arc to Execution and records a failure-registry entry; an unconfirmed one becomes a §11 lesson candidate and does not block. It is shared by `adh eval`, `run`, and `step` so evaluation is deterministic on every path, never a model step.
Index ¶
- Constants
- func Adjudicate(ctx context.Context, adjudicator Adjudicator, specs []nfr.Spec, ...) (critic.Verdict, error)
- func Apply(arc *adh.Arc, verdict *critic.Verdict, recordLessons bool, maxReworks int, ...) error
- func BarHash(bar string) string
- func BarMoved(arc *adh.Arc, current string) bool
- func GuardFindings(specs []nfr.Spec) []adh.Finding
- type Adjudicator
- type CheckRunner
- type Disposition
- type RepoAdjudicator
- type ShellRunner
Constants ¶
const DefaultMaxReworks = 2
DefaultMaxReworks bounds the rework loop (SPEC §4.1, §19.3): the number of times Evaluation may confirm a finding and return an arc to Execution before the arc fails terminally instead of looping. A sensible default beats a config knob no one tunes; a caller that knows better passes its own budget.
Variables ¶
This section is empty.
Functions ¶
func Adjudicate ¶
func Adjudicate( ctx context.Context, adjudicator Adjudicator, specs []nfr.Spec, findings []adh.Finding, ) (critic.Verdict, error)
Adjudicate runs each finding's artifact and disposes of the results (§19.2). It is pure with respect to the arc — the caller applies the verdict.
func Apply ¶
func Apply( arc *adh.Arc, verdict *critic.Verdict, recordLessons bool, maxReworks int, stratum string, ) error
Apply records the verdict and moves the arc on (SPEC §4.1, §19.2): unconfirmed findings become lesson candidates (when recordLessons is set), and the arc's disposition follows Decide — advance to the ops gate, return to Execution to rework (within maxReworks, incrementing Reworks), or fail terminally once the budget is spent. A confirmed finding is appended to the failure registry on both the rework and the terminal path, and every disposed class is stamped into the failure-record log (stratum + scope + root cause) for the §11 accretion gate. It clears the arc's findings. It mutates arc and writes the registries; it neither saves the arc nor sets an exit code — those stay with the caller. stratum is the year-month the shell computes, kept out of the core.
func BarHash ¶
BarHash fingerprints an acceptance bar for pre-registration (§19.2).
Requires: nothing; an empty bar hashes like any other string. Ensures: identity.Hash, the same fingerprint every other artifact in this family carries, so a bar hash is comparable to one written by any other tool. Pure.
func BarMoved ¶
BarMoved reports whether the acceptance bar in force differs from the one the arc was planned against.
Requires: nothing. Ensures: **false when the arc records no bar**, which is an arc planned before pre-registration shipped or one that never reached Strategy. That case is *unknown* and reporting it as drift would fire on every pre-existing arc, which teaches a reader to ignore the report — the failure a check like this most easily causes. Pure.
func GuardFindings ¶
GuardFindings turns every declared guard into a finding to adjudicate (§10.5, §19.2).
Requires: specs is the repository's NFR set; a nil or guard-free set yields none. Ensures: one finding per guard, ordered as declared, each naming its spec so the existing NFR adjudicator measures it. Empty rather than nil. Pure.
**Guards are adjudicated whether or not a critic mentioned them, and that is the whole point.** A critic's findings say what it thought to look at; a guard says what the repository will not trade away regardless. Routing them through the same adjudication path means a breached guard is a *confirmed* finding and blocks by the existing rule — no second gate, no new verdict, and the same evidence requirement every other confirmation meets.
The synthesised summary names the spec rather than describing the breach, because the measurement that would describe it has not happened yet: adjudicateSpec measures the meter and decides. A summary asserting a breach here would be a finding claiming its own conclusion.
Types ¶
type Adjudicator ¶
type Adjudicator interface {
Adjudicate(ctx context.Context, finding adh.Finding) (ran, failed bool, err error)
}
Adjudicator runs the repository artifact a critic finding names and reports whether it could be run and whether it failed (§19.2). It is the point-of-use seam: RepoAdjudicator is the real one; tests inject a fake.
type CheckRunner ¶
type CheckRunner interface {
RunCheck(ctx context.Context, command, dir string) (passed, ran bool)
// Measure runs a Meter tool and returns the numeric value it emits (§10.5), so an
// NFR finding can gate on a declarative Fail threshold rather than an exit code.
// ran is false when the command could not start or emitted no parseable number.
Measure(ctx context.Context, command, dir string) (value float64, ran bool)
}
CheckRunner runs a repository-declared executable check (an NFR constraint, §10) and reports whether it passed (exited zero) and whether it ran at all. ran is false when the check could not start (the command is absent or the context was canceled). There is no error channel: a non-zero exit is a finding confirmation (§19.2), not an error, and an unstartable check is unconfirmed, not a failure of the Evaluation stage. The command is repository-owned config, never model input.
type Disposition ¶
type Disposition int
Disposition is what Evaluation does with an arc given a verdict and the arc's rework history (SPEC §4.1): advance to the ops gate — with or without a recorded reservation — return to Execution to rework, or fail terminally once the rework budget is spent.
const ( AdvanceToOps Disposition = iota // no finding confirmed; on to the ops gate ReturnToExecution // a finding confirmed, within the rework budget Fail // a finding confirmed, rework budget exhausted // AdvanceWithReservation: nothing confirmed, and something measured worse than // its baseline while still clearing the acceptance bar. // // **It advances.** A regression inside the bar is information, not a breach — // blocking on it would make Fail and Baseline the same threshold and would refuse // changes the repository declared acceptable. What it is not is silent: an arc // that advances having cost something says so. // // A disposition rather than a field on AdvanceToOps, because Decide's result is // what the shell branches on to report, and a caller that only ever sees // AdvanceToOps will not think to look for a field. AdvanceWithReservation )
Disposition values (see Disposition).
func Decide ¶
func Decide(verdict *critic.Verdict, reworks, maxReworks int) Disposition
Decide picks the arc's disposition (SPEC §4.1, §19.2): a clean verdict advances to the ops gate; a confirmed structural finding fails terminally at once (a design change the rework loop cannot close — escalate to a human); an ordinary confirmed finding returns the arc to Execution to rework until the budget is spent, after which it fails terminally rather than looping. It is pure — the caller (Apply) mutates the arc.
type RepoAdjudicator ¶
type RepoAdjudicator struct {
// contains filtered or unexported fields
}
RepoAdjudicator runs the concrete repository artifacts a finding can name. An oracle/invariant/device finding runs the declared §13 tool its ref names (the real domain target the repository provides) or, when it names none, adh's built-in check for that kind; a contract finding names a proof manifest verified against it; an NFR finding names a declared check in the tool registry, run by runner. dir is the repository root the checks run in. The zero value is usable (dir defaults to ".", a nil runner makes tool-backed findings unrunnable), so a caller with no registry configured needs no setup.
func NewRepoAdjudicator ¶
func NewRepoAdjudicator( dir string, checks toolreg.Registry, specs []nfr.Spec, runner CheckRunner, ) RepoAdjudicator
NewRepoAdjudicator builds an adjudicator rooted at dir, resolving NFR findings against checks and running them with runner. An empty dir means the current directory; an empty registry or nil runner leaves NFR findings unrunnable.
func RepoAdjudicatorFor ¶
func RepoAdjudicatorFor(repoDir string) (RepoAdjudicator, error)
RepoAdjudicatorFor is the real adjudicator for a repository: it loads the tool registry (§13) that resolves NFR findings and wires the shell runner. A repo with no registry file adjudicates NFR findings as unrunnable, not an error, so the common case needs no setup. It is the one call the `eval`, `run`, and `step` shells make.
func (*RepoAdjudicator) Adjudicate ¶
func (a *RepoAdjudicator) Adjudicate( ctx context.Context, finding adh.Finding, ) (ran, failed bool, err error)
Adjudicate runs the artifact a finding names (§19.2). An oracle/invariant/device finding runs the repository's declared §13 tool when its ref names one (the real domain target), else adh's built-in check for that kind; a contract finding names a proof manifest and fails when the packet is missing or does not verify; an NFR finding names a declared check or Planguage spec and fails when it breaches. A finding whose artifact cannot be run — a declared check the repository does not hold, an unstartable tool — is unrunnable (ran=false) and disposes as unconfirmed, so the gate drops a prior the repository does not hold.
func (*RepoAdjudicator) AdjudicateWhy ¶
func (a *RepoAdjudicator) AdjudicateWhy( ctx context.Context, finding adh.Finding, ) (critic.Adjudicated, error)
AdjudicateWhy is Adjudicate, and also says why an artifact did not run (§19.2).
Requires: nothing. Ensures: Unrunnable is meaningful only when Ran is false, and is UnrunnableToolFailed only for a **registered** tool that could not start — the one case that is evidence about the repository rather than about the finding.
A second method rather than a widened Adjudicate, because Adjudicator is an interface with test implementations and this reason is the RepoAdjudicator's to know: a mock has no registry to have missed.
type ShellRunner ¶
type ShellRunner struct{}
ShellRunner runs a check as `sh -c <command>` in dir through the shared internal/shell edge. RepoAdjudicator holds it behind CheckRunner so tests inject a fake.
func (ShellRunner) Measure ¶
Measure runs a Meter tool capturing its stdout and parses the value it emits (§10.5): the last whitespace-separated float token. The tool's exit code is not the signal — a Meter that exits non-zero but prints a number still measures — so only a command that could not start (not found) or emits no parseable number is unmeasurable (ran=false), leaving the NFR finding unconfirmed rather than a false gate (§19.2).