store

package
v2.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package store defines the two ports the engine is built against (ManifestStore and OperationalStore) and the record shapes they persist. The SQLite adapter (package sqlite) and the in-memory adapter (package memory) both satisfy these interfaces and are proven equivalent by the conformance suite in package conformance.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoDocument = errors.New("no such document")

ErrNoDocument is returned for a document id or a manifest that has no shared document.

Functions

This section is empty.

Types

type ApplyFile

type ApplyFile struct {
	Path    string // Path relative to vault root (e.g., "vault.yaml" or "Goal/some-goal.yaml")
	SHA256  string // SHA-256 hash of content (hex, 64 chars)
	Content []byte // File content
}

ApplyFile is one file write within an ApplyUnit: path relative to vault root, sha256 hash of content (for idempotence during replay), and the content itself.

type ApplyJournal

type ApplyJournal interface {
	// PutUnit writes a new unit with applied=false.
	PutUnit(ctx context.Context, unit ApplyUnit) error

	// MarkApplied marks a unit as applied and updates the index. Called in the
	// same transaction as index updates (rebuild, rehydration, reference indexing).
	MarkApplied(ctx context.Context, unitID string) error

	// ListUnapplied returns every unit with applied=false, oldest first.
	ListUnapplied(ctx context.Context) ([]ApplyUnit, error)

	// PruneOld removes applied units older than the given count (keeps the most
	// recent `keepCount` applied units). A keepCount of 0 or less means unlimited.
	PruneOld(ctx context.Context, keepCount int) error

	// WithinTransaction runs fn against the journal in a transaction (adapters
	// that support transactions). Idempotent with database adapters.
	WithinTransaction(ctx context.Context, fn func(ctx context.Context, tx ApplyJournal) error) error
}

ApplyJournal records every mutation (apply, exclude, recover, etc.) with a two-phase commit: write the unit (applied=false), commit files to disk, then mark applied and update the index in one transaction.

type ApplyUnit

type ApplyUnit struct {
	ID       string      // Unique identifier for this unit (uuid)
	On       time.Time   // When the mutation happened
	Operator string      // Who performed it (actor, "local" for file operations)
	Reason   string      // Why (for exclusion, recovery, or user-supplied reason)
	Files    []ApplyFile // Files to write: {path, sha256, content}
	Applied  bool        // Whether this unit was applied (committed to files and index)
}

ApplyUnit is one mutation: a set of file writes recorded first with applied=false, then files written (temp file and rename, vault.yaml last), then marked applied and the index updated in one transaction. On open, replay every unit not applied.

type Bundle

type Bundle struct {
	Location string
	Paths    map[string]string
}

Bundle is where a handoff's files went. Location is what the state history records: a path relative to the vault root for the vault adapter, an opaque name for others. Paths maps each file name to where a person can find it, for the command line to print.

type BundleStore

type BundleStore interface {
	// PutBundle stores files (name to content) for one project at one
	// version, all or nothing, and returns where they went.
	PutBundle(ctx context.Context, projectID string, version int, files map[string][]byte) (Bundle, error)
}

BundleStore keeps the files a handoff produces (the rendered charter in its formats) beside the version they were rendered from. The vault adapter writes them under .cartograph/handoff; the memory adapter keeps them in memory, which is what tests need.

type Chunk

type Chunk struct {
	Seq  int64
	Data []byte
}

Chunk is one saved piece of a shared document: incremental changes, appended in the order the store received them.

type ConflictError

type ConflictError struct {
	Kind   string
	ID     string
	Ours   []byte
	Theirs []byte
}

ConflictError is returned by PutVersion when a manifest's file on disk has changed since it was last read, indicating concurrent edits that need reconciliation. Both Ours (the version being written) and Theirs (the current file on disk) carry the full YAML text.

func (*ConflictError) Error

func (e *ConflictError) Error() string

type DocStore

type DocStore interface {
	// Create records a new document for a manifest with its first
	// snapshot, unless the manifest already has one. created is false,
	// and docID the existing document's, when another writer got there
	// first; the caller then loads that one and discards its own.
	Create(ctx context.Context, kind, id, docID string, snapshot []byte) (existing string, created bool, err error)
	// DocumentFor returns the document id of a manifest's draft, or
	// ErrNoDocument.
	DocumentFor(ctx context.Context, kind, id string) (string, error)
	// ManifestFor returns the manifest a document belongs to, or
	// ErrNoDocument.
	ManifestFor(ctx context.Context, docID string) (kind, id string, err error)
	// Append stores a chunk and returns its sequence number, which is
	// greater than every earlier one for the document.
	Append(ctx context.Context, docID string, data []byte) (int64, error)
	// Load returns the snapshot and every chunk since, oldest first.
	Load(ctx context.Context, docID string) (snapshot []byte, chunks []Chunk, err error)
	// Since returns the chunks after seq, oldest first; always non-nil.
	Since(ctx context.Context, docID string, after int64) ([]Chunk, error)
	// Compact replaces the snapshot with one that includes every chunk
	// up to and including upTo, and drops those chunks. Chunks appended
	// after upTo, even while compacting, are kept.
	Compact(ctx context.Context, docID string, snapshot []byte, upTo int64) error
}

DocStore keeps the shared draft of each manifest as a CRDT document (docs/adr/0007): a compacted snapshot plus the chunks appended since. Loading the snapshot and every chunk, in any order, reproduces the document, so concurrent appends from different replicas never need arbitration. It holds bytes only; what they mean is the crdt port's business.

The document is not the record of truth; versions are. It is the draft between them, and it carries on across versions.

type Exclusion

type Exclusion struct {
	Kind     string    // Manifest kind
	ID       string    // Manifest id
	Name     string    // Manifest name at time of exclusion
	On       time.Time // When excluded
	Reason   string    // Why excluded
	Operator string    // Who excluded it
}

Exclusion is one manifest that has been excluded from the live state.

type ManifestDraft

type ManifestDraft struct {
	Kind string
	ID   string
	YAML []byte
	On   time.Time
}

ManifestDraft is a manifest's working copy: the YAML as last saved with the draft header, never validated beyond its basic shape, never versioned. Keyed by Kind and ID (generalised from the Project-only project_draft table).

type ManifestStore

type ManifestStore interface {
	// PutVersion appends a new version. The caller is responsible for
	// computing Number as one more than the current version (or 1 for a
	// new manifest); an adapter rejects a Number that is not exactly that,
	// which is what guards immutability under concurrent writers.
	PutVersion(ctx context.Context, v Version) error

	// GetCurrent returns the highest-numbered version of a manifest.
	GetCurrent(ctx context.Context, kind, id string) (Version, bool, error)

	// GetVersion returns one specific historical version.
	GetVersion(ctx context.Context, kind, id string, number int) (Version, bool, error)

	// ListVersions returns every version of a manifest, oldest first.
	ListVersions(ctx context.Context, kind, id string) ([]Version, error)

	// ListAllVersions returns every version of every manifest across all
	// kinds, newest first, with optional pagination (limit defaults to 20,
	// cursor is opaque and returned by the preceding call). Used by the
	// snapshots list endpoint.
	ListAllVersions(ctx context.Context, limit int, cursor string) (versions []Version, nextCursor string, err error)

	// ListSummaries returns the current version of every manifest of a
	// kind, optionally filtered by a case-insensitive substring match over
	// id and name (query, ignored when empty) and by zero or more
	// reference filters (refs; a manifest must have a current outgoing
	// reference matching every one of them - logical AND, ignored when
	// empty). Always returns a non-nil, possibly empty, slice.
	ListSummaries(ctx context.Context, kind, query string, refs []RefFilter) ([]Summary, error)

	// ListIDs returns the id of every manifest of a kind that has a
	// current version.
	ListIDs(ctx context.Context, kind string) ([]string, error)

	// Counts returns the number of manifests (by id, not by version) for
	// every kind that has at least one.
	Counts(ctx context.Context) (map[string]int, error)

	// IndexReferences replaces the recorded outgoing references for one
	// manifest's current version. The engine calls this once per commit,
	// right after PutVersion, inside the same transaction, with every
	// x-cartograph-ref value found by walking that kind's schema.
	IndexReferences(ctx context.Context, fromKind, fromID string, refs []Ref) error

	// ListReferencedBy returns every outgoing reference recorded for one
	// manifest's current version.
	ListReferencedBy(ctx context.Context, fromKind, fromID string) ([]Ref, error)

	// ListReferencing returns the current summary of every manifest that
	// has an outgoing reference to toKind/toID.
	ListReferencing(ctx context.Context, toKind, toID string) ([]Summary, error)

	// PutWorking writes a manifest's YAML as the working copy without creating
	// a version. Used for autosave writes that don't create an explicit
	// snapshot. The working copy is updated, but no version entry is created.
	PutWorking(ctx context.Context, kind, id string, yamlBytes []byte) error

	// GetWorking returns a manifest's working copy (the file on disk that may
	// not yet be versioned). Returns found=false when no working copy exists.
	GetWorking(ctx context.Context, kind, id string) ([]byte, bool, error)

	// WithinTransaction runs fn against a ManifestStore that commits all of
	// its writes together, or none of them if fn returns an error.
	WithinTransaction(ctx context.Context, fn func(ctx context.Context, tx ManifestStore) error) error

	// Exclude removes a manifest from vault.yaml and records the exclusion.
	// The file remains on disk. Returns an error if the manifest does not exist
	// (use GetCurrent to check first). Refused if the manifest is referenced by
	// a live manifest (call ListReferencing first).
	Exclude(ctx context.Context, kind, id, name, reason, operator string) error

	// ListExcluded returns every excluded manifest, newest first.
	ListExcluded(ctx context.Context) ([]Exclusion, error)

	// Recover re-adds an excluded manifest to vault.yaml and removes the
	// exclusion record. Returns an error if the file is missing from disk.
	Recover(ctx context.Context, kind, id, reason, operator string) error
}

ManifestStore holds every manifest version ever committed. Rows are immutable: once written, a version is never updated or deleted.

type NotFoundError

type NotFoundError struct {
	Kind string
	ID   string
}

ErrNotFound is returned by Get-style methods is expressed with the bool "found" return instead of a sentinel error, to keep call sites simple; this type exists only for adapters that need a concrete not-found error internally (for example inside a transaction helper).

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

type OperationalStore

type OperationalStore interface {
	// PutProjectStateTransition appends one row to a project's state
	// history (the table is append-only; the current state is the most
	// recent row). A project with no rows is implicitly "draft".
	PutProjectStateTransition(ctx context.Context, e ProjectStateEntry) error

	// ListProjectStateHistory returns every state transition for a
	// project, oldest first. Empty (never nil) when the project has never
	// moved out of the implicit "draft" state.
	ListProjectStateHistory(ctx context.Context, projectID string) ([]ProjectStateEntry, error)
}

OperationalStore holds facts about manifests: project state history and project drafts. Later increments add readings and data-quality check results.

type ProjectStateEntry

type ProjectStateEntry struct {
	ProjectID string
	State     string
	Actor     string
	Reason    string
	On        time.Time
	Snapshot  int
	Bundle    string
}

ProjectStateEntry is one row of a project's state history: at On, Actor moved the project to State, giving Reason (required only for a move to cancelled; empty otherwise). Snapshot and Bundle are set when state is handed off: Snapshot is the version number, Bundle is the directory path where the charter bundle was written (relative to vault root).

type Ref

type Ref struct {
	Path   string
	ToKind string
	ToID   string
}

Ref is one outgoing reference recorded for a manifest: at Path (a JSON pointer within it) it names ToKind/ToID.

type RefFilter

type RefFilter struct {
	Kind string
	ID   string
}

RefFilter narrows ListSummaries to manifests with an outgoing reference to Kind/ID.

type StateStore

type StateStore interface {
	// Name is the state manifest's own id (metadata.id).
	Name() string

	// Included returns every ref ("Kind/id") the state manifest includes,
	// in the order it lists them.
	Included(ctx context.Context) ([]string, error)

	// ListUnapplied returns every manifest the adapter can see that is
	// neither included nor excluded: what a person could apply next.
	ListUnapplied(ctx context.Context) ([]UnappliedRef, error)

	// Apply includes the given refs. Applying a ref that is already
	// included is quiet; a ref the adapter cannot find is refused with
	// an error that names it. Returns how many refs were newly included.
	Apply(ctx context.Context, refs []string) (added int, err error)

	// StateYAML returns the state manifest's own bytes, or found=false
	// when the adapter has not written one yet.
	StateYAML(ctx context.Context) (yaml []byte, found bool, err error)

	// Rehydrate reloads the live state from the adapter's source of
	// truth. Called after Apply or Recover changed what is included.
	Rehydrate(ctx context.Context) error
}

StateStore is the apply gate: an adapter keeps a state manifest (an include list) over manifests that can exist before they are included. The vault adapter implements it over vault.yaml. A database adapter where every row is live has no need for it.

type Summary

type Summary struct {
	Kind      string
	ID        string
	Name      string
	Version   int
	UpdatedOn time.Time

	// Labels is metadata.labels, carried on the summary so a list can be
	// grouped and filtered by them. Nil where the manifest sets none.
	Labels map[string]string

	// Draft is true only for a synthetic Summary standing in for a
	// project that has only ever been saved as a draft, never committed
	// (includeDrafts=true): Version is then 0 and UpdatedOn is the
	// draft's own save time. False (the zero value) for every ordinary,
	// committed Summary a store adapter's own ListSummaries builds.
	Draft bool
}

Summary is the list-view projection of a manifest's current version.

type UnappliedRef

type UnappliedRef struct {
	Kind string
	ID   string
	Name string
}

UnappliedRef is one manifest that could be applied.

type VaultIndex

type VaultIndex interface {
	ManifestStore                  // versions, summaries, references, working copies, exclusions
	Journal() ApplyJournal         // the two-phase apply journal
	Operational() OperationalStore // project state history (same database)
	Docs() DocStore                // the shared drafts' CRDT documents (same database)

	// File hashes: what the vault compares a file against on open and on a watcher event.
	GetFileHash(ctx context.Context, kind, id string) (sha256 string, found bool, err error)
	PutFileHash(ctx context.Context, kind, id, sha256, mtime string) error
	DeleteFileHash(ctx context.Context, kind, id string) error

	// Meta: small key/value facts about the index itself (the recorded vault.yaml hash, rebuild time).
	GetMeta(ctx context.Context, key string) (value string, found bool, err error)
	PutMeta(ctx context.Context, key, value string) error

	Close() error
}

VaultIndex is everything the vault adapter keeps beside its files: the rebuildable cache a database holds for it. SQLite implements it today; any adapter that passes conformance.RunVaultIndex may.

type Version

type Version struct {
	Kind   string
	ID     string
	Number int
	YAML   []byte
	Actor  string
	Reason string
	On     time.Time
}

Version is one immutable, committed version of a manifest.

Directories

Path Synopsis
Package conformance is the one test suite every store adapter must pass, proving the SQLite, in-memory and Postgres adapters are interchangeable behind the store ports.
Package conformance is the one test suite every store adapter must pass, proving the SQLite, in-memory and Postgres adapters are interchangeable behind the store ports.
Package manifestmeta reads metadata.name and metadata.labels out of a manifest's text, for the store adapters that index both beside each version.
Package manifestmeta reads metadata.name and metadata.labels out of a manifest's text, for the store adapters that index both beside each version.
Package memory is the in-memory ManifestStore and OperationalStore adapter used by tests and by the conformance suite.
Package memory is the in-memory ManifestStore and OperationalStore adapter used by tests and by the conformance suite.
Package postgres is the Postgres adapter for the ports a stateless deployment needs (docs/adr/0008): store.ManifestStore, store.OperationalStore, store.BundleStore and store.DocStore, over one database through pgx, a pure-Go driver, so the binary stays CGO_ENABLED=0.
Package postgres is the Postgres adapter for the ports a stateless deployment needs (docs/adr/0008): store.ManifestStore, store.OperationalStore, store.BundleStore and store.DocStore, over one database through pgx, a pure-Go driver, so the binary stays CGO_ENABLED=0.
Package sqlite is the SQLite adapter for both store.ManifestStore and store.OperationalStore, backed by modernc.org/sqlite (a pure-Go driver, no cgo).
Package sqlite is the SQLite adapter for both store.ManifestStore and store.OperationalStore, backed by modernc.org/sqlite (a pure-Go driver, no cgo).
Package vault implements store.ManifestStore over a directory tree: files are the source of truth.
Package vault implements store.ManifestStore over a directory tree: files are the source of truth.

Jump to

Keyboard shortcuts

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