KB-37F7

dot-iu-cutter v0.4 — Transaction Mapping Design (design only)

11 min read Revision 1
dot-iu-cutterdieu44v0.4db-adapterdesign-onlytransaction-mapping

dot-iu-cutter v0.4 — Transaction Mapping Design

document_path: knowledge/dev/laws/dieu44-trien-khai/v0.4-db-adapter-design/dot-iu-cutter-v0.4-transaction-mapping-design-2026-05-17.md
revision: r1
date: 2026-05-17
author: Agent (Claude Code CLI, Opus 4.7 1M)
phase: v0.4 — REAL DB ADAPTER DESIGN (companion to design-master)
status: design_only_pending_gpt_review

⛔ DESIGN ONLY. No SQL executed, no write, no connection. Every txn below is a target boundary for a future authorized cycle. Faithful to the v0.4 write-path & flow designs and the v0.2/v0.3 frozen schema (12 tables, 19 FKs, no CHECK/trigger/DEFAULT).


§1 — Invariant: One Atomic Transaction Per Phase

TX-1 each of MARK / REVIEW / CUT / VERIFY = exactly ONE BEGIN…COMMIT.
TX-2 no cross-phase transaction; phases commit independently and are
     resumable from the persisted decision_backlog_entry.status.
TX-3 no nested transaction (mirrors PASSed skeleton transaction(): a second
     BEGIN on an open txn raises TransactionError; no SAVEPOINT-as-subtxn).
TX-4 a phase txn COMMITs whole or ROLLBACKs whole. A partially-written phase
     is forbidden to exist on disk.
TX-5 autocommit OFF; the adapter owns BEGIN/COMMIT/ROLLBACK explicitly
     (the skeleton transaction() context manager maps 1:1 to this).

§2 — TXN-MARK (principal: cutter_exec)

writes (in order):
  1. INSERT decision_backlog_entry            (entry_id uuid, kind, status='marked',
                                                payload jsonb{idempotency_key,…},
                                                emitted_at, scenario_ref)
  2. INSERT decision_backlog_history          (∅→marked transition; FK entry_id)
  3. INSERT decision_backlog_dependency  (0..N) (from_entry_id=this, to_entry_id=blocker)
  4. INSERT decision_backlog_sweep_log        (one row: this sweep pass; soft uuid refs)
idempotency (pre-write, same txn or a preceding read):
  read-before-write find(decision_backlog_entry where payload.idempotency_key=K).
  if present → COMMIT nothing new, return existing entry (replay = no dup).
commit boundary: all 1..4 commit together or none.
retry vs escalate:
  - 40001/40P01/53300/08xxx → bounded retry of the WHOLE txn (idempotent).
  - 23505 on idempotency_key → another worker won the race → SELECT & return
    the existing entry (success, dedup), do not retry as new.
  - 23503 FK / 23502 NOT NULL → STOP (design bug or bad input), NEEDS_HUMAN.

§3 — TXN-REVIEW (principal: cutter_exec)

writes:
  1. INSERT manifest_envelope                 (envelope_id, scope, content hash)
  2. INSERT manifest_unit_block         (1..N) (composite PK envelope_id,unit_local_id)
  3. INSERT review_decision                   (FK manifest_id→envelope; composite FK
                                                (manifest_id,manifest_unit_local_id)
                                                →manifest_unit_block; decision text)
  4. [re-review only] UPDATE review_decision SET superseded_by_review_decision_id
        on the PRIOR row (write-once; the ONLY non-status UPDATE column granted
        to cutter_exec) AND new row carries prior_review_decision_id
  5. UPDATE decision_backlog_entry SET status   (column-scoped) → reviewed_*
  6. INSERT decision_backlog_history          (transition row)
commit boundary: the manifest pair + the review_decision that binds it +
  the status move + history co-commit. G-REVIEW-MANIFEST: a review_decision
  must NEVER reference a manifest that did not commit (same txn guarantees it).
retry vs escalate:
  - transient (40001/40P01/53300/08xxx) → bounded retry whole txn.
  - re-review chaining must be deterministic on replay: prior/superseded
    stamping is write-once; a replay that finds the stamp already set is a
    no-op success, not a second supersede.
  - 23503 composite FK miss (unit block not committed) → STOP design bug.

§4 — TXN-CUT (principal: cutter_exec)

precondition guards (read-only checks INSIDE the txn before writes):
  G-CUT-APPROVED  non-superseded review_decision.decision='approve' exists
  G-CUT-DEPS      every decision_backlog_dependency.to_entry_id for entry is
                  verified_complete (DAG gate)
  G-CUT-ONCE      no existing non-rolled-back cut_change_set for
                  (entry, approved review_decision)  [IK-CUT + row guard §7]
writes:
  1. INSERT dot_pair_signature                (executor / DOT-991 lane;
                                                prior_signature_id chains)
  2. INSERT cut_change_set                    (FK decision_backlog_entry_id,
                                                FK executor_signature_id=the
                                                row from step 1;
                                                verifier_signature_id LEFT NULL
                                                — OD-6, CUT writes only CUT)
  3. INSERT cut_change_set_affected_row (1..N) (FK change_set_id)
  4. UPDATE decision_backlog_entry SET status (column-scoped) → cut_applied
  5. INSERT decision_backlog_history          (transition row)
commit boundary: a cut_change_set without its affected rows and its executor
  signature is forbidden to exist → all co-commit.
retry vs escalate:
  - transient → bounded retry whole txn; G-CUT-ONCE + IK-CUT make a retried
    CUT converge (returns the existing change_set, no double-apply).
  - 23505 on the CUT idempotency natural key → already applied → return
    existing change_set_id (success).
  - guard failure (not approved / deps unmet / already cut) → NOT an error
    to retry: STOP this phase, leave entry pre-CUT, let sweep/escalation
    own it.
note: cutter_exec has NO grant to write verify_result / verifier signature
  (matrix); verifier_signature_id stays NULL here by both design AND
  privilege (defence in depth).

§5 — TXN-VERIFY (principal: cutter_verify)

precondition guards (read-only, in txn):
  G-VERIFY-CUT  a cut_change_set with a valid DOT-991 executor signature exists
  G-VERIFY-SOD  verifier principal (cutter_verify) ≠ executor principal
                (cutter_exec) — enforced structurally by principal routing (doc 4)
writes:
  1. INSERT dot_pair_signature                (verifier / DOT-992 lane)
  2. INSERT verify_result                     (FK change_set_id,
                                                FK executor_signature_id=checked,
                                                FK verifier_signature_id=step 1,
                                                outcome pass|fail,
                                                prior_verify_result_id on re-verify)
  on PASS:
  3a. UPDATE decision_backlog_entry SET status → verified_complete (TERMINAL)
  on FAIL:
  3b. INSERT compensating cut_change_set + cut_change_set_affected_row
        (forward "undo"; NO physical delete of the original)
  3c. verify_result.rollback_change_set_id_triggered → that compensating set
  3d. INSERT escalation decision_backlog_entry (kind='escalation',
        status='marked') + its ∅→marked history
  3e. UPDATE decision_backlog_entry SET status → verify_failed_escalated
  4. INSERT decision_backlog_history          (pass/fail transition row)
commit boundary: verify_result + verifier signature + (on fail) the whole
  compensating+escalation bundle + status + history co-commit. A verify_result
  must never exist without its verifier signature.
matrix note: cutter_verify is granted INSERT on cut_change_set,
  cut_change_set_affected_row, decision_backlog_entry, decision_backlog_history,
  verify_result, dot_pair_signature + UPDATE(status) on decision_backlog_entry
  — EXACTLY what 3b/3d/3e/4 require. It has NO INSERT on review_decision/
  manifest_*, NO UPDATE on superseded_by → it physically cannot do REVIEW/CUT
  authoring work (separation of duty backed by the live grant matrix).
retry vs escalate:
  - transient → bounded retry whole txn (idempotent on change_set_id key).
  - verification MISMATCH (the semantic pass/fail itself = fail) is NOT a DB
    error: it is the designed FAIL path (3b–3e) → status verify_failed_escalated
    → a human/GPT loop owns the escalation entry (NEEDS_HUMAN).
  - 23505 on (change_set_id) verify key → a verify_result already exists →
    return it (dedup); a re-verify must chain via prior_verify_result_id, not
    duplicate.

§6 — Exact Commit / Rollback Boundary Table

phase   principal     COMMIT iff                         ROLLBACK on
MARK    cutter_exec   entry+history(+dep*)(+sweep) all ok any exception → whole
REVIEW  cutter_exec   manifest pair+decision+status+hist  any exception → whole
CUT     cutter_exec   sig+changeset+affrows+status+hist   any exception → whole
VERIFY  cutter_verify result+sig(+comp+esc on fail)+stat  any exception → whole
global: ROLLBACK leaves zero trace of that phase EXCEPT decision_backlog_sweep_log
  may intentionally retain an attempt row (audit of the try) IF it was a
  prior committed sweep pass — a sweep_log row inside a rolled-back TXN-MARK
  is itself rolled back; only previously-committed sweep passes persist.

§7 — Concurrency Guard (what makes multi-worker safe)

guard-1 status CAS: every status move is UPDATE decision_backlog_entry SET
  status=new WHERE entry_id=? AND status=expected. 0 rows updated → another
  worker moved it → this phase aborts (no force). Maps 1:1 to the PASSed
  skeleton cas_status().
guard-2 advisory lock: pg_advisory_xact_lock(hashtext(entry_id)) at phase
  entry serialises same-entry phases within their txn lifetime; auto-released
  at COMMIT/ROLLBACK (xact-scoped, no leak). (DA-7: advisory-lock vs rely
  solely on CAS — recommend BOTH belt-and-braces.)
guard-3 isolation: READ COMMITTED default; SERIALIZABLE for TXN-CUT &
  TXN-VERIFY (write-path doc recommendation) to prevent two writers racing
  the same entry's effect set; 40001 → bounded retry (doc 5 / error doc).
  (DA-8: SERIALIZABLE everywhere vs only CUT/VERIFY — recommend only
  CUT/VERIFY to limit retry churn on MARK/REVIEW.)
guard-4 idempotency keys (doc: write-path §4) make any retried/replayed
  phase converge instead of duplicating — the ultimate backstop.
no_single_worker_assumption: guards 1–4 hold for N workers; the only
  external limit is CONNECTION_LIMIT 2/principal (doc 2 §5 / DA-6).

§8 — Open Decisions (this doc)

DA-7 advisory xact lock + CAS (recommended) vs CAS only.
DA-8 SERIALIZABLE scope: CUT/VERIFY only (recommended) vs all four.
DA-10 sweep_log attempt-row persistence on rolled-back MARK: confirm
  "rolled back with the txn; only committed sweeps persist" is the intended
  liveness semantics (write-path doc allowed a persisted attempt row — this
  design says it persists only if it was its OWN committed sweep pass).
DA-11 re-review / re-verify replay determinism acceptance criteria for the
  dry-run plan (doc 5).

End of transaction mapping design (design only; no SQL; no write; no connection).

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.4-db-adapter-design/dot-iu-cutter-v0.4-transaction-mapping-design-2026-05-17.md