toolsnap

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package toolsnap captures ephemeral workspace snapshots around agent-executed tool windows. Snapshots are Git trees stored in an isolated bare object store under .semantica, with the user repository's object database as a read-only alternate. The user repository's objects and refs are never modified.

Index

Constants

View Source
const (
	// DefaultMaxCandidatePaths bounds nominated paths per snapshot.
	DefaultMaxCandidatePaths = 5000
	// DefaultMaxBytesRead bounds newly read file content per snapshot.
	DefaultMaxBytesRead = 128 << 20
)

Default capture limits bound per-snapshot work and storage.

View Source
const (
	ReasonFileLimit        = "file_limit"
	ReasonByteLimit        = "byte_limit"
	ReasonUnsupportedPath  = "unsupported_path"
	ReasonMalformedStatus  = "malformed_status"
	ReasonHeadChanged      = "head_changed"
	ReasonAlternateGone    = "alternate_object_missing"
	ReasonTimeout          = "timeout"
	ReasonLockTimeout      = "registry_lock_timeout"
	ReasonStoreUnavailable = "snapshot_store_unavailable"
	// ReasonPostSnapshotLost marks a retry without durable post state.
	ReasonPostSnapshotLost = "post_snapshot_lost"
	// ReasonStaleActiveWindow marks evidence from a sealed group.
	// Its unbounded span contributes no attribution.
	ReasonStaleActiveWindow = "stale_active_window"
)

Partial reason strings are persisted and must remain stable.

View Source
const DefaultPruneGrace = DefaultStaleWindowAge

DefaultPruneGrace protects objects for at least the pending-snapshot period.

View Source
const DefaultStaleActiveAge = time.Hour

DefaultStaleActiveAge bounds a group's join horizon. Later completions produce partial evidence.

View Source
const DefaultStaleWindowAge = 24 * time.Hour

DefaultStaleWindowAge is the retention period for pending snapshots.

View Source
const DeltaVersion = 1

DeltaVersion is the canonical tool-delta schema version.

Variables

View Source
var ErrNoPendingSnapshot = errors.New("toolsnap: no pending snapshot for key")

ErrNoPendingSnapshot reports a post hook without a matching registered window; callers degrade to pre_snapshot_missing.

View Source
var ErrRegistryCorrupt = errors.New("toolsnap: registry state corrupt")

ErrRegistryCorrupt reports invalid persisted window or final state.

View Source
var ErrStoreIncompatible = errors.New("toolsnap: snapshot store incompatible with repository")

ErrStoreIncompatible reports an object-format mismatch between the snapshot store and repository.

View Source
var ErrWindowSealed = errors.New("toolsnap: window group sealed")

ErrWindowSealed reports a post hook for a sealed group.

View Source
var ErrWindowTombstoned = errors.New("toolsnap: window tombstoned")

ErrWindowTombstoned reports a post hook for an abandoned window.

Functions

func GroupPostRef

func GroupPostRef(worktreeID, groupID string) string

GroupPostRef returns the ref that protects a group's post tree.

func SnapshotRef

func SnapshotRef(worktreeID, groupID, toolUseID string) string

SnapshotRef returns the private pre-snapshot ref for a tool window.

Types

type Actor

type Actor struct {
	Provider  string `json:"provider"`
	SessionID string `json:"session_id"`
	TurnID    string `json:"turn_id"`
}

type CaptureResult

type CaptureResult struct {
	Post      Snapshot
	Files     []FileDelta
	BytesRead int64
	Truncated bool
}

CaptureResult contains a post-tool snapshot and its bounded delta.

type CompletionInfo

type CompletionInfo struct {
	At             int64
	EventID        string
	CommandSummary string
}

CompletionInfo carries what a post hook knows about its member at completion time.

type Delta

type Delta struct {
	Version  int         `json:"version"`
	Kind     string      `json:"kind"`
	Scope    string      `json:"scope"`  // "tool" or "concurrent_group"
	Status   string      `json:"status"` // "complete" or "partial"
	Reason   string      `json:"reason,omitempty"`
	Window   Window      `json:"window"`
	Actors   []Actor     `json:"actors"`
	ToolUses []ToolUse   `json:"tool_uses"`
	Files    []FileDelta `json:"files"`
	Limits   Limits      `json:"limits"`
}

Delta is canonical evidence for one tool window or concurrency group. Equivalent values serialize identically for content addressing.

func ParseDelta

func ParseDelta(raw []byte) (*Delta, error)

ParseDelta accepts only the canonical encoding for this schema version.

func (*Delta) CanonicalBytes

func (d *Delta) CanonicalBytes() ([]byte, error)

CanonicalBytes validates, normalizes, and serializes the delta.

func (*Delta) Normalize

func (d *Delta) Normalize()

Normalize orders repeated fields and replaces nil collections with empty collections.

func (*Delta) Validate

func (d *Delta) Validate() error

Validate rejects structurally invalid or contradictory evidence.

type FileChange

type FileChange struct {
	Path       string
	Op         byte // A, D, M, T (typechange)
	BeforeMode string
	AfterMode  string
	BeforeHash string
	AfterHash  string
}

FileChange is one raw tree-level difference between two snapshots. Only path, mode, and object identity come from Git; content loading, binary detection, and line diffing happen under Semantica's own versioned rules so repository diff configuration can never shape evidence.

type FileDelta

type FileDelta struct {
	Path            string `json:"path"`
	Operation       string `json:"operation"` // create, edit, delete, typechange
	BeforeHash      string `json:"before_hash"`
	AfterHash       string `json:"after_hash"`
	BeforeMode      string `json:"before_mode"`
	AfterMode       string `json:"after_mode"`
	Binary          bool   `json:"binary,omitempty"`
	Truncated       bool   `json:"truncated,omitempty"`
	OldNoEOFNewline bool   `json:"old_no_eof_newline,omitempty"`
	NewNoEOFNewline bool   `json:"new_no_eof_newline,omitempty"`
	Hunks           []Hunk `json:"hunks,omitempty"`
}

FileDelta records one file change. Modes are Git octal strings. Binary and truncated files carry file-level evidence without hunks.

type FinalizeResult

type FinalizeResult struct {
	// Done means the group's evidence is fully durable; the group is
	// removed in the same registry transaction.
	Done bool
	// Final preserves captured state for retry. A zero value requires
	// deterministic partial evidence instead of another workspace read.
	Final GroupFinal
}

FinalizeResult is the outcome of one finalization attempt.

type GroupFinal

type GroupFinal struct {
	// PostTreeHash identifies the final workspace tree. The caller
	// must have made it reachable (post ref) before returning it.
	PostTreeHash string `json:"post_tree_hash,omitempty"`
	// DeltaHash identifies the canonical delta when it was computed.
	DeltaHash string `json:"delta_hash,omitempty"`
	// PartialReason marks a group whose final capture is permanently
	// unavailable; retries finalize deterministic partial evidence.
	PartialReason string `json:"partial_reason,omitempty"`
	CapturedAt    int64  `json:"captured_at,omitempty"`
}

GroupFinal preserves a group's closing state across finalization retries.

type GroupMeta

type GroupMeta struct {
	CreatedAt int64 `json:"created_at"`
	JoinUntil int64 `json:"join_until"`
	// Sealed groups accept no members and produce only partial evidence.
	Sealed bool `json:"sealed,omitempty"`
}

GroupMeta records an immutable join horizon and seal state.

type Hunk

type Hunk struct {
	OldStart int      `json:"old_start"`
	OldCount int      `json:"old_count"`
	NewStart int      `json:"new_start"`
	NewCount int      `json:"new_count"`
	OldLines []string `json:"old_lines"`
	NewLines []string `json:"new_lines"`
}

Hunk is a contiguous, context-free change between two text blobs. Lines omit trailing newlines, and positions are 1-based.

type Limits

type Limits struct {
	FilesObserved int   `json:"files_observed"`
	BytesRead     int64 `json:"bytes_read"`
	Truncated     bool  `json:"truncated"`
}

type MaintenanceReport

type MaintenanceReport struct {
	// Deferred reports an incomplete pass. Counters include completed work.
	Deferred      bool
	ActiveWindows int
	RefsDeleted   int
	RefsKept      int
	MarkersPruned int
	PruneRan      bool
	// StoreBytes is the store object size after the pass.
	StoreBytes int64
}

MaintenanceReport describes one maintenance pass.

type PartialError

type PartialError struct {
	Reason string
	Detail string
}

PartialError reports a capture that must degrade to partial evidence with a stable reason instead of guessing.

func (*PartialError) Error

func (e *PartialError) Error() string

type PendingFinalization

type PendingFinalization struct {
	GroupID string
	Members []PendingToolSnapshot
	Final   *GroupFinal
}

PendingFinalization is a group whose members are all complete but whose finalization has not durably finished.

type PendingPartialRecord

type PendingPartialRecord struct {
	Key            ToolKey `json:"key"`
	EventID        string  `json:"event_id"`
	Reason         string  `json:"reason"`
	ToolName       string  `json:"tool_name"`
	CommandSummary string  `json:"command_summary,omitempty"`
	Timestamp      int64   `json:"timestamp"`
}

PendingPartialRecord preserves canonical inputs for a groupless partial. Retries rebuild evidence from this record instead of a reparsed hook.

type PendingToolSnapshot

type PendingToolSnapshot struct {
	Key          ToolKey `json:"key"`
	ToolName     string  `json:"tool_name"`
	SnapshotRef  string  `json:"snapshot_ref"`
	TreeHash     string  `json:"tree_hash"`
	HeadHash     string  `json:"head_hash"`
	ObjectFormat string  `json:"object_format"`
	StartedAt    int64   `json:"started_at"`
	CompletedAt  int64   `json:"completed_at,omitempty"`
	// Seq is the registry-local capture order. Wall clocks can
	// collide within a millisecond; the sequence, assigned under the
	// lock, identifies the earliest captured snapshot.
	Seq int64 `json:"seq"`
	// EventID and CommandSummary are recorded at completion so group
	// closure can link and describe every member without loading its
	// event.
	EventID        string `json:"event_id,omitempty"`
	CommandSummary string `json:"command_summary,omitempty"`
	GroupID        string `json:"group_id"`
	Status         string `json:"status"` // "active" or "complete"
}

PendingToolSnapshot is one registered tool window.

type ReclaimedGroup

type ReclaimedGroup struct {
	GroupID string
	// Completed members converted to pending partials.
	Completed int
	// Tombstoned member identities.
	Tombstoned int
}

ReclaimedGroup summarizes one removed sealed group.

type Registry

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

Registry coordinates repository-scoped tool windows across hook processes.

func OpenRegistry

func OpenRegistry(semDir string) (*Registry, error)

OpenRegistry prepares the registry directories under semDir.

func (*Registry) Begin

func (r *Registry) Begin(ctx context.Context, entry PendingToolSnapshot) (string, error)

Begin registers a pre-existing snapshot. Hook callers use CaptureAndBegin so capture and registration share one lock.

func (*Registry) CaptureAndBegin

func (r *Registry) CaptureAndBegin(ctx context.Context, s *Store, key ToolKey, toolName string, startedAt int64) (PendingToolSnapshot, error)

CaptureAndBegin captures and registers a pre-tool snapshot while holding the registry lock. Duplicate deliveries reuse the existing entry.

func (*Registry) Complete

func (r *Registry) Complete(ctx context.Context, key ToolKey, info CompletionInfo, persistMember func(PendingToolSnapshot) error, finalize func(members []PendingToolSnapshot, prior *GroupFinal, retry bool, recordIntent func() error) (FinalizeResult, error)) (bool, error)

Complete persists a member and finalizes its group in capture order. Retries must use durable state rather than recapturing the workspace.

func (*Registry) HasTombstone

func (r *Registry) HasTombstone(key ToolKey) (bool, error)

HasTombstone reports whether a window is ineligible. Probe errors fail closed.

func (*Registry) ListTombstones

func (r *Registry) ListTombstones() (tombs []Tombstone, malformed []string, err error)

ListTombstones returns valid records and the names of malformed entries. Each record must match its filename identity.

func (*Registry) LoadOrRecordPendingPartial

func (r *Registry) LoadOrRecordPendingPartial(rec PendingPartialRecord) (PendingPartialRecord, error)

LoadOrRecordPendingPartial creates or returns an event's first record. Existing records are validated before use.

func (*Registry) PendingFinalizations

func (r *Registry) PendingFinalizations(ctx context.Context) ([]PendingFinalization, error)

PendingFinalizations lists complete groups awaiting finalization.

func (*Registry) PendingPartialRecords

func (r *Registry) PendingPartialRecords() ([]PendingPartialRecord, error)

PendingPartialRecords lists the records for recovery sweeps.

func (*Registry) ReclaimSealedGroups

func (r *Registry) ReclaimSealedGroups(ctx context.Context, now int64) ([]ReclaimedGroup, error)

ReclaimSealedGroups records partial evidence, tombstones every member, and removes sealed groups. Recovery writes occur outside the registry lock. Removal revalidates the captured member state before publication.

func (*Registry) RemoveGroup

func (r *Registry) RemoveGroup(ctx context.Context, groupID string) error

RemoveGroup deletes stale group members and their pending final identity.

func (*Registry) RemovePendingPartial

func (r *Registry) RemovePendingPartial(eventID string) error

RemovePendingPartial deletes a recovery record after its link is durable.

func (*Registry) RemoveTombstone

func (r *Registry) RemoveTombstone(key ToolKey) error

RemoveTombstone deletes a tombstone after its partial evidence is durable.

func (*Registry) ResumeFinalization

func (r *Registry) ResumeFinalization(ctx context.Context, groupID string, finalize func(members []PendingToolSnapshot, prior *GroupFinal, retry bool, recordIntent func() error) (FinalizeResult, error)) (bool, error)

ResumeFinalization retries a complete group from durable state.

func (*Registry) Stale

func (r *Registry) Stale(ctx context.Context, cutoff int64) ([]PendingToolSnapshot, error)

Stale returns windows whose pre snapshot is older than cutoff, for doctor reporting and tidy cleanup.

func (*Registry) WriteTombstone

func (r *Registry) WriteTombstone(key ToolKey, at int64) error

WriteTombstone atomically marks a window ineligible without the registry lock. The first complete record wins; interrupted writes remain temporary files.

type RegistrySnapshot

type RegistrySnapshot struct {
	// Exists reports whether the registry directory is present.
	Exists              bool
	Windows             []PendingToolSnapshot
	Groups              map[string]GroupMeta
	Finals              map[string]GroupFinal
	Partials            []PendingPartialRecord
	Tombstones          []Tombstone
	MalformedTombstones []string
}

RegistrySnapshot is a read-only view used by diagnostics and dry runs.

func InspectRegistry

func InspectRegistry(semDir string) (RegistrySnapshot, error)

InspectRegistry validates registry state and overlays receipts in memory. It does not create, consume, or publish files.

func (RegistrySnapshot) CompleteGroups

func (s RegistrySnapshot) CompleteGroups() []PendingFinalization

CompleteGroups returns groups whose members are all complete.

type RepoContext

type RepoContext struct {
	// WorktreeRoot is the top-level directory of the active worktree.
	WorktreeRoot string
	// GitDir is the worktree's git directory (per-worktree for linked
	// worktrees).
	GitDir string
	// CommonDir is the shared git directory holding the object database.
	CommonDir string
	// ObjectFormat is "sha1" or "sha256".
	ObjectFormat string
	// WorktreeID is a stable identifier for this worktree, used to
	// namespace snapshot refs.
	WorktreeID string
	// HeadCommit and HeadTree are resolved with the same rev-parse
	// invocation when HEAD exists; both are empty on an unborn branch.
	// The values can go stale before capture reads the worktree, so
	// CaptureBefore cross-checks them against the branch.oid reported
	// by status and degrades to partial on mismatch.
	HeadCommit string
	HeadTree   string
}

RepoContext identifies the worktree and object database a snapshot operates against. All paths are absolute and symlink-canonicalized.

func ResolveRepoContext

func ResolveRepoContext(ctx context.Context, dir string) (RepoContext, error)

ResolveRepoContext discovers the repository context for the given directory. It fails on bare repositories: snapshots capture worktree state, so a worktree is required.

type Snapshot

type Snapshot struct {
	TreeHash  string
	HeadHash  string
	DirtyPath []string
}

Snapshot identifies a captured workspace tree and its repository state.

type Store

type Store struct {
	// Dir is the bare store's git directory.
	Dir string

	// MaxCandidatePaths and MaxBytesRead bound one snapshot; zero
	// values apply the package defaults.
	MaxCandidatePaths int
	MaxBytesRead      int64
	// contains filtered or unexported fields
}

Store is the isolated bare Git object store holding ephemeral snapshot trees. It reads the user repository's objects through a read-only alternate but never writes to them.

func OpenStore

func OpenStore(ctx context.Context, rc RepoContext, semDir string) (*Store, error)

OpenStore opens or initializes the isolated snapshot store for the repository, wiring the repository's common object directory as a read-only alternate. semDir is the repository's .semantica directory.

func (*Store) CaptureAfter

func (s *Store) CaptureAfter(ctx context.Context, before Snapshot) (CaptureResult, error)

CaptureAfter compares the post-tool workspace with a prior snapshot. Capture limits and unavailable evidence return typed partial errors.

func (*Store) CaptureBefore

func (s *Store) CaptureBefore(ctx context.Context) (Snapshot, error)

CaptureBefore builds a workspace tree from HEAD and current changes. Unchanged subtrees retain their existing object IDs.

func (*Store) CreateRef

func (s *Store) CreateRef(ctx context.Context, ref, tree string) error

CreateRef atomically publishes a tree ref without replacing an existing ref.

func (*Store) DeleteRef

func (s *Store) DeleteRef(ctx context.Context, ref, expectedTree string) error

DeleteRef removes ref only when it still points to expectedTree.

func (*Store) DeltaBetweenTrees

func (s *Store) DeltaBetweenTrees(ctx context.Context, beforeTree, afterTree string) ([]FileDelta, int64, bool, error)

DeltaBetweenTrees computes bounded file deltas between captured trees.

func (*Store) DiffTrees

func (s *Store) DiffTrees(ctx context.Context, beforeTree, afterTree string) ([]FileChange, error)

DiffTrees compares two snapshot trees with fixed raw plumbing. Renames are never inferred; a rename appears as delete plus create. Git patch output is never consulted.

func (*Store) EnsureRef

func (s *Store) EnsureRef(ctx context.Context, ref, tree string) error

EnsureRef creates a ref or accepts an identical existing ref.

func (*Store) ListRefs

func (s *Store) ListRefs(ctx context.Context) (map[string]string, error)

ListRefs returns all snapshot refs and their targets.

func (*Store) Maintain

func (s *Store) Maintain(ctx context.Context, reg *Registry, grace time.Duration) (MaintenanceReport, error)

Maintain removes stale refs and prunes expired objects from the isolated store. It holds the registry lock and defers while capture is active.

func (*Store) ReadBlob

func (s *Store) ReadBlob(ctx context.Context, hash string) ([]byte, error)

ReadBlob loads blob content from the store, resolving through the repository alternate when the blob belongs to a committed tree.

func (*Store) WorktreeID

func (s *Store) WorktreeID() string

WorktreeID returns the store's worktree identity.

type Tombstone

type Tombstone struct {
	Key ToolKey `json:"key"`
	At  int64   `json:"at"`
}

Tombstone records when a tool window became ineligible.

type ToolKey

type ToolKey struct {
	RepositoryID string `json:"repository_id"`
	Provider     string `json:"provider"`
	SessionID    string `json:"session_id"`
	TurnID       string `json:"turn_id"`
	ToolUseID    string `json:"tool_use_id"`
}

ToolKey identifies a tool window. Only hook events enter the registry.

type ToolUse

type ToolUse struct {
	ToolUseID      string `json:"tool_use_id"`
	ToolName       string `json:"tool_name"`
	CommandSummary string `json:"command_summary,omitempty"`
	EventID        string `json:"event_id"`
	Actor          int    `json:"actor"`
}

type Window

type Window struct {
	StartedAt   int64 `json:"started_at"`
	CompletedAt int64 `json:"completed_at"`
	DurationMS  int64 `json:"duration_ms"`
}

Jump to

Keyboard shortcuts

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