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