Documentation
¶
Overview ¶
Package checkpoints keeps backups of the files celeste's write tools change, on disk per session, so a change can be undone later — also from another process (2.0 F4).
Index ¶
- Constants
- Variables
- func DisplayPath(workspace, path string) string
- func FormatChanges(changes []FileChange, workspace string) string
- func LatestSessionFor(root, path string) (string, error)
- func Prune(root, keep string, now time.Time) error
- func Root() string
- func SameEntry(a, b Entry) bool
- func SessionDir(root, sessionID string) string
- type Checkpoint
- type Entry
- type FileChange
- type FileState
- type FileTracker
- type SnapshotManager
- func (sm *SnapshotManager) Checkpoint(path, messageID string) (*Checkpoint, error)
- func (sm *SnapshotManager) CheckpointIn(workspace, path, messageID string) (*Checkpoint, error)
- func (sm *SnapshotManager) Cleanup() error
- func (sm *SnapshotManager) ComputeDiff() ([]FileChange, error)
- func (sm *SnapshotManager) Dir() string
- func (sm *SnapshotManager) Entries() []Entry
- func (sm *SnapshotManager) Evicted(ids []string) bool
- func (sm *SnapshotManager) Files() []string
- func (sm *SnapshotManager) Revert(path string) (Entry, error)
- func (sm *SnapshotManager) RevertIf(path string, check func(Entry) error) (Entry, error)
- func (sm *SnapshotManager) RevertLast() (Entry, error)
- func (sm *SnapshotManager) RevertLastIf(check func(Entry) error) (Entry, error)
- func (sm *SnapshotManager) RewindTo(messageID string) ([]Entry, error)
- func (sm *SnapshotManager) RewindToAnyIf(ids []string, check func([]Entry) error) ([]Entry, error)
Constants ¶
const ( KeepSessions = 20 KeepAge = 30 * 24 * time.Hour )
Retention (2.0 F4): a session's checkpoints survive while the session is one of the KeepSessions most recently changed, or was changed less than KeepAge ago — whichever keeps more.
Variables ¶
var ErrDisabled = errors.New("file checkpoints are disabled: no home or cache directory")
ErrDisabled is returned by a store with no directory to keep checkpoints in (see Root): checkpoints are off.
var ErrNotRead = errors.New("not read in this session")
ErrNotRead: an existing file was never read in this session.
Functions ¶
func DisplayPath ¶
DisplayPath shows path relative to workspace when it is inside it, and as it is otherwise.
func FormatChanges ¶
func FormatChanges(changes []FileChange, workspace string) string
FormatChanges renders ComputeDiff's result for /diff: one line per file, relative to workspace when inside it, in the order given (ComputeDiff sorts by path).
func LatestSessionFor ¶
LatestSessionFor returns the session (its directory name under root) whose newest checkpoint of path is the most recent across all sessions (celeste revert without --session). Paths match as pathutil.Same does.
func Prune ¶
Prune deletes the session directories under root that retention does not keep. keep (the session starting now) always survives. A missing root is nothing to prune.
func Root ¶
func Root() string
Root is where every session's checkpoints live: ~/.celeste/checkpoints, or <user cache dir>/celeste/checkpoints when there is no home directory. With neither it is "" and checkpoints are off (one warning): they are never written under the working directory.
func SessionDir ¶
SessionDir is sessionID's directory under root (ruling 5), or "" (no checkpoints) when root is "".
Types ¶
type Checkpoint ¶
type Checkpoint struct {
// contains filtered or unexported fields
}
Checkpoint is an entry its writer can still roll back.
func (*Checkpoint) Commit ¶
func (c *Checkpoint) Commit() error
Commit records the file as the write left it (Entry.After), once the tool's write succeeded, and closes the checkpoint. Every Checkpoint ends in Commit or Rollback. An entry already undone or evicted is left alone.
func (*Checkpoint) Rollback ¶
func (c *Checkpoint) Rollback() error
Rollback restores the file to its checkpointed state (deleting a file that did not exist) and removes the entry and its backup: for a write that failed after Checkpoint. After the entry was already undone, reverted or evicted it does nothing.
type Entry ¶
type Entry struct {
// MessageID is the ID of the tool call that made the change ("" when
// unknown), so a rewind can map entries to messages.
MessageID string `json:"message_id"`
// Path is the absolute path of the changed file.
Path string `json:"path"`
// Version counts the session's changes to Path: 1, 2, …
Version int `json:"version"`
// Backup is the backup's file name in the session directory; "" when
// the file did not exist, so undoing the change deletes it.
Backup string `json:"backup"`
// Time is when the checkpoint was taken (UTC).
Time time.Time `json:"time"`
// After is the file as the change left it, recorded when the tool's
// write succeeded (Commit); nil until then, or when it could not be
// read. Undoing the change compares the file with it (Changed).
After *FileState `json:"after,omitempty"`
// Root is the real directory undoing the change must stay inside (the
// workspace, see CheckpointIn), and Rel the file's path inside it at
// the time of the change. Undo reaches the file only through Root, so
// a directory replaced by a symlink since cannot redirect it. Empty in
// an index written before they existed.
Root string `json:"root,omitempty"`
Rel string `json:"rel,omitempty"`
}
Entry is one checkpoint: Path as it was before a tool changed it.
func RevertFile ¶
RevertFile restores path from its newest checkpoint in sessionID — or, when sessionID is "", in the session that changed it last — and removes that entry (celeste revert, 2.0 F4). check, when not nil, is asked about that entry first, as in RevertIf.
type FileChange ¶
type FileChange struct {
Path string
Insertions int
Deletions int
IsNew bool
// Deleted: the file no longer exists.
Deleted bool
// Binary: either side holds a NUL byte; no line counts.
Binary bool
// Approx: the line comparison was too large; the counts are the
// difference in line count only.
Approx bool
// TooBig: a side is over maxDiffBytes and was not read; OldSize and
// NewSize are the byte sizes.
TooBig bool
OldSize, NewSize int64
// Err: this file could not be compared (the others still are).
Err string
}
FileChange represents the diff stats for a single file.
type FileState ¶
FileState identifies a file's contents: its size and SHA-256.
func Changed ¶
Changed reports whether e's file is no longer as e's change left it: something else (an editor, a formatter, a bash command, another write whose checkpoint failed) changed it since, so undoing e would overwrite that — or, for a file the change created, delete it. A file that is gone has nothing to lose (false). An entry whose After was never recorded counts as changed: celeste cannot tell. now is the file's state (zero when it is gone or unreadable).
type FileTracker ¶
type FileTracker struct {
// contains filtered or unexported fields
}
FileTracker tracks file modification times to detect stale reads. When a file is read, its mtime is recorded. Before writing, the caller can check whether the file was modified externally since the last read.
func (*FileTracker) CheckRead ¶
func (ft *FileTracker) CheckRead(path string) error
CheckRead is the must-read-before-edit rule (2.0 W4 ruling 6): nil for a file that does not exist, or that was read (or written) in this session and is unchanged since; ErrNotRead for an existing file never read; the CheckStale error for one changed since its read; the stat error for a file that cannot be checked (reading it first would not help).
func (*FileTracker) CheckStale ¶
func (ft *FileTracker) CheckStale(path string) error
CheckStale compares the file's current mtime against the stored mtime. Returns an error if the file was modified externally since the last read. Returns nil if the file has never been tracked (first write is allowed).
func (*FileTracker) ClearStale ¶
func (ft *FileTracker) ClearStale(path string)
ClearStale removes tracking for the given path (e.g. after a successful re-read).
func (*FileTracker) RecordRead ¶
func (ft *FileTracker) RecordRead(path string) error
RecordRead stats the file at path and stores its current mtime.
func (*FileTracker) Reset ¶
func (ft *FileTracker) Reset()
Reset forgets every recorded read, so the next write to any file is allowed as a first write. MCP chat calls it when a shared environment starts a call with no other call in flight: staleness is judged per call.
type SnapshotManager ¶
type SnapshotManager struct {
// contains filtered or unexported fields
}
SnapshotManager is one session's checkpoints: its backups and index.json in one directory. It is safe for concurrent use. Every change is written to the index before it takes effect in memory.
func NewSnapshotManager ¶
func NewSnapshotManager(sessionID string) *SnapshotManager
NewSnapshotManager opens the checkpoints of sessionID under Root(), with whatever an earlier process recorded for it (a resumed session). The first call in a process prunes old sessions first (never this one). Nothing is created on disk until the first checkpoint.
func (*SnapshotManager) Checkpoint ¶
func (sm *SnapshotManager) Checkpoint(path, messageID string) (*Checkpoint, error)
Checkpoint backs up path as it is now and records the entry in the index. Call it after the tool's input validated, immediately before the write, and Rollback the result if the write then fails (2.0 F4). A path that does not exist yet is recorded without a backup. A path that is not a regular file is refused and nothing is recorded. The backup is a full copy, read into memory, with no size limit: a session holds at most defaultMaxEntries of them, which bounds the count but not the bytes (a session that rewrites a large file keeps up to that many copies of it).
Undoing the change stays inside path's own directory; the write tools use CheckpointIn to keep it inside the workspace.
func (*SnapshotManager) CheckpointIn ¶
func (sm *SnapshotManager) CheckpointIn(workspace, path, messageID string) (*Checkpoint, error)
CheckpointIn is Checkpoint for a path inside workspace: undoing the change (undo, rewind, celeste revert, Rollback) reaches the file only through workspace, so it cannot write or delete anything outside it, even after a directory on the way was replaced by a symlink. path's directory must exist and resolve inside workspace.
func (*SnapshotManager) Cleanup ¶
func (sm *SnapshotManager) Cleanup() error
Cleanup removes the session's directory and forgets its entries.
func (*SnapshotManager) ComputeDiff ¶
func (sm *SnapshotManager) ComputeDiff() ([]FileChange, error)
ComputeDiff compares each changed file's oldest backup (its state before the session changed it) with the file now. Sorted by path. A file that cannot be compared carries its error in FileChange.Err. The session lock is held only while the index and the backups are read, so an undo in another process cannot remove a backup between the two; the current files are read and diffed after it is released, so a large /diff never keeps other processes waiting.
func (*SnapshotManager) Dir ¶
func (sm *SnapshotManager) Dir() string
Dir is the session's checkpoint directory.
func (*SnapshotManager) Entries ¶
func (sm *SnapshotManager) Entries() []Entry
Entries returns a copy of the session's entries, oldest first.
func (*SnapshotManager) Evicted ¶
func (sm *SnapshotManager) Evicted(ids []string) bool
Evicted reports whether the cap evicted an entry recorded for any of ids (see evictedFile): a rewind of them cannot restore every change.
func (*SnapshotManager) Files ¶
func (sm *SnapshotManager) Files() []string
Files returns the sorted, de-duplicated paths of this session's entries: the files it changed (#200's files-modified list).
func (*SnapshotManager) Revert ¶
func (sm *SnapshotManager) Revert(path string) (Entry, error)
Revert restores path from its newest entry and removes that entry.
func (*SnapshotManager) RevertIf ¶
RevertIf is Revert that first asks check (when not nil) about the entry it would undo, under the store's locks, and undoes nothing when check returns an error, which it returns.
func (*SnapshotManager) RevertLast ¶
func (sm *SnapshotManager) RevertLast() (Entry, error)
RevertLast restores the file of the newest entry and removes it (/undo).
func (*SnapshotManager) RevertLastIf ¶
func (sm *SnapshotManager) RevertLastIf(check func(Entry) error) (Entry, error)
RevertLastIf is RevertLast with check, as RevertIf.
func (*SnapshotManager) RewindTo ¶
func (sm *SnapshotManager) RewindTo(messageID string) ([]Entry, error)
RewindTo undoes, newest first, every entry from the first one recorded for messageID to the newest (W4's /rewind) and returns them in that order. On an error it stops and returns what it undid so far.
func (*SnapshotManager) RewindToAnyIf ¶
RewindToAnyIf is RewindTo from the first entry recorded for any of ids (2.0 W4's /rewind, ruling 5). check (when not nil) first sees the entries it would undo, oldest first, under the store's locks; an error from it undoes nothing and is returned. When no entry has one of ids it undoes nothing and returns no error.