/** * Deterministic "stale duplicate literal" signal for PR reviews. * * The stale-duplicate rule asks the agent to grep for literals the diff changed * in one place but left hardcoded elsewhere. That grep-and-reason step is the * flaky, model-dependent part — and in the offline harness the agent's * grep/read tools are blind (they read a capture-time temp dir that no longer * exists), so the rule can never find the surviving copy. * * This module pre-computes the structural fact instead, mirroring the * `` / `` precedents: collect every distinctive * literal the diff TOUCHES (on a `+` or `-` line — this covers both a literal * the PR removed and one it conditionalized in place), then scan the indexed * repo's post-image chunk content for the SAME literal surviving unchanged * OUTSIDE the diff. The result is injected as a * `` block so the agent confirms a handed-to-it * candidate (narrow judgement) rather than discovering it via blind grep. * * It injects FACTS (literal + locations + a short snippet), never reconstructed * chunk content — a blunt content dump was tried and regressed unrelated rules. */ import type { SignalContext } from './signal-context.js'; export type LiteralKind = 'string'; /** A distinctive string literal the diff touches (on a `+` or `-` line). */ export interface ChangedLiteral { /** Inner text of the string literal, without surrounding quotes. */ value: string; kind: LiteralKind; /** Display form: the literal re-quoted, e.g. `'claude-sonnet-4-6'`. */ display: string; /** File whose diff removed/changed this literal. */ file: string; /** Best-effort new-file line near where the change occurred. */ changedLine: number; } /** A surviving occurrence of a changed literal, outside the diff. */ export interface StaleSite { file: string; line: number; snippet: string; isComment: boolean; isTest: boolean; /** Build/tooling config or manifest (tsup/vitest/eslint config, tsconfig, package.json). */ isConfig: boolean; } export interface StaleLiteralCandidate { /** Display form of the literal (quoted for strings). */ literal: string; kind: LiteralKind; changedSite: { file: string; line: number; }; staleSites: StaleSite[]; confidence: 'low' | 'medium' | 'high'; } /** * Collect every distinctive literal each changed file's diff touches. * Exposed for testing. */ export declare function extractChangedLiterals(patches: Map): ChangedLiteral[]; /** * Pre-compute stale-duplicate literal candidates from the review context. * Returns [] when there is no diff or no full-repo index to scan against. */ export declare function computeStaleLiteralCandidates(context: SignalContext): StaleLiteralCandidate[]; /** * Same as {@link computeStaleLiteralCandidates}, but with an explicit * absolute deadline (a value comparable to `Date.now()`) instead of the * default budget. Exposed for testing the budget-exceeded path * deterministically — pass an already-past deadline — without relying on * real elapsed time. */ export declare function computeStaleLiteralCandidatesWithDeadline(context: SignalContext, deadline: number): StaleLiteralCandidate[]; /** * Render stale-literal candidates as a `` block for * the agent's initial message. Returns '' when there are no candidates so * callers can append unconditionally. */ export declare function renderStaleLiteralCandidates(candidates: StaleLiteralCandidate[]): string; /** * Same as {@link renderStaleLiteralSection}, but with an explicit absolute * deadline instead of the default budget. Exposed for testing the * timeout-path rendering deterministically — pass an already-past deadline — * mirroring {@link computeStaleLiteralCandidatesWithDeadline}. */ export declare function renderStaleLiteralSectionWithDeadline(context: SignalContext, deadline: number): string; /** * Build the `` section for the agent's initial * message. Returns the candidate block when there are candidates, an explicit * "None" block when the scan ran to completion and was clean, an explicit * "incomplete" block when the scan hit its time budget before finding any * survivors, or '' when no scan was possible (no diff or no repo index — e.g. * CLI mode). Callers append unconditionally. */ export declare function renderStaleLiteralSection(context: SignalContext): string; //# sourceMappingURL=stale-literal-signals.d.ts.map