summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-01-26 23:49:40 +0000
committersoryu <soryu@soryu.co>2026-01-26 23:49:40 +0000
commit64cc98783d067625d633eea1142d114e324f76bb (patch)
tree5a172d365e17ba1c3fc6aa32bcb2237cf05b0d0f
parent63128cc45d3b677acadb30c37b79c0e13dc2cdc1 (diff)
downloadsoryu-64cc98783d067625d633eea1142d114e324f76bb.tar.gz
soryu-64cc98783d067625d633eea1142d114e324f76bb.zip
Use phase deliverables configured in contract types
-rw-r--r--makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx196
-rw-r--r--makima/frontend/src/lib/api.ts6
-rw-r--r--makima/frontend/src/types/templates.ts11
-rw-r--r--makima/frontend/tsconfig.tsbuildinfo2
4 files changed, 129 insertions, 86 deletions
diff --git a/makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx b/makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx
index da5025b..b2c2e58 100644
--- a/makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx
+++ b/makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx
@@ -1,82 +1,116 @@
import { useMemo } from "react";
-import type { ContractWithRelations, ContractPhase } from "../../lib/api";
+import type { ContractWithRelations, ContractPhase, ContractType } from "../../lib/api";
// Phase deliverables configuration (mirrors backend phase_guidance.rs)
-interface RecommendedFile {
- templateId: string;
+// IDs must match backend phase_guidance.rs exactly for mark_deliverable_complete
+interface PhaseDeliverable {
+ id: string; // Must match backend deliverable ID
name: string;
priority: "required" | "recommended" | "optional";
description: string;
}
-interface PhaseDeliverables {
- phase: ContractPhase;
- files: RecommendedFile[];
+interface PhaseConfig {
+ deliverables: PhaseDeliverable[];
requiresRepository: boolean;
requiresTasks: boolean;
guidance: string;
}
-const PHASE_DELIVERABLES: Record<ContractPhase, PhaseDeliverables> = {
- research: {
- phase: "research",
- files: [
- { templateId: "research-notes", name: "Research Notes", priority: "recommended", description: "Document findings and insights" },
- { templateId: "competitor-analysis", name: "Competitor Analysis", priority: "recommended", description: "Analyze competitors" },
- { templateId: "user-research", name: "User Research", priority: "optional", description: "User interviews and personas" },
- ],
- requiresRepository: false,
- requiresTasks: false,
- guidance: "Gather information and document findings before moving to Specify.",
- },
- specify: {
- phase: "specify",
- files: [
- { templateId: "requirements", name: "Requirements Document", priority: "required", description: "Functional and non-functional requirements" },
- { templateId: "user-stories", name: "User Stories", priority: "recommended", description: "Features from user perspective" },
- { templateId: "acceptance-criteria", name: "Acceptance Criteria", priority: "recommended", description: "Testable conditions for completion" },
- ],
- requiresRepository: false,
- requiresTasks: false,
- guidance: "Define clear requirements and acceptance criteria.",
+// Contract type specific deliverables (must match backend phase_guidance.rs)
+type ContractTypeDeliverables = Partial<Record<ContractPhase, PhaseConfig>>;
+
+const CONTRACT_TYPE_DELIVERABLES: Record<ContractType, ContractTypeDeliverables> = {
+ simple: {
+ plan: {
+ deliverables: [
+ { id: "plan-document", name: "Plan", priority: "required", description: "Implementation plan detailing the approach and tasks" },
+ ],
+ requiresRepository: true,
+ requiresTasks: false,
+ guidance: "Create a plan document that outlines the implementation approach. A repository must be configured before moving to Execute phase.",
+ },
+ execute: {
+ deliverables: [
+ { id: "pull-request", name: "Pull Request", priority: "required", description: "Pull request with the implemented changes" },
+ ],
+ requiresRepository: true,
+ requiresTasks: true,
+ guidance: "Execute the plan and create a PR with the implemented changes. Complete all tasks to finish the contract.",
+ },
},
- plan: {
- phase: "plan",
- files: [
- { templateId: "architecture", name: "Architecture Document", priority: "recommended", description: "System architecture and design" },
- { templateId: "task-breakdown", name: "Task Breakdown", priority: "required", description: "Work broken into tasks" },
- { templateId: "technical-design", name: "Technical Design", priority: "optional", description: "Detailed technical specs" },
- ],
- requiresRepository: true,
- requiresTasks: false,
- guidance: "Design the solution and create a task breakdown. Configure a repository.",
+ specification: {
+ research: {
+ deliverables: [
+ { id: "research-notes", name: "Research Notes", priority: "required", description: "Document findings and insights during research" },
+ ],
+ requiresRepository: false,
+ requiresTasks: false,
+ guidance: "Focus on understanding the problem space and document your findings in the Research Notes before moving to Specify phase.",
+ },
+ specify: {
+ deliverables: [
+ { id: "requirements-document", name: "Requirements Document", priority: "required", description: "Define functional and non-functional requirements" },
+ ],
+ requiresRepository: false,
+ requiresTasks: false,
+ guidance: "Define what needs to be built with clear requirements in the Requirements Document. Ensure specifications are detailed enough for planning.",
+ },
+ plan: {
+ deliverables: [
+ { id: "plan-document", name: "Plan", priority: "required", description: "Implementation plan detailing the approach and tasks" },
+ ],
+ requiresRepository: true,
+ requiresTasks: false,
+ guidance: "Create a plan document that outlines the implementation approach. A repository must be configured before moving to Execute phase.",
+ },
+ execute: {
+ deliverables: [
+ { id: "pull-request", name: "Pull Request", priority: "required", description: "Pull request with the implemented changes" },
+ ],
+ requiresRepository: true,
+ requiresTasks: true,
+ guidance: "Execute the plan and create a PR with the implemented changes. Complete all tasks before moving to Review phase.",
+ },
+ review: {
+ deliverables: [
+ { id: "release-notes", name: "Release Notes", priority: "required", description: "Document changes for release communication" },
+ ],
+ requiresRepository: false,
+ requiresTasks: false,
+ guidance: "Review completed work and document the release in the Release Notes. The contract can be completed after review.",
+ },
},
execute: {
- phase: "execute",
- files: [
- { templateId: "dev-notes", name: "Development Notes", priority: "recommended", description: "Implementation details" },
- { templateId: "test-plan", name: "Test Plan", priority: "optional", description: "Testing strategy" },
- { templateId: "implementation-log", name: "Implementation Log", priority: "optional", description: "Progress log" },
- ],
- requiresRepository: true,
- requiresTasks: true,
- guidance: "Execute tasks and track implementation progress.",
+ execute: {
+ deliverables: [], // No deliverables for execute-only contract type
+ requiresRepository: true,
+ requiresTasks: true,
+ guidance: "Execute the tasks directly. No deliverable documents are required for this contract type.",
+ },
},
- review: {
- phase: "review",
- files: [
- { templateId: "release-notes", name: "Release Notes", priority: "required", description: "Changes for release" },
- { templateId: "review-checklist", name: "Review Checklist", priority: "recommended", description: "Code and feature review" },
- { templateId: "retrospective", name: "Retrospective", priority: "optional", description: "Project learnings" },
- ],
+};
+
+// Get phase config for a specific contract type and phase
+function getPhaseConfig(contractType: ContractType, phase: ContractPhase): PhaseConfig {
+ const typeConfig = CONTRACT_TYPE_DELIVERABLES[contractType];
+ const phaseConfig = typeConfig?.[phase];
+
+ if (phaseConfig) {
+ return phaseConfig;
+ }
+
+ // Fallback for unknown phase/type combinations
+ return {
+ deliverables: [],
requiresRepository: false,
requiresTasks: false,
- guidance: "Review work and document the release.",
- },
-};
+ guidance: `Unknown phase "${phase}" for contract type "${contractType}"`,
+ };
+}
interface DeliverableStatus {
- templateId: string;
+ id: string;
name: string;
priority: "required" | "recommended" | "optional";
description: string;
@@ -91,29 +125,33 @@ interface PhaseDeliverablesProps {
}
export function PhaseDeliverablesPanel({ contract, onCreateFile }: PhaseDeliverablesProps) {
- const deliverables = PHASE_DELIVERABLES[contract.phase];
+ // Get phase config based on contract type AND phase
+ const phaseConfig = useMemo(
+ () => getPhaseConfig(contract.contractType, contract.phase),
+ [contract.contractType, contract.phase]
+ );
// Calculate deliverable status
- const fileStatuses = useMemo((): DeliverableStatus[] => {
- return deliverables.files.map((rec) => {
+ const deliverableStatuses = useMemo((): DeliverableStatus[] => {
+ return phaseConfig.deliverables.map((deliverable) => {
// Find matching file by name similarity
const matchedFile = contract.files.find((f) => {
const nameLower = f.name.toLowerCase();
- const recLower = rec.name.toLowerCase();
+ const deliverableLower = deliverable.name.toLowerCase();
return (
f.contractPhase === contract.phase &&
- (nameLower.includes(recLower) || recLower.includes(nameLower) || nameLower.includes(rec.templateId.replace("-", " ")))
+ (nameLower.includes(deliverableLower) || deliverableLower.includes(nameLower) || nameLower.includes(deliverable.id.replace("-", " ")))
);
});
return {
- ...rec,
+ ...deliverable,
completed: !!matchedFile,
fileId: matchedFile?.id,
actualName: matchedFile?.name,
};
});
- }, [contract.files, contract.phase, deliverables.files]);
+ }, [contract.files, contract.phase, phaseConfig.deliverables]);
// Check repository status
const hasRepository = contract.repositories.length > 0;
@@ -133,8 +171,8 @@ export function PhaseDeliverablesPanel({ contract, onCreateFile }: PhaseDelivera
let completed = 0;
let total = 0;
- // Count required and recommended files
- fileStatuses.forEach((s) => {
+ // Count required and recommended deliverables
+ deliverableStatuses.forEach((s) => {
if (s.priority !== "optional") {
total++;
if (s.completed) completed++;
@@ -142,19 +180,19 @@ export function PhaseDeliverablesPanel({ contract, onCreateFile }: PhaseDelivera
});
// Count repository if required
- if (deliverables.requiresRepository) {
+ if (phaseConfig.requiresRepository) {
total++;
if (hasRepository) completed++;
}
- // Count tasks if in execute phase
- if (deliverables.requiresTasks && taskStats.total > 0) {
+ // Count tasks if required
+ if (phaseConfig.requiresTasks && taskStats.total > 0) {
total++;
if (taskStats.done === taskStats.total) completed++;
}
return total > 0 ? Math.round((completed / total) * 100) : 100;
- }, [fileStatuses, hasRepository, deliverables, taskStats]);
+ }, [deliverableStatuses, hasRepository, phaseConfig, taskStats]);
const priorityColors = {
required: "text-red-400",
@@ -182,13 +220,13 @@ export function PhaseDeliverablesPanel({ contract, onCreateFile }: PhaseDelivera
</div>
{/* Guidance text */}
- <p className="font-mono text-xs text-[#555] italic">{deliverables.guidance}</p>
+ <p className="font-mono text-xs text-[#555] italic">{phaseConfig.guidance}</p>
- {/* File deliverables */}
+ {/* Deliverables checklist */}
<div className="space-y-2">
- {fileStatuses.map((status) => (
+ {deliverableStatuses.map((status) => (
<div
- key={status.templateId}
+ key={status.id}
className={`flex items-center justify-between p-2 border ${
status.completed
? "border-green-400/20 bg-green-400/5"
@@ -221,7 +259,7 @@ export function PhaseDeliverablesPanel({ contract, onCreateFile }: PhaseDelivera
</div>
{!status.completed && onCreateFile && (
<button
- onClick={() => onCreateFile(status.templateId, status.name)}
+ onClick={() => onCreateFile(status.id, status.name)}
className="px-2 py-1 font-mono text-[10px] text-[#75aafc] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors"
>
Create
@@ -232,7 +270,7 @@ export function PhaseDeliverablesPanel({ contract, onCreateFile }: PhaseDelivera
</div>
{/* Repository status */}
- {deliverables.requiresRepository && (
+ {phaseConfig.requiresRepository && (
<div
className={`flex items-center gap-2 p-2 border ${
hasRepository
@@ -260,8 +298,8 @@ export function PhaseDeliverablesPanel({ contract, onCreateFile }: PhaseDelivera
</div>
)}
- {/* Task status (execute phase) */}
- {deliverables.requiresTasks && (
+ {/* Task status */}
+ {phaseConfig.requiresTasks && (
<div
className={`flex items-center justify-between p-2 border ${
taskStats.total > 0 && taskStats.done === taskStats.total
diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts
index 7c9fcd6..4390b20 100644
--- a/makima/frontend/src/lib/api.ts
+++ b/makima/frontend/src/lib/api.ts
@@ -1593,7 +1593,7 @@ export async function deleteAccount(
// =============================================================================
/** Contract type determines the workflow and required documents */
-export type ContractType = "simple" | "specification";
+export type ContractType = "simple" | "specification" | "execute";
export type ContractPhase = "research" | "specify" | "plan" | "execute" | "review";
export type ContractStatus = "active" | "completed" | "archived";
export type RepositorySourceType = "remote" | "local" | "managed";
@@ -1604,12 +1604,16 @@ export function getValidPhases(contractType: ContractType): ContractPhase[] {
if (contractType === "simple") {
return ["plan", "execute"];
}
+ if (contractType === "execute") {
+ return ["execute"];
+ }
return ["research", "specify", "plan", "execute", "review"];
}
/** Get default initial phase for a contract type */
export function getDefaultPhase(contractType: ContractType): ContractPhase {
if (contractType === "simple") return "plan";
+ if (contractType === "execute") return "execute";
return "research";
}
diff --git a/makima/frontend/src/types/templates.ts b/makima/frontend/src/types/templates.ts
index 77ba89e..ca337c5 100644
--- a/makima/frontend/src/types/templates.ts
+++ b/makima/frontend/src/types/templates.ts
@@ -19,6 +19,7 @@ export interface ContractTemplate {
}
// Default built-in templates
+// NOTE: Deliverable IDs must match backend phase_guidance.rs exactly
export const DEFAULT_TEMPLATES: ContractTemplate[] = [
{
id: "simple",
@@ -29,12 +30,12 @@ export const DEFAULT_TEMPLATES: ContractTemplate[] = [
{
id: "plan",
name: "Plan",
- deliverables: [{ id: "plan-deliverable", name: "Plan" }],
+ deliverables: [{ id: "plan-document", name: "Plan" }],
},
{
id: "execute",
name: "Execute",
- deliverables: [{ id: "pr-deliverable", name: "PR" }],
+ deliverables: [{ id: "pull-request", name: "Pull Request" }],
},
],
},
@@ -53,17 +54,17 @@ export const DEFAULT_TEMPLATES: ContractTemplate[] = [
{
id: "specify",
name: "Specify",
- deliverables: [{ id: "requirements", name: "Requirements Document" }],
+ deliverables: [{ id: "requirements-document", name: "Requirements Document" }],
},
{
id: "plan",
name: "Plan",
- deliverables: [{ id: "plan-deliverable", name: "Plan" }],
+ deliverables: [{ id: "plan-document", name: "Plan" }],
},
{
id: "execute",
name: "Execute",
- deliverables: [{ id: "pr-deliverable", name: "PR" }],
+ deliverables: [{ id: "pull-request", name: "Pull Request" }],
},
{
id: "review",
diff --git a/makima/frontend/tsconfig.tsbuildinfo b/makima/frontend/tsconfig.tsbuildinfo
index eff7718..b02179d 100644
--- a/makima/frontend/tsconfig.tsbuildinfo
+++ b/makima/frontend/tsconfig.tsbuildinfo
@@ -1 +1 @@
-{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/autopilotpanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/templates/templateeditor.tsx","./src/components/workflow/phasecolumn.tsx","./src/components/workflow/workflowboard.tsx","./src/components/workflow/workflowcontractcard.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/settings.tsx","./src/routes/templates.tsx","./src/routes/workflow.tsx","./src/types/messages.ts","./src/types/templates.ts"],"errors":true,"version":"5.9.3"} \ No newline at end of file
+{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/components/gridoverlay.tsx","./src/components/japanesehovertext.tsx","./src/components/logo.tsx","./src/components/masthead.tsx","./src/components/navstrip.tsx","./src/components/phaseconfirmationnotification.tsx","./src/components/protectedroute.tsx","./src/components/rewritelink.tsx","./src/components/simplemarkdown.tsx","./src/components/supervisorquestionnotification.tsx","./src/components/charts/chartrenderer.tsx","./src/components/contracts/autopilotpanel.tsx","./src/components/contracts/contractcliinput.tsx","./src/components/contracts/contractcontextmenu.tsx","./src/components/contracts/contractdetail.tsx","./src/components/contracts/contractlist.tsx","./src/components/contracts/phasebadge.tsx","./src/components/contracts/phaseconfirmationmodal.tsx","./src/components/contracts/phasedeliverablespanel.tsx","./src/components/contracts/phasehint.tsx","./src/components/contracts/phaseprogressbar.tsx","./src/components/contracts/quickactionbuttons.tsx","./src/components/contracts/repositorypanel.tsx","./src/components/contracts/taskderivationpreview.tsx","./src/components/files/bodyrenderer.tsx","./src/components/files/cliinput.tsx","./src/components/files/conflictnotification.tsx","./src/components/files/elementcontextmenu.tsx","./src/components/files/filedetail.tsx","./src/components/files/filelist.tsx","./src/components/files/reposyncindicator.tsx","./src/components/files/updatenotification.tsx","./src/components/files/versionhistorydropdown.tsx","./src/components/history/checkpointcard.tsx","./src/components/history/checkpointlist.tsx","./src/components/history/conversationmessage.tsx","./src/components/history/conversationview.tsx","./src/components/history/historyfilters.tsx","./src/components/history/resumecontrols.tsx","./src/components/history/timelineeventcard.tsx","./src/components/history/timelinelist.tsx","./src/components/history/index.ts","./src/components/listen/contractpickermodal.tsx","./src/components/listen/controlpanel.tsx","./src/components/listen/speakerpanel.tsx","./src/components/listen/transcriptanalysispanel.tsx","./src/components/listen/transcriptpanel.tsx","./src/components/mesh/branchtaskmodal.tsx","./src/components/mesh/contractcompletequestion.tsx","./src/components/mesh/directoryinput.tsx","./src/components/mesh/gitactionspanel.tsx","./src/components/mesh/inlinesubtaskeditor.tsx","./src/components/mesh/mergeconflictresolver.tsx","./src/components/mesh/overlaydiffviewer.tsx","./src/components/mesh/prpreview.tsx","./src/components/mesh/patcheslistpanel.tsx","./src/components/mesh/subtasktree.tsx","./src/components/mesh/taskdetail.tsx","./src/components/mesh/tasklist.tsx","./src/components/mesh/taskoutput.tsx","./src/components/mesh/tasktree.tsx","./src/components/mesh/unifiedmeshchatinput.tsx","./src/components/mesh/worktreefilespanel.tsx","./src/components/templates/templateeditor.tsx","./src/components/workflow/phasecolumn.tsx","./src/components/workflow/workflowboard.tsx","./src/components/workflow/workflowcontractcard.tsx","./src/contexts/authcontext.tsx","./src/contexts/supervisorquestionscontext.tsx","./src/hooks/usecontracts.ts","./src/hooks/usefilesubscription.ts","./src/hooks/usefiles.ts","./src/hooks/usemeshchathistory.ts","./src/hooks/usemicrophone.ts","./src/hooks/usetasksubscription.ts","./src/hooks/usetasks.ts","./src/hooks/usetextscramble.ts","./src/hooks/useversionhistory.ts","./src/hooks/usewebsocket.ts","./src/lib/api.ts","./src/lib/listenapi.ts","./src/lib/markdown.ts","./src/lib/supabase.ts","./src/routes/_index.tsx","./src/routes/contract-file.tsx","./src/routes/contracts.tsx","./src/routes/files.tsx","./src/routes/history.tsx","./src/routes/listen.tsx","./src/routes/login.tsx","./src/routes/mesh.tsx","./src/routes/settings.tsx","./src/routes/templates.tsx","./src/routes/workflow.tsx","./src/types/messages.ts","./src/types/templates.ts"],"version":"5.9.3"} \ No newline at end of file