/** * Milestone — Milestone and requirements lifecycle operations. * * ADR-457 build-at-publish: the hand-written bin/lib/milestone.cjs collapsed to * a TypeScript source of truth, compiled by tsc to a gitignored .cjs at the same * require() path. Behaviour preserved byte-for-behaviour; only types are added. */ import fs from 'node:fs'; import path from 'node:path'; // eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module import planningWorkspace = require('./planning-workspace.cjs'); // eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module import frontmatterMod = require('./frontmatter.cjs'); // eslint-disable-next-line @typescript-eslint/no-require-imports -- state.cjs is an export= CommonJS module import stateMod = require('./state.cjs'); import { platformWriteSync, platformReadSync, platformEnsureDir, execGit, retryRenameSync, contentChangedAfterNormalize } from './shell-command-projection.cjs'; import { formatGsdSlash, resolveRuntime } from './runtime-slash.cjs'; import { realClock } from './clock.cjs'; import { transitionCore } from './state-transition.cjs'; import { writeSetComplete } from './write-set.cjs'; import type { WriteSet } from './write-set.cjs'; import { updateTableCell, resetQuickTaskRows, QUICK_TASKS_SECTION_ABSENT } from './markdown-table.cjs'; import { requireSafePath, PathAcceptance, type ContainedPath } from './security.cjs'; // eslint-disable-next-line @typescript-eslint/no-require-imports -- audit.cjs is an export= CommonJS module import auditMod = require('./audit.cjs'); const { resolveQuickTaskSummaryFile } = auditMod; // eslint-disable-next-line @typescript-eslint/no-require-imports import ioMod = require('./io.cjs'); const { output, error } = ioMod; // eslint-disable-next-line @typescript-eslint/no-require-imports import cliExitMod = require('./cli-exit.cjs'); const { ExitError } = cliExitMod; // eslint-disable-next-line @typescript-eslint/no-require-imports import stateContract = require('./state-contract.cjs'); const { publishStateContract } = stateContract; // eslint-disable-next-line @typescript-eslint/no-require-imports import phaseIdMod = require('./phase-id.cjs'); const { normalizePhaseName, matchPhaseDirs, PHASE_NUMBER_TOKEN_SOURCE, isSentinelPhaseId, isSentinelPhaseDir } = phaseIdMod; import { escapeRegex } from './pattern.cjs'; // eslint-disable-next-line @typescript-eslint/no-require-imports import roadmapParserMod = require('./roadmap-parser.cjs'); const { getMilestonePhaseFilter, extractCurrentMilestone, getMilestoneInfo, sliceMilestoneWindow, } = roadmapParserMod; // eslint-disable-next-line @typescript-eslint/no-require-imports import planningScopeMod = require('./planning-scope.cjs'); const { SCOPE } = planningScopeMod; // eslint-disable-next-line @typescript-eslint/no-require-imports import coreUtilsMod = require('./core-utils.cjs'); const { extractOneLinerFromBody, countMatchedSummaries } = coreUtilsMod; // eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-scan.cjs is an export= CommonJS module import planScanMod = require('./plan-scan.cjs'); const { scanPhasePlans } = planScanMod; // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module import phaseLocatorMod = require('./phase-locator.cjs'); const { listMilestonePhaseDirs } = phaseLocatorMod; const { planningPaths, resolvePhaseIdConvention } = planningWorkspace; const { extractFrontmatter } = frontmatterMod; // ADR-3408 §8.3 / #3469: `writeStateMd` gets sync and NO preservation — the // same #3374-shaped exposure the milestone-complete write used to carry (a // stale body value silently clobbering fresher frontmatter, with no // divergence signal). Routed through the single write-seam composition // (`syncAndPreserveStateMd`) instead, under `withStateLock` — see // `cmdMilestoneComplete`'s own STATE.md-update block for the full rationale. const { syncAndPreserveStateMd, withStateLock, readModifyWriteStateMd } = stateMod; // #2288 security: a milestone version label becomes a filesystem directory // component (`milestones/