From 6b07707a4cc99c7e127a2bf6a0ca790fa033b5f5 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Sat, 17 Jan 2026 06:44:22 +0000
Subject: feat(frontend): Add UI for phase transition confirmation requests

When phase_guard is enabled and a supervisor tries to advance the contract
phase, users now see a confirmation modal with:
- Current and proposed next phase visualization
- Phase deliverables checklist (if available)
- Summary of the phase work
- Options to "Approve & Advance" or "Request Changes" with feedback

Components added:
- PhaseConfirmationModal: Full modal dialog for phase confirmations
- PhaseConfirmationInline: Inline variant for task output view
- PhaseConfirmationNotification: Global notification wrapper
- PhaseConfirmationToast: Alternative toast-style notification

Integration:
- Added phase_confirmation message type to TaskOutput renderer
- Extended PendingQuestion API type with phase confirmation data
- Integrated notification into main app layout

The UI uses the existing supervisor question infrastructure (polling via
/api/v1/mesh/questions) and responds with APPROVE or CHANGES_REQUESTED
prefixed feedback.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---
 makima/frontend/src/components/mesh/TaskOutput.tsx | 66 ++++++++++++++++++++++
 1 file changed, 66 insertions(+)

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

diff --git a/makima/frontend/src/components/mesh/TaskOutput.tsx b/makima/frontend/src/components/mesh/TaskOutput.tsx
index d53429d..7140c8a 100644
--- a/makima/frontend/src/components/mesh/TaskOutput.tsx
+++ b/makima/frontend/src/components/mesh/TaskOutput.tsx
@@ -2,6 +2,11 @@ import { useRef, useEffect, useState, useCallback } from "react";
 import { SimpleMarkdown } from "../SimpleMarkdown";
 import type { TaskOutputEvent } from "../../hooks/useTaskSubscription";
 import { sendTaskMessage } from "../../lib/api";
+import {
+  PhaseConfirmationInline,
+  type PhaseConfirmationData,
+} from "../contracts/PhaseConfirmationModal";
+import type { ContractPhase } from "../../lib/api";
 
 interface TaskOutputProps {
   /** Array of parsed output events from the backend */
@@ -312,6 +317,15 @@ function OutputEntryRenderer({ entry, pendingQuestionIds, onAnswerQuestion }: Ou
         />
       );
 
+    case "phase_confirmation":
+      return (
+        <PhaseConfirmationEntry
+          entry={entry}
+          pendingQuestionIds={pendingQuestionIds}
+          onAnswerQuestion={onAnswerQuestion}
+        />
+      );
+
     default:
       return null;
   }
@@ -520,3 +534,55 @@ function AuthRequiredEntry({ entry }: { entry: TaskOutputEvent }) {
     </div>
   );
 }
+
+/** Entry for phase transition confirmations */
+function PhaseConfirmationEntry({
+  entry,
+  pendingQuestionIds,
+  onAnswerQuestion,
+}: {
+  entry: TaskOutputEvent;
+  pendingQuestionIds?: Set<string>;
+  onAnswerQuestion?: (questionId: string, response: string) => Promise<void>;
+}) {
+  const questionId = entry.toolInput?.question_id as string;
+  const currentPhase = entry.toolInput?.current_phase as ContractPhase;
+  const nextPhase = entry.toolInput?.next_phase as ContractPhase;
+  const contractId = entry.toolInput?.contract_id as string;
+  const contractName = entry.toolInput?.contract_name as string | undefined;
+  const summary = entry.toolInput?.summary as string | undefined;
+  const deliverables = entry.toolInput?.deliverables as
+    | Array<{ name: string; completed: boolean }>
+    | undefined;
+
+  const isPending = pendingQuestionIds?.has(questionId) ?? false;
+
+  const data: PhaseConfirmationData = {
+    questionId,
+    contractId,
+    contractName,
+    currentPhase,
+    nextPhase,
+    summary,
+    deliverables,
+  };
+
+  const handleApprove = async (qId: string) => {
+    if (!onAnswerQuestion) return;
+    await onAnswerQuestion(qId, "APPROVE");
+  };
+
+  const handleRequestChanges = async (qId: string, feedback: string) => {
+    if (!onAnswerQuestion) return;
+    await onAnswerQuestion(qId, `CHANGES_REQUESTED: ${feedback}`);
+  };
+
+  return (
+    <PhaseConfirmationInline
+      data={data}
+      isPending={isPending}
+      onApprove={handleApprove}
+      onRequestChanges={handleRequestChanges}
+    />
+  );
+}
-- 
cgit v1.2.3

