Documentation
¶
Overview ¶
Package evo is Evident Output: a presentation library for CLI state, progress, evidence, changes, plans, messages, actions, and conclusions.
Application code owns execution. Evo owns presentation.
func main() {
evo.Init(evo.Config{Title: "repo"}) // first statement — arms first paint before any I/O
evo.Main(run)
}
func run() error {
evo.Println("Reading configuration")
evo.Task("working tree").Done()
t := evo.Task("fetch")
output := t.Evidence()
// run.Run(ctx, "git", args, output); t.Fail(..., output.DetailTail()) on error
return nil // Block is a presentation outcome, not a Go error
}
Adoption ladder (guess-driven defaults — the naive spelling is the correct one):
- evo.Init(Config) once in main, before any I/O; evo.Main(run) — dry-run wording, empty-case, and exit codes are all owned; run returns only error.
- Print / Printf / Println / Verbose — start as casually as fmt.
- evo.Task(name) for everything — a check/gate resolved directly (Done/Warn/Block/Fail/Skip, no Doing/Progress call) renders as a fact row; work with Doing/Progress or a mutation verb (Add/Delete/Create/Update/Remove/Write/Push/Record/RecordName) shows a spinner while running — the verb picks [planned] vs [changed] from Config.DryRun; no call site ever flips its own tense. name is a printf format whenever args follow it (evo.Task("build %s", ref)); no args leaves name untouched.
- evo.Task(name).Each(items) for loop progress (absolute, never double-counted). Each takes []string (the item name becomes the live doing-text); for any other slice type, drive the same absolute progress with EachN(len(items)) — no []string copy needed just to get a progress bar. .Writer() as cmd.Stdout so a talkative child's last line becomes the live doing-text; Task.Run(cmd) wires an *exec.Cmd through that same capture/doing-text plumbing in one call and hands back the subprocess error verbatim for the caller to resolve. An item that fails inside the loop body resolves on the loop's own task handle (task.Fail(...); break) — never a second evo.Task declared per item — leaving Progress sealed at the count already reached (release-gate round 6 finding 7).
- evo.Task(name).Skipped(evo.Reason("..."), name) / .Kept(evo.Reason("..."), name) — taxonomy counted and summed, never a bare "skipped N". evo.Reason(name) is a get-or-create lookup on the default instance: the same string at every call site merges into one bucket, so an inline evo.Reason("protected") is always legal — lifting it to a package var is optional, never required for correctness. Individual names render under Config.Verbosity: VerbosityVerbose (see doc there); at the default VerbosityNormal the human line stays the aggregated "! skipped N (...)" count. The names are never dropped — they always live on TaskSnapshot.Skipped/Kept (Output.Snapshot / TaskHandle.Snapshot); the wire JSON document does not carry them.
- evo.Confirm(question, ...) — owns the whole ask-decide-resolve gate (prompt, quiesce, Done/Blocked resolution, exit code). question is verbatim text, not a printf format like Task/Sequence/Reason/Doing/Skip's text — use fmt.Sprintf to build a dynamic question first. Confirm is the one entity-text spelling that stays non-printf (release-gate round 6 finding 4).
- evo.Sequence(name) for named children with derived, auto-lifecycle state.
- task.Fail(summary) / task.Block(summary) are statements — no return value, so a bare call is errcheck-clean. `return task.Failf("schema mismatch: %w", err)` (task declared as evo.Task("validate manifest")) builds and returns one error in a single line: a trailing ": %w"/", %w" splits the formatted text into the rendered summary and an evidence line for the wrapped error; Blockf is the same for Block. The summary states WHAT went wrong, not the task's own name again — the rendered row already carries the task label, so a summary of "validate manifest: %w" would just repeat it back. Warn, and success/skip verbs, stay void too — this is never fluent chaining. Done/Warn/Task/Sequence/Reason/Doing/Skip are printf-variadic themselves (fmt.Sprintf semantics when args follow); there is no separate Donef/Warnf/Taskf/Reasonf/Doingf/ Skipf (C6). Output.Failf stays void rather than mirroring TaskHandle.Failf's *Failure return (release-gate round 4 finding 5): every call site uses it as a bare statement, a returned error would fail errcheck at each of them with no lint-config exception on this repo, and there is no per-call Next chain for an output-level failure to attach to the way TaskHandle.Failf's *Failure attaches to its task (Output.Next already covers the output-level case). Documented asymmetry, not an oversight.
- Config{Debug: evo.DebugConfig{Level: evo.LevelDebug}} selects the journal threshold for Debug/Capture mirrors and the slog bridge. evo.LogLevel is its own type, distinct from stdlib slog.Level — SlogHandler translates between the two internally, but Config.Debug.Level itself never takes a slog.Level value. LevelUnset (the zero value) resolves to LevelInfo; LevelTrace/LevelDebug are the two levels that surface Debug journal lines. Package-level evo.SlogHandler() journals to the default instance, the same default-instance sugar evo.Task/evo.Verbose already offer.
Ordinary surface: evo.Init/evo.Main, Print*, evo.Task/evo.Sequence (+ ID), Task.Evidence, Task.Each / Task.Writer / Task.Run, Task.Fail / Task.Failf / Task.Block / Task.Blockf, evo.Confirm, evo.Reason, Changes/Plan (tooling call sites, see below), slog via SlogHandler (level from Config.Debug.Level).
Advanced surface, for testing and tooling call sites that need a hosted instance instead of the package-level default: Config.Isolated returns an independent *Output that never touches package state; Output.Run(run func(*Output) error) seals it (the hosted counterpart of Main's run func() error, called on the *Output itself instead of the default instance); Config.Options is the raw-Option escape hatch for exact writer/ terminal/clock wiring. Plan/Changes for the would/did split without a Task, session Evidence, terminal drivers, and testkit.
Index ¶
- Constants
- Variables
- func Confirm(question string, opts ...ConfirmOption) bool
- func Delay(d time.Duration) *time.Duration
- func EncodeJSON(s Snapshot) ([]byte, error)
- func EncodeJSONL(events []Event) ([]byte, error)
- func Fact(name, value string)
- func IsCharDevice(w io.Writer) bool
- func Main(run func() error)
- func MainWith(out *Output, run func(*Output) error)
- func Pluralize(quantity int64, singular string) string
- func Print(args ...any)
- func Printf(format string, args ...any)
- func Println(args ...any)
- func RenderPlain(s Snapshot, opts PlainOptions) ([]byte, error)
- func Run(run func() error) int
- func SetDefault(out *Output)
- func SlogHandler() slog.Handler
- func TruncateNames(names []string, visible int, profile ...GlyphProfile) string
- func Warn(summary string, args ...any)
- type Action
- type Attachment
- type ChangesSnapshot
- type ColorMode
- type CommandSpec
- type Conclusion
- type ConclusionJSON
- type ConclusionState
- type Config
- type ConfirmOption
- type DebugConfig
- type DebugPaneOption
- type DebugPresentation
- type DisplayGroup
- func (g *DisplayGroup) DisplayGroup(name string, args ...any) *DisplayGroup
- func (g *DisplayGroup) Sequence(name string, args ...any) *SequenceHandle
- func (g *DisplayGroup) Snapshot() TasksSnapshot
- func (g *DisplayGroup) Summary(text string, args ...any) *DisplayGroup
- func (g *DisplayGroup) Task(name string, args ...any) *TaskHandle
- type EffectOption
- type EffectRecord
- type EntityOption
- type EntityState
- type Event
- type EventJSON
- type Evidence
- type EvidenceOption
- type EvidenceStream
- type FactRecord
- type Failure
- type Field
- type FixedClock
- type Format
- type GlyphProfile
- type JSONAction
- type JSONChanges
- type JSONCollection
- type JSONCommand
- type JSONDocument
- type JSONEffectRecord
- type JSONMessage
- type JSONOutputMeta
- type JSONPlan
- type JSONProblem
- type JSONProgress
- type JSONTask
- type LiveSurface
- type LogLevel
- type LogRecord
- type MessageSnapshot
- type NoopRedactor
- type Option
- func AlsoWrite(w io.Writer) Option
- func Clock(ts TimeSource) Option
- func DataProjection() Option
- func DebugAddSource() Option
- func DebugHistory() Option
- func DebugLevel(level LogLevel) Option
- func DebugPane(opts ...DebugPaneOption) Option
- func Diagnostics(w io.Writer) Option
- func DryRun() Option
- func ExternalProjection() Option
- func Glyphs(p GlyphProfile) Option
- func MaxEntities(n int) Option
- func MaxEvents(n int) Option
- func MaxFrameRate(framesPerSecond int) Option
- func NoColor() Option
- func Plain() Option
- func Redact(r Redactor) Option
- func ResultStream(w io.Writer) Option
- func Stdin(r io.Reader) Option
- func Strict() Option
- func Terminal(driver TerminalDriver) Option
- func Title(subject string) Option
- func To(w io.Writer) Option
- func VisibilityDelay(delay time.Duration) Option
- func Width(columns int) Option
- type Output
- func (o *Output) At(visibility Visibility) *Printer
- func (o *Output) Cancel(reason string)
- func (o *Output) Close() error
- func (o *Output) Conclusion() Conclusion
- func (o *Output) Confirm(question string, opts ...ConfirmOption) bool
- func (o *Output) Debug(message string, fields ...Field)
- func (o *Output) DebugWriter() io.WriteCloser
- func (o *Output) DeclareDryRun()
- func (o *Output) DisplayGroup(name string, args ...any) *DisplayGroup
- func (o *Output) Err() error
- func (o *Output) Events() []Event
- func (o *Output) Evidence(opts ...EvidenceOption) *Evidence
- func (o *Output) Fact(name, value string)
- func (o *Output) Fail(summary string, options ...ProblemOption)
- func (o *Output) Failf(format string, args ...any)
- func (o *Output) Finish() error
- func (o *Output) Next(actions ...Action)
- func (o *Output) NextCommand(executable string, args ...string)
- func (o *Output) Print(args ...any)
- func (o *Output) Printf(format string, args ...any)
- func (o *Output) Println(args ...any)
- func (o *Output) ResultWriter() io.Writer
- func (o *Output) Run(run func(*Output) error) int
- func (o *Output) Scope(name string) *Scope
- func (o *Output) Sequence(name string, args ...any) *SequenceHandle
- func (o *Output) SlogHandler() slog.Handler
- func (o *Output) Snapshot() Snapshot
- func (o *Output) Subject(text string)
- func (o *Output) Suspend(fn func() error) error
- func (o *Output) Task(name string, args ...any) *TaskHandle
- func (o *Output) Warn(summary string, args ...any)
- func (o *Output) Writer() io.Writer
- type PlainOptions
- type PlanSnapshot
- type Printer
- type Problem
- type ProblemOption
- func Code(value string) ProblemOption
- func Count(value int64, unit ...string) ProblemOption
- func Detail(text string) ProblemOption
- func Location(path string, line, column int) ProblemOption
- func Next(action Action) ProblemOption
- func NextCommand(executable string, args ...string) ProblemOption
- func On(subject string) ProblemOption
- type Progress
- type ProgressKind
- type ReasonOption
- type Redactor
- type Scope
- type SequenceHandle
- func (g *SequenceHandle) DisplayGroup(name string, args ...any) *DisplayGroup
- func (g *SequenceHandle) Sequence(name string, args ...any) *SequenceHandle
- func (g *SequenceHandle) Snapshot() TasksSnapshot
- func (g *SequenceHandle) Summary(text string, args ...any) *SequenceHandle
- func (g *SequenceHandle) Task(name string, args ...any) *TaskHandle
- type Snapshot
- type SourceLocation
- type SystemClock
- type TaskHandle
- func (t *TaskHandle) Add(object string, call func() error, opts ...EffectOption) error
- func (t *TaskHandle) Block(summary string, options ...ProblemOption)
- func (t *TaskHandle) Blockf(format string, args ...any) *Failure
- func (t *TaskHandle) Bytes(completed, total int64) *TaskHandle
- func (t *TaskHandle) Cancel(reason string) *TaskHandle
- func (t *TaskHandle) Create(object string, call func() error, opts ...EffectOption) error
- func (t *TaskHandle) Delete(object string, call func() error, opts ...EffectOption) error
- func (t *TaskHandle) Doing(text string, args ...any) *TaskHandle
- func (t *TaskHandle) Done(args ...any) *TaskHandle
- func (t *TaskHandle) Each(items []string) iter.Seq[string]
- func (t *TaskHandle) EachN(n int) iter.Seq[int]
- func (t *TaskHandle) Evidence(opts ...EvidenceOption) *Evidence
- func (t *TaskHandle) Fact(name, value string)
- func (t *TaskHandle) Fail(summary string, options ...ProblemOption)
- func (t *TaskHandle) Failf(format string, args ...any) *Failure
- func (t *TaskHandle) Kept(reason TaxonomyReason, name string, errs ...error)
- func (t *TaskHandle) Next(actions ...Action) *TaskHandle
- func (t *TaskHandle) NextCommand(executable string, args ...string) *TaskHandle
- func (t *TaskHandle) NextSelf(args ...string) *TaskHandle
- func (t *TaskHandle) Progress(completed, total int) *TaskHandle
- func (t *TaskHandle) Push(object string, call func() error, opts ...EffectOption) error
- func (t *TaskHandle) Record(verb string, quantity int, object string)
- func (t *TaskHandle) RecordLabel(label string, quantity int, object string)
- func (t *TaskHandle) RecordName(verb, object string)
- func (t *TaskHandle) Remove(object string, call func() error, opts ...EffectOption) error
- func (t *TaskHandle) Run(cmd *exec.Cmd) error
- func (t *TaskHandle) Skip(reason string, args ...any) *TaskHandle
- func (t *TaskHandle) Skipped(reason TaxonomyReason, name string, errs ...error)
- func (t *TaskHandle) Snapshot() TaskSnapshot
- func (t *TaskHandle) Step(completed, total int, name string) *TaskHandle
- func (t *TaskHandle) Update(object string, call func() error, opts ...EffectOption) error
- func (t *TaskHandle) Warn(summary string, args ...any)
- func (t *TaskHandle) Write(object string, call func() error, opts ...EffectOption) error
- func (t *TaskHandle) Writer() io.Writer
- type TaskSnapshot
- type TasksSnapshot
- type TaxonomyReason
- type TaxonomyRecord
- type TerminalDriver
- type TimeSource
- type Verbosity
- type Visibility
Constants ¶
const ( ExitOK = core.ExitOK ExitBlocked = core.ExitBlocked ExitFailed = core.ExitFailed ExitCancelled = core.ExitCancelled )
Default exit codes from architecture §26.
const ( // GlyphsAuto detects the vocabulary from locale and TTY interactivity. GlyphsAuto = txt.GlyphsAuto // GlyphsUnicode forces the Unicode vocabulary regardless of locale. GlyphsUnicode = txt.GlyphsUnicode // GlyphsASCII forces the ASCII vocabulary regardless of locale. GlyphsASCII = txt.GlyphsASCII )
const ( // VisibilityNormal messages always project at VerbosityNormal (C11: // prefixed consistently with VisibilityVerbose — the two enum members // previously disagreed on their own naming convention). VisibilityNormal = core.VisibilityNormal // VisibilityVerbose messages project only when Config.Verbosity is VerbosityVerbose. VisibilityVerbose = core.VisibilityVerbose )
const ( Pending = core.Pending Running = core.Running Done = core.Done Blocked = core.Blocked Failed = core.Failed Skipped = core.Skipped Cancelled = core.Cancelled Empty = core.Empty Incomplete = core.Incomplete // NotStarted marks a group task that never ran because an earlier sibling // already failed or was cancelled — rendered "- <name> not started" and // excluded from the conclusion (the group's verdict comes from the // failed/cancelled sibling, not from its unstarted followers). NotStarted = core.NotStarted )
EntityState values — see the type doc comment above for the naming rationale (no State* prefix on this block).
const ( StateReady = core.StateReady StateChanged = core.StateChanged StateWarning = core.StateWarning StateBlocked = core.StateBlocked StateFailed = core.StateFailed StateCancelled = core.StateCancelled StatePlanned = core.StatePlanned )
ConclusionState values — the trailing "[state]" band a run can end in.
const ( Indeterminate = core.Indeterminate Determinate = core.Determinate BytesKind = core.BytesKind )
ProgressKind values — which measurement a task's Progress reports.
const DefaultVisibleNames = txt.DefaultVisibleNames
DefaultVisibleNames is how many names TruncateNames keeps before summarizing.
const EventSchemaVersion = core.EventSchemaVersion
EventSchemaVersion is the durable event schema version. Tracks the 0.2 contract series (pre-1.0 wire format may still evolve).
const JSONSchemaVersion = render.JSONSchemaVersion
JSONSchemaVersion is the final JSON document schema version. Tracks the 0.3 contract series (pre-1.0 wire format may still evolve). Bumped from 0.2: "items" no longer exists as a separate wire kind — the item/task fold means every entity (including a fact-check resolved without ever running) is a "tasks" row (CHANGELOG "Unreleased").
const PublishedRelease = "v0.4.6"
PublishedRelease is the single source of truth for the current published module and MCP pin used in install guidance.
Maintenance class this protects (v0.2.10 hygiene, generalized):
- skills / integrations / README disagree on which tag to install
- MCP config generator falls back to a stale hardcoded tag
- portable docs recommend @latest or a personal-machine clone path
- signed tags ship with stale README pins (next patch, never rewrite history)
When cutting a release:
- Set PublishedRelease to the new tag (e.g. "v0.2.11").
- Run: go run ./scripts/sync-release-pins
- Run: go test . -run VersionDrift
- Tag that commit; do not move prior tags.
version_drift_test.go enforces the portable surface stays synchronized.
Variables ¶
var ( ErrClosed = errors.New("evo: output is closed") ErrAlreadyResolved = errors.New("evo: entity is already resolved") ErrUnresolvedTask = errors.New("evo: task has no final state") ErrInvalidProgress = errors.New("evo: invalid progress") ErrProgressRegression = errors.New("evo: progress moved backward") ErrDuplicateKey = errors.New("evo: duplicate entity key") ErrInvalidConfig = errors.New("evo: invalid configuration") ErrRenderer = errors.New("evo: renderer failure") ErrLimitExceeded = errors.New("evo: resource limit exceeded") ErrReasonSkipOnly = errors.New("evo: reason restricted to Skipped was recorded via Kept") ErrReasonWrongTask = errors.New("evo: reason restricted to another task") ErrConcurrentRunning = errors.New("evo: two siblings in the same collection are Running simultaneously") ErrDryRunDeclaredLate = errors.New("evo: DeclareDryRun called after a durable row was already emitted") // ErrTerminalWithoutSink is recorded when Config.Options supplies a // Terminal driver but no primary writer (To), and the driver cannot // report its own destination (it does not implement the Sink() io.Writer // accessor) — release-gate round 8 finding 2. Without either, a // non-interactive Finish has nowhere to write the residual/plain // projection and would otherwise render nothing at exit 0. ErrTerminalWithoutSink = errors.New("evo: Terminal driver configured without a primary writer") )
Sentinel misuse and lifecycle errors recorded by the output aggregate.
Functions ¶
func Confirm ¶ added in v0.3.0
func Confirm(question string, opts ...ConfirmOption) bool
Confirm asks a yes/no question on the default instance. See Output.Confirm.
func Delay ¶ added in v0.2.7
Delay returns a non-nil *time.Duration for Config fields where zero is meaningful.
cfg.VisibilityDelay = evo.Delay(0) // immediate cfg.VisibilityDelay = evo.Delay(80 * time.Millisecond) // explicit default
func EncodeJSON ¶
EncodeJSON encodes a snapshot as final JSON (§25.1 / §25.4).
func EncodeJSONL ¶
EncodeJSONL encodes durable events as JSON Lines (§25.2 / §25.4).
func Fact ¶ added in v0.4.0
func Fact(name, value string)
Fact records a discovered name/value annotation on the default instance's run itself, not on any one task — evo.Fact's package-level form. See Output.Fact.
func IsCharDevice ¶
IsCharDevice reports whether w is an *os.File backed by a character device (typical interactive TTY). Pipes, files, and non-file writers return false.
Prefer Config{Stdout, Stderr} for ordinary dual-stream construction. This helper remains for hosts that choose Plain/NoColor from a concrete writer.
func Main ¶
func Main(run func() error)
Main runs a CLI presentation lifecycle against the package-level default instance (see Init) and exits the process with the resulting code via the exitProcess facade — the library still never calls os.Exit directly (API-018); Main is the sole sanctioned path to it.
func main() {
evo.Init(evo.Config{Title: "tool"})
evo.Main(run)
}
Callers that need the exit code without exiting (tests, or composing a larger CLI) call Run instead.
func MainWith ¶ added in v0.4.0
MainWith runs a CLI presentation lifecycle against a caller-held *Output (evo.Init(evo.Config{Isolated: true})) and exits the process with the resulting code via the exitProcess facade — the Isolated-instance counterpart of Main, for a caller holding its own *Output. Output.Run stays the non-exiting form Main/MainWith are both built on.
func main() {
out := evo.Init(evo.Config{Title: "tool", Isolated: true})
evo.MainWith(out, run)
}
func Pluralize ¶ added in v0.3.0
Pluralize returns the plural spelling of singular when quantity != 1 (an irregular table for the common exceptions, else the regular English +s/+es/+ies rule), and singular unchanged when quantity == 1 — the object-pluralization counterpart to the ledger's verb tense, so a mutation call site stops writing its own singular/plural noun() switch:
worktrees.Remove(n, evo.Pluralize(n, "worktree")) // "1 worktree" / "2 worktrees"
A glob/path/symbol object ("stale origin/*", "*.tmp") renders unchanged at any quantity instead of blindly gaining a trailing "s" ("stale origin/*s") the +s/+es/+ies rule was never designed to produce.
func Print ¶ added in v0.3.0
func Print(args ...any)
Print formats like fmt.Sprint and enqueues human-facing text on the default instance.
func Printf ¶ added in v0.3.0
Printf formats like fmt.Sprintf and enqueues human-facing text on the default instance.
func Println ¶ added in v0.3.0
func Println(args ...any)
Println formats like fmt.Sprintln and enqueues a complete line on the default instance.
func RenderPlain ¶
func RenderPlain(s Snapshot, opts PlainOptions) ([]byte, error)
RenderPlain projects a snapshot to plain text without terminal ownership.
func Run ¶ added in v0.4.0
Run executes a CLI presentation lifecycle against the package-level default instance (see Init) and returns the process exit code, never exiting — the package-level counterpart of Output.Run, for callers (tests, or a caller composing its own exit path) that need the code without Main's os.Exit.
run reports only an error; the Conclusion (0/1/2/130) is the sole source of the exit code — see Output.Run for the full lifecycle and signal contract.
func SetDefault ¶ added in v0.3.0
func SetDefault(out *Output)
SetDefault installs out as the package-level default Output.
func SlogHandler ¶ added in v0.3.0
SlogHandler returns a slog.Handler journaling to the default instance — package-level sugar (release-gate round 8 finding 6) matching Task/Verbose: a caller using the default-instance facade throughout a run should never have to reach for a hosted *Output just for the slog bridge. See Output.SlogHandler for the level policy and full contract.
func TruncateNames ¶ added in v0.2.16
func TruncateNames(names []string, visible int, profile ...GlyphProfile) string
TruncateNames joins names for a skip/kept-style summary. Empty names yields "". visible <= 0 uses DefaultVisibleNames. When more names remain than visible, appends the overflow glyph for profile (evo-rec.md's tightened vocabulary: "… +N more", ASCII "... +N more") instead of a bare ", +N" that carries no glyph at all. profile is variadic so the simplest call — TruncateNames(names, visible) — stays correct: an omitted profile renders the Unicode overflow glyph; a caller that has already resolved a GlyphProfile passes it explicitly.
Types ¶
type Action ¶
Action is a recommended next step for the user.
Aliased into internal/core alongside the rest of the data model — see Snapshot's doc comment (snapshot.go) for why.
type Attachment ¶ added in v0.3.0
type Attachment = core.Attachment
Attachment is an additional label/value problem attachment.
Named Attachment (not Evidence) because Evidence names the retained process-output sink (see Evidence in capture.go) — this is a single labeled fact attached to a Problem, a different concept from that sink.
type ChangesSnapshot ¶
type ChangesSnapshot = core.ChangesSnapshot
ChangesSnapshot is an immutable changes section.
type ColorMode ¶ added in v0.2.0
type ColorMode int
ColorMode selects color policy. The zero value is automatic.
type CommandSpec ¶
type CommandSpec = core.CommandSpec
CommandSpec is an executable plus argv (never a shell string).
type Conclusion ¶
type Conclusion = core.Conclusion
Conclusion is the multidimensional meaning of a finished command.
Aliased into internal/core alongside the rest of the data model — see Snapshot's doc comment (snapshot.go) for why, and EVIDENT_OUTPUT_ARCHITECTURE_SPEC_v0.5.md §38 for the full layout.
type ConclusionJSON ¶
type ConclusionJSON = render.ConclusionJSON
ConclusionJSON is JSON-friendly conclusion.
type ConclusionState ¶
type ConclusionState = core.ConclusionState
ConclusionState is the human headline for a finished output.
type Config ¶
type Config struct {
// Title is the subject shown in the conclusion (formerly For's argument).
Title string
// Subject is an optional durable line rendered once, immediately, right
// under the title — a repo path, a target host, the thing every
// projection needs the reader to see up front. Set it once in Config
// instead of calling out.Println(root) (or whatever the identifying
// value is) at every projection/command that needs to show it.
//
// When the subject text isn't known until after Init (e.g. resolved
// from a flag parsed later, but still before any other I/O), call
// Output.Subject(text) instead — same one-shot durable-line semantics,
// as a post-construction setter (I3).
Subject string
// Stdout is the ordinary human stream (default os.Stdout).
// In FormatData mode, Stdout is reserved for domain payload via ResultWriter;
// human presentation moves to Stderr.
Stdout io.Writer
// Stderr owns diagnostics by default (default os.Stderr).
Stderr io.Writer
// Result is an optional domain-payload writer. When nil and Format is
// FormatData, ResultWriter returns Stdout. Presentation never writes here.
Result io.Writer
// Stdin is the facade Confirm reads one answer line from (default os.Stdin).
Stdin io.Reader
// Verbosity gates Verbose() print messages (default VerbosityNormal).
Verbosity Verbosity
// Color policy (default ColorAuto).
Color ColorMode
// Format projection mode (default FormatHuman).
Format Format
// Debug configures the debug journal.
Debug DebugConfig
// Advanced (optional) — zero values inherit safe defaults.
Clock TimeSource
Redactor Redactor
Terminal TerminalDriver
Strict bool
Width int
// VisibilityDelay is the wait before the first live paint.
// nil means default (80ms). Non-nil is exact, including 0 for immediate.
// Use evo.Delay(d) to set a value from a duration literal.
VisibilityDelay *time.Duration
MaxFrameRate int
MaxEntities int
MaxEvents int
// Plain disables live interactive frames, on a TTY or off (C3: replaces
// the former separate ForcePlain and NonInteractive fields — every read
// site combined them with OR, so there was never a distinct behavior
// between the two to preserve).
Plain bool
// Glyphs selects the state-glyph vocabulary (default GlyphsAuto: Unicode
// off a TTY or on a UTF-8 locale, ASCII on a non-UTF-8 interactive TTY).
Glyphs GlyphProfile
// FailedExitCode is the process exit code when the conclusion is failed.
// Zero means use ExitFailed (2). Set to 1 for conventional CLI tools that
// treat any non-zero failure as exit 1 (e.g. quality gates / git hooks).
FailedExitCode int
// DryRun declares this run a dry run once, for the whole process: every
// TaskHandle mutation verb (Delete, Create, Update, Remove, Write, Push,
// Record, RecordName) renders as a [planned] row with the imperative verb
// instead of a [changed] row with the past-tense verb. No call site writes
// its own tense.
DryRun bool
// Isolated returns an independent Output that never touches package
// state: it is not installed as the package-level default and does not
// arm first paint. Use for parallel tests and embedders that hold their
// own *Output instead of going through Default()/Task()/Print() et al.
// This is the one and only opt-out from default installation — it
// applies identically whether or not Options is also set (release-gate
// round 8 finding 1): Options is an orthogonal escape hatch for how the
// Output is built, not for whether it becomes the default.
Isolated bool
// Options is the advanced, raw Option escape hatch for tests and
// specialized embedding (custom Terminal, Clock, exact writer wiring)
// that need to bypass Config's ordinary stream/TTY/color inference
// entirely. When set, every other Config field except Title, DryRun, and
// Subject is ignored and the Output is built from these Options alone.
// It still installs as the package-level default and arms first paint
// exactly like every other Init call, unless Isolated is also set —
// see Isolated and Init.
Options []Option
}
Config is the sole application-facing construction surface.
Zero values mean automatic/default behavior. Use DefaultConfig() when you need a mutable baseline for advanced fields.
out := evo.Init()
out := evo.Init(evo.Config{Title: "bpp-csharp"})
cfg := evo.DefaultConfig(); cfg.Title = "x"; out := evo.Init(cfg)
func DefaultConfig ¶ added in v0.2.0
func DefaultConfig() Config
DefaultConfig returns a fresh ordinary CLI configuration. Mutating the result does not affect later DefaultConfig() calls.
type ConfirmOption ¶ added in v0.3.0
type ConfirmOption func(*confirmConfig)
ConfirmOption configures a Confirm gate.
func AssumeYes ¶ added in v0.3.0
func AssumeYes(v bool) ConfirmOption
AssumeYes skips the interactive prompt when v is true (the caller's --yes flag). The gate resolves immediately as Done "assumed --yes", and Confirm returns true without touching stdin or the live region.
func ConfirmDetail ¶ added in v0.3.0
func ConfirmDetail(lines ...string) ConfirmOption
ConfirmDetail attaches one or more context lines rendered under the "? <question> y/N" prompt line — e.g. what will actually happen, which host is affected. Repeated calls append.
func Destructive ¶ added in v0.3.0
func Destructive() ConfirmOption
Destructive annotates the rendered prompt line as "(destructive)".
func PolicyFlag ¶ added in v0.3.0
func PolicyFlag(flag string) ConfirmOption
PolicyFlag sets the non-interactive policy hint to a flag on the caller's own executable (e.g. "--apply") instead of PolicyHint's explicit foreign- command spelling — the common case where the flag that unblocks a non-interactive run belongs to the very binary calling Confirm. The executable name is resolved from the same identity Config.Title/I2's executable-basename fallback uses. Reach for PolicyHint instead when the hint should point at a different tool.
func PolicyHint ¶ added in v0.3.0
func PolicyHint(command string, args ...string) ConfirmOption
PolicyHint overrides the Next action rendered by Confirm's non-interactive policy block. Without this option the block points at "pass --yes to confirm non-interactively" — wrong for a caller whose confirm flag isn't --yes (e.g. zq clean-repo's --apply). Pass the caller's own executable and args so the hint names the flag that actually unblocks it.
type DebugConfig ¶ added in v0.2.0
type DebugConfig struct {
// Level is the minimum debug journal level. Zero (LevelUnset) resolves to
// LevelInfo. Use LevelTrace or LevelDebug to surface Debug/Capture mirrors.
Level LogLevel
// View selects history vs pane presentation (default History).
View DebugPresentation
// PaneHeight is used when View is DebugPresentationPane (default 5).
PaneHeight int
// NewestFirst orders the pane (default true when zero-config pane).
NewestFirst *bool
// PreserveAlways forces a diagnostic tail on every Finish in pane mode.
PreserveAlways bool
// AddSource resolves each record's call site to a source=file.go:line
// field on human/pane/history rendering (slog.HandlerOptions.AddSource
// semantics). Off by default: the raw program counter is always kept on
// LogRecord.PC for machine consumers, but a human debug line never shows
// a bare pc=<uintptr> unless this is set.
AddSource bool
}
DebugConfig configures the debug journal presentation.
type DebugPaneOption ¶
type DebugPaneOption interface {
// contains filtered or unexported methods
}
DebugPaneOption configures DebugPane presentation.
func NewestFirst ¶
func NewestFirst() DebugPaneOption
NewestFirst orders the pane with the most recent record first (default).
func OldestFirst ¶
func OldestFirst() DebugPaneOption
OldestFirst orders the pane chronologically (oldest visible first).
func PaneHeight ¶
func PaneHeight(lines int) DebugPaneOption
PaneHeight sets how many debug records are visible in the pane (not including heading).
func PreserveDebugTail ¶
func PreserveDebugTail() DebugPaneOption
PreserveDebugTail always emits a bounded diagnostic tail under the final report. Without this, pane mode still preserves a tail on failed/blocked/cancelled conclusions.
type DebugPresentation ¶
type DebugPresentation int
DebugPresentation selects how structured debug records project to a TTY (§4.6 / §21.3).
const ( // DebugPresentationHistory appends durable scrollback above the live region (default). DebugPresentationHistory DebugPresentation = iota // DebugPresentationPane keeps a bounded rolling viewport inside the live region. DebugPresentationPane )
type DisplayGroup ¶ added in v0.4.0
type DisplayGroup struct {
// contains filtered or unexported fields
}
DisplayGroup is a handle for a presentation-only collection of independent child tasks: state is always derived from children (glyph + header only, no Done/Fail/Progress methods), and a group reads Failed iff any child failed — no ordering assumed, so concurrent Running children are the expected shape (see Sequence for the ordered alternative).
func (*DisplayGroup) DisplayGroup ¶ added in v0.4.0
func (g *DisplayGroup) DisplayGroup(name string, args ...any) *DisplayGroup
DisplayGroup declares a fresh child container nested under this DisplayGroup (P3's recursive nesting) — see Output.DisplayGroup for the fan-out contract.
func (*DisplayGroup) Sequence ¶ added in v0.4.0
func (g *DisplayGroup) Sequence(name string, args ...any) *SequenceHandle
Sequence declares (or, for a repeated name, returns) an ordered child container nested under this DisplayGroup (P3's recursive nesting) — see Output.Sequence for the get-or-create and cascade contract.
func (*DisplayGroup) Snapshot ¶ added in v0.4.0
func (g *DisplayGroup) Snapshot() TasksSnapshot
Snapshot returns the collection snapshot with derived state.
func (*DisplayGroup) Summary ¶ added in v0.4.0
func (g *DisplayGroup) Summary(text string, args ...any) *DisplayGroup
Summary sets a success-oriented collection summary. text is a printf format when args are present (fmt.Sprintf semantics) — one text spelling shared with Task/Sequence/Reason (C6).
func (*DisplayGroup) Task ¶ added in v0.4.0
func (g *DisplayGroup) Task(name string, args ...any) *TaskHandle
Task declares a child task in declaration order. Optional evo.ID sets a stable machine key. name is a printf format when args are present (fmt.Sprintf semantics) — evo.ID (or any other EntityOption) may be mixed into args in any position and still applies.
type EffectOption ¶ added in v0.4.0
type EffectOption interface {
// contains filtered or unexported methods
}
EffectOption configures a mutation-verb call (TaskHandle.Add/Delete/ Create/Update/Remove/Write/Push). Affected is the only option today.
func Affected ¶ added in v0.4.0
func Affected(n int) EffectOption
Affected sets how many objects a mutation verb call affected — evo derives the ledger's quantity and plural noun from n at render time; the verb call's object argument stays a singular noun phrase regardless (see TaskHandle.Delete). Without Affected, the mutation records one named object with no count (Create/Write's traditional shape): evo owns tense and number either way, never the caller.
type EffectRecord ¶
type EffectRecord = core.EffectRecord
EffectRecord is one semantic change or plan row.
type EntityOption ¶ added in v0.2.3
type EntityOption interface {
// contains filtered or unexported methods
}
EntityOption configures Task declaration (stable keys). The common path remains Task("label"); options are platform-scale.
func ID ¶ added in v0.2.3
func ID(id string) EntityOption
ID sets a stable machine key independent of the human label. Labels may be localized or reworded; IDs should not.
out.Task("download base image", evo.ID("build.base-image.download"))
func StartPhase ¶ added in v0.3.0
func StartPhase(text string) EntityOption
StartPhase declares a task with its first Phase already set, in one call — declare-then-phase collapsed into the declaration itself:
out.Task("download base image", evo.StartPhase("resolving tag"))
is exactly out.Task("download base image").Doing("resolving tag"), with no separate statement (and no gap where the task sits Pending) between them.
type EntityState ¶
type EntityState = core.EntityState
EntityState is the lifecycle state of an item or task.
C11 naming sweep: these members stay bare (Done, Failed, Blocked, ...) rather than gaining a State* prefix to match ConclusionState below — prefixing would collide outright with ConclusionState's own StateFailed/ StateBlocked/StateCancelled/StateWarning constants (same package, same identifiers, different types is still a duplicate declaration in Go). Renaming ConclusionState's constants instead would ripple into the JSON wire (schema 0.3, frozen this release) and every existing golden — this is the "document instead" branch the census decision allows.
Aliased into internal/core alongside the rest of the data model — see Snapshot's doc comment (snapshot.go) for why.
type Event ¶
Event is an immutable journal record.
Aliased into internal/core alongside the rest of the data model — see Snapshot's doc comment (snapshot.go) for why.
type Evidence ¶
type Evidence struct {
// contains filtered or unexported fields
}
Evidence is the retained/redacted process-output sink owned by a Task (preferred) or Output. "Stdout" would lie as a name — it also takes stderr and combined writes; Evidence says what it is for: durable, sanitized proof a failure can point back to.
upgrade := out.Task("brew packages")
proof := upgrade.Evidence() // silent retention by default
if err := run.Run(ctx, "brew", args, proof); err != nil {
upgrade.Failf("brew upgrade failed: %w", err)
return nil
}
upgrade.Done()
Prefer task.Run for an *exec.Cmd — it wires Evidence and Phase together in one call. Reach for Evidence directly only when the caller already owns stdout/stderr plumbing (a custom runner, a non-exec.Cmd tool integration).
Combined streams by default (P1): Write (merged), Stdout(), and Stderr() all feed the same bounded ring used by Text/Tail/DetailTail. Linters and most subprocess tools write diagnostics on stderr — route both streams into Evidence (or write the combined pipe into it directly) so failure evidence cannot escape the owning Task.
Semantics:
- Always retains a bounded ring of sanitized lines (evidence exists even when debug presentation is disabled).
- Default is silent: no Diagnostics/Debug mirror on success.
- Opt in with MirrorToDiagnostics / MirrorToDebug.
- Stdout/Stderr have independent pending buffers (no partial-line merge).
- DetailTail prefers stderr when separate streams were used, else combined.
func (*Evidence) Close ¶ added in v0.3.0
Close flushes trailing partial lines.
On the root Evidence (task.Evidence()), every stream pending buffer is flushed so Stdout/Stderr partial lines are retained. On a side writer (Stdout/Stderr), only that stream is flushed.
func (*Evidence) DetailTail ¶ added in v0.3.0
func (c *Evidence) DetailTail() ProblemOption
DetailTail returns a ProblemOption attaching a user-visible presentation of the capture tail. Prefers stderr when separate streams were used. Sets Problem.EvidenceTail rather than Problem.Detail: when the same Fail/Block call also carries an explicit Detail, that explicit text still renders (as the primary detail line) and this tail renders as an additional evidence line underneath, regardless of which option was passed first.
func (*Evidence) Empty ¶ added in v0.3.0
Empty reports whether no completed lines and no pending fragments exist.
func (*Evidence) Stderr ¶ added in v0.3.0
Stderr returns a writer that records lines as stderr with its own pending buffer.
func (*Evidence) Stdout ¶ added in v0.3.0
Stdout returns a writer that records lines as stdout with its own pending buffer.
type EvidenceOption ¶ added in v0.3.0
type EvidenceOption interface {
// contains filtered or unexported methods
}
EvidenceOption configures Evidence.
func KeepLastLines ¶ added in v0.1.2
func KeepLastLines(n int) EvidenceOption
KeepLastLines sets how many trailing lines are retained (default 200).
func MaxEvidenceBytes ¶ added in v0.3.0
func MaxEvidenceBytes(n int) EvidenceOption
MaxEvidenceBytes sets an approximate byte budget for retained lines (default 256KiB).
func MirrorToDebug ¶ added in v0.2.2
func MirrorToDebug() EvidenceOption
MirrorToDebug journals each completed line via Debug when DebugLevel allows. Default is off.
func MirrorToDiagnostics ¶ added in v0.2.2
func MirrorToDiagnostics() EvidenceOption
MirrorToDiagnostics copies each completed line to the Diagnostics writer. Default is off — Evidence retains proof without displaying it on success.
type EvidenceStream ¶ added in v0.3.0
type EvidenceStream uint8
EvidenceStream identifies which process stream a line came from.
const ( // EvidenceStreamCombined is Write() on the Evidence itself (merged by the runner). EvidenceStreamCombined EvidenceStream = iota // EvidenceStreamStdout is output.Stdout(). EvidenceStreamStdout // EvidenceStreamStderr is output.Stderr(). EvidenceStreamStderr )
type FactRecord ¶ added in v0.4.0
FactRecord is a discovered name/value annotation — information, not work (user-13-problems.md Problem 8). Named FactRecord, not Fact, because Fact is the evo.Fact/TaskHandle.Fact verb (EffectRecord sets the naming precedent: the verb keeps the plain name, its stored shape gets Record). See TaskHandle.Fact and Output.Fact.
Aliased into internal/core alongside the rest of the data model — see Snapshot's doc comment (snapshot.go) for why.
type Failure ¶ added in v0.3.0
type Failure struct {
// contains filtered or unexported fields
}
Failure is the error TaskHandle.Failf/Blockf return. It renders exactly like the fmt.Errorf result it replaces (Error() is the formatted summary plus evidence, Unwrap() reaches the %w-wrapped cause so errors.Is/As keep working), so a bare `return task.Failf("clone %s: %w", url, err)` stays a valid error return.
Failure also carries Next/NextCommand, so the remedy for a failure finally has somewhere to attach at the return site instead of a second statement the caller has to remember to write:
return task.Failf("clone %s: %w", url, err).
Next(evo.Label("check network access"))
func (*Failure) Next ¶ added in v0.3.0
Next attaches a recommended follow-up action to the task this Failure resolved, and returns the same *Failure so it stays valid as a bare error return.
func (*Failure) NextCommand ¶ added in v0.3.0
NextCommand attaches a recommended command action; see Next.
type FixedClock ¶
FixedClock always returns the same instant.
type Format ¶ added in v0.2.0
type Format int
Format selects the overall projection mode. Zero is ordinary human output.
const ( // FormatHuman is ordinary human (and optional interactive) presentation. FormatHuman Format = iota // FormatData reserves stdout for the application's domain payload; Evo // human presentation goes to stderr (data-command mode). FormatData // FormatExternal disables inline rendering (snapshots only). FormatExternal )
type GlyphProfile ¶ added in v0.3.0
type GlyphProfile = txt.GlyphProfile
GlyphProfile selects which glyph vocabulary state markers render in (evo-rec.md "Tightened glyph vocabulary", rule GLYPH-001: glyph selection via capability profile, cell-width measurement not rune counts).
The zero value, GlyphsAuto, keeps today's Unicode vocabulary off a TTY (a non-interactive stream can't show a human mojibake, so there is nothing to guard against) and on any TTY whose locale already advertises UTF-8. It downgrades to the ASCII vocabulary only on an interactive terminal without UTF-8 locale support — the one case where the status column would otherwise render as mojibake.
Aliased into internal/text (glyph tables and the rendering primitives that select from them live there — see EVIDENT_OUTPUT_ARCHITECTURE_SPEC_v0.5.md §38).
type JSONCollection ¶
type JSONCollection = render.JSONCollection
JSONCollection is a wire-format task collection with child IDs (§25.1).
type JSONDocument ¶
type JSONDocument = render.JSONDocument
JSONDocument is the final machine projection (§25.1).
Aliased into internal/render alongside the JSON encoding machinery that produces it — see EVIDENT_OUTPUT_ARCHITECTURE_SPEC_v0.5.md §38.
type JSONEffectRecord ¶
type JSONEffectRecord = render.JSONEffectRecord
JSONEffectRecord is a change/plan row.
type JSONMessage ¶ added in v0.2.0
type JSONMessage = render.JSONMessage
JSONMessage is a wire-format user-facing message.
type JSONOutputMeta ¶
type JSONOutputMeta = render.JSONOutputMeta
JSONOutputMeta identifies the output instance.
type JSONProblem ¶
type JSONProblem = render.JSONProblem
JSONProblem is a wire-format problem (no raw Cause by default).
type LiveSurface ¶
type LiveSurface interface {
TerminalDriver
Columns() int
Rows() int
IsInteractive() bool
WriteLive(text string)
ClearLive()
WriteDurable(line string)
WriteFinal(text string)
}
LiveSurface is an interactive terminal sink for live-region rendering. testkit.Screen implements this; production drivers can as well.
type LogLevel ¶
type LogLevel int
LogLevel is a diagnostic severity.
The zero value is LevelUnset (Config resolves it to LevelInfo). Named levels start at LevelTrace so ordinary Config{Debug: DebugConfig{Level: LevelTrace}} is expressible without falling through the default path.
const ( // LevelUnset is the zero value. Config resolve maps it to LevelInfo. LevelUnset LogLevel = iota // LevelTrace is the most verbose journal level selectable via Config. LevelTrace // LevelDebug enables Debug journal lines (and Capture MirrorToDebug). LevelDebug // LevelInfo is the ordinary default (Debug journal suppressed). LevelInfo // LevelWarn is reserved for future warn-threshold filtering. LevelWarn // LevelError is reserved for future error-threshold filtering. LevelError )
type LogRecord ¶ added in v0.2.4
type LogRecord struct {
Time time.Time
Level slog.Level
Message string
Attrs []slog.Attr
PC uintptr
}
LogRecord is a complete internal log entry preserved from slog (or peer bridges). History and pane projectors read Time/Level/Message/Attrs without lossy remapping beyond the usual Field redaction path.
type MessageSnapshot ¶ added in v0.2.0
type MessageSnapshot = core.MessageSnapshot
MessageSnapshot is one logical user-facing message in the canonical model.
type NoopRedactor ¶
type NoopRedactor struct{}
NoopRedactor leaves strings unchanged.
func (NoopRedactor) RedactString ¶
func (NoopRedactor) RedactString(s string) string
RedactString implements Redactor.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option configures an Output.
func AlsoWrite ¶
AlsoWrite adds an additional human projection writer. On Finish, each writer receives the plain projection; failures on one do not skip the others (CON-009).
func DataProjection ¶
func DataProjection() Option
DataProjection selects data-command mode (UI/progress on diagnostic stream) — a self-documenting marker at the call site; the routing itself comes from pairing it with To(stderr)/Diagnostics(stderr) (configToOptions).
func DebugAddSource ¶ added in v0.3.0
func DebugAddSource() Option
DebugAddSource resolves each debug record's call site to a source=file.go:line field on human/pane/history rendering, matching slog.HandlerOptions.AddSource semantics. Off by default (release-gate round 6 finding 2): a bare pc=<uintptr> field never belongs on a human debug line; the raw PC still lives on LogRecord for machine consumers regardless of this setting.
func DebugHistory ¶
func DebugHistory() Option
DebugHistory selects durable append-above-and-redraw presentation (v0.4 default).
func DebugLevel ¶
DebugLevel sets the minimum debug emission level. Pass LevelTrace or LevelDebug to surface Debug journal lines.
func DebugPane ¶
func DebugPane(opts ...DebugPaneOption) Option
DebugPane selects a rolling TTY debug viewport at the bottom of the live region.
func Diagnostics ¶
Diagnostics sets the diagnostic writer for Debug history and Capture mirrors. When set and distinct from the primary writer (To), Debug lines are not also written to the human primary stream — use dual-stream for LaunchAgent / data-command layouts (human on stdout, diagnostics on stderr).
func DryRun ¶ added in v0.3.0
func DryRun() Option
DryRun declares this run a dry run: TaskHandle mutation verbs (Delete, Create, Update, Remove, Write, Push, Record, RecordName) render as [planned] rows with imperative verbs instead of [changed] rows with past-tense verbs. Set once via Config.DryRun in ordinary application code; this Option exists for the advanced NewWithOptions surface and tests.
func ExternalProjection ¶
func ExternalProjection() Option
ExternalProjection selects snapshot-only host rendering.
func Glyphs ¶ added in v0.3.0
func Glyphs(p GlyphProfile) Option
Glyphs selects the glyph capability profile (default GlyphsAuto).
func MaxEntities ¶
MaxEntities caps total items and tasks for one Output (0 uses default).
func MaxEvents ¶
MaxEvents caps durable journal events; when exceeded, oldest non-critical events are dropped so critical terminal events are retained (CON-008).
func MaxFrameRate ¶
MaxFrameRate caps interactive redraws per second.
func Plain ¶
func Plain() Option
Plain forces final-report projection (no live spinner region). Semantic color is still emitted unless NoColor is set.
func ResultStream ¶ added in v0.2.3
ResultStream sets the domain-payload writer (see Output.ResultWriter). Presentation never writes here. FormatData defaults this to Config.Stdout.
func Stdin ¶ added in v0.3.0
Stdin injects the reader Confirm reads answers from (facade rule — no direct os.Stdin read in Confirm's logic). Default os.Stdin.
func Terminal ¶
func Terminal(driver TerminalDriver) Option
Terminal injects a terminal driver (interactive projection; v0.2).
func Title ¶ added in v0.2.7
Title sets the conclusion subject for Config.Options's raw Option path.
func VisibilityDelay ¶
VisibilityDelay sets how long live activity must persist before the first interactive paint (default 80ms). Zero paints immediately. Prevents Phase→fast Done spinner flash (H.2). Domain TimeSource is used for the threshold.
type Output ¶
type Output struct {
// contains filtered or unexported fields
}
Output is the aggregate root for one command's presentation lifecycle.
func Default ¶ added in v0.3.0
func Default() *Output
Default returns the package-level default Output, lazily creating one with a zero Config the first time it's needed — package-level Task/Print* never panic even when the caller skipped Init.
func Init ¶ added in v0.3.0
Init is the sole Output constructor. It builds an Output from cfg, installs it as the package-level default, and arms first paint — call once, in main, before any I/O:
func main() {
evo.Init(evo.Config{Title: "repo-retire"})
evo.Main(run)
}
evo.Init() (zero args) or evo.Init(evo.Config{}) (or evo.Init(evo.DefaultConfig())) all build an ordinary default instance — Init is variadic (I9) so the zero-config call needs no empty Config{} literal. Passing more than one Config uses only the first; there is one construction call, not a merge.
Config.Isolated returns an independent instance that skips both steps — it never touches package state (parallel tests, embedders holding their own *Output).
Config.Options is the advanced raw-Option escape hatch for tests and specialized embedding; when set, ordinary Config fields (besides Title, DryRun, and Subject) are ignored. Options installs as the package-level default and arms first paint exactly like every other Init call — Isolated is the one and only opt-out, orthogonal to Options (release-gate round 8 finding 1: a caller who set Options but not Isolated must still be able to reach the instance they configured via the package-level Task/Print facade, instead of those facades lazily building a second, bare Output that silently drops DryRun/Title/writer wiring).
func (*Output) At ¶ added in v0.2.0
func (o *Output) At(visibility Visibility) *Printer
At returns a printer for the given visibility.
func (*Output) Conclusion ¶
func (o *Output) Conclusion() Conclusion
Conclusion returns the computed conclusion after Finish.
func (*Output) Confirm ¶ added in v0.3.0
func (o *Output) Confirm(question string, opts ...ConfirmOption) bool
Confirm quiesces the live region, renders a durable "? <question> y/N" gate, and reads one answer line — owning the whole gate so no call site hand-rolls a y/N prompt that fights the spinner or misreports "no" as failure (evo-rec.md "confirm gate" default).
question is verbatim text, not a printf format (unlike Task/Group/Reason's C6 name argument) — build a dynamic question with fmt.Sprintf before calling Confirm.
Resolution:
- AssumeYes(true): Done "assumed --yes", returns true, no prompt.
- No TTY / NonInteractive / plain, without AssumeYes: never blocks on stdin — Blocked "blocked by policy" with a Next hint to pass --yes, returns false.
- "y"/"yes" (case-insensitive): Done, returns true.
- Anything else, including empty: Blocked "declined", returns false — exit 1 via Conclusion precedence, never Failed, never Cancelled.
- Zero-byte EOF on stdin (stdin closed or redirected from /dev/null before any answer arrived): Blocked "no answer — stdin closed" with the same --yes Next hint, returns false — distinct wording from the no-TTY policy block above, since nothing decided to refuse; the stream simply gave no answer.
- SIGINT/SIGTERM while waiting: the existing signal path (runInterruptible) cancels the gate — it renders Cancelled, not declined — and Confirm returns false.
func (*Output) Debug ¶
Debug records a structured diagnostic (§4.6 / §21.3).
History mode (default): durable scrollback above the live region (or plain stream). Pane mode: record is journaled and shown in the rolling live pane; not durable scrollback unless a diagnostic tail is preserved at Finish.
When Diagnostics is configured and is a different writer than the primary stream, debug lines go to Diagnostics only (not the human Items/Tasks stream). Use Capture for child-process evidence instead of DebugWriter when you need Fail Detail.
func (*Output) DebugWriter ¶
func (o *Output) DebugWriter() io.WriteCloser
DebugWriter returns a line-oriented writer that emits Debug lines on newline. Partial UTF-8 sequences are buffered; control bytes are sanitized.
Prefer Capture for external process stdout/stderr: DebugWriter is filtered by DebugLevel (default LevelInfo drops all lines) and is the wrong dialect for child-command evidence used in Fail Detail. Use DebugWriter only when you intentionally want DEBUG-level journal lines (and set DebugLevel(Debug)).
func (*Output) DeclareDryRun ¶ added in v0.3.0
func (o *Output) DeclareDryRun()
DeclareDryRun switches this run into dry-run mode after construction — a bounded late setter (I8): calling it once any durable row has already streamed is misuse (ErrDryRunDeclaredLate), since those earlier rows would not reflect the switch. There is no argv-sniffing helper; the caller decides (e.g. from a flag parsed after Init) and calls this explicitly, before any Task/Print/Confirm call. A no-op when the run is already dry-run.
func (*Output) DisplayGroup ¶ added in v0.4.0
func (o *Output) DisplayGroup(name string, args ...any) *DisplayGroup
DisplayGroup declares a collection of independent child tasks: state is fully derived from children, glyph and header only, no ordering assumed (worker-pool fan-out is a supported, concurrency-safe pattern here — see Sequence for the ordered alternative). name is a printf format when args are present (fmt.Sprintf semantics) — one text spelling shared with Task/Sequence/Reason (C6); no args leaves name untouched.
func (*Output) Evidence ¶ added in v0.3.0
func (o *Output) Evidence(opts ...EvidenceOption) *Evidence
Evidence returns a session-level retained/redacted writer with no owning Task. Prefer Task.Evidence so failure evidence attaches to an entity. Session-level Evidence is advanced; ordinary call sites should not use it.
func (*Output) Fact ¶ added in v0.4.0
Fact accumulates a run-scoped discovered name/value annotation (P8 symmetry with TaskHandle.Fact) — information about the run, fire-and- forget: it both renders a durable dim "name value" line immediately (the same "act now" contract Println has) and stores the annotation for the structured Snapshot/JSON views. A nil Output is safe and records nothing.
func (*Output) Fail ¶
func (o *Output) Fail(summary string, options ...ProblemOption)
Fail records an output-level failure.
func (*Output) Failf ¶ added in v0.3.0
Failf records an output-level failure with a formatted summary. fmt.Errorf semantics: a trailing ": %w"/", %w" splits the formatted text into the recorded summary and evidence line exactly like TaskHandle.Failf.
Failf stays void rather than returning an error like TaskHandle.Failf does (release-gate round 4 finding 5): every existing call site uses Failf as a bare statement (e.g. Output.Run's own runInterruptible), and errcheck flags a discarded error return with no lint-config exception on this repo — so matching TaskHandle.Failf's signature here would force every one of those call sites to add a needless `_ = ` just to stay lint-clean. There is also no per-call Next chain to attach an error return to here the way TaskHandle.Failf's *Failure does (Output.Next already covers the output-level case), so a returned error would carry less than TaskHandle.Failf's does anyway. Documented asymmetry, not an oversight.
func (*Output) Finish ¶
Finish validates, computes conclusion, emits final projections. Projection I/O runs outside the domain lock (§17.1).
func (*Output) NextCommand ¶
NextCommand attaches an output-level command action.
func (*Output) Print ¶ added in v0.2.0
Print formats like fmt.Sprint and enqueues human-facing text (line-buffered). Errors are recorded on the Output and returned by Finish/Main — not ignored mid-stream.
func (*Output) Printf ¶ added in v0.2.0
Printf formats like fmt.Sprintf and enqueues human-facing text (line-buffered).
func (*Output) Println ¶ added in v0.2.0
Println formats like fmt.Sprintln and enqueues a complete human-facing line.
func (*Output) ResultWriter ¶ added in v0.2.3
ResultWriter returns the domain-payload stream. Presentation never writes here.
In FormatData mode this is Config.Result if set, otherwise Config.Stdout — so machine JSON stays pure while Tasks render on stderr. When no result stream is configured, returns io.Discard.
out := evo.Init(evo.Config{Title: "build", Format: evo.FormatData})
// after work succeeds:
_ = json.NewEncoder(out.ResultWriter()).Encode(payload)
func (*Output) Run ¶ added in v0.3.0
Run executes a CLI presentation lifecycle against this Output and returns the process exit code — the Isolated-instance counterpart of Main, for a caller holding its own *Output (evo.Init(evo.Config{Isolated: true})).
Typical entrypoint:
func main() {
out := evo.Init(evo.Config{Title: "tool", Isolated: true})
os.Exit(out.Run(run))
}
Lifecycle: arm first paint → run → (reconcile run error into model) → Finish → Close.
Exit codes:
- nil Output → ExitFailed (2)
- SIGINT/SIGTERM → Cancel on the active task (or the output) → ExitCancelled (130)
- a second SIGINT/SIGTERM → ExitCancelled (130) returned immediately, without waiting for run to unwind, so the caller's os.Exit(out.Run(...)) exits now
- Finish/Close bookkeeping misuse (a leftover unresolved task, a double-resolve, ...) is folded into the Conclusion before it renders, so the printed band and Conclusion.ExitCode already agree; a Blocked conclusion keeps ExitBlocked (1) regardless — the documented "Block → exit 1" contract wins over a leftover bookkeeping misuse
- a genuine renderer/write failure (surfacing only after the band is already flushed) still escalates an otherwise-OK exit code to ExitFailed (2)
- otherwise Conclusion.ExitCode after reconciling run errors into Fail
Config.FailedExitCode (when non-zero) overrides ExitFailed for a failed conclusion so CLIs that contract on exit 1 can set FailedExitCode: 1.
A non-nil application error is recorded as an output-level Fail before Finish so the human conclusion cannot show [ready] while the process fails.
func (*Output) Scope ¶ added in v0.2.3
Scope returns a namespaced handle. It does not render a visible section.
func (*Output) Sequence ¶ added in v0.4.0
func (o *Output) Sequence(name string, args ...any) *SequenceHandle
Sequence declares (or, for a repeated name, returns) a self-managing, ordered container — the front door for a sequence of steps that must stop implying "still might run" once a member has already failed or been cancelled. A second evo.Sequence("python") call returns the same Sequence, mirroring Task's get-or-create identity. name is a printf format when args are present (fmt.Sprintf semantics); no args leaves name untouched.
func (*Output) SlogHandler ¶
SlogHandler returns a slog.Handler that journals every accepted record as structured diagnostics (history or pane), without mutating Output configuration.
Level policy is Config.Debug.Level (one conductor):
out := evo.Init(evo.Config{
Debug: evo.DebugConfig{Level: evo.LevelDebug},
})
logger := slog.New(out.SlogHandler())
Application human prose uses Print/Printf/Println or Task outcomes — not slog. Infrastructure diagnostics use slog through this handler.
func (*Output) Subject ¶ added in v0.3.0
Subject prints one durable line immediately — the same one-shot semantics as Config.Subject, for a caller who doesn't know the subject text until after Init (e.g. resolved from a flag), but still before any other I/O (I3). A no-op on a nil Output or empty text.
func (*Output) Suspend ¶
Suspend temporarily pauses interactive presentation for host-owned output. In v0.3 plain/non-interactive mode this is a no-op around fn. With a live surface it clears the live region, runs fn, then resumes.
func (*Output) Task ¶
func (o *Output) Task(name string, args ...any) *TaskHandle
Task declares a single operation. Optional evo.ID sets a stable machine key. name is a printf format when args are present (fmt.Sprintf semantics) — evo.ID (or any other EntityOption) may be mixed into args in any position and still applies.
func (*Output) Warn ¶ added in v0.4.0
Warn accumulates a run-scoped warning annotation (P8 symmetry with TaskHandle.Warn) — a warning about the run itself, not about any one task. Feeds the conclusion's "· warned" band exactly like a task warning, never a headline of its own (evo-rec.md "warnings annotate lifecycle; they do not replace it"). summary is a printf format when fmt args are present, matching TaskHandle.Warn's C6 shape. A nil Output is safe and records nothing.
type PlainOptions ¶
type PlainOptions struct {
Width int
NoColor bool
// Verbose additionally emits per-reason name lists under a task's skip/keep
// taxonomy line. Counts and the reason partition always render; Verbose
// only adds the bounded (TruncateNames) name detail.
Verbose bool
// Glyphs selects the state-glyph vocabulary. Plain projection has no live
// TTY to detect, so GlyphsAuto (the default) resolves to GlyphsUnicode —
// callers rendering off a known non-UTF-8 destination pass GlyphsASCII.
Glyphs GlyphProfile
}
PlainOptions configures pure plain projection (§25.4).
type PlanSnapshot ¶
type PlanSnapshot = core.PlanSnapshot
PlanSnapshot is an immutable plan section.
type Printer ¶ added in v0.2.0
type Printer struct {
// contains filtered or unexported fields
}
Printer is a visibility-scoped view of Output for Print/Printf/Println.
func Verbose ¶ added in v0.2.0
func Verbose() *Printer
Verbose returns a Printer scoped to Verbose visibility on the default instance.
type Problem ¶
Problem is structured evidence explaining a negative item or task outcome.
Aliased into internal/core alongside the rest of the data model — see Snapshot's doc comment (snapshot.go) for why.
type ProblemOption ¶
type ProblemOption interface {
// contains filtered or unexported methods
}
ProblemOption configures a problem constructed by Block/Warn/Fail helpers.
func Count ¶
func Count(value int64, unit ...string) ProblemOption
Count sets a quantity and optional unit.
func Detail ¶
func Detail(text string) ProblemOption
Detail sets user-visible detail text (strings only).
func Location ¶
func Location(path string, line, column int) ProblemOption
Location sets a source location on a Problem (renamed from At — C5: a free-function At collided in name, though not in call syntax, with Output.At(visibility), confusing autocomplete and readers alike).
func NextCommand ¶
func NextCommand(executable string, args ...string) ProblemOption
NextCommand attaches a recommended command action.
type ReasonOption ¶ added in v0.3.0
type ReasonOption interface {
// contains filtered or unexported methods
}
ReasonOption constrains how a Reason may be used; a violated constraint is recorded as misuse (Strict panics; production still counts the record).
func ForSkip ¶ added in v0.3.0
func ForSkip() ReasonOption
ForSkip restricts a reason to TaskHandle.Skipped — recording it via Kept is misuse.
func OnTask ¶ added in v0.3.0
func OnTask(taskName string) ReasonOption
OnTask restricts a reason to the named task — recording it from a different task is misuse.
type Redactor ¶
type Redactor interface {
// RedactString returns a display-safe form of s.
RedactString(s string) string
}
Redactor redacts sensitive values before journal, Capture retention, and human rendering.
type Scope ¶ added in v0.2.3
type Scope struct {
// contains filtered or unexported fields
}
Scope is a namespaced declaration handle for plugins and subsystems.
Contract (honest limits):
Qualifies evo.ID keys as "scope.key" for stable machine identity.
Exposes only Task and Tasks — operations that actually take the namespace.
Is NOT a security sandbox: plugins holding *Output bypass Scope entirely.
Session Capture, Writer, and SlogHandler stay on *Output (shared session).
registry := out.Scope("registry") registry.Task("credentials", evo.ID("auth")).Done() // key → "registry.auth"
type SequenceHandle ¶ added in v0.4.0
type SequenceHandle struct {
// contains filtered or unexported fields
}
SequenceHandle is the front door for an ordered dependency of steps that must stop implying "still might run" once a child reaches Failed or Cancelled: every later-declared sibling still unresolved auto-resolves to NotStarted ("- <name> not started") — no caller code required. It is a thin identity layer over DisplayGroup (the underlying collection) that adds get-or-create children. Construct one via evo.Sequence or Output.Sequence.
func Sequence ¶ added in v0.4.0
func Sequence(name string, args ...any) *SequenceHandle
Sequence declares (or, for a repeated name, returns) a self-managing, ordered task container on the default instance — see Output.Sequence for the auto-lifecycle contract. name is a printf format when args are present (fmt.Sprintf semantics).
func (*SequenceHandle) DisplayGroup ¶ added in v0.4.0
func (g *SequenceHandle) DisplayGroup(name string, args ...any) *DisplayGroup
DisplayGroup declares a fresh child container nested under this Sequence (P3's recursive nesting).
func (*SequenceHandle) Sequence ¶ added in v0.4.0
func (g *SequenceHandle) Sequence(name string, args ...any) *SequenceHandle
Sequence declares (or, for a repeated name, returns) an ordered child container nested under this Sequence (P3's recursive nesting).
func (*SequenceHandle) Snapshot ¶ added in v0.4.0
func (g *SequenceHandle) Snapshot() TasksSnapshot
Snapshot returns the sequence snapshot with derived state.
func (*SequenceHandle) Summary ¶ added in v0.4.0
func (g *SequenceHandle) Summary(text string, args ...any) *SequenceHandle
Summary sets a success-oriented sequence summary. text is a printf format when args are present (fmt.Sprintf semantics) — see Task (C6).
func (*SequenceHandle) Task ¶ added in v0.4.0
func (g *SequenceHandle) Task(name string, args ...any) *TaskHandle
Task declares (or, for a repeated name, returns) a child task in declaration order. name is a printf format when args are present (fmt.Sprintf semantics); the get-or-create key is the formatted name. args may also carry evo.ID to set a stable machine key.
type Snapshot ¶
Snapshot is an immutable complete presentation state at a version.
Declared as a type alias into internal/core (the repo's data-model package — see EVIDENT_OUTPUT_ARCHITECTURE_SPEC_v0.5.md §38): rendering and evidence-capture machinery import core, never this root package, so the data model has to live where they can reach it without an import cycle back through the behavioral facades (Output, TaskHandle, Evidence) that stay declared here. pkg.go.dev cannot expand an aliased type's fields (internal/core is never rendered) — see docs/reference.md for the full field-level reference this doc comment summarizes.
type SourceLocation ¶ added in v0.3.0
type SourceLocation = core.SourceLocation
SourceLocation is a path-based source position. Named SourceLocation (not Location) so the Location(...) ProblemOption constructor below can keep that name without colliding with its own return type.
type TaskHandle ¶ added in v0.3.0
type TaskHandle struct {
// contains filtered or unexported fields
}
TaskHandle is a handle for one operation with phases or progress.
func Task ¶
func Task(name string, args ...any) *TaskHandle
Task declares (or, for a repeated name, returns) a Task on the default instance. Calling Task with the same name twice returns the same handle — the identity a caller doing evo.Task("branches") from two call sites expects. name is a printf format when args are present (fmt.Sprintf semantics); the get-or-create key is the formatted name.
func (*TaskHandle) Add ¶ added in v0.3.0
func (t *TaskHandle) Add(object string, call func() error, opts ...EffectOption) error
Add records an addition of object.
func (*TaskHandle) Block ¶ added in v0.3.0
func (t *TaskHandle) Block(summary string, options ...ProblemOption)
Block resolves the task as blocked. This is a statement, not a fluent chain — Block returns nothing, so a bare `task.Block("summary")` is errcheck-clean. A nil *TaskHandle is safe and resolves nothing. Use Blockf to build and return a %w-wrapped error in one line.
func (*TaskHandle) Blockf ¶ added in v0.3.0
func (t *TaskHandle) Blockf(format string, args ...any) *Failure
Blockf resolves the task as blocked with a formatted summary and returns a *Failure exactly like Failf — see Failf for the fmt.Errorf %w, summary/evidence split, and Next/NextCommand remedy-attachment contract.
func (*TaskHandle) Bytes ¶ added in v0.3.0
func (t *TaskHandle) Bytes(completed, total int64) *TaskHandle
Bytes sets absolute byte progress (units and rate formatting).
func (*TaskHandle) Cancel ¶ added in v0.3.0
func (t *TaskHandle) Cancel(reason string) *TaskHandle
Cancel resolves the task as cancelled.
func (*TaskHandle) Create ¶ added in v0.3.0
func (t *TaskHandle) Create(object string, call func() error, opts ...EffectOption) error
Create records the creation of object.
func (*TaskHandle) Delete ¶ added in v0.3.0
func (t *TaskHandle) Delete(object string, call func() error, opts ...EffectOption) error
Delete records a deletion of object; see Add for the call/dry-run/ singular-object contract shared by every mutation verb.
func (*TaskHandle) Doing ¶ added in v0.4.0
func (t *TaskHandle) Doing(text string, args ...any) *TaskHandle
Doing sets the active current-step live text and starts the task if pending — replaces the previous text, promotes the task to Running, and becomes a durable line per step off-TTY. text is a printf format when args are present (fmt.Sprintf semantics) — one text spelling shared with Done/Task/Group/Reason/Skip (C6; release-gate round 6 finding 4: Confirm's question is the one true non-printf exception now). Named Doing, not Phase (P6/rename): "phase" stays the name of the run-level section header (StartPhase), a different concept from a task's own narrated step.
func (*TaskHandle) Done ¶ added in v0.3.0
func (t *TaskHandle) Done(args ...any) *TaskHandle
Done resolves the task successfully, with no summary (Done()), a literal one (Done("modules cached")), or a printf-formatted one (Done("%d packages", 18), fmt.Sprintf semantics) — one text spelling shared with Task/Group/Reason/Warn (C6), including the same "no args leaves text untouched" rule that keeps a literal "%" safe. A non-string first argument is misuse (ErrInvalidConfig): Done's format position is still meant to be a caller-written string, not an accidental value.
func (*TaskHandle) Each ¶ added in v0.3.0
func (t *TaskHandle) Each(items []string) iter.Seq[string]
Each iterates items, driving absolute Progress(i, len(items)) and a phase default of item before each item is yielded — the bar reads "items completed so far", not "items completed including the one still running", so it never shows full while the last item is in flight. Evo owns the counter: because progress is set from the loop index rather than a hand-maintained counter, re-running work for an item inside the loop body cannot double-count or move the bar backward — only advancing to the next item does. Normal completion of the loop seals progress at total/total.
The item-name phase is a courtesy default, not a declared phase: if the loop body calls Phase itself before the next paint, that call's own text is what streams (in plain mode) — the bare item name never forces its own redundant durable line first (beginner-10).
Breaking out of the loop early leaves progress at the count already reached; the task is not auto-resolved (call Done/Fail/etc. explicitly).
func (*TaskHandle) EachN ¶ added in v0.3.0
func (t *TaskHandle) EachN(n int) iter.Seq[int]
EachN iterates a count-only loop with no item names, driving absolute Progress(i, n) before each index is yielded — see Each for why the bar reads the count completed so far rather than including the in-flight item. Use Each when items have names worth showing as the phase. Normal completion of the loop seals progress at n/n.
func (*TaskHandle) Evidence ¶ added in v0.3.0
func (t *TaskHandle) Evidence(opts ...EvidenceOption) *Evidence
Evidence returns the retained/redacted writer bound to this Task, get-or-create: the first call (from Evidence or PhaseWriter) allocates the ring and every later call returns that same instance, so evidence recorded through either path lands together and survives for DetailTail after Fail.
func (*TaskHandle) Fact ¶ added in v0.4.0
func (t *TaskHandle) Fact(name, value string)
Fact accumulates a discovered name/value annotation on the task — info severity, Warn's non-terminal sibling (user-13-problems.md Problem 8: "Tasks are work. Facts are information."). Renders as a dim "name value" line, inline when it is the task's only annotation, nested otherwise. Like Warn, this is a statement (no return value) and never resolves the task — call it any number of times before the task's terminal verb.
func (*TaskHandle) Fail ¶ added in v0.3.0
func (t *TaskHandle) Fail(summary string, options ...ProblemOption)
Fail resolves the task as failed. This is a statement, not a fluent chain — Fail returns nothing, so a bare `task.Fail("summary")` is errcheck-clean. A nil *TaskHandle is safe and resolves nothing. Use Failf to build and return a %w-wrapped error in one line.
func (*TaskHandle) Failf ¶ added in v0.3.0
func (t *TaskHandle) Failf(format string, args ...any) *Failure
Failf resolves the task as failed with a formatted summary and returns a *Failure so a call site can `return` it directly: `return task.Failf("validate policy manifest: %w", err)`, and attach a remedy in the same statement: `.Next(evo.Label("..."))`. fmt.Errorf semantics: %w wraps its argument so errors.Is/As still reach it. See splitWrappedMessage for how a trailing ": %w"/", %w" splits the formatted text into the rendered summary and evidence line.
func (*TaskHandle) Kept ¶ added in v0.3.0
func (t *TaskHandle) Kept(reason TaxonomyReason, name string, errs ...error)
Kept accumulates a (reason, name) keep record on the task — same machinery as Skipped, second verb ("! kept N (...)").
func (*TaskHandle) Next ¶ added in v0.3.0
func (t *TaskHandle) Next(actions ...Action) *TaskHandle
Next attaches actions.
func (*TaskHandle) NextCommand ¶ added in v0.3.0
func (t *TaskHandle) NextCommand(executable string, args ...string) *TaskHandle
NextCommand attaches a command action. args names a foreign tool's own executable explicitly — the common case, since most remedies point at a different tool than the one running right now.
func (*TaskHandle) NextSelf ¶ added in v0.3.0
func (t *TaskHandle) NextSelf(args ...string) *TaskHandle
NextSelf attaches a command action that re-runs the caller's own binary with args — a self-referencing remedy ("rerun with --apply") that doesn't restate which binary to run (I6). Uses the same identity source as Confirm's PolicyFlag / I2's Failf fallback: Config.Title when set, else the binary's own basename. Use NextCommand instead when the remedy is a different (foreign) tool.
func (*TaskHandle) Progress ¶ added in v0.3.0
func (t *TaskHandle) Progress(completed, total int) *TaskHandle
Progress sets absolute completed/total count progress. Counts use int (collection lengths, indices). For byte quantities use Bytes. Prefer absolute Progress over Advance so retries cannot double-count.
func (*TaskHandle) Push ¶ added in v0.3.0
func (t *TaskHandle) Push(object string, call func() error, opts ...EffectOption) error
Push records a push of object.
func (*TaskHandle) Record ¶ added in v0.3.0
func (t *TaskHandle) Record(verb string, quantity int, object string)
Record records an arbitrary imperative verb/quantity/object mutation directly, resolving the target task's dry-run status the same way the named verbs do (it does not bypass Plan/Changes routing — only the call/error boundary the named verbs wrap around an executed callback). The low-level primitive the named verbs (and the conformance goldens) share. Nil-safe: a nil TaskHandle, or one whose Output is already gone, records nothing instead of panicking.
func (*TaskHandle) RecordLabel ¶ added in v0.3.0
func (t *TaskHandle) RecordLabel(label string, quantity int, object string)
RecordLabel records quantity of object (singular; see Delete) under label, verbatim, into the task's Changes ledger. Unlike Record's mutation verbs, label is a classification result (e.g. "ready", "blocked") rather than an imperative action, so it is never conjugated to past tense, and it never moves under [planned] during DryRun — classifying/observing already happened whether or not other mutations on this run are a dry run. Nil-safe: see Record.
func (*TaskHandle) RecordName ¶ added in v0.3.0
func (t *TaskHandle) RecordName(verb, object string)
RecordName records an arbitrary imperative verb and one named object without a quantity. Quantity is for collapsed counts; RecordName is one named object. Nil-safe: see Record.
func (*TaskHandle) Remove ¶ added in v0.3.0
func (t *TaskHandle) Remove(object string, call func() error, opts ...EffectOption) error
Remove records a removal of object.
func (*TaskHandle) Run ¶ added in v0.3.0
func (t *TaskHandle) Run(cmd *exec.Cmd) error
Run is the ordinary way to shell out from a Task: it executes cmd as this task's subprocess, wiring cmd.Stdout/cmd.Stderr through the same Evidence + PhaseWriter plumbing PhaseWriter uses directly. Each line becomes the task's live Phase, and every byte is retained (redacted, bounded) in the task's Evidence ring so DetailTail has proof after Fail — reach for Evidence directly only when the caller isn't running an *exec.Cmd. If cmd.Stdout/cmd.Stderr already point somewhere (a caller wiring its own log file, say), Run tees into it rather than replacing it.
If the task has no Phase yet, Run sets one from cmd's basename (filepath.Base(cmd.Path) or cmd.Args[0]) so a live view shows what's running before the child ever writes a line.
Run does not touch cmd.Stdin and does not Suspend — a subprocess that needs the terminal (a prompt, a pager) stays on the explicit Suspend path. A context baked into cmd via exec.CommandContext still governs cancellation exactly as it would for a bare cmd.Run(); Run adds no context handling of its own.
Run returns the subprocess error verbatim and never resolves the task — the caller chooses Done/Fail from the result:
cmd := exec.Command("go", "build", "./...")
if err := task.Run(cmd); err != nil {
return task.Failf("build failed: %w", err)
}
task.Done()
func (*TaskHandle) Skip ¶ added in v0.3.0
func (t *TaskHandle) Skip(reason string, args ...any) *TaskHandle
Skip resolves the task as skipped. reason is a printf format when args are present (fmt.Sprintf semantics) — one text spelling shared with Done/Task/Group/Reason/Phase (C6; release-gate round 6 finding 4).
func (*TaskHandle) Skipped ¶ added in v0.3.0
func (t *TaskHandle) Skipped(reason TaxonomyReason, name string, errs ...error)
Skipped accumulates a (reason, name) skip record on the task, with an optional trailing errs for evidence of why. It returns nothing — accumulating a record is an act, not a value to chain — is usable before the task resolves, and does not itself resolve the task. The taxonomy line ("! skipped N (a reasonA, b reasonB)") is derived from every accumulated record at render time, so no caller can hand-build (and thereby miscount) the summary; the aggregation key is untouched by errs. Any errs render as one bounded evidence line under the count row (first cause + "(+N more)"), full list under Verbose.
func (*TaskHandle) Snapshot ¶ added in v0.3.0
func (t *TaskHandle) Snapshot() TaskSnapshot
Snapshot returns the task snapshot.
func (*TaskHandle) Step ¶ added in v0.3.0
func (t *TaskHandle) Step(completed, total int, name string) *TaskHandle
Step sets absolute progress and phase text together under one lock acquisition, so a concurrent worker can never observe one goroutine's count paired with another goroutine's phase name — the exact interleaving two separate Progress(...) + Phase(...) calls (two separate locks) allow.
func (*TaskHandle) Update ¶ added in v0.3.0
func (t *TaskHandle) Update(object string, call func() error, opts ...EffectOption) error
Update records an update of object.
func (*TaskHandle) Warn ¶ added in v0.3.0
func (t *TaskHandle) Warn(summary string, args ...any)
Warn accumulates a warning annotation on the task. This is a statement, not a fluent chain — Warn returns nothing, so a bare `task.Warn("summary")` is errcheck-clean, matching Fail/Block (beginner-9: doc.go's no-fluent promise). Unlike Fail/Block, Warn does not resolve the task (13-problem doc P2: "warnings annotate lifecycle; they do not replace it") — call it any number of times before the task's terminal verb. A warned task that never reaches a terminal verb auto-resolves Done at Finish. summary is a printf format when fmt args are present — one text spelling shared with Done/Task/Group/Reason (C6); evo.Detail(...) and other ProblemOptions may be mixed into args in any position and still apply.
func (*TaskHandle) Write ¶ added in v0.3.0
func (t *TaskHandle) Write(object string, call func() error, opts ...EffectOption) error
Write records the writing of object.
func (*TaskHandle) Writer ¶ added in v0.4.0
func (t *TaskHandle) Writer() io.Writer
Writer returns a line-buffered io.Writer for narrating a talkative child process: each complete line (CR or LF terminated, trimmed, non-empty) becomes the task's live doing-text (see Doing), and every byte is also retained in the task's Evidence ring (get-or-create, shared with Task.Evidence) so DetailTail has proof after Fail. Lines pass through the same sanitize layer as Task.Doing, so hostile escape sequences never reach the display. Off a TTY, these mirrored lines update the live status only — they never force their own durable row the way an explicit TaskHandle.Doing call does, since the Evidence ring (and its failure-path DetailTail) is already the child's one durable home (release-gate round 9 finding 4). Concurrent-safe. Named Writer, not PhaseWriter (P6/rename): an io.Writer sink whose lines become the live-status text, following logrus's Logger.Writer()/zapio.Writer precedent.
cmd.Stdout = evo.Task("push").Writer()
type TasksSnapshot ¶
type TasksSnapshot = core.TasksSnapshot
TasksSnapshot is an immutable collection view.
type TaxonomyReason ¶ added in v0.3.0
type TaxonomyReason struct {
// contains filtered or unexported fields
}
TaxonomyReason names why a task skipped or kept an item. It is the opaque handle returned by evo.Reason — duplicate strings merge into one taxonomy bucket so a caller can construct one inline at every call site (evo.Reason("dirty")) without hand-tracking identity, or lift it to a package var once it repeats.
func Reason ¶ added in v0.3.0
func Reason(name string, args ...any) TaxonomyReason
Reason returns a get-or-create taxonomy Reason by name on the default instance registry — duplicate strings merge into one bucket, so an inline evo.Reason("protected") at every call site is always legal; lifting it to a package var (var reasonProtected = evo.Reason("protected")) is optional, not required for correctness. name is a printf format when args are present (fmt.Sprintf semantics) — one text spelling shared with Task/Group (C6); evo.ForSkip()/evo.OnTask(...) may be mixed into args in any position and still applies, exactly like Task's evo.ID.
func (TaxonomyReason) Name ¶ added in v0.3.0
func (r TaxonomyReason) Name() string
Name returns the reason's display label.
type TaxonomyRecord ¶ added in v0.3.0
type TaxonomyRecord = core.TaxonomyRecord
TaxonomyRecord is one accumulated (reason, name) disposition entry — recorded by TaskHandle.Skipped or TaskHandle.Kept, never assembled by hand.
type TerminalDriver ¶
type TerminalDriver interface {
ID() string
}
TerminalDriver is the exclusive owner of terminal control sequences. Interactive implementation arrives in v0.2; the interface is defined early so options and tests compile.
type TimeSource ¶
TimeSource provides the current time for deterministic tests. Option constructor is Clock(TimeSource) to match the public API examples.
type Verbosity ¶ added in v0.2.0
type Verbosity int
Verbosity selects which message visibilities are projected to the human stream. Zero is normal (non-verbose) human detail.
const ( // VerbosityNormal projects Normal-visibility messages only. VerbosityNormal Verbosity = iota // VerbosityVerbose also projects Verbose-visibility messages. It also // expands each TaskHandle.Skipped/Kept taxonomy row from its default // aggregated "! skipped N (reason1, reason2)" count into one named line // per reason ("reason: name1, name2, ..."). The names themselves are // never lost at VerbosityNormal — they are always present on the Go // TaskSnapshot.Skipped/Kept fields (returned by Output.Snapshot and // TaskHandle.Snapshot); VerbosityVerbose only changes whether the plain // human render surfaces them. The wire JSONDocument (JSONTask) does not // currently carry Skipped/Kept at all — read the Go snapshot directly to // get the names programmatically. VerbosityVerbose )
type Visibility ¶ added in v0.2.0
type Visibility = core.Visibility
Visibility selects whether a message is ordinary or verbose user detail. Zero is VisibilityNormal.
Aliased into internal/core alongside the rest of the data model — see Snapshot's doc comment (snapshot.go) for why.
Source Files
¶
- action.go
- capability.go
- capture.go
- changes.go
- clock.go
- conclusion.go
- config.go
- confirm.go
- conjugate.go
- construct.go
- debug.go
- debug_writer.go
- default.go
- displaygroup.go
- doc.go
- each.go
- effect.go
- entity.go
- errors.go
- event.go
- fact.go
- failure.go
- glyph.go
- jsonout.go
- live.go
- misuse_hints.go
- names.go
- option.go
- output.go
- phase_writer.go
- plain.go
- plan.go
- print.go
- problem.go
- progressive.go
- projection.go
- reason.go
- redact.go
- release.go
- run.go
- run_annotations.go
- sequence.go
- slog.go
- snapshot.go
- state.go
- suspend.go
- task.go
- task_mutations.go
- task_run.go
- task_taxonomy.go
- writer.go
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
evident-output
command
Command evident-output provides review/preview/explain CLI parity with MCP tools.
|
Command evident-output provides review/preview/explain CLI parity with MCP tools. |
|
evident-output-mcp
command
Command evident-output-mcp is the stdio MCP server.
|
Command evident-output-mcp is the stdio MCP server. |
|
examples
|
|
|
data-command
command
Command data-command: domain JSON on stdout, human presentation on stderr.
|
Command data-command: domain JSON on stdout, human presentation on stderr. |
|
debug-history
command
Command debug-history demos durable debug scrollback via slog.
|
Command debug-history demos durable debug scrollback via slog. |
|
debug-pane
command
Command debug-pane demos rolling slog-text viewport.
|
Command debug-pane demos rolling slog-text viewport. |
|
doctor
command
Command doctor is an environment/health check CLI.
|
Command doctor is an environment/health check CLI. |
|
install-pipeline
command
Command install-pipeline demos Tasks, Progress, Capture, and Main.
|
Command install-pipeline demos Tasks, Progress, Capture, and Main. |
|
internal/demo
Package demo is reserved for advanced example utilities.
|
Package demo is reserved for advanced example utilities. |
|
live-progress
command
Command live-progress is the ordinary multi-progress demo (user API only).
|
Command live-progress is the ordinary multi-progress demo (user API only). |
|
migrate
command
Command migrate demonstrates the mutation-verb effect boundary (Add/Create/Write, ...): the same call site records a planned effect under --dry-run (evo.DryRun) or a committed one when it actually runs, and evo derives Changed/Ready/Planned from what happened — the caller never chooses which ledger a mutation lands in.
|
Command migrate demonstrates the mutation-verb effect boundary (Add/Create/Write, ...): the same call site records a planned effect under --dry-run (evo.DryRun) or a committed one when it actually runs, and evo derives Changed/Ready/Planned from what happened — the caller never chooses which ledger a mutation lands in. |
|
print
command
Command print is the first adoption rung: Print/Printf/Println like fmt.
|
Command print is the first adoption rung: Print/Printf/Println like fmt. |
|
repo-status
command
Command repo-status is a realistic "is this repo safe to retire?" check.
|
Command repo-status is a realistic "is this repo safe to retire?" check. |
|
scope-plugin
command
Command scope-plugin demos evo.Scope + evo.ID for plugin-owned presentation.
|
Command scope-plugin demos evo.Scope + evo.ID for plugin-owned presentation. |
|
terminal-driver
command
Command terminal-driver is an advanced teaching surface: custom TerminalDriver, frame-by-frame logging, and explicit renderer knobs.
|
Command terminal-driver is an advanced teaching surface: custom TerminalDriver, frame-by-frame logging, and explicit renderer knobs. |
|
verbose
command
Command verbose shows Normal vs Verbose message visibility.
|
Command verbose shows Normal vs Verbose message visibility. |
|
internal
|
|
|
agent/adopt
Package adopt inventories non-evo CLI output in an existing codebase and proposes a migration plan keyed to the adoption ladder (Init/Main → Task/Done → effects → containers → facts/warnings → confirm/dry-run).
|
Package adopt inventories non-evo CLI output in an existing codebase and proposes a migration plan keyed to the adoption ladder (Init/Main → Task/Done → effects → containers → facts/warnings → confirm/dry-run). |
|
agent/catalog
Package catalog is the task-oriented guidance catalog for agent assistance.
|
Package catalog is the task-oriented guidance catalog for agent assistance. |
|
agent/harness
Package harness evaluates agent-assistance scenarios (§30.9, MCP-022/049).
|
Package harness evaluates agent-assistance scenarios (§30.9, MCP-022/049). |
|
agent/preview
Package preview generates multi-profile plain previews from snapshots.
|
Package preview generates multi-profile plain previews from snapshots. |
|
agent/review
Package review provides deterministic static review of Evident Output usage.
|
Package review provides deterministic static review of Evident Output usage. |
|
agent/rules
Package rules is the stable review-rule registry (Appendix C namespaces).
|
Package rules is the stable review-rule registry (Appendix C namespaces). |
|
agent/sections
Package sections is the full-docs counterpart to catalog: catalog holds hand-curated, token-budgeted guidance snippets; sections serves the whole authoritative doc corpus (reference, development, MCP wiring, the adoption ladder, and the per-concept guides catalog already owns) through one list/get pair, mirroring the Svelte MCP's list-sections / get-documentation shape.
|
Package sections is the full-docs counterpart to catalog: catalog holds hand-curated, token-budgeted guidance snippets; sections serves the whole authoritative doc corpus (reference, development, MCP wiring, the adoption ladder, and the per-concept guides catalog already owns) through one list/get pair, mirroring the Svelte MCP's list-sections / get-documentation shape. |
|
core
Package core owns evident-output's data model: the immutable, fields-only value types a finished or in-flight run presents (Snapshot, Problem, Conclusion, and peers) plus the pure functions that derive one from another (InferConclusion, SanitizeProblem, ...).
|
Package core owns evident-output's data model: the immutable, fields-only value types a finished or in-flight run presents (Snapshot, Problem, Conclusion, and peers) plus the pure functions that derive one from another (InferConclusion, SanitizeProblem, ...). |
|
render
Package render is evident-output's presentation machinery: plain, structured (JSON/JSONL), and interactive (live) projection of a internal/core Snapshot.
|
Package render is evident-output's presentation machinery: plain, structured (JSON/JSONL), and interactive (live) projection of a internal/core Snapshot. |
|
text
Package text is repo-owned CLI text machinery: sanitization, terminal cell width measurement, truncation, glyph-safe pluralization/conjugation.
|
Package text is repo-owned CLI text machinery: sanitization, terminal cell width measurement, truncation, glyph-safe pluralization/conjugation. |
|
wireschema
Package wireschema validates a rendered JSON document against evo's own published JSON Schema (schema/output.v1.json) — the facade that makes the schema file an enforced gate instead of prose nobody checks (P8: "today nothing references that file — make it a gate").
|
Package wireschema validates a rendered JSON document against evo's own published JSON Schema (schema/output.v1.json) — the facade that makes the schema file an enforced gate instead of prose nobody checks (P8: "today nothing references that file — make it a gate"). |
|
Package terminal provides production terminal drivers for Evident Output.
|
Package terminal provides production terminal drivers for Evident Output. |
|
Package testkit provides deterministic clocks, screens, and assertions for evo tests.
|
Package testkit provides deterministic clocks, screens, and assertions for evo tests. |
|
tools
|
|
|
gensections
command
Command gensections copies the docs the MCP server's list_sections / get_documentation tools serve into internal/agent/sections/embedded, the only directory go:embed can reach from that package.
|
Command gensections copies the docs the MCP server's list_sections / get_documentation tools serve into internal/agent/sections/embedded, the only directory go:embed can reach from that package. |
|
scripts/sync-release-pins
command
Command sync-release-pins rewrites install pins on portable surfaces to match evo.PublishedRelease.
|
Command sync-release-pins rewrites install pins on portable surfaces to match evo.PublishedRelease. |
|
scripts/traceability-check
command
Command traceability-check verifies every expected §31 ID is present, and that every .go file TRACEABILITY.md's Test(s) column names still exists (a moved or renamed test file that nobody updated the table for used to pass silently — IDs-present-only checking let the table rot green).
|
Command traceability-check verifies every expected §31 ID is present, and that every .go file TRACEABILITY.md's Test(s) column names still exists (a moved or renamed test file that nobody updated the table for used to pass silently — IDs-present-only checking let the table rot green). |