Guiding Principles & Invariants
Gonzalo's design philosophy is otherwise recoverable only by reading the full ADR log and the founding design spec. This page synthesizes that philosophy into one place: the guiding principles that shape the system and the inviolable invariants it must never break. It complements — it does not replace — the ADRs; each item cites the ADR(s) it derives from, and should be kept in sync when those are superseded.
Guiding principles
- One uniform
Record, one genericStore("Approach A"). Every domain type is a serde view overRecord { key, kind, revision, parent, body, meta, links }; versioning, concurrency, conflict, and sync are written once in the core (ADR 0002). - Substrate pluggability — the backend is configuration, not API.
fs/git/S3/daemon-client each implement only
Store; moving from local to git/S3/daemon is a config change, not a code change; fs is the zero-dependency default (ADR 0004, 0009). - Optimistic concurrency with explicit, typed conflict surfacing.
put(record, expected_parent_rev); a stale parent yieldsPutResult::Conflict(recoverable, not an error); merge is keyed byRecordKind, andSyncreuses the exact same machinery (ADR 0005). - Layering discipline — capabilities compose over a storage-only core.
Vector, graph, tickets, and knowledge are added as layers, each keyed by
RecordKey; substrates never know about vectors or graphs (ADR 0008, 0010, 0011, 0012). - Minimal-core-touch for new capabilities. A new layer may register a
RecordKindat most — no new core traits or types enter (ADR 0010, 0011). This aligns with the rule of three for promoting anything into the core. - Provider-agnostic boundaries via traits. Embedding goes through
Embedder, tickets throughTicketSource; provider divergence is handled bycapabilities()negotiation, neverif provider == …branches (ADR 0008, 0010). - The conformance suite is the executable contract. One shared conformance
suite that every
Storeimplementation must pass (ADR 0006). - A single canonical schema behind dual transports. gRPC (tonic) and
HTTP/JSON (axum) sit over one service layer;
gonzalo-protois the single schema, so the transports cannot drift (ADR 0007). - Single-facade public surface + workspace discipline. Caliban depends on
one crate — the
gonzalofacade; substrates and layers are toggled by Cargo feature (ADR 0009). That surface is grouped by domain: the root holds the record and store core and nothing else, and every domain type lives under the module naming its layer —gonzalo::ticket::State,gonzalo::fleet::Person— so gonzalo's nouns cannot collide with a consumer's own, and two layers may use the same word (ADR 0026). - Normalize on the shared spine, preserve the raw losslessly. Tickets
normalize to
State { category, resolution, raw_name, raw_id }plus a boundedfieldsmap; the status signal is configured per connection, not hard-coded per provider (ADR 0010). - Separate storage identity from query identity. The code graph keys slices by content + grammar hash (which dedups) and resolves them through a per-worktree manifest — git's blob/tree split (ADR 0012).
- Retrieval returns first-class records, never bare ids. Vector, graph,
knowledge, and ticket queries resolve back through the
Storeto whole records (ADR 0008, 0011). - Rust-native deliverables; the daemon is the non-Rust boundary. We ship
Rust crates, the
gonzalodimage, and binaries of our own binary crates — not client SDKs in other languages. Non-Rust consumers integrate over the daemon and its published schema, which ADR 0007 already keeps from drifting (ADR 0020). The schema is published as artifacts, not just promised: the.protoand an OpenAPI document are attached to each release, so a client is generated and owned by its consumer rather than maintained here. - Deletion is a write, not an absence. A delete writes a tombstone that
syncand gitpullreplicate like any other record, so a delete made on one store takes effect on its peers instead of being undone by the next sync. Reclaiming the space is a separate, deliberate step (ADR 0021). - Derived bytes are swept by marking from the records, never from a caller. Blobs are shared, so nothing frees one implicitly. GC builds its mark set from the store's own records — record bodies, the blob a tombstone pins, a graph manifest's slices, a vector manifest's shards, and the retained body of a manifest tombstone — and a caller cannot narrow it (ADR 0024, ADR 0029). The sweep also spares anything younger than a grace period, because a blob is uploaded before the record that references it commits (ADR 0028).
- Gonzalo stores; it does not decide. The fleet access-control records
hold identity, role grants and channel configuration, and gonzalo never
evaluates a role in them — the consumer reads them and decides.
gonzalodkeeps authorizing its own API by ADR 0015's token principal, which is unrelated toFleetRole(ADR 0022, ADR 0023). - Declare what cannot be verified, and fail loudly on a mismatch. Gonzalo never runs the embedder behind a caller's vectors, so a durable vector index stores the embedding space and dimension it was opened with and errors naming both values when a later open disagrees. Configuration drift is caught once, at startup, instead of silently skewing every query after (ADR 0027).
Inviolable invariants
- Concurrent edits are never silently lost — the core invariant. A
stale-parent write MUST return
Conflict, never overwrite; ambiguous merges MUST surface (ADR 0005). - Conflict is a typed, recoverable result —
PutResult::Conflict, never collapsed intoGonzaloError(ADR 0005). - All persistence funnels through the one
Record/Store— no parallel typed store re-implements versioning (ADR 0002). - Substrates implement only the generic
Storeand stay type-blind — no substrate-specific escape hatches (ADR 0004, 0008). - Every
Storeimplementation must pass the shared conformance suite (ADR 0006). - Capability layers never bypass or mutate the core — a layer may register
a
RecordKind+ merge class at most (ADR 0008, 0010, 0011). Syncreuses the exact local-write conflict/merge machinery — anyStorecan be a sync peer (ADR 0005).- The daemon's two transports derive from one canonical schema + one service
layer (
gonzalo-proto) (ADR 0007). - The code graph is NEVER keyed by
(repo, path)— two-level keying; slices are content-addressed, path-agnostic, and stored raw, and resolution tolerates missing targets (ADR 0012). - No query engine ever sits under the
Storesubstrate — engines back only regenerable index layers, never the durable source of truth (ADR 0012). unsafe_codeis forbidden workspace-wide (ADR 0009).- License is AGPL-3.0-only (ADR 0003).
- The core does no I/O —
gonzalo-coreis pure logic; all I/O lives in substrates (design spec §3). - Every write carries provenance identity — an
Identity;Metarecordsauthorandorigin_system(design spec §4, §9). The store also stampscreatedandupdatedinside the same critical section that decides the write, overwriting whatever the client sent: times a caller can set are times a caller can lie about. A replication write keeps the source's times, because syncing a record is not an edit. - ADRs are an append-only log — superseded, never deleted (ADR 0001).
- A consumer read never shows a tombstone —
getandlisthide them. Only the raw surface exposes a tombstone, and that is the surface replication uses; a deleted key can never reappear in a listing (ADR 0021, ADR 0025). - A tombstone pins the blobs of the record it replaces until the tombstone
itself is collected, so a delete never destroys content a peer can still
sync back (ADR 0024). A record whose body is a blob pins that blob; a
manifest, which names its blobs out of line, keeps its body in the tombstone
so the pin survives with the mapping that makes the bytes nameable — which
is also what lets
undeleterestore a deleted index (ADR 0029). - The daemon's surface stays described, not merely implemented — the served-operation table is checked against the router's own source and against the published OpenAPI document, in both directions, so an operation can be neither served without a description nor described without being served (ADR 0007, ADR 0020).