Dataface Tasks

Auto-reconcile merged PRs and orphaned in-progress tasks in task manager

IDINFRA_TOOLING-AUTO_RECONCILE_MERGED_PRS_AND_ORPHANED_IN_PROGRESS_TASKS_IN_TASK_MANAGER
Statuscompleted
Priorityp1
Milestonem1-ft-analytics-analyst-pilot
Ownersr-engineer-architect
Completed byrj
Completed2026-03-26

Problem

Extend task-manager cleanup so heartbeat/reconcile can automatically mark tasks completed when their PR is merged, demote orphaned in_progress tasks that have no register/worktree/live dispatch, and clear stale dispatch/register state when runtime has already handed off to PR lifecycle.

Context

  • scripts/task-manager-heartbeat already calls reconcile_register_orphans, detect_metadata_drift, and detect_dispatch_completed_unreconciled on every cycle.
  • Those hooks are not enough because only reconcile_register_orphans mutates state, and it only prunes a narrow case: terminal task plus missing worktree, or missing task plus missing worktree.
  • Current failures in live manager state:
  • merged PR task files can remain in_progress indefinitely
  • tasks can remain in_progress with no register entry, no worktree, and no live dispatch
  • stale dispatch status files linger after successful handoff to PR lifecycle
  • Relevant files:
  • scripts/task-manager-heartbeat
  • scripts/task_manager_lib.py
  • tests/scripts/test_task_manager_scripts.py
  • Constraint: automatic repair should only handle high-confidence cases and should not silently rewrite genuinely active tasks.

Possible Solutions

  • Option 1: Keep the current model and only improve attention text. Low risk, but it preserves manual cleanup and leaves heartbeat noisy. Rejected.
  • Option 2: Recommended Add explicit auto-reconcile helpers for high-confidence cases:
  • merged PR => mark task completed
  • in_progress with no register/worktree/live dispatch and no recent activity => demote out of active state
  • dispatch exited plus PR/terminal task => clear stale runtime residue This keeps repair logic narrow and testable while removing the most common false-stall cases.
  • Option 3: Build a broad self-healing loop that edits any suspicious task state heuristically. Faster cleanup, but too risky without stronger invariants.

Plan

  1. Add explicit reconciliation helpers in scripts/task_manager_lib.py for: - merged-PR completion - orphaned in_progress demotion - stale dispatch/register cleanup after handoff
  2. Call those helpers from scripts/task-manager-heartbeat after CI/merge refresh and before summary rendering.
  3. Add focused tests in tests/scripts/test_task_manager_scripts.py covering both repair and non-repair cases.
  4. Update task-manager docs/skill if the repair rules change operator expectations.

Implementation Progress

Added three auto-reconcile helpers to scripts/task_manager_lib.py:

  • auto_complete_merged_pr_tasks(tasks) — marks non-terminal tasks completed when task.pr_state == "MERGED". Updates only the status line in frontmatter via regex to preserve all other fields.

  • auto_demote_orphaned_in_progress(tasks, *, stale_seconds=DEFAULT_IDLE_SECONDS) — resets in_progress tasks to ready when: not registered, no existing worktree directory, dispatch not running, and last update > stale_seconds (1200s default). Covers tasks stuck in_progress with no active worker.

  • auto_clear_stale_dispatch_state(tasks) — removes .tasks/dispatch-{slug}.log and .tasks/dispatch-{slug}.status.json when dispatch exited cleanly and the task has a PR number (handed off to PR lifecycle) or is terminal.

Private helper _set_task_status(path, new_status) rewrites only the status: line in frontmatter using re.sub, preserving all other fields and body.

scripts/task-manager-heartbeat calls all three after refresh_pr_ci_for_tasks. If any task files were mutated, tasks are re-collected and PR CI re-fetched before classification.

Added 13 focused tests covering repair and non-repair cases for all three helpers. Updated one existing integration test that expected a stale orphaned in_progress task to produce a stuck_in_progress escalation — with auto-demotion active, that task is now reset to ready before classification runs.

QA Exploration

  • [x] QA exploration completed (or N/A for non-UI tasks)
  • N/A: backend task-manager behavior only

Review Feedback

  • [x] Review cleared