Documentation
¶
Overview ¶
Package store keeps local review versions with immutable generation provenance. A SQLite transaction publishes the document and metadata together, comparing the previous version under the same write lock so concurrent generations cannot fork history. A refresh replaces the latest document only while its exact bytes are unchanged, so concurrent refreshes cannot lose each other, and only when it changes more than the refresh time. Triggers keep the index projection of each latest document in the summaries table current in the writer's own transaction; see Snapshot. Others renew each version's document revision with every write; see Revision.
A stored document holds no hunk evidence: Put removes every indexed hunk's patch text and GitHub fragment (review.StripHunkEvidence), which the clone and the hunk's path and range give back when the document is read.
Index ¶
- Constants
- Variables
- func Pending(state string) bool
- func Roots(commonDir string) ([]string, error)
- type Attachment
- type Commits
- type Conversation
- type Ended
- type FileStamp
- type Key
- type Metadata
- type ReviewSession
- type Run
- type Settlement
- type Store
- func (s *Store) AppendTurn(ctx context.Context, k Key, id string, t Turn, ended Ended) (Conversation, int, error)
- func (s *Store) Attachment(ctx context.Context, k Key, version int, id string) (Attachment, error)
- func (s *Store) BeginRun(ctx context.Context, k Key, deadline time.Time) (int64, error)
- func (s *Store) Commits(ctx context.Context) ([]Commits, error)
- func (s *Store) CompleteCurrent(ctx context.Context, k Key, previous Version, document []byte) error
- func (s *Store) Conversation(ctx context.Context, k Key, id string) (Conversation, error)
- func (s *Store) Conversations(ctx context.Context, k Key) ([]Conversation, error)
- func (s *Store) CreateConversation(ctx context.Context, k Key, c Conversation, first Turn) error
- func (s *Store) FillTurnCost(ctx context.Context, k Key, id string, seq int, cost *float64, reason string) error
- func (s *Store) FinishRun(ctx context.Context, id int64, reason string) error
- func (s *Store) Get(ctx context.Context, k Key, version int) (Version, error)
- func (s *Store) HeartbeatRun(ctx context.Context, id int64, deadline time.Time) error
- func (s *Store) Latest(ctx context.Context, k Key) (Version, error)
- func (s *Store) NoteTurn(ctx context.Context, k Key, id string, seq int, note string) error
- func (s *Store) Put(ctx context.Context, k Key, previous Version, v Version) error
- func (s *Store) RefreshCurrent(ctx context.Context, k Key, previous Version, document []byte) error
- func (s *Store) Revision(ctx context.Context, k Key, n int) (version int, revision int64, err error)
- func (s *Store) Root() string
- func (s *Store) SetConversationSession(ctx context.Context, k Key, id, session, directory string, seeded bool, ...) error
- func (s *Store) SettleTurn(ctx context.Context, k Key, id string, seq int, settled Settlement) (bool, error)
- func (s *Store) Snapshot(ctx context.Context) ([]Summary, error)
- func (s *Store) StartTurn(ctx context.Context, k Key, id string, seq int) (bool, error)
- func (s *Store) Versions(ctx context.Context, k Key) ([]Metadata, error)
- type Summary
- type Turn
- type Version
Constants ¶
const ( TurnQueued = "queued" TurnRunning = "running" TurnCompleted = "completed" TurnFailed = "failed" TurnCancelled = "cancelled" TurnInterrupted = "interrupted" )
Turn states. Queued and running are pending; the others are settled.
const MaxAttachmentBytes = 10 << 20
const MaxAttachments = 20
Variables ¶
var ErrConflict = errors.New("the stored review changed during the operation; retry using the latest version")
var ErrConversationBusy = errors.New("the conversation is still answering a question")
ErrConversationBusy refuses a question while another of the same conversation is queued or running.
var ErrConversationNotFound = errors.New("no such conversation for this pull request")
ErrConversationNotFound is a conversation the store does not hold for the pull request.
var ErrNotFound = errors.New("no stored review version; generate a review in the baum desktop app first")
Functions ¶
Types ¶
type Attachment ¶
Attachment is a captured raster image. Bytes are persisted transactionally with the version, never inferred from a path or fetched from a document URL.
type Conversation ¶
type Conversation struct {
ID string `json:"id"`
Version int `json:"version"`
Backend string `json:"backend"`
Model string `json:"model"`
SessionID string `json:"-"`
Directory string `json:"-"`
Seeded bool `json:"seeded"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
Turns []Turn `json:"turns"`
}
Conversation is one chat about a review version. SessionID and Directory are the agent session the next turn resumes and the working directory it was recorded under; they never leave the daemon. Seeded says the session started from the saved review and earlier turns instead of the review agent's own session.
type Ended ¶
Ended reports how the job of a pending turn ended: the state and reason to settle the turn with, and false while the job is still queued or running.
type FileStamp ¶ added in v0.2.0
type FileStamp struct{ Size, ModTime, WALSize, WALModTime int64 }
FileStamp is the size and modification time (nanoseconds) of a store's database file and of its write-ahead log beside it. A commit appends to the log, which a checkpoint later copies into the database file, so the database file alone does not move with every write. WALSize is -1 when there is no log.
func SeenThroughHeartbeats ¶ added in v0.2.0
SeenThroughHeartbeats maps the stamp of the store file at path to what it was before this process's latest lease heartbeat wrote it, when the file is exactly as that heartbeat left it. A lease renewal changes nothing a reader of the store sees, so a watcher that compares stamps can look through it; any other write, before or after the heartbeat, leaves a different stamp and is not hidden. A write by another process landing between the heartbeat's two stats would be folded in; the review index's MaxAge bounds that.
type Metadata ¶
type Metadata struct {
PriceVersion string `json:"price_version,omitempty"`
Accounting *agent.Accounting `json:"accounting,omitempty"`
CostReason string `json:"cost_reason,omitempty"`
Version int `json:"version"`
Head string `json:"head"`
Base string `json:"base"`
MergeBase string `json:"merge_base"`
BundleDigest string `json:"bundle_digest"`
Backend string `json:"backend"`
BackendVersion string `json:"backend_version"`
Model string `json:"model"`
Schema string `json:"schema"`
SchemaRevision string `json:"schema_revision"`
PromptVersion string `json:"prompt_version"`
PromptDigest string `json:"prompt_digest"`
// PromptSource says where the custom prompts the run used came from:
// "repository" when any of them did, "global" when only global ones were
// used, absent when every prompt was built in.
PromptSource string `json:"prompt_source,omitempty"`
Policy *string `json:"policy"`
GeneratedAt string `json:"generated_at"`
Elapsed time.Duration `json:"elapsed_ns"`
Calls int `json:"calls"`
Tokens int `json:"tokens"`
RecordedCostUSD *float64 `json:"recorded_cost_usd"`
// Session is the local agent session that produced the version, kept with
// its run directory so a chat can continue from it. Absent for hosted and
// other runs, and in versions stored before it existed.
Session *ReviewSession `json:"session,omitempty"`
}
Metadata records reproducible input identities and the generation that produced a version. Policy is the digest of the applied base REVIEW.md, null when absent.
type ReviewSession ¶
type ReviewSession struct {
Backend string `json:"backend"`
ID string `json:"id"`
Directory string `json:"directory"`
}
ReviewSession names a kept Claude Code session or Codex thread: resuming it has to run in Directory, the cwd the session was recorded under.
type Run ¶
type Run struct {
ID int64 `json:"id"`
State string `json:"state"`
Reason string `json:"reason,omitempty"`
Started time.Time `json:"started_at"`
Expires time.Time `json:"expires_at"`
Finished time.Time `json:"finished_at,omitempty"`
}
Run records an attempt independently of immutable successful versions. IDs order starts, so an older attempt finishing cannot replace a newer attempt. Expires is renewed while the owning process is alive. A process killed without cleanup becomes interrupted instead of generating forever.
type Settlement ¶
type Settlement struct {
State, Reason string
// Answer is the redacted answer document of a completed turn.
Answer json.RawMessage
CostUSD *float64
CostReason string
Finished time.Time
}
Settlement is how a turn ends.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func Open ¶
Open selects the store under the common git directory. An existing store always wins, even when read-only, rather than hiding its history. New stores fall back to XDG_DATA_HOME/baum/reviews/<clone digest> when git is read-only. Override is an explicit root: errors there never silently select another one. Read-only opens neither create directories nor change database contents.
func (*Store) AppendTurn ¶
func (s *Store) AppendTurn(ctx context.Context, k Key, id string, t Turn, ended Ended) (Conversation, int, error)
AppendTurn adds t to the conversation as its next turn and returns the conversation as it was before, with its turns, and the new turn's number. A pending turn whose job ended reports ended (a job that just failed, or a daemon that stopped while it ran) is settled first; any turn still pending after that refuses the new one with ErrConversationBusy. The check and the insert are one transaction.
func (*Store) Attachment ¶
Attachment reads a single document-listed captured image. Version zero selects latest in one read transaction. Earlier stores without bytes return ErrNotFound without creating a table or changing their contents.
func (*Store) Commits ¶
Commits lists the recorded commits of every stored version of every pull request, without reading documents. A root with no database is ErrNotFound.
func (*Store) CompleteCurrent ¶
func (s *Store) CompleteCurrent(ctx context.Context, k Key, previous Version, document []byte) error
CompleteCurrent replaces the latest document with the same version whose failed section details were written again, preserving its metadata. It is fenced as RefreshCurrent is: the generation identity (repository, pull request, version, head, base, merge base and generation time) must not change, and the version and exact previous bytes must still be the latest's, so a refresh or a new version in the meantime makes it a conflict. Like a refresh, a completion that changes nothing is not written.
func (*Store) Conversation ¶
Conversation returns one conversation of the pull request with its turns.
func (*Store) Conversations ¶
Conversations returns the pull request's conversations, newest first, each with its turns in order. A store without any, or made before chats existed, has none.
func (*Store) CreateConversation ¶
CreateConversation stores a new conversation of the pull request with its first turn.
func (*Store) FillTurnCost ¶
func (s *Store) FillTurnCost(ctx context.Context, k Key, id string, seq int, cost *float64, reason string) error
FillTurnCost records what a turn's job cost on a turn already settled without it: one a new question settled from its ended job a moment before the job's own settlement arrived. A completed turn, or one with a cost, keeps its own.
func (*Store) FinishRun ¶
FinishRun completes exactly one matching unfinished attempt. Empty reason means successful publication; arbitrary provider errors are never persisted.
func (*Store) HeartbeatRun ¶
HeartbeatRun renews an unfinished attempt without changing its start order.
func (*Store) NoteTurn ¶
NoteTurn records a note on a turn, such as the conversation falling back to a session started without the review's own context.
func (*Store) Put ¶
Put appends v only while previous is still the latest version; the zero Version means no history. The document is stored without hunk evidence (review.StripHunkEvidence), so the stored bytes differ from v.Document when it carries any. A refresh of previous during generation replaces only its live facts in place, so it does not reject the generated version: the next refresh brings the new version's live facts up to date. A failed or cancelled transaction leaves every published version untouched.
func (*Store) RefreshCurrent ¶
RefreshCurrent replaces only the latest document, preserving its metadata. A document stored without hunk evidence is replaced without it; one stored with it, before Put removed it, is replaced as given, so no write takes the evidence away from a version saved with it. A document that differs from the previous one only in review.evidence_refreshed_at (review.RefreshChanged) is not written, so the stored time is that of the last refresh that changed something. Both the version and exact previous bytes fence concurrent generation and refresh, a refresh that writes nothing included. A running generation does not block it: live facts refresh independently, and the generated version still publishes over it.
func (*Store) Revision ¶ added in v0.2.0
func (s *Store) Revision(ctx context.Context, k Key, n int) (version int, revision int64, err error)
Revision returns the version Latest (n 0) or Get (n positive) would read and its document's revision (Version.Revision), without reading the document. The revision is 0 when the store cannot vouch for one; it is then not an identity of the document.
func (*Store) SetConversationSession ¶
func (s *Store) SetConversationSession(ctx context.Context, k Key, id, session, directory string, seeded bool, at time.Time) error
SetConversationSession records the agent session the conversation's next turn resumes, the directory it runs in and whether it was seeded.
func (*Store) SettleTurn ¶
func (s *Store) SettleTurn(ctx context.Context, k Key, id string, seq int, settled Settlement) (bool, error)
SettleTurn ends a pending turn and reports whether it did: a turn already settled keeps how it ended.
type Summary ¶
type Summary struct {
Key Key `json:"key"`
Latest *Metadata `json:"latest"`
VersionCount int `json:"version_count"`
Title string `json:"title"`
PRState string `json:"pr_state"`
ReviewMinutes *int `json:"review_minutes"`
RiskScore json.RawMessage `json:"risk_score"`
Run *Run `json:"run,omitempty"`
}
Summary is the latest document's small index projection and version count. All entries and run records in Snapshot come from one SQLite read snapshot.
type Turn ¶
type Turn struct {
Seq int `json:"seq"`
Question string `json:"question"`
Focus json.RawMessage `json:"focus"`
Answer json.RawMessage `json:"answer,omitempty"`
JobID string `json:"job_id"`
State string `json:"state"`
Reason string `json:"reason,omitempty"`
Note string `json:"note,omitempty"`
CostUSD *float64 `json:"cost_usd,omitempty"`
CostReason string `json:"cost_reason,omitempty"`
Created time.Time `json:"created"`
Finished *time.Time `json:"finished,omitempty"`
}
Turn is one question and its answer. Focus is the part of the review the question is about, as the app sent it; Answer is the redacted answer document, absent until the turn completes.
type Version ¶
type Version struct {
Attachments []Attachment `json:"-"`
Metadata Metadata `json:"metadata"`
Document json.RawMessage `json:"document"`
// Revision names Document's stored bytes (see Store.Revision), read with
// them: a nonzero revision equal to one read before means the same bytes.
// 0 when the store cannot vouch for one, and on versions not yet stored.
Revision int64 `json:"-"`
}