/** * Retrospect schemas + pure helpers for `totem retrospect ` * (mmnto-ai/totem#1713). * * Substrate of the bot-tax cluster — the deterministic circuit-breaker * that fires when a PR has accumulated enough bot-review rounds to * suggest the loop has stalled. Reuses `recurrence-stats.ts` primitives * (`computeSignature`, `normalizeFindingBody`, `toSeverityBucket`) so * the bot-tax cluster (#1715 / #1714 / #1713) has a single severity + * signature vocabulary. * * Everything in this file is pure: Zod schemas + deterministic helpers. * No I/O, no command logic. No LLM. No GitHub API writes. */ import { z } from 'zod'; /** A push-grouped review round on the target PR. */ export declare const RetrospectRoundSchema: z.ZodObject<{ /** 1-based, monotonically increasing in submission order. */ roundNumber: z.ZodNumber; /** ISO 8601 timestamp of the earliest review submission for this round. */ submittedAt: z.ZodString; /** PR head SHA at review time; absent when the upstream review record lacks `commit_id`. */ headSha: z.ZodOptional; /** Number of bot findings tied to this round. */ findingCount: z.ZodNumber; }, "strip", z.ZodTypeAny, { submittedAt: string; roundNumber: number; findingCount: number; headSha?: string | undefined; }, { submittedAt: string; roundNumber: number; findingCount: number; headSha?: string | undefined; }>; export type RetrospectRound = z.infer; /** * Classification verdict for a single bot finding. * * - `route-out` — finding is better tracked as a follow-up issue than blocking the current PR. * - `in-pr-fix` — finding should be addressed before merge. * - `undetermined` — no heuristic signal fired; contributor judgment. */ export declare const RetrospectClassificationSchema: z.ZodEnum<["route-out", "in-pr-fix", "undetermined"]>; export type RetrospectClassification = z.infer; /** * Closed catalog of `route-out` reasons. The Zod-side mirror of * `RETROSPECT_ROUTE_OUT_REASONS` (defined later in the file). Used by the * discriminated `RetrospectFindingSchema` to enforce the deterministic * contract: every `route-out` finding carries a reason from this enum, and * non-route-out findings forbid the field entirely. Per CR mmnto-ai/totem#1734 * round-2. */ export declare const RetrospectRouteOutReasonSchema: z.ZodEnum<["covered by existing compiled rule", "rule-covered class flagged late — retroactive shouldn’t block ship", "frequent across other PRs — file follow-up to compile into a rule", "low/nit severity flagged late in the bot-review loop"]>; /** * Discriminated union on `classification`: `route-out` requires a reason from * the closed catalog; `in-pr-fix` and `undetermined` forbid the field entirely. * Per CR mmnto-ai/totem#1734 round-2 — the prior `routeOutReason: z.string().optional()` * leaked the deterministic contract. */ export declare const RetrospectFindingSchema: z.ZodDiscriminatedUnion<"classification", [z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"route-out">; /** Reason from the closed catalog (`RETROSPECT_ROUTE_OUT_REASONS`). */ routeOutReason: z.ZodEnum<["covered by existing compiled rule", "rule-covered class flagged late — retroactive shouldn’t block ship", "frequent across other PRs — file follow-up to compile into a rule", "low/nit severity flagged late in the bot-review loop"]>; }, "strip", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; }>, z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"in-pr-fix">; }, "strict", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; }>, z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"undetermined">; }, "strict", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; }>]>; export type RetrospectFinding = z.infer; /** Top-level shape emitted by `runRetrospect` and optionally written via `--out`. */ export declare const RetrospectReportSchema: z.ZodObject<{ /** Schema version for forward-compat. */ version: z.ZodLiteral<1>; /** Target PR number (string for parity with recurrence-stats prsScanned). */ prNumber: z.ZodString; /** Open / closed / merged at fetch time. */ prState: z.ZodString; /** ISO 8601 generation timestamp. */ generatedAt: z.ZodString; /** Resolved threshold (after option clamping). */ threshold: z.ZodNumber; /** True iff `.totem/recurrence-stats.json` was loaded successfully. */ substrateAvailable: z.ZodBoolean; /** True iff `compiled-rules.json` was loaded successfully. */ compiledRulesAvailable: z.ZodBoolean; /** Push-grouped rounds (ordered by earliest submittedAt per head_sha). */ rounds: z.ZodArray; /** Number of bot findings tied to this round. */ findingCount: z.ZodNumber; }, "strip", z.ZodTypeAny, { submittedAt: string; roundNumber: number; findingCount: number; headSha?: string | undefined; }, { submittedAt: string; roundNumber: number; findingCount: number; headSha?: string | undefined; }>, "many">; /** Total bot findings (inline + review-body). */ totalFindings: z.ZodNumber; /** `1 - uniqueSignatures/totalFindings`, clamped to [0,1]; 0 when totalFindings == 0. */ dedupRate: z.ZodNumber; /** Distribution counts. */ findingDistribution: z.ZodObject<{ byTool: z.ZodRecord; bySeverity: z.ZodRecord; byClassification: z.ZodRecord; }, "strip", z.ZodTypeAny, { byTool: Record; bySeverity: Record; byClassification: Record; }, { byTool: Record; bySeverity: Record; byClassification: Record; }>; /** Findings classified as `route-out`. */ routeOutCandidates: z.ZodArray; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"route-out">; /** Reason from the closed catalog (`RETROSPECT_ROUTE_OUT_REASONS`). */ routeOutReason: z.ZodEnum<["covered by existing compiled rule", "rule-covered class flagged late — retroactive shouldn’t block ship", "frequent across other PRs — file follow-up to compile into a rule", "low/nit severity flagged late in the bot-review loop"]>; }, "strip", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; }>, z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"in-pr-fix">; }, "strict", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; }>, z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"undetermined">; }, "strict", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; }>]>, "many">; /** Findings classified as `in-pr-fix`. */ inPrFixes: z.ZodArray; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"route-out">; /** Reason from the closed catalog (`RETROSPECT_ROUTE_OUT_REASONS`). */ routeOutReason: z.ZodEnum<["covered by existing compiled rule", "rule-covered class flagged late — retroactive shouldn’t block ship", "frequent across other PRs — file follow-up to compile into a rule", "low/nit severity flagged late in the bot-review loop"]>; }, "strip", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; }>, z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"in-pr-fix">; }, "strict", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; }>, z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"undetermined">; }, "strict", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; }>]>, "many">; /** Findings classified as `undetermined`. */ undetermined: z.ZodArray; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"route-out">; /** Reason from the closed catalog (`RETROSPECT_ROUTE_OUT_REASONS`). */ routeOutReason: z.ZodEnum<["covered by existing compiled rule", "rule-covered class flagged late — retroactive shouldn’t block ship", "frequent across other PRs — file follow-up to compile into a rule", "low/nit severity flagged late in the bot-review loop"]>; }, "strip", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; }>, z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"in-pr-fix">; }, "strict", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; }>, z.ZodObject<{ /** Stable signature hash (16-char) — matches the recurrence-stats vocabulary. */ signature: z.ZodString; /** Source bot/tool. */ tool: z.ZodEnum<["coderabbit", "gca", "greptile", "ghcq", "sarif", "override", "unknown"]>; /** Normalized severity bucket. */ severityBucket: z.ZodEnum<["critical", "high", "medium", "low", "nit"]>; /** First 280 chars of the raw body (for human triage). */ bodyExcerpt: z.ZodString; /** File path attached to the finding (`(review body)` for CR outside-diff/nits). */ file: z.ZodString; /** Best-effort line number. */ line: z.ZodOptional; /** Round number assigned by `groupFindingsByRound`. */ roundNumber: z.ZodNumber; /** Count of OTHER PRs sharing this signature (target PR excluded). */ crossPrRecurrence: z.ZodNumber; /** True if the signature heuristically maps to an existing compiled rule (Jaccard ≥ 0.6). */ coveredByRule: z.ZodBoolean; } & { classification: z.ZodLiteral<"undetermined">; }, "strict", z.ZodTypeAny, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; }, { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; }>]>, "many">; /** Deterministic stop-condition templates (no prose generation). */ stopConditions: z.ZodArray; /** Trap-ledger override events read for this PR (read-only count). */ overrideEventsObserved: z.ZodNumber; }, "strip", z.ZodTypeAny, { version: 1; undetermined: ({ file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; })[]; prNumber: string; prState: string; generatedAt: string; threshold: number; substrateAvailable: boolean; compiledRulesAvailable: boolean; rounds: { submittedAt: string; roundNumber: number; findingCount: number; headSha?: string | undefined; }[]; totalFindings: number; dedupRate: number; findingDistribution: { byTool: Record; bySeverity: Record; byClassification: Record; }; routeOutCandidates: ({ file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; })[]; inPrFixes: ({ file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; })[]; stopConditions: string[]; overrideEventsObserved: number; }, { version: 1; undetermined: ({ file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; })[]; prNumber: string; prState: string; generatedAt: string; threshold: number; substrateAvailable: boolean; compiledRulesAvailable: boolean; rounds: { submittedAt: string; roundNumber: number; findingCount: number; headSha?: string | undefined; }[]; totalFindings: number; dedupRate: number; findingDistribution: { byTool: Record; bySeverity: Record; byClassification: Record; }; routeOutCandidates: ({ file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; })[]; inPrFixes: ({ file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "route-out"; routeOutReason: "covered by existing compiled rule" | "rule-covered class flagged late — retroactive shouldn’t block ship" | "frequent across other PRs — file follow-up to compile into a rule" | "low/nit severity flagged late in the bot-review loop"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "in-pr-fix"; line?: number | undefined; } | { file: string; tool: "unknown" | "override" | "coderabbit" | "gca" | "greptile" | "ghcq" | "sarif"; signature: string; severityBucket: "high" | "critical" | "medium" | "low" | "nit"; coveredByRule: boolean; roundNumber: number; bodyExcerpt: string; crossPrRecurrence: number; classification: "undetermined"; line?: number | undefined; })[]; stopConditions: string[]; overrideEventsObserved: number; }>; export type RetrospectReport = z.infer; /** Minimal review-submission shape (subset of GitHub's `pulls/N/reviews`). */ export interface RetrospectReviewSubmission { id: number; /** PR head SHA at review submission time; may be absent for some review states. */ commit_id?: string | null; /** ISO 8601 timestamp. */ submitted_at?: string | null; /** Bot login. */ user_login: string; } /** Minimal review-comment shape (subset of `StandardReviewComment`). */ export interface RetrospectReviewComment { id: number; author: string; createdAt?: string; } /** * Group bot review submissions by `commit_id` (head SHA). * * - Two reviews on the same `commit_id` → same round. * - Reviews missing `commit_id` are bucketed into a single synthetic * round (head SHA `undefined`) so they don't double-count as N rounds. * - Round ordering is by EARLIEST `submitted_at` within each SHA bucket. * - Round numbers are 1-based. * * `findingCount` requires `groupFindingsByRound` to receive the corresponding * inline-comment list keyed by SHA via `findingShaBySubmissionId`. The mapping * is computed by the caller because `pulls/N/comments` and `pulls/N/reviews` * are separate GH endpoints and the join is push-based, not in-memory native. * * To keep the helper pure, this function returns the round index ONLY. * Callers walk findings + assign each to a round by submission timestamp. */ export declare function groupFindingsByRound(reviewSubmissions: ReadonlyArray, findingsPerRoundCount: ReadonlyMap): RetrospectRound[]; /** * Coarse round-position bucket. The exact thresholds are a v0.1 heuristic * documented in `.totem/specs/1713.md` — early ≤ 3, mid 4-9, late ≥ 10. */ export type RetrospectRoundPosition = 'early' | 'mid' | 'late'; export declare function toRoundPosition(roundNumber: number): RetrospectRoundPosition; /** Coarse cross-PR recurrence bucket. */ export type RetrospectCrossPrBucket = 'none' | 'some' | 'frequent'; export declare function toCrossPrBucket(crossPrRecurrence: number): RetrospectCrossPrBucket; /** * Fixed catalog of route-out reasons. Keep deterministic — every reason * a classifier emits MUST come from this catalog so the report doesn't * accumulate a long-tail of one-off prose strings. */ export declare const RETROSPECT_ROUTE_OUT_REASONS: { readonly COVERED_BY_RULE: "covered by existing compiled rule"; readonly RULE_COVERED_LATE: "rule-covered class flagged late — retroactive shouldn’t block ship"; readonly FREQUENT_CROSS_PR: "frequent across other PRs — file follow-up to compile into a rule"; readonly LOW_NIT_LATE: "low/nit severity flagged late in the bot-review loop"; }; export type RetrospectRouteOutReason = (typeof RETROSPECT_ROUTE_OUT_REASONS)[keyof typeof RETROSPECT_ROUTE_OUT_REASONS]; /** Input to the deterministic classifier. */ export interface ClassifyFindingInput { severityBucket: 'critical' | 'high' | 'medium' | 'low' | 'nit'; roundNumber: number; crossPrRecurrence: number; coveredByRule: boolean; } /** * Deterministic, table-driven classifier. Same inputs → same outputs across * invocations. Reason strings are drawn exclusively from * `RETROSPECT_ROUTE_OUT_REASONS` so consumers can treat them as a closed set. * * Heuristic intent (v0.1): * - `critical` or `high` at any round → `in-pr-fix` (regardless of recurrence/coverage) * - `low`/`nit` late + (`coveredByRule` OR `crossPrRecurrence frequent`) → `route-out` * - `medium` late + `coveredByRule` → `route-out` * - `medium` early/mid → `in-pr-fix` * - `nit` early/mid → `in-pr-fix` * - else → `undetermined` */ export declare function classifyFinding(input: ClassifyFindingInput): { classification: RetrospectClassification; routeOutReason?: RetrospectRouteOutReason; }; /** * Deterministic stop-condition templates keyed off the report shape. No * prose generation; only integer substitution. Returns the catalog * filtered to entries whose triggering condition fires for the given * report. */ export declare function buildStopConditions(report: Pick): string[]; /** * Compute `1 - uniqueSignatures / totalFindings`, clamped to [0, 1]. * * - 0 findings → 0 (no findings, no dedup pressure). * - All unique → 0. * - All duplicates of one signature → close to 1 (`1 - 1/N`). */ export declare function computeDedupRate(findings: ReadonlyArray<{ signature: string; }>): number; /** * Compute a stable signature for a finding body using the * recurrence-stats normalization pipeline. Re-exported here so callers * outside the cli package don't need to call `normalizeFindingBody + * computeSignature` separately. */ export declare function signatureOfBody(body: string): string; //# sourceMappingURL=retrospect.d.ts.map