/** * scoreTask — THE task ranker (T12661, tiered per council verdict D11161 / T12691). * * Pure functional — no I/O, no DB access, no async. * * `cleo next`, the briefing's `nextTasks` and both `cleo analyze` paths rank * through this module. The order is LEXICOGRAPHIC, never an additive sum: * * 1. **Priority band** — the owner's priority: critical > high > medium > low. * 2. **Severity** — attested severity: P0 > P1 > P2 > P3 > unknown. An unset * severity is `unknown`; nothing is imputed (not even for a bug). * 3. **Bounded tiebreak** — computed graph facts: dependencies ready (+10), * phase alignment (+20), leverage (+5 per open dependent, capped at +20), * anti-starvation age (+1 per week after the first, capped at +10). * 4. **createdAt** — older first (parsed and normalised to UTC). * 5. **id**. * * Computed signals therefore never cross a band: a plain ready critical task * always outranks a low-priority P0 bug carrying every bonus. The additive * scorer this replaces let that bug win 115 to 110. BRAIN success/failure * patterns are informational factors only; they are not part of the order. * * @arch SDK Tool (Category B) — pure, no side effects, contracts-typed * @task T10068 * @task T12661 * @task T12691 * @epic T9835 */ import type { ScoreFactor, ScoreTaskContext, ScoreTaskInput, ScoreTaskKey, ScoreTaskResult, TaskSeverity } from '@cleocode/contracts'; /** Tier 1 — owner priority band rank (higher first). */ export declare const PRIORITY_BAND: Readonly>; /** Tier 2 — attested severity rank (higher first); `unknown` is 0. */ export declare const SEVERITY_RANK: Readonly>; /** Tier 3 — bonus when every dependency is satisfied. */ export declare const DEPS_READY_SCORE = 10; /** Tier 3 — bonus when the task's phase matches the current phase. */ export declare const PHASE_ALIGNMENT_SCORE = 20; /** Tier 3 — bonus per open task this task unblocks. */ export declare const LEVERAGE_PER_DEPENDENT = 5; /** Tier 3 — cap on the leverage bonus. */ export declare const MAX_LEVERAGE_SCORE = 20; /** Tier 3 — cap on the anti-starvation age bonus (+1 per week after the first). */ export declare const MAX_AGE_SCORE = 10; /** * Parse a task timestamp to epoch milliseconds, normalised to UTC. * * Stores hold both ISO-8601 with a zone (`2026-09-01T10:00:00Z`) and SQLite's * zone-less `YYYY-MM-DD HH:MM:SS`. `Date.parse` reads the zone-less form as * LOCAL time, so the same instant scored and sorted differently by machine * (T12661 review). A timestamp without a zone is taken as UTC. * * @param value - Timestamp text. * @returns Epoch milliseconds, or `NaN` when unparseable. */ export declare function parseTimestampMs(value: string | null | undefined): number; /** * Score a single task: its lexicographic key, every factor tagged with its * comparator tier, and the key folded into one number that sorts identically. * * @param task - Task to score * @param ctx - Scoring context (phase, dep statuses, leverage, `nowMs`, patterns) * @returns Score, key and factors * * @example * ```typescript * scoreTask({ id: 'T1', title: 'Auth', priority: 'high', kind: 'bug' }, {}).key; * // { band: 3, severity: 0, tiebreak: 10 } — severity unknown, deps ready * ``` */ export declare function scoreTask(task: ScoreTaskInput, ctx: ScoreTaskContext): ScoreTaskResult; /** * Render one factor as an `--explain` line naming its comparator tier. * * @param factor - Scored factor. * @returns e.g. `tier 1 band: priority high`, `tier 3 depsReady: all dependencies satisfied (+10)`, * or `info brainSuccess: … (not in the order)`. */ export declare function formatScoreFactor(factor: ScoreFactor): string; /** * Count, for every task id, the OPEN tasks that depend on it — the leverage * input of {@link scoreTask}. Done, archived and cancelled dependents are not * work this task unblocks. * * @param tasks - Tasks with `depends` and `status`. * @returns Map of task id to number of open dependents. */ export declare function computeLeverage(tasks: ReadonlyArray<{ depends?: string[]; status?: string; }>): Map; /** A task with its score, key and factors, as {@link rankTasks} returns it. */ export interface RankedTask { /** The task. */ task: T; /** The key folded into one sortable number. */ score: number; /** Every factor, tagged with its comparator tier. */ factors: ScoreFactor[]; /** The lexicographic key. */ key?: ScoreTaskKey; } /** * Order tasks by the tiered key: band, severity, tiebreak (all higher first), * then older `createdAt` (parsed, UTC), then id — deterministic, so every * ranker returns the same order for the same input. * * @param tasks - Candidate tasks. * @param ctx - Shared scoring context. * @returns Ranked tasks. */ export declare function rankTasks(tasks: readonly T[], ctx: ScoreTaskContext): RankedTask[]; /** * Build the {@link ScoreTaskContext} every ordering surface shares (T12692): * dependency statuses from the canonical readiness lookup, leverage over the * population, the ranking phase and one clock instant. `cleo next`, * `cleo orchestrate ready`, the focus ready wave and the wave listing all * build their context here, so the same tasks rank the same way everywhere. * * @param population - Tasks whose open dependents count as leverage. * @param dependencyLookup - Canonical dependency records (archived included). * @param opts - Ranking phase and optional clock. * @returns The scoring context. * @task T12692 */ export declare function buildRankingContext(population: Iterable<{ depends?: string[]; status?: string; }>, dependencyLookup: ReadonlyMap, opts: { currentPhase: string | null; nowMs?: number; }): ScoreTaskContext & { leverage: ReadonlyMap; }; /** * Reorder items by THE comparator ({@link rankTasks}, D11161): band, * severity, bounded tiebreak, createdAt, id. Items whose task record is not * in `tasksById` keep their input order after every ranked item. * * @param items - Items to order (not mutated). * @param idOf - Task id of an item. * @param tasksById - Task records to rank the items by. * @param ctx - Shared scoring context (see {@link buildRankingContext}). * @returns A new array in ranked order. * @task T12692 */ export declare function orderByRanking(items: readonly T[], idOf: (item: T) => string, tasksById: ReadonlyMap, ctx: ScoreTaskContext): T[]; //# sourceMappingURL=score-task-priority.d.ts.map