1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! The **effect vocabulary** (VISION §4.11 item 1) and the classification of one
//! invocation into it.
//!
//! The vocabulary classifies **invocations, never tool names**. That is the
//! whole reason the shipped grant stays whole-pool (bl-7fc8): `bash` is every
//! class at once, so a per-name allow-list is theatre and only per-invocation
//! adjudication can tell a `ls` from a `curl | sh`.
//!
//! Seven classes. Six are reaches, ordered by how far each goes past the job;
//! the seventh is the absence of one:
//!
//! | Class | Reaches |
//! |---|---|
//! | [`Read`](Effect::Read) | observes only |
//! | [`TargetWrite`](Effect::TargetWrite) | the writable root, or the world's own substrates through their gated verbs |
//! | [`Process`](Effect::Process) | mints agents or processes beyond the invocation |
//! | [`OpenWorld`](Effect::OpenWorld) | past the root and the world: network egress, host writes, a `cd` out |
//! | [`Destructive`](Effect::Destructive) | irreversible loss: history rewrite, forced refs, deletion past git's reach |
//! | [`Secret`](Effect::Secret) | credentials and environment |
//! | [`Opaque`](Effect::Opaque) | **unknown** — this control could not read what the invocation does |
//!
//! **There is no arm from a tool NAME to a passing class** (bl-72bd). Names are
//! folded into a closed enum first ([`intrinsic::Known`]) and matched
//! exhaustively, so a name added without a row does not compile; everything the
//! enum does not name goes to [`routed`], whose two answers are the command
//! line's own class and [`Opaque`](Effect::Opaque). The arm this replaced read
//! `other => OpenWorld`, and open-world passes: a foot's `box2_shell` running
//! `rm -rf` was therefore passed unread while the engine's own `bash` refused
//! the same line. Falling off a match into the most permissive class is the one
//! answer nobody chose, and it is now unrepresentable rather than merely fixed.
use Root;
use Request;
/// The input field a command line rides in — litany's own `bash` schema and
/// every thrall shell tool's ([`routed`]), said once here so the built-in and
/// the routed lane read the same field name and cannot drift.
const COMMAND: &str = "command";
/// The intrinsic map: the closed set of names this control implements a row
/// for, and the row each carries.
/// The two intrinsic rows judged against the writable root at consult time.
/// The fail-closed lane for every name the intrinsic map does not hold
/// (bl-72bd) — a foot's routed tool, or anything a later litany adds.
/// A tool's reach, in the seven-class vocabulary. Ordered: a higher variant is
/// a wider reach, which is what lets a compound command take the worst of its
/// parts without a table of pairs. [`Opaque`](Effect::Opaque) is highest
/// because an unread invocation may be any of them — the fold has to carry the
/// unknown outward, never let a known part bury it.
/// A classified invocation: its reach, and the one clause that says why. The
/// clause is what a refusal hands the model and a hold hands the operator, so
/// it names concrete things (the command, the path) and never a doc coordinate.
/// Classify one invocation. Total over every tool name and every input shape:
/// an input that does not match its schema simply yields no operands, and a
/// name no row names goes to the [`routed`] lane rather than to a default arm.