transaction

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: AGPL-3.0 Imports: 21 Imported by: 0

Documentation

Overview

Package transaction provides atomic file operations with git-based rollback.

Description

This package implements transactional file operations using git checkpoints. When a transaction begins, the current git state is captured. If the transaction fails or is explicitly rolled back, all changes are reverted to the checkpoint. If the transaction succeeds, changes can be committed as a single unit.

Thread Safety

TransactionManager is safe for concurrent use from multiple goroutines. Only one transaction may be active at a time per manager instance.

Nested Transactions

Nested transactions are NOT supported. Attempting to begin a transaction while one is already active will return ErrTransactionActive.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrTransactionActive is returned when attempting to begin a transaction
	// while one is already in progress.
	ErrTransactionActive = errors.New("transaction already active")

	// ErrNoTransaction is returned when attempting to commit or rollback
	// when no transaction is active.
	ErrNoTransaction = errors.New("no active transaction")

	// ErrNotGitRepository is returned when the working directory is not
	// a git repository.
	ErrNotGitRepository = errors.New("not a git repository")

	// ErrRebaseInProgress is returned when a git rebase is in progress,
	// which would interfere with transaction operations.
	ErrRebaseInProgress = errors.New("git rebase in progress")

	// ErrMergeInProgress is returned when a git merge is in progress,
	// which would interfere with transaction operations.
	ErrMergeInProgress = errors.New("git merge in progress")

	// ErrDetachedHead is returned when HEAD is not attached to a branch.
	// Agent operations may not work correctly in detached HEAD state.
	ErrDetachedHead = errors.New("repository is in detached HEAD state")

	// ErrCherryPickInProgress is returned when a cherry-pick operation is incomplete.
	// Complete or abort the cherry-pick before starting agent operations.
	ErrCherryPickInProgress = errors.New("cherry-pick in progress")

	// ErrBisectInProgress is returned when a git bisect is in progress.
	// Complete or reset the bisect before starting agent operations.
	ErrBisectInProgress = errors.New("git bisect in progress")

	// ErrDirtyWorkingTree is returned when uncommitted changes are present.
	// Commit, stash, or discard changes before starting agent operations.
	ErrDirtyWorkingTree = errors.New("uncommitted changes in working tree")

	// ErrTransactionExpired is returned when a transaction has exceeded
	// its TTL and was auto-rolled back.
	ErrTransactionExpired = errors.New("transaction expired")

	// ErrRollbackFailed is returned when rollback fails, leaving the
	// repository in an inconsistent state requiring manual intervention.
	ErrRollbackFailed = errors.New("rollback failed: manual intervention required")

	// ErrCheckpointNotFound is returned when the checkpoint reference
	// cannot be found (may have been garbage collected or corrupted).
	ErrCheckpointNotFound = errors.New("checkpoint not found")

	// ErrMaxFilesExceeded is returned when trying to track more files
	// than the configured maximum.
	ErrMaxFilesExceeded = errors.New("maximum tracked files exceeded")
)

Functions

func LoggerWithTrace

func LoggerWithTrace(ctx context.Context, logger *slog.Logger) *slog.Logger

LoggerWithTrace returns a logger with trace context fields.

Description

Extracts trace_id and span_id from the context and adds them to the logger for correlation with distributed traces.

Inputs

  • ctx: Context that may contain trace information.
  • logger: Base logger to extend.

Outputs

  • *slog.Logger: Logger with trace_id and span_id if available.

func SetMetricsEnabled

func SetMetricsEnabled(enabled bool)

SetMetricsEnabled controls whether metrics are recorded.

Thread Safety: Safe for concurrent use.

func ValidateConfig

func ValidateConfig(config PreFlightConfig) error

ValidateConfig validates a PreFlightConfig for consistency.

Description

Returns an error if the configuration is invalid or contradictory.

Inputs

  • config: Configuration to validate.

Outputs

  • error: Non-nil if configuration is invalid.

Types

type Config

type Config struct {
	// RepoPath is the git repository root directory.
	// Must be an absolute path.
	RepoPath string

	// Strategy is the checkpoint strategy to use.
	// Default: StrategyWorktree (prevents LSP/IDE conflicts)
	// Note: Use StrategyBranch if worktrees not supported or disk space is a concern.
	Strategy Strategy

	// TransactionTTL is how long a transaction can be active before
	// auto-rollback. Zero means no timeout.
	// Default: 30 minutes
	TransactionTTL time.Duration

	// GitTimeout is the maximum time for a single git operation.
	// Default: 30 seconds
	GitTimeout time.Duration

	// MaxTrackedFiles is the maximum number of files to track per transaction.
	// Prevents memory exhaustion on large operations.
	// Default: 10000
	MaxTrackedFiles int

	// StateDir is where transaction state is persisted for crash recovery.
	// Default: {RepoPath}/.aleutian/transactions
	StateDir string

	// CleanupOnInit removes stale transactions on manager creation.
	// Default: true
	CleanupOnInit bool

	// TracingEnabled controls whether OpenTelemetry spans are emitted.
	// When false, uses noop tracer for zero overhead.
	// Default: true
	TracingEnabled bool

	// MetricsEnabled controls whether Prometheus metrics are recorded.
	// Default: true
	MetricsEnabled bool

	// PreFlight configures pre-flight repository state validation.
	// These checks run before Begin() to prevent data loss.
	PreFlight PreFlightConfig
}

Config configures the TransactionManager behavior.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults.

Note on Strategy Default

The default is StrategyWorktree to prevent IDE/LSP conflicts. When the agent uses branch switching, the user's gopls/LSP sees thousands of file changes, causing cache invalidation and IDE freezes. Worktrees isolate agent work to a separate directory, leaving the user's workspace untouched.

Use StrategyBranch if:

  • Git worktrees are not supported (older git versions)
  • Disk space is constrained (worktrees require ~2x repo size)
  • User explicitly requests branch-based isolation

type DefaultGitClient

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

DefaultGitClient implements GitClient using the git command line.

Description

Executes git commands with configurable timeout and working directory. All operations are performed in the configured repository path.

Thread Safety

All methods are safe for concurrent use.

func NewGitClient

func NewGitClient(repoPath string, timeout time.Duration) (*DefaultGitClient, error)

NewGitClient creates a new git client for the specified repository.

Description

Creates a client that executes git commands in the given directory.

Inputs

  • repoPath: Absolute path to the git repository.
  • timeout: Maximum duration for each git operation.

Outputs

  • *DefaultGitClient: Ready-to-use git client.
  • error: Non-nil if repoPath is not absolute.

func (*DefaultGitClient) Add

func (g *DefaultGitClient) Add(ctx context.Context, paths ...string) error

Add stages files for commit.

Description

Stages the specified files for the next commit.

Inputs

  • ctx: Context for timeout and cancellation.
  • paths: File paths to stage.

Outputs

  • error: Non-nil if staging fails.

func (*DefaultGitClient) AddAll

func (g *DefaultGitClient) AddAll(ctx context.Context) error

AddAll stages all changes for commit.

Description

Stages all tracked and untracked changes.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • error: Non-nil if staging fails.

func (*DefaultGitClient) BranchExists

func (g *DefaultGitClient) BranchExists(ctx context.Context, name string) bool

BranchExists checks if a branch exists.

Description

Uses `git show-ref` to check for branch existence.

Inputs

  • ctx: Context for timeout and cancellation.
  • name: Branch name to check.

Outputs

  • bool: True if the branch exists.

func (*DefaultGitClient) Checkout

func (g *DefaultGitClient) Checkout(ctx context.Context, ref string) error

Checkout switches to the specified ref.

Description

Checks out a branch, tag, or commit SHA.

Inputs

  • ctx: Context for timeout and cancellation.
  • ref: Branch name, tag, or commit SHA.

Outputs

  • error: Non-nil if checkout fails.

func (*DefaultGitClient) CleanUntracked

func (g *DefaultGitClient) CleanUntracked(ctx context.Context) error

CleanUntracked removes untracked files and directories.

Description

Removes all untracked files and directories from the working tree. Does not remove ignored files.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • error: Non-nil if clean fails.

func (*DefaultGitClient) Commit

func (g *DefaultGitClient) Commit(ctx context.Context, message string) error

Commit creates a new commit with the staged changes.

Description

Creates a commit with the specified message. Fails if nothing is staged.

Inputs

  • ctx: Context for timeout and cancellation.
  • message: Commit message.

Outputs

  • error: Non-nil if commit fails.

func (*DefaultGitClient) CreateBranch

func (g *DefaultGitClient) CreateBranch(ctx context.Context, name string) error

CreateBranch creates a new branch at the current HEAD.

Description

Creates a branch without switching to it.

Inputs

  • ctx: Context for timeout and cancellation.
  • name: Branch name to create.

Outputs

  • error: Non-nil if branch already exists or creation fails.

func (*DefaultGitClient) CreateWorktree

func (g *DefaultGitClient) CreateWorktree(ctx context.Context, path string, ref string) error

CreateWorktree creates a new git worktree at the specified path.

Description

Creates a detached worktree at the given path, checked out to the specified ref. The worktree is independent from the main working directory.

Inputs

  • ctx: Context for timeout and cancellation.
  • path: Absolute path for the new worktree.
  • ref: Git ref to checkout (branch, tag, or commit SHA).

Outputs

  • error: Non-nil if worktree creation fails.

func (*DefaultGitClient) DeleteBranch

func (g *DefaultGitClient) DeleteBranch(ctx context.Context, name string, force bool) error

DeleteBranch deletes a branch.

Description

Deletes the specified branch. Use force=true for unmerged branches.

Inputs

  • ctx: Context for timeout and cancellation.
  • name: Branch name to delete.
  • force: If true, force delete even if unmerged.

Outputs

  • error: Non-nil if deletion fails.

func (*DefaultGitClient) GetCurrentBranch

func (g *DefaultGitClient) GetCurrentBranch(ctx context.Context) (string, error)

GetCurrentBranch returns the current branch name.

Description

Returns the current branch name, or "HEAD" if in detached HEAD state.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • string: Branch name or "HEAD".
  • error: Non-nil if not a git repository.

func (*DefaultGitClient) HasBisectInProgress

func (g *DefaultGitClient) HasBisectInProgress(ctx context.Context) bool

HasBisectInProgress checks if a git bisect is in progress.

Description

Checks for .git/BISECT_LOG file which indicates an active bisect session.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • bool: True if a bisect is in progress.

func (*DefaultGitClient) HasCherryPickInProgress

func (g *DefaultGitClient) HasCherryPickInProgress(ctx context.Context) bool

HasCherryPickInProgress checks if a cherry-pick is in progress.

Description

Checks for .git/CHERRY_PICK_HEAD file which indicates an incomplete cherry-pick.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • bool: True if a cherry-pick is in progress.

func (*DefaultGitClient) HasMergeInProgress

func (g *DefaultGitClient) HasMergeInProgress(ctx context.Context) bool

HasMergeInProgress checks if a merge is in progress.

Description

Checks for .git/MERGE_HEAD file.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • bool: True if a merge is in progress.

func (*DefaultGitClient) HasRebaseInProgress

func (g *DefaultGitClient) HasRebaseInProgress(ctx context.Context) bool

HasRebaseInProgress checks if a rebase is in progress.

Description

Checks for .git/rebase-merge or .git/rebase-apply directories.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • bool: True if a rebase is in progress.

func (*DefaultGitClient) HasStagedChanges

func (g *DefaultGitClient) HasStagedChanges(ctx context.Context) bool

HasStagedChanges checks if there are staged changes.

Description

Uses `git diff --cached` to check for staged changes.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • bool: True if there are staged changes.

func (*DefaultGitClient) HasUnstagedChanges

func (g *DefaultGitClient) HasUnstagedChanges(ctx context.Context) bool

HasUnstagedChanges checks if there are unstaged changes.

Description

Uses `git diff` to check for unstaged changes.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • bool: True if there are unstaged changes.

func (*DefaultGitClient) IsDetachedHead

func (g *DefaultGitClient) IsDetachedHead(ctx context.Context) bool

IsDetachedHead checks if the repository is in detached HEAD state.

Description

Returns true if HEAD is not pointing to a branch (detached HEAD state). This is detected when `git rev-parse --abbrev-ref HEAD` returns "HEAD".

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • bool: True if in detached HEAD state.

func (*DefaultGitClient) IsGitRepository

func (g *DefaultGitClient) IsGitRepository(ctx context.Context) bool

IsGitRepository checks if the path is a git repository.

Description

Uses `git rev-parse --git-dir` to determine if inside a git repository.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • bool: True if the path is inside a git repository.

func (*DefaultGitClient) RefExists

func (g *DefaultGitClient) RefExists(ctx context.Context, ref string) bool

RefExists checks if a git ref exists.

Description

Uses `git show-ref` to check if the ref exists.

Inputs

  • ctx: Context for timeout and cancellation.
  • ref: Git reference to check.

Outputs

  • bool: True if the ref exists.

func (*DefaultGitClient) RemoveWorktree

func (g *DefaultGitClient) RemoveWorktree(ctx context.Context, path string, force bool) error

RemoveWorktree removes a git worktree.

Description

Removes the worktree at the specified path. The worktree directory is deleted. Use force=true to remove even if there are uncommitted changes.

Inputs

  • ctx: Context for timeout and cancellation.
  • path: Absolute path to the worktree to remove.
  • force: If true, force removal even with uncommitted changes.

Outputs

  • error: Non-nil if removal fails.

func (*DefaultGitClient) ResetHard

func (g *DefaultGitClient) ResetHard(ctx context.Context, ref string) error

ResetHard performs a hard reset to the specified ref.

Description

Resets the working tree and index to match the specified commit. All uncommitted changes are discarded.

Inputs

  • ctx: Context for timeout and cancellation.
  • ref: Commit SHA or ref to reset to.

Outputs

  • error: Non-nil if reset fails.

func (*DefaultGitClient) RevParse

func (g *DefaultGitClient) RevParse(ctx context.Context, ref string) (string, error)

RevParse resolves a git ref to a commit SHA.

Description

Resolves references like HEAD, branch names, or tags to full SHA.

Inputs

  • ctx: Context for timeout and cancellation.
  • ref: Git reference to resolve.

Outputs

  • string: Full commit SHA.
  • error: Non-nil if ref doesn't exist.

func (*DefaultGitClient) StashDrop

func (g *DefaultGitClient) StashDrop(ctx context.Context, ref string) error

StashDrop removes a stash entry.

Description

Removes the specified stash from the stash list.

Inputs

  • ctx: Context for timeout and cancellation.
  • ref: Stash reference (e.g., "stash@{0}").

Outputs

  • error: Non-nil if drop fails.

func (*DefaultGitClient) StashList

func (g *DefaultGitClient) StashList(ctx context.Context) ([]StashEntry, error)

StashList returns all stash entries.

Description

Parses the stash list into structured entries.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • []StashEntry: List of stash entries.
  • error: Non-nil if listing fails.

func (*DefaultGitClient) StashPop

func (g *DefaultGitClient) StashPop(ctx context.Context) error

StashPop applies and removes the top stash.

Description

Applies the most recent stash and removes it from the stash list.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • error: Non-nil if pop fails (e.g., conflicts).

func (*DefaultGitClient) StashPush

func (g *DefaultGitClient) StashPush(ctx context.Context, message string) error

StashPush creates a new stash with the given message.

Description

Stashes all tracked changes with the specified message. Includes untracked files.

Inputs

  • ctx: Context for timeout and cancellation.
  • message: Descriptive message for the stash.

Outputs

  • error: Non-nil if stash fails.

func (*DefaultGitClient) Status

func (g *DefaultGitClient) Status(ctx context.Context) (*GitStatus, error)

Status returns the current git status.

Description

Parses `git status --porcelain` into a structured GitStatus.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • *GitStatus: Current repository status.
  • error: Non-nil if status fails.

func (*DefaultGitClient) WorktreeList

func (g *DefaultGitClient) WorktreeList(ctx context.Context) ([]WorktreeEntry, error)

WorktreeList returns all worktrees in the repository.

Description

Parses the output of `git worktree list --porcelain` into structured entries.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • []WorktreeEntry: List of all worktrees including the main one.
  • error: Non-nil if listing fails.

type GitClient

type GitClient interface {
	// IsGitRepository checks if the path is a git repository.
	IsGitRepository(ctx context.Context) bool

	// HasRebaseInProgress checks if a rebase is in progress.
	HasRebaseInProgress(ctx context.Context) bool

	// HasMergeInProgress checks if a merge is in progress.
	HasMergeInProgress(ctx context.Context) bool

	// IsDetachedHead checks if HEAD is detached (not on a branch).
	IsDetachedHead(ctx context.Context) bool

	// HasCherryPickInProgress checks if a cherry-pick is in progress.
	HasCherryPickInProgress(ctx context.Context) bool

	// HasBisectInProgress checks if a git bisect is in progress.
	HasBisectInProgress(ctx context.Context) bool

	// GetCurrentBranch returns the current branch name, or "HEAD" if detached.
	GetCurrentBranch(ctx context.Context) (string, error)

	// RevParse resolves a git ref to a commit SHA.
	RevParse(ctx context.Context, ref string) (string, error)

	// RefExists checks if a git ref exists.
	RefExists(ctx context.Context, ref string) bool

	// Stash operations
	StashPush(ctx context.Context, message string) error
	StashPop(ctx context.Context) error
	StashDrop(ctx context.Context, ref string) error
	StashList(ctx context.Context) ([]StashEntry, error)

	// Branch operations
	CreateBranch(ctx context.Context, name string) error
	DeleteBranch(ctx context.Context, name string, force bool) error
	Checkout(ctx context.Context, ref string) error
	BranchExists(ctx context.Context, name string) bool

	// Reset operations
	ResetHard(ctx context.Context, ref string) error
	CleanUntracked(ctx context.Context) error

	// Commit operations
	Add(ctx context.Context, paths ...string) error
	AddAll(ctx context.Context) error
	Commit(ctx context.Context, message string) error
	HasStagedChanges(ctx context.Context) bool
	HasUnstagedChanges(ctx context.Context) bool

	// Status
	Status(ctx context.Context) (*GitStatus, error)

	// Worktree operations
	CreateWorktree(ctx context.Context, path string, ref string) error
	RemoveWorktree(ctx context.Context, path string, force bool) error
	WorktreeList(ctx context.Context) ([]WorktreeEntry, error)
}

GitClient abstracts git operations for testing.

Description

Implementations must be safe for concurrent use.

type GitStatus

type GitStatus struct {
	// Branch is the current branch name.
	Branch string

	// IsClean is true if there are no uncommitted changes.
	IsClean bool

	// StagedFiles are files staged for commit.
	StagedFiles []string

	// ModifiedFiles are files with unstaged changes.
	ModifiedFiles []string

	// UntrackedFiles are untracked files.
	UntrackedFiles []string
}

GitStatus represents the current git working tree status.

type Manager

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

Manager provides atomic file operations with git-based rollback.

Description

TransactionManager wraps file operations in git checkpoints. On failure, all changes can be rolled back to the pre-transaction state. On success, changes can be committed as a single atomic unit.

Thread Safety

All public methods are safe for concurrent use. Only one transaction may be active at a time.

Nested Transactions

Nested transactions are NOT supported. Calling Begin() while a transaction is active returns ErrTransactionActive.

func NewManager

func NewManager(config Config) (*Manager, error)

NewManager creates a new transaction manager.

Description

Creates a manager with the specified configuration. If CleanupOnInit is true, recovers or cleans up any stale transactions from previous crashed sessions.

Inputs

  • config: Manager configuration. Use DefaultConfig() for defaults.

Outputs

  • *Manager: Ready-to-use transaction manager.
  • error: Non-nil if setup fails.

Example

config := transaction.DefaultConfig()
config.RepoPath = "/path/to/repo"
manager, err := transaction.NewManager(config)
if err != nil {
    return err
}
defer manager.Close()

func NewManagerWithGit

func NewManagerWithGit(config Config, git GitClient) (*Manager, error)

NewManagerWithGit creates a manager with a custom git client (for testing).

Description

Allows injection of a mock GitClient for testing.

Inputs

  • config: Manager configuration.
  • git: Custom git client implementation.

Outputs

  • *Manager: Ready-to-use transaction manager.
  • error: Non-nil if setup fails.

func (*Manager) Active

func (m *Manager) Active() *Transaction

Active returns the currently active transaction, or nil if none.

Description

Returns a copy of the active transaction for inspection. The returned Transaction should not be modified.

Outputs

  • *Transaction: Copy of active transaction, or nil.

func (*Manager) Begin

func (m *Manager) Begin(ctx context.Context, sessionID string) (tx *Transaction, err error)

Begin starts a new transaction with a checkpoint.

Description

Creates a checkpoint of the current git state. All subsequent file modifications can be rolled back to this point. Only one transaction may be active at a time.

Inputs

  • ctx: Context for timeout and cancellation.
  • sessionID: Identifier for the agent session (for logging/debugging).

Outputs

  • *Transaction: The active transaction.
  • error: ErrTransactionActive if a transaction is already in progress, ErrNotGitRepository if not a git repo, or other errors on failure.

Example

tx, err := manager.Begin(ctx, "session-123")
if err != nil {
    return err
}
// ... make changes ...
if failed {
    manager.Rollback(ctx, "operation failed")
} else {
    manager.Commit(ctx, "completed task")
}

func (*Manager) Close

func (m *Manager) Close() error

Close cleans up the manager.

Description

If a transaction is active, it is rolled back. Auto-stashed changes are restored. Resources are released.

Outputs

  • error: Non-nil if rollback fails.

func (*Manager) Commit

func (m *Manager) Commit(ctx context.Context, message string) (result *Result, err error)

Commit finalizes the transaction and persists changes.

Description

Completes the transaction, making all changes permanent. The checkpoint is removed. After commit, no rollback is possible.

Inputs

  • ctx: Context for timeout and cancellation.
  • message: Commit message describing the changes.

Outputs

  • *Result: Information about the completed transaction.
  • error: ErrNoTransaction if no transaction is active, or other errors.

func (*Manager) IsActive

func (m *Manager) IsActive() bool

IsActive returns true if a transaction is currently active.

func (*Manager) RecordModification

func (m *Manager) RecordModification(filePath string) error

RecordModification tracks a file change for auditing.

Description

Records that a file was modified during this transaction. This is used for logging and debugging - the actual rollback uses git state.

Inputs

  • filePath: Path to the modified file.

Outputs

  • error: ErrMaxFilesExceeded if limit reached, nil otherwise.

func (*Manager) RecordModifications

func (m *Manager) RecordModifications(paths []string) error

RecordModifications tracks multiple file changes.

Description

Batch version of RecordModification for efficiency.

Inputs

  • paths: Paths to modified files.

Outputs

  • error: ErrMaxFilesExceeded if limit would be exceeded.

func (*Manager) Rollback

func (m *Manager) Rollback(ctx context.Context, reason string) (result *Result, err error)

Rollback discards all changes and restores to checkpoint.

Description

Reverts all changes made since Begin() was called. The repository is restored to the exact state it was in when the transaction started.

Inputs

  • ctx: Context for timeout and cancellation. Note: rollback uses a background context internally to ensure completion even if ctx is cancelled.
  • reason: Human-readable reason for the rollback (for logging).

Outputs

  • *Result: Information about the rolled-back transaction.
  • error: ErrNoTransaction if no transaction is active, ErrRollbackFailed if rollback itself fails.

type PreFlightConfig

type PreFlightConfig struct {
	// Force skips the dirty working tree check (dangerous).
	// Use only when you're certain the agent won't conflict with user changes.
	Force bool

	// AutoStash automatically stashes user changes before operations
	// and restores them after. Conflicts may still occur on restore.
	AutoStash bool

	// AllowDetached permits operations in detached HEAD state.
	// Useful for CI/CD pipelines that checkout specific commits.
	AllowDetached bool
}

PreFlightConfig configures pre-flight check behavior.

Description

Controls how the pre-flight guard validates repository state before allowing agent operations to proceed.

type PreFlightError

type PreFlightError struct {
	// Code is a machine-readable error identifier.
	Code string

	// Message is a human-readable description of the error.
	Message string

	// Details contains additional information, such as affected files
	// or remediation steps.
	Details []string
}

PreFlightError represents a fatal issue that blocks execution.

Description

Contains structured error information for programmatic handling and human-readable messages for display.

func (*PreFlightError) Error

func (e *PreFlightError) Error() string

Error implements the error interface.

type PreFlightGuard

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

PreFlightGuard performs repository state validation before agent operations.

Description

Validates that the repository is in a safe state for agent operations. This prevents data loss from agent modifications overwriting user's uncommitted work.

Thread Safety

All methods are safe for concurrent use.

func NewPreFlightGuard

func NewPreFlightGuard(git GitClient, config PreFlightConfig, logger *slog.Logger) *PreFlightGuard

NewPreFlightGuard creates a new pre-flight guard.

Description

Creates a guard configured with the specified options.

Inputs

  • git: Git client for repository operations. Must not be nil.
  • config: Configuration options.
  • logger: Logger for diagnostic output (can be nil for no logging).

Outputs

  • *PreFlightGuard: Ready-to-use guard.

Panics

  • Panics if git is nil.

func (*PreFlightGuard) Check

Check performs all pre-flight validations.

Description

Validates the repository state against all configured checks. Returns a result with Passed=true if the repository is safe for agent operations.

Inputs

  • ctx: Context for timeout and cancellation.

Outputs

  • *PreFlightResult: Check outcomes including errors, warnings, and stash ref.
  • error: Non-nil only if the check itself failed (not if checks didn't pass).

Example

guard := NewPreFlightGuard(git, config, logger)
result, err := guard.Check(ctx)
if err != nil {
    return fmt.Errorf("preflight check failed: %w", err)
}
if !result.Passed {
    return result.FirstError()
}
// Safe to proceed with agent operations

func (*PreFlightGuard) Cleanup

func (g *PreFlightGuard) Cleanup(ctx context.Context, stashRef string) error

Cleanup restores stashed changes after agent operations complete.

Description

If AutoStash was used during Check(), this method restores the stashed changes. Should be called regardless of whether agent operations succeeded or failed.

Inputs

  • ctx: Context for timeout and cancellation.
  • stashRef: The StashRef from PreFlightResult. If empty, does nothing.

Outputs

  • error: Non-nil if restore failed. May indicate conflicts that need manual resolution.

Example

defer func() {
    if err := guard.Cleanup(ctx, result.StashRef); err != nil {
        log.Warn("failed to restore stashed changes", "error", err)
    }
}()

type PreFlightResult

type PreFlightResult struct {
	// Passed is true if all checks passed (no fatal errors).
	Passed bool

	// Errors are fatal issues that block execution.
	Errors []PreFlightError

	// Warnings are non-fatal issues the user should be aware of.
	Warnings []PreFlightWarning

	// StashRef is set if AutoStash was used, containing the stash reference.
	// The caller must call Cleanup() with this reference after operations complete.
	StashRef string
}

PreFlightResult contains the outcomes of pre-flight validation.

Description

Aggregates all errors and warnings from the pre-flight checks. If Passed is false, at least one error is present and the operation should not proceed.

func (*PreFlightResult) FirstError

func (r *PreFlightResult) FirstError() error

FirstError returns the first error, or nil if no errors.

Description

Convenience method for returning a single error from the result.

func (*PreFlightResult) FormatErrors

func (r *PreFlightResult) FormatErrors() string

FormatErrors returns a human-readable multi-line error summary.

Description

Formats all errors with their details for display to the user.

type PreFlightWarning

type PreFlightWarning struct {
	// Code is a machine-readable warning identifier.
	Code string

	// Message is a human-readable description of the warning.
	Message string
}

PreFlightWarning represents a non-fatal issue.

Description

Warnings don't block execution but should be communicated to the user.

type Result

type Result struct {
	// TransactionID is the completed transaction's ID.
	TransactionID string

	// Status is the final status.
	Status Status

	// Duration is how long the transaction was active.
	Duration time.Duration

	// FilesModified is the count of files changed.
	FilesModified int

	// CommitSHA is the new commit SHA (if committed).
	CommitSHA string

	// RollbackReason is why the transaction was rolled back (if applicable).
	RollbackReason string
}

Result contains information about a completed transaction.

type StashEntry

type StashEntry struct {
	Index   int
	Ref     string
	Message string
}

StashEntry represents a git stash entry.

type Status

type Status string

Status represents the current state of a transaction.

const (
	// StatusIdle indicates no active transaction.
	StatusIdle Status = "idle"

	// StatusActive indicates a transaction is in progress.
	StatusActive Status = "active"

	// StatusCommitting indicates a commit is in progress.
	StatusCommitting Status = "committing"

	// StatusRollingBack indicates a rollback is in progress.
	StatusRollingBack Status = "rolling_back"

	// StatusCommitted indicates the transaction was successfully committed.
	StatusCommitted Status = "committed"

	// StatusRolledBack indicates the transaction was rolled back.
	StatusRolledBack Status = "rolled_back"

	// StatusFailed indicates the transaction failed (rollback also failed).
	StatusFailed Status = "failed"
)

func (Status) IsTerminal

func (s Status) IsTerminal() bool

IsTerminal returns true if the status is a terminal state.

func (Status) String

func (s Status) String() string

String returns the string representation of the status.

type Strategy

type Strategy string

Strategy defines how checkpoints are created and managed.

const (
	// StrategyStash uses git stash to save the current state.
	// Pros: Simple, no branch pollution.
	// Cons: Can conflict on pop, limited metadata.
	// Best for: Small changes, quick operations.
	StrategyStash Strategy = "stash"

	// StrategyBranch creates a temporary branch for the transaction.
	// Pros: Clean history, easy to inspect agent's work.
	// Cons: More git operations, potential merge conflicts.
	// Best for: Complex changes, long-running transactions.
	StrategyBranch Strategy = "branch"

	// StrategyWorktree creates an isolated git worktree.
	// Pros: Complete isolation, can't corrupt original.
	// Cons: More disk space, path management complexity.
	// Best for: Sandboxed operations, parallel transactions.
	StrategyWorktree Strategy = "worktree"
)

func (Strategy) String

func (s Strategy) String() string

String returns the string representation of the strategy.

type Tracer

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

Tracer provides OpenTelemetry tracing for transaction operations.

Description

Wraps the OpenTelemetry tracer with transaction-specific span creation and attribute management. When disabled, returns noop spans for zero overhead.

Thread Safety

All methods are safe for concurrent use.

func NewTracer

func NewTracer(logger *slog.Logger, enabled bool) *Tracer

NewTracer creates a new transaction tracer.

Inputs

  • logger: Logger for structured logging. Uses slog.Default() if nil.
  • enabled: Whether tracing is enabled. When false, uses noop spans.

Outputs

  • *Tracer: Ready-to-use tracer instance.

func (*Tracer) EndBegin

func (t *Tracer) EndBegin(span trace.Span, tx *Transaction, err error)

EndBegin completes a transaction begin span.

Inputs

  • span: The span to end.
  • tx: The created transaction (may be nil on error).
  • err: Error if begin failed.

func (*Tracer) EndCommit

func (t *Tracer) EndCommit(span trace.Span, result *Result, err error)

EndCommit completes a transaction commit span.

Inputs

  • span: The span to end.
  • result: The commit result (may be nil on error).
  • err: Error if commit failed.

func (*Tracer) EndGitOp

func (t *Tracer) EndGitOp(span trace.Span, err error)

EndGitOp completes a git operation span.

Inputs

  • span: The span to end.
  • err: Error if the operation failed.

func (*Tracer) EndRollback

func (t *Tracer) EndRollback(span trace.Span, result *Result, err error)

EndRollback completes a transaction rollback span.

Inputs

  • span: The span to end.
  • result: The rollback result (may be nil on error).
  • err: Error if rollback failed.

func (*Tracer) RecordExpiration

func (t *Tracer) RecordExpiration(ctx context.Context, txID string)

RecordExpiration records a transaction expiration event.

Inputs

  • ctx: Context containing the active span.
  • txID: Transaction identifier.

func (*Tracer) RecordStateTransition

func (t *Tracer) RecordStateTransition(ctx context.Context, txID string, from, to Status, duration time.Duration)

RecordStateTransition records a state transition event on the current span.

Inputs

  • ctx: Context containing the active span.
  • txID: Transaction identifier.
  • from: Previous state.
  • to: New state.
  • duration: Time spent in the previous state.

func (*Tracer) StartBegin

func (t *Tracer) StartBegin(ctx context.Context, sessionID string, strategy Strategy) (context.Context, trace.Span)

StartBegin starts a span for a transaction begin operation.

Inputs

  • ctx: Parent context for span creation.
  • sessionID: Agent session identifier.
  • strategy: Checkpoint strategy being used.

Outputs

  • context.Context: Context with span attached.
  • trace.Span: The created span. Caller must call End() when done.

func (*Tracer) StartCommit

func (t *Tracer) StartCommit(ctx context.Context, tx *Transaction, message string) (context.Context, trace.Span)

StartCommit starts a span for a transaction commit operation.

Inputs

  • ctx: Parent context for span creation.
  • tx: The transaction being committed.
  • message: Commit message.

Outputs

  • context.Context: Context with span attached.
  • trace.Span: The created span. Caller must call End() when done.

func (*Tracer) StartGitOp

func (t *Tracer) StartGitOp(ctx context.Context, operation string) (context.Context, trace.Span)

StartGitOp starts a child span for a git operation.

Inputs

  • ctx: Parent context (should contain parent span).
  • operation: Name of the git operation (e.g., "create_branch", "reset_hard").

Outputs

  • context.Context: Context with span attached.
  • trace.Span: The created span. Caller must call End() when done.

func (*Tracer) StartRollback

func (t *Tracer) StartRollback(ctx context.Context, tx *Transaction, reason string) (context.Context, trace.Span)

StartRollback starts a span for a transaction rollback operation.

Inputs

  • ctx: Parent context for span creation.
  • tx: The transaction being rolled back.
  • reason: Why the rollback is occurring.

Outputs

  • context.Context: Context with span attached.
  • trace.Span: The created span. Caller must call End() when done.

type Transaction

type Transaction struct {
	// ID is the unique identifier for this transaction.
	ID string `json:"id"`

	// SessionID links the transaction to an agent session.
	SessionID string `json:"session_id"`

	// StartedAt is when the transaction began (Unix milliseconds UTC).
	StartedAt int64 `json:"started_at"`

	// ExpiresAt is when the transaction will auto-rollback if not completed (Unix milliseconds UTC).
	ExpiresAt int64 `json:"expires_at"`

	// CheckpointRef is the git ref (commit SHA) to restore on rollback.
	CheckpointRef string `json:"checkpoint_ref"`

	// OriginalBranch is the branch we started on.
	OriginalBranch string `json:"original_branch"`

	// WorkBranch is the temporary branch name (for branch strategy).
	WorkBranch string `json:"work_branch,omitempty"`

	// WorktreePath is the worktree directory (for worktree strategy).
	WorktreePath string `json:"worktree_path,omitempty"`

	// StashRef is the stash reference (for stash strategy).
	StashRef string `json:"stash_ref,omitempty"`

	// ModifiedFiles tracks files changed during the transaction.
	// Stored as a map for O(1) deduplication.
	ModifiedFiles map[string]struct{} `json:"modified_files"`

	// Status is the current transaction state.
	Status Status `json:"status"`

	// Strategy used for this transaction.
	Strategy Strategy `json:"strategy"`

	// RollbackReason is set if the transaction was rolled back.
	RollbackReason string `json:"rollback_reason,omitempty"`

	// Error is set if the transaction failed.
	Error string `json:"error,omitempty"`
}

Transaction represents an active transactional checkpoint.

Description

A Transaction captures the state at Begin() and allows either committing changes or rolling back to the captured state.

Thread Safety

Transaction objects should only be accessed through the TransactionManager.

func (*Transaction) Duration

func (t *Transaction) Duration() time.Duration

Duration returns how long the transaction has been active.

func (*Transaction) FileCount

func (t *Transaction) FileCount() int

FileCount returns the number of files modified in this transaction.

func (*Transaction) Files

func (t *Transaction) Files() []string

Files returns the list of modified files as a slice.

func (*Transaction) IsExpired

func (t *Transaction) IsExpired() bool

IsExpired returns true if the transaction has exceeded its TTL.

type WorktreeEntry

type WorktreeEntry struct {
	// Path is the absolute filesystem path to the worktree.
	Path string

	// HEAD is the current commit SHA of the worktree.
	HEAD string

	// Branch is the branch name, or empty string if detached HEAD.
	Branch string

	// Locked indicates if the worktree is locked.
	Locked bool
}

WorktreeEntry represents a git worktree.

Jump to

Keyboard shortcuts

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