Skip to main content

Module atomic_plan

Module atomic_plan 

Source
Expand description

ADR-099 (cross-op atomicity for bulk apply) — prepared write-plan types.

Async prepare materializes a synchronous write plan outside any transaction; commit later applies its statements as DML under a per-op SAVEPOINT. This module defines the plan shapes only, one family per admissible verb group (update, delete, link, merge, gtd.transition, gtd.complete, the governance verbs) — not yet wired into a live handler or the dispatch path. Every plan is deliberately inert (plain data, no async, no embedding reference).

Two validation-staleness invariants every plan must satisfy:

  1. Predicate-based plans carry an “all rows matching a condition” effect as a statement evaluated inside the transaction (PlanPredicate), never as a prepare-time-enumerated row list.
  2. Affected-row guards (PlanStatement::guard) are attached to the exact statement they validate, checked in-transaction; a mismatch fails the op and rolls back the whole unit.

See docs/atomic-plan.md for why guards are per-statement rather than per-plan.

Structs§

AddEntityPlan
Write plan for an AddEntity proposal change: a fresh entity row plus its FTS document in the same atomic unit. Vector indexing remains a deferred effect because embedding may suspend.
AddNotePlan
Write plan for an AddNote proposal change: a fresh note row plus its FTS document in the same atomic unit.
AffectedRowGuard
An affected-row guard (ADR-099 D1, rule 2): the row-count prepare assumed its target write would affect, re-verified in-transaction. A prepare-time validation is a plan hypothesis, never a commitment — if the guard does not hold at apply time, the op fails inside the atomic unit and the whole unit rolls back (ADR-099 acceptance criteria: “zero-row apply fails the unit”).
DeletePlan
Write plan for a delete op (soft or hard).
EdgeNaturalKey
The natural key a committed symmetric edge update’s surviving row must be looked up by (ADR-099 B3, second half). khive-db’s edge_symmetric_refresh_or_update_inplace_statement pair never trusts a prepare-time-computed target id (see that builder’s doc comment); a caller rendering this op’s result derives the actual surviving id by querying graph_edges’s own UNIQUE(namespace, source_id, target_id, relation) constraint (e.g. via KhiveRuntime::list_edges filtered on these fields — the same mechanism the atomic link op’s own result rendering already uses), strictly after commit.
GovernancePlan
Write plan for a governance op (propose, review, or withdraw — the event-sourced change-proposal lifecycle, ADR-046).
GtdCompletePlan
Write plan for a gtd.complete op (task lifecycle terminal transition).
GtdTransitionPlan
Write plan for a gtd.transition op (explicit task lifecycle change).
LinkPlan
Write plan for a link op (create a typed directed edge). Endpoint existence is checked structurally, not via an unanchored plan-level guard: statement is a guarded INSERT ... SELECT ... WHERE EXISTS shape whose SELECT re-probes both endpoints inside the transaction, so the runner’s affected-row check on this one statement is the in-transaction existence probe (ADR-099 acceptance criteria’s dangling-edge case — [delete(X, hard), link(A, X)] — is closed by this guard failing once X is gone, regardless of statement ordering convention).
MergePlan
Write plan for a merge op (deduplicate two entities). Rewires and lifecycle writes are split into separate fields precisely so a guard is never ambiguous between them: the edge rewire is predicate-based (ADR-099 D1 rule 1) and may touch zero or many rows depending on earlier in-file writes, so it is never guarded; the from/into entity lifecycle write assumes both rows exist, so it always is.
PlanPredicate
The predicate a prepare pass validated a plan’s target against, replayed as a statement evaluated inside the transaction (ADR-099 D1, rule 1: “predicate-based plans wherever a write’s scope depends on current state”). Carrying the predicate rather than a prepare-time-enumerated row list is what lets a later op in the same file (e.g. an intervening link) be visible to this plan’s apply.
PlanStatement
One statement in a plan, paired with the guard (if any) that validates it. Runner contract: a present guard is checked against the affected-row count of applying statement alone (SqlWriter::execute’s return value for this statement), not a batch total and not another statement’s count. guard: None means prepare made no row-existence assumption about this particular statement (e.g. a cascade delete that may legitimately touch zero rows).
UpdatePlan
Write plan for an update op (entity or note shape — ADR-099 D3’s update caveat covers both substrates the same way: row/FTS DML in the plan, any reindex deferred to post_commit).

Enums§

GovernanceOp
Which governance verb (propose / review / withdraw) a GovernancePlan applies.
PostCommitEffect
A deferred side effect recorded during prepare and run once, after the atomic unit commits (ADR-099 D1, “post-commit pass”). v1’s admissible set computes no embeddings during prepare (D3’s update/merge caveat), so the only post-commit effects are reindex kicks computed from the committed row content, plus the GAP-5 addition under B3: the best-effort GTD lifecycle audit row.