- 1271
assert!(handle.is_none()); - 1272
} - 1273
- 1274
#[test] - 1275
fn disabling_commitments_opens_nothing() { - 1276
let dir = tempfile::tempdir().unwrap(); - 1277
let mut config = config(); - 1278
config.commitment.enabled = false; - 1279
assert!( - 1280
begin_episode( - 1281
dir.path(), - 1282
&config, - 1283
&intent_with(Evidence::None, Horizon::Durable, 0.9), - 1284
"watch the bill every day", - 1285
"s1", - 1286
dir.path(), - 1287
) - 1288
.is_none() - 1289
); - 1290
} - 1291
- 1292
/// The runtime may only propose criteria it could also check. Guessing a - 1293
/// shell command would manufacture `Observed` evidence from a guess. - 1294
#[test] - 1295
fn seeded_criteria_are_never_stronger_than_asserted() { - 1296
for evidence in [Evidence::Verified, Evidence::Audited] { - 1297
let intent = intent_with(evidence, Horizon::Durable, 0.9); - 1298
for criterion in seed_criteria(&intent, "do the thing") { - 1299
assert_eq!( - 1300
vak_commit::strength_of(&criterion.kind), - 1301
Satisfaction::Asserted - 1302
); - 1303
} - 1304
} - 1305
// And nothing at all is seeded when no proof is owed. - 1306
assert!(seed_criteria(&intent_with(Evidence::None, Horizon::Durable, 0.9), "x").is_empty()); - 1307
} - 1308
- 1309
/// Which means a seeded commitment cannot close itself: it stays visibly - 1310
/// open until a checkable criterion or a human attestation arrives. - 1311
#[test] - 1312
fn a_seeded_verified_commitment_cannot_be_closed_by_the_seed_alone() { - 1313
let dir = tempfile::tempdir().unwrap(); - 1314
let handle = begin_episode( - 1315
dir.path(), - 1316
&config(), - 1317
&intent_with(Evidence::Verified, Horizon::Durable, 0.9), - 1318
"migrate the schema and prove it works", - 1319
"s1", - 1320
dir.path(), - 1321
) - 1322
.unwrap(); - 1323
let ledger = CommitmentLedger::new(dir.path()); - 1324
ledger - 1325
.append(&Event::new( - 1326
&handle.commitment_id, - 1327
EventKind::CriterionEvaluated { - 1328
criterion_id: "objective".into(), - 1329
result: CriterionResult::Passed { - 1330
evidence: "I believe this is done".into(), - 1331
}, - 1332
strength: Satisfaction::Asserted, - 1333
}, - 1334
)) - 1335
.unwrap(); - 1336
let refused = ledger.append(&Event::new( - 1337
&handle.commitment_id, - 1338
EventKind::Closed { - 1339
verdict: Verdict::Fulfilled, - 1340
strength: Satisfaction::Asserted, - 1341
evidence: Vec::new(), - 1342
note: "done".into(), - 1343
}, - 1344
)); - 1345
assert!(refused.is_err()); - 1346
} - 1347
- 1348
#[test] - 1349
fn exhausting_the_turn_budget_counts_as_a_stall_but_answering_does_not() { - 1350
let stalled = classify(&vak_agent::TurnOutcome::MaxTurnsReached, 12, Vec::new()); - 1351
assert!(stalled.is_stall()); - 1352
- 1353
let answered = classify( - 1354
&vak_agent::TurnOutcome::Completed { - 1355
response: vak_llm::AssistantMessage { - 1356
content: vec![vak_llm::ContentBlock::Text { - 1357
text: "Here is a substantive answer that reduced uncertainty a lot.".into(), - 1358
}], - 1359
..vak_llm::AssistantMessage::empty("test-model") - 1360
}, - 1361
}, - 1362
0, - 1363
Vec::new(), - 1364
); - 1365
assert!(!answered.is_stall()); - 1366
assert!(matches!(answered, Advancement::Learned { .. })); - 1367
- 1368
let tool_only = classify( - 1369
&vak_agent::TurnOutcome::Completed { - 1370
response: vak_llm::AssistantMessage::empty("test-model"), - 1371
}, - 1372
12, - 1373
Vec::new(), - 1374
); - 1375
assert!(tool_only.is_stall()); - 1376
} - 1377
- 1378
#[test] - 1379
fn moving_a_criterion_is_advancement_whatever_else_happened() { - 1380
let advancement = classify( - 1381
&vak_agent::TurnOutcome::MaxTurnsReached, - 1382
0, - 1383
vec!["tests-pass".into()], - 1384
); - 1385
assert!(matches!(advancement, Advancement::Advanced { .. })); - 1386
assert!(!advancement.is_stall()); - 1387
} - 1388
- 1389
#[tokio::test] - 1390
async fn the_workspace_evaluator_observes_files_and_abstains_on_the_rest() { - 1391
let dir = tempfile::tempdir().unwrap(); - 1392
std::fs::write(dir.path().join("out.txt"), "all good").unwrap(); - 1393
let evaluator = WorkspaceEvaluator { cwd: dir.path() }; - 1394
- 1395
let exists = evaluator - 1396
.evaluate(&WorkCriterion { - 1397
criterion_id: "c1".into(), - 1398
statement: "output exists".into(), - 1399
kind: CriterionKind::FileExists { - 1400
path: "out.txt".into(), - 1401
}, - 1402
required: true, - 1403
}) - 1404
.await; - 1405
assert!(exists.passed()); - 1406
assert_eq!(exists.strength, Satisfaction::Observed); - 1407
- 1408
// A permissioned check abstains rather than guessing. - 1409
let shell = evaluator - 1410
.evaluate(&WorkCriterion { - 1411
criterion_id: "c2".into(), - 1412
statement: "tests pass".into(), - 1413
kind: CriterionKind::Shell { - 1414
command: "cargo test".into(), - 1415
}, - 1416
required: true, - 1417
}) - 1418
.await; - 1419
assert!(!shell.passed()); - 1420
assert!(matches!(shell.result, CriterionResult::Unknown { .. })); - 1421
} - 1422
- 1423
#[tokio::test] - 1424
async fn a_predicate_suspension_wakes_when_the_condition_becomes_true() { - 1425
let dir = tempfile::tempdir().unwrap(); - 1426
let ledger = CommitmentLedger::new(dir.path()); - 1427
let handle = begin_episode( - 1428
dir.path(), - 1429
&config(), - 1430
&intent_with(Evidence::None, Horizon::Durable, 0.9), - 1431
"tell me when the report lands, every day", - 1432
"s1", - 1433
dir.path(), - 1434
) - 1435
.unwrap(); - 1436
let criterion = WorkCriterion { - 1437
criterion_id: "landed".into(), - 1438
statement: "report.csv exists".into(), - 1439
kind: CriterionKind::FileExists { - 1440
path: "report.csv".into(), - 1441
}, - 1442
required: true, - 1443
}; - 1444
ledger - 1445
.append(&Event::new( - 1446
&handle.commitment_id, - 1447
EventKind::Suspended { - 1448
suspension: vak_commit::Suspension::Predicate { - 1449
criterion: criterion.clone(), - 1450
}, - 1451
}, - 1452
)) - 1453
.unwrap(); - 1454
- 1455
// While the condition is false the pass costs nothing and changes - 1456
// nothing — this is the zero-token watch path. - 1457
let report = maintain(dir.path()).await; - 1458
assert!(report.satisfied.is_empty()); - 1459
assert_eq!( - 1460
ledger.get(&handle.commitment_id).unwrap().unwrap().phase, - 1461
vak_commit::Phase::Suspended - 1462
); - 1463
- 1464
std::fs::write(dir.path().join("report.csv"), "done").unwrap(); - 1465
let report = maintain(dir.path()).await; - 1466
assert_eq!(report.satisfied, vec![handle.commitment_id.clone()]); - 1467
assert_ne!( - 1468
ledger.get(&handle.commitment_id).unwrap().unwrap().phase, - 1469
vak_commit::Phase::Suspended - 1470
); - 1471
} - 1472
- 1473
#[tokio::test] - 1474
async fn a_commitment_dependency_wakes_after_fulfillment() { - 1475
let dir = tempfile::tempdir().unwrap(); - 1476
let ledger = CommitmentLedger::new(dir.path()); - 1477
let dependency = ledger - 1478
.open_commitment(vak_commit::spec_from_reading( - 1479
"dependency", - 1480
Reading::general(), - 1481
Vec::new(), - 1482
dir.path().to_path_buf(), - 1483
Economics::default(), - 1484
)) - 1485
.unwrap(); - 1486
ledger - 1487
.append(&Event::new( - 1488
&dependency, - 1489
EventKind::Closed { - 1490
verdict: Verdict::Fulfilled, - 1491
strength: Satisfaction::Asserted, - 1492
evidence: Vec::new(), - 1493
note: "done".into(), - 1494
}, - 1495
)) - 1496
.unwrap(); - 1497
let waiting = ledger - 1498
.open_commitment(vak_commit::spec_from_reading( - 1499
"waiting", - 1500
Reading::general(), - 1501
Vec::new(), - 1502
dir.path().to_path_buf(), - 1503
Economics::default(), - 1504
)) - 1505
.unwrap(); - 1506
ledger - 1507
.append(&Event::new( - 1508
&waiting, - 1509
EventKind::Suspended { - 1510
suspension: vak_commit::Suspension::Commitment { - 1511
commitment_id: dependency, - 1512
}, - 1513
}, - 1514
)) - 1515
.unwrap(); - 1516
let report = maintain(dir.path()).await; - 1517
assert_eq!(report.resumed, vec![waiting]); - 1518
} - 1519
- 1520
#[tokio::test] - 1521
async fn a_scheduled_suspension_wakes_once_its_time_arrives() { - 1522
let dir = tempfile::tempdir().unwrap(); - 1523
let ledger = CommitmentLedger::new(dir.path()); - 1524
let handle = begin_episode( - 1525
dir.path(), - 1526
&config(), - 1527
&intent_with(Evidence::None, Horizon::Durable, 0.9), - 1528
"check the bill every day", - 1529
"s1", - 1530
dir.path(), - 1531
) - 1532
.unwrap(); - 1533
ledger - 1534
.append(&Event::new( - 1535
&handle.commitment_id, - 1536
EventKind::Suspended { - 1537
suspension: vak_commit::Suspension::Schedule { - 1538
at: Some(chrono::Utc::now() + chrono::Duration::hours(2)), - 1539
cron: None, - 1540
}, - 1541
}, - 1542
)) - 1543
.unwrap(); - 1544
assert!(maintain(dir.path()).await.resumed.is_empty()); - 1545
- 1546
ledger - 1547
.append(&Event::new( - 1548
&handle.commitment_id, - 1549
EventKind::Suspended { - 1550
suspension: vak_commit::Suspension::Schedule { - 1551
at: Some(chrono::Utc::now() - chrono::Duration::minutes(1)), - 1552
cron: None, - 1553
}, - 1554
}, - 1555
)) - 1556
.unwrap(); - 1557
assert_eq!( - 1558
maintain(dir.path()).await.resumed, - 1559
vec![handle.commitment_id] - 1560
); - 1561
} - 1562
- 1563
/// A question with no policy waits forever by design. That is a decision, - 1564
/// not a leak — and it must not quietly become an assumption. - 1565
#[tokio::test] - 1566
async fn an_unanswered_question_waits_unless_a_policy_says_otherwise() { - 1567
let dir = tempfile::tempdir().unwrap(); - 1568
let ledger = CommitmentLedger::new(dir.path()); - 1569
let handle = begin_episode( - 1570
dir.path(), - 1571
&config(), - 1572
&intent_with(Evidence::None, Horizon::Durable, 0.9), - 1573
"migrate the schema every night", - 1574
"s1", - 1575
dir.path(), - 1576
) - 1577
.unwrap(); - 1578
defer_for_human( - 1579
dir.path(), - 1580
&handle.commitment_id, - 1581
"which database?", - 1582
None, - 1583
vak_intent::Escalation::WaitIndefinitely, - 1584
) - 1585
.unwrap(); - 1586
assert!(maintain(dir.path()).await.escalated.is_empty()); - 1587
assert_eq!( - 1588
ledger.get(&handle.commitment_id).unwrap().unwrap().phase, - 1589
vak_commit::Phase::Suspended - 1590
); - 1591
} - 1592
- 1593
#[test] - 1594
fn expiry_closes_explicitly_and_budget_exhaustion_does_not() { - 1595
let dir = tempfile::tempdir().unwrap(); - 1596
let ledger = CommitmentLedger::new(dir.path()); - 1597
let mut config = config(); - 1598
config.commitment.default_ttl_days = Some(1); - 1599
- 1600
let handle = begin_episode( - 1601
dir.path(), - 1602
&config, - 1603
&intent_with(Evidence::None, Horizon::Durable, 0.9), - 1604
"watch it every day", - 1605
"s1", - 1606
dir.path(), - 1607
) - 1608
.unwrap(); - 1609
- 1610
// Not yet expired. - 1611
assert!(sweep_expired(dir.path()).is_empty()); - 1612
- 1613
// An over-budget commitment is held by the scheduler, never closed. - 1614
ledger - 1615
.append(&Event::new( - 1616
&handle.commitment_id, - 1617
EventKind::EpisodeEnded { - 1618
episode_id: handle.episode_id.clone(), - 1619
advancement: Advancement::Learned { fact: "x".into() }, - 1620
spend_usd: 9_999.0, - 1621
}, - 1622
)) - 1623
.unwrap(); - 1624
assert!(sweep_expired(dir.path()).is_empty()); - 1625
let commitment = ledger.get(&handle.commitment_id).unwrap().unwrap(); - 1626
assert!( - 1627
commitment.is_over_budget() || commitment.spec.economics.lifetime_budget_usd.is_none() - 1628
); - 1629
assert!(!commitment.phase.is_terminal()); - 1630
} - 1631
} - 1632
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.