Skip to content

fix(taskctl): Pulse not detecting commits in worktrees causing adversarial never to spawn #382

@randomm

Description

@randomm

Critical Bug

Pulse fails to detect commits in worktrees, causing adversarial-developer to never spawn. Pipeline hangs in "reviewing" forever with infinite developer respawns.

Correct Workflow (Verified by Code Analysis)

Intended flow:

  1. Developer writes code → commits in worktree (git add -A && git commit)
  2. Developer session ends → Pulse heartbeat detects this → Task transitions to "reviewing"
  3. Pulse schedules adversarial → checks hasCommittedChanges() → If true, spawn adversarial
  4. Adversarial reviews committed work → Records verdict via taskctl verdict
  5. If APPROVED → @ops commits and pushes
  6. If ISSUES_FOUND → Respawn developer for fixes

Evidence from code:

  • developer-pipeline.txt:17 - ✅ git add + git commit in your worktree
  • developer-pipeline.txt:51 - When all checks pass: commit your changes
  • pulse-utils.ts:53 - git diff ${base_commit}..HEAD --stat (checks COMMITTED changes)
  • pulse-scheduler.ts:349 - hasChanges = await hasCommittedChanges(...) (must be true to spawn adversarial)

Bug: hasCommittedChanges Not Detecting Commits

Symptoms:

  • Developer commits work (verified by git log in worktree)
  • Pulse reports: "No committed changes found. Developer wrote code but did not commit."
  • Adversarial never spawns → Pipeline stuck forever

Root Cause (Per Investigation):

spawnDeveloper in pulse-scheduler.ts:177 calculates base_commit incorrectly:

const res = await $`git merge-base ${base} HEAD`.cwd(worktreeInfo.directory)

Problem: HEAD in a worktree is relative to that worktree's current state, which may not align with how hasCommittedChanges checks for commits.

Evidence:

  • Worktree has commit 9c13ebf21 (verified by git log)
  • hasCommittedChanges returns false despite commits existing
  • Base commit calculation is wrong for worktrees

Fix Required

  1. Base commit calculation: Use worktree branch name instead of HEAD for merge-base:

    const res = await $`git merge-base ${base} ${worktreeInfo.branch}`.cwd(worktreeInfo.directory)
  2. Alternative: Check for uncommitted changes instead:

    • git diff --cached for staged changes
    • git diff HEAD for all changes (committed + staged)
    • git log -1 to check if any commits exist since branch creation

Files Affected

  • packages/opencode/src/tasks/pulse-utils.ts - hasCommittedChanges (line 43-61)
  • packages/opencode/src/tasks/pulse-scheduler.ts - spawnDeveloper base_commit calculation (line 174-201)

Quality Gates

  • Test: hasCommittedChanges returns true for committed work in worktree
  • Test: Adversarial spawns after developer commits
  • End-to-end: Run taskctl pipeline and verify adversarial spawns

Acceptance Criteria

  • Pulse detects commits correctly in worktrees
  • Adversarial spawns after developer commits
  • No false "No committed changes found" messages
  • Pipeline progresses: developing → reviewing → adversarial-running → done

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions