Harden reconciliation conflict handling

This commit is contained in:
2026-05-23 18:18:44 +02:00
parent 7831673820
commit 706b360736
8 changed files with 445 additions and 22 deletions

View File

@@ -65,7 +65,13 @@ async function readError(response) {
}
}
async function reconcileStatusChange({entity, type, nextStatus, blockingReason = null}) {
async function reconcileStatusChange({
entity,
type,
currentStatus,
nextStatus,
blockingReason = null,
}) {
const response = await apiFetch("/reconciliation/state-change", {
method: "POST",
headers: {"Content-Type": "application/json"},
@@ -75,6 +81,7 @@ async function reconcileStatusChange({entity, type, nextStatus, blockingReason =
target_status: nextStatus,
actor: "dashboard",
intent: `${type} status change via dashboard`,
expected_current_status: currentStatus,
blocking_reason: blockingReason,
apply: true,
}),
@@ -84,6 +91,7 @@ async function reconcileStatusChange({entity, type, nextStatus, blockingReason =
}
function messageForReconciliation(result) {
if (result.conflict) return {text: "out of sync", kind: "review"};
if (result.write_through_result === "applied") return {text: "synced", kind: "ok"};
if (result.reconciliation_class === "human_confirmation") return {text: "needs review", kind: "review"};
if (result.reconciliation_class === "deferred") return {text: "queued", kind: ""};
@@ -155,7 +163,13 @@ export function statusControl({
select.disabled = true;
setMessage("saving");
try {
const result = await reconcileStatusChange({entity, type, nextStatus, blockingReason});
const result = await reconcileStatusChange({
entity,
type,
currentStatus,
nextStatus,
blockingReason,
});
const messageResult = messageForReconciliation(result);
if (result.write_through_result === "applied") {
Object.assign(entity, {status: result.target_status});