Skip to main content

khive_runtime/
atomic_prepare.rs

1//! ADR-099: the per-verb async prepare pass for the KG-substrate v1
2//! admissible verbs (`update`, `delete`, `link`, `merge`), plus
3//! [`prepare_add_entity`]/[`prepare_add_note`] for the ADR-046 proposal
4//! changeset `AddEntity`/`AddNote` arms. Each `prepare_*` function reads
5//! current state (async, outside any transaction) and returns a plain-data
6//! [`crate::atomic_runner::AtomicOpPlan`] ([`crate::atomic_plan`]) for the
7//! synchronous commit pass ([`crate::atomic_runner::run_atomic_unit`]) to
8//! apply.
9//!
10//! `gtd.transition`/`gtd.complete` prepare is deliberately not here (lives in
11//! `kkernel` instead), and `propose`/`review`/`withdraw`/`merge` are on the
12//! v1 admissible list but have no working prepare implementation in this
13//! module (`prepare_governance_unimplemented` fails loudly rather than
14//! silently no-opping; `prepare_merge` is unreachable through `--atomic` and
15//! kept only for its own tests and as defense in depth). See
16//! `docs/api/atomic_prepare.md#scope-what-is-excluded-and-why` for why each of these is excluded
17//! and what would be required to admit them.
18
19use serde_json::Value;
20use uuid::Uuid;
21
22mod index_purge;
23
24pub(crate) use index_purge::event_append_statements;
25use index_purge::push_index_purge_statements;
26
27use khive_storage::types::SqlValue;
28use khive_storage::{AttachmentSubstrate, EdgeRelation, EdgeUpsertDisposition, SqlStatement};
29use khive_types::pack::pack_registry_tag;
30use khive_types::{EventKind, SubstrateKind};
31
32use crate::atomic_plan::{
33    AddEntityPlan, AddNotePlan, AffectedRowGuard, DeletePlan, EdgeNaturalKey, LinkPlan, MergePlan,
34    PlanStatement, PostCommitEffect, UpdatePlan,
35};
36use crate::atomic_runner::AtomicOpPlan;
37use crate::atomic_runner::CommittedPostCommitEffects;
38use crate::curation::{entity_fts_document, note_fts_document};
39use crate::error::{RuntimeError, RuntimeResult};
40use crate::operations::{
41    canonical_edge_endpoint_kinds, canonical_edge_endpoints, merge_dependency_kind,
42    validate_edge_metadata, validate_edge_weight, Resolved,
43};
44use crate::runtime::{KhiveRuntime, NamespaceToken};
45
46use khive_db::stores::attachment::delete_record_attachments_statement;
47use khive_db::stores::entity::{
48    entity_hard_delete_statement, entity_replace_if_unchanged_statement,
49    entity_soft_delete_statement, entity_upsert_statement,
50};
51use khive_db::stores::event::event_insert_statements;
52use khive_db::stores::event::hard_delete_lineage_warning_statements;
53use khive_db::stores::graph::{
54    edge_hard_delete_statement, edge_insert_new_guarded_by_endpoints_statement,
55    edge_link_replace_if_unchanged_and_endpoints_exist_statement,
56    edge_replace_if_unchanged_statement, edge_soft_delete_statement,
57    edge_symmetric_absorb_or_update_inplace_statement, edge_symmetric_delete_if_conflict_statement,
58    purge_incident_edges_statement,
59};
60use khive_db::stores::note::{
61    note_hard_delete_statement, note_soft_delete_statement, note_upsert_statement,
62};
63use khive_db::stores::text::{delete_document_statements, insert_document_statements};
64
65mod add_update;
66pub(crate) use add_update::{
67    attach_note_update_effects, prepare_add_entity_record, prepare_add_note_record,
68};
69pub use add_update::{
70    prepare_add_entity, prepare_add_note, prepare_op, prepare_update, prepare_update_entity_plan,
71    prepare_update_from_note_snapshot, validate_note_update_expected_kind, AtomicUpdateKind,
72};
73use add_update::{
74    prepare_update_entity_plan_with_version_and_type, reject_inapplicable_update_fields,
75};
76
77mod note_reindex_effect;
78mod patch_helpers;
79
80use patch_helpers::{
81    entity_name_patch, obj, optional_create_string, optional_entity_type_patch, optional_f64,
82    optional_f64_patch, optional_properties, optional_str, optional_string_patch, optional_tags,
83    refuse_pack_registry_tags, require_str, require_uuid,
84};
85
86pub(crate) fn canonical_refuse_registry_tags(tags: &[String], verb: &str) -> RuntimeResult<()> {
87    refuse_pack_registry_tags(tags, verb)
88}
89
90pub(crate) async fn prepare_update_entity_plan_with_version(
91    runtime: &KhiveRuntime,
92    token: &NamespaceToken,
93    id: Uuid,
94    patch: crate::curation::EntityPatch,
95    expected_version: Option<i64>,
96) -> RuntimeResult<AtomicOpPlan> {
97    prepare_update_entity_plan_with_version_and_type(
98        runtime,
99        token,
100        id,
101        patch,
102        expected_version,
103        None,
104    )
105    .await
106}
107
108// ---------------------------------------------------------------------------
109// post-commit effects
110// ---------------------------------------------------------------------------
111
112mod embedding_outcome;
113pub use embedding_outcome::{
114    apply_post_commit_effects_with_failures, PostCommitEffectsReport, PostCommitEmbeddingOutcome,
115    ReindexModelFailure, ReindexModelStage,
116};
117
118mod link_merge;
119use link_merge::{parse_edge_relation, prepare_link, prepare_merge};
120
121mod post_commit;
122pub(crate) use post_commit::apply_one_post_commit_effect;
123pub use post_commit::{apply_post_commit_effects, apply_post_commit_effects_with_report};
124
125mod edge_delete;
126pub(crate) use edge_delete::prepare_delete_edge;
127use edge_delete::prepare_update_edge;
128pub use edge_delete::{prepare_delete, AtomicDeleteKind};
129pub(crate) use edge_delete::{prepare_delete_from_snapshot, prepare_update_edge_plan};
130
131#[cfg(test)]
132#[path = "atomic_prepare_tests.rs"]
133mod tests;