/** * Deterministic discovery signal for the doc-truth rule. * * doc-truth's MANDATORY protocol tells the agent to "list every claim-bearing * line the diff touched" and verify each against the code — but nothing makes * that inventory non-optional, and calibration of the four seed fixtures * (pr667 0/10, pr711 0/10, pr716 2/10, pr687 1/10 on kimi-k2.7-code) showed the * model, under budget pressure on a bug-rich PR, doesn't reliably *chase* the * doc claim on its own initiative (issue #729). The claim was in-prompt and the * rule was active; what was missing was a handed-to-it worklist. * * This module pre-computes that worklist, mirroring the * `` / `` precedents: a * zero-LLM regex pass over the ADDED lines of the touched guidance/doc surfaces * (the exact surfaces `guidance-surface-signals` passes through), extracting * claim-shaped prose and injecting it as a `` block — "the * discovery step is done for you; verify EACH against the code". It hands the * agent concrete claims to check, countering the initiative-driven miss. * * It is a DISCOVERY aid, not a verdict: extraction is intentionally recall- * biased on claim shapes but skips code (fenced blocks) and tabular data, and * the render layer reminds the agent that a worklist entry may be descriptive * prose rather than a falsifiable behavioral claim — those need no finding, so * the block never manufactures a contradiction the code doesn't support. * * `citedPath` (issue #749) handles the doc-that-cites-its-own-source shape: on * PR #748, 3 of 4 doc-truth findings were the model flagging "the code proving * this could not be located" against prose that named its own evidence file * (e.g. "see `packages/review/src/defaults.ts` for the source of truth") — the * evidence pre-fetch never treated the citation as an anchor, so the model * never saw the file and flagged absence-of-verification as if it were * falsehood. When a claim's own excerpt names a repo file, that citation * outranks every other evidence tier (a claim that ships its own pointer * should arrive pre-verified), and a citation that does NOT resolve gets a * one-line "not found" note instead — a stale citation is itself doc-truth * signal, not something to search around. * * `collectClaimSources`/`addedCodeCommentLines` widen extraction to changed CODE files (not just * guidance/doc surfaces) — motivated by pr658's Finding A, the canonical case * this widening exists to catch: the stale `embeddings.enabled` doc COMMENT * in `packages/core/src/config/schema.ts`, an ordinary source file that * `collectGuidanceSurfaceChanges` never looks at, so the claim was never * LISTED for any contract (v1's open findings list or v2's per-claim one) to * force engagement with. The scan stays tight to the same claim-shaped-prose * discipline as the doc-surface scan, but the LINE shape narrows further: a * changed code file's added line only counts when it reads as a comment, * docstring, or description-valued string literal (`.describe(...)`, * `description: "..."` — the zod/JSON-schema shape) — ordinary code is never * scanned. TODOs, attribution lines, and doc-comment tags (`@param`, …) are * excluded before classification even though they're comment-shaped: a TODO * describes intended/future work, not a claim about current behavior (see * `extractCommentProse`). Claims mined this way share the render/render-cap * pipeline with doc-surface claims but are capped separately * (`MAX_CODE_CLAIMS`) so a comment-heavy code diff can't crowd out the * doc-surface claims that are the historically reliable signal. * * `findCitedPathDiffEvidence` widens evidence PREFETCH for a claim's own file * citation (`DocClaim.citedPath`) to the PR's own diff, not just the indexed * `repoChunks`. Motivated by PR #811's own review: a doc claim cited * `.github/workflows/lien-review.yml`, which WAS part of that PR's diff but * is not an AST-analyzable language, so it never appears in `repoChunks` — * the doc-truth pass reported "not available in the review material… before * budget exhaustion" instead of comparing against the one hunk that would * have settled it in one read. When a citation names a file that's part of * THIS PR, its diff hunk is fetched directly (byte-capped like every other * signal) in preference to any indexed excerpt — the diff is the more * PR-relevant material and needs no index entry to exist. Falls back to the * existing indexed-chunk lookup when the cited file isn't part of the diff, * and to the existing "not found" note (now naming both sources checked) * when neither resolves — degrading loudly rather than silently omitting. */ import type { CodeChunk } from '../types.js'; import type { SignalContext } from './signal-context.js'; /** * The claim shapes doc-truth enumerates, plus the two scope shapes the seed * fixtures need. Used as a hint label internally (extraction order) — the * rendered worklist lists the prose, not the shape. */ export type DocClaimShape = 'mechanism' | 'state' | 'default' | 'scope-gate' | 'scope-unchanged' | 'requirement' | 'negation'; /** * A code (or sibling-doc) excerpt located deterministically for a claim: the * material the reviewer must COMPARE the claim against. Turns verification from * an O(n) tool investigation into a single comparison, mirroring how * `` pre-computes the surviving line. */ export interface DocClaimEvidence { /** Repo-relative path of the chunk the evidence was taken from. */ file: string; /** New-file line number of the first excerpt line. */ startLine: number; /** The relevant lines of the located chunk (windowed + capped). */ excerpt: string; /** The anchor token that located this evidence (used to center the excerpt). */ anchor: string; /** * True when the evidence is a sibling DOC/guidance file rather than code — * the acceptable evidence shape for omission claims (e.g. a claim's * enumeration compared against an ADR's fuller list), flagged so the agent * weighs it as prose-vs-prose, not prose-vs-code. */ fromDoc: boolean; /** * True when this entry is a one-line "cited file not found" note rather * than a located excerpt — the claim named its own evidence file (see * `DocClaim.citedPath`) but that file resolves against neither the PR's * own diff nor `repoChunks`. `file` carries the cited (unresolved) path; * `excerpt` is unused. A stale citation is itself doc-truth signal (issue * #749). */ citedPathMissing?: boolean; /** * True when this evidence is the cited file's raw PR diff hunk (referenced- * file evidence prefetch) rather than an indexed chunk excerpt — the file * is part of THIS PR's diff but was not necessarily analyzable/indexed * (e.g. a workflow YAML, see PR #811). Always false alongside `fromDoc` * (a diff hunk isn't chunk-sourced). */ fromDiff?: boolean; } /** * An explicit repo-file citation found in a claim's own excerpt — "see * `packages/review/src/defaults.ts`" — plus, when present, an adjacent * backticked identifier in the same excerpt that narrows which chunk of that * file is the described one (issue #749). See `extractCitedPath`. */ export interface DocClaimCitedPath { /** The path token as it appeared in the claim excerpt (repo-relative). */ path: string; /** An adjacent backticked identifier in the same excerpt, if any. */ symbol?: string; } /** A claim-shaped line the diff added to a guidance/doc surface. */ export interface DocClaim { /** Repo-relative path of the guidance/doc surface the claim was added to. */ file: string; /** The added line, trimmed and capped (see MAX_CLAIM_CHARS). */ claimText: string; /** Which claim shape matched — most-specific-first (see CLAIM_SHAPES). */ shape: DocClaimShape; /** * An explicit file citation the claim's own excerpt names, if any. When * present it takes priority over every other evidence tier — see * `findClaimEvidence`. */ citedPath?: DocClaimCitedPath; /** * The code/sibling-doc the claim describes, located deterministically over * the indexed repo. Absent when no anchor in the claim resolves to a chunk * (or when there is no repo index to search) — the render layer then falls * back to the investigate-it-yourself instruction. */ evidence?: DocClaimEvidence; } /** Lines of context above / below the anchor line in an evidence excerpt (~6-line window). * Exported for reuse by docs-drift-signals.ts's own excerpt/tier windowing. */ export declare const EVIDENCE_LINES_BEFORE = 2; export declare const EVIDENCE_LINES_AFTER = 3; /** A fenced code block opener/closer — claims are prose, not code samples. Exported for reuse * by docs-drift-signals.ts, which tracks fence state over a plain doc chunk the same way this * module tracks it over a diff's post-image lines. */ export declare const FENCE_RE: RegExp; /** Return the first matching claim shape for a prose line, or null. Exported (behavior-neutral) * for reuse as docs-drift-signals.ts's Tier-1 behavioral-claim detector — see that module. */ export declare function classifyClaim(text: string): { shape: DocClaimShape; matchIndex: number; } | null; /** * Extract claim-candidate prose from one line of a code file's patch, or undefined when the line * is not a comment/docstring/description-literal shape (ordinary code) or is noise (see above). A * `.describe(...)`/`description: "..."` match is checked FIRST since those lines are otherwise * ordinary code; everything else must additionally read as a comment/docstring line. Exposed for * testing. */ export declare function extractCommentProse(rawLine: string): string | undefined; /** * The candidate-claim prose lines of one changed CODE file's patch: ADDED lines whose text is a * comment, docstring, or description-valued string literal (see `extractCommentProse`) — never * arbitrary code. This is the widened surface pr658's Finding A motivated: a JSDoc comment on a * `LienConfig` field in `packages/core/src/config/schema.ts`, an ordinary source file the * doc-surface-only scan could never see. * * Tracks `OpenComment` state across the patch's full NEW-file view (context + added, like * `addedProseLines`' fence tracking) so an interior CONTINUATION line — whether or not it repeats * a `*`/triple-quote marker, and even when the construct's OPENING line was unmodified context — * still contributes its prose (the multi-line gap this widening closes; see `OpenComment`). * Exposed for testing. */ export declare function addedCodeCommentLines(patch: string): string[]; /** * Collect claim-shaped lines from every changed guidance/doc surface AND changed code file * (comments/docstrings/description literals only — see `collectClaimSources`), smallest hunk * first. Identical claim lines are deduped across both sources (see `claimAdmitter`). * Code-derived claims are capped separately at `MAX_CODE_CLAIMS` so a comment-heavy code diff * cannot crowd out doc-surface claims. Returns ALL claims up to that cap (uncapped for * doc-surface claims — the render layer applies `MAX_CLAIMS` and notes any overflow). Exposed * for testing. */ export declare function extractDocClaims(patches: Map): DocClaim[]; /** * Extract locate-able anchors from a claim line, most-distinctive first: * dotted/starred config keys, then camelCase/PascalCase and snake identifiers, * then the plain words inside backtick spans (e.g. `backend: "lancedb"` yields * `backend`, `lancedb`). Free-prose plain words are deliberately NOT anchors — * only backtick-quoted ones — to keep the lookup from matching generic vocab. * Exposed for testing. */ export declare function extractAnchors(text: string): string[]; /** * Extract an explicit repo-file citation from a claim's own excerpt, plus an * adjacent backticked identifier in the same excerpt (if any) to narrow which * chunk of that file is the described one. Both the backtick form * ("see `packages/review/src/defaults.ts`") and the markdown-link form whose * display text is the path itself ("[packages/review/src/defaults.ts](../..)") * parse identically here, since both leave a bare path token in `text` once * `stripLinkTargets` and backtick delimiters are out of the way. A bare word * with no extension/path shape (e.g. "defaults") never matches. Exposed for * testing. */ export declare function extractCitedPath(text: string): DocClaimCitedPath | undefined; /** * Locate the code (or sibling doc) a single claim describes. An explicit * self-citation (`claim.citedPath`) is resolved first — against the PR's own * diff, then the repo index (see `findCitedPathEvidence`) — and, when * present, short-circuits the rest of this function; this is the ONE path * that can still produce evidence with no repo index at all (a citation * resolving purely against `patches`). Otherwise tries a case-sensitive * anchor pass against `repoChunks` (identifiers/keys are case-bearing), then * a case-insensitive fallback. Returns undefined when no anchor resolves or * there is no repo index to search. */ export declare function findClaimEvidence(claim: DocClaim, repoChunks: CodeChunk[] | undefined, changed: Set, patches?: Map): DocClaimEvidence | undefined; /** * Attach located evidence to each claim (up to the render cap — evidence for * claims that would be dropped is never computed). Returns a new array; input * claims are not mutated. Exposed for testing. */ export declare function attachEvidence(claims: DocClaim[], context: SignalContext): DocClaim[]; /** * Render the doc-claims worklist as a `` block for the agent's * initial message. Returns '' when there are no claims so callers can append * unconditionally. Caps at MAX_CLAIMS with an explicit omission note; drops * per-entry evidence (never a whole claim) once MAX_DOC_CLAIMS_CHARS is hit. */ export declare function renderDocClaims(claims: DocClaim[]): string; /** * Build the `` section from the review context. Returns '' when * there is no diff or no changed guidance/doc surface with a claim-shaped line. * Each rendered claim carries a deterministically-located code/sibling-doc * evidence excerpt when one is found in the repo index. */ export declare function renderDocClaimsSection(context: SignalContext): string; //# sourceMappingURL=doc-claims-signals.d.ts.map