From 7155e6cd7ddf25a5a4d4f6d6abecd49f0cf519dc Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Tue, 20 Jan 2026 23:20:32 +0000
Subject: Add non-blocking persistent contract completion questions (#14)

* [WIP] Heartbeat checkpoint - 2026-01-20 22:40:37 UTC

* Task completion checkpoint
---
 makima/frontend/src/routes/mesh.tsx | 63 +++++++++++++++++++++++++------------
 1 file changed, 43 insertions(+), 20 deletions(-)

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

diff --git a/makima/frontend/src/routes/mesh.tsx b/makima/frontend/src/routes/mesh.tsx
index a8d3574..142cc54 100644
--- a/makima/frontend/src/routes/mesh.tsx
+++ b/makima/frontend/src/routes/mesh.tsx
@@ -5,6 +5,7 @@ import { TaskList } from "../components/mesh/TaskList";
 import { TaskDetail } from "../components/mesh/TaskDetail";
 import { TaskOutput } from "../components/mesh/TaskOutput";
 import { UnifiedMeshChatInput } from "../components/mesh/UnifiedMeshChatInput";
+import { ContractCompleteQuestion } from "../components/mesh/ContractCompleteQuestion";
 import { useTasks } from "../hooks/useTasks";
 import { useTaskSubscription, type TaskUpdateEvent, type TaskOutputEvent } from "../hooks/useTaskSubscription";
 import type { TaskWithSubtasks, MeshChatContext, ContractSummary, ContractWithRelations, DaemonDirectory, TaskSummary } from "../lib/api";
@@ -89,6 +90,14 @@ export default function MeshPage() {
     [pendingQuestions]
   );
 
+  // Filter contract_complete questions for the current task
+  const contractCompleteQuestionsForTask = useMemo(
+    () => pendingQuestions.filter(
+      (q) => q.questionType === "contract_complete" && q.taskId === id
+    ),
+    [pendingQuestions, id]
+  );
+
   // Handler for answering supervisor questions
   const handleAnswerQuestion = useCallback(async (questionId: string, response: string) => {
     await submitAnswer(questionId, response);
@@ -751,27 +760,41 @@ export default function MeshPage() {
                 {/* Output panel */}
                 {(viewMode === "split" || viewMode === "output") && (
                   <div
-                    className="panel min-h-0 overflow-hidden flex-1"
+                    className="panel min-h-0 overflow-hidden flex-1 flex flex-col"
                   >
-                    <TaskOutput
-                      entries={taskOutputEntries}
-                      isStreaming={isStreaming || taskDetail.status === "running"}
-                      viewingSubtaskName={viewingSubtaskName}
-                      onClearSubtaskView={viewingSubtaskId ? () => {
-                        setViewingSubtaskId(null);
-                        setViewingSubtaskName(null);
-                      } : undefined}
-                      onClear={() => {
-                        setTaskOutputEntries([]);
-                        if (activeOutputTaskId) {
-                          clearPersistedOutput(activeOutputTaskId);
-                        }
-                      }}
-                      taskId={activeOutputTaskId}
-                      onUserInput={handleUserInput}
-                      pendingQuestionIds={pendingQuestionIds}
-                      onAnswerQuestion={handleAnswerQuestion}
-                    />
+                    {/* Contract complete questions - shown prominently at top */}
+                    {contractCompleteQuestionsForTask.length > 0 && (
+                      <div className="shrink-0 px-3 pt-3">
+                        {contractCompleteQuestionsForTask.map((question) => (
+                          <ContractCompleteQuestion
+                            key={question.questionId}
+                            question={question}
+                            onAnswer={handleAnswerQuestion}
+                          />
+                        ))}
+                      </div>
+                    )}
+                    <div className="flex-1 min-h-0 overflow-hidden">
+                      <TaskOutput
+                        entries={taskOutputEntries}
+                        isStreaming={isStreaming || taskDetail.status === "running"}
+                        viewingSubtaskName={viewingSubtaskName}
+                        onClearSubtaskView={viewingSubtaskId ? () => {
+                          setViewingSubtaskId(null);
+                          setViewingSubtaskName(null);
+                        } : undefined}
+                        onClear={() => {
+                          setTaskOutputEntries([]);
+                          if (activeOutputTaskId) {
+                            clearPersistedOutput(activeOutputTaskId);
+                          }
+                        }}
+                        taskId={activeOutputTaskId}
+                        onUserInput={handleUserInput}
+                        pendingQuestionIds={pendingQuestionIds}
+                        onAnswerQuestion={handleAnswerQuestion}
+                      />
+                    </div>
                   </div>
                 )}
               </div>
-- 
cgit v1.2.3

