From 900472091e4d9b4000508b0d266d786ef41107bd Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Sat, 17 Jan 2026 19:47:35 +0000
Subject: Add phase guard toggle for contract phase confirmation (#2)

* Add phase_guard field to Contract model and database

This adds a new boolean field to control whether the supervisor should
wait for user confirmation before progressing to the next phase. When
enabled, users can review and potentially amend phase outputs (like
plans, requirements docs) before the contract continues.

Changes:
- Add migration for phase_guard column (defaults to false)
- Add phase_guard to Contract, CreateContractRequest, and
  UpdateContractRequest structs
- Update create_contract_for_owner and update_contract_for_owner
  repository functions to handle phase_guard
- Update all CreateContractRequest instantiations with phase_guard field

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

* feat: Add phase_guard for contract phase transitions

Implement phase_guard logic in the advance_phase tool. When a contract
has phase_guard enabled, the phase transition now:

1. Asks for user confirmation before advancing
2. Allows users to request changes to phase deliverables
3. Passes feedback to the task without advancing if changes requested

Changes:
- Add phase_guard field to Contract model and CreateContractRequest
- Add PhaseTransitionRequest, PhaseFileInfo, PhaseTaskInfo structs
- Update ChangePhaseRequest with confirmed and feedback fields
- Update ContractToolRequest::AdvancePhase with confirmed/feedback
- Modify advance_phase handling in contract_chat.rs with phase_guard logic
- Update change_phase endpoint in contracts.rs with phase_guard support
- Add database migration for phase_guard column

When phase_guard=false: Phase advances immediately (current behavior)
When phase_guard=true: Returns pending_confirmation status with deliverables
If user provides feedback: Returns feedback to task, doesn't advance

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

* 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>

* feat(frontend): Add Phase Guard toggle to AutopilotPanel

Added the phase_guard toggle to the AutopilotPanel component, which allows
users to enable/disable requiring confirmation before phase transitions.

Changes:
- Added phaseGuard and autonomousLoop fields to Contract interface in api.ts
- Added phaseGuard field to UpdateContractRequest in api.ts
- Added Phase Guard toggle UI in AutopilotPanel with similar styling to master
- Toggle shows an 'active' badge when enabled
- Connected toggle to contract update API

The toggle appears below the autopilot control buttons and allows users to
require confirmation before the supervisor advances to the next phase.

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
---
 .../src/components/contracts/AutopilotPanel.tsx    | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)

(limited to 'makima/frontend/src/components/contracts/AutopilotPanel.tsx')

diff --git a/makima/frontend/src/components/contracts/AutopilotPanel.tsx b/makima/frontend/src/components/contracts/AutopilotPanel.tsx
index a8a8e2e..cf42e44 100644
--- a/makima/frontend/src/components/contracts/AutopilotPanel.tsx
+++ b/makima/frontend/src/components/contracts/AutopilotPanel.tsx
@@ -5,6 +5,7 @@ import {
   startSupervisor,
   stopSupervisor,
   resumeSupervisor,
+  updateContract,
   type SupervisorStatus,
 } from "../../lib/api";
 
@@ -111,6 +112,23 @@ export function AutopilotPanel({ contract, onUpdate }: AutopilotPanelProps) {
     }
   }, [contract.id, supervisorStatus.supervisorTaskId, onUpdate]);
 
+  const handlePhaseGuardChange = useCallback(async (enabled: boolean) => {
+    setLoading(true);
+    setError(null);
+
+    try {
+      await updateContract(contract.id, {
+        phaseGuard: enabled,
+        version: contract.version,
+      });
+      onUpdate();
+    } catch (e) {
+      setError(e instanceof Error ? e.message : "Failed to update phase guard setting");
+    } finally {
+      setLoading(false);
+    }
+  }, [contract.id, contract.version, onUpdate]);
+
   // Don't show panel for task-type contracts (they don't have supervisors)
   if (contract.contractType === "task") {
     return null;
@@ -185,6 +203,38 @@ export function AutopilotPanel({ contract, onUpdate }: AutopilotPanelProps) {
         )}
       </div>
 
+      {/* Phase Guard Toggle */}
+      <div className="pt-3 border-t border-dashed border-[rgba(117,170,252,0.2)]">
+        <label className="flex items-start gap-3 cursor-pointer group">
+          <div className="relative mt-0.5">
+            <input
+              type="checkbox"
+              checked={contract.phaseGuard ?? false}
+              onChange={(e) => handlePhaseGuardChange(e.target.checked)}
+              disabled={loading}
+              className="sr-only peer"
+            />
+            <div className="w-9 h-5 bg-[rgba(117,170,252,0.1)] border border-[rgba(117,170,252,0.3)] rounded-full peer-checked:bg-[rgba(117,170,252,0.3)] transition-colors peer-disabled:opacity-50" />
+            <div className="absolute left-0.5 top-0.5 w-4 h-4 bg-[#555] rounded-full transition-transform peer-checked:translate-x-4 peer-checked:bg-[#75aafc] peer-disabled:opacity-50" />
+          </div>
+          <div className="flex-1">
+            <div className="flex items-center gap-2">
+              <span className="font-mono text-sm text-[#dbe7ff] group-hover:text-white transition-colors">
+                Phase Guard
+              </span>
+              {contract.phaseGuard && (
+                <span className="px-1.5 py-0.5 text-[9px] font-mono uppercase bg-yellow-500/20 text-yellow-400 border border-yellow-400/30 rounded">
+                  active
+                </span>
+              )}
+            </div>
+            <div className="font-mono text-xs text-[#555] mt-0.5">
+              Ask for confirmation before advancing to the next phase
+            </div>
+          </div>
+        </label>
+      </div>
+
       {/* Show running indicator when active */}
       {supervisorStatus.status === "running" && (
         <div className="flex items-center gap-2 pt-2 border-t border-dashed border-[rgba(117,170,252,0.2)]">
-- 
cgit v1.2.3

