mcp-ai-agent-guidelines
Version:
A comprehensive Model Context Protocol server providing professional tools, resources, and prompts for implementing AI agent best practices
436 lines • 18.2 kB
JavaScript
// Design Phase Workflow - Orchestrates the structured design process
import { z } from "zod";
import { ConfigurationError, PhaseError, SessionError, } from "../shared/errors.js";
import { confirmationModule } from "./confirmation-module.js";
import { constraintManager } from "./constraint-manager.js";
import { pivotModule } from "./pivot-module.js";
const _WorkflowRequestSchema = z.object({
action: z.enum(["start", "advance", "complete", "reset", "status"]),
sessionId: z.string(),
phaseId: z.string().optional(),
content: z.string().optional(),
config: z.any().optional(), // DesignSessionConfig for start action
methodologyProfile: z.any().optional(), // MethodologyProfile for methodology-driven sessions
});
class DesignPhaseWorkflowImpl {
sessions = new Map();
// Standard design phases in sequence
PHASE_SEQUENCE = [
"discovery",
"requirements",
"architecture",
"specification",
"planning",
];
async initialize() {
// No-op initializer for test compatibility
}
async executeWorkflow(request) {
const { action, sessionId } = request;
switch (action) {
case "start":
if (!request.config) {
throw new ConfigurationError("Configuration is required for start action", { sessionId, action });
}
return this.startSession(sessionId, request.config, request.methodologyProfile);
case "advance":
return this.advancePhase(sessionId, request.phaseId, request.content);
case "complete":
if (!request.phaseId || !request.content) {
throw new ConfigurationError("Phase ID and content are required for complete action", { sessionId, action });
}
return this.completePhase(sessionId, request.phaseId, request.content);
case "reset":
return this.resetSession(sessionId);
case "status":
return this.getSessionStatus(sessionId);
default:
throw new ConfigurationError(`Unknown workflow action: ${action}`, {
action,
sessionId,
});
}
}
async startSession(sessionId, config, methodologyProfile) {
// Determine phase sequence: use methodology-specific phases if available, otherwise default
const phaseSequence = methodologyProfile?.methodology.phases ||
config.metadata?.customPhaseSequence ||
this.PHASE_SEQUENCE;
// Initialize phases based on methodology profile or default configuration
const phases = {};
for (const phaseId of phaseSequence) {
// Use methodology profile phase mapping if available
if (methodologyProfile?.phaseMapping[phaseId]) {
phases[phaseId] = {
...methodologyProfile.phaseMapping[phaseId],
status: phaseId === phaseSequence[0] ? "in-progress" : "pending",
coverage: 0,
artifacts: [],
};
}
else {
// Fallback to constraint manager or default phase
const phaseReq = constraintManager.getPhaseRequirements(phaseId);
phases[phaseId] = {
id: phaseId,
name: phaseReq?.name || phaseId,
description: phaseReq?.description || `${phaseId} phase`,
inputs: [],
outputs: phaseReq?.required_outputs || [],
criteria: phaseReq?.criteria || [],
coverage: 0,
status: phaseId === phaseSequence[0] ? "in-progress" : "pending",
artifacts: [],
dependencies: this.getPhaseDepedencies(phaseId),
};
}
}
const sessionState = {
config,
currentPhase: phaseSequence[0],
phases,
coverage: {
overall: 0,
phases: {},
constraints: {},
assumptions: {},
documentation: {},
testCoverage: 0,
},
artifacts: [],
history: [
{
timestamp: new Date().toISOString(),
type: methodologyProfile ? "methodology-selected" : "phase-start",
phase: phaseSequence[0],
description: methodologyProfile
? `Design session started with ${methodologyProfile.methodology.name} methodology`
: "Design session started",
data: {
sessionId,
config,
methodology: methodologyProfile?.methodology.id,
},
},
],
status: "active",
// Add methodology information to session state
methodologySelection: methodologyProfile
? {
selected: methodologyProfile.methodology,
alternatives: [],
signals: config.methodologySignals || {},
timestamp: new Date().toISOString(),
selectionRationale: `Using ${methodologyProfile.methodology.name} methodology`,
}
: undefined,
methodologyProfile,
};
this.sessions.set(sessionId, sessionState);
return {
success: true,
sessionState,
currentPhase: sessionState.currentPhase,
nextPhase: this.computeNextPhase(sessionState.currentPhase, sessionState),
recommendations: [
`Started design session in ${phases[sessionState.currentPhase].name} phase`,
"Begin by establishing clear context and stakeholder analysis",
"Ensure all requirements are captured before moving to next phase",
],
artifacts: [],
message: `Design session ${sessionId} started successfully`,
};
}
async advancePhase(sessionId, targetPhaseId, content) {
const sessionState = this.sessions.get(sessionId);
if (!sessionState) {
throw new SessionError(`Session ${sessionId} not found`, { sessionId });
}
const currentPhase = sessionState.phases[sessionState.currentPhase];
const nextPhaseId = targetPhaseId ||
this.computeNextPhase(sessionState.currentPhase, sessionState);
if (!nextPhaseId) {
return {
success: false,
sessionState,
currentPhase: sessionState.currentPhase,
recommendations: [
"All phases completed. Session ready for finalization.",
],
artifacts: [],
message: "No more phases to advance to",
};
}
// Check if current phase can be completed
if (content) {
const confirmation = await confirmationModule.confirmPhaseCompletion({
sessionState,
phaseId: sessionState.currentPhase,
content,
autoAdvance: true,
});
if (!confirmation.canProceed) {
return {
success: false,
sessionState,
currentPhase: sessionState.currentPhase,
recommendations: confirmation.recommendations,
artifacts: [],
message: `Cannot advance: ${confirmation.issues.join(", ")}`,
};
}
// Mark current phase as completed
currentPhase.status = "completed";
currentPhase.coverage = confirmation.coverage;
}
// Advance to next phase
const nextPhase = sessionState.phases[nextPhaseId];
nextPhase.status = "in-progress";
sessionState.currentPhase = nextPhaseId;
// Add event to history
this.addSessionEvent(sessionState, {
type: "phase-complete",
phase: currentPhase.id,
description: `Completed ${currentPhase.name} phase`,
});
this.addSessionEvent(sessionState, {
type: "phase-start",
phase: nextPhaseId,
description: `Started ${nextPhase.name} phase`,
});
// Check if a pivot is recommended
const pivotDecision = content
? await pivotModule.evaluatePivotNeed({
sessionState,
currentContent: content,
})
: null;
const recommendations = [
`Advanced to ${nextPhase.name} phase`,
`Focus on: ${nextPhase.criteria.join(", ")}`,
];
if (pivotDecision?.triggered) {
recommendations.push(`⚠️ Pivot recommended: ${pivotDecision.reason}`);
recommendations.push(...pivotDecision.alternatives.slice(0, 2));
}
return {
success: true,
sessionState,
currentPhase: nextPhaseId,
nextPhase: this.computeNextPhase(nextPhaseId, sessionState),
recommendations,
artifacts: [],
message: `Advanced to ${nextPhase.name} phase`,
};
}
async completePhase(sessionId, phaseId, content) {
const sessionState = this.sessions.get(sessionId);
if (!sessionState) {
throw new SessionError(`Session ${sessionId} not found`, { sessionId });
}
const phase = sessionState.phases[phaseId];
if (!phase) {
throw new PhaseError(`Phase ${phaseId} not found in session`, {
sessionId,
phaseId,
});
}
// Validate phase completion
const confirmation = await confirmationModule.confirmPhaseCompletion({
sessionState,
phaseId,
content,
strictMode: true,
});
if (!confirmation.passed) {
return {
success: false,
sessionState,
currentPhase: sessionState.currentPhase,
recommendations: confirmation.recommendations,
artifacts: [],
message: `Phase completion failed: ${confirmation.issues.join(", ")}`,
};
}
// Mark phase as completed
phase.status = "completed";
phase.coverage = confirmation.coverage;
// Update overall coverage
this.updateSessionCoverage(sessionState, content);
// Add completion event
this.addSessionEvent(sessionState, {
type: "phase-complete",
phase: phaseId,
description: `Successfully completed ${phase.name} phase`,
data: { coverage: confirmation.coverage },
});
const recommendations = [
`✅ ${phase.name} phase completed successfully`,
`Coverage: ${confirmation.coverage.toFixed(1)}%`,
];
// Check if all phases are complete
const allPhasesComplete = this.PHASE_SEQUENCE.every((id) => sessionState.phases[id].status === "completed");
if (allPhasesComplete) {
sessionState.status = "completed";
recommendations.push("🎉 All design phases completed! Session ready for artifact generation.");
}
return {
success: true,
sessionState,
currentPhase: sessionState.currentPhase,
nextPhase: this.computeNextPhase(sessionState.currentPhase, sessionState),
recommendations,
artifacts: phase.artifacts,
message: `${phase.name} phase completed successfully`,
};
}
async resetSession(sessionId) {
const sessionState = this.sessions.get(sessionId);
if (!sessionState) {
throw new SessionError(`Session ${sessionId} not found`, { sessionId });
}
// Reset all phases to initial state
for (const phaseId of this.PHASE_SEQUENCE) {
const phase = sessionState.phases[phaseId];
phase.status =
phaseId === this.PHASE_SEQUENCE[0] ? "in-progress" : "pending";
phase.coverage = 0;
phase.artifacts = [];
}
sessionState.currentPhase = this.PHASE_SEQUENCE[0];
sessionState.status = "active";
sessionState.artifacts = [];
this.addSessionEvent(sessionState, {
type: "phase-start",
description: "Session reset to initial state",
});
return {
success: true,
sessionState,
currentPhase: sessionState.currentPhase,
nextPhase: this.computeNextPhase(sessionState.currentPhase, sessionState),
recommendations: [
"Session reset successfully",
"Starting from discovery phase",
],
artifacts: [],
message: "Session reset to initial state",
};
}
async getSessionStatus(sessionId) {
const sessionState = this.sessions.get(sessionId);
if (!sessionState) {
throw new SessionError(`Session ${sessionId} not found`, { sessionId });
}
const completedPhases = Object.values(sessionState.phases).filter((p) => p.status === "completed").length;
const totalPhases = this.PHASE_SEQUENCE.length;
const recommendations = [
`Session progress: ${completedPhases}/${totalPhases} phases completed`,
`Current phase: ${sessionState.phases[sessionState.currentPhase].name}`,
`Overall coverage: ${sessionState.coverage.overall.toFixed(1)}%`,
];
if (sessionState.status === "completed") {
recommendations.push("🎉 Session completed successfully!");
}
return {
success: true,
sessionState,
currentPhase: sessionState.currentPhase,
nextPhase: this.computeNextPhase(sessionState.currentPhase, sessionState),
recommendations,
artifacts: sessionState.artifacts,
message: `Session status: ${sessionState.status}`,
};
}
computeNextPhase(currentPhaseId, sessionState) {
// Determine phase sequence: from sessionState phases, methodology selection, or default
let phaseSequence;
if (sessionState?.phases && Object.keys(sessionState.phases).length > 0) {
phaseSequence = Object.keys(sessionState.phases);
}
else {
// Safe extraction of methodologySelection phases when present
const maybeMethodology = sessionState;
const phasesCandidate = maybeMethodology.methodologySelection?.phases;
if (phasesCandidate && Array.isArray(phasesCandidate)) {
phaseSequence = phasesCandidate.slice();
}
else {
phaseSequence = this.PHASE_SEQUENCE;
}
}
const effectiveCurrent = currentPhaseId || phaseSequence[0];
const currentIndex = phaseSequence.indexOf(effectiveCurrent);
return currentIndex >= 0 && currentIndex < phaseSequence.length - 1
? phaseSequence[currentIndex + 1]
: undefined;
}
// Public convenience for tests: accepts sessionState only
getNextPhase(sessionState) {
return this.computeNextPhase(sessionState.currentPhase, sessionState);
}
getPhaseDepedencies(phaseId, phaseSequence) {
const sequence = phaseSequence || this.PHASE_SEQUENCE;
const index = sequence.indexOf(phaseId);
return index > 0 ? [sequence[index - 1]] : [];
}
async generateWorkflowGuide(sessionState) {
const current = sessionState.currentPhase;
const next = this.computeNextPhase(current, sessionState);
const steps = [
`Complete ${current} phase criteria`,
next ? `Prepare for ${next} phase` : "Finalize artifacts",
];
return { currentPhase: current, nextPhase: next, steps };
}
// Backwards-compatible helpers expected by tests
async canTransitionToPhase(sessionState, targetPhaseId) {
const next = this.computeNextPhase(sessionState.currentPhase, sessionState);
return next ? next === targetPhaseId : false;
}
async transitionToPhase(sessionState, targetPhaseId) {
const can = await this.canTransitionToPhase(sessionState, targetPhaseId);
return {
success: can,
from: sessionState.currentPhase,
to: can ? targetPhaseId : undefined,
};
}
updateSessionCoverage(sessionState, content) {
const coverageReport = constraintManager.generateCoverageReport(sessionState.config, content);
sessionState.coverage = {
overall: coverageReport.overall,
phases: coverageReport.phases,
constraints: coverageReport.constraints,
assumptions: {},
documentation: {},
testCoverage: 0,
};
this.addSessionEvent(sessionState, {
type: "coverage-update",
description: `Coverage updated: ${coverageReport.overall.toFixed(1)}%`,
data: { coverage: coverageReport.overall },
});
}
addSessionEvent(sessionState, event) {
sessionState.history.push({
timestamp: new Date().toISOString(),
...event,
});
}
// Utility methods for external access
getSession(sessionId) {
return this.sessions.get(sessionId);
}
listSessions() {
return Array.from(this.sessions.keys());
}
getPhaseSequence() {
return [...this.PHASE_SEQUENCE];
}
}
// Export singleton instance
export const designPhaseWorkflow = new DesignPhaseWorkflowImpl();
// Module Implementation Status Sentinel
export const IMPLEMENTATION_STATUS = "IMPLEMENTED";
//# sourceMappingURL=design-phase-workflow.js.map