import {
  isDocumentationType
} from "./chunk-2D2VOCA4.js";
import {
  resolveStateDir
} from "./chunk-NRAXROED.js";
import {
  runGates
} from "./chunk-GDNSBQVK.js";
import {
  WUStateStore,
  WU_OPTIONS,
  createWUParser,
  defaultBranchFrom,
  formatPreflightResult,
  formatPreflightWarnings,
  getCurrentBranch,
  getLatestWuBriefEvidence,
  getWuBriefEvidenceAgeMinutes,
  hasMatchingWuBriefEvidenceHash,
  isWuBriefEvidenceStale,
  resolveLocation,
  runCLI,
  validatePreflight
} from "./chunk-2GXVIN57.js";
import {
  DelegationRegistryStore
} from "./chunk-6W5XHWYV.js";
import {
  DOCS_ONLY_ROOT_FILES,
  TEST_FILE_PATTERNS,
  findMissingCodePathCoverage,
  getDocsOnlyPrefixes,
  isCodePathCoveredByChangedFiles,
  resolveChangedFiles,
  validateExposure,
  validateFeatureAccessibility
} from "./chunk-L737LQ4C.js";
import {
  createGitForPath
} from "./chunk-2UFQ3A3C.js";
import {
  createSignal
} from "./chunk-OMJD6A3S.js";
import {
  readWU
} from "./chunk-NRIZR3A7.js";
import {
  WU_PATHS,
  createWuPaths
} from "./chunk-6HO4GWJE.js";
import {
  CLI_FLAGS,
  CONTEXT_VALIDATION,
  DIRECTORIES,
  EMOJI,
  EXIT_CODES,
  LOG_PREFIX,
  LUMENFLOW_PATHS,
  PATTERNS,
  PKG_MANAGER,
  SCRIPTS
} from "./chunk-DWMLTXKQ.js";
import {
  CLAIMED_MODES,
  GIT_DIRECTORY_NAME,
  MS_PER_DAY,
  WU_EXPOSURE,
  WU_STATUS,
  WU_TYPES,
  getConfig
} from "./chunk-V6OJGLBA.js";
import {
  die,
  getErrorMessage
} from "./chunk-RXRKBBSM.js";

// src/wu-prep.ts
import { spawnSync as spawnSync2 } from "child_process";
import { existsSync as existsSync2 } from "fs";
import path3 from "path";
import { fileURLToPath } from "url";

// ../core/dist/wu-checkpoint.js
import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync } from "fs";
import path from "path";
import crypto from "crypto";
var CHECKPOINT_SCHEMA_VERSION = 1;
var CHECKPOINT_DIR = "checkpoints";
var CHECKPOINT_MAX_AGE_MS = MS_PER_DAY;
function getCheckpointPath(wuId, options = {}) {
  const baseDir = options.baseDir || process.cwd();
  return path.join(baseDir, LUMENFLOW_PATHS.BASE, CHECKPOINT_DIR, `${wuId}.checkpoint.json`);
}
function ensureCheckpointDir(options = {}) {
  const baseDir = options.baseDir || process.cwd();
  const checkpointDir = path.join(baseDir, LUMENFLOW_PATHS.BASE, CHECKPOINT_DIR);
  if (!existsSync(checkpointDir)) {
    mkdirSync(checkpointDir, { recursive: true });
  }
}
function generateCheckpointId() {
  return `ckpt-${crypto.randomUUID().slice(0, 8)}`;
}
function getHeadSha(dir) {
  try {
    const gitDir = path.join(dir, GIT_DIRECTORY_NAME);
    if (existsSync(gitDir)) {
      const headPath = path.join(gitDir, "HEAD");
      if (existsSync(headPath)) {
        const headContent = readFileSync(headPath, "utf8").trim();
        if (headContent.startsWith("ref: ")) {
          const refPath = path.join(gitDir, headContent.slice(5));
          if (existsSync(refPath)) {
            return readFileSync(refPath, "utf8").trim();
          }
        }
        return headContent;
      }
    }
    const gitFile = path.join(dir, GIT_DIRECTORY_NAME);
    if (existsSync(gitFile)) {
      const gitFileContent = readFileSync(gitFile, "utf8").trim();
      if (gitFileContent.startsWith("gitdir: ")) {
        const worktreeGitDir = gitFileContent.slice(8);
        const headPath = path.join(worktreeGitDir, "HEAD");
        if (existsSync(headPath)) {
          const headContent = readFileSync(headPath, "utf8").trim();
          if (headContent.startsWith("ref: ")) {
            const mainGitDir = path.resolve(worktreeGitDir, "..", "..");
            const refPath = path.join(mainGitDir, headContent.slice(5));
            if (existsSync(refPath)) {
              return readFileSync(refPath, "utf8").trim();
            }
          }
          return headContent;
        }
      }
    }
    return "unknown-sha";
  } catch {
    return "unknown-sha";
  }
}
async function createPreGatesCheckpoint(params, options = {}) {
  const { wuId, worktreePath, branchName, gatesPassed = false } = params;
  const { baseDir } = options;
  ensureCheckpointDir({ baseDir });
  const checkpoint = {
    schemaVersion: CHECKPOINT_SCHEMA_VERSION,
    checkpointId: generateCheckpointId(),
    wuId,
    worktreePath,
    branchName,
    worktreeHeadSha: getHeadSha(worktreePath),
    createdAt: (/* @__PURE__ */ new Date()).toISOString(),
    gatesPassed,
    gatesPassedAt: gatesPassed ? (/* @__PURE__ */ new Date()).toISOString() : null
  };
  const checkpointPath = getCheckpointPath(wuId, { baseDir });
  writeFileSync(checkpointPath, JSON.stringify(checkpoint, null, 2));
  console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Created pre-gates checkpoint for ${wuId}`);
  return checkpoint;
}
function markGatesPassed(wuId, options = {}) {
  const checkpoint = getCheckpoint(wuId, options);
  if (!checkpoint) {
    return false;
  }
  checkpoint.gatesPassed = true;
  checkpoint.gatesPassedAt = (/* @__PURE__ */ new Date()).toISOString();
  const checkpointPath = getCheckpointPath(wuId, options);
  writeFileSync(checkpointPath, JSON.stringify(checkpoint, null, 2));
  console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Marked gates passed for ${wuId}`);
  return true;
}
function getCheckpoint(wuId, options = {}) {
  const checkpointPath = getCheckpointPath(wuId, options);
  if (!existsSync(checkpointPath)) {
    return null;
  }
  try {
    const content = readFileSync(checkpointPath, "utf8");
    return JSON.parse(content);
  } catch {
    return null;
  }
}
function clearCheckpoint(wuId, options = {}) {
  const checkpointPath = getCheckpointPath(wuId, options);
  if (existsSync(checkpointPath)) {
    unlinkSync(checkpointPath);
    console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Cleared checkpoint for ${wuId}`);
  }
}
function canSkipGates(wuId, options = {}) {
  const { baseDir, currentHeadSha } = options;
  const checkpoint = getCheckpoint(wuId, { baseDir });
  if (!checkpoint) {
    return { canSkip: false, reason: "No checkpoint exists" };
  }
  if (checkpoint.schemaVersion !== CHECKPOINT_SCHEMA_VERSION) {
    return {
      canSkip: false,
      reason: `Checkpoint schema version mismatch (got ${checkpoint.schemaVersion}, expected ${CHECKPOINT_SCHEMA_VERSION})`
    };
  }
  if (!checkpoint.gatesPassed) {
    return { canSkip: false, reason: "Gates did not pass at checkpoint" };
  }
  const checkpointAge = Date.now() - new Date(checkpoint.createdAt).getTime();
  if (checkpointAge > CHECKPOINT_MAX_AGE_MS) {
    return { canSkip: false, reason: "Checkpoint is stale (older than 24 hours)" };
  }
  if (currentHeadSha && currentHeadSha !== checkpoint.worktreeHeadSha) {
    return {
      canSkip: false,
      reason: "Worktree has changed since checkpoint (SHA mismatch)"
    };
  }
  console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Gates can be skipped - checkpoint valid (${checkpoint.checkpointId})`);
  return { canSkip: true, checkpoint };
}

// src/hooks/path-utils.ts
import * as path2 from "path";
import * as os from "os";
var GIT_STATUS_QUOTE = '"';
var PATH_PREFIX_CURRENT_DIR = "./";
var PATH_SEPARATOR_WINDOWS = "\\";
var PATH_SEPARATOR_POSIX = "/";
function ensureRepoRelativePrefix(value) {
  const normalized = value.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
  return normalized.length > 0 ? `${normalized}/` : "";
}
function stripWrappingQuotes(value) {
  if (value.startsWith(GIT_STATUS_QUOTE) && value.endsWith(GIT_STATUS_QUOTE) && value.length >= 2) {
    return value.slice(1, -1);
  }
  return value;
}
function normalizeRepoRelativePath(value) {
  const withoutQuotes = stripWrappingQuotes(value.trim());
  const normalizedSeparators = withoutQuotes.split(PATH_SEPARATOR_WINDOWS).join(PATH_SEPARATOR_POSIX);
  if (normalizedSeparators.startsWith(PATH_PREFIX_CURRENT_DIR)) {
    return normalizedSeparators.slice(PATH_PREFIX_CURRENT_DIR.length);
  }
  return normalizedSeparators;
}

// src/hooks/config-resolver.ts
var MAIN_WRITE_STATIC_ALLOWLIST_PREFIXES = [".lumenflow/", ".claude/", "plan/"];
var DEFAULT_WORKTREES_DIR_SEGMENT = DIRECTORIES.WORKTREES.replace(/\/+$/g, "");
var DEFAULT_WU_ALLOWLIST_PREFIX = `${DIRECTORIES.WU_DIR.replace(/\/+$/g, "")}/`;
function resolveWuAllowlistPrefix(mainRepoPath) {
  try {
    const configuredPath = createWuPaths({ projectRoot: mainRepoPath }).WU_DIR();
    return ensureRepoRelativePrefix(configuredPath);
  } catch {
    return DEFAULT_WU_ALLOWLIST_PREFIX;
  }
}
function resolveMainWriteAllowlistPrefixes(mainRepoPath) {
  return [resolveWuAllowlistPrefix(mainRepoPath), ...MAIN_WRITE_STATIC_ALLOWLIST_PREFIXES];
}

// src/hooks/git-status-parser.ts
var GIT_STATUS_PREFIX_LENGTH = 3;
var GIT_STATUS_RENAME_SEPARATOR = " -> ";
var MAX_BLOCKED_PATHS_IN_MESSAGE = 10;
function parsePathFromStatusLine(line) {
  if (line.length < GIT_STATUS_PREFIX_LENGTH) {
    return null;
  }
  const pathField = line.slice(GIT_STATUS_PREFIX_LENGTH).trim();
  if (pathField.length === 0) {
    return null;
  }
  const renameSegments = pathField.split(GIT_STATUS_RENAME_SEPARATOR);
  const destinationPath = renameSegments[renameSegments.length - 1];
  const normalizedPath = normalizeRepoRelativePath(destinationPath);
  return normalizedPath.length > 0 ? normalizedPath : null;
}
function parseDirtyPathsFromStatus(mainStatus) {
  const uniquePaths = /* @__PURE__ */ new Set();
  for (const line of mainStatus.split("\n")) {
    const trimmed = line.trimEnd();
    if (trimmed.length === 0) {
      continue;
    }
    const parsed = parsePathFromStatusLine(trimmed);
    if (parsed) {
      uniquePaths.add(parsed);
    }
  }
  return Array.from(uniquePaths);
}
function getNonAllowlistedDirtyPaths(mainStatus, allowlistPrefixes) {
  return parseDirtyPathsFromStatus(mainStatus).filter(
    (relativePath) => !allowlistPrefixes.some((prefix) => relativePath.startsWith(prefix))
  );
}
function formatBlockedPaths(paths) {
  const displayed = paths.slice(0, MAX_BLOCKED_PATHS_IN_MESSAGE);
  const lines = displayed.map((dirtyPath) => `  - ${dirtyPath}`);
  const remainder = paths.length - displayed.length;
  if (remainder > 0) {
    lines.push(`  - ... and ${remainder} more`);
  }
  return lines.join("\n");
}
function formatMainDirtyMutationGuardMessage(options) {
  const { commandName, mainCheckout, blockedPaths, allowlistPrefixes } = options;
  const allowlistLines = allowlistPrefixes.map((prefix) => `  - ${prefix}`).join("\n");
  return `${commandName} blocked: main checkout has non-allowlisted dirty files while a worktree WU is active.

Dirty paths:
${formatBlockedPaths(blockedPaths)}

Allowed dirty prefixes on main:
${allowlistLines}

How to resolve:
  1. Move edits into the active worktree (recommended)
  2. Revert or commit unintended main edits
  3. If writes came from MCP/tools, rerun them in the worktree path
  4. Retry ${commandName}

Main checkout: ${mainCheckout}`;
}

// src/hooks/dirty-guard.ts
var DIRTY_MAIN_GUARD_REASONS = {
  BRANCH_PR_MODE: "branch-pr-mode",
  NO_WORKTREE_CONTEXT: "no-worktree-context",
  CLEAN_OR_ALLOWLISTED: "clean-or-allowlisted",
  BLOCKED_NON_ALLOWLISTED_DIRTY_MAIN: "blocked-non-allowlisted-dirty-main"
};
function evaluateMainDirtyMutationGuard(options) {
  const { commandName, mainCheckout, mainStatus, hasActiveWorktreeContext, isBranchPrMode: isBranchPrMode2 } = options;
  const allowlistPrefixes = resolveMainWriteAllowlistPrefixes(mainCheckout);
  if (isBranchPrMode2) {
    return {
      blocked: false,
      blockedPaths: [],
      reason: DIRTY_MAIN_GUARD_REASONS.BRANCH_PR_MODE
    };
  }
  if (!hasActiveWorktreeContext) {
    return {
      blocked: false,
      blockedPaths: [],
      reason: DIRTY_MAIN_GUARD_REASONS.NO_WORKTREE_CONTEXT
    };
  }
  const blockedPaths = getNonAllowlistedDirtyPaths(mainStatus, allowlistPrefixes);
  if (blockedPaths.length === 0) {
    return {
      blocked: false,
      blockedPaths: [],
      reason: DIRTY_MAIN_GUARD_REASONS.CLEAN_OR_ALLOWLISTED
    };
  }
  return {
    blocked: true,
    blockedPaths,
    reason: DIRTY_MAIN_GUARD_REASONS.BLOCKED_NON_ALLOWLISTED_DIRTY_MAIN,
    message: formatMainDirtyMutationGuardMessage({
      commandName,
      mainCheckout,
      blockedPaths,
      allowlistPrefixes
    })
  };
}

// src/wu-done-policies.ts
var WU_BRIEF_POLICY_MODES = ["off", "manual", "auto", "required"];
var DEFAULT_WU_BRIEF_POLICY_MODE = "auto";
var DEFAULT_WU_BRIEF_FRESHNESS_MINUTES = 1440;
var PREP_LOG_PREFIX = "[wu-prep]";
var PREP_FORCE_REASON_REQUIRED_MESSAGE = "Missing required --reason for wu:brief policy bypass in wu:prep.";
function printExposureWarnings(wu, options = {}) {
  const result = validateExposure(wu, { skipExposureCheck: options.skipExposureCheck });
  if (result.warnings.length > 0) {
    console.log(`
${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1999: Exposure validation warnings:`);
    for (const warning of result.warnings) {
      console.log(`${LOG_PREFIX.DONE}   ${warning}`);
    }
    console.log(
      `${LOG_PREFIX.DONE} These are non-blocking warnings. To skip, use --skip-exposure-check flag.
`
    );
  }
}
function validateAccessibilityOrDie(wu, options = {}) {
  const result = validateFeatureAccessibility(wu, {
    skipAccessibilityCheck: options.skipAccessibilityCheck
  });
  if (!result.valid) {
    console.log(
      `
${LOG_PREFIX.DONE} ${EMOJI.FAILURE} WU-2022: Feature accessibility validation failed`
    );
    die(
      `\u274C FEATURE ACCESSIBILITY VALIDATION FAILED (WU-2022)

Cannot complete wu:done - UI feature accessibility not verified.

${result.errors.join("\n\n")}

This gate prevents "orphaned code" - features that exist but users cannot access.`
    );
  }
}
function validateDocsOnlyFlag(wu, args) {
  if (!args.docsOnly) {
    return { valid: true, errors: [] };
  }
  const wuId = wu.id || "unknown";
  const exposure = wu.exposure;
  const type = wu.type;
  const codePaths = wu.code_paths;
  if (exposure === WU_EXPOSURE.DOCUMENTATION) {
    return { valid: true, errors: [] };
  }
  if (isDocumentationType(type)) {
    return { valid: true, errors: [] };
  }
  const docsOnlyPrefixes = getDocsOnlyPrefixes().map((prefix) => prefix.toLowerCase());
  const isDocsPath = (p) => {
    const normalizedPath = p.trim().toLowerCase();
    for (const prefix of docsOnlyPrefixes) {
      if (normalizedPath.startsWith(prefix)) {
        return true;
      }
    }
    if (normalizedPath.endsWith(".md")) {
      return true;
    }
    for (const pattern of DOCS_ONLY_ROOT_FILES) {
      if (normalizedPath.startsWith(pattern)) {
        return true;
      }
    }
    return false;
  };
  if (codePaths && Array.isArray(codePaths) && codePaths.length > 0) {
    const allDocsOnly = codePaths.every((p) => typeof p === "string" && isDocsPath(p));
    if (allDocsOnly) {
      return { valid: true, errors: [] };
    }
  }
  const currentExposure = exposure || "not set";
  const currentType = type || "not set";
  return {
    valid: false,
    errors: [
      `--docs-only flag used on ${wuId} but WU is not documentation-focused.

Current exposure: ${currentExposure}
Current type: ${currentType}

--docs-only requires one of:
  1. exposure: documentation
  2. type: documentation
  3. All code_paths under configured docs prefixes (${docsOnlyPrefixes.join(", ")}), or *.md files

To fix, either:
  - Remove --docs-only flag and run full gates
  - Change WU exposure to 'documentation' if this is truly a docs-only change`
    ]
  };
}
function buildGatesCommand(options) {
  const { docsOnly = false, isDocsOnly = false } = options;
  if (docsOnly || isDocsOnly) {
    return `${PKG_MANAGER} ${SCRIPTS.GATES} -- ${CLI_FLAGS.DOCS_ONLY}`;
  }
  return `${PKG_MANAGER} ${SCRIPTS.GATES}`;
}
function shouldEnforceWuBriefEvidence(doc) {
  return doc.type === WU_TYPES.FEATURE || doc.type === WU_TYPES.BUG;
}
function isWuBriefPolicyMode(value) {
  return typeof value === "string" && WU_BRIEF_POLICY_MODES.includes(value);
}
function resolveWuBriefPolicyMode(config = getConfig()) {
  const configured = config.wu?.brief?.policyMode;
  if (isWuBriefPolicyMode(configured)) {
    return configured;
  }
  return DEFAULT_WU_BRIEF_POLICY_MODE;
}
function resolveWuBriefFreshnessMinutes(config = getConfig()) {
  const configured = config.wu?.brief?.freshnessMinutes;
  if (typeof configured === "number" && Number.isFinite(configured)) {
    if (configured <= 0) {
      return null;
    }
    return Math.floor(configured);
  }
  return DEFAULT_WU_BRIEF_FRESHNESS_MINUTES;
}
function buildMissingWuBriefEvidenceMessage(id) {
  return `Missing wu:brief evidence for ${id}.

Completion policy requires an auditable wu:brief execution record for feature/bug WUs.

Fix options:
  1. If you are delegating this WU, generate handoff prompt + evidence:
     pnpm wu:brief --id ${id}
  2. If you are implementing this WU yourself, record evidence only:
     pnpm wu:brief --id ${id} --evidence-only
  3. Retry completion:
     pnpm wu:done --id ${id}
  4. Legacy/manual override (audited):
     pnpm wu:done --id ${id} --force`;
}
function buildMissingWuBriefEvidenceMessageForPrep(id, mode) {
  return `Missing wu:brief evidence for ${id} (policy=${mode}).

wu:prep enforces wu:brief evidence when policy mode is required.

Fix options:
  1. Record evidence by generating a brief prompt:
     pnpm wu:brief --id ${id}
  2. If self-implementing, record evidence only:
     pnpm wu:brief --id ${id} --evidence-only
  3. Retry prep:
     pnpm wu:prep --id ${id}
  4. Emergency audited bypass (requires explicit reason):
     pnpm wu:prep --id ${id} --force --reason "<why bypass is required>"`;
}
function describeWuBriefFreshness(evidenceTimestamp, freshnessMinutes, now) {
  const ageMinutes = getWuBriefEvidenceAgeMinutes(evidenceTimestamp, now);
  if (ageMinutes === null) {
    return `Evidence timestamp is invalid: ${evidenceTimestamp}`;
  }
  return `Evidence age is ${ageMinutes} minute(s); threshold is ${freshnessMinutes} minute(s).`;
}
function buildStaleWuBriefEvidenceMessageForPrep(options) {
  return `Stale wu:brief evidence for ${options.id} (policy=${options.mode}).

${describeWuBriefFreshness(options.evidenceTimestamp, options.freshnessMinutes, options.now)}

Fix options:
  1. Refresh evidence:
     pnpm wu:brief --id ${options.id}
  2. If self-implementing, refresh evidence only:
     pnpm wu:brief --id ${options.id} --evidence-only
  3. Retry prep:
     pnpm wu:prep --id ${options.id}
  4. Emergency audited bypass (requires explicit reason):
     pnpm wu:prep --id ${options.id} --force --reason "<why stale evidence bypass is required>"`;
}
function buildStaleWuBriefEvidenceMessageForDone(options) {
  return `Stale wu:brief evidence for ${options.id} (policy=${options.mode}).

${describeWuBriefFreshness(options.evidenceTimestamp, options.freshnessMinutes, options.now)}

Fix options:
  1. Refresh evidence:
     pnpm wu:brief --id ${options.id}
  2. If self-implementing, refresh evidence only:
     pnpm wu:brief --id ${options.id} --evidence-only
  3. Retry completion:
     pnpm wu:done --id ${options.id}
  4. Legacy/manual override (audited):
     pnpm wu:done --id ${options.id} --force`;
}
async function recordWuBriefPrepBypassAudit(options) {
  const baseDir = options.baseDir ?? process.cwd();
  const stateDir = resolveStateDir(baseDir);
  const store = new WUStateStore(stateDir);
  await store.checkpoint(
    options.wuId,
    `[wu:brief] prep force bypass accepted (${options.policyMode}): ${options.reason}`,
    {
      progress: "wu:brief policy bypass",
      nextSteps: `policy=${options.policyMode}`
    }
  );
}
async function enforceWuBriefEvidenceForPrep(id, doc, options = {}) {
  if (!shouldEnforceWuBriefEvidence(doc)) {
    return;
  }
  const mode = options.mode ?? resolveWuBriefPolicyMode();
  if (mode === "off" || mode === "manual") {
    return;
  }
  const baseDir = options.baseDir ?? process.cwd();
  const stateDir = resolveStateDir(baseDir);
  const force = options.force === true;
  const getBriefEvidenceFn = options.getBriefEvidenceFn ?? getLatestWuBriefEvidence;
  const blocker = options.blocker ?? ((message) => die(message));
  const warn = options.warn ?? console.warn;
  const recordBypassAudit = options.recordBypassAudit ?? recordWuBriefPrepBypassAudit;
  const freshnessMinutes = options.freshnessMinutes === void 0 ? resolveWuBriefFreshnessMinutes() : options.freshnessMinutes;
  const now = options.now ?? /* @__PURE__ */ new Date();
  let evidence;
  try {
    evidence = await getBriefEvidenceFn(stateDir, id);
  } catch (error) {
    if (mode === "required" && !force) {
      blocker(buildWuBriefEvidenceReadFailureMessage(id, stateDir, error));
      return;
    }
    warn(
      `${PREP_LOG_PREFIX} ${EMOJI.WARNING} Could not verify wu:brief evidence for ${id}: ${getErrorMessage(error)}`
    );
    return;
  }
  if (evidence && freshnessMinutes !== null && isWuBriefEvidenceStale({ timestamp: evidence.timestamp, freshnessMinutes, now })) {
    if (mode === "auto") {
      warn(`${PREP_LOG_PREFIX} ${EMOJI.WARNING} wu:brief evidence stale for ${id} (policy=auto).`);
      warn(
        buildStaleWuBriefEvidenceMessageForPrep({
          id,
          mode,
          evidenceTimestamp: evidence.timestamp,
          freshnessMinutes,
          now
        })
      );
      return;
    }
    if (!force) {
      blocker(
        buildStaleWuBriefEvidenceMessageForPrep({
          id,
          mode,
          evidenceTimestamp: evidence.timestamp,
          freshnessMinutes,
          now
        })
      );
      return;
    }
    const reason2 = typeof options.reason === "string" ? options.reason.trim() : "";
    if (!reason2) {
      blocker(PREP_FORCE_REASON_REQUIRED_MESSAGE);
      return;
    }
    await recordBypassAudit({ wuId: id, baseDir, reason: reason2, policyMode: mode });
    warn(
      `${PREP_LOG_PREFIX} ${EMOJI.WARNING} wu:brief policy override accepted for ${id} (policy=${mode}, reason="${reason2}").`
    );
    return;
  }
  if (evidence) {
    return;
  }
  if (mode === "auto") {
    warn(`${PREP_LOG_PREFIX} ${EMOJI.WARNING} wu:brief evidence missing for ${id} (policy=auto).`);
    warn(buildMissingWuBriefEvidenceMessageForPrep(id, mode));
    return;
  }
  if (!force) {
    blocker(buildMissingWuBriefEvidenceMessageForPrep(id, mode));
    return;
  }
  const reason = typeof options.reason === "string" ? options.reason.trim() : "";
  if (!reason) {
    blocker(PREP_FORCE_REASON_REQUIRED_MESSAGE);
    return;
  }
  await recordBypassAudit({ wuId: id, baseDir, reason, policyMode: mode });
  warn(
    `${PREP_LOG_PREFIX} ${EMOJI.WARNING} wu:brief policy override accepted for ${id} (policy=${mode}, reason="${reason}").`
  );
}
function buildWuBriefEvidenceReadFailureMessage(id, stateDir, error) {
  return `Could not verify wu:brief evidence for ${id}.

State path: ${stateDir}
Error: ${getErrorMessage(error)}

Fix options:
  1. Repair/restore state store, then rerun wu:done
  2. Use --force for audited override when recovery is not possible`;
}
async function enforceWuBriefEvidenceForDone(id, doc, options = {}) {
  if (!shouldEnforceWuBriefEvidence(doc)) {
    return;
  }
  const baseDir = options.baseDir ?? process.cwd();
  const mode = options.mode ?? resolveWuBriefPolicyMode();
  const force = options.force === true;
  const freshnessMinutes = options.freshnessMinutes === void 0 ? resolveWuBriefFreshnessMinutes() : options.freshnessMinutes;
  const now = options.now ?? /* @__PURE__ */ new Date();
  const stateDir = resolveStateDir(baseDir);
  const getBriefEvidenceFn = options.getBriefEvidenceFn ?? getLatestWuBriefEvidence;
  const blocker = options.blocker ?? ((message) => die(message));
  const warn = options.warn ?? console.warn;
  let evidence;
  try {
    evidence = await getBriefEvidenceFn(stateDir, id);
  } catch (error) {
    if (!force) {
      blocker(buildWuBriefEvidenceReadFailureMessage(id, stateDir, error));
      return;
    }
    warn(
      `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2132: brief evidence verification failed for ${id}, override accepted via --force`
    );
    return;
  }
  if (evidence && freshnessMinutes !== null && isWuBriefEvidenceStale({ timestamp: evidence.timestamp, freshnessMinutes, now })) {
    if (mode === "off" || mode === "manual") {
      return;
    }
    if (mode === "auto") {
      warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2289: stale wu:brief evidence for ${id}`);
      warn(
        buildStaleWuBriefEvidenceMessageForDone({
          id,
          mode,
          evidenceTimestamp: evidence.timestamp,
          freshnessMinutes,
          now
        })
      );
      return;
    }
    if (!force) {
      blocker(
        buildStaleWuBriefEvidenceMessageForDone({
          id,
          mode,
          evidenceTimestamp: evidence.timestamp,
          freshnessMinutes,
          now
        })
      );
      return;
    }
    warn(
      `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2289: stale brief evidence override accepted for ${id} via --force`
    );
    return;
  }
  if (evidence) {
    return;
  }
  if (!force) {
    blocker(buildMissingWuBriefEvidenceMessage(id));
    return;
  }
  warn(
    `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2132: brief evidence override accepted for ${id} via --force`
  );
}
function shouldEnforceSpawnProvenance(doc) {
  return typeof doc?.initiative === "string" && doc.initiative.trim().length > 0;
}
function buildMissingSpawnProvenanceMessage(id, initiativeId) {
  return `Missing spawn provenance for initiative-governed WU ${id} (${initiativeId}).

This completion path enforces auditable delegation lineage for initiative work.

Fix options:
  1. Re-run with --force for an audited override (legacy/manual workflow)
  2. Register spawn lineage before completion (preferred):
     pnpm wu:delegate --id ${id} --parent-wu WU-XXXX --client codex-cli

Then retry: pnpm wu:done --id ${id}`;
}
function buildMissingSpawnPickupEvidenceMessage(id, initiativeId) {
  return `Missing pickup evidence for initiative-governed WU ${id} (${initiativeId}).

Delegation intent exists, but this WU has no claim-time pickup handshake.
Completion policy requires both intent and pickup evidence.

Fix options:
  1. Re-run with --force for an audited override (legacy/manual claim)
  2. Ensure future delegated work is picked up via wu:claim (records handshake automatically)

Then retry: pnpm wu:done --id ${id}`;
}
function buildMissingSpawnBriefAttestationMessage(id, initiativeId) {
  return `Missing brief attestation for delegated WU ${id} (${initiativeId}).

Delegation provenance exists, but no attested wu:brief payload hash was stored.
Completion policy now requires auditable prompt-hash attestation.

Fix options:
  1. Re-run delegation from parent WU using full wu:delegate output:
     pnpm wu:delegate --id ${id} --parent-wu WU-XXXX --client codex-cli
  2. Re-run with --force for audited legacy override

Then retry: pnpm wu:done --id ${id}`;
}
function buildSpawnBriefAttestationMismatchMessage(id, initiativeId) {
  return `Spawn brief attestation mismatch for delegated WU ${id} (${initiativeId}).

Stored delegation hash could not be matched to wu:brief evidence for this WU.
This indicates the delegated prompt was modified, condensed, or not generated through wu:delegate.

Fix options:
  1. Re-run delegation using full wu:delegate output for ${id}
  2. Re-run with --force for audited legacy override

Then retry: pnpm wu:done --id ${id}`;
}
function hasSpawnPickupEvidence(spawnEntry) {
  const pickedUpAt = typeof spawnEntry?.pickedUpAt === "string" && spawnEntry.pickedUpAt.trim().length > 0 ? spawnEntry.pickedUpAt : "";
  const pickedUpBy = typeof spawnEntry?.pickedUpBy === "string" && spawnEntry.pickedUpBy.trim().length > 0 ? spawnEntry.pickedUpBy : "";
  return pickedUpAt.length > 0 && pickedUpBy.length > 0;
}
function hasDelegationIntent(spawnEntry) {
  return typeof spawnEntry?.intent === "string" && spawnEntry.intent.trim() === "delegation";
}
function hasSpawnBriefAttestation(spawnEntry) {
  const attestation = spawnEntry?.briefAttestation;
  if (!attestation) {
    return false;
  }
  return attestation.algorithm === "sha256" && typeof attestation.promptHash === "string" && /^[a-f0-9]{64}$/.test(attestation.promptHash);
}
async function recordSpawnProvenanceOverride(id, doc, baseDir = process.cwd()) {
  try {
    const initiativeId = typeof doc?.initiative === "string" ? doc.initiative.trim() : "unknown";
    const lane = typeof doc?.lane === "string" ? doc.lane : void 0;
    const result = await createSignal(baseDir, {
      message: `spawn-provenance override used for ${id} in ${initiativeId} via --force`,
      wuId: id,
      lane
    });
    if (result.success) {
      console.log(
        `${LOG_PREFIX.DONE} ${EMOJI.INFO} Spawn-provenance override recorded (${result.signal.id})`
      );
    }
  } catch (err) {
    console.warn(
      `${LOG_PREFIX.DONE} ${EMOJI.WARNING} Could not record spawn-provenance override: ${getErrorMessage(err)}`
    );
  }
}
async function enforceSpawnProvenanceForDone(id, doc, options = {}) {
  const initiativeId = typeof doc.initiative === "string" && doc.initiative.trim() ? doc.initiative.trim() : "unknown";
  const enforceForInitiative = shouldEnforceSpawnProvenance(doc);
  const baseDir = options.baseDir ?? process.cwd();
  const force = options.force === true;
  const stateDir = resolveStateDir(baseDir);
  const store = new DelegationRegistryStore(stateDir);
  await store.load();
  const spawnEntry = store.getByTarget(id);
  const enforceForDelegation = hasDelegationIntent(spawnEntry);
  if (!enforceForInitiative && !enforceForDelegation) {
    return;
  }
  if (!spawnEntry) {
    if (!force) {
      die(buildMissingSpawnProvenanceMessage(id, initiativeId));
    }
    console.warn(
      `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1599: spawn provenance override accepted for ${id} (${initiativeId}) via --force`
    );
    await recordSpawnProvenanceOverride(id, doc, baseDir);
    return;
  }
  if (hasSpawnPickupEvidence(spawnEntry)) {
    if (!hasSpawnBriefAttestation(spawnEntry)) {
      if (!force) {
        die(buildMissingSpawnBriefAttestationMessage(id, initiativeId));
      }
      console.warn(
        `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2301: brief attestation override accepted for ${id} (${initiativeId}) via --force`
      );
      await recordSpawnProvenanceOverride(id, doc, baseDir);
      return;
    }
    const attestedPromptHash = spawnEntry.briefAttestation?.promptHash;
    if (!attestedPromptHash) {
      if (!force) {
        die(buildMissingSpawnBriefAttestationMessage(id, initiativeId));
      }
      console.warn(
        `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2301: missing prompt hash override accepted for ${id} (${initiativeId}) via --force`
      );
      await recordSpawnProvenanceOverride(id, doc, baseDir);
      return;
    }
    let hashMatchesEvidence;
    try {
      hashMatchesEvidence = await hasMatchingWuBriefEvidenceHash(stateDir, id, attestedPromptHash);
    } catch (error) {
      if (!force) {
        die(
          `Could not verify spawn brief attestation for ${id}.

State path: ${stateDir}
Error: ${getErrorMessage(error)}

Fix options:
  1. Repair/restore state store, then rerun wu:done
  2. Use --force for audited override when recovery is not possible`
        );
      }
      console.warn(
        `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2301: attestation verification failure override accepted for ${id} (${initiativeId}) via --force`
      );
      await recordSpawnProvenanceOverride(id, doc, baseDir);
      return;
    }
    if (hashMatchesEvidence) {
      return;
    }
    if (!force) {
      die(buildSpawnBriefAttestationMismatchMessage(id, initiativeId));
    }
    console.warn(
      `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-2301: attestation mismatch override accepted for ${id} (${initiativeId}) via --force`
    );
    await recordSpawnProvenanceOverride(id, doc, baseDir);
    return;
  }
  if (!force) {
    die(buildMissingSpawnPickupEvidenceMessage(id, initiativeId));
  }
  console.warn(
    `${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1605: pickup evidence override accepted for ${id} (${initiativeId}) via --force`
  );
  await recordSpawnProvenanceOverride(id, doc, baseDir);
}

// src/wu-code-path-coverage.ts
import { spawnSync } from "child_process";
var DEFAULT_BASE_REF = "main";
var DEFAULT_HEAD_REF = "HEAD";
var GIT_DIFF_STDIO = ["pipe", "pipe", "pipe"];
function isCodePathCoveredByChanges(options) {
  return isCodePathCoveredByChangedFiles(options);
}
function findMissingCodePathCoverage2(options) {
  return findMissingCodePathCoverage(options);
}
function checkCodePathCoverageBeforeGates(options) {
  const {
    codePaths = [],
    cwd,
    baseRef = DEFAULT_BASE_REF,
    headRef = DEFAULT_HEAD_REF,
    spawnSyncFn = spawnSync
  } = options;
  const scopedCodePaths = codePaths.filter((codePath) => typeof codePath === "string").map((codePath) => codePath.trim()).filter(Boolean);
  if (scopedCodePaths.length === 0) {
    return {
      valid: true,
      missingCodePaths: [],
      changedFiles: []
    };
  }
  const range = `${baseRef}...${headRef}`;
  const diffResult = spawnSyncFn("git", ["diff", "--name-only", range], {
    cwd,
    encoding: "utf-8",
    stdio: GIT_DIFF_STDIO
  });
  if ((diffResult.status ?? EXIT_CODES.ERROR) !== EXIT_CODES.SUCCESS) {
    const stderrText = String(diffResult.stderr ?? "").trim();
    const errorText = stderrText || (diffResult.error instanceof Error ? diffResult.error.message : "");
    return {
      valid: false,
      missingCodePaths: scopedCodePaths,
      changedFiles: [],
      error: errorText || `git diff --name-only ${range} failed`
    };
  }
  const changedFiles = String(diffResult.stdout ?? "").split("\n").map((line) => line.trim()).filter(Boolean);
  const missingCodePaths = findMissingCodePathCoverage2({
    codePaths: scopedCodePaths,
    changedFiles
  });
  return {
    valid: missingCodePaths.length === 0,
    missingCodePaths,
    changedFiles
  };
}
function formatCodePathCoverageFailure(options) {
  const { wuId, missingCodePaths, changedFiles, error } = options;
  const missingSection = missingCodePaths.map((filePath) => `  - ${filePath}`).join("\n");
  const changedSection = changedFiles.length > 0 ? changedFiles.map((filePath) => `  - ${filePath}`).join("\n") : "  - (none)";
  const diffErrorSection = error ? `
Unable to evaluate branch diff:
  ${error}
` : "";
  return `${EMOJI.FAILURE} code_paths preflight failed for ${wuId}.
${diffErrorSection}
The following code_paths are not modified on this branch (vs main):
${missingSection}

Changed files detected on branch:
${changedSection}

Fix options:
  1. Commit changes that touch each missing code_path
  2. Update WU scope to match actual branch work:
     pnpm wu:edit --id ${wuId} --replace-code-paths --code-paths "<path1>" --code-paths "<path2>"
  3. Re-run: pnpm wu:prep --id ${wuId}`;
}

// src/wu-prep.ts
var { LOCATION_TYPES } = CONTEXT_VALIDATION;
var TDD_EXCEPTION_MARKER = "tdd-exception:";
var PREP_PREFIX = "[wu-prep]";
var PREP_OPTIONS = {
  docsOnly: {
    name: "docsOnly",
    flags: "--docs-only",
    description: "Run docs-only gates (format, spec-linter)"
  },
  fullTests: {
    name: "fullTests",
    flags: "--full-tests",
    description: "Run full incremental test suite (disable tests.unit scoped execution)"
  },
  force: WU_OPTIONS.force,
  reason: WU_OPTIONS.reason
};
function resolveScopedUnitTestsForPrep(options) {
  if (options.fullTests) {
    return [];
  }
  const unitTestsRaw = options.tests?.unit;
  if (!Array.isArray(unitTestsRaw)) {
    return [];
  }
  return unitTestsRaw.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean);
}
function shouldEnforceTddDiffEvidence(doc) {
  return doc.type === WU_TYPES.FEATURE || doc.type === WU_TYPES.BUG;
}
function hasDocumentedTddException(notes) {
  if (typeof notes === "string") {
    return notes.toLowerCase().includes(TDD_EXCEPTION_MARKER);
  }
  if (Array.isArray(notes)) {
    return notes.some((entry) => typeof entry === "string" && hasDocumentedTddException(entry));
  }
  return false;
}
function normalizeDeclaredAutomatedTestPaths(tests) {
  const buckets = ["unit", "e2e", "integration"];
  const paths = [];
  for (const bucket of buckets) {
    const value = tests?.[bucket];
    if (!Array.isArray(value)) {
      continue;
    }
    for (const entry of value) {
      if (typeof entry !== "string") {
        continue;
      }
      const normalized = entry.trim();
      if (normalized.length === 0) {
        continue;
      }
      paths.push(normalized);
    }
  }
  return paths;
}
function isTestFileFromDiff(filePath) {
  const normalized = filePath.replace(/\\/g, "/");
  return TEST_FILE_PATTERNS.some((pattern) => pattern.test(normalized));
}
function hasTddEvidenceInWorkingDiff(options) {
  const { changedFiles, declaredTestPaths } = options;
  if (changedFiles.some((filePath) => isTestFileFromDiff(filePath))) {
    return true;
  }
  for (const testPath of declaredTestPaths) {
    if (isCodePathCoveredByChangedFiles({ codePath: testPath, changedFiles })) {
      return true;
    }
  }
  return false;
}
async function evaluateTddDiffEvidenceForPrep(options) {
  const { doc } = options;
  if (!shouldEnforceTddDiffEvidence(doc)) {
    return { valid: true, reason: "not-applicable", changedFiles: [] };
  }
  if (hasDocumentedTddException(doc.notes)) {
    return { valid: true, reason: "documented-exception", changedFiles: [] };
  }
  const resolveChangedFilesFn = options.resolveChangedFilesFn ?? resolveChangedFiles;
  const changedFilesResult = await resolveChangedFilesFn({ cwd: options.cwd });
  if (!changedFilesResult.ok) {
    return {
      valid: false,
      reason: "diff-unavailable",
      changedFiles: [],
      diffReason: changedFilesResult.reason
    };
  }
  const declaredTestPaths = normalizeDeclaredAutomatedTestPaths(doc.tests ?? {});
  const hasEvidence = hasTddEvidenceInWorkingDiff({
    changedFiles: changedFilesResult.files,
    declaredTestPaths
  });
  if (hasEvidence) {
    return {
      valid: true,
      reason: "test-diff-evidence-found",
      changedFiles: changedFilesResult.files,
      baseRef: changedFilesResult.baseRef,
      headRef: changedFilesResult.headRef
    };
  }
  return {
    valid: false,
    reason: "missing-test-diff-evidence",
    changedFiles: changedFilesResult.files,
    baseRef: changedFilesResult.baseRef,
    headRef: changedFilesResult.headRef
  };
}
function formatTddDiffEvidenceFailure(options) {
  const { wuId, result } = options;
  if (result.reason === "diff-unavailable") {
    return `${EMOJI.FAILURE} TDD provenance check failed for ${wuId}.

Unable to evaluate branch diff for test evidence: ${result.diffReason || "unknown diff error"}

Fix options:
  1. Ensure base ref is available (origin/main or main)
  2. Retry: pnpm wu:prep --id ${wuId}`;
  }
  const changedSection = result.changedFiles.length > 0 ? result.changedFiles.map((filePath) => `  - ${filePath}`).join("\n") : "  - (none)";
  return `${EMOJI.FAILURE} Missing TDD diff evidence for ${wuId}.

Feature/bug WUs must touch at least one automated test file before wu:prep.

Changed files (${result.baseRef || "main"}...${result.headRef || "HEAD"}):
${changedSection}

Fix options:
  1. Add/update test files in this branch and retry wu:prep
  2. Add an explicit exception marker in WU notes when test edits are not expected:
     ${TDD_EXCEPTION_MARKER} <reason>`;
}
function isPreExistingSpecLinterFailure(gateName) {
  const normalizedName = gateName.toLowerCase().replace(/[:-]/g, "");
  return normalizedName === "speclinter";
}
function formatSkipGatesCommand(options) {
  const { wuId, mainCheckout } = options;
  return `cd ${mainCheckout} && pnpm wu:done --id ${wuId} --skip-gates --reason "pre-existing on main" --fix-wu WU-XXXX`;
}
function defaultExecOnMain(mainCheckout) {
  return async (cmd) => {
    const distDir = path3.dirname(fileURLToPath(import.meta.url));
    const wuValidateDist = path3.join(distDir, "wu-validate.js");
    const shouldUseDistWuValidate = cmd.includes("wu:validate") && cmd.includes("--json") && existsSync2(wuValidateDist);
    if (shouldUseDistWuValidate) {
      const result2 = spawnSync2("node", [wuValidateDist, "--all", "--json"], {
        cwd: mainCheckout,
        encoding: "utf-8",
        stdio: ["pipe", "pipe", "pipe"]
      });
      return {
        exitCode: result2.status ?? 1,
        stdout: result2.stdout ?? "",
        stderr: result2.stderr ?? ""
      };
    }
    const parts = cmd.split(/\s+/);
    const executable = parts[0];
    const args = parts.slice(1);
    const result = spawnSync2(executable, args, {
      cwd: mainCheckout,
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"]
    });
    return {
      exitCode: result.status ?? 1,
      stdout: result.stdout ?? "",
      stderr: result.stderr ?? ""
    };
  };
}
function runScopedValidation({ wuId, cwd }) {
  const result = spawnSync2("pnpm", ["wu:validate", "--id", wuId], {
    cwd,
    encoding: "utf-8",
    stdio: "inherit"
  });
  return (result.status ?? 1) === EXIT_CODES.SUCCESS;
}
function parseSpecLinterReport(output) {
  try {
    const parsed = JSON.parse(output.trim());
    return parsed;
  } catch {
    return null;
  }
}
function classifySpecLinterFailures(options) {
  const mainSet = new Set(options.mainInvalid);
  const worktreeSet = new Set(options.worktreeInvalid);
  const preExistingFailures = [...worktreeSet].filter((id) => mainSet.has(id));
  const newFailures = [...worktreeSet].filter((id) => !mainSet.has(id));
  return {
    hasPreExisting: preExistingFailures.length > 0,
    hasNewFailures: newFailures.length > 0,
    newFailures,
    preExistingFailures
  };
}
async function runSpecLinterJson(execOnCwd) {
  const result = await execOnCwd("pnpm --silent wu:validate --all --json");
  const report = parseSpecLinterReport(result.stdout);
  if (!report || !Array.isArray(report.invalid)) {
    return {
      invalidIds: [],
      error: "Failed to parse wu:validate JSON output."
    };
  }
  const invalidIds = report.invalid.map((item) => item?.wuId).filter((id) => typeof id === "string");
  return { invalidIds };
}
async function checkPreExistingFailures(options) {
  const {
    mainCheckout,
    execOnMain = defaultExecOnMain(mainCheckout),
    execOnWorktree = defaultExecOnMain(process.cwd())
  } = options;
  try {
    const worktreeResult = await runSpecLinterJson(execOnWorktree);
    const mainResult = await runSpecLinterJson(execOnMain);
    if (worktreeResult.error || mainResult.error) {
      return {
        hasPreExisting: false,
        hasNewFailures: false,
        newFailures: [],
        preExistingFailures: [],
        error: worktreeResult.error || mainResult.error
      };
    }
    const classification = classifySpecLinterFailures({
      mainInvalid: mainResult.invalidIds,
      worktreeInvalid: worktreeResult.invalidIds
    });
    return {
      ...classification,
      error: void 0
    };
  } catch (error) {
    return {
      hasPreExisting: false,
      hasNewFailures: false,
      newFailures: [],
      preExistingFailures: [],
      error: error.message
    };
  }
}
function isBranchPrMode(doc) {
  return doc.claimed_mode === CLAIMED_MODES.BRANCH_PR;
}
function validateBranchPrBranch(options) {
  const { currentBranch, expectedBranch } = options;
  if (currentBranch === expectedBranch) {
    return { valid: true };
  }
  return {
    valid: false,
    error: `Current branch '${currentBranch}' does not match expected lane branch '${expectedBranch}'.

Switch to the lane branch first:
  git checkout ${expectedBranch}`
  };
}
function formatBranchPrSuccessMessage(options) {
  const { wuId, laneBranch } = options;
  return `${PREP_PREFIX} ${EMOJI.SUCCESS} ${wuId}: Prep completed successfully!

${PREP_PREFIX} Gates passed on branch ${laneBranch}.

${PREP_PREFIX} Next steps for branch-pr mode:

  1. Push the branch:  git push origin ${laneBranch}
  2. Create a PR:      gh pr create --base main --head ${laneBranch}
  3. After PR merge:   pnpm wu:cleanup --id ${wuId}
`;
}
function evaluatePrepMainMutationGuard(options) {
  return evaluateMainDirtyMutationGuard({
    commandName: "wu:prep",
    mainCheckout: options.mainCheckout,
    mainStatus: options.mainStatus,
    hasActiveWorktreeContext: !options.isBranchPr,
    isBranchPrMode: options.isBranchPr
  });
}
function printSuccessMessage(wuId, mainCheckout) {
  console.log("");
  console.log(`${PREP_PREFIX} ${EMOJI.SUCCESS} ${wuId}: Prep completed successfully!`);
  console.log("");
  console.log(`${PREP_PREFIX} Gates passed in worktree.`);
  console.log("");
  console.log(`${PREP_PREFIX} Next step - copy and paste this command:`);
  console.log("");
  console.log(`    cd ${mainCheckout} && pnpm wu:done --id ${wuId}`);
  console.log("");
}
async function createPrepCheckpoint(params) {
  await createPreGatesCheckpoint(
    {
      wuId: params.wuId,
      worktreePath: params.worktreePath,
      branchName: params.branchName,
      gatesPassed: true
    },
    {}
  );
}
async function main() {
  const args = createWUParser({
    name: "wu-prep",
    description: "Prepare WU for completion (run gates in worktree)",
    options: [
      WU_OPTIONS.id,
      PREP_OPTIONS.docsOnly,
      PREP_OPTIONS.fullTests,
      PREP_OPTIONS.force,
      PREP_OPTIONS.reason
    ],
    required: ["id"],
    allowPositionalId: true
  });
  const id = args.id.toUpperCase();
  if (!PATTERNS.WU_ID.test(id)) {
    die(`Invalid WU id '${args.id}'. Expected format WU-123`);
  }
  const location = await resolveLocation();
  const wuPath = WU_PATHS.WU(id);
  let doc;
  try {
    doc = readWU(wuPath, id);
  } catch (error) {
    die(
      `Failed to read WU ${id}: ${error.message}

Options:
  1. Check if WU file exists: ls -la ${wuPath}
  2. Validate YAML syntax: pnpm wu:validate --id ${id}`
    );
  }
  const branchPr = isBranchPrMode(doc);
  if (branchPr) {
    const expectedBranch = defaultBranchFrom(doc);
    const currentBranch = getCurrentBranch();
    if (!expectedBranch) {
      die(
        `${EMOJI.FAILURE} Cannot determine lane branch for ${id}.

Check that the WU has a valid lane and id in its YAML spec.`
      );
    }
    if (!currentBranch) {
      die(
        `${EMOJI.FAILURE} Cannot determine current git branch.

Ensure you are in a git repository.`
      );
    }
    const branchCheck = validateBranchPrBranch({
      currentBranch,
      expectedBranch
    });
    if (!branchCheck.valid) {
      die(`${EMOJI.FAILURE} ${branchCheck.error}`);
    }
    console.log(`${PREP_PREFIX} branch-pr mode detected for ${id}`);
  } else {
    if (location.type !== LOCATION_TYPES.WORKTREE) {
      die(
        `${EMOJI.FAILURE} wu:prep must be run from a worktree, not ${location.type}.

Current location: ${location.cwd}

If you have a worktree for ${id}, navigate to it first:
  cd worktrees/<lane>-${id.toLowerCase()}

If you don't have a worktree yet, claim the WU first:
  pnpm wu:claim --id ${id} --lane "<lane>"`
      );
    }
    if (location.worktreeWuId && location.worktreeWuId !== id) {
      console.warn(
        `${PREP_PREFIX} ${EMOJI.WARNING} Worktree is for ${location.worktreeWuId}, but you specified ${id}.`
      );
      console.warn(`${PREP_PREFIX} Proceeding with ${id} as specified.`);
    }
  }
  if (doc.status !== WU_STATUS.IN_PROGRESS) {
    die(
      `${EMOJI.FAILURE} WU ${id} status is '${doc.status}', expected '${WU_STATUS.IN_PROGRESS}'.

wu:prep can only be run on WUs that are in progress.`
    );
  }
  await enforceWuBriefEvidenceForPrep(id, doc, {
    baseDir: location.cwd,
    mode: resolveWuBriefPolicyMode(),
    freshnessMinutes: resolveWuBriefFreshnessMinutes(),
    force: Boolean(args.force),
    reason: typeof args.reason === "string" ? args.reason : void 0
  });
  if (!branchPr) {
    const mainStatus = await createGitForPath(location.mainCheckout).getStatus();
    const mainMutationGuard = evaluatePrepMainMutationGuard({
      mainCheckout: location.mainCheckout,
      isBranchPr: branchPr,
      mainStatus
    });
    if (mainMutationGuard.blocked) {
      die(mainMutationGuard.message ?? `${EMOJI.FAILURE} wu:prep blocked by dirty-main guard.`);
    }
  }
  console.log(`${PREP_PREFIX} Preparing ${id} for completion...`);
  console.log(`${PREP_PREFIX} Location: ${location.cwd}`);
  console.log(`${PREP_PREFIX} Main checkout: ${location.mainCheckout}`);
  console.log("");
  console.log(`${PREP_PREFIX} Running scoped validation for ${id}...`);
  const scopedOk = runScopedValidation({ wuId: id, cwd: location.cwd });
  if (!scopedOk) {
    die(
      `${EMOJI.FAILURE} Scoped validation failed for ${id}.

Fix the WU spec issues and rerun wu:prep.`
    );
  }
  console.log(`${PREP_PREFIX} Running reality preflight checks...`);
  const realityPreflight = await validatePreflight(id, {
    rootDir: location.cwd,
    worktreePath: location.cwd,
    phase: "reality"
  });
  if (!realityPreflight.valid) {
    die(formatPreflightResult(id, realityPreflight));
  }
  if (Array.isArray(realityPreflight.warnings) && realityPreflight.warnings.length > 0) {
    const warningLines = formatPreflightWarnings(
      realityPreflight.warnings,
      `${PREP_PREFIX} ${EMOJI.WARNING} Reality preflight warnings:`
    );
    for (const line of warningLines) {
      console.log(line.startsWith("  - ") ? `${PREP_PREFIX} ${line}` : line);
    }
  }
  const tddEvidenceResult = await evaluateTddDiffEvidenceForPrep({
    wuId: id,
    doc,
    cwd: location.cwd
  });
  if (!tddEvidenceResult.valid) {
    die(formatTddDiffEvidenceFailure({ wuId: id, result: tddEvidenceResult }));
  }
  console.log(`${PREP_PREFIX} Checking for pre-existing spec:linter failures on main...`);
  const preExistingCheck = await checkPreExistingFailures({
    mainCheckout: location.mainCheckout
  });
  const hasPreExistingOnly = preExistingCheck.hasPreExisting && !preExistingCheck.hasNewFailures;
  if (preExistingCheck.error) {
    console.log(
      `${PREP_PREFIX} ${EMOJI.WARNING} Unable to compare spec:linter results: ${preExistingCheck.error}`
    );
  } else if (hasPreExistingOnly) {
    console.log(`${PREP_PREFIX} ${EMOJI.WARNING} Pre-existing failures detected on main.`);
    process.on("exit", (code) => {
      if (code !== EXIT_CODES.SUCCESS) {
        console.log("");
        console.log(
          `${PREP_PREFIX} ${EMOJI.WARNING} Since failures are pre-existing on main, you can skip gates:`
        );
        console.log("");
        console.log(
          `    ${formatSkipGatesCommand({ wuId: id, mainCheckout: location.mainCheckout })}`
        );
        console.log("");
        console.log(`${PREP_PREFIX} Replace WU-XXXX with the WU that will fix these spec issues.`);
        console.log("");
      }
    });
  } else if (preExistingCheck.hasNewFailures) {
    console.log(
      `${PREP_PREFIX} ${EMOJI.WARNING} New spec:linter failures detected in worktree: ${preExistingCheck.newFailures.join(
        ", "
      )}`
    );
  } else {
    console.log(`${PREP_PREFIX} No pre-existing failures on main.`);
  }
  console.log("");
  const scopedUnitTests = resolveScopedUnitTestsForPrep({
    fullTests: args.fullTests,
    tests: doc.tests
  });
  if (args.fullTests) {
    console.log(
      `${PREP_PREFIX} --full-tests enabled; using default incremental/full test gate flow.`
    );
  } else if (scopedUnitTests.length > 0) {
    console.log(
      `${PREP_PREFIX} Using scoped tests from tests.unit (${scopedUnitTests.length} path${scopedUnitTests.length === 1 ? "" : "s"}).`
    );
  }
  console.log(`${PREP_PREFIX} Running gates...`);
  const gatesResult = await runGates({
    cwd: location.cwd,
    docsOnly: args.docsOnly,
    fullTests: args.fullTests,
    scopedTestPaths: scopedUnitTests
  });
  if (!gatesResult) {
    if (!hasPreExistingOnly) {
      die(`${EMOJI.FAILURE} Gates failed.

Fix the gate failures and run wu:prep again.`);
    }
    process.exit(EXIT_CODES.ERROR);
  }
  const branchName = defaultBranchFrom(doc) ?? getCurrentBranch() ?? "";
  await createPrepCheckpoint({ wuId: id, worktreePath: location.cwd, branchName });
  if (branchPr) {
    const laneBranch = defaultBranchFrom(doc) ?? "";
    const message = formatBranchPrSuccessMessage({ wuId: id, laneBranch });
    console.log("");
    console.log(message);
  } else {
    printSuccessMessage(id, location.mainCheckout);
  }
}
if (import.meta.main) {
  void runCLI(main);
}

export {
  createPreGatesCheckpoint,
  markGatesPassed,
  clearCheckpoint,
  canSkipGates,
  evaluateMainDirtyMutationGuard,
  printExposureWarnings,
  validateAccessibilityOrDie,
  validateDocsOnlyFlag,
  buildGatesCommand,
  shouldEnforceWuBriefEvidence,
  buildMissingWuBriefEvidenceMessage,
  enforceWuBriefEvidenceForDone,
  shouldEnforceSpawnProvenance,
  buildMissingSpawnProvenanceMessage,
  buildMissingSpawnPickupEvidenceMessage,
  hasSpawnPickupEvidence,
  enforceSpawnProvenanceForDone,
  isCodePathCoveredByChanges,
  findMissingCodePathCoverage2 as findMissingCodePathCoverage,
  checkCodePathCoverageBeforeGates,
  formatCodePathCoverageFailure,
  resolveScopedUnitTestsForPrep,
  shouldEnforceTddDiffEvidence,
  hasDocumentedTddException,
  hasTddEvidenceInWorkingDiff,
  evaluateTddDiffEvidenceForPrep,
  isPreExistingSpecLinterFailure,
  formatSkipGatesCommand,
  classifySpecLinterFailures,
  checkPreExistingFailures,
  isBranchPrMode,
  validateBranchPrBranch,
  formatBranchPrSuccessMessage,
  evaluatePrepMainMutationGuard,
  createPrepCheckpoint,
  main
};
