/** Club selection for a roadmap ticket (mirrors core ClubSelection) */ export type RoadmapClub = 'driver' | 'long_iron' | 'short_iron' | 'wedge' | 'putter'; /** Canonical values plus bounded aliases retained by pre-schema roadmap history. */ export type RoadmapTicketComplexity = 'trivial' | 'small' | 'standard' | 'moderate' | 'multi_package' | 'multi-package' | 'risky'; /** A single ticket within a sprint */ export interface RoadmapTicket { key: string; id?: string; title: string; club: RoadmapClub; complexity: RoadmapTicketComplexity; depends_on?: string[]; /** New tickets use one issue; legacy history may retain multiple issue links. */ github_issue?: number | number[]; note?: string; } /** A sprint within the roadmap */ export interface RoadmapSprint { id: number; /** * Canonical string id, present only when authored as a string to preserve an * exact suffix a number cannot hold (e.g. "458.10", distinct from 458.1). When * absent, identity derives from `id` via the roadmap-aware helpers. `id` remains * the numeric mirror used for ordering arithmetic and the store (GH #635). */ id_key?: string; theme: string; par: 3 | 4 | 5; slope: number; type: string; tickets: RoadmapTicket[]; depends_on?: number[]; status?: string; note?: string; outcome?: string; phase?: string; wave?: string; artifacts?: string[]; expected_artifacts?: string[]; research?: string[]; } /** A phase grouping sprints */ export interface RoadmapPhase { name: string; sprints: number[]; /** * Canonical membership keys, present when any member was authored as a string * to preserve an exact suffix (e.g. "458.10" alongside "458.1"). When present, * this is the authoritative membership; `sprints` is the numeric mirror, which * cannot distinguish 458.10 from 458.1 (GH #635). */ sprint_keys?: string[]; description?: string; status?: string; note?: string; } /** Top-level roadmap definition */ export interface RoadmapDefinition { name: string; description?: string; phases: RoadmapPhase[]; sprints: RoadmapSprint[]; } export interface RoadmapValidationError { type: 'error'; sprint?: number; ticket?: string; message: string; } export interface RoadmapValidationWarning { type: 'warning'; sprint?: number; ticket?: string; message: string; } export interface RoadmapValidationResult { valid: boolean; errors: RoadmapValidationError[]; warnings: RoadmapValidationWarning[]; } /** Return true for legacy encoded inserted half-sprint ids like 435 => S43.5. * Decimal roadmap ids such as 75.5 are already explicit and are left as-is. * The encoding is intentionally limited to pre-S100-style three-digit ids * ending in 5 so ordinary post-S100 ids like 105 and 115 stay canonical. */ export declare function isEncodedInsertedSprintId(id: number): boolean; /** Numeric value used for ordering sprint ids. Encoded inserted ids sort * between their surrounding canonical sprints: 435 sorts as 43.5. */ export declare function sprintOrderValue(id: number): number; /** Format the numeric portion of a sprint id for human-facing output. */ export declare function formatSprintNumber(id: number): string; /** Format a full sprint label, e.g. S95 or S43.5. */ export declare function formatSprintLabel(id: number): string; /** * Describe why a written sprint id cannot round-trip, or null when it is fine. * * Sprint ids are stored as JSON/YAML numbers, so a decimal whose fraction ends in * a zero loses that zero and silently becomes a different — usually existing — * sprint: `458.10` reads back as `458.1`, and `458.0` as `458`. That corrupts * dependencies, focused context, evidence lookup and scorecard identity, and it is * why a phase had to be renumbered to whole sprints (GH #635). * * Takes a **string** deliberately. By the time such an id has been parsed into a * number the trailing zero is already gone, so the ambiguity is only detectable in * the text as written. * * Rejecting these is a stopgap; the durable fix is canonical string ids. */ export declare function describeSprintIdAmbiguity(written: string): string | null; /** Parse human-entered sprint ids such as "114", "114.5", or "S114.5". */ export declare function parseSprintNumber(value: string | number): number | null; export declare function compareSprintIds(a: number, b: number): number; /** Return the next canonical sprint id after a completed sprint/inserted sprint. */ export declare function nextCanonicalSprintId(id: number): number; export declare const ROADMAP_TERMINAL_STATUSES: Set; /** * Statuses that are not durably terminal but are intentionally not offered as * the next sprint to work on. A deferred sprint is postponed by choice — it is * neither done (so not terminal) nor queued work (so not selectable). It becomes * selectable again once its status is edited back to planned/active (GH #660). */ export declare const ROADMAP_DEFERRED_STATUSES: Set; /** Return true when a roadmap sprint has a durable terminal disposition. */ export declare function isRoadmapSprintTerminal(sprint: RoadmapSprint): boolean; /** * Return true when a roadmap sprint should still be considered selectable work — * the pool the "next sprint" pickers, rollover target, and current-sprint * inference draw from. Excludes both terminal statuses and deferred (a * deliberately postponed sprint must not be surfaced as the next thing to do). */ export declare function isRoadmapSprintPending(sprint: RoadmapSprint): boolean; type RoadmapTicketInput = Partial & { id?: unknown; key?: unknown; }; export declare function getRoadmapTicketKey(ticket: RoadmapTicketInput): string | null; /** Resolve legacy encoded half-sprint IDs using evidence from one roadmap. */ export declare function isEncodedInsertedSprintInRoadmap(roadmap: RoadmapDefinition, id: number): boolean; export declare function roadmapSprintOrderValue(roadmap: RoadmapDefinition, id: number): number; export declare function compareRoadmapSprintIds(roadmap: RoadmapDefinition, a: number, b: number): number; export declare function formatRoadmapSprintLabel(roadmap: RoadmapDefinition, id: number): string; /** * Canonical string identity for a sprint: the authored `id_key` when present * (preserving an exact suffix), else derived from the numeric `id` with the same * roadmap-aware legacy decode as the label. This is THE identity for a sprint — * use it wherever `458.10` must stay distinct from `458.1` (GH #635). */ export declare function roadmapSprintKey(roadmap: RoadmapDefinition, sprint: RoadmapSprint): string; /** Validate a roadmap definition for structural correctness. * Optionally cross-check sprint status against scorecards and/or shipped * sprint commits on main when provided. Caller is responsible for collecting * scorecards (via loadScorecards) and shipped IDs (via findShippedSprintsOnMain). */ export declare function validateRoadmap(roadmap: RoadmapDefinition, scorecards?: { sprint_number: number; }[], shippedSprintIds?: Set): RoadmapValidationResult; export interface CriticalPathResult { path: number[]; length: number; totalPar: number; } /** Compute the critical path (longest dependency chain) through the roadmap */ export declare function computeCriticalPath(roadmap: RoadmapDefinition): CriticalPathResult; export interface ParallelGroup { sprints: number[]; reason: string; } /** Find sprints that can run in parallel (no mutual dependencies) */ export declare function findParallelOpportunities(roadmap: RoadmapDefinition): ParallelGroup[]; /** Cast a raw JSON object to RoadmapDefinition if minimally structurally valid. * Unlike parseRoadmap, this does not run full validation — useful when callers * want to flag validation issues against a structurally-cast roadmap (e.g. * drift detection should still run when ticket counts or numbering are off). */ export declare function castRoadmapStructure(json: unknown): RoadmapDefinition | null; /** Parse and validate a roadmap from a JSON object */ export declare function parseRoadmap(json: unknown): { roadmap: RoadmapDefinition | null; validation: RoadmapValidationResult; }; /** Format a roadmap summary as markdown */ export declare function formatRoadmapSummary(roadmap: RoadmapDefinition): string; /** Format strategic context for briefings — concise 3-5 line summary */ export declare function formatStrategicContext(roadmap: RoadmapDefinition, currentSprint: number): string | null; /** Find the next planned sprint after currentSprint. * Prefers a sprint with all dependencies satisfied (status:complete); * falls back to the lowest-id non-complete sprint when nothing is unblocked. * Returns null if no candidate found. */ export declare function findNextPlannedSprint(roadmap: RoadmapDefinition, currentSprint: number): RoadmapSprint | null; export {}; //# sourceMappingURL=roadmap.d.ts.map