dag

package
v0.1.1 Latest Latest
Warning

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

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

Documentation

Overview

Package dag maintains the commit-DAG of environment snapshots.

Design:

  • Each Node maps to one read-only btrfs snapshot (an immutable state).
  • A single parent per node is enough for environment rollback, so this is a tree; extend to a real DAG later if merges are ever needed.
  • Metadata is persisted as a single JSON file (meta.json): single writer, zero external dependencies.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Node

type Node struct {
	ID        string            `json:"id"`
	Parent    string            `json:"parent"`   // empty for the root node
	Children  []string          `json:"children"` // child IDs, forming the tree
	Message   string            `json:"message"`  // git-like commit message
	Command   string            `json:"command"`  // action that produced it, e.g. "apt install nginx"
	CreatedAt time.Time         `json:"created_at"`
	Meta      map[string]string `json:"meta,omitempty"`
}

Node is an immutable node in the DAG, i.e. one commit.

type Repo

type Repo struct {
	Nodes map[string]*Node  `json:"nodes"`
	Head  string            `json:"head"`           // node the current working subvolume derives from
	Tags  map[string]string `json:"tags,omitempty"` // human name → node ID
	// contains filtered or unexported fields
}

Repo is the whole commit graph plus the current HEAD and named tags.

CONCURRENCY CONTRACT: Repo is NOT goroutine-safe on its own. All callers (currently the orchestration layer in internal/repo) MUST hold a higher-level mutex — repo.Repo.opMu — across any read or write of Nodes / Tags / Head. This is intentional: the agentenv DAG is small and updates are infrequent, so a single repo-wide lock is simpler than embedding a mutex here and trying to keep the lock-ordering between dag.Repo and the snapshotter consistent. The doc comment exists so a future caller from a new package doesn't assume dag.Repo is safe to share.

func Load

func Load(root string) (*Repo, error)

Load reads root/meta.json, returning an empty repo if it does not exist.

func (*Repo) Add

func (r *Repo) Add(n *Node)

Add attaches a node under its parent and inserts it into the graph.

func (*Repo) Get

func (r *Repo) Get(id string) (*Node, bool)

Get returns a node by ID.

func (*Repo) Roots

func (r *Repo) Roots() []*Node

Roots returns all nodes without a parent (normally just one).

func (*Repo) Save

func (r *Repo) Save() error

Save writes meta.json atomically and crash-safely. meta.json is the only source of truth for the commit DAG (every node, parent, tag, HEAD), so a torn or zero-byte file would lose the entire history. Sequence:

  1. write the new contents to a sibling temp file,
  2. fsync the temp file so the bytes are durably on disk,
  3. rename → meta.json (atomic on POSIX),
  4. fsync the parent directory so the rename itself is durable.

Steps 2 and 4 are what plain `os.WriteFile`+`os.Rename` is missing — after a kernel/host crash without them, the rename can land first and the data later, or the new file can be empty.

Jump to

Keyboard shortcuts

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