Terms bundle (.terms.json)
A terms bundle is the portable, lossless serialization of a project's terminology: the concepts a project has agreed on, their terms per locale, and the relations between them.
A terms bundle is an artifact: the shape a project's terms take when they
leave the project store, and the shape they are read back
from. kapi terms export writes one, kapi store import reads one, and
the store is what every lookup and every gate answers from.
Implemented in Go at
terms/ktb.
When kapi writes one
kapi terms export --format bundle: the explicit export.kapi pack: as theterms.jsonmember inside a.kpz.
kapi terms import accepts csv, tsv, json, tbx, or bundle. Only
bundle is lossless; the rest are covered under
what the other formats drop.
Where an import looks for one
A flow that needs a project's terms reads the project's terms store; no flow
opens a bundle. kapi store import reads
one, and with no path it looks at <project-root>/.kapi/terms.json, the
conventional place beside the rest of a project's context files. Name a
directory, kapi store import context, to read the terms.json in it. A
recipe has no key for a terms file.
Content memory has no single conventional file
A project has one terms bundle, so one conventional filename works. A project
can hold several content-memory bundles; this repository's own dogfood recipe
keeps one per surface. An import therefore reads every bundle in
.kapi/memory/.
Envelope
{
"schemaVersion": "1.0",
"kind": "kapi-terms",
"created": "2026-07-26T09:12:04Z",
"generator": { "id": "kapi", "version": "1.2.3" },
"concepts": [],
"relations": []
}
| Field | Type | Notes |
|---|---|---|
schemaVersion | string | MAJOR.MINOR. Unknown major rejected; unknown minor of a known major accepted. |
kind | string | Always kapi-terms. |
created | string | RFC 3339. Optional. |
generator | object | {id, version}. Optional. |
concepts | array | The concepts. Always present. |
relations | array | Relations between concepts. Omitted entirely when there are none. |
concepts and relations reuse the terms store's own types directly rather
than a parallel wire struct, so the serialization cannot drift from the model
it serializes.
Determinism
Marshal produces byte-stable output: concepts and relations sorted by id,
terms within a concept sorted, timestamps normalized to UTC, HTML escaping off,
two-space indent, trailing newline. Two exports of the same store are the same
bytes, so a bundle diffs cleanly and can be hashed as a whole, which is what
lets it sit inside a .kpz under
the package's Merkle root.
What the other formats drop
kapi terms export also emits csv, json, and tbx (tsv is accepted on
import only). All three are lossy, and the JSON one is the easiest to mistake
for the bundle because both are JSON:
--format jsonwrites{name, version, concepts}and nothing else. It carries no relations at all, and its envelope has nokindmarker, so it cannot be sniffed, and a reader cannot tell which schema it is holding. It is a convenience export for spreadsheets and scripts.--format tbxis the industry interchange format. It has no representation for the cross-links that make a kapi concept useful inside kapi, most notably the entity cross-references content memory uses.--format csvis flat: one row per term, no concept structure and no relations.
Reach for bundle whenever the file is staying inside kapi.
What the bundle leaves out
- The store's own tables. A bundle carries the concepts and relations; the terms tables in the project store are what an import writes them into.
- Match state. Which blocks a term was found in is a check result;
kapi terms occurrencesreads it from the project's block cache.
See also
- Overview: store, bundle, and exported artifact.
- Content memory: the peer bundle.
- Choosing a format: native versus interchange.