fleetproof 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fleetproof-0.3.0/src/fleetproof.egg-info → fleetproof-0.4.0}/PKG-INFO +200 -35
- {fleetproof-0.3.0 → fleetproof-0.4.0}/README.md +199 -34
- {fleetproof-0.3.0 → fleetproof-0.4.0}/pyproject.toml +1 -1
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/__init__.py +1 -1
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/checker.py +166 -27
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/checks.py +146 -5
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/cli.py +320 -11
- fleetproof-0.4.0/src/fleetproof/hookgate.py +1051 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/ledger.py +456 -21
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/report.py +31 -5
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/runlog.py +11 -3
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/telemetry.py +29 -7
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/telemetry_export.py +14 -4
- {fleetproof-0.3.0 → fleetproof-0.4.0/src/fleetproof.egg-info}/PKG-INFO +200 -35
- fleetproof-0.4.0/tests/test_checker.py +467 -0
- fleetproof-0.4.0/tests/test_checks.py +280 -0
- fleetproof-0.4.0/tests/test_cli.py +677 -0
- fleetproof-0.4.0/tests/test_hookgate.py +1651 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/tests/test_ledger.py +142 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/tests/test_report.py +11 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/tests/test_runlog.py +33 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/tests/test_telemetry.py +36 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/tests/test_telemetry_export.py +22 -0
- fleetproof-0.3.0/src/fleetproof/hookgate.py +0 -624
- fleetproof-0.3.0/tests/test_checker.py +0 -244
- fleetproof-0.3.0/tests/test_checks.py +0 -107
- fleetproof-0.3.0/tests/test_cli.py +0 -348
- fleetproof-0.3.0/tests/test_hookgate.py +0 -633
- {fleetproof-0.3.0 → fleetproof-0.4.0}/LICENSE +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/setup.cfg +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/__main__.py +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof/config.py +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof.egg-info/SOURCES.txt +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof.egg-info/dependency_links.txt +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof.egg-info/entry_points.txt +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof.egg-info/requires.txt +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/src/fleetproof.egg-info/top_level.txt +0 -0
- {fleetproof-0.3.0 → fleetproof-0.4.0}/tests/test_adversarial_regressions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fleetproof
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Independent, out-of-band verification for agent fleets.
|
|
5
5
|
Author: Gathrazio
|
|
6
6
|
License: MIT
|
|
@@ -28,6 +28,16 @@ records what your agents do to a durable run log, and — critically — grades
|
|
|
28
28
|
work with a checker that runs in a separate process from the agent that did
|
|
29
29
|
it. An agent may author the checks; it never executes and grades its own work.
|
|
30
30
|
|
|
31
|
+
> **Install and enable the plugin *before* the session starts.** Hooks
|
|
32
|
+
> register at session start, so a mid-session install is a **silently inert
|
|
33
|
+
> gate**: nothing fires, nothing blocks, and every "your stop will be gated"
|
|
34
|
+
> belief is false — the exact failure mode this tool exists to prevent, in the
|
|
35
|
+
> tool itself. Preflight every gated session: run one trivial tool call, then
|
|
36
|
+
> confirm a `claude-tool-*` run record with a non-null session id appeared
|
|
37
|
+
> under `.fleetproof/runs/`. No record means not live. The CLI backstops this:
|
|
38
|
+
> `fleetproof fleet`, `check`, and `report` each warn when the current session
|
|
39
|
+
> has no hook-produced record.
|
|
40
|
+
|
|
31
41
|
## Why this exists
|
|
32
42
|
|
|
33
43
|
The failure mode this targets is the false "done": an agent reports a task
|
|
@@ -75,6 +85,7 @@ No language model sits in the grading path. Grading is comparison.
|
|
|
75
85
|
| `fleetproof fleet` | The dispatch board: every dispatch, its state, its tier, and whether anything graded it. |
|
|
76
86
|
| `fleetproof report` | One self-contained HTML file: per run, claimed-done vs. independently-verified. |
|
|
77
87
|
| `fleetproof telemetry` | v0.3: per-dispatch outcome records, a local reliability summary, and a strict-allowlist export. |
|
|
88
|
+
| `fleetproof arm` / `disarm`, `dispatch park`, `fleet --orphans` | v0.4: the fleet-operating surface — phase arming, deliberate termination, and the orphan-stop view. |
|
|
78
89
|
|
|
79
90
|
Runtime dependencies: none (Python standard library only). A tool whose job is
|
|
80
91
|
being trustworthy should add as little dependency and supply-chain surface as it can.
|
|
@@ -146,7 +157,9 @@ Inference reads the shape of the run tree: no parent means `bridge`, a parent th
|
|
|
146
157
|
tops its own chain means `lane`, anything deeper means `leaf`. `coordinator` is
|
|
147
158
|
never inferred — it is a role you assign, not a shape that shows up in a run tree.
|
|
148
159
|
On the board a declared tier carries a trailing `!`, so you can tell a decision
|
|
149
|
-
from a guess
|
|
160
|
+
from a guess — and a tier that is neither declared nor inferred but *defaulted*
|
|
161
|
+
(a captured spawn no intent matched) renders as `lane?`, because a guess
|
|
162
|
+
wearing a declared tier's clothes is how a leaf gets graded as a lane.
|
|
150
163
|
|
|
151
164
|
### The two new hooks
|
|
152
165
|
|
|
@@ -158,13 +171,30 @@ SubagentStop is the gate. Each step is its own reason to refuse the stop:
|
|
|
158
171
|
1. Report-before-idle. No final message means no report: an agent that went idle
|
|
159
172
|
saying nothing has not reported, and writing that down as `reported` would
|
|
160
173
|
launder silence into a claim. Blocked without transitioning.
|
|
161
|
-
2. Pin drift. Every dispatch pins the SHA-256 of the check spec that was in
|
|
162
|
-
when the work was ordered
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
174
|
+
2. Pin drift. Every dispatch pins the SHA-256 of the check spec that was in
|
|
175
|
+
force when the work was ordered — and, as of 0.4.0, a tree hash over the
|
|
176
|
+
check scripts under `.fleetproof/checks/`, because the graders are part of
|
|
177
|
+
the spec and a rewritten grader is drift the same as a rewritten spec. If
|
|
178
|
+
either changed mid-flight, the stop is blocked rather than graded against a
|
|
179
|
+
spec the agent could have edited itself, and the block names which one
|
|
180
|
+
drifted. The one exception: if the agent's tier selects nothing runnable,
|
|
181
|
+
there is nothing to mis-grade — the drift is noted and the stop allowed,
|
|
182
|
+
ungraded.
|
|
183
|
+
3. That agent's tier of the spec, unioned with any checks declared on the
|
|
184
|
+
dispatch's own manifest (`{"id", "cmd"}` entries — blocking, expect-exit0).
|
|
185
|
+
A blocking failure records `contradicted` and blocks; a pass records
|
|
186
|
+
`verified` and closes the dispatch. If nothing is runnable — the tier selects
|
|
187
|
+
no repo checks and the manifest declares none — no verdict is recorded: an
|
|
188
|
+
absent grade must never read as a passing one.
|
|
189
|
+
|
|
190
|
+
Two guardrails around the gate. A SubagentStop with no dispatch to join —
|
|
191
|
+
harness-internal helper agents emit these every session — is recorded as an
|
|
192
|
+
**orphan**: a sighting, never a graded dispatch, because a ledger that
|
|
193
|
+
manufactures dispatches out of unpaired stops manufactures verdicts too. And a
|
|
194
|
+
dispatch that keeps failing the same gate does not loop forever: after three
|
|
195
|
+
contradicted stops it is terminated as `abandoned` — never `verified` — and
|
|
196
|
+
the dispatcher is told to park it, fix the spec or tier, or re-dispatch (see
|
|
197
|
+
*Operating a fleet* below).
|
|
168
198
|
|
|
169
199
|
The Stop hook keeps its v0.1 job and adds a ledger sweep. A dispatch that reported
|
|
170
200
|
and then never terminated blocks the bridge from stopping: something started
|
|
@@ -176,14 +206,27 @@ it is reported and not blocked on.
|
|
|
176
206
|
|
|
177
207
|
```
|
|
178
208
|
fleetproof dispatch new --prompt "..." # --prompt-file for a real, long one
|
|
209
|
+
fleetproof dispatch new --agent-name worker \
|
|
210
|
+
--prompt-file p.md # joinable: the next SubagentStop of
|
|
211
|
+
# that spawn name adopts this record
|
|
179
212
|
fleetproof dispatch report <run-id> --report report.json
|
|
180
213
|
fleetproof dispatch close <run-id>
|
|
214
|
+
fleetproof dispatch park <run-id> --reason "..." # terminate on purpose, reason kept
|
|
215
|
+
|
|
216
|
+
fleetproof dispatch intent --agent recon --prompt-file p.md \
|
|
217
|
+
[--manifest m.json] [--tier lane] [--role tester]
|
|
218
|
+
# declare the NEXT spawn of an agent
|
|
219
|
+
# type; the start capture consumes it
|
|
181
220
|
|
|
182
221
|
fleetproof fleet # the board, newest first
|
|
183
222
|
fleetproof fleet --open # only what has not terminated
|
|
223
|
+
fleetproof fleet --orphans # unpaired stops: sightings, not dispatches
|
|
184
224
|
fleetproof fleet --format json # full records, for an agent to read
|
|
185
225
|
|
|
186
226
|
fleetproof check --tier lane # grade one rung of the spec
|
|
227
|
+
|
|
228
|
+
fleetproof arm # bridge Stop gate blocks (the default)
|
|
229
|
+
fleetproof disarm --note "why" # bridge gate advisory; note required
|
|
187
230
|
```
|
|
188
231
|
|
|
189
232
|
The board never prints a bare dash where a verdict goes. A dispatch nothing graded
|
|
@@ -194,19 +237,31 @@ transition trail, and which process wrote each transition.
|
|
|
194
237
|
|
|
195
238
|
### What the ledger does not do yet
|
|
196
239
|
|
|
197
|
-
- The spawn prompt is in neither subagent hook payload
|
|
198
|
-
a placeholder saying so, rather than inventing a prompt it never
|
|
199
|
-
real prompt
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
240
|
+
- The spawn prompt is in neither subagent hook payload, so an undeclared spawn
|
|
241
|
+
still records a placeholder saying so, rather than inventing a prompt it never
|
|
242
|
+
saw. To get the real prompt (and manifest, and tier) onto a captured dispatch,
|
|
243
|
+
write an intent sidecar first: `fleetproof dispatch intent --agent <type>
|
|
244
|
+
--prompt-file <file>` writes `.fleetproof/intents/<agent_type>.json`, and the
|
|
245
|
+
next SubagentStart of that agent type consumes it — one intent, one spawn.
|
|
246
|
+
- Captured subagents without a declared intent tier are recorded at `lane`
|
|
247
|
+
tier, marked `defaulted` — `lane?` on the board — and the miss is loud: the
|
|
248
|
+
start capture writes a stderr line naming the spawn that matched nothing and
|
|
249
|
+
listing the sidecars that were present. Real nesting depth is not visible
|
|
250
|
+
from the payload — there is no parent-agent field — so declaring a tier
|
|
251
|
+
beats inferring it wrongly.
|
|
203
252
|
- An agent killed mid-turn can reach SubagentStop with an empty final message,
|
|
204
253
|
which report-before-idle blocks. Blocking the stop of an agent that is already
|
|
205
254
|
gone is not useful; how often it happens is under observation.
|
|
206
255
|
- There is no correlation field between the Task call that spawned an agent and
|
|
207
|
-
that agent's stop, so `agent_id` plus `session_id` is the
|
|
208
|
-
|
|
209
|
-
|
|
256
|
+
that agent's stop, so `agent_id` plus `session_id` is the primary join key.
|
|
257
|
+
A dispatch recorded from the CLI with `--agent-name` has no agent id yet; the
|
|
258
|
+
first SubagentStop in the same session whose spawn name matches it uniquely
|
|
259
|
+
adopts it (the adoption is written into the transitions). An ambiguous name
|
|
260
|
+
match — two open dispatches, same name — joins nothing: the stop lands as an
|
|
261
|
+
orphan with a stderr note naming the ambiguity, because guessing which
|
|
262
|
+
dispatch to grade is worse than grading neither. The lookup is scoped to
|
|
263
|
+
non-terminal dispatches: an agent id can be reused once a dispatch is closed
|
|
264
|
+
out.
|
|
210
265
|
|
|
211
266
|
## Verification telemetry (v0.3)
|
|
212
267
|
|
|
@@ -267,7 +322,95 @@ operator-attested. The checker writes verdicts from a separate process, and v0.3
|
|
|
267
322
|
chains a rolling hash over the records as they're written (`anchor` gives you a
|
|
268
323
|
head you can sign or store elsewhere) — but a local ledger on a shared filesystem
|
|
269
324
|
is not tamper-proof against everything that can write to it, and FleetProof will
|
|
270
|
-
not pretend otherwise.
|
|
325
|
+
not pretend otherwise. The dispatch-intent sidecar (0.3.1) sits in the same
|
|
326
|
+
trust class as `checks.json` itself: operator-attested, exactly as forgeable as
|
|
327
|
+
the rest of `.fleetproof/`, no worse — anything that can write the repo can
|
|
328
|
+
write an intent, and the manifest checks it carries run as shell commands at
|
|
329
|
+
the stop gate the same way repo checks do.
|
|
330
|
+
|
|
331
|
+
## Operating a fleet (v0.4)
|
|
332
|
+
|
|
333
|
+
v0.4 is the field-hardening release: everything in it was shaped by running a
|
|
334
|
+
multi-lane fleet under the gate in a field deployment on Windows. The
|
|
335
|
+
mechanics are FleetProof's; the discipline below is yours. It is what keeps a
|
|
336
|
+
verification gate from decaying into either wallpaper or a wedge.
|
|
337
|
+
|
|
338
|
+
**Declare intent before every spawn, and verify the capture.** Intent sidecars
|
|
339
|
+
are keyed by the *exact spawn name* — the `agent_type` the harness reports —
|
|
340
|
+
not by what you think of the role as. If the names in your head and your spawn
|
|
341
|
+
calls drift apart, key the sidecar to the role instead with
|
|
342
|
+
`dispatch intent --role`; matching is exact string equality on filename OR
|
|
343
|
+
role, never a prefix guess. Then look at `fleetproof fleet` right after each
|
|
344
|
+
spawn: the row should show the real prompt and a `tier!`. A `lane?` means no
|
|
345
|
+
intent matched — the dispatch is running with a placeholder prompt at a
|
|
346
|
+
defaulted tier, and the capture you thought you declared didn't happen.
|
|
347
|
+
|
|
348
|
+
**Never dispatch an agent against a blocking check its seat cannot satisfy.**
|
|
349
|
+
A lane gated on something only the bridge or the operator can do will fail,
|
|
350
|
+
retry, and fail again — the gate is working; the dispatch was wrong. Declare
|
|
351
|
+
`"owner"` on such checks (`leaf|lane|coordinator|bridge|operator`): at any
|
|
352
|
+
other tier the check still runs and renders, but as advisory — it blocks only
|
|
353
|
+
at the seat that can actually satisfy it. `operator`-owned checks are advisory
|
|
354
|
+
at every agent tier; they keep a criterion visible without wedging anyone, and
|
|
355
|
+
the operator closes them out of band.
|
|
356
|
+
|
|
357
|
+
**Expensive checks belong where they run once.** The gate runs a tier's checks
|
|
358
|
+
at every graded stop on that tier. A slow suite gated on every leaf stop taxes
|
|
359
|
+
the whole fleet; tier it to the rung where it runs once, and demote what
|
|
360
|
+
doesn't need to block to `block: false`.
|
|
361
|
+
|
|
362
|
+
**Edit specs between phases, not under pinned dispatches.** Every dispatch
|
|
363
|
+
pins the spec and the check-script tree; an edit while agents are in flight
|
|
364
|
+
drift-blocks each of them at stop. Propose spec edits as text mid-phase, land
|
|
365
|
+
them at the phase boundary, then dispatch. And if the temptation is to flip
|
|
366
|
+
checks to `block: false` because a phase can't pass them yet, that flip is
|
|
367
|
+
itself a spec edit — use arming instead.
|
|
368
|
+
|
|
369
|
+
**Arming is the phase switch; disarming is an operator action.**
|
|
370
|
+
`fleetproof disarm --note "why"` sets the bridge Stop gate to advisory:
|
|
371
|
+
checks still run and render their failures, but the bridge can end its turn —
|
|
372
|
+
for build phases whose release checks can only pass at publish.
|
|
373
|
+
`fleetproof arm` turns blocking back on, and the armed state is the default —
|
|
374
|
+
no file, a corrupt file, or an unknown value all read as armed. The note is
|
|
375
|
+
required on disarm by design, and the file records who set it and when; the
|
|
376
|
+
board echoes a disarmed gate on every render. A bridge disarming its own gate
|
|
377
|
+
is therefore visible and attributed, not silent: anything that can write the
|
|
378
|
+
repo can flip the switch, but it cannot flip it quietly. The ledger sweep
|
|
379
|
+
still blocks on stalled dispatches regardless of arming, and subagent grading
|
|
380
|
+
is never affected by it.
|
|
381
|
+
|
|
382
|
+
**The graders are part of the spec.** The tree hash pins every file under
|
|
383
|
+
`.fleetproof/checks/` alongside `checks.json` itself. A check whose grading
|
|
384
|
+
logic lives in a script is only as trustworthy as that script's bytes, so a
|
|
385
|
+
rewritten grader now trips the same drift block as a rewritten spec.
|
|
386
|
+
|
|
387
|
+
**Orphans are sightings, never verdicts.** Harness-internal helper agents emit
|
|
388
|
+
SubagentStops that never had a start; a parked or abandoned dispatch's agent
|
|
389
|
+
may stop again after its dispatch is terminal. These land in the orphan list
|
|
390
|
+
(`fleet --orphans`), not on the board, and are never graded — a stop that
|
|
391
|
+
joins no dispatch must not be allowed to manufacture one, because once lane
|
|
392
|
+
checks are green a manufactured dispatch grades itself `verified`.
|
|
393
|
+
|
|
394
|
+
**A wedge must terminate, not loop.** The escalation ladder: a blocking
|
|
395
|
+
failure blocks; the third contradicted stop on the same dispatch terminates it
|
|
396
|
+
as `abandoned` instead — the agent is released, the board shows `abandoned!`
|
|
397
|
+
(never `verified`), and the dispatcher is told to park, fix, or re-dispatch.
|
|
398
|
+
For a dispatch you can see is wedged before the ladder runs out — or one whose
|
|
399
|
+
check its seat can't satisfy — `fleetproof dispatch park <run-id> --reason`
|
|
400
|
+
closes it on purpose with the reason kept. A parked dispatch is terminal; its
|
|
401
|
+
agent, if it stops again, lands as an orphan.
|
|
402
|
+
|
|
403
|
+
**Treat `.fleetproof/runs/` as secret-class, and keep secrets off command
|
|
404
|
+
lines.** Check stdout and stderr are persisted into the run log — that is the
|
|
405
|
+
point, and the risk. Builtin redactions (connection-string keys, SAS-style
|
|
406
|
+
signatures, private-key blocks, JWTs, bearer tokens) plus per-check `redact`
|
|
407
|
+
patterns run before anything is written, and the env snapshot is
|
|
408
|
+
denylist-filtered — but redaction is a backstop, not a permission slip. The
|
|
409
|
+
PostToolUse hook records `tool_input` *verbatim by design*: redacting
|
|
410
|
+
arbitrary code before recording it would be false comfort about what your
|
|
411
|
+
agents ran, so a secret typed on a command line is a secret in the run log,
|
|
412
|
+
full stop. Never type one. And add `.fleetproof/runs/` to `.gitignore` — it
|
|
413
|
+
is evidence, not source.
|
|
271
414
|
|
|
272
415
|
## Install (each line is one command in Claude Code)
|
|
273
416
|
|
|
@@ -325,6 +468,17 @@ you (or an agent, under your review) write *before* work is graded, kept in the
|
|
|
325
468
|
repo where it is versioned and diffable. JSON, not YAML, on purpose: there is no
|
|
326
469
|
third-party parser in the runtime.
|
|
327
470
|
|
|
471
|
+
Beyond `{ "id", "run", "expect", "block" }`, a check can declare a `tier`
|
|
472
|
+
(which rung grades it — see the ledger section), an `owner` (which rung can
|
|
473
|
+
*satisfy* it — see *Operating a fleet*), and `redact` patterns applied to its
|
|
474
|
+
captured output before anything is persisted. `run` may also be a JSON array
|
|
475
|
+
of argv strings, executed with `shell=False` — no shell at all, which on
|
|
476
|
+
Windows means no cmd.exe quoting hazards; the string form keeps shell
|
|
477
|
+
semantics for compatibility. Manifest checks (`{ "id", "cmd" }`) accept the
|
|
478
|
+
same array form. Checks execute from the resolved project root, not from
|
|
479
|
+
wherever the hook's shell happened to be `cd`'d, and every rendered verdict
|
|
480
|
+
prints the cwd it ran from.
|
|
481
|
+
|
|
328
482
|
An agent is welcome to propose or edit checks. The guarantee FleetProof makes is
|
|
329
483
|
narrower and firmer than "the agent verified its work": it is that *something
|
|
330
484
|
other than the agent* ran the checks and recorded the result.
|
|
@@ -361,36 +515,42 @@ choose runs outside the agent's process*.
|
|
|
361
515
|
|
|
362
516
|
Guidance that keeps the gate honest: keep blocking checks fast and
|
|
363
517
|
deterministic (flaky checks flap the gate; slow ones tax every stop) and demote
|
|
364
|
-
heavy suites to `block: false`.
|
|
518
|
+
heavy suites to `block: false`. A spec whose selected checks are *all*
|
|
519
|
+
advisory renders its verdict as `ADVISORY - 0 blocking; ...`, never the bare
|
|
520
|
+
word PASS — green with nothing blocking is a fact worth labelling. For fleets doing varied tasks in one repo,
|
|
365
521
|
the working convention is to have the agent author task-specific checks at task
|
|
366
522
|
start — authoring is a feature — and let the spec-drift flag make any later
|
|
367
523
|
revision loud. Per-task check scoping as a first-class mechanism is on the
|
|
368
524
|
roadmap.
|
|
369
525
|
|
|
370
|
-
## Scope and limitations
|
|
526
|
+
## Scope and limitations
|
|
371
527
|
|
|
372
|
-
|
|
528
|
+
Honest boundaries:
|
|
373
529
|
|
|
374
530
|
- **`python` must be on `PATH`.** The plugin hooks invoke the checker via
|
|
375
531
|
`python "${CLAUDE_PLUGIN_ROOT}/scripts/..."`. Environments where the interpreter
|
|
376
532
|
is only reachable as `python3`, or not on `PATH`, need a shim — any wrapper on
|
|
377
533
|
`PATH` named `python` works, e.g. `sudo ln -s $(which python3) /usr/local/bin/python`.
|
|
378
|
-
- **Checks are
|
|
379
|
-
check that shells out to `grep` won't behave identically on every OS
|
|
380
|
-
|
|
534
|
+
- **Checks are commands you chose.** Their portability is your responsibility —
|
|
535
|
+
a check that shells out to `grep` won't behave identically on every OS
|
|
536
|
+
(the argv form of `run` sidesteps the shell-quoting half of that problem).
|
|
537
|
+
And because a repo's `checks.json` runs commands on the Stop event, treat a
|
|
381
538
|
cloned repo's `checks.json` with the same trust you give its `Makefile` or
|
|
382
539
|
pre-commit config: it is executable code. FleetProof adds no network surface of
|
|
383
540
|
its own, and first-run consent-per-spec-hash is on the roadmap.
|
|
384
541
|
- **The gate is only as good as the checks.** FleetProof enforces that an
|
|
385
542
|
independent process runs your checks; it cannot know whether your checks capture
|
|
386
543
|
what "done" really means. Weak checks give false confidence.
|
|
387
|
-
- **An agent can weaken its own checks
|
|
388
|
-
nothing
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
544
|
+
- **An agent can weaken its own checks — but not quietly.** Authoring
|
|
545
|
+
`checks.json` is a feature, so nothing forbids an agent editing it
|
|
546
|
+
mid-session to slip the gate. What stands in the way: every dispatch pins
|
|
547
|
+
the spec hash *and* the tree hash over the check scripts under
|
|
548
|
+
`.fleetproof/checks/`, and a pinned agent whose spec or graders drifted is
|
|
549
|
+
blocked at its stop rather than graded against the edited version. At the
|
|
550
|
+
session level, drift from the session's first spec is flagged loudly — in
|
|
551
|
+
the Stop-gate output, the CLI, and the report. What remains open: an agent
|
|
552
|
+
that edits the spec *before* any dispatch pins it is drift-invisible;
|
|
553
|
+
consent-on-change is on the roadmap.
|
|
394
554
|
- **The local ledger is forgeable by design.** An agent with write access to the
|
|
395
555
|
repo can, in principle, forge or tamper with records in its own `.fleetproof/`
|
|
396
556
|
— verdicts, transitions, reports. The gates make honest mistakes loud and
|
|
@@ -403,9 +563,14 @@ This is an early release. Honest boundaries:
|
|
|
403
563
|
rather than blocking every task. But once a spec has graded work this session
|
|
404
564
|
— or a dispatch has pinned one — a missing, emptied, or unreadable spec
|
|
405
565
|
*blocks*: a promised gate cannot be switched off by deleting its spec.
|
|
406
|
-
- **Blocking can repeat.** If a blocking check keeps
|
|
407
|
-
|
|
408
|
-
that
|
|
566
|
+
- **Blocking can repeat — but not unboundedly.** If a blocking check keeps
|
|
567
|
+
failing, the gate keeps blocking: don't stop on a false done. Two escape
|
|
568
|
+
hatches keep that from becoming a wedge. A subagent's dispatch is terminated
|
|
569
|
+
as `abandoned` after three contradicted stops — the agent is released, the
|
|
570
|
+
work is marked unverified, and the dispatcher is told. And the bridge's Stop
|
|
571
|
+
gate can be set to advisory with `fleetproof disarm --note` when a phase
|
|
572
|
+
legitimately cannot pass its checks yet — an operator decision, recorded and
|
|
573
|
+
echoed on the board.
|
|
409
574
|
|
|
410
575
|
## FleetProof for teams (coming — waitlist open)
|
|
411
576
|
|