phasegraph

package
v1.10.4 Latest Latest
Warning

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

Go to latest
Published: May 5, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package phasegraph is a stdlib-only DAG orchestrator for ordered, validated pipelines (daemon bootstrap, semantic indexing, live updates, evaluation).

The package validates a slice of PhaseSpec against three correctness rules (no duplicate IDs, no missing dependencies, no cycles), produces a topologically-sorted PhaseGraph, and runs phases in dependency order with reverse-topological shutdown.

SPEC §39 owns the contract; this package is the v1.10 reference implementation.

Threat model

  • Phase Run functions are caller-supplied closures. Per-phase timeouts are the caller's responsibility (wrap with context.WithTimeout). The library does not enforce per-phase budgets — it cannot impose policy on downstream consumers.
  • DOT output (see WriteDOT) contains only declared phase IDs and dependency edges. Both are static, developer-chosen constants. No file paths, env vars, or runtime data leak.
  • There is no public RunPhases([]PhaseSpec) overload: RunPhaseGraph only accepts a *PhaseGraph obtainable from a successful ValidatePhaseGraph call. This makes validate-before-run an API-shape invariant rather than a documentation note.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ShutdownCompleted

func ShutdownCompleted(ctx context.Context, graph *PhaseGraph, outputs map[PhaseID]PhaseOutput) error

ShutdownCompleted invokes Shutdown on every phase whose output was recorded in `outputs`, walking PhaseGraph.ShutdownOrder (reverse topological order) and skipping phases that never completed. Errors from individual Shutdown calls are aggregated via errors.Join and returned together. Shutdown itself never panics — a missing Shutdown func is a no-op.

Public so callers (e.g. test helpers, P02 daemon shutdown) can reuse the same teardown sequence outside of RunPhaseGraph.

func WriteDOT

func WriteDOT(w io.Writer, phases []PhaseSpec) error

WriteDOT emits a Graphviz `digraph` describing `phases`: one node per phase, one directed edge per Requires entry (drawn from dependent → dependency). Output is deterministic — nodes and edges are sorted by PhaseID.

SPEC §39.9 describes the artifact convention (`.helix/debug/phasegraph-*.dot`); the path is the caller's choice — this function only writes to the supplied io.Writer.

Threat note (T-57-01-03): output contains only phase IDs and edges, both caller-defined static constants. No file paths, env vars, or runtime data.

Types

type PhaseDeps

type PhaseDeps map[PhaseID]any

PhaseDeps is the dependency map passed to PhaseRunFunc. Keys are the PhaseIDs declared in PhaseSpec.Requires; values are the PhaseOutput of the phase with that ID.

type PhaseGraph

type PhaseGraph struct {
	Order         []PhaseSpec
	ShutdownOrder []PhaseSpec
}

PhaseGraph is the validated, topologically-sorted result of a successful ValidatePhaseGraph call. ShutdownOrder is always Reverse(Order).

func ValidatePhaseGraph

func ValidatePhaseGraph(phases []PhaseSpec) (*PhaseGraph, error)

ValidatePhaseGraph is the SPEC §39.3 entrypoint. It runs three correctness checks (no duplicate IDs, no missing deps, no cycles) and returns a topologically-sorted PhaseGraph on success.

On failure the returned error is always a PhaseGraphError — use errors.As to extract typed fields.

For a variant that emits a Graphviz `.dot` file on validation failure, see ValidatePhaseGraphWithOptions.

func ValidatePhaseGraphWithOptions

func ValidatePhaseGraphWithOptions(phases []PhaseSpec, opts ValidateOptions) (*PhaseGraph, error)

ValidatePhaseGraphWithOptions is the option-bearing variant of ValidatePhaseGraph. The original signature is preserved so existing callers do not need to adopt the options struct.

When opts.DOTSink is non-nil and validation fails, a Graphviz digraph is written to the sink before the error is returned. WriteDOT errors are intentionally suppressed: they are debug-only output, and surfacing them would mask the original PhaseGraphError that the caller cares about.

type PhaseGraphError

type PhaseGraphError struct {
	Kind    string
	Phase   PhaseID
	Missing []PhaseID
	Cycle   []PhaseID
}

PhaseGraphError is the typed error returned by ValidatePhaseGraph on every failure mode. Callers should use errors.As to discriminate — never assert on Error() string contents.

Field population by Kind:

"duplicate_phase"    → Phase
"missing_dependency" → Missing
"cycle"              → Cycle (offending node sequence; len ≥ 1)

func (PhaseGraphError) Error

func (e PhaseGraphError) Error() string

Error formats the typed fields into a human-readable message. Format is stable but not part of the public API — tests assert on the typed fields.

type PhaseGraphResult

type PhaseGraphResult struct {
	PhaseDurations map[PhaseID]time.Duration
	Outputs        map[PhaseID]PhaseOutput
	Failed         PhaseID
}

PhaseGraphResult is what RunPhaseGraph returns. Failed is empty on success and set to the offending PhaseID on failure. PhaseDurations records every phase whose Run was invoked, including the failing one.

func RunPhaseGraph

func RunPhaseGraph(ctx context.Context, graph *PhaseGraph) (*PhaseGraphResult, error)

RunPhaseGraph executes a validated PhaseGraph in topological order. On any Run or Validate error it invokes Shutdown for every phase whose output was recorded so far (in reverse-topological order) and returns the original error wrapped in the result.

The contract is verbatim from SPEC §39.8.

Per-phase deadlines are NOT enforced here — callers wrap ctx with context.WithTimeout before invoking RunPhaseGraph or wrap individual PhaseRunFunc closures with their own budget. See package doc threat note.

func (*PhaseGraphResult) Record

func (r *PhaseGraphResult) Record(id PhaseID, d time.Duration, err error)

Record appends one phase's wall-clock duration to PhaseDurations and, if err is non-nil, marks the result as failed at that phase. It allocates the PhaseDurations map lazily so a zero-value PhaseGraphResult is usable.

type PhaseID

type PhaseID string

PhaseID is the stable string identifier for a phase. The empty PhaseID is reserved as the "no-phase" sentinel (e.g., PhaseGraphResult.Failed is empty on success).

type PhaseOutput

type PhaseOutput interface{}

PhaseOutput is the value a phase emits and that downstream phases consume. It is intentionally an empty interface: phases type-assert at the read site rather than threading a generic parameter through the entire library (CONTEXT.md §"Claude's Discretion").

type PhaseRunFunc

type PhaseRunFunc func(ctx context.Context, deps PhaseDeps) (PhaseOutput, error)

PhaseRunFunc is the executable body of a phase. ctx carries cancellation; deps holds outputs from already-completed dependency phases.

type PhaseShutdownFunc

type PhaseShutdownFunc func(ctx context.Context, output PhaseOutput) error

PhaseShutdownFunc is invoked in reverse-topological order on every phase whose output was recorded in the outputs map. It runs both on a clean teardown and after a Run/Validate failure mid-pipeline.

type PhaseSpec

type PhaseSpec struct {
	ID       PhaseID
	Requires []PhaseID
	Provides []string
	Run      PhaseRunFunc
	Validate PhaseValidateFunc
	Shutdown PhaseShutdownFunc
}

PhaseSpec is the static declaration of one phase. The contract is verbatim from SPEC §39.2 — fields are intentionally exported so callers compose pipelines as ordinary slice literals.

type PhaseValidateFunc

type PhaseValidateFunc func(output PhaseOutput) error

PhaseValidateFunc lets a phase optionally validate its own output before downstream phases see it. A non-nil error aborts the run and triggers shutdown of completed phases.

type ValidateOptions

type ValidateOptions struct {
	// DOTSink, when non-nil, receives a Graphviz `digraph` rendering of
	// `phases` whenever validation fails. SPEC §39.9 calls out the
	// `.helix/debug/phasegraph-*.dot` path convention; the writer choice is
	// the caller's. On success nothing is written.
	DOTSink io.Writer
}

ValidateOptions is the optional configuration accepted by ValidatePhaseGraphWithOptions. Each field is independently optional — the zero value is equivalent to calling ValidatePhaseGraph.

Directories

Path Synopsis
Package pipelines ships the SHAPE of Helix's order-sensitive workflows (semantic indexing, live updates, evaluation runs) as []phasegraph.PhaseSpec literals.
Package pipelines ships the SHAPE of Helix's order-sensitive workflows (semantic indexing, live updates, evaluation runs) as []phasegraph.PhaseSpec literals.

Jump to

Keyboard shortcuts

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