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 ¶
- func ShutdownCompleted(ctx context.Context, graph *PhaseGraph, outputs map[PhaseID]PhaseOutput) error
- func WriteDOT(w io.Writer, phases []PhaseSpec) error
- type PhaseDeps
- type PhaseGraph
- type PhaseGraphError
- type PhaseGraphResult
- type PhaseID
- type PhaseOutput
- type PhaseRunFunc
- type PhaseShutdownFunc
- type PhaseSpec
- type PhaseValidateFunc
- type ValidateOptions
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 ¶
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 ¶
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 ¶
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 ¶
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. |