summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components/contracts/CommandModePanel.tsx
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-05-01 23:56:51 +0100
committerGitHub <noreply@github.com>2026-05-01 23:56:51 +0100
commite11759447b1ac00becfb1e979e488f7f9c9cf478 (patch)
treef8a58368de3f6dda3f2f5c1af34e869a0e714205 /makima/frontend/src/components/contracts/CommandModePanel.tsx
parent80085c7cfa9d679ed3e3fd54a7d55fa8ab1addef (diff)
downloadsoryu-e11759447b1ac00becfb1e979e488f7f9c9cf478.tar.gz
soryu-e11759447b1ac00becfb1e979e488f7f9c9cf478.zip
chore(cleanup): Phase 5 contracts removal + tmp directive + 30-day expiry + scroll fix (#118)
Sweeping cleanup across the surface and the wire. Net: -14k LOC of legacy contracts code, plus the tmp/scroll/UX fixes the user asked for. ## Sidebar/editor independent scroll Replace `height: calc(100vh - 80px)` (which assumed an 80px masthead and quietly clipped or pushed the whole page below the fold when the masthead was taller) with `h-screen + overflow-hidden` on the page root and proper `flex-1 min-h-0` sizing on `<main>`. Sidebar and editor pane now manage their own scroll independently; the page itself never scrolls. Same fix in /tmp/:taskId. ## tmp directive — real backing for orphans/ephemerals New migration `20260501100000_tmp_directive_and_clear_orphans.sql`: * Adds `directives.is_tmp` BOOLEAN NOT NULL DEFAULT false. * Partial unique index `(owner_id) WHERE is_tmp` — at most ONE tmp directive per owner. * Hard-deletes every existing orphan task (`directive_id IS NULL`). Per the user spec: "ALSO there are TOO MANY old tasks in tmp, we need to remove all of them as well." New repository helpers: * `get_or_create_tmp_directive(pool, owner_id) -> Directive` INSERT ON CONFLICT DO NOTHING + fallback SELECT, race-safe. * `list_all_tmp_directives` — drives the expiry sweep. * `delete_expired_tmp_tasks(tmp_directive_id) -> u64`. * `list_tmp_tasks_for_owner` (replaces `list_orphan_tasks_for_owner`). `mesh::create_task`: every top-level task must have a directive. If a caller doesn't supply `directive_id` and isn't a subtask, attach to the caller's tmp directive (auto-creating it on first use). `list_directives_for_owner` filters out `is_tmp=true` so the scratchpad directive doesn't pollute the contract list — surfaced via the sidebar's `tmp/` folder instead. ## 30-day expiry on tmp tasks New `phase_tmp_expiry` in the directive reconciler. Throttled to once per hour: enumerates every tmp directive, calls `delete_expired_tmp_tasks`, logs the count. The actual delete is `WHERE created_at < NOW() - INTERVAL '30 days'` and is fast on the existing index. Subtasks die via FK cascade. ## Phase 5 — contracts removed ### Frontend Deleted entire `/contracts` surface: * routes: `contracts.tsx`, `contract-file.tsx` * components/contracts: ContractList, ContractDetail, ContractCliInput, ContractContextMenu, CommandModePanel, PhaseBadge, PhaseHint, PhaseDeliverablesPanel, PhaseProgressBar, QuickActionButtons, RepositoryPanel, TaskDerivationPreview * (Kept `PhaseConfirmationModal` — used outside the contracts surface by `TaskOutput` and `PhaseConfirmationNotification`.) * Routes deregistered from `main.tsx`; nav entry removed from `NavStrip`. ### Backend handlers Deleted: `contracts.rs` (2.4k LOC), `contract_chat.rs` (3.2k LOC), `contract_daemon.rs` (~940 LOC), `contract_discuss.rs` (~590 LOC), `transcript_analysis.rs` (~690 LOC). All `/api/v1/contracts/*` routes deregistered. OpenAPI entries dropped. Module declarations removed from `server/handlers/mod.rs`. ### CLI Removed `makima contract` and `makima supervisor` subcommands. Deleted `daemon/cli/contract.rs` and `daemon/cli/supervisor.rs`. Bin dispatch trimmed (~377 LOC). ### Orchestrator Removed the contract-spawn path from `phase_execution` (`spawn_step_contract` and its caller). `directive_steps.contract_type` now logs a warning and falls through to standalone-task spawn. Column itself stays — old data still reads, just no longer triggers a contract+supervisor spawn. ### TUI `Action::PerformCreateContract` is now a no-op that surfaces a status message: "Contracts have been removed. Use directives instead." The TUI form is dead code pending a wider refresh. ## Out of scope (deliberately left) * Contracts DB tables (`contracts`, `contract_repositories`, `contract_chat_history`, `contract_events`, `contract_templates`) are retained for historical data + because some peripheral code still joins to them in TaskSummary queries. * `mesh_supervisor` handlers are retained — they aren't only used by contracts (some mesh-level supervisor behaviour persists), and the cross-cutting cleanup is bigger than this PR. * `directive_steps.contract_type` column itself isn't dropped; just no longer functional. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'makima/frontend/src/components/contracts/CommandModePanel.tsx')
-rw-r--r--makima/frontend/src/components/contracts/CommandModePanel.tsx272
1 files changed, 0 insertions, 272 deletions
diff --git a/makima/frontend/src/components/contracts/CommandModePanel.tsx b/makima/frontend/src/components/contracts/CommandModePanel.tsx
deleted file mode 100644
index b39b309..0000000
--- a/makima/frontend/src/components/contracts/CommandModePanel.tsx
+++ /dev/null
@@ -1,272 +0,0 @@
-import { useState, useCallback } from "react";
-import { useNavigate } from "react-router";
-import type { ContractWithRelations } from "../../lib/api";
-import {
- getSupervisorStatus,
- startSupervisor,
- stopSupervisor,
- resumeSupervisor,
- updateContract,
- type SupervisorStatus,
-} from "../../lib/api";
-
-interface CommandModePanelProps {
- contract: ContractWithRelations;
- onUpdate: () => void;
-}
-
-const statusConfig: Record<
- SupervisorStatus["status"],
- { label: string; color: string; bgColor: string }
-> = {
- not_configured: {
- label: "Not Configured",
- color: "text-[#555]",
- bgColor: "bg-[#555]/10",
- },
- pending: {
- label: "Ready",
- color: "text-yellow-400",
- bgColor: "bg-yellow-400/10",
- },
- starting: {
- label: "Starting...",
- color: "text-blue-400",
- bgColor: "bg-blue-400/10",
- },
- running: {
- label: "Running",
- color: "text-green-400",
- bgColor: "bg-green-400/10",
- },
- paused: {
- label: "Paused",
- color: "text-orange-400",
- bgColor: "bg-orange-400/10",
- },
- done: {
- label: "Completed",
- color: "text-blue-400",
- bgColor: "bg-blue-400/10",
- },
- failed: {
- label: "Failed",
- color: "text-red-400",
- bgColor: "bg-red-400/10",
- },
-};
-
-export function CommandModePanel({ contract, onUpdate }: CommandModePanelProps) {
- const navigate = useNavigate();
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState<string | null>(null);
-
- const supervisorStatus = getSupervisorStatus(contract);
-
- const handleGoToSupervisor = useCallback(() => {
- if (supervisorStatus.supervisorTaskId) {
- navigate(`/exec/${supervisorStatus.supervisorTaskId}`);
- }
- }, [supervisorStatus.supervisorTaskId, navigate]);
- const config = statusConfig[supervisorStatus.status];
-
- const handleStart = useCallback(async () => {
- if (!supervisorStatus.supervisorTaskId) return;
-
- setLoading(true);
- setError(null);
-
- try {
- await startSupervisor(supervisorStatus.supervisorTaskId);
- onUpdate();
- } catch (e) {
- setError(e instanceof Error ? e.message : "Failed to start command mode");
- } finally {
- setLoading(false);
- }
- }, [supervisorStatus.supervisorTaskId, onUpdate]);
-
- const handleStop = useCallback(async () => {
- if (!supervisorStatus.supervisorTaskId) return;
-
- setLoading(true);
- setError(null);
-
- try {
- await stopSupervisor(supervisorStatus.supervisorTaskId);
- onUpdate();
- } catch (e) {
- setError(e instanceof Error ? e.message : "Failed to stop command mode");
- } finally {
- setLoading(false);
- }
- }, [supervisorStatus.supervisorTaskId, onUpdate]);
-
- const handleResume = useCallback(async () => {
- setLoading(true);
- setError(null);
-
- try {
- await resumeSupervisor(contract.id, { resumeMode: "continue" });
- // After resuming, we need to start the task
- if (supervisorStatus.supervisorTaskId) {
- await startSupervisor(supervisorStatus.supervisorTaskId);
- }
- onUpdate();
- } catch (e) {
- setError(e instanceof Error ? e.message : "Failed to resume command mode");
- } finally {
- setLoading(false);
- }
- }, [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]);
-
- return (
- <div className="space-y-3">
- <div className="flex items-center justify-between">
- <h3 className="font-mono text-xs text-[#75aafc] uppercase">
- Command Mode
- </h3>
- <div className="flex items-center gap-2">
- {supervisorStatus.supervisorTaskId && (
- <button
- onClick={handleGoToSupervisor}
- className="px-2 py-1 font-mono text-xs text-[#9bc3ff] hover:text-[#dbe7ff] hover:bg-[rgba(117,170,252,0.1)] transition-colors flex items-center gap-1"
- >
- <span className="text-[#75aafc]">▶</span>
- Supervisor
- </button>
- )}
- <div
- className={`px-2 py-1 rounded font-mono text-xs ${config.color} ${config.bgColor}`}
- >
- {config.label}
- </div>
- </div>
- </div>
-
- <p className="font-mono text-xs text-[#555]">
- {supervisorStatus.status === "not_configured" ? (
- "This contract does not have a Command Mode supervisor configured."
- ) : supervisorStatus.status === "running" ? (
- "Command Mode is actively working on this contract, spawning tasks and managing progress."
- ) : supervisorStatus.status === "pending" ? (
- "Command Mode is ready to start. Click 'Enable' to begin autonomous work."
- ) : supervisorStatus.status === "paused" ? (
- "Command Mode is paused. Click 'Resume' to continue work."
- ) : supervisorStatus.status === "failed" ? (
- "Command Mode encountered an error. You can resume to retry."
- ) : supervisorStatus.status === "done" ? (
- "Command Mode has completed its work on this contract."
- ) : (
- "Command Mode is initializing..."
- )}
- </p>
-
- {error && (
- <div className="px-3 py-2 bg-red-500/10 border border-red-400/30 font-mono text-xs text-red-400">
- {error}
- </div>
- )}
-
- <div className="flex gap-2">
- {supervisorStatus.canStart && (
- <button
- onClick={handleStart}
- disabled={loading}
- className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-green-600/20 border border-green-400/50 hover:bg-green-600/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- >
- {loading ? "Starting..." : "Enable Command Mode"}
- </button>
- )}
-
- {supervisorStatus.canResume && (
- <button
- onClick={handleResume}
- disabled={loading}
- className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-blue-600/20 border border-blue-400/50 hover:bg-blue-600/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- >
- {loading ? "Resuming..." : "Resume Command Mode"}
- </button>
- )}
-
- {supervisorStatus.canStop && (
- <button
- onClick={handleStop}
- disabled={loading}
- className="px-4 py-2 font-mono text-xs text-[#dbe7ff] bg-orange-600/20 border border-orange-400/50 hover:bg-orange-600/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- >
- {loading ? "Stopping..." : "Pause Command Mode"}
- </button>
- )}
- </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)]">
- <div className="w-2 h-2 rounded-full bg-green-400 animate-pulse" />
- <span className="font-mono text-xs text-green-400">
- Command Mode is actively working
- </span>
- </div>
- )}
-
- {supervisorStatus.status === "starting" && (
- <div className="flex items-center gap-2 pt-2 border-t border-dashed border-[rgba(117,170,252,0.2)]">
- <div className="w-2 h-2 rounded-full bg-blue-400 animate-pulse" />
- <span className="font-mono text-xs text-blue-400">
- Initializing command mode...
- </span>
- </div>
- )}
- </div>
- );
-}