diff --git a/internal/platform/canvas_ai_model_integration_test.go b/internal/platform/canvas_ai_model_integration_test.go index 71fdc0b..bacf8ce 100644 --- a/internal/platform/canvas_ai_model_integration_test.go +++ b/internal/platform/canvas_ai_model_integration_test.go @@ -315,7 +315,12 @@ func TestCanonicalAIAdmissionAndLifecycle(t *testing.T) { if read().Elements[node]["data"].(map[string]any)["text"] != "keep this" { t.Fatal("restore was overwritten") } - call("POST", stalePath+"/apply", response{"node_id": node, "output_id": staleOutput}, 409, read().Versions[root], modelTestID(6201)) + staleApplyKey := modelTestID(6201) + staleApply := call("POST", stalePath+"/apply", response{"protocol_version": 2, "request_id": staleApplyKey, "operation_id": modelTestID(6205), "target_incarnation": 1, "target_canvas_id": root, "node_id": node, "output_id": staleOutput}, 409, read().Versions[root], staleApplyKey) + if staleApply["status"] != "not-applied" || staleApply["terminal"] != true || !modelEqual(staleApply, call("GET", base(root)+"/ai/operations/"+staleApplyKey, nil, 200, 0, "")) { + t.Fatal("stale apply has no queryable zero-effect receipt") + } + bad, badKey := command(root, node, "text", 1) call("POST", base(root)+"/ai/runs", bad, 409, read().Versions[root], badKey) restored := start(root, node, "text", 2) @@ -406,8 +411,22 @@ func TestCanonicalAIAdmissionAndLifecycle(t *testing.T) { missingInput, missingKey := command(child, imageID, "image", 1) delete(missingInput, "expected_versions") call("POST", base(child)+"/ai/runs", missingInput, 400, read().Versions[child], missingKey) - childRun := start(child, imageID, "image", 1) + // An explicit replan can reuse a logical operation after its known zero-effect rejection. + replanned := response{} + for key, value := range staleInput { + replanned[key] = value + } + replanKey := modelTestID(6405) + replanned["request_id"], replanned["expected_versions"] = replanKey, read().Versions + childRun := call("POST", base(child)+"/ai/runs", replanned, 202, read().Versions[child], replanKey) + execute(childRun["run_id"].(string)) + beforeLateCancel := read() + lateKey := modelTestID(6406) + lateCancel := call("POST", base(child)+"/ai/runs/"+childRun["run_id"].(string)+"/cancel", response{"protocol_version": 2, "request_id": lateKey, "operation_id": modelTestID(6407)}, 200, 0, lateKey) + if lateCancel["run_status"] != "succeeded" || !modelEqual(beforeLateCancel, read()) { + t.Fatal("late cancellation pretended to undo a completed result") + } if read().Elements[imageID]["ownerCanvasId"] != child { t.Fatal("child result moved its target back") }