Documentation
¶
Overview ¶
Package root defines the root configuration for the CLI.
Index ¶
- Constants
- func CodeForError(err error) int
- func LogLevel(verbose, quiet bool) slog.Level
- func NewLogger(w io.Writer, jsonl bool, level slog.Level) *slog.Logger
- func ReasonForError(err error) string
- type Config
- type DryRunUnsupportedError
- type ExitError
- type MissingFlagError
- type MissingOperandError
- type MissingVerbError
- type OperandKind
- type Outcome
- type UnknownVerbError
Constants ¶
const ( StatusOK = "ok" // the command succeeded StatusBlocked = "blocked" // stopped at a human gate or an environment gap, not a failure StatusError = "error" // the command failed )
Outcome status values (SPEC §8): the class of a command's result, the field an agent switches on under --jsonl.
const ( ReasonAtOps = "at_ops" // arc reached the ops ship gate (step) ReasonUngrounded = "ungrounded" // critic routing gap, exit 12 (step) ReasonGate = "gate" // pending human approval, exit 4 (approve) ReasonProof = "proof" // proof verification failed, exit 8 (proof/close) ReasonRequalify = "requalify" // worker changed; requalify before running, exit 9 (§14) ReasonFailed = "failed" // arc failed evaluation past its rework budget (run, §4.1) )
Outcome reason tokens: a stable machine string for a blocked/error outcome, so an agent branches on the token instead of matching prose. Free-form reasons (e.g. a confirmed finding's kind) may also appear; these are the shared ones.
Variables ¶
This section is empty.
Functions ¶
func CodeForError ¶
CodeForError maps a returned error to the process exit code an error outcome reports when the call site set none: a validation error is a usage error (2), everything else is generic (1). Domain gates (4, 5–8, 12) set their own code. This specializes the stub climax generates, reading adh's error taxonomy. It reads the reason rather than adh.ErrorCode directly, so a validation error raised by a library selects the usage code too — the two must not disagree about one error, and only ReasonForError knows both classifications.
func LogLevel ¶
LogLevel resolves the diagnostic log level from the verbosity flags: --quiet shows only errors ("suppress non-error output", SPEC §2), --verbose unlocks debug, and the default shows warnings and errors — the incidental notices. quiet wins when both are set.
func NewLogger ¶
NewLogger builds the stderr diagnostic logger at the given level: a JSON handler under --jsonl, so the log stream is structured like the stdout data plane, or a text handler for humans.
func ReasonForError ¶
ReasonForError is the machine reason token for a returned error: its domain error code (e.g. "not_found", "invalid", "conflict"), or "internal" for an untyped error. It lets an agent branch on the failure class under --jsonl.
Types ¶
type Config ¶
type Config struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
Getenv func(string) string
// Global flags, available on every command via the parent flag chain.
ConfigPath string // --config: override the config file path
Profile string // --profile: named config profile (not yet wired; SPEC §3 tier 3)
Repo string // --repo: target repository root (not yet threaded to the store)
Verbose bool // --verbose: increase log verbosity (no logging subsystem yet)
Quiet bool // --quiet: suppress non-error stdout (wired in cmd.Run)
NoColor bool // --no-color: disable ANSI color (no color output yet)
JSONL bool // --jsonl: emit machine output as JSON Lines (SPEC §8)
Yes bool // --yes: pre-answer non-gate prompts; never satisfies a safety gate (§5.2)
DryRun bool // --dry-run: preview a mutation without persisting (approve/reject/close)
// Log is the diagnostic stream on stderr, separate from the stdout data plane
// (SPEC §8). New seeds a default; Run rebuilds it once the flags are parsed, so
// --verbose/--quiet set the level and --jsonl selects the JSON handler.
Log *slog.Logger
Flags *ff.FlagSet
Command *ff.Command
}
Config holds shared I/O writers, the injected environment accessor, the global flags (SPEC §2, bound once), and the root ff.Command. All subcommand configs embed *Config to inherit these.
func New ¶
New returns a new root Config with the given I/O writers and environment accessor. getenv is injected (not os.Getenv) so config precedence is testable.
func (*Config) ConfigGetenv ¶
ConfigGetenv returns an environment accessor that bridges the config flags into the injected environment: --config overrides ADH_CONFIG (the chosen file) and --profile overrides ADH_PROFILE (the config.Load profile layer, SPEC §3 tier 3); other keys pass through to the injected Getenv. The approval phrase is still never sourced from the environment (§5.2) — ADH_APPROVAL_PHRASE stays ignored.
func (*Config) EmitBlocked ¶
EmitBlocked writes a non-fatal "blocked" Outcome.
type DryRunUnsupportedError ¶
type DryRunUnsupportedError string
DryRunUnsupportedError is the error a command returns when --dry-run is set but the command does not honor it — only approve, reject, and close do — so a global --dry-run never silently mutates state on a command that would otherwise ignore the flag. Its string value is the command name, so the message reads "<cmd>: --dry-run not supported ...". Like ExitError, it is a defined type, so callers return it by conversion (root.DryRunUnsupportedError("run")) rather than a cross-package call; the dispatcher envelopes it like any other command error.
func (DryRunUnsupportedError) Error ¶
func (c DryRunUnsupportedError) Error() string
Error renders the refusal in the standard "<cmd>: <reason>" form (SPEC §8).
func (DryRunUnsupportedError) Unwrap ¶
func (c DryRunUnsupportedError) Unwrap() error
Unwrap gives the refusal a machine-readable code without giving up the defined type, which a test matches with errors.As.
Asking for --dry-run where it is not honored is a malformed invocation, not an adh fault, and with no coded cause in the chain it was reported as reason "internal".
type ExitError ¶
type ExitError int
ExitError is returned by commands that want a specific non-zero exit code without printing an additional error message. run() in main.go checks for ExitError with errors.As and calls os.Exit(int(e)) directly, bypassing the default "error: ..." printer.
type MissingFlagError ¶
type MissingFlagError struct {
Scope string // "harness gate" — the command and verb as the caller typed them
Flags []string // "--candidate", with the dashes, as the caller would type them
}
MissingFlagError is returned by a verb invoked without a flag it requires.
Separate from MissingOperandError because the two are answerable in different ways: an operand is positional and names a thing that has to exist, so a caller — or a test fixture — can go and get one from a kind alone. A required flag names a knob whose value only the caller's intent supplies, and there is no vocabulary of flag values that would mean anything.
It exists so the ratchet over refusals needs no exemptions. Three verbs refused for a missing flag in prose, and every mechanical check of "does this refusal say what it wants" then had to carve them out by name — a list that would go stale the way two others in this repository already did. A type they can carry is cheaper than a list of commands allowed not to.
func (MissingFlagError) Error ¶
func (e MissingFlagError) Error() string
Error renders the refusal in the standard "<cmd>: <reason>" form (SPEC §8).
func (MissingFlagError) Unwrap ¶
func (e MissingFlagError) Unwrap() error
Unwrap gives the refusal a machine-readable code; see MissingVerbError.Unwrap.
type MissingOperandError ¶
type MissingOperandError struct {
Scope string // "arc show" — the command and verb as the caller typed them
Kind OperandKind
}
MissingOperandError is returned by a verb invoked without the positional operand it needs.
**The refusal carries the requirement as data, and that is the whole point.** Twenty-two verbs each wrote this refusal as prose, so the only way to learn that `arc show` wants an arc id was to read the message — and the --jsonl contract test, which enumerates every verb, could not supply an operand it had no way to ask about. Every payload reachable only with one therefore went unasserted, and two commands were printing prose under --jsonl inside that blind spot.
It is the same correction MissingVerbError made one level up, for the same reason: the test used to parse the verb list out of the usage line and silently lost coverage twice when the prose drifted. Prose is not a contract.
A missing operand is a *malformed invocation*, distinct from a well-formed one refused for its state (ECONFLICT) and from a missing flag, which is not positional and has MissingFlagError instead. Only the positional case belongs here.
func (MissingOperandError) Error ¶
func (e MissingOperandError) Error() string
Error renders the refusal in the standard "<cmd>: <reason>" form (SPEC §8).
func (MissingOperandError) Unwrap ¶
func (e MissingOperandError) Unwrap() error
Unwrap gives the refusal a machine-readable code; see MissingVerbError.Unwrap.
type MissingVerbError ¶
MissingVerbError is returned by a command invoked with no verb at all.
A struct rather than a constructor, matching DryRunUnsupportedError: a composite literal is not a cross-package call, so callers return it directly instead of wrapping an error they did not need to wrap. It also means the verb list travels as data, which is what lets one Error method own the wording for every command.
Thirteen commands each wrote this message and its unknown-verb twin by hand, naming their own verb list in both -- and again in the usage line, a third copy the --jsonl contract test parses to decide what to enumerate. The three agree today; nothing made them, and a drift in the usage copy would quietly narrow the contract's coverage rather than fail.
func (MissingVerbError) Error ¶
func (e MissingVerbError) Error() string
Error renders the refusal in the standard "<cmd>: <reason>" form (SPEC §8).
func (MissingVerbError) Unwrap ¶
func (e MissingVerbError) Unwrap() error
Unwrap gives the refusal a machine-readable code.
An invocation with no verb is malformed, not an adh fault. Without a coded cause in the chain, ReasonForError reports "internal" -- the token meaning adh itself broke -- and a caller with a retry-on-internal policy retries what can never succeed. EINVALID also selects the usage exit code in CodeForError, so the code fixes both at once.
type OperandKind ¶
type OperandKind string
OperandKind is the closed vocabulary of positional operands a command can require.
A named type rather than a bare string, for the reason SPEC-ADDITIONS §13.2 gives for reusing adh.FindingKind instead of inventing a second taxonomy: a closed set with one owner can be switched over exhaustively, so a member added later is named by the compiler and the linter rather than forgotten by whoever needed it.
const ( OperandArc OperandKind = "an arc id" OperandUnit OperandKind = "a context unit id" OperandLabel OperandKind = "a label or path to route by" OperandPath OperandKind = "an artifact path" OperandManifest OperandKind = "a proof manifest path" OperandDir OperandKind = "a directory of transcripts" OperandTitle OperandKind = "a title" OperandLevel OperandKind = "a level (L0-L4)" OperandTool OperandKind = "a tool id (see `tool list`)" OperandSpec OperandKind = "an NFR spec id" OperandClass OperandKind = "a lesson class" OperandStaging OperandKind = "a staging id" OperandBranch OperandKind = "a branch name" )
The operand kinds a command can require, and the whole vocabulary of them.
Each value is the noun phrase the refusal reads with, so the wording lives once beside the identifier rather than in each command. A kind names *what must exist* for the operand to be satisfiable — which is what a caller has to go and get, and what a test fixture has to mint — so two commands wanting an arc id share one kind while an artifact path and a transcript directory do not.
type Outcome ¶
type Outcome struct {
Status string `json:"status"` // ok | blocked | error
Code int `json:"code"` // process exit code (0 for ok)
Reason string `json:"reason,omitempty"` // stable machine token (app-defined)
Message string `json:"message,omitempty"` // human-readable detail
Data any `json:"data,omitempty"` // payload on success
}
Outcome is a generic machine-readable result envelope, written as one JSON object per line (JSONL) on stdout.
type UnknownVerbError ¶
UnknownVerbError is returned for a verb the command does not dispatch.
func (UnknownVerbError) Error ¶
func (e UnknownVerbError) Error() string
Error renders the refusal in the standard "<cmd>: <reason>" form (SPEC §8).
func (UnknownVerbError) Unwrap ¶
func (e UnknownVerbError) Unwrap() error
Unwrap gives the refusal a machine-readable code; see MissingVerbError.Unwrap.