state

package
v1.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 28, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AuditsDir added in v1.8.0

func AuditsDir() (string, error)

AuditsDir returns the absolute path to the audits snapshot directory and ensures the directory exists.

func DeleteStateFile added in v1.6.0

func DeleteStateFile(prNumber string) error

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

func ReviewsDir() (string, error)

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 Save

func Save(state *State) error

Save writes the given State to disk.

func SaveAuditSnapshot added in v1.8.0

func SaveAuditSnapshot(data []byte) (string, error)

SaveAuditSnapshot writes data to .git/pr-tui/audits/audit-<timestamp>.json. Returns the absolute path on success.

func SaveReviewSnapshot added in v1.8.0

func SaveReviewSnapshot(prNumber string, data []byte) (string, error)

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

func SetCategoryValidator(f func(string) bool)

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

func MustParseCategory(s string) Category

MustParseCategory panics on invalid input. Use for compile-time constants and tests only.

func ParseCategory added in v1.10.0

func ParseCategory(s string) (Category, error)

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) IsZero added in v1.10.0

func (c Category) IsZero() bool

func (Category) MarshalJSON added in v1.10.0

func (c Category) MarshalJSON() ([]byte, error)

func (Category) String added in v1.10.0

func (c Category) String() string

func (*Category) UnmarshalJSON added in v1.10.0

func (c *Category) UnmarshalJSON(b []byte) error

UnmarshalJSON is the validation boundary: an unknown slug surfaces as a parse error on the enclosing struct.

type CorruptStateError added in v1.6.0

type CorruptStateError struct {
	Path  string
	Cause error
}

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 Message

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

Message represents a single chat message in an AI conversation

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

func Load(prNumber string) (*State, error)

Load reads the state for a given PR number from disk. If the file does not exist, it returns a new, empty State.

func NewState

func NewState(prNumber string) *State

NewState initializes a new empty state object for a PR

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

func (s *State) CountCachedBatchFindings(paths []string) int

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

func (s *State) DiffSnapshotFromFiles() map[string]string

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

func (s *State) GetBatchFindings(path string) (purpose, findings string)

GetBatchFindings returns the cached purpose and findings for a file.

func (*State) GetBoundaryInventory added in v1.6.0

func (s *State) GetBoundaryInventory() (boundaries []Boundary, inputHash string)

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

func (s *State) GetFileType(path string) string

GetFileType returns the cached classification type for a file, or empty string.

func (*State) GetPRBrief added in v1.4.0

func (s *State) GetPRBrief() (brief, inputHash string)

GetPRBrief returns the cached PR brief and its input hash.

func (*State) GetProjectContext added in v1.3.0

func (s *State) GetProjectContext() (summary, inputHash string)

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

func (s *State) HasCachedBatch(paths []string) bool

HasCachedBatch reports whether all files in the given paths have cached findings.

func (*State) HasFile added in v1.3.0

func (s *State) HasFile(path string) bool

HasFile reports whether a file exists in the state.

func (*State) HasReviewArtifact added in v1.6.0

func (s *State) HasReviewArtifact() bool

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

func (s *State) IsReviewStale() bool

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

func (s *State) SetBatchFindings(path, purpose, findings string)

SetBatchFindings stores the batch review purpose and findings for a file.

func (*State) SetBoundaryInventory added in v1.6.0

func (s *State) SetBoundaryInventory(boundaries []Boundary, inputHash string)

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

func (s *State) SetFileType(path, fileType string)

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

func (s *State) SetPRBrief(brief, inputHash string)

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

func (s *State) SetProjectContext(summary, inputHash string)

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

func (s *State) SyncWithDiffs(currentDiffHashes map[string]string, prFiles map[string]bool)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL