import {
  decideLifecycleTransition
} from "./chunk-TBBDFYXW.js";
import {
  StorageManager
} from "./chunk-MCC6KDQF.js";

// src/maintenance/memory-governance.ts
import path from "path";
import { mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
var RULE_VERSION = "memory-governance.v2";
var SEMANTIC_DUPLICATE_MIN_TOKENS = 6;
var SEMANTIC_DUPLICATE_MIN_JACCARD = 0.66;
var QUALITY_SCORE_WEIGHTS = {
  exact_duplicate: 6,
  semantic_duplicate_candidate: 4,
  disputed_memory: 15,
  speculative_low_confidence: 8,
  archive_candidate: 2,
  explicit_capture_review: 5,
  malformed_import: 12
};
function governanceRunsDir(memoryDir) {
  return path.join(memoryDir, "state", "memory-governance", "runs");
}
function governanceRunDir(memoryDir, runId) {
  return path.join(governanceRunsDir(memoryDir), runId);
}
function governanceRestorePath(memoryDir, runId) {
  return path.join(governanceRunDir(memoryDir, runId), "restore.json");
}
function buildRunId(now) {
  return `gov-${now.toISOString().replace(/[:.]/g, "-")}`;
}
function plannedArchivePath(memoryDir, originalPath, now) {
  return path.join(
    memoryDir,
    "archive",
    now.toISOString().slice(0, 10),
    path.basename(originalPath)
  );
}
function normalizeContent(content) {
  return content.trim().replace(/\s+/g, " ").toLowerCase();
}
function statusOf(memory) {
  return memory.frontmatter.status ?? "active";
}
function parseIsoMs(value) {
  if (!value) return null;
  const ms = Date.parse(value);
  return Number.isFinite(ms) ? ms : null;
}
function daysSince(value, now) {
  const ts = parseIsoMs(value);
  if (ts === null) return 365;
  return Math.max(0, (now.getTime() - ts) / 864e5);
}
function compareCanonicalPreference(left, right) {
  if (left.frontmatter.confidence !== right.frontmatter.confidence) {
    return (right.frontmatter.confidence ?? 0) - (left.frontmatter.confidence ?? 0);
  }
  return left.frontmatter.created.localeCompare(right.frontmatter.created);
}
function proposedStatusPriority(status) {
  switch (status) {
    case "quarantined":
      return 4;
    case "rejected":
      return 3;
    case "archived":
      return 2;
    case "pending_review":
      return 1;
    default:
      return 0;
  }
}
function proposedActionPriority(action) {
  if (action.action === "archive") {
    return proposedStatusPriority("archived");
  }
  return proposedStatusPriority(action.afterStatus ?? "active");
}
function tokenizeSemanticContent(content) {
  return Array.from(
    new Set(
      normalizeContent(content).replaceAll(/[^\p{L}\p{N}]+/gu, " ").split(" ").filter((token) => token.length >= 4)
    )
  );
}
function jaccardSimilarity(left, right) {
  if (left.length === 0 || right.length === 0) return 0;
  const leftSet = new Set(left);
  const rightSet = new Set(right);
  let intersection = 0;
  for (const token of leftSet) {
    if (rightSet.has(token)) intersection += 1;
  }
  const union = (/* @__PURE__ */ new Set([...leftSet, ...rightSet])).size;
  return union > 0 ? intersection / union : 0;
}
function sameSemanticDuplicateScope(left, right) {
  if (left.frontmatter.category !== right.frontmatter.category) return false;
  const leftEntityRef = left.frontmatter.entityRef?.trim();
  const rightEntityRef = right.frontmatter.entityRef?.trim();
  if (leftEntityRef && rightEntityRef && leftEntityRef !== rightEntityRef) return false;
  return true;
}
function buildSemanticDuplicateEntries(activeMemories) {
  const reviewQueue = [];
  const ordered = [...activeMemories].sort(compareCanonicalPreference);
  const tokensByMemoryId = new Map(
    ordered.map((memory) => [memory.frontmatter.id, tokenizeSemanticContent(memory.content)])
  );
  const claimed = /* @__PURE__ */ new Set();
  for (let candidateIndex = 1; candidateIndex < ordered.length; candidateIndex += 1) {
    const candidate = ordered[candidateIndex];
    if (claimed.has(candidate.frontmatter.id)) continue;
    const candidateTokens = tokensByMemoryId.get(candidate.frontmatter.id) ?? [];
    if (candidateTokens.length < SEMANTIC_DUPLICATE_MIN_TOKENS) continue;
    const candidateNormalized = normalizeContent(candidate.content);
    for (let canonicalIndex = 0; canonicalIndex < candidateIndex; canonicalIndex += 1) {
      const canonical = ordered[canonicalIndex];
      if (!sameSemanticDuplicateScope(canonical, candidate)) continue;
      const canonicalNormalized = normalizeContent(canonical.content);
      if (canonicalNormalized === candidateNormalized) continue;
      const canonicalTokens = tokensByMemoryId.get(canonical.frontmatter.id) ?? [];
      if (canonicalTokens.length < SEMANTIC_DUPLICATE_MIN_TOKENS) continue;
      const shorter = Math.min(candidateTokens.length, canonicalTokens.length);
      const longer = Math.max(candidateTokens.length, canonicalTokens.length);
      if (shorter / longer < 0.6) continue;
      if (jaccardSimilarity(candidateTokens, canonicalTokens) < SEMANTIC_DUPLICATE_MIN_JACCARD) continue;
      reviewQueue.push({
        entryId: `review:${candidate.frontmatter.id}:semantic_duplicate_candidate`,
        memoryId: candidate.frontmatter.id,
        path: candidate.path,
        reasonCode: "semantic_duplicate_candidate",
        severity: "medium",
        suggestedAction: "set_status",
        suggestedStatus: "pending_review",
        relatedMemoryIds: [canonical.frontmatter.id]
      });
      claimed.add(candidate.frontmatter.id);
      break;
    }
  }
  return reviewQueue;
}
function buildExplicitCaptureReviewEntries(memories, lifecycleEvents) {
  const explicitQueuedIds = new Set(
    lifecycleEvents.filter((event) => event.eventType === "explicit_capture_queued").map((event) => event.memoryId)
  );
  return memories.filter((memory) => {
    if (statusOf(memory) !== "pending_review") return false;
    const tags = memory.frontmatter.tags ?? [];
    if (tags.includes("queued-review")) return true;
    if (explicitQueuedIds.has(memory.frontmatter.id)) return true;
    return memory.frontmatter.source === "explicit-review" || memory.frontmatter.source === "explicit-inline-review";
  }).map((memory) => ({
    entryId: `review:${memory.frontmatter.id}:explicit_capture_review`,
    memoryId: memory.frontmatter.id,
    path: memory.path,
    reasonCode: "explicit_capture_review",
    severity: "medium",
    suggestedAction: "set_status",
    suggestedStatus: "pending_review",
    relatedMemoryIds: []
  }));
}
async function listMarkdownFiles(root) {
  const files = [];
  const walk = async (dir) => {
    try {
      const entries = await readdir(dir, { withFileTypes: true });
      for (const entry of entries) {
        const fullPath = path.join(dir, entry.name);
        if (entry.isDirectory()) {
          await walk(fullPath);
          continue;
        }
        if (entry.isFile() && entry.name.endsWith(".md")) {
          files.push(fullPath);
        }
      }
    } catch {
    }
  };
  await walk(root);
  return files;
}
function malformedMemoryId(memoryDir, filePath) {
  return `malformed:${path.relative(memoryDir, filePath).replaceAll(path.sep, "/")}`;
}
async function buildMalformedImportEntries(memoryDir, storage, parsedMemories, candidateFiles) {
  const parsedPaths = new Set(parsedMemories.map((memory) => memory.path));
  const filesToInspect = candidateFiles ?? [
    ...await listMarkdownFiles(path.join(memoryDir, "facts")),
    ...await listMarkdownFiles(path.join(memoryDir, "corrections"))
  ];
  const entries = [];
  for (const filePath of filesToInspect) {
    if (parsedPaths.has(filePath)) continue;
    const parsed = await storage.readMemoryByPath(filePath);
    if (parsed) continue;
    entries.push({
      entryId: `review:${malformedMemoryId(memoryDir, filePath)}:malformed_import`,
      memoryId: malformedMemoryId(memoryDir, filePath),
      path: filePath,
      reasonCode: "malformed_import",
      severity: "high",
      suggestedAction: "set_status",
      suggestedStatus: "quarantined",
      relatedMemoryIds: []
    });
  }
  return entries;
}
async function buildReviewQueue(memoryDir, storage, memories, now, options = {}) {
  const reviewQueue = [];
  const activeMemories = memories.filter((memory) => statusOf(memory) === "active");
  const duplicateBuckets = /* @__PURE__ */ new Map();
  for (const memory of activeMemories) {
    const key = `${memory.frontmatter.category}:${normalizeContent(memory.content)}`;
    const bucket = duplicateBuckets.get(key) ?? [];
    bucket.push(memory);
    duplicateBuckets.set(key, bucket);
  }
  for (const bucket of duplicateBuckets.values()) {
    if (bucket.length < 2) continue;
    const ordered = [...bucket].sort(compareCanonicalPreference);
    const canonical = ordered[0];
    for (const duplicate of ordered.slice(1)) {
      reviewQueue.push({
        entryId: `review:${duplicate.frontmatter.id}:exact_duplicate`,
        memoryId: duplicate.frontmatter.id,
        path: duplicate.path,
        reasonCode: "exact_duplicate",
        severity: "medium",
        suggestedAction: "set_status",
        suggestedStatus: "pending_review",
        relatedMemoryIds: canonical ? [canonical.frontmatter.id] : []
      });
    }
  }
  reviewQueue.push(...buildSemanticDuplicateEntries(activeMemories));
  for (const memory of activeMemories) {
    if (memory.frontmatter.verificationState === "disputed") {
      reviewQueue.push({
        entryId: `review:${memory.frontmatter.id}:disputed_memory`,
        memoryId: memory.frontmatter.id,
        path: memory.path,
        reasonCode: "disputed_memory",
        severity: "high",
        suggestedAction: "set_status",
        suggestedStatus: "quarantined",
        relatedMemoryIds: []
      });
    }
    if (memory.frontmatter.confidenceTier === "speculative" && (memory.frontmatter.confidence ?? 0) <= 0.25) {
      reviewQueue.push({
        entryId: `review:${memory.frontmatter.id}:speculative_low_confidence`,
        memoryId: memory.frontmatter.id,
        path: memory.path,
        reasonCode: "speculative_low_confidence",
        severity: "medium",
        suggestedAction: "set_status",
        suggestedStatus: "pending_review",
        relatedMemoryIds: []
      });
    }
    const lifecycle = decideLifecycleTransition(memory, {}, now);
    const staleForArchive = memory.frontmatter.lifecycleState === "stale" && daysSince(memory.frontmatter.updated ?? memory.frontmatter.created, now) >= 180;
    if (lifecycle.nextState === "archived" && lifecycle.changed || staleForArchive) {
      reviewQueue.push({
        entryId: `review:${memory.frontmatter.id}:archive_candidate`,
        memoryId: memory.frontmatter.id,
        path: memory.path,
        reasonCode: "archive_candidate",
        severity: "low",
        suggestedAction: "archive",
        relatedMemoryIds: []
      });
    }
  }
  const lifecycleEvents = await storage.readMemoryLifecycleEvents(Number.MAX_SAFE_INTEGER);
  reviewQueue.push(...buildExplicitCaptureReviewEntries(memories, lifecycleEvents));
  reviewQueue.push(...await buildMalformedImportEntries(
    memoryDir,
    storage,
    memories,
    options.malformedCandidateFiles
  ));
  return reviewQueue;
}
function buildProposedActions(reviewQueue, memories) {
  const byMemory = new Map(memories.map((memory) => [memory.frontmatter.id, memory]));
  const selected = /* @__PURE__ */ new Map();
  for (const entry of reviewQueue) {
    const memory = byMemory.get(entry.memoryId);
    if (!memory) continue;
    const currentStatus = statusOf(memory);
    if (entry.suggestedAction === "set_status" && entry.suggestedStatus && entry.suggestedStatus === currentStatus) {
      continue;
    }
    const candidate = {
      action: entry.suggestedAction,
      memoryId: entry.memoryId,
      reasonCode: entry.reasonCode,
      beforeStatus: currentStatus,
      afterStatus: entry.suggestedStatus,
      originalPath: memory.path,
      currentPath: memory.path
    };
    const existing = selected.get(entry.memoryId);
    if (!existing) {
      selected.set(entry.memoryId, candidate);
      continue;
    }
    const existingPriority = proposedActionPriority(existing);
    const candidatePriority = proposedActionPriority(candidate);
    if (candidatePriority > existingPriority) {
      selected.set(entry.memoryId, candidate);
    }
  }
  return [...selected.values()];
}
function buildMetrics(reviewQueue, proposedActions, scannedMemories) {
  const reviewReasons = {
    exact_duplicate: 0,
    semantic_duplicate_candidate: 0,
    disputed_memory: 0,
    speculative_low_confidence: 0,
    archive_candidate: 0,
    explicit_capture_review: 0,
    malformed_import: 0
  };
  const proposedStatuses = {};
  for (const entry of reviewQueue) {
    reviewReasons[entry.reasonCode] += 1;
  }
  for (const action of proposedActions) {
    const effectiveStatus = action.afterStatus ?? (action.action === "archive" ? "archived" : void 0);
    if (!effectiveStatus) continue;
    proposedStatuses[effectiveStatus] = (proposedStatuses[effectiveStatus] ?? 0) + 1;
  }
  return {
    reviewReasons,
    proposedStatuses,
    keptMemoryCount: Math.max(0, scannedMemories - proposedActions.length),
    qualityScore: buildQualityScore(reviewReasons)
  };
}
function buildQualityScore(reviewReasons) {
  const deductions = Object.entries(reviewReasons).map(([reasonCode, count]) => ({
    reasonCode,
    count,
    pointsLost: count * QUALITY_SCORE_WEIGHTS[reasonCode]
  })).filter((entry) => entry.count > 0).sort((left, right) => right.pointsLost - left.pointsLost);
  const totalPointsLost = deductions.reduce((sum, entry) => sum + entry.pointsLost, 0);
  const score = Math.max(0, 100 - totalPointsLost);
  const grade = score >= 90 ? "excellent" : score >= 75 ? "good" : score >= 50 ? "fair" : "poor";
  return {
    score,
    maxScore: 100,
    grade,
    deductions
  };
}
function groupActionsByStatus(actions) {
  const grouped = {};
  for (const action of actions) {
    const status = action.afterStatus ?? (action.action === "archive" ? "archived" : "unchanged");
    const bucket = grouped[status] ?? [];
    bucket.push(action);
    grouped[status] = bucket;
  }
  return grouped;
}
async function safeRead(filePath) {
  try {
    return await readFile(filePath, "utf-8");
  } catch {
    return null;
  }
}
async function persistRestoreManifest(memoryDir, manifest) {
  const restorePath = governanceRestorePath(memoryDir, manifest.runId);
  await mkdir(path.dirname(restorePath), { recursive: true });
  await writeFile(restorePath, JSON.stringify(manifest, null, 2), "utf-8");
  return restorePath;
}
async function writeGovernanceArtifacts(options) {
  const runDir = governanceRunDir(options.memoryDir, options.runId);
  await mkdir(runDir, { recursive: true });
  const summaryPath = path.join(runDir, "summary.json");
  const reviewQueuePath = path.join(runDir, "review-queue.json");
  const qualityScorePath = path.join(runDir, "quality-score.json");
  const transitionReportPath = path.join(runDir, "status-transitions.json");
  const reportPath = path.join(runDir, "report.md");
  const keptMemoriesPath = path.join(runDir, "kept-memories.json");
  const appliedActionsPath = path.join(runDir, "applied-actions.json");
  const metricsPath = path.join(runDir, "metrics.json");
  const manifestPath = path.join(runDir, "manifest.json");
  const restorePath = options.restoreManifest ? governanceRestorePath(options.memoryDir, options.runId) : void 0;
  await writeFile(summaryPath, JSON.stringify(options.summary, null, 2), "utf-8");
  await writeFile(reviewQueuePath, JSON.stringify(options.reviewQueue, null, 2), "utf-8");
  await writeFile(qualityScorePath, JSON.stringify(options.qualityScore, null, 2), "utf-8");
  await writeFile(transitionReportPath, JSON.stringify(options.transitionReport, null, 2), "utf-8");
  await writeFile(keptMemoriesPath, JSON.stringify(options.keptMemoryIds, null, 2), "utf-8");
  await writeFile(appliedActionsPath, JSON.stringify(options.appliedActions, null, 2), "utf-8");
  await writeFile(metricsPath, JSON.stringify(options.metrics, null, 2), "utf-8");
  await writeFile(
    reportPath,
    [
      `# Memory Governance Run ${options.runId}`,
      "",
      `- Trace ID: ${options.traceId}`,
      `- Mode: ${options.summary.mode}`,
      `- Scanned memories: ${options.summary.scannedMemories}`,
      `- Kept memories: ${options.metrics.keptMemoryCount}`,
      `- Review queue entries: ${options.summary.reviewQueueCount}`,
      `- Proposed actions: ${options.summary.proposedActionCount}`,
      `- Applied actions: ${options.summary.appliedActionCount}`,
      `- Quality score: ${options.qualityScore.score}/${options.qualityScore.maxScore} (${options.qualityScore.grade})`,
      "",
      "## Metrics",
      ...Object.entries(options.metrics.reviewReasons).map(([reason, count]) => `- ${reason}: ${count}`),
      ...Object.entries(options.metrics.proposedStatuses).length > 0 ? Object.entries(options.metrics.proposedStatuses).map(([status, count]) => `- proposed ${status}: ${count}`) : ["- proposed statuses: (none)"],
      "",
      "## Quality Score",
      ...options.qualityScore.deductions.length > 0 ? options.qualityScore.deductions.map((entry) => `- ${entry.reasonCode}: ${entry.count} -> -${entry.pointsLost}`) : ["- no deductions"],
      "",
      "## Proposed Actions",
      ...options.proposedActions.length > 0 ? options.proposedActions.map(
        (action) => `- ${action.memoryId}: ${action.action}${action.afterStatus ? ` -> ${action.afterStatus}` : ""} [${action.reasonCode}]`
      ) : ["- (empty)"],
      "",
      "## Applied Actions",
      ...options.appliedActions.length > 0 ? options.appliedActions.map(
        (action) => `- ${action.memoryId}: ${action.action}${action.afterStatus ? ` -> ${action.afterStatus}` : ""} [${action.reasonCode}]`
      ) : ["- (empty)"],
      "",
      "## Review Queue",
      ...options.reviewQueue.length > 0 ? options.reviewQueue.map(
        (entry) => `- ${entry.memoryId}: ${entry.reasonCode} -> ${entry.suggestedAction}${entry.suggestedStatus ? ` (${entry.suggestedStatus})` : ""}`
      ) : ["- (empty)"]
    ].join("\n"),
    "utf-8"
  );
  const manifest = {
    schemaVersion: 1,
    runId: options.runId,
    traceId: options.traceId,
    mode: options.summary.mode,
    createdAt: options.summary.createdAt,
    ruleVersion: options.summary.ruleVersion,
    artifacts: {
      summary: summaryPath,
      reviewQueue: reviewQueuePath,
      qualityScore: qualityScorePath,
      transitionReport: transitionReportPath,
      report: reportPath,
      keptMemories: keptMemoriesPath,
      appliedActions: appliedActionsPath,
      metrics: metricsPath,
      ...restorePath ? { restore: restorePath } : {}
    }
  };
  await writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
  if (restorePath && options.restoreManifest && await safeRead(restorePath) === null) {
    await writeFile(restorePath, JSON.stringify(options.restoreManifest, null, 2), "utf-8");
  }
  return {
    summaryPath,
    reviewQueuePath,
    qualityScorePath,
    transitionReportPath,
    reportPath,
    keptMemoriesPath,
    appliedActionsPath,
    metricsPath,
    manifestPath,
    restorePath
  };
}
async function runMemoryGovernance(options) {
  const now = options.now ?? /* @__PURE__ */ new Date();
  const runId = buildRunId(now);
  const traceId = runId;
  const storage = new StorageManager(options.memoryDir);
  const boundedScan = options.maxMemories !== void 0 || options.recentDays !== void 0 || options.batchSize !== void 0;
  const normalizedRecentDays = typeof options.recentDays === "number" && Number.isFinite(options.recentDays) ? Math.max(1, Math.floor(options.recentDays)) : void 0;
  const updatedAfter = normalizedRecentDays !== void 0 ? new Date(now.getTime() - normalizedRecentDays * 864e5) : void 0;
  const memoryWindow = boundedScan ? await storage.readMemoriesWindow({
    maxMemories: options.maxMemories,
    batchSize: options.batchSize,
    updatedAfter
  }) : void 0;
  const memories = memoryWindow?.memories ?? await storage.readAllMemories();
  const reviewQueue = await buildReviewQueue(options.memoryDir, storage, memories, now, {
    malformedCandidateFiles: memoryWindow?.filePaths
  });
  const proposedActions = buildProposedActions(reviewQueue, memories);
  const reviewEntryByActionKey = new Map(
    reviewQueue.map((entry) => [`${entry.memoryId}:${entry.reasonCode}`, entry])
  );
  const metrics = buildMetrics(reviewQueue, proposedActions, memories.length);
  const transitionReport = {
    proposed: groupActionsByStatus(proposedActions),
    applied: {}
  };
  const memoryPathById = new Map(memories.map((memory) => [memory.frontmatter.id, memory.path]));
  const targetedMemoryIds = new Set(proposedActions.map((action) => action.memoryId));
  const keptMemoryIds = memories.map((memory) => memory.frontmatter.id).filter((memoryId) => !targetedMemoryIds.has(memoryId));
  const appliedActions = [];
  const restoreEntries = [];
  const restoreEntryByMemoryId = /* @__PURE__ */ new Map();
  if (options.mode === "apply") {
    for (const action of proposedActions) {
      const memoryPath = memoryPathById.get(action.memoryId) ?? null;
      if (!memoryPath) continue;
      const memory = await storage.readMemoryByPath(memoryPath);
      if (!memory) continue;
      if (memory.frontmatter.id !== action.memoryId) continue;
      const beforeRaw = await safeRead(memory.path);
      if (!beforeRaw) continue;
      const entry = {
        action: action.action,
        memoryId: action.memoryId,
        reasonCode: action.reasonCode,
        originalPath: memory.path,
        currentPath: action.action === "archive" ? plannedArchivePath(options.memoryDir, memory.path, now) : memory.path,
        beforeRaw,
        applied: false
      };
      restoreEntries.push(entry);
      restoreEntryByMemoryId.set(action.memoryId, entry);
    }
    const restoreManifest2 = {
      runId,
      createdAt: now.toISOString(),
      entries: restoreEntries
    };
    await persistRestoreManifest(options.memoryDir, restoreManifest2);
    for (const action of proposedActions) {
      const memoryPath = memoryPathById.get(action.memoryId) ?? null;
      if (!memoryPath) continue;
      const memory = await storage.readMemoryByPath(memoryPath);
      if (!memory) continue;
      if (memory.frontmatter.id !== action.memoryId) continue;
      const restoreEntry = restoreEntryByMemoryId.get(action.memoryId);
      if (!restoreEntry) continue;
      if (action.action === "archive") {
        const reviewEntry2 = reviewEntryByActionKey.get(`${action.memoryId}:${action.reasonCode}`);
        const archivedPath = await storage.archiveMemory(memory, {
          at: now,
          actor: "memory-governance.apply",
          reasonCode: action.reasonCode,
          ruleVersion: RULE_VERSION,
          relatedMemoryIds: reviewEntry2?.relatedMemoryIds ?? [],
          correlationId: traceId
        });
        if (!archivedPath) continue;
        restoreEntry.currentPath = archivedPath;
        restoreEntry.expectedCurrentRaw = await safeRead(archivedPath) ?? void 0;
        restoreEntry.applied = true;
        await persistRestoreManifest(options.memoryDir, restoreManifest2);
        appliedActions.push({
          ...action,
          currentPath: archivedPath,
          afterStatus: "archived"
        });
        continue;
      }
      if (!action.afterStatus || action.beforeStatus === action.afterStatus) continue;
      const reviewEntry = reviewEntryByActionKey.get(`${action.memoryId}:${action.reasonCode}`);
      const updated = await storage.writeMemoryFrontmatter(memory, {
        status: action.afterStatus,
        updated: now.toISOString()
      }, {
        actor: "memory-governance.apply",
        reasonCode: action.reasonCode,
        ruleVersion: RULE_VERSION,
        relatedMemoryIds: reviewEntry?.relatedMemoryIds ?? [],
        correlationId: traceId
      });
      if (!updated) continue;
      restoreEntry.expectedCurrentRaw = await safeRead(memory.path) ?? void 0;
      restoreEntry.applied = true;
      await persistRestoreManifest(options.memoryDir, restoreManifest2);
      appliedActions.push({
        ...action,
        currentPath: memory.path
      });
    }
  }
  const summary = {
    schemaVersion: 1,
    runId,
    traceId,
    mode: options.mode,
    createdAt: now.toISOString(),
    scannedMemories: memories.length,
    reviewQueueCount: reviewQueue.length,
    proposedActionCount: proposedActions.length,
    appliedActionCount: appliedActions.length,
    ruleVersion: RULE_VERSION
  };
  const restoreManifest = options.mode === "apply" ? {
    runId,
    createdAt: now.toISOString(),
    entries: restoreEntries
  } : void 0;
  transitionReport.applied = groupActionsByStatus(appliedActions);
  const paths = await writeGovernanceArtifacts({
    memoryDir: options.memoryDir,
    runId,
    traceId,
    summary,
    metrics,
    qualityScore: metrics.qualityScore,
    transitionReport,
    keptMemoryIds,
    reviewQueue,
    proposedActions,
    appliedActions,
    restoreManifest
  });
  return {
    runId,
    traceId,
    mode: options.mode,
    summary,
    ...paths,
    reviewQueue,
    proposedActions,
    appliedActions
  };
}
async function restoreMemoryGovernanceRun(options) {
  void options.now;
  const restorePath = governanceRestorePath(options.memoryDir, options.runId);
  const raw = JSON.parse(await readFile(restorePath, "utf-8"));
  let restoredActions = 0;
  for (const entry of [...raw.entries].reverse()) {
    if (!entry.applied) {
      continue;
    }
    const currentRaw = await safeRead(entry.currentPath);
    if (entry.expectedCurrentRaw && currentRaw !== entry.expectedCurrentRaw) {
      throw new Error(`restore conflict for ${entry.memoryId}: current contents diverged from governance run`);
    }
    if (entry.action === "archive") {
      await rm(entry.currentPath, { force: true });
    }
    await mkdir(path.dirname(entry.originalPath), { recursive: true });
    await writeFile(entry.originalPath, entry.beforeRaw, "utf-8");
    restoredActions += 1;
  }
  return {
    runId: raw.runId,
    restoredActions,
    restorePath
  };
}
async function listMemoryGovernanceRuns(memoryDir) {
  try {
    return (await readdir(governanceRunsDir(memoryDir))).sort().reverse();
  } catch {
    return [];
  }
}
async function readMemoryGovernanceRunArtifact(memoryDir, runId) {
  const runDir = governanceRunDir(memoryDir, runId);
  const summary = JSON.parse(await readFile(path.join(runDir, "summary.json"), "utf-8"));
  const metrics = JSON.parse(await readFile(path.join(runDir, "metrics.json"), "utf-8"));
  metrics.qualityScore ??= buildQualityScore(metrics.reviewReasons);
  const keptMemoryIds = JSON.parse(await readFile(path.join(runDir, "kept-memories.json"), "utf-8"));
  const reviewQueue = JSON.parse(
    await readFile(path.join(runDir, "review-queue.json"), "utf-8")
  );
  const appliedActions = JSON.parse(
    await readFile(path.join(runDir, "applied-actions.json"), "utf-8")
  );
  const qualityScoreRaw = await safeRead(path.join(runDir, "quality-score.json"));
  const transitionReportRaw = await safeRead(path.join(runDir, "status-transitions.json"));
  const manifest = JSON.parse(
    await readFile(path.join(runDir, "manifest.json"), "utf-8")
  );
  const report = await readFile(path.join(runDir, "report.md"), "utf-8");
  const restoreRaw = await safeRead(path.join(runDir, "restore.json"));
  const qualityScore = qualityScoreRaw ? JSON.parse(qualityScoreRaw) : metrics.qualityScore ?? buildQualityScore(metrics.reviewReasons);
  const transitionReport = transitionReportRaw ? JSON.parse(transitionReportRaw) : {
    proposed: {},
    applied: groupActionsByStatus(appliedActions)
  };
  return {
    summary,
    metrics,
    qualityScore,
    keptMemoryIds,
    reviewQueue,
    appliedActions,
    transitionReport,
    report,
    manifest,
    restore: restoreRaw ? JSON.parse(restoreRaw) : void 0
  };
}

export {
  buildProposedActions,
  buildQualityScore,
  groupActionsByStatus,
  runMemoryGovernance,
  restoreMemoryGovernanceRun,
  listMemoryGovernanceRuns,
  readMemoryGovernanceRunArtifact
};
//# sourceMappingURL=chunk-EYNQTST2.js.map