Documentation
¶
Overview ¶
Pre-push OPF rewrite for the git-refs checkpoint backend, the sibling of manual_commit_opf_rewrite.go's entire/checkpoints/v1 rewrite. Both run the OPF-augmented redaction once per push; only discovery and ref update differ.
Pre-push OPF rewrite for entire/checkpoints/v1.
This is the ONLY production code path that runs the OPF-augmented redaction entry points. Post-commit condensation stays on the regex-only pipeline for predictable latency; OPF runs here, once per push, after the user opted in via settings.
Package strategy provides the manual-commit strategy: it tracks agent sessions in session state and condenses them into checkpoints when the user commits.
Index ¶
- Constants
- Variables
- func AccumulateSessionTokenUsage(ctx context.Context, sessionID string, delta *agent.TokenUsage) error
- func AnyGitHookInstalled(ctx context.Context) bool
- func AppendNewSkillEvents(state *SessionState, candidates []agent.SkillEvent) []agent.SkillEvent
- func AttachTokenUsage(ctx context.Context, ag agent.Agent, state *SessionState, transcript []byte, ...) (*agent.TokenUsage, int)
- func CheckAndWarnHookManagers(ctx context.Context, w io.Writer, absolutePath bool)
- func CheckpointPushTarget(ctx context.Context, pushRemoteName string) (target string, disabled bool)
- func CheckpointReadRemotes(ctx context.Context) []string
- func ClaimSessionStartBanner(ctx context.Context, sessionID string) (claimed bool, err error)
- func ClearHooksDirCache()
- func ClearSessionState(ctx context.Context, sessionID string) error
- func ClearSessionStateWithProgress(ctx context.Context, sessionID string, errW io.Writer, ...) error
- func CollectUntrackedFiles(ctx context.Context) ([]string, error)
- func CompleteTaskRecord(ctx context.Context, sessionID string, rec session.TaskRecord) (bool, error)
- func ConsumeAttachTokenWindow(state *SessionState, pos int)
- func CountUnpushedCheckpoints(ctx context.Context, remoteName string) (int, error)
- func DeclaredPushRemoteForBranch(ctx context.Context, branch string) string
- func DeleteBranchCLI(ctx context.Context, branchName string) error
- func DeleteLegacyShadowBranches(ctx context.Context, branches []string) (deleted []string, failed []string)
- func DeleteOrphanedCheckpoints(ctx context.Context, checkpointIDs []string) (deleted []string, failed []string, err error)
- func DeleteOrphanedSessionStates(ctx context.Context, sessionIDs []string) (deleted []string, failed []string, err error)
- func EmitSkillInvocationTelemetry(ctx context.Context, events []agent.SkillEvent)
- func EnsureEntireGitignore(ctx context.Context) error
- func EnsurePrimaryRef(ctx context.Context, repo *git.Repository) error
- func EnsureRedactionConfigured(ctx context.Context) error
- func EnsureSetup(ctx context.Context) error
- func ExtractFirstPrompt(content string) string
- func FetchMetadataBranch(ctx context.Context, remoteURL string) error
- func FilterTrackableChanges(ctx context.Context, repoRoot string, modified, added, deleted []string) (keptModified, keptAdded, keptDeleted []string)
- func FindMostRecentSessionInCurrentWorktree(ctx context.Context) string
- func FirstDisplayPrompt(prompts []string) string
- func FirstReadCandidateTrackingRef(ctx context.Context, repo *git.Repository, primary plumbing.ReferenceName) (string, plumbing.ReferenceName, bool)
- func FormatSubagentEndMessage(agentType, description, toolUseID string) string
- func FormatSubagentRunningMessage(agentType, description, toolUseID string) string
- func GetCurrentBranchName(repo *git.Repository) string
- func GetDefaultBranchName(repo *git.Repository) string
- func GetGitAuthorFromRepo(repo *git.Repository) (name, email string)
- func GetGitCommonDir(ctx context.Context) (string, error)
- func GetGitDir(ctx context.Context) (string, error)
- func GetHooksDir(ctx context.Context) (string, error)
- func GetMainBranchHash(repo *git.Repository) plumbing.Hash
- func GetMetadataRefTree(repo *git.Repository, ref plumbing.ReferenceName) (*object.Tree, error)
- func GetRemotePrimaryTree(ctx context.Context, repo *git.Repository) (*object.Tree, error)
- func GitOperationInProgress(ctx context.Context) string
- func HooksDirLinkTarget(hooksDir string) (string, bool)
- func HooksPathCommand(dir string) string
- func InstallGitHook(ctx context.Context, silent, absolutePath bool) (int, error)
- func InvalidateGitRemoteCache(ctx context.Context)
- func IsCondensableEndedSession(state *SessionState) bool
- func IsEmptyRepository(repo *git.Repository) bool
- func IsGitHookInstalled(ctx context.Context) bool
- func IsGitHookInstalledInDir(ctx context.Context, repoDir string) bool
- func IsLegacyShadowBranch(branchName string) bool
- func IsMetadataDisconnected(ctx context.Context, repo *git.Repository, ...) (bool, error)
- func IsOnDefaultBranch(repo *git.Repository) (bool, string)
- func LeadCheckpointReadRemote(ctx context.Context) string
- func ListLegacyShadowBranches(ctx context.Context) ([]string, error)
- func ListRemovableLegacyShadowBranches(ctx context.Context) ([]string, error)
- func LoadAgentTypeHint(ctx context.Context, sessionID string) types.AgentType
- func LoadModelHint(ctx context.Context, sessionID string) string
- func ManagedGitHookNames() []string
- func MergeUnhashedFilesTouched(state *SessionState, fileLists ...[]string)
- func MutateSessionState(ctx context.Context, sessionID string, fn func(*SessionState) error) error
- func MutateSessionStateOnSaved(ctx context.Context, sessionID string, fn func(*SessionState) error, ...) error
- func OpenRepository(ctx context.Context) (*git.Repository, error)
- func PrepareSessionEndCondensation(ctx context.Context, state *SessionState) error
- func PromoteTmpRefSafely(ctx context.Context, tmpRefName, destRefName plumbing.ReferenceName, ...) error
- func PushQueuedCheckpointRefs(ctx context.Context, repo *git.Repository, remote string) (pushed int, pushDisabled bool, err error)
- func ReadAgentTypeFromTree(tree *object.Tree, checkpointPath string) types.AgentType
- func ReadAllSessionPromptsFromTree(tree *object.Tree, checkpointPath string, sessionCount int, ...) []string
- func ReadLatestSessionPromptFromCommittedTree(tree *object.Tree, cpID id.CheckpointID, sessionCount int) string
- func ReadSessionPromptFromTree(tree *object.Tree, checkpointPath string) string
- func ReconcileDisconnectedMetadataRef(ctx context.Context, repo *git.Repository, localRefName plumbing.ReferenceName, ...) error
- func RecordFilesTouched(ctx context.Context, sessionID string, modified, added, deleted []string) error
- func ReinstallGitHooks(ctx context.Context) (int, error)
- func RemoveGitHook(ctx context.Context) (int, error)
- func ResetRedactionConfiguredForTest()
- func ResolveAgentForResume(agentType types.AgentType) (agent.Agent, error)
- func ResolveTranscriptPath(state *SessionState) (string, error)
- func RewriteQueuedCheckpointRefsWithOPF(ctx context.Context, repo *git.Repository) error
- func RewriteUnpushedV1WithOPF(ctx context.Context, repo *git.Repository, target string) (plumbing.Hash, error)
- func SafelyAdvanceLocalRef(ctx context.Context, repo *git.Repository, localRefName plumbing.ReferenceName, ...) error
- func SaveSessionState(ctx context.Context, state *SessionState) error
- func SetBeforeCondense(fn func(ctx context.Context, sessions []*SessionState) bool)
- func ShellQuoteForDisplay(s string) string
- func StoreAgentTypeHint(ctx context.Context, sessionID string, agentType types.AgentType) (created bool, err error)
- func StoreModelHint(ctx context.Context, sessionID, model string) error
- func TaskMetadataDir(sessionMetadataDir, toolUseID string) string
- func TransitionAndLog(goCtx context.Context, state *SessionState, event session.Event, ...) error
- func TruncateDescription(s string, maxLen int) string
- func UpsertCompletedTaskRecord(ctx context.Context, sessionID string, rec session.TaskRecord) error
- func WarnIfMetadataDisconnected(ctx context.Context)
- func WithCheckpointRemoteBootstrap(ctx context.Context) context.Context
- func WithFreshGitRemoteCache(ctx context.Context) context.Context
- func WithGitRemoteCache(ctx context.Context) context.Context
- func WithSessionLockWait(ctx context.Context, wait time.Duration) context.Context
- func WithSessionStateLocks(ctx context.Context, sessionID string, commonDirs []string, fn func() error) error
- type BackgroundClaimContext
- type BootstrapTooLargeError
- type CheckpointDeleteOptions
- type CheckpointDeletePlan
- func (p *CheckpointDeletePlan) ActiveStates() []CheckpointDeleteState
- func (p *CheckpointDeletePlan) CheckLocalV1Propagation(targets []CheckpointDeleteTarget, selection CheckpointDeleteSelection) error
- func (p *CheckpointDeletePlan) HasLocalCopy() bool
- func (p *CheckpointDeletePlan) HolderTargets() []CheckpointDeleteTarget
- func (p *CheckpointDeletePlan) UnreachableTargetNames() []string
- type CheckpointDeletePlanOptions
- type CheckpointDeleteResult
- type CheckpointDeleteSelection
- type CheckpointDeleteSession
- type CheckpointDeleteSibling
- type CheckpointDeleteState
- type CheckpointDeleteTarget
- type CheckpointDeleteTargetResult
- type CheckpointInfo
- type CheckpointReadResolution
- type CheckpointSyncRemote
- type CheckpointSyncRemoteSource
- type CleanupItem
- type CleanupResult
- type CleanupType
- type CondenseResult
- type DeleteOutcome
- type ExtractedSessionData
- type GitHookRemoval
- type GitHookState
- type ManualCommitStrategy
- func (s *ManualCommitStrategy) CommitMsg(_ context.Context, commitMsgFile string) error
- func (s *ManualCommitStrategy) CondenseAndMarkFullyCondensed(ctx context.Context, sessionID string) error
- func (s *ManualCommitStrategy) CondenseSession(ctx context.Context, repo *git.Repository, checkpointID id.CheckpointID, ...) (*CondenseResult, error)
- func (s *ManualCommitStrategy) CondenseSessionByID(ctx context.Context, sessionID string) error
- func (s *ManualCommitStrategy) CountOtherActiveSessionsWithCheckpoints(ctx context.Context, currentSessionID string) (int, error)
- func (s *ManualCommitStrategy) CreateSnapshotCheckpoint(ctx context.Context, sessionID string) (id.CheckpointID, error)
- func (s *ManualCommitStrategy) EnsureSessionExists(ctx context.Context, sessionID string, agentType types.AgentType) error
- func (s *ManualCommitStrategy) FindSessionsForCommit(ctx context.Context, baseCommitSHA string) ([]*SessionState, error)
- func (s *ManualCommitStrategy) HandleTurnEnd(ctx context.Context, state *SessionState) error
- func (s *ManualCommitStrategy) InitializeSession(ctx context.Context, sessionID string, agentType types.AgentType, ...) error
- func (s *ManualCommitStrategy) ListLogsOnlyPendingCheckpoints(ctx context.Context, limit int) ([]PendingCheckpoint, error)
- func (s *ManualCommitStrategy) ListPendingCheckpoints(ctx context.Context, limit int) ([]PendingCheckpoint, error)
- func (s *ManualCommitStrategy) PostCommit(ctx context.Context) error
- func (s *ManualCommitStrategy) PostRewrite(ctx context.Context, rewriteType string, r io.Reader) error
- func (s *ManualCommitStrategy) PrePush(ctx context.Context, remote string) error
- func (s *ManualCommitStrategy) PrePushFromGitHook(ctx context.Context, remote string) error
- func (s *ManualCommitStrategy) PrepareCommitMsg(ctx context.Context, commitMsgFile string, source string) error
- func (s *ManualCommitStrategy) PreviewNextCheckpoint(ctx context.Context) ([]NextCheckpointPreview, error)
- func (s *ManualCommitStrategy) RecordVanishedUntrackedFiles(ctx context.Context, sessionID string) error
- func (s *ManualCommitStrategy) Reset(ctx context.Context, w, errW io.Writer) error
- func (s *ManualCommitStrategy) ResetSession(ctx context.Context, w, errW io.Writer, sessionID string) error
- func (s *ManualCommitStrategy) RestoreLogsOnly(ctx context.Context, w, errW io.Writer, point PendingCheckpoint, force bool) ([]RestoredSession, error)
- func (s *ManualCommitStrategy) SaveStep(ctx context.Context, step StepContext) error
- func (s *ManualCommitStrategy) SetBlobFetcher(f checkpoint.BlobFetchFunc)
- type MetadataComparison
- type MetadataRelation
- type NextCheckpointPreview
- type NextCheckpointTaskRecord
- type OPFBatchTooLargeError
- type OPFDecision
- type OPFNoCategoriesError
- type OPFRawBytesTooLargeError
- type OPFRuntimeFailedError
- type PendingCheckpoint
- type ResolvedSession
- type RestoredSession
- type SessionResolution
- type SessionRestoreInfo
- type SessionRestoreStatus
- type SessionState
- type StepContext
- type V1CopyState
- type V1DivergedError
- type V1RefMovedError
Constants ¶
const CheckpointRemoteTargetName = "checkpoint_remote"
CheckpointRemoteTargetName names the dedicated checkpoint_remote store in delete plans and in --remote selection, which otherwise take git remote names.
const CommitLinkingPromptLogMsg = "prepare-commit-msg: prompting to link commit"
CommitLinkingPromptLogMsg is logged when prepare-commit-msg shows the commit-linking prompt. E2E fails any test whose logs contain it: every E2E commit comes from an agent or the harness, and neither can answer.
const EntireSearchSubagentName = "entire-search"
EntireSearchSubagentName is the search skill name setup_search_skill.go (package cli) scaffolds into <agent>/skills/entire-search/SKILL.md for every agent. Exported so the installer and its tests pin their scaffolded paths and template bodies against the value this probe matches — strategy cannot import cli, so the constant lives here and cli reaches down.
The subagent-dispatch match below exists for the artifact this feature shipped as before it became a skill: a dispatchable subagent under this name (.claude/agents/entire-search.md and its other-agent equivalents). Those installs linger — the installer only removes them on the next --search-skill run — and a subagent's own `entire search` Bash call is written to a SEPARATE transcript file that condensation never reads, so dropping the dispatch match reports "did not search" for exactly the sessions still on the old artifact. Skill-based sessions run the command in the main transcript and surface through the command match instead — but only where the command match runs at all: ScanToolInvocations requires ToolInvocationScanner, which Claude Code alone implements, so the other agents' sessions report searchSourceUnsupported regardless of skill use.
const FetchTmpRefPrefix = "refs/entire-fetch-tmp/"
FetchTmpRefPrefix is the namespace for temporary refs used by fetch helpers to land a fetched hash before safely promoting it to a final ref (via PromoteTmpRefSafely). Prefer using the named constants below when possible.
const GitHookBackupSuffix = ".pre-entire"
GitHookBackupSuffix is what InstallGitHook moves a pre-existing hook to before writing its own. Exported so diagnostics elsewhere can name the file the user will find rather than spelling it a second time; backupSuffix is the in-package shorthand for it.
const MaxCommitTraversalDepth = 1000
MaxCommitTraversalDepth is the safety limit for walking git commit history. Prevents unbounded traversal in repositories with very long histories.
const MaxDescriptionLength = 60
MaxDescriptionLength is the maximum length for descriptions in commit messages before truncation occurs.
const SessionLockDirName = "entire-session-locks"
SessionLockDirName is the lock directory inside the git common dir. Exported so uninstall can sweep it without re-spelling the name.
const SessionLockNoticeDelay = time.Second
SessionLockNoticeDelay is how long a user-facing clear waits before telling the user it is blocked on a session's state lock. Short enough that a real stall is announced promptly, long enough that the uncontended case -- the common one -- stays silent.
const (
// Strategy name constants
StrategyNameManualCommit = "manual-commit"
)
Common branch name constants for default branch detection.
Variables ¶
var ErrBranchNotFound = errors.New("branch not found")
ErrBranchNotFound is returned by DeleteBranchCLI when the branch does not exist.
var ErrCheckpointDeleteNotFound = errors.New("checkpoint not found")
ErrCheckpointDeleteNotFound means no local copy and no reachable remote holds the checkpoint.
var ErrCheckpointDeleteV1WouldPropagate = errors.New("the local v1 removal would reach an unselected remote on the next push")
ErrCheckpointDeleteV1WouldPropagate refuses a delete that would remove the local v1 copy while leaving the copy on the remote the next pre-push sends the v1 branch to: that push would carry the removal there anyway.
var ErrMutationSkip = errors.New("session state mutation skipped")
ErrMutationSkip signals MutateSessionState to skip the save without treating fn's return as an error. Use it when the mutation function observes the loaded state and decides no write is needed (for example, when a merge produces no new entries).
var ErrNothingToCheckpoint = errors.New("session has nothing to checkpoint")
ErrNothingToCheckpoint reports that a session has no transcript or files to write, so no checkpoint was created.
var ErrOPFAbortedByUser = errors.New("OPF prompt aborted by user; push cancelled")
ErrOPFAbortedByUser is returned when the user chose Abort (or pressed Ctrl-C) at the OPF prompt. PrePush returns it verbatim; the hook command propagates the non-zero exit code so git push aborts.
Exported because a user-initiated abort is a decline, not a failure, and only the calling command knows how to say so: `doctor migrate-checkpoints` matches it to report the same clean "refs stay queued" its own declined prompt does, rather than an error.
var ErrPendingFileChanges = errors.New("session has uncommitted file changes")
ErrPendingFileChanges reports that a session has uncommitted file changes. Its work is checkpointed by the next commit, so a snapshot is refused.
var ErrStateNotFound = errors.New("session state not found")
ErrStateNotFound is returned by MutateSessionState when no state file exists for the session ID (typically because the event arrived before InitializeSession ran). Callers that need to distinguish "no state" from a successful no-op can branch on errors.Is(err, ErrStateNotFound).
Functions ¶
func AccumulateSessionTokenUsage ¶ added in v0.11.3
func AccumulateSessionTokenUsage(ctx context.Context, sessionID string, delta *agent.TokenUsage) error
AccumulateSessionTokenUsage adds a token-usage delta to both the session-cumulative total and the checkpoint-scoped accumulator, mirroring SaveStep's accounting (manual_commit_git.go). It exists for turns that end with no uncommitted changes — e.g. Antigravity committing all its work mid-turn, its normal flow — where the TurnEnd handler skips SaveStep entirely but the turn's out-of-band token delta must still be recorded so the next condensation (or `entire status`) attributes it. A nil or empty delta is a no-op.
func AnyGitHookInstalled ¶ added in v0.10.1
AnyGitHookInstalled reports whether Entire git hooks are present at all, current or not. This is what uninstall needs: a stale hook is still ours, and still ours to remove.
func AppendNewSkillEvents ¶ added in v0.10.3
func AppendNewSkillEvents(state *SessionState, candidates []agent.SkillEvent) []agent.SkillEvent
AppendNewSkillEvents records candidates into state.SkillEvents, stamping the session's TurnID on events that lack one, and returns those that were not already recorded. It is the single dedupe used by every path that adds skill events — hook-provided (lifecycle) and transcript-extracted (condensation and turn-end finalize) alike — so "already recorded" means the same thing everywhere. Exactly-once telemetry rests on that agreement: two paths with different notions of duplicate would let one re-report what the other recorded.
func AttachTokenUsage ¶ added in v0.11.5
func AttachTokenUsage(ctx context.Context, ag agent.Agent, state *SessionState, transcript []byte, transcriptPath string) (*agent.TokenUsage, int)
AttachTokenUsage returns the tokens `entire session attach` stores for a session's transcript and the transcript end position in the agent's offset units. With existing state it counts only what no checkpoint has counted yet (from TokenStart), falling back to the pending hook-reported usage the way condensation does; without state it counts the whole transcript. The caller records pos with ConsumeAttachTokenWindow once the checkpoint is written. While a turn runs, its subagents' tokens aren't in the pending usage yet (Stop records them), so they are read live as a mid-turn condensation does; that also moves state.TokenUsage's cumulative subagent total, which the caller must keep so the re-baseline covers what this checkpoint stored.
func CheckAndWarnHookManagers ¶ added in v0.4.5
CheckAndWarnHookManagers detects external hook managers and writes a warning to w if any are found. absolutePath embeds the full binary path for GUI git clients.
func CheckpointPushTarget ¶ added in v0.11.5
func CheckpointPushTarget(ctx context.Context, pushRemoteName string) (target string, disabled bool)
CheckpointPushTarget returns where PrePush sends checkpoint metadata for pushRemoteName — the checkpoint_remote URL when one is configured, otherwise the remote itself — and whether push_sessions turns pushing off. Callers use it to confirm delivery after a push, since PrePush is deliberately fail-soft.
func CheckpointReadRemotes ¶ added in v0.10.1
CheckpointReadRemotes returns the ordered, deduped remotes that checkpoint READS consult: the elected sync remote first, then "origin" as the legacy tier (pre-single-remote-sync checkpoints live there, and a fresh clone may lack the local settings that elected a non-origin remote).
Unlike the write-side election, this fails OPEN: on an election error (misconfigured checkpoint_push_remote, unreadable settings) the chain is ["origin"] when configured. Writes fail closed to prevent leaking data; failing reads closed would only prevent FINDING data, with no privacy benefit. Callers must uphold the read-only rule for every candidate after the first: only the elected remote may seed or advance local refs.
An empty result means no candidates — callers needing the "checkpoint absent" classification must gather their own positive evidence (successful remote listing, readable settings); an empty chain alone is not proof.
The election itself is deliberately un-memoized: every call re-runs it, so a settings change or a captured election written mid-invocation is observed immediately. What IS memoized, per invocation, are the two .git/config reads underneath it — which remotes exist, and does remote X exist (see git_remote_cache.go). That split is load-bearing: caching the election result would make #1991's capture invisible to the gate that runs right after it.
func ClaimSessionStartBanner ¶ added in v0.6.0
ClaimSessionStartBanner records that the SessionStart banner has been emitted for a session. First-writer-wins semantics, separate from StoreAgentTypeHint so a non-banner-capable agent winning the ownership race (e.g. Cursor, which doesn't implement HookResponseWriter) doesn't suppress the banner from a banner-capable agent that fires SessionStart for the same session.
Callers MUST only invoke this from within the HookResponseWriter branch — the claim represents "a banner was actually shown", not just "an agent considered showing one". Otherwise a non-writer claimant would re-introduce the bug.
Returns (claimed=true) when this call won the race and the caller should emit the banner; (claimed=false) when an earlier call already claimed it.
func ClearHooksDirCache ¶ added in v0.4.6
func ClearHooksDirCache()
ClearHooksDirCache clears the cached hooks directory. This is primarily useful for testing when changing directories.
func ClearSessionState ¶
ClearSessionState removes the session state file for the given session ID.
It takes sessionID's gate first, for the same reason (*ManualCommitStrategy).clearSessionState does: this deletes the file that every other mutation of this state writes under MutateSessionState's per-session lock, so an unlocked delete landing mid-mutation destroys the write that was in flight, with nothing surfacing the loss. This is the implementation `entire doctor` reaches (doctor.go's discardSession) -- a separate one from the strategy method, and the command most likely to be run while other sessions are live -- so gating the strategy method alone left the race open exactly where it mattered most.
Calling it from inside a MutateSessionState frame for the same session is refused rather than tolerated. Holding the gate makes the delete safe, not effective: when the frame's closure returns, MutateSessionState writes the state back out, so the file is resurrected and the caller still sees a nil error -- `entire doctor` would report a session discarded and the session would reappear. Clear from inside an active closure with (*ManualCommitStrategy).clearSessionStateLocked instead, and return ErrMutationSkip so the frame does not save (see CondenseSessionByID's clear path, the one caller that legitimately does this).
The wait is not cancellable. With no WithSessionLockWait deadline on ctx, acquireSessionGate falls through to the ctx-free flock.AcquireIn, so ctx cancellation -- a first Ctrl-C included -- does not abort a blocked acquire; only the force-quit path in main.go escapes. ctx is still honored for everything else here. Adding a timeout via ctx alone would silently do nothing; it would have to go through WithSessionLockWait.
func ClearSessionStateWithProgress ¶ added in v0.10.5
func ClearSessionStateWithProgress(ctx context.Context, sessionID string, errW io.Writer, notifyAfter time.Duration) error
ClearSessionStateWithProgress is ClearSessionState with the shared lock-wait notice on errW. This is what user-facing commands should call; the bare ClearSessionState stays for callers with nowhere to print.
func CollectUntrackedFiles ¶ added in v0.7.8
CollectUntrackedFiles collects untracked, non-ignored paths relative to the repository root.
func CompleteTaskRecord ¶ added in v0.10.3
func CompleteTaskRecord(ctx context.Context, sessionID string, rec session.TaskRecord) (bool, error)
CompleteTaskRecord marks the record for rec.ToolUseID completed exactly once and attaches the capture's results (files, declared transcript path, tokens) in the same MutateSessionState closure, per session.State.CompleteTaskRecord's contract. When no launch stub exists the record is created first: foreground tasks have no launch hook, so completion is the only event that can produce their record. Returns false without error when a racing Final event already completed the record, or when no session state exists.
func ConsumeAttachTokenWindow ¶ added in v0.11.5
func ConsumeAttachTokenWindow(state *SessionState, pos int)
ConsumeAttachTokenWindow marks the tokens an attach checkpoint stored as counted: the token offset moves to pos and the pending window is reset, so the session's next checkpoint does not count them again. The displayed transcript window (CheckpointTranscriptStart) is left alone.
func CountUnpushedCheckpoints ¶ added in v0.10.0
CountUnpushedCheckpoints approximates how many checkpoints exist locally but not on the checkpoint sync remote. Fully local — no network. git-refs primary: push-queue length. git-branch primary: v1 commits ahead of refs/remotes/<remote>/<v1>; an absent tracking ref with a local v1 branch counts every v1 commit (correct for the deferred-publish case; a stale tracking ref overcounts — acceptable for a local, no-network heuristic).
func DeclaredPushRemoteForBranch ¶ added in v0.10.3
DeclaredPushRemoteForBranch resolves where `git push` would send branch, through git's own precedence: branch.<name>.pushRemote, then remote.pushDefault, then branch.<name>.remote. Empty when branch is empty or nothing is declared — callers supply their own default, since the right one differs per caller ("origin" for a command issuing its own push; "no capture" for the checkpoint gate).
This is the "where does this branch push" question, which is NOT the question ResolveCheckpointSyncRemote answers ("which single remote may carry checkpoint data"). Do not substitute one for the other in either direction; that resolver's doc comment holds the full argument, including why checkpoint sync deliberately does not elect from the declaration returned here. The short version: that objection is about a gate deciding whether to piggyback on someone else's push, and does not apply to a caller issuing its own.
func DeleteBranchCLI ¶ added in v0.3.13
DeleteBranchCLI deletes a git branch using the git CLI. Uses `git branch -D` instead of go-git's RemoveReference because go-git v5 doesn't properly persist deletions when refs are packed (.git/packed-refs) or in a worktree context. This is the same class of go-git v5 bug that affects checkout and reset --hard (see CheckoutBranch in git_operations.go, which shells out to the git CLI for the same reason).
Returns ErrBranchNotFound if the branch does not exist, allowing callers to use errors.Is for idempotent deletion patterns.
func DeleteLegacyShadowBranches ¶ added in v0.11.5
func DeleteLegacyShadowBranches(ctx context.Context, branches []string) (deleted []string, failed []string)
DeleteLegacyShadowBranches deletes the specified branches from the repository. Returns two slices: successfully deleted branches and branches that failed to delete. Individual branch deletion failures do not stop the operation - all branches are attempted.
func DeleteOrphanedCheckpoints ¶
func DeleteOrphanedCheckpoints(ctx context.Context, checkpointIDs []string) (deleted []string, failed []string, err error)
DeleteOrphanedCheckpoints removes checkpoint directories from the entire/checkpoints/v1 branch.
func DeleteOrphanedSessionStates ¶
func DeleteOrphanedSessionStates(ctx context.Context, sessionIDs []string) (deleted []string, failed []string, err error)
DeleteOrphanedSessionStates deletes the specified session state files.
func EmitSkillInvocationTelemetry ¶ added in v0.10.3
func EmitSkillInvocationTelemetry(ctx context.Context, events []agent.SkillEvent)
EmitSkillInvocationTelemetry forwards newly recorded skill events to telemetry. Gated on the same opt-in telemetry setting as command tracking; skill names are additionally allowlisted inside the telemetry package, so custom names never leave the machine verbatim. Skill names and signal kinds only, never prompt text or transcript content.
Hand this to MutateSessionStateOnSaved as the post-save effect rather than calling it inside the mutation closure. That helper runs the effect only once the state was durably written, and only after the session gate is released: the settings load (disk I/O) and detached-process spawn must not extend the gate hold time and block concurrent hooks, and emitting only after a save keeps reporting exactly-once — events whose append was never persisted are re-derived, and re-announced, by the next extraction pass.
func EnsureEntireGitignore ¶
EnsureEntireGitignore ensures all required entries are in .entire/.gitignore Works correctly from any subdirectory within the repository.
func EnsurePrimaryRef ¶ added in v0.7.6
func EnsurePrimaryRef(ctx context.Context, repo *git.Repository) error
func EnsureRedactionConfigured ¶ added in v0.5.1
EnsureRedactionConfigured loads redaction settings and configures the redact package: scanner engines, PII detection (opt-in), inline custom_redactions, and rule packs auto-discovered from .entire/redactors/.
Must be called at each process entry point before checkpoint writes. Returns an error ONLY for scanner-specific failures (invalid scanner config, goredact engine construction) — proceeding would run a scanner set the team's committed settings did not choose. All other settings problems keep the historical warn-and-default behavior.
Pass a context descended from the root pre-run, so redact's diagnostics reach .entire/logs/: hook contexts swallow stderr, so otherwise a user grepping for component=redaction finds nothing and concludes their rules never ran. Without a context logger they fall back to stderr and the load-time summary is skipped.
sync.Once: the first caller's context wins, and every caller is an entry point that just built one.
func EnsureSetup ¶ added in v0.4.8
EnsureSetup ensures the strategy is properly set up.
func ExtractFirstPrompt ¶
ExtractFirstPrompt extracts and truncates the first meaningful prompt from prompt content. Prompts are separated by "\n\n---\n\n". Skips empty prompts and separator-only content. Returns empty string if no valid prompt is found.
func FetchMetadataBranch ¶ added in v0.5.3
FetchMetadataBranch fetches the metadata branch from the checkpoint remote URL and updates the local branch. Unlike fetchMetadataBranchIfMissing, this always fetches regardless of whether the branch exists locally (for resume scenarios where the local branch may be stale).
The fetch is unfiltered (NoFilter: true) because resume needs blob content (transcripts, metadata JSON) — not just tree objects, and it runs on the foreground budget for the same reason: it is the call that actually moves the transcript archive.
func FilterTrackableChanges ¶ added in v0.11.5
func FilterTrackableChanges(ctx context.Context, repoRoot string, modified, added, deleted []string) (keptModified, keptAdded, keptDeleted []string)
FilterTrackableChanges removes, from repo-relative path lists, the paths that are not the session's committable file work, so they never enter FilesTouched on any route (turn end, task-record completion, per-tool hooks, Codex child files, and the transcript extraction a mid-turn commit falls back to):
- gitignored files, from modified and added: no commit can carry them. Deleted paths are never ignore-checked: a deletion reported by git status is of a tracked path by construction, and `git check-ignore` reports a path whose deletion is staged (it has left the index), which would drop the deletion of a tracked file that matches an ignore rule;
- submodule gitlinks (mode 160000 in HEAD or the index), and every path beneath one, from all three: a submodule pointer is not a file, the prepare hook would stamp a trailer for it, and PostCommit could never match it. Edits inside a submodule are not tracked by the superproject's session at all: they belong to the nested repository, whose own session records them. Each path's ancestor directories are part of the gitlink query, so sub/lib.txt is recognized by its gitlink ancestor sub, and such paths are dropped before the ignore check, where git would refuse them;
- paths git refuses to ignore-check (gitrepo.IgnoredPaths' refused set), such as one beneath a symlinked directory: git cannot commit them through that path either.
Either kind would keep the session pending forever: it stays in carry-forward, re-condenses the full transcript on every later commit, and leaves an ENDED session for the zombie sweep.
If git cannot answer either question, the affected paths are kept: tracking a path that turns out to be uncommittable is recoverable, silently dropping real work is not.
Answers are cached per process (trackablePathCache), so the several sessions one hook processes — a commit hook walks every session of the worktree — ask git about each path once.
func FindMostRecentSessionInCurrentWorktree ¶ added in v0.7.8
FindMostRecentSessionInCurrentWorktree returns the most recently interacted session from the current worktree only, never falling back to another worktree's.
This is the whole of what it does, deliberately. Anything asking "which session is running me" must go through strategy.ResolveCallerSession, which identifies the caller first and reports which tier answered; a bare most-recent lookup cannot tell "mine" from "whichever moved last", and worktrees share one session store, so guessing there returns an unrelated session that looks exactly like a real answer.
func FirstDisplayPrompt ¶ added in v0.10.1
FirstDisplayPrompt returns the first prompt in the list worth showing as a title/preview: the first entry that is non-empty, not separator-only, and not agent-injected (runtime preambles, AGENTS.md dumps — see textutil.IsInjectedPrompt). Returns "" if none qualifies, which callers that must show something should treat as "fall back to the raw first prompt".
The returned text is not truncated; callers that render it into a fixed-width display are responsible for that (see restoredPromptPreview).
func FirstReadCandidateTrackingRef ¶ added in v0.10.1
func FirstReadCandidateTrackingRef(ctx context.Context, repo *git.Repository, primary plumbing.ReferenceName) (string, plumbing.ReferenceName, bool)
FirstReadCandidateTrackingRef returns the remote name and remote-tracking ref of the first checkpoint read candidate (elected sync remote first, then the legacy origin tier) whose tracking ref for primary exists locally. Pure read; ok is false when no candidate has a tracking ref (or the chain is empty).
func FormatSubagentEndMessage ¶
FormatSubagentEndMessage formats a commit message for when a subagent completes. Format: "Completed '<agent-type>' agent: <description> (<tool-use-id>)"
Edge cases:
- Empty description: "Completed '<agent-type>' agent (<tool-use-id>)"
- Empty agentType: "Completed agent: <description> (<tool-use-id>)"
- Both empty: "Task: <tool-use-id>"
func FormatSubagentRunningMessage ¶ added in v0.10.3
FormatSubagentRunningMessage is FormatSubagentEndMessage's in-flight counterpart, for pending-list rows whose task record is still live.
func GetCurrentBranchName ¶
func GetCurrentBranchName(repo *git.Repository) string
GetCurrentBranchName returns the short name of the current branch if HEAD points to a branch. Returns an empty string if in detached HEAD state or if there's an error reading HEAD. This is used to capture branch metadata for checkpoints.
func GetDefaultBranchName ¶
func GetDefaultBranchName(repo *git.Repository) string
GetDefaultBranchName returns the name of the default branch. First checks origin/HEAD, then falls back to checking if main/master exists. Returns empty string if unable to determine. NOTE: Duplicated from cli/git_operations.go - see ENT-129 for consolidation.
func GetGitAuthorFromRepo ¶
func GetGitAuthorFromRepo(repo *git.Repository) (name, email string)
GetGitAuthorFromRepo retrieves the git user.name and user.email, checking both the repository-local config and the global ~/.gitconfig. Delegates to checkpoint.GetGitAuthorFromRepo — this wrapper exists so callers within the strategy package don't need a qualified import.
func GetGitCommonDir ¶
GetGitCommonDir returns the path to the shared git directory. In a regular checkout, this is .git/ In a worktree, this is the main repo's .git/ (not .git/worktrees/<name>/) Uses git rev-parse --git-common-dir for reliable handling of worktrees.
func GetGitDir ¶
GetGitDir returns the actual git directory path by delegating to git itself. This handles both regular repositories and worktrees, and inherits git's security validation for gitdir references.
func GetHooksDir ¶ added in v0.4.5
GetHooksDir returns the active hooks directory path. This respects core.hooksPath and correctly resolves to the common hooks directory when called from a linked worktree. The result is cached per working directory.
func GetMainBranchHash ¶
func GetMainBranchHash(repo *git.Repository) plumbing.Hash
getMainBranchHash returns the hash of the main branch (main or master). Returns ZeroHash if no main branch is found.
func GetMetadataRefTree ¶ added in v0.7.6
func GetMetadataRefTree(repo *git.Repository, ref plumbing.ReferenceName) (*object.Tree, error)
GetMetadataRefTree returns the tree object at the given committed-metadata ref.
func GetRemotePrimaryTree ¶ added in v0.7.6
GetRemotePrimaryTree returns the tree at the first checkpoint read candidate's remote-tracking ref for the configured Primary (elected sync remote first, then the legacy origin tier; first existing tracking ref wins). Pure read — it never writes local refs. Errors when Primary isn't in Push (no remote-tracking shadow), when no read candidate is configured, or when no candidate's tracking ref exists (surfacing the first candidate's error).
func GitOperationInProgress ¶ added in v0.11.5
GitOperationInProgress names the git operation the worktree is in the middle of (rebase or am, cherry-pick, revert, merge, bisect), or returns "" when none is. A command about to move a branch must not do so mid-operation. A git dir that can't be read reports none, as isGitSequenceOperation does.
func HooksDirLinkTarget ¶ added in v0.11.0
HooksDirLinkTarget resolves a symlinked hooks directory to the absolute directory it names, reporting false when it cannot be resolved (a dangling link, or one whose target is unreadable).
Exported because doctor prints the same remedy, and it must print the same path: os.Readlink returns the link's raw contents, which for a relative link is relative to the link's own parent rather than to anywhere the user's shell will be. Pasting that into `git config core.hooksPath` sets a path git then resolves from somewhere else entirely.
func HooksPathCommand ¶ added in v0.11.0
HooksPathCommand renders the `git config core.hooksPath <dir>` remedy with dir quoted so the line survives being pasted into a shell.
Exported because doctor prints the same remedy, and printing it twice means getting the quoting right twice. A hooks directory containing a space is ordinary -- a macOS "Application Support" path, a Windows "C:\Users\First Last\..." -- and unquoted it produces `git config core.hooksPath /tmp/my hooks dir`, which git rejects with "error: no action specified" because it sees four arguments. The remedy is presented as a command to paste, so it has to be one.
Quoted only when it needs to be, so the common clean path stays readable.
func InstallGitHook ¶
InstallGitHook installs generic git hooks that delegate to `entire hook` commands. These hooks work with any strategy - the strategy is determined at runtime. If silent is true, no output is printed (except backup notifications, which always print). absolutePath embeds the full binary path in hooks for GUI git clients. Returns the number of hooks that were installed (0 if all already up to date).
Hooks always invoke the "entire" binary. Git hooks written by older versions in local-dev mode ran a repo-relative launcher script instead; they still carry entireHookMarker, so the content comparison below rewrites them to the binary form on the next install rather than leaving them pointed at repo content.
func InvalidateGitRemoteCache ¶ added in v0.10.1
InvalidateGitRemoteCache drops the memoized remote reads for every repository. Callers that add, rename, remove or re-point a git remote must call this, or later elections in the same command answer from the pre-mutation config. A caller performing several mutations needs one call after the last of them.
func IsCondensableEndedSession ¶ added in v0.10.3
func IsCondensableEndedSession(state *SessionState) bool
IsCondensableEndedSession reports whether an ENDED, not fully condensed session still carries uncondensed content (State.HasPendingWork), which condensing (CondenseSessionByID) can always salvage: turn-end steps condense from the session's transcript and recorded files, task records from their own transcripts. Used by the PostCommit stale-session warning and the background zombie sweep.
func IsEmptyRepository ¶ added in v0.4.3
func IsEmptyRepository(repo *git.Repository) bool
IsEmptyRepository returns true if the repository has no commits yet. After git-init, HEAD points to an unborn branch (e.g., refs/heads/main) whose target does not yet exist. repo.Head() returns ErrReferenceNotFound in this case.
func IsGitHookInstalled ¶
IsGitHookInstalled reports whether a CURRENT set of Entire git hooks is installed. Callers that need to tell "none" from "ours but stale" — uninstall, doctor — must use CheckGitHookState instead, or they will treat a stale hook as if it were not there.
func IsGitHookInstalledInDir ¶ added in v0.4.4
IsGitHookInstalledInDir checks if a current set of Entire CLI hooks is installed in the given repo directory. Useful for tests that must not change cwd.
func IsLegacyShadowBranch ¶ added in v0.11.5
IsLegacyShadowBranch reports whether the branch name has the shape of a shadow branch written by an older CLI. The metadata and trails branches are never shadow branches.
This is a name-shape check, not an ownership check -- see the legacyShadowBranchPattern doc comment. Do not use it to gate deletion a human has not reviewed; use isAutoDeletableLegacyShadowBranch for that.
func IsMetadataDisconnected ¶ added in v0.5.0
func IsMetadataDisconnected(ctx context.Context, repo *git.Repository, remoteRefName plumbing.ReferenceName) (bool, error)
IsMetadataDisconnected reports whether the local primary metadata ref and the provided fetched or remote-tracking ref exist but share no common ancestor.
func IsOnDefaultBranch ¶
func IsOnDefaultBranch(repo *git.Repository) (bool, string)
IsOnDefaultBranch checks if the repository HEAD is on the default branch. Returns (isOnDefault, currentBranchName). NOTE: Duplicated from cli/git_operations.go - see ENT-129 for consolidation.
func LeadCheckpointReadRemote ¶ added in v0.11.0
LeadCheckpointReadRemote returns the head of the read-candidate chain (the elected sync remote) for remote.FetchURL's ownership vote. Callers that resolve a configured checkpoint_remote pass it as FetchURLOptions.LeadReadRemote so the fetch-side inherited check sees the same fork-shaped identity the push side sees: in the "cloned the base, added a fork" topology only the elected remote's owner exposes the mismatch. Returns "" when no candidate resolves.
Pass it only on paths that already confirmed a checkpoint_remote is configured. On the no-config path a lead does more than vote: it selects which remote serves the fetch.
func ListLegacyShadowBranches ¶ added in v0.11.5
ListLegacyShadowBranches returns the local branches that look like shadow branches written by an older CLI (IsLegacyShadowBranch), sorted. Returns an empty slice (not nil) if there are none.
func ListRemovableLegacyShadowBranches ¶ added in v0.11.5
ListRemovableLegacyShadowBranches returns the local shadow branches older CLIs left behind that are safe to delete without listing them for a human first: the strict worktree-suffixed shape only (see autoDeletableLegacyShadowBranchPattern), sorted.
func LoadAgentTypeHint ¶ added in v0.6.0
LoadAgentTypeHint reads the agent type hint written by SessionStart. Returns empty string if the hint file doesn't exist, can't be read, or the value isn't a registered agent type.
func LoadModelHint ¶ added in v0.5.0
LoadModelHint reads the LLM model name from the hint file for the given session. Returns empty string if the hint file doesn't exist or can't be read.
func ManagedGitHookNames ¶ added in v0.4.4
func ManagedGitHookNames() []string
ManagedGitHookNames returns the list of git hooks managed by Entire CLI. This is useful for tests that need to manipulate hooks.
func MergeUnhashedFilesTouched ¶ added in v0.11.5
func MergeUnhashedFilesTouched(state *SessionState, fileLists ...[]string)
MergeUnhashedFilesTouched merges paths into state.FilesTouched for every route other than a turn-end step: task-record completion, per-tool hooks, and subagent child-file merges. Those routes do not hash what they add, so any hash recorded for a merged path by an earlier step is dropped and the path falls back to name matching, exactly as applyTouchedFileHashes does for a changed path it could not hash. Keeping the older hash would make a commit of the newer content read as "the human replaced the agent's work".
Every non-SaveStep write that adds to FilesTouched must go through here.
func MutateSessionState ¶ added in v0.6.2
MutateSessionState is the safe load → mutate → save helper. It takes an OS-level advisory lock against .git/entire-session-locks/<id>.lock for the duration of the read+write so concurrent processes cannot lose each other's updates. fn receives the freshly-loaded state and mutates it in place; returning ErrMutationSkip skips the save. Reentrant within the same goroutine: nested calls share the outer's state pointer and skip the inner load/save, so all mutations are flushed by the outermost call.
fn may hold the lock for slow operations — PostCommit's callback, for example, runs CondenseSession (transcript redaction and compaction, checkpoint tree builds) inside the gate. That's deliberate: PostToolUse must not slip in mid-condense and revert CheckpointTranscriptStart or files_touched. A concurrent PostToolUse on the same session waits for the commit to finish.
Returns ErrStateNotFound if the state file doesn't exist (event arrived before InitializeSession). Errors from fn or from load/save propagate.
All session-state mutations funnel through this helper so the hot-path PostToolUse hook cannot revert fields written by lifecycle handlers (TurnEnd, PostCommit, ModelUpdate) that ran between our load and our save.
A nil return is not proof that anything was written. Callers with a side effect that must follow a durable write want MutateSessionStateOnSaved.
func MutateSessionStateOnSaved ¶ added in v0.10.3
func MutateSessionStateOnSaved(ctx context.Context, sessionID string, fn func(*SessionState) error, onSaved func()) error
MutateSessionStateOnSaved is MutateSessionState plus onSaved, an effect that runs only once the state fn mutated has been durably written — and never while the session gate is held.
It exists because a nil error is not proof of a save (ErrMutationSkip reports success without writing) and because *this* frame is not necessarily the one that saves. Only the outermost frame loads and saves; a nested frame's mutations are flushed by whoever owns that save. So a nested frame cannot answer "was this persisted?" at the moment it returns — the answer arrives later, from a frame further up the stack. Handing the effect to the helper rather than returning a bool moves the decision to the only frame that can make it: nested registrations are queued on the gate, and the outermost frame runs them after its own save succeeds, discarding them if it skips or fails.
That distinction is load-bearing for anything irreversible. Skill-invocation telemetry is the motivating case: extraction re-derives from transcript offset 0 on every pass and dedupes against a ledger in session state, so announcing an event whose ledger entry never landed does not self-correct — the next pass sees an unrecorded event and reports it again, duplicating it in PostHog. Not emitting is the recoverable direction: the append is re-derived and re-announced by the next pass.
onSaved runs after the gate is released, so its I/O (settings load, detached process spawn) never extends the hold time for a concurrent hook. Effects run in registration order, so nested registrations precede the outermost frame's own effect.
One gap this does not close: a nested frame whose fn *errors* has already mutated the shared state, and an outer frame that swallows that error still saves those mutations — with no effect registered. That direction loses an announcement rather than duplicating one, and is inherent to nested frames sharing a state pointer.
func OpenRepository ¶
func OpenRepository(ctx context.Context) (*git.Repository, error)
OpenRepository opens the git repository from the repo root. It uses 'git rev-parse --show-toplevel' to find the repository root, which works correctly even when called from a subdirectory or a linked worktree.
func PrepareSessionEndCondensation ¶ added in v0.10.3
func PrepareSessionEndCondensation(ctx context.Context, state *SessionState) error
PrepareSessionEndCondensation reserves an ID for content-bearing ENDED sessions or marks empty ENDED sessions fully condensed. File-bearing sessions remain eligible for PostCommit.
func PromoteTmpRefSafely ¶ added in v0.5.6
func PromoteTmpRefSafely(ctx context.Context, tmpRefName, destRefName plumbing.ReferenceName, label string) error
PromoteTmpRefSafely reads tmpRefName (the ref a fetch just landed into), advances destRefName to its hash via SafelyAdvanceLocalRef, then removes the tmp ref. The cleanup is deferred so the tmp ref is reaped even when the advance fails.
label is a short human-readable name used in error messages. Typical use:
// fetch with refspec "+<src>:<tmpRefName>" refs := checkpoint.ResolveRefs(ctx) return PromoteTmpRefSafely(ctx, tmpRefName, refs.Primary, refs.Primary.Short())
func PushQueuedCheckpointRefs ¶ added in v0.9.0
func PushQueuedCheckpointRefs(ctx context.Context, repo *git.Repository, remote string) (pushed int, pushDisabled bool, err error)
PushQueuedCheckpointRefs pushes any queued checkpoint refs to the configured checkpoint remote, surfacing errors (unlike the fail-soft pre-push path); the caller owns the repo. It returns the number of refs pushed and whether pushing is disabled in settings — a distinct signal from pushed==0 with pushing enabled (an empty queue), so callers can report the two accurately. Like the pre-push paths, an OPF rewrite that cannot run when OPF is enabled errors with the refs left queued. Currently used by the checkpoint migration command's opt-in "push now".
func ReadAgentTypeFromTree ¶ added in v0.4.4
ReadAgentTypeFromTree reads the agent type from a checkpoint's metadata.json file in a git tree. If metadata.json doesn't exist, it falls back to detecting the agent from the presence of agent-specific config markers (.claude/, .codex/, .cursor/, etc.). Returns agent.AgentTypeUnknown if the agent type cannot be determined.
func ReadAllSessionPromptsFromTree ¶
func ReadAllSessionPromptsFromTree(tree *object.Tree, checkpointPath string, sessionCount int, sessionIDs []string) []string
ReadAllSessionPromptsFromTree reads the first prompt for all sessions in a multi-session checkpoint. Returns a slice of prompts parallel to sessionIDs (oldest to newest). For single-session checkpoints, returns a slice with just the session prompt.
func ReadLatestSessionPromptFromCommittedTree ¶ added in v0.4.9
func ReadLatestSessionPromptFromCommittedTree(tree *object.Tree, cpID id.CheckpointID, sessionCount int) string
ReadLatestSessionPromptFromCommittedTree reads the first prompt from a committed checkpoint's latest session on the metadata branch tree. This navigates the sharded directory layout: <cpID.Path()>/<latestSessionIndex>/prompt.txt
Falls back through earlier sessions when the latest has no prompt. Avoids reading full transcripts — only reads prompt.txt files. sessionCount is the number of sessions in the checkpoint (from CheckpointInfo.SessionCount).
func ReadSessionPromptFromTree ¶
ReadSessionPromptFromTree reads the first meaningful prompt from a checkpoint's prompt.txt file in a git tree. Returns an empty string if the prompt cannot be read.
func ReconcileDisconnectedMetadataRef ¶ added in v0.7.6
func ReconcileDisconnectedMetadataRef( ctx context.Context, repo *git.Repository, localRefName plumbing.ReferenceName, remoteRefName plumbing.ReferenceName, w io.Writer, ) error
ReconcileDisconnectedMetadataRef detects and repairs disconnected local/remote metadata refs. Disconnected means no common ancestor, which only happens due to the empty-orphan bug. Diverged (shared ancestor) is normal and handled by the push path.
Repair strategy: cherry-pick local commits onto remote tip, preserving all data. Checkpoint shards use unique paths (<id[:2]>/<id[2:]>/), so cherry-picks always apply cleanly.
Progress messages are written to w (typically os.Stderr for hooks or cmd.ErrOrStderr() for commands). The remote ref can be either a remote-tracking ref or a temporary fetched ref.
func RecordFilesTouched ¶ added in v0.6.2
func RecordFilesTouched(ctx context.Context, sessionID string, modified, added, deleted []string) error
RecordFilesTouched merges paths into the session's FilesTouched, used by mid-turn lifecycle events (per-tool-use hooks) so PostCommit's carry-forward decision sees an accurate file list. Caller must pre-normalize paths to repo-relative form. No-ops when the session state doesn't exist or the merge produced no changes.
func ReinstallGitHooks ¶ added in v0.10.1
ReinstallGitHooks rewrites the managed git hooks using this repo's hook settings — the same pair EnsureSetup runs, exported so callers outside this package cannot open-code it and silently drop absolute_git_hook_path.
func RemoveGitHook ¶
RemoveGitHook removes all Entire CLI git hooks from the repository. If a .pre-entire backup exists, it is restored. Returns the number of hooks removed.
func ResetRedactionConfiguredForTest ¶ added in v0.10.6
func ResetRedactionConfiguredForTest()
ResetRedactionConfiguredForTest reinitializes the EnsureRedactionConfigured sync.Once so a test in another package can assert that a command's entry point configures redaction. Without it the Once may already be consumed by an earlier test in the same process, and the assertion passes or fails on test order rather than on the wiring under test.
func ResolveAgentForResume ¶ added in v0.10.4
ResolveAgentForResume resolves the agent from checkpoint metadata.
func ResolveTranscriptPath ¶ added in v0.6.2
func ResolveTranscriptPath(state *SessionState) (string, error)
ResolveTranscriptPath is the exported wrapper around resolveTranscriptPath for callers outside the strategy package (e.g. CLI commands streaming a live transcript to stdout). The underlying function may update state.TranscriptPath when an agent has relocated the file mid-session.
func RewriteQueuedCheckpointRefsWithOPF ¶ added in v0.10.3
func RewriteQueuedCheckpointRefsWithOPF(ctx context.Context, repo *git.Repository) error
RewriteQueuedCheckpointRefsWithOPF re-redacts the checkpoint refs awaiting push with OPF, rebuilds every un-applied commit on each as a commit carrying Entire-OPF-Applied: true, and points the ref at the new tip. Idempotent: a ref whose tip already carries the trailer is left byte-identical.
Discovery is the push-discovery queue rather than a local-vs-remote diff: it already names exactly the refs this push will carry, so there is no merge-base or divergence analysis. Within a ref, pushing it carries its whole unpushed ancestry, so the walk covers every commit back to the first one already carrying the trailer (unappliedAncestry) — bounded by the same resolveBootstrapLimit the v1 path uses.
Caller checks redact.OPFEnabled() and skips this when OPF is off. Returns the same error taxonomy as RewriteUnpushedV1WithOPF; the caller fails closed by withholding the flush (see prePushCheckpointRefs).
func RewriteUnpushedV1WithOPF ¶ added in v0.7.8
func RewriteUnpushedV1WithOPF(ctx context.Context, repo *git.Repository, target string) (plumbing.Hash, error)
RewriteUnpushedV1WithOPF re-redacts unpushed entire/checkpoints/v1 commits with OPF, builds new commits carrying Entire-OPF-Applied: true, and CAS-updates the local ref. Idempotent: already-applied commits are re-parented without re-running OPF.
Caller checks redact.OPFEnabled() and skips this when OPF is off. Returns one of {V1DivergedError, BootstrapTooLargeError, V1RefMovedError, OPFRuntimeFailedError, OPFNoCategoriesError, OPFBatchTooLargeError, OPFRawBytesTooLargeError} for privacy-critical failures — the pre-push hook propagates these so git push aborts.
func SafelyAdvanceLocalRef ¶ added in v0.5.6
func SafelyAdvanceLocalRef(ctx context.Context, repo *git.Repository, localRefName plumbing.ReferenceName, targetHash plumbing.Hash) error
SafelyAdvanceLocalRef updates localRefName to include targetHash without dropping local-only commits. Missing and behind refs advance to targetHash; already-current or locally-ahead refs are left unchanged; diverged refs replay commits after the merge-base onto targetHash and move localRefName to the replayed tip. If there is no merge-base and the reachable histories are complete, the full local chain is replayed.
func SaveSessionState ¶
func SaveSessionState(ctx context.Context, state *SessionState) error
SaveSessionState saves the session state atomically.
func SetBeforeCondense ¶ added in v0.11.4
func SetBeforeCondense(fn func(ctx context.Context, sessions []*SessionState) bool)
SetBeforeCondense registers the hook PostCommit runs for the sessions it is about to condense (nil clears it). The cli registers the Codex child-ledger refresh here, so child task records are reconciled in exactly the set PostCommit stores.
func ShellQuoteForDisplay ¶ added in v0.11.4
ShellQuoteForDisplay quotes a value for a command line the USER will paste. It is not for building an argv -- nothing here is executed, and a path that reaches an exec goes as a separate argument instead (see docs/development/git-safety.md's "Never Put a Dynamic Value on a cmd.exe Line").
The POSIX branch delegates to shellQuote, this file's existing quoter for the #!/bin/sh hooks it generates, rather than repeating the escaping: single quoting is total, since the only character with meaning inside a single-quoted string is the closing quote. Windows shells have no equivalent, so double quotes are used there, which is what cmd.exe and PowerShell both accept for a path.
Quoted only when it has to be, so the ordinary path stays readable.
func StoreAgentTypeHint ¶ added in v0.6.0
func StoreAgentTypeHint(ctx context.Context, sessionID string, agentType types.AgentType) (created bool, err error)
StoreAgentTypeHint records the agent type that owns a session before SessionState exists. Used by the lifecycle dispatcher when SessionStart fires (state isn't created until TurnStart, so we need a place to remember which agent claimed the session first).
Semantics: first writer wins. When multiple agents fire hooks for the same session ID — e.g., Cursor IDE running cursor-agent while also forwarding to Claude Code's hook system — only the agent that fires SessionStart first gets recorded. Subsequent calls return nil without overwriting.
At TurnStart, InitializeSession reads this hint to override agentType when the hook firing isn't the same agent that owns the session. After the state file is written, the hint is unused but remains until ClearSessionState removes it alongside the state file.
Returns (created=true) when this call wrote the hint, (created=false) when the hint already existed (no-op) or agentType was empty/Unknown.
Banner display is gated separately via ClaimSessionStartBanner — winning the ownership claim does NOT mean this agent should also print the banner, because the winner may not implement HookResponseWriter (e.g., Cursor).
func StoreModelHint ¶ added in v0.5.0
StoreModelHint writes the LLM model name to a lightweight hint file (.git/entire-sessions/{session_id}.model) for cross-process persistence.
Why a separate file instead of SessionState?
SessionState requires BaseCommit (used for commit linking, checkpoint writing, doctor classification, etc.) and is only created during TurnStart when the git repo is fully inspected. Some agents report the model on earlier hooks that fire as separate CLI processes before TurnStart:
- Claude Code sends "model" on SessionStart (before any TurnStart)
The hint is read by handleLifecycleTurnStart/TurnEnd when event.Model is empty, passed to InitializeSession, and persisted in state.ModelName. After that the hint file is redundant — it sits unused until ClearSessionState removes it alongside the session state file.
func TaskMetadataDir ¶
TaskMetadataDir returns the path to a task's metadata directory within the session metadata directory.
func TransitionAndLog ¶ added in v0.3.13
func TransitionAndLog(goCtx context.Context, state *SessionState, event session.Event, ctx session.TransitionContext, handler session.ActionHandler) error
TransitionAndLog runs a session phase transition, applies actions via the handler, and logs the transition. Returns the first handler error from ApplyTransition (if any) so callers can surface it. The error is also logged internally for diagnostics. This is the single entry point for all state machine transitions to ensure consistent logging of phase changes.
func TruncateDescription ¶
TruncateDescription truncates a string to maxLen runes, adding "..." if truncated. Uses rune-based slicing to avoid splitting multi-byte UTF-8 characters. If maxLen is less than 3, truncates without ellipsis.
func UpsertCompletedTaskRecord ¶ added in v0.10.3
UpsertCompletedTaskRecord writes a COMPLETED task record, merging files into any existing record for the same ToolUseID — the multi-turn producer shape (Factory Droid Workers reach turn-end once per Worker turn, all attributed to one parent tool use), unlike CompleteTaskRecord's exactly-once claim.
func WarnIfMetadataDisconnected ¶ added in v0.5.0
WarnIfMetadataDisconnected checks (once per process) whether the metadata branch is disconnected and prints a warning to stderr if so. The check runs against the first checkpoint read candidate whose remote-tracking ref exists (pure read across both tiers). It does NOT fix the problem — users are directed to 'entire doctor'.
Takes the caller's context rather than context.Background(): the probe below reads git, so a detached context ignored both interrupt cancellation and the invocation-scoped remote-read cache, re-shelling out for answers the caller already had.
Uses sync.Once, so a transient failure on the first call permanently suppresses the warning. This is acceptable because the check is advisory only and 'entire doctor' is the authoritative repair path.
func WithCheckpointRemoteBootstrap ¶ added in v0.9.0
WithCheckpointRemoteBootstrap marks ctx as permitting EnsurePrimaryRef to fetch a missing primary metadata ref from a configured checkpoint_remote. This must only be set on explicit, user-initiated setup flows (`entire enable`, agent add/setup) — never on the per-turn hook hot path. EnsureSetup runs synchronously on every TurnStart hook and hook execution has a hard timeout; without this guard a slow/unreachable checkpoint remote would repeat an expensive fetch on every turn instead of self-healing once via the empty-orphan fallback.
func WithFreshGitRemoteCache ¶ added in v0.10.1
WithFreshGitRemoteCache installs a new cache even when the parent context already carries one, giving a long-lived process a per-unit-of-work window instead of one snapshot for its whole lifetime.
func WithGitRemoteCache ¶ added in v0.10.1
WithGitRemoteCache returns a context whose .git/config remote reads are memoized for the lifetime of that context, reusing an already-installed cache if there is one. Never span a git-remote mutation without calling InvalidateGitRemoteCache.
main() installs this on the root context, which is the whole process. Answers are partitioned per repository (see gitRemoteCache), so a command walking several repositories stays correct. What process scope does NOT give is freshness over time: `entire mcp` serves an agent session from one context, so it installs a fresh window per request rather than pinning one snapshot for hours. A command that runs long enough for a remote to be added underneath it should do the same.
func WithSessionLockWait ¶ added in v0.10.0
WithSessionLockWait returns a context whose session-state flock acquisitions are bounded to complete within wait from now (a shared wall-clock budget). Zero or negative leaves the wait unbounded (the default, used by turn-end/condensation which must not drop work). Only the lock acquisition is bounded; the mutation itself still runs under the caller's original context.
func WithSessionStateLocks ¶ added in v0.7.8
func WithSessionStateLocks(ctx context.Context, sessionID string, commonDirs []string, fn func() error) error
WithSessionStateLocks acquires the per-session state lock in each git common dir, then runs fn. Lock paths are deduplicated and sorted so callers that span repositories or worktrees can safely acquire more than one lock.
Types ¶
type BackgroundClaimContext ¶ added in v0.11.5
type BackgroundClaimContext struct {
// Recorded holds every path a session in the task's worktree has in
// FilesTouched.
Recorded map[string]struct{}
// Overlapped reports that another background task in the worktree was
// running at some point since this task launched: still running, or
// completed after this task's launch.
Overlapped bool
}
BackgroundClaimContext is what a background task's SubagentStop needs to decide which new untracked files it may claim.
func LoadBackgroundClaimContext ¶ added in v0.11.5
func LoadBackgroundClaimContext(ctx context.Context, sessionID, toolUseID string) (claims BackgroundClaimContext, ok bool, err error)
LoadBackgroundClaimContext loads the BackgroundClaimContext for the task toolUseID of sessionID. ok is false when the session has no state, no recorded worktree, or no record for the task, so its launch time and the files other sessions own are unknown.
type BootstrapTooLargeError ¶ added in v0.7.8
type BootstrapTooLargeError struct {
Count, Limit int
}
BootstrapTooLargeError: more un-OPF'd commits to rewrite than the safety cap — a first push to a remote with no v1 yet, or a checkpoint ref whose un-trailered ancestry runs deep because OPF was enabled late. OPF inference is ~30s per commit, so unbounded bootstraps could take hours.
func (*BootstrapTooLargeError) Error ¶ added in v0.7.8
func (e *BootstrapTooLargeError) Error() string
type CheckpointDeleteOptions ¶ added in v0.11.5
type CheckpointDeleteOptions struct {
// Targets are the remote targets to delete from (a subset of the plan's).
Targets []CheckpointDeleteTarget
// LocalOnly skips every remote.
LocalOnly bool
// Selection says how Targets were chosen; it only shapes the wording of
// a refusal. LocalOnly implies SelectionLocalOnly.
Selection CheckpointDeleteSelection
}
CheckpointDeleteOptions selects what ExecuteCheckpointDelete touches.
type CheckpointDeletePlan ¶ added in v0.11.5
type CheckpointDeletePlan struct {
CheckpointID id.CheckpointID
// LocalRef is the local per-checkpoint ref ("" when absent).
LocalRef plumbing.ReferenceName
LocalRefOID plumbing.Hash
// LocalV1 reports a copy on the local entire/checkpoints/v1 branch.
LocalV1 bool
// TrackingV1 lists remote names whose remote-tracking v1 ref holds it.
// Those refs move only when that remote's copy is deleted.
TrackingV1 []string
// V1PushURLs are where the next pre-push sends the local v1 branch: set
// only when git-branch is the primary backend and pushes are enabled.
V1PushURLs []string
Targets []CheckpointDeleteTarget
Sessions []CheckpointDeleteSession
SessionStates []CheckpointDeleteState
OtherLocalCheckpoints []CheckpointDeleteSibling
OtherLocalTruncated bool
// PushSessionsDisabled reports push_sessions=false; the delete still
// reaches remotes when the user asks for it.
PushSessionsDisabled bool
Warnings []string
}
CheckpointDeletePlan is everything a delete would touch, gathered without writing anything.
func PlanCheckpointDelete ¶ added in v0.11.5
func PlanCheckpointDelete(ctx context.Context, cid id.CheckpointID, opts CheckpointDeletePlanOptions) (*CheckpointDeletePlan, error)
PlanCheckpointDelete inspects the local repository and every checkpoint remote for cid. It writes nothing: no fetch, no ref update, no state save.
func (*CheckpointDeletePlan) ActiveStates ¶ added in v0.11.5
func (p *CheckpointDeletePlan) ActiveStates() []CheckpointDeleteState
ActiveStates returns the session states whose session is still running.
func (*CheckpointDeletePlan) CheckLocalV1Propagation ¶ added in v0.11.5
func (p *CheckpointDeletePlan) CheckLocalV1Propagation(targets []CheckpointDeleteTarget, selection CheckpointDeleteSelection) error
CheckLocalV1Propagation refuses a delete whose local v1 removal the next pre-push would carry to a remote the delete leaves alone. The removal is a commit on the local v1 branch, and pre-push fast-forwards each v1 push destination to it, deleting that destination's copy too. A destination probed and found without a v1 copy loses nothing; one that could not be probed is refused, since its copy cannot be ruled out.
Known limitations: a push to a remote that is not yet the sync remote can elect it on the spot (capture) and send v1 there; such a remote is not a destination here until it is elected. A dedicated checkpoint_remote URL derived from a remote spelling this clone cannot authenticate to (another remote's protocol, say) probes as unreachable, so the delete keeps refusing with "retry when reachable" until that spelling works or the remote is removed.
func (*CheckpointDeletePlan) HasLocalCopy ¶ added in v0.11.5
func (p *CheckpointDeletePlan) HasLocalCopy() bool
HasLocalCopy reports whether this clone holds the checkpoint.
func (*CheckpointDeletePlan) HolderTargets ¶ added in v0.11.5
func (p *CheckpointDeletePlan) HolderTargets() []CheckpointDeleteTarget
HolderTargets returns the reachable targets that hold the checkpoint.
func (*CheckpointDeletePlan) UnreachableTargetNames ¶ added in v0.11.5
func (p *CheckpointDeletePlan) UnreachableTargetNames() []string
UnreachableTargetNames names the targets the probe could not reach.
type CheckpointDeletePlanOptions ¶ added in v0.11.5
type CheckpointDeletePlanOptions struct {
// LocalOnly resolves the remote targets but never contacts them: each is
// marked NotChecked.
LocalOnly bool
}
CheckpointDeletePlanOptions narrows what PlanCheckpointDelete inspects.
type CheckpointDeleteResult ¶ added in v0.11.5
type CheckpointDeleteResult struct {
StatesCleared []string
LocalRef DeleteOutcome
LocalV1 DeleteOutcome
Targets []CheckpointDeleteTargetResult
}
CheckpointDeleteResult reports every step of a delete.
func ExecuteCheckpointDelete ¶ added in v0.11.5
func ExecuteCheckpointDelete(ctx context.Context, plan *CheckpointDeletePlan, opts CheckpointDeleteOptions) (_ *CheckpointDeleteResult, retErr error)
ExecuteCheckpointDelete deletes the planned checkpoint.
Order matters. Session states are cleared first so no local writer (an amend restoring the trailer, a pending condensation) recreates the ID, and the ID is recorded on the deleted list before any ref goes. The local ref is then removed before any remote one: a concurrent pre-push drains the queue and pushes refs that exist locally, so deleting remote-first would let it push the ref back between the two deletes. Remote failures are reported per target and never restore the local copy; the user retries with --remote.
func (*CheckpointDeleteResult) Failed ¶ added in v0.11.5
func (r *CheckpointDeleteResult) Failed() bool
Failed reports whether any requested remote delete failed.
type CheckpointDeleteSelection ¶ added in v0.11.5
type CheckpointDeleteSelection int
CheckpointDeleteSelection says how the remote targets were chosen, so a refusal can name a remedy that works for that way of choosing.
const ( // SelectionAllHolders: every reachable holder (no narrowing). SelectionAllHolders CheckpointDeleteSelection = iota // SelectionLocalOnly: --local-only, no remote at all. SelectionLocalOnly // SelectionRemoteFlag: narrowed with --remote. SelectionRemoteFlag // SelectionPrompt: narrowed in the interactive remote picker. SelectionPrompt )
type CheckpointDeleteSession ¶ added in v0.11.5
type CheckpointDeleteSession struct {
SessionID string
Agent string
InputTokens int
OutputTokens int
}
CheckpointDeleteSession is one session stored in the checkpoint, with the token delta the checkpoint carries for it.
type CheckpointDeleteSibling ¶ added in v0.11.5
type CheckpointDeleteSibling struct {
CheckpointID id.CheckpointID
SessionIDs []string
}
CheckpointDeleteSibling is another local checkpoint that carries one of the deleted checkpoint's sessions.
type CheckpointDeleteState ¶ added in v0.11.5
type CheckpointDeleteState struct {
SessionID string
// Active is true for a session that has not ended and whose agent may
// still be running.
Active bool
// Fields names the state fields that will be cleared.
Fields []string
}
CheckpointDeleteState is a local session state that references the checkpoint and would otherwise write it again.
type CheckpointDeleteTarget ¶ added in v0.11.5
type CheckpointDeleteTarget struct {
// Remotes are the names that resolve to URL: git remote names, or
// CheckpointRemoteTargetName for the dedicated store.
Remotes []string
// URL is passed verbatim to git. Display it through Display only.
URL string
// Reachable is false when the probe failed (ProbeError says why) or was
// skipped (NotChecked).
Reachable bool
ProbeError string
// NotChecked: --local-only planned the delete without contacting it.
NotChecked bool
// RefOID is the checkpoint ref's oid on the remote, zero when absent.
RefOID plumbing.Hash
// RefName is the checkpoint ref as the remote spells it (its shard's case
// can differ from a fresh computation).
RefName plumbing.ReferenceName
// V1Tip is the remote's v1 branch tip, zero when it has none.
V1Tip plumbing.Hash
V1 V1CopyState
}
CheckpointDeleteTarget is one concrete place a checkpoint can be deleted from: a single URL or path, resolved up front so the probe, the lease and the push all address the same repository.
func (CheckpointDeleteTarget) Display ¶ added in v0.11.5
func (t CheckpointDeleteTarget) Display() string
Display returns the target's URL with credentials redacted.
func (CheckpointDeleteTarget) HoldsCheckpoint ¶ added in v0.11.5
func (t CheckpointDeleteTarget) HoldsCheckpoint() bool
HoldsCheckpoint reports whether the target has (or may have) a copy.
func (CheckpointDeleteTarget) Name ¶ added in v0.11.5
func (t CheckpointDeleteTarget) Name() string
Name returns the target's primary name for messages and retry commands.
type CheckpointDeleteTargetResult ¶ added in v0.11.5
type CheckpointDeleteTargetResult struct {
Target CheckpointDeleteTarget
Ref DeleteOutcome
V1 DeleteOutcome
Error string
}
CheckpointDeleteTargetResult is the outcome on one remote target.
func (CheckpointDeleteTargetResult) Failed ¶ added in v0.11.5
func (r CheckpointDeleteTargetResult) Failed() bool
Failed reports whether anything on this target failed.
type CheckpointInfo ¶
type CheckpointInfo struct {
CheckpointID id.CheckpointID `json:"checkpoint_id"` // 12-hex-char from Entire-Checkpoint trailer, used as directory path
SessionID string `json:"session_id"`
CreatedAt time.Time `json:"created_at"`
CheckpointsCount int `json:"checkpoints_count"`
FilesTouched []string `json:"files_touched"`
Agent types.AgentType `json:"agent,omitempty"` // Human-readable agent name (e.g., "Claude Code")
IsTask bool `json:"is_task,omitempty"`
ToolUseID string `json:"tool_use_id,omitempty"`
SessionCount int `json:"session_count,omitempty"` // Number of sessions (1 if omitted)
SessionIDs []string `json:"session_ids,omitempty"` // All session IDs in this checkpoint
Imported bool `json:"imported,omitempty"` // True for read-only imported (commit-less) checkpoints
}
CheckpointInfo represents checkpoint metadata stored on the sessions branch. Metadata is stored at sharded path: <checkpoint_id[:2]>/<checkpoint_id[2:]>/
func ListCheckpoints ¶
func ListCheckpoints(ctx context.Context) ([]CheckpointInfo, error)
ListCheckpoints returns all checkpoints from committed checkpoint storage.
func ResolveLatestCheckpointFromMap ¶ added in v0.5.0
func ResolveLatestCheckpointFromMap(cpIDs []id.CheckpointID, infoMap map[id.CheckpointID]CheckpointInfo) (CheckpointInfo, bool)
ResolveLatestCheckpointFromMap picks the checkpoint with the latest CreatedAt from a list of checkpoint IDs. Filters to IDs present in infoMap, then picks the one with the most recent CreatedAt.
type CheckpointReadResolution ¶ added in v0.10.1
type CheckpointReadResolution struct {
// Candidates is the ordered read-candidate chain (see
// CheckpointReadRemotes).
Candidates []string
// ElectedName is the elected checkpoint sync remote; "" when the election
// failed or elected nothing. Only this remote may seed or advance local
// refs.
ElectedName string
// ElectionErr records a failed election. It is advisory for readers (the
// chain fail-opens to ["origin"] when configured); write-adjacent callers
// may surface it when they need a push target.
ElectionErr error
}
CheckpointReadResolution is the read-candidate chain bundled with the election result it was derived from.
func CheckpointReadRemotesWithElection ¶ added in v0.10.1
func CheckpointReadRemotesWithElection(ctx context.Context) CheckpointReadResolution
CheckpointReadRemotesWithElection returns the read-candidate chain together with the election result it was derived from, so callers that need both — e.g. to confine local-ref advancement to the elected remote while iterating the chain — make ONE election call instead of two that could disagree if settings or remotes change mid-operation. See CheckpointReadRemotes for the read-side fail-open rationale and cost note.
type CheckpointSyncRemote ¶ added in v0.10.0
type CheckpointSyncRemote struct {
Name string
Source CheckpointSyncRemoteSource
}
CheckpointSyncRemote is the single git remote elected to carry checkpoint data. Name is empty when no remotes are configured.
func ResolveCheckpointSyncRemote ¶ added in v0.10.0
func ResolveCheckpointSyncRemote(ctx context.Context) (CheckpointSyncRemote, error)
ResolveCheckpointSyncRemote elects the one configured git remote that checkpoint data syncs to. Pure local lookup — no network. Precedence: checkpoint_push_remote setting (fail-closed if the named remote has no fetch URL), then the captured election (evidence-elected by a past push that agreed with the branch's declared push destination; fail-soft if that remote is no longer fetchable), then "origin", then the sole remote, then the first remote in local config traversal order, including included files. It knows nothing about the checkpoint_remote URL feature; callers exempt that case.
Deliberately NOT keyed on the branch's tracking config alone (branch.<name>.pushRemote / remote.pushDefault / branch.<name>.remote). Election is compared against the remote of the push actually being made, so electing the tracking remote from config at rest silently drops checkpoint sync on every push to any OTHER remote — `git push <other> HEAD`, a `git clone -o base` whose checkpoints go to a separately added origin, any repo with remote.pushDefault set. TestAlternates_RelativeObjectAlternate_CheckpointSync is the regression: it clones with `-o base` and pushes checkpoints to `origin`, and a tracking tier makes the pre-push hook a silent no-op. The captured tier is the safe form of the same intent: tracking config nominates a remote, but only an actual push that delivers checkpoints to it elects it (see commitCapturedSyncRemote).
The fork setup that motivated the tracking tier — clone the base repo, add your fork, push there, with origin unpushable — is served automatically by capture, or explicitly by checkpoint_push_remote. Either way the result is readable from the same clone: the read paths (resume, explain, discovery) consult the elected remote first and fall back to origin as a read-only legacy tier (see CheckpointReadRemotes). Tracking config on its own still elects nothing, for the silent-no-op reason above.
type CheckpointSyncRemoteSource ¶ added in v0.10.0
type CheckpointSyncRemoteSource string
CheckpointSyncRemoteSource identifies which precedence rule elected the checkpoint sync remote.
const ( // SyncRemoteSourceConfig: strategy_options.checkpoint_push_remote. SyncRemoteSourceConfig CheckpointSyncRemoteSource = "config" // SyncRemoteSourceObserved: elected by evidence — a past push that agreed // with the branch's declared push destination (see // commitCapturedSyncRemote). Named for what was observed, not for the // latch that recorded it, which is why the internal names still say "capture". SyncRemoteSourceObserved CheckpointSyncRemoteSource = "observed" // SyncRemoteSourceDefault: "origin" exists. SyncRemoteSourceDefault CheckpointSyncRemoteSource = "default" // SyncRemoteSourceSole: exactly one remote configured. SyncRemoteSourceSole CheckpointSyncRemoteSource = "sole" // SyncRemoteSourceFirst: first remote in .git/config order. SyncRemoteSourceFirst CheckpointSyncRemoteSource = "first" )
type CleanupItem ¶
type CleanupItem struct {
Type CleanupType
ID string // Branch name, session ID, or checkpoint ID
Reason string // Why this item is being cleaned
}
CleanupItem represents an item that can be cleaned up.
func ListAllItems ¶ added in v0.5.4
func ListAllItems(ctx context.Context) ([]CleanupItem, error)
ListAllItems returns all Entire items for full cleanup: legacy shadow branches left by older CLIs, all session states, and the redaction cache.
type CleanupResult ¶
type CleanupResult struct {
RedactCaches []string // Deleted redaction prefix cache directories
ShadowBranches []string // Deleted legacy shadow branches
SessionStates []string // Deleted session state files
Checkpoints []string // Deleted checkpoint metadata
FailedBranches []string // Legacy shadow branches that failed to delete
FailedStates []string // Session states that failed to delete
FailedCheckpoints []string // Checkpoints that failed to delete
FailedRedactCache []string // Redaction caches that failed to delete
}
CleanupResult contains the results of a cleanup operation.
func DeleteAllCleanupItems ¶
func DeleteAllCleanupItems(ctx context.Context, items []CleanupItem) (*CleanupResult, error)
DeleteAllCleanupItems deletes all specified cleanup items. Logs each deletion for audit purposes.
type CleanupType ¶
type CleanupType string
CleanupType identifies the type of item to clean up.
const ( // CleanupTypeShadowBranch is a legacy shadow branch: older CLIs kept each // session's in-progress work on a local "entire/<commit>-<worktree>" branch. // Nothing writes them anymore, and nothing removes them automatically: // `entire doctor` reports them, and `entire doctor --force`, `entire clean` // and uninstall delete what upgraded repositories still carry. CleanupTypeShadowBranch CleanupType = "shadow-branch" CleanupTypeSessionState CleanupType = "session-state" CleanupTypeCheckpoint CleanupType = "checkpoint" // CleanupTypeRedactCache is the redaction prefix cache in the git common dir. // Purely derived data -- it is rebuilt on the next checkpoint -- so it is // removed wholesale rather than per entry. CleanupTypeRedactCache CleanupType = "redact-cache" )
type CondenseResult ¶
type CondenseResult struct {
CheckpointID id.CheckpointID // 12-hex-char from Entire-Checkpoint trailer, used as directory path
SessionID string
CheckpointsCount int
FilesTouched []string
Prompts []string // User prompts from the condensed session
TotalTranscriptLines int // Total transcript units after this condensation (JSONL line count or message count by agent format)
Skipped bool // True if condensation was skipped (no transcript or files to condense)
// SearchProbe records whether the session invoked Entire's history search
// and how that was determined. Telemetry only; see detectSearchUsage for why
// "could not tell" is a distinct state from "did not search".
SearchProbe searchProbe
// TranscriptSizeBaseline is the byte size to record as
// SessionState.CheckpointTranscriptSize. It must be measured on the SANITIZED,
// pre-externalization transcript so it lives in the same coordinate as the
// stored turn-end transcript it is later compared against in
// sessionHasNewContent (storedTranscriptSize). A raw-transcript size makes
// `storedSize > baseline` false forever for agents with
// a TranscriptSanitizer, so the session silently stops condensing after its
// first commit.
TranscriptSizeBaseline int64
// NewSkillEvents are the transcript-extracted skill events this
// condensation appended to session state (already deduped against
// everything previously recorded). Callers forward them to
// EmitSkillInvocationTelemetry after their MutateSessionState saves — an
// unsaved append is re-derived by the next extraction pass, so emitting
// early would double-report.
NewSkillEvents []agent.SkillEvent
}
CondenseResult contains the result of a session condensation operation.
type DeleteOutcome ¶ added in v0.11.5
type DeleteOutcome string
DeleteOutcome is what happened to one copy of a checkpoint.
const ( DeleteOutcomeDeleted DeleteOutcome = "deleted" // DeleteOutcomeAbsent: there was nothing to delete there (already gone). DeleteOutcomeAbsent DeleteOutcome = "absent" DeleteOutcomeFailed DeleteOutcome = "failed" // DeleteOutcomeSkipped: not attempted (not selected, or not applicable). DeleteOutcomeSkipped DeleteOutcome = "skipped" )
type ExtractedSessionData ¶
type ExtractedSessionData struct {
Transcript []byte // Full transcript content for the session
FullTranscriptLines int // Total line count in full transcript
Prompts []string // User prompts from the current checkpoint portion
FilesTouched []string
TokenUsage *agent.TokenUsage // Token usage calculated from transcript (since the token offset, TokenStart)
// SkillEvents are this condensation's extracted events. Transient — the
// durable ledger, and the per-hook cost of carrying it, is
// session.SessionState.SkillEvents; see its size note.
SkillEvents []agent.SkillEvent
// SearchProbe is `entire search` usage; see detectSearchUsage. Not set by
// the extractors: CondenseSession assigns it exactly once, gated on
// condenseOpts.searchProbeAllowed, so every result path carries the same
// value and ungated paths never pay the scan.
SearchProbe searchProbe
}
ExtractedSessionData contains the session data a condensation stores.
type GitHookRemoval ¶ added in v0.11.5
type GitHookRemoval struct {
// Removed is the number of Entire hooks removed.
Removed int
// Restored names the hooks whose .pre-entire backup was put back.
Restored []string
// Older names the rotated copies (<hook>.pre-entire.<timestamp>) left in
// place: they no longer run, and uninstall does not delete user files.
Older []string
}
GitHookRemoval reports what RemoveGitHookDetailed did.
func RemoveGitHookDetailed ¶ added in v0.11.5
func RemoveGitHookDetailed(ctx context.Context) (GitHookRemoval, error)
RemoveGitHookDetailed is RemoveGitHook, also reporting which backups were restored, for the caller to show the user.
type GitHookState ¶ added in v0.10.1
type GitHookState int
GitHookState describes .git/hooks relative to what InstallGitHook writes today.
Deliberately the same vocabulary as agent.HookConfigState: there are two hook surfaces (git hooks and per-agent configs) and one set of words for "ours and current" vs "ours but stale" vs "not ours". A single bool cannot serve both questions callers ask — EnsureSetup needs "are these current", while uninstall needs "is there anything of ours to remove", and a stale hook answers those differently.
const ( // GitHooksAbsent means there is no complete set of Entire git hooks: one is // missing, or a foreign hook sits at one of our paths. A partial set reads // Absent, which is what the predicate has always reported. GitHooksAbsent GitHookState = iota // GitHooksCurrent means every managed hook is ours and in the shape this // version writes. GitHooksCurrent // GitHooksOutdated means the hooks are ours but at least one is a shape we no // longer write. Today that means running Entire from the working tree, which // is broken as well as stale — the path it names is gone. GitHooksOutdated )
func CheckGitHookState ¶ added in v0.10.1
func CheckGitHookState(ctx context.Context) GitHookState
CheckGitHookState reports the state of the active hooks directory.
func CheckGitHookStateInDir ¶ added in v0.10.1
func CheckGitHookStateInDir(ctx context.Context, repoDir string) GitHookState
CheckGitHookStateInDir reports the state for a specific repo directory, for callers that must not depend on the working directory.
type ManualCommitStrategy ¶
type ManualCommitStrategy struct {
// contains filtered or unexported fields
}
ManualCommitStrategy implements the manual-commit strategy for session management. It tracks each session's pending work in session state and condenses session logs to permanent checkpoint storage when the user commits.
func NewManualCommitStrategy ¶
func NewManualCommitStrategy() *ManualCommitStrategy
NewManualCommitStrategy creates a new manual-commit strategy instance.
func (*ManualCommitStrategy) CommitMsg ¶
func (s *ManualCommitStrategy) CommitMsg(_ context.Context, commitMsgFile string) error
func (*ManualCommitStrategy) CondenseAndMarkFullyCondensed ¶ added in v0.5.3
func (s *ManualCommitStrategy) CondenseAndMarkFullyCondensed(ctx context.Context, sessionID string) error
CondenseAndMarkFullyCondensed condenses an ENDED session and marks it FullyCondensed in one operation. Used by the session stop hook to eagerly clean up sessions so PostCommit doesn't have to process them.
This does NOT call CondenseSessionByID because that method has two behaviors we don't want: (1) it calls clearSessionState when there is no pending work (deletes the state file entirely), and (2) it sets Phase = IDLE. Instead, we inline the condensation logic with ENDED-appropriate behavior.
Fail-open: if condensation fails, the session remains eligible for a later retry with the same reserved checkpoint ID.
func (*ManualCommitStrategy) CondenseSession ¶
func (s *ManualCommitStrategy) CondenseSession(ctx context.Context, repo *git.Repository, checkpointID id.CheckpointID, state *SessionState, committedFiles map[string]struct{}, opts ...condenseOpts) (*CondenseResult, error)
CondenseSession condenses a session's pending work to permanent storage. checkpointID is the 12-hex-char value from the Entire-Checkpoint trailer. Metadata is stored at sharded path: <checkpoint_id[:2]>/<checkpoint_id[2:]>/ Uses checkpoint.PersistentStore.Write with a checkpoint.Session request for persistent storage.
The transcript comes from the agent's live transcript, falling back to the sanitized copy the last Stop stored under .entire/metadata/<session>/ (see condensationTranscript); files touched come from session state.
func (*ManualCommitStrategy) CondenseSessionByID ¶ added in v0.3.13
func (s *ManualCommitStrategy) CondenseSessionByID(ctx context.Context, sessionID string) error
CondenseSessionByID condenses a session by its ID and cleans up. This is used by "entire doctor" to salvage stuck sessions.
func (*ManualCommitStrategy) CountOtherActiveSessionsWithCheckpoints ¶ added in v0.3.12
func (s *ManualCommitStrategy) CountOtherActiveSessionsWithCheckpoints(ctx context.Context, currentSessionID string) (int, error)
CountOtherActiveSessionsWithCheckpoints counts how many other active sessions from the SAME worktree (different from currentSessionID) have created checkpoints on the SAME base commit (current HEAD). This is used to show an informational message about concurrent sessions that will be included in the next commit. Returns 0, nil if no such sessions exist.
func (*ManualCommitStrategy) CreateSnapshotCheckpoint ¶ added in v0.11.4
func (s *ManualCommitStrategy) CreateSnapshotCheckpoint(ctx context.Context, sessionID string) (id.CheckpointID, error)
CreateSnapshotCheckpoint writes a checkpoint from a session's current transcript and returns its ID. It exists for sessions that change no files — research, planning, review — which otherwise never get a checkpoint: turn end skips SaveStep when nothing changed, so session end finds no steps and writes nothing. A session with pending file changes is refused with ErrPendingFileChanges; the next commit checkpoints that work, and a snapshot would only duplicate it. "Pending" is what the transcript and file-touch hooks can see: an edit made through a shell command rather than an edit tool is only detected at turn end, so a snapshot in that same turn is not refused and overlaps the next commit's checkpoint.
The checkpoint is written exactly as a condensation writes one — same extraction, redaction, and store write, so it is enqueued for push like any other — but it is not linked to a commit. A redaction failure is an error here rather than a dropped transcript (see condenseOpts.failOnRedactionError).
It runs under the session's state lock, like every other condensation: the per-session redaction prefix cache is written in two steps, and an unlocked writer racing a commit's condensation can leave its entry pointing at the other writer's payload, corrupting every later checkpoint of the session. It is still a snapshot: the locked state is condensed in memory and never saved back (ErrMutationSkip), so the session's checkpoint window is untouched and repeated snapshots each cover the session from the same start. The crash-recovery reservation machinery therefore does not apply either — an interrupted write leaves at most an orphaned checkpoint, never a stuck session.
Callers must configure redaction first (EnsureRedactionConfigured).
func (*ManualCommitStrategy) EnsureSessionExists ¶ added in v0.10.3
func (s *ManualCommitStrategy) EnsureSessionExists(ctx context.Context, sessionID string, agentType types.AgentType) error
EnsureSessionExists creates sessionID's state when missing, for producers that record subagent task records before the parent session has a turn.
func (*ManualCommitStrategy) FindSessionsForCommit ¶
func (s *ManualCommitStrategy) FindSessionsForCommit(ctx context.Context, baseCommitSHA string) ([]*SessionState, error)
FindSessionsForCommit is the exported version of findSessionsForCommit. Used by `entire clean` to find sessions to clean up.
func (*ManualCommitStrategy) HandleTurnEnd ¶ added in v0.3.13
func (s *ManualCommitStrategy) HandleTurnEnd(ctx context.Context, state *SessionState) error
func (*ManualCommitStrategy) InitializeSession ¶
func (s *ManualCommitStrategy) InitializeSession(ctx context.Context, sessionID string, agentType types.AgentType, transcriptPath string, userPrompt string, model string) error
InitializeSession creates session state for a new session or updates an existing one. This implements the optional SessionInitializer interface. Called during UserPromptSubmit to allow git hooks to detect active sessions.
If the session already exists and HEAD has moved (e.g., user committed), updates BaseCommit to the new HEAD.
agentType is the human-readable name of the agent (e.g., "Claude Code"). transcriptPath is the path to the live transcript file (for mid-session commit detection). userPrompt is the user's prompt text (stored truncated as LastPrompt for display). model is the LLM model identifier (e.g., "claude-sonnet-4-20250514"); empty if unknown.
func (*ManualCommitStrategy) ListLogsOnlyPendingCheckpoints ¶ added in v0.10.4
func (s *ManualCommitStrategy) ListLogsOnlyPendingCheckpoints(ctx context.Context, limit int) ([]PendingCheckpoint, error)
ListLogsOnlyPendingCheckpoints finds commits in the current branch's history that have condensed session logs in committed checkpoint storage. These are commits that were created with session data that has since been condensed.
The function works by: 1. Getting all checkpoints from committed checkpoint storage 2. Building a map of checkpoint ID -> checkpoint info 3. Scanning the current branch history for commits with Entire-Checkpoint trailers 4. Matching by checkpoint ID (stable across amend/rebase)
func (*ManualCommitStrategy) ListPendingCheckpoints ¶ added in v0.10.4
func (s *ManualCommitStrategy) ListPendingCheckpoints(ctx context.Context, limit int) ([]PendingCheckpoint, error)
ListPendingCheckpoints returns the pending rows for `checkpoint list --pending`: subagent task records of sessions based on HEAD (live and completed-unmaterialized records are both pending until condensed), plus logs-only resume points from commit history (see ListLogsOnlyPendingCheckpoints). Turn-end steps are tracked in session state only, so they have no row of their own.
func (*ManualCommitStrategy) PostCommit ¶
func (s *ManualCommitStrategy) PostCommit(ctx context.Context) error
func (*ManualCommitStrategy) PostRewrite ¶ added in v0.5.6
func (s *ManualCommitStrategy) PostRewrite(ctx context.Context, rewriteType string, r io.Reader) error
PostRewrite is called by the git post-rewrite hook after amend/rebase operations. It keeps session linkage aligned with rewritten commit SHAs in the current worktree.
func (*ManualCommitStrategy) PrePush ¶
func (s *ManualCommitStrategy) PrePush(ctx context.Context, remote string) error
PrePush is called by the git pre-push hook before pushing to a remote. It pushes each ref in refs.Push alongside the user's push.
If a checkpoint_remote is configured in settings, checkpoint branches/refs are pushed to the derived URL instead of the user's push remote.
Configuration options (stored in .entire/settings.json under strategy_options):
- push_sessions: false to disable automatic pushing of checkpoints
- checkpoint_remote: {"provider": "github", "repo": "org/repo"} to push to a separate repo
func (*ManualCommitStrategy) PrePushFromGitHook ¶ added in v0.9.0
func (s *ManualCommitStrategy) PrePushFromGitHook(ctx context.Context, remote string) error
PrePushFromGitHook handles a push initiated by Git's pre-push hook. Unlike direct callers, it protects an empty user remote from receiving checkpoint metadata before the user's first normal branch is published.
func (*ManualCommitStrategy) PrepareCommitMsg ¶
func (*ManualCommitStrategy) PreviewNextCheckpoint ¶ added in v0.11.5
func (s *ManualCommitStrategy) PreviewNextCheckpoint(ctx context.Context) ([]NextCheckpointPreview, error)
PreviewNextCheckpoint returns, for each session in the current worktree that has pending work (State.HasPendingWork), what the next commit's checkpoint would carry from it, most recent activity first. Fully condensed ended sessions are left out: PostCommit skips them too. It reads session state and the session's local metadata only; nothing is written.
func (*ManualCommitStrategy) RecordVanishedUntrackedFiles ¶ added in v0.11.5
func (s *ManualCommitStrategy) RecordVanishedUntrackedFiles(ctx context.Context, sessionID string) error
RecordVanishedUntrackedFiles records, for a turn end that saves no step (no file changes git can see), the untracked files this session created earlier and has since removed; see untrackedDeletionCandidates. A turn whose only effect is `rm` of such a file is exactly that case. No-op when the session has no state.
func (*ManualCommitStrategy) Reset ¶
Reset clears the session state of every session based on the current HEAD, and deletes the shadow branches older CLIs left behind (see ListRemovableLegacyShadowBranches). File changes remain in the working directory.
func (*ManualCommitStrategy) ResetSession ¶ added in v0.3.13
func (s *ManualCommitStrategy) ResetSession(ctx context.Context, w, errW io.Writer, sessionID string) error
ResetSession clears a single session's state. File changes remain in the working directory.
func (*ManualCommitStrategy) RestoreLogsOnly ¶
func (s *ManualCommitStrategy) RestoreLogsOnly(ctx context.Context, w, errW io.Writer, point PendingCheckpoint, force bool) ([]RestoredSession, error)
RestoreLogsOnly restores session logs from a logs-only pending checkpoint. This fetches the transcript from entire/checkpoints/v1 and writes it to the agent's session directory. Does not modify the working directory. When multiple sessions were condensed to the same checkpoint, ALL sessions are restored. If force is false, prompts for confirmation when local logs have newer timestamps. Returns info about each restored session so callers can print correct per-session resume commands.
func (*ManualCommitStrategy) SaveStep ¶ added in v0.4.6
func (s *ManualCommitStrategy) SaveStep(ctx context.Context, step StepContext) error
SaveStep records a turn-end step in session state: it merges the step's files into FilesTouched, records their content hashes (TouchedFileHashes), and accumulates token usage. Nothing is written to git — the transcript and files are read from disk again when a commit condenses the session.
func (*ManualCommitStrategy) SetBlobFetcher ¶ added in v0.5.1
func (s *ManualCommitStrategy) SetBlobFetcher(f checkpoint.BlobFetchFunc)
SetBlobFetcher configures on-demand blob fetching for the checkpoint store. Must be called before the first checkpoint store access (e.g., before RestoreLogsOnly).
type MetadataComparison ¶ added in v0.10.1
type MetadataComparison struct {
Relation MetadataRelation
Local plumbing.Hash
Remote plumbing.Hash
}
MetadataComparison is a MetadataRelation together with the tips it was derived from. Local and Remote are zero when Relation is MetadataRelationAbsent.
func CompareMetadataWithRemote ¶ added in v0.10.1
func CompareMetadataWithRemote(ctx context.Context, repo *git.Repository, remoteRefName plumbing.ReferenceName) (MetadataComparison, error)
CompareMetadataWithRemote classifies the local primary metadata ref against remoteRefName. Pure read — it never writes a ref, and it runs exactly one merge-base subprocess.
type MetadataRelation ¶ added in v0.10.1
type MetadataRelation int
MetadataRelation classifies how the local primary metadata ref stands relative to a remote-tracking ref. Derived from a single `git merge-base`, so the disconnected, fast-forward and diverged verdicts can never disagree — they used to be computed by two functions that each shelled out for the same pair.
const ( // MetadataRelationAbsent: one of the two refs does not exist, so there is // nothing to relate. MetadataRelationAbsent MetadataRelation = iota // MetadataRelationAligned: identical tips. MetadataRelationAligned // MetadataRelationLocalAhead: the remote tip is an ancestor of local. MetadataRelationLocalAhead // MetadataRelationRemoteAhead: local is an ancestor of the remote tip, so a // fetch fast-forwards. MetadataRelationRemoteAhead // MetadataRelationDiverged: both sides advanced past their merge base, so a // fetch from the elected remote replays the local commits onto the remote tip. MetadataRelationDiverged // MetadataRelationDisconnected: no merge base at all (the "empty-orphan bug"). MetadataRelationDisconnected )
type NextCheckpointPreview ¶ added in v0.11.5
type NextCheckpointPreview struct {
SessionID string
Agent types.AgentType
// LastPrompt is the session's most recent prompt, for labelling.
LastPrompt string
// LastActivity is the session's last interaction (StartedAt when none).
LastActivity time.Time
// Turns is the number of turn-end steps recorded since the last
// checkpoint (StepCount).
Turns int
// FilesTouched lists the files waiting for a commit, sorted.
FilesTouched []string
// TaskRecords lists the subagent task records waiting to be materialized.
TaskRecords []NextCheckpointTaskRecord
// Prompts are the user prompts since the last checkpoint, read from the
// session's prompt.txt and, when that is empty, extracted from its
// transcript after the last checkpoint's offset.
Prompts []string
}
NextCheckpointPreview describes what one session would contribute to the next checkpoint: the work recorded in session state since its last checkpoint. Turn ends write no git objects, so until a commit condenses it this work has no checkpoint ID of its own.
type NextCheckpointTaskRecord ¶ added in v0.11.5
type NextCheckpointTaskRecord struct {
ToolUseID string
SubagentType string
Description string
Completed bool
}
NextCheckpointTaskRecord is one subagent task record in a preview.
type OPFBatchTooLargeError ¶ added in v0.7.8
OPFBatchTooLargeError: a single push has more prose-leaf content than ENTIRE_OPF_BATCH_LIMIT will allow OPF to chew through in one inference call. Pushing under the limit yields a single ~10-30s pause; without the cap, a 100MB-of-prose push could take an hour.
The user-facing remediation is identical in shape to BootstrapTooLargeError: bump the limit, push without OPF, or break the push into smaller pieces.
func (*OPFBatchTooLargeError) Error ¶ added in v0.7.8
func (e *OPFBatchTooLargeError) Error() string
type OPFDecision ¶ added in v0.7.8
type OPFDecision int
OPFDecision is the resolved gate for a single pre-push OPF run.
const ( OPFRun OPFDecision = iota // run the rewrite, push OPF-applied (9-layer) OPFSkip // skip the rewrite, push regex-only (8-layer) OPFAbort // cancel the push entirely (Ctrl-C / non-TTY abort) )
type OPFNoCategoriesError ¶ added in v0.10.1
type OPFNoCategoriesError struct{}
OPFNoCategoriesError: OPF is enabled but the effective category set is empty (categories omitted, {}, or all-false), so the model scan cannot run. Proceeding would stamp Entire-OPF-Applied on commits OPF never saw, and later rewrites would skip them permanently — even after the user fixes the config. Abort the push with remediation instead. Two gates enforce this: the decision gate in prePush runs whenever OPF is enabled, with no knowledge of whether anything is unpushed, so a misconfigured repo aborts every push — including one with nothing to rewrite. The rewrite-level gate in RewriteUnpushedV1WithOPF sits after the no-work early returns and fires only when commits are unpushed; it deliberately aborts even a re-parent-only push (every unpushed commit already tagged) rather than poking a no-new-stamping hole through the invariant.
func (*OPFNoCategoriesError) Error ¶ added in v0.10.1
func (e *OPFNoCategoriesError) Error() string
type OPFRawBytesTooLargeError ¶ added in v0.7.8
OPFRawBytesTooLargeError: the cumulative raw blob bytes the collection pass loaded into memory exceeded the safety ceiling. Unlike the leaf-byte cap, this is about RAM headroom rather than inference wall-clock — a 200 MiB push of mostly-structural JSON has tiny leaf content but huge raw bytes, and would OOM the user's shell before the leaf-byte cap got a chance to fire.
The raw ceiling is derived from ENTIRE_OPF_BATCH_LIMIT (raw = leaf × rawByteCapMultiplier) so a user bumping the leaf cap proportionally bumps the raw ceiling and doesn't need a second env var.
func (*OPFRawBytesTooLargeError) Error ¶ added in v0.7.8
func (e *OPFRawBytesTooLargeError) Error() string
type OPFRuntimeFailedError ¶ added in v0.7.8
OPFRuntimeFailedError: the OPF circuit breaker tripped mid-rewrite. Some blobs were silently downgraded to regex-only; tagging those commits as Entire-OPF-Applied would be a privacy regression (future pushes would skip them while their content is regex-only). Abort before CAS so the user fixes their OPF install and retries.
Cause carries the underlying batch error when there is one (the breaker-tripped-at-start construction has none) so the real failure survives into the pre-push abort message and errors.Is/As chains.
func (*OPFRuntimeFailedError) Error ¶ added in v0.7.8
func (e *OPFRuntimeFailedError) Error() string
func (*OPFRuntimeFailedError) Unwrap ¶ added in v0.10.1
func (e *OPFRuntimeFailedError) Unwrap() error
type PendingCheckpoint ¶ added in v0.10.4
type PendingCheckpoint struct {
// ID is the unique identifier for this pending checkpoint
// (commit hash, branch name, stash ref, etc.)
ID string
// Message is the human-readable description/summary
Message string
// MetadataDir is the path to the metadata directory
MetadataDir string
// Date is when this pending checkpoint was created
Date time.Time
// IsTaskCheckpoint indicates if this is a task checkpoint (vs a session checkpoint)
IsTaskCheckpoint bool
// ToolUseID is the tool use ID for task checkpoints (empty for session checkpoints)
ToolUseID string
// IsLogsOnly indicates this is a commit with condensed session logs.
// The logs can be restored from entire/checkpoints/v1, but file state requires git checkout.
IsLogsOnly bool
// CheckpointID is the stable 12-hex-char identifier for logs-only points.
// Used to retrieve logs from entire/checkpoints/v1/<id[:2]>/<id[2:]>/full.jsonl
// Empty for task-record rows (uncommitted).
CheckpointID id.CheckpointID
// Agent is the human-readable name of the agent that created this checkpoint
// (e.g., "Claude Code", "Cursor")
Agent types.AgentType
// SessionID is the session identifier for this checkpoint.
// Used to distinguish checkpoints from different concurrent sessions.
SessionID string
// SessionPrompt is the initial prompt that started this session.
// Used to help users identify which session a checkpoint belongs to.
SessionPrompt string
// SessionCount is the number of sessions in this checkpoint (1 for single-session).
// Only populated for logs-only points with multi-session checkpoints.
SessionCount int
// SessionIDs contains all session IDs when this is a multi-session checkpoint.
// The last entry is the most recent session (same as SessionID).
// Only populated for logs-only points with multi-session checkpoints.
SessionIDs []string
// SessionPrompts contains the first prompt for each session (parallel to SessionIDs).
// Used to display context when showing resume commands for multi-session checkpoints.
SessionPrompts []string
// Imported indicates this point is a read-only imported (commit-less)
// checkpoint on the v1 metadata branch. Imported points are read-only.
Imported bool
}
PendingCheckpoint is one row of `checkpoint list --pending`, which is the resume view of the current branch rather than a single kind of thing. A row is one of two shapes:
- A subagent task record of a session based on HEAD, live or completed but not yet condensed onto entire/checkpoints/v1. IsTaskCheckpoint is true, ToolUseID names the task, and ID and CheckpointID are empty: nothing is committed yet. (Turn-end steps live only in session state and have no row.)
- A logs-only resume point: a commit on the current branch whose Entire-Checkpoint trailer resolves to a checkpoint that IS already condensed onto entire/checkpoints/v1. ID is that commit, CheckpointID the condensed checkpoint, and IsLogsOnly is true. It is listed so the session transcript can be restored from v1 (RestoreLogsOnly); file state would need a git checkout.
So "pending" describes the listing, not a guarantee that the underlying work is un-condensed — half these rows are recovered from condensed history. This abstraction allows different strategies to use different identifiers (commit hashes, branch names, stash refs, etc.)
type ResolvedSession ¶ added in v0.11.0
type ResolvedSession struct {
SessionID string
Resolution SessionResolution
// AgentType names the agent the session belongs to, on any resolution the
// identification tier produced — from the environment when a claim named
// the session, otherwise from the session's own state. Set even when
// Tracked is false, which is the case it exists for: the environment
// identifies the agent before any state has been loaded, and "you are
// inside a Codex session Entire is not recording" is the whole diagnosis.
// Empty on the worktree and other-worktree tiers, which report a session
// rather than a caller.
AgentType types.AgentType
// Tracked reports whether Entire holds session state for SessionID.
//
// False only when the environment named a session Entire has not
// recorded — hooks not installed, hooks failed, or the first turn not yet
// landed, since state is created at turn start. That reaches the caller as
// ResolutionCallerEnv for a single claim or ResolutionCallerAmbiguous for
// several, so do NOT test it by comparing Resolution; test this field.
//
// A diagnosis rather than an error: the caller is genuinely in a session,
// and Entire is genuinely not tracking it. Load-bearing for exactly that
// reason — `session current` and `session tokens` branch to their
// untracked diagnostic on it — so resolveUntrackedClaims assigns it
// explicitly rather than leaning on the zero value, matching the
// `Tracked: true` its sibling paths write.
Tracked bool
}
ResolvedSession is a session plus the provenance of how it was picked.
func ResolveCallerSession ¶ added in v0.11.0
func ResolveCallerSession(ctx context.Context) ResolvedSession
ResolveCallerSession answers "which session is running this process?", falling back to "which session is current here?" when nothing can identify the caller. Tiers, strongest first:
- Identification — the caller's own environment, its process ancestry, or both, ranked together by how near the owning process is (resolveCallerIdentity). Reports caller-env, ancestry, or caller-ambiguous.
- The most recently active session recorded in this worktree.
- The most recently active session anywhere in the shared store.
Every tier is reported through Resolution, and the weak ones are not silently interchangeable with identification — which is the whole point. `entire session current` used to collapse tiers 2 and 3 and report either as "the active session for the current worktree", so in a worktree with no sessions of its own it returned a live session belonging to a different worktree, indistinguishably from the real thing. An agent that read that ID back and acted on it operated on someone else's session.
Environment and ancestry are ONE tier rather than two, ordered internally. An earlier revision tried the environment first and returned on any hit, which is wrong for the nesting case: an inner agent that publishes no ID of its own passes the outer agent's variable through, so the lone claim named the outer session while the inner one sat in our ancestry one hop away. See ResolutionAncestry.
func (ResolvedSession) Found ¶ added in v0.11.0
func (r ResolvedSession) Found() bool
Found reports whether any session was resolved.
type RestoredSession ¶ added in v0.4.3
type RestoredSession struct {
SessionID string
CheckpointID string
Agent types.AgentType
Prompt string
CreatedAt time.Time // From session metadata; used by resume to determine most recent
Kind string
ReviewPrompt string
}
RestoredSession describes a single session that was restored by RestoreLogsOnly. Each session may come from a different agent, so callers use this to print per-session resume commands without re-reading the metadata tree.
type SessionResolution ¶ added in v0.11.0
type SessionResolution string
SessionResolution says how a session was arrived at, so a caller can tell an exact answer from a guess.
This exists because the guess is genuinely useful to a human — someone who asks which session is current in a terminal means "the one that has been working here" — and genuinely dangerous to an agent, which means "mine" and may go on to act on the answer. One command can serve both only if it says which question it answered.
const ( // ResolutionCallerEnv means an agent published this session's ID into our // environment and nothing nearer in our process ancestry contradicted it. ResolutionCallerEnv SessionResolution = "caller-env" // ResolutionAncestry means this session's recorded owner process is the // nearest such ancestor of ours — its agent spawned us, however // indirectly — and no environment variable named it. // // This outranks a bare environment claim rather than backing it up, which // is the opposite of what it looks like it should do. An environment // variable says only that some ancestor published it, not that the // publisher is our caller: agents nest, and an inner agent that publishes // nothing of its own (opencode, for one) passes the OUTER agent's // variable straight through to us. Depth is the only signal that // distinguishes "Codex ran me" from "Codex ran opencode ran me", so the // nearest owner wins wherever ancestry can rank at all. ResolutionAncestry SessionResolution = "ancestry" // ResolutionCallerAmbiguous means we are demonstrably inside an agent // session but cannot say which: several agents claim us and nothing // ordered their claims — no owner recorded on any of them, or a platform // that cannot introspect processes. The reported session is the most // plausible of them and explicitly a guess, so this does NOT satisfy // IsCaller. // // Reported rather than resolved on purpose. Picking one and calling it // "your own session" is the failure this whole type exists to prevent, and // registry order — which is what an arbitrary pick amounts to — carries no // information about who invoked the command. ResolutionCallerAmbiguous SessionResolution = "caller-ambiguous" // ResolutionWorktree means nothing identified the caller, and this is the // most recently active session recorded in the current worktree. ResolutionWorktree SessionResolution = "worktree" // ResolutionOtherWorktree means the current worktree has no sessions at // all, and this is the most recently active session from somewhere else in // the shared store. Correct for "what has been happening in this repo", // never evidence about the caller — worktrees share one session store, so // this can be any of dozens of unrelated sessions. ResolutionOtherWorktree SessionResolution = "other-worktree" // ResolutionNone means no session was found by any tier. ResolutionNone SessionResolution = "" )
func (SessionResolution) IsCaller ¶ added in v0.11.0
func (r SessionResolution) IsCaller() bool
IsCaller reports whether the resolution actually identified the calling process's own session, rather than inferring or guessing a plausible one. Gate anything that acts on a session — as opposed to merely displaying it — on this.
ResolutionCallerAmbiguous is deliberately excluded even though it means "you are definitely inside an agent session": knowing that is not knowing which.
type SessionRestoreInfo ¶
type SessionRestoreInfo struct {
SessionID string
Prompt string // First prompt preview for display
Status SessionRestoreStatus // Status of this session
LocalTime time.Time
CheckpointTime time.Time
}
SessionRestoreInfo contains information about a session being restored.
type SessionRestoreStatus ¶
type SessionRestoreStatus int
SessionRestoreStatus represents the status of a session being restored.
const ( StatusNew SessionRestoreStatus = iota // Local file doesn't exist StatusUnchanged // Local and checkpoint are the same StatusCheckpointNewer // Checkpoint has newer entries StatusLocalNewer // Local has newer entries (conflict) )
func ClassifyTimestamps ¶
func ClassifyTimestamps(localTime, checkpointTime time.Time) SessionRestoreStatus
ClassifyTimestamps determines the restore status based on local and checkpoint timestamps.
type SessionState ¶
SessionState is an alias for session.State. Previously this was a separate struct with manual conversion functions.
func ListSessionStates ¶ added in v0.3.13
func ListSessionStates(ctx context.Context) ([]*SessionState, error)
ListSessionStates returns all session states from the state directory. This is a package-level function that doesn't require a specific strategy instance.
func LoadSessionState ¶
func LoadSessionState(ctx context.Context, sessionID string) (*SessionState, error)
LoadSessionState loads the session state for the given session ID. Returns (nil, nil) when session file doesn't exist or session is stale (not an error condition). Stale sessions are automatically deleted by the underlying StateStore.
type StepContext ¶ added in v0.4.6
type StepContext struct {
// SessionID is the Claude Code session identifier
SessionID string
// ModifiedFiles is the list of files modified during the session
// (extracted from the transcript, already filtered and relative)
ModifiedFiles []string
// NewFiles is the list of new files created during the session
// (pre-computed by CLI from pre-prompt state comparison)
NewFiles []string
// DeletedFiles is the list of files deleted during the session
// (tracked files that no longer exist)
DeletedFiles []string
// MetadataDir is the repo-relative path to the session metadata directory
// (.entire/metadata/<session>). It is both the git tree path the directory's
// files land at and the name the .entire root reads them through; see
// checkpoint.WriteOptions.MetadataDir for why there is no absolute twin.
MetadataDir string
// CommitMessage is the generated commit message
CommitMessage string
// TranscriptPath is the path to the transcript file
TranscriptPath string
// AuthorName is the name to use for commits
AuthorName string
// AuthorEmail is the email to use for commits
AuthorEmail string
// AgentType is the human-readable agent name (e.g., "Claude Code", "Cursor")
AgentType types.AgentType
// Transcript position at step/turn start - tracks what was added during this step
StepTranscriptIdentifier string // Last identifier when step started (e.g., message UUID for Claude Code)
StepTranscriptStart int // Transcript line count when this step/turn started
// TokenUsage contains the token usage for this checkpoint
TokenUsage *agent.TokenUsage
// SubagentLedgerVersion is the authoritative inventory version observed
// while token evidence was extracted. nil means no inventory snapshot;
// a pointer to zero is a valid snapshot before the first child is observed.
SubagentLedgerVersion *uint64
}
StepContext contains all information needed for saving a step checkpoint. All file paths should be pre-filtered and normalized by the CLI layer.
type V1CopyState ¶ added in v0.11.5
type V1CopyState string
V1CopyState says whether a remote's v1 branch holds the checkpoint.
const ( V1CopyAbsent V1CopyState = "absent" V1CopyPresent V1CopyState = "present" // V1CopyUnknown: the remote has a v1 branch whose tip is not in this // clone, so its contents are verified only when the delete runs. V1CopyUnknown V1CopyState = "unknown" )
type V1DivergedError ¶ added in v0.7.8
V1DivergedError: local entire/checkpoints/v1 has commits that aren't ancestors of the remote tip (force-push or another machine pushed). Rewriting under divergence would silently rebase rejected work, so we refuse.
func (*V1DivergedError) Error ¶ added in v0.7.8
func (e *V1DivergedError) Error() string
type V1RefMovedError ¶ added in v0.7.8
V1RefMovedError: another worktree advanced the local ref during our rewrite (CAS conflict). Orphan rewritten objects sit in .git/objects until git gc --prune; no manual cleanup needed.
func (*V1RefMovedError) Error ¶ added in v0.7.8
func (e *V1RefMovedError) Error() string
Source Files
¶
- caller_session.go
- checkpoint_delete.go
- checkpoint_delete_exec.go
- checkpoint_read_remotes.go
- checkpoint_remote.go
- checkpoint_remote_hint.go
- checkpoint_sync_capture.go
- checkpoint_sync_remote.go
- cleanup.go
- common.go
- content_overlap.go
- git_remote_cache.go
- hook_backup.go
- hook_managers.go
- hooks.go
- manual_commit.go
- manual_commit_condensation.go
- manual_commit_condensation_recovery.go
- manual_commit_git.go
- manual_commit_hooks.go
- manual_commit_opf_prompt.go
- manual_commit_opf_refs.go
- manual_commit_opf_rewrite.go
- manual_commit_pending.go
- manual_commit_preview.go
- manual_commit_provenance.go
- manual_commit_push.go
- manual_commit_redo.go
- manual_commit_reset.go
- manual_commit_session.go
- manual_commit_snapshot.go
- manual_commit_types.go
- messages.go
- metadata_reconcile.go
- push_common.go
- refs_push_destination.go
- resolve_transcript.go
- session_identity.go
- session_state.go
- skill_events.go
- skill_telemetry.go
- strategy.go
- telemetry_signals.go
- testhelpers.go
- touched_file_hashes.go
- trackable_paths.go
- unpushed_checkpoints.go