Skip to main content

Module atomic_prepare

Module atomic_prepare 

Source
Expand description

ADR-099: the per-verb async prepare pass for the KG-substrate v1 admissible verbs (update, delete, link, merge). Each prepare_* function reads current state (async, outside any transaction) and returns a plain-data crate::atomic_runner::AtomicOpPlan (crate::atomic_plan) for the synchronous commit pass (crate::atomic_runner::run_atomic_unit) to apply.

gtd.transition / gtd.complete prepare is deliberately not here: their lifecycle vocabulary (is_terminal, can_transition, …) lives in khive-pack-gtd, which depends on khive-runtime: not the other way around. Reproducing that dependency here would invert the crate graph, so their prepare functions live in kkernel (which already depends on both crates), calling back into the plain PlanStatement/AffectedRowGuard shapes exported from this module’s sibling, crate::atomic_plan.

propose / review / withdraw (the event-sourced governance lifecycle) are on the v1 admissible list ([khive_types::pack:: ATOMIC_ADMISSIBLE_VERBS]) but have no prepare implementation here: their apply path is a changeset-interpreter (apply_worker) over a dedicated proposals_open table, not a small number of guarded DML statements — a faithful, non-stub atomic prepare for them is separate follow-on work. prepare_governance_unimplemented fails loudly, before any write, naming this as a known scope gap rather than silently no-opping.

merge is likewise on the v1 admissible list but is deferred: full-parity field folding, survivor index reindex, loser index purge, provenance, and same-kind rejection are achievable as static DML, but curation::merge_entity_sql’s graceful edge-conflict resolution is not (it is per-row procedural, incompatible with the static predicate/guard plan shape): rather than ship a partially-scoped atomic merge, it is rejected at the same pre-runtime static guard as governance (khive_types::pack::ATOMIC_KNOWN_UNIMPLEMENTED_VERBS). prepare_merge below is therefore unreachable through --atomic; it remains only as the pre-fix-round direct-prepare implementation, exercised by this module’s own tests, and as defense in depth.

Enums§

AtomicDeleteKind
Caller-supplied delete-kind expectation, resolved via the canonical resolve_kind_spec at the kkernel --atomic seam. khive-runtime must not depend on khive-pack-kg (packs depend on the runtime, not the other way around), so this is a plain substrate-level shape rather than khive_pack_kg::handlers::KindSpec itself: the kkernel seam does the pack-aware resolve_kind_spec resolution (which needs a VerbRegistry, unreachable from this crate) and passes down only what prepare_delete needs to enforce the mismatch check.
AtomicUpdateKind
Caller-supplied update-kind expectation, resolved via the canonical resolve_kind_spec at the kkernel --atomic seam: the same pattern AtomicDeleteKind uses. Without this check, update(kind="document", id=<concept>) would be canonically NotFound but the atomic path would ignore the explicit kind and mutate the resolved entity anyway. khive-runtime must not depend on khive-pack-kg, so this is a plain substrate-level shape rather than khive_pack_kg::handlers::KindSpec itself: the kkernel seam does the pack-aware resolution and passes down only what prepare_update needs to enforce the mismatch check.

Functions§

apply_post_commit_effects
Run every deferred PostCommitEffect after a committed atomic unit, outside any transaction. Re-fetches each target’s now-committed row and reuses the existing reindex_entity/reindex_note (FTS + embedding, same as the non-atomic path) for exact parity.
prepare_delete
expected_kind: None when the caller omitted kind (no check, parity with canonical’s own optional discriminator); Some(_) enforces an exact-parity mismatch check against the resolved record’s actual substrate/specific kind, mirroring handle_delete’s entity.kind != *expected / note.kind != *expected checks.
prepare_op
Build the prepared AtomicOpPlan for one KG-substrate admissible op (update, delete, link, merge). Returns a loud RuntimeError for propose/review/withdraw (known scope gap, see module doc) and any other verb (the CLI boundary must reject those before calling this — a verb reaching here is either KG-substrate-admissible or a bug upstream).
prepare_update
expected_kind: None when the caller omitted kind (no check, parity with canonical’s own optional discriminator); Some(_) enforces an exact-parity mismatch check against the resolved record’s actual substrate/specific kind, mirroring handle_update’s entity.kind != *k / note kind checks (update.rs:200-201, :229-234).