diff options
| author | soryu <soryu@soryu.co> | 2026-01-26 23:49:40 +0000 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-01-26 23:49:40 +0000 |
| commit | 64cc98783d067625d633eea1142d114e324f76bb (patch) | |
| tree | 5a172d365e17ba1c3fc6aa32bcb2237cf05b0d0f | |
| parent | 63128cc45d3b677acadb30c37b79c0e13dc2cdc1 (diff) | |
| download | soryu-64cc98783d067625d633eea1142d114e324f76bb.tar.gz soryu-64cc98783d067625d633eea1142d114e324f76bb.zip | |
Use phase deliverables configured in contract types
| -rw-r--r-- | makima/frontend/src/components/contracts/PhaseDeliverablesPanel.tsx | 196 | ||||
| -rw-r--r-- | makima/frontend/src/lib/api.ts | 6 | ||||
| -rw-r--r-- | makima/frontend/src/types/templates.ts | 11 | ||||
| -rw-r--r-- | makima/frontend/tsconfig.tsbuildinfo | 2 |
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 |
