/**
 * Workflow MCP tools — exposes the core SF mutation/read handlers over MCP.
 */

import { isAbsolute, relative, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { z } from "zod";

type WorkflowToolExecutors = {
  SUPPORTED_SUMMARY_ARTIFACT_TYPES: readonly string[];
  executeMilestoneStatus: (params: { milestoneId: string }, basePath?: string) => Promise<unknown>;
  executePlanMilestone: (
    params: {
      milestoneId: string;
      title: string;
      vision: string;
      slices?: Array<{
        sliceId: string;
        title: string;
        risk: string;
        depends: string[];
        demo: string;
        goal: string;
        successCriteria: string;
        proofLevel: string;
        integrationClosure: string;
        observabilityImpact: string;
      }>;
      templateId?: string;
      status?: string;
      dependsOn?: string[];
      successCriteria?: string[];
      keyRisks?: Array<{ risk: string; whyItMatters: string }>;
      proofStrategy?: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
      verificationContract?: string;
      verificationIntegration?: string;
      verificationOperational?: string;
      verificationUat?: string;
      definitionOfDone?: string[];
      requirementCoverage?: string;
      boundaryMapMarkdown?: string;
      visionMeeting?: {
        trigger: string;
        pm: string;
        userAdvocate: string;
        customerPanel: string;
        business: string;
        researcher: string;
        deliveryLead: string;
        partner: string;
        combatant: string;
        architect: string;
        moderator: string;
        weightedSynthesis: string;
        confidenceByArea: string;
        recommendedRoute: "discussing" | "researching" | "planning";
      };
    },
    basePath?: string,
  ) => Promise<unknown>;
  executePlanSlice: (
    params: {
      milestoneId: string;
      sliceId: string;
      goal: string;
      tasks: Array<{
        taskId: string;
        title: string;
        description: string;
        estimate: string;
        files: string[];
        verify: string;
        inputs: string[];
        expectedOutput: string[];
        observabilityImpact?: string;
      }>;
      successCriteria?: string;
      proofLevel?: string;
      integrationClosure?: string;
      observabilityImpact?: string;
    },
    basePath?: string,
  ) => Promise<unknown>;
  executeReplanSlice: (
    params: {
      milestoneId: string;
      sliceId: string;
      blockerTaskId: string;
      blockerDescription: string;
      whatChanged: string;
      updatedTasks: Array<{
        taskId: string;
        title: string;
        description: string;
        estimate: string;
        files: string[];
        verify: string;
        inputs: string[];
        expectedOutput: string[];
        fullPlanMd?: string;
      }>;
      removedTaskIds: string[];
    },
    basePath?: string,
  ) => Promise<unknown>;
  executeSliceComplete: (
    params: {
      sliceId: string;
      milestoneId: string;
      sliceTitle: string;
      oneLiner: string;
      narrative: string;
      verification: string;
      uatContent: string;
      deviations?: string;
      knownLimitations?: string;
      followUps?: string;
      keyFiles?: string[] | string;
      keyDecisions?: string[] | string;
      patternsEstablished?: string[] | string;
      observabilitySurfaces?: string[] | string;
      provides?: string[] | string;
      requirementsSurfaced?: string[] | string;
      drillDownPaths?: string[] | string;
      affects?: string[] | string;
      requirementsAdvanced?: Array<{ id: string; how: string } | string>;
      requirementsValidated?: Array<{ id: string; proof: string } | string>;
      requirementsInvalidated?: Array<{ id: string; what: string } | string>;
      filesModified?: Array<{ path: string; description: string } | string>;
      requires?: Array<{ slice: string; provides: string } | string>;
    },
    basePath?: string,
  ) => Promise<unknown>;
  executeCompleteMilestone: (
    params: {
      milestoneId: string;
      title: string;
      oneLiner: string;
      narrative: string;
      verificationPassed: boolean;
      successCriteriaResults?: string;
      definitionOfDoneResults?: string;
      requirementOutcomes?: string;
      keyDecisions?: string[];
      keyFiles?: string[];
      lessonsLearned?: string[];
      followUps?: string;
      deviations?: string;
    },
    basePath?: string,
  ) => Promise<unknown>;
  executeValidateMilestone: (
    params: {
      milestoneId: string;
      verdict: "pass" | "needs-attention" | "needs-remediation";
      remediationRound: number;
      successCriteriaChecklist: string;
      sliceDeliveryAudit: string;
      crossSliceIntegration: string;
      requirementCoverage: string;
      verificationClasses?: string;
      verdictRationale: string;
      remediationPlan?: string;
    },
    basePath?: string,
  ) => Promise<unknown>;
  executeReassessRoadmap: (
    params: {
      milestoneId: string;
      completedSliceId: string;
      verdict: string;
      assessment: string;
      sliceChanges: {
        modified: Array<{
          sliceId: string;
          title: string;
          risk?: string;
          depends?: string[];
          demo?: string;
        }>;
        added: Array<{
          sliceId: string;
          title: string;
          risk?: string;
          depends?: string[];
          demo?: string;
        }>;
        removed: string[];
      };
    },
    basePath?: string,
  ) => Promise<unknown>;
  executeSaveGateResult: (
    params: {
      milestoneId: string;
      sliceId: string;
      gateId: string;
      taskId?: string;
      verdict: "pass" | "flag" | "omitted";
      rationale: string;
      findings?: string;
    },
    basePath?: string,
  ) => Promise<unknown>;
  executeSummarySave: (
    params: {
      milestone_id: string;
      slice_id?: string;
      task_id?: string;
      artifact_type: string;
      content: string;
    },
    basePath?: string,
  ) => Promise<unknown>;
  executeTaskComplete: (
    params: {
      taskId: string;
      sliceId: string;
      milestoneId: string;
      oneLiner: string;
      narrative: string;
      verification: string;
      deviations?: string;
      knownIssues?: string;
      keyFiles?: string[];
      keyDecisions?: string[];
      blockerDiscovered?: boolean;
      verificationEvidence?: Array<
        { command: string; exitCode: number; verdict: string; durationMs: number } | string
      >;
    },
    basePath?: string,
  ) => Promise<unknown>;
};

type WorkflowWriteGateModule = {
  loadWriteGateSnapshot: (basePath?: string) => {
    verifiedDepthMilestones: string[];
    activeQueuePhase: boolean;
    pendingGateId: string | null;
  };
  shouldBlockPendingGateInSnapshot: (
    snapshot: {
      verifiedDepthMilestones: string[];
      activeQueuePhase: boolean;
      pendingGateId: string | null;
    },
    toolName: string,
    milestoneId: string | null,
    queuePhaseActive?: boolean,
  ) => { block: boolean; reason?: string };
  shouldBlockQueueExecutionInSnapshot: (
    snapshot: {
      verifiedDepthMilestones: string[];
      activeQueuePhase: boolean;
      pendingGateId: string | null;
    },
    toolName: string,
    input: string,
    queuePhaseActive?: boolean,
  ) => { block: boolean; reason?: string };
};

type WorkflowDbBootstrapModule = {
  ensureDbOpen: (basePath?: string) => Promise<boolean>;
};

let workflowToolExecutorsPromise: Promise<WorkflowToolExecutors> | null = null;
let workflowExecutionQueue: Promise<void> = Promise.resolve();
let workflowWriteGatePromise: Promise<WorkflowWriteGateModule> | null = null;

/** Reset module-level singletons so tests can vary env vars between runs. */
export function _resetWorkflowModuleState(): void {
  workflowToolExecutorsPromise = null;
  workflowExecutionQueue = Promise.resolve();
  workflowWriteGatePromise = null;
}

function getAllowedProjectRoot(env: NodeJS.ProcessEnv = process.env): string | null {
  const configuredRoot = env.SF_WORKFLOW_PROJECT_ROOT?.trim();
  return configuredRoot ? resolve(configuredRoot) : null;
}

function isWithinRoot(candidatePath: string, rootPath: string): boolean {
  const rel = relative(rootPath, candidatePath);
  return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
}

function validateProjectDir(projectDir: string, env: NodeJS.ProcessEnv = process.env): string {
  if (!isAbsolute(projectDir)) {
    throw new Error(`projectDir must be an absolute path. Received: ${projectDir}`);
  }

  const resolvedProjectDir = resolve(projectDir);
  const allowedRoot = getAllowedProjectRoot(env);
  if (allowedRoot && !isWithinRoot(resolvedProjectDir, allowedRoot)) {
    throw new Error(
      `projectDir must stay within the configured workflow project root. Received: ${resolvedProjectDir}; allowed root: ${allowedRoot}`,
    );
  }

  return resolvedProjectDir;
}

function parseToolArgs<T>(schema: z.ZodType<T>, args: Record<string, unknown>): T {
  return schema.parse(args);
}

function parseWorkflowArgs<T extends { projectDir: string }>(
  schema: z.ZodType<T>,
  args: Record<string, unknown>,
): T {
  const parsed = parseToolArgs(schema, args);
  return {
    ...parsed,
    projectDir: validateProjectDir(parsed.projectDir),
  };
}

function isWorkflowToolExecutors(value: unknown): value is WorkflowToolExecutors {
  if (!value || typeof value !== "object") return false;
  const record = value as Record<string, unknown>;
  const functionExports = [
    "executeMilestoneStatus",
    "executePlanMilestone",
    "executePlanSlice",
    "executeReplanSlice",
    "executeSliceComplete",
    "executeCompleteMilestone",
    "executeValidateMilestone",
    "executeReassessRoadmap",
    "executeSaveGateResult",
    "executeSummarySave",
    "executeTaskComplete",
  ];

  return Array.isArray(record.SUPPORTED_SUMMARY_ARTIFACT_TYPES) &&
    functionExports.every((key) => typeof record[key] === "function");
}

function getSupportedSummaryArtifactTypes(executors: WorkflowToolExecutors): readonly string[] {
  return executors.SUPPORTED_SUMMARY_ARTIFACT_TYPES;
}

function getWriteGateModuleCandidates(): string[] {
  const candidates: string[] = [];
  const explicitModule = process.env.SF_WORKFLOW_WRITE_GATE_MODULE?.trim();
  if (explicitModule) {
    if (/^[a-z]{2,}:/i.test(explicitModule) && !explicitModule.startsWith("file:")) {
      throw new Error("SF_WORKFLOW_WRITE_GATE_MODULE only supports file: URLs or filesystem paths.");
    }
    candidates.push(explicitModule.startsWith("file:") ? explicitModule : toFileUrl(explicitModule));
  }

  candidates.push(
    new URL("../../../src/resources/extensions/sf/bootstrap/write-gate.js", import.meta.url).href,
    new URL("../../../dist/resources/extensions/sf/bootstrap/write-gate.js", import.meta.url).href,
    new URL("../../../src/resources/extensions/sf/bootstrap/write-gate.ts", import.meta.url).href,
  );

  return [...new Set(candidates)];
}

function toFileUrl(modulePath: string): string {
  return pathToFileURL(resolve(modulePath)).href;
}

/** @internal — exported for testing only */
export function _buildImportCandidates(relativePath: string): string[] {
  // Build candidate paths: try the given path first, then swap src/<->dist/
  // and try .ts extension. This handles both dev (tsx from src/) and prod
  // (compiled from dist/) execution contexts.
  const candidates: string[] = [relativePath];
  const swapped = relativePath.includes("/src/")
    ? relativePath.replace("/src/", "/dist/")
    : relativePath.includes("/dist/")
      ? relativePath.replace("/dist/", "/src/")
      : null;
  if (swapped) candidates.push(swapped);
  // Also try .ts variants for dev-mode tsx execution
  if (relativePath.endsWith(".js")) {
    candidates.push(relativePath.replace(/\.js$/, ".ts"));
    if (swapped) candidates.push(swapped.replace(/\.js$/, ".ts"));
  }
  return candidates;
}

async function importLocalModule<T>(relativePath: string): Promise<T> {
  const candidates = _buildImportCandidates(relativePath)
    .map((p) => new URL(p, import.meta.url).href);

  let lastErr: unknown;
  for (const candidate of candidates) {
    try {
      return await import(candidate) as T;
    } catch (err) {
      lastErr = err;
    }
  }
  throw lastErr;
}

function getWorkflowExecutorModuleCandidates(env: NodeJS.ProcessEnv = process.env): string[] {
  const candidates: string[] = [];
  const explicitModule = env.SF_WORKFLOW_EXECUTORS_MODULE?.trim();
  if (explicitModule) {
    if (/^[a-z]{2,}:/i.test(explicitModule) && !explicitModule.startsWith("file:")) {
      throw new Error("SF_WORKFLOW_EXECUTORS_MODULE only supports file: URLs or filesystem paths.");
    }
    candidates.push(explicitModule.startsWith("file:") ? explicitModule : toFileUrl(explicitModule));
  }

  candidates.push(
    new URL("../../../src/resources/extensions/sf/tools/workflow-tool-executors.js", import.meta.url).href,
    new URL("../../../dist/resources/extensions/sf/tools/workflow-tool-executors.js", import.meta.url).href,
    new URL("../../../src/resources/extensions/sf/tools/workflow-tool-executors.ts", import.meta.url).href,
  );

  return [...new Set(candidates)];
}

async function getWorkflowToolExecutors(): Promise<WorkflowToolExecutors> {
  if (!workflowToolExecutorsPromise) {
    workflowToolExecutorsPromise = (async () => {
      const attempts: string[] = [];
      for (const candidate of getWorkflowExecutorModuleCandidates()) {
        try {
          const loaded = await import(candidate);
          if (isWorkflowToolExecutors(loaded)) {
            return loaded;
          }
          attempts.push(`${candidate} (module shape mismatch)`);
        } catch (err) {
          attempts.push(`${candidate} (${err instanceof Error ? err.message : String(err)})`);
        }
      }

      throw new Error(
        "Unable to load SF workflow executor bridge for MCP mutation tools. " +
        "Set SF_WORKFLOW_EXECUTORS_MODULE to an importable workflow-tool-executors module, " +
        "or run the MCP server from a SF checkout that includes src/resources/extensions/sf/tools/workflow-tool-executors.(js|ts). " +
        `Attempts: ${attempts.join("; ")}`,
      );
    })();
  }
  return workflowToolExecutorsPromise;
}

async function getWorkflowWriteGateModule(): Promise<WorkflowWriteGateModule> {
  if (!workflowWriteGatePromise) {
    workflowWriteGatePromise = (async () => {
      const attempts: string[] = [];
      for (const candidate of getWriteGateModuleCandidates()) {
        try {
          const loaded = await import(candidate);
          if (
            loaded &&
            typeof loaded.loadWriteGateSnapshot === "function" &&
            typeof loaded.shouldBlockPendingGateInSnapshot === "function" &&
            typeof loaded.shouldBlockQueueExecutionInSnapshot === "function"
          ) {
            return loaded as WorkflowWriteGateModule;
          }
          attempts.push(`${candidate} (module shape mismatch)`);
        } catch (err) {
          attempts.push(`${candidate} (${err instanceof Error ? err.message : String(err)})`);
        }
      }

      throw new Error(
        "Unable to load SF write-gate bridge for workflow MCP tools. " +
        `Attempts: ${attempts.join("; ")}`,
      );
    })();
  }
  return workflowWriteGatePromise;
}

interface McpToolServer {
  tool(
    name: string,
    description: string,
    params: Record<string, unknown>,
    handler: (args: Record<string, unknown>) => Promise<unknown>,
  ): unknown;
}

export const WORKFLOW_TOOL_NAMES = [
  "sf_decision_save",
  "sf_save_decision",
  "sf_requirement_update",
  "sf_update_requirement",
  "sf_requirement_save",
  "sf_save_requirement",
  "sf_milestone_generate_id",
  "sf_generate_milestone_id",
  "sf_plan_milestone",
  "sf_plan_slice",
  "sf_plan_task",
  "sf_task_plan",
  "sf_replan_slice",
  "sf_slice_replan",
  "sf_slice_complete",
  "sf_complete_slice",
  "sf_skip_slice",
  "sf_complete_milestone",
  "sf_milestone_complete",
  "sf_validate_milestone",
  "sf_milestone_validate",
  "sf_reassess_roadmap",
  "sf_roadmap_reassess",
  "sf_save_gate_result",
  "sf_summary_save",
  "sf_task_complete",
  "sf_complete_task",
  "sf_milestone_status",
  "sf_journal_query",
] as const;

async function runSerializedWorkflowOperation<T>(fn: () => Promise<T>): Promise<T> {
  // The shared DB adapter and workflow log base path are process-global, so
  // workflow MCP mutations must not overlap within a single server process.
  const prior = workflowExecutionQueue;
  let release!: () => void;
  workflowExecutionQueue = new Promise<void>((resolve) => {
    release = resolve;
  });

  await prior;
  try {
    return await fn();
  } finally {
    release();
  }
}

async function runSerializedWorkflowDbOperation<T>(
  projectDir: string,
  fn: () => Promise<T>,
): Promise<T> {
  return runSerializedWorkflowOperation(async () => {
    const { ensureDbOpen } = await importLocalModule<WorkflowDbBootstrapModule>(
      "../../../src/resources/extensions/sf/bootstrap/dynamic-tools.js",
    );
    const dbAvailable = await ensureDbOpen(projectDir);
    if (!dbAvailable) {
      throw new Error("SF database is not available");
    }
    return fn();
  });
}

async function enforceWorkflowWriteGate(
  toolName: string,
  projectDir: string,
  milestoneId: string | null = null,
): Promise<void> {
  const writeGate = await getWorkflowWriteGateModule();
  const snapshot = writeGate.loadWriteGateSnapshot(projectDir);
  const pendingGate = writeGate.shouldBlockPendingGateInSnapshot(
    snapshot,
    toolName,
    milestoneId,
    snapshot.activeQueuePhase,
  );
  if (pendingGate.block) {
    throw new Error(pendingGate.reason ?? "workflow tool blocked by pending discussion gate");
  }

  const queueGuard = writeGate.shouldBlockQueueExecutionInSnapshot(
    snapshot,
    toolName,
    "",
    snapshot.activeQueuePhase,
  );
  if (queueGuard.block) {
    throw new Error(queueGuard.reason ?? "workflow tool blocked during queue mode");
  }
}

async function handleTaskComplete(
  projectDir: string,
  args: Omit<z.infer<typeof taskCompleteSchema>, "projectDir">,
): Promise<unknown> {
  await enforceWorkflowWriteGate("sf_task_complete", projectDir, args.milestoneId);
  const {
    taskId,
    sliceId,
    milestoneId,
    oneLiner,
    narrative,
    verification,
    deviations,
    knownIssues,
    keyFiles,
    keyDecisions,
    blockerDiscovered,
    verificationEvidence,
  } = args;
  const { executeTaskComplete } = await getWorkflowToolExecutors();
  return runSerializedWorkflowOperation(() =>
    executeTaskComplete(
      {
        taskId,
        sliceId,
        milestoneId,
        oneLiner,
        narrative,
        verification,
        deviations,
        knownIssues,
        keyFiles,
        keyDecisions,
        blockerDiscovered,
        verificationEvidence,
      },
      projectDir,
    ),
  );
}

async function handleSliceComplete(
  projectDir: string,
  args: z.infer<typeof sliceCompleteSchema>,
): Promise<unknown> {
  await enforceWorkflowWriteGate("sf_slice_complete", projectDir, args.milestoneId);
  const { executeSliceComplete } = await getWorkflowToolExecutors();
  const { projectDir: _projectDir, ...params } = args;
  return runSerializedWorkflowOperation(() => executeSliceComplete(params, projectDir));
}

async function handleReplanSlice(
  projectDir: string,
  args: z.infer<typeof replanSliceSchema>,
): Promise<unknown> {
  await enforceWorkflowWriteGate("sf_replan_slice", projectDir, args.milestoneId);
  const { executeReplanSlice } = await getWorkflowToolExecutors();
  const { projectDir: _projectDir, ...params } = args;
  return runSerializedWorkflowOperation(() => executeReplanSlice(params, projectDir));
}

async function handleCompleteMilestone(
  projectDir: string,
  args: z.infer<typeof completeMilestoneSchema>,
): Promise<unknown> {
  await enforceWorkflowWriteGate("sf_complete_milestone", projectDir, args.milestoneId);
  const { executeCompleteMilestone } = await getWorkflowToolExecutors();
  const { projectDir: _projectDir, ...params } = args;
  return runSerializedWorkflowOperation(() => executeCompleteMilestone(params, projectDir));
}

async function handleValidateMilestone(
  projectDir: string,
  args: z.infer<typeof validateMilestoneSchema>,
): Promise<unknown> {
  await enforceWorkflowWriteGate("sf_validate_milestone", projectDir, args.milestoneId);
  const { executeValidateMilestone } = await getWorkflowToolExecutors();
  const { projectDir: _projectDir, ...params } = args;
  return runSerializedWorkflowOperation(() => executeValidateMilestone(params, projectDir));
}

async function handleReassessRoadmap(
  projectDir: string,
  args: z.infer<typeof reassessRoadmapSchema>,
): Promise<unknown> {
  await enforceWorkflowWriteGate("sf_reassess_roadmap", projectDir, args.milestoneId);
  const { executeReassessRoadmap } = await getWorkflowToolExecutors();
  const { projectDir: _projectDir, ...params } = args;
  return runSerializedWorkflowOperation(() => executeReassessRoadmap(params, projectDir));
}

async function handleSaveGateResult(
  projectDir: string,
  args: z.infer<typeof saveGateResultSchema>,
): Promise<unknown> {
  await enforceWorkflowWriteGate("sf_save_gate_result", projectDir, args.milestoneId);
  const { executeSaveGateResult } = await getWorkflowToolExecutors();
  const { projectDir: _projectDir, ...params } = args;
  return runSerializedWorkflowOperation(() => executeSaveGateResult(params, projectDir));
}

async function ensureMilestoneDbRow(milestoneId: string): Promise<void> {
  try {
    const { insertMilestone } = await importLocalModule<any>("../../../src/resources/extensions/sf/sf-db.js");
    insertMilestone({ id: milestoneId, status: "queued" });
  } catch {
    // Ignore pre-existing rows or transient DB availability issues.
  }
}

const projectDirParam = z.string().describe("Absolute path to the project directory within the configured workflow root");

const planMilestoneParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  title: z.string().describe("Milestone title"),
  vision: z.string().describe("Milestone vision"),
  slices: z.array(z.object({
    sliceId: z.string(),
    title: z.string(),
    risk: z.string(),
    depends: z.array(z.string()),
    demo: z.string(),
    goal: z.string(),
    successCriteria: z.string(),
    proofLevel: z.string(),
    integrationClosure: z.string(),
    observabilityImpact: z.string(),
  })).optional().describe("Planned slices for the milestone. Optional when templateId is used for scaffolding."),
  templateId: z.string().optional().describe("Optional milestone template scaffold (e.g. bugfix, small-feature, refactor)"),
  status: z.string().optional().describe("Milestone status"),
  dependsOn: z.array(z.string()).optional().describe("Milestone dependencies"),
  successCriteria: z.array(z.string()).optional().describe("Top-level success criteria bullets"),
  keyRisks: z.array(z.object({
    risk: z.string(),
    whyItMatters: z.string(),
  })).optional().describe("Structured risk entries"),
  proofStrategy: z.array(z.object({
    riskOrUnknown: z.string(),
    retireIn: z.string(),
    whatWillBeProven: z.string(),
  })).optional().describe("Structured proof strategy entries"),
  verificationContract: z.string().optional(),
  verificationIntegration: z.string().optional(),
  verificationOperational: z.string().optional(),
  verificationUat: z.string().optional(),
  definitionOfDone: z.array(z.string()).optional(),
  requirementCoverage: z.string().optional(),
  boundaryMapMarkdown: z.string().optional(),
  visionMeeting: z.object({
    trigger: z.string(),
    pm: z.string(),
    userAdvocate: z.string(),
    customerPanel: z.string(),
    business: z.string(),
    researcher: z.string(),
    deliveryLead: z.string(),
    partner: z.string(),
    combatant: z.string(),
    architect: z.string(),
    moderator: z.string(),
    weightedSynthesis: z.string(),
    confidenceByArea: z.string(),
    recommendedRoute: z.enum(["discussing", "researching", "planning"]),
  }).optional().describe("Structured top-level vision and roadmap alignment meeting with weighted synthesis"),
};
const planMilestoneSchema = z.object(planMilestoneParams);

const planSliceParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  sliceId: z.string().describe("Slice ID (e.g. S01)"),
  goal: z.string().describe("Slice goal"),
  adversarialReview: z.object({
    partner: z.string(),
    combatant: z.string(),
    architect: z.string(),
  }).optional().describe("Adversarial review summary with partner, combatant, and architect perspectives"),
  planningMeeting: z.object({
    trigger: z.string(),
    pm: z.string(),
    userAdvocate: z.string().optional(),
    customerPanel: z.string().optional(),
    business: z.string().optional(),
    researcher: z.string(),
    deliveryLead: z.string().optional(),
    partner: z.string(),
    combatant: z.string(),
    architect: z.string(),
    moderator: z.string(),
    recommendedRoute: z.enum(["discussing", "researching", "planning"]),
    confidenceSummary: z.string(),
  }).describe("Required populated planning meeting. Empty, null, or missing planningMeeting is not acceptable."),
  tasks: z.array(z.object({
    taskId: z.string(),
    title: z.string(),
    description: z.string(),
    estimate: z.string(),
    files: z.array(z.string()),
    verify: z.string(),
    inputs: z.array(z.string()),
    expectedOutput: z.array(z.string()),
    observabilityImpact: z.string().optional(),
  })).describe("Planned tasks for the slice"),
  successCriteria: z.string().optional(),
  proofLevel: z.string().optional(),
  integrationClosure: z.string().optional(),
  observabilityImpact: z.string().optional(),
};
const planSliceSchema = z.object(planSliceParams);

const completeMilestoneParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  title: z.string().describe("Milestone title"),
  oneLiner: z.string().describe("One-sentence summary of what the milestone achieved"),
  narrative: z.string().describe("Detailed narrative of what happened during the milestone"),
  verificationPassed: z.boolean().describe("Must be true after milestone verification succeeds"),
  successCriteriaResults: z.string().optional(),
  definitionOfDoneResults: z.string().optional(),
  requirementOutcomes: z.string().optional(),
  keyDecisions: z.array(z.string()).optional(),
  keyFiles: z.array(z.string()).optional(),
  lessonsLearned: z.array(z.string()).optional(),
  followUps: z.string().optional(),
  deviations: z.string().optional(),
};
const completeMilestoneSchema = z.object(completeMilestoneParams);

const validateMilestoneParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  verdict: z.enum(["pass", "needs-attention", "needs-remediation"]).describe("Validation verdict"),
  remediationRound: z.number().describe("Remediation round (0 for first validation)"),
  successCriteriaChecklist: z.string().describe("Markdown checklist of success criteria with evidence"),
  sliceDeliveryAudit: z.string().describe("Markdown auditing each slice's claimed vs delivered output"),
  crossSliceIntegration: z.string().describe("Markdown describing cross-slice issues or closure"),
  requirementCoverage: z.string().describe("Markdown describing requirement coverage and gaps"),
  verificationClasses: z.string().optional(),
  verdictRationale: z.string().describe("Why this verdict was chosen"),
  remediationPlan: z.string().optional(),
};
const validateMilestoneSchema = z.object(validateMilestoneParams);

const roadmapSliceChangeSchema = z.object({
  sliceId: z.string(),
  title: z.string(),
  risk: z.string().optional(),
  depends: z.array(z.string()).optional(),
  demo: z.string().optional(),
});

const reassessRoadmapParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  completedSliceId: z.string().describe("Slice ID that just completed"),
  verdict: z.string().describe("Assessment verdict such as roadmap-confirmed or roadmap-adjusted"),
  assessment: z.string().describe("Assessment text explaining the roadmap decision"),
  sliceChanges: z.object({
    modified: z.array(roadmapSliceChangeSchema),
    added: z.array(roadmapSliceChangeSchema),
    removed: z.array(z.string()),
  }).describe("Slice changes to apply"),
};
const reassessRoadmapSchema = z.object(reassessRoadmapParams);

const saveGateResultParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  sliceId: z.string().describe("Slice ID (e.g. S01)"),
  gateId: z.enum(["Q3", "Q4", "Q5", "Q6", "Q7", "Q8", "MV01", "MV02", "MV03", "MV04"]).describe("Gate ID"),
  taskId: z.string().optional().describe("Task ID for task-scoped gates"),
  verdict: z.enum(["pass", "flag", "omitted"]).describe("Gate verdict"),
  rationale: z.string().describe("One-sentence justification"),
  findings: z.string().optional().describe("Detailed markdown findings"),
};
const saveGateResultSchema = z.object(saveGateResultParams);

const replanSliceParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  sliceId: z.string().describe("Slice ID (e.g. S01)"),
  blockerTaskId: z.string().describe("Task ID that discovered the blocker"),
  blockerDescription: z.string().describe("Description of the blocker"),
  whatChanged: z.string().describe("Summary of what changed in the plan"),
  goal: z.string().optional().describe("Updated slice goal when the replan changes the slice contract"),
  successCriteria: z.string().optional().describe("Updated slice success criteria block"),
  proofLevel: z.string().optional().describe("Updated slice proof level"),
  integrationClosure: z.string().optional().describe("Updated slice integration closure"),
  observabilityImpact: z.string().optional().describe("Updated slice observability impact"),
  adversarialReview: z.object({
    partner: z.string(),
    combatant: z.string(),
    architect: z.string(),
  }).optional().describe("Updated adversarial review summary for the replanned slice"),
  planningMeeting: z.object({
    trigger: z.string(),
    pm: z.string(),
    researcher: z.string(),
    partner: z.string(),
    combatant: z.string(),
    architect: z.string(),
    moderator: z.string(),
    recommendedRoute: z.enum(["discussing", "researching", "planning"]),
    confidenceSummary: z.string(),
  }).optional().describe("Updated structured planning meeting artifact for the replanned slice"),
  updatedTasks: z.array(z.object({
    taskId: z.string(),
    title: z.string(),
    description: z.string(),
    estimate: z.string(),
    files: z.array(z.string()),
    verify: z.string(),
    inputs: z.array(z.string()),
    expectedOutput: z.array(z.string()),
    fullPlanMd: z.string().optional(),
  })).describe("Tasks to upsert into the replanned slice"),
  removedTaskIds: z.array(z.string()).describe("Task IDs to remove from the slice"),
};
const replanSliceSchema = z.object(replanSliceParams);

const sliceCompleteParams = {
  projectDir: projectDirParam,
  sliceId: z.string().describe("Slice ID (e.g. S01)"),
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  sliceTitle: z.string().describe("Title of the slice"),
  oneLiner: z.string().describe("One-line summary of what the slice accomplished"),
  narrative: z.string().describe("Detailed narrative of what happened across all tasks"),
  verification: z.string().describe("What was verified across all tasks"),
  uatContent: z.string().describe("UAT test content (markdown body)"),
  deviations: z.string().optional(),
  knownLimitations: z.string().optional(),
  followUps: z.string().optional(),
  keyFiles: z.union([z.array(z.string()), z.string()]).optional(),
  keyDecisions: z.union([z.array(z.string()), z.string()]).optional(),
  patternsEstablished: z.union([z.array(z.string()), z.string()]).optional(),
  observabilitySurfaces: z.union([z.array(z.string()), z.string()]).optional(),
  provides: z.union([z.array(z.string()), z.string()]).optional(),
  requirementsSurfaced: z.union([z.array(z.string()), z.string()]).optional(),
  drillDownPaths: z.union([z.array(z.string()), z.string()]).optional(),
  affects: z.union([z.array(z.string()), z.string()]).optional(),
  requirementsAdvanced: z.array(z.union([
    z.object({ id: z.string(), how: z.string() }),
    z.string(),
  ])).optional(),
  requirementsValidated: z.array(z.union([
    z.object({ id: z.string(), proof: z.string() }),
    z.string(),
  ])).optional(),
  requirementsInvalidated: z.array(z.union([
    z.object({ id: z.string(), what: z.string() }),
    z.string(),
  ])).optional(),
  filesModified: z.array(z.union([
    z.object({ path: z.string(), description: z.string() }),
    z.string(),
  ])).optional(),
  requires: z.array(z.union([
    z.object({ slice: z.string(), provides: z.string() }),
    z.string(),
  ])).optional(),
};
const sliceCompleteSchema = z.object(sliceCompleteParams);

const summarySaveParams = {
  projectDir: projectDirParam,
  milestone_id: z.string().describe("Milestone ID (e.g. M001)"),
  slice_id: z.string().optional().describe("Slice ID (e.g. S01)"),
  task_id: z.string().optional().describe("Task ID (e.g. T01)"),
  artifact_type: z.string().describe("Artifact type to save (SUMMARY, RESEARCH, CONTEXT, ASSESSMENT, CONTEXT-DRAFT)"),
  content: z.string().describe("The full markdown content of the artifact"),
};
const summarySaveSchema = z.object(summarySaveParams);

const decisionSaveParams = {
  projectDir: projectDirParam,
  scope: z.string().describe("Scope of the decision (e.g. architecture, library, observability)"),
  decision: z.string().describe("What is being decided"),
  choice: z.string().describe("The choice made"),
  rationale: z.string().describe("Why this choice was made"),
  revisable: z.string().optional().describe("Whether this can be revisited"),
  when_context: z.string().optional().describe("When/context for the decision"),
  made_by: z.enum(["human", "agent", "collaborative"]).optional().describe("Who made the decision"),
};
const decisionSaveSchema = z.object(decisionSaveParams);

const requirementUpdateParams = {
  projectDir: projectDirParam,
  id: z.string().describe("Requirement ID (e.g. R001)"),
  status: z.string().optional().describe("New status"),
  validation: z.string().optional().describe("Validation criteria or proof"),
  notes: z.string().optional().describe("Additional notes"),
  description: z.string().optional().describe("Updated description"),
  primary_owner: z.string().optional().describe("Primary owning slice"),
  supporting_slices: z.string().optional().describe("Supporting slices"),
};
const requirementUpdateSchema = z.object(requirementUpdateParams);

const requirementSaveParams = {
  projectDir: projectDirParam,
  class: z.string().describe("Requirement class"),
  description: z.string().describe("Short description of the requirement"),
  why: z.string().describe("Why this requirement matters"),
  source: z.string().describe("Origin of the requirement"),
  status: z.string().optional().describe("Requirement status"),
  primary_owner: z.string().optional().describe("Primary owning slice"),
  supporting_slices: z.string().optional().describe("Supporting slices"),
  validation: z.string().optional().describe("Validation criteria"),
  notes: z.string().optional().describe("Additional notes"),
};
const requirementSaveSchema = z.object(requirementSaveParams);

const milestoneGenerateIdParams = {
  projectDir: projectDirParam,
};
const milestoneGenerateIdSchema = z.object(milestoneGenerateIdParams);

const planTaskParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  sliceId: z.string().describe("Slice ID (e.g. S01)"),
  taskId: z.string().describe("Task ID (e.g. T01)"),
  title: z.string().describe("Task title"),
  description: z.string().describe("Task description / steps block"),
  estimate: z.string().describe("Task estimate"),
  files: z.array(z.string()).describe("Files likely touched"),
  verify: z.string().describe("Verification command or block"),
  inputs: z.array(z.string()).describe("Input files or references"),
  expectedOutput: z.array(z.string()).describe("Expected output files or artifacts"),
  observabilityImpact: z.string().optional().describe("Task observability impact"),
};
const planTaskSchema = z.object(planTaskParams);

const skipSliceParams = {
  projectDir: projectDirParam,
  sliceId: z.string().describe("Slice ID (e.g. S02)"),
  milestoneId: z.string().describe("Milestone ID (e.g. M003)"),
  reason: z.string().optional().describe("Reason for skipping this slice"),
};
const skipSliceSchema = z.object(skipSliceParams);

const taskCompleteParams = {
  projectDir: projectDirParam,
  taskId: z.string().describe("Task ID (e.g. T01)"),
  sliceId: z.string().describe("Slice ID (e.g. S01)"),
  milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
  oneLiner: z.string().describe("One-line summary of what was accomplished"),
  narrative: z.string().describe("Detailed narrative of what happened during the task"),
  verification: z.string().describe("What was verified and how"),
  deviations: z.string().optional().describe("Deviations from the task plan"),
  knownIssues: z.string().optional().describe("Known issues discovered but not fixed"),
  keyFiles: z.array(z.string()).optional().describe("List of key files created or modified"),
  keyDecisions: z.array(z.string()).optional().describe("List of key decisions made during this task"),
  blockerDiscovered: z.boolean().optional().describe("Whether a plan-invalidating blocker was discovered"),
  verificationEvidence: z.array(z.union([
    z.object({
      command: z.string(),
      exitCode: z.number(),
      verdict: z.string(),
      durationMs: z.number(),
    }),
    z.string(),
  ])).optional().describe("Verification evidence entries"),
};
const taskCompleteSchema = z.object(taskCompleteParams);

const milestoneStatusParams = {
  projectDir: projectDirParam,
  milestoneId: z.string().describe("Milestone ID to query (e.g. M001)"),
};
const milestoneStatusSchema = z.object(milestoneStatusParams);

const journalQueryParams = {
  projectDir: projectDirParam,
  flowId: z.string().optional().describe("Filter by flow ID"),
  unitId: z.string().optional().describe("Filter by unit ID"),
  rule: z.string().optional().describe("Filter by rule name"),
  eventType: z.string().optional().describe("Filter by event type"),
  after: z.string().optional().describe("ISO-8601 lower bound (inclusive)"),
  before: z.string().optional().describe("ISO-8601 upper bound (inclusive)"),
  limit: z.number().optional().describe("Maximum entries to return"),
};
const journalQuerySchema = z.object(journalQueryParams);

export function registerWorkflowTools(server: McpToolServer): void {
  server.tool(
    "sf_decision_save",
    "Record a project decision to the SF database and regenerate DECISIONS.md.",
    decisionSaveParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(decisionSaveSchema, args);
      const { projectDir, ...params } = parsed;
      await enforceWorkflowWriteGate("sf_decision_save", projectDir);
      const result = await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { saveDecisionToDb } = await importLocalModule<any>("../../../src/resources/extensions/sf/db-writer.js");
        return saveDecisionToDb(params, projectDir);
      });
      return { content: [{ type: "text" as const, text: `Saved decision ${result.id}` }] };
    },
  );

  server.tool(
    "sf_save_decision",
    "Alias for sf_decision_save. Record a project decision to the SF database and regenerate DECISIONS.md.",
    decisionSaveParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(decisionSaveSchema, args);
      const { projectDir, ...params } = parsed;
      await enforceWorkflowWriteGate("sf_decision_save", projectDir);
      const result = await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { saveDecisionToDb } = await importLocalModule<any>("../../../src/resources/extensions/sf/db-writer.js");
        return saveDecisionToDb(params, projectDir);
      });
      return { content: [{ type: "text" as const, text: `Saved decision ${result.id}` }] };
    },
  );

  server.tool(
    "sf_requirement_update",
    "Update an existing requirement in the SF database and regenerate REQUIREMENTS.md.",
    requirementUpdateParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(requirementUpdateSchema, args);
      const { projectDir, id, ...updates } = parsed;
      await enforceWorkflowWriteGate("sf_requirement_update", projectDir);
      await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { updateRequirementInDb } = await importLocalModule<any>("../../../src/resources/extensions/sf/db-writer.js");
        return updateRequirementInDb(id, updates, projectDir);
      });
      return { content: [{ type: "text" as const, text: `Updated requirement ${id}` }] };
    },
  );

  server.tool(
    "sf_update_requirement",
    "Alias for sf_requirement_update. Update an existing requirement in the SF database and regenerate REQUIREMENTS.md.",
    requirementUpdateParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(requirementUpdateSchema, args);
      const { projectDir, id, ...updates } = parsed;
      await enforceWorkflowWriteGate("sf_requirement_update", projectDir);
      await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { updateRequirementInDb } = await importLocalModule<any>("../../../src/resources/extensions/sf/db-writer.js");
        return updateRequirementInDb(id, updates, projectDir);
      });
      return { content: [{ type: "text" as const, text: `Updated requirement ${id}` }] };
    },
  );

  server.tool(
    "sf_requirement_save",
    "Record a new requirement to the SF database and regenerate REQUIREMENTS.md.",
    requirementSaveParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(requirementSaveSchema, args);
      const { projectDir, ...params } = parsed;
      await enforceWorkflowWriteGate("sf_requirement_save", projectDir);
      const result = await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { saveRequirementToDb } = await importLocalModule<any>("../../../src/resources/extensions/sf/db-writer.js");
        return saveRequirementToDb(params, projectDir);
      });
      return { content: [{ type: "text" as const, text: `Saved requirement ${result.id}` }] };
    },
  );

  server.tool(
    "sf_save_requirement",
    "Alias for sf_requirement_save. Record a new requirement to the SF database and regenerate REQUIREMENTS.md.",
    requirementSaveParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(requirementSaveSchema, args);
      const { projectDir, ...params } = parsed;
      await enforceWorkflowWriteGate("sf_requirement_save", projectDir);
      const result = await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { saveRequirementToDb } = await importLocalModule<any>("../../../src/resources/extensions/sf/db-writer.js");
        return saveRequirementToDb(params, projectDir);
      });
      return { content: [{ type: "text" as const, text: `Saved requirement ${result.id}` }] };
    },
  );

  server.tool(
    "sf_milestone_generate_id",
    "Generate the next milestone ID for a new SF milestone.",
    milestoneGenerateIdParams,
    async (args: Record<string, unknown>) => {
      const { projectDir } = parseWorkflowArgs(milestoneGenerateIdSchema, args);
      await enforceWorkflowWriteGate("sf_milestone_generate_id", projectDir);
      const id = await runSerializedWorkflowDbOperation(projectDir, async () => {
        const {
          claimReservedId,
          findMilestoneIds,
          getReservedMilestoneIds,
          nextMilestoneId,
        } = await importLocalModule<any>("../../../src/resources/extensions/sf/milestone-ids.js");
        const reserved = claimReservedId();
        if (reserved) {
          await ensureMilestoneDbRow(reserved);
          return reserved;
        }
        const allIds = [...new Set([...findMilestoneIds(projectDir), ...getReservedMilestoneIds()])];
        const nextId = nextMilestoneId(allIds);
        await ensureMilestoneDbRow(nextId);
        return nextId;
      });
      return { content: [{ type: "text" as const, text: id }] };
    },
  );

  server.tool(
    "sf_generate_milestone_id",
    "Alias for sf_milestone_generate_id. Generate the next milestone ID for a new SF milestone.",
    milestoneGenerateIdParams,
    async (args: Record<string, unknown>) => {
      const { projectDir } = parseWorkflowArgs(milestoneGenerateIdSchema, args);
      await enforceWorkflowWriteGate("sf_milestone_generate_id", projectDir);
      const id = await runSerializedWorkflowDbOperation(projectDir, async () => {
        const {
          claimReservedId,
          findMilestoneIds,
          getReservedMilestoneIds,
          nextMilestoneId,
        } = await importLocalModule<any>("../../../src/resources/extensions/sf/milestone-ids.js");
        const reserved = claimReservedId();
        if (reserved) {
          await ensureMilestoneDbRow(reserved);
          return reserved;
        }
        const allIds = [...new Set([...findMilestoneIds(projectDir), ...getReservedMilestoneIds()])];
        const nextId = nextMilestoneId(allIds);
        await ensureMilestoneDbRow(nextId);
        return nextId;
      });
      return { content: [{ type: "text" as const, text: id }] };
    },
  );

  server.tool(
    "sf_plan_milestone",
    "Write milestone planning state to the SF database and render ROADMAP.md from DB.",
    planMilestoneParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(planMilestoneSchema, args);
      const { projectDir, ...params } = parsed;
      await enforceWorkflowWriteGate("sf_plan_milestone", projectDir, params.milestoneId);
      const { executePlanMilestone } = await getWorkflowToolExecutors();
      return runSerializedWorkflowOperation(() => executePlanMilestone(params, projectDir));
    },
  );

  server.tool(
    "sf_plan_slice",
    "Write slice/task planning state to the SF database and render plan artifacts from DB.",
    planSliceParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(planSliceSchema, args);
      const { projectDir, ...params } = parsed;
      await enforceWorkflowWriteGate("sf_plan_slice", projectDir, params.milestoneId);
      const { executePlanSlice } = await getWorkflowToolExecutors();
      return runSerializedWorkflowOperation(() => executePlanSlice(params, projectDir));
    },
  );

  server.tool(
    "sf_plan_task",
    "Write task planning state to the SF database and render tasks/T##-PLAN.md from DB.",
    planTaskParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(planTaskSchema, args);
      const { projectDir, ...params } = parsed;
      await enforceWorkflowWriteGate("sf_plan_task", projectDir, params.milestoneId);
      const result = await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { handlePlanTask } = await importLocalModule<any>("../../../src/resources/extensions/sf/tools/plan-task.js");
        return handlePlanTask(params, projectDir);
      });
      if ("error" in result) {
        throw new Error(result.error);
      }
      return {
        content: [{ type: "text" as const, text: `Planned task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
      };
    },
  );

  server.tool(
    "sf_task_plan",
    "Alias for sf_plan_task. Write task planning state to the SF database and render tasks/T##-PLAN.md from DB.",
    planTaskParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(planTaskSchema, args);
      const { projectDir, ...params } = parsed;
      await enforceWorkflowWriteGate("sf_plan_task", projectDir, params.milestoneId);
      const result = await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { handlePlanTask } = await importLocalModule<any>("../../../src/resources/extensions/sf/tools/plan-task.js");
        return handlePlanTask(params, projectDir);
      });
      if ("error" in result) {
        throw new Error(result.error);
      }
      return {
        content: [{ type: "text" as const, text: `Planned task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
      };
    },
  );

  server.tool(
    "sf_replan_slice",
    "Replan a slice after a blocker is discovered, preserving completed tasks and re-rendering PLAN.md + REPLAN.md.",
    replanSliceParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(replanSliceSchema, args);
      return handleReplanSlice(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_slice_replan",
    "Alias for sf_replan_slice. Replan a slice after a blocker is discovered.",
    replanSliceParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(replanSliceSchema, args);
      return handleReplanSlice(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_slice_complete",
    "Record a completed slice to the SF database, render SUMMARY.md + UAT.md, and update roadmap projection.",
    sliceCompleteParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(sliceCompleteSchema, args);
      return handleSliceComplete(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_complete_slice",
    "Alias for sf_slice_complete. Record a completed slice to the SF database and render summary/UAT artifacts.",
    sliceCompleteParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(sliceCompleteSchema, args);
      return handleSliceComplete(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_skip_slice",
    "Mark a slice as skipped so auto-mode advances past it without executing.",
    skipSliceParams,
    async (args: Record<string, unknown>) => {
      const { projectDir, milestoneId, sliceId, reason } = parseWorkflowArgs(skipSliceSchema, args);
      await enforceWorkflowWriteGate("sf_skip_slice", projectDir, milestoneId);
      await runSerializedWorkflowDbOperation(projectDir, async () => {
        const { getSlice, updateSliceStatus } = await importLocalModule<any>("../../../src/resources/extensions/sf/sf-db.js");
        const { invalidateStateCache } = await importLocalModule<any>("../../../src/resources/extensions/sf/state.js");
        const { rebuildState } = await importLocalModule<any>("../../../src/resources/extensions/sf/doctor.js");
        const slice = getSlice(milestoneId, sliceId);
        if (!slice) {
          throw new Error(`Slice ${sliceId} not found in milestone ${milestoneId}`);
        }
        if (slice.status === "complete" || slice.status === "done") {
          throw new Error(`Slice ${sliceId} is already complete and cannot be skipped`);
        }
        if (slice.status !== "skipped") {
          updateSliceStatus(milestoneId, sliceId, "skipped");
          invalidateStateCache();
          await rebuildState(projectDir);
        }
      });
      return {
        content: [{ type: "text" as const, text: `Skipped slice ${sliceId} (${milestoneId}). Reason: ${reason ?? "User-directed skip"}.` }],
      };
    },
  );

  server.tool(
    "sf_complete_milestone",
    "Record a completed milestone to the SF database and render its SUMMARY.md.",
    completeMilestoneParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(completeMilestoneSchema, args);
      return handleCompleteMilestone(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_milestone_complete",
    "Alias for sf_complete_milestone. Record a completed milestone to the SF database and render its SUMMARY.md.",
    completeMilestoneParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(completeMilestoneSchema, args);
      return handleCompleteMilestone(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_validate_milestone",
    "Validate a milestone, persist validation results to the SF database, and render VALIDATION.md.",
    validateMilestoneParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(validateMilestoneSchema, args);
      return handleValidateMilestone(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_milestone_validate",
    "Alias for sf_validate_milestone. Validate a milestone and render VALIDATION.md.",
    validateMilestoneParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(validateMilestoneSchema, args);
      return handleValidateMilestone(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_reassess_roadmap",
    "Reassess a milestone roadmap after a slice completes, writing ASSESSMENT.md and re-rendering ROADMAP.md.",
    reassessRoadmapParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(reassessRoadmapSchema, args);
      return handleReassessRoadmap(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_roadmap_reassess",
    "Alias for sf_reassess_roadmap. Reassess a roadmap after slice completion.",
    reassessRoadmapParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(reassessRoadmapSchema, args);
      return handleReassessRoadmap(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_save_gate_result",
    "Save a quality gate result to the SF database.",
    saveGateResultParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(saveGateResultSchema, args);
      return handleSaveGateResult(parsed.projectDir, parsed);
    },
  );

  server.tool(
    "sf_summary_save",
    "Save a SF summary/research/context/assessment artifact to the database and disk.",
    summarySaveParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(summarySaveSchema, args);
      const { projectDir, milestone_id, slice_id, task_id, artifact_type, content } = parsed;
      await enforceWorkflowWriteGate("sf_summary_save", projectDir, milestone_id);
      const executors = await getWorkflowToolExecutors();
      const supportedArtifactTypes = getSupportedSummaryArtifactTypes(executors);
      if (!supportedArtifactTypes.includes(artifact_type)) {
        throw new Error(
          `artifact_type must be one of: ${supportedArtifactTypes.join(", ")}`,
        );
      }
      return runSerializedWorkflowOperation(() =>
        executors.executeSummarySave({ milestone_id, slice_id, task_id, artifact_type, content }, projectDir),
      );
    },
  );

  server.tool(
    "sf_task_complete",
    "Record a completed task to the SF database and render its SUMMARY.md.",
    taskCompleteParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(taskCompleteSchema, args);
      const { projectDir, ...taskArgs } = parsed;
      return handleTaskComplete(projectDir, taskArgs);
    },
  );

  server.tool(
    "sf_complete_task",
    "Alias for sf_task_complete. Record a completed task to the SF database and render its SUMMARY.md.",
    taskCompleteParams,
    async (args: Record<string, unknown>) => {
      const parsed = parseWorkflowArgs(taskCompleteSchema, args);
      const { projectDir, ...taskArgs } = parsed;
      return handleTaskComplete(projectDir, taskArgs);
    },
  );

  server.tool(
    "sf_milestone_status",
    "Read the current status of a milestone and all its slices from the SF database.",
    milestoneStatusParams,
    async (args: Record<string, unknown>) => {
      const { projectDir, milestoneId } = parseWorkflowArgs(milestoneStatusSchema, args);
      await enforceWorkflowWriteGate("sf_milestone_status", projectDir, milestoneId);
      const { executeMilestoneStatus } = await getWorkflowToolExecutors();
      return runSerializedWorkflowOperation(() => executeMilestoneStatus({ milestoneId }, projectDir));
    },
  );

  server.tool(
    "sf_journal_query",
    "Query the structured event journal for auto-mode iterations.",
    journalQueryParams,
    async (args: Record<string, unknown>) => {
      const { projectDir, limit, ...filters } = parseWorkflowArgs(journalQuerySchema, args);
      const { queryJournal } = await importLocalModule<any>("../../../src/resources/extensions/sf/journal.js");
      const entries = queryJournal(projectDir, filters).slice(0, limit ?? 100);
      if (entries.length === 0) {
        return { content: [{ type: "text" as const, text: "No matching journal entries found." }] };
      }
      return { content: [{ type: "text" as const, text: JSON.stringify(entries, null, 2) }] };
    },
  );
}
