Documentation
¶
Overview ¶
Package handoff provides the canonical YAML handoff format for context preservation. Handoffs are compact (~400 tokens vs ~2000 for markdown) representations of session state that can be passed between agent sessions for continuity.
Index ¶
- Constants
- Variables
- func MarshalYAML(h *Handoff) ([]byte, error)
- type FileChanges
- type GenerateHandoffOptions
- type Generator
- func (g *Generator) EnrichWithGitState(h *Handoff) error
- func (g *Generator) GenerateAutoHandoff(sessionName, agentType, paneID string, output []byte, ...) (*Handoff, error)
- func (g *Generator) GenerateFromOutput(sessionName string, output []byte) (*Handoff, error)
- func (g *Generator) GenerateFromTranscript(sessionName, transcriptPath string) (*Handoff, error)
- func (g *Generator) GenerateHandoff(ctx context.Context, opts GenerateHandoffOptions) (*Handoff, error)
- func (g *Generator) ProjectDir() string
- type Handoff
- func (h *Handoff) AddBlocker(blocker string) *Handoff
- func (h *Handoff) AddDecision(key, value string) *Handoff
- func (h *Handoff) AddFinding(key, value string) *Handoff
- func (h *Handoff) AddTask(task string, files ...string) *Handoff
- func (h *Handoff) HasChanges() bool
- func (h *Handoff) IsBlocked() bool
- func (h *Handoff) IsComplete() bool
- func (h *Handoff) IsValid() bool
- func (h *Handoff) MarkCreated(files ...string) *Handoff
- func (h *Handoff) MarkDeleted(files ...string) *Handoff
- func (h *Handoff) MarkModified(files ...string) *Handoff
- func (h *Handoff) MustValidate()
- func (h *Handoff) SetAgentInfo(agentID, agentType, paneID string) *Handoff
- func (h *Handoff) SetDefaults()
- func (h *Handoff) SetTokenInfo(used, max int) *Handoff
- func (h *Handoff) TotalFileChanges() int
- func (h *Handoff) Validate() ValidationErrors
- func (h *Handoff) ValidateAndSetDefaults() ValidationErrors
- func (h *Handoff) ValidateMinimal() error
- func (h *Handoff) WithGoalAndNow(goal, now string) *Handoff
- func (h *Handoff) WithStatus(status, outcome string) *Handoff
- type HandoffMeta
- type Reader
- func (r *Reader) BaseDir() string
- func (r *Reader) ExtractGoalNow(sessionName string) (goal, now string, err error)
- func (r *Reader) FindLatest(sessionName string) (*Handoff, string, error)
- func (r *Reader) FindLatestAny() (*Handoff, string, error)
- func (r *Reader) InvalidateCache()
- func (r *Reader) ListHandoffs(sessionName string) ([]HandoffMeta, error)
- func (r *Reader) ListSessions() ([]string, error)
- func (r *Reader) Read(path string) (*Handoff, error)
- type ReservationSnapshot
- type ReservationTransfer
- type ReservationTransferClient
- type ReservationTransferResult
- type TaskRecord
- type TransferReservationsOptions
- type ValidationError
- type ValidationErrors
- type Writer
- func (w *Writer) Archive(path string) error
- func (w *Writer) BaseDir() string
- func (w *Writer) CleanArchive(sessionName string, olderThan time.Duration) (int, error)
- func (w *Writer) Delete(path string) error
- func (w *Writer) EnsureDir(sessionName string) error
- func (w *Writer) Write(h *Handoff, description string) (string, error)
- func (w *Writer) WriteAuto(h *Handoff) (string, error)
- func (w *Writer) WriteToPath(h *Handoff, path string) error
Constants ¶
const ( // StatusComplete indicates the session completed all planned work. StatusComplete = "complete" // StatusPartial indicates the session completed some work but not all. StatusPartial = "partial" // StatusBlocked indicates the session is blocked and cannot proceed. StatusBlocked = "blocked" )
Status values for handoff status field.
const ( // OutcomeSucceeded indicates all goals were achieved. OutcomeSucceeded = "SUCCEEDED" // OutcomePartialPlus indicates most goals achieved, some work remains. OutcomePartialPlus = "PARTIAL_PLUS" // OutcomePartialMinus indicates some goals achieved but significant work remains. OutcomePartialMinus = "PARTIAL_MINUS" // OutcomeFailed indicates the session failed to achieve its goals. OutcomeFailed = "FAILED" )
Outcome values for handoff outcome field. These provide more granular detail than status.
const ( AgentTypeClaude = "cc" AgentTypeCodex = "cod" AgentTypeGemini = "gmi" )
AgentType values for identifying agent types.
const DefaultMaxPerDir = 50
DefaultMaxPerDir is the default number of handoffs to keep before rotation.
const HandoffVersion = "1.0"
HandoffVersion tracks the format version for backwards compatibility and migrations.
Variables ¶
var ValidAgentTypes = map[string]bool{ AgentTypeClaude: true, AgentTypeCodex: true, AgentTypeGemini: true, "": true, }
ValidAgentTypes is the set of valid agent type values.
var ValidOutcomes = map[string]bool{ OutcomeSucceeded: true, OutcomePartialPlus: true, OutcomePartialMinus: true, OutcomeFailed: true, "": true, }
ValidOutcomes is the set of valid outcome values.
var ValidStatuses = map[string]bool{ StatusComplete: true, StatusPartial: true, StatusBlocked: true, "": true, }
ValidStatuses is the set of valid status values.
Functions ¶
func MarshalYAML ¶
MarshalYAML serializes a handoff to YAML bytes.
Types ¶
type FileChanges ¶
type FileChanges struct {
Created []string `yaml:"created,omitempty"`
Modified []string `yaml:"modified,omitempty"`
Deleted []string `yaml:"deleted,omitempty"`
}
FileChanges tracks file modifications during a session.
type GenerateHandoffOptions ¶
type GenerateHandoffOptions struct {
// SessionName identifies this session (required)
SessionName string
// AgentName is the agent's Agent Mail identity (optional, enables Agent Mail integration)
AgentName string
// AgentType is the agent type (cc, cod, gmi)
AgentType string
// PaneID is the tmux pane ID (optional)
PaneID string
// ProjectKey is the project path for Agent Mail (defaults to projectDir)
ProjectKey string
// TokensUsed is the current token usage
TokensUsed int
// TokensMax is the maximum token budget
TokensMax int
// Goal is the explicit goal (if known, skips analysis)
Goal string
// Now is the explicit next action (if known, skips analysis)
Now string
// Output is optional agent output to analyze
Output []byte
// IncludeBeads enables BV integration (default: true if bv available)
IncludeBeads *bool
// IncludeAgentMail enables Agent Mail integration (default: true if agentmail available)
IncludeAgentMail *bool
// BVClient is an optional pre-configured BV client
BVClient *bv.BVClient
// AgentMailClient is an optional pre-configured Agent Mail client
AgentMailClient *agentmail.Client
// TransferTTLSeconds refreshes reservation TTL when preparing transfer instructions.
TransferTTLSeconds int
// TransferGraceSeconds adds a retry grace period during transfer (seconds).
TransferGraceSeconds int
}
GenerateHandoffOptions configures handoff generation.
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator creates handoff content from various sources.
func NewGenerator ¶
NewGenerator creates a Generator for the given project directory.
func NewGeneratorWithLogger ¶
NewGeneratorWithLogger creates a Generator with a custom logger.
func (*Generator) EnrichWithGitState ¶
EnrichWithGitState adds git information to handoff.
func (*Generator) GenerateAutoHandoff ¶
func (g *Generator) GenerateAutoHandoff(sessionName, agentType, paneID string, output []byte, tokensUsed, tokensMax int) (*Handoff, error)
GenerateAutoHandoff creates an auto-generated handoff suitable for pre-compact hooks. It combines output analysis with git state for a complete picture.
func (*Generator) GenerateFromOutput ¶
GenerateFromOutput creates a handoff by analyzing agent output text.
func (*Generator) GenerateFromTranscript ¶
GenerateFromTranscript creates handoff from Claude Code transcript. Transcript path: ~/.claude/projects/.../session.jsonl
func (*Generator) GenerateHandoff ¶
func (g *Generator) GenerateHandoff(ctx context.Context, opts GenerateHandoffOptions) (*Handoff, error)
GenerateHandoff creates a complete handoff with BV and Agent Mail integration. This is the main entry point for handoff generation, gathering:
- Git state (uncommitted changes, branch, recent commits)
- Active beads from BV (in-progress tasks assigned to this agent)
- Agent Mail state (inbox messages, file reservations)
All integrations are optional and fail gracefully if unavailable.
func (*Generator) ProjectDir ¶
ProjectDir returns the project directory for this generator.
type Handoff ¶
type Handoff struct {
// Metadata
Version string `yaml:"version"` // Format version for migrations
Session string `yaml:"session"` // Session identifier
Date string `yaml:"date"` // Date in YYYY-MM-DD format
CreatedAt time.Time `yaml:"created_at"` // Precise creation timestamp
UpdatedAt time.Time `yaml:"updated_at"` // Last update timestamp
// Status tracking
Status string `yaml:"status"` // complete|partial|blocked
Outcome string `yaml:"outcome"` // SUCCEEDED|PARTIAL_PLUS|PARTIAL_MINUS|FAILED
// Core fields (REQUIRED for status line)
Goal string `yaml:"goal"` // What this session accomplished - REQUIRED
Now string `yaml:"now"` // What next session should do first - REQUIRED
Test string `yaml:"test"` // Command to verify this work
// Work tracking
DoneThisSession []TaskRecord `yaml:"done_this_session,omitempty"`
// Context for future self
Blockers []string `yaml:"blockers,omitempty"`
Questions []string `yaml:"questions,omitempty"`
Decisions map[string]string `yaml:"decisions,omitempty"`
Findings map[string]string `yaml:"findings,omitempty"`
// What worked and what didn't
Worked []string `yaml:"worked,omitempty"`
Failed []string `yaml:"failed,omitempty"`
// Next steps
Next []string `yaml:"next,omitempty"`
// File tracking
Files FileChanges `yaml:"files,omitempty"`
// Integration fields - populated during recovery
ActiveBeads []string `yaml:"active_beads,omitempty"` // From BV
AgentMailThreads []string `yaml:"agent_mail_threads,omitempty"` // From Agent Mail
CMMemories []string `yaml:"cm_memories,omitempty"` // From CM
// Agent info for multi-agent sessions
AgentID string `yaml:"agent_id,omitempty"`
AgentType string `yaml:"agent_type,omitempty"` // cc, cod, gmi
PaneID string `yaml:"pane_id,omitempty"`
// Token context at time of handoff
TokensUsed int `yaml:"tokens_used,omitempty"`
TokensMax int `yaml:"tokens_max,omitempty"`
TokensPct float64 `yaml:"tokens_pct,omitempty"`
// File reservation transfer instructions (optional)
ReservationTransfer *ReservationTransfer `yaml:"reservation_transfer,omitempty"`
}
Handoff represents a complete context handoff between sessions. The Goal and Now fields are REQUIRED and used by the status line integration.
func (*Handoff) AddBlocker ¶
AddBlocker adds a blocker to the list.
func (*Handoff) AddDecision ¶
AddDecision records a key decision.
func (*Handoff) AddFinding ¶
AddFinding records an important discovery.
func (*Handoff) HasChanges ¶
HasChanges returns true if any file changes were recorded.
func (*Handoff) IsComplete ¶
IsComplete returns true if the handoff is marked as complete.
func (*Handoff) MarkCreated ¶
MarkCreated adds a file to the created list.
func (*Handoff) MarkDeleted ¶
MarkDeleted adds a file to the deleted list.
func (*Handoff) MarkModified ¶
MarkModified adds a file to the modified list.
func (*Handoff) MustValidate ¶
func (h *Handoff) MustValidate()
MustValidate panics if validation fails. Use for testing or when validation failure should be impossible (e.g., programmatically constructed handoffs).
func (*Handoff) SetAgentInfo ¶
SetAgentInfo sets the agent-related fields.
func (*Handoff) SetDefaults ¶
func (h *Handoff) SetDefaults()
SetDefaults populates default values for optional fields. This should be called before serialization to ensure consistent output.
func (*Handoff) SetTokenInfo ¶
SetTokenInfo sets the token context fields.
func (*Handoff) TotalFileChanges ¶
TotalFileChanges returns the total number of file changes.
func (*Handoff) Validate ¶
func (h *Handoff) Validate() ValidationErrors
Validate checks the handoff for required fields and correct values. Returns ALL validation errors (not just the first) for comprehensive logging. Use IsValid() for a simple boolean check.
func (*Handoff) ValidateAndSetDefaults ¶
func (h *Handoff) ValidateAndSetDefaults() ValidationErrors
ValidateAndSetDefaults combines SetDefaults and Validate for convenience. Returns validation errors after setting defaults.
func (*Handoff) ValidateMinimal ¶
ValidateMinimal performs a minimal validation check for just the required fields. Use this for quick checks where full validation is not needed.
func (*Handoff) WithGoalAndNow ¶
WithGoalAndNow is a convenience method for setting the required fields.
func (*Handoff) WithStatus ¶
WithStatus sets the status and outcome fields.
type HandoffMeta ¶
type HandoffMeta struct {
Path string
Session string
Date time.Time
Status string
Goal string // For quick display
IsAuto bool
}
HandoffMeta provides summary information about a handoff file.
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader handles reading handoff files from disk with caching.
func NewReaderWithOptions ¶
func NewReaderWithOptions(projectDir string, cacheExpiry time.Duration, logger *slog.Logger) *Reader
NewReaderWithOptions creates a Reader with custom options.
func (*Reader) ExtractGoalNow ¶
ExtractGoalNow extracts just goal and now fields efficiently. For status line use - uses regex and caching for speed.
func (*Reader) FindLatest ¶
FindLatest returns the most recent handoff for a session. Returns (nil, "", nil) if no handoffs exist (not an error).
func (*Reader) FindLatestAny ¶
FindLatestAny returns the most recent handoff across all sessions.
func (*Reader) InvalidateCache ¶
func (r *Reader) InvalidateCache()
InvalidateCache clears the goal/now cache. Call when you know handoffs have been written.
func (*Reader) ListHandoffs ¶
func (r *Reader) ListHandoffs(sessionName string) ([]HandoffMeta, error)
ListHandoffs returns all handoffs for a session, sorted by date descending.
func (*Reader) ListSessions ¶
ListSessions returns all sessions that have handoffs.
type ReservationSnapshot ¶
type ReservationSnapshot struct {
PathPattern string `yaml:"path_pattern"`
Exclusive bool `yaml:"exclusive,omitempty"`
Reason string `yaml:"reason,omitempty"`
ExpiresAt time.Time `yaml:"expires_at,omitempty"`
}
ReservationSnapshot captures a single reservation for transfer.
type ReservationTransfer ¶
type ReservationTransfer struct {
FromAgent string `yaml:"from_agent,omitempty"`
ProjectKey string `yaml:"project_key,omitempty"`
TTLSeconds int `yaml:"ttl_seconds,omitempty"`
GracePeriodSeconds int `yaml:"grace_period_seconds,omitempty"`
CreatedAt time.Time `yaml:"created_at,omitempty"`
Reservations []ReservationSnapshot `yaml:"reservations,omitempty"`
}
ReservationTransfer describes how to transfer file reservations to a new session.
type ReservationTransferClient ¶
type ReservationTransferClient interface {
ReservePaths(ctx context.Context, opts agentmail.FileReservationOptions) (*agentmail.ReservationResult, error)
ReleaseReservations(ctx context.Context, projectKey, agentName string, paths []string, ids []int) error
RenewReservations(ctx context.Context, opts agentmail.RenewReservationsOptions) (*agentmail.RenewReservationsResult, error)
}
ReservationTransferClient is the subset of Agent Mail client methods needed for transfers.
type ReservationTransferResult ¶
type ReservationTransferResult struct {
FromAgent string `json:"from_agent"`
ToAgent string `json:"to_agent"`
RequestedPaths []string `json:"requested_paths"`
GrantedPaths []string `json:"granted_paths"`
ReleasedPaths []string `json:"released_paths"`
Conflicts []agentmail.ReservationConflict `json:"conflicts,omitempty"`
RolledBack bool `json:"rolled_back,omitempty"`
Success bool `json:"success"`
Error string `json:"error,omitempty"`
}
ReservationTransferResult reports transfer outcomes for debugging and recovery.
func TransferReservations ¶
func TransferReservations(ctx context.Context, client ReservationTransferClient, opts TransferReservationsOptions) (*ReservationTransferResult, error)
TransferReservations moves reservations from one agent to another. It releases the old reservations, attempts to reserve for the new agent, and rolls back on conflicts where possible to approximate atomicity.
type TaskRecord ¶
TaskRecord represents a completed task with associated file changes.
type TransferReservationsOptions ¶
type TransferReservationsOptions struct {
ProjectKey string
FromAgent string
ToAgent string
Reservations []ReservationSnapshot
// TTLSeconds refreshes the reservation TTL on transfer (0 uses default).
TTLSeconds int
// GracePeriod waits and retries once on conflict to allow release propagation.
GracePeriod time.Duration
Logger *slog.Logger
}
TransferReservationsOptions configures a reservation transfer.
type ValidationError ¶
type ValidationError struct {
Field string // The field that failed validation
Message string // Human-readable error message
Value interface{} // The invalid value (for logging)
}
ValidationError represents a validation error with structured fields.
func (ValidationError) Error ¶
func (e ValidationError) Error() string
Error implements the error interface.
type ValidationErrors ¶
type ValidationErrors []ValidationError
ValidationErrors is a collection of validation errors.
func (ValidationErrors) Error ¶
func (errs ValidationErrors) Error() string
Error implements the error interface for multiple errors.
func (ValidationErrors) FieldNames ¶
func (errs ValidationErrors) FieldNames() []string
FieldNames returns the list of fields with errors.
func (ValidationErrors) ForField ¶
func (errs ValidationErrors) ForField(field string) ValidationErrors
ForField returns all errors for a specific field.
func (ValidationErrors) HasErrors ¶
func (errs ValidationErrors) HasErrors() bool
HasErrors returns true if there are any validation errors.
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer handles writing handoff files to disk with atomic writes and rotation.
func NewWriterWithOptions ¶
NewWriterWithOptions creates a Writer with custom options.
func (*Writer) CleanArchive ¶
CleanArchive removes archived handoffs older than the given duration.
func (*Writer) Delete ¶
Delete removes a specific handoff file. Use with caution - typically handoffs should be archived, not deleted.
func (*Writer) Write ¶
Write saves a handoff to the appropriate directory using atomic write. The description is sanitized to kebab-case for use in the filename. Returns the path to the written file.