Gå til hovedinnhold

The project store

A kapi project has three things, and it helps to keep them straight:

  • the recipe (kapi.yaml), your config: content, governance bindings, flows, defaults, languages;
  • the files, your deliverable: the source and target files on disk;
  • the project store, your work: what kapi derives under .kapi/work/ while you build, plus the committed record of the reviews you make along the way.

This page is about the third one. The guiding rule:

Files and the committed context are the source of truth. .kapi/work/store.db is a derived index over them, plus the unit state you have staged but not yet committed.

What's in the store

LayerLives inKindLose it?
committed context.kapi/: the terms and memory bundles, the voice profile, and state/*.jsonlauthored: unit state, approved terms, memory pairscommitted; keep it in git
local store.kapi/work/store.dbderived index over the committed context and your content, plus the working set of staged unit staterebuilds, except unit state staged since the last kapi commit
cache.kapi/work/cache/derived: parse cache, extractions, collections, sync cacherebuilds on next run

A project keeps one local database. .kapi/work/store.db holds every subsystem's tables in a single SQLite file, on a single connection, each with its own migration ledger: the block cache, the terms store, the content memory, the voice store, the unit-state working set, and the property graph (graph_nodes / graph_edges) that links a term to the blocks that use it, a state record to the unit it blesses, and both to their context coordinates.

It sits at the top of .kapi/work/ rather than under cache/, and the distinction matters: rm -rf .kapi/work/cache is completely free, while store.db carries the working set. Between a review landing and kapi commit, that working set holds the only copy of the staged unit state. Delete store.db and you lose at most what you staged since the last commit; everything else is rebuilt from the committed context and your files.

The committed side is plain text, and it is what a person reads in a pull request:

  • .kapi/state/*.jsonl: the unit-state record, one JSON Lines shard per document (serialization);
  • the terms bundle the recipe binds with defaults.terms_source (a .terms.json) and the content-memory bundle it binds with defaults.memory_source (a .memory.json), conventionally .kapi/terms.json and .kapi/memory/memory.json, though both keys bind any path;
  • the voice profile, .kapi/voice.yaml;
  • the content files themselves.

What to commit, and what to ignore

.kapi/ is committed. It is your project's context, and it belongs in review like the rest of your source. The one thing kapi keeps out of git is .kapi/work/, where everything it derives goes, which makes the ignore rule two lines, with no exceptions to remember:

/.kapi/work/
/.kapi/filters.local.json

The second line is your personal filter overlay: your own reader settings, not the project's.

Deleting is worth being precise about:

  • rm -rf .kapi/work/cache is always safe. Everything under it is rebuilt on the next run.
  • rm -rf .kapi/work costs two things. Any unit state you staged since the last kapi commit lives only in store.db. And if you use redaction, the vault under .kapi/work/vault/ holds the withheld originals, deliberately local-only, never committed and never synced, so no copy exists anywhere else. Merge the batches you have in flight before you clear it.

One local context graph

Because the graph tables live beside the block, terms and memory tables, a project answers graph questions without a server: which blocks use this term, in which collection, at which coordinate. A connected project answers the same query shapes server-side across more dimensions; locally they are fixed to a single value. See C-03: The Local Context Graph Store.

Two stages: working, then ship

The store also explains the two stages of a run:

  1. Working: kapi run (with no -o, in a project) lands its translations as overlays in the block tables of store.db. The work is in the store and not yet in your files.
  2. Ship: kapi merge writes those overlays out to the target files; kapi status then reads the files (plus the unit-state record) for the real ship state, the kapi loop's ladders and gates.

So coverage has two readings: "what's translated in the store" (working, pre-merge) and "what's in the files and reviewed" (ship). In Kapi Desktop, the project home's Content axis is the working reading; kapi status reports the ship reading.

Unit state is staged, then committed

Approving a translation writes unit state, and publishing it is explicit, like a git commit. When you approve a unit (see Review), the state record lands in the working set inside store.db. kapi status reports what is pending, and kapi commit materializes the working set into .kapi/state/, where you commit it with your sources. The record is bound to the content hash of the translation it blesses, so editing that translation later drops the unit back below reviewed; an approval can't silently outlive the text it approved.

kapi status --review # the worklist: units awaiting approval
kapi apply <<<'{"kind":"review","file":"src/nb.json","id":"save.label","locale":"nb","status":"reviewed"}'
kapi status # staged unit state, not yet committed
kapi commit # → .kapi/state/*.jsonl, ready for git

The same model maps to a server-backed project: with the server plugin installed, instead of committing shards it pushes its unit state to a remote (kapi push) and pulls it back (kapi pull), the same record with a different sink.

An empty subsystem is an absent store

Presence is table-level. A store.db whose terms tables are empty behaves exactly as though there were no terms store at all: on a fresh checkout the terminology gate reads the committed .terms.json directly, kapi up --plan never creates state, and kapi pack carries only the parts that hold something. The file's presence conjures nothing into existence.

In the browser, the same model runs without SQLite: the memory and terms backends are in-memory, blocks are keyed by path in memory, and the working set persists to a JSON sidecar at .kapi/work/store.json.

What the store is not

  • It is not the content memory. The .memory.json bundle (defaults.memory_source) is a recycle corpus: kapi apply with kind:"memory" adds leverage for future translation and records no review. Review state lives in the unit-state record. See Memory & terms storage.
  • It is not authoritative for your translations; the files are. The store indexes and stages; the deliverable is the source of truth.

See also