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
//! Per-command `tracing` vocabulary, and the redaction rule it follows.
//!
//! # Why this lives at one seam
//!
//! Every command word a script runs passes through [`super::Evaluator::run_argv`], after
//! [`crate::dispatch::resolve`] has decided what it is. One span there therefore covers every
//! builtin, capability call, shell function, refused word, and unknown word — including builtins
//! that do not exist yet. Instrumenting the individual builtin implementations instead would leave
//! each newly added one silently untraced, and would put the same twenty-line preamble in twenty
//! files.
//!
//! # What is recorded, and what is withheld
//!
//! This crate knows nothing about where its spans go: it emits plain `tracing` spans and events,
//! and the embedding binary's subscriber decides whether they reach a terminal, a file, or a
//! remote collector. It must therefore assume they leave the process, and record only what is safe
//! to export:
//!
//! - the resolution kind, the argument *count*, the duration, the exit code, and a stable outcome
//! label — all bounded, low-cardinality, and derived rather than copied from the script;
//! - the command word itself, but **only when it came from a fixed vocabulary this crate owns**: a
//! builtin name, a control word, a word the shell refuses by name, or a capability identifier.
//!
//! Argument *values* are never recorded in any form. `curl -d '{"apiKey":...}'` and
//! `cap some.id '{"token":...}'` put secrets in argv exactly the way capability input does, and
//! capability input is already excluded from this workspace's telemetry. For the same reason a
//! model-authored command word — a shell function's name, or a word that resolved to nothing — is
//! reported as [`WITHHELD`] rather than copied, mirroring the runner's existing refusal to copy a
//! model-selected invalid tool name into a rejection event.
use crate::;
/// Stands in for a command word this crate declines to copy into telemetry.
///
/// A fixed placeholder rather than an absent field: "this word was model-authored" is itself worth
/// knowing, and a missing field would be indistinguishable from instrumentation that never ran.
pub const WITHHELD: &str = "<withheld>";
/// Command words [`super::Evaluator::run_control_word`] executes itself, before dispatch.
///
/// Classification has to happen *before* the word runs, so that the span and its opening event
/// carry the kind from the start; `run_control_word` cannot be asked, because it executes as it
/// matches. This list is that question's answer, and
/// [`super::tests::control_words_and_their_dispatcher_agree`] pins the two together.
pub const CONTROL_WORDS: & = &;
/// Reports whether the evaluator owns this command word rather than the dispatch table.
pub
/// How one command word resolved, as a stable telemetry label.
///
/// This mirrors [`Resolution`] rather than reusing it: `Resolution` carries a `&'static dyn
/// Builtin` and a rejection reason that telemetry has no business holding, and it gains variants
/// for dispatch reasons, not for reporting ones.
pub
/// Returns the command word when exporting it is safe, and [`WITHHELD`] when it is not.
pub
/// Maps one command's exit code onto a stable outcome label.
///
/// A denial, a missing capability, and a generic failure stay distinct here for the same reason
/// [`crate::CapabilityCallResult`] keeps them distinct: an authorization refusal is materially
/// different telemetry from a capability that ran and errored, and flattening the two hides the
/// refusal behind the noise of ordinary failures.
pub
/// Returns the exit code a fatal error will make the whole script report.
///
/// [`super::Evaluator::report_fatal`] renders its message from the same match, so a command's
/// recorded exit code cannot drift from the one the script actually exits with.
pub
/// Maps a fatal error onto its outcome label.
///
/// These do not go through [`outcome_label`]: both a refused construct and an exhausted budget
/// exit with code 2, and "the script asked for `eval`" is a different operational story from "the
/// script ran out of steps".
pub