Skip to main content
Glama

overseer.advance_phase

Advance project phases by validating deliverables and transitioning to the next phase, ensuring structured workflow progression in software development.

Instructions

Advance a phase to the next phase after validating all deliverables are complete. Marks current phase as "locked" and sets next phase as current.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
repo_rootYesRoot path of the repository
expected_current_phaseYesPhase ID that should currently be active (e.g., "01", "02")

Implementation Reference

  • The primary handler function executing the overseer.advance_phase tool logic: checks completion of deliverables and criteria, advances phase status, updates repository files (PHASES.md and PHASE-XX.md).
    export async function handleAdvancePhase( args: { repo_root: string; expected_current_phase: string; }, phaseManager: PhaseManager ): Promise<{ success: boolean; message: string; status: 'advanced' | 'incomplete'; current_phase: { id: string; name: string; status: string; } | null; next_phase: { id: string; name: string; status: string; } | null; missing_items: Array<{ type: 'deliverable' | 'done_criterion'; item: string; reason: string; }>; changes_applied: string[]; }> { const missingItems: Array<{ type: 'deliverable' | 'done_criterion'; item: string; reason: string }> = []; const changesApplied: string[] = []; try { // Resolve repo path let repoPath = args.repo_root; if (!repoPath.startsWith('/')) { repoPath = join(homedir(), 'dev', repoPath); } repoPath = FSUtils.expandPath(repoPath); const expectedPhaseId = args.expected_current_phase.padStart(2, '0'); // Read PHASES.md using absolute path (handles paths with spaces) const repoName = repoPath.split('/').pop() || 'unknown'; const repoHandler = new RepoHandler(); const projectPhases = repoHandler.readPhasesIndexFromPath(repoPath); if (!projectPhases) { return { success: false, message: 'Project not found. Run plan_project first.', status: 'incomplete', current_phase: null, next_phase: null, missing_items: [], changes_applied: [], }; } // Find the expected phase const currentPhase = projectPhases.phases.find(p => p.id === expectedPhaseId); if (!currentPhase) { return { success: false, message: `Phase ${expectedPhaseId} not found`, status: 'incomplete', current_phase: null, next_phase: null, missing_items: [], changes_applied: [], }; } // Verify this is the current phase if (currentPhase.status !== 'in_progress' && currentPhase.status !== 'active') { return { success: false, message: `Phase ${expectedPhaseId} is not currently active (status: ${currentPhase.status})`, status: 'incomplete', current_phase: { id: currentPhase.id, name: currentPhase.name, status: currentPhase.status, }, next_phase: null, missing_items: [], changes_applied: [], }; } // Read PHASE-XX.md to check deliverables and done criteria (handles paths with spaces) const phaseFilePath = repoHandler.getPhaseFileByIdFromPath(repoPath, expectedPhaseId); if (!FSUtils.fileExists(phaseFilePath)) { return { success: false, message: `Phase file PHASE-${expectedPhaseId}.md not found`, status: 'incomplete', current_phase: { id: currentPhase.id, name: currentPhase.name, status: currentPhase.status, }, next_phase: null, missing_items: [{ type: 'deliverable', item: 'Phase file', reason: 'PHASE-XX.md file missing' }], changes_applied: [], }; } const phaseContent = FSUtils.readFile(phaseFilePath); // Extract deliverables and check completion const deliverablesMatch = phaseContent.match(/## Deliverables\s*\n((?:- \[[ x]\] .+\n?)+)/); if (deliverablesMatch) { const items = deliverablesMatch[1].match(/- \[([ x])\] (.+)/g); if (items) { items.forEach(item => { const checked = item.startsWith('- [x]'); const text = item.replace(/- \[[ x]\] /, '').trim(); if (!checked) { missingItems.push({ type: 'deliverable', item: text, reason: 'Deliverable not marked as complete in checklist', }); } else { // Verify the deliverable actually exists const verification = verifyDeliverableExists(text, repoPath); if (!verification.exists) { missingItems.push({ type: 'deliverable', item: text, reason: verification.reason, }); } } }); } } // Extract done criteria and check completion const doneCriteriaMatch = phaseContent.match(/## Done Criteria\s*\n((?:- \[[ x]\] .+\n?)+)/); if (doneCriteriaMatch) { const items = doneCriteriaMatch[1].match(/- \[([ x])\] (.+)/g); if (items) { items.forEach(item => { const checked = item.startsWith('- [x]'); const text = item.replace(/- \[[ x]\] /, '').trim(); if (!checked) { missingItems.push({ type: 'done_criterion', item: text, reason: 'Done criterion not marked as complete in checklist', }); } else { // Verify the criterion is actually met const verification = verifyCriterionMet(text, repoPath); if (!verification.met) { missingItems.push({ type: 'done_criterion', item: text, reason: verification.reason, }); } } }); } } // If there are missing items, return incomplete if (missingItems.length > 0) { return { success: true, message: `Phase ${expectedPhaseId} is incomplete. ${missingItems.length} item(s) missing.`, status: 'incomplete', current_phase: { id: currentPhase.id, name: currentPhase.name, status: currentPhase.status, }, next_phase: null, missing_items: missingItems, changes_applied: [], }; } // All items complete - advance the phase // Mark current phase as "locked" (completed) currentPhase.status = 'locked'; currentPhase.completed_at = new Date().toISOString(); changesApplied.push(join(repoPath, 'PHASES.md')); // Update phase file with completion summary const updatedPhaseContent = addCompletionSummary(phaseContent, currentPhase); FSUtils.writeFile(phaseFilePath, updatedPhaseContent); changesApplied.push(phaseFilePath); // Find and activate next phase const currentPhaseIndex = projectPhases.phases.findIndex(p => p.id === expectedPhaseId); let nextPhase = null; if (currentPhaseIndex < projectPhases.phases.length - 1) { nextPhase = projectPhases.phases[currentPhaseIndex + 1]; nextPhase.status = 'in_progress'; nextPhase.started_at = new Date().toISOString(); changesApplied.push(join(repoPath, 'PHASES.md')); } // Write updated PHASES.md using absolute path repoHandler.writePhasesIndexToPath(repoPath, projectPhases); return { success: true, message: `Phase ${expectedPhaseId} advanced successfully. ${nextPhase ? `Next phase ${nextPhase.id} activated.` : 'No more phases.'}`, status: 'advanced', current_phase: { id: currentPhase.id, name: currentPhase.name, status: currentPhase.status, }, next_phase: nextPhase ? { id: nextPhase.id, name: nextPhase.name, status: nextPhase.status, } : null, missing_items: [], changes_applied: changesApplied, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { success: false, message: `Failed to advance phase: ${errorMessage}`, status: 'incomplete', current_phase: null, next_phase: null, missing_items: [], changes_applied: [], }; } }
  • Creates the Tool object defining the name, description, and input schema for overseer.advance_phase.
    export function createAdvancePhaseTool(phaseManager: PhaseManager): Tool { return { name: 'overseer.advance_phase', description: 'Advance a phase to the next phase after validating all deliverables are complete. Marks current phase as "locked" and sets next phase as current.', inputSchema: { type: 'object', required: ['repo_root', 'expected_current_phase'], properties: { repo_root: { type: 'string', description: 'Root path of the repository', }, expected_current_phase: { type: 'string', description: 'Phase ID that should currently be active (e.g., "01", "02")', }, }, }, }; }
  • Registers the advance phase tool in the list of available tools via createTools.
    export function createTools(context: ToolContext): Tool[] { return [ // Planning tools createPlanProjectTool(context.phaseManager), createInferPhasesTool(context.configLoader), createUpdatePhasesTool(context.phaseManager), // Execution tools createRunPhaseTool(context.phaseManager), createAdvancePhaseTool(context.phaseManager), createStatusTool(context.phaseManager), // QA tools createLintRepoTool(context.configLoader), createSyncDocsTool(context.phaseManager), createCheckComplianceTool(context.phaseManager), // Environment tools createEnvMapTool(context.phaseManager), createGenerateCiTool(context.phaseManager), createSecretsTemplateTool(context.phaseManager), ]; }
  • Registers the handler for overseer.advance_phase in the tool dispatcher switch statement.
    case 'overseer.advance_phase': return await handleAdvancePhase(args, context.phaseManager);
  • Helper function to verify if deliverables (files/directories) actually exist in the repo.
    function verifyDeliverableExists(deliverable: string, repoPath: string): { exists: boolean; reason: string } { // Check for file references const filePattern = /([a-zA-Z0-9_\-./]+\.(md|ts|js|json|yml|yaml|txt|py|java|go|rs|php|rb|ex|exs))/g; const fileMatches = deliverable.match(filePattern); if (fileMatches) { for (const fileRef of fileMatches) { const filePath = join(repoPath, fileRef); if (!FSUtils.fileExists(filePath)) { return { exists: false, reason: `File ${fileRef} does not exist`, }; } } return { exists: true, reason: '' }; } // Check for directory references const dirPattern = /(src|app|lib|tests|docs|config|dist|build)\//; if (dirPattern.test(deliverable)) { const dirMatch = deliverable.match(dirPattern); if (dirMatch) { const dirPath = join(repoPath, dirMatch[1]); if (!FSUtils.dirExists(dirPath)) { return { exists: false, reason: `Directory ${dirMatch[1]}/ does not exist`, }; } } return { exists: true, reason: '' }; } // Generic deliverable - assume exists if we can't verify return { exists: true, reason: '' }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/freqkflag/PROJECT-OVERSEER-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server