Documentation
¶
Overview ¶
Package engine provides a shared execution engine for graph-based operations.
Architecture ¶
The engine is shared by both writ (configuration deployment) and lore (package management). Each tool builds an execution graph containing nodes that represent operations to perform. The engine processes these graphs uniformly:
┌─────────────────┐ ┌─────────────────┐
│ writ deploy │ │ lore deploy │
│ (config files) │ │ (packages) │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ File Tree │ │ Package Graph │
│ Builder │ │ Builder │
└────────┬────────┘ └────────┬────────┘
│ │
└───────────┬───────────┘
▼
┌─────────────────────┐
│ Execution Graph │
│ (unified nodes) │
└──────────┬──────────┘
▼
┌─────────────────────┐
│ Engine.Run() │
│ (shared runner) │
└──────────┬──────────┘
▼
┌─────────────────────┐
│ Receipt │
└─────────────────────┘
Graph Builders ¶
Different graph builders produce nodes for different operation types:
File Tree Builder (writ): Walks environment repositories, produces nodes for link, copy, expand, decrypt operations on configuration files.
Package Graph Builder (lore): Resolves package specifications, produces nodes for install, configure, verify operations on software packages. NOT YET IMPLEMENTED - see internal/lore/graph package.
When writ encounters a packages-manifest.yaml file, it should use the Package Graph Builder to add package nodes to the same execution graph. There is no delegation or handoff between tools—both use the same engine.
Operation Categories ¶
Operations are classified by their data flow behavior:
- Transform: Read content, produce transformed content (decrypt, expand)
- Writer: Read content, write to filesystem (copy)
- Direct: Manage own I/O, no content flow (link, mkdir, install, verify)
The engine threads content through Transform operations, passes it to Writer operations, and executes Direct operations independently.
Index ¶
- func ExpandDelegates(ctx context.Context, graph *Graph, builder GraphBuilder, opts BuildOptions) error
- type BackupOp
- type BuildOptions
- type Conflict
- type ConflictResolution
- type ConflictType
- type Context
- type CopyOp
- type DecryptOp
- type Direct
- type Edge
- type Engine
- type ExpandOp
- type Graph
- type GraphBuilder
- type LinkOp
- type MkdirOp
- type Node
- type OpCategory
- type Operation
- type Options
- type PipelineInput
- type PipelineOutput
- type PipelineState
- type Plan
- func (p *Plan) Backup(target string) *Node
- func (p *Plan) Copy(source, target string, transforms ...string) *Node
- func (p *Plan) CopyWithMode(source, target string, mode os.FileMode, transforms ...string) *Node
- func (p *Plan) DependsOn(from, to *Node)
- func (p *Plan) Graph() *Graph
- func (p *Plan) Link(source, target string) *Node
- func (p *Plan) Mkdir(target string) *Node
- func (p *Plan) Orders(from, to *Node)
- func (p *Plan) Remove(target string) *Node
- func (p *Plan) Rename(source, target string) *Node
- func (p *Plan) Unlink(target string) *Node
- func (p *Plan) Validate(check, message string) *Node
- type PreflightResult
- type Registry
- type RemoveOp
- type RenameOp
- type Result
- type Status
- type Transform
- type UnlinkOp
- type ValidateOp
- type Writer
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ExpandDelegates ¶
func ExpandDelegates(ctx context.Context, graph *Graph, builder GraphBuilder, opts BuildOptions) error
ExpandDelegates replaces delegate nodes in the graph with subgraphs produced by the given builder. Delegate nodes are identified by having "delegate" as their sole operation.
The expanded subgraph's nodes and edges are appended to the parent graph. An ordering edge is added from the delegate node's predecessor (if any) to each root node of the subgraph.
The original delegate node is removed from the graph after expansion.
Types ¶
type BackupOp ¶
type BackupOp struct{}
BackupOp moves the existing file at node.Target to a timestamped backup. The backup path is stored in node.Metadata["backup_path"] after execution.
func (*BackupOp) Category ¶
func (o *BackupOp) Category() OpCategory
type BuildOptions ¶
type BuildOptions struct {
// DryRun prevents the builder from making any filesystem queries
// that have side effects.
DryRun bool
// Features lists enabled features (e.g., "rootless", "compose").
Features []string
// Data holds tool context: platform info, environment, segments.
Data map[string]any
}
BuildOptions configures graph building behavior.
type Conflict ¶
type Conflict struct {
Node *Node
Type ConflictType
ExistingPath string // For symlinks, where it points
Message string
}
Conflict represents a pre-flight detected conflict.
type ConflictResolution ¶
type ConflictResolution int
ConflictResolution specifies how to handle conflicts.
const ( // ResolutionStop aborts execution on first conflict. ResolutionStop ConflictResolution = iota // ResolutionBackup moves conflicting files to timestamped backups. ResolutionBackup // ResolutionOverwrite removes conflicting files without backup. ResolutionOverwrite // ResolutionSkip skips conflicting files and continues. ResolutionSkip )
type ConflictType ¶
type ConflictType int
ConflictType describes the kind of conflict at a target path.
const ( // ConflictNone indicates no conflict exists. ConflictNone ConflictType = iota // ConflictRegularFile indicates a regular file exists at target. ConflictRegularFile // ConflictDirectory indicates a directory exists at target. ConflictDirectory // ConflictForeignSymlink indicates a symlink pointing elsewhere exists. ConflictForeignSymlink // ConflictOurSymlink indicates our symlink already exists (no action needed). ConflictOurSymlink )
type Context ¶
type Context struct {
context.Context
// DryRun prevents filesystem modifications when true.
DryRun bool
// Logger receives operation output messages.
Logger io.Writer
// Data holds tool-provided context: template variables, SOPS config,
// identities, segment maps, etc. Each tool populates this before
// calling Engine.Run().
Data map[string]any
}
Context provides execution context to operations.
type CopyOp ¶
type CopyOp struct{}
CopyOp writes content to node.Target and returns the target checksum.
func (*CopyOp) Category ¶
func (o *CopyOp) Category() OpCategory
type DecryptOp ¶
type DecryptOp struct{}
DecryptOp decrypts content using the SOPS API. The decryption configuration is expected in ctx.Data. Returns the decrypted content.
func (*DecryptOp) Category ¶
func (o *DecryptOp) Category() OpCategory
type Direct ¶
Direct operations manage their own I/O with no content flow. Used for: link, mkdir, backup, unlink, remove, validate, rename.
type Edge ¶
type Edge struct {
From string // Source node ID
To string // Target node ID
Relation string // "depends_on", "orders"
}
Edge defines an ordering constraint between nodes.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine executes operation graphs.
func (*Engine) Preflight ¶
func (e *Engine) Preflight(graph *Graph) *PreflightResult
Preflight performs pre-flight conflict detection without modifying anything. Only applies to nodes with file operations (link, copy).
func (*Engine) Run ¶
Run executes all nodes in the graph, respecting ordering constraints. Nodes are processed in topological order when edges define dependencies. Returns results for each node.
TODO: Add graph optimization pass before execution. Native PM operations (e.g., multiple "install" nodes with the same package manager) should be batched into a single operation to reduce PM invocations. See docs/plans/uniform-pipeline-interface.md for design details.
type ExpandOp ¶
type ExpandOp struct{}
ExpandOp processes content as a Go text/template with ctx.Data as the template data. Returns the expanded content.
func (*ExpandOp) Category ¶
func (o *ExpandOp) Category() OpCategory
type Graph ¶
Graph represents an execution graph: nodes with operations and edges defining ordering constraints.
type GraphBuilder ¶
type GraphBuilder interface {
BuildGraph(ctx context.Context, manifestPath string, opts BuildOptions) (*Graph, error)
}
GraphBuilder builds an execution graph from a manifest file. Tools implement this interface to translate their manifest format into an execution graph that the engine can process.
When writ encounters a delegate node, it calls BuildGraph with the manifest path to get a subgraph. That subgraph is then executed by the same engine — no separate tool invocation needed.
type LinkOp ¶
type LinkOp struct{}
LinkOp creates a symlink from node.Target pointing to node.Source.
func (*LinkOp) Category ¶
func (o *LinkOp) Category() OpCategory
type MkdirOp ¶
type MkdirOp struct{}
MkdirOp creates a directory at node.Target.
func (*MkdirOp) Category ¶
func (o *MkdirOp) Category() OpCategory
type Node ¶
type Node struct {
// ID uniquely identifies this node (e.g., relative target path for writ,
// package name for lore).
ID string
// Operations is the pipeline of operations to execute on this node.
// Examples: ["link"], ["decrypt", "expand", "copy"], ["install"].
Operations []string
// Source is the source path (for file operations).
Source string
// Target is the target path (for file operations).
Target string
// Project is the grouping key (writ: project name, lore: package name).
Project string
// Mode is the target file permissions (0 means use default 0644).
Mode os.FileMode
// DelegateTo is DEPRECATED - there is no delegation between tools.
// writ and lore share the same execution engine. Retained for backwards
// compatibility with old receipts.
DelegateTo string
// Metadata holds tool-specific extensions.
Metadata map[string]string
}
Node represents a unit of work in the execution graph.
type OpCategory ¶
type OpCategory int
OpCategory classifies operations by their data flow behavior.
const ( // OpTransform reads content, produces transformed content. OpTransform OpCategory = iota // OpWriter reads content, writes to filesystem, produces checksum. OpWriter // OpDirect manages its own I/O, no content flow. OpDirect )
type Operation ¶
type Operation interface {
// Name returns the operation identifier (e.g., "link", "decrypt").
Name() string
// Category returns the operation category for pipeline validation.
Category() OpCategory
}
Operation is the base interface for all executable actions.
func FileOps ¶
func FileOps() []Operation
FileOps returns all built-in file operations for registration.
Transform operations (content in → content out):
- decrypt: decrypts encrypted content via ctx.Data["decryptor"]
- expand: expands Go text/template content with ctx.Data
Writer operations (content in → checksum out):
- copy: writes content to node.Target
Direct operations (no content flow):
- link: creates symlink from node.Target → node.Source
- mkdir: creates directory at node.Target
- backup: moves node.Target to timestamped backup
- unlink: removes symlink at node.Target
- remove: deletes file at node.Target
- validate: checks precondition from ctx.Data["validators"]
- rename: moves node.Source → node.Target (git mv when possible)
NOTE: Package operations (install, configure, verify) are provided by the Package Graph Builder (internal/lore/graph) - NOT YET IMPLEMENTED.
type Options ¶
type Options struct {
// DryRun prevents filesystem modifications.
DryRun bool
// Logger receives operation output.
Logger io.Writer
// Data holds tool-provided context (template vars, SOPS config, etc.).
Data map[string]any
// ConflictResolution specifies how to handle conflicts detected during preflight.
ConflictResolution ConflictResolution
// BackupSuffix is appended to backup filenames (default: ".writ-backup").
BackupSuffix string
}
Options configures engine behavior.
type PipelineInput ¶
type PipelineInput struct {
// Content is the source file content (read by engine before pipeline starts).
Content []byte
// SourceChecksum is the SHA256 of the original content.
SourceChecksum string
}
PipelineInput holds the input data for a pipeline.
type PipelineOutput ¶
type PipelineOutput struct {
// Content is the final transformed content (after all transforms).
Content []byte
// TargetChecksum is the SHA256 of the written target file.
TargetChecksum string
}
PipelineOutput holds the output data from a pipeline.
type PipelineState ¶
type PipelineState struct {
// Content is the current file content. Transforms modify this in place;
// writers consume it to produce output files.
Content []byte
// SourceChecksum is computed from the original source file content
// before any transforms are applied. Format: "sha256:<hex>".
SourceChecksum string
// TargetChecksum is set by writer operations after writing content
// to the target path. Format: "sha256:<hex>".
TargetChecksum string
// Metadata holds per-node extensible state that operations can read/write.
Metadata map[string]string
}
PipelineState holds mutable state threaded through a node's operation pipeline. The engine pre-reads source content into Content when the pipeline begins with a transform or writer operation.
type Plan ¶
type Plan struct {
// contains filtered or unexported fields
}
Plan provides binding functions for building an execution graph. Graph producers (writ tree builder, lore pipeline executor, LLM graph builder) use Plan to add operations to the graph. Each method returns the created node for edge construction.
In Starlark scripts, the plan object is passed to each phase function:
def install(system, package, plan):
plan.mkdir("/usr/local/bin")
plan.link("/usr/local/bin/foo", source="/path/to/foo")
func (*Plan) Copy ¶
Copy adds a file copy operation. Transforms (decrypt, expand) can be prepended to the pipeline.
func (*Plan) CopyWithMode ¶
CopyWithMode adds a file copy operation with explicit permissions.
type PreflightResult ¶
type PreflightResult struct {
Conflicts []Conflict
AlreadyDone []Conflict // Symlinks that already point correctly
Ready []*Node // Nodes ready to deploy (no conflict)
}
PreflightResult contains the results of pre-flight conflict detection.
func (*PreflightResult) HasConflicts ¶
func (p *PreflightResult) HasConflicts() bool
HasConflicts returns true if any conflicts were detected.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry maps operation names to implementations. Each tool registers its operations before calling Engine.Run().
type RemoveOp ¶
type RemoveOp struct{}
RemoveOp deletes the file at node.Target. If ctx.Data["prune_empty_dirs"] is true and ctx.Data["prune_boundary"] is set, empty parent directories are removed up to the boundary.
func (*RemoveOp) Category ¶
func (o *RemoveOp) Category() OpCategory
type RenameOp ¶
type RenameOp struct{}
RenameOp moves a file or directory from node.Source to node.Target using git mv when inside a git repository, falling back to os.Rename otherwise.
func (*RenameOp) Category ¶
func (o *RenameOp) Category() OpCategory
type Result ¶
type Result struct {
NodeID string
Status Status
Error error
Message string
SourceChecksum string
TargetChecksum string
}
Result represents the outcome of executing a single node.
type Status ¶
type Status int
Status represents the execution status of a node.
const ( // StatusPending means the node has not been processed yet. StatusPending Status = iota // StatusRunning means the node is currently executing. StatusRunning // StatusCompleted means the node executed successfully. StatusCompleted // StatusFailed means the node encountered an error. StatusFailed // StatusSkipped means the node was skipped (conflict, already deployed, etc.). StatusSkipped )
type Transform ¶
type Transform interface {
Operation
Transform(ctx *Context, node *Node, content []byte) ([]byte, error)
}
Transform operations read content and produce transformed content. Used for: decrypt, expand.
type UnlinkOp ¶
type UnlinkOp struct{}
UnlinkOp removes a symlink at node.Target. If ctx.Data["prune_empty_dirs"] is true and ctx.Data["prune_boundary"] is set, empty parent directories are removed up to the boundary.
func (*UnlinkOp) Category ¶
func (o *UnlinkOp) Category() OpCategory
type ValidateOp ¶
type ValidateOp struct{}
ValidateOp checks a precondition and fails with a message if unmet. The check function is retrieved from ctx.Data["validators"][node.Metadata["check"]].
func (*ValidateOp) Category ¶
func (o *ValidateOp) Category() OpCategory
func (*ValidateOp) Name ¶
func (o *ValidateOp) Name() string