Redesign manager status overview around active tasks table and stale-active cleanup
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.pyrenders/statusand currently shows cards plus separateEscalation Signals,Needs Attention,Active tasks,Ready to pick up,Waiting on dependencies, andOther taskssections.tasks/tools/tasks_server.pyalso owns theActivitypage, soWaiting for mergeandRecently finishedshould be added there instead of overloading/status.scripts/task_manager_lib.pybuilds the heartbeat snapshot, including per-taskstatus,registered,worktree_path,dispatch_state,needs_attention,escalation_reasons,pr_number,pr_ci_state,pr_mergeable, anddisplay_group.- In the current classifier, any task with
registeredordispatch_stateis treated as active even if the task frontmatter is alreadycompleted. 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
/statusaround a single runtime-managedActive taskstable 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. ExtendActivitywithWaiting for mergeandRecently finishedlists 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
- Refine runtime semantics in
scripts/task_manager_lib.pyso 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. - Update
tasks/tools/tasks_server.pyto replace the separateEscalation SignalsandNeeds Attentionsections with a singleActive taskstable for runtime-managed tasks, keeping summary cards only as top-level rollups. - 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.
- In the manager view only, make task links prefer the worktree task file when
worktree_path + task_pathexists; otherwise fall back cleanly to the main task file. - Extend the
Activitypage with lists forWaiting for mergeandRecently finished, using heartbeat-derived task state so operators can see what is done-but-not-merged and what just completed. - Extend tests in
tests/core/test_tasks_server.pyandtests/scripts/test_task_manager_scripts.pyto 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. - Run browser QA against
/statusandActivityto confirm the new overview is faster to scan and that the common operator actions are obvious.
Implementation Progress
Changes made
-
Stale-active cleanup (
scripts/task_manager_lib.py): -classify_tasks()now gatesactive_tasksonnot is_completed(task.status), preventing completed tasks with stale register/dispatch entries from appearing active. - Addedpr_statefield toTaskInfodataclass 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()includespr_statein per-task data. -
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()mergesactive_tasksandneeds_attentioninto one deduped table; Ready/Blocked/Other remain as simple lists. -
Worktree-preferring links (
tasks/tools/tasks_server.py): -_task_browse_href()acceptsprefer_worktree=True(used in the active table) to check if the worktree task file exists on disk and link to it via/browse/. -
Activity page (
tasks/tools/tasks_server.py): - New/activityroute 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