checkpoint

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package checkpoint is fairpeer's snapshot-based edit safety net. Before a writer tool changes a file, the agent records the file's pre-edit content here, keyed to the current user turn; a frontend can then rewind the workspace (and, via the controller, the conversation) to an earlier turn.

It is deliberately git-free (like Claude Code's rewind): snapshots live beside the session, never touch the user's git, and work in a non-git directory. Only edit-tool changes are tracked — bash side effects are not (a shell command's targets can't be known in advance), which is why the capture hook only fires for tools that can Preview their change.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Checkpoint

type Checkpoint struct {
	Turn     int        `json:"turn"`
	Time     time.Time  `json:"time"`
	Prompt   string     `json:"prompt"`
	MsgIndex int        `json:"msgIndex"`
	Files    []FileSnap `json:"files"`
}

Checkpoint anchors the pre-edit state of every distinct file touched during one user turn. MsgIndex is len(Session.Messages) at the turn's start — the conversation-rewind boundary — persisted so a resumed session can rewind the conversation and fork, not just the code.

type FileSnap

type FileSnap struct {
	Path     string        `json:"path"`
	Content  *string       `json:"content"`
	Encoding *fileenc.Kind `json:"encoding,omitempty"`
	Perm     *uint32       `json:"perm,omitempty"`
	// Hash is the sha256 of the snapshotted (pre-edit) decoded text — the
	// rewind preview uses it to detect edits made outside this agent.
	Hash string `json:"hash,omitempty"`
	// PostHash is the sha256 of the file's decoded text right after the writer
	// tool that touched it completed (updated on every edit within the turn),
	// so the preview can tell "agent wrote this last" from "changed since".
	PostHash string `json:"postHash,omitempty"`
}

FileSnap is one file's state at the moment it was first touched in a turn. Content == nil means the file did not exist then, so a restore deletes it. Perm captures the file's permission bits (e.g. 0755 for scripts) so a restore preserves executability instead of forcing everything to 0644.

type Meta

type Meta struct {
	Turn   int
	Time   time.Time
	Prompt string
	Paths  []string
}

Meta is the picker-facing summary of a checkpoint (no file contents).

type Store

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

Store holds a session's checkpoints in memory and, when dir is set, persists one JSON file per turn under it (cheap delete, corruption-isolated). All methods are safe for concurrent use — the agent snapshots from tool goroutines.

func New

func New(dir, root string) *Store

New returns a store for the given checkpoint dir and workspace root, loading any checkpoints already persisted under dir. A "" dir disables persistence (the store still works in memory for the session).

func NewWithLimit added in v0.2.0

func NewWithLimit(dir, root string, max int) *Store

NewWithLimit is New with a custom retention cap. Event-oriented consumers (e.g. the netdev state history, one checkpoint per state transition rather than per conversation turn) fire far more often than a chat session and pass a larger cap. max < 1 is clamped to 1.

func (*Store) Begin

func (s *Store) Begin(turn int, prompt string, msgIndex int)

Begin opens a checkpoint for a new user turn, finalizing the previous one. The prompt labels it in the picker; msgIndex is the conversation-rewind boundary.

func (*Store) Bounds

func (s *Store) Bounds() map[int]int

Bounds returns turn → MsgIndex over all checkpoints (persisted + current), so the controller can rebuild its conversation-rewind boundaries after loading a resumed session's checkpoints from disk.

func (*Store) CurrentHash added in v0.2.0

func (s *Store) CurrentHash(path string) string

CurrentHash returns the file's decoded-text hash now ("" when absent or unreadable) — the "now" side of the rewind safety classification.

func (*Store) DiffForTurn added in v0.2.0

func (s *Store) DiffForTurn(turn int) []diff.Change

DiffForTurn (upgrade spec 3-6) renders what a code-scope rewind of `turn` would restore: one change per snapshotted file, old side = current on-disk content, new side = the snapshotted pre-edit state — so the preview reads as "these are the changes the rewind will revert". A create snapshot (nil Content) previews as a delete of the current file; a file removed since the snapshot previews as a recreate.

func (*Store) Finalize added in v0.2.0

func (s *Store) Finalize()

Finalize closes the active checkpoint so it reports its Paths through List and participates in restore bookkeeping as a completed entry. Conversation-turn consumers leave the turn open until the next Begin (an in-progress turn's files must not propagate CanCode); event-oriented consumers (netdev state history) finalize immediately after each event so the newest entry is fully visible. A no-op when no checkpoint is open.

func (*Store) KeepCurrent added in v0.2.0

func (s *Store) KeepCurrent(prompt string, msgIndex int, paths []string) int

KeepCurrent snapshots the CURRENT on-disk content of paths as a finalized synthetic checkpoint — the reverse side of a rewind. Restoring back to that checkpoint replays (reapplies) the rewound edits; a path absent now is kept as a create marker so the replay deletes it again. msgIndex is the conversation boundary to record for the synthetic turn. Returns the new turn, or -1 when there was nothing to keep.

func (*Store) List

func (s *Store) List() []Meta

List returns every checkpoint's metadata, oldest turn first.

func (*Store) NextTurn

func (s *Store) NextTurn() int

NextTurn returns the turn number a new checkpoint should take: one past the highest existing turn (0 when empty), so a resumed session keeps numbering without colliding with checkpoints loaded from disk.

func (*Store) NotePostEdit added in v0.2.0

func (s *Store) NotePostEdit(path string)

NotePostEdit records the file's on-disk content hash after the writer tool that touched it completed, on the current turn's snapshot for that path (no-op when the path wasn't snapshotted this turn). The rewind preview compares it against the current hash to classify a rewind as safe (nothing external since the agent's last write) or unsafe.

func (*Store) RestoreCode

func (s *Store) RestoreCode(fromTurn int) (written, deleted []string, err error)

RestoreCode reverts the workspace to its state at the start of turn `fromTurn`: for every file touched in turn fromTurn or later, it writes back that file's earliest recorded content (or deletes it when the earliest snapshot was nil). Returns the paths written and deleted.

func (*Store) Snapshot

func (s *Store) Snapshot(ch diff.Change)

Snapshot records the pre-edit state of the file a writer is about to change. Only the first touch of a path in the current turn is kept (that is its turn-start content). A no-op before the first Begin.

func (*Store) SuffixInfo added in v0.2.0

func (s *Store) SuffixInfo(fromTurn int) []SuffixFileInfo

SuffixInfo summarizes the suffix of fromTurn per path: the earliest snapshot (what a rewind restores to) and the most recent post-edit hash (the last state the agent itself wrote).

func (*Store) SuffixPaths added in v0.2.0

func (s *Store) SuffixPaths(fromTurn int) []string

SuffixPaths lists every distinct path touched from fromTurn onward — exactly the set a code rewind of fromTurn would restore.

type SuffixFileInfo added in v0.2.0

type SuffixFileInfo struct {
	Path     string
	PreHash  string // hash of the earliest snapshot's content
	PostHash string // latest post-edit hash recorded across the suffix
}

SuffixFileInfo is what the rewind preview needs for one suffix path.

Jump to

Keyboard shortcuts

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