- 770
options.len() - 771
))); - 772
} - 773
} - 774
} - 775
} - 776
} - 777
- 778
let lower = text.to_ascii_lowercase(); - 779
if lower.contains("decision matrix") - 780
|| lower.contains("comparison matrix") - 781
|| lower.contains("tradeoff analysis") - 782
{ - 783
if let Some(tab_res) = verify_tabular_data(text) { - 784
return match tab_res { - 785
Ok(msg) => Some(Ok(format!( - 786
"decision matrix verified via tabular layout ({msg})" - 787
))), - 788
Err(err) => Some(Err(format!( - 789
"decision matrix tabular structure malformed: {err}" - 790
))), - 791
}; - 792
} - 793
} - 794
- 795
None - 796
} - 797
- 798
/// Structural oracle for claim-to-citation integrity. - 799
#[allow(clippy::collapsible_if)] - 800
pub fn verify_claim_citations(text: &str) -> Option<Result<String, String>> { - 801
let mut refs = std::collections::HashSet::new(); - 802
let mut defs = std::collections::HashSet::new(); - 803
- 804
for line in text.lines() { - 805
let trimmed = line.trim(); - 806
if trimmed.starts_with("[^") { - 807
if let Some(colon_pos) = trimmed.find("]:") { - 808
let tag = &trimmed[..colon_pos + 1]; - 809
defs.insert(tag.to_string()); - 810
} - 811
} - 812
let mut rest = line; - 813
while let Some(pos) = rest.find("[^") { - 814
let after = &rest[pos..]; - 815
if let Some(end_pos) = after.find(']') { - 816
let tag = &after[..end_pos + 1]; - 817
if !tag.ends_with("]:") { - 818
refs.insert(tag.to_string()); - 819
} - 820
rest = &after[end_pos + 1..]; - 821
} else { - 822
break; - 823
} - 824
} - 825
} - 826
- 827
if refs.is_empty() { - 828
return None; - 829
} - 830
- 831
let mut unlinked: Vec<_> = refs - 832
.iter() - 833
.filter(|r| !defs.contains(*r)) - 834
.map(|s| s.as_str()) - 835
.collect(); - 836
unlinked.sort(); - 837
- 838
if unlinked.is_empty() { - 839
Some(Ok(format!( - 840
"claim citations verified: {} citations linked", - 841
refs.len() - 842
))) - 843
} else { - 844
Some(Err(format!( - 845
"unlinked footnote citations: {}", - 846
unlinked.join(", ") - 847
))) - 848
} - 849
} - 850
- 851
/// Evaluate requirements using a concrete evidence receipt and its - 852
/// requirement freshness window. Retrieval success alone is insufficient. - 853
pub fn evaluate_requirements_with_receipt( - 854
spec: &OutcomeSpec, - 855
response: Option<&str>, - 856
now: chrono::DateTime<chrono::Utc>, - 857
receipt: Option<&EvidenceReceipt>, - 858
) -> Vec<RequirementEvaluation> { - 859
let state = receipt - 860
.map(|value| { - 861
let max_age = chrono::Duration::seconds(spec.evidence_max_age_secs.unwrap_or(86_400)); - 862
evidence_state_from_receipt(now, value, max_age) - 863
}) - 864
.unwrap_or(EvidenceState::None); - 865
evaluate_requirements_with_state(spec, response, state) - 866
} - 867
- 868
/// An `Evidence` requirement is never `Met` here, by design: this function - 869
/// can see that a source reference exists and whether a retrieval receipt is - 870
/// fresh, but not whether the source *supports the claim*. That is a - 871
/// judgement, and the runtime does not make it structurally — so a turn - 872
/// held to `cited` or stronger evidence closes at best `Unknown` from this - 873
/// evaluator, with review recommended, until a linked criterion (a - 874
/// `Shell`/`FileContains` check, an external receipt, a human attestation) - 875
/// establishes it through the commitment ledger. - 876
pub fn evaluate_requirements_with_state( - 877
spec: &OutcomeSpec, - 878
response: Option<&str>, - 879
evidence_state: EvidenceState, - 880
) -> Vec<RequirementEvaluation> { - 881
let has_response = response.is_some_and(|text| !text.trim().is_empty()); - 882
let is_refusal = response.is_some_and(|text| { - 883
let normalized = text.trim().to_ascii_lowercase(); - 884
[ - 885
"i cannot", - 886
"i can't", - 887
"i can’t", - 888
"unable to", - 889
"cannot do", - 890
"can't do", - 891
"can’t do", - 892
] - 893
.iter() - 894
.any(|prefix| normalized.starts_with(prefix)) - 895
}); - 896
let has_structured_evidence = response.is_some_and(|text| { - 897
text.contains("\"semantic_type\":\"research.synthesis\"") - 898
|| text.contains("\"semantic_type\": \"research.synthesis\"") - 899
|| text.contains("\"semantic_type\":\"evidence\"") - 900
|| text.contains("\"semantic_type\": \"evidence\"") - 901
}); - 902
let has_reference = response.is_some_and(|text| { - 903
text.contains("http://") || text.contains("https://") || text.contains("[^") - 904
}) || has_structured_evidence; - 905
spec.requirements - 906
.iter() - 907
.map(|requirement| { - 908
let (status, reason) = match requirement.kind { - 909
RequirementKind::Deliverable if is_refusal => ( - 910
RequirementStatus::Unknown, - 911
"response is a refusal; the requested deliverable was not established".into(), - 912
), - 913
RequirementKind::Deliverable if has_response => ( - 914
RequirementStatus::Met, - 915
"response content exists".into(), - 916
), - 917
RequirementKind::Deliverable => ( - 918
RequirementStatus::Unmet, - 919
"no response content was produced".into(), - 920
), - 921
RequirementKind::Evidence if evidence_state == EvidenceState::Fresh => ( - 922
RequirementStatus::Unknown, - 923
"successful retrieval receipt exists; support and freshness still require evaluation".into(), - 924
), - 925
RequirementKind::Evidence if evidence_state == EvidenceState::Stale => ( - 926
RequirementStatus::Unknown, - 927
"evidence receipt exists but is stale for this request".into(), - 928
), - 929
RequirementKind::Evidence if !has_reference => ( - 930
RequirementStatus::Unmet, - 931
"no source reference was found in the response".into(), - 932
), - 933
RequirementKind::Evidence => ( - 934
RequirementStatus::Unknown, - 935
"a source reference exists, but support and freshness were not established" - 936
.into(), - 937
), - 938
RequirementKind::Constraint => ( - 939
RequirementStatus::Unknown, - 940
"constraint applicability requires a linked result".into(), - 941
), - 942
RequirementKind::Integrity if is_refusal => ( - 943
RequirementStatus::Unknown, - 944
"response is a refusal; domain integrity check bypassed".into(), - 945
), - 946
RequirementKind::Integrity if has_response => { - 947
let text = response.unwrap_or_default(); - 948
let mut checks = Vec::new(); - 949
if let Some(tab) = verify_tabular_data(text) { - 950
checks.push(tab); - 951
} - 952
if let Some(dec) = verify_decision_matrix(text) { - 953
checks.push(dec); - 954
} - 955
if let Some(cit) = verify_claim_citations(text) { - 956
checks.push(cit); - 957
} - 958
if checks.is_empty() { - 959
(RequirementStatus::Met, "no structured domain violations found".into()) - 960
} else if checks.iter().all(|c| c.is_ok()) { - 961
(RequirementStatus::Met, "all structured domain integrity checks passed".into()) - 962
} else { - 963
let violations: Vec<_> = checks.into_iter().filter_map(|c| c.err()).collect(); - 964
(RequirementStatus::Unmet, format!("domain integrity check failed: {}", violations.join("; "))) - 965
} - 966
} - 967
RequirementKind::Integrity => ( - 968
RequirementStatus::Unmet, - 969
"no response content was produced to verify domain integrity".into(), - 970
), - 971
}; - 972
RequirementEvaluation { - 973
requirement_id: requirement.id.clone(), - 974
status, - 975
reason, - 976
} - 977
}) - 978
.collect() - 979
} - 980
- 981
impl OutcomeSpec { - 982
/// A named file deliverable cannot be established by prose alone. This - 983
/// conservative signal only affects outcome assessment; it grants no tool. - 984
/// - 985
/// Only a request with a part that produces something — authoring, - 986
/// modifying, operating — can owe a file: "explain how to write a - 987
/// README.md" names a file and asks for an explanation, and demanding a - 988
/// write for it made the stop gate send a correct answer back for a - 989
/// file nobody asked for. - 990
pub fn saved_file_target(&self) -> Option<String> { - 991
if !self - 992
.acts - 993
.iter() - 994
.any(|act| matches!(act, Act::Author | Act::Modify | Act::Operate)) - 995
{ - 996
return None; - 997
} - 998
let request = self.objective.to_ascii_lowercase(); - 999
let asks_to_write = [ - 1000
"create ", - 1001
"write ", - 1002
"save ", - 1003
"generate ", - 1004
"make ", - 1005
"build ", - 1006
"export ", - 1007
] - 1008
.iter() - 1009
.any(|verb| request.contains(verb)); - 1010
if !asks_to_write { - 1011
return None; - 1012
} - 1013
request.split_whitespace().find_map(|word| { - 1014
let token = word.trim_matches(|c: char| { - 1015
!c.is_ascii_alphanumeric() && c != '.' && c != '_' && c != '-' && c != '/' - 1016
}); - 1017
let is_file = [ - 1018
".html", ".htm", ".md", ".txt", ".json", ".csv", ".pdf", ".docx", ".pptx", ".xlsx", - 1019
".svg", ".png", ".js", ".ts", ".tsx", ".rs", ".py", ".css", ".sql", - 1020
] - 1021
.iter() - 1022
.any(|extension| token.ends_with(extension) && token.len() > extension.len()); - 1023
is_file.then(|| token.to_string()) - 1024
}) - 1025
} - 1026
- 1027
pub fn expects_saved_file(&self) -> bool { - 1028
self.saved_file_target().is_some() - 1029
} - 1030
- 1031
/// Build the conservative baseline contract for an ordinary turn. - 1032
/// - 1033
/// The request text is preserved as the objective; inferred requirements - 1034
/// never grant tools or claim that evidence exists. - 1035
pub fn from_reading( - 1036
objective: impl Into<String>, - 1037
reading: &Reading, - 1038
resolver_version: u32, - 1039
) -> Self { - 1040
let mut requirements = vec![OutcomeRequirement { - 1041
id: "deliverable-1".into(), - 1042
kind: RequirementKind::Deliverable, - 1043
description: format!("produce an {} result", reading.act.as_str()), - 1044
origin: RequirementOrigin::Inferred, - 1045
importance: RequirementImportance::Must, - 1046
target: Some("primary".into()), - 1047
}]; - 1048
if reading.evidence != Evidence::None { - 1049
requirements.push(OutcomeRequirement { - 1050
id: "evidence-1".into(), - 1051
kind: RequirementKind::Evidence, - 1052
description: format!("meet the {} evidence standard", reading.evidence.as_str()), - 1053
origin: RequirementOrigin::Inferred, - 1054
importance: RequirementImportance::Must, - 1055
target: Some("primary".into()), - 1056
}); - 1057
} - 1058
OutcomeSpec { - 1059
schema_version: 1, - 1060
revision: 0, - 1061
objective: objective.into(), - 1062
assumptions: Vec::new(), - 1063
requirements, - 1064
resolver_version, - 1065
evidence_max_age_secs: match reading.evidence { - 1066
Evidence::None => None, - 1067
Evidence::Cited => Some(86_400), - 1068
Evidence::Verified | Evidence::Audited => Some(3_600), - 1069
}, - 1070
max_turns: None, - 1071
acts: BTreeSet::from([reading.act]), - 1072
stop: crate::StopProfile::default(), - 1073
} - 1074
} - 1075
- 1076
/// The baseline contract with the engagement's own stop rule and the - 1077
/// primary act of every part. A reading never sets `max_turns`: the turn - 1078
/// budget is the operator's, whatever the request looked like. - 1079
pub fn from_intent(objective: impl Into<String>, intent: &crate::Intent) -> Self { - 1080
let mut spec = Self::from_reading( - 1081
objective, - 1082
&intent.reading, - 1083
intent.provenance.resolver_version, - 1084
); - 1085
spec.stop = intent.engagement.posture.stop; - 1086
spec.acts - 1087
.extend(intent.strands.iter().map(|strand| strand.reading.act)); - 1088
spec - 1089
} - 1090
- 1091
/// Whether this outcome requires execution or file modifications. - 1092
/// - 1093
/// True when an act genuinely needs one (`Act::requires_execution`: - 1094
/// `Modify`/`Operate`/`Govern`/`Verify`), or when the request names a - 1095
/// file deliverable outright (`expects_saved_file`) — the only case - 1096
/// where authoring content also demands a receipt, since `Act` alone has - 1097
/// no view of the request text. - 1098
pub fn requires_execution(&self) -> bool { - 1099
self.expects_saved_file() || self.acts.iter().any(|act| act.requires_execution()) - 1100
} - 1101
- 1102
/// Whether this outcome requires inspection, search, or enumeration. - 1103
pub fn requires_inspection(&self) -> bool { - 1104
self.acts.iter().any(|act| act.requires_inspection()) - 1105
} - 1106
- 1107
/// Whether this outcome requires real tool execution or evidence receipts. - 1108
pub fn requires_tool(&self) -> bool { - 1109
self.expects_saved_file() - 1110
|| self.acts.iter().any(|act| act.requires_tool()) - 1111
|| self - 1112
.requirements - 1113
.iter() - 1114
.any(|r| r.kind == RequirementKind::Evidence) - 1115
} - 1116
- 1117
/// The act to name in logs and nudges: the most demanding one. - 1118
pub fn deliverable_act(&self) -> Option<&str> { - 1119
self.acts - 1120
.iter() - 1121
.max_by_key(|act| { - 1122
( - 1123
act.is_effectful(), - 1124
act.requires_execution(), - 1125
act.requires_inspection(), - 1126
) - 1127
}) - 1128
.map(|act| act.as_str()) - 1129
} - 1130
- 1131
/// Merge an extension-provided requirement without allowing it to alter - 1132
/// authority. Invalid declarations are rejected at the contract boundary. - 1133
pub fn merge_declared_requirement( - 1134
&mut self, - 1135
id: impl Into<String>, - 1136
kind: &str, - 1137
description: impl Into<String>, - 1138
importance: &str, - 1139
target: Option<String>, - 1140
) -> Result<(), String> { - 1141
let kind = match kind { - 1142
"deliverable" => RequirementKind::Deliverable, - 1143
"evidence" => RequirementKind::Evidence, - 1144
"constraint" => RequirementKind::Constraint, - 1145
"integrity" => RequirementKind::Integrity, - 1146
other => return Err(format!("unsupported outcome requirement kind: {other}")), - 1147
}; - 1148
let importance = match importance { - 1149
"" | "prefer" => RequirementImportance::Prefer, - 1150
"must" => RequirementImportance::Must, - 1151
other => { - 1152
return Err(format!( - 1153
"unsupported outcome requirement importance: {other}" - 1154
)); - 1155
} - 1156
}; - 1157
let id = id.into(); - 1158
let description = description.into(); - 1159
if id.trim().is_empty() || description.trim().is_empty() { - 1160
return Err("outcome requirement id and description are required".into()); - 1161
} - 1162
if self - 1163
.requirements - 1164
.iter() - 1165
.any(|requirement| requirement.id == id) - 1166
{ - 1167
return Err(format!("duplicate outcome requirement id: {id}")); - 1168
} - 1169
self.requirements.push(OutcomeRequirement { - 1170
id, - 1171
kind, - 1172
description, - 1173
origin: RequirementOrigin::Inferred, - 1174
importance, - 1175
target, - 1176
}); - 1177
Ok(()) - 1178
} - 1179
} - 1180
- 1181
#[cfg(test)] - 1182
mod tests { - 1183
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] - 1184
use super::*; - 1185
use crate::{Act, Reading}; - 1186
- 1187
#[test] - 1188
fn named_saved_file_requires_evidence_but_an_inline_plan_does_not() { - 1189
let authoring = Reading { - 1190
act: Act::Author, - 1191
..Reading::general() - 1192
}; - 1193
let file = OutcomeSpec::from_reading( - 1194
"Create an invitation in this workspace as invitation.html", - 1195
&authoring, - 1196
1, - 1197
); - 1198
assert!(file.expects_saved_file()); - 1199
let plan = OutcomeSpec::from_reading("Create a two-day lunch plan", &authoring, 1); - 1200
assert!(!plan.expects_saved_file()); - 1201
let inspection = OutcomeSpec::from_reading("Explain README.md", &Reading::general(), 1); - 1202
assert!(!inspection.expects_saved_file()); - 1203
// Naming a file inside a question is not asking for one. - 1204
let explanation = - 1205
OutcomeSpec::from_reading("explain how to write a README.md", &Reading::general(), 1); - 1206
assert!(!explanation.expects_saved_file()); - 1207
assert!(!explanation.requires_execution()); - 1208
} - 1209
- 1210
#[test] - 1211
fn baseline_contract_preserves_objective_and_adds_only_inferred_requirements() { - 1212
let reading = Reading { - 1213
act: Act::Answer, - 1214
evidence: Evidence::Cited, - 1215
..Reading::general() - 1216
}; - 1217
let spec = OutcomeSpec::from_reading("current events in India", &reading, 1); - 1218
assert_eq!(spec.objective, "current events in India"); - 1219
assert!(spec.requirements.iter().any(|requirement| { - 1220
requirement.kind == RequirementKind::Evidence - 1221
&& requirement.origin == RequirementOrigin::Inferred - 1222
&& requirement.importance == RequirementImportance::Must - 1223
})); - 1224
assert!( - 1225
spec.requirements - 1226
.iter() - 1227
.all(|requirement| { requirement.origin == RequirementOrigin::Inferred }) - 1228
); - 1229
} - 1230
- 1231
#[test] - 1232
fn produced_output_is_not_called_complete() { - 1233
assert_eq!( - 1234
evaluate_response(Some("answer"), false, false), - 1235
OutcomeStatus::Produced - 1236
); - 1237
} - 1238
- 1239
#[test] - 1240
fn refusal_cannot_satisfy_a_deliverable_requirement() { - 1241
let mut spec = OutcomeSpec::from_reading("create a report", &Reading::general(), 1); - 1242
assert!( - 1243
spec.merge_declared_requirement( - 1244
"report", - 1245
"deliverable", - 1246
"create report.md", - 1247
"must", - 1248
None - 1249
) - 1250
.is_ok() - 1251
); - 1252
let evaluations = evaluate_requirements(&spec, Some("I cannot do that.")); - 1253
assert_eq!(evaluations[1].status, RequirementStatus::Unknown); - 1254
assert_eq!( - 1255
evaluate_completion(OutcomeStatus::Produced, &evaluations, &spec), - 1256
CompletionVerdict::Unknown - 1257
); - 1258
} - 1259
- 1260
#[test] - 1261
fn cited_output_remains_unknown_until_source_support_is_checked() { - 1262
let reading = Reading { - 1263
evidence: Evidence::Cited, - 1264
..Reading::general() - 1265
}; - 1266
let spec = OutcomeSpec::from_reading("research", &reading, 1); - 1267
let evaluations = evaluate_requirements(&spec, Some("claim https://example.com")); - 1268
assert_eq!(evaluations[0].status, RequirementStatus::Met); - 1269
assert_eq!(evaluations[1].status, RequirementStatus::Unknown); - 1270
} - 1271
- 1272
#[test] - 1273
fn retrieval_receipt_is_not_mistaken_for_supported_evidence() { - 1274
let reading = Reading { - 1275
evidence: Evidence::Cited, - 1276
..Reading::general() - 1277
}; - 1278
let spec = OutcomeSpec::from_reading("research", &reading, 1); - 1279
let evaluations = - 1280
evaluate_requirements_with_evidence(&spec, Some("claim https://example.com"), true); - 1281
assert_eq!(evaluations[1].status, RequirementStatus::Unknown); - 1282
assert!(evaluations[1].reason.contains("receipt")); - 1283
} - 1284
- 1285
#[test] - 1286
fn missing_and_unlinked_evidence_are_distinct_failures() { - 1287
let reading = Reading { - 1288
evidence: Evidence::Cited, - 1289
..Reading::general() - 1290
}; - 1291
let spec = OutcomeSpec::from_reading("research", &reading, 1); - 1292
let missing = evaluate_requirements(&spec, Some("plain answer")); - 1293
assert_eq!(missing[1].status, RequirementStatus::Unmet); - 1294
let unlinked = evaluate_requirements(&spec, Some("answer https://example.com")); - 1295
assert_eq!(unlinked[1].status, RequirementStatus::Unknown); - 1296
assert!(unlinked[1].reason.contains("support and freshness")); - 1297
} - 1298
- 1299
#[test] - 1300
fn stale_evidence_is_unknown_and_explains_why() { - 1301
let reading = Reading { - 1302
evidence: Evidence::Cited, - 1303
..Reading::general() - 1304
}; - 1305
let spec = OutcomeSpec::from_reading("current events", &reading, 1); - 1306
let evaluations = evaluate_requirements_with_state( - 1307
&spec, - 1308
Some("answer https://example.com"), - 1309
EvidenceState::Stale, - 1310
); - 1311
assert_eq!(evaluations[1].status, RequirementStatus::Unknown); - 1312
assert!(evaluations[1].reason.contains("stale")); - 1313
} - 1314
- 1315
#[test] - 1316
#[allow(clippy::expect_used)] - 1317
fn evidence_receipt_keeps_observation_and_source_times_distinct() { - 1318
let observed = chrono::DateTime::parse_from_rfc3339("2026-01-02T00:00:00Z") - 1319
.expect("timestamp") - 1320
.with_timezone(&chrono::Utc); - 1321
let published = chrono::DateTime::parse_from_rfc3339("2025-12-31T00:00:00Z") - 1322
.expect("timestamp") - 1323
.with_timezone(&chrono::Utc); - 1324
let receipt = EvidenceReceipt { - 1325
id: "r1".into(), - 1326
kind: "article".into(), - 1327
producer: "web".into(), - 1328
observed_at: observed, - 1329
source_published_at: Some(published), - 1330
effective_at: None, - 1331
state: EvidenceState::Fresh, - 1332
}; - 1333
assert_eq!(receipt.freshness_timestamp(), published); - 1334
assert_eq!(receipt.observed_at, observed); - 1335
assert_eq!( - 1336
evidence_state_from_receipt(observed, &receipt, chrono::Duration::days(1)), - 1337
EvidenceState::Stale - 1338
); - 1339
} - 1340
- 1341
#[test] - 1342
fn evidence_freshness_is_deterministic_and_domain_configurable() { - 1343
let now = chrono::Utc::now(); - 1344
assert_eq!( - 1345
evidence_state_from_age( - 1346
now, - 1347
now - chrono::Duration::hours(1), - 1348
chrono::Duration::hours(2) - 1349
), - 1350
EvidenceState::Fresh - 1351
); - 1352
assert_eq!( - 1353
evidence_state_from_age( - 1354
now, - 1355
now - chrono::Duration::hours(3), - 1356
chrono::Duration::hours(2) - 1357
), - 1358
EvidenceState::Stale - 1359
); - 1360
assert_eq!( - 1361
evidence_state_from_age( - 1362
now, - 1363
now + chrono::Duration::minutes(1), - 1364
chrono::Duration::hours(2) - 1365
), - 1366
EvidenceState::Fresh - 1367
); - 1368
} - 1369
- 1370
#[test] - 1371
fn extension_requirements_are_data_driven_and_narrowing_only() { - 1372
let mut spec = OutcomeSpec::from_reading("make a plan", &Reading::general(), 1); - 1373
let result = spec.merge_declared_requirement( - 1374
"plan-structure", - 1375
"constraint", - 1376
"include assumptions and next steps", - 1377
"must", - 1378
Some("primary".into()), - 1379
); - 1380
assert!(result.is_ok()); - 1381
assert_eq!( - 1382
spec.requirements.last().map(|item| item.origin), - 1383
Some(RequirementOrigin::Inferred) - 1384
); - 1385
assert!( - 1386
spec.merge_declared_requirement("", "constraint", "x", "must", None) - 1387
.is_err() - 1388
); - 1389
assert!( - 1390
spec.merge_declared_requirement("bad", "grant", "x", "must", None) - 1391
.is_err() - 1392
); - 1393
} - 1394
- 1395
#[test] - 1396
fn completion_verdict_never_confuses_output_with_satisfaction() { - 1397
let reading = Reading { - 1398
evidence: Evidence::Cited, - 1399
..Reading::general() - 1400
}; - 1401
let spec = OutcomeSpec::from_reading("research", &reading, 1); - 1402
let evaluations = evaluate_requirements(&spec, Some("answer https://example.com")); - 1403
assert_eq!( - 1404
evaluate_completion(OutcomeStatus::Produced, &evaluations, &spec), - 1405
CompletionVerdict::Unknown - 1406
); - 1407
} - 1408
- 1409
#[test] - 1410
fn completion_verdict_handles_partial_failed_and_cancelled_work() { - 1411
let reading = Reading { - 1412
evidence: Evidence::Cited, - 1413
..Reading::general() - 1414
}; - 1415
let spec = OutcomeSpec::from_reading("research", &reading, 1); - 1416
let partial = vec![ - 1417
RequirementEvaluation { - 1418
requirement_id: "deliverable-1".into(), - 1419
status: RequirementStatus::Met, - 1420
reason: "content exists".into(), - 1421
}, - 1422
RequirementEvaluation { - 1423
requirement_id: "evidence-1".into(), - 1424
status: RequirementStatus::Unmet, - 1425
reason: "no linked evidence".into(), - 1426
}, - 1427
]; - 1428
assert_eq!( - 1429
evaluate_completion(OutcomeStatus::Produced, &partial, &spec), - 1430
CompletionVerdict::Partial - 1431
); - 1432
assert_eq!( - 1433
evaluate_completion(OutcomeStatus::Failed, &[], &spec), - 1434
CompletionVerdict::Failed - 1435
); - 1436
assert_eq!( - 1437
evaluate_completion(OutcomeStatus::Cancelled, &[], &spec), - 1438
CompletionVerdict::Cancelled - 1439
); - 1440
} - 1441
- 1442
#[test] - 1443
fn review_state_is_deterministic() { - 1444
assert_eq!( - 1445
human_review_state(CompletionVerdict::Complete), - 1446
"not_required" - 1447
); - 1448
assert_eq!( - 1449
human_review_state(CompletionVerdict::Unknown), - 1450
"recommended" - 1451
); - 1452
assert_eq!( - 1453
human_review_state(CompletionVerdict::Failed), - 1454
"required_for_recovery" - 1455
); - 1456
} - 1457
- 1458
#[test] - 1459
fn only_explicit_commands_are_control() { - 1460
assert_eq!(parse_command("/status"), Some(Command::Status)); - 1461
assert_eq!(parse_command("/stop"), Some(Command::Cancel)); - 1462
assert_eq!(parse_command("Stop!"), Some(Command::Cancel)); - 1463
assert_eq!(parse_command("pause"), Some(Command::Pause)); - 1464
assert_eq!( - 1465
parse_command("/replan around the new constraint"), - 1466
Some(Command::Replan { - 1467
text: "around the new constraint".into() - 1468
}) - 1469
); - 1470
assert_eq!( - 1471
parse_command("/goal replace ship the index only"), - 1472
Some(Command::GoalReplace { - 1473
text: "ship the index only".into() - 1474
}) - 1475
); - 1476
assert_eq!( - 1477
parse_command("/approve gate-7"), - 1478
Some(Command::Approve { - 1479
gate_id: "gate-7".into() - 1480
}) - 1481
); - 1482
// Natural language is steering, whatever word it starts with. - 1483
for text in [ - 1484
"stop using semicolons in the output", - 1485
"pause the music service before deploying", - 1486
"also include a CSV", - 1487
"status of the migration please", - 1488
"replace the deprecated API call", - 1489
"use a shorter answer", - 1490
] { - 1491
assert_eq!(parse_command(text), None, "{text}"); - 1492
} - 1493
// A slash command without its argument is not a command either. - 1494
assert_eq!(parse_command("/replan"), None); - 1495
assert_eq!(parse_command("/goal replace"), None); - 1496
} - 1497
- 1498
fn intervention(kind: InterventionKind, source: ControlSource) -> InterventionRequest { - 1499
InterventionRequest { - 1500
request_id: "i-1".into(), - 1501
kind, - 1502
text: String::new(), - 1503
source, - 1504
target_revision: Some(1), - 1505
target_session_id: Some("child".into()), - 1506
target_parent_session_id: Some("parent".into()), - 1507
} - 1508
} - 1509
- 1510
fn human() -> ControlSource { - 1511
ControlSource::Human { - 1512
surface: "desktop".into(), - 1513
principal: None, - 1514
} - 1515
} - 1516
- 1517
fn agent(session_id: &str) -> ControlSource { - 1518
ControlSource::Agent { - 1519
session_id: session_id.into(), - 1520
parent_session_id: None, - 1521
} - 1522
} - 1523
- 1524
fn system() -> ControlSource { - 1525
ControlSource::System { - 1526
origin: "webhook".into(), - 1527
} - 1528
} - 1529
- 1530
/// The authority matrix: humans may do anything, agents only their own - 1531
/// children, systems only observe. - 1532
#[test] - 1533
fn intervention_authority_comes_from_the_source() { - 1534
use InterventionDecision as D; - 1535
use InterventionKind as K; - 1536
let decide = |kind: K, source: ControlSource| { - 1537
evaluate_intervention(intervention(kind, source)).decision - 1538
}; - 1539
- 1540
assert_eq!(decide(K::Cancel, human()), D::Accepted); - 1541
assert_eq!(decide(K::Replan, human()), D::Queued); - 1542
assert_eq!(decide(K::Approve, human()), D::Accepted); - 1543
assert!(evaluate_intervention(intervention(K::Replan, human())).creates_revision); - 1544
- 1545
// An agent controlling a child it dispatched. - 1546
assert_eq!(decide(K::Cancel, agent("parent")), D::Accepted); - 1547
// An agent trying to control the session it lives in — or one that - 1548
// is not its child — is refused. - 1549
assert_eq!(decide(K::Cancel, agent("child")), D::Rejected); - 1550
assert_eq!(decide(K::Cancel, agent("sibling")), D::Rejected); - 1551
assert_eq!(decide(K::Pause, agent("stranger")), D::Rejected); - 1552
assert_eq!(decide(K::Resume, agent("stranger")), D::Rejected); - 1553
// Without the target's parentage there is nothing to prove, so the - 1554
// answer is no. - 1555
let mut unknown_parent = intervention(K::Cancel, agent("parent")); - 1556
unknown_parent.target_parent_session_id = None; - 1557
assert_eq!(evaluate_intervention(unknown_parent).decision, D::Rejected); - 1558
assert_eq!(decide(K::Replan, agent("parent")), D::RequiresHuman); - 1559
assert_eq!(decide(K::Approve, agent("parent")), D::Rejected); - 1560
- 1561
assert_eq!(decide(K::Status, system()), D::Accepted); - 1562
assert_eq!(decide(K::Resume, system()), D::Accepted); - 1563
assert_eq!(decide(K::Cancel, system()), D::Rejected); - 1564
assert_eq!(decide(K::Steer, system()), D::Rejected); - 1565
assert_eq!(decide(K::Approve, system()), D::Rejected); - 1566
} - 1567
- 1568
#[test] - 1569
fn domain_verification_oracles_validate_tables_matrices_citations() { - 1570
// Tabular markdown: well-formed - 1571
let valid_table = "| Col A | Col B |\n| --- | --- |\n| Val 1 | Val 2 |\n| Val 3 | Val 4 |"; - 1572
assert!(verify_tabular_data(valid_table).unwrap().is_ok()); - 1573
- 1574
// Tabular markdown: ragged (col count mismatch) - 1575
let ragged_table = "| Col A | Col B |\n| --- | --- |\n| Val 1 |\n| Val 3 | Val 4 |"; - 1576
assert!(verify_tabular_data(ragged_table).unwrap().is_err()); - 1577
- 1578
// Tabular vak block - 1579
let valid_block = "```vak-table\n{\"columns\": [\"A\", \"B\"], \"rows\": [[\"1\", \"2\"], [\"3\", \"4\"]]}\n```"; - 1580
assert!(verify_tabular_data(valid_block).unwrap().is_ok()); - 1581
- 1582
let invalid_block = - 1583
"```vak-table\n{\"columns\": [\"A\", \"B\"], \"rows\": [[\"1\"], [\"3\", \"4\"]]}\n```"; - 1584
assert!(verify_tabular_data(invalid_block).unwrap().is_err()); - 1585
- 1586
// Decision matrix vak block - 1587
let valid_matrix = "```vak-decision\n{\"options\": [{\"label\": \"Option A\"}, {\"label\": \"Option B\"}]}\n```"; - 1588
assert!(verify_decision_matrix(valid_matrix).unwrap().is_ok()); - 1589
- 1590
let single_option_matrix = - 1591
"```vak-decision\n{\"options\": [{\"label\": \"Option A\"}]}\n```"; - 1592
assert!( - 1593
verify_decision_matrix(single_option_matrix) - 1594
.unwrap() - 1595
.is_err() - 1596
); - 1597
- 1598
// Claim citations - 1599
let valid_citations = - 1600
"According to study[^1] and report[^2].\n\n[^1]: Reference one\n[^2]: Reference two"; - 1601
assert!(verify_claim_citations(valid_citations).unwrap().is_ok()); - 1602
- 1603
let unlinked_citations = "According to study[^1] and missing[^3].\n\n[^1]: Reference one"; - 1604
assert!(verify_claim_citations(unlinked_citations).unwrap().is_err()); - 1605
- 1606
// RequirementKind::Integrity evaluation - 1607
let mut spec = OutcomeSpec::from_reading("produce analysis", &Reading::general(), 1); - 1608
spec.merge_declared_requirement( - 1609
"integ-1", - 1610
"integrity", - 1611
"verify data integrity", - 1612
"must", - 1613
None, - 1614
) - 1615
.unwrap(); - 1616
- 1617
let good_eval = - 1618
evaluate_requirements_with_state(&spec, Some(valid_table), EvidenceState::None); - 1619
let integ_eval = good_eval - 1620
.iter() - 1621
.find(|e| e.requirement_id == "integ-1") - 1622
.unwrap(); - 1623
assert_eq!(integ_eval.status, RequirementStatus::Met); - 1624
- 1625
let bad_eval = - 1626
evaluate_requirements_with_state(&spec, Some(ragged_table), EvidenceState::None); - 1627
let bad_integ = bad_eval - 1628
.iter() - 1629
.find(|e| e.requirement_id == "integ-1") - 1630
.unwrap(); - 1631
assert_eq!(bad_integ.status, RequirementStatus::Unmet); - 1632
} - 1633
} - 1634
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.