mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-30 02:49:39 +02:00
feat(cluster): execute approved graph deletes in cluster apply
Stage 4C execution half (RFC-004 §D5/§D6 + sweep rows 7/7b/8): an approved graph.<id> delete — and its riding schema/query deletes — classifies Applied and executes LAST in the run, sidecar-fenced: pre-op manifest pin (best effort; partial roots still delete), approval_id carried in the sidecar, recursive root removal (NotFound tolerated), subtree tombstoned out of the ledger with a tombstone observation, the approval consumed in the same state CAS (ledger summary) and its artifact file rewritten with consumed_at only after the CAS lands — a failed run consumes nothing and the approval stays valid for the retry. Sweep rows: already-tombstoned intents retire (7); a completed delete with a stale ledger rolls forward — tombstone + approval consumption + audit entry (7b, idempotent); a still-present root retires the stale intent with a graph_delete_incomplete warning and the still-approved delete re-executes in the same run (8) — prefix removal is idempotent, so retry IS the repair. The multi-graph mixed e2e gets its conclusion: blocked without approval, cluster approve graph.engineering --as andrew, converge, tombstone visible in status. Phase 4's disposition matrix is now fully executable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f4e9105272
commit
d1d04217ab
2 changed files with 513 additions and 9 deletions
|
|
@ -1358,7 +1358,7 @@ fn cluster_e2e_graph_root_destruction_drifts_then_apply_recreates_empty_graph()
|
|||
/// (applied), its composite (derived) — shows all four dispositions at once
|
||||
/// before the graph-plane schema apply closes the loop.
|
||||
#[test]
|
||||
fn cluster_e2e_multi_graph_mixed_dispositions_then_converge() {
|
||||
fn cluster_e2e_multi_graph_mixed_dispositions_then_approve_and_converge() {
|
||||
let temp = tempdir().unwrap();
|
||||
write_multi_graph_cluster_fixture(temp.path());
|
||||
// No manual init: Stage 4A creates both graphs.
|
||||
|
|
@ -1468,7 +1468,55 @@ policies:
|
|||
let mut sorted = order.clone();
|
||||
sorted.sort_unstable();
|
||||
assert_eq!(order, sorted, "{mixed}");
|
||||
// Conclusion (approve + converge) extends below once the delete executor lands.
|
||||
// The conclusion: an apply without approval stays blocked; the approved
|
||||
// delete converges the cluster, tombstoning the removed graph.
|
||||
let still_blocked = cluster_json(temp.path(), "apply");
|
||||
assert_eq!(still_blocked["converged"], false, "{still_blocked}");
|
||||
|
||||
let approve = parse_stdout_json(&output_success(
|
||||
cli()
|
||||
.arg("--as")
|
||||
.arg("andrew")
|
||||
.arg("cluster")
|
||||
.arg("approve")
|
||||
.arg("graph.engineering")
|
||||
.arg("--config")
|
||||
.arg(temp.path())
|
||||
.arg("--json"),
|
||||
));
|
||||
assert_eq!(approve["ok"], true, "{approve}");
|
||||
assert_eq!(approve["approved_by"], "andrew");
|
||||
|
||||
let converge = cluster_json(temp.path(), "apply");
|
||||
assert_eq!(converge["ok"], true, "{converge}");
|
||||
assert_eq!(converge["converged"], true, "{converge}");
|
||||
assert!(!temp.path().join("graphs/engineering.omni").exists());
|
||||
|
||||
let status = cluster_json(temp.path(), "status");
|
||||
assert_eq!(status["observations"]["graph.engineering"]["kind"], "tombstone");
|
||||
let final_plan = cluster_json(temp.path(), "plan");
|
||||
assert!(
|
||||
final_plan["changes"].as_array().unwrap().is_empty(),
|
||||
"{final_plan}"
|
||||
);
|
||||
}
|
||||
|
||||
/// An approval without an approver is meaningless: approve requires --as.
|
||||
#[test]
|
||||
fn cluster_e2e_approve_requires_actor() {
|
||||
let temp = tempdir().unwrap();
|
||||
write_cluster_config_fixture(temp.path());
|
||||
|
||||
let output = output_failure(
|
||||
cli()
|
||||
.arg("cluster")
|
||||
.arg("approve")
|
||||
.arg("graph.knowledge")
|
||||
.arg("--config")
|
||||
.arg(temp.path()),
|
||||
);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
assert!(stderr.contains("--as"), "{stderr}");
|
||||
}
|
||||
|
||||
/// Stage 4A headline: a declared graph is created by `cluster apply` itself —
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue