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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "ev", version, about = "git for decisions")]
struct Cli {
#[command(subcommand)]
cmd: Cmd,
}
#[derive(Subcommand)]
enum Cmd {
/// Create the .evolving/ store
Init,
/// Print one decision in full
Show { id: String },
/// List every decision in the ledger (id, status, decision).
List,
/// Show the decision lineage from HEAD back to genesis.
Log,
/// Boot-read: the user-ruled decisions and the roads they rejected
Brief {
/// Cap the number of decisions shown (overrides config brief_limit; 0 = show all).
#[arg(long)]
limit: Option<usize>,
},
/// Audit the chain + refusals
Verify {
/// reproduce the frozen golden vectors and exit
#[arg(long)]
self_test: bool,
},
/// Record a decision (with grounds + roads-not-taken)
Decide {
/// The decision text; omit it and pass --from-git <commit> to seed from a commit envelope.
/// allow_hyphen_values lets a leading --from-git reach us; cmd::decide re-routes it into args.
#[arg(allow_hyphen_values = true)]
decision: Option<String>,
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},
/// Attach an existing test to a decision's ground (writes a new child)
Guard {
selector: String,
id: String,
target: Option<String>,
#[arg(long)]
counter_test: String,
#[arg(long = "on-platform")]
platforms: Vec<String>,
#[arg(long = "triggered-by")]
triggered_by: Vec<String>,
#[arg(long = "surface")]
surfaces: Vec<String>,
#[arg(long)]
verified_at_sha: Option<String>,
#[arg(long)]
blame: Option<String>,
#[arg(long)]
authority: Option<String>,
},
/// Evaluate bound checks against cached receipts and report a flat verdict set.
Check {
/// Exit non-zero if any ground is not green.
#[arg(long)]
exit_on_red: bool,
/// Run each bound test (that declares --platform) locally and record a receipt before evaluating.
#[arg(long)]
run: bool,
/// The platform this --run represents (which declared platform the local run satisfies).
#[arg(long, default_value = "local")]
platform: String,
/// Use only the cached staleness reference; never resolve it fresh (non-blocking).
#[arg(long)]
offline: bool,
/// Platforms THIS runner speaks for (comma-separated). Declared platforms not in this
/// set are exempt here, not not-run. Omit to attest ALL declared platforms (default).
#[arg(long, value_delimiter = ',')]
attest: Vec<String>,
},
/// Backfill an existing decision history into the ledger (multi-source, idempotent).
Migrate {
/// A source to import, as `<kind>:<path>` — kind ∈ {gitlog, to-human, decisions-immutable,
/// escalation}. Repeatable; sources are imported in deterministic source_key order.
#[arg(long = "source")]
sources: Vec<String>,
/// Parse + report what WOULD import without writing any tick.
#[arg(long)]
dry_run: bool,
/// Reconcile mode: join the --against source against the store and report the buckets
/// (in-both / source-only gap / store-only / un-keyable) instead of importing.
#[arg(long)]
reconcile: bool,
/// The source to reconcile against, as `<kind>:<path>` (with --reconcile).
#[arg(long)]
against: Option<String>,
/// Fallback author for any source record carrying no author (R5 stays intact — no fabrication).
#[arg(long)]
blame: Option<String>,
/// Harvest an existing test as a bound check on the named selector (counter_test None, full
/// liveness still required). Prints the harvested check shape; does not write a tick by itself.
#[arg(long)]
bind_check: Option<String>,
/// The platforms the --bind-check harvest declares (with --bind-check).
#[arg(long = "on-platform")]
platforms: Vec<String>,
/// The triggered-by paths the --bind-check harvest declares (with --bind-check).
#[arg(long = "triggered-by")]
triggered_by: Vec<String>,
/// The surfaces the --bind-check harvest declares (with --bind-check).
#[arg(long = "surface")]
surfaces: Vec<String>,
/// The 40-hex sha the --bind-check harvest was verified at (defaults to HEAD).
#[arg(long)]
verified_at_sha: Option<String>,
/// A `<source_key> <bucket>` map file (one pair per line, `#` comments + blanks skipped) tagging
/// each imported decision with its A/B/C/D jurisdiction. Omitted ⇒ every record imports untagged.
#[arg(long = "jurisdiction-map")]
jurisdiction_map: Option<String>,
},
/// Correct a stale non-hashed tag (authority/jurisdiction/provenance) by appending a child.
Correct {
/// The tick id whose tag to correct.
id: String,
#[arg(long)]
authority: Option<String>,
#[arg(long)]
jurisdiction: Option<String>,
#[arg(long)]
provenance: Option<String>,
#[arg(long)]
blame: Option<String>,
},
/// Reverse lookup: name the decision + ground a test selector guards.
Why {
/// The bound test selector to look up.
selector: String,
},
/// Pull the full decision object (decision, grounds + current verdicts, roads-not-taken). Present only.
Reopen {
/// The tick id to reopen.
id: String,
},
}
fn main() -> std::process::ExitCode {
let cli = Cli::parse();
let repo = std::env::current_dir().expect("cwd");
match cli.cmd {
Cmd::Init => ev::cmd::init(&repo),
Cmd::Show { id } => ev::cmd::show(&repo, &id),
Cmd::List => ev::cmd::list(&repo),
Cmd::Log => ev::cmd::log(&repo),
Cmd::Brief { limit } => ev::cmd::brief(&repo, limit),
Cmd::Verify { self_test } => ev::cmd::verify_cmd(&repo, self_test),
Cmd::Decide { decision, args } => ev::cmd::decide(&repo, decision.as_deref(), &args),
Cmd::Guard {
selector,
id,
target,
counter_test,
platforms,
triggered_by,
surfaces,
verified_at_sha,
blame,
authority,
} => ev::cmd::guard(
&repo,
ev::guard::GuardArgs {
selector,
id,
target,
counter_test,
platforms,
triggered_by,
surfaces,
verified_at_sha,
blame,
authority,
},
),
Cmd::Check {
exit_on_red,
run,
platform,
offline,
attest,
} => ev::cmd::check(&repo, exit_on_red, run, &platform, offline, attest),
Cmd::Migrate {
sources,
dry_run,
reconcile,
against,
blame,
bind_check,
platforms,
triggered_by,
surfaces,
verified_at_sha,
jurisdiction_map,
} => ev::cmd::migrate(
&repo,
ev::cmd::MigrateArgs {
sources,
dry_run,
reconcile,
against,
blame,
bind_check,
platforms,
triggered_by,
surfaces,
verified_at_sha,
jurisdiction_map,
},
),
Cmd::Correct {
id,
authority,
jurisdiction,
provenance,
blame,
} => ev::cmd::correct(
&repo,
ev::correct::CorrectArgs {
id,
authority,
jurisdiction,
provenance,
blame,
},
),
Cmd::Why { selector } => ev::cmd::why(&repo, &selector),
Cmd::Reopen { id } => ev::cmd::reopen(&repo, &id),
}
}