Skip to content

fix(worker): eliminate branch UDA — session name now derived from description#341

Merged
birdmanmandbir merged 2 commits intomainfrom
worker/1ef631bb
Mar 20, 2026
Merged

fix(worker): eliminate branch UDA — session name now derived from description#341
birdmanmandbir merged 2 commits intomainfrom
worker/1ef631bb

Conversation

@birdmanmandbir
Copy link
Contributor

Summary

  • Root cause: SessionName() used task.Branch (preferred) or task.Description (fallback). The branch UDA was written after spawn, so lookup time computed a different session name than spawn time → "no worker session assigned" error.
  • Fix: Remove the branch UDA entirely. SessionName() now always derives the slug from task.Description, which is immutable and available at both spawn and lookup time.

Changes

  • internal/taskwarrior/taskwarrior.go — remove Branch field from Task struct, remove UpdateWorkerMetadata, fix SessionName() to always use description
  • internal/taskwarrior/queries.go — replace branch.any: with spawner.any: in GetActiveWorkerTasks
  • internal/worker/spawn.go — remove UpdateWorkerMetadata call (no longer writes branch UDA at spawn)
  • cmd/pr.go, internal/pr/pr.go, internal/review/review.go — remove task.Branch fallbacks, use WorktreeBranch() exclusively
  • cmd/pr.go merge — fix cleanup guard that was gated on ctx.Task.Branch != "" (would have silently skipped cleanup after this change)
  • internal/worker/list.go — use WorktreeBranch() for live branch display; fall back to GenerateBranch() if worktree gone
  • internal/tui/task_detail.go — derive display branch from description (display-only, no I/O needed)
  • internal/tui/actions.go — remove branch-based fallback path in resolveWorkDir
  • internal/worker/hook.go — remove unused hookTask.Branch() accessor

Test plan

  • make ci passes
  • ttal task advance <uuid> spawns worker — verify no branch: UDA written (task <uuid> export | jq .branch returns null)
  • ttal open session <uuid> attaches successfully after spawn
  • ttal worker list shows correct session names
  • ttal pr merge triggers cleanup (no longer blocked by missing branch field)

…cription

Removes the branch UDA from the task lifecycle entirely. The branch UDA
was written after spawn via UpdateWorkerMetadata(), causing SessionName()
to compute different slugs between spawn time (description-based) and
lookup time (branch-based), resulting in 'no worker session assigned' errors.

- Remove Branch field from Task struct and UpdateWorkerMetadata function
- SessionName() now always derives slug from description (immutable)
- GetActiveWorkerTasks() uses spawner.any: filter instead of branch.any:
- Remove branch UDA fallback in cmd/pr.go, internal/pr/pr.go, review.go
- Fix cleanup guard in pr merge — no longer gated on Branch field
- worker/list.go uses WorktreeBranch() for live branch; falls back to generated
- TUI task_detail derives display branch from description (display-only)
- Remove branch-based fallback path in tui/actions.go resolveWorkDir
- Remove unused hookTask.Branch() accessor
@birdmanmandbir
Copy link
Contributor Author

PR Review — fix(worker): eliminate branch UDA

Summary

This PR fixes the "no worker session assigned" error by removing the race condition between session creation and the branch UDA write. SessionName() now derives deterministically from the task description (immutable), so open/attach commands always find the session regardless of timing.

The fix is sound and the implementation is consistent across all call sites.


Critical Issues

None.

Important Issues

None.

Suggestions

[DRY, low-priority] internal/pr/pr.go:14 and cmd/pr.go:49 contain identical two-step branch resolution logic (try WorktreeBranch, zero on error, error if empty). This duplication was previously masked by the Branch UDA fallback. Consider extracting a RequiredBranch(ctx) helper to consolidate it — though the different error messages at each site are intentional, so it's not urgent.

[Clarity] internal/worker/list.go:149 is now the only site that uses a two-tier WorktreeBranchGenerateBranch soft fallback for display. Worth a comment clarifying it's display-only, to prevent future callers from copying this pattern into non-display paths:

// display only — falls back to generated name when no live worktree exists

Strengths

  • Root cause is correctly identified and cleanly eliminated — SessionName() is now a pure, stable function
  • spawner.any: is a better discriminator for active worker tasks than branch.any: was
  • UpdateWorkerMetadata, hookTask.Branch(), and branch-based resolveWorkDir all removed without backward-compat shims (good YAGNI hygiene)
  • Task struct is now a clean data record with no derived/mutable state
  • Tests updated proportionally to match removed behavior

VERDICT: LGTM

@birdmanmandbir
Copy link
Contributor Author

Triage Update

Fixed

  • Clarity comment on two-tier branch fallback in internal/worker/list.go:149 — added // display only — falls back to generated name when no live worktree exists (commit 29880e3)

Deferred

  • DRY: extract RequiredBranch(ctx) helper for internal/pr/pr.go:14 + cmd/pr.go:49 — valid observation, but the two sites have intentionally different error messages making extraction awkward. Low priority, not a correctness issue.

@birdmanmandbir birdmanmandbir merged commit 624cdd9 into main Mar 20, 2026
1 check passed
@birdmanmandbir birdmanmandbir deleted the worker/1ef631bb branch March 20, 2026 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant