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 ¶
- func AttributeNestedSiblingCopies(local []*model.Task, scanDir string, siblings []gitmeta.Worktree) []*model.Task
- func DiscoverSiblings(scanDir string) ([]gitmeta.Worktree, error)
- func DisplayPath(root string) string
- type Builder
- func (b Builder) Build(scanDir string, localTasks []*model.Task) (*Overlay, error)
- func (b Builder) Overlay(scanDir string, siblings []gitmeta.Worktree, localTasks []*model.Task) *Overlay
- func (b Builder) SiblingGuard(taskID, scanDir string) error
- func (b Builder) Siblings(scanDir string) ([]gitmeta.Worktree, error)
- type CopyEntry
- type Discoverer
- type Exclusion
- type Overlay
- func (o *Overlay) Copies(id string) []CopyEntry
- func (o *Overlay) EffectiveTasks() []*model.Task
- func (o *Overlay) Exclusions() []Exclusion
- func (o *Overlay) Get(id string) *Task
- func (o *Overlay) Local() []*model.Task
- func (o *Overlay) RecommendationInputs() ([]*model.Task, map[string]string)
- func (o *Overlay) RelativizeSiblingPaths()
- func (o *Overlay) SiblingGuard(taskID string) error
- type SiblingOnlyError
- type SiblingTasks
- type Task
- type Warning
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 ¶
DiscoverSiblings is the default Discoverer, backed by git via gitmeta.
func DisplayPath ¶
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 ¶
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 ¶
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.
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
Exclusions returns every overlay-imposed exclusion, sorted by task ID.
func (*Overlay) RecommendationInputs ¶
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 ¶
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 ¶
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 ¶
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.