worktrees

package
v0.27.2 Latest Latest
Warning

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

Go to latest
Published: Aug 10, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package worktrees creates and validates the isolated Git worktrees used for human and agent development. Canonical clones remain clean, current mirrors of their base branches; all feature work lives below .wb/worktrees.

Index

Constants

View Source
const SecureCanonicalGitHelperArgument = "--wb-internal-canonical-git"

SecureCanonicalGitHelperArgument selects the private WB child-process path that validates retained canonical root and Git-directory descriptors before executing a canonical-clone Git operation.

View Source
const SecureCleanupGitHelperArgument = "--wb-internal-cleanup-git"

SecureCleanupGitHelperArgument selects the private WB child process that runs cleanup Git commands from retained canonical and worktree descriptors.

View Source
const SecureStageCanonicalGitHelperArgument = "--wb-internal-stage-canonical-git"

SecureStageCanonicalGitHelperArgument selects the private WB child-process path that combines an inherited private stage with an inherited canonical Git capability. It is deliberately separate from SecureStageGitHelper so the small stage inspection helper never needs a Git capability.

View Source
const SecureStageGitHelperArgument = "--wb-internal-stage-git"

SecureStageGitHelperArgument selects the private WB child-process path that enters the stage directory from inherited file descriptor 3 before running Git. It is handled before normal CLI parsing and is not a user command.

Variables

This section is empty.

Functions

func CanonicalRepositoryPath added in v0.22.2

func CanonicalRepositoryPath(projectsRoot, repository string) (string, error)

CanonicalRepositoryPath validates one owner/repository slug with the same strict parser used by Create, then returns its canonical-clone path below projectsRoot. Callers that perform work before Create (for example managed hook refresh) must use this resolver rather than constructing a path from user-supplied segments themselves.

func DefaultCleanupReportDir added in v0.18.0

func DefaultCleanupReportDir(home string, now time.Time) string

DefaultCleanupReportDir returns the durable audit directory for one apply, below the already-resolved WB home directory (see wbhome.Root).

func DefaultRenameReportDir added in v0.26.0

func DefaultRenameReportDir(home string, now time.Time) string

DefaultRenameReportDir returns the durable audit directory for one apply, below the already-resolved WB write home — see DefaultCleanupReportDir.

func OriginSlug

func OriginSlug(ctx context.Context, path string) (string, error)

OriginSlug returns the owner/repository identity of path's origin remote.

func PreflightWorkLogOptions added in v0.27.0

func PreflightWorkLogOptions(task string, options WorkLogOptions) error

PreflightWorkLogOptions remains the pure, path-independent validation used by callers that have not resolved a projects root yet. Mutation paths use PrepareWorkLogOptions so an existing run is corroborated as well.

func RunSecureCanonicalGitHelper added in v0.22.2

func RunSecureCanonicalGitHelper(args []string) int

RunSecureCanonicalGitHelper runs Git from the inherited canonical root only after opening and comparing its `.git` entry with the inherited Git directory descriptor. This prevents Git's own discovery from treating a substituted `.git` pathname as authority.

func RunSecureCleanupGitHelper added in v0.22.2

func RunSecureCleanupGitHelper(args []string) int

RunSecureCleanupGitHelper is the child half of descriptor-anchored cleanup Git operations. FD 3 is the canonical repository, FD 4 is its held `.git` directory, FD 5 is the held worktree parent, and FD 6 is the target worktree. Both canonical descriptors and the optional parent/worktree pair are reauthorized immediately before Git executes.

func RunSecureStageCanonicalGitHelper added in v0.22.2

func RunSecureStageCanonicalGitHelper(args []string) int

RunSecureStageCanonicalGitHelper is the last authority before Git creates a staged checkout. FD 3 is the private stage, FD 4 is the canonical root, and FD 5 is its `.git` directory. The stage target is derived only after the inherited stage passes containment; Git itself receives the inherited `.git` directory through GIT_DIR instead of resolving a lexical canonical path.

func RunSecureStageGitHelper added in v0.22.2

func RunSecureStageGitHelper(args []string) int

RunSecureStageGitHelper is the child-side half of a secure worktree add. The caller passes the stage directory in fd 3 via exec.Cmd.ExtraFiles. This child alone changes its current directory from that immutable descriptor, then runs Git with only the already-constructed arguments supplied by its parent. It returns an ordinary process exit code for cmd/wb's early main dispatch and for the worktrees package's test helper.

func ValidateRepositories added in v0.22.2

func ValidateRepositories(repositories []string) ([]string, error)

ValidateRepositories rejects unsafe and duplicate repository coordinates before callers mutate canonical clones, hooks, or WB home. It also returns a sorted copy so every later phase has deterministic order.

Types

type AbortDisposition added in v0.27.0

type AbortDisposition string

AbortDisposition makes an unfinished effort legible instead of leaving an ambiguous directory behind. Handoff and not_landed retain the worktree for a later claim; discarded is the only disposition that removes local Git state.

const (
	AbortHandoff   AbortDisposition = "handoff"
	AbortNotLanded AbortDisposition = "not_landed"
	AbortDiscarded AbortDisposition = "discarded"
)

func (AbortDisposition) String added in v0.27.0

func (d AbortDisposition) String() string

type AbortOptions added in v0.27.0

type AbortOptions struct {
	ProjectsRoot string
	Task         string
	Base         string
	Disposition  AbortDisposition
	Successor    string
	DeleteRemote bool
	Apply        bool
	// contains filtered or unexported fields
}

type AbortResult added in v0.27.0

type AbortResult struct {
	ListResult
	Disposition   AbortDisposition `json:"disposition"`
	Successor     string           `json:"successor,omitempty"`
	Eligible      bool             `json:"eligible"`
	Applied       bool             `json:"applied"`
	WorktreeGone  bool             `json:"worktree_gone"`
	BranchDeleted bool             `json:"branch_deleted"`
	RemoteDeleted bool             `json:"remote_deleted"`
	BacklogID     string           `json:"backlog_id,omitempty"`
	Reason        string           `json:"reason,omitempty"`
}

func Abort added in v0.27.0

func Abort(ctx context.Context, options AbortOptions) ([]AbortResult, error)

Abort seals every Work Log in a coordinated task. It is the deliberate escape hatch for unused or interrupted claims which cannot meet the merged PR evidence required by Cleanup. --apply never destroys resumable work: only an explicit discarded disposition removes a clean linked checkout and its exact local branch ref. The private archive/outbox is written first.

type CleanupOptions added in v0.18.0

type CleanupOptions struct {
	ProjectsRoot string
	Task         string
	Base         string
	// Filter narrows both which candidates are validated and which are acted
	// on to those whose owner/repository slug contains this substring — see
	// ListOptions.Filter. An empty Filter matches everything, preserving
	// today's behavior exactly.
	Filter       string
	AllMerged    bool
	Apply        bool
	DeleteRemote bool
	OlderThan    time.Duration
	ReportDir    string
	Now          func() time.Time
	// contains filtered or unexported fields
}

CleanupOptions controls planning and removal of merged WB tasks.

type CleanupOutcome added in v0.18.0

type CleanupOutcome struct {
	Results     []CleanupResult     `json:"results"`
	ReportPath  string              `json:"report_path,omitempty"`
	Diagnostics []ListDiagnostic    `json:"diagnostics,omitempty"`
	Artifacts   []LifecycleArtifact `json:"artifacts,omitempty"`
}

CleanupOutcome contains the decisions plus the durable audit report written before any destructive apply.

Diagnostics never abort a run. A malformed candidate inside the selection (see CleanupOptions.Filter) is skipped and reported here as a warning, and blocks eligibility only for its own coordinated task — the same all-or-nothing unit blockUnsafeTasks already applies to an unclean, locked, or unmerged sibling. Every other task in the run proceeds normally.

func Cleanup added in v0.18.0

func Cleanup(ctx context.Context, options CleanupOptions) (CleanupOutcome, error)

Cleanup plans or applies cleanup for one task or every safely merged task. A coordinated task is all-or-nothing: one unsafe repository blocks all of its worktrees.

type CleanupResult added in v0.18.0

type CleanupResult struct {
	ListResult
	Eligible      bool   `json:"eligible"`
	Applied       bool   `json:"applied"`
	RemoteDeleted bool   `json:"remote_deleted"`
	WorktreeGone  bool   `json:"worktree_gone"`
	BranchDeleted bool   `json:"branch_deleted"`
	BacklogID     string `json:"backlog_id,omitempty"`
	Reason        string `json:"reason,omitempty"`
}

CleanupResult records one repository's cleanup decision and outcome.

type CreateOptions

type CreateOptions struct {
	ProjectsRoot string
	Operation    string
	Branch       string
	Base         string
	Resume       bool
	WorkLog      WorkLogOptions
	// contains filtered or unexported fields
}

CreateOptions controls one coordinated worktree creation operation.

type CreateResult

type CreateResult struct {
	Repository   string `json:"repository"`
	CanonicalDir string `json:"canonical_dir"`
	WorktreeDir  string `json:"worktree_dir"`
	Branch       string `json:"branch"`
	Base         string `json:"base"`
	BaseSHA      string `json:"base_sha"`
	Action       string `json:"action"`
	WorkLogPath  string `json:"work_log_path,omitempty"`
}

CreateResult identifies the isolated checkout prepared for one repository.

func Create

func Create(ctx context.Context, repositories []string, options CreateOptions) ([]CreateResult, error)

Create verifies each clean canonical clone, fetches its requested origin base without changing any local branch, then creates (or explicitly resumes) the corresponding isolated worktree.

Synchronization happens before any branch is created. This is deliberate: every new feature branch must be based on a verified latest remote base. The canonical checkout is only a clean Git capability: WB never switches it to the base or fast-forwards a local branch as a side effect of creation.

type GuardOptions

type GuardOptions struct {
	ProjectsRoot string
	Base         string
}

GuardOptions defines the local checkout policy checked by hooks and agents.

type GuardResult

type GuardResult struct {
	Path          string `json:"path"`
	CanonicalDir  string `json:"canonical_dir"`
	WorktreesRoot string `json:"worktrees_root"`
	Branch        string `json:"branch"`
	Kind          string `json:"kind"`
	Transient     bool   `json:"transient,omitempty"`
}

GuardResult describes a checkout that satisfies the worktree policy.

func Guard

func Guard(ctx context.Context, path string, options GuardOptions) (GuardResult, error)

Guard verifies that path is either a clean canonical checkout of the base branch or a non-base linked worktree in WB's central worktree hierarchy.

type LifecycleArtifact added in v0.27.0

type LifecycleArtifact struct {
	Task          string `json:"task"`
	WorktreesRoot string `json:"worktrees_root"`
	Path          string `json:"path"`
	Kind          string `json:"kind"`
	State         string `json:"state"`
	Disposition   string `json:"disposition"`
	Eligible      bool   `json:"eligible"`
	Applied       bool   `json:"applied"`
	ArchivePath   string `json:"archive_path,omitempty"`
	Reason        string `json:"reason,omitempty"`
}

LifecycleArtifact is WB-owned control-plane state, never a user worktree candidate. Active secure stages are transient under the task lock. Retired stages are identity-bound quarantine evidence: a later create may reclaim one only when it is still the same empty directory. Inventory reports the classification but cleanup must never reinterpret or delete it as a legacy dot-prefixed repository checkout.

type ListDiagnostic added in v0.22.2

type ListDiagnostic struct {
	Task          string `json:"task,omitempty"`
	WorktreesRoot string `json:"worktrees_root,omitempty"`
	Path          string `json:"path"`
	Message       string `json:"message"`
}

ListDiagnostic describes a malformed task-layout candidate that was skipped without hiding valid sibling worktrees. It is intentionally separate from ListResult so cleanup can never mistake an unvalidated path for a safe linked checkout. WorktreesRoot is carried alongside Task so a diagnostic can be matched back to the exact coordinated task it belongs to even when more than one resolver-recognized layout is being read at once (see wbhome.Resolve) — Task name alone is not always unique across layouts.

type ListOptions added in v0.18.0

type ListOptions struct {
	ProjectsRoot string
	Task         string
	Base         string
	// Filter narrows the inventory to candidates whose owner/repository slug
	// (or, for a candidate that cannot be identified that cleanly, whatever
	// raw path-derived identity is available) contains this substring — the
	// same "only repos whose org/name contains this substring" semantics as
	// the root --filter flag elsewhere in WB. An empty Filter matches
	// everything, exactly like today. Filtering happens before a candidate's
	// diagnostic or result is retained, so a candidate outside the selection
	// can neither appear in the report nor influence it.
	Filter string
	GitHub bool
}

ListOptions selects WB-managed task worktrees and optional GitHub PR state.

type ListOutcome added in v0.22.2

type ListOutcome struct {
	SchemaVersion int                 `json:"schema_version"`
	Results       []ListResult        `json:"results"`
	Diagnostics   []ListDiagnostic    `json:"diagnostics,omitempty"`
	Artifacts     []LifecycleArtifact `json:"artifacts,omitempty"`
}

ListOutcome preserves the valid local inventory while exposing every deterministic malformed-candidate diagnostic encountered during scanning.

func ListWithDiagnostics added in v0.22.2

func ListWithDiagnostics(ctx context.Context, options ListOptions) (ListOutcome, error)

ListWithDiagnostics inventories every resolver-recognized layout. It never descends below a Git root, which prevents ordinary repository directories such as .claude, .github, source, and generated trees from being re-read as task-level repositories.

type ListResult added in v0.18.0

type ListResult struct {
	Task               string       `json:"task"`
	Repository         string       `json:"repository"`
	CanonicalDir       string       `json:"canonical_dir"`
	WorktreeDir        string       `json:"worktree_dir"`
	WorktreesRoot      string       `json:"worktrees_root"`
	Branch             string       `json:"branch"`
	Base               string       `json:"base"`
	HeadSHA            string       `json:"head_sha"`
	RemoteHeadSHA      string       `json:"remote_head_sha,omitempty"`
	RemoteTargetSHA    string       `json:"remote_target_sha,omitempty"`
	IntegratedAtOrigin bool         `json:"integrated_at_origin"`
	Clean              bool         `json:"clean"`
	LocallyMerged      bool         `json:"locally_merged"`
	Locked             bool         `json:"locked"`
	LastCommit         time.Time    `json:"last_commit"`
	OpenPullRequest    *PullRequest `json:"open_pull_request,omitempty"`
	MergedPullRequest  *PullRequest `json:"merged_pull_request,omitempty"`
}

ListResult describes one linked checkout below the WB task hierarchy.

func List added in v0.18.0

func List(ctx context.Context, options ListOptions) ([]ListResult, error)

List inspects real Git worktrees. It stays local unless GitHub is requested. Callers that present diagnostics should use ListWithDiagnostics.

type PullRequest added in v0.18.0

type PullRequest struct {
	Number  int        `json:"number"`
	URL     string     `json:"url"`
	State   string     `json:"state"`
	Base    string     `json:"base"`
	HeadSHA string     `json:"head_sha"`
	Merged  *time.Time `json:"merged_at,omitempty"`
}

PullRequest is the GitHub evidence used to decide whether a branch is safe to clean up. HeadSHA must match the current branch tip.

type RenameOptions added in v0.26.0

type RenameOptions struct {
	ProjectsRoot string
	OldTask      string
	NewTask      string
	// Filter narrows which of OldTask's repositories are renamed to those
	// whose owner/repository slug contains this substring — see
	// ListOptions.Filter for the exact semantics. An empty Filter renames
	// every repository under OldTask.
	Filter string
	// Branch is the feature branch created in every renamed worktree. Empty
	// derives "codex/<new-task>", exactly like `wb worktree create` derives
	// "codex/<task>" when --branch is omitted.
	Branch string
	Base   string
	// DeleteOldBranch is retained for source compatibility; recycle always
	// deletes the old local branch. Force is the explicit discarded-work
	// authorization for an old branch not integrated into origin/Base.
	DeleteOldBranch bool
	// DeleteRemote must be explicit for apply. If origin/<old-branch> exists,
	// it must still equal the preflight head and is retired with an exact
	// force-with-lease after the old Work Log is durable.
	DeleteRemote bool
	Force        bool
	// PreserveCachePaths is the allow-list of ignored/untracked paths that may
	// survive recycle. Empty means no cache survives. Paths are repository
	// relative, safe, and audited in the rename report.
	PreserveCachePaths []string
	WorkLog            WorkLogOptions
	// Apply performs the rename. The default is a dry-run plan, exactly like
	// `wb worktree cleanup`.
	Apply     bool
	ReportDir string
	Now       func() time.Time
	// contains filtered or unexported fields
}

RenameOptions controls re-homing every worktree below one task to a new task name. Recycling is deliberately opt-in and starts from a clean base: every untracked and ignored path outside an explicit, safe cache allow-list makes the operation refuse. WB never broadly cleans those paths. Callers may preserve a cache path (for example "node_modules") when the setup-time saving is worth it. This prevents a previous effort's source, credentials, or generated artefacts leaking merely because Git happened to ignore them.

The branch itself is never recycled. Every renamed worktree is switched onto a freshly created branch based on an up-to-date Base, matching the rule that "the branch always goes; the worktree may be recycled."

type RenameOutcome added in v0.26.0

type RenameOutcome struct {
	Results     []RenameResult   `json:"results"`
	ReportPath  string           `json:"report_path,omitempty"`
	Diagnostics []ListDiagnostic `json:"diagnostics,omitempty"`
}

RenameOutcome contains the decisions plus the durable audit report written before any destructive apply — see Cleanup's identical convention. A malformed candidate or an ineligible sibling blocks the whole task: moving part of a coordinated task to the new name and leaving the rest behind would strand exactly the recycling this verb exists to enable.

func Rename added in v0.26.0

func Rename(ctx context.Context, options RenameOptions) (RenameOutcome, error)

Rename re-homes every worktree under OldTask (optionally narrowed by Filter) to NewTask. It always uses `git worktree move` — with a plain move plus `git worktree repair` as a verified fallback — because a bare directory move leaves Git's own administrative gitdir pointer stale and `wb worktree guard` (and Git itself) rejects the result.

type RenameResult added in v0.26.0

type RenameResult struct {
	OldTask             string   `json:"old_task"`
	NewTask             string   `json:"new_task"`
	Repository          string   `json:"repository"`
	CanonicalDir        string   `json:"canonical_dir"`
	OldWorktreeDir      string   `json:"old_worktree_dir"`
	NewWorktreeDir      string   `json:"new_worktree_dir"`
	OldBranch           string   `json:"old_branch"`
	NewBranch           string   `json:"new_branch"`
	Base                string   `json:"base"`
	Eligible            bool     `json:"eligible"`
	Applied             bool     `json:"applied"`
	Repaired            bool     `json:"repaired,omitempty"`
	OldBranchDeleted    bool     `json:"old_branch_deleted"`
	OldRemoteDeleted    bool     `json:"old_remote_deleted"`
	PreservedCachePaths []string `json:"preserved_cache_paths,omitempty"`
	Reason              string   `json:"reason,omitempty"`
}

RenameResult records one repository's rename decision and outcome.

type RepositoryRenameMismatchError added in v0.25.3

type RepositoryRenameMismatchError struct {
	Worktree            string
	Owner               string
	PathRepository      string
	CanonicalRepository string
}

RepositoryRenameMismatchError reports a managed worktree whose on-disk <task>/<owner>/<repository> path segment no longer names its canonical clone's current repository — exactly the signature a GitHub repository rename leaves behind on every worktree that predates it. Its Error text is unchanged from the plain fmt.Errorf this replaced, so `wb worktree guard` (which still treats this as a hard, single-checkout rejection) reports the same message as before. List and Cleanup recognize it structurally via errors.As to survive it: this is ordinary history, not corruption, and a stale path segment alone is not evidence that anyone's work is at risk.

func (*RepositoryRenameMismatchError) Error added in v0.25.3

func (mismatch *RepositoryRenameMismatchError) Error() string

type WorkLogOptions added in v0.27.0

type WorkLogOptions struct {
	EffortID              string
	RunID                 string
	Initiator             string
	AgentID               string
	AgentRuntime          string
	Model                 string
	OriginalPrompt        string // readable local file, copied to the private archive
	RequireOriginalPrompt bool   // public create/recycle commands require exact local recovery input
	// contains filtered or unexported fields
}

WorkLogOptions is transport-neutral. The exact prompt is private local data; only opaque IDs and bounded Git evidence enter the projection/outbox.

func PrepareWorkLogOptions added in v0.27.0

func PrepareWorkLogOptions(projectsRoot, task string, options WorkLogOptions) (WorkLogOptions, error)

PrepareWorkLogOptions validates every identifier and snapshots the exact private prompt before a caller mutates Git, hooks, or a worktree. It also corroborates an existing run's immutable prompt archive so reusing a Run ID with different bytes is rejected before worktree creation.

Jump to

Keyboard shortcuts

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