pub struct CheckpointStore { /* private fields */ }Expand description
A directory-backed, content-addressed shadow store rooted at root —
see the module doc comment. Never touches anything outside root except
(during Self::restore) the project files a manifest names, which are
re-validated against project_root independently of how the manifest
was produced.
Implementations§
Source§impl CheckpointStore
impl CheckpointStore
Sourcepub fn open(root: impl Into<PathBuf>) -> Result<Self>
pub fn open(root: impl Into<PathBuf>) -> Result<Self>
Open (creating if needed) a store at root. Err if root can’t
be created (e.g. an unwritable state dir) — callers (see
observer_for_config) treat that as “disable checkpoint, warn
once”, never a crash.
Sourcepub fn create_checkpoint(&self, label: &str) -> Result<CheckpointId>
pub fn create_checkpoint(&self, label: &str) -> Result<CheckpointId>
Mint a fresh checkpoint (a new turn) with an empty file list. label
is display-only.
Sourcepub fn record_pre_image(
&self,
id: &str,
rel: &str,
content: Option<Vec<u8>>,
) -> Result<()>
pub fn record_pre_image( &self, id: &str, rel: &str, content: Option<Vec<u8>>, ) -> Result<()>
Idempotently record rel’s pre-image under checkpoint id — a
SECOND call for the same (id, rel) pair is a no-op (the manifest
always keeps the EARLIEST pre-image seen this turn, which is the one
a revert needs). content: None means the file did not exist yet.
Sourcepub fn manifest(&self, id: &str) -> Result<CheckpointManifest>
pub fn manifest(&self, id: &str) -> Result<CheckpointManifest>
Read one checkpoint’s full manifest.
Sourcepub fn list(&self) -> Result<Vec<CheckpointMeta>>
pub fn list(&self) -> Result<Vec<CheckpointMeta>>
Every checkpoint in the store, newest first (ids are millis-prefixed so lexical descending order IS chronological descending order). A corrupt individual manifest is skipped (resilience — one bad file never hides every other checkpoint), not a hard error.
Sourcepub fn turn_diff(&self, id: &str) -> Result<Vec<String>>
pub fn turn_diff(&self, id: &str) -> Result<Vec<String>>
D3 turn-diff: the set of project-relative paths this checkpoint’s turn touched — exactly the manifest’s file list (every entry exists BECAUSE a write-tool call captured a pre-image for it this turn).
Sourcepub fn restore(
&self,
id: &str,
project_root: &Path,
protected_globs: &[String],
) -> Result<RestoreReport>
pub fn restore( &self, id: &str, project_root: &Path, protected_globs: &[String], ) -> Result<RestoreReport>
D4-adjacent revert: restore project_root’s working files to
checkpoint id. For each manifest entry: Some(blob) rewrites the
file to that pre-image; None (didn’t exist before the turn)
deletes it if present now (undoing a create). SECURITY: every
target is independently re-validated (never trusts the manifest was
produced honestly) against project_root containment (no symlink
escape, no .. traversal) AND protected_globs (plus an
unconditional .git/** floor) checked against BOTH the LEXICAL
normalized path and the symlink-RESOLVED path — a lexical-only
check would miss a manifest entry like foo/config where foo is
a pre-existing symlink into .git: lexically it’s clean, but it
resolves inside root (so containment alone accepts it too) and
lands on the real .git/config. A refused entry is recorded in
RestoreReport::refused, never silently applied AND never aborts
the rest of the restore (partial-success, fully reported).