Documentation menu
Concepts

Pipelines and transactions

Knowledge changes in batches: a single piece of work usually creates and updates several related entities at once. Kvendra groups those changes into a transaction (TXN) so they land together or not at all, and records every change in a typed, server-side changelog.

The transaction (TXN) flow

Changes are grouped into a transaction (TXN). The flow: create the TXN; for each phase, create entities carrying the txn id so the server forces them to ‘draft’ status; on success, activate the TXN so the drafts move to their terminal status; on failure, cancel the TXN so the drafts become ‘cancelled’. Before starting, you check for an interrupted TXN to avoid duplicates.

Naming and identifiers

Ids are stable and human-readable. PRJ-<PROJECT>, CMP-<PROJECT>-<COMP> and REL-<PROJECT>[-<COMP>]-<X.Y.Z> accept an explicit id. Every other type gets an auto id of the form <TYPE>-<PROJECT>[-<COMP>]-<6HEX>. Transaction ids look like TXN-<PROJECT>-<YYYYMMDD>-<NNN>. STD entities follow STD-<PROJECT>-<COMP?>-<TOPIC>.

Validation

The engine validates entity ids against the naming rules, validates relation targets, and enforces transaction state. Typical conflicts: a duplicate TXN (resolved by checking for an interrupted TXN first), or operating on an already-cancelled or already-completed TXN (re-read the state).

The lifecycle, step by step

1. txn_create        -> open a transaction, scoped to a project (and optionally a component)
2. entity_create     -> create entities carrying the txn id; the server forces them to "draft"
   entity_update      -> further edits stay inside the same transaction
3a. txn_activate      -> success: every draft moves to its terminal status
3b. txn_cancel        -> failure: every draft becomes "cancelled"

Drafts are invisible to normal reads until the transaction activates, so a half-finished pipeline never pollutes the knowledge base. The terminal status depends on the type: most land on active, an issue on open, a decision on accepted, a run or an environment on recorded.

Why a transaction at all

A single piece of work — planning a feature, fixing a bug, cutting a release — rarely touches one entity. It creates a requirement, drafts the tests, updates the component, links them together. The transaction makes that an atomic, multi-entity change-set: every draft in the transaction commits on txn_activate, or none of them do on txn_cancel. There is no intermediate state where half the entities are real and half are not.

That is also where the resilience comes from. If a pipeline fails partway through — an agent stops, a step errors — the work so far is sitting in draft, quarantined from every normal read. Nothing leaks into the knowledge base until the transaction is deliberately activated, so a crashed pipeline leaves the KB exactly as it was, not subtly corrupted.

A walkthrough: from idea to release

The lifecycle is easiest to see on a real arc of work. Suppose a developer asks their agent to plan a new export feature for the knowledge base.

  1. Plan. The developer says "let's plan a clean JSON export". The agent reaches for a planning skill, which opens a transaction and drafts a ROADMAP (a ROAD entity) describing the goal and the slices it breaks into. Activating that transaction promotes the roadmap from draft to active: now it is real, and the rest of the work has something to hang off.
  2. Specify. As the first slice starts, another transaction drafts a requirement — a REQ entity — capturing exactly what "clean export" means, linked back to the roadmap with a typed part_of relation. It lands active on activation.
  3. Build, and hit a snag. Mid-implementation the agent finds that nested entities double-encode. It opens a transaction and drafts an ISSUE (which lands in status open), related to the REQ it affects. The fix and the issue travel in the same change-set, so the record of the problem and its resolution stay together.
  4. Prove it. Before calling the slice done, a tester skill records one or more TEST entities — preconditions, steps, expected output, evidence — linked to the REQ they verify. Now the requirement is not just written down, it is demonstrably met.
  5. Release. Finally a release skill cuts a REL entity that groups the slice's changes. Because every REQ, ISSUE and TEST is connected in the graph and recorded in history, the release ships with an automatic changelog — assembled from what actually changed, not hand-written after the fact.

Two properties make this trustworthy. Every multi-entity step is atomic: the whole step activates together or, if anything goes wrong, the transaction is cancelled and none of its drafts ever existed for a normal reader. And the combination of the typed relation graph and the server-side history leaves a complete trace of how the roadmap became a release — which requirement drove which change, which issue it fixed, which test proved it — without anyone having to remember to write it down.

Concurrent writers: optimistic locking

When two writers touch the same entity at once, Kvendra does not silently take the last write. Each update carries the expected_version it was based on — a compare-and-set check. If the entity has moved on since, the write is rejected as a conflict, and the response hands back the intervening changes so the caller can reconcile against what actually happened rather than clobber it. Optimistic, because writes are not blocked up front; they are validated at commit. The effect is that concurrent work — say, the owner editing the same project in another session — cannot be lost to a blind overwrite.

The typed changelog

Activation is also what populates the audit trail. Every create and update is recorded by the engine itself — not by the caller — as a versioned entity_history entry carrying the author, the trigger (often the transaction id), and a change summary of who changed what, when, and why. Because history is written server-side, it cannot be skipped, back-dated or rewritten by a client, which is exactly what makes the audit trail trustworthy: the changelog is a consequence of committing, not a courtesy the caller has to remember.

This is the other half of the entity model. The typed relations tell you how things connect; the history tells you how they got that way. Together they let you trace any entity both across the graph and back through time.

Avoiding duplicates

Before starting, a pipeline checks for an interrupted transaction so it does not open a duplicate. Operating on a transaction that is already cancelled or already completed is reported as a conflict, prompting the caller to re-read the current state rather than guess.