From e0da93a20a965125ba4cbb46e3e0e179f06c2a08 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Sun, 18 Jan 2026 18:02:08 +0000
Subject: Improve Mesh Tab: Organize by contract with status filter (#5)

* Add status filter toggle to Mesh Tab TaskList component

Add a filter toggle at the top of the TaskList that allows filtering by
contract status (All, Active, Completed, Archive) with Active as the default.

Changes:
- Backend: Add contract_status field to TaskSummary struct in models.rs
- Backend: Update all SQL queries returning TaskSummary to include
  c.status as contract_status from the contracts table join
- Frontend: Add contractStatus to TaskSummary TypeScript type
- Frontend: Add useState for statusFilter with 'active' as default
- Frontend: Add filter button group in header area next to '+ New Task'
- Frontend: Update groupedTasks useMemo to filter based on contract status
- Frontend: Update empty state message to reflect current filter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Task completion checkpoint

* feat(mesh): show all contract tasks for supervisor tasks

When viewing a supervisor task (task.isSupervisor === true), the
TaskDetail component now shows all tasks in the contract instead
of showing the subtasks tree.

Changes:
- Add contractTasks prop to TaskDetailProps for passing contract tasks
- Add displayTasks computed value that uses contractTasks for supervisors
- Change section header from "Subtasks" to "Contract Tasks" for supervisors
- Hide the "+ Add Subtask" button for supervisor tasks
- Update empty state message for supervisors
- Fetch contract tasks in mesh.tsx when viewing a supervisor task
- Filter out the supervisor itself from the contract tasks list

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Task completion checkpoint

* Task completion checkpoint

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
---
 makima/frontend/src/components/mesh/TaskDetail.tsx | 43 ++++++++++++++--------
 1 file changed, 27 insertions(+), 16 deletions(-)

(limited to 'makima/frontend/src/components/mesh/TaskDetail.tsx')

diff --git a/makima/frontend/src/components/mesh/TaskDetail.tsx b/makima/frontend/src/components/mesh/TaskDetail.tsx
index 8e853e7..efe26a8 100644
--- a/makima/frontend/src/components/mesh/TaskDetail.tsx
+++ b/makima/frontend/src/components/mesh/TaskDetail.tsx
@@ -32,6 +32,8 @@ interface TaskDetailProps {
   onCreatePR?: (title: string, body: string, draft: boolean) => Promise<void>;
   onAutoMerge?: () => Promise<void>;
   fetchSubtasks?: (taskId: string) => Promise<TaskSummary[]>;
+  /** For supervisor tasks: all tasks in the contract (excluding the supervisor itself) */
+  contractTasks?: TaskSummary[];
 }
 
 function formatDate(dateStr: string): string {
@@ -114,6 +116,7 @@ export function TaskDetail({
   onCreatePR,
   onAutoMerge,
   fetchSubtasks,
+  contractTasks,
 }: TaskDetailProps) {
   const [isEditing, setIsEditing] = useState(false);
   const [editName, setEditName] = useState(task.name);
@@ -149,10 +152,18 @@ export function TaskDetail({
   // Show continue for supervisors (always) or terminal states for other tasks
   const canContinue = isSupervisor || isTaskTerminal;
 
-  // Calculate subtask statistics
+  // Determine which tasks to show: for supervisors, show contractTasks; for regular tasks, show subtasks
+  const displayTasks = useMemo(() => {
+    if (isSupervisor && contractTasks) {
+      return contractTasks;
+    }
+    return task.subtasks;
+  }, [isSupervisor, contractTasks, task.subtasks]);
+
+  // Calculate task statistics for progress bar
   const subtaskStats = useMemo(
-    () => calculateTreeStats(task.subtasks),
-    [task.subtasks]
+    () => calculateTreeStats(displayTasks),
+    [displayTasks]
   );
 
   // Check if task can create PR
@@ -645,14 +656,14 @@ export function TaskDetail({
           </div>
         )}
 
-        {/* Subtasks */}
+        {/* Subtasks / Contract Tasks (for supervisors) */}
         <div className="space-y-2">
           <div className="flex items-center justify-between">
             <div className="flex items-center gap-3">
               <div className="font-mono text-xs text-[#9bc3ff] tracking-wide uppercase">
-                Subtasks ({task.subtasks.length})
+                {isSupervisor ? `Contract Tasks (${displayTasks.length})` : `Subtasks (${displayTasks.length})`}
               </div>
-              {task.subtasks.length > 0 && (
+              {displayTasks.length > 0 && (
                 <button
                   onClick={() => setUseTreeView(!useTreeView)}
                   className="font-mono text-[9px] text-[#555] hover:text-[#75aafc]"
@@ -661,41 +672,41 @@ export function TaskDetail({
                 </button>
               )}
             </div>
-            {/* Disable adding subtasks at max depth (2 = sub-subtask, cannot have children) */}
-            {task.depth < 2 ? (
+            {/* Disable adding subtasks for supervisors and at max depth (2 = sub-subtask, cannot have children) */}
+            {!isSupervisor && task.depth < 2 ? (
               <button
                 onClick={onCreateSubtask}
                 className="px-2 py-1 font-mono text-[10px] text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors uppercase"
               >
                 + Add Subtask
               </button>
-            ) : (
+            ) : !isSupervisor ? (
               <span className="px-2 py-1 font-mono text-[10px] text-[#555] border border-[#333]" title="Maximum depth reached">
                 Max depth
               </span>
-            )}
+            ) : null}
           </div>
 
-          {/* Progress bar for subtasks */}
-          {task.subtasks.length > 0 && (
+          {/* Progress bar for tasks */}
+          {displayTasks.length > 0 && (
             <SubtaskProgressBar stats={subtaskStats} />
           )}
 
-          {task.subtasks.length === 0 ? (
+          {displayTasks.length === 0 ? (
             <div className="text-[#555] font-mono text-xs py-4 text-center">
-              No subtasks yet
+              {isSupervisor ? "No tasks in contract yet" : "No subtasks yet"}
             </div>
           ) : useTreeView ? (
             <div className="border border-[rgba(117,170,252,0.15)]">
               <SubtaskTree
-                subtasks={task.subtasks}
+                subtasks={displayTasks}
                 onSelect={onSelectSubtask}
                 fetchSubtasks={fetchSubtasks}
               />
             </div>
           ) : (
             <div className="divide-y divide-[rgba(117,170,252,0.15)] border border-[rgba(117,170,252,0.15)]">
-              {task.subtasks.map((subtask: TaskSummary) => {
+              {displayTasks.map((subtask: TaskSummary) => {
                 const isRunning = subtask.status === "running" || subtask.status === "initializing" || subtask.status === "starting";
                 const isViewingOutput = viewingSubtaskId === subtask.id;
                 const isExpanded = expandedSubtaskId === subtask.id;
-- 
cgit v1.2.3

