/** * Milestones: the goal each roadmap item serves (roadmap-milestones). * * `.aidlc/roadmap/milestones.yaml` lists milestones in order, and an item names one in its * `milestone:` line. Everything else is derived on read — a milestone's status, which one is * current, what sits under it — so there is no second copy of a fact to drift. fieldmodel-apps * kept its goals as prose in hold reasons, which no tool could count; this is the countable form. * * Pure over items, apart from `readMilestones`, so `roadmap next`, the page, `roadmap status` * and doctor all read one grouping and cannot disagree about a count (roadmap-milestones/AC-19). * * @module */ import { type RoadmapItem } from './paths.js'; /** File name inside the roadmap root. */ export declare const MILESTONES_FILE = "milestones.yaml"; /** A milestone id; `none` is the Unassigned marker and is refused as an id (AC-1). */ export declare const MILESTONE_ID_RE: RegExp; /** The value that says "no milestone, on purpose". */ export declare const UNASSIGNED = "none"; export interface Milestone { id: string; title: string; /** One-line exit test. */ exit: string; } export type MilestonesRead = { kind: 'absent'; } | { kind: 'ok'; milestones: Milestone[]; } | { kind: 'invalid'; error: string; }; /** Check the parsed file against AC-1, naming the first broken rule. */ export declare function validateMilestones(raw: unknown): { ok: true; milestones: Milestone[]; } | { ok: false; error: string; }; /** Read `/milestones.yaml`; `root` is the roadmap root. */ export declare function readMilestones(root: string): MilestonesRead; /** The milestones, or null when the file is absent or invalid — the "off" every reader but status and doctor takes (AC-2). */ export declare function milestonesOrNull(root: string): Milestone[] | null; export type MilestoneStatus = 'done' | 'current' | 'open' | 'not-started'; /** The groups under a milestone, in display order (AC-14). */ export declare const MILESTONE_GROUPS: readonly ["done", "in-flight", "next", "parked", "open", "dropped"]; export type MilestoneGroup = (typeof MILESTONE_GROUPS)[number]; export declare const GROUP_NAME: Record; export declare const STATUS_NAME: Record; /** * Where an item sits under its milestone. A declined hold item is dropped by the Board's rule * (`holdNote`: the last history reason begins `declined`); any other hold item is parked and * keeps its milestone open (AC-9). */ export declare function groupOf(item: RoadmapItem): MilestoneGroup; /** The milestone id an item is under, or null for Unassigned: `none`, absent, or unlisted (AC-3). */ export declare function assignedId(item: RoadmapItem, ids: ReadonlySet): string | null; export interface MilestoneSection { /** Null for Unassigned. */ id: string | null; title: string; exit: string | null; /** Null for Unassigned. */ status: MilestoneStatus | null; groups: Record; count: number; } export interface MilestonesView { /** Id of the current milestone; null when every milestone is done. */ current: string | null; sections: MilestoneSection[]; unassigned: MilestoneSection; } /** * Group every item under its milestone and derive each status (AC-3, AC-8..AC-10). Every item * lands in exactly one section, so the counts sum to the number of items. */ export declare function milestonesView(items: readonly RoadmapItem[], milestones: readonly Milestone[]): MilestonesView; /** Map a view's items to another shape — refs, for the page. */ export declare function mapView(view: MilestonesView, fn: (t: T) => U): MilestonesView; export interface FieldProblem { item: RoadmapItem; kind: 'missing' | 'unknown'; value?: unknown; } /** Open items with no `milestone:` line, or one naming neither `none` nor a listed id (AC-4, AC-5). */ export declare function fieldProblems(items: readonly RoadmapItem[], milestones: readonly Milestone[]): FieldProblem[]; /** One line naming the item and the `milestone` field. */ export declare function describeProblem(p: FieldProblem): string; //# sourceMappingURL=milestones.d.ts.map