- 2198
backup_path: None, - 2199
state: PromotionFileState::Applying, - 2200
operation: CandidateOperation::Upsert, - 2201
}, - 2202
], - 2203
}; - 2204
write_transaction(&directory.join("journal.json"), &interrupted).unwrap(); - 2205
- 2206
let receipt = promote_recoverable(&candidate, control.path()).unwrap(); - 2207
assert_eq!(receipt.applied, vec!["a.txt", "b.txt"]); - 2208
assert_eq!( - 2209
fs::read_to_string(target.path().join("a.txt")).unwrap(), - 2210
"new a" - 2211
); - 2212
assert_eq!( - 2213
fs::read_to_string(target.path().join("b.txt")).unwrap(), - 2214
"new b" - 2215
); - 2216
let journal = load_transaction(&directory.join("journal.json")).unwrap(); - 2217
assert_eq!(journal.state, PromotionTransactionState::Completed); - 2218
} - 2219
- 2220
#[test] - 2221
fn recovery_refuses_to_erase_a_later_workspace_edit() { - 2222
let source = tempfile::tempdir().unwrap(); - 2223
let target = tempfile::tempdir().unwrap(); - 2224
let control = tempfile::tempdir().unwrap(); - 2225
fs::write(source.path().join("a.txt"), "candidate").unwrap(); - 2226
fs::write(target.path().join("a.txt"), "human edit").unwrap(); - 2227
let candidate = CandidateManifest { - 2228
candidate_id: "conflicted-recovery".into(), - 2229
source_root: source.path().into(), - 2230
destination_root: target.path().into(), - 2231
target_checks: Vec::new(), - 2232
workspace_checks: Vec::new(), - 2233
files: vec![CandidateFile { - 2234
path: "a.txt".into(), - 2235
candidate_hash: digest(b"candidate"), - 2236
base_hash: Some(digest(b"original")), - 2237
bytes: 9, - 2238
operation: CandidateOperation::Upsert, - 2239
}], - 2240
}; - 2241
let directory = control.path().join("conflicted-recovery"); - 2242
fs::create_dir_all(directory.join("backups")).unwrap(); - 2243
let backup = directory.join("backups/0"); - 2244
fs::write(&backup, "original").unwrap(); - 2245
write_transaction( - 2246
&directory.join("journal.json"), - 2247
&PromotionTransaction { - 2248
schema_version: 1, - 2249
candidate_id: candidate.candidate_id.clone(), - 2250
candidate_digest: candidate_digest(&candidate).unwrap(), - 2251
destination_root: target.path().into(), - 2252
state: PromotionTransactionState::Applying, - 2253
files: vec![PromotionTransactionFile { - 2254
path: "a.txt".into(), - 2255
before_hash: Some(digest(b"original")), - 2256
after_hash: digest(b"candidate"), - 2257
backup_path: Some(backup), - 2258
state: PromotionFileState::Applied, - 2259
operation: CandidateOperation::Upsert, - 2260
}], - 2261
}, - 2262
) - 2263
.unwrap(); - 2264
- 2265
assert!(matches!( - 2266
promote_recoverable(&candidate, control.path()), - 2267
Err(Error::Conflict(message)) if message.contains("later workspace change") - 2268
)); - 2269
assert_eq!( - 2270
fs::read_to_string(target.path().join("a.txt")).unwrap(), - 2271
"human edit" - 2272
); - 2273
} - 2274
- 2275
#[test] - 2276
fn completed_transaction_is_bound_to_the_selected_file_set() { - 2277
let source = tempfile::tempdir().unwrap(); - 2278
let target = tempfile::tempdir().unwrap(); - 2279
let control = tempfile::tempdir().unwrap(); - 2280
fs::write(source.path().join("a.txt"), "a").unwrap(); - 2281
fs::write(source.path().join("b.txt"), "b").unwrap(); - 2282
let file = |path: &str, bytes: &[u8]| CandidateFile { - 2283
path: path.into(), - 2284
candidate_hash: digest(bytes), - 2285
base_hash: None, - 2286
bytes: bytes.len() as u64, - 2287
operation: CandidateOperation::Upsert, - 2288
}; - 2289
let first = CandidateManifest { - 2290
candidate_id: "selection".into(), - 2291
source_root: source.path().into(), - 2292
destination_root: target.path().into(), - 2293
target_checks: Vec::new(), - 2294
workspace_checks: Vec::new(), - 2295
files: vec![file("a.txt", b"a")], - 2296
}; - 2297
promote_recoverable(&first, control.path()).unwrap(); - 2298
let different_selection = CandidateManifest { - 2299
files: vec![file("b.txt", b"b")], - 2300
..first - 2301
}; - 2302
assert!(matches!( - 2303
promote_recoverable(&different_selection, control.path()), - 2304
Err(Error::InvalidPlan(message)) if message.contains("identity mismatch") - 2305
)); - 2306
assert!(!target.path().join("b.txt").exists()); - 2307
} - 2308
- 2309
#[test] - 2310
fn scoped_undo_restores_changed_files_and_removes_new_files() { - 2311
let source = tempfile::tempdir().unwrap(); - 2312
let target = tempfile::tempdir().unwrap(); - 2313
let control = tempfile::tempdir().unwrap(); - 2314
fs::write(source.path().join("changed.txt"), "after").unwrap(); - 2315
fs::write(source.path().join("new.txt"), "new").unwrap(); - 2316
fs::write(target.path().join("changed.txt"), "before").unwrap(); - 2317
let candidate = CandidateManifest { - 2318
candidate_id: "undoable".into(), - 2319
source_root: source.path().into(), - 2320
destination_root: target.path().into(), - 2321
target_checks: Vec::new(), - 2322
workspace_checks: Vec::new(), - 2323
files: vec![ - 2324
CandidateFile { - 2325
path: "changed.txt".into(), - 2326
candidate_hash: digest(b"after"), - 2327
base_hash: Some(digest(b"before")), - 2328
bytes: 5, - 2329
operation: CandidateOperation::Upsert, - 2330
}, - 2331
CandidateFile { - 2332
path: "new.txt".into(), - 2333
candidate_hash: digest(b"new"), - 2334
base_hash: None, - 2335
bytes: 3, - 2336
operation: CandidateOperation::Upsert, - 2337
}, - 2338
], - 2339
}; - 2340
promote_recoverable(&candidate, control.path()).unwrap(); - 2341
let receipt = undo_promotion("undoable", control.path()).unwrap(); - 2342
assert_eq!(receipt.restored, vec!["changed.txt", "new.txt"]); - 2343
assert_eq!( - 2344
fs::read_to_string(target.path().join("changed.txt")).unwrap(), - 2345
"before" - 2346
); - 2347
assert!(!target.path().join("new.txt").exists()); - 2348
assert_eq!( - 2349
load_transaction(&control.path().join("undoable/journal.json")) - 2350
.unwrap() - 2351
.state, - 2352
PromotionTransactionState::Undone - 2353
); - 2354
assert_eq!(undo_promotion("undoable", control.path()).unwrap(), receipt); - 2355
} - 2356
- 2357
#[test] - 2358
fn scoped_undo_refuses_to_erase_post_acceptance_edits() { - 2359
let source = tempfile::tempdir().unwrap(); - 2360
let target = tempfile::tempdir().unwrap(); - 2361
let control = tempfile::tempdir().unwrap(); - 2362
fs::write(source.path().join("draft.txt"), "accepted").unwrap(); - 2363
let candidate = CandidateManifest { - 2364
candidate_id: "undo-conflict".into(), - 2365
source_root: source.path().into(), - 2366
destination_root: target.path().into(), - 2367
target_checks: Vec::new(), - 2368
workspace_checks: Vec::new(), - 2369
files: vec![CandidateFile { - 2370
path: "draft.txt".into(), - 2371
candidate_hash: digest(b"accepted"), - 2372
base_hash: None, - 2373
bytes: 8, - 2374
operation: CandidateOperation::Upsert, - 2375
}], - 2376
}; - 2377
promote_recoverable(&candidate, control.path()).unwrap(); - 2378
fs::write(target.path().join("draft.txt"), "human changed it").unwrap(); - 2379
assert!(matches!( - 2380
undo_promotion("undo-conflict", control.path()), - 2381
Err(Error::Conflict(message)) if message.contains("later workspace change") - 2382
)); - 2383
assert_eq!( - 2384
fs::read_to_string(target.path().join("draft.txt")).unwrap(), - 2385
"human changed it" - 2386
); - 2387
} - 2388
- 2389
#[test] - 2390
fn scoped_undo_resumes_after_restore_before_progress_was_recorded() { - 2391
let source = tempfile::tempdir().unwrap(); - 2392
let target = tempfile::tempdir().unwrap(); - 2393
let control = tempfile::tempdir().unwrap(); - 2394
fs::write(source.path().join("draft.txt"), "after").unwrap(); - 2395
fs::write(target.path().join("draft.txt"), "before").unwrap(); - 2396
let candidate = CandidateManifest { - 2397
candidate_id: "undo-resume".into(), - 2398
source_root: source.path().into(), - 2399
destination_root: target.path().into(), - 2400
target_checks: Vec::new(), - 2401
workspace_checks: Vec::new(), - 2402
files: vec![CandidateFile { - 2403
path: "draft.txt".into(), - 2404
candidate_hash: digest(b"after"), - 2405
base_hash: Some(digest(b"before")), - 2406
bytes: 5, - 2407
operation: CandidateOperation::Upsert, - 2408
}], - 2409
}; - 2410
promote_recoverable(&candidate, control.path()).unwrap(); - 2411
let journal_path = control.path().join("undo-resume/journal.json"); - 2412
let mut journal = load_transaction(&journal_path).unwrap(); - 2413
journal.state = PromotionTransactionState::Undoing; - 2414
journal.files[0].state = PromotionFileState::Undoing; - 2415
write_transaction(&journal_path, &journal).unwrap(); - 2416
fs::write(target.path().join("draft.txt"), "before").unwrap(); - 2417
- 2418
undo_promotion("undo-resume", control.path()).unwrap(); - 2419
assert_eq!( - 2420
load_transaction(&journal_path).unwrap().state, - 2421
PromotionTransactionState::Undone - 2422
); - 2423
assert_eq!( - 2424
fs::read_to_string(target.path().join("draft.txt")).unwrap(), - 2425
"before" - 2426
); - 2427
} - 2428
- 2429
#[test] - 2430
fn revised_candidate_can_review_accept_and_undo_a_deletion() { - 2431
let source = tempfile::tempdir().unwrap(); - 2432
let target = tempfile::tempdir().unwrap(); - 2433
let store = tempfile::tempdir().unwrap(); - 2434
let control = tempfile::tempdir().unwrap(); - 2435
fs::write(target.path().join("obsolete.txt"), "workspace original").unwrap(); - 2436
fs::write(source.path().join("obsolete.txt"), "draft version").unwrap(); - 2437
let first = freeze_candidate( - 2438
"delete-v1", - 2439
source.path(), - 2440
target.path(), - 2441
&store.path().join("v1"), - 2442
) - 2443
.unwrap(); - 2444
let task = store.path().join("task"); - 2445
prepare_revision_copy(&first, &task).unwrap(); - 2446
fs::remove_file(task.join("obsolete.txt")).unwrap(); - 2447
let deletion = - 2448
freeze_revision_candidate("delete-v2", &task, &first, &store.path().join("v2")) - 2449
.unwrap(); - 2450
assert_eq!(deletion.files.len(), 1); - 2451
assert_eq!(deletion.files[0].operation, CandidateOperation::Delete); - 2452
let receipt = promote_recoverable(&deletion, control.path()).unwrap(); - 2453
assert_eq!(receipt.deleted, vec!["obsolete.txt"]); - 2454
assert!(!target.path().join("obsolete.txt").exists()); - 2455
undo_promotion("delete-v2", control.path()).unwrap(); - 2456
assert_eq!( - 2457
fs::read_to_string(target.path().join("obsolete.txt")).unwrap(), - 2458
"workspace original" - 2459
); - 2460
} - 2461
- 2462
#[test] - 2463
fn durable_records_are_append_only_and_replayable() { - 2464
let dir = tempfile::tempdir().unwrap(); - 2465
let path = dir.path().join("sandbox").join("records.jsonl"); - 2466
let plan = EnvironmentPlan { - 2467
id: "env-1".into(), - 2468
outcome_revision: 7, - 2469
input_root: dir.path().into(), - 2470
task_root: dir.path().join("task"), - 2471
backend: "local".into(), - 2472
image: None, - 2473
network_policy: "none".into(), - 2474
setup_recipe: vec!["make".into()], - 2475
}; - 2476
let record = DurableRecord::Environment(EnvironmentRecord { - 2477
record_id: "r-1".into(), - 2478
environment_id: "env-1".into(), - 2479
state: EnvironmentState::Ready, - 2480
plan, - 2481
updated_at: "2026-09-08T00:00:00Z".into(), - 2482
detail: None, - 2483
}); - 2484
append_record(&path, &record).unwrap(); - 2485
let preparation = DurableRecord::PreviewPreparation(PreviewPreparationRecord { - 2486
record_id: "preview-1".into(), - 2487
session_id: "session-1".into(), - 2488
result_id: "result-1".into(), - 2489
candidate_id: "candidate-1".into(), - 2490
candidate_digest: "sha256:digest".into(), - 2491
environment_id: "preview:candidate-1".into(), - 2492
state: EnvironmentState::Ready, - 2493
command: "npm ci --ignore-scripts --no-audit --no-fund".into(), - 2494
evidence: "dependencies prepared".into(), - 2495
updated_at: "2026-09-08T00:01:00Z".into(), - 2496
}); - 2497
append_record(&path, &preparation).unwrap(); - 2498
assert_eq!(load_records(&path).unwrap(), vec![record, preparation]); - 2499
} - 2500
} - 2501
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.