UNPKG

paqad-ai

Version:

Spec-driven development framework — AI agents that think before they type

4,378 lines • 154 kB
import { createRequire as __paqadCreateRequire } from 'module'; const require = __paqadCreateRequire(import.meta.url);
import {
  cachedMessageUsage,
  computeChangeCost,
  messageUsd,
  readChangeCost,
  readCostCache,
  roundUsd
} from "./chunk-4GDYY56G.js";
import {
  rulesDriftHeadline
} from "./chunk-URTQMBPY.js";
import {
  CHIEF_ROLE,
  addTokenUsage,
  emptyTokenUsage,
  foldHighTable,
  highTableDoneTimes,
  maxTokenUsage,
  readHighTable
} from "./chunk-YZTSRIE3.js";
import {
  stageLedgerEra
} from "./chunk-WKGFBXSZ.js";
import {
  hasOpenUnendedStage
} from "./chunk-FVFH4UAE.js";
import {
  episodeStart,
  readRouteTurns
} from "./chunk-FUBOIUNU.js";
import {
  PAQAD_VERDICT
} from "./chunk-VZJTQQG2.js";
import {
  aggregateSpecPipelineMetrics,
  designSignOffApplies,
  isFrameworkEnabledForRoot,
  loadDesignSignOff,
  readFeaturePlan,
  readFeatureReview,
  readFeatureSpecification,
  readRuleStopRecord,
  readRulesLoaded,
  resolveRuleComplianceMode,
  sessionWroteInEpisode
} from "./chunk-3AKVAXRJ.js";
import {
  STAGE_AGENT_HOSTS,
  resolveFlooredMode
} from "./chunk-KXN6TRQP.js";
import {
  latestPost,
  readHld
} from "./chunk-LGAFOBYN.js";
import {
  StackPackLoader
} from "./chunk-K4E6ACLH.js";
import {
  readAnyDecisionPacket,
  readContractDecisions
} from "./chunk-4ZAWKFXT.js";
import {
  appendHooksLog
} from "./chunk-4S3XFJAH.js";
import {
  readProjectProfile
} from "./chunk-AK3HYELM.js";
import {
  hudCostCachePath,
  hudReceiptPath,
  hudSnapshotPath,
  hudVerdictAgreementPath,
  hudVerdictPath,
  writeFileAtomic
} from "./chunk-3UV6TK6X.js";
import {
  readDuplication,
  readFeatureEvidence,
  readRuleRun
} from "./chunk-5ETIY5I3.js";
import {
  isTestFile
} from "./chunk-EQYAXXZO.js";
import {
  BUNDLE_MANIFEST,
  isBundleFileRequired,
  isSpecFrozenBeforePipeline,
  readSpecAdoption
} from "./chunk-OAEANL22.js";
import {
  readWorkflowState
} from "./chunk-VOJO7HJ6.js";
import {
  require_out
} from "./chunk-P4RPU2KB.js";
import {
  stripTrailingChars,
  toPosixPath,
  toProjectRelativePath
} from "./chunk-LYNDVGNE.js";
import {
  changedFilesSince,
  currentFeature,
  foldFeature,
  isPaqadArtifactPath,
  readChangeStartPoint,
  readFeatureDelivery,
  readFeatureIndexEntries,
  readFeatureStageUnit,
  stampFeatureDocument,
  writeFeatureIndexDocument
} from "./chunk-5M55PDWL.js";
import {
  RECIPE_INJECTED_KIND,
  RECORD_REPAIR_KIND,
  SKILL_USED_KIND,
  STAGE_AGENT_KIND,
  featureBranch,
  isBundleClosed,
  isBundleMaterialized,
  isFeatureFinished,
  isLaneStage,
  laneStages,
  listFeatureDirs,
  liveStageAgents,
  readChangeConstants,
  readFeatureRecord,
  readRepairedReports,
  sessionReattachableFeature
} from "./chunk-7TBVBCDH.js";
import {
  ENVELOPE_HEADER_KEYS,
  rowRecordedAt,
  stampBundleRow
} from "./chunk-RJMJYPHL.js";
import {
  STAGE_AGENT_STAGES,
  isSubagentCapableAdapter,
  stageForAgentName,
  stripPluginAgentNamespace
} from "./chunk-NCYDV2N5.js";
import {
  readGitState,
  readHeadRef
} from "./chunk-BOEVU2BD.js";
import {
  LANES,
  envConfigValue,
  layeredConfigMap,
  loadGitWorkflow,
  readConfigsDir,
  readDotConfig,
  require_dist,
  resolveFrameworkConfig,
  resolveNumericConfig,
  resolveSpecPipelineSwitches,
  resolveStageIsolationLanes
} from "./chunk-ACJQW7SB.js";
import {
  renameWithRetrySync
} from "./chunk-Z3NGWHFX.js";
import {
  getRuntimeRoot
} from "./chunk-YWAORJG6.js";
import {
  appendStampedRowToUnit,
  readUnitFile,
  readUnitFileHealth
} from "./chunk-Q353EDKY.js";
import {
  readLedgerSessionId,
  resolveSessionId
} from "./chunk-SG6LXLDH.js";
import {
  FEATURE_DOC_SCHEMA_VERSION,
  FIX_PROOF_DOC_TYPE,
  RULE_OVERRIDE_DOC_TYPE,
  TECH_DEBT_DOC_TYPE,
  ULID_BODY,
  featureChangeKey,
  featureDir,
  featureFilePath,
  parseFeatureDirName,
  ulid
} from "./chunk-AZLA53A6.js";
import {
  PATHS
} from "./chunk-IHMBVS6H.js";
import {
  __toESM,
  init_esm_shims
} from "./chunk-IDFRWBMN.js";

// src/hud/refresh.ts
init_esm_shims();
import { existsSync as existsSync7 } from "fs";
import { join as join13 } from "path";

// src/hud/snapshot.ts
init_esm_shims();
import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "fs";
import { join as join12 } from "path";

// src/fix-protocol/fix-proof-record.ts
init_esm_shims();
var ROWS_KEY = "rows";
function readFixProofRows(projectRoot, dirName) {
  const rows = readFeatureIndexEntries(projectRoot, dirName, "fixProof", ROWS_KEY);
  return (rows ?? []).filter(
    (row) => row !== null && typeof row === "object" && (row.phase === "red" || row.phase === "green") && typeof row.outcome === "string" && typeof row.at === "string"
  );
}
function appendFixProofRow(projectRoot, dirName, row, options = {}) {
  const rows = [...readFixProofRows(projectRoot, dirName), row];
  return writeFeatureIndexDocument({
    projectRoot,
    dirName,
    file: "fixProof",
    key: ROWS_KEY,
    docType: FIX_PROOF_DOC_TYPE,
    schemaVersion: FEATURE_DOC_SCHEMA_VERSION,
    entries: rows,
    sessionId: options.sessionId,
    now: options.now
  }).path;
}
function countedRedRow(rows) {
  let red = null;
  for (const row of rows) {
    if (row.phase === "green" && row.outcome === "green") break;
    if (row.phase === "red" && row.outcome === "red") red = row;
  }
  return red;
}
var FIX_PROOF_STATE_LABEL = {
  none: "no proof recorded",
  red: "fails before the fix, no green yet",
  "red-green": "fails before the fix, passes after",
  "green-only": "passes, but never shown failing before the fix",
  "not-reproduced": "passed before the fix, so it never reproduced the problem",
  held: "held: the fix broke a check that passed before"
};
function foldFixProofState(rows) {
  const red = countedRedRow(rows);
  if (red !== null) {
    const after2 = rows.slice(rows.indexOf(red) + 1);
    const settled = after2.filter(
      (row) => row.phase === "green" && (row.outcome === "green" || row.outcome === "held")
    );
    const last = settled.at(-1);
    if (last === void 0) return "red";
    return last.outcome === "green" ? "red-green" : "held";
  }
  if (rows.some((row) => row.phase === "green" && row.outcome === "green")) return "green-only";
  if (rows.some((row) => row.outcome === "not-reproduced")) return "not-reproduced";
  return "none";
}

// src/checks/report-store.ts
init_esm_shims();
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path";
var CHECKS_REPORT_SCHEMA_VERSION = 3;
var CHECKS_BUNDLE_SCHEMA_VERSION = 3;
var CHECKS_DOC_TYPE = "paqad.checks";
var REPORT_HEADER_KEYS = /* @__PURE__ */ new Set([...ENVELOPE_HEADER_KEYS, "generated_at"]);
function checksReportPath(projectRoot) {
  return join(projectRoot, PATHS.CHECKS_REPORT);
}
function featureChecksPath(projectRoot, dirName) {
  return join(projectRoot, featureFilePath(dirName, "checks"));
}
function atomicWriteReport(target, report) {
  mkdirSync(dirname(target), { recursive: true });
  const tmp = `${target}.tmp`;
  writeFileSync(tmp, JSON.stringify(report, null, 2) + "\n", "utf8");
  renameWithRetrySync(tmp, target);
  return target;
}
function readReportAt(target) {
  if (!existsSync(target)) return null;
  try {
    const parsed = JSON.parse(readFileSync(target, "utf8"));
    if (typeof parsed?.passed !== "boolean" || !Array.isArray(parsed?.results)) {
      return null;
    }
    return parsed;
  } catch {
    return null;
  }
}
function writeChecksReport(projectRoot, report) {
  return atomicWriteReport(checksReportPath(projectRoot), report);
}
function writeFeatureChecks(projectRoot, dirName, report, sessionId) {
  const body = Object.fromEntries(
    Object.entries(report).filter(([key]) => !REPORT_HEADER_KEYS.has(key))
  );
  const recordedAt = report.generated_at ?? report.recorded_at;
  const stamped = stampFeatureDocument({
    projectRoot,
    dirName,
    docType: CHECKS_DOC_TYPE,
    schemaVersion: CHECKS_BUNDLE_SCHEMA_VERSION,
    sessionId,
    now: recordedAt === void 0 ? void 0 : () => new Date(recordedAt),
    body
  });
  return atomicWriteReport(featureChecksPath(projectRoot, dirName), stamped);
}
function readChecksReport(projectRoot) {
  return readReportAt(checksReportPath(projectRoot));
}
function readFeatureChecks(projectRoot, dirName) {
  return readReportAt(featureChecksPath(projectRoot, dirName));
}

// src/feature-evidence/rule-overrides.ts
init_esm_shims();
function appendRuleOverride(projectRoot, dirName, input) {
  const at = (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
  const row = {
    kind: "rule-override",
    rule: input.rule,
    file: input.file,
    tool_use_id: input.toolUseId,
    by: "human",
    ...input.reason ? { reason: input.reason } : {},
    at,
    session: input.sessionId
  };
  const stamped = stampBundleRow({
    docType: RULE_OVERRIDE_DOC_TYPE,
    change: featureChangeKey(dirName),
    sessionId: input.sessionId,
    schemaVersion: FEATURE_DOC_SCHEMA_VERSION,
    row: { ...row },
    now: () => new Date(at)
  });
  appendStampedRowToUnit(projectRoot, featureFilePath(dirName, "ruleOverrides"), stamped);
  return row;
}
function str(value) {
  return typeof value === "string" && value !== "" ? value : null;
}
function readRuleOverrides(projectRoot, dirName) {
  return readUnitFile(projectRoot, featureFilePath(dirName, "ruleOverrides")).flatMap((raw) => {
    const row = raw;
    const rule = str(row.rule);
    const file = str(row.file);
    const at = str(row.at);
    const session = str(row.session);
    if (row.kind !== "rule-override" || !rule || !file || !at || !session) return [];
    const reason = str(row.reason);
    return [
      {
        kind: "rule-override",
        rule,
        file,
        tool_use_id: str(row.tool_use_id),
        by: "human",
        ...reason ? { reason } : {},
        at,
        session
      }
    ];
  });
}
var EDIT_OUTCOMES = ["landed", "refused", "still-blocked"];
function isEditOutcome(value) {
  return EDIT_OUTCOMES.some((outcome) => outcome === value);
}
function overrideAwaitsOutcome(projectRoot, dirName, call) {
  const rows = readUnitFile(projectRoot, featureFilePath(dirName, "ruleOverrides")).map(
    (raw) => raw
  );
  const sameCall = (row) => row.tool_use_id === call.toolUseId;
  return rows.some(
    (row) => row.kind === "rule-override" && sameCall(row) && row.rule === call.rule && row.file === call.file
  ) && !rows.some((row) => row.kind === "rule-override-outcome" && sameCall(row));
}
function appendRuleOverrideOutcome(projectRoot, dirName, input) {
  const at = (input.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
  const row = {
    kind: "rule-override-outcome",
    rule: input.rule,
    file: input.file,
    tool_use_id: input.toolUseId,
    outcome: input.outcome,
    at,
    session: input.sessionId
  };
  const stamped = stampBundleRow({
    docType: RULE_OVERRIDE_DOC_TYPE,
    change: featureChangeKey(dirName),
    sessionId: input.sessionId,
    schemaVersion: FEATURE_DOC_SCHEMA_VERSION,
    row: { ...row },
    now: () => new Date(at)
  });
  appendStampedRowToUnit(projectRoot, featureFilePath(dirName, "ruleOverrides"), stamped);
  return row;
}

// src/feature-evidence/tech-debt-index.ts
init_esm_shims();

// src/tech-debt/items.ts
init_esm_shims();
var import_yaml = __toESM(require_dist(), 1);
import { mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
import { join as join2 } from "path";
var DEBT_KINDS = ["bug", "debt", "gap", "follow-up"];
var DEBT_SEVERITIES = ["low", "medium", "high"];
var DEBT_STATUSES = ["open", "promoted", "closed"];
var TD_ID_PATTERN = new RegExp(`^TD-${ULID_BODY}$`);
var FRONT_MATTER_BOUNDARY = "---";
var BODY_HEADING = "## What";
var TechDebtError = class extends Error {
};
function mintTechDebtId() {
  return `TD-${ulid()}`;
}
function isTechDebtId(id) {
  return TD_ID_PATTERN.test(id);
}
function assertTechDebtId(id) {
  if (!isTechDebtId(id)) {
    throw new TechDebtError(`"${id}" is not a tech-debt id; expected TD-<ULID>.`);
  }
}
function techDebtDir(projectRoot) {
  return join2(projectRoot, PATHS.TECH_DEBT_DIR);
}
function techDebtItemPath(projectRoot, id) {
  assertTechDebtId(id);
  return join2(techDebtDir(projectRoot), `${id}.md`);
}
function normalizeDebtTitle(title) {
  return title.toLowerCase().replace(/\s+/g, " ").trim();
}
function normalizeDebtFiles(projectRoot, files) {
  const normalized = files.map((file) => {
    const relativePath = toProjectRelativePath(projectRoot, file);
    if (relativePath === null) {
      throw new TechDebtError(`--file "${file}" resolves outside the project root.`);
    }
    return relativePath;
  });
  return [...new Set(normalized)].sort();
}
function dedupeKey(title, files) {
  return JSON.stringify([normalizeDebtTitle(title), [...files].sort()]);
}
function oneOf(value, allowed, flag) {
  if (allowed.includes(value)) {
    return value;
  }
  throw new TechDebtError(`${flag} must be one of ${allowed.join(", ")}; got "${value}".`);
}
function textOrNull(value) {
  return typeof value === "string" && value.length > 0 ? value : null;
}
function serializeDebtItem(item) {
  const frontMatter = {
    id: item.id,
    title: item.title,
    kind: item.kind,
    severity: item.severity,
    status: item.status,
    files: item.files,
    found_in: item.found_in,
    found_by: item.found_by,
    session: item.session,
    created_at: item.created_at,
    reopened_at: item.reopened_at,
    issue: item.issue,
    closed_reason: item.closed_reason
  };
  const yaml = import_yaml.default.stringify(frontMatter, {
    defaultStringType: "QUOTE_DOUBLE",
    defaultKeyType: "PLAIN",
    lineWidth: 0
  });
  const body = stripTrailingChars(item.body, " 	\r\n");
  return `${FRONT_MATTER_BOUNDARY}
${yaml}${FRONT_MATTER_BOUNDARY}

${BODY_HEADING}

${body}
`;
}
function parseDebtItem(text) {
  const lines = text.split(/\r?\n/);
  if (lines[0] !== FRONT_MATTER_BOUNDARY) {
    return null;
  }
  const closing = lines.indexOf(FRONT_MATTER_BOUNDARY, 1);
  if (closing === -1) {
    return null;
  }
  let data;
  try {
    const parsed = import_yaml.default.parse(lines.slice(1, closing).join("\n"));
    if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
      return null;
    }
    data = parsed;
  } catch {
    return null;
  }
  const id = textOrNull(data.id);
  const title = textOrNull(data.title);
  if (id === null || !isTechDebtId(id) || title === null) {
    return null;
  }
  const kind = String(data.kind);
  const severity = String(data.severity);
  const status = String(data.status);
  if (!DEBT_KINDS.includes(kind) || !DEBT_SEVERITIES.includes(severity) || !DEBT_STATUSES.includes(status)) {
    return null;
  }
  const foundIn = data.found_in !== null && typeof data.found_in === "object" ? data.found_in : {};
  const rest = lines.slice(closing + 1);
  const headingAt = rest.indexOf(BODY_HEADING);
  const bodyLines = headingAt === -1 ? rest : rest.slice(headingAt + 1);
  return {
    id,
    title,
    kind,
    severity,
    status,
    files: Array.isArray(data.files) ? data.files.filter((f) => typeof f === "string") : [],
    found_in: {
      change: textOrNull(foundIn.change),
      issue: textOrNull(foundIn.issue),
      stage: textOrNull(foundIn.stage)
    },
    found_by: textOrNull(data.found_by) ?? "orchestrator",
    session: textOrNull(data.session),
    created_at: textOrNull(data.created_at) ?? "",
    reopened_at: textOrNull(data.reopened_at),
    issue: textOrNull(data.issue),
    closed_reason: textOrNull(data.closed_reason),
    body: bodyLines.join("\n").trim()
  };
}
function readDebtItems(projectRoot) {
  let names;
  try {
    names = readdirSync(techDebtDir(projectRoot));
  } catch {
    return [];
  }
  const items = [];
  for (const name of names.filter((entry2) => /^TD-.+\.md$/.test(entry2)).sort()) {
    const item = parseDebtItem(readFileSync2(join2(techDebtDir(projectRoot), name), "utf8"));
    if (item !== null && `${item.id}.md` === name) {
      items.push(item);
    }
  }
  return items;
}
function readDebtItem(projectRoot, id) {
  const path = techDebtItemPath(projectRoot, id);
  let text;
  try {
    text = readFileSync2(path, "utf8");
  } catch {
    throw new TechDebtError(`No tech-debt item ${id} under ${PATHS.TECH_DEBT_DIR}.`);
  }
  const item = parseDebtItem(text);
  if (item === null || item.id !== id) {
    throw new TechDebtError(`Tech-debt item ${id} has unreadable front matter.`);
  }
  return item;
}
function writeDebtItem(projectRoot, item) {
  const path = techDebtItemPath(projectRoot, item.id);
  mkdirSync2(techDebtDir(projectRoot), { recursive: true });
  writeFileSync2(path, serializeDebtItem(item));
}
function addDebtItem(projectRoot, input, now = /* @__PURE__ */ new Date()) {
  const title = input.title.trim();
  if (title.length === 0) {
    throw new TechDebtError("--title must not be empty.");
  }
  const kind = oneOf(input.kind, DEBT_KINDS, "--kind");
  const severity = oneOf(input.severity, DEBT_SEVERITIES, "--severity");
  const files = normalizeDebtFiles(projectRoot, input.files);
  const key = dedupeKey(title, files);
  const match = readDebtItems(projectRoot).find(
    (item2) => dedupeKey(item2.title, item2.files) === key
  );
  if (match !== void 0) {
    if (match.status !== "closed") {
      return { id: match.id, outcome: "existing", item: match };
    }
    const reopened = { ...match, status: "open", reopened_at: now.toISOString() };
    writeDebtItem(projectRoot, reopened);
    return { id: reopened.id, outcome: "reopened", item: reopened };
  }
  const item = {
    id: mintTechDebtId(),
    title,
    kind,
    severity,
    status: "open",
    files,
    found_in: {
      change: input.foundIn?.change ?? null,
      issue: input.foundIn?.issue ?? null,
      stage: input.foundIn?.stage ?? null
    },
    found_by: textOrNull(input.foundBy?.trim()) ?? "orchestrator",
    session: input.session ?? null,
    created_at: now.toISOString(),
    reopened_at: null,
    issue: null,
    closed_reason: null,
    body: input.body ?? ""
  };
  writeDebtItem(projectRoot, item);
  return { id: item.id, outcome: "created", item };
}
function promoteDebtItem(projectRoot, id, issue) {
  const ref = issue.trim();
  if (ref.length === 0) {
    throw new TechDebtError("--issue must not be empty.");
  }
  const item = { ...readDebtItem(projectRoot, id), status: "promoted", issue: ref };
  writeDebtItem(projectRoot, item);
  return item;
}
function closeDebtItem(projectRoot, id, reason) {
  const why = reason.trim();
  if (why.length === 0) {
    throw new TechDebtError("--reason must not be empty.");
  }
  const item = {
    ...readDebtItem(projectRoot, id),
    status: "closed",
    closed_reason: why
  };
  writeDebtItem(projectRoot, item);
  return item;
}
function listDebtItems(projectRoot, filter = {}) {
  const status = filter.status ?? "open";
  return readDebtItems(projectRoot).filter((item) => status === "all" || item.status === status).filter((item) => filter.change === void 0 || item.found_in.change === filter.change).sort(newestFirst);
}
function newestFirst(a, b) {
  return b.created_at.localeCompare(a.created_at) || b.id.localeCompare(a.id);
}

// src/feature-evidence/tech-debt-index.ts
function collectFeatureTechDebt(projectRoot, dirName) {
  const changeKey = featureChangeKey(dirName);
  return readDebtItems(projectRoot).filter((item) => item.found_in.change === changeKey).map((item) => ({ id: item.id, title: item.title, kind: item.kind, severity: item.severity })).sort((a, b) => a.id.localeCompare(b.id));
}
function readFeatureTechDebtIndex(projectRoot, dirName) {
  return readFeatureIndexEntries(projectRoot, dirName, "techDebt", "items");
}
function writeFeatureTechDebtIndex(projectRoot, dirName, options = {}) {
  const items = collectFeatureTechDebt(projectRoot, dirName);
  const { path, written } = writeFeatureIndexDocument({
    projectRoot,
    dirName,
    file: "techDebt",
    key: "items",
    docType: TECH_DEBT_DOC_TYPE,
    schemaVersion: FEATURE_DOC_SCHEMA_VERSION,
    entries: items,
    sessionId: options.sessionId,
    now: options.now
  });
  return { path, items, written };
}

// src/cost/session-cost.ts
init_esm_shims();
function workflowCostSince(projectRoot, sessionId, since) {
  const from = Date.parse(since);
  const cache = readCostCache(hudCostCachePath(projectRoot, sessionId), sessionId);
  const mains = Object.values(cache.sources).filter((source) => source.kind === "main");
  if (mains.length === 0 || Number.isNaN(from)) return { tokens: null, usd: null, unpriced: [] };
  const messages = /* @__PURE__ */ new Map();
  for (const source of mains) {
    for (const [id, kept] of Object.entries(source.messages)) {
      const at = kept[1] === null ? Number.NaN : Date.parse(kept[1]);
      if (Number.isNaN(at) || at < from) continue;
      const usage2 = cachedMessageUsage(kept);
      const existing = messages.get(id);
      messages.set(id, {
        model: existing?.model ?? kept[0],
        usage: existing ? maxTokenUsage(existing.usage, usage2) : usage2
      });
    }
  }
  let usage = emptyTokenUsage();
  let usd = 0;
  let priced = false;
  const unpriced = /* @__PURE__ */ new Set();
  for (const message of messages.values()) {
    usage = addTokenUsage(usage, message.usage);
    const cost = messageUsd(message.usage, message.model);
    if (cost === null) {
      unpriced.add(message.model ?? "unknown");
    } else {
      usd += cost;
      priced = true;
    }
  }
  return {
    tokens: usage.total,
    usd: priced ? roundUsd(usd) : null,
    unpriced: [...unpriced].sort()
  };
}

// src/traceability/change-map.ts
init_esm_shims();
import { closeSync, fstatSync, openSync, readFileSync as readFileSync4 } from "fs";
import { join as join7 } from "path";

// src/verification/gates/ac-test-mapping.ts
init_esm_shims();

// src/verification/gates/shared.ts
init_esm_shims();
function checkBooleanGate(gate, value, passDetail, failDetail, remediation) {
  return {
    gate,
    passed: value,
    detail: value ? passDetail : failDetail,
    remediation: value ? void 0 : remediation
  };
}
function checkJudgedGate(gate, value, passDetail, failDetail, remediation, absentDetail) {
  return value === void 0 ? createDidNotRun(gate, absentDetail) : checkBooleanGate(gate, value, passDetail, failDetail, remediation);
}
function createPass(gate, detail) {
  return { gate, passed: true, detail };
}
function createFail(gate, detail, remediation) {
  return { gate, passed: false, detail, remediation };
}
function createInconclusive(gate, detail, remediation) {
  return { gate, passed: false, inconclusive: true, detail, remediation };
}
function createNotRequired(gate, detail) {
  return { gate, passed: true, outcome: "not-required", detail };
}
function createDidNotRun(gate, detail, remediation) {
  return {
    gate,
    passed: true,
    outcome: "did-not-run",
    detail,
    ...remediation !== void 0 ? { remediation } : {}
  };
}

// src/verification/gates/ac-test-mapping.ts
var AC_ID_PATTERN = /AC-\d+(?:\.\d+)?/;
function extractAcIdFromIssue(issue) {
  const candidates = [issue.test_id, issue.suite ?? ""];
  for (const candidate of candidates) {
    if (!candidate) continue;
    const match = AC_ID_PATTERN.exec(candidate);
    if (match) {
      return match[0];
    }
  }
  return null;
}
function collectObservedAcIds(structuredResults) {
  const observed = /* @__PURE__ */ new Set();
  for (const result of structuredResults) {
    for (const issue of result.failures) {
      const acId = extractAcIdFromIssue(issue);
      if (acId) observed.add(acId);
    }
    for (const issue of result.errors) {
      const acId = extractAcIdFromIssue(issue);
      if (acId) observed.add(acId);
    }
  }
  return [...observed].sort();
}
var AcTestMappingGate = class {
  gate = "ac-test-mapping";
  async check(context) {
    const detail = context.ac_test_mapping_detail;
    if (!context.ac_test_mapping_passed || context.ac_test_mapping_outcome === "fail") {
      return createFail(
        this.gate,
        detail ?? "Acceptance criteria are missing test mappings",
        "Add a test that names each listed criterion id in its title, describe title or an `@obligation` tag."
      );
    }
    if (context.ac_test_mapping_outcome === "not-required") {
      return createNotRequired(
        this.gate,
        detail ?? "No frozen spec, so no acceptance criteria to map."
      );
    }
    if (context.ac_test_mapping_outcome === "did-not-run" || detail === void 0) {
      return createDidNotRun(
        this.gate,
        detail ?? "No acceptance-criteria map was built, so no criterion was checked."
      );
    }
    const observed = collectObservedAcIds(context.structured_test_results ?? []);
    return createPass(
      this.gate,
      observed.length > 0 ? `${detail} Observed in failing tests: ${observed.join(", ")}.` : detail
    );
  }
};

// src/traceability/inputs.ts
init_esm_shims();
var import_fast_glob = __toESM(require_out(), 1);
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
import { join as join5 } from "path";

// src/graph/import-scanner.ts
init_esm_shims();
import { existsSync as existsSync2 } from "fs";
import { readFile } from "fs/promises";
import {
  dirname as dirname2,
  extname,
  isAbsolute,
  join as join3,
  normalize,
  posix,
  relative,
  resolve,
  sep
} from "path";
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
var RESOLUTION_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
var IMPORT_PATTERNS = [
  /\bimport\s+(?:[^'";]+\s+from\s+)?["']([^"']+)["']/g,
  /\bexport\s+(?:\*|\{[^}]*\})\s+from\s+["']([^"']+)["']/g,
  /\bimport\(\s*["']([^"']+)["']\s*\)/g,
  /\brequire\(\s*["']([^"']+)["']\s*\)/g
];
function toForward(p) {
  return p.split(sep).join("/");
}
function toProjectRelative(projectRoot, abs) {
  const rel = relative(projectRoot, abs);
  return rel === "" ? "." : toForward(rel);
}
function isSourceFile(relPath) {
  return SOURCE_EXTENSIONS.has(extname(relPath).toLowerCase());
}
function resolveWithExtensions(absCandidate) {
  if (existsSync2(absCandidate)) {
    const ext = extname(absCandidate).toLowerCase();
    if (SOURCE_EXTENSIONS.has(ext)) return absCandidate;
  }
  for (const ext of RESOLUTION_EXTENSIONS) {
    const withExt = absCandidate + ext;
    if (existsSync2(withExt)) return withExt;
  }
  for (const ext of RESOLUTION_EXTENSIONS) {
    const indexFile = join3(absCandidate, "index" + ext);
    if (existsSync2(indexFile)) return indexFile;
  }
  return null;
}
function stripExplicitExtension(spec) {
  const ext = extname(spec).toLowerCase();
  if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
    return spec.slice(0, -ext.length);
  }
  return spec;
}
function extractImportSpecifiers(content) {
  const specs = [];
  for (const pattern of IMPORT_PATTERNS) {
    pattern.lastIndex = 0;
    let m;
    while ((m = pattern.exec(content)) !== null) {
      specs.push(m[1]);
    }
  }
  return specs;
}
function resolveSpecifier(spec, fromAbsFile, projectRoot, aliases) {
  if (spec.startsWith("node:")) return null;
  for (const [alias, target] of Object.entries(aliases)) {
    if (spec === alias.replace(/\/$/, "") || spec.startsWith(alias)) {
      const remainder = spec.slice(alias.length);
      const aliasedAbs = join3(projectRoot, target, remainder);
      const resolved = resolveWithExtensions(stripExplicitExtension(aliasedAbs));
      return resolved ? toProjectRelative(projectRoot, resolved) : null;
    }
  }
  if (spec.startsWith(".") || isAbsolute(spec)) {
    const base = isAbsolute(spec) ? spec : resolve(dirname2(fromAbsFile), spec);
    const resolved = resolveWithExtensions(stripExplicitExtension(base));
    return resolved ? toProjectRelative(projectRoot, resolved) : null;
  }
  return null;
}
async function scanImports(options) {
  const projectRoot = options.projectRoot;
  const aliases = options.aliases ?? {};
  const edges = [];
  const seen = /* @__PURE__ */ new Set();
  for (const relPath of options.files) {
    if (!isSourceFile(relPath)) continue;
    const absFile = join3(projectRoot, relPath);
    let content;
    try {
      content = await readFile(absFile, "utf8");
    } catch {
      continue;
    }
    const specs = extractImportSpecifiers(content);
    for (const spec of specs) {
      const target = resolveSpecifier(spec, absFile, projectRoot, aliases);
      if (!target) continue;
      const key = relPath + "\0" + target;
      if (seen.has(key)) continue;
      seen.add(key);
      edges.push({ from: normalize(relPath).split(sep).join(posix.sep), to: target });
    }
  }
  return edges;
}

// src/module-map/source-roots.ts
init_esm_shims();
function packModuleHealth(pack) {
  return pack.manifest.module_health;
}
function discoverSourceRoots(projectRoot) {
  const profile = readProjectProfile(projectRoot);
  const requested = profile?.stack_profile?.frameworks ?? [];
  const loader = new StackPackLoader();
  const registry = loader.load({
    runtimeRoot: getRuntimeRoot(),
    projectRoot
  });
  const orderedNames = requested.length > 0 ? [...requested, ...Array.from(registry.packs.keys()).filter((n) => !requested.includes(n))] : Array.from(registry.packs.keys());
  for (const name of orderedNames) {
    const pack = registry.packs.get(name);
    if (pack === void 0) continue;
    const mh = packModuleHealth(pack);
    if (mh !== void 0 && Array.isArray(mh.source_roots) && mh.source_roots.length > 0) {
      return { source_roots: mh.source_roots, reason: "pack", pack_name: name };
    }
  }
  return { source_roots: null, reason: "unknown", pack_name: null };
}
function discoverModuleHealth(projectRoot) {
  const profile = readProjectProfile(projectRoot);
  const requested = profile?.stack_profile?.frameworks ?? [];
  const loader = new StackPackLoader();
  const registry = loader.load({
    runtimeRoot: getRuntimeRoot(),
    projectRoot
  });
  const orderedNames = requested.length > 0 ? [...requested, ...Array.from(registry.packs.keys()).filter((n) => !requested.includes(n))] : Array.from(registry.packs.keys());
  for (const name of orderedNames) {
    const pack = registry.packs.get(name);
    if (pack === void 0) continue;
    const mh = packModuleHealth(pack);
    if (mh !== void 0 && Array.isArray(mh.source_roots) && mh.source_roots.length > 0) {
      return { module_health: mh, pack_name: name };
    }
  }
  return { module_health: null, pack_name: null };
}

// src/verification/evidence.ts
init_esm_shims();
import { mkdir, rename, writeFile } from "fs/promises";
import { dirname as dirname3, join as join4 } from "path";

// src/core/types/verification.ts
init_esm_shims();
var VERIFICATION_GATES = [
  "change-completeness",
  "requirement-completeness",
  "story-quality",
  "ac-test-mapping",
  "spec-review",
  "architecture-compliance",
  "code-tests-lint",
  "implementation-review",
  "behavioral-correctness",
  "mutation-testing",
  "quality-ratchet",
  "database-quality",
  "module-docs-structure",
  "instructions-docs-structure",
  "documentation-freshness",
  "site-map-freshness",
  "extension-surface",
  "duplication"
];
var LATE_GATES = [
  "stage-evidence",
  "bundle-completeness",
  "evidence-existence",
  "visual-evidence",
  "rules-loaded",
  "fix-proof",
  "unrecorded-mandatory-stages"
];
var EVERY_LANE = ["fast", "graduated", "full"];
var STAGED_LANES = ["graduated", "full"];
function entry(stage, strength, backstop, label, defaultRunner = backstop !== "late") {
  const lanes = stage === "specification" || stage === "review" || stage === "documentation_sync" ? STAGED_LANES : EVERY_LANE;
  return { stage, lanes, strength, backstop, default_runner: defaultRunner, label };
}
var GATE_ENTRIES = {
  "change-completeness": entry("development", "deterministic", "runner", "Nothing left half done"),
  "requirement-completeness": entry(
    "specification",
    "llm-judged",
    "none",
    "Every requirement is complete"
  ),
  "story-quality": entry("specification", "llm-judged", "none", "The stories are well formed"),
  "ac-test-mapping": entry("checks", "deterministic", "runner", "Every criterion has a test"),
  "spec-review": entry("specification", "llm-judged", "runner", "The spec was reviewed"),
  "architecture-compliance": entry(
    "development",
    "deterministic",
    "none",
    "The architecture rules hold"
  ),
  "code-tests-lint": entry(
    "checks",
    "deterministic",
    "checks-report",
    "Tests, types and lint pass"
  ),
  "implementation-review": entry("review", "llm-judged", "runner", "The code was reviewed"),
  "behavioral-correctness": entry(
    "checks",
    "deterministic",
    "none",
    "The behaviour matches the spec"
  ),
  "mutation-testing": entry("checks", "deterministic", "runner", "Tests catch real mutations"),
  "quality-ratchet": entry("checks", "deterministic", "runner", "Quality did not drop"),
  "database-quality": entry("development", "deterministic", "none", "Database changes are safe"),
  "module-docs-structure": entry(
    "documentation_sync",
    "deterministic",
    "runner",
    "Module docs keep their shape"
  ),
  "instructions-docs-structure": entry(
    "documentation_sync",
    "deterministic",
    "runner",
    "Instruction docs keep their shape"
  ),
  "documentation-freshness": entry(
    "documentation_sync",
    "deterministic",
    "runner",
    "Docs match the code"
  ),
  "site-map-freshness": entry(
    "documentation_sync",
    "deterministic",
    "none",
    "The site map matches the app"
  ),
  "extension-surface": entry(
    "development",
    "deterministic",
    "runner",
    "Public surface stays stable"
  ),
  duplication: entry("development", "deterministic", "runner", "No near-copies added", false),
  "stage-evidence": entry("checks", "deterministic", "late", "Every stage was recorded"),
  "bundle-completeness": entry(
    "checks",
    "deterministic",
    "late",
    "The evidence bundle is complete"
  ),
  "evidence-existence": entry("checks", "deterministic", "late", "The evidence files exist"),
  "visual-evidence": entry("checks", "deterministic", "late", "The screens were captured"),
  "rules-loaded": entry("checks", "deterministic", "late", "The rules were loaded"),
  "fix-proof": entry("development", "deterministic", "late", "The fix is proven by a test"),
  "unrecorded-mandatory-stages": entry(
    "checks",
    "deterministic",
    "late",
    "Every mandatory stage has evidence"
  )
};
var PAQAD_RECORDS = {
  "stage-evidence": "every stage of this change",
  "bundle-completeness": "the full evidence bundle",
  "unrecorded-mandatory-stages": "evidence for every mandatory stage",
  "rules-loaded": "that the rules were loaded",
  "evidence-existence": "every evidence file",
  "fix-proof": "the proof for this fix"
};
var GATE_CATALOG = Object.fromEntries(
  Object.entries(GATE_ENTRIES).map(([code, base]) => {
    const records2 = PAQAD_RECORDS[code];
    const owned = records2 ? { ...base, owner: "paqad-record", records: records2 } : { ...base, owner: "work" };
    return [code, owned];
  })
);
function gateOwner(code) {
  return gateCatalogEntry(code)?.owner ?? "work";
}
function recordGapText(code) {
  return `paqad could not record ${gateCatalogEntry(code)?.records ?? code}`;
}
function gateCatalogEntry(code) {
  return Object.hasOwn(GATE_CATALOG, code) ? GATE_CATALOG[code] : null;
}
function gateExpectedOnLane(code, lane) {
  const found = gateCatalogEntry(code);
  if (!found || !lane || !EVERY_LANE.includes(lane)) return true;
  return found.lanes.includes(lane);
}
var BACKSTOP_VERIFICATION_ORIGINS = [
  "hook-completion",
  "git-backstop",
  "ci-backstop"
];
function isBackstopVerificationOrigin(origin) {
  return origin !== void 0 && BACKSTOP_VERIFICATION_ORIGINS.includes(origin);
}

// src/core/types/verification-evidence.ts
init_esm_shims();
var VERIFICATION_EVIDENCE_SCHEMA_VERSION = "1.1.0";
var EVIDENCE_GATE_STATUSES = ["pass", "fail", "inconclusive", "skipped"];
var EVIDENCE_OVERALL_STATUSES = ["pass", "fail", "error"];
var EVIDENCE_FAILURE_CATEGORIES = [
  "test-failure",
  "test-error",
  "test-timeout",
  "gate-failure"
];

// src/evidence/grading.ts
init_esm_shims();
var GATE_STRENGTH_TIER = Object.fromEntries(
  VERIFICATION_GATES.map((gate) => [gate, GATE_CATALOG[gate].strength])
);
var LATE_GATE_STRENGTH_TIER = Object.fromEntries(
  LATE_GATES.map((gate) => [gate, GATE_CATALOG[gate].strength])
);
var GATE_STATE_LABEL = {
  pass: "Pass",
  fail: "Fail",
  inconclusive: "Could not prove",
  "did-not-run": "Did not run",
  "not-required": "Not required"
};
function gateStateOf(result) {
  if (!result) return "did-not-run";
  if (result.outcome) return result.outcome;
  if (result.inconclusive) return "inconclusive";
  return result.passed ? "pass" : "fail";
}
function gradeGateResult(result) {
  const state = gateStateOf(result);
  if (state === "did-not-run" || state === "not-required") {
    return { verdict: "skipped", strength_class: "deterministic", skip_kind: state };
  }
  if (state === "inconclusive") {
    return { verdict: "inconclusive", strength_class: "blocked" };
  }
  return {
    verdict: state,
    strength_class: gateCatalogEntry(result.gate)?.strength ?? "deterministic"
  };
}

// src/verification/evidence.ts
var VERIFICATION_EVIDENCE_RELATIVE_PATH = ".paqad/session/verification-evidence.json";
var VERIFICATION_EVIDENCE_STDERR_BUDGET_BYTES = 2048;
function buildVerificationEvidence(input) {
  const resultByGate = /* @__PURE__ */ new Map();
  for (const result of input.results) {
    resultByGate.set(result.gate, result);
  }
  const testFailures = collectTestFailures(input.context.structured_test_results ?? []);
  const mutationResult = input.context.mutation_result;
  const gates = VERIFICATION_GATES.map((gate) => {
    const result = resultByGate.get(gate);
    if (!result) {
      return {
        name: gate,
        status: "skipped",
        detail: "Gate did not run.",
        remediation: null,
        failures: [],
        skip_reason: "not checked in this run",
        skip_kind: "did-not-run"
      };
    }
    const status = mapGateStatus(result);
    const unproven = status === "pass" && gradeGateResult(result).verdict === "inconclusive";
    const failures = gate === "code-tests-lint" ? testFailures : gate === "mutation-testing" ? collectMutationFailures(mutationResult) : [];
    return {
      name: gate,
      status,
      detail: result.detail,
      remediation: result.remediation ?? null,
      failures,
      ...result.outcome ? { skip_kind: result.outcome } : {},
      ...unproven ? { could_not_prove: true } : {},
      ...result.outcome && result.skip_reason ? { skip_reason: result.skip_reason } : {},
      ...gate === "mutation-testing" && mutationResult ? { confidence: mutationResult.confidence } : {}
    };
  });
  const firstFailureGate = gates.find(
    (gate) => gate.status === "fail" || gate.status === "inconclusive"
  );
  const overallStatus = firstFailureGate ? "fail" : "pass";
  return {
    schema_version: VERIFICATION_EVIDENCE_SCHEMA_VERSION,
    run_id: input.run_id,
    started_at: input.started_at,
    completed_at: input.completed_at,
    overall_status: overallStatus,
    first_failure_gate: firstFailureGate?.name ?? null,
    gates
  };
}
async function writeVerificationEvidence(evidence, options) {
  const targetPath = join4(options.project_root, VERIFICATION_EVIDENCE_RELATIVE_PATH);
  await mkdir(dirname3(targetPath), { recursive: true });
  const tempPath = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
  const payload = `${JSON.stringify(evidence, null, 2)}
`;
  await writeFile(tempPath, payload, "utf8");
  await rename(tempPath, targetPath);
  return targetPath;
}
function mapGateStatus(result) {
  const { verdict } = gradeGateResult(result);
  if (verdict === "skipped" || verdict === "fail") {
    return verdict;
  }
  return verdict === "inconclusive" && !result.passed ? "inconclusive" : "pass";
}
function collectTestFailures(structuredResults) {
  const failures = [];
  for (const result of structuredResults) {
    for (const issue of result.failures) {
      failures.push(toEvidenceFailure(issue));
    }
    for (const issue of result.errors) {
      failures.push(toEvidenceFailure(issue));
    }
  }
  return failures;
}
function collectMutationFailures(mutationResult) {
  if (!mutationResult) {
    return [];
  }
  return mutationResult.surviving_mutants.map((mutant) => ({
    category: "gate-failure",
    file: mutant.file,
    line: mutant.line,
    test_id: null,
    suite: null,
    ac_id: null,
    message: mutant.description ? `Surviving mutant (${mutant.operator}): ${mutant.description}` : `Surviving mutant (${mutant.operator})`,
    stderr_excerpt: null
  }));
}
function toEvidenceFailure(issue) {
  const message = issue.message;
  const stderrSource = issue.stack_trace ?? issue.message;
  const stderrExcerpt = truncateForBudget(stderrSource, VERIFICATION_EVIDENCE_STDERR_BUDGET_BYTES);
  const acId = extractAcIdFromIssue(issue);
  return {
    category: mapTestCategory(issue.category),
    file: issue.file_path,
    line: issue.line_number,
    test_id: issue.test_id || null,
    suite: issue.suite,
    ac_id: acId,
    message,
    stderr_excerpt: stderrExcerpt
  };
}
function mapTestCategory(category) {
  if (category === "error") return "test-error";
  if (category === "timeout") return "test-timeout";
  if (category === "assertion" || category === "unknown") return "test-failure";
  return "test-failure";
}
function truncateForBudget(value, budgetBytes) {
  if (!value) {
    return null;
  }
  const buffer = Buffer.from(value, "utf8");
  if (buffer.byteLength <= budgetBytes) {
    return value;
  }
  return buffer.subarray(0, budgetBytes).toString("utf8");
}

// src/traceability/inputs.ts
function readComplianceReports(projectRoot) {
  const dir = join5(projectRoot, ".paqad", "compliance");
  if (!existsSync3(dir)) return [];
  const files = import_fast_glob.default.sync("**/report.json", { cwd: dir, absolute: true });
  const reports = [];
  for (const file of files) {
    try {
      const report = JSON.parse(readFileSync3(file, "utf8"));
      if (Array.isArray(report.obligations)) reports.push(report);
    } catch {
    }
  }
  return reports;
}
function readObligationPromises(projectRoot) {
  const promises = [];
  const proofs = [];
  const seenPromise = /* @__PURE__ */ new Set();
  for (const report of readComplianceReports(projectRoot)) {
    for (const obligation of report.obligations) {
      if (seenPromise.has(obligation.obligation_id)) continue;
      seenPromise.add(obligation.obligation_id);
      promises.push({
        promise_id: obligation.obligation_id,
        description: obligation.description,
        source: "obligation"
      });
      const checks = (obligation.evidence ?? []).map(toPosixPath);
      if (checks.length > 0) proofs.push({ promise_id: obligation.obligation_id, checks });
    }
  }
  return { promises, proofs };
}

// src/traceability/map-builder.ts
init_esm_shims();

// src/core/types/traceability.ts
init_esm_shims();
var TRACEABILITY_MAP_SCHEMA_VERSION = "1.0.0";

// src/traceability/reachability.ts
init_esm_shims();
var REACHED_FROM_SAMPLE = 3;
function solveReachability(input) {
  const importsOf = /* @__PURE__ */ new Map();
  const importedBy = /* @__PURE__ */ new Map();
  const push = (map, key, value) => {
    const bucket = map.get(key);
    if (bucket) bucket.push(value);
    else map.set(key, [value]);
  };
  for (const { from, to } of input.edges) {
    push(importsOf, from, to);
    push(importedBy, to, from);
  }
  const used = /* @__PURE__ */ new Set();
  const queue = [];
  for (const anchor of input.anchors) {
    if (!used.has(anchor)) {
      used.add(anchor);
      queue.push(anchor);
    }
  }
  while (queue.length > 0) {
    const current = queue.shift();
    for (const next of importsOf.get(current) ?? []) {
      if (!used.has(next)) {
        used.add(next);
        queue.push(next);
      }
    }
  }
  const anchorSet = new Set(input.anchors);
  const reachedFrom = /* @__PURE__ */ new Map();
  for (const file of used) {
    if (anchorSet.has(file)) continue;
    const importers = (importedBy.get(file) ?? []).filter((f) => used.has(f));
    reachedFrom.set(file, importers.slice(0, REACHED_FROM_SAMPLE));
  }
  const orphans = input.universe.filter((file) => !used.has(file)).sort();
  return { used, orphans, reachedFrom };
}

// src/traceability/map-builder.ts
function indexBy(rows, key) {
  const map = /* @__PURE__ */ new Map();
  for (const row of rows) map.set(key(row), row);
  return map;
}
function uniqueSorted(values) {
  return [...new Set(values)].sort();
}
function buildTraceabilityMap(input) {
  const mode = input.lane === "fast" ? "light" : "full";
  const universe = uniqueSorted(input.codeUniverse);
  const universeSet = new Set(universe);
  const deliveryByPromise = /* @__PURE__ */ new Map();
  const ensurePromise = (id) => {
    const existing = deliveryByPromise.get(id);
    if (existing) return existing;
    const fresh = /* @__PURE__ */ new Set();
    deliveryByPromise.set(id, fresh);
    return fresh;
  };
  for (const entry2 of input.delivery) {
    const bucket = ensurePromise(entry2.promise_id);
    for (const file of entry2.files) bucket.add(file);
  }
  for (const marker of input.markers ?? []) {
    for (const id of marker.promise_ids) ensurePromise(id).add(marker.file);
  }
  const proofsByPromise = /* @__PURE__ */ new Map();
  for (const entry2 of input.proofs) {
    const bucket = proofsByPromise.get(entry2.promise_id) ?? /* @__PURE__ */ new Set();
    for (const check of entry2.checks) bucket.add(check);
    proofsByPromise.set(entry2.promise_id, bucket);
  }
  const promiseById = indexBy(input.promises, (p) => p.promise_id);
  const forward = [];
  const findings = [];
  for (const promise of input.promises) {
    const delivering = uniqueSorted(deliveryByPromise.get(promise.promise_id) ?? []);
    const checks = uniqueSorted(proofsByPromise.get(promise.promise_id) ?? []);
    const proven = checks.length > 0;
    forward.push({
      promise_id: promise.promise_id,
      source: promise.source,
      description: promise.description,
      delivering_code: delivering,
      proving_checks: checks,
      proven
    });
    if (!proven) {
      findings.push({
        code: "TR-UNTESTED-PROMISE",
        promise_id: promise.promise_id,
        paths: delivering,
        detail: `Promise "${promise.promise_id}" has no proving check \u2014 we said we'd do this and nothing tests it.`
      });
    }
  }
  const anchorFiles = /* @__PURE__ */ new Set();
  const promiseIdsByFile = /* @__PURE__ */ new Map();
  for (const [promiseId, files] of deliveryByPromise) {
    if (!promiseById.has(promiseId)) continue;
    for (const file of files) {
      if (!universeSet.has(file)) continue;
      anchorFiles.add(file);
      const ids = promiseIdsByFile.get(file) ?? /* @__PURE__ */ new Set();
      ids.add(promiseId);
      promiseIdsByFile.set(file, ids);
    }
  }
  const anchorsKnown = anchorFiles.size > 0;
  const blockedReason = anchorsKnown ? null : input.promises.length === 0 ? "no_promises_discovered" : "no_delivering_code_resolved";
  const { used, orphans, reachedFrom } = solveReachability({
    edges: input.edges,
    anchors: [...anchorFiles],
    universe
  });
  const changedSet = mode === "light" ? new Set(input.changedFiles ?? []) : null;
  const inOrphanScope = (file) => changedSet === null ? true : changedSet.has(file);
  const backward = [];
  let deliversCount = 0;
  let sharedCount = 0;
  let orphanCount = 0;
  const orphanSet = new Set(orphans);
  for (const file of universe) {
    const promiseIds = uniqueSorted(promiseIdsByFile.get(file) ?? []);
    const isAnchor = anchorFiles.has(file);
    const isUsed = used.has(file);
    let role;
    if (isAnchor) {
      role = "delivers-promise";
      deliversCount++;
    } else if (isUsed) {
      role = "shared-groundwork";
      sharedCount++;
    } else {
      role = "orphan";
    }
    backward.push({
      file,
      promise_ids: promiseIds,
      used_by_promise: isUsed && !isAnchor,
      reached_from: reachedFrom.get(file) ?? [],
      role
    });
    if (role === "orphan" && anchorsKnown && orphanSet.has(file) && inOrphanScope(file)) {
      orphanCount++;
      findings.push({
        code: "TR-CODE-ORPHAN",
        promise_id: null,
        paths: [file],
        detail: `Code "${file}" answers to no promise and nothing-with-a-promise uses it \u2014 exists, nothing asked for it, nothing uses it.`
      });
    }
  }
  return {
    schema_version: TRACEABILITY_MAP_SCHEMA_VERSION,
    generated_at: input.now(),
    lane: input.lane,
    mode,
    anchors_known: anchorsKnown,
    blocked_reason: blockedReason,
    forward,
    backward,
    findings,
    counts: {
      promises: input.promises.length,
      untested_promises: findings.filter((f) => f.code === "TR-UNTESTED-PROMISE").length,
      delivers_promise: deliversCount,
      shared_groundwork: sharedCount,
      orphan_code: orphanCount
    }
  };
}

// src/traceability/writer.ts
init_esm_shims();
import { randomBytes } from "crypto";
import { existsSync as existsSync4 } from "fs";
import { mkdir as mkdir2, readFile as readFile2, rename as rename2, rm, writeFile as writeFile2 } from "fs/promises";
import { dirname as dirname4, join as join6 } from "path";
function traceabilityMapPath(projectRoot) {
  return join6(projectRoot, PATHS.TRACEABILITY_MAP);
}
async function writeTraceabilityMap(projectRoot, map) {
  const target = traceabilityMapPath(projectRoot);
  await mkdir2(dirname4(target), { recursive: true });
  const tmp = `${target}.tmp-${process.pid}-${randomBytes(6).toString("hex")}`;
  try {
    await writeFile2(tmp, JSON.stringify(map, null, 2) + "\n", "utf8");
    await rename2(tmp, target);
  } catch (error) {
    await rm(tmp, { force: true });
    throw error;
  }
  return target;
}
async function readTraceabilityMap(projectRoot) {
  const target = traceabilityMapPath(projectRoot);
  if (!existsSync4(target)) return null;
  try {
    return JSON.parse(await readFile2(target, "utf8"));
  } catch {
    return null;
  }
}

// src/traceability/change-map.ts
var SCANNABLE_TEST_FILE = /\.(?:[cm]?[jt]sx?|py|rb|php|go|java|kt|kts|cs|rs|swift|ex|exs)$/;
var MAX_TEST_FILE_BYTES = 2 * 1024 * 1024;
var TEST_CALL = /(?<![\w$.])(?:it|test|describe|suite|context)((?:\.[A-Za-z]+)*)\s*\(/g;
var OBLIGATION_TAG = /@obligation\b([^\n]*)/g;
function buildChangeTraceabilityMap(options) {
  if (!options.dirName) return null;
  const spec = readFeatureSpecification(options.projectRoot, options.dirName);
  if (!spec || !spec.frozen) return null;
  const criteria = Array.isArray(spec.acceptance_criteria) ? spec.acceptance_criteria : [];
  const automated = criteria.filter((c) => c.proof_type === "automated");
  const automatedIds = automated.map((c) => c.criterion_id);
  const failing = collectFailingAcIds(automatedIds, options.failingAcIds, options.testResults);
  const proofsByAc = /* @__PURE__ */ new Map();
  for (const file of changedTestFiles(options.changedFiles)) {
    const texts = readTitlesAndTags(options.projectRoot, file);
    if (texts.length === 0) continue;
    for (const id of automatedIds) {
      if (failing.has(id)) continue;
      if (!texts.some((text) => namesCriterion(text, id))) continue;
      const bucket = proofsByAc.get(id) ?? /* @__PURE__ */ new Set();
      bucket.add(file);
      proofsByAc.set(id, bucket);
    }
  }
  const acIdSet = new Set(automatedIds);
  const promises = automated.map((c) => ({
    promise_id: c.criterion_id,
    description: describeCriterion(c),
    source: "acceptance-criterion"
  }));
  const proofs = [...proofsByAc].map(([promise_id, files]) => ({
    promise_id,
    checks: [...files].sort()
  }));
  const obligations = readObligationPromises(options.projectRoot);
  promises.push(...obligations.promises.filter((p) => !acIdSet.has(p.promise_id)));
  proofs.push(...obligations.proofs.filter((p) => !acIdSet.has(p.promise_id)));
  const map = buildTraceabilityMap({
    lane: options.lane,
    now: options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()),
    promises,
    delivery: [],
    proofs,
    edges: [],
    codeUniverse: [],
    changedFiles: [...options.changedFiles],
    markers: []
  });
  return {
    map: { ...map, scope: "change", change: options.dirName },
    automated: automatedIds,
    visual: criteria.filter((c) => c.proof_type === "visual").map((c) => c.criterion_id),
    manual: criteria.filter((c) => c.proof_type === "manual").map((c) => c.criterion_id)
  };
}
async function buildAndWriteChangeTraceabilityMap(options, onError) {
  let result;
  try {
    result = buildChangeTraceabilityMap(options);
  } catch (error) {
    const message = `could not build the acceptance-criteria map (${describeError(error)})`;
    onError(message);
    return { result: null, error: message };
  }
  if (result) {
    try {
      await writeTraceabilityMap(options.projectRoot, result.map);
    } catch (error) {
      onError(`could not write the acceptance-criteria map (${describeError(error)})`);
    }
  }
  return { result, error: null };
}
function namesCriterion(text, id) {
  if (!id) return false;
  const escaped = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  return new RegExp(`(?<![A-Za-z0-9_.-])${escaped}(?![A-Za-z0-9_]|[-.][A-Za-z0-9])`).test(text);
}
function extractTitlesAndTags(source) {
  const texts = [];
  TEST_CALL.lastIndex = 0;
  let match;
  while ((match = TEST_CALL.exec(source)) !== null) {
    let index = match.index + match[0].length;
    if (/\.(?:each|for)\b/.test(match[1])) {
      const close = skipBalanced(source, index);
      if (close === -1) continue;
      index = skipSpace(source, close + 1);
      if (source[index] !== "(") continue;
      index += 1;
    }
    const title = readStringLiteral(source, skipSpace(source, index));
    if (title !== null) texts.push(title);
  }
  OBLIGATION_TAG.lastIndex = 0;
  while ((match = OBLIGATION_TAG.exec(source)) !== null) {
    const rest = match[1].split("*/")[0].trim();
    if (rest.length > 0) texts.push(rest);
  }
  return texts;
}
function changedTestFiles(changedFiles) {
  const files = /* @__PURE__ */ new Set();
  for (const raw of changedFiles) {
    const file = toPosixPath(raw);
    if (isTestFile(file) && SCANNABLE_TEST_FILE.test(file)) files.add(file);
  }
  return [...files].sort();
}
function readTitlesAndTags(projectRoot, file) {
  const abs = join7(projectRoot, file);
  let fd;
  try {
    fd = openSync(abs, "r");
  } catch {
    return [];
  }
  try {
    if (fstatSync(fd).size > MAX_TEST_FILE_BYTES) return [];
    return extractTitlesAndTags(readFileSync4(fd, "utf8"));
  } catch {
    return [];
  } finally {
    closeSync(fd);
  }
}
function collectFailingAcIds(automatedIds, explicit, results) {
  const failing = new Set(explicit ?? []);
  for (const result of results ?? []) {
    for (const issue of [...result.failures ?? [], ...result.errors ?? []]) {
      const extracted = extractAcIdFromIssue(issue);
      if (extracted) failing.add(extracted);
      const texts = [issue.test_id, issue.suite ?? ""].filter((text) => text.length > 0);
      for (const id of automatedIds) {
        if (texts.some((text) => namesCriterion(text, id))) failing.add(id);
      }
    }
  }
  return failing;
}
function describeCriterion(criterion) {
  const parts = [
    criterion.given ? `Given ${criterion.given}` : "",
    criterion.when ? `when ${criterion.when}` : "",
    criterion.then ? `then ${criterion.then}` : ""
  ].filter((part) => part.length > 0);
  return parts.length > 0 ? parts.join(", ") : criterion.criterion_id;
}
function skipSpace(source, index) {
  let i = index;
  while (i < source.length && /\s/.test(source[i])) i += 1;
  return i;
}
function skipBalanced(source, index) {
  let depth = 1;
  const limit = Math.min(source.length, index + 2e5);
  for (let i = index; i < limit; i += 1) {
    const ch = source[i];
    if (ch === "'" || ch === '"' || ch === "`") {
      const end = stringEnd(source, i);
      if (end === -1) return -1;
      i = end;
      continue;
    }
    if (ch === "(" || ch === "[" || ch === "{") depth += 1;
    else if (ch === ")" || ch === "]" || ch === "}") {
      depth -= 1;
      if (depth === 0) return ch === ")" ? i : -1;
    }
  }
  return -1;
}
function stringEnd(source, index) {
  const quote = source[index];
  for (let i = index + 1; i < source.length; i += 1) {
    const ch = source[i];
    if (ch === "\\") {
      i += 1;
      continue;
    }
    if (ch === quote) return i;
    if (ch === "\n" && quote !== "`") return -1;
  }
  return -1;
}
function readStringLiteral(source, index) {
  const ch = source[index];
  if (ch !== "'" && ch !== '"' && ch !== "`") return null;
  const end = stringEnd(source, index);
  return end === -1 ? null : source.slice(index + 1, end);
}
function describeError(error) {
  return error instanceof Error ? error.message : String(error);
}

// src/stage-isolation/isolation-summary.ts
init_esm_shims();
var LEGACY_CONTEXT_EFFICIENCY_FILE = "context-efficiency.jsonl";
function stagesOfRow(row) {
  const listed = Array.isArray(row.stages) ? row.stages.map(String) : [];
  return [String(row.stage), ...listed].filter((stage) => STAGE_AGENT_STAGES.includes(stage));
}
function isIsolatedStageRow(row) {
  return stagesOfRow(row).length > 0;
}
function readStageAgentRows(projectRoot, dirName) {
  return readFeatureStageUnit(projectRoot, dirName).filter(
    (row) => row.kind === STAGE_AGENT_KIND && row.event_status !== "started" && isIsolatedStageRow(row)
  );
}
function legacyRows(projectRoot, dirName) {
  return readUnitFile(
    projectRoot,
    `${featureDir(dirName)}/${LEGACY_CONTEXT_EFFICIENCY_FILE}`
  ).filter(isIsolatedStageRow);
}
function count(value) {
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
}
function agentKey(row) {
  return typeof row.agent_id === "string" && row.agent_id.trim() !== "" ? row.agent_id.trim() : null;
}
function tokensNotRecarried(rows) {
  const latestByAgent = /* @__PURE__ */ new Map();
  let unkeyed = 0;
  for (const row of rows) {
    const tokens = count(row.tokens_not_recarried);
    const key = agentKey(row);
    if (key === null) {
      unkeyed += tokens;
    } else {
      latestByAgent.set(key, Math.max(latestByAgent.get(key) ?? 0, tokens));
    }
  }
  return [...latestByAgent.values()].reduce((sum2, tokens) => sum2 + tokens, unkeyed);
}
function hasStageAgentEvidence(projectRoot, dirName) {
  return readStageAgentRows(projectRoot, dirName).length > 0 || legacyRows(projectRoot, dirName).length > 0;
}
function hasStageAgentEvidenceSince(projectRoot, dirName, since) {
  const from = Date.parse(since);
  return readStageAgentRows(projectRoot, dirName).some(
    (row) => Date.parse(rowRecordedAt(row) ?? "") >= from
  );
}
function summarizeStageIsolation(projectRoot, dirName) {
  const rows = readStageAgentRows(projectRoot, dirName);
  const legacy = legacyRows(projectRoot, dirName);
  if (rows.length === 0 && legacy.length === 0) {
    return null;
  }
  return {
    stages: new Set([...rows, ...legacy].flatMap(stagesOfRow)).size,
    tokensNotRecarried: tokensNotRecarried(rows) + legacy.reduce((sum2, row) => sum2 + count(row.carried_history_avoided_estimate), 0),
    estimate: legacy.length > 0 || rows.some((row) => row.estimate !== false)
  };
}
function formatStageIsolationLine(summary) {
  const plural = summary.stages === 1 ? "stage" : "stages";
  const provenance = summary.estimate ? "estimate" : "exact";
  return `context: ${summary.stages} ${plural} isolated, ~${summary.tokensNotRecarried} tokens not re-carried (${provenance})`;
}

// src/verification/repository/bundle-completeness-config.ts
init_esm_shims();
import { readFileSync as readFileSync5 } from "fs";
import { join as join8 } from "path";

// src/core/enterprise-policy.ts
init_esm_shims();
var ALL_OFF = {
  enabled: false,
  evidence_ledger: false,
  ai_bom: false,
  compliance_citations: false
};
function resolveEnterprisePolicy(profile) {
  const block = profile?.enterprise;
  if (block?.enabled !== true) {
    return { ...ALL_OFF };
  }
  return {
    enabled: true,
    evidence_ledger: block.evidence_ledger === true,
    ai_bom: block.ai_bom === true,
    compliance_citations: block.compliance_citations === true
  };
}
function writesLedger(policy) {
  return policy.evidence_ledger || policy.ai_bom;
}

// src/duplication/config.ts
init_esm_shims();
var DUPLICATION_MODES = ["off", "warn", "strict"];
var DEFAULT_DUPLICATION_MODE = "warn";
var DEFAULT_SIMILARITY_THRESHOLD = 0.9;
var HEURISTIC_BAND_FLOOR = 0.8;
var DEFAULT_MIN_LINES = 8;
function resolveDuplicationConfig(projectRoot, env = process.env) {
  return {
    mode: resolveDuplicationMode(projectRoot, env),
    similarityThreshold: resolveNumericConfig(
      projectRoot,
      env,
      "duplication_similarity_threshold",
      DEFAULT_SIMILARITY_THRESHOLD,
      (value) => value > 0 && value <= 1
    ),
    minLines: resolveNumericConfig(
      projectRoot,
      env,
      "duplication_min_lines",
      DEFAULT_MIN_LINES,
      (value) => Number.isInteger(value) && value >= 1
    )
  };
}
function resolveDuplicationMode(projectRoot, env = process.env) {
  return resolveFlooredMode(
    {
      team: readConfigsDir(projectRoot).merged.get("duplication_mode"),
      local: readDotConfig(projectRoot).get("duplication_mode"),
      env: envConfigValue(env, "duplication_mode")
    },
    DUPLICATION_MODES,
    DEFAULT_DUPLICATION_MODE
  );
}

// src/verification/repository/bundle-completeness-config.ts
function stageIsolationStatus(projectRoot, sessionId, dirName) {
  if (!sessionId || !dirName) return "not-applicable";
  return isolationRead(projectRoot, dirName, readChangeConstantsSafe(projectRoot, dirName)).status;
}
function isolationRead(projectRoot, dirName, constants) {
  const status = isolationStatusFrom(constants, resolveIsolationLanes(projectRoot));
  const since = status === "expected" ? readFeatureRecord(projectRoot, dirName)?.lane_raised?.at ?? null : null;
  if (since === null) return { status, since: null };
  return stageStartedSince(projectRoot, dirName, since) ? { status, since } : { status: "not-applicable", since: null };
}
function stageStartedSince(projectRoot, dirName, since) {
  const from = Date.parse(since);
  return readFeatureStageUnit(projectRoot, dirName).some(
    (row) => row.kind === "stage_start" && STAGE_AGENT_STAGES.includes(String(row.stage)) && Date.parse(rowRecordedAt(row) ?? "") >= from
  );
}
function readChangeConstantsSafe(projectRoot, dirName) {
  if (!dirName) return null;
  try {
    return readChangeConstants(projectRoot, dirName);
  } catch {
    return null;
  }
}
function resolveIsolationLanes(projectRoot) {
  return resolveStageIsolationLanes(layeredConfigMap(projectRoot, process.env));
}
function isolationStatusFrom(constants, lanes) {
  if (!constants) return "not-applicable";
  const { lane, adapter } = constants;
  if (!isSubagentCapableAdapter(adapter, STAGE_AGENT_HOSTS)) return "not-applicable";
  if (lane === null) return "unresolved";
  return lanes.includes(lane) ? "expected" : "not-applicable";
}
function isolationFlags(status) {
  return {
    stageIsolationExpected: status === "expected",
    stageIsolationUnresolved: status === "unresolved"
  };
}
function isolationConfigFrom(read) {
  return {
    ...isolationFlags(read.status),
    ...read.since === null ? {} : { stageIsolationSince: read.since }
  };
}
function buildBundleCompletenessConfig(projectRoot, sessionId, dirName) {
  const frameworkConfig = resolveFrameworkConfig(projectRoot);
  const policy = resolveEnterprisePolicy(readProjectProfile(projectRoot));
  const pipelineFlags = readSpecPipelineFlags(projectRoot);
  const constants = readChangeConstantsSafe(projectRoot, dirName);
  return {
    lane: constants?.lane ?? null,
    ruleComplianceOn: resolveRuleComplianceMode(projectRoot) !== "off",
    metricsEnabled: frameworkConfig.features.metrics_enabled,
    duplicationOn: resolveDuplicationMode(projectRoot) !== "off",
    featureReport: frameworkConfig.features.feature_report,
    ragEnabled: frameworkConfig.intelligence.rag_enabled,
    enterprise: policy.enabled,
    evidenceLedger: policy.evidence_ledger,
    aiBom: policy.ai_bom,
    specPipelineStrict: pipelineFlags.strict,
    specPipelineEnabled: pipelineFlags.enabled,
    expertsEnabled: pipelineFlags.experts,
    specFrozenBeforePipeline: specFrozenBeforePipeline(projectRoot, dirName),
    ...isolationConfigFrom(
      sessionId && dirName ? isolationRead(projectRoot, dirName, constants) : { status: "not-applicable", since: null }
    )
  };
}
function specFrozenBeforePipeline(projectRoot, dirName) {
  if (!dirName) return false;
  try {
    const text = readFileSync5(join8(projectRoot, featureFilePath(dirName, "specification")), "utf8");
    return isSpecFrozenBeforePipeline(JSON.parse(text));
  } catch {
    return false;
  }
}
function readSpecPipelineFlags(projectRoot, env = process.env) {
  const switches = resolveSpecPipelineSwitches(layeredConfigMap(projectRoot, env));
  return {
    enabled: switches.enabled,
    strict: switches.enabled && switches.adoption === "strict",
    experts: switches.experts_enabled
  };
}

// src/verification/verdict-kind.ts
init_esm_shims();
function verdictKindFor(gates, unrecordedMandatoryStages = []) {
  if (gates.some((gate) => gate.status === "fail")) return "fail";
  if (gates.some((gate) => gate.status === "inconclusive")) return "inconclusive";
  if (unrecordedMandatoryStages.length > 0) return "inconclusive";
  return "pass";
}
var LANE_RECORD_GAP = {
  code: "lane",
  text: "paqad could not record the lane for this change",
  detail: null
};
var UNPROVEN_STATUSES = /* @__PURE__ */ new Set(["fail", "inconclusive"]);
var STAGE_WORDS = {
  ticket_intake: "Intake",
  planning: "Plan",
  specification: "Spec",
  development: "Build",
  review: "Review",
  checks: "Checks",
  documentation_sync: "Docs",
  delivery: "Deliver"
};
var UNRECORDED_STAGES_PREFIX = "No record yet for: ";
function unrecordedStagesGap(stages) {
  return {
    code: "unrecorded-mandatory-stages",
    text: `${UNRECORDED_STAGES_PREFIX}${stages.map((stage) => STAGE_WORDS[stage] ?? stage).join(", ")}`,
    detail: null
  };
}
function unrecordedStagesOf(text) {
  if (!text.startsWith(UNRECORDED_STAGES_PREFIX)) return [];
  const byWord = new Map(Object.entries(STAGE_WORDS).map(([stage, word]) => [word, stage]));
  return text.slice(UNRECORDED_STAGES_PREFIX.length).split(",").map((word) => word.trim()).map((word) => byWord.get(word) ?? (word in STAGE_WORDS ? word : null)).filter((stage) => stage !== null);
}
function verdictParts(gates, unrecordedMandatoryStages = []) {
  const work = gates.filter((gate) => gateOwner(gate.gate) === "work");
  const workReasons = work.filter((gate) => UNPROVEN_STATUSES.has(gate.status)).sort((a, b) => a.status === b.status ? 0 : a.status === "fail" ? -1 : 1).map((gate) => `${gate.gate}: ${gate.detail}`);
  const gaps = gates.filter((gate) => gateOwner(gate.gate) === "paqad-record").filter((gate) => UNPROVEN_STATUSES.has(gate.status)).map((gate) => ({ code: gate.gate, text: recordGapText(gate.gate), detail: gate.detail }));
  if (unrecordedMandatoryStages.length > 0) {
    gaps.push(unrecordedStagesGap(unrecordedMandatoryStages));
  }
  return { work_kind: verdictKindFor(work), work_reasons: workReasons, record_gaps: gaps };
}

// src/hud/change-scope.ts
init_esm_shims();
var GATE_JUDGED_AFTER = Object.fromEntries(
  Object.entries(GATE_CATALOG).map(([code, entry2]) => [code, entry2.stage])
);
var UNKNOWN_GATE_STAGE = "checks";
var EDIT_STAGES = /* @__PURE__ */ new Set(["development", "documentation_sync"]);
var STAGE_ROW_KINDS = /* @__PURE__ */ new Set(["stage_start", "stage_end"]);
var COMPLETE_END_STATUSES = /* @__PURE__ */ new Set(["completed", "inferred"]);
function qualifyHudChange(projectRoot, sessionId, now = () => /* @__PURE__ */ new Date()) {
  const workflow = readWorkflowState(projectRoot, sessionId);
  if (workflow.active?.workflow !== "feature-development") {
    return { dir: null, reason: "not-feature-route" };
  }
  const dir = currentFeature(projectRoot, sessionId, now);
  if (!dir || !isBundleMaterialized(projectRoot, dir)) {
    return finishedChange(projectRoot, sessionId, workflow.turn_started_at ?? null);
  }
  const status = readFeatureRecord(projectRoot, dir)?.status;
  if (isBundleClosed(projectRoot, dir) || isFeatureFinished(status)) {
    return { dir: null, reason: "closed" };
  }
  const head = readHeadRef(projectRoot);
  const recorded = featureBranch(projectRoot, dir);
  if (head.detached || recorded !== null && recorded !== head.branch) {
    return { dir: null, reason: "other-branch" };
  }
  const start = episodeStart(readRouteTurns(projectRoot, sessionId), "feature-development") ?? workflow.turn_started_at ?? null;
  if (!sessionWroteInEpisode(projectRoot, sessionId, dir, start) && !stageStillRunning(projectRoot, dir)) {
    return { dir: null, reason: "no-row-this-episode" };
  }
  return { dir, reason: "qualified" };
}
function stageStillRunning(projectRoot, dirName) {
  const rows = readFeatureStageUnit(projectRoot, dirName);
  return hasOpenUnendedStage(rows) || liveStageAgents(rows).length > 0;
}
function finishedChange(projectRoot, sessionId, turnStartedAt) {
  const none = { dir: null, reason: "no-active-change" };
  const head = readHeadRef(projectRoot);
  if (head.detached) return none;
  const dir = sessionReattachableFeature(projectRoot, sessionId, head.branch);
  if (dir) {
    const start = episodeStart(readRouteTurns(projectRoot, sessionId), "feature-development") ?? turnStartedAt;
    if (sessionWroteInEpisode(projectRoot, sessionId, dir, start) || stageStillRunning(projectRoot, dir)) {
      return { dir, reason: "finished" };
    }
  }
  return noChangeYet(projectRoot, sessionId, turnStartedAt) ? { dir: null, reason: "no-change-yet" } : none;
}
function noChangeYet(projectRoot, sessionId, turnStartedAt) {
  if (turnStartedAt === null || Number.isNaN(Date.parse(turnStartedAt))) return false;
  return !listFeatureDirs(projectRoot).some(
    (dir) => sessionWroteInEpisode(projectRoot, sessionId, dir, turnStartedAt)
  );
}
function rowTime(row) {
  return Date.parse(rowRecordedAt(row) ?? "");
}
function carriesEdit(row) {
  if (!STAGE_ROW_KINDS.has(String(row.kind)) || typeof row.stage !== "string") return false;
  if (EDIT_STAGES.has(row.stage)) return true;
  return row.stage === "specification" && typeof row.subject_digest === "string";
}
function latestEditAt(projectRoot, dirName) {
  let latest = null;
  for (const row of readFeatureStageUnit(projectRoot, dirName)) {
    if (!carriesEdit(row)) continue;
    const at = rowTime(row);
    if (!Number.isNaN(at) && (latest === null || at > latest)) latest = at;
  }
  return latest;
}
var RECHECK_STAGES = /* @__PURE__ */ new Set(["checks"]);
function movesRunOn(row) {
  if (row.kind === "completion-deferred") return true;
  return STAGE_ROW_KINDS.has(String(row.kind)) && !RECHECK_STAGES.has(String(row.stage));
}
function latestStageMoveAt(projectRoot, dirName) {
  let latest = null;
  for (const row of readFeatureStageUnit(projectRoot, dirName)) {
    if (!movesRunOn(row)) continue;
    const at = rowTime(row);
    if (!Number.isNaN(at) && (latest === null || at > latest)) latest = at;
  }
  return latest;
}
function stageEndTimes(projectRoot, dirName) {
  const ends = /* @__PURE__ */ new Map();
  for (const row of readFeatureStageUnit(projectRoot, dirName)) {
    if (row.kind !== "stage_end" || typeof row.stage !== "string") continue;
    if (!COMPLETE_END_STATUSES.has(String(row.event_status))) continue;
    const at = rowTime(row);
    if (!Number.isNaN(at)) ends.set(row.stage, Math.max(ends.get(row.stage) ?? at, at));
  }
  return ends;
}
function gateIsJudged(code, recordedAt, stageEnds) {
  const owner = GATE_JUDGED_AFTER[code] ?? UNKNOWN_GATE_STAGE;
  const endedAt = stageEnds.get(owner);
  if (endedAt === void 0) return false;
  const at = Date.parse(recordedAt ?? "");
  return !Number.isNaN(at) && at >= endedAt;
}

// src/hud/changed-files.ts
init_esm_shims();
import { execFileSync } from "child_process";
import { closeSync as closeSync2, openSync as openSync2, readSync, statSync } from "fs";
import { join as join9 } from "path";
var GIT_READ_TIMEOUT_MS = 2e3;
var COMMIT_TIMES_LIMIT = 200;
var GIT_READ_MAX_BYTES = 16 * 1024 * 1024;
function gitRead(projectRoot, args) {
  return execFileSync("git", args, {
    cwd: projectRoot,
    encoding: "utf8",
    stdio: ["ignore", "pipe", "ignore"],
    timeout: GIT_READ_TIMEOUT_MS,
    maxBuffer: GIT_READ_MAX_BYTES
  }).split("\0").filter((entry2) => entry2.length > 0);
}
function readChangeGit(projectRoot, baseBranch, onError, start) {
  const base = start === void 0 ? readGitState(projectRoot, baseBranch ? { baseBranch } : {}).base_commit : start?.commit ?? void 0;
  if (!base) return null;
  try {
    const files = /* @__PURE__ */ new Set();
    if (start) {
      const since = changedFilesSince(projectRoot, start);
      if (since === null) throw new Error("git could not list the changed files");
      for (const path of since) files.add(path);
    } else {
      for (const path of [
        ...gitRead(projectRoot, ["diff", "--name-only", "--relative", "-z", base]),
        ...gitRead(projectRoot, ["ls-files", "--others", "--exclude-standard", "-z"])
      ]) {
        if (!isPaqadArtifactPath(path)) files.add(path);
      }
    }
    const commitTimes = /* @__PURE__ */ new Map();
    for (const entry2 of gitRead(projectRoot, [
      "log",
      "-z",
      `--max-count=${COMMIT_TIMES_LIMIT}`,
      "--format=%H %cI",
      `${base}..HEAD`
    ])) {
      const [sha, at] = entry2.trim().split(" ");
      if (sha && at) commitTimes.set(sha, at);
    }
    return { files: [...files].sort(), commitTimes };
  } catch (error) {
    onError(error);
    return null;
  }
}
function readWorkingTreeDirty(projectRoot) {
  try {
    const paths = [
      ...gitRead(projectRoot, ["diff", "--name-only", "--relative", "-z", "HEAD"]),
      ...gitRead(projectRoot, ["ls-files", "--others", "--exclude-standard", "-z"])
    ];
    return paths.some((path) => !isPaqadArtifactPath(path));
  } catch {
    return null;
  }
}
var NEW_FILE_MAX_BYTES = 1024 * 1024;
var NUL_PROBE_BYTES = 8 * 1024;
var NEW_FILES_PER_SNAPSHOT = 500;
var NEW_FILE_BYTES_PER_SNAPSHOT = 8 * 1024 * 1024;
var NEW_FILE_CACHE_LIMIT = 1e4;
var newFileLines = /* @__PURE__ */ new Map();
function numstatEntries(items) {
  const entries = [];
  for (let index = 0; index < items.length; index += 1) {
    const [added = "", removed = "", ...rest] = items[index].split("	");
    let path = rest.join("	");
    if (path === "") {
      path = items[index + 2] ?? "";
      index += 2;
    }
    if (path === "") continue;
    entries.push({
      path: path.replace(/\\/g, "/"),
      added: Number.parseInt(added, 10) || 0,
      removed: Number.parseInt(removed, 10) || 0
    });
  }
  return entries;
}
function countLines(buffer, length) {
  if (length === 0) return 0;
  let lines = 0;
  for (let index = 0; index < length; index += 1) {
    if (buffer[index] === 10) lines += 1;
  }
  return buffer[length - 1] === 10 ? lines : lines + 1;
}
function readNewFileLines(path, size) {
  let fd = null;
  try {
    fd = openSync2(path, "r");
    const buffer = Buffer.alloc(size);
    const probe = Math.min(size, NUL_PROBE_BYTES);
    let read = readSync(fd, buffer, 0, probe, 0);
    if (buffer.subarray(0, read).includes(0)) return { lines: 0, bytes: read };
    while (read < size) {
      const more = readSync(fd, buffer, read, size - read, read);
      if (more === 0) break;
      read += more;
    }
    return { lines: countLines(buffer, read), bytes: read };
  } catch {
    return null;
  } finally {
    if (fd !== null) {
      try {
        closeSync2(fd);
      } catch {
      }
    }
  }
}
function readChangeDiff(projectRoot, start, files, onError) {
  if (!start || !files) return null;
  const listed = new Set(files.map((path) => path.replace(/\\/g, "/")));
  let additions = 0;
  let deletions = 0;
  let partial = false;
  let untracked;
  try {
    const counted = /* @__PURE__ */ new Set();
    for (const entry2 of numstatEntries(
      gitRead(projectRoot, ["diff", "--numstat", "-z", "--relative", start.commit])
    )) {
      if (!listed.has(entry2.path) || counted.has(entry2.path)) continue;
      counted.add(entry2.path);
      additions += entry2.added;
      deletions += entry2.removed;
    }
    const missing = [...listed].filter((path) => !counted.has(path));
    untracked = missing.length === 0 ? [] : gitRead(projectRoot, ["ls-files", "--others", "--exclude-standard", "-z"]).map((path) => path.replace(/\\/g, "/")).filter((path) => listed.has(path) && !counted.has(path));
  } catch (error) {
    onError(error);
    return null;
  }
  let filesRead = 0;
  let bytesRead = 0;
  for (const path of [...new Set(untracked)].sort()) {
    const absolute = join9(projectRoot, path);
    let stats;
    try {
      stats = statSync(absolute);
    } catch {
      continue;
    }
    if (!stats.isFile() || stats.size > NEW_FILE_MAX_BYTES) continue;
    const cached = newFileLines.get(absolute);
    if (cached && cached.size === stats.size && cached.mtimeMs === stats.mtimeMs) {
      additions += cached.lines;
      continue;
    }
    if (partial || filesRead >= NEW_FILES_PER_SNAPSHOT || bytesRead + stats.size > NEW_FILE_BYTES_PER_SNAPSHOT) {
      partial = true;
      continue;
    }
    const counted = readNewFileLines(absolute, stats.size);
    if (counted === null) continue;
    filesRead += 1;
    bytesRead += counted.bytes;
    additions += counted.lines;
    if (newFileLines.size >= NEW_FILE_CACHE_LIMIT) newFileLines.clear();
    newFileLines.set(absolute, { size: stats.size, mtimeMs: stats.mtimeMs, lines: counted.lines });
  }
  return { additions, deletions, partial };
}

// src/hud/done-ladder.ts
init_esm_shims();
var KNOWN_CI = /* @__PURE__ */ new Set(["pass", "fail", "pending"]);
function readCi(ci) {
  return ci !== null && KNOWN_CI.has(ci) ? ci : "unknown";
}
function computeDoneLadder(input) {
  const uncommitted = input.uncommitted === true;
  const none = { rung: null, ci: null, ci_failed: false, uncommitted };
  const earned = input.workKind === "pass" || input.workDone === true;
  if (!earned || input.checks !== "green") return none;
  if (input.prState === "open") {
    const ci = readCi(input.ci);
    if (ci === "fail") return { ...none, rung: "committed", ci, ci_failed: true };
    return { ...none, rung: "pr-open", ci };
  }
  if (input.commits > 0 && input.uncommitted === false) return { ...none, rung: "committed" };
  return { ...none, rung: "done" };
}

// src/hud/cost-view.ts
init_esm_shims();
function hudCostOf(record, live = false) {
  return {
    computed_at: record.computed_at,
    window: record.window,
    scope: record.scope ?? "whole-session",
    price_table_date: record.price_table_date,
    price_source: record.price_source,
    basis: record.basis,
    label: record.label,
    plan_note: record.plan_note,
    tokens: record.tokens,
    usd: record.usd,
    unpriced_models: record.unpriced_models,
    by_stage: record.by_stage,
    by_expert: record.by_expert,
    chief: record.chief,
    not_counted: record.not_counted,
    live
  };
}
function countFromCache(projectRoot, dirName, options) {
  try {
    const counted = computeChangeCost(projectRoot, dirName, { now: options.now });
    return counted && counted.sources > 0 && counted.tokens.total > 0 ? counted : null;
  } catch (error) {
    options.onError?.(error);
    return null;
  }
}
function buildHudCost(projectRoot, dirName, options = {}) {
  const running = options.turnRunning === true;
  const record = readChangeCost(projectRoot, dirName);
  if (record) return hudCostOf(record, running && !record.window.sealed);
  if (!running) return null;
  const counted = countFromCache(projectRoot, dirName, options);
  return counted ? hudCostOf(counted, true) : null;
}

// src/hud/high-table-view.ts
init_esm_shims();
import { readFileSync as readFileSync6 } from "fs";
import { join as join10 } from "path";
function readJsonFile(path) {
  try {
    return JSON.parse(readFileSync6(path, "utf8"));
  } catch {
    return null;
  }
}
function asRecord(value) {
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
}
function records(value) {
  return Array.isArray(value) ? value.map(asRecord).filter((entry2) => entry2 !== null) : [];
}
function str2(value) {
  return typeof value === "string" && value.length > 0 ? value : null;
}
function num(value) {
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}
function after(at, frozenAt) {
  if (at === null || frozenAt === null) return false;
  const time = Date.parse(at);
  return !Number.isNaN(time) && time > frozenAt;
}
function frozenAtOf(specJson) {
  const at = str2(asRecord(asRecord(specJson)?.frozen)?.frozen_at);
  if (at === null) return null;
  const time = Date.parse(at);
  return Number.isNaN(time) ? null : time;
}
function seatFrom(role, entry2, folded, recordedFindings, frozenAt) {
  const tokensUsed = num(entry2?.tokens_used);
  const fallbackState = tokensUsed !== null || (recordedFindings ?? 0) > 0 ? "done" : "seated";
  const exact = folded?.tokens ?? null;
  return {
    role,
    reason: folded?.reason ?? str2(entry2?.reason),
    origin: folded?.origin ?? str2(entry2?.origin),
    state: folded?.state ?? fallbackState,
    seated_at: folded?.seated_at ?? null,
    started_at: folded?.started_at ?? null,
    done_at: folded?.done_at ?? null,
    budget_tokens: folded?.budget_tokens ?? num(entry2?.budget_tokens),
    clamped: folded?.clamped ?? null,
    tokens_estimate: folded?.tokens_estimate ?? tokensUsed,
    tokens: exact ? {
      input: exact.input,
      output: exact.output,
      cache_read: exact.cache_read,
      cache_write: exact.cache_write,
      total: exact.total
    } : null,
    tokens_exact: exact !== null,
    findings: folded?.findings ?? recordedFindings,
    late: after(folded?.done_at ?? null, frozenAt),
    track_record: null
  };
}
function rulingsFrom(synthesis) {
  return {
    accepted: Array.isArray(synthesis.accepted) ? synthesis.accepted.filter((id) => typeof id === "string") : [],
    declined: records(synthesis.declined).map((entry2) => ({
      id: str2(entry2.id) ?? "",
      reason: str2(entry2.reason) ?? ""
    })),
    conflicts: records(synthesis.conflicts).filter((entry2) => entry2.ruling !== "compatible").map((entry2) => ({
      target: str2(entry2.target) ?? "",
      recommendation: str2(entry2.recommendation) ?? "",
      rationale: str2(entry2.rationale) ?? ""
    })),
    merged: records(synthesis.conflicts).filter((entry2) => entry2.ruling === "compatible").map((entry2) => ({
      target: str2(entry2.target) ?? "",
      merged_claim: str2(entry2.merged_claim) ?? "",
      rationale: str2(entry2.rationale) ?? ""
    })),
    renames: records(synthesis.renames).map((entry2) => ({
      id: str2(entry2.id) ?? "",
      target: str2(entry2.target) ?? ""
    })),
    settled: records(synthesis.auto_resolved).map((entry2) => ({
      target: str2(entry2.target) ?? "",
      chosen: str2(entry2.chosen) ?? "",
      source: str2(entry2.source) ?? ""
    }))
  };
}
function chiefFrom(folded, synthesis, frozenAt) {
  if (!folded && !synthesis) return null;
  const seat = seatFrom(CHIEF_ROLE, null, folded, null, frozenAt);
  return {
    ...seat,
    state: synthesis ? "done" : seat.state,
    tokens_estimate: seat.tokens_estimate ?? num(synthesis?.tokens),
    findings: null,
    verdict: str2(synthesis?.verdict)
  };
}
function gapsFrom(synthesis) {
  return records(synthesis?.gaps).map((gap) => ({
    area: str2(gap.area) ?? "",
    why_it_matters: str2(gap.why_it_matters) ?? "",
    question: str2(asRecord(gap.question)?.business_text)
  }));
}
function questionsFrom(projectRoot, dirName) {
  const clarification = asRecord(
    readJsonFile(join10(projectRoot, featureFilePath(dirName, "clarification")))
  );
  const questions = asRecord(clarification?.questions);
  if (!questions) return null;
  const counts = asRecord(questions.counts);
  return {
    asked: num(counts?.asked) ?? (Array.isArray(questions.asked) ? questions.asked.length : 0),
    auto_answered: records(questions.auto_answered).map((entry2) => ({
      question: str2(entry2.question) ?? "",
      source: str2(entry2.source) ?? ""
    }))
  };
}
var EXPERT_SOURCE = /^EX-(.+)-\d+$/;
function creditsFrom(specJson) {
  const trace = asRecord(asRecord(specJson)?.trace);
  if (!trace) return [];
  const credits = [];
  for (const [line, source] of Object.entries(trace)) {
    const roles = (typeof source === "string" ? source.split(/[,\s]+/) : []).map((part) => EXPERT_SOURCE.exec(part)?.[1]).filter((role) => typeof role === "string");
    if (roles.length > 0) credits.push({ line, roles: [...new Set(roles)] });
  }
  return credits;
}
function buildHighTableView(projectRoot, dirName, specJson, lane) {
  if (lane === "fast") return null;
  const adoption = readSpecAdoption(specJson);
  if (adoption?.fast_lane) return null;
  const skipped = adoption !== null && !adoption.produced;
  const experts = asRecord(readJsonFile(join10(projectRoot, featureFilePath(dirName, "experts"))));
  const roster = Array.isArray(experts?.roster) ? records(experts.roster) : null;
  const rows = readHighTable(projectRoot, dirName);
  const synthesis = asRecord(experts?.synthesis);
  if (!roster && rows.length === 0) {
    return skipped ? {
      skipped,
      met_after_freeze: false,
      seats: [],
      chief: null,
      rulings: null,
      gaps: [],
      questions: null,
      credits: []
    } : null;
  }
  const frozenAt = frozenAtOf(specJson);
  const folded = foldHighTable(rows);
  const findingCounts = /* @__PURE__ */ new Map();
  const recordedFindings = Array.isArray(experts?.findings) ? records(experts.findings) : null;
  for (const finding of recordedFindings ?? []) {
    const role = str2(finding.role);
    if (role) findingCounts.set(role, (findingCounts.get(role) ?? 0) + 1);
  }
  const findingsFor = (role) => recordedFindings === null ? null : findingCounts.get(role) ?? 0;
  const roles = [
    ...(roster ?? []).map((entry2) => str2(entry2.role) ?? "unknown"),
    ...[...folded.keys()].filter((role) => role !== CHIEF_ROLE)
  ];
  const seats = [...new Set(roles)].map(
    (role) => seatFrom(
      role,
      (roster ?? []).find((entry2) => entry2.role === role) ?? null,
      folded.get(role),
      findingsFor(role),
      frozenAt
    )
  );
  const metAfterFreeze = highTableDoneTimes(rows).some((at) => after(at, frozenAt));
  return {
    skipped,
    met_after_freeze: metAfterFreeze,
    seats,
    chief: chiefFrom(folded.get(CHIEF_ROLE), synthesis, frozenAt),
    rulings: synthesis ? rulingsFrom(synthesis) : null,
    gaps: gapsFrom(synthesis),
    questions: questionsFrom(projectRoot, dirName),
    credits: metAfterFreeze ? [] : creditsFrom(specJson)
  };
}

// src/hud/receipt-file.ts
init_esm_shims();
import { readFileSync as readFileSync7 } from "fs";
var NO_VERDICT_RECORD = "no verdict record";
var HUD_VERDICT_WORDS = Object.values(PAQAD_VERDICT);
function verdictSessionId(projectRoot, hostSessionId) {
  return resolveSessionId(projectRoot, hostSessionId);
}
function readVerdictAgreement(projectRoot, sessionId) {
  const empty = { blocked_changes: [], no_record: null };
  try {
    const parsed = JSON.parse(
      readFileSync7(hudVerdictAgreementPath(projectRoot, sessionId), "utf8")
    );
    if (typeof parsed !== "object" || parsed === null) return empty;
    const blocked = Array.isArray(parsed.blocked_changes) ? parsed.blocked_changes.filter((entry2) => typeof entry2 === "string") : [];
    const noRecord = parsed.no_record && typeof parsed.no_record.why === "string" ? parsed.no_record : null;
    return { blocked_changes: blocked, no_record: noRecord };
  } catch {
    return empty;
  }
}
function writeVerdictAgreement(projectRoot, sessionId, state) {
  writeFileAtomic(
    hudVerdictAgreementPath(projectRoot, sessionId),
    `${JSON.stringify(state, null, 2)}
`
  );
}
function readHudVerdict(projectRoot, sessionId) {
  const path = hudVerdictPath(projectRoot, sessionId);
  let text;
  try {
    text = readFileSync7(path, "utf8");
  } catch {
    return { status: "missing", path, why: `no verdict record at ${path}` };
  }
  let parsed;
  try {
    parsed = JSON.parse(text);
  } catch {
    return { status: "unreadable", path, why: `the verdict record at ${path} is not valid JSON` };
  }
  const record = typeof parsed === "object" && parsed !== null ? parsed : null;
  if (record?.schema !== "paqad.hud-verdict/1" || typeof record.word !== "string") {
    return {
      status: "unreadable",
      path,
      why: `the verdict record at ${path} is not a paqad.hud-verdict/1 record`
    };
  }
  return { status: "found", path, record };
}
function writeHudReceipt(projectRoot, hostSessionId, verdict, now = () => /* @__PURE__ */ new Date()) {
  if (verdict.applicable === false || verdict.kind === void 0 && verdict.gates.length === 0) {
    return null;
  }
  const sessionId = verdictSessionId(projectRoot, hostSessionId);
  const kind = verdict.kind ?? verdictKindFor(verdict.gates);
  const record = {
    schema: "paqad.hud-verdict/1",
    session_id: sessionId,
    change: verdict.change !== void 0 ? verdict.change : activeChange(projectRoot, sessionId),
    word: PAQAD_VERDICT[kind],
    kind,
    passed: kind === "pass",
    reasons: verdict.gates.filter((gate) => gate.status === "fail" || gate.status === "inconclusive").sort((a, b) => a.status === b.status ? 0 : a.status === "fail" ? -1 : 1).map((gate) => `${gate.gate}: ${gate.detail}`),
    ...recordParts(verdict),
    written_at: now().toISOString()
  };
  const receiptPath = hudReceiptPath(projectRoot, sessionId);
  const verdictPath = hudVerdictPath(projectRoot, sessionId);
  writeFileAtomic(receiptPath, `${verdict.receipt ?? verdict.summary}
`);
  writeFileAtomic(verdictPath, `${JSON.stringify(record, null, 2)}
`);
  return { receiptPath, verdictPath, verdict: record };
}
function recordParts(verdict) {
  const parts = verdict.work_kind !== void 0 ? verdict : verdict.gates.length > 0 ? verdictParts(verdict.gates) : null;
  if (!parts?.work_kind) return {};
  return {
    work_kind: parts.work_kind,
    work_reasons: parts.work_reasons ?? [],
    record_gaps: (parts.record_gaps ?? []).map((gap) => gap.text)
  };
}
function activeChange(projectRoot, sessionId) {
  try {
    return currentFeature(projectRoot, sessionId);
  } catch {
    return null;
  }
}

// src/hud/stage-proof.ts
init_esm_shims();
import { existsSync as existsSync5, readFileSync as readFileSync8 } from "fs";
import { join as join11 } from "path";
var PROOF_GATE_WORDS = {
  ...GATE_STATE_LABEL,
  "not-judged": "Not judged yet"
};
var PROOF_STAGE_WORDS = {
  planning: "Plan",
  specification: "Spec",
  development: "Build",
  review: "Review",
  checks: "Checks",
  documentation_sync: "Docs"
};
var JUDGED_STATES = new Set(Object.keys(GATE_STATE_LABEL));
var FINISHED_STAGES = /* @__PURE__ */ new Set(["complete", "redone"]);
function count2(value, one, many) {
  return `${value} ${value === 1 ? one : many}`;
}
function step(id, words, detail, state) {
  return { id, words, detail, state };
}
function stageFinished(input, stage) {
  const found = input.stages.find((entry2) => entry2.stage === stage);
  return found !== void 0 && FINISHED_STAGES.has(found.state);
}
function stageStarted(input, stage) {
  const found = input.stages.find((entry2) => entry2.stage === stage);
  return found !== void 0 && found.state !== "missing";
}
function planSteps(input) {
  const plan = input.plan;
  const reuse = plan?.reuse ?? null;
  return [
    plan ? step("plan", "Plan compiled", count2(plan.steps, "step", "steps"), "done") : step("plan", "Plan compiled", null, "not-yet"),
    reuse !== null && reuse > 0 ? step(
      "reuse",
      "Reuse checked",
      count2(reuse, "source consulted", "sources consulted"),
      "done"
    ) : step("reuse", "Reuse checked", null, "not-yet")
  ];
}
function questionsStep(input) {
  const questions = input.questions;
  if (!questions) return step("questions", "Questions answered", null, "not-yet");
  if (questions.asked === 0) return step("questions", "Questions answered", "none asked", "done");
  const answered = questions.answered + questions.auto_answered;
  return step(
    "questions",
    "Questions answered",
    `${answered} of ${questions.asked}`,
    answered >= questions.asked ? "done" : "not-yet"
  );
}
function specSteps(input) {
  const spec = input.spec;
  const grounded = spec?.grounded ?? null;
  const frozen = spec?.frozen === true || stageFinished(input, "specification");
  return [
    grounded !== null && grounded > 0 ? step("grounded", "Grounded in the code", count2(grounded, "reference", "references"), "done") : step("grounded", "Grounded in the code", null, "not-yet"),
    input.experts !== null && input.experts > 0 ? step("experts", "Experts seated", count2(input.experts, "expert", "experts"), "done") : step("experts", "Experts seated", null, "not-yet"),
    questionsStep(input),
    step(
      "frozen",
      "Spec frozen",
      spec ? count2(spec.criteria, "criterion", "criteria") : null,
      frozen ? "done" : "not-yet"
    )
  ];
}
function buildSteps(input) {
  const files = stageStarted(input, "development") ? input.changedFiles ?? [] : [];
  const progress = input.planProgress;
  return [
    input.rules !== null ? step("rules", "Rules loaded", count2(input.rules, "rule applies", "rules apply"), "done") : step("rules", "Rules loaded", null, "not-yet"),
    files.length > 0 ? step("edits", "Edits made", count2(files.length, "file changed", "files changed"), "done") : step("edits", "Edits made", null, "not-yet"),
    ...progress ? [
      step(
        "built",
        "Plan steps built",
        `${progress.done} of ${progress.steps}`,
        progress.done >= progress.steps ? "done" : "not-yet"
      )
    ] : []
  ];
}
function reviewSteps(input) {
  const review = input.review;
  return [
    review ? step("review", "Review recorded", count2(review.findings, "finding", "findings"), "done") : step("review", "Review recorded", null, "not-yet")
  ];
}
function checksSteps(input) {
  const checks = input.checks;
  if (!checks) return [step("checks", "Checks run", null, "not-yet")];
  if (checks.passed) {
    return [
      step("checks", "Checks passed", `${checks.commands} of ${checks.commands} commands`, "done")
    ];
  }
  return [
    step(
      "checks",
      "Checks failed",
      `${checks.failed} of ${checks.commands} commands failed`,
      "failed"
    )
  ];
}
function docsSteps(input) {
  const files = stageStarted(input, "documentation_sync") ? input.changedFiles ?? [] : [];
  const docs = files.filter(
    (file) => toPosixPath(file).replace(/^\.\//, "").startsWith("docs/")
  ).length;
  if (docs > 0) {
    return [step("docs", "Docs updated", count2(docs, "file changed", "files changed"), "done")];
  }
  if (stageFinished(input, "documentation_sync")) {
    return [step("docs", "Docs checked", "no doc needed a change", "done")];
  }
  return [step("docs", "Docs updated", null, "not-yet")];
}
var STEPS = {
  planning: planSteps,
  specification: specSteps,
  development: buildSteps,
  review: reviewSteps,
  checks: checksSteps,
  documentation_sync: docsSteps
};
function gateRuns(code, input, recorded) {
  const entry2 = GATE_CATALOG[code];
  if (entry2.owner !== "work") return false;
  if (code === "site-map-freshness") return input.siteMap;
  if (entry2.backstop === "late") return recorded;
  return entry2.backstop !== "none";
}
function gateOnLane(code, lane) {
  const lanes = GATE_CATALOG[code].lanes;
  return lane === null || !["fast", "graduated", "full"].includes(lane) || lanes.includes(lane);
}
function gateState(row, judged) {
  if (!row) return judged ? "did-not-run" : "not-judged";
  return JUDGED_STATES.has(row.verdict) ? row.verdict : "not-judged";
}
function stageGates(stage, input) {
  const rows = new Map(input.gates.map((row) => [row.name, row]));
  return Object.keys(GATE_CATALOG).filter(
    (code) => GATE_CATALOG[code].stage === stage && gateOnLane(code, input.lane) && gateRuns(code, input, rows.has(code))
  ).map((code) => ({
    code,
    words: GATE_CATALOG[code].label,
    state: gateState(rows.get(code), input.judged)
  }));
}
function knownLane(lane) {
  return lane === "fast" || lane === "graduated" || lane === "full" ? lane : null;
}
function buildStageProof(input) {
  return laneStages(knownLane(input.lane)).map((stage) => ({
    stage,
    words: PROOF_STAGE_WORDS[stage] ?? stage,
    state: input.stages.find((entry2) => entry2.stage === stage)?.state ?? "missing",
    steps: STEPS[stage]?.(input) ?? [],
    gates: stageGates(stage, input)
  }));
}
function readRecord(path) {
  if (!existsSync5(path)) return null;
  try {
    const value = JSON.parse(readFileSync8(path, "utf8"));
    return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
  } catch {
    return null;
  }
}
function listLength(value) {
  return Array.isArray(value) ? value.length : null;
}
function siteMapOn(projectRoot) {
  try {
    return resolveFrameworkConfig(projectRoot).features.site_map === true;
  } catch {
    return false;
  }
}
function readStageProofEvidence(projectRoot, dirName) {
  const plan = readFeaturePlan(projectRoot, dirName);
  const spec = readFeatureSpecification(projectRoot, dirName);
  const review = readFeatureReview(projectRoot, dirName);
  const report = readFeatureChecks(projectRoot, dirName);
  const experts = readRecord(join11(projectRoot, featureFilePath(dirName, "experts")));
  const commands = Array.isArray(report?.commands) ? report.commands : [];
  return {
    plan: plan ? {
      steps: listLength(plan.steps) ?? 0,
      reuse: listLength(plan.reuse?.consulted)
    } : null,
    spec: spec ? {
      grounded: listLength(spec.grounding?.references),
      criteria: listLength(spec.acceptance_criteria) ?? 0,
      frozen: spec.frozen !== null && spec.frozen !== void 0
    } : null,
    experts: listLength(experts?.roster),
    review: review ? { findings: listLength(review.findings) ?? 0 } : null,
    checks: report && report.ran ? {
      passed: report.passed,
      commands: commands.length,
      failed: commands.filter((command) => !command.passed).length
    } : null,
    siteMap: siteMapOn(projectRoot)
  };
}

// src/hud/stage-run.ts
init_esm_shims();
var REPORT_TONE = {
  BLOCKED: "fail",
  DONE_WITH_CONCERNS: "cau",
  NEEDS_CONTEXT: "cau"
};
function ranStage(row, stage) {
  return row.stage === stage || Array.isArray(row.stages) && row.stages.includes(stage);
}
function recipeWord(rows, stage) {
  const has = (kind) => rows.some((row) => row.kind === kind && row.stage === stage);
  const checked = rows.some(
    (row) => row.kind === SKILL_USED_KIND && row.stage === stage && row.unchecked === void 0
  );
  if (checked) return "proven";
  if (has(SKILL_USED_KIND)) return "unchecked";
  if (has(RECIPE_INJECTED_KIND)) return "injected";
  return stageLedgerEra(rows, stage) === "v3" ? "none" : "not recorded";
}
function reportWord(row, repaired) {
  if (row === void 0) return "not yet known";
  const fixed = repaired.find((entry2) => entry2.agent_id === row.agent_id);
  if (fixed !== void 0 && row.report_stated !== true) return fixed.report_status;
  if (row.report_source === "none") return "no report found";
  if (typeof row.report_source !== "string" && row.report_stated !== true) return "not recorded";
  return typeof row.report_status === "string" ? row.report_status : "not recorded";
}
function sum(rows, field) {
  const values = rows.map((row) => row[field]).filter((value) => typeof value === "number");
  return values.length === 0 ? null : values.reduce((total, value) => total + value, 0);
}
function footnoteOf(row, report) {
  if (row === void 0) return null;
  if (typeof row.decision === "string" && row.decision !== "") return row.decision;
  return report === "BLOCKED" && typeof row.reason === "string" ? row.reason : null;
}
function buildStageRun(rows, stage, lane) {
  if (lane === "fast" || lane === "graduated") return null;
  const agentRows = rows.filter((row) => row.kind === STAGE_AGENT_KIND && ranStage(row, stage));
  const recipe = recipeWord(rows, stage);
  if (agentRows.length === 0 && !["proven", "unchecked", "injected"].includes(recipe)) return null;
  const completed = agentRows.filter((row) => row.event_status === "completed");
  const latest = agentRows.at(-1);
  const running = latest?.event_status === "started";
  const newest = running ? void 0 : completed.at(-1);
  const report = reportWord(newest, readRepairedReports(rows));
  const turns = sum(completed, "api_turns");
  const figure = running ? "counting" : turns === null ? "no turns recorded" : `${turns} turns`;
  const short_line = [recipe, report, figure].join(" \xB7 ");
  return {
    agent: typeof latest?.agent === "string" ? latest.agent : null,
    recipe,
    report,
    tone: REPORT_TONE[report] ?? "dim",
    turns,
    tokens_used: sum(completed, "tokens_used"),
    counting: running,
    runs: new Set(agentRows.map((row) => row.agent_id ?? row.content_hash)).size,
    needs_look: report === "BLOCKED",
    footnote: footnoteOf(newest, report),
    line: `${stage.replace(/_/gu, " ")} \xB7 ${short_line}`,
    short_line
  };
}
function stagesWithAgentRows(rows) {
  const stages = /* @__PURE__ */ new Set();
  for (const row of rows) {
    if (row.kind !== STAGE_AGENT_KIND) continue;
    if (typeof row.stage === "string") stages.add(row.stage);
    if (Array.isArray(row.stages)) {
      for (const stage of row.stages) {
        if (typeof stage === "string") stages.add(stage);
      }
    }
  }
  return [...stages];
}

// src/hud/snapshot.ts
var HUD_SNAPSHOT_SCHEMA = "paqad.hud/3";
var TIMELINE_ROWS = 20;
var TIMELINE_COMMITS = 10;
var TIMELINE_KINDS = /* @__PURE__ */ new Set(["open", "close", "stage_start", "stage_end"]);
function isTime(value) {
  return typeof value === "string" && Number.isFinite(Date.parse(value));
}
function spanMs(from, to) {
  if (!isTime(from) || !isTime(to)) return null;
  const ms = Date.parse(to) - Date.parse(from);
  return ms >= 0 ? ms : null;
}
function inOrder(times) {
  return times.filter(isTime).sort((a, b) => Date.parse(a) - Date.parse(b));
}
function stageEndDuration(rows, index, stage) {
  const start = rows.slice(0, index).reverse().find((row) => row.kind === "stage_start" && str3(row.stage) === stage);
  return start ? spanMs(rowRecordedAt(start), rowRecordedAt(rows[index])) : null;
}
function startedInOwnAgent(agent, stage) {
  return agent !== null && stage !== null && stageForAgentName(stripPluginAgentNamespace(agent)) === stage;
}
function buildTimeline(projectRoot, dirName, change = null) {
  const rows = readFeatureStageUnit(projectRoot, dirName);
  const entries = [];
  rows.forEach((row, index) => {
    const kind = String(row.kind);
    const at = rowRecordedAt(row);
    if (!TIMELINE_KINDS.has(kind) || !at) return;
    const stage = str3(row.stage);
    const entry2 = { at, kind, stage, note: str3(row.note) };
    if (kind === "open" && entry2.note === null && change) Object.assign(entry2, change);
    if (kind === "stage_start") {
      entry2.agent = str3(row.agent);
      entry2.own_agent = startedInOwnAgent(entry2.agent, stage);
    }
    if (kind === "stage_end") entry2.duration_ms = stageEndDuration(rows, index, stage);
    entries.push(entry2);
  });
  return entries.slice(-TIMELINE_ROWS);
}
function event(at, kind, fields = {}) {
  return { at, kind, stage: null, note: null, ...fields };
}
function tableEvents(table) {
  if (!table || table.skipped) return [];
  const events = [];
  const seatedAt = inOrder(table.seats.map((seat) => seat.seated_at))[0];
  if (seatedAt) {
    const standing = table.seats.filter((seat) => seat.origin === "standing").length;
    events.push(
      event(seatedAt, "table_seated", { picked: table.seats.length - standing, standing })
    );
  }
  const landed = table.seats.length > 0 && table.seats.every((seat) => seat.state === "done");
  const landedAt = landed ? inOrder(table.seats.map((seat) => seat.done_at)).at(-1) : void 0;
  if (landedAt) {
    events.push(
      event(landedAt, "notes_landed", {
        count: table.seats.length,
        findings: table.seats.reduce((sum2, seat) => sum2 + (seat.findings ?? 0), 0)
      })
    );
  }
  const chief = table.chief;
  if (chief && isTime(chief.done_at)) {
    events.push(
      event(chief.done_at, "chief_ruled", {
        duration_ms: spanMs(chief.started_at, chief.done_at),
        verdict: chief.verdict
      })
    );
  }
  return events;
}
var DECISION_QUESTION_CHARS = 80;
function shortQuestion(packet) {
  const text = str3(packet.question) ?? str3(packet.title);
  if (!text) return null;
  const flat = text.replace(/\s+/g, " ").trim();
  return flat.length > DECISION_QUESTION_CHARS ? `${flat.slice(0, DECISION_QUESTION_CHARS - 1).trimEnd()}\u2026` : flat;
}
function decisionEvents(decisions) {
  const events = [];
  for (const { packet, status } of decisions) {
    const raw = packet;
    const decision = str3(raw.decision_id) ?? str3(raw.id);
    const named = decision ? { decision } : {};
    const createdAt = str3(raw.created_at);
    if (isTime(createdAt)) {
      const subject = shortQuestion(raw);
      events.push(
        event(createdAt, "decision_opened", { ...named, ...subject ? { subject } : {} })
      );
    }
    const resolvedAt = str3(raw.resolved_at);
    if (status === "resolved" && isTime(resolvedAt)) {
      events.push(event(resolvedAt, "decision_answered", named));
    }
  }
  return events;
}
function specEvents(specJson) {
  const spec = asRecord2(specJson);
  const at = str3(asRecord2(spec?.frozen)?.frozen_at);
  if (!spec || !isTime(at)) return [];
  const count3 = (value) => Array.isArray(value) ? value.length : 0;
  return [
    event(at, "spec_frozen", {
      requirements: count3(spec.behaviour),
      criteria: count3(spec.acceptance_criteria)
    })
  ];
}
function deliveryEvents(delivery, ship, commitTimes) {
  const own = delivery.commit_split?.own_shas ? new Set(delivery.commit_split.own_shas) : null;
  const events = delivery.commits.filter((commit) => own === null || own.has(commit.sha)).flatMap((commit) => {
    const at = commitTimes.get(commit.sha);
    return at ? [event(at, "commit", { sha: commit.sha.slice(0, 7), subject: commit.subject })] : [];
  }).slice(-TIMELINE_COMMITS);
  if (ship.push) {
    events.push(event(ship.push.at, "push", { state: ship.push.state, count: ship.commits }));
  }
  if (ship.pr && typeof ship.pr.number === "number") {
    events.push(event(ship.pr.first_at ?? ship.pr.at, "pr_opened", { number: ship.pr.number }));
  }
  const ci = ship.ci;
  if (ci && (ci.state === "pass" || ci.state === "fail")) {
    const jobs = ci.jobs ?? [];
    events.push(
      event(inOrder(jobs.map((job) => job.completed_at)).at(-1) ?? ci.at, "ci", {
        state: ci.state,
        count: ci.total ?? jobs.length
      })
    );
  }
  return events;
}
function buildRecordEvents(sources) {
  const rulesAt = sources.rulesLoaded?.recorded_at;
  return [
    ...isTime(rulesAt) ? [
      event(rulesAt, "rules_loaded", {
        count: sources.rulesLoaded?.applicable_rules?.length ?? 0
      })
    ] : [],
    ...tableEvents(sources.table),
    ...decisionEvents(sources.decisions),
    ...specEvents(sources.specJson),
    ...deliveryEvents(sources.delivery, sources.ship, sources.commitTimes)
  ].filter((entry2) => isTime(entry2.at));
}
function recordEventsSafe(projectRoot, sources) {
  try {
    return buildRecordEvents(sources);
  } catch (error) {
    logHudWriteError(projectRoot, "record events", error);
    return [];
  }
}
function mergeTimeline(stages, events) {
  const timeOf = (entry2) => {
    const at = Date.parse(entry2.at);
    return Number.isNaN(at) ? Number.POSITIVE_INFINITY : at;
  };
  return [...stages, ...events].map((entry2, index) => ({ entry: entry2, index, at: timeOf(entry2) })).sort((a, b) => a.at === b.at ? a.index - b.index : a.at < b.at ? -1 : 1).map(({ entry: entry2 }) => entry2);
}
function computeDeliveryPlan(input) {
  const commit = !input.commitDeclined;
  const push = input.enabled && commit;
  const ci = push && input.ciGate !== "off" && input.hasCi;
  const reason = !commit ? "Edit only \xB7 no commit" : !push ? "No push for this change" : !ci ? "No CI" : null;
  return { commit, push, pr: push, ci, merge: push, reason };
}
function repoHasCi(projectRoot) {
  try {
    return readdirSync2(join12(projectRoot, ".github", "workflows")).some(
      (name) => /\.ya?ml$/.test(name)
    );
  } catch {
    return false;
  }
}
function readPolicy(projectRoot) {
  try {
    return loadGitWorkflow(projectRoot).config;
  } catch (error) {
    logHudWriteError(projectRoot, "delivery policy", error);
    return null;
  }
}
function buildDeliveryPlan(projectRoot, policy, delivery) {
  if (!policy) return null;
  return computeDeliveryPlan({
    enabled: policy.enforcement.enabled,
    commitDeclined: delivery?.commit_decision?.answer === "decline",
    ciGate: policy.pull_requests.ci.gate,
    hasCi: repoHasCi(projectRoot)
  });
}
function planFile(path) {
  return toPosixPath(path).replace(/^\.\//, "");
}
function stageHasStarted(stages, stage) {
  const found = stages.find((entry2) => entry2.stage === stage);
  return found !== void 0 && found.state !== "missing";
}
function buildPlanProgress(projectRoot, dirName, changedFiles, buildStarted) {
  const steps = readFeaturePlan(projectRoot, dirName)?.steps;
  if (!buildStarted) return null;
  if (!Array.isArray(steps) || steps.length === 0 || changedFiles === null) return null;
  const changed = new Set(changedFiles.map(planFile));
  const done = steps.filter(
    (step2) => Array.isArray(step2?.files) && step2.files.some((file) => typeof file === "string" && changed.has(planFile(file)))
  ).length;
  return { steps: steps.length, done };
}
function buildSignoff(projectRoot, dirName, lane, specJson, issue) {
  let applies;
  try {
    applies = designSignOffApplies(loadDesignSignOff(projectRoot), lane);
  } catch (error) {
    logHudWriteError(projectRoot, "design sign-off", error);
    return null;
  }
  if (!applies) return null;
  const record = readHld(projectRoot, dirName);
  if (!record) {
    const frozen = isTime(str3(asRecord2(asRecord2(specJson)?.frozen)?.frozen_at));
    return {
      state: frozen ? "writing" : "pending",
      ref: issue,
      posted_at: null,
      checked_at: null,
      link: null
    };
  }
  const post = latestPost(record);
  return {
    state: record.state,
    ref: record.ref || issue,
    posted_at: post?.at ?? null,
    checked_at: record.checked_at,
    link: record.approval_link ?? post?.link ?? null
  };
}
function closedChangeOf(rows, verdict, commitTimes) {
  let closedAt = null;
  for (const row of rows) {
    if (row.kind === "open") closedAt = null;
    if (row.kind === "close") closedAt = rowRecordedAt(row) ?? null;
  }
  if (!closedAt) return null;
  const closedMs = Date.parse(closedAt);
  const newCommits = [...commitTimes].filter((at) => Date.parse(at) > closedMs).length;
  return { closed_at: closedAt, word: verdict?.word ?? null, new_commits: newCommits };
}
function readJsonFile2(path) {
  try {
    return JSON.parse(readFileSync9(path, "utf8"));
  } catch {
    return null;
  }
}
function asRecord2(value) {
  return typeof value === "object" && value !== null ? value : null;
}
function str3(value) {
  return typeof value === "string" && value.length > 0 ? value : null;
}
function num2(value) {
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}
var TRACK_RECORD_BUNDLES = 50;
function buildTrackRecords(projectRoot, activeDir, roles) {
  const records2 = /* @__PURE__ */ new Map();
  if (roles.length === 0) return records2;
  try {
    const others = listFeatureDirs(projectRoot).filter((dir) => dir !== activeDir).sort(
      (a, b) => (parseFeatureDirName(b)?.ulid ?? "").localeCompare(parseFeatureDirName(a)?.ulid ?? "")
    ).slice(0, TRACK_RECORD_BUNDLES);
    if (others.length === 0) return records2;
    const metrics = aggregateSpecPipelineMetrics(projectRoot, others);
    for (const role of roles) {
      const key = role;
      const rate = metrics.changed_spec_rate[key];
      if (!rate || rate.fired === 0) continue;
      records2.set(role, {
        changes: rate.fired,
        changed_spec: rate.changed,
        empty: metrics.empty_by_role[key]?.empty ?? 0,
        avg_tokens: Math.round((metrics.tokens_by_role[key] ?? 0) / rate.fired)
      });
    }
  } catch (error) {
    logHudWriteError(projectRoot, "track record", error);
    records2.clear();
  }
  return records2;
}
function withTrackRecords(projectRoot, dirName, table) {
  if (!table || table.seats.length === 0) return table;
  const records2 = buildTrackRecords(
    projectRoot,
    dirName,
    table.seats.map((seat) => seat.role)
  );
  return {
    ...table,
    seats: table.seats.map((seat) => ({ ...seat, track_record: records2.get(seat.role) ?? null }))
  };
}
function gapStages(gaps) {
  return new Set(gaps.flatMap((gap) => unrecordedStagesOf(gap)));
}
function helperGaveNoReport(rows, stage) {
  const last = [...rows].reverse().find(
    (row) => row.kind === STAGE_AGENT_KIND && row.stage === stage && row.event_status !== "started"
  );
  return last !== void 0 && (last.report_stated === false || last.report_source === "none");
}
function buildStages(projectRoot, dirName, fold, gapped = /* @__PURE__ */ new Set()) {
  const rows = readFeatureStageUnit(projectRoot, dirName);
  return fold.stages.map((stage) => {
    const own = rows.filter((row) => row.stage === stage.stage);
    const last = own.at(-1);
    const end = [...own].reverse().find((row) => row.kind === "stage_end");
    const artifacts = end?.artifact_paths;
    return {
      stage: stage.stage,
      state: stage.state,
      mandatory: isLaneStage(fold.lane, stage.stage),
      started_at: stage.started_at,
      ended_at: stage.ended_at,
      first_started_at: stage.first_started_at,
      last_ended_at: stage.last_ended_at,
      runs: stage.runs,
      duration_ms: stage.duration_ms,
      duration_unreliable: stage.duration_unreliable,
      agent: str3(last?.agent),
      proof_file: Array.isArray(artifacts) ? str3(artifacts[0]) ?? null : null,
      ...gapped.has(stage.stage) ? { record_gap: true } : {},
      ...helperGaveNoReport(rows, stage.stage) ? { no_report: true } : {}
    };
  });
}
function criterionThens(specJson) {
  const criteria = asRecord2(specJson)?.acceptance_criteria;
  const thens = /* @__PURE__ */ new Map();
  for (const entry2 of Array.isArray(criteria) ? criteria : []) {
    const criterion = asRecord2(entry2);
    const id = str3(criterion?.criterion_id);
    const then = str3(criterion?.then);
    if (id && then) thens.set(id, then);
  }
  return thens;
}
function checksPassedSince(projectRoot, dirName, since) {
  const startedAt = Date.parse(since ?? "");
  if (Number.isNaN(startedAt)) return false;
  const report = readFeatureChecks(projectRoot, dirName);
  const reportAt = Date.parse(report?.generated_at ?? report?.recorded_at ?? "");
  return report !== null && report.ran && report.passed && reportAt >= startedAt;
}
function buildAcceptanceClaims(projectRoot, dirName, lane, changedFiles, specJson, buildStartedAt) {
  if (changedFiles === null) return [];
  const checked = checksPassedSince(projectRoot, dirName, buildStartedAt);
  try {
    const result = buildChangeTraceabilityMap({
      projectRoot,
      dirName,
      changedFiles,
      lane: LANES.find((known) => known === lane) ?? "graduated"
    });
    if (!result) return [];
    const thens = criterionThens(specJson);
    const links = new Map(result.map.forward.map((link) => [link.promise_id, link]));
    return result.automated.map((id) => {
      const testId = links.get(id)?.proving_checks[0] ?? null;
      return {
        kind: "acceptance",
        id,
        label: thens.get(id) ?? id,
        test_id: testId,
        verdict: testId ? checked ? "pass" : "mapped" : "missing"
      };
    });
  } catch (error) {
    logHudWriteError(projectRoot, "claims", error);
    return [];
  }
}
function buildClaims(kind, projectRoot, dirName) {
  if (kind !== "fix") return [];
  const rows = readFixProofRows(projectRoot, dirName);
  const proof = foldFixProofState(rows);
  return [
    {
      kind: "fix-proof",
      proof,
      label: FIX_PROOF_STATE_LABEL[proof],
      verdict: proof === "red-green" ? "pass" : "inconclusive",
      test_id: rows.at(-1)?.test_id ?? null
    }
  ];
}
function readGateVerdict(row) {
  const raw = String(row.verdict ?? "").toLowerCase();
  if (raw === "pass" || raw === "fail" || raw === "inconclusive") return raw;
  if (raw === "skipped") return row.skip_kind === "did-not-run" ? "did-not-run" : "not-required";
  return "pending";
}
function buildGates(projectRoot, dirName) {
  const latest = /* @__PURE__ */ new Map();
  for (const row of readFeatureEvidence(projectRoot, dirName)) {
    if (row.engine !== "verification-gate") continue;
    latest.set(row.code, row);
  }
  const stageEnds = stageEndTimes(projectRoot, dirName);
  return [...latest.entries()].map(([code, row]) => {
    const verdict = gateIsJudged(code, rowRecordedAt(row), stageEnds) ? readGateVerdict(row) : "not-judged";
    const owner = gateCatalogEntry(code)?.owner;
    return {
      name: code,
      required: verdict !== "not-required",
      strength: gateStrengthFromRun(code, row, verdict),
      verdict,
      ...owner ? { owner } : {}
    };
  });
}
function gateStrengthFromRun(code, row, verdict) {
  if (verdict === "did-not-run" || verdict === "not-required") return null;
  const tier = gateCatalogEntry(code)?.strength;
  if (tier) return tier;
  return row.strength_class === "deterministic" || row.strength_class === "llm-judged" ? row.strength_class : null;
}
function expectedGateCodes(lane) {
  return VERIFICATION_GATES.filter((code) => gateExpectedOnLane(code, lane));
}
function ownPr(delivery, ownShas, inheritedShas) {
  const pr = delivery.pr ?? null;
  if (!pr) return null;
  const head = pr.head_sha ?? null;
  if (head === null) return (pr.state ?? null) === null ? pr : null;
  if (ownShas !== null && ownShas.has(head)) return pr;
  const recordedHere = delivery.commits.some((commit) => commit.sha === head) && !inheritedShas.includes(head);
  return pr.state === "merged" && recordedHere ? pr : null;
}
function readingIsCurrent(ci, pr, push, head) {
  if (pr?.head_sha && ci.sha !== pr.head_sha) return false;
  if (!pr?.head_sha && head && ci.sha && ci.sha !== head) return false;
  if (push?.sha && ci.sha !== push.sha && Date.parse(ci.at) < Date.parse(push.at)) return false;
  return true;
}
function pushIsCurrent(delivery) {
  const push = delivery.push ?? null;
  if (push === null) return false;
  if (push.sha === null || delivery.head_sha === null) return true;
  return push.sha === delivery.head_sha;
}
function buildShip(delivery, filesChanged = null, diff = null) {
  const split = delivery.commit_split ?? null;
  const ownShas = split?.own_shas ? new Set(split.own_shas) : null;
  const inheritedShas = split?.inherited_shas ?? [];
  const commitList = delivery.commits.map((commit) => ({
    sha: commit.sha.slice(0, 7),
    subject: commit.subject,
    own: ownShas === null ? null : ownShas.has(commit.sha)
  }));
  const pr = ownPr(delivery, ownShas, inheritedShas);
  const prState = pr?.state ?? null;
  const rawCi = delivery.ci ?? null;
  const settled = rawCi?.state === "pass" || rawCi?.state === "fail";
  const ci = rawCi && settled && !readingIsCurrent(rawCi, pr, delivery.push ?? null, delivery.head_sha) ? { ...rawCi, state: "pending" } : rawCi;
  return {
    commits: ownShas === null ? split?.own?.count ?? 0 : delivery.commits.filter((commit) => ownShas.has(commit.sha)).length,
    commit_list: commitList,
    commit_decision: delivery.commit_decision?.answer ?? null,
    merge_commit: delivery.merge_commit,
    push: delivery.push ?? null,
    push_current: pushIsCurrent(delivery),
    pr,
    pr_state: prState,
    pr_title: pr?.title ?? null,
    pr_files: pr?.changed_files ?? null,
    pr_additions: pr?.additions ?? null,
    pr_deletions: pr?.deletions ?? null,
    ci,
    ci_read_at: rawCi?.at ?? null,
    all_green: ci?.state === "pass" && prState === "open",
    merged: prState === "merged",
    own: split?.own ?? null,
    inherited: split?.inherited ?? null,
    inherited_count: inheritedShas.length,
    files_changed: filesChanged,
    diff
  };
}
function buildWorkflow(projectRoot, sessionId, state) {
  const name = state.active?.workflow ?? null;
  if (name === null || name === "feature-development" || name === "no-workflow") return null;
  const startedAt = episodeStart(readRouteTurns(projectRoot, sessionId), name) ?? state.turn_started_at ?? null;
  const cost = startedAt === null ? { tokens: null, usd: null, unpriced: [] } : workflowCostSince(projectRoot, sessionId, startedAt);
  return { name, started_at: startedAt, ...cost };
}
function buildQuestions(projectRoot, dirName) {
  const clarification = asRecord2(
    readJsonFile2(join12(projectRoot, featureFilePath(dirName, "clarification")))
  );
  const counts = asRecord2(asRecord2(clarification?.questions)?.counts);
  if (!counts) return null;
  return {
    asked: num2(counts.asked) ?? 0,
    answered: num2(counts.answered) ?? 0,
    auto_answered: num2(counts.auto_answered) ?? 0,
    deferred: num2(counts.deferred) ?? 0
  };
}
function changeDecisions(projectRoot, dirName) {
  if (dirName === null) return [];
  const changeKey = featureChangeKey(dirName);
  return readContractDecisions(projectRoot).filter(
    ({ packet }) => packet.change === changeKey
  );
}
function buildDecisions(rows) {
  const pending = [];
  let resolved = 0;
  for (const { packet, status } of rows) {
    if (status === "resolved") {
      resolved += 1;
      continue;
    }
    const rich = readAnyDecisionPacket(packet);
    const raw = packet;
    pending.push({
      id: rich?.decision_id ?? str3(raw.id) ?? str3(raw.decision_id) ?? "unknown",
      category: rich?.category ?? str3(raw.category) ?? "unknown",
      question: rich?.question ?? str3(raw.title) ?? str3(raw.question) ?? ""
    });
  }
  return { pending, resolved };
}
function buildEvidenceExpected(projectRoot, sessionId, dirName) {
  const config = buildBundleCompletenessConfig(projectRoot, sessionId, dirName);
  return BUNDLE_MANIFEST.map((entry2) => ({
    file: entry2.file,
    expectation: entry2.required === "always" ? "always" : entry2.required === "optional" ? "if present" : isBundleFileRequired(entry2, config) ? "on here" : "off here",
    present: existsSync6(join12(projectRoot, featureDir(dirName), entry2.file))
  }));
}
function findingsScan(row) {
  if (!row) {
    return {
      status: "did not run",
      total: null,
      counts: null,
      blocking: false,
      from_cache: false,
      items: null,
      overflow: 0,
      recorded_at: null
    };
  }
  const counts = asRecord2(row.counts);
  const total = (num2(counts?.deterministic) ?? 0) + (num2(counts?.heuristic) ?? 0);
  return {
    status: total > 0 ? "open" : "clean",
    total,
    counts,
    blocking: row.blocking === true,
    from_cache: row.from_cache === true,
    items: Array.isArray(row.findings) ? row.findings : null,
    overflow: num2(row.findings_overflow) ?? 0,
    recorded_at: rowRecordedAt(row)
  };
}
function buildFindings(projectRoot, dirName) {
  const ruleRow = readRuleRun(projectRoot, dirName).filter((row) => row.kind === "findings").at(-1);
  const dupRow = readDuplication(projectRoot, dirName).at(-1);
  if (!ruleRow && !dupRow) return null;
  return {
    rules: findingsScan(ruleRow),
    near_copies: findingsScan(dupRow)
  };
}
function readVerdictRecord(projectRoot, sessionId) {
  const reading = readHudVerdict(projectRoot, sessionId);
  return reading.status === "found" ? reading.record : null;
}
function hudSessionId(projectRoot, sessionHint) {
  return sessionHint?.trim() || readLedgerSessionId(projectRoot) || resolveSessionId(projectRoot, null);
}
var UNJUDGED_CHANGE_GAP = "paqad could not record which change this verdict judged";
var VERDICT_KINDS = /* @__PURE__ */ new Set(["pass", "fail", "inconclusive"]);
function couldNotProve(verdict, gap) {
  const gaps = Array.isArray(verdict.record_gaps) ? verdict.record_gaps : [];
  return {
    ...verdict,
    kind: "inconclusive",
    passed: false,
    word: PAQAD_VERDICT.inconclusive,
    reasons: Array.isArray(verdict.reasons) ? verdict.reasons : [],
    ...gap ? { record_gaps: [...gaps, gap] } : {}
  };
}
function freshVerdict(projectRoot, sessionId, dirName, open) {
  const none = { verdict: null, stale: false };
  const verdict = readVerdictRecord(projectRoot, sessionId);
  if (!verdict) return none;
  const unjudged = verdict.change === null || verdict.change === void 0;
  if (!unjudged && verdict.change !== dirName) return none;
  const writtenAt = Date.parse(verdict.written_at);
  if (Number.isNaN(writtenAt)) return none;
  const editedAt = latestEditAt(projectRoot, dirName);
  if (editedAt !== null && writtenAt < editedAt) return { verdict: null, stale: true };
  const movedAt = open ? latestStageMoveAt(projectRoot, dirName) : null;
  if (movedAt !== null && writtenAt < movedAt) return { verdict: null, stale: true };
  if (unjudged) return { verdict: couldNotProve(verdict, UNJUDGED_CHANGE_GAP), stale: false };
  return {
    verdict: VERDICT_KINDS.has(verdict.kind) ? verdict : couldNotProve(verdict, null),
    stale: false
  };
}
function ladderChecks(projectRoot, dirName, verdict) {
  const report = readFeatureChecks(projectRoot, dirName);
  const reportAt = Date.parse(report?.generated_at ?? report?.recorded_at ?? "");
  const verdictAt = Date.parse(verdict?.written_at ?? "");
  if (report && Number.isFinite(reportAt) && Number.isFinite(verdictAt) && reportAt > verdictAt) {
    return report.ran && report.passed ? "green" : "red";
  }
  const row = readFeatureEvidence(projectRoot, dirName).filter((entry2) => entry2.engine === "verification-gate" && entry2.code === "code-tests-lint").at(-1);
  const gate = row ? readGateVerdict(row) : null;
  if (gate === "pass") return "green";
  if (gate === "fail") return "red";
  return "unknown";
}
function workKindOf(verdict) {
  if (!verdict) return null;
  return verdict.work_kind ?? verdict.kind;
}
function lowerFirst(text) {
  return text.length > 0 ? `${text[0].toLowerCase()}${text.slice(1)}` : text;
}
function unprovenLines(gates, verdict) {
  const named = new Set(
    (verdict?.work_reasons ?? []).map((reason) => reason.slice(0, reason.indexOf(":")))
  );
  return gates.filter(
    (gate) => gate.verdict === "inconclusive" && gate.owner !== "paqad-record" && !named.has(gate.name)
  ).map(
    (gate) => `paqad could not prove ${lowerFirst(gateCatalogEntry(gate.name)?.label ?? gate.name)}`
  );
}
function verdictFromEarlierTurn(verdict, workflow, turnRunning) {
  if (!verdict || !turnRunning) return false;
  const turnAt = Date.parse(workflow.turn_started_at ?? "");
  const writtenAt = Date.parse(verdict.written_at);
  return Number.isFinite(turnAt) && Number.isFinite(writtenAt) && writtenAt < turnAt;
}
function recordGaps(verdict, laneRecorded) {
  const gaps = new Set(Array.isArray(verdict?.record_gaps) ? verdict.record_gaps : []);
  if (!laneRecorded) gaps.add(LANE_RECORD_GAP.text);
  return [...gaps];
}
function buildRepairs(projectRoot, dirName) {
  return readFeatureStageUnit(projectRoot, dirName).filter((row) => row.kind === RECORD_REPAIR_KIND && typeof row.reason === "string").map((row) => ({ what: String(row.reason), at: rowRecordedAt(row) }));
}
var STAGE_WORDS2 = {
  ticket_intake: "Intake",
  planning: "Plan",
  specification: "Spec",
  development: "Build",
  review: "Review",
  checks: "Checks",
  documentation_sync: "Docs",
  delivery: "Deliver"
};
function stageWord(stage) {
  return STAGE_WORDS2[stage] ?? stage;
}
var FAILURE_PHRASES = {
  "code-tests-lint": "Tests, types or lint failed",
  "spec-review": "The spec review found a blocking defect",
  "implementation-review": "The code review found a blocking issue",
  "quality-ratchet": "Quality dropped below the baseline",
  duplication: "A near-copy of existing code was added",
  "documentation-freshness": "The docs no longer match the code",
  "ac-test-mapping": "A criterion has no test",
  "change-completeness": "Something is left half done",
  "mutation-testing": "The tests missed real mutations",
  "module-docs-structure": "A module doc lost its shape",
  "instructions-docs-structure": "An instruction doc lost its shape",
  "extension-surface": "The public surface changed",
  "architecture-compliance": "An architecture rule is broken",
  "database-quality": "A database change is unsafe",
  "behavioral-correctness": "The behaviour does not match the spec",
  "requirement-completeness": "A requirement is incomplete",
  "story-quality": "A story is not well formed",
  "site-map-freshness": "The site map no longer matches the app"
};
function failurePhrase(code) {
  return FAILURE_PHRASES[code] ?? `${code} failed`;
}
var RUNG_WORDS = {
  done: "Done",
  committed: "Committed",
  "pr-open": "PR open"
};
var BUILD_ENDED_STATES = /* @__PURE__ */ new Set(["complete", "redone"]);
var LANE_STAGE_ENDED_STATES = /* @__PURE__ */ new Set([
  "complete",
  "redone",
  "inconclusive",
  "failed"
]);
function computeHudFlags(input) {
  const flags = [];
  const buildEnded = input.stages.some(
    (stage) => stage.stage === "development" && BUILD_ENDED_STATES.has(stage.state) && stage.started_at !== null
  );
  if (buildEnded && !input.rulesRecorded) {
    flags.push({ id: "rules-not-loaded", text: "The rules for this change were never loaded." });
  }
  if (input.tableSkipped) {
    flags.push({
      id: "high-table-skipped",
      text: "The spec was written by hand, so the High Table never sat."
    });
  }
  if (input.tableLate) {
    flags.push({
      id: "high-table-late",
      text: "The High Table met after the spec was frozen, so it shaped no spec line."
    });
  }
  const laneStageEnded = input.stages.some(
    (stage) => stage.mandatory && LANE_STAGE_ENDED_STATES.has(stage.state)
  );
  if (input.isolationStatus === "expected" && input.isolatedStages === 0 && (input.stageAgentsSeen ?? 0) === 0 && laneStageEnded) {
    flags.push({ id: "isolation-missing", text: "No stage ran in its own agent on this lane." });
  } else if (input.isolationStatus === "expected" && input.stageAgentStages) {
    const seen = new Set(input.stageAgentStages);
    const missing = input.stages.filter(
      (stage) => stage.mandatory && LANE_STAGE_ENDED_STATES.has(stage.state) && STAGE_AGENT_STAGES.includes(stage.stage) && !seen.has(stage.stage)
    ).map((stage) => stage.stage);
    if (missing.length > 0 && seen.size > 0) {
      flags.push({ id: "isolation-missing", text: `isolation missing: ${missing.join(", ")}` });
    }
  }
  if (input.isolationStatus === "unresolved") {
    flags.push({
      id: "lane-unresolved",
      text: "The lane was never set, so isolation is unproven."
    });
  }
  const ruleFindings = input.findings?.rules.blocking ? input.findings.rules.total ?? 0 : 0;
  if (ruleFindings > 0) {
    flags.push({
      id: "rule-findings",
      text: ruleFindings === 1 ? "1 rule finding is open." : `${ruleFindings} rule findings are open.`
    });
  }
  const nearCopies = input.findings?.near_copies.total ?? 0;
  if (nearCopies > 0) {
    flags.push({
      id: "near-copies",
      text: nearCopies === 1 ? "1 near-copy of existing code is open." : `${nearCopies} near-copies of existing code are open.`
    });
  }
  return flags;
}
var WAITING_FOR_ANSWER = "Waiting for your answer";
function prName(ship) {
  const number = ship?.pr?.number;
  return typeof number === "number" ? `PR #${number}` : "The PR";
}
function runningWords(input, stage) {
  switch (stage) {
    case "ticket_intake":
      return "Reading the ticket";
    case "planning":
      return "Planning the change";
    case "specification": {
      const table = input.table;
      if (table?.seats.some((seat) => seat.state === "writing")) {
        return "The High Table is writing its notes";
      }
      if (table?.chief?.state === "writing") return "The chief architect is ruling";
      return "Writing the spec";
    }
    case "development":
      return input.plan ? `${input.plan.done} of ${input.plan.steps} plan steps built` : "Building";
    case "review":
      return "Reviewing the change";
    case "checks":
      return "Running the checks";
    case "documentation_sync":
      return "Syncing the docs";
    case "delivery":
      return "Delivering";
    default:
      return `${stageWord(stage)} is running`;
  }
}
function workFailure(input) {
  const expected = input.gatesExpected ?? [];
  const rank = (name) => {
    const index = expected.indexOf(name);
    return index === -1 ? expected.length : index;
  };
  const [first, ...rest] = (input.gates ?? []).filter((gate) => gate.required && gate.verdict === "fail" && gate.owner !== "paqad-record").map((gate) => gate.name).sort((a, b) => rank(a) - rank(b));
  if (!first) return null;
  return rest.length > 0 ? `${failurePhrase(first)} and ${rest.length} more` : failurePhrase(first);
}
function inconclusiveHeadline(verdict, gap) {
  const reason = (verdict.work_reasons ?? verdict.reasons ?? [])[0];
  return headlineRow(
    13,
    "warn",
    reason ? `Inconclusive: ${reason}` : "Inconclusive",
    "Inconclusive",
    gap
  );
}
function gapLine(gaps) {
  const [first] = gaps;
  if (!first) return {};
  return { gap: gaps.length > 1 ? `${first} and ${gaps.length - 1} more` : first };
}
function headlineRow(row, tone, text, word, extra = {}) {
  return { text, tone, row, word, ...extra };
}
function settledHeadline(stages) {
  const next = stages.find((stage) => stage.mandatory && stage.state === "missing");
  const done = [...stages].reverse().find((stage) => stage.state === "complete" || stage.state === "redone");
  if (done && next) {
    return headlineRow(
      18,
      "neutral",
      `${stageWord(done.stage)} done. Next: ${stageWord(next.stage)}`,
      "Working"
    );
  }
  if (!done) {
    return headlineRow(19, "neutral", `Next: ${next ? stageWord(next.stage) : "Plan"}`, "Working");
  }
  return headlineRow(20, "neutral", "Every stage is recorded. Waiting for the verdict.", "Working");
}
function idleHeadline(input) {
  if (input.paused > 0) {
    return { text: 'A change is paused. Say "continue" to pick it up.', tone: "neutral" };
  }
  if (input.route && input.route !== "no-workflow") {
    return { text: `Working on ${input.route.replace(/-/g, " ")}.`, tone: "working" };
  }
  if (input.rulesDrift) {
    return { text: input.rulesDrift, tone: "warn" };
  }
  return { text: "Ready. No change in progress.", tone: "neutral" };
}
function workIsDone(input) {
  if (!input.verdict || input.checks !== "green") return false;
  if (input.stages.some((stage) => stage.state === "running")) return false;
  const ended = input.stages.filter((stage) => stage.mandatory).every((stage) => BUILD_ENDED_STATES.has(stage.state));
  return ended && workFailure({ gates: input.gates }) === null;
}
function notProvenLines(verdict) {
  if (workKindOf(verdict) === "pass") return [];
  return (verdict.work_reasons ?? verdict.reasons ?? []).map(
    (reason) => reason.startsWith("paqad ") ? reason : `paqad could not prove ${reason}`
  );
}
var WATCHING_CI = "Coding Done: Watching CI";
function doneHeadline(input, ladder, gaps) {
  const rung = ladder?.rung ?? "done";
  const declined = input.ship?.commit_decision === "decline";
  const note = rung === "done" && ladder?.uncommitted ? declined ? "left uncommitted, as you asked" : "not committed" : null;
  return headlineRow(16, "good", RUNG_WORDS.done, RUNG_WORDS.done, {
    rung,
    ...note ? { note } : {},
    ...gapLine(gaps)
  });
}
function computeHudHeadline(input) {
  if (input.pendingDecisions > 0) {
    return headlineRow(6, "needs-you", WAITING_FOR_ANSWER, WAITING_FOR_ANSWER);
  }
  if (!input.active) return idleHeadline(input);
  const ship = input.ship ?? null;
  const ladder = input.ladder ?? null;
  const pr = prName(ship);
  const gaps = [...input.recordGaps ?? [], ...input.unproven ?? []];
  const gap = gapLine(gaps);
  if (ship?.pr_state === "closed") return headlineRow(9, "warn", `${pr} was closed`, "PR closed");
  if (ship?.pr_state === "open" && ship.ci?.state === "fail") {
    return headlineRow(10, "bad", `CI failed on ${pr}`, "CI failed", {
      ...ladder?.rung ? { rung: ladder.rung } : {}
    });
  }
  if (ship?.push?.state === "failed")
    return headlineRow(11, "bad", "The push failed", "Push failed");
  const running = input.stages.find((stage) => stage.state === "running");
  const held = running && (input.verdictEarlier || input.stageAgentLive);
  const verdict = held ? null : input.verdict;
  const work = workKindOf(verdict);
  const failure = verdict && work === "fail" ? workFailure(input) : null;
  if (failure) return headlineRow(12, "bad", failure, "Needs your attention", gap);
  if (work === "pass" && input.checks === "red") {
    return headlineRow(12, "bad", failurePhrase("code-tests-lint"), "Needs your attention", gap);
  }
  if (verdict && workIsDone({ ...input, verdict })) {
    const done = doneHeadline(input, ladder, [...gaps, ...notProvenLines(verdict)]);
    if (ship?.pr_state === "open" && ship.ci?.state === "pending") {
      return headlineRow(14, "good", WATCHING_CI, RUNG_WORDS.done, {
        ...done.rung ? { rung: done.rung } : {},
        ...done.gap ? { gap: done.gap } : {}
      });
    }
    return done;
  }
  if (verdict && (work === "inconclusive" || work === "fail")) {
    return inconclusiveHeadline(verdict, gap);
  }
  if (running) {
    return headlineRow(15, "working", runningWords(input, running.stage), "Working", {
      ...running.started_at ? { clock_from: running.started_at } : {}
    });
  }
  if (input.signoff?.state === "awaiting_sign_off") {
    const ref = input.signoff.ref ?? "this change";
    return headlineRow(21, "warn", `Waiting for sign-off on ${ref}`, "Waiting for sign-off");
  }
  if (work === "pass" && input.checks !== "green") {
    return headlineRow(
      13,
      "warn",
      "Inconclusive: no checks result for this change",
      "Inconclusive",
      gap
    );
  }
  const missing = input.stages.some((stage) => stage.mandatory && stage.state === "missing");
  if (verdict && !missing) return inconclusiveHeadline(verdict, gap);
  return settledHeadline(input.stages);
}
var TURN_END_SOURCES = /* @__PURE__ */ new Set([
  "hook:verification-completion",
  "hook:capability-gate-completion"
]);
function buildHudSnapshot(projectRoot, sessionHint, options = {}) {
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
  const sessionId = hudSessionId(projectRoot, sessionHint);
  const workflow = readWorkflowState(projectRoot, sessionId);
  const route = workflow.active?.workflow ?? null;
  const paused = workflow.paused.map((entry2) => ({
    workflow: entry2.workflow,
    lane: entry2.lane ?? null
  }));
  const scope = qualifyHudChange(projectRoot, sessionId, now);
  const dirName = scope.dir;
  const decisionRows = changeDecisions(projectRoot, dirName);
  const decisions = buildDecisions(decisionRows);
  const lastBlock = readRuleStopRecord(projectRoot, sessionId);
  if (!dirName) {
    return {
      schema: HUD_SNAPSHOT_SCHEMA,
      session_id: sessionId,
      generated_at: now().toISOString(),
      scope: { qualified: false, reason: scope.reason },
      workflow: buildWorkflow(projectRoot, sessionId, workflow),
      active: false,
      route,
      lane: workflow.active?.lane ?? null,
      lane_reason: workflow.active?.reason ?? null,
      headline: computeHudHeadline({
        route,
        active: false,
        stages: [],
        pendingDecisions: decisions.pending.length,
        verdict: null,
        flags: [],
        paused: paused.length,
        decisions: decisions.pending,
        rulesDrift: rulesDriftHeadline(projectRoot)
      }),
      change: null,
      paused,
      stages: [],
      timeline: [],
      table: null,
      claims: [],
      gates: [],
      gates_expected: expectedGateCodes(workflow.active?.lane ?? null),
      flags: [],
      rules: { recorded: false, applicable: null, loaded_at: null },
      findings: null,
      decisions,
      questions: null,
      last_block: lastBlock,
      delivery_plan: buildDeliveryPlan(projectRoot, readPolicy(projectRoot), null),
      plan: null,
      ship: null,
      signoff: null,
      verdict: null,
      receipt: null,
      evidence_expected: [],
      isolation: null,
      cost: null,
      tech_debt: null,
      rule_overrides: null
    };
  }
  const reading = freshVerdict(projectRoot, sessionId, dirName, scope.reason === "qualified");
  const verdict = reading.verdict;
  const receiptPath = hudReceiptPath(projectRoot, sessionId);
  const receipt = verdict && existsSync6(receiptPath) ? { path: receiptPath } : null;
  const record = readFeatureRecord(projectRoot, dirName);
  const fold = foldFeature(projectRoot, sessionId, dirName);
  const stages = buildStages(
    projectRoot,
    dirName,
    fold,
    gapStages(Array.isArray(verdict?.record_gaps) ? verdict.record_gaps : [])
  );
  const gates = buildGates(projectRoot, dirName);
  const specJson = readJsonFile2(join12(projectRoot, featureFilePath(dirName, "specification")));
  const lane = fold.lane ?? record?.lane ?? workflow.active?.lane ?? null;
  const table = withTrackRecords(
    projectRoot,
    dirName,
    buildHighTableView(projectRoot, dirName, specJson, lane)
  );
  const rulesLoaded = readRulesLoaded(projectRoot, dirName);
  const delivery = readFeatureDelivery(projectRoot, dirName);
  const policy = readPolicy(projectRoot);
  const base = policy && !policy.branches.base_branch.includes("*") ? policy.branches.base_branch : null;
  const startPoint = readChangeStartPoint(projectRoot, dirName, base);
  const signoff = buildSignoff(projectRoot, dirName, lane, specJson, record?.issue ?? null);
  const changeGit = readChangeGit(
    projectRoot,
    base,
    (error) => logHudWriteError(projectRoot, "changed files", error),
    startPoint
  );
  const changedFiles = changeGit?.files ?? null;
  const diff = readChangeDiff(
    projectRoot,
    startPoint,
    changedFiles,
    (error) => logHudWriteError(projectRoot, "changed diff", error)
  );
  const ship = buildShip(delivery, changedFiles ? changedFiles.length : null, diff);
  const isolationStatus = stageIsolationStatus(projectRoot, sessionId, dirName);
  const isolationSummary = summarizeStageIsolation(projectRoot, dirName);
  const findings = buildFindings(projectRoot, dirName);
  const checks = ladderChecks(projectRoot, dirName, verdict);
  const ladder = computeDoneLadder({
    workKind: workKindOf(verdict),
    checks,
    uncommitted: readWorkingTreeDirty(projectRoot),
    commits: Math.max(ship.commits, changeGit?.commitTimes.size ?? 0),
    prState: ship.pr_state,
    ci: ship.ci?.state ?? null,
    workDone: workIsDone({ stages, checks, verdict, gates })
  });
  const buildStage = stages.find((entry2) => entry2.stage === "development");
  const plan = buildPlanProgress(
    projectRoot,
    dirName,
    changedFiles,
    stageHasStarted(stages, "development")
  );
  const gatesExpected = expectedGateCodes(lane);
  const gaps = recordGaps(verdict, (fold.lane ?? record?.lane ?? null) !== null);
  if (!verdict && readVerdictAgreement(projectRoot, sessionId).no_record) {
    gaps.push(NO_VERDICT_RECORD);
  }
  const questions = buildQuestions(projectRoot, dirName);
  const stageRows = readFeatureStageUnit(projectRoot, dirName);
  const ledgerHealth = readUnitFileHealth(projectRoot, featureFilePath(dirName, "stageEvidence"));
  const proof = buildStageProof({
    ...readStageProofEvidence(projectRoot, dirName),
    lane,
    stages,
    gates,
    judged: verdict !== null,
    planProgress: plan,
    questions,
    rules: rulesLoaded ? rulesLoaded.applicable_rules.length : null,
    changedFiles
  });
  const closedChange = closedChangeOf(
    stageRows,
    verdict,
    changeGit?.commitTimes.values() ?? []
  );
  const flags = computeHudFlags({
    stages,
    gates,
    rulesRecorded: rulesLoaded !== null,
    tableSkipped: table?.skipped ?? false,
    tableLate: table?.met_after_freeze ?? false,
    isolationStatus,
    isolatedStages: isolationSummary?.stages ?? 0,
    stageAgentsSeen: stageRows.filter((row) => row.kind === STAGE_AGENT_KIND).length,
    stageAgentStages: stagesWithAgentRows(stageRows),
    findings,
    commitSplit: delivery.commit_split ?? null
  });
  return {
    schema: HUD_SNAPSHOT_SCHEMA,
    session_id: sessionId,
    generated_at: now().toISOString(),
    scope: { qualified: true, reason: scope.reason },
    workflow: null,
    active: closedChange === null,
    route,
    lane,
    lane_stages: laneStages(fold.lane),
    lane_reason: workflow.active?.reason ?? null,
    headline: computeHudHeadline({
      route,
      active: true,
      stages,
      pendingDecisions: decisions.pending.length,
      verdict,
      flags,
      paused: paused.length,
      ladder,
      checks,
      decisions: decisions.pending,
      table,
      ship,
      gates,
      gatesExpected,
      plan,
      recordGaps: stages.some((stage) => stage.state === "running") ? [] : gaps,
      unproven: unprovenLines(gates, verdict),
      verdictEarlier: verdictFromEarlierTurn(verdict, workflow, options.turnRunning === true),
      stageAgentLive: liveStageAgents(readFeatureStageUnit(projectRoot, dirName)).length > 0,
      signoff
    }),
    change: {
      dir: dirName,
      id: record?.change ?? null,
      issue: record?.issue ?? null,
      title: record?.title ?? null,
      branch: record?.branch ?? null,
      status: record?.status ?? null
    },
    paused,
    stages,
    timeline: mergeTimeline(
      buildTimeline(projectRoot, dirName, {
        title: record?.title ?? null,
        issue: record?.issue ?? null,
        lane
      }),
      recordEventsSafe(projectRoot, {
        rulesLoaded,
        table,
        decisions: decisionRows,
        specJson,
        delivery,
        ship,
        commitTimes: changeGit?.commitTimes ?? /* @__PURE__ */ new Map()
      })
    ),
    table,
    claims: [
      ...buildClaims(record?.kind, projectRoot, dirName),
      ...buildAcceptanceClaims(
        projectRoot,
        dirName,
        lane,
        changedFiles,
        specJson,
        buildStage?.started_at ?? null
      )
    ],
    gates,
    gates_expected: gatesExpected,
    flags,
    rules: {
      recorded: rulesLoaded !== null,
      applicable: rulesLoaded ? rulesLoaded.applicable_rules.length : null,
      loaded_at: rulesLoaded?.recorded_at ?? null
    },
    findings,
    decisions,
    questions,
    last_block: lastBlock,
    delivery_plan: buildDeliveryPlan(projectRoot, policy, delivery),
    plan,
    ship,
    signoff,
    verdict,
    receipt,
    evidence_expected: buildEvidenceExpected(projectRoot, sessionId, dirName),
    isolation: {
      status: isolationStatus,
      stages: isolationSummary?.stages ?? 0,
      tokens_not_recarried: isolationSummary?.tokensNotRecarried ?? 0
    },
    cost: buildHudCost(projectRoot, dirName, {
      turnRunning: options.turnRunning === true,
      now,
      onError: (error) => logHudWriteError(projectRoot, "cost", error)
    }),
    tech_debt: { raised: readFeatureTechDebtIndex(projectRoot, dirName) ?? [] },
    rule_overrides: readRuleOverrides(projectRoot, dirName).map(({ rule, file, by, at }) => ({
      rule,
      file,
      by,
      at
    })),
    ...optionalList("record_gaps", gaps),
    ...optionalList("repairs", buildRepairs(projectRoot, dirName)),
    ...ladder.rung ? { rung: ladder.rung } : {},
    ...reading.stale ? { verdict_stale: true } : {},
    ...closedChange ? { closed_change: closedChange } : {},
    start_point: startPoint ? {
      source: startPoint.source,
      base: startPoint.base,
      files_known: startPoint.worktree !== null
    } : null,
    proof: proof.map((entry2) => ({ ...entry2, run: buildStageRun(stageRows, entry2.stage, lane) })),
    ...ledgerHealth.torn_tail ? { ledger_note: "The last line of this ledger was cut, so paqad read every line before it." } : {}
  };
}
function optionalList(key, list) {
  return list.length > 0 ? { [key]: list } : {};
}
function writeHudSnapshot(projectRoot, sessionHint, options = {}) {
  const snapshot = buildHudSnapshot(projectRoot, sessionHint, options);
  const path = hudSnapshotPath(projectRoot, snapshot.session_id);
  writeFileAtomic(path, `${JSON.stringify(snapshot, null, 2)}
`);
  return { path, snapshot };
}
function writeHudSnapshotSafe(projectRoot, sessionHint, source) {
  try {
    return writeHudSnapshot(projectRoot, sessionHint, {
      turnRunning: !TURN_END_SOURCES.has(source)
    }).path;
  } catch (error) {
    logHudWriteError(projectRoot, source, error);
    return null;
  }
}
function logHudWriteError(projectRoot, source, error) {
  const message = error instanceof Error ? error.message : String(error);
  appendHooksLog(projectRoot, `hud-snapshot (${source}): not written: ${message}`);
  try {
    const logs = join12(projectRoot, ".paqad", "logs");
    mkdirSync3(logs, { recursive: true });
    appendFileSync(
      join12(logs, "hook-failures.log"),
      `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR hud-snapshot (${source}): ${message}
`
    );
  } catch {
  }
}

// src/hud/refresh.ts
var HUD_REFRESH_VERBS = [
  "decision create",
  "decision resolve",
  "rules load",
  "rules override",
  "fix prove",
  "lane set",
  "route set",
  "stage",
  "plan compile",
  "spec pipeline experts",
  "spec freeze",
  "review record",
  "checks run",
  "delivery-link",
  "deliver",
  "debt add",
  "debt promote",
  "debt close"
];
function isHudRefreshVerb(commandPath) {
  return HUD_REFRESH_VERBS.some(
    (verb) => commandPath === verb || commandPath.startsWith(`${verb} `)
  );
}
function refreshHudSnapshot(projectRoot, sessionHint, source, env = process.env) {
  try {
    if (!existsSync7(join13(projectRoot, PATHS.FRAMEWORK_PATH))) return null;
    if (!isFrameworkEnabledForRoot(projectRoot, env)) return null;
  } catch {
    return null;
  }
  return writeHudSnapshotSafe(projectRoot, sessionHint, source);
}

export {
  VERIFICATION_GATES,
  LATE_GATES,
  GATE_CATALOG,
  gateOwner,
  recordGapText,
  gateCatalogEntry,
  gateExpectedOnLane,
  BACKSTOP_VERIFICATION_ORIGINS,
  isBackstopVerificationOrigin,
  VERIFICATION_EVIDENCE_SCHEMA_VERSION,
  EVIDENCE_GATE_STATUSES,
  EVIDENCE_OVERALL_STATUSES,
  EVIDENCE_FAILURE_CATEGORIES,
  verdictKindFor,
  LANE_RECORD_GAP,
  unrecordedStagesGap,
  verdictParts,
  NO_VERDICT_RECORD,
  HUD_VERDICT_WORDS,
  verdictSessionId,
  readVerdictAgreement,
  writeVerdictAgreement,
  readHudVerdict,
  writeHudReceipt,
  readFixProofRows,
  appendFixProofRow,
  countedRedRow,
  FIX_PROOF_STATE_LABEL,
  foldFixProofState,
  CHECKS_REPORT_SCHEMA_VERSION,
  writeChecksReport,
  writeFeatureChecks,
  readChecksReport,
  readFeatureChecks,
  appendRuleOverride,
  readRuleOverrides,
  isEditOutcome,
  overrideAwaitsOutcome,
  appendRuleOverrideOutcome,
  DEBT_STATUSES,
  techDebtDir,
  readDebtItems,
  addDebtItem,
  promoteDebtItem,
  closeDebtItem,
  listDebtItems,
  newestFirst,
  readFeatureTechDebtIndex,
  writeFeatureTechDebtIndex,
  checkBooleanGate,
  checkJudgedGate,
  createPass,
  createFail,
  createInconclusive,
  createNotRequired,
  createDidNotRun,
  AC_ID_PATTERN,
  extractAcIdFromIssue,
  collectObservedAcIds,
  AcTestMappingGate,
  extractImportSpecifiers,
  scanImports,
  discoverSourceRoots,
  discoverModuleHealth,
  GATE_STATE_LABEL,
  gradeGateResult,
  VERIFICATION_EVIDENCE_RELATIVE_PATH,
  VERIFICATION_EVIDENCE_STDERR_BUDGET_BYTES,
  buildVerificationEvidence,
  writeVerificationEvidence,
  readTraceabilityMap,
  buildAndWriteChangeTraceabilityMap,
  hasStageAgentEvidence,
  hasStageAgentEvidenceSince,
  summarizeStageIsolation,
  formatStageIsolationLine,
  resolveEnterprisePolicy,
  writesLedger,
  HEURISTIC_BAND_FLOOR,
  resolveDuplicationConfig,
  resolveDuplicationMode,
  buildBundleCompletenessConfig,
  HUD_SNAPSHOT_SCHEMA,
  TIMELINE_ROWS,
  TIMELINE_COMMITS,
  buildTimeline,
  buildRecordEvents,
  computeDeliveryPlan,
  buildSignoff,
  closedChangeOf,
  TRACK_RECORD_BUNDLES,
  buildTrackRecords,
  gapStages,
  expectedGateCodes,
  buildShip,
  hudSessionId,
  workKindOf,
  FAILURE_PHRASES,
  failurePhrase,
  RUNG_WORDS,
  computeHudFlags,
  WAITING_FOR_ANSWER,
  workIsDone,
  WATCHING_CI,
  computeHudHeadline,
  TURN_END_SOURCES,
  buildHudSnapshot,
  writeHudSnapshot,
  writeHudSnapshotSafe,
  HUD_REFRESH_VERBS,
  isHudRefreshVerb,
  refreshHudSnapshot
};