store

package
v0.2.0-rc.3 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: MIT Imports: 27 Imported by: 0

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

View Source
const (
	TurnQueued      = "queued"
	TurnRunning     = "running"
	TurnCompleted   = "completed"
	TurnFailed      = "failed"
	TurnCancelled   = "cancelled"
	TurnInterrupted = "interrupted"
)

Turn states. Queued and running are pending; the others are settled.

View Source
const MaxAttachmentBytes = 10 << 20
View Source
const MaxAttachments = 20

Variables

View Source
var ErrConflict = errors.New("the stored review changed during the operation; retry using the latest version")
View Source
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.

View Source
var ErrConversationNotFound = errors.New("no such conversation for this pull request")

ErrConversationNotFound is a conversation the store does not hold for the pull request.

View Source
var ErrNotFound = errors.New("no stored review version; generate a review in the baum desktop app first")

Functions

func Pending

func Pending(state string) bool

Pending reports whether a turn in state is still waiting for its answer.

func Roots

func Roots(commonDir string) ([]string, error)

Roots are the roots Open chooses between for a clone's common directory, in its order: the clone-local one, then the per-user data fallback.

Types

type Attachment

type Attachment struct {
	ID          string
	ContentType string
	Bytes       []byte
}

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 Commits

type Commits struct {
	Key       Key
	Version   int
	Head      string
	Base      string
	MergeBase string
}

Commits are the commits a stored version recorded.

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

type Ended func(jobID string) (state, reason string, ended bool)

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

func SeenThroughHeartbeats(path string, stamp FileStamp) FileStamp

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.

func StampOf added in v0.2.0

func StampOf(path string) (FileStamp, error)

StampOf stats the database file at path and its write-ahead log, without opening either: closing a descriptor to a SQLite file would drop the locks this process's connections hold on it.

type Key

type Key struct {
	Repository string
	Number     int
}

Key isolates pull requests of different GitHub repositories, including hosts.

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

func Open(commonDir, override string, write bool) (*Store, error)

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

func (s *Store) Attachment(ctx context.Context, k Key, version int, id string) (Attachment, error)

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) BeginRun

func (s *Store) BeginRun(ctx context.Context, k Key, deadline time.Time) (int64, error)

func (*Store) Commits

func (s *Store) Commits(ctx context.Context) ([]Commits, error)

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

func (s *Store) Conversation(ctx context.Context, k Key, id string) (Conversation, error)

Conversation returns one conversation of the pull request with its turns.

func (*Store) Conversations

func (s *Store) Conversations(ctx context.Context, k Key) ([]Conversation, error)

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

func (s *Store) CreateConversation(ctx context.Context, k Key, c Conversation, first Turn) error

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

func (s *Store) FinishRun(ctx context.Context, id int64, reason string) error

FinishRun completes exactly one matching unfinished attempt. Empty reason means successful publication; arbitrary provider errors are never persisted.

func (*Store) Get

func (s *Store) Get(ctx context.Context, k Key, version int) (Version, error)

func (*Store) HeartbeatRun

func (s *Store) HeartbeatRun(ctx context.Context, id int64, deadline time.Time) error

HeartbeatRun renews an unfinished attempt without changing its start order.

func (*Store) Latest

func (s *Store) Latest(ctx context.Context, k Key) (Version, error)

func (*Store) NoteTurn

func (s *Store) NoteTurn(ctx context.Context, k Key, id string, seq int, note string) error

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

func (s *Store) Put(ctx context.Context, k Key, previous Version, v Version) error

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

func (s *Store) RefreshCurrent(ctx context.Context, k Key, previous Version, document []byte) error

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) Root

func (s *Store) Root() string

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.

func (*Store) Snapshot

func (s *Store) Snapshot(ctx context.Context) ([]Summary, error)

func (*Store) StartTurn

func (s *Store) StartTurn(ctx context.Context, k Key, id string, seq int) (bool, error)

StartTurn marks a queued turn running. It reports false when the turn is not queued (cancelled before it started, or already settled).

func (*Store) Versions

func (s *Store) Versions(ctx context.Context, k Key) ([]Metadata, error)

Versions returns metadata newest first without reading document bodies.

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:"-"`
}

Jump to

Keyboard shortcuts

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