Documentation
¶
Index ¶
- Constants
- func BuildSynthesisUserMessage(findings []state.DeepFinding, crossCutting []string, projectContext string, ...) string
- func EstimateSynthesisChars(findingCount int) int
- func Export(result *Result, synthesis *SynthesisResult, path string) error
- func ExportJSON(result *Result, synthesis *SynthesisResult, path string) error
- func ExportMarkdown(result *Result, synthesis *SynthesisResult, path string) error
- func IsBinary(content []byte) bool
- func IsTransientUntracked(path string) bool
- func LoadExcludeFile(path string) ([]string, error)
- func LoadSnapshot(repoRoot string) ([]state.DeepFinding, error)
- func NeedsHierarchical(findingCount int) bool
- func RenderCategoryChart(findings []state.DeepFinding) string
- func RenderSeverityBar(findings []state.DeepFinding) string
- func RunPlain(ctx context.Context, reviewClient ai.Client, aoiClient ai.Client, opts Options, ...) (*Result, *SynthesisResult, error)
- func RunWithUI(ctx context.Context, reviewClient ai.Client, aoiClient ai.Client, opts Options, ...) (*Result, *SynthesisResult, error)
- func SaveSnapshot(repoRoot string, findings []state.DeepFinding) error
- func SetHierarchicalSynthConcurrency(n int)
- func ShouldExcludeFromAudit(path string) bool
- func ShouldExcludeFromAuditWithCustom(path string, customPatterns []string) bool
- func ShouldForceInclude(path string, includePatterns []string) bool
- type CollectStats
- type CompareResult
- type ConcurrencyConfig
- type CostEstimate
- type ModelPricing
- type OnProgress
- type Options
- type PhaseUsage
- type ReportJSON
- type Result
- type SynthesisResult
- func ParseSynthesisResult(raw string) (*SynthesisResult, error)
- func Synthesize(ctx context.Context, client ai.Client, findings []state.DeepFinding, ...) (*SynthesisResult, error)
- func SynthesizeCached(ctx context.Context, client ai.Client, findings []state.DeepFinding, ...) (*SynthesisResult, error)
Constants ¶
const MaxAuditFileBytes = 512 * 1024
MaxAuditFileBytes is the per-file size cap for audit ingestion. Files larger than this are skipped with a warning rather than loaded into memory and shipped to the classifier/AOI scanner — a single 10MB JSON config can balloon prompt sizes and offers no real audit value.
512KB comfortably accommodates hand-written code (the largest .go files in stdlib are ~300KB) while still catching generated artifacts and accidentally-committed data dumps. Audit.Options.MaxFileBytes overrides this at the call site.
Variables ¶
This section is empty.
Functions ¶
func BuildSynthesisUserMessage ¶
func BuildSynthesisUserMessage(findings []state.DeepFinding, crossCutting []string, projectContext string, failedAOICount int) string
BuildSynthesisUserMessage formats findings and context into the user message sent to the LLM for synthesis.
failedAOICount > 0 appends a "## Audit Recall Gap" section asking the model to mention the gap in the executive summary. Synthesis otherwise can produce a confident-sounding summary on top of degraded inputs with no way for the reader to know.
func EstimateSynthesisChars ¶
EstimateSynthesisChars returns the expected total output character count for synthesis given the finding count. Used to drive a truthful streaming progress bar.
The estimate is bounded:
- 3000 chars minimum (clean audit: executive_summary + 1-2 risks)
- 10000 chars maximum (lots of findings hit the prompt's per-item word caps, so output stops growing past a point)
100 chars per finding is the slope: each finding contributes ~1 short risk/pattern/recommendation entry on average (≤100 words ≈ 400 chars per entry, but only a fraction of findings generate one).
The estimate intentionally errs HIGH so the streaming bar fills slower than reality — better to see a bar that holds at 95% for a moment than to claim 100% before the response actually ends.
func Export ¶
func Export(result *Result, synthesis *SynthesisResult, path string) error
Export auto-detects format from file extension (.json or .md/.markdown). synthesis may be nil; the report is still written without the executive summary section in that case.
func ExportJSON ¶
func ExportJSON(result *Result, synthesis *SynthesisResult, path string) error
ExportJSON writes the audit result as structured JSON to the given path. synthesis may be nil when synthesis was disabled or failed.
func ExportMarkdown ¶
func ExportMarkdown(result *Result, synthesis *SynthesisResult, path string) error
ExportMarkdown writes a styled markdown report to the given path. synthesis may be nil when synthesis was disabled or failed.
func IsBinary ¶
IsBinary returns true if content appears to be a binary file. The heuristic: if any of the first binarySniffLimit bytes is NUL (0x00), treat as binary. Valid text encodings (UTF-8, ASCII, ISO-8859-*) never produce 0x00 in real content; binaries almost always have NUL bytes in headers, length-prefixed sections, or padding.
Known false positives: UTF-16/UTF-32 source files (vanishingly rare for code). Users can force-include via --include if needed.
func IsTransientUntracked ¶
IsTransientUntracked reports whether path looks like a locally generated artifact that should probably be in .gitignore. Only meaningful for files that came from `git ls-files --others`.
func LoadExcludeFile ¶
LoadExcludeFile reads a glob-per-line exclude file (like .prr/audit-exclude). Blank lines and lines starting with # are skipped.
func LoadSnapshot ¶
func LoadSnapshot(repoRoot string) ([]state.DeepFinding, error)
LoadSnapshot loads the previous audit's findings snapshot. Returns nil slice (not error) if no previous snapshot exists.
func NeedsHierarchical ¶
NeedsHierarchical reports whether the given finding count exceeds the hierarchical synthesis threshold.
func RenderCategoryChart ¶
func RenderCategoryChart(findings []state.DeepFinding) string
RenderCategoryChart returns a horizontal bar chart of findings by category.
func RenderSeverityBar ¶
func RenderSeverityBar(findings []state.DeepFinding) string
RenderSeverityBar returns a single proportional bar showing severity distribution.
func RunPlain ¶
func RunPlain( ctx context.Context, reviewClient ai.Client, aoiClient ai.Client, opts Options, reviewModel, aoiModel string, noSynthesis bool, ) (*Result, *SynthesisResult, error)
RunPlain executes the audit without the Bubble Tea UI — prints progress to stderr. Used in --debug mode so debug output isn't clobbered by terminal animations.
func RunWithUI ¶
func RunWithUI( ctx context.Context, reviewClient ai.Client, aoiClient ai.Client, opts Options, reviewModel, aoiModel string, noSynthesis bool, ) (*Result, *SynthesisResult, error)
RunWithUI executes the audit with the shared progress TUI. Returns the audit result and synthesis after completion.
func SaveSnapshot ¶
func SaveSnapshot(repoRoot string, findings []state.DeepFinding) error
SaveSnapshot persists current findings as the "last audit" baseline. Stored in .git/pr-tui/audit-snapshot.json
func SetHierarchicalSynthConcurrency ¶
func SetHierarchicalSynthConcurrency(n int)
SetHierarchicalSynthConcurrency sets the max number of per-category synthesis calls run in parallel. Values <= 0 reset to the default. Not safe to call concurrently with synthesis in flight.
func ShouldExcludeFromAudit ¶
ShouldExcludeFromAudit returns true if the given file path should be excluded from audit mode review. Checks both the standard review exclusions and the audit-specific exclusions.
func ShouldExcludeFromAuditWithCustom ¶
ShouldExcludeFromAuditWithCustom checks against standard, audit, and user-provided custom exclusion patterns.
func ShouldForceInclude ¶
ShouldForceInclude returns true if the path matches any --include pattern. Force-included files override exclusion filters.
Types ¶
type CollectStats ¶
type CollectStats struct {
// TotalListed is the count of paths returned by `git ls-files`
// (tracked + untracked-non-ignored) before any filter ran.
TotalListed int
// Included is the count of paths that survived all filters.
// Equal to len(files) returned by CollectFiles.
Included int
// Tracked / Untracked break Included down by git origin.
Tracked int
Untracked int
// Exclusion counts, ordered by the precedence applied in
// partitionFiles. Each path increments exactly one of these (or
// makes it into Included).
ExcludedReview int // standard review exclusions (lock/vendor/generated/etc.)
ExcludedAudit int // audit-specific exclusions (docs/build/IDE/etc.)
ExcludedCustom int // user-provided --exclude patterns
// ForceIncluded counts paths that matched --include and bypassed
// exclusion. Subset of Included.
ForceIncluded int
// UntrackedTransients lists untracked files that survived
// filtering AND look like locally generated artifacts (logs,
// debug dumps, state snapshots). The caller surfaces a warning
// suggesting .gitignore updates — we do NOT auto-exclude because
// a legitimately-named file could match these patterns.
UntrackedTransients []string
}
CollectStats summarizes what CollectFiles saw and dropped. Exposed so callers can surface drop reasons in --debug and warn the user about untracked files that look like local tooling output.
func CollectFiles ¶
func CollectFiles(repoRoot string, excludePatterns, includePatterns []string) ([]string, CollectStats, error)
CollectFiles returns the list of files to audit after applying all filters, plus a CollectStats breakdown of what was dropped and why. Runs two `git ls-files` queries so we can distinguish tracked from untracked origin (needed for the transient-file warning).
type CompareResult ¶
type CompareResult struct {
New []state.DeepFinding // findings in current but not previous
Resolved []state.DeepFinding // findings in previous but not current
Persistent []state.DeepFinding // findings in both (may have changed severity etc)
}
CompareResult holds the diff between two audit runs.
func CompareFindings ¶
func CompareFindings(current, previous []state.DeepFinding) CompareResult
CompareFindings compares current findings against a previous set. Matching is done by (File, Category, Subcategory, Title) tuple — line numbers may shift between runs.
func (CompareResult) FormatComparison ¶
func (c CompareResult) FormatComparison() string
FormatComparison returns a human-readable summary like: "3 new findings, 2 resolved, 10 persistent"
type ConcurrencyConfig ¶
type ConcurrencyConfig struct {
// Classify caps Phase 1b classification calls.
Classify int
// AOIScan caps Phase 2 AOI batch calls.
AOIScan int
// DeepReview caps Phase 3 review calls.
DeepReview int
// Recheck caps Phase 3b recheck batch calls.
Recheck int
// HierarchicalSynth caps Phase 4 per-category synthesis calls.
HierarchicalSynth int
}
ConcurrencyConfig holds per-phase concurrency caps. Each field is the maximum number of in-flight LLM calls for that phase. Zero = use the package default.
type CostEstimate ¶
type CostEstimate struct {
TotalCalls int
IndividualCalls int
GroupedCalls int
EstInputTokens int // rough estimate of total input tokens
EstOutputTokens int // rough estimate of total output tokens
EstCostUSD float64
SkippedCalls int // calls that would be skipped by --max-reviews
}
CostEstimate holds pre-execution cost projections for Phase 3.
func EstimateCost ¶
func EstimateCost(routing *review.RouteResult, maxReviews int, pricing ModelPricing) CostEstimate
EstimateCost projects Phase 3 costs from routing results.
func (CostEstimate) FormatEstimate ¶
func (e CostEstimate) FormatEstimate() string
FormatEstimate returns a human-readable cost summary.
type ModelPricing ¶
ModelPricing holds per-1M-token costs.
func DefaultPricing ¶
func DefaultPricing(modelName string) ModelPricing
DefaultPricing returns pricing for a model, looking up from the known models registry.
type OnProgress ¶
OnProgress is called with status updates during the audit.
type Options ¶
type Options struct {
// RepoRoot is the absolute path to the repository root.
RepoRoot string
// Focus is a list of dimension slugs to focus on. Empty = all.
Focus []string
// ExcludePatterns are additional globs to exclude (from --exclude and .prr/audit-exclude).
ExcludePatterns []string
// IncludePatterns are globs to force-include (from --include).
IncludePatterns []string
// MaxReviews caps the number of Phase 3 review calls. 0 = no limit.
MaxReviews int
// NoCache disables incremental caching — re-audit everything.
NoCache bool
// AOIContextLines is the number of context lines for AOI generation.
AOIContextLines int
// Debug enables verbose output of all LLM prompts and responses.
Debug bool
// DebugFile restricts the audit to a single file (path relative to repo root).
DebugFile string
// LargeFileThreshold is the file count above which a warning is
// displayed. Defaults to 200 when zero.
LargeFileThreshold int
// MaxFileBytes caps per-file size during Phase 1 ingestion. Files
// exceeding the cap are skipped with a warning (not loaded into
// prompts). Zero falls back to MaxAuditFileBytes.
MaxFileBytes int
// Concurrency tunes per-phase concurrency caps. Each zero field falls
// back to the package default (currently 5).
Concurrency ConcurrencyConfig
}
Options configures an audit run.
type PhaseUsage ¶
type PhaseUsage struct {
AOI ai.TokenUsage // Phase 2: AOI pre-scan
Review ai.TokenUsage // Phase 3: deep review
Recheck ai.TokenUsage // Phase 3b: recheck/dedup
Synth ai.TokenUsage // Phase 4: synthesis
}
PhaseUsage holds per-phase token usage for cost reporting.
func (PhaseUsage) Total ¶
func (u PhaseUsage) Total() ai.TokenUsage
Total returns aggregate token usage across all phases.
type ReportJSON ¶
type ReportJSON struct {
FilesScanned int `json:"files_scanned"`
AOIsGenerated int `json:"aois_generated"`
ReviewCalls int `json:"review_calls"`
IndividualReviews int `json:"individual_reviews"`
GroupedReviews int `json:"grouped_reviews"`
FailedReviews int `json:"failed_reviews,omitempty"`
Findings []state.DeepFinding `json:"findings"`
Dismissals int `json:"dismissals"`
CrossCuttingObservations []string `json:"cross_cutting_observations,omitempty"`
SkippedSubcategories []string `json:"skipped_subcategories,omitempty"`
Synthesis *SynthesisResult `json:"synthesis,omitempty"`
}
ReportJSON is the JSON-serializable form of an audit result.
type Result ¶
type Result struct {
// FilesScanned is the number of files that passed Phase 1 filtering.
FilesScanned int
// AOIsGenerated is the total number of AOIs from Phase 2.
AOIsGenerated int
// ReviewCalls is the number of Phase 3 LLM calls made.
ReviewCalls int
// IndividualReviews is the count of individual review calls.
IndividualReviews int
// GroupedReviews is the count of grouped review calls.
GroupedReviews int
// FailedReviews is the count of Phase 3 review calls that errored and
// produced no findings. The user should know about these — silently
// dropping them means the audit's recall is lower than it appears.
FailedReviews int
// FailedAOIIDs is the list of AOI IDs whose Phase 3 review call
// failed (and therefore produced no finding or dismissal). Carries
// the per-call FailedAOIIDs from review.ExecuteResult so synthesis
// can mention recall gaps in the executive summary and the reporter
// can name exactly which areas of the codebase didn't get reviewed.
FailedAOIIDs []string
// Findings is all confirmed findings from Phase 3.
Findings []state.DeepFinding
// Dismissals is all dismissed AOIs from Phase 3.
Dismissals int
// CrossCuttingObservations from grouped reviews.
CrossCuttingObservations []string
// SkippedSubcategories lists subcategories skipped due to --max-reviews.
SkippedSubcategories []string
// Routing is the Phase 3 routing result (for summary display).
Routing *review.RouteResult
// ProjectContext is the Phase 0 project briefing. Captured here so the
// caller can plumb it into Phase 4 synthesis (otherwise the work done in
// Phase 0 would not reach the executive summary).
ProjectContext string
// TokenUsage tracks actual token consumption per phase.
Usage PhaseUsage
}
Result holds the output of an audit run.
type SynthesisResult ¶
type SynthesisResult struct {
// ExecutiveSummary is a 2-3 paragraph overview of audit findings.
ExecutiveSummary string `json:"executive_summary"`
// TopRisks are the most critical issues identified, ranked.
TopRisks []string `json:"top_risks"`
// SystemicPatterns are recurring issues across the codebase.
SystemicPatterns []string `json:"systemic_patterns"`
// Recommendations are prioritized action items.
Recommendations []string `json:"recommendations"`
// RawOutput is the full LLM response.
RawOutput string `json:"-"`
}
SynthesisResult holds the output of Phase 4 synthesis.
func ParseSynthesisResult ¶
func ParseSynthesisResult(raw string) (*SynthesisResult, error)
ParseSynthesisResult extracts a SynthesisResult from the LLM's raw response. Uses ai.ExtractJSON so trailing prose, embedded fences, and multi-round agent output don't break parsing.
Returns errSynthesisParse-wrapped errors on parse-shape failures so callers (and the retry wrapper) can distinguish "model output was malformed" from "transport error" — the former won't be fixed by retrying the same prompt.
func Synthesize ¶
func Synthesize( ctx context.Context, client ai.Client, findings []state.DeepFinding, crossCutting []string, projectContext string, failedAOICount int, onToken func(string), ) (*SynthesisResult, error)
Synthesize runs Phase 4: takes all findings and produces an executive summary. onToken is called for streaming output (can be nil).
failedAOICount is the number of AOIs whose Phase 3 review failed (so they have no finding/dismissal verdict). When > 0, synthesis is told to mention the recall gap in the executive summary — otherwise the user reads a confident summary on top of degraded inputs and has no way to know.
func SynthesizeCached ¶
func SynthesizeCached( ctx context.Context, client ai.Client, findings []state.DeepFinding, crossCutting []string, projectContext string, failedAOICount int, onToken func(string), noCache bool, ) (*SynthesisResult, error)
SynthesizeCached wraps Synthesize with persistent cache lookup against the audit state. Cache misses run synthesis and persist the result. Cache hits are returned without an LLM call. Pass noCache=true to bypass.
The audit state is loaded and (if updated) saved by this function — callers don't need to manage it.