worktree

package
v0.0.0-...-8ea0922 Latest Latest
Warning

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

Go to latest
Published: Sep 19, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package worktree implements the cross-worktree overlay: a merged task view where coordination state (status, owner) is combined across all worktrees of one repository while content always comes from the local copy. It is shared by the CLI read views, the MCP server, and the web server's data layer.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AttributeNestedSiblingCopies

func AttributeNestedSiblingCopies(local []*model.Task, scanDir string, siblings []gitmeta.Worktree) []*model.Task

AttributeNestedSiblingCopies drops local-scan copies whose file path lies inside a sibling worktree's root: a non-hidden checkout nested in the scan root gets double-scanned, and those files belong to that worktree, not this one (spec §8). The sibling's own scan already carries them, so dropping the local-scan copies attributes them instead of flagging duplicates.

Only siblings nested *inside* scanDir can double-scan, so only those are considered. A sibling whose root is an ancestor of scanDir — the primary checkout, when this worktree lives under it (`repo/.claude/worktrees/x`) — contains every local task path without having scanned any of them; treating that as nesting would drop the entire local task list.

func DiscoverSiblings

func DiscoverSiblings(scanDir string) ([]gitmeta.Worktree, error)

DiscoverSiblings is the default Discoverer, backed by git via gitmeta.

func DisplayPath

func DisplayPath(root string) string

DisplayPath renders a worktree root relative to the current directory when possible (matching how users navigate between worktrees), falling back to the absolute path.

Types

type Builder

type Builder struct {
	// Enabled activates the overlay (worktree_scope "unified", the default;
	// "isolated" disables). Even when true, the overlay only forms when the
	// scan dir is inside a git repo with sibling worktrees.
	Enabled bool
	// Discover lists sibling worktrees; nil means DiscoverSiblings.
	Discover Discoverer
	// Verbose enables warnings about skipped sibling scans on stderr.
	Verbose bool
	// IgnoreDirs is passed through to sibling scans, matching the local scan.
	IgnoreDirs []string
}

Builder builds the cross-worktree overlay for a scan directory. The zero value is a disabled builder, so surfaces that never configure worktrees keep exactly today's behavior.

func (Builder) Build

func (b Builder) Build(scanDir string, localTasks []*model.Task) (*Overlay, error)

Build builds the overlay for the local task list, or returns nil when the overlay is inactive: disabled, scanDir not in a git repo, or no sibling worktrees to merge (in which case behavior is identical to today's). Sibling task file paths are left absolute; callers that display them relativize afterwards.

func (Builder) Overlay

func (b Builder) Overlay(scanDir string, siblings []gitmeta.Worktree, localTasks []*model.Task) *Overlay

Overlay merges localTasks with scans of the given sibling worktrees, or returns nil (overlay inactive) when there are none. Callers that already discovered siblings (e.g. to derive watch dirs) use this to avoid a second discovery.

func (Builder) SiblingGuard

func (b Builder) SiblingGuard(taskID, scanDir string) error

SiblingGuard scans the builder's sibling worktrees for taskID and returns the guard error when a copy exists there; nil otherwise. It is the overlay-less variant for mutation paths that never built the full merge.

func (Builder) Siblings

func (b Builder) Siblings(scanDir string) ([]gitmeta.Worktree, error)

Siblings discovers sibling worktrees when the builder is enabled. Discovery failures deactivate the overlay rather than failing the command.

type CopyEntry

type CopyEntry struct {
	Worktree string `json:"worktree,omitempty" yaml:"worktree,omitempty"`
	Branch   string `json:"branch,omitempty" yaml:"branch,omitempty"`
	Status   string `json:"status" yaml:"status"`
	Owner    string `json:"owner,omitempty" yaml:"owner,omitempty"`
	Local    bool   `json:"local,omitempty" yaml:"local,omitempty"`
}

CopyEntry is one copy of a task in one worktree, shaped for get's Worktrees section and its JSON/YAML output.

type Discoverer

type Discoverer func(scanDir string) ([]gitmeta.Worktree, error)

Discoverer lists the sibling worktrees of the repo containing scanDir (nil when scanDir is not in a repo). It is a seam so overlay consumers can inject worktrees in tests without git.

type Exclusion

type Exclusion struct {
	ID       string `json:"id" yaml:"id"`
	Reason   string `json:"reason" yaml:"reason"`
	Worktree string `json:"worktree,omitempty" yaml:"worktree,omitempty"`
	Branch   string `json:"branch,omitempty" yaml:"branch,omitempty"`
	Status   string `json:"status,omitempty" yaml:"status,omitempty"`
}

Exclusion is one task the overlay keeps out of next's recommendations, together with the provenance that explains why. Reason is the same sentence the table view prints; the remaining fields carry it structurally for json/yaml consumers.

type Overlay

type Overlay struct {
	Tasks []*Task

	Warnings []Warning
	// contains filtered or unexported fields
}

Overlay is the merged cross-worktree task view: local tasks in scan order, then tasks that exist only in sibling worktrees, sorted by ID.

func Merge

func Merge(local []*model.Task, siblings []SiblingTasks) *Overlay

Merge merges the local task list with sibling worktree scans. It is pure with respect to git: worktree discovery and scanning happen upstream.

func (*Overlay) Copies

func (o *Overlay) Copies(id string) []CopyEntry

Copies returns an entry for every copy of id across worktrees when the copies disagree on status or owner, local copy first; nil when there is a single copy or all copies agree (detail views render the section only on divergence).

func (*Overlay) EffectiveTasks

func (o *Overlay) EffectiveTasks() []*model.Task

EffectiveTasks returns the merged task list with the effective status substituted on shallow copies: local tasks in scan order, then sibling-only tasks. Status-aggregating views (board, stats, graph, report, tracks, phases) render this; content still comes from the base copy.

func (*Overlay) Exclusions

func (o *Overlay) Exclusions() []Exclusion

Exclusions returns every overlay-imposed exclusion, sorted by task ID.

func (*Overlay) Get

func (o *Overlay) Get(id string) *Task

Get returns the overlay task for id, or nil when the id is unknown.

func (*Overlay) Local

func (o *Overlay) Local() []*model.Task

Local returns the local task list after sibling-root attribution.

func (*Overlay) RecommendationInputs

func (o *Overlay) RecommendationInputs() ([]*model.Task, map[string]string)

RecommendationInputs returns the task list and exclusion map to feed the next recommender. Effective statuses are substituted on copies (so a task completed in a sibling unblocks its local dependents) and sibling-suppressed tasks are mapped to a human-readable exclusion reason; they stay in the task list so dependency, children, and critical-path resolution still see them.

func (*Overlay) RelativizeSiblingPaths

func (o *Overlay) RelativizeSiblingPaths()

RelativizeSiblingPaths rewrites each sibling copy's file path relative to its own worktree's tasks dir, for display. Call it only after the merge — status ties are broken by stat'ing the absolute paths. Server surfaces skip this so file paths stay resolvable (e.g. for worklog lookups).

func (*Overlay) SiblingGuard

func (o *Overlay) SiblingGuard(taskID string) error

SiblingGuard returns the guard error when taskID exists only in a sibling worktree, and nil when it has a local copy or is unknown. Callers invoke it after a local lookup misses, so mutations name where the task actually lives instead of reporting "not found".

type SiblingOnlyError

type SiblingOnlyError struct {
	TaskID   string
	Worktree string // display path of the sibling worktree root
	Branch   string // "" when detached
}

SiblingOnlyError is the mutation guard error for a task that exists only in a sibling worktree. Writes are never redirected — the mutation must fail and tell the user where the task actually lives.

func (*SiblingOnlyError) Error

func (e *SiblingOnlyError) Error() string

type SiblingTasks

type SiblingTasks struct {
	WT    gitmeta.Worktree
	Tasks []*model.Task
}

SiblingTasks pairs a sibling worktree with the tasks scanned from it.

type Task

type Task struct {
	*model.Task
	EffectiveStatus model.Status `json:"effective_status" yaml:"effective_status"`
	EffectiveOwner  string       `json:"effective_owner,omitempty" yaml:"effective_owner,omitempty"`
	Worktree        string       `json:"worktree,omitempty" yaml:"worktree,omitempty"`
	Branch          string       `json:"branch,omitempty" yaml:"branch,omitempty"`
	LocalOnly       bool         `json:"-" yaml:"-"`
	RemoteOnly      bool         `json:"remote_only,omitempty" yaml:"remote_only,omitempty"`
	// contains filtered or unexported fields
}

Task decorates a task with cross-worktree provenance. The embedded task is the local copy when one exists; content always comes from it, only coordination state (status, owner) is merged across worktrees.

func (*Task) ExclusionReason

func (t *Task) ExclusionReason() string

ExclusionReason returns why next must not recommend this task, or "" when the overlay imposes no exclusion. A task is excluded when it exists only in a sibling worktree, or when a sibling copy advanced it beyond the local status — a locally in-progress task keeps today's resume semantics even if a sibling also claims it.

func (*Task) Origin

func (t *Task) Origin() (gitmeta.Worktree, bool)

Origin returns the sibling worktree the winning copy of this task came from, and false when that copy is local. Detail views use it to resolve paths — a worklog, say — inside the checkout that actually holds the file.

type Warning

type Warning struct {
	TaskID  string
	Message string
}

Warning is a cross-worktree consistency warning tied to a task.

Jump to

Keyboard shortcuts

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