build

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Jun 4, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package build orchestrates the coding agent integration.

Index

Constants

View Source
const (
	NodePending    = "pending"
	NodeInProgress = "in-progress"
	NodeComplete   = "complete"
	NodeFailed     = "failed"
)

Node status values for the per-node ledger.

Variables

This section is empty.

Functions

func AdvanceStep

func AdvanceStep(db *store.DB, session *SessionState) error

AdvanceStep marks the current step as complete and moves to the next. It also mirrors the change into the node ledger so the DAG source of truth stays in sync during the sequential-to-DAG transition.

func AllLeavesHaveDraftPR added in v0.16.0

func AllLeavesHaveDraftPR(prSectionContent string) bool

AllLeavesHaveDraftPR reports whether every leaf node in a §7.3 PR stack has a recorded draft-PR URL (a `<!-- pr: <url> -->` annotation). It is the verifier behind the pr_stack_exists gate: review may not begin until each stack tip has an open draft PR. prSectionContent is the §7.3 section body.

Returns false when the plan is empty, malformed (not a DAG), has no leaves, or any leaf is missing its PR — so an unbuilt or partially-finished spec never passes the gate.

func LogActivity

func LogActivity(specID, entry string) error

LogActivity appends an entry to both the SQLite activity log and the session file.

func SaveSession

func SaveSession(db *store.DB, session *SessionState) error

SaveSession persists a session to the database.

func SessionDir

func SessionDir(specID string) string

SessionDir returns the path to the session directory.

func SetActivityDB

func SetActivityDB(db *store.DB)

SetActivityDB sets the database used for activity logging.

func WriteContextFile

func WriteContextFile(ctx *BuildContext, outputPath string) error

WriteContextFile writes a consolidated context markdown file for non-MCP agents.

Types

type BuildContext

type BuildContext struct {
	SpecPath     string
	SpecContent  string
	PriorDiffs   []string
	FailingTests string
	Conventions  string
	CurrentStep  PRStep
	SystemPrompt string
	// Skills holds the resolved skill bodies (Agent Skills markdown). Empty
	// when no skills are present under .spec/agent/skills/ or in config.
	Skills []string
	// SkillPaths is the deduplicated union of skill paths across all DAG nodes,
	// passed to skill-capable agents as the set the orchestrator may dispatch.
	SkillPaths []string
}

BuildContext is the assembled context payload passed to an agent.

func AssembleContext

func AssembleContext(specPath string, session *SessionState, conventions string) (*BuildContext, error)

AssembleContext builds the full context payload for an agent.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine orchestrates the build handoff. spec-cli owns the DAG, the durable node ledger, and all git/worktree/GitHub mechanics; the agent (pi) conducts the traversal via MCP. One StartOrResume call = one whole-DAG invocation.

func NewEngine

func NewEngine(db *store.DB, agent adapter.AgentAdapter, opts Options) *Engine

NewEngine creates a new build engine.

func (*Engine) StartOrResume

func (e *Engine) StartOrResume(ctx context.Context, specID, specPath, startDir string) error

StartOrResume begins or continues a build session for a spec. It ensures the session and DAG exist, validates the workspaces the DAG needs, assembles the context, and invokes the agent once with the DAG exposed via MCP. The agent provisions and checkpoints each node back through the spec MCP tools; on return the engine reconciles the node ledger and, if work remains, prints resume guidance. A resume re-dispatches only the surviving ready nodes — completed nodes are never re-run.

type Graph added in v0.16.0

type Graph struct {
	// Nodes are stored in their original plan order.
	Nodes []PRStep
	// contains filtered or unexported fields
}

Graph is a validated DAG of PR-stack nodes. It is the deterministic substrate the orchestrator walks: spec-cli owns the graph, pi conducts traversal.

func BuildGraph added in v0.16.0

func BuildGraph(steps []PRStep) (*Graph, error)

BuildGraph validates a PR stack and returns its DAG. It rejects edges that reference unknown steps and dependency cycles, naming the offending nodes so the author can fix the §7.3 plan.

func (*Graph) Dependencies added in v0.16.0

func (g *Graph) Dependencies(id string) []string

Dependencies returns the node IDs the given node depends on.

func (*Graph) Leaves added in v0.16.0

func (g *Graph) Leaves() []PRStep

Leaves returns the nodes no other node depends on — the tips of the stack, which are the PRs that must exist before review.

func (*Graph) Node added in v0.16.0

func (g *Graph) Node(id string) (PRStep, bool)

Node returns the step for a node ID and whether it exists.

func (*Graph) ReadySet added in v0.16.0

func (g *Graph) ReadySet(done map[string]bool) []PRStep

ReadySet returns the nodes whose dependencies are all complete and which are not themselves complete. This is the resume primitive: given the set of completed node IDs, it yields exactly the nodes safe to dispatch next.

func (*Graph) Waves added in v0.16.0

func (g *Graph) Waves() [][]PRStep

Waves returns a Kahn topological sort grouped into waves. Every node in a wave has all of its dependencies satisfied by earlier waves, so a wave can be fanned out in parallel. Plan order is preserved within each wave.

type MCPResource

type MCPResource struct {
	URI     string `json:"uri"`
	Name    string `json:"name"`
	Content string `json:"content"`
}

MCPResource represents a resource served by the MCP server.

type MCPServer

type MCPServer struct {
	// contains filtered or unexported fields
}

MCPServer serves spec context to MCP-compatible agents. It owns the DAG, the node ledger, and every git/worktree operation: the orchestrator reads the DAG once and checkpoints each node back through the node tools, while spec-cli keeps all branch/worktree mechanics on this side of the contract.

func NewMCPServer

func NewMCPServer(session *SessionState, buildCtx *BuildContext, db *store.DB, specPath string, opts Options) *MCPServer

NewMCPServer creates a new MCP server for a build session. opts carries the workspace map (source repos for worktrees) and skill routing inputs. The DAG is built from the session's steps; a malformed plan yields a nil graph and a descriptive DAG resource rather than a panic.

func (*MCPServer) CallTool

func (s *MCPServer) CallTool(name string, args json.RawMessage) (*MCPToolResult, error)

CallTool executes an MCP tool.

func (*MCPServer) GetResource

func (s *MCPServer) GetResource(uri string) (*MCPResource, error)

GetResource returns a specific resource by URI.

func (*MCPServer) ListResources

func (s *MCPServer) ListResources() []MCPResource

ListResources returns all available resources.

func (*MCPServer) WithRepo added in v0.16.0

func (s *MCPServer) WithRepo(r adapter.RepoAdapter) *MCPServer

WithRepo injects the repo adapter used by the draft-PR tools and returns the server for chaining. Kept separate from the constructor so call sites that never open PRs need not thread an adapter through.

type MCPToolResult

type MCPToolResult struct {
	Success bool   `json:"success"`
	Message string `json:"message"`
}

MCPToolResult represents the result of an MCP tool call.

type NodeState added in v0.16.0

type NodeState struct {
	ID       string `json:"id"`
	Status   string `json:"status"`
	Branch   string `json:"branch,omitempty"`
	BaseRef  string `json:"base_ref,omitempty"`
	Worktree string `json:"worktree,omitempty"`
	Reason   string `json:"reason,omitempty"`
	PRNumber int    `json:"pr_number,omitempty"`
	PRURL    string `json:"pr_url,omitempty"`
}

NodeState is the durable per-node record in the DAG ledger. It carries everything needed to resume, diff, and stack a node: its status, the branch and worktree git placed it on, the base ref it was cut from, an optional failure reason, and any draft-PR coordinates recorded during finishing.

type Options added in v0.15.0

type Options struct {
	// Headless runs the agent autonomously (e.g. `spec fix --auto`).
	Headless bool
	// SkillRefs are explicit skill paths from config
	// (integrations.agent.settings.skill). They take precedence over
	// profile.yaml refs and the .spec/agent/skills/ directory.
	SkillRefs []string
	// TestCommand, when set, is run to populate FailingTests (best-effort).
	TestCommand string
	// Workspaces maps a PR-step repo name to a local directory. It is the
	// source-of-truth repo that worktrees are added to for each node.
	Workspaces map[string]string
	// MaxParallel bounds orchestrator fan-out across ready nodes. Surfaced to
	// the agent via the DAG resource; 0 means "use the default".
	MaxParallel int
}

Options configures a build engine beyond its adapters.

type PRStep

type PRStep struct {
	Number      int    `yaml:"number" json:"number"`
	ID          string `yaml:"id" json:"id"`
	Repo        string `yaml:"repo" json:"repo"`
	Layer       string `yaml:"layer" json:"layer,omitempty"`
	Description string `yaml:"description" json:"description"`
	Branch      string `yaml:"branch" json:"branch"`
	Status      string `yaml:"status" json:"status"` // "pending", "in-progress", "complete", "failed"
	// DependsOn holds the step numbers this node depends on (parsed from the
	// `(after: 1,2)` edge annotation). Empty for a root node.
	DependsOn []int `yaml:"depends_on" json:"depends_on,omitempty"`
	// PRURL is the draft PR recorded for this node in §7.3 via a trailing
	// `<!-- pr: <url> -->` annotation. Empty until the finisher opens a PR.
	PRURL string `yaml:"pr_url" json:"pr_url,omitempty"`
	// BaseRef is the commit the step branch was created from. Used to capture
	// the step's diff for cumulative cross-step context.
	BaseRef string `yaml:"base_ref" json:"base_ref,omitempty"`
}

PRStep represents one node in the §7.3 PR stack plan. A plan is a DAG: each step has a stable ID, an optional repo and layer (which drive worktree placement and skill routing), and zero or more dependencies on earlier steps.

func ParsePRStack

func ParsePRStack(content string) ([]PRStep, error)

ParsePRStack extracts PR steps from the §7.3 PR Stack Plan section.

func ParsePRStackFromFile

func ParsePRStackFromFile(path string) ([]PRStep, error)

ParsePRStackFromFile reads a spec file and extracts PR steps.

func (PRStep) NodeID added in v0.16.0

func (s PRStep) NodeID() string

NodeID returns the stable node identifier, deriving it from the step number when an explicit ID was not parsed (e.g. "n3").

type SessionState

type SessionState struct {
	SpecID       string    `json:"spec_id"`
	CurrentStep  int       `json:"current_step"`
	Branch       string    `json:"branch"`
	Repo         string    `json:"repo"`
	WorkDir      string    `json:"work_dir"`
	LastActivity time.Time `json:"last_activity"`
	Steps        []PRStep  `json:"steps"`
	// Nodes is the per-node status ledger keyed by PRStep.ID. It is the DAG
	// source of truth for the orchestrated build; CurrentStep is retained only
	// for the legacy sequential walk and is removed once the DAG engine lands.
	Nodes map[string]*NodeState `json:"nodes,omitempty"`
}

SessionState persists the build session for `spec do` resume.

func CreateSession

func CreateSession(db *store.DB, specID string, steps []PRStep, workDir string) (*SessionState, error)

CreateSession creates a new build session.

func LoadSession

func LoadSession(db *store.DB, specID string) (*SessionState, error)

LoadSession loads a session from the database.

func (*SessionState) CurrentPRStep

func (s *SessionState) CurrentPRStep() *PRStep

CurrentPRStep returns the current step, or nil if complete.

func (*SessionState) DoneSet added in v0.16.0

func (s *SessionState) DoneSet() map[string]bool

DoneSet returns the set of completed node IDs, the input to Graph.ReadySet.

func (*SessionState) FailedNodes added in v0.16.0

func (s *SessionState) FailedNodes() []string

FailedNodes returns the IDs of nodes recorded as failed, sorted for stable reporting.

func (*SessionState) InitNodes added in v0.16.0

func (s *SessionState) InitNodes()

InitNodes populates the ledger from the session's steps when it is empty, giving every node a pending record. Existing records are preserved so a reload never clobbers progress.

func (*SessionState) IsComplete

func (s *SessionState) IsComplete() bool

IsComplete returns true if all steps are done.

func (*SessionState) MarkNodeComplete added in v0.16.0

func (s *SessionState) MarkNodeComplete(id string)

MarkNodeComplete records a node as complete and clears any failure reason. It is idempotent: completing an already-complete node is a no-op.

func (*SessionState) MarkNodeFailed added in v0.16.0

func (s *SessionState) MarkNodeFailed(id, reason string)

MarkNodeFailed records a node as failed with a reason for resume/reporting.

func (*SessionState) NodeStatus added in v0.16.0

func (s *SessionState) NodeStatus(id string) string

NodeStatus returns the status of a node, or "pending" if unknown.

func (*SessionState) NodesComplete added in v0.16.0

func (s *SessionState) NodesComplete() bool

NodesComplete reports whether every node in the ledger is complete. It returns false for an empty ledger so an uninitialised session is never mistaken for a finished one.

func (*SessionState) SetNodeStatus added in v0.16.0

func (s *SessionState) SetNodeStatus(id, status string)

SetNodeStatus updates a node's status in the ledger.

Jump to

Keyboard shortcuts

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