- 1
//! The seven axes of a reading. - 2
//! - 3
//! These are deliberately **behavioural and orthogonal**, not a subject-matter - 4
//! taxonomy. vak runs everything from "hi" to a multi-month programme, and a - 5
//! domain label ("research", "coding") says nothing about what the runtime - 6
//! should *do*. Each axis below drives one distinct subsystem, and no axis - 7
//! collapses into another: - 8
//! - 9
//! * a long task can be closely watched and a ten-second automation - 10
//! unattended, so `Horizon` is not `Attendance`; - 11
//! * work can be irreversible yet need only an assertion, or inert yet need - 12
//! citations, so `Stakes` is not `Evidence`. - 13
//! - 14
//! Subject matter survives only as an open-vocabulary `domains` tag on - 15
//! [`crate::Reading`], used for skill affinity and telemetry and **never** for - 16
//! control flow. - 17
//! - 18
//! Every ordered axis spells its `rank` out explicitly rather than deriving it - 19
//! from declaration order. `vak_config::PermissionMode::rank` sets that - 20
//! precedent for exactly the reason it applies here: the narrowing lattice in - 21
//! [`crate::Limits`] depends on the ranking being right, and reordering the - 22
//! variants some later day must not silently invert a safety decision. - 23
- 24
use serde::{Deserialize, Serialize}; - 25
- 26
/// The shape of the work — what the runtime must actually do. - 27
/// - 28
/// The `Author` / `Modify` / `Operate` split is the safety-relevant one and is - 29
/// genuinely universal: it means the same thing for prose, a spreadsheet, and - 30
/// a production deploy. Authoring produces new content, modifying changes - 31
/// existing state, operating reaches outside the workspace. - 32
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] - 33
#[serde(rename_all = "kebab-case")] - 34
pub enum Act { - 35
/// Social or conversational. No effect, no retrieval. - 36
Converse, - 37
/// Recall or explain from knowledge already in context. - 38
Answer, - 39
/// Find something: search, grep, retrieve, enumerate. - 40
Locate, - 41
/// Reason over gathered material and produce a judgement. - 42
Analyze, - 43
/// Produce new artifact content — prose, code, config, a plan. - 44
Author, - 45
/// Change existing state that the workspace owns. - 46
Modify, - 47
/// Act on the world outside the workspace: deploy, send, schedule, - 48
/// install, control a service, drive a screen. - 49
Operate, - 50
/// Check a claim: run tests, audit, reproduce. - 51
Verify, - 52
/// Decompose and delegate across workers or flows. - 53
Orchestrate, - 54
/// Meta-work on the agent itself: config, permissions, memory, skills. - 55
Govern, - 56
} - 57
- 58
impl Act { - 59
pub const ALL: [Act; 10] = [ - 60
Act::Converse, - 61
Act::Answer, - 62
Act::Locate, - 63
Act::Analyze, - 64
Act::Author, - 65
Act::Modify, - 66
Act::Operate, - 67
Act::Verify, - 68
Act::Orchestrate, - 69
Act::Govern, - 70
]; - 71
- 72
pub fn as_str(self) -> &'static str { - 73
match self { - 74
Act::Converse => "converse", - 75
Act::Answer => "answer", - 76
Act::Locate => "locate", - 77
Act::Analyze => "analyze", - 78
Act::Author => "author", - 79
Act::Modify => "modify", - 80
Act::Operate => "operate", - 81
Act::Verify => "verify", - 82
Act::Orchestrate => "orchestrate", - 83
Act::Govern => "govern", - 84
} - 85
} - 86
- 87
pub fn parse(value: &str) -> Option<Act> { - 88
Act::ALL - 89
.into_iter() - 90
.find(|act| act.as_str() == value.trim().to_ascii_lowercase()) - 91
} - 92
- 93
/// Whether this act, by its nature, changes something outside the - 94
/// conversation. Used to decide whether an episode that produced no - 95
/// effect can honestly claim completion. - 96
pub fn is_effectful(self) -> bool { - 97
matches!(self, Act::Modify | Act::Operate | Act::Govern) - 98
} - 99
- 100
/// Whether this act, by its nature, demands a runtime execution or - 101
/// file-modification receipt (a bash call or a file write) before it can - 102
/// honestly claim to be done — the same set as [`Act::is_effectful`] plus - 103
/// `Verify`, which has no other way to be proven: the whole act is - 104
/// running a check. `Author` is deliberately absent: producing prose, - 105
/// code, or a plan is proven by the response itself, and demanding a - 106
/// shell or file receipt for a poem is the bug this split exists to - 107
/// avoid. A request that *names* a file deliverable still needs one — - 108
/// that is `OutcomeSpec::requires_execution`, which has the request text - 109
/// and this does not. `Orchestrate` is also absent: delegating to a - 110
/// worker or flow is proven by a tool dispatch, not a shell command or a - 111
/// file write, so it belongs under `requires_tool` instead. - 112
pub fn requires_execution(self) -> bool { - 113
matches!(self, Act::Modify | Act::Operate | Act::Govern | Act::Verify) - 114
} - 115
- 116
/// Whether this act requires inspection, search, or enumeration tools. - 117
pub fn requires_inspection(self) -> bool { - 118
matches!(self, Act::Locate) - 119
} - 120
- 121
/// Whether this act requires any tool invocation at all. `Orchestrate` - 122
/// joins here rather than in `requires_execution`: it needs proof that a - 123
/// worker or flow was actually dispatched, but that proof is a tool - 124
/// call, not specifically a shell command or a file write. - 125
pub fn requires_tool(self) -> bool { - 126
self.requires_execution() || self.requires_inspection() || matches!(self, Act::Orchestrate) - 127
} - 128
} - 129
- 130
/// How long the work lives. Promotion to a durable commitment happens at - 131
/// `Session` and above; below that an intent resolves and dies inside the - 132
/// turn, so a simple prompt pays nothing for this machinery. - 133
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] - 134
#[serde(rename_all = "kebab-case")] - 135
pub enum Horizon { - 136
/// One reply. No tools expected. - 137
Immediate, - 138
/// A handful of tool calls inside one turn. - 139
Turn, - 140
/// Multi-step work that wants a plan inside this session. - 141
Session, - 142
/// Spans sessions, restarts, and potentially months. - 143
Durable, - 144
} - 145
- 146
impl Horizon { - 147
pub const ALL: [Horizon; 4] = [ - 148
Horizon::Immediate, - 149
Horizon::Turn, - 150
Horizon::Session, - 151
Horizon::Durable, - 152
]; - 153
- 154
/// Explicit total order; see the module note on why this is not derived. - 155
pub fn rank(self) -> u8 { - 156
match self { - 157
Horizon::Immediate => 0, - 158
Horizon::Turn => 1, - 159
Horizon::Session => 2, - 160
Horizon::Durable => 3, - 161
} - 162
} - 163
- 164
pub fn as_str(self) -> &'static str { - 165
match self { - 166
Horizon::Immediate => "immediate", - 167
Horizon::Turn => "turn", - 168
Horizon::Session => "session", - 169
Horizon::Durable => "durable", - 170
} - 171
} - 172
- 173
pub fn parse(value: &str) -> Option<Horizon> { - 174
Horizon::ALL - 175
.into_iter() - 176
.find(|h| h.as_str() == value.trim().to_ascii_lowercase()) - 177
} - 178
- 179
/// Work at this horizon earns a durable commitment of its own. - 180
/// - 181
/// Only work that outlives the session: multi-step work inside one - 182
/// session runs under its plan (the managed work contract), and opening a - 183
/// month-long obligation for it left the ledger full of commitments - 184
/// nobody would ever close. - 185
pub fn opens_commitment(self) -> bool { - 186
self == Horizon::Durable - 187
} - 188
} - 189
- 190
/// Blast radius if the work goes wrong. - 191
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] - 192
#[serde(rename_all = "kebab-case")] - 193
pub enum Stakes { - 194
/// No effect at all; nothing to undo. - 195
Inert, - 196
/// Workspace state, recoverable from a checkpoint or version control. - 197
Reversible, - 198
/// Spends money, quota, or meaningful time. - 199
Costly, - 200
/// Cannot be undone by this runtime: external sends, deletes, deploys, - 201
/// payments, anything a third party observes. - 202
Irreversible, - 203
} - 204
- 205
impl Stakes { - 206
pub const ALL: [Stakes; 4] = [ - 207
Stakes::Inert, - 208
Stakes::Reversible, - 209
Stakes::Costly, - 210
Stakes::Irreversible, - 211
]; - 212
- 213
pub fn rank(self) -> u8 { - 214
match self { - 215
Stakes::Inert => 0, - 216
Stakes::Reversible => 1, - 217
Stakes::Costly => 2, - 218
Stakes::Irreversible => 3, - 219
} - 220
} - 221
- 222
pub fn as_str(self) -> &'static str { - 223
match self { - 224
Stakes::Inert => "inert", - 225
Stakes::Reversible => "reversible", - 226
Stakes::Costly => "costly", - 227
Stakes::Irreversible => "irreversible", - 228
} - 229
} - 230
- 231
pub fn parse(value: &str) -> Option<Stakes> { - 232
Stakes::ALL - 233
.into_iter() - 234
.find(|s| s.as_str() == value.trim().to_ascii_lowercase()) - 235
} - 236
- 237
/// Take a checkpoint before the first effect, so `Review`-mode - 238
/// human-in-the-loop has something to offer a reversal against. - 239
pub fn wants_checkpoint(self) -> bool { - 240
self.rank() >= Stakes::Reversible.rank() - 241
} - 242
} - 243
- 244
/// The standard of proof the result must meet. This axis is the one that - 245
/// answers "how do we know it is done": it sets the **minimum** - 246
/// [`Satisfaction`] strength at which a commitment may close `Fulfilled`. - 247
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] - 248
#[serde(rename_all = "kebab-case")] - 249
pub enum Evidence { - 250
/// The agent's word is enough. - 251
None, - 252
/// Claims must name their sources. - 253
Cited, - 254
/// A machine-checkable predicate must pass. - 255
Verified, - 256
/// An independent party must confirm. - 257
Audited, - 258
} - 259
- 260
impl Evidence { - 261
pub const ALL: [Evidence; 4] = [ - 262
Evidence::None, - 263
Evidence::Cited, - 264
Evidence::Verified, - 265
Evidence::Audited, - 266
]; - 267
- 268
pub fn rank(self) -> u8 { - 269
match self { - 270
Evidence::None => 0, - 271
Evidence::Cited => 1, - 272
Evidence::Verified => 2, - 273
Evidence::Audited => 3, - 274
} - 275
} - 276
- 277
pub fn as_str(self) -> &'static str { - 278
match self { - 279
Evidence::None => "none", - 280
Evidence::Cited => "cited", - 281
Evidence::Verified => "verified", - 282
Evidence::Audited => "audited", - 283
} - 284
} - 285
- 286
pub fn parse(value: &str) -> Option<Evidence> { - 287
Evidence::ALL - 288
.into_iter() - 289
.find(|e| e.as_str() == value.trim().to_ascii_lowercase()) - 290
} - 291
- 292
/// The weakest satisfaction strength that may close work held to this - 293
/// standard. This mapping is the closure invariant in one place. - 294
pub fn min_satisfaction(self) -> Satisfaction { - 295
match self { - 296
Evidence::None => Satisfaction::Asserted, - 297
Evidence::Cited => Satisfaction::Cited, - 298
Evidence::Verified => Satisfaction::Observed, - 299
Evidence::Audited => Satisfaction::Attested, - 300
} - 301
} - 302
} - 303
- 304
/// How strongly a completion claim is backed. Ordered weakest to strongest. - 305
/// - 306
/// The distinction that matters: `Asserted` and `Cited` come from the model, - 307
/// while `Observed` and `Attested` come from outside it. The runtime evaluates - 308
/// `Observed` predicates itself and the model never marks one passed — the - 309
/// same separation of powers as permission-before-dispatch. - 310
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash, Serialize, Deserialize)] - 311
#[serde(rename_all = "kebab-case")] - 312
pub enum Satisfaction { - 313
/// The model says so. Transcript only. - 314
#[default] - 315
Asserted, - 316
/// The model says so and names sources that can be followed. - 317
Cited, - 318
/// A predicate the runtime evaluated against the world passed. - 319
Observed, - 320
/// An independent party confirmed: a human, an auditor with no write - 321
/// access to what it audits, or an external receipt. - 322
Attested, - 323
} - 324
- 325
impl Satisfaction { - 326
pub const ALL: [Satisfaction; 4] = [ - 327
Satisfaction::Asserted, - 328
Satisfaction::Cited, - 329
Satisfaction::Observed, - 330
Satisfaction::Attested, - 331
]; - 332
- 333
pub fn rank(self) -> u8 { - 334
match self { - 335
Satisfaction::Asserted => 0, - 336
Satisfaction::Cited => 1, - 337
Satisfaction::Observed => 2, - 338
Satisfaction::Attested => 3, - 339
} - 340
} - 341
- 342
pub fn as_str(self) -> &'static str { - 343
match self { - 344
Satisfaction::Asserted => "asserted", - 345
Satisfaction::Cited => "cited", - 346
Satisfaction::Observed => "observed", - 347
Satisfaction::Attested => "attested", - 348
} - 349
} - 350
- 351
pub fn parse(value: &str) -> Option<Satisfaction> { - 352
Satisfaction::ALL - 353
.into_iter() - 354
.find(|s| s.as_str() == value.trim().to_ascii_lowercase()) - 355
} - 356
- 357
/// Whether evidence of this strength discharges a requirement for - 358
/// `required`. The whole closure invariant reduces to this call. - 359
pub fn satisfies(self, required: Satisfaction) -> bool { - 360
self.rank() >= required.rank() - 361
} - 362
- 363
/// Evidence produced by the model itself rather than observed from - 364
/// outside it. Kept explicit because "the model checked its own work" is - 365
/// the failure mode this lattice exists to make visible. - 366
pub fn is_self_reported(self) -> bool { - 367
matches!(self, Satisfaction::Asserted | Satisfaction::Cited) - 368
} - 369
} - 370
- 371
/// How well specified the request is. - 372
/// - 373
/// Interaction rule, applied in [`crate::engage`]: ambiguity is only worth - 374
/// interrupting for when the stakes are high. `Ambiguous + Inert` proceeds on - 375
/// a stated assumption; `Ambiguous + Irreversible` asks. - 376
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] - 377
#[serde(rename_all = "kebab-case")] - 378
pub enum Clarity { - 379
/// One plausible reading, all required parameters present or inferable. - 380
Clear, - 381
/// One plausible reading, but something needed is missing. - 382
Underspecified, - 383
/// Two or more plausible readings that imply different work. - 384
Ambiguous, - 385
} - 386
- 387
impl Clarity { - 388
pub const ALL: [Clarity; 3] = [Clarity::Clear, Clarity::Underspecified, Clarity::Ambiguous]; - 389
- 390
pub fn rank(self) -> u8 { - 391
match self { - 392
Clarity::Clear => 0, - 393
Clarity::Underspecified => 1, - 394
Clarity::Ambiguous => 2, - 395
} - 396
} - 397
- 398
pub fn as_str(self) -> &'static str { - 399
match self { - 400
Clarity::Clear => "clear", - 401
Clarity::Underspecified => "underspecified", - 402
Clarity::Ambiguous => "ambiguous", - 403
} - 404
} - 405
- 406
pub fn parse(value: &str) -> Option<Clarity> { - 407
Clarity::ALL - 408
.into_iter() - 409
.find(|c| c.as_str() == value.trim().to_ascii_lowercase()) - 410
} - 411
} - 412
- 413
/// A channel of input or output. - 414
/// - 415
/// Modality is a **hard constraint on dispatch**, not a preference: a leg that - 416
/// cannot see is not a valid fallback for a vision turn. Silently dropping an - 417
/// image because the serving model is text-only is precisely the "everything - 418
/// worked as designed and the outcome was a lie" failure that `vak_core::reach` - 419
/// exists to prevent, so an unsatisfiable modality is a typed error rather than - 420
/// a quiet degradation. - 421
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] - 422
#[serde(rename_all = "kebab-case")] - 423
pub enum Modality { - 424
Text, - 425
Image, - 426
Audio, - 427
Video, - 428
/// Live screen control: reading pixels and driving a pointer/keyboard. - 429
Screen, - 430
/// Structured records — tabular, JSON, a query result set. - 431
Data, - 432
/// An open-ended feed consumed or produced incrementally. - 433
Stream, - 434
} - 435
- 436
impl Modality { - 437
pub const ALL: [Modality; 7] = [ - 438
Modality::Text, - 439
Modality::Image, - 440
Modality::Audio, - 441
Modality::Video, - 442
Modality::Screen, - 443
Modality::Data, - 444
Modality::Stream, - 445
]; - 446
- 447
pub fn as_str(self) -> &'static str { - 448
match self { - 449
Modality::Text => "text", - 450
Modality::Image => "image", - 451
Modality::Audio => "audio", - 452
Modality::Video => "video", - 453
Modality::Screen => "screen", - 454
Modality::Data => "data", - 455
Modality::Stream => "stream", - 456
} - 457
} - 458
- 459
pub fn parse(value: &str) -> Option<Modality> { - 460
Modality::ALL - 461
.into_iter() - 462
.find(|m| m.as_str() == value.trim().to_ascii_lowercase()) - 463
} - 464
- 465
/// Whether serving this modality needs a capability beyond plain text - 466
/// completion. `Text` and `Data` ride the ordinary text interface; the - 467
/// rest constrain which legs may serve the turn. - 468
pub fn needs_declared_support(self) -> bool { - 469
!matches!(self, Modality::Text | Modality::Data) - 470
} - 471
} - 472
- 473
/// Whether a human is available to answer, as **observed by the runtime**. - 474
/// - 475
/// This is a fact about the environment, not a grant. It is deliberately - 476
/// separate from [`crate::Autonomy`], which is a fact about what a human has - 477
/// delegated. Conflating the two is why agents nag when you wanted autonomy - 478
/// and barrel ahead when nobody is watching. - 479
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash, Serialize, Deserialize)] - 480
#[serde(rename_all = "kebab-case")] - 481
pub enum Attendance { - 482
/// Someone is here now and will answer within seconds. - 483
/// - 484
/// The default, matching `Core`'s existing optimistic `approver_answerable` - 485
/// and its `reconcile_answerability` correction: assume a human is - 486
/// reachable, then let the surface that actually serves the run say - 487
/// otherwise. Assuming nobody is there would make every gate an immediate - 488
/// denial on surfaces that had simply not answered yet. - 489
#[default] - 490
Interactive, - 491
/// Someone is reachable and will answer within minutes or hours. - 492
Supervised, - 493
/// Nobody will answer. Gates raised here are not questions, they are - 494
/// denials, unless the work can suspend and wait. - 495
Unattended, - 496
} - 497
- 498
impl Attendance { - 499
pub const ALL: [Attendance; 3] = [ - 500
Attendance::Interactive, - 501
Attendance::Supervised, - 502
Attendance::Unattended, - 503
]; - 504
- 505
/// Ranked by how much supervision is available, most first. - 506
/// - 507
/// An ordering for reporting and for choosing the more cautious of two - 508
/// observations — deliberately *not* a cap on [`crate::Autonomy`]: - 509
/// delegation and oversight are independent facts, and their - 510
/// interaction lives in [`crate::Authority::gate_fallback`]. - 511
pub fn rank(self) -> u8 { - 512
match self { - 513
Attendance::Unattended => 0, - 514
Attendance::Supervised => 1, - 515
Attendance::Interactive => 2, - 516
} - 517
} - 518
- 519
pub fn as_str(self) -> &'static str { - 520
match self { - 521
Attendance::Interactive => "interactive", - 522
Attendance::Supervised => "supervised", - 523
Attendance::Unattended => "unattended", - 524
} - 525
} - 526
- 527
pub fn parse(value: &str) -> Option<Attendance> { - 528
Attendance::ALL - 529
.into_iter() - 530
.find(|a| a.as_str() == value.trim().to_ascii_lowercase()) - 531
} - 532
- 533
/// Whether a raised gate can actually reach somebody in time to matter. - 534
pub fn can_answer_now(self) -> bool { - 535
matches!(self, Attendance::Interactive) - 536
} - 537
} - 538
- 539
/// The epistemic cognitive stance for this turn — how the model postures its reasoning, - 540
/// evidence evaluation, and communication across any domain of work. - 541
/// - 542
/// While `Act` describes *what* action is being requested, `EpistemicStance` defines - 543
/// *how to think, inquire, and communicate*. This is completely domain-neutral: - 544
/// it governs financial modeling, legal research, scientific synthesis, creative writing, - 545
/// system operations, data analysis, and software engineering with equal fidelity. - 546
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] - 547
#[serde(rename_all = "kebab-case")] - 548
pub enum EpistemicStance { - 549
/// Social, relational, or conversational. Low cognitive ceremony, direct and friendly. - 550
Conversational, - 551
/// Direct explanation or recall from existing knowledge. Clear and concise. - 552
#[default] - 553
DirectAnswer, - 554
/// Critical evaluation, synthesis, and deduction. Claims are separated from verified - 555
/// facts, counter-hypotheses are evaluated, and assertions require citations. - 556
Analytical, - 557
/// Breadth-first mapping, discovery, and search. Systematically surveys options, - 558
/// trade-offs, and unknowns without premature closure. - 559
Exploratory, - 560
/// High-density synthesis, creation, or composition (prose, plans, designs, code, briefs). - 561
/// Focuses on tone alignment, structural elegance, and publication-ready finish. - 562
Generative, - 563
/// State-changing action, execution, or environment mutation. Emphasizes inspecting - 564
/// state first, minimal blast radius, invariant verification, and rollback awareness. - 565
Operational, - 566
/// Root-cause investigation, discrepancy audit, or debugging. Isolates underlying causes - 567
/// before proposing repairs; tests hypotheses against observed evidence. - 568
Diagnostic, - 569
} - 570
- 571
impl EpistemicStance { - 572
pub const ALL: [EpistemicStance; 7] = [ - 573
EpistemicStance::Conversational, - 574
EpistemicStance::DirectAnswer, - 575
EpistemicStance::Analytical, - 576
EpistemicStance::Exploratory, - 577
EpistemicStance::Generative, - 578
EpistemicStance::Operational, - 579
EpistemicStance::Diagnostic, - 580
]; - 581
- 582
pub fn as_str(self) -> &'static str { - 583
match self { - 584
EpistemicStance::Conversational => "conversational", - 585
EpistemicStance::DirectAnswer => "direct-answer", - 586
EpistemicStance::Analytical => "analytical", - 587
EpistemicStance::Exploratory => "exploratory", - 588
EpistemicStance::Generative => "generative", - 589
EpistemicStance::Operational => "operational", - 590
EpistemicStance::Diagnostic => "diagnostic", - 591
} - 592
} - 593
- 594
pub fn parse(value: &str) -> Option<EpistemicStance> { - 595
EpistemicStance::ALL - 596
.into_iter() - 597
.find(|s| s.as_str() == value.trim().to_ascii_lowercase()) - 598
} - 599
- 600
/// Concise, universal operating guidelines for this stance. - 601
/// Free of domain-specific jargon; applies equally across all disciplines. - 602
pub fn guideline_prompt(self) -> &'static str { - 603
match self { - 604
EpistemicStance::Conversational => { - 605
"Engage directly and naturally. Keep replies concise and conversational; \ - 606
do not force structured workflows or unprompted actions when simple dialogue is requested." - 607
} - 608
EpistemicStance::DirectAnswer => { - 609
"Provide a clear, direct answer to the question. Avoid unnecessary meta-commentary, \ - 610
unsolicited execution plans, or unwarranted tool calls when knowledge in context suffices." - 611
} - 612
// Citation is demanded by the intent note when the request asks - 613
// for it, not by the stance: measured live, "cite sources for - 614
// every factual assertion" made a small local model deliberate - 615
// and emit nothing on requests that asked for no sources. - 616
EpistemicStance::Analytical => { - 617
"Scrutinize claims objectively. Separate verified facts from inferences and \ - 618
evaluate counter-arguments or alternative explanations before concluding." - 619
} - 620
EpistemicStance::Exploratory => { - 621
"Map the landscape systematically. Prioritize breadth, surface key trade-offs, \ - 622
and report gaps, uncertainties, or negative findings honestly without forcing premature conclusions." - 623
} - 624
EpistemicStance::Generative => { - 625
"Focus on polished, high-density craftsmanship. Align voice and tone to the audience, \ - 626
eliminate filler and boilerplate, and deliver complete, publication-ready outputs." - 627
} - 628
EpistemicStance::Operational => { - 629
"Inspect existing state before acting. Minimize blast radius, verify outcomes \ - 630
immediately after each step, and ensure changes can be safely understood or reversed." - 631
} - 632
EpistemicStance::Diagnostic => { - 633
"Isolate root causes before applying fixes. Form explicit hypotheses, test them \ - 634
against observed evidence, and verify edge cases before concluding an issue is resolved." - 635
} - 636
} - 637
} - 638
} - 639
- 640
#[cfg(test)] - 641
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] - 642
mod tests { - 643
use super::*; - 644
- 645
#[test] - 646
fn ranks_are_total_and_strictly_increasing() { - 647
for pair in Horizon::ALL.windows(2) { - 648
assert!(pair[0].rank() < pair[1].rank()); - 649
} - 650
for pair in Stakes::ALL.windows(2) { - 651
assert!(pair[0].rank() < pair[1].rank()); - 652
} - 653
for pair in Evidence::ALL.windows(2) { - 654
assert!(pair[0].rank() < pair[1].rank()); - 655
} - 656
for pair in Satisfaction::ALL.windows(2) { - 657
assert!(pair[0].rank() < pair[1].rank()); - 658
} - 659
for pair in Clarity::ALL.windows(2) { - 660
assert!(pair[0].rank() < pair[1].rank()); - 661
} - 662
} - 663
- 664
#[test] - 665
fn every_axis_round_trips_through_its_wire_name() { - 666
for act in Act::ALL { - 667
assert_eq!(Act::parse(act.as_str()), Some(act)); - 668
} - 669
for horizon in Horizon::ALL { - 670
assert_eq!(Horizon::parse(horizon.as_str()), Some(horizon)); - 671
} - 672
for stakes in Stakes::ALL { - 673
assert_eq!(Stakes::parse(stakes.as_str()), Some(stakes)); - 674
} - 675
for evidence in Evidence::ALL { - 676
assert_eq!(Evidence::parse(evidence.as_str()), Some(evidence)); - 677
} - 678
for clarity in Clarity::ALL { - 679
assert_eq!(Clarity::parse(clarity.as_str()), Some(clarity)); - 680
} - 681
for modality in Modality::ALL { - 682
assert_eq!(Modality::parse(modality.as_str()), Some(modality)); - 683
} - 684
for attendance in Attendance::ALL { - 685
assert_eq!(Attendance::parse(attendance.as_str()), Some(attendance)); - 686
} - 687
for satisfaction in Satisfaction::ALL { - 688
assert_eq!( - 689
Satisfaction::parse(satisfaction.as_str()), - 690
Some(satisfaction) - 691
); - 692
} - 693
for stance in EpistemicStance::ALL { - 694
assert_eq!(EpistemicStance::parse(stance.as_str()), Some(stance)); - 695
assert!(!stance.guideline_prompt().is_empty()); - 696
} - 697
} - 698
- 699
/// The closure invariant reduced to its smallest statement: self-reported - 700
/// evidence can never discharge a requirement for observed or attested - 701
/// proof. If this ever passes, a model can close its own audit. - 702
#[test] - 703
fn self_reported_evidence_never_satisfies_external_requirements() { - 704
for weak in [Satisfaction::Asserted, Satisfaction::Cited] { - 705
assert!(weak.is_self_reported()); - 706
assert!(!weak.satisfies(Satisfaction::Observed)); - 707
assert!(!weak.satisfies(Satisfaction::Attested)); - 708
} - 709
assert!(Satisfaction::Observed.satisfies(Satisfaction::Observed)); - 710
assert!(Satisfaction::Attested.satisfies(Satisfaction::Observed)); - 711
assert!(!Satisfaction::Observed.satisfies(Satisfaction::Attested)); - 712
} - 713
- 714
#[test] - 715
fn evidence_maps_onto_the_satisfaction_lattice_monotonically() { - 716
for pair in Evidence::ALL.windows(2) { - 717
assert!(pair[0].min_satisfaction().rank() < pair[1].min_satisfaction().rank()); - 718
} - 719
} - 720
} - 721
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.