temper

package
v0.19.0 Latest Latest
Warning

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

Go to latest
Published: May 29, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package temper runs build, lint, and test verification in a worktree.

Temper ("tempering the steel") validates that Claude's changes compile, pass linting, and pass tests before progressing to the Warden review stage. Commands are configurable per-anvil.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ChangedFilesFromGit added in v0.14.0

func ChangedFilesFromGit(ctx context.Context, worktreePath, baseBranch string) ([]string, error)

ChangedFilesFromGit returns the list of changed file paths (relative to the repo root) by running "git diff --name-only <base>..HEAD" in the worktree. It returns an error if git fails or times out, so callers can log a warning and distinguish "no changes" from "couldn't compute changes".

Types

type Config

type Config struct {
	// Steps is the ordered list of verification steps.
	Steps []Step
	// GoRaceDetection is a configuration hint indicating whether Go race
	// detection should be enabled (e.g., by adding a separate "race" step
	// such as 'go test -race -short ./...'). It does not automatically
	// modify Steps; callers are responsible for constructing Steps
	// accordingly (e.g., via DefaultConfigWithRace). Default is false since
	// -race slows tests and increases memory usage.
	GoRaceDetection bool
	// ChangedFiles is the list of file paths changed in the current diff
	// (relative to the repository root). When non-nil, steps with Paths
	// globs are checked against this list and skipped when no files match.
	// A nil value means "unknown" and disables path-based filtering.
	ChangedFiles []string
}

Config holds per-anvil verification configuration.

func ConfigFromCommands added in v0.13.0

func ConfigFromCommands(build, test, lint string, lintRequired bool) *Config

ConfigFromCommands builds a Config from explicit command strings. Each non-empty command becomes a Step. The command string is split on whitespace into the executable and arguments (no shell expansion). If all commands are empty, it returns nil so callers can fall back to auto-detection.

func ConfigFromSteps added in v0.14.0

func ConfigFromSteps(steps []config.TemperStepConfig) *Config

ConfigFromSteps builds a Config from an ordered list of TemperStepConfig entries. Returns nil if the slice is empty so callers can fall back to auto-detection.

func DefaultConfig

func DefaultConfig(worktreePath string, opts *DetectOptions) Config

DefaultConfig returns a default config that auto-detects the project type.

func DefaultConfigWithRace

func DefaultConfigWithRace(worktreePath string, opts *DetectOptions, raceEnabled bool) Config

DefaultConfigWithRace returns a default config with race detection support.

type DetectOptions

type DetectOptions struct {
	// DisableGolangciLint skips the golangci-lint step even if the binary
	// is available. When false (default), golangci-lint is added as an
	// optional step for Go projects if the binary is found on PATH.
	DisableGolangciLint bool
}

DetectOptions controls optional steps during auto-detection.

func DetectOptionsFromAnvilFlag

func DetectOptionsFromAnvilFlag(golangciLint *bool) *DetectOptions

DetectOptionsFromAnvilFlag converts a nullable boolean anvil config flag into DetectOptions. When golangciLint is non-nil and false, golangci-lint is disabled. This centralises the anvil-config → DetectOptions translation so all call sites stay in sync when new detection toggles are added.

type Result

type Result struct {
	// Steps is the ordered list of step results.
	Steps []StepResult
	// Passed is true if all required steps passed (optional steps may have
	// warned without affecting this flag).
	Passed bool
	// Duration is the total time for all steps.
	Duration time.Duration
	// FailedStep is the name of the first failed step, or empty if all passed.
	FailedStep string
	// Summary is a human-readable summary of the verification.
	Summary string
}

Result is the overall Temper verification result.

func Run

func Run(ctx context.Context, worktreePath string, cfg Config, db *state.DB, beadID, anvil string) *Result

Run executes all verification steps in sequence. It stops on the first non-optional failure. db, beadID, and anvil are used to log lifecycle events; db may be nil to skip logging.

type Step

type Step struct {
	// Name identifies the step.
	Name string
	// Command is the shell command to run.
	Command string
	// Args are the command arguments.
	Args []string
	// Dir is the working directory (relative to worktree, or absolute).
	// If empty, runs in the worktree root.
	Dir string
	// Timeout is the maximum duration for this step. Zero means 5 minutes.
	Timeout time.Duration
	// Optional means failure here doesn't fail the overall check.
	Optional bool
	// Paths is a list of glob patterns (doublestar syntax). When non-empty,
	// the step is skipped if no changed files match any pattern.
	Paths []string
	// VerifyClean is a list of pathspecs (relative to the worktree) that must
	// remain clean — meaning unchanged versus HEAD with no untracked files —
	// after the step completes. When the step itself passes but produces
	// changes under any of these paths, the step is converted to a failure
	// with a message explaining that the committed artifacts are stale. Use
	// this for steps that rebuild committed build output (e.g. an embedded
	// frontend bundle): if running `npm run build` mutates the committed
	// dist/ directory, the bundle Smith committed does not match the source.
	VerifyClean []string
	// VerifyNoConflictMarkers is a list of pathspecs (relative to the
	// worktree) that must not contain git merge-conflict markers
	// (`<<<<<<<`, `=======`, `>>>>>>>` — 7+ consecutive marker characters at
	// the start of a line). When set, temper walks the listed paths and
	// fails the step when any marker is found, naming the offending
	// file:line. Unlike VerifyClean this scan does NOT depend on a rebuild
	// — when Command is empty the step becomes a cheap scan-only check that
	// runs unconditionally (no Paths gating). That independence is the
	// point: a rebase Smith can commit conflict markers into a built bundle
	// that the freshness check would miss when the rebuild is skipped or
	// happens to land the same bytes.
	VerifyNoConflictMarkers []string
}

Step defines a verification step to run.

type StepResult

type StepResult struct {
	// Name identifies the step (e.g., "build", "lint", "test").
	Name string
	// Command is the full command that was run.
	Command string
	// ExitCode is the process exit code.
	ExitCode int
	// Output is the combined stdout+stderr.
	Output string
	// Duration is how long the step took.
	Duration time.Duration
	// Passed indicates whether the step succeeded.
	Passed bool
	// Optional mirrors the Step.Optional flag — failure here does not fail
	// the overall check. Surfaced so summaries can render it distinctly.
	Optional bool
	// Skipped is true when the step was skipped because no changed files
	// matched its Paths globs.
	Skipped bool
}

StepResult captures the outcome of a single verification step.

type TemperYAML

type TemperYAML struct {
	GoRaceDetection *bool `yaml:"go_race_detection"`
}

TemperYAML represents the per-anvil .forge/temper.yaml configuration.

func LoadAnvilConfig

func LoadAnvilConfig(anvilPath string) (*TemperYAML, error)

LoadAnvilConfig loads per-anvil temper configuration from .forge/temper.yaml within the given anvil path. Returns (nil, nil) if the file does not exist. Returns a non-nil error for read or parse failures so the caller can decide how to surface it (e.g., log once per change, return structured error).

Jump to

Keyboard shortcuts

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