Documentation
¶
Index ¶
- func AuditsDir() (string, error)
- func DeleteStateFile(prNumber string) error
- func ReviewsDir() (string, error)
- func Save(state *State) error
- func SaveAuditSnapshot(data []byte) (string, error)
- func SaveReviewSnapshot(prNumber string, data []byte) (string, error)
- func SetCategoryValidator(f func(string) bool)
- type AIReview
- type Boundary
- type Category
- type CorruptStateError
- type DeepDismissal
- type DeepFinding
- type DeepReviewResult
- type DismissedRecord
- type FileCoverage
- type FileState
- type FindingRevalidation
- type FindingTrigger
- type Message
- type ReviewCoverage
- type ReviewFinding
- type ReviewMeta
- type ReviewOutput
- type ReviewSnapshot
- type ReviewStatus
- type RuntimeEntryPoint
- type RuntimeModel
- type SiblingDeviation
- type SiteRef
- type State
- func (s *State) AppendDeepFindings(findings []DeepFinding)
- func (s *State) ClearAllCaches()
- func (s *State) ClearDeepFindings()
- func (s *State) ClearDeepReviews()
- func (s *State) ClearPRBrief()
- func (s *State) CollectCachedFindings(paths []string) (combined string, fileFindings map[string]string)
- func (s *State) CountCachedBatchFindings(paths []string) int
- func (s *State) DiffSnapshotFromFiles() map[string]string
- func (s *State) GetAOIResults(path string) (json.RawMessage, int)
- func (s *State) GetBatchFindings(path string) (purpose, findings string)
- func (s *State) GetBoundaryInventory() (boundaries []Boundary, inputHash string)
- func (s *State) GetDeepFindings() []DeepFinding
- func (s *State) GetDeepReview(key string) *DeepReviewResult
- func (s *State) GetFileType(path string) string
- func (s *State) GetPRBrief() (brief, inputHash string)
- func (s *State) GetProjectContext() (summary, inputHash string)
- func (s *State) GetRecheckCache(key string) json.RawMessage
- func (s *State) GetRecheckDismissals() []DismissedRecord
- func (s *State) GetRuntimeModel() (model *RuntimeModel, inputHash string)
- func (s *State) GetSiblingClusterCache() (outliers json.RawMessage, inputHash string)
- func (s *State) GetSynthesisCache(key string) json.RawMessage
- func (s *State) HasCachedBatch(paths []string) bool
- func (s *State) HasFile(path string) bool
- func (s *State) HasReviewArtifact() bool
- func (s *State) IsReviewStale() bool
- func (s *State) SetAOIResults(path string, data json.RawMessage, contextLines int, promptHash string)
- func (s *State) SetBatchFindings(path, purpose, findings string)
- func (s *State) SetBoundaryInventory(boundaries []Boundary, inputHash string)
- func (s *State) SetDeepFindings(findings []DeepFinding)
- func (s *State) SetDeepReview(key string, result *DeepReviewResult)
- func (s *State) SetFileType(path, fileType string)
- func (s *State) SetLastReview(meta *ReviewMeta)
- func (s *State) SetPRBrief(brief, inputHash string)
- func (s *State) SetProjectContext(summary, inputHash string)
- func (s *State) SetRecheckCache(key string, raw json.RawMessage)
- func (s *State) SetRecheckDismissals(dismissals []DismissedRecord)
- func (s *State) SetRuntimeModel(model *RuntimeModel, inputHash string)
- func (s *State) SetSiblingClusterCache(outliers json.RawMessage, inputHash string)
- func (s *State) SetSynthesisCache(key string, raw json.RawMessage)
- func (s *State) SyncWithDiffs(currentDiffHashes map[string]string, prFiles map[string]bool)
- type TraceHop
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AuditsDir ¶ added in v1.8.0
AuditsDir returns the absolute path to the audits snapshot directory and ensures the directory exists.
func DeleteStateFile ¶ added in v1.6.0
DeleteStateFile removes the state file for the given PR. Idempotent: no error when the file is already gone. Used by the TUI's "delete corrupt state" confirmation flow.
func ReviewsDir ¶ added in v1.8.0
ReviewsDir returns the absolute path to the reviews snapshot directory and ensures the directory exists. Used by callers that want to enumerate or clean past snapshots without writing a new one.
func SaveAuditSnapshot ¶ added in v1.8.0
SaveAuditSnapshot writes data to .git/pr-tui/audits/audit-<timestamp>.json. Returns the absolute path on success.
func SaveReviewSnapshot ¶ added in v1.8.0
SaveReviewSnapshot writes data (already-marshalled JSON bytes from the caller — state owns the path layout, not the schema) to .git/pr-tui/reviews/pr-<N>-review-<timestamp>.json. Returns the absolute path on success.
Caller chooses the bytes. This is intentional: state.SaveReviewSnapshot knows where files live; the review package knows what shape they take.
prNumber is validated against the same charset Save() uses for state keys (no path separators, no shell metacharacters).
func SetCategoryValidator ¶ added in v1.10.0
SetCategoryValidator registers the slug-validity check. Package ai's init wires its CategoryExists in. Must not be called concurrently with parsing.
Types ¶
type AIReview ¶
type AIReview struct {
Summary string `json:"summary"` // rendered final review text (legacy: free-form markdown)
Findings string `json:"findings,omitempty"` // per-batch raw findings (PR-level only)
// Structured review output — populated by Phase 5+ review flows.
// When present, the TUI renders this instead of the legacy Summary field.
Structured *ReviewOutput `json:"structured,omitempty"`
// DiffSnapshot records the DiffHash of each file at the time the review
// was generated. Used to detect staleness when diffs change after a review.
DiffSnapshot map[string]string `json:"diff_snapshot,omitempty"`
// SecurityDigest is the AOI pre-scan summary injected into review prompts.
// Persisted so the TUI can display it even after the review is cached.
SecurityDigest string `json:"security_digest,omitempty"`
// DeepFindings from AOI-driven review calls (structured findings with severity, category, etc.)
DeepFindings []DeepFinding `json:"deep_findings,omitempty"`
}
AIReview stores the result of an AI review for a file or the overall PR.
type Boundary ¶ added in v1.6.0
type Boundary struct {
// Kind matches RuntimeEntryPoint.Kind. One of: "http", "queue",
// "scheduled", "cli", "rpc", "webhook", "other".
Kind string `json:"kind"`
// File is the path holding the boundary declaration (e.g. the
// route file, queue subscription, scheduled job).
File string `json:"file"`
// Lines is the optional line range hint within File. Best-effort
// from the LLM's read of the file header.
Lines string `json:"lines,omitempty"`
// Symbol is the boundary's identifier (route name, handler
// function, subscription topic) when one is identifiable. Used
// to anchor the synthesized AOIs to specific code.
Symbol string `json:"symbol,omitempty"`
// Description is a one-line free-form explanation: "POST
// /admin/users — admin creation handler", "SNS subscription to
// payment-events topic", "scheduled daily reconciliation".
Description string `json:"description"`
}
Boundary is one concrete externally-reachable surface located in a specific file. It is the persisted output of Phase 1.5 (boundary discovery). Each boundary seeds 1-3 defense-coverage AOIs for Phase 3 so the audit can guarantee at least one review pass at every boundary regardless of what the AOI scanner caught on its own.
Boundary differs from RuntimeEntryPoint: RuntimeEntryPoint describes the codebase's *classes* of entry points abstractly ("HTTP routes use schema validation at the boundary"); Boundary names a specific surface at a specific path so review can be targeted.
type Category ¶ added in v1.10.0
type Category string
Category is a category slug validated at the JSON boundary against the set registered by SetCategoryValidator. The empty Category is legal — it represents "not yet assigned" in pipeline plumbing.
func MustParseCategory ¶ added in v1.10.0
MustParseCategory panics on invalid input. Use for compile-time constants and tests only.
func ParseCategory ¶ added in v1.10.0
ParseCategory promotes a raw string to a Category, lowercasing and trimming first. Empty input returns the zero Category and no error. Non-empty slugs rejected by the registered validator return an error.
func (Category) MarshalJSON ¶ added in v1.10.0
func (*Category) UnmarshalJSON ¶ added in v1.10.0
UnmarshalJSON is the validation boundary: an unknown slug surfaces as a parse error on the enclosing struct.
type CorruptStateError ¶ added in v1.6.0
CorruptStateError wraps a state-file parse failure with the path of the offending file so the TUI can offer to delete it and start fresh. Wrapping (rather than just %w) lets callers identify the corruption case via errors.As without parsing error message strings.
func (*CorruptStateError) Error ¶ added in v1.6.0
func (e *CorruptStateError) Error() string
func (*CorruptStateError) Unwrap ¶ added in v1.6.0
func (e *CorruptStateError) Unwrap() error
type DeepDismissal ¶ added in v1.4.0
type DeepDismissal struct {
AOIID string `json:"aoi_id"`
File string `json:"file,omitempty"`
Evidence string `json:"evidence,omitempty"`
Rationale string `json:"rationale"`
ConfidenceScore int `json:"confidence_score,omitempty"`
ConfidenceReasoning string `json:"confidence_reasoning,omitempty"`
}
DeepDismissal is a dismissed AOI from Phase 3 review. Carries enough metadata to surface per-file coverage downstream: which file the dismissed AOI was on, how confident the reviewer is that it isn't a bug, and a one-line reasoning. ConfidenceScore is optional (0 means "unknown"); older cached state may not have it.
type DeepFinding ¶ added in v1.4.0
type DeepFinding struct {
FindingID string `json:"finding_id,omitempty"` // assigned before recheck (e.g. "F-001")
AOIID string `json:"aoi_id"`
File string `json:"file"`
Lines string `json:"lines"`
Severity string `json:"severity"` // "critical", "high", "medium", "low", "nit"
Category Category `json:"category"`
Subcategory string `json:"subcategory,omitempty"`
Title string `json:"title"`
Description string `json:"description"`
Evidence string `json:"evidence,omitempty"` // what was verified and found (tool-backed)
// EvidenceSnippet is 1-3 verbatim lines from File at Lines that
// prove the finding. Phase 3's prompt requires it, and the
// in-loop verifier matches this against the file before the
// finding is accepted — anchoring every claim to text that
// actually exists at the cited location. Findings whose snippet
// doesn't match get one refinement round trip and, if still
// unmatched, are dropped.
EvidenceSnippet string `json:"evidence_snippet,omitempty"`
Trigger FindingTrigger `json:"trigger"`
Suggestion string `json:"suggestion,omitempty"`
// ConfidenceScore (0-100) is the model's certainty that the
// finding is real, independent of severity. Downstream validators
// (commits 4 + 5 in the audit-quality plan) subtract from this
// score when required evidence is missing without touching
// severity. 0 means unknown (legacy data).
ConfidenceScore int `json:"confidence_score,omitempty"`
// ConfidenceReasoning is a short justification. Validators append
// concrete tags (e.g., "missing-trace", "defenses-not-checked")
// so the reviewer can see why the score moved.
ConfidenceReasoning string `json:"confidence_reasoning,omitempty"`
// Trace is the end-to-end path the reviewer walked from the
// suspect line to the next system boundary. The Phase 3 prompt
// asks for at least three hops for findings at severity
// critical/high so a snippet-in-isolation flag can't survive at
// severe severity without the reviewer showing their work.
// Findings at lower severity (medium/low/nit) don't require a
// trace.
Trace []TraceHop `json:"trace,omitempty"`
// DefensesChecked lists the canonical defense layers the
// reviewer inspected before flagging this finding. Each entry
// is a short tag from a fixed vocabulary the Phase 3 prompts
// enumerate (boundary-authz, handler-guard, conditional-write,
// idempotency-key, schema-validation, framework-escape,
// result-discipline, native-limit) or a free-form
// `other:<tag>` for cases outside the list.
//
// For findings whose category is in the "required" set
// (authorization, concurrency, input-validation, external-io),
// an empty list triggers a confidence penalty in
// ApplyConfidencePenalties — the reviewer didn't show which
// defenses they ruled out, so we can't trust the finding as
// fully as one that does. Other categories leave this
// optional.
DefensesChecked []string `json:"defenses_checked,omitempty"`
// SiblingDeviation is carried over from the AOI when the
// finding came from a Phase 2.5 outlier — the AOI scanner
// noticed N siblings all follow a pattern and this one doesn't.
// Set when the AOI's SiblingDeviation was non-nil and the
// reviewer confirmed the deviation as a real finding (rather
// than dismissing it as intentional). Carried so the audit
// report can render "9 of 11 admin POSTs call guardAdmin —
// this one doesn't" under the finding.
SiblingDeviation *SiblingDeviation `json:"sibling_deviation,omitempty"`
// AffectedSites lists the specific call sites a Systemic /
// consolidated finding covers. Required (3+ distinct files) for
// any finding whose Title starts with "Systemic:" — see
// ApplySystemicGate. Optional and typically empty for per-file
// findings; the consolidator populates this when merging
// findings across files so the audit report can render each
// site explicitly rather than burying them in description text.
AffectedSites []SiteRef `json:"affected_sites,omitempty"`
// Systemic is set by the recheck parser when a finding came out
// of the `consolidated` bucket — i.e. it represents a cross-file
// pattern merged from multiple per-file findings, not an
// individual issue. The two-pass recheck pipeline uses this to
// route systemic findings around the per-file dismiss pass
// (their context is the multi-file aggregation, not any single
// file). Was previously inferred via an `isSystemic` heuristic
// (File=="multiple" / Title prefix); the flag is authoritative
// because the producer sets it explicitly.
Systemic bool `json:"systemic,omitempty"`
}
DeepFinding is a confirmed issue from Phase 3 review.
type DeepReviewResult ¶ added in v1.4.0
type DeepReviewResult struct {
// Type is "individual" or "grouped".
Type string `json:"type"`
// CacheKey is the hash used to look up this result.
CacheKey string `json:"cache_key"`
// Category and Subcategory identify the concern area.
Category Category `json:"category"`
Subcategory string `json:"subcategory,omitempty"`
// RawOutput is the LLM's JSON response (unparsed for flexibility).
RawOutput json.RawMessage `json:"raw_output"`
// Findings extracted from the LLM output.
Findings []DeepFinding `json:"findings,omitempty"`
// Dismissals extracted from the LLM output.
Dismissals []DeepDismissal `json:"dismissals,omitempty"`
// CrossCutting observation (grouped reviews only).
CrossCutting string `json:"cross_cutting,omitempty"`
}
DeepReviewResult stores the cached output of a Phase 3 review call.
type DismissedRecord ¶ added in v1.5.0
type DismissedRecord struct {
FindingID string `json:"finding_id"`
Finding DeepFinding `json:"finding"`
Rationale string `json:"rationale"`
}
DismissedRecord is one finding that recheck removed, with the rationale the model gave for removing it. The original finding is kept so the report can render it inline next to the rationale — users need to see what got dismissed, not just that something did.
type FileCoverage ¶ added in v1.6.1
type FileCoverage struct {
File string `json:"file"`
AOIsScanned int `json:"aois_scanned"`
Findings int `json:"findings"`
Dismissals int `json:"dismissals"`
Failed int `json:"failed,omitempty"`
AvgDismissConf int `json:"avg_dismiss_confidence,omitempty"`
MaxFindingSeverity string `json:"max_finding_severity,omitempty"`
}
FileCoverage records what happened to one file's AOIs in Phase 3. Surfacing dismissed-with-confidence as well as findings lets downstream consumers tell "reviewed clean" from "never looked".
type FileState ¶
type FileState struct {
Status ReviewStatus `json:"status"`
DiffHash string `json:"diff_hash"`
Chat []Message `json:"chat,omitempty"`
Purpose string `json:"purpose,omitempty"` // AI-generated description of what the file does
BatchFindings string `json:"batch_findings,omitempty"` // cached findings from PR-level batch review
AOIResults json.RawMessage `json:"aoi_results,omitempty"` // cached AOI scan result (AOIScanResult JSON)
// AOIPromptHash is sha256 of the AOI scanner prompt at the time
// AOIResults was written. The audit pipeline includes this in the
// cache-hit check so prompt changes (e.g. commit 7's TODO/FIXME
// + unit-type rules) auto-invalidate stale entries rather than
// silently serving results produced by the previous prompt.
AOIPromptHash string `json:"aoi_prompt_hash,omitempty"`
AOIContextLines int `json:"aoi_context_lines,omitempty"` // context lines used when AOI was generated
FileType string `json:"file_type,omitempty"` // cached file classification (e.g. "handler", "test")
}
FileState holds the review status and chat history for a specific file
type FindingRevalidation ¶ added in v1.3.0
type FindingRevalidation struct {
Verdict string `json:"verdict"` // "true-positive", "false-positive", "fixed", "uncertain"
Reasoning string `json:"reasoning"`
Confidence string `json:"confidence"` // "high", "medium", "low"
}
FindingRevalidation holds the result of a security revalidation pass.
type FindingTrigger ¶ added in v1.6.0
type FindingTrigger struct {
Repro string `json:"repro,omitempty"`
Observable string `json:"observable,omitempty"`
}
FindingTrigger is the concrete scenario that exercises a finding. Repro is the input or request to send (e.g., a curl command, a payload, an API call). Observable is what the caller sees when the bug fires (status code, returned value, side effect). Both are short strings — the prompt asks for the smallest concrete thing that distinguishes "real bug" from "theoretical concern".
func (FindingTrigger) IsZero ¶ added in v1.6.0
func (t FindingTrigger) IsZero() bool
IsZero reports whether the trigger carries no information.
func (*FindingTrigger) UnmarshalJSON ¶ added in v1.6.0
func (t *FindingTrigger) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts either the structured object form or a legacy string (which becomes Repro with Observable empty). Older cached state and the previous prompt schema both produced strings; the new prompt schema produces objects.
type ReviewCoverage ¶ added in v1.6.1
type ReviewCoverage struct {
Files []FileCoverage `json:"files"`
FilesInScope int `json:"files_in_scope"`
FilesWithAOIs int `json:"files_with_aois"`
FilesReviewed int `json:"files_reviewed"`
// OrphanFiles are in-scope files that produced zero AOIs. The
// most actionable signal in the coverage view: differentiates
// "AOI scanner skipped this" from "AOIs all came back clean".
OrphanFiles []string `json:"orphan_files,omitempty"`
// SkippedFiles are in-scope files that the pipeline did not send
// to any review call. Populated when the user opts into
// --review-mode=aoi-only and one or more files had no AOIs.
// Different from OrphanFiles (which means "AOIs not generated
// for this file"): SkippedFiles is the user-chosen consequence
// of the review-mode flag, not a quirk of the scanner.
SkippedFiles []string `json:"skipped_files,omitempty"`
}
ReviewCoverage is the per-run coverage view across all files in the PR / audit scope. Sums and lists are computed once at the end of the pipeline; values never change after stamping.
type ReviewFinding ¶
type ReviewFinding struct {
Severity string `json:"severity"` // "critical", "high", "medium", "low", "nit"
// Confidence is the legacy string band ("high"|"medium"|"low") kept
// for backward compatibility with cached state and older prompts.
// New code should read ConfidenceScore; render via ConfidenceBand().
Confidence string `json:"confidence,omitempty"`
// ConfidenceScore is the 0-100 certainty that the finding is real,
// independent of its severity. Severity = "how bad if real";
// ConfidenceScore = "how sure I am it's real". 0 means unknown
// (e.g., a finding loaded from legacy state that only had the
// string band).
ConfidenceScore int `json:"confidence_score,omitempty"`
// ConfidenceReasoning is a short one-line justification for the
// score. Concrete signals like "missing-trace" or
// "defenses-not-checked" are appended by downstream validators
// (see commits 4 + 5 in the audit-quality plan).
ConfidenceReasoning string `json:"confidence_reasoning,omitempty"`
Category Category `json:"category"`
File string `json:"file"`
Line int `json:"line"`
Title string `json:"title"`
Detail string `json:"detail"`
Suggestion string `json:"suggestion,omitempty"`
CWE string `json:"cwe,omitempty"` // e.g. "CWE-89" — populated for security findings
Resolved bool `json:"resolved,omitempty"` // user-toggled or auto-resolved by task completion
// SourceIDs lists the deep-finding IDs (e.g. "F-001", "F-007") this
// synthesis finding derives from. One synthesis finding can cite
// multiple deep findings when synthesis consolidates a systemic
// pattern across files. Consumers should dereference these against
// the top-level `deep_findings` list for evidence / trigger / per-
// site file:line ranges — synthesis findings keep only a single
// representative file/line and a short narrative.
//
// Empty/omitted when synthesis ran without deep findings as input
// (single-pass review path).
SourceIDs []string `json:"source_ids,omitempty"`
// Revalidation — populated by the security revalidation pass (Phase 4).
Revalidation *FindingRevalidation `json:"revalidation,omitempty"`
}
ReviewFinding is a single finding from the structured review.
func (ReviewFinding) ConfidenceBand ¶ added in v1.6.0
func (f ReviewFinding) ConfidenceBand() string
ConfidenceBand returns a coarse band derived from ConfidenceScore for UIs that still want a "high"|"medium"|"low" label.
Bands: >=80 high, 50-79 medium, <50 low.
Legacy-fallback rule: only when ConfidenceScore is zero AND the legacy Confidence string is populated do we treat that as "this finding came from old cached state, use its string band as-is." A score of zero with an empty legacy field means the score was either never set OR was penalized to zero by ApplyConfidencePenalties — both render as "low" so the UI doesn't blank out the band.
func (ReviewFinding) SeverityRank ¶
func (f ReviewFinding) SeverityRank() int
SeverityRank returns a numeric rank for sorting findings by severity (lower = more severe).
type ReviewMeta ¶ added in v1.6.0
type ReviewMeta struct {
// CompletedAt is the wall-clock time the run finished.
CompletedAt time.Time `json:"completed_at"`
// Verdict is the structured-review verdict (or a synthesised
// "approve"/"comment" when synthesis was skipped).
// Empty for failure runs (use Error to disambiguate).
Verdict string `json:"verdict,omitempty"`
// Summary is a one-line description for the Review tab when no
// per-finding renderer is available (e.g. clean PR).
Summary string `json:"summary,omitempty"`
// FindingsCount is the number of findings that survived recheck
// (i.e. the length of DeepFindings or Review.Structured.Findings
// at end of run). Zero is a valid value — clean PR.
FindingsCount int `json:"findings_count"`
// DismissedCount is the number of findings recheck removed. Useful
// when the user wants to confirm "the model did look at things and
// decide they were fine."
DismissedCount int `json:"dismissed_count"`
// Error captures a failure message when the run aborted partway
// through (e.g. ">20% of AOI calls failed", upstream HTTP error,
// JSON parse error). Empty on successful runs. Persisted so the
// user can see what failed on reopen rather than losing the
// context after the TUI exits.
Error string `json:"error,omitempty"`
// MinSeverity records the --min-severity threshold the run was
// filtered at (one of "critical", "high", "medium", "low", "nit").
// Empty when no filter was applied. The TUI uses this to render a
// "partial run" badge so the user knows findings below the
// threshold were dropped before recheck and can re-run without
// the filter to see the full set.
MinSeverity string `json:"min_severity,omitempty"`
}
ReviewMeta is the proof-of-run record stored on State.LastReview. It lets the TUI distinguish:
- "review ran, found N findings" (LastReview != nil, FindingsCount > 0)
- "review ran, clean PR" (LastReview != nil, FindingsCount == 0, Error == "")
- "review attempted, failed mid-flight" (LastReview != nil, Error != "")
- "no review has been run yet" (LastReview == nil)
The pipeline is responsible for populating success metadata; the TUI is responsible for stamping the Error field on AIChatDoneMsg failure paths so the next session shows what went wrong instead of "no review yet."
type ReviewOutput ¶
type ReviewOutput struct {
Summary string `json:"summary"`
Verdict string `json:"verdict"` // "approve", "request_changes", "comment"
Findings []ReviewFinding `json:"findings"`
MissingTests []string `json:"missing_tests"`
QuestionsForAuthor []string `json:"questions_for_author"`
// Coverage is the per-file breakdown of what was reviewed and
// what wasn't — stamped deterministically by the pipeline after
// synthesis runs, not authored by the LLM. Nil when the pipeline
// chose not to compute it (e.g. no AOI report available).
Coverage *ReviewCoverage `json:"coverage,omitempty"`
}
ReviewOutput is the structured JSON output from a PR review. Both single-pass and multi-pass synthesis produce this format.
type ReviewSnapshot ¶ added in v1.9.0
type ReviewSnapshot struct {
PRNumber int `json:"pr_number,omitempty"`
PRTitle string `json:"pr_title,omitempty"`
FilesReviewed int `json:"files_reviewed,omitempty"`
Review *ReviewOutput `json:"review,omitempty"`
DeepFindings []DeepFinding `json:"deep_findings,omitempty"`
}
ReviewSnapshot mirrors the JSON shape written by review.MarshalResultJSON. Single source of truth for "what one review run produced" — the TUI hydrates from this on open so a headless `prr review` and a TUI-triggered review both surface the same way.
func LatestReviewSnapshot ¶ added in v1.9.0
func LatestReviewSnapshot(prNumber string) (*ReviewSnapshot, string, error)
LatestReviewSnapshot returns the most recent snapshot for the given PR, picked by the timestamp embedded in the filename (sortable as a string by construction). Returns (nil, "", nil) when no snapshot exists — callers treat that as "no prior review."
Filename pattern: pr-<N>-review-<timestamp>.json. We rely on the timestamp being lexically sortable so this stays O(n) without stat'ing every file.
type ReviewStatus ¶
type ReviewStatus string
ReviewStatus represents the current review state of a file
const ( StatusUnreviewed ReviewStatus = "unreviewed" StatusReviewed ReviewStatus = "reviewed" StatusModified ReviewStatus = "modified" // Represents a file that was reviewed but has new changes )
type RuntimeEntryPoint ¶ added in v1.6.0
type RuntimeEntryPoint struct {
// Kind classifies the surface: "http", "queue", "scheduled",
// "cli", "rpc", "webhook", "other".
Kind string `json:"kind"`
// File is an optional path to a representative example of this
// entry-point kind (e.g. "internal/api/handlers/routes.go").
// Populated when one identifiable file exemplifies the kind;
// omitted when the surface is spread across many files with no
// single anchor. Used by Phase 3 prompts as a concrete reference
// when classifying boundaries.
File string `json:"file,omitempty"`
// Symbol is an optional function/route/handler name within File
// (e.g. "registerAdminRoutes"). Pairs with File. Omitted when
// File is empty or when no specific symbol stands out.
Symbol string `json:"symbol,omitempty"`
// RetryModel describes who retries on failure and what triggers
// retry (e.g., "API Gateway does not retry — caller's job",
// "SNS retries with exponential backoff per record").
RetryModel string `json:"retry_model,omitempty"`
// BatchModel describes batching: single-record vs. batched, and
// whether one bad record fails the batch.
BatchModel string `json:"batch_model,omitempty"`
// ValidationAt is one of "boundary", "handler", "both", "none".
// Where in the call chain inputs get validated.
ValidationAt string `json:"validation_at,omitempty"`
}
RuntimeEntryPoint describes one externally-reachable surface.
type RuntimeModel ¶ added in v1.6.0
type RuntimeModel struct {
// AuthModel describes who guards what — gateway authorizers,
// middleware, in-handler checks. 1-2 sentences.
AuthModel string `json:"auth_model,omitempty"`
// ValidationSites lists where user/network input gets validated
// before reaching business logic. Short strings, one per layer
// or location.
ValidationSites []string `json:"validation_sites,omitempty"`
// EntryPoints enumerates the externally-reachable surfaces. Each
// entry classifies its kind, retry model, batching model, and
// where validation happens. Empty list means "no entry points
// identified" (could be a library/CLI-only repo).
EntryPoints []RuntimeEntryPoint `json:"entry_points,omitempty"`
// ResultDiscipline describes how errors propagate — Result types,
// exception handling, error wrapping, sentinel values. One line.
ResultDiscipline string `json:"result_discipline,omitempty"`
// Invariants lists the load-bearing assumptions the codebase
// relies on but doesn't necessarily enforce in every call site
// (e.g., "all IDs are UUID v4", "amounts are stored in minor
// units", "the inbox table is append-only"). Short statements.
Invariants []string `json:"invariants,omitempty"`
}
RuntimeModel captures the codebase's runtime shape so Phase 3 review can ground findings in the project's actual entry points, validation sites, and error-handling discipline. Produced once per audit by Phase 0.5 and injected verbatim into every Phase 3 prompt.
The fields are intentionally narrow and prose-based — they encode the same answers a senior engineer would give a new hire on their first day. Downstream review prompts reference these answers when judging whether a finding traces through the runtime model or contradicts it.
func (*RuntimeModel) IsZero ¶ added in v1.6.0
func (m *RuntimeModel) IsZero() bool
IsZero reports whether the model carries no information.
func (*RuntimeModel) Render ¶ added in v1.6.0
func (m *RuntimeModel) Render() string
Render formats the model for injection into a Phase 3 prompt under a `## Runtime Model` section. Returns the empty string when the model is zero so callers can skip the section entirely.
The output is compact (well under 1KB on a typical repo) so it doesn't crowd out the rest of the prompt. Each field gets a labeled line; empty fields are omitted.
type SiblingDeviation ¶ added in v1.6.0
type SiblingDeviation struct {
Pattern string `json:"pattern"`
SiblingIDs []string `json:"sibling_ids,omitempty"`
}
SiblingDeviation captures a "1 of N doesn't follow the pattern" observation produced by Phase 2.5 sibling clustering. The pattern field is a one-line description ("9 of 11 admin POSTs call guardAdmin()") and SiblingIDs lists the AOI ids that DO follow the pattern (the conforming siblings), so a reviewer can compare the deviant against them.
Carried verbatim from the AreaOfInterest into the DeepFinding so the audit report can render the deviation alongside the finding.
type SiteRef ¶ added in v1.6.0
type SiteRef struct {
File string `json:"file"`
Lines string `json:"lines,omitempty"`
Symbol string `json:"symbol,omitempty"` // calling function/handler when identifiable
}
SiteRef is one call site in a Systemic / consolidated finding. It names the specific file (and optionally line range + caller symbol) where the cross-file pattern manifests. The consolidator prompt asks for at least three distinct sites before allowing a "Systemic:" title — a heuristic the validator enforces.
type State ¶
type State struct {
PRNumber string `json:"pr_number"`
GlobalChat []Message `json:"global_chat,omitempty"`
Review *AIReview `json:"review,omitempty"` // PR-level AI review
Files map[string]*FileState `json:"files"`
ProjectContext string `json:"project_context,omitempty"` // cached project briefing
ProjectContextHash string `json:"project_context_hash,omitempty"` // hash of inputs used to generate it
PRBrief string `json:"pr_brief,omitempty"` // cached PR-specific briefing (comments, prior reviews, CI)
PRBriefHash string `json:"pr_brief_hash,omitempty"` // hash of inputs used to generate the PR brief
// RuntimeModel is the structured codebase shape produced by Phase
// 0.5 — auth model, validation sites, entry points, result
// discipline, invariants. Injected into every Phase 3 prompt so
// the reviewer can ground findings in "what this codebase looks
// like at runtime". RuntimeModelHash carries the hash of inputs
// used to produce it.
RuntimeModel *RuntimeModel `json:"runtime_model,omitempty"`
RuntimeModelHash string `json:"runtime_model_hash,omitempty"`
// BoundaryInventory is the Phase 1.5 list of externally-reachable
// surfaces (HTTP routes, queue consumers, schedulers, storage
// triggers, CAS-shaped DB writes). Each entry seeds 1-3 defense-
// coverage AOIs synthesized before Phase 3 so every boundary is
// guaranteed to be reviewed for the standard defense questions
// (schema validation, error handling, authorization, per-record
// isolation, result discipline).
BoundaryInventory []Boundary `json:"boundary_inventory,omitempty"`
BoundaryInventoryHash string `json:"boundary_inventory_hash,omitempty"`
// SiblingClusterOutliers caches the outlier AOIs synthesized by
// Phase 2.5 sibling clustering. Stored as opaque JSON because the
// outliers are typed []security.AreaOfInterest and state must not
// import security (security already imports state for
// SiblingDeviation). On cache hit the audit package unmarshals
// into its own typed slice. Hash covers the candidate AOI set
// + the cluster prompt; either rolls the cache.
SiblingClusterOutliers json.RawMessage `json:"sibling_cluster_outliers,omitempty"`
SiblingClusterHash string `json:"sibling_cluster_hash,omitempty"`
// DeepReviews caches Phase 3 deep review results. Keyed by a hash of the
// review inputs (file content + AOI content + focus categories for individual;
// all AOI content + focus categories for grouped).
DeepReviews map[string]*DeepReviewResult `json:"deep_reviews,omitempty"`
// RecheckCache caches Phase 3b recheck output by hash of the input
// findings + project context + mode. The value is a serialized
// []DeepFinding (the cleaned, deduplicated set).
RecheckCache map[string]json.RawMessage `json:"recheck_cache,omitempty"`
// SynthesisCache caches Phase 4 synthesis output by hash of the input
// findings + cross-cutting + project context. The value is a serialized
// SynthesisResult (audit-package type, opaque to this package).
SynthesisCache map[string]json.RawMessage `json:"synthesis_cache,omitempty"`
// DeepFindings is the top-level persisted list of Phase 1 + Phase 1c
// findings, independent of the synthesized Review object. Populated
// incrementally as each batch completes so a crash, cancellation, or
// failed-synthesis still leaves the user with their findings on
// reopen. The Review tab reads this when state.Review is nil — which
// is the default in TUI mode where synthesis is skipped.
DeepFindings []DeepFinding `json:"deep_findings,omitempty"`
// RecheckDismissals records every finding the Phase 3b recheck pass
// removed, along with the LLM's rationale. Persisted so the audit
// report can show users WHY a finding disappeared, and so prompt
// tuning can be measured (compare dismissal counts and rationales
// across runs). Replaces the previous "DismissedCount only" output,
// which routed rationales to log.Printf and lost them.
RecheckDismissals []DismissedRecord `json:"recheck_dismissals,omitempty"`
// LastReview records that a review run completed against this PR,
// regardless of whether it produced any findings. Without this,
// "review ran and found nothing" is indistinguishable from "no
// review has ever been attempted" — both leave Review/DeepFindings
// empty and force the TUI to fall back to "no review yet" prompts.
//
// Set by the pipeline at the end of a successful run (or partial
// run that produced any artifacts). Cleared by ClearForFreshReview
// when the user forces a re-review.
LastReview *ReviewMeta `json:"last_review,omitempty"`
// contains filtered or unexported fields
}
State represents the persisted review state for a single pull request
func Load ¶
Load reads the state for a given PR number from disk. If the file does not exist, it returns a new, empty State.
func (*State) AppendDeepFindings ¶ added in v1.4.0
func (s *State) AppendDeepFindings(findings []DeepFinding)
AppendDeepFindings adds findings to the top-level list. Holds the write lock for the whole append so concurrent batch goroutines don't drop entries via read-then-write races.
func (*State) ClearAllCaches ¶ added in v1.3.0
func (s *State) ClearAllCaches()
ClearAllCaches clears all per-file cached data (batch findings, AOI results) and the PR-level review. Used by forceReReview.
func (*State) ClearDeepFindings ¶ added in v1.4.0
func (s *State) ClearDeepFindings()
ClearDeepFindings empties the persisted list. Used when the pipeline starts a fresh review run and wants to discard stale incremental findings before accumulating new ones.
func (*State) ClearDeepReviews ¶ added in v1.4.0
func (s *State) ClearDeepReviews()
ClearDeepReviews removes all cached Phase 3 results.
func (*State) ClearPRBrief ¶ added in v1.4.0
func (s *State) ClearPRBrief()
ClearPRBrief invalidates the cached PR brief. Use when external state (e.g. the prior AI review) changes in a way that should force regeneration even if the input hash hasn't moved.
func (*State) CollectCachedFindings ¶ added in v1.3.0
func (s *State) CollectCachedFindings(paths []string) (combined string, fileFindings map[string]string)
CollectCachedFindings reassembles per-file findings from cache.
func (*State) CountCachedBatchFindings ¶ added in v1.4.0
CountCachedBatchFindings returns how many of the given paths have a non-empty BatchFindings entry in state. Holds the read lock for the entire iteration so the count is consistent against concurrent writers — callers that build their own loops via HasFile + direct Files map access would race.
func (*State) DiffSnapshotFromFiles ¶
DiffSnapshotFromFiles returns a snapshot of the current DiffHash for each file in the state. This is meant to be stored on AIReview.DiffSnapshot at the time a review is generated so staleness can be detected later.
func (*State) GetAOIResults ¶ added in v1.3.0
func (s *State) GetAOIResults(path string) (json.RawMessage, int)
GetAOIResults returns the cached AOI results for a file, or nil. Also returns the context lines used when the results were generated.
func (*State) GetBatchFindings ¶ added in v1.3.0
GetBatchFindings returns the cached purpose and findings for a file.
func (*State) GetBoundaryInventory ¶ added in v1.6.0
GetBoundaryInventory returns the cached boundary inventory and its input hash. The returned slice is the live one held by State; treat it as read-only.
func (*State) GetDeepFindings ¶ added in v1.4.0
func (s *State) GetDeepFindings() []DeepFinding
GetDeepFindings returns a copy of the persisted deep findings. Returning a copy keeps callers from racing with concurrent writers.
func (*State) GetDeepReview ¶ added in v1.4.0
func (s *State) GetDeepReview(key string) *DeepReviewResult
GetDeepReview returns a cached Phase 3 result by key, or nil.
func (*State) GetFileType ¶ added in v1.4.0
GetFileType returns the cached classification type for a file, or empty string.
func (*State) GetPRBrief ¶ added in v1.4.0
GetPRBrief returns the cached PR brief and its input hash.
func (*State) GetProjectContext ¶ added in v1.3.0
GetProjectContext returns the cached project context and its input hash.
func (*State) GetRecheckCache ¶ added in v1.4.0
func (s *State) GetRecheckCache(key string) json.RawMessage
GetRecheckCache returns a cached recheck result by key, or nil.
func (*State) GetRecheckDismissals ¶ added in v1.5.0
func (s *State) GetRecheckDismissals() []DismissedRecord
GetRecheckDismissals returns a copy of the persisted dismissal log.
func (*State) GetRuntimeModel ¶ added in v1.6.0
func (s *State) GetRuntimeModel() (model *RuntimeModel, inputHash string)
GetRuntimeModel returns the cached runtime model and its input hash. The returned pointer is the live one held by State; treat it as read-only.
func (*State) GetSiblingClusterCache ¶ added in v1.6.0
func (s *State) GetSiblingClusterCache() (outliers json.RawMessage, inputHash string)
GetSiblingClusterCache returns the cached outlier JSON and its input hash. Returns (nil, "") when no cache entry exists.
func (*State) GetSynthesisCache ¶ added in v1.4.0
func (s *State) GetSynthesisCache(key string) json.RawMessage
GetSynthesisCache returns a cached synthesis result by key, or nil.
func (*State) HasCachedBatch ¶ added in v1.3.0
HasCachedBatch reports whether all files in the given paths have cached findings.
func (*State) HasReviewArtifact ¶ added in v1.6.0
HasReviewArtifact returns true when this PR's state shows any evidence that a review has been attempted — a synthesized Review, persisted deep findings, OR a LastReview marker. The marker is what makes "clean PR" honest: zero findings is a result, not absence.
func (*State) IsReviewStale ¶
IsReviewStale reports whether the stored review's DiffSnapshot differs from the current file diff hashes. A review is considered stale when any file's hash has changed, files have been added, or files have been removed since the review was generated.
func (*State) SetAOIResults ¶ added in v1.3.0
func (s *State) SetAOIResults(path string, data json.RawMessage, contextLines int, promptHash string)
SetAOIResults stores AOI scan results for a file along with the context lines and the AOI scanner prompt hash used to generate them. Creates the FileState if it doesn't exist. The promptHash is what gates cache reuse on prompt edits — passing "" disables that check (useful for tests).
func (*State) SetBatchFindings ¶ added in v1.3.0
SetBatchFindings stores the batch review purpose and findings for a file.
func (*State) SetBoundaryInventory ¶ added in v1.6.0
SetBoundaryInventory stores the Phase 1.5 boundary inventory and its input hash.
func (*State) SetDeepFindings ¶ added in v1.4.0
func (s *State) SetDeepFindings(findings []DeepFinding)
SetDeepFindings replaces the persisted top-level deep findings. Used by the pipeline at recheck boundaries (replace) and on load migration. For incremental append during Phase 1, use AppendDeepFindings.
func (*State) SetDeepReview ¶ added in v1.4.0
func (s *State) SetDeepReview(key string, result *DeepReviewResult)
SetDeepReview stores a Phase 3 deep review result by cache key.
func (*State) SetFileType ¶ added in v1.4.0
SetFileType stores the classification type for a file.
func (*State) SetLastReview ¶ added in v1.6.0
func (s *State) SetLastReview(meta *ReviewMeta)
SetLastReview records that a review run completed against this PR, regardless of whether it produced findings. Pipeline calls this at end-of-run; the TUI reads it to distinguish "review ran (maybe nothing found)" from "never reviewed."
Pass nil to clear (e.g. fresh re-review). Otherwise CompletedAt is auto-stamped if the caller leaves it zero.
func (*State) SetPRBrief ¶ added in v1.4.0
SetPRBrief stores a cached PR-specific briefing (summary of comments, prior AI reviews, CI status) and its input hash. Mirrors the SetProjectContext API so callers in internal/prcontext can use it the same way internal/project uses ProjectContext.
func (*State) SetProjectContext ¶ added in v1.3.0
SetProjectContext stores a cached project context and its input hash.
func (*State) SetRecheckCache ¶ added in v1.4.0
func (s *State) SetRecheckCache(key string, raw json.RawMessage)
SetRecheckCache stores a recheck output (serialized JSON) by cache key.
func (*State) SetRecheckDismissals ¶ added in v1.5.0
func (s *State) SetRecheckDismissals(dismissals []DismissedRecord)
SetRecheckDismissals replaces the persisted dismissal log. Called once per recheck run with the full list — recheck dismissals are produced as a batch, not incrementally, so a full overwrite is the right shape (vs. AppendDeepFindings which streams during Phase 3).
func (*State) SetRuntimeModel ¶ added in v1.6.0
func (s *State) SetRuntimeModel(model *RuntimeModel, inputHash string)
SetRuntimeModel stores the discovered runtime model and the hash of the inputs used to produce it. Mirrors SetProjectContext.
func (*State) SetSiblingClusterCache ¶ added in v1.6.0
func (s *State) SetSiblingClusterCache(outliers json.RawMessage, inputHash string)
SetSiblingClusterCache stores the Phase 2.5 outlier list (as opaque JSON) along with its input hash. The audit package owns the typed shape of the JSON; state stores it opaquely to avoid importing the security package.
func (*State) SetSynthesisCache ¶ added in v1.4.0
func (s *State) SetSynthesisCache(key string, raw json.RawMessage)
SetSynthesisCache stores a synthesis output (serialized JSON) by cache key.
func (*State) SyncWithDiffs ¶
SyncWithDiffs compares current diff hashes against stored hashes and invalidates state where necessary. currentDiffHashes maps file path to SHA-256 hash of its current diff (only files where diff succeeded). prFiles is the complete set of files in the PR (used to detect removed files).
type TraceHop ¶ added in v1.6.0
type TraceHop struct {
Role string `json:"role"` // "suspect" | "caller" | "boundary" | free-form
File string `json:"file,omitempty"` // path of the hop's file
Lines string `json:"lines,omitempty"` // line range within the file
Evidence string `json:"evidence,omitempty"` // 1-line summary of what was confirmed at this hop
}
TraceHop is one step of the end-to-end trace the reviewer walked before flagging a finding. The 3-hop minimum (suspect line → caller → boundary) is enforced for critical/high severity by the confidence-penalty rule in applyConfidencePenalties.
Role is one of:
- "suspect" — the cited line itself; the alleged bug location.
- "caller" — the immediate function/handler that invokes the suspect code.
- "boundary" — the next system boundary the value reaches (transport: HTTP response, RPC reply, message send; persistence: any write that may CAS, versioned column, or conditional update; trust: input from network, file, env, message body). The runtime model from Phase 0.5 anchors which boundaries exist in this codebase.
Findings can include additional hops between caller and boundary when the data flow passes through layered helpers; the validator only requires that at least 3 hops are present.