Gå til hovedinnhold

kapi serialization formats

These are the formats kapi writes for itself: the on-disk shapes it uses to move its own content, memory, terminology, annotations and decisions between commands, machines and people.

They are distinct from the formats kapi reads and writes on your behalf (JSON catalogs, DOCX, Markdown, strings.xml and the rest), which are covered in the format reference.

Three kinds of artifact​

A file in this family plays one of three roles.

A projection is derived working state. .kapi/work/store.db holds what kapi read out of the working tree: the block cache, the overlays a run wrote, the skeleton and parse caches beside it. It is fast, indexed, mutable and rebuildable from the content files.

A context store is authored state. The project's own store, in a workspace outside the checkout, holds the content memory, the terms, the voice profiles and the decision ledger. Every checkout of the project reads it, nothing rebuilds it, and kapi store export is its backup. See the project store.

A bundle is a portable serialization. One reviewable, hashable, diff-able file carrying the same content losslessly: a content bundle, a content-memory bundle, a terms bundle, a voice profile, a project block-state record, or a whole project archive. Bundles move: they are what you hand over, ship, or put in a pull request.

How bundles and the store relate​

Bundles serialize authored context for exchange and review. kapi terms export and kapi memory export write them, and kapi store import <dir> reads a directory of them. The context itself moves between machines as operations, through a context backend or a transfer file. .kapi/ is ignored by default; adjust its ignore rules if you choose it as the snapshot directory.

The formats​

FormatSuffixkindWhat it carries
Content bundle.kbf.jsonkapi-bundleExtracted blocks and their per-locale targets
Content memory.memory.jsonkapi-memoryMultilingual entries, entities, provenance
Terms.terms.jsonkapi-termsConcepts and their relations
Annotation sidecar.overlays.jsonlheader annotationTypeStand-off annotations anchored into runs
Overlay set.overlays.jsonkapi-overlay-setIn-progress workspace overlay layers
Project archive.kpzkapi-project, kapi-interchangeA whole project, or one interchange task
Project state.jsonl (under .kapi/state/)kapi-stateReview and workflow decisions
Voice profile.yaml(none)Tone, style, patterns, examples

Not sure which you want? Choosing a format.

The compound suffix convention​

Every one of these files except the project archive and the voice profile is JSON or JSON Lines, and each portable one keeps a marker segment ahead of the serialization suffix: .kbf.json, .terms.json, .overlays.jsonl. The state shards are the exception: they never travel alone, their names are derived from document keys, and the directory holding them (.kapi/state/) already says what they are.

The .json tail keeps jq, GitHub, editors and syntax highlighting treating the file as what it is. The marker segment says which JSON it is, so a directory listing stays readable and a format can be recognised before parsing.

The compound suffix has one cost: the standard library's filepath.Ext reports .json for every one of them. Extension-driven code goes through the helpers in core/format (Ext, TrimExt, Stem, HasExt), which return the most specific match. A naive filepath.Ext on en-US.kbf.json yields .json, and trimming that leaves a stray .kbf behind.

One version contract, applied per format​

Every format in the family carries a schemaVersion as MAJOR.MINOR and follows the same forward-compatibility rule:

Reject an unknown major. Accept unknown minors of a known major.

Strict on major, best-effort on minor. A file from a future major version is refused with a message naming the version the build speaks.

The rule is the same for every format, but each format implements the check separately, and the overlay set inside a project archive carries its own schemaVersion, versioned independently of the enclosing package.

Two ways a file says what it is​

A file identifies itself in two ways, which answer different questions:

  • kind: the magic string on the document root. Answers what schema is this?, works on content alone, and survives a rename. It is what sniffing uses.
  • The extension: answers what should I expect? before the file is opened, which is what lets a command route a path without reading it.

The two are checked independently, so a file that is not what it claims to be is caught by whichever mechanism notices first: a wrong kind by the reader that owns the envelope, a wrong extension by the command that had to choose a reader before opening anything.

Import refuses a file it cannot identify​

kapi memory import and kapi terms import resolve --format auto from the extension, and they refuse to guess, because a wrong guess is silent. The TMX reader handed a file with no <tu> element returns zero entries, and the CSV reader skips every row it cannot turn into a concept, so a file the importer never actually read reports Imported 0 entries and exits 0, which is indistinguishable from an empty content memory.

An extension the command does not recognise is therefore an error, and one that names the file, the extensions that are supported, and the --format flag that settles the question:

cannot identify seed.dat: ".dat" is not a content-memory extension
(expected .memory.json, .tmx or .tmx.gz); pass --format (bundle, tmx) to say what it is

Export works the other way round. An output path names what the caller wants written, and stdout carries no extension at all, so there auto falls back to the interchange default.

See also​