From b141fca0c0604bdeba9fa563a8049cf29cc03bcf Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Wed, 28 Jan 2026 03:51:07 +0000
Subject: Fix starting phase dropdown to show correct phase names from
 templates (#42)

* Add comprehensive Red Team system specification

Defines the adversarial review feature for contracts that monitors work tasks
in real-time to catch quality issues, plan deviations, and standards violations.

Key components specified:
- Contract configuration (red_team_enabled, red_team_prompt)
- Red team task lifecycle and spawning logic
- makima red-team notify CLI command for supervisor alerts
- Task output subscription for real-time monitoring
- Database schema changes (contracts, tasks, notifications table)
- API endpoints for notification and status
- System prompt template for red team behavior
- Security considerations and access control

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

* Task completion checkpoint

* Task completion checkpoint

* Task completion checkpoint

* Fix starting phase dropdown to show correct phase names from templates

Add phaseNames map to ContractTypeTemplate to preserve display names
from custom templates loaded from localStorage. The dropdown now uses
the template's phase name (e.g., 'Design & Architecture') instead of
naive capitalization of the phase ID. Falls back to capitalization for
built-in templates that don't provide phaseNames.

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
---
 makima/frontend/src/routes/contracts.tsx | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

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

diff --git a/makima/frontend/src/routes/contracts.tsx b/makima/frontend/src/routes/contracts.tsx
index aa62bd9..9891f29 100644
--- a/makima/frontend/src/routes/contracts.tsx
+++ b/makima/frontend/src/routes/contracts.tsx
@@ -108,11 +108,12 @@ function ContractsPageContent() {
             // Convert user templates to ContractTypeTemplate format, excluding built-ins
             return templates
               .filter((t: { isBuiltIn?: boolean }) => !t.isBuiltIn)
-              .map((t: { id: string; name: string; description: string; phases: { id: string }[] }) => ({
+              .map((t: { id: string; name: string; description: string; phases: { id: string; name: string }[] }) => ({
                 id: t.id,
                 name: t.name,
                 description: t.description,
                 phases: t.phases.map((p: { id: string }) => p.id) as ContractPhase[],
+                phaseNames: Object.fromEntries(t.phases.map((p: { id: string; name: string }) => [p.id, p.name])),
                 defaultPhase: (t.phases[0]?.id || "execute") as ContractPhase,
                 isBuiltin: false,
               }));
@@ -652,11 +653,17 @@ function ContractsPageContent() {
                     onChange={(e) => setInitialPhase(e.target.value as ContractPhase)}
                     className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
                   >
-                    {(contractTypes.find((t) => t.id === contractType)?.phases || []).map((phase) => (
-                      <option key={phase} value={phase}>
-                        {phase.charAt(0).toUpperCase() + phase.slice(1)}
-                      </option>
-                    ))}
+                    {(() => {
+                      const template = contractTypes.find((t) => t.id === contractType);
+                      return (template?.phases || []).map((phase) => {
+                        const displayName = template?.phaseNames?.[phase] || (phase.charAt(0).toUpperCase() + phase.slice(1));
+                        return (
+                          <option key={phase} value={phase}>
+                            {displayName}
+                          </option>
+                        );
+                      });
+                    })()}
                   </select>
                   <p className="mt-1 font-mono text-xs text-[#8b949e]">
                     {contractType === "simple"
-- 
cgit v1.2.3


From 45a433c0eb63cae1322203ee14292f1c427a09c9 Mon Sep 17 00:00:00 2001
From: soryu <soryu@soryu.co>
Date: Thu, 29 Jan 2026 01:26:17 +0000
Subject: feat: Add Red Team UI to makima/frontend contract creation (#45)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- Add redTeamEnabled and redTeamPrompt state to contracts page
- Add "Enable Red Team Monitoring" checkbox with description
- Add conditional "Custom Review Criteria" textarea when enabled
- Include redTeamEnabled/redTeamPrompt in CreateContractRequest
- Reset red team fields when canceling contract creation
- Add redTeamEnabled to ContractSummary and Contract types
- Add redTeamEnabled/redTeamPrompt to CreateContractRequest type
- Add Red Team badge (🔍) to ContractList for enabled contracts

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
---
 .../src/components/contracts/ContractList.tsx      |  5 ++
 makima/frontend/src/lib/api.ts                     | 10 ++++
 makima/frontend/src/routes/contracts.tsx           | 59 ++++++++++++++++++++++
 3 files changed, 74 insertions(+)

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

diff --git a/makima/frontend/src/components/contracts/ContractList.tsx b/makima/frontend/src/components/contracts/ContractList.tsx
index 98f8ff6..532ab87 100644
--- a/makima/frontend/src/components/contracts/ContractList.tsx
+++ b/makima/frontend/src/components/contracts/ContractList.tsx
@@ -136,6 +136,11 @@ export function ContractList({
                         Local
                       </span>
                     )}
+                    {contract.redTeamEnabled && (
+                      <span className="px-1.5 py-0.5 font-mono text-[9px] uppercase text-cyan-400 border border-cyan-400/30 bg-cyan-400/10 shrink-0" title="Red Team monitoring enabled">
+                        🔍 Red Team
+                      </span>
+                    )}
                   </div>
                   <span
                     className={`text-[10px] font-mono uppercase shrink-0 ${
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts
index c9648a2..8838dbd 100644
--- a/makima/frontend/src/lib/api.ts
+++ b/makima/frontend/src/lib/api.ts
@@ -1682,6 +1682,8 @@ export interface ContractSummary {
   supervisorTaskId: string | null;
   /** When true, tasks won't auto-push or create PRs - use patch files instead */
   localOnly: boolean;
+  /** When true, a red team task monitors work output for quality */
+  redTeamEnabled: boolean;
   fileCount: number;
   taskCount: number;
   repositoryCount: number;
@@ -1706,6 +1708,10 @@ export interface Contract {
   phaseGuard: boolean;
   /** When true, tasks won't auto-push or create PRs - use patch files instead */
   localOnly: boolean;
+  /** When true, a red team task monitors work output for quality */
+  redTeamEnabled: boolean;
+  /** Custom criteria for the red team to evaluate */
+  redTeamPrompt: string | null;
   version: number;
   createdAt: string;
   updatedAt: string;
@@ -1741,6 +1747,10 @@ export interface CreateContractRequest {
   initialPhase?: ContractPhase;
   /** When true, tasks won't auto-push or create PRs - use patch files instead */
   localOnly?: boolean;
+  /** When true, spawn a red team task to monitor work output */
+  redTeamEnabled?: boolean;
+  /** Custom criteria for the red team to evaluate */
+  redTeamPrompt?: string;
 }
 
 export interface UpdateContractRequest {
diff --git a/makima/frontend/src/routes/contracts.tsx b/makima/frontend/src/routes/contracts.tsx
index 9891f29..bb66215 100644
--- a/makima/frontend/src/routes/contracts.tsx
+++ b/makima/frontend/src/routes/contracts.tsx
@@ -93,6 +93,8 @@ function ContractsPageContent() {
   const [contractTypes, setContractTypes] = useState<ContractTypeTemplate[]>([]);
   const [contractTypesLoading, setContractTypesLoading] = useState(false);
   const [localOnly, setLocalOnly] = useState(false);
+  const [redTeamEnabled, setRedTeamEnabled] = useState(false);
+  const [redTeamPrompt, setRedTeamPrompt] = useState("");
 
   // Fetch contract types when modal opens - merges built-in types with user templates
   useEffect(() => {
@@ -266,6 +268,8 @@ function ContractsPageContent() {
       contractType: contractType,
       initialPhase: initialPhase !== defaultPhaseForType ? initialPhase : undefined,
       localOnly: localOnly || undefined,
+      redTeamEnabled: redTeamEnabled || undefined,
+      redTeamPrompt: redTeamEnabled && redTeamPrompt.trim() ? redTeamPrompt.trim() : undefined,
     };
 
     try {
@@ -341,6 +345,8 @@ function ContractsPageContent() {
     setRepoUrl("");
     setRepoPath("");
     setLocalOnly(false);
+    setRedTeamEnabled(false);
+    setRedTeamPrompt("");
     setCreateError(null);
   }, []);
 
@@ -712,6 +718,59 @@ function ContractsPageContent() {
                   </p>
                 </div>
 
+                {/* Red Team Monitoring */}
+                <div className="border-t border-[rgba(117,170,252,0.2)] pt-4">
+                  <div className="flex items-center gap-3">
+                    <button
+                      type="button"
+                      onClick={() => setRedTeamEnabled(!redTeamEnabled)}
+                      className={`w-5 h-5 flex items-center justify-center border transition-colors ${
+                        redTeamEnabled
+                          ? "bg-[#0f3c78] border-[#75aafc] text-[#dbe7ff]"
+                          : "bg-[#0d1b2d] border-[#3f6fb3] text-transparent"
+                      }`}
+                    >
+                      {redTeamEnabled && (
+                        <svg
+                          xmlns="http://www.w3.org/2000/svg"
+                          width="12"
+                          height="12"
+                          viewBox="0 0 24 24"
+                          fill="none"
+                          stroke="currentColor"
+                          strokeWidth="3"
+                          strokeLinecap="round"
+                          strokeLinejoin="round"
+                        >
+                          <polyline points="20 6 9 17 4 12" />
+                        </svg>
+                      )}
+                    </button>
+                    <label
+                      className="font-mono text-sm text-[#dbe7ff] cursor-pointer select-none"
+                      onClick={() => setRedTeamEnabled(!redTeamEnabled)}
+                    >
+                      Enable Red Team Monitoring
+                    </label>
+                  </div>
+                  <p className="font-mono text-xs text-[#8b949e] pl-8">
+                    Spawns a parallel task to monitor work output for quality and compliance.
+                  </p>
+                  {redTeamEnabled && (
+                    <div className="mt-3 pl-8">
+                      <label className="block font-mono text-xs text-[#75aafc] uppercase mb-2">
+                        Custom Review Criteria (Optional)
+                      </label>
+                      <textarea
+                        value={redTeamPrompt}
+                        onChange={(e) => setRedTeamPrompt(e.target.value)}
+                        placeholder="e.g., 'Focus on security best practices' or 'Ensure all functions have tests'"
+                        className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] text-sm font-mono h-20 resize-none focus:border-[#75aafc] focus:outline-none"
+                      />
+                    </div>
+                  )}
+                </div>
+
                 {/* Repository Configuration */}
                 <div className="border-t border-[rgba(117,170,252,0.2)] pt-4">
                   <label className="block font-mono text-xs text-[#75aafc] uppercase mb-3">
-- 
cgit v1.2.3

