Found while scoping the D13 promotion tool (cloud#874). Not a blocker for any single tool — the point is that four migration-class consumers are now queued behind the same missing primitive, and each is being forced into the same hand-rolled shape.
Current state
- The data-engine contract declares an optional
batch?(requests, { transaction?: boolean }) (packages/spec/src/contracts/data-engine.ts:70-91) — no engine implementation, no callers. - driver-turso has real primitives (
beginTransaction / commit / rollback, turso-driver.ts:764-776; remote-transport.ts:430-443) — not surfaced through ObjectQL. - Plugin-visible multi-row mutation is therefore a JS loop of single-row writes (or
multi: true predicate updates). A failure mid-sequence strands partial state; a process crash (as opposed to a thrown error) defeats in-process compensation entirely.
The queued consumers
| Tool | Status | Tables touched |
|---|
| ADR-0105 D13 BU-subtree → org promotion (分立) | in design, cloud | sys_organization, sys_business_unit(_member), sys_member, sys_user_position, N business objects |
| ADR-0117 D8 ownership backfill | Proposed | every object gaining owning_business_unit_id |
| ADR-0110 org lifecycle hooks(新设/分立/合并) | Proposed | memberships, grants, master data |
| D10 master-data distribution (#4585) | blocked on its ADR | distribution/mirror rows per policy |
Each independently converges on: dry-run preflight → undo journal → LIFO compensation → re-entrant forward recovery (precedent: cloud organization-lifecycle.ts slug rename). One copy of that machinery is an engineering answer; four copies is a platform debt signal.
Decision needed (one of three)
- Surface driver transactions through the engine. The honest cost: middleware/hook semantics inside a transaction (do post-hooks fire before commit?), cache invalidation on rollback, cross-driver support — a real design project, not a plumbing task.
- Bless a shared migration-journal primitive — a framework-owned journal + compensation/resume runner that migration tools consume, standardizing the pattern instead of transactionalizing the engine.
- Explicitly rule both out and document journal+compensation as the blessed pattern — then at least extract the shared helper so the four consumers stop hand-rolling it.
No urgency ranking implied between 1 and 2; what needs deciding is which debt we are choosing. D13 proceeds on the hand-rolled pattern regardless and simplifies later if 1/2 lands.
Refs: #4346 (the multi: true no-predicate footgun — evidence this write path is sharp), ADR-0110, ADR-0117, cloud#874, cloud#1006.
Found while scoping the D13 promotion tool (cloud#874). Not a blocker for any single tool — the point is that four migration-class consumers are now queued behind the same missing primitive, and each is being forced into the same hand-rolled shape.
Current state
batch?(requests, { transaction?: boolean })(packages/spec/src/contracts/data-engine.ts:70-91) — no engine implementation, no callers.beginTransaction/commit/rollback,turso-driver.ts:764-776;remote-transport.ts:430-443) — not surfaced through ObjectQL.multi: truepredicate updates). A failure mid-sequence strands partial state; a process crash (as opposed to a thrown error) defeats in-process compensation entirely.The queued consumers
sys_organization,sys_business_unit(_member),sys_member,sys_user_position, N business objectsowning_business_unit_idEach independently converges on: dry-run preflight → undo journal → LIFO compensation → re-entrant forward recovery (precedent: cloud
organization-lifecycle.tsslug rename). One copy of that machinery is an engineering answer; four copies is a platform debt signal.Decision needed (one of three)
No urgency ranking implied between 1 and 2; what needs deciding is which debt we are choosing. D13 proceeds on the hand-rolled pattern regardless and simplifies later if 1/2 lands.
Refs: #4346 (the
multi: trueno-predicate footgun — evidence this write path is sharp), ADR-0110, ADR-0117, cloud#874, cloud#1006.