import type { Finding, RecommendationPriority, RecommendationReadiness } from '@manehorizons/cadence-types'; /** * Phase 242 (T2, §7.3, dec-20260731-001) — pure finding-routing derivation. * Turns a settle's raw `codeReview` findings into the set of new * `Recommendation` candidates T3 should write to the ledger, without ever * touching the ledger itself: no fs, no clock reads (`now` is injected), and * deliberately no import from `services/settle.ts` or any `store/*` * ledger-store module — T3 owns wiring this module's output into * `addRecommendation` (`intelligence/store/recommendations.ts`). Matches the * house pure-core/impure-shell split used throughout `verify/*` * (`finding-identity.ts`, `criteria-gap.ts`). * * Three responsibilities, in order: * 1. Group findings by `Finding.id` first (dec-20260731-001): two or more * findings that collide on identity within this one call merge into a * single candidate, whose summary text records how many occurrences * were collapsed. `computeFindingId` (`verify/finding-identity.ts`) * hashes `(file, normalized message)` only (phase 245 narrowed this — * `anchor`/`severity` no longer participate), so a shared id can only * arise from the same file and message — but occurrences CAN * legitimately disagree on `severity` (and therefore `anchor`/`line`), * since neither is a hash input anymore. The group's canonical * `finding` starts as the first occurrence encountered but is replaced * whenever a strictly more severe later occurrence arrives (see * `SEVERITY_RANK` below), so the merged candidate's * `severity`/`priority`/`file`/`line` always reflect the most severe * occurrence in the group — never silently whichever came first * (AC-5, phase 245). * 2. Skip any finding with no `id` at all (AC-3) — un-identified findings * (e.g. security-audit, which has no identity wired in yet) are * deliberately never force-routed. An empty-string id is treated the * same as a missing one. * 3. Skip any id already present in the caller-supplied `alreadyRoutedIds` * set (AC-2) — the cross-settle dedup lookup. This check is orthogonal * to the intra-call merge above: it applies regardless of how many * occurrences of that id appear in *this* call. * * Every finding message is passed through `redactSecrets` (the same choke * point `addRecommendation` uses for its own evidence text) before it is * embedded in a candidate's title/summary/evidence — `addRecommendation` * itself only redacts `evidence.summary`, and this is the first automated * caller whose text is never human-typed/human-reviewed before reaching the * ledger, since a finding's message can quote diff content verbatim. * * Exactly one `scoutId` is minted per call (AC-4), derived from the injected * `now` using the existing `scout-YYYYMMDD-HHMM` convention (UTC) — never one * per finding. */ /** Settle-pointer facts the caller already knows and this module has no * business re-deriving: which phase/draft this settle belongs to, its * SUMMARY content hash, and the on-disk SUMMARY.json path each routed * candidate's evidence should point at. */ export interface RoutingSettlePointer { phaseId: string; draftId: string; contentHash: string; summaryPath: string; } /** Structurally identical to `RecommendationEvidenceOverride` * (`intelligence/store/recommendations.ts`) on purpose, but declared * locally rather than imported — this module must not import a * ledger-store module (pure-core boundary). Because the shape matches * exactly, a `RoutingCandidate.evidence` value is directly assignable * wherever `RecommendationEvidenceOverride` is expected. */ export interface RoutingCandidateEvidence { kind: 'cadence-artifact'; summary: string; path: string; } /** * One new ledger candidate, shaped so T3 can pass it into * `addRecommendation`'s `AddRecommendationInput` almost verbatim — nothing * here needs re-deriving at the call site. */ export interface RoutingCandidate { title: string; summary: string; priority: RecommendationPriority; readiness: RecommendationReadiness; affectedAreas: string[]; affectedFiles: string[]; source: 'review'; sourceFindingId: string; scoutId: string; evidence: RoutingCandidateEvidence; } /** * Derive this settle's new routing candidates. `findingsByFile` is the * settle's raw `SummaryZ.codeReview` shape; `alreadyRoutedIds` is the set of * `Finding.id`s the caller already found routed in the ledger (read by the * caller — this function never reads the ledger itself); `pointer` names the * settle whose evidence every candidate should cite; `now` is the injected * clock (no `Date.now()`/`new Date()` inside this module). */ export declare function deriveRoutingCandidates(findingsByFile: Record, alreadyRoutedIds: ReadonlySet, pointer: RoutingSettlePointer, now: Date): RoutingCandidate[]; //# sourceMappingURL=finding-routing.d.ts.map