fix(done): get issue ID from agent hook and detect integration branches (#411) (#453)

Branch names like "polecat/furiosa-mkb0vq9f" don't contain the actual
issue ID, causing gt done to incorrectly parse "furiosa-mkb0vq9f" as the
issue. This broke integration branch auto-detection since the wrong issue
was used for parent epic lookup.

Changes:
- After parsing branch name, check the agent's hook_bead field which
  contains the actual issue ID (e.g., "gt-845.1")
- Fix parseBranchName to not extract fake issue IDs from modern polecat branches
- Fix detectIntegrationBranch to traverse full parent chain (molecule → bug → epic)
- Include issue ID in polecat branch names when HookBead is set

Added tests covering:
- Agent hook returns correct issue ID
- Modern polecat branch format parsing
- Integration branch detection through parent chain

Fixes #411

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Julian Knutsen
2026-01-16 19:40:18 +00:00
committed by GitHub
parent 8332a719ab
commit e5aea04fa1
5 changed files with 220 additions and 51 deletions

View File

@@ -249,9 +249,18 @@ func (m *Manager) AddWithOptions(name string, opts AddOptions) (*Polecat, error)
polecatDir := m.polecatDir(name)
clonePath := filepath.Join(polecatDir, m.rig.Name)
// Unique branch per run - prevents drift from stale branches
// Use base36 encoding for shorter branch names (8 chars vs 13 digits)
branchName := fmt.Sprintf("polecat/%s-%s", name, strconv.FormatInt(time.Now().UnixMilli(), 36))
// Branch naming: include issue ID when available for better traceability.
// Format: polecat/<worker>/<issue>@<timestamp> when HookBead is set
// The @timestamp suffix ensures uniqueness if the same issue is re-slung.
// parseBranchName strips the @suffix to extract the issue ID.
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 36)
var branchName string
if opts.HookBead != "" {
branchName = fmt.Sprintf("polecat/%s/%s@%s", name, opts.HookBead, timestamp)
} else {
// Fallback to timestamp format when no issue is known at spawn time
branchName = fmt.Sprintf("polecat/%s-%s", name, timestamp)
}
// Create polecat directory (polecats/<name>/)
if err := os.MkdirAll(polecatDir, 0755); err != nil {
@@ -574,8 +583,14 @@ func (m *Manager) RepairWorktreeWithOptions(name string, force bool, opts AddOpt
// Create fresh worktree with unique branch name, starting from origin's default branch
// Old branches are left behind - they're ephemeral (never pushed to origin)
// and will be cleaned up by garbage collection
// Use base36 encoding for shorter branch names (8 chars vs 13 digits)
branchName := fmt.Sprintf("polecat/%s-%s", name, strconv.FormatInt(time.Now().UnixMilli(), 36))
// Branch naming: include issue ID when available for better traceability.
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 36)
var branchName string
if opts.HookBead != "" {
branchName = fmt.Sprintf("polecat/%s/%s@%s", name, opts.HookBead, timestamp)
} else {
branchName = fmt.Sprintf("polecat/%s-%s", name, timestamp)
}
if err := repoGit.WorktreeAddFromRef(newClonePath, branchName, startPoint); err != nil {
return nil, fmt.Errorf("creating fresh worktree from %s: %w", startPoint, err)
}