qualifier

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package qualifier raises a definition of done when there is none to read.

This is the deterministic half: running each proposed criterion against the repository as it stands, before any work, and classifying what came back. Deriving the proposal is a model's job and the operator's signature is a person's; neither is here yet.

Spec: docs/specs/architecture/done-qualifier/202608261730-done-qualifier.*.spec.md

Index

Constants

View Source
const MaxOutput = 2000

MaxOutput is how much of a criterion's output reaches the operator.

It is a constant rather than a setting for now: the configuration key the spec declares arrives with the caller that could pass it, and a key nothing reads is a promise of a control that is not there.

View Source
const MaxSignRounds = 3

MaxSignRounds is how many times a proposal goes back to the operator when their own edit changed what a criterion measures.

Exhausting it REFUSES. A ceiling that accepted the last state on running out is the same defect as a deadline that approves, wearing another name.

A constant rather than a setting for now: the configuration key the spec declares arrives with the caller that could pass it, and a key nothing reads is a promise of a control that is not there.

Variables

View Source
var ErrEmptyProposal = errors.New("qualifier: the proposal declares no criteria")

ErrEmptyProposal is a proposal with no criteria at all.

An error, never an empty DoneSet. The absence of a definition is not a permissive definition — an empty DoneSet means "nothing to verify", which the agent loop reports as done, and that is how a proposal empty of content would become a green report.

View Source
var ErrRefused = errors.New("qualifier: the definition of done was not signed")

ErrRefused is the operator declining, the deadline passing, or the round limit running out.

All three end the turn the same way and none of them starts a loop. The cost of refusing is the operator typing again; the cost of approving on their behalf is an agent working against a ruler nobody read, and a report at the end that says done.

Functions

func Measure

func Measure(ctx context.Context, p Proposal, run loop.CriterionRunner, timeout time.Duration) ([]Measured, Conditions, error)

Measure runs every proposed criterion once against the workspace as it stands, before any work.

It never writes and it never retries. The runner is injected and is the same one the loop uses for a criterion, so what runs here goes through the sandbox exactly as it will later.

func Render added in v0.13.0

func Render(measured []Measured, protected []string, cond Conditions) []byte

Render writes the file a person reviews.

The measurement goes in as comments beside each criterion, because the file IS the review surface: it is diffable, it survives the session that produced it, and it is what the next run reads. A number that only ever appeared on a screen is a number nobody can go back to.

Being what the next run reads is also why a broken criterion is written commented out rather than declared: see the note at the loop below.

func Sign

func Sign(ctx context.Context, measured []Measured, ask Asker, run loop.CriterionRunner, timeout time.Duration) (loop.DoneSet, error)

Sign runs the operator round trip and returns the frozen definition of done.

Criteria the operator edited are measured AGAIN before anything freezes. Without that the edit escapes the rule this package exists to apply: a hand-written, already-green command would enter as acceptance without ever having been red. The fail-first rule is about what will be measured, not about who wrote it.

func Summary added in v0.13.0

func Summary(measured []Measured, cond Conditions, path string) string

Summary is what the loop prints once the proposal has been measured.

It goes to the person, not to the model: the measurement happens after the qualifying turn has ended, so there is nobody left in the turn to correct anything. What it is for is the review — the same numbers as the file, on screen, so the person knows what they are about to read.

Types

type Asker

type Asker func(ctx context.Context, measured []Measured, c Conditions) (SignedAnswer, error)

Asker puts a measured proposal in front of the operator and returns what came back.

The transport is the server's. This is the seam that keeps the qualifier testable without one, and it is the same shape as the criterion runner: the package decides what is asked, not how it travels.

type Class

type Class string

Class is what the run before any work says the criterion IS, whatever the proposer said it would be.

const (
	// ClassAcceptance failed: it can testify that the work happened.
	//
	// This is the rule the whole family is named for. A criterion that already
	// passed cannot testify that the work met it — it would have passed with
	// nothing done, so the green at the end is coincidence rather than
	// evidence. The red-to-green transition is the proof.
	ClassAcceptance Class = "acceptance"
	// ClassRegression passed: it can testify that nothing else broke.
	//
	// Legitimate for the opposite reason. A green suite before any work is
	// exactly what is wanted from a regression guard: its job is to stay green.
	ClassRegression Class = "regression"
	// ClassBroken failed because there was nothing to run.
	//
	// It testifies to nothing and would stay red forever. Without this class it
	// disguises itself as acceptance while measuring the absence of a tool
	// rather than the absence of work.
	ClassBroken Class = "broken"
)

type Conditions

type Conditions struct {
	// NoAcceptance is a set where nothing is red.
	//
	// A warning the operator signs, never a refusal. Such a set will report
	// done without anything having to change, and almost always that is a
	// defect — but not always: a genuine refactor has nothing new to prove and
	// everything to preserve. The harness cannot tell those apart, so it names
	// the condition and does not decide. Deciding here would be the harness
	// choosing what counts as the measurement.
	NoAcceptance bool
}

Conditions are what the whole measured set says about itself.

type Expectation

type Expectation string

Expectation is the proposer's own claim about the state before any work.

const (
	// ExpectFail — an acceptance criterion. The work has not happened, so this
	// has to be red now.
	ExpectFail Expectation = "fail"
	// ExpectPass — a regression guard. It works today and must keep working.
	ExpectPass Expectation = "pass"
)

type Measured

type Measured struct {
	Proposed
	Class Class
	// Exit is what the command actually exited with.
	Exit int
	// Output is capped at MaxOutput, and says so when it was cut. It is the
	// only thing that separates a criterion red because the work is missing
	// from one red because the world is.
	Output string
	// Truncated marks output that did not fit.
	Truncated bool
	// Mismatch is Expects disagreeing with Class. Not an error and not a
	// rejection: it is where the operator's eye should land.
	Mismatch bool
}

Measured is one criterion after the run that happens before any work.

type Proposal

type Proposal struct {
	Criteria  []Proposed
	Protected []string
}

Proposal is a candidate definition of done.

It is a proposal and nothing else: no part of it reaches the loop before a signature. Nothing here is authority.

type Proposed

type Proposed struct {
	// Name is what the report prints.
	Name string
	// Command is what decides. A criterion is a command, never a sentence.
	Command string
	// ExitCode is what counts as met; zero by default.
	ExitCode int
	// Expects is what the PROPOSER says this will do against the repository as
	// it stands, before any work.
	//
	// It decides nothing — the class comes from the run. What it produces is
	// the DISAGREEMENT: said it would fail, and it passed. That is the exact
	// signature of a criterion that does not measure what it should, and
	// without it "criterion 2 passed" is a neutral fact.
	Expects Expectation
	// Why is one line for the human deciding. No machine consumes it.
	Why string
}

Proposed is one candidate criterion.

type SignedAnswer

type SignedAnswer struct {
	Signed    bool
	Criteria  []Proposed
	Protected []string
}

SignedAnswer is what came back from the operator.

Jump to

Keyboard shortcuts

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