Documentation
¶
Overview ¶
Package tracker is the public library API for running and inspecting tracker pipelines.
API stability: tracker is pre-v1.0. Public APIs are intended to be production-usable, but breaking changes may still occur between minor releases while the surface is finalized. Read CHANGELOG.md before upgrading.
ABOUTME: Top-level convenience API for running pipelines (.dip preferred, .dot deprecated) with auto-wired dependencies. ABOUTME: Consumers import only this package — LLM clients, registries, and environments are built automatically.
ABOUTME: Shared helpers for resolving run directories and parsing activity.jsonl. ABOUTME: Promoted from cmd/tracker/ so library and CLI use one implementation.
ABOUTME: Library API for auditing a completed pipeline run. ABOUTME: Returns structured timeline, retries, errors, and recommendations.
ABOUTME: Git bundle export for pipeline run artifact repositories. ABOUTME: ExportBundle wraps `git bundle create --all` so a completed run can be ABOUTME: shipped as a single portable file and restored with `git clone <bundle>`.
ABOUTME: Library API for diagnosing pipeline run failures. ABOUTME: Reads checkpoint + status.json + activity.jsonl and returns a structured report.
ABOUTME: Library API for preflight health checks. ABOUTME: Pure read-only — no network probes unless ProbeProviders: true.
ABOUTME: Public NDJSON event writer for the tracker --json wire format. ABOUTME: Threaded from pipeline/LLM/agent event streams; thread-safe for concurrent writers.
ABOUTME: Library-facing helpers for resolving pipeline sources and run checkpoints. ABOUTME: Mirrors the CLI's bare-name and run-ID resolution for library consumers.
ABOUTME: Library API for dry-running a pipeline: returns the parsed graph, ABOUTME: BFS execution plan, and list of unreachable nodes.
ABOUTME: Built-in workflow catalog — embedded .dip files and name resolution. ABOUTME: Library consumers can list, read, and resolve workflows without shelling to the CLI.
Index ¶
- Constants
- Variables
- func ExportBundle(runDir, outPath string) error
- func MostRecentRunID(workdir string) (string, error)
- func ResolveActivityLogPath(runDir string) (path string, secureUsed bool)
- func ResolveBudgetLimits(cfg pipeline.BudgetLimits, graph *pipeline.Graph) pipeline.BudgetLimits
- func ResolveCheckpoint(workDir, runID string) (string, error)
- func ResolveProviderBaseURL(provider string) string
- func ResolveProviderBaseURLStrict(provider string) (string, error)
- func ResolveRunDir(workdir, runID string) (string, error)
- func SortActivityByTime(entries []ActivityEntry)
- type ActivityEntry
- type ActivityError
- type ActivityLogScan
- type AuditConfig
- type AuditReport
- type BudgetHalt
- type CheckDetail
- type CheckResult
- type CheckStatus
- type Config
- type CostReport
- type DiagnoseConfig
- type DiagnoseReport
- type DoctorConfig
- type DoctorOption
- type DoctorReport
- type Engine
- type GatewayKind
- type GitConfig
- type GitPreflight
- type NDJSONWriter
- type NodeFailure
- type PlanStep
- type Result
- type RetryRecord
- type RunSummary
- type SimEdge
- type SimNode
- type SimulateReport
- type StreamEvent
- type Suggestion
- type SuggestionKind
- type TimelineEntry
- type ValidateOption
- type ValidationResult
- type WebhookGateConfig
- type WorkflowInfo
Examples ¶
Constants ¶
const ( FormatDip = "dip" // Dippin format (current, default) FormatDOT = "dot" // DOT/Graphviz format (deprecated) )
Pipeline format identifiers.
const ( GitPreflightAuto = pipeline.GitPreflightAuto GitPreflightOff = pipeline.GitPreflightOff GitPreflightWarn = pipeline.GitPreflightWarn GitPreflightRequire = pipeline.GitPreflightRequire GitPreflightInit = pipeline.GitPreflightInit )
GitPreflight values re-exported from pipeline for caller convenience.
const PinnedDippinVersion = "v0.39.0"
PinnedDippinVersion is the dippin-lang version from go.mod. Kept in sync with go.mod by TestPinnedDippinVersionMatchesGoMod.
Variables ¶
var ErrGatewayRouteRefused = errors.New("gateway route refused: kind/provider combination unsupported or unknown")
ErrGatewayRouteRefused is returned by the strict resolver functions when a gateway URL is configured but the (kind, provider) pair is unsupported or the kind is unknown. Surfacing this as an error prevents the silent SDK-default fallback (e.g. openai-compat defaulting to openrouter.ai) that contradicts the documented fail-closed semantics of #276.
Functions ¶
func ExportBundle ¶
ExportBundle writes a git bundle of the run directory's artifact repository to outPath. The bundle captures all commits and tags (including checkpoint tags) produced by WithGitArtifacts during the run. Users can restore the run with `git clone <bundlePath> <dir>` and inspect history with `git log`.
Returns an error if runDir is not a git repository, if git is not in PATH, or if `git bundle create` fails. The error wraps the command's stderr so callers can surface meaningful diagnostics.
The bundle file is portable — it can be copied to another machine and cloned there without network access. This is the recommended way for a remote factory-worker instance to hand a completed run back to the user.
func MostRecentRunID ¶
MostRecentRunID returns the run ID of the most recent run (by checkpoint timestamp) under workdir. Returns an error if no runs with valid checkpoints exist.
func ResolveActivityLogPath ¶
ResolveActivityLogPath returns the on-disk location of the activity log for runDir. It prefers the integrity-protected secure path (#213) when present, falling back to <runDir>/activity.jsonl for pre-#213 runs and post-run snapshots. The returned secureUsed flag is true when the path came from the secure location — callers that validate the runtime sentinel should only do so in that case.
runID is derived from runDir's basename, matching the .tracker/runs/<runID> layout enforced by ResolveRunDir.
func ResolveBudgetLimits ¶
func ResolveBudgetLimits(cfg pipeline.BudgetLimits, graph *pipeline.Graph) pipeline.BudgetLimits
ResolveBudgetLimits fills any zero field on cfg from the matching workflow-level default in graph.Attrs. Config values take precedence — the graph attrs are only consulted for fields the caller left unset. Returns the original cfg unchanged if graph is nil or has no attrs.
The graph-level keys consulted are max_total_tokens, max_cost_cents, and max_wall_time, which the dippin adapter writes from WorkflowDefaults.Max* fields in v0.21.0+.
Exported so the tracker CLI can merge its --max-* flag values with workflow defaults without re-implementing the same logic.
func ResolveCheckpoint ¶
ResolveCheckpoint finds the checkpoint file path for a given run ID under the working directory's .tracker/runs/<runID>/checkpoint.json layout. The runID argument may be a unique prefix of a real run ID.
Returns the path to checkpoint.json (relative to workDir if workDir is relative), or an error if the run is not found, the prefix is ambiguous, or the checkpoint file is missing.
This is the library equivalent of the CLI's `tracker -r <runID>` flag. Library consumers can set Config.ResumeRunID to have NewEngine resolve the checkpoint automatically.
func ResolveProviderBaseURL ¶
ResolveProviderBaseURL returns the base URL a provider's HTTP client should use. Resolution order:
- The provider-specific env var (ANTHROPIC_BASE_URL, OPENAI_BASE_URL, GEMINI_BASE_URL, OPENAI_COMPAT_BASE_URL).
- TRACKER_GATEWAY_URL with a per-provider suffix appended; the suffix map is selected by TRACKER_GATEWAY_KIND (default cf-aig — Cloudflare AI Gateway conventions).
- Empty string, meaning the provider's SDK default.
Per-provider env vars always win over TRACKER_GATEWAY_URL.
**Lax variant.** This function returns the empty string for BOTH "no gateway configured" AND "gateway configured but routing refused." It is preserved for backward compatibility with library callers that existed before #276 added kind dispatch. New code on the adapter construction path MUST use ResolveProviderBaseURLStrict so that refuse-to-route surfaces as an error rather than a silent SDK-default fallback.
func ResolveProviderBaseURLStrict ¶ added in v0.36.0
ResolveProviderBaseURLStrict is the fail-closed sibling of ResolveProviderBaseURL. It returns the same URL resolution but distinguishes "no gateway needed" (returns "", nil) from "gateway configured but routing refused" (returns "", ErrGatewayRouteRefused wrapped). Adapter constructors call this so a misconfigured gateway cannot silently leak requests to public SDK default endpoints.
func ResolveRunDir ¶
ResolveRunDir finds the run directory under <workdir>/.tracker/runs matching runID by exact name or unique prefix. Returns an absolute path.
func SortActivityByTime ¶
func SortActivityByTime(entries []ActivityEntry)
SortActivityByTime sorts entries ascending by Timestamp.
Types ¶
type ActivityEntry ¶
type ActivityEntry struct {
Timestamp time.Time
Type string
RunID string
NodeID string
Message string
Error string
// Override fields — populated for "validation_overridden" entries.
// Mirror the wire-format fields written by the runtime's
// jsonlLogEntry (see pipeline/events_jsonl.go): the gate that
// produced the override, the label that selected the override
// edge, who acted, and the subgraph_path when propagated up from
// a child run. Empty for non-override entries.
OverrideGate string
OverrideLabel string
OverrideActor pipeline.Actor
OverrideSubgraphPath []string
}
ActivityEntry is a parsed line from activity.jsonl. Populate via ParseActivityLine — ActivityEntry is not itself a JSON-wire type because tracker has historically used two timestamp formats and time.Time's default unmarshal handles only RFC3339Nano.
Marshal/unmarshal contract: do not json.Marshal/json.Unmarshal ActivityEntry directly. Use ParseActivityLine and LoadActivityLog for decoding and map to your own wire type when encoding.
func LoadActivityLog ¶
func LoadActivityLog(runDir string) ([]ActivityEntry, error)
LoadActivityLog reads and parses the activity log for runDir, preferring the integrity-protected secure path with fallback to the legacy <runDir>/activity.jsonl. Returns (nil, nil) if neither location has a file. Malformed lines are skipped. Sentinel-stripped lines that don't parse as JSON are dropped silently — callers needing tamper-detection granularity should use ScanActivityLog (or the Diagnose path).
func ParseActivityLine ¶
func ParseActivityLine(line string) (ActivityEntry, bool)
ParseActivityLine decodes a single JSONL line. Returns (zero, false) on any parse error.
type ActivityError ¶
type ActivityError struct {
Timestamp time.Time `json:"ts"`
NodeID string `json:"node_id,omitempty"`
Message string `json:"message"`
}
ActivityError is an error entry extracted from the activity log.
type ActivityLogScan ¶
type ActivityLogScan struct {
Path string
SecureUsed bool
Entries []ActivityEntry
InjectedLines int
TotalLines int
SentinelLines int
}
ActivityLogScan is the structured result of reading an activity log. Path is the on-disk location read; SecureUsed reflects whether the integrity-protected secure log was the source; InjectedLines counts non-sentinel lines observed when reading from the secure path (always 0 when SecureUsed is false — legacy/snapshot files don't carry the runtime sentinel, so absence is not a signal).
func ScanActivityLog ¶
func ScanActivityLog(runDir string) (*ActivityLogScan, error)
ScanActivityLog is LoadActivityLog with tamper-detection counters exposed for callers (e.g. Diagnose) that need to surface injection signals. Lines without the runtime sentinel prefix in the secure file count toward InjectedLines; the line is still parsed best-effort so its content is visible to forensics.
type AuditConfig ¶
type AuditConfig struct {
// LogWriter receives non-fatal warnings (unreadable activity.jsonl
// in a run directory, etc.). Nil is treated as io.Discard so
// embedded library callers do not see warnings on os.Stderr. The
// tracker CLI sets this to io.Discard for user-facing commands.
LogWriter io.Writer
}
AuditConfig configures an Audit() or ListRuns() call.
type AuditReport ¶
type AuditReport struct {
RunID string `json:"run_id"`
// Status is one of:
// - "success"
// - "fail"
// - "budget_exceeded"
// - "validation_overridden"
// The enum is open — future minor releases may add new values. Consumers
// should use StatusClass for stable {succeeded|failed} bucketing.
// See classifyStatus for the resolution algorithm.
Status string `json:"status"`
// StatusClass is one of "succeeded" or "failed" — stable companion to
// Status for downstream consumers that need bucket categorization that
// survives future enum extensions. Computed via
// pipeline.TerminalStatus(Status).IsSuccess().
StatusClass string `json:"status_class"`
// TotalDuration is encoded as integer nanoseconds in JSON
// ("total_duration_ns"), not as a duration string.
TotalDuration time.Duration `json:"total_duration_ns"`
Timeline []TimelineEntry `json:"timeline"`
Retries []RetryRecord `json:"retries,omitempty"`
Errors []ActivityError `json:"errors,omitempty"`
Recommendations []string `json:"recommendations,omitempty"`
// CompletedNodes is the number of completed nodes recorded in checkpoint.json.
CompletedNodes int `json:"completed_nodes"`
// RestartCount is the checkpoint restart counter for the run.
RestartCount int `json:"restart_count"`
// CheckpointTimestamp is the last checkpoint write time.
CheckpointTimestamp time.Time `json:"checkpoint_timestamp"`
// BundleIdentity is the content-addressed identity ("sha256:<hex>") of
// the .dipx bundle the run was executed against. Read from the run's
// checkpoint. Empty for runs from a plain .dip file.
BundleIdentity string `json:"bundle_identity,omitempty"`
// ValidationOverrides is populated when one or more override edges were
// traversed during the run. Sourced from activity-log
// EventValidationOverridden entries (chronological order); falls back to
// Checkpoint.ValidationOverrides when the activity log carries no
// override entries. Empty for runs with no override edges.
ValidationOverrides []pipeline.OverrideDetail `json:"validation_overrides,omitempty"`
// OverrideCount is len(ValidationOverrides). Kept as its own field so
// thin consumers can read it without unmarshaling the slice.
OverrideCount int `json:"override_count,omitempty"`
}
AuditReport is the structured result of Audit().
func Audit ¶
func Audit(ctx context.Context, runDir string) (*AuditReport, error)
Audit reads checkpoint.json and activity.jsonl under runDir and returns a structured report.
The runDir argument must be a trusted path — Audit reads checkpoint.json and activity.jsonl directly under it. For user-supplied input, resolve the path via ResolveRunDir or use MostRecentRunID first, which enforce the .tracker/runs/<runID> layout.
ctx is checked at entry so a caller that passes an already-cancelled context gets an immediate error instead of silent work. Full cancellation mid-parse would require threading ctx through pipeline.LoadCheckpoint and LoadActivityLog, which is out of scope today (both are fast and bounded). Nil is coalesced to context.Background().
Audit does not accept AuditConfig — it emits no warnings to suppress. Use ListRuns + AuditConfig{LogWriter} for bulk enumeration where the summary builder may skip unreadable activity logs.
type BudgetHalt ¶
type BudgetHalt struct {
TotalTokens int `json:"total_tokens"`
TotalCostUSD float64 `json:"total_cost_usd"`
WallElapsedMs int64 `json:"wall_elapsed_ms"`
Message string `json:"message"`
}
BudgetHalt holds information about a budget halt detected in the activity log.
type CheckDetail ¶
type CheckDetail struct {
Status CheckStatus `json:"status"` // "ok" | "warn" | "error" | "hint"
Message string `json:"message"`
Hint string `json:"hint,omitempty"`
}
CheckDetail is one sub-line within a CheckResult — used for per-item status lines (per-provider, per-binary, per-subdirectory).
type CheckResult ¶
type CheckResult struct {
Name string `json:"name"`
Status CheckStatus `json:"status"` // "ok" | "warn" | "error" | "skip"
Message string `json:"message,omitempty"`
Hint string `json:"hint,omitempty"`
Details []CheckDetail `json:"details,omitempty"`
}
CheckResult is one section of a DoctorReport.
type CheckStatus ¶
type CheckStatus string
CheckStatus is the status of a CheckResult or CheckDetail. Enum-like typed string so consumers can switch-exhaust. "hint" is only valid on CheckDetail.Status (informational sub-items such as optional providers not configured).
const ( CheckStatusOK CheckStatus = "ok" CheckStatusWarn CheckStatus = "warn" CheckStatusError CheckStatus = "error" CheckStatusSkip CheckStatus = "skip" CheckStatusHint CheckStatus = "hint" )
CheckStatus values.
type Config ¶
type Config struct {
WorkingDir string // default: os.Getwd()
CheckpointDir string // checkpoint file path (checkpoint.json); default: empty (engine auto-generates)
ResumeRunID string // optional: resume a previous run by ID or unique prefix; resolved via ResolveCheckpoint
ArtifactDir string // default: empty (engine auto-generates)
Format string // "dip" (default), "dot" (deprecated); empty = auto-detect
Model string // default: env or claude-sonnet-4-6; graph-level attrs take precedence
Provider string // default: auto-detect from env
RetryPolicy string // "none" (default), "standard", "aggressive"; graph-level attrs take precedence
EventHandler pipeline.PipelineEventHandler // optional: live pipeline events
AgentEvents agent.EventHandler // optional: live agent session events
LLMClient agent.Completer // optional: override auto-created client
Context map[string]string // optional: initial pipeline context
Params map[string]string // optional: override declared workflow params (keys without "params." prefix)
Backend string // "native" (default), "claude-code", "acp"; selects agent backend
Autopilot string // "" (interactive), "lax", "mid", "hard", "mentor"; LLM-driven gate decisions
AutoApprove bool // auto-approve all human gates with default/first option
Budget pipeline.BudgetLimits // configures pipeline-level token, cost, and wall-time ceilings
// GatewayURL is the root URL of a Cloudflare AI Gateway (or any compatible
// proxy). When non-empty it is used as the base for all provider URLs, with
// the per-provider suffix appended (e.g. "<gateway>/anthropic"). A
// per-provider *_BASE_URL env var always takes precedence over GatewayURL so
// library callers can still override individual providers. The TRACKER_GATEWAY_URL
// env var is the fallback when GatewayURL is empty.
GatewayURL string
// GatewayKind selects the path convention used with GatewayURL (or its
// TRACKER_GATEWAY_URL env-var fallback). Empty or GatewayKindCFAIG
// (default, backcompat) routes via Cloudflare AI Gateway conventions:
// /anthropic, /openai, /google-ai-studio, /compat. GatewayKindBedrock
// targets the 2389 bedrock-gateway Worker which uses native SDK paths.
// The TRACKER_GATEWAY_KIND env var is the fallback when GatewayKind is
// empty. See ResolveProviderBaseURL.
GatewayKind GatewayKind
WebhookGate *WebhookGateConfig // optional: post human gates to an HTTP webhook and wait for callback
// BundleIdentity is the content-addressed identity ("sha256:<hex>") of
// the .dipx bundle this run was loaded from. Stamped onto every emitted
// PipelineEvent and persisted to the checkpoint for resume verification.
// Empty (the default) is a no-op and matches plain .dip behavior.
//
// Callers that build their own JSONLEventHandler should also call
// activityLog.SetBundleIdentity(cfg.BundleIdentity) so agent/llm writes
// outside the engine event chain carry the same provenance.
BundleIdentity string
// Git configures the v0.29.0 git preflight check. Nil = auto, which
// respects the workflow's `requires:` block. See GitConfig.
Git *GitConfig
}
Config controls pipeline execution. All fields are optional. Zero-value Config uses environment variables for LLM credentials, the current working directory, and auto-generated run directories.
type CostReport ¶
type CostReport struct {
TotalUSD float64
ByProvider map[string]llm.ProviderCost
LimitsHit []string
}
CostReport summarizes spend for a pipeline run. TotalUSD is the sum of ByProvider[*].USD. LimitsHit names the budget dimensions that halted the run (empty when the run completed normally).
type DiagnoseConfig ¶
type DiagnoseConfig struct {
// LogWriter receives non-fatal parse/read warnings — specifically
// malformed status.json content (one warning per bad file) and
// bufio.Scanner errors while reading activity.jsonl (e.g. lines
// exceeding the 1 MB buffer limit, I/O failures). Nil is treated
// as io.Discard so library callers do not see stray warnings on
// os.Stderr. The tracker CLI sets this to io.Discard for user-
// facing commands.
LogWriter io.Writer
}
DiagnoseConfig configures a Diagnose() run.
type DiagnoseReport ¶
type DiagnoseReport struct {
RunID string `json:"run_id"`
CompletedNodes int `json:"completed_nodes"`
BudgetHalt *BudgetHalt `json:"budget_halt,omitempty"`
// ValidationOverrides is the list of override edges traversed during the run.
// Sourced from activity log first, with checkpoint fallback for runs whose
// activity log is missing (legacy / archived). Empty for runs with no
// override edges. Populated for every terminal status (success, fail,
// budget_exceeded, validation_overridden) so override forensics survive
// failure-after-override scenarios. Per spec §9.4 the override section in
// `tracker diagnose` is informational only — override is NOT a failure and
// does NOT raise a Suggestion of its own.
ValidationOverrides []pipeline.OverrideDetail `json:"validation_overrides,omitempty"`
// OverrideCount is len(ValidationOverrides). Kept as its own field so
// JSON consumers can branch without iterating the slice.
OverrideCount int `json:"override_count,omitempty"`
Failures []NodeFailure `json:"failures"`
Suggestions []Suggestion `json:"suggestions"`
}
DiagnoseReport is the structured output of Diagnose / DiagnoseMostRecent.
func Diagnose ¶
func Diagnose(ctx context.Context, runDir string, opts ...DiagnoseConfig) (*DiagnoseReport, error)
Diagnose analyzes a run directory and returns a structured report.
The runDir argument must be a trusted path — Diagnose reads checkpoint.json, activity.jsonl, and every <nodeID>/status.json under it. For user-supplied input, resolve the path via ResolveRunDir or DiagnoseMostRecent first, which enforce the .tracker/runs/<runID> layout.
If ctx is cancelled mid-parse, Diagnose returns ctx.Err() — a partial report is never returned as a success, so callers using deadlines can distinguish complete from truncated analysis. A nil ctx is treated as context.Background() (no cancellation possible).
Example ¶
package main
import (
"context"
"fmt"
tracker "github.com/2389-research/tracker"
)
func main() {
report, err := tracker.Diagnose(context.Background(), "testdata/runs/failed")
if err != nil {
fmt.Println("diagnose failed")
return
}
if len(report.Failures) == 0 {
fmt.Println("no failures")
return
}
fmt.Println("failed node:", report.Failures[0].NodeID)
fmt.Println("retry count:", report.Failures[0].RetryCount)
}
Output: failed node: Build retry count: 2
func DiagnoseMostRecent ¶
func DiagnoseMostRecent(ctx context.Context, workdir string, opts ...DiagnoseConfig) (*DiagnoseReport, error)
DiagnoseMostRecent finds the most recent run under workdir and diagnoses it.
type DoctorConfig ¶
type DoctorConfig struct {
// WorkDir is the working directory to check. If empty, os.Getwd() is used.
WorkDir string
// Backend is the agent backend ("", "native", "claude-code"). When
// "claude-code", a missing claude binary is a hard error.
Backend string
// ProbeProviders, when true, makes a minimal network call to each
// configured provider to verify auth. Default false — key presence only.
ProbeProviders bool
// PipelineFile, when non-empty, adds a "Pipeline File" check that parses
// and validates the given .dip / .dot file.
PipelineFile string
// contains filtered or unexported fields
}
DoctorConfig configures a Doctor() run.
type DoctorOption ¶
type DoctorOption func(*DoctorConfig)
DoctorOption configures a Doctor run via a functional option.
func WithGitConfig ¶
func WithGitConfig(policy GitPreflight, allowInit bool) DoctorOption
WithGitConfig sets the git preflight policy considered by the Git Requires check. Library callers that don't set this get GitPreflightAuto behavior (respect workflow `requires:`). CLI doctor mode passes --git/--allow-init through this option.
func WithVersionInfo ¶
func WithVersionInfo(version, commit string) DoctorOption
WithVersionInfo attaches a tracker version and commit hash for display in the "Version Compatibility" check. CLI callers populate these from build-time ldflags; library callers typically do not need this.
type DoctorReport ¶
type DoctorReport struct {
Checks []CheckResult `json:"checks"`
OK bool `json:"ok"`
Warnings int `json:"warnings"`
Errors int `json:"errors"`
}
DoctorReport is the structured result of a Doctor() call.
func Doctor ¶
func Doctor(ctx context.Context, cfg DoctorConfig, opts ...DoctorOption) (*DoctorReport, error)
Doctor runs a suite of preflight checks and returns a structured report.
By default Doctor makes no network calls: provider configuration is detected via env-var presence and basic format validation. Set cfg.ProbeProviders = true to additionally make a 1-token API call per provider to verify auth. The CLI's "tracker doctor" command sets that flag; library callers should leave it false unless they specifically want live credential verification.
Provider probes and binary version lookups honor ctx: cancelling the context aborts in-flight checks. A nil context is treated as context.Background().
Write side effects (gitignore fix-up, workdir creation prompts) are NOT performed by Doctor — callers inspect the report and apply any fixes themselves.
Example ¶
package main
import (
"context"
"fmt"
"os"
tracker "github.com/2389-research/tracker"
)
func main() {
workDir, err := os.MkdirTemp("", "tracker-example-doctor-*")
if err != nil {
fmt.Println("doctor failed")
return
}
defer os.RemoveAll(workDir)
report, err := tracker.Doctor(context.Background(), tracker.DoctorConfig{
WorkDir: workDir,
})
if err != nil {
fmt.Println("doctor failed")
return
}
fmt.Println("checks:", len(report.Checks) > 0)
}
Output: checks: true
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine wraps pipeline.Engine with auto-wired internals.
func NewEngine ¶
NewEngine parses a pipeline source (.dip preferred, DOT deprecated), auto-wires all internals, and returns an Engine. Format is auto-detected from content if Config.Format is empty: sources starting with "digraph" or "strict digraph" are treated as DOT, everything else as .dip. The caller must call Close() when done to release resources.
This wrapper exists for backward compatibility: it uses context.Background() for the v0.29.0 git preflight. New callers that want to support cancellation of the preflight (especially with `--git=init` which has a `git init` side effect) should use NewEngineWithContext instead.
func NewEngineWithContext ¶
NewEngineWithContext is the context-aware form of NewEngine. The supplied ctx threads into the v0.29.0 git preflight check; a canceled context aborts preflight (including the `--git=init` `git init` side effect) rather than letting it complete before Engine.Run(ctx) observes the cancellation. tracker.Run(ctx, ...) calls this form so library callers who pass a real ctx get end-to-end cancellation coverage.
type GatewayKind ¶ added in v0.36.0
type GatewayKind string
GatewayKind selects the path convention used when TRACKER_GATEWAY_URL is set. The default (cf-aig) matches Cloudflare AI Gateway's per-provider subpath convention; bedrock targets the 2389 bedrock-gateway Worker which uses native SDK URL paths.
See docs/superpowers/specs/2026-06-01-issue-274-bedrock-gateway-integration-design.md.
const ( // GatewayKindCFAIG routes via Cloudflare AI Gateway path conventions: // /anthropic, /openai, /google-ai-studio, /compat. Default. GatewayKindCFAIG GatewayKind = "cf-aig" // GatewayKindBedrock routes via the 2389 bedrock-gateway Worker which // translates SDK requests to AWS Bedrock Converse. Uses native SDK // URL conventions: empty suffix for Anthropic, /v1 for OpenAI and // Gemini. openai-compat is not supported on this gateway. GatewayKindBedrock GatewayKind = "bedrock" )
type GitConfig ¶
type GitConfig struct {
Preflight GitPreflight
AllowInit bool
}
GitConfig configures the git preflight check that runs before any node executes. Zero value (or nil *GitConfig on Config.Git) resolves to GitPreflightAuto, which respects the workflow's `requires:` block.
AllowInit is required when Preflight == GitPreflightInit and stdin is not a TTY — it is the second safety latch on automatic `git init`.
type GitPreflight ¶
type GitPreflight = pipeline.GitPreflight
GitPreflight is the resolved preflight policy that controls the v0.29.0 git environment check. Type alias to pipeline.GitPreflight so callers don't have to import the pipeline package for this single value.
func ResolveGitConfig ¶
func ResolveGitConfig(cfg Config) (GitPreflight, bool)
ResolveGitConfig returns the (policy, allowInit) pair to apply for this run, considering Config.Git. The zero value resolves to (auto, false).
type NDJSONWriter ¶
type NDJSONWriter struct {
// contains filtered or unexported fields
}
NDJSONWriter is a thread-safe writer that serializes StreamEvents line by line onto an io.Writer. Library consumers use it to produce the same stream as the tracker CLI's --json mode.
Backpressure note: Write holds an internal mutex for the duration of the underlying io.Writer.Write call. When three handler sources (pipeline, agent, LLM trace) share one writer, a slow backing writer serializes handler callbacks across those sources. If the backing writer can block (network socket, pipe), wrap it in a bufio.Writer or a channel-backed forwarder to decouple producers from the slow sink.
func NewNDJSONWriter ¶
func NewNDJSONWriter(w io.Writer) *NDJSONWriter
NewNDJSONWriter returns a new writer backed by w.
Example ¶
package main
import (
"bytes"
"fmt"
tracker "github.com/2389-research/tracker"
)
func main() {
var buf bytes.Buffer
w := tracker.NewNDJSONWriter(&buf)
_ = w.Write(tracker.StreamEvent{
Timestamp: "2026-04-17T10:00:00.000Z",
Source: "pipeline",
Type: "pipeline_started",
RunID: "run-123",
})
fmt.Println(buf.Len() > 0)
}
Output: true
func (*NDJSONWriter) AgentHandler ¶
func (s *NDJSONWriter) AgentHandler() agent.EventHandler
AgentHandler returns an agent.EventHandler that writes agent events to this stream. Panics in the underlying writer are recovered (see PipelineHandler).
func (*NDJSONWriter) PipelineHandler ¶
func (s *NDJSONWriter) PipelineHandler() pipeline.PipelineEventHandler
PipelineHandler returns a pipeline.PipelineEventHandler that writes events to this stream. Panics in the underlying writer are recovered and logged to os.Stderr once (per writer instance) so a misbehaving sink cannot crash the pipeline goroutine.
func (*NDJSONWriter) TraceObserver ¶
func (s *NDJSONWriter) TraceObserver() llm.TraceObserver
TraceObserver returns an llm.TraceObserver that writes trace events to this stream. Panics in the underlying writer are recovered (see PipelineHandler).
func (*NDJSONWriter) Write ¶
func (s *NDJSONWriter) Write(evt StreamEvent) error
Write serializes evt as a JSON line. Safe to call from multiple goroutines. Returns a non-nil error if marshalling or writing to the underlying io.Writer fails, including short writes (io.Writer.Write may legally return n < len(data) with a nil error). The first write error is also logged to os.Stderr once so long-running callers that ignore the return value still surface it.
type NodeFailure ¶
type NodeFailure struct {
NodeID string `json:"node_id"`
Outcome string `json:"outcome"`
Handler string `json:"handler,omitempty"`
// Duration is the elapsed time for the most recent attempt of the node.
// It is encoded as integer nanoseconds in JSON ("duration_ns"), not
// as a duration string.
Duration time.Duration `json:"duration_ns,omitempty"`
// RetryCount is the number of stage_failed events observed for this node
// — i.e., the total failure count, not "retries beyond the first attempt."
// A node that failed once (no retry) has RetryCount == 1.
RetryCount int `json:"retry_count,omitempty"`
// IdenticalRetries is true when every stage_failed event had the same
// error/tool_error signature — a deterministic bug, not a flaky one.
IdenticalRetries bool `json:"identical_retries,omitempty"`
Stdout string `json:"stdout,omitempty"`
Stderr string `json:"stderr,omitempty"`
Errors []string `json:"errors,omitempty"`
}
NodeFailure captures everything known about a failed node.
type PlanStep ¶
type PlanStep struct {
Step int `json:"step"`
NodeID string `json:"node_id"`
Edges []SimEdge `json:"edges,omitempty"`
}
PlanStep is one step in an execution plan.
type Result ¶
type Result struct {
RunID string
// Status carries the run's terminal status. One of:
// - "success"
// - "fail"
// - "budget_exceeded"
// - "validation_overridden"
// The enum is open — future minor releases may add new values. Use
// pipeline.TerminalStatus(r.Status).IsSuccess() to classify rather than
// switching on the raw string.
Status string
CompletedNodes []string
Context map[string]string
EngineResult *pipeline.EngineResult
Trace *pipeline.Trace // full execution trace (nodes, timing, stats)
TokensByProvider map[string]llm.Usage // per-provider token totals
ToolCallsByName map[string]int // tool call counts by name
Cost *CostReport // per-provider cost rollup; nil when no usage recorded
// ArtifactRunDir is the run-specific artifact directory (e.g.
// "<artifactDir>/<runID>"). Populated when WithArtifactDir is set via
// Config.ArtifactDir. Pass this to ExportBundle to create a portable
// git bundle of the run's history.
ArtifactRunDir string
// BundlePath is the path of the exported git bundle. Populated only when
// ExportBundle is invoked by the caller after Run completes.
BundlePath string
// BundleIdentity is the content-addressed identity ("sha256:<hex>") of
// the .dipx bundle the run was loaded from, mirrored from Config.BundleIdentity
// for the caller's convenience. Empty for plain .dip runs.
BundleIdentity string
// ValidationOverrides is the list of override edges traversed during the run.
// Empty for runs with no override edges. Populated for every terminal status
// (including fail and budget_exceeded) so forensics see overrides even when
// failure dominates.
ValidationOverrides []pipeline.OverrideDetail
}
Result contains the outcome of a pipeline execution.
type RetryRecord ¶
RetryRecord records how many times a node was retried.
type RunSummary ¶
type RunSummary struct {
RunID string `json:"run_id"`
// Status is one of: "success", "fail", "budget_exceeded",
// "validation_overridden". Open enum; prefer StatusClass for stable
// {succeeded|failed} bucketing. See classifyStatus for the resolution
// algorithm.
Status string `json:"status"`
// StatusClass is one of "succeeded" or "failed" — stable companion to
// Status. Computed via pipeline.TerminalStatus(Status).IsSuccess().
StatusClass string `json:"status_class"`
Nodes int `json:"nodes"`
Retries int `json:"retries"`
Restarts int `json:"restarts"`
Timestamp time.Time `json:"timestamp"`
// Duration is encoded as integer nanoseconds in JSON ("duration_ns"),
// not as a duration string.
Duration time.Duration `json:"duration_ns"`
// FailedAt is the node ID where the run halted, populated for both
// "fail" and "budget_exceeded" runs (Gap 5.2: budget-halted runs are
// no longer classified as "fail", so the gate also fires on
// "budget_exceeded").
FailedAt string `json:"failed_at,omitempty"`
// BundleIdentity is the content-addressed identity ("sha256:<hex>") of
// the .dipx bundle the run was executed against. Read from the run's
// checkpoint at summary-build time. Empty for runs from a plain .dip file.
BundleIdentity string `json:"bundle_identity,omitempty"`
// OverrideCount is the number of override edges traversed in this run.
// Sourced from activity log when present, else
// len(Checkpoint.ValidationOverrides). RunSummary stays thin — for the
// full OverrideDetail slice see AuditReport.ValidationOverrides.
OverrideCount int `json:"override_count,omitempty"`
}
RunSummary is a condensed view of a single pipeline run for listing.
func ListRuns ¶
func ListRuns(workdir string, opts ...AuditConfig) ([]RunSummary, error)
ListRuns returns all runs under workdir/.tracker/runs, sorted newest first. If the runs directory does not exist, ListRuns returns (nil, nil).
type SimEdge ¶
type SimEdge struct {
From string `json:"from"`
To string `json:"to"`
Label string `json:"label,omitempty"`
Condition string `json:"condition,omitempty"`
}
SimEdge is an edge in a SimulateReport.
type SimNode ¶
type SimNode struct {
ID string `json:"id"`
Handler string `json:"handler,omitempty"`
Shape string `json:"shape,omitempty"`
Label string `json:"label,omitempty"`
Attrs map[string]string `json:"attrs,omitempty"`
}
SimNode is a node in a SimulateReport.
type SimulateReport ¶
type SimulateReport struct {
Format string `json:"format"`
Name string `json:"name,omitempty"`
StartNode string `json:"start_node,omitempty"`
ExitNode string `json:"exit_node,omitempty"`
GraphAttrs map[string]string `json:"graph_attrs,omitempty"`
Nodes []SimNode `json:"nodes"`
Edges []SimEdge `json:"edges"`
ExecutionPlan []PlanStep `json:"execution_plan"`
Unreachable []string `json:"unreachable,omitempty"`
}
SimulateReport is the structured output of a dry-run over a pipeline source. No LLM calls, no side effects — pure graph introspection.
func Simulate ¶
func Simulate(ctx context.Context, source string) (*SimulateReport, error)
Simulate parses source and returns a SimulateReport. Format is detected from content.
ctx is checked at entry so a caller that passes an already-cancelled context gets an immediate error instead of silent work. Full cancellation mid-parse would require threading ctx through parsePipelineSource → dippin-lang's parser, which is out of scope today (parses are fast and O(n) anyway). Nil is coalesced to context.Background().
func SimulateGraph ¶
SimulateGraph returns a SimulateReport for a pre-parsed graph. It is the graph-in variant of Simulate: callers that already parsed (e.g. the CLI's validate + simulate flow, or tooling that built a graph programmatically) avoid a second parse. Unlike Simulate, Format is left empty — there is no source string to inspect; the caller can set it if desired.
ctx is honored at entry; nil is coalesced to context.Background(). The graph is consumed synchronously. For graphs built via the library's parsers or Graph.AddEdge, the BFS plan is O(nodes+edges); graphs that populate Graph.Edges directly without AddEdge leave the adjacency index unbuilt, and OutgoingEdges then scans all edges per lookup — callers that construct graphs by hand should prefer AddEdge to keep that bound.
type StreamEvent ¶
type StreamEvent struct {
Timestamp string `json:"ts"`
Source string `json:"source"` // "pipeline", "llm", "agent"
Type string `json:"type"` // event type within source
RunID string `json:"run_id,omitempty"` // pipeline run ID
NodeID string `json:"node_id,omitempty"` // pipeline node ID
Message string `json:"message,omitempty"` // human-readable message
Error string `json:"error,omitempty"` // error text
Provider string `json:"provider,omitempty"` // LLM provider
Model string `json:"model,omitempty"` // LLM model
ToolName string `json:"tool_name,omitempty"` // tool name for agent/LLM tool events
Content string `json:"content,omitempty"` // text content (LLM output, tool output)
}
StreamEvent is the stable wire format for the tracker --json mode. Field tags are stable; new optional fields may be added without a major bump.
type Suggestion ¶
type Suggestion struct {
NodeID string `json:"node_id,omitempty"`
Kind SuggestionKind `json:"kind"`
Message string `json:"message"`
}
Suggestion is an actionable recommendation produced by Diagnose.
type SuggestionKind ¶
type SuggestionKind string
SuggestionKind is the typed string identifying which template produced a Suggestion. The underlying string values are stable; new kinds may be added additively.
const ( SuggestionRetryPattern SuggestionKind = "retry_pattern" SuggestionEscalateLimit SuggestionKind = "escalate_limit" SuggestionNoOutput SuggestionKind = "no_output" SuggestionShellCommand SuggestionKind = "shell_command" SuggestionGoTest SuggestionKind = "go_test" SuggestionSuspiciousTiming SuggestionKind = "suspicious_timing" SuggestionBudget SuggestionKind = "budget" // SuggestionToolOutputTruncated fires when a tool node's output stream // exceeded its per-stream cap. Surfaces actionable copy pointing at // output_limit and at the canonical authoring pattern. Issue #208. SuggestionToolOutputTruncated SuggestionKind = "tool_output_truncated" // SuggestionConditionalFallthrough fires when a node's conditional // routing edges all evaluated false and routing fell back to an // unconditional edge. Issue #208. SuggestionConditionalFallthrough SuggestionKind = "conditional_fallthrough" // SuggestionToolMarkerMissing fires when a tool node declared // marker_grep but the regex matched nothing (or failed to compile). // Surfaces the configured pattern, the captured stdout tail, and the // recommended fix. Issue #210. SuggestionToolMarkerMissing SuggestionKind = "tool_marker_missing" // SuggestionToolRouteMissing fires when a tool node had // route_required: true but no _TRACKER_ROUTE= sentinel line was // emitted to stdout. Surfaces the captured stdout tail and the // recommended author pattern. Issue #212. SuggestionToolRouteMissing SuggestionKind = "tool_route_missing" // SuggestionAutoStatusMissing fires when an auto_status agent node // completed normally but its response contained no parseable STATUS // line (#346). Goal-gate nodes fail closed; plain auto_status nodes // keep the legacy success default — the suggestion copy distinguishes // the two so a silently-defaulted verdict is visible post-run. SuggestionAutoStatusMissing SuggestionKind = "auto_status_missing" // SuggestionAuditLogInjection fires when the integrity-protected // activity log has one or more lines missing the runtime sentinel // prefix (#213). Detection-only — the suggestion text is explicit // that the sentinel is not authentication; a motivated forger who // reads tracker's source can emit the bytes. Surfaces the count of // suspect lines and the audit-log path so operators can // investigate. SuggestionAuditLogInjection SuggestionKind = "audit_log_injection" )
Suggestion kinds (stable; new ones may be added additively).
type TimelineEntry ¶
type TimelineEntry struct {
Timestamp time.Time `json:"ts"`
Type string `json:"type"`
NodeID string `json:"node_id,omitempty"`
Message string `json:"message,omitempty"`
// Duration is encoded as integer nanoseconds in JSON ("duration_ns"),
// not as a duration string.
Duration time.Duration `json:"duration_ns,omitempty"`
}
TimelineEntry is a single entry in the audit timeline.
type ValidateOption ¶
type ValidateOption func(*validateConfig)
ValidateOption configures ValidateSource behavior.
func WithValidateFormat ¶
func WithValidateFormat(format string) ValidateOption
WithValidateFormat sets the pipeline source format ("dip" or "dot").
type ValidationResult ¶
type ValidationResult struct {
Graph *pipeline.Graph
Errors []string
Warnings []string
Hints []string
}
ValidationResult contains the outcome of pipeline validation.
func ValidateSource ¶
func ValidateSource(source string, opts ...ValidateOption) (*ValidationResult, error)
ValidateSource parses and validates a pipeline source string without executing it. Returns a ValidationResult with structured errors, warnings, and hints. An error is returned when the source cannot be parsed or has structural errors.
type WebhookGateConfig ¶
type WebhookGateConfig struct {
WebhookURL string // required: URL to post gate payloads to
CallbackAddr string // local listen addr for callback server (default: :8789)
Timeout time.Duration // wait timeout per gate (default: 10m)
TimeoutAction string // "fail" (default) or "success" on timeout
AuthHeader string // Authorization header for outbound requests
RunID string // optional: run ID embedded in gate payloads
}
WebhookGateConfig controls headless webhook-based human gate handling. When set, human gate prompts are POSTed to WebhookURL and the pipeline waits for a callback POST to the local callback server.
type WorkflowInfo ¶
type WorkflowInfo struct {
Name string // bare name used for lookup, e.g. "build_product"
File string // path within the embedded FS, e.g. "examples/build_product.dip"
DisplayName string // workflow declaration name, e.g. "BuildProduct"
Goal string // parsed from the goal: field at the top of the .dip file
Requires []string // parsed from the `requires:` field (v0.29.0); nil if not declared
}
WorkflowInfo describes a built-in workflow embedded in the tracker binary.
func LookupWorkflow ¶
func LookupWorkflow(name string) (WorkflowInfo, bool)
LookupWorkflow returns the WorkflowInfo for a built-in workflow by bare name, or (zero, false) if no built-in matches. The returned value shares no mutable state with the cached catalog.
func OpenWorkflow ¶
func OpenWorkflow(name string) ([]byte, WorkflowInfo, error)
OpenWorkflow returns the raw source bytes of a built-in workflow by bare name. This is the same content that `tracker init <name>` would copy to disk. Returns an error if the name is not a known built-in.
func ResolveSource ¶
func ResolveSource(name, workDir string) (source string, info WorkflowInfo, err error)
ResolveSource resolves a pipeline name to the actual workflow source text. The resolution order matches the CLI's tracker.resolvePipelineSource:
- If name contains "/" or "\" (forward or back slash) or ends in ".dip"/".dot", treat it as a filesystem path and read it from disk.
- If "<name>.dip" exists under workDir, read that.
- If "<name>" exists under workDir, read that.
- If name matches a built-in workflow, return the embedded source.
- Otherwise return an error listing available built-ins.
The returned WorkflowInfo is populated only for built-in workflows; it's the zero value for filesystem sources. workDir may be empty — in that case the current working directory is used for relative lookups.
func Workflows ¶
func Workflows() []WorkflowInfo
Workflows returns the list of workflows embedded in the tracker binary, sorted by name. Library consumers can use this to show users the available built-ins without shelling out to `tracker workflows`. Returned values share no mutable state with the cached catalog.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
ABOUTME: Turn-budget checkpoint evaluation for the agent session loop.
|
ABOUTME: Turn-budget checkpoint evaluation for the agent session loop. |
|
exec
ABOUTME: ExecutionEnvironment interface abstracting where agent tools run.
|
ABOUTME: ExecutionEnvironment interface abstracting where agent tools run. |
|
tools
ABOUTME: Bash tool executes shell commands in the working directory.
|
ABOUTME: Bash tool executes shell commands in the working directory. |
|
cmd
|
|
|
tracker
command
ABOUTME: Audit subcommand — analyzes completed pipeline runs from on-disk artifacts.
|
ABOUTME: Audit subcommand — analyzes completed pipeline runs from on-disk artifacts. |
|
tracker-conformance
command
ABOUTME: CLI entry point for the tracker-conformance attractorbench binary.
|
ABOUTME: CLI entry point for the tracker-conformance attractorbench binary. |
|
tracker-swebench
command
ABOUTME: `tracker-swebench analyze <results-dir>` — bulk-triage a prior run's artifacts.
|
ABOUTME: `tracker-swebench analyze <results-dir>` — bulk-triage a prior run's artifacts. |
|
tracker-swebench/agent-runner
command
ABOUTME: In-container agent binary for SWE-bench evaluation harness.
|
ABOUTME: In-container agent binary for SWE-bench evaluation harness. |
|
internal
|
|
|
bundleid
Package bundleid provides shared formatting helpers for content-addressed .dipx bundle identities.
|
Package bundleid provides shared formatting helpers for content-addressed .dipx bundle identities. |
|
dipxtest
ABOUTME: Test helper that packs real .dipx bundles for use in downstream tests.
|
ABOUTME: Test helper that packs real .dipx bundles for use in downstream tests. |
|
ABOUTME: Middleware that emits summaries of LLM call activity for live UI display.
|
ABOUTME: Middleware that emits summaries of LLM call activity for live UI display. |
|
anthropic
ABOUTME: Anthropic Messages API adapter implementing the ProviderAdapter interface.
|
ABOUTME: Anthropic Messages API adapter implementing the ProviderAdapter interface. |
|
google
ABOUTME: Google Gemini API adapter implementing the ProviderAdapter interface.
|
ABOUTME: Google Gemini API adapter implementing the ProviderAdapter interface. |
|
openai
ABOUTME: OpenAI Responses API adapter implementing the ProviderAdapter interface.
|
ABOUTME: OpenAI Responses API adapter implementing the ProviderAdapter interface. |
|
openaicompat
ABOUTME: OpenAI Chat Completions compatible adapter implementing the ProviderAdapter interface.
|
ABOUTME: OpenAI Chat Completions compatible adapter implementing the ProviderAdapter interface. |
|
ABOUTME: Resolves the on-disk location of the integrity-protected activity log.
|
ABOUTME: Resolves the on-disk location of the integrity-protected activity log. |
|
handlers
ABOUTME: LLM-backed autopilot interviewer that replaces human gates with automated decisions.
|
ABOUTME: LLM-backed autopilot interviewer that replaces human gates with automated decisions. |
|
tools
|
|
|
jailcheck
command
ABOUTME: jailcheck flags direct filesystem-mutation / subprocess calls in ABOUTME: agent/tools that bypass the ExecutionEnvironment seam guarding the writable_paths jail (#272/#275/#283).
|
ABOUTME: jailcheck flags direct filesystem-mutation / subprocess calls in ABOUTME: agent/tools that bypass the ExecutionEnvironment seam guarding the writable_paths jail (#272/#275/#283). |
|
ABOUTME: Event adapter — converts raw engine events into typed TUI messages.
|
ABOUTME: Event adapter — converts raw engine events into typed TUI messages. |
|
render
ABOUTME: Shared prompt rendering for human gate UIs.
|
ABOUTME: Shared prompt rendering for human gate UIs. |