22The model catalog — referential integrity, measured remote == embedded
Recipe:fine_tune (the only public registration path) · list_models · describe_model · delete_model · Theory: the model catalog as a referenced store — a model is a row other rows point at (Kleppmann 2017), so a hard-delete is refused while any reference holds, and absence is a typed condition, not an ambiguous error · Rail: measurement (every delete-matrix cell asserted against a frozen golden) + the cross-transport parity contract (remote == embedded, the engine swaps transports without changing the call).
The engine’s model catalog lets you see the models it resolves and trains — list_models and describe_model — and clean them up — delete_model. The three verbs are on both the embedded jammi.EmbeddedBackend and the remote jammi.RemoteDatabase. This chapter is the engine↔︎cookbook validator that measures that surface as a real, asserted property: the referential-integrity matrix for delete_model, validated identical across the two transports.
22.1 What this chapter validates — and the honesty contract
Read the framing plainly:
It validates the catalog SURFACE on a CPU/hermetic fixture. The model is registered the only public way — a tiny fine_tune (LoRA, text_embedding) over a 12-row in-memory (anchor, positive) pairs corpus with the engine’s public tiny_modernbert fixture. Registering a model is a catalog operation; the fine-tune trains in seconds on CPU (the candle backend falls back off CUDA), so the whole emit is GPU-free and hermetic. No keystone corpus, no external services.
There is no public register_model verb (introspected against the wheel and the jammi client). The only public path that puts a model row in the catalog is training: a fine_tune registers the base model (path-keyed) at submission and the fine-tuned model (jammi:fine-tuned:<uuid>) on completion. Pre-trained models are served by id — the resolver loads an HF or local model by reference — so a model enters the catalog by being trained, not by a separate registration step.
Every model in the catalog is trained-and-referenced, so the delete-unreferenced-succeeds path is simply unreachable here — a measured property of the catalog, not a gap. Every model reachable from the surface is born referenced (a fine-tuned model by training_jobs.output_model_id; its base by training_jobs.base_model_id), so delete_model on either is correctly refused. The cookbook measures every_catalog_model_is_referenced = True: there is no bare unreferenced model in the catalog to delete.
The cache is emitted once by scripts/build_lifecycle_cache.py, which runs the whole catalog interaction on both the embedded engine and a live remote grpc://jammi-server, asserts remote == embedded for every observable, and freezes the embedded-canonical matrix to artifacts/lifecycle/. This chapter reads that cache on CPU and asserts every cell against the frozen golden — it never re-emits.
registration path: TRAINING
transports: embedded (file://, in-process), remote (grpc://, live jammi-server)
22.2.1 Register → the minimal projection
fine_tune registers the model; describe_model reads it back as the minimal client-facing projection — backend, task, status (the per-run UUID model_id is stripped from the committed form, since it differs across engine instances by construction). The same projection both transports return, with no server-internal field (e.g. a version/lineage/path bookkeeping field) leaking.
registered = matrix["registered"]print("registered model projection:", registered)# A fresh model reads as `registered`; the public registration path registers TWO# rows (the base model + the fine-tuned model).assertset(registered) == {"backend", "task", "status"}assert"model_id"notin registeredcontracts.assert_close("lifecycle.register.status_registered",1.0if registered["status"] =="registered"else0.0)n_registered =len(matrix["list_after_register"])print(f"list_models after register: {n_registered} rows "f"(base model + fine-tuned model), both registered")assert n_registered ==2
registered model projection: {'backend': 'candle', 'status': 'registered', 'task': 'text_embedding'}
list_models after register: 2 rows (base model + fine-tuned model), both registered
22.2.2 Describe and list → the same shape on both transports
list_models returns one record per model in the catalog; describe_model reads one back by id (or None if it is not in the catalog). Both return the same minimal projection, key-for-key, on the embedded engine and the remote server — the see-the-catalog half of the surface.
for proj in matrix["list_after_register"]:assertset(proj) == {"backend", "task", "status"}print("list_models / describe_model projection keys: backend, task, status ""(model_id stripped from the committed form)")
list_models / describe_model projection keys: backend, task, status (model_id stripped from the committed form)
22.3 The referential-integrity matrix for delete_model
delete_model is how you clean up the catalog: it removes the row, so it is refused while any reference still points at the model — the referential guarantee (Kleppmann 2017). The matrix has three exercisable cells; a fourth (delete-unreferenced succeeds) is simply unreachable here, because every model in the catalog is trained-and-referenced.
22.3.1 Delete-while-referenced → the typed ModelReferenced guard
Every model in the catalog is referenced (the fine-tuned model by training_jobs.output_model_id; its base by training_jobs.base_model_id), so delete_model on either raises the typed ModelReferenced error — embedded as a RuntimeError("Model referenced: … still referenced by training_jobs.<edge>"), remote as a wire StatusCode.FAILED_PRECONDITION. The honest cross-transport observable is the normalized error classreferenced.
for cell in ("delete_referenced", "ft_delete_referenced", "base_delete_referenced"): outcome = matrix[cell]assert outcome["raised"] isTrueand outcome["error_class"] =="referenced", cellprint("delete-while-referenced → error class 'referenced' ""(both the fine-tuned model and its base; FAILED_PRECONDITION on the wire)")contracts.assert_close("lifecycle.delete.referenced_raises", 1.0)
delete-while-referenced → error class 'referenced' (both the fine-tuned model and its base; FAILED_PRECONDITION on the wire)
1.0
22.3.2 The measured property — every catalog model is referenced
The headline referential property is that every model in the catalog is referenced, so a hard-delete is always guarded. This is also why the delete-unreferenced-succeeds cell is unreachable here: a model enters the catalog by being trained, and a trained model is referenced by its training-job row. There is no bare unreferenced model in the catalog to delete — a measured property of the engine’s catalog.
assert matrix["every_catalog_model_is_referenced"] isTruecontracts.assert_close("lifecycle.delete.every_catalog_model_referenced", 1.0)print("every catalog model is referenced =", matrix["every_catalog_model_is_referenced"])
every catalog model is referenced = True
22.3.3 Delete-absent → NotFound, not invalid-argument
Deleting a model that is not in the catalog is a typed not-found condition (the ModelNotFound error) — not an invalid-argument error. The wire status is NOT_FOUND; embedded it is RuntimeError("Model not found: …"). With if_exists=True, deleting an absent model is a clean no-op instead.
strict = matrix["delete_absent_strict"]if_exists = matrix["delete_absent_if_exists"]assert strict["raised"] isTrueand strict["error_class"] =="not_found"assert if_exists["raised"] isFalseand if_exists["error_class"] isNoneprint("delete-absent (strict) → error class 'not_found' ""(NOT_FOUND on the wire — the typed ModelNotFound, not INVALID_ARGUMENT)")print("delete-absent (if_exists) → no-op (no raise)")contracts.assert_close("lifecycle.delete.absent_strict_not_found", 1.0)contracts.assert_close("lifecycle.delete.absent_if_exists_noop", 1.0)
delete-absent (strict) → error class 'not_found' (NOT_FOUND on the wire — the typed ModelNotFound, not INVALID_ARGUMENT)
delete-absent (if_exists) → no-op (no raise)
1.0
22.4 The cross-transport parity contract — remote == embedded
The engine’s spine is that the catalog verbs carry the same call surface and the same observable behaviour on both transports, so a caller swaps connect("file://…") for connect("grpc://…") without changing a single call. The emit ran the whole catalog interaction on both and asserted remote == embeddedlive for every observable: the model projections (UUID excluded) and the normalized delete-error class.
The two transports raise different Python exception types by construction — embedded a RuntimeError, the gRPC client a grpc.RpcError carrying a StatusCode. So the honest contract for a typed error is the error class, derived from each native error (FAILED_PRECONDITION → referenced, NOT_FOUND → not_found). That normalization is what the parity check compares.
parity = record["parity"]n =len(parity)all_equal =all(p["equal"] for p in parity)print(f"remote == embedded: {sum(p['equal'] for p in parity)}/{n} observables agree")print("verdict:", record["parity_verdict"])assert all_equal and n ==8contracts.assert_close("lifecycle.parity.all_observables_equal", 1.0)
remote == embedded: 8/8 observables agree
verdict: remote == embedded for every catalog observable
1.0
22.5 The measured catalog, at a glance
m = record["measured"]print("register : status =", m["registered_status"],"| backend =", m["registered_backend"],"| task =", m["registered_task"])print("delete : referenced →", repr(m["delete_referenced_class"]),"| absent(strict) →", repr(m["delete_absent_strict_class"]),"| absent(if_exists) raised =", m["delete_absent_if_exists_raised"])print("property : every catalog model referenced =", m["every_catalog_model_is_referenced"])
The model catalog is a real, asserted property: a model registers as registered, reads back through describe_model / list_models as the minimal projection, and is hard-deletable only when unreferenced — a condition that does not arise in a catalog where every model is trained-and-referenced, which the engine refuses with a typed ModelReferenced guard rather than a silent cascade. Every delete-matrix cell holds identically across the embedded and remote transports — the cross-transport contract, measured.
Kleppmann, Martin. 2017. Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems. O’Reilly Media.