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 ¶
- type Checkpoint
- type FileSnap
- type Meta
- type Store
- func (s *Store) Begin(turn int, prompt string, msgIndex int)
- func (s *Store) Bounds() map[int]int
- func (s *Store) CurrentHash(path string) string
- func (s *Store) DiffForTurn(turn int) []diff.Change
- func (s *Store) Finalize()
- func (s *Store) KeepCurrent(prompt string, msgIndex int, paths []string) int
- func (s *Store) List() []Meta
- func (s *Store) NextTurn() int
- func (s *Store) NotePostEdit(path string)
- func (s *Store) RestoreCode(fromTurn int) (written, deleted []string, err error)
- func (s *Store) Snapshot(ch diff.Change)
- func (s *Store) SuffixInfo(fromTurn int) []SuffixFileInfo
- func (s *Store) SuffixPaths(fromTurn int) []string
- type SuffixFileInfo
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 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 ¶
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
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 ¶
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 ¶
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
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
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
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) NextTurn ¶
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
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 ¶
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 ¶
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
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.