Dataface Tasks

Redesign manager status overview around active tasks table and stale-active cleanup

IDINFRA_TOOLING-REDESIGN_MANAGER_STATUS_OVERVIEW_AROUND_ACTIVE_TASKS_TABLE_AND_STALE_ACTIVE_CLEANUP
Statuscompleted
Priorityp2
Milestonem1-ft-analytics-analyst-pilot
Ownersr-engineer-architect
Completed bydave
Completed2026-03-24

Problem

The current /status page makes operators scan multiple regions to answer one basic question: what work is currently being managed, what is healthy, and what needs action right now. Escalation Signals, Needs Attention, and Active tasks split the same runtime state across separate sections, which forces manual correlation by slug and makes the page feel diagnostic rather than operational.

The Active tasks concept is also too loose. Today a task can remain "active" after it is effectively done because stale register entries or old dispatch state keep it in the runtime bucket. A task whose PR has merged should not keep looking live on the manager overview, and completed tasks with dead worktrees should be reconciled out of the active set automatically instead of lingering until manual cleanup.

The Activity page is also missing two operator-useful views: tasks that are effectively done but still waiting on merge, and tasks that were just finished recently enough to matter. Without those lists, operators bounce between manager status, PR state, and historical task pages to understand what just closed and what is merely waiting on GitHub.

Context

  • tasks/tools/tasks_server.py renders /status and currently shows cards plus separate Escalation Signals, Needs Attention, Active tasks, Ready to pick up, Waiting on dependencies, and Other tasks sections.
  • tasks/tools/tasks_server.py also owns the Activity page, so Waiting for merge and Recently finished should be added there instead of overloading /status.
  • scripts/task_manager_lib.py builds the heartbeat snapshot, including per-task status, registered, worktree_path, dispatch_state, needs_attention, escalation_reasons, pr_number, pr_ci_state, pr_mergeable, and display_group.
  • In the current classifier, any task with registered or dispatch_state is treated as active even if the task frontmatter is already completed. That can inflate Active counts and make the page look stale.
  • The manager view already has enough per-task data to collapse attention/escalation into row-level flags instead of a separate table.
  • In this manager view only, task links should prefer the worktree copy of the task file when that file exists. Other browse surfaces should keep their current main-repo behavior.
  • Cleanup should be driven by the existing heartbeat/reconciliation loop rather than a Git hook on pull. A git-triggered cleanup would be partial and user-dependent; the heartbeat is the system that already owns register, dispatch, and PR-state reconciliation.

Possible Solutions

  • A - Cosmetic table only: Replace the existing list sections with a table but keep current active classification and separate cleanup behavior. Fastest UI change, but it preserves the main source of operator confusion.
  • B - Git-triggered cleanup: Add cleanup logic to a pull/rebase-related Git wrapper so merged tasks get removed from active state when local branches change. This is fragile because it depends on local Git activity and misses the case where the manager keeps running without a user pulling.
  • C - Heartbeat-owned operator console (Recommended): Redesign /status around a single runtime-managed Active tasks table fed by heartbeat data. Show row-level state, links, worktree path, age, PR/CI info, and inline attention/escalation flags in one place. At the same time, tighten heartbeat reconciliation so tasks stop appearing active once they are completed and merged or otherwise clearly no longer live. Extend Activity with Waiting for merge and Recently finished lists derived from the same heartbeat/runtime view. This keeps UI and cleanup under the same ownership model and matches the manager's real decision loop.

Plan

  1. Refine runtime semantics in scripts/task_manager_lib.py so the snapshot distinguishes genuinely live managed tasks from stale registered/completed ones, and add reconciliation rules for when merged/completed work should be pruned from the active set.
  2. Update tasks/tools/tasks_server.py to replace the separate Escalation Signals and Needs Attention sections with a single Active tasks table for runtime-managed tasks, keeping summary cards only as top-level rollups.
  3. Design the table around operator actions: task link, status, dispatch/runtime state, worktree path or worktree link, age/staleness, PR/CI state, and inline flags for attention/escalation with readable detail text.
  4. In the manager view only, make task links prefer the worktree task file when worktree_path + task_path exists; otherwise fall back cleanly to the main task file.
  5. Extend the Activity page with lists for Waiting for merge and Recently finished, using heartbeat-derived task state so operators can see what is done-but-not-merged and what just completed.
  6. Extend tests in tests/core/test_tasks_server.py and tests/scripts/test_task_manager_scripts.py to cover the new table layout, inline flags, worktree-link preference, activity-list rendering, and stale-active cleanup cases such as completed + merged tasks dropping out of the active table.
  7. Run browser QA against /status and Activity to confirm the new overview is faster to scan and that the common operator actions are obvious.

Implementation Progress

Changes made

  1. Stale-active cleanup (scripts/task_manager_lib.py): - classify_tasks() now gates active_tasks on not is_completed(task.status), preventing completed tasks with stale register/dispatch entries from appearing active. - Added pr_state field to TaskInfo dataclass to track GitHub PR state (MERGED, OPEN, etc.). - refresh_pr_ci_for_tasks() now fetches PR state for all tasks with PR numbers (including completed ones) for activity page merge tracking. - snapshot_payload() includes pr_state in per-task data.

  2. Active tasks table (tasks/tools/tasks_server.py): - Removed the separate "Escalation Signals" table section from /status. - Replaced the flat <ul> task lists for Needs Attention and Active tasks with a single <table> with columns: Task, Status, Dispatch, Worktree, Age, PR/CI, Flags. - _active_task_table_row() renders each row with inline attention/escalation badges in the Flags column. - _render_task_sections() merges active_tasks and needs_attention into one deduped table; Ready/Blocked/Other remain as simple lists.

  3. Worktree-preferring links (tasks/tools/tasks_server.py): - _task_browse_href() accepts prefer_worktree=True (used in the active table) to check if the worktree task file exists on disk and link to it via /browse/.

  4. Activity page (tasks/tools/tasks_server.py): - New /activity route with heartbeat-refresh and auto-reload. - _render_activity_body() derives "Waiting for merge" (completed + PR not merged) and "Recently finished" (completed + merged or no PR, within 7 days) from snapshot tasks. - Nav bar updated to include Activity link.

Tests added

  • test_classify_tasks_completed_registered_not_active — completed+registered tasks excluded from active_tasks.
  • test_status_page_active_tasks_rendered_as_table — verifies <table> layout and absence of old "Escalation Signals" section.
  • test_escalation_signals_inline_in_active_table — escalation codes appear as inline flags.
  • test_status_page_worktree_link_preferred — worktree path appears in task links.
  • test_activity_page_waiting_for_merge_and_recently_finished — activity page shows both lists.

QA Exploration

  • Browser QA required: this changes the primary manager/operator UI and should be exercised against a realistic snapshot with healthy, stuck, merged, waiting-for-merge, recently finished, and stale-register cases.

  • [ ] QA exploration completed (or N/A for non-UI tasks)

Review Feedback

  • [ ] Review cleared