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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//! Assertions: what a command CLAIMED, checked after it ran.
//!
//! Every rule in `rules/` reads a command before it runs and can refuse it.
//! That catches a mistake visible in the command string. It cannot catch the
//! other half, which is a command that ran, exited 0, and did not do the thing:
//!
//! - `git push` printing "Everything up-to-date" because the branch was never
//! the one you thought, or reaching a remote that took the ref and dropped
//! it;
//! - `gh run watch --exit-status` returning 0 for a run that concluded
//! `failure`;
//! - `git tag v1.4.0` naming the commit BEFORE the one you just made, so CI
//! builds the new version number out of stale code.
//!
//! None of those is a failure anybody can see. The exit code says success, the
//! output looks like success, and the model reports success — which is exactly
//! the admission test this crate applies to any new guard: *does the failure it
//! prevents go unnoticed?*
//!
//! ## Only successful calls
//!
//! Claude Code sends a failed tool call to `PostToolUseFailure`, a separate
//! event this crate ignores. So everything reaching here claimed to work, and
//! an assertion's whole job is to ask whether the claim holds.
//!
//! ## The same two halves as a rule, on the other side of execution
//!
//! [`Assertion::examine`] is pure — it reads the command string and nothing
//! else, which is what lets the backtester replay it against a transcript.
//! [`Assertion::verify`] is the one place the world may be consulted, mirroring
//! `Rule::confirm`. It is never replayed: the world has moved since those
//! commands ran, so a replayed verdict would describe today rather than then.
//!
//! ## An assertion cannot refuse
//!
//! The tool already ran; there is nothing left to deny. A `deny` stance on an
//! assertion therefore speaks exactly like `advise`, the same way it does at a
//! session opening. What an assertion can do is put a FACT in front of the
//! model — "the remote is still on abc123" — which is a thing it cannot skim
//! past, unlike advice.
//!
//! ## Failing to establish something is silence
//!
//! A refspec this crate cannot read unambiguously, a remote that needs a
//! password, a deadline, a `cwd` that no longer exists: all [`Verdict::Unknown`],
//! all silent. An assertion that guessed would teach the model to ignore the
//! channel, and the channel is the entire value.
use Range;
use ;
use ;
use crate;
use crateParsed;
/// What the command said it did. Produced by a pure `examine`.
pub const ASSERTIONS: & = &;
/// Consumed by `explain` and `graduate` once this tier has a measured rate;
/// kept beside `ASSERTIONS` so the lookup has one spelling from the start.
/// Every assertion whose claim is present in this command.
///
/// Panic-isolated per assertion, like `rules::examine_all`: one bad assertion
/// must not take the others with it.
/// How long a single `verify` may take before it is abandoned.
///
/// A hook runs with no controlling terminal. A network round trip is fine; a
/// prompt nobody can answer is not, and neither is a remote that hangs. This
/// bounds the pathological case, and the environment below prevents the common
/// one.
const DEADLINE: Duration = from_secs;
/// Run a command that must never block on a human.
///
/// Hooks have no `/dev/tty`, so a credential prompt does not fail — it HANGS,
/// and it hangs the session, not just this process. Every one of these
/// variables closes one door: git's own prompt, the askpass helper it falls
/// back to, ssh's password prompt, and ssh's host-key question. Without them a
/// guard installed to make pushing safer would occasionally make it impossible.