Documentation
¶
Overview ¶
Package checkcache is where what file-guards concluded about commits is kept, so that no one asks a judge the same question twice.
The unit it stores is a RUN: one rule evaluated once over one commit range, with its Checks (one per subject and kind, each with its Items) — a10n's check-results shape, which internal/checkstore speaks. What makes a check reusable is its KEY and nothing else: which session, agent, branch or range recorded it is provenance on the run, never part of a lookup. A finished pass with the same key is a cache hit; a stored fail is kept so a reader can say why it is red.
This file is the whole contract the engine depends on: Lookup and Put. The in-memory implementation below is the reference and what the unit tests use; the git-ref store (an orphan branch of content-addressed zstd segments) implements the same interface.
Index ¶
- Constants
- Variables
- func Findable(c Check) bool
- func Newer(a, b Found) bool
- type Cache
- type Check
- type File
- type Found
- type GcStats
- type Item
- type Key
- type Memory
- type Options
- type Run
- type Store
- func (s *Store) Find(subject string) ([]Found, error)
- func (s *Store) FreezeTip()
- func (s *Store) Gc() (GcStats, error)
- func (s *Store) Lookup(keys []Key) (map[string]Found, error)
- func (s *Store) PendingPush() error
- func (s *Store) Pull() error
- func (s *Store) Put(runs []Run) error
- func (s *Store) Runs() ([]Run, error)
- func (s *Store) Show(subject string) (string, error)
- func (s *Store) Sync() error
- func (s *Store) Tip() string
Constants ¶
const ( StatusPass = "pass" StatusFail = "fail" StatusSkip = "skip" StatusError = "error" StatusInterrupted = "interrupted" )
Check statuses. Only a pass is a cache hit.
const ( // TrainMin is how many records Gc needs before it trains a dictionary // from them; below it segments are written with no dictionary (plain zstd). TrainMin = 300 // SegmentTarget is the record count Gc compacts segments to. SegmentTarget = 1000 )
const GcSegments = 24
GcSegments is how many segments a Put leaves behind before the store compacts them: every Lookup scans every segment index.
const PushErrorFile = "sloprail-checks-push-error"
PushErrorFile is the file under the git common dir holding why the last push of local results failed (absent once a push succeeds). The push gate (when enabled), a separate process, reads it.
const SchemaDir = "v2026-10-08"
SchemaDir is the schema version (a date) and the single directory the store reads and writes. A ref carrying a newer v<date>/ directory is refused.
A change to what a key is made of bumps it (with SchemaVersion). Opening or writing a ref that holds only older directories just starts this one, empty: the older directories are not read or rewritten, and everything is judged again. An older binary that meets the new directory refuses it (ErrFutureSchema), so CI's sr-checks is upgraded with the engine that writes it.
v2026-10-08: sr3 keys, no citations in the fingerprint.
const SchemaVersion = "sr3"
SchemaVersion is the key schema. It is part of every key, so a change to how fingerprints are derived, or to what a key is made of, never collides with an older result.
sr1: rule, the rule's hash, kind, subject, fingerprint (see legacyID). sr2: no rule hash. sr3: the fingerprint no longer covers citations.
Changing what a key is made of means bumping this AND SchemaDir: the older directory is left as it is and the new one starts empty.
Variables ¶
var ErrCorrupt = errors.New("checkcache: corrupt segment")
ErrCorrupt means stored bytes do not match their content address or cannot be decoded. It is never reported as a miss.
var ErrFutureSchema = errors.New("checkcache: ref uses a newer schema; upgrade sloprail")
ErrFutureSchema means the ref holds a schema directory newer than this binary.
Functions ¶
func Findable ¶
Findable is whether a check can be looked up: it has a fingerprint and finished as a pass or a fail — or is a fail that was resolved as stale (a skip carrying metadata "staleFrom"), which is found so that, being the newest result of its key, it supersedes the fail it resolves: a reader sees a skip, and a skip is no hit. sr:invariant cache/unfinished-never-stored
Types ¶
type Cache ¶
type Cache interface {
// Lookup returns, for each key it has a pass or fail for, the check and its run, by
// Key.ID(). A key with no result is absent. When several runs hold the key (two
// writers), the latest wins. Only checks with a fingerprint are findable.
Lookup(keys []Key) (map[string]Found, error)
// Put stores runs. Writing a key that already has a result adds to it, never rewrites
// it; a reader resolves duplicates as Lookup says.
Put(runs []Run) error
// Runs returns every run stored, each once (a run put again replaces the earlier copy),
// newest first: the history a reader that lists (not looks up) works over. A run with no
// findable check is a run too.
Runs() ([]Run, error)
}
Cache is the whole contract: look keys up, put runs in.
type Check ¶
type Check struct {
Subject string `json:"subject"`
Kind string `json:"kind"`
Status string `json:"status"`
// Fingerprint is the cache key part; only a guard's verdict carries one (its steps inside
// it do not), and what has none is not findable.
Fingerprint string `json:"fingerprint,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
Items []Item `json:"items,omitempty"`
}
Check is one (subject, kind) of a run.
type File ¶
type File struct {
// contains filtered or unexported fields
}
File is a Cache kept as an append-only JSON-lines file of runs: the local, single-machine store until the git-ref store (an orphan branch of content-addressed segments) replaces it behind the same interface. Appending a line is atomic enough for several processes writing at once, and a reader resolves duplicates as Cache.Lookup says.
type GcStats ¶
type GcStats struct {
Records int // latest result per key kept
Duplicates int // superseded results dropped
SegsBefore int
SegsAfter int
Retrained bool
}
GcStats says what a compaction did.
type Item ¶
type Item struct {
Key string `json:"key,omitempty"`
Passed bool `json:"passed"`
Metadata map[string]any `json:"metadata,omitempty"`
}
Item is one finding inside a check (a file a judge named, a citation a prerequisite saw).
type Key ¶
type Key struct {
// Rule is the rule's qualified name (<plugin>/file-guard/<name>).
Rule string `json:"rule"`
// Kind names the check inside the rule: check[1]:judge:./rubric.md.j2.
Kind string `json:"kind"`
// Subject is the unit judged. Today the one subject of a rule's checks is "changeset".
Subject string `json:"subject"`
// Fingerprint is what the verdict is about: the subject's files (path and content) and the
// fingerprint the rule's `subjects:` script gave the subject (changeset.GuardFingerprint).
// Not in it: a commit SHA, a citation, the model, the judge's template, `prepare`'s output.
Fingerprint string `json:"fingerprint"`
}
Key is what a check's result is a fact about.
type Memory ¶
type Memory struct {
// contains filtered or unexported fields
}
Memory is the in-memory Cache.
type Options ¶
type Options struct {
// Dir is any directory inside the repository whose object store holds the ref.
Dir string
// Remote is a remote name or URL the ref is synced with. Empty = local only.
Remote string
// Ref is the local ref that mirrors the branch. Default refs/sloprail/checks.
// CI points it at a ref it fetched (refs/remotes/origin/sloprail/checks).
Ref string
// Branch is the remote ref. Default refs/heads/sloprail/checks.
Branch string
// NoAutoGc stops a Put from compacting the branch when it grows (see maybeGc). For tests
// that count segments or commits.
NoAutoGc bool
}
Options configures a Store.
type Run ¶
type Run struct {
ID string `json:"id"`
RunAt string `json:"run_at"` // fixed-width UTC, so stamps compare as strings
BatchID string `json:"batch,omitempty"`
// Rule is the rule's qualified name; RuleHash its definition's hash when the run was made.
// The hash is recorded for the trace only: a verdict's key never holds it, so a rule's
// logic changing does not void what was judged before.
Rule string `json:"rule"`
RuleHash string `json:"ruleHash"`
BaseRef string `json:"base_ref"`
HeadRef string `json:"head_ref"`
// BaseTree and HeadTree are the tree ids of BaseRef and HeadRef when the run was made
// (absent on a run recorded before they existed): equal trees are an equal net change,
// so a range over the same two trees can read this run's verdict (Store.CachedByTrees).
BaseTree string `json:"base_tree,omitempty"`
HeadTree string `json:"head_tree,omitempty"`
// ExitCode and Error record an ENGINE failure (git, a range that could not be read):
// such a run passes nothing.
ExitCode int `json:"exit_code,omitempty"`
Error string `json:"error,omitempty"`
Complete bool `json:"complete"` // every check it was going to run is stored
Metadata map[string]any `json:"metadata,omitempty"`
// Provenance: who ran it. Never part of a key.
RepoID string `json:"repo_id,omitempty"`
Branch string `json:"branch,omitempty"`
SessionID string `json:"session_id,omitempty"`
AgentID string `json:"agent_id,omitempty"`
Checks []Check `json:"checks"`
}
Run is one rule evaluated once over one commit range.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the git-ref Cache. It implements Cache.
func (*Store) Find ¶
Find returns the latest stored result of every key of a subject, newest first. It reads the local ref only.
func (*Store) FreezeTip ¶
func (s *Store) FreezeTip()
FreezeTip reads the local ref once and answers every later read from that commit and its index, with no git process: for a caller that only reads (`verify`, `show`) and asks hundreds of times. Do not write through a frozen store.
func (*Store) Gc ¶
Gc compacts the branch: one new commit on top of the tip (never a rewrite of the shared history) whose tree holds the latest result of every key and every run (run-only history included, so Runs is the same before and after) in segments of about SegmentTarget runs, with a dictionary trained on the runs when there are TrainMin or more of them, and none (plain zstd) below that. With a remote the commit is pushed like any Put's, as a fast-forward; a push that loses a race is fetched, replayed on the new tip and retried, and one that fails for another reason stays pending (PendingPush).
Gc runs by itself after a Put once the branch holds GcSegments segments, or TrainMin runs with no dictionary yet (see maybeGc); it is also callable on its own.
func (*Store) Lookup ¶
Lookup returns the stored result of each key, by Key.ID(). It reads the local ref only (call Sync first for fresh data); a corrupt segment is an error. sr:invariant cache/store-failures-not-misses
func (*Store) PendingPush ¶
PendingPush is why the last push of local results failed (nil when nothing is waiting). The results are stored locally regardless and pushed on a later Sync or Put.
func (*Store) Pull ¶
Pull brings the remote branch into the local ref, and nothing else: it never pushes and never writes a result. It is what a read-only command (`verify`, `show`) uses. A remote without the branch yet is not an error; only a failed fetch is.
func (*Store) Put ¶
Put stores results as one new segment. The local ref moves first, so a result is never lost to a push that fails (offline, denied): it is pushed best-effort afterwards and retried by the next Sync or Put (PendingPush says why it is waiting). Concurrent writers never conflict: the segment is content-addressed, so on a lost race the store re-bases the same files onto the new tip.
func (*Store) Runs ¶
Runs returns every run the local ref holds, each once, newest first. It reads the local ref only (call Sync first for fresh data); for a test or a reader that lists, not for a lookup.
func (*Store) Sync ¶
Sync brings the remote branch into the local ref and pushes local results the remote does not hold yet (a failed push is retried here). A remote without the branch yet is not an error. It is a no-op for a local-only store. Only a failed FETCH is an error: a push that fails leaves the results local and is reported by PendingPush.