Read every invariant proof at the file, and find twelve that cannot fail

cf7d68e2d6cd · AtlantisPleb · · parent efe115790d02

Read every invariant proof at the file, and find twelve that cannot fail

The 2026-08-23 audit classified every claim and said a second pass over the
proofs themselves would be worth doing. This is that pass: all 130 contracts,
every cited test, script, and constraint read at the file and checked against
one question — when the claim is violated, does the proof go red?

61 can fail, 57 carry named clauses no test can redden, and 12 cannot fail
for a load-bearing clause. One is red right now: ADMIN-001's operator-surface
enumeration fails at HEAD because the Gym landed classified :operator without
the contract naming it — the proof worked and the gate's Markdown-only
precommit skip let it land anyway. The sharpest of the twelve: MEMORY-005's
cited tests assert the violating behavior the code actually has, RELEASE-003's
cited test covers a module nothing in production calls, VOICE-005's disclosure
refutation names an element ID that exists nowhere, and THREAD-001's race
proof serializes on one shared sandbox connection, so deleting both FOR UPDATE
locks leaves it green.

Verification: elixir ops/ci/docs-check.exs (passes; the proof index still
resolves every path).

Refs #166.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GoYpb8FEmdxVErsv7ABCYi
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

pushed
by user · WAL seq 330 · 2026-08-25T02:55:14.022375Z

Changed files

  • added docs/2026-08-24-invariant-proof-audit.md

Diff

1 file changed, +858 -0

docs/2026-08-24-invariant-proof-audit.md added +858

@@ -0,0 +1,858 @@

1
# Invariant proof audit: the second pass, at the test files
2
3
`docs/2026-08-23-invariant-proof-audit.md` classified every contract in
4
`INVARIANTS.md` as specific or universal and repaired nineteen proofs that
5
could not fail for their claims. It also recorded its own limit: most verdicts
6
"were judged from the mechanism the contract names rather than re-derived. A
7
second pass over those would be worth doing and is not what this one did."
8
9
This document is that second pass. Every contract's cited proof was read at
10
the file — the test, the script, the migration, and the production module it
11
claims to cover — and checked against issue #166's mechanical test: name a
12
violation the proof would not catch. Line numbers refer to the working tree on
13
2026-08-24.
14
15
The first pass asked whether a universal claim's population is closed. This
16
pass asks a different question about the same proofs: when the claim is
17
violated, does the named proof actually go red? Five defect classes recur:
18
19
1. **Fixture-path assertions.** The test asserts on a fixture, a hand-built
20
   map, or a JSON file's own fields rather than the production path.
21
2. **Subset coverage.** The proof examines some members of the claimed
22
   population — two adapters of four, one extension object of four, one route
23
   of a class.
24
3. **Shared-code tautologies.** The expected value is computed by the code
25
   under test, so a bug moves both sides together and the assertion holds.
26
4. **Vacuous assertions.** The assertion is true by construction — an element
27
   ID that exists nowhere, a literal read back from the module that hard-codes
28
   it, an immutability property every BEAM value has.
29
5. **Empty-input passes.** An enumeration or scan that matched nothing passes
30
   as if it had checked everything, with no positive control forcing it to
31
   find what it should find.
32
33
## Result
34
35
130 contracts, one verdict each.
36
37
| Verdict | Count |
38
| --- | --- |
39
| Can fail — the proof goes red when the claim is violated | 61 |
40
| Partial — the core clause bites, but named clauses have no proof that can | 57 |
41
| Cannot fail — a load-bearing clause has no proof that can go red | 12 |
42
43
One proof is red right now, and that is the mechanism working: see the next
44
section. The verdicts here are about the cited proofs as they exist; "partial"
45
is not a defect report against the code, and several "cannot fail" entries
46
cover code that is currently correct. The finding in each case is that nothing
47
executable would notice when it stops being correct — which is the exact
48
condition `INVARIANTS.md` lines 13-25 names as the companion to taxonomy
49
rule 7.
50
51
## The proof that is failing right now
52
53
`ADMIN-001`'s enumerating proof, `test/openagents_web/operator_surface_test.exs`,
54
is statically red at HEAD. Commit `310dac1` ("Open the Gym", an ancestor of
55
HEAD) added:
56
57
- `/gym`, classified `:operator` with scope `gym:read` at
58
  `lib/openagents_web/route_authority.ex` lines 192-193, absent from the
59
  declared operator-route table at `test/openagents_web/operator_surface_test.exs`
60
  lines 33-72;
61
- `OpenAgentsWeb.GymLive` (lines 21 and 30) and `OpenAgentsWeb.GymRunController`
62
  (line 46), both calling `OpenAgents.Accounts.admin?/1`, absent from the
63
  declared authority-module table at lines 76-104.
64
65
`ADMIN-001` does not name the Gym anywhere. The proof is doing exactly what
66
#146 built it to do — a new operator surface fails until the contract is
67
amended to name it — and the failure reached `main` anyway. That is a process
68
finding, not a proof finding: the commit's own message reports a scoped test
69
run, and the section below on the gate shows how a candidate can carry a
70
`passed` receipt without this file ever running. The fix is two-sided: amend
71
`ADMIN-001` to name the Gym read surface, and close the gate path that let a
72
red enumeration land.
73
74
## Proofs that cannot fail for a load-bearing clause
75
76
Each entry names the claim, the cited proof, the violation the proof cannot
77
catch, and the smallest change that would let it fail.
78
79
### MEMORY-005 — the tests assert the violating behavior
80
81
**Claim:** `memory_remember.v1` accepts only a claim directly authorized by
82
the current user message, a host-recorded confirmation, or a first-party UI
83
action; "model arguments cannot substitute."
84
85
**Reality:** `lib/openagents/tools/memory_remember.ex` lines 130-135 swallow
86
every `OpenAgents.Memory.Consent` failure and proceed with
87
`%{kind: "conversation_context"}` — the model's arguments are the write
88
authority. The cited proof encodes this as correct:
89
`test/openagents/tools/profile_memory_tools_test.exs` lines 240-262 store a
90
model-paraphrased claim under `consent_kind == "conversation_context"`, and
91
lines 292-307 store a claim that **differs from** the exact confirmation on
92
record ("I prefer concise answers" confirmed, "I prefer detailed answers"
93
stored) and assert success. The `Consent` unit tests (lines 22-119) prove the
94
strict module in isolation; no test runs the tool with a fabricated claim and
95
asserts refusal.
96
97
**Smallest change:** delete the `{:error, _} -> %{kind: "conversation_context"}`
98
fallback so the tool returns the consent error, or amend MEMORY-005 to say
99
what the code does — a conversation-context write authorized by topical
100
relevance, not by exact consent. Either way the current sentence and the
101
current code cannot both stand.
102
103
### RELEASE-003 — the cited proof tests dead code
104
105
**Claim:** invalid, insecure, or path-bearing origins fail startup.
106
107
**Reality:** `OpenAgentsWeb.AllowedOrigins` has no production caller — the
108
only references in the repository are its own file and its test. Production
109
origin checking is `config/runtime.exs` line 190 feeding `check_origin` at
110
line 545, validated by `lib/openagents/runtime_config.ex` lines 823-840.
111
`test/openagents_web/allowed_origins_test.exs` therefore tests a module you
112
can delete without changing production behavior, and `ops/ci/release-smoke.sh`
113
never opens a WebSocket or sends an `Origin` header (lines 99-114 curl
114
`/health` over plain loopback HTTP), so the "production WebSocket read-back"
115
in the evidence line has no executable artifact.
116
117
**Smallest change:** either route `config/runtime.exs` through
118
`OpenAgentsWeb.AllowedOrigins.for_production/2` so the tested module is the
119
production path, or retarget the proof at `OpenAgents.RuntimeConfig`'s origin
120
validation and delete the dead module.
121
122
### VOICE-005 — the disclosure assertion checks an element that exists nowhere
123
124
**Claim:** while recording is on, the surface that carries `START VOICE`
125
states before the microphone opens that calls are recorded; a visible marker
126
announces capture while running.
127
128
**Reality:** `test/openagents_web/live/chat_live_test.exs` line 206 asserts
129
`refute has_element?(view, "#voice-recording-disclosure")` — an ID that
130
appears nowhere else in the repository, so the assertion cannot fail under any
131
change. The positive disclosure is proven only on the memory surface
132
(`test/openagents_web/controllers/data_controller_test.exs` lines 243-247
133
against `lib/openagents_web/live/memory_live.ex`), while the surface that
134
carries `START VOICE` (`lib/openagents_web/live/chat_live.ex` line 2069)
135
renders no disclosure text. The capture marker at line 2063 is
136
`visually-hidden` and referenced by no test. The teardown half is also
137
unclosed: `assets/test/voice_state_test.mjs` lines 55-71 call the teardown
138
function directly with fakes; nothing proves `destroyed()` or reconnect
139
actually calls it (`assets/js/voice_controller.js` line 501 is the one call
140
site).
141
142
**Smallest change:** assert the disclosure element that the chat surface
143
actually renders — which first requires the chat surface to render one — and
144
delete the impossible-ID refutation.
145
146
### MODULE-004 — the proof is a JSON fixture identity check
147
148
**Claim:** every capability surface preserves the same authority boundary;
149
external effects need receipts; oversized catalogs degrade to discovery;
150
missing executors fail honestly.
151
152
**Reality:** `test/openagents/surface_eval_test.exs` is 28 lines. Lines 13-18
153
assert that the case IDs in `priv/sarah/evals/surfaces/identity-authority.v1.json`
154
equal a hard-coded list. Three of the four corpus cases — external effect
155
under read authority, oversized catalog, unavailable executor — declare
156
expectations that nothing executes. The file never touches
157
`OpenAgents.Modules.SurfacePolicy`, `OpenAgents.Tools.Registry`, or
158
`OpenAgents.Tools.Runner` — the three modules the invariant names as
159
evidence. Renaming a corpus case turns the test red; an external effect
160
executing under read-only authority does not.
161
162
**Smallest change:** drive the three unexecuted corpus cases through
163
`OpenAgents.Tools.Runner`, or narrow MODULE-004's proof index row to the
164
surface-vocabulary and revalidation tests in
165
`test/openagents/modules/router_test.exs` (lines 107-134), which do bite.
166
167
### COLLECTIVE-002 — the leak scanner has zero coverage
168
169
**Claim:** generalizer output "is scanned again for secrets, contacts,
170
identifiers, paths, exact source fragments, authority-bearing fields, and
171
size before storage."
172
173
**Reality:** the scanner exists (`lib/openagents/collective/generalizer.ex`
174
lines 267-299, five reject branches including the four-gram quote detector)
175
and no test reaches any reject branch — a grep for the rejection reasons
176
across `test/` returns nothing. The leak assertions in
177
`test/openagents/collective_generalizer_test.exs` lines 38-48 run against a
178
payload built from fixed-vocabulary maps that no input text ever flows into,
179
so they are true by construction.
180
181
**Smallest change:** construct a payload carrying a source fragment and
182
assert `validate_payload/2` rejects it — one test per reject branch.
183
184
### IDENTITY-006 — the branch discipline is enforced but never tested
185
186
**Claim:** Git receive-pack authorizes every requested ref update; the
187
assignment credential can update only its assignment branch, never a default
188
or protected branch.
189
190
**Reality:** nothing in `test/` exercises `authorize_receive_pack/3` or
191
`allowed_assignment_ref?/3` (`lib/openagents/forge/git_http.ex` lines
192
303-329). Deleting the default-branch and protected-branch checks fails no
193
test. `test/openagents/forge/assignment_test.exs` lines 133-147 test pkt-line
194
parsing — the input to the decision, not the decision. The non-Git-API
195
refusal in the auth plug is likewise untested, and the "stores only a digest"
196
assertion at lines 96-97 is tautological (the schema has no plaintext field
197
to leak).
198
199
**Smallest change:** a receive-pack push with an assignment credential
200
against the default branch, over the same real-HTTP harness
201
`test/openagents/forge/push_closes_issues_test.exs` already stands up,
202
asserting refusal — plus one against the assignment branch asserting success.
203
204
### DEPLOYPLANE-004 — "the only definition" has no only-ness proof
205
206
**Claim:** `OpenAgents.Deployments.Lifecycle` is the only definition of legal
207
states and transitions, enforced transactionally.
208
209
**Reality:** the transitions themselves are well proven
210
(`test/openagents/deployments/lifecycle_test.exs` lines 12-18 quantify over
211
the transition table), but nothing enumerates writers of run state — no
212
compiled-import-table assertion like FLEETPROMOTE-001's, and no PostgreSQL
213
transition trigger on the runs table (contrast `work_jobs`, whose trigger
214
`test/openagents/work_job_test.exs` line 232 exercises). The suite itself
215
demonstrates the bypass: `test/openagents/issues/completion_claims_test.exs`
216
lines 565-571 force a run to `succeeded` with a raw update.
217
218
**Smallest change:** either a transition trigger on the runs table with a
219
raw-update test, or a dependency-boundary assertion that the lifecycle module
220
is the sole caller of the state-writing function.
221
222
### OBSERVABILITY-001 — the read-back is proven against an empty database
223
224
**Claim:** release read-back recomputes zero-tolerance leakage, consent,
225
provenance, executor-disclosure, and attribution checks from authoritative
226
records without selecting private content.
227
228
**Reality:** `test/openagents/observability_test.exs` line 52 runs
229
`OpenAgents.Observability.Readback` against a freshly sandboxed database with
230
no conversation, message, or memory row seeded. The
231
"contains no private content" refutations (lines 65-66) are vacuous — there
232
is no content to leak — and the all-planes-healthy loop (lines 59-63) passes
233
over empty status maps. The blocked case is manufactured with `put_in` rather
234
than by seeding a violation, so neither direction (detects a real violation;
235
excludes real content) is exercised. The emit half does bite, but only 2 of 6
236
refusal branches are tested (lines 22-48).
237
238
**Smallest change:** seed one conversation with a known sentinel string and
239
one zero-tolerance violation, then assert the read-back reports the violation
240
and the sentinel appears nowhere in the snapshot.
241
242
### RELEASE-001 — nothing can fail when migrations stop preceding traffic
243
244
**Claim:** the production image runs all pending migrations before starting
245
the HTTP server.
246
247
**Reality:** the evidence line names the Docker `CMD`, but the `CMD` contains
248
no migration step — the ordering lives in `lib/openagents/application.ex`
249
lines 22-24 behind `migrate_on_boot`, which
250
`lib/openagents/runtime_config.ex` lines 267-268 require in staging and
251
production. No test asserts that requirement (the only `migrate_on_boot` hit
252
in `test/` is a fixture setting it true), and
253
`test/openagents_web/controllers/health_controller_test.exs` runs against an
254
already-migrated test database. The smoke boots the release, but health is
255
`SELECT 1` (`lib/openagents_web/controllers/health_controller.ex` line 5),
256
which succeeds on an unmigrated database, and the smoke never verifies the
257
database is fresh. Remove the migration call and every cited proof stays
258
green.
259
260
**Smallest change:** a `test/openagents/runtime_config_test.exs` case
261
asserting staging and production refuse `migrate_on_boot: false`, and a
262
migration-sensitive probe in the smoke (query a table the newest migration
263
creates). Also correct the evidence line: the mechanism is
264
`OpenAgents.Release` invoked from the application start, not the `CMD`.
265
266
### RELEASE-002 — the absence half has no detector
267
268
**Claim:** secrets are absent from source, the build context, and image build
269
arguments.
270
271
**Reality:** `ops/ci/reference-check.sh` is a naming check, not a secret
272
scan, and it cannot fail even at its own job when `rg` is missing: lines 37
273
and 39 end in `|| true`, and `ops/ci/gate.sh` line 71 checks for `jq mix npm`
274
but never `rg`, so on a builder without ripgrep the check silently passes.
275
Nothing reads `.gitignore` or `.dockerignore` in any test; nothing scans
276
`config/` or the Dockerfile for credential material; the Cloud Logging
277
exclusion the contract names exists only as prose in `INVARIANTS.md`. The
278
GitHub-config half (`test/openagents/github_oauth/runtime_config_test.exs`)
279
does bite, narrowly.
280
281
**Smallest change:** fail `ops/ci/reference-check.sh` when `rg` is absent
282
(and add it to the gate's tool check), and add a repository-tree scan test
283
for high-signal secret shapes in `config/` and the Docker build context —
284
the same read-the-repo shape `test/openagents/hosted_ci_absence_test.exs`
285
already uses.
286
287
### UI-003 — no population of surfaces exists to enumerate
288
289
**Claim:** product surfaces render only through the sanctioned component
290
library.
291
292
**Reality:** `test/openagents_web/component_catalog_test.exs` proves the
293
catalog page documents the components — the arrow points the wrong way — and
294
`test/openagents_web/ui_test.exs` proves the primitives behave. Nothing scans
295
surfaces: a new LiveView rendering hand-rolled markup with its own palette
296
passes every cited proof. The narrow template scan that exists
297
(`test/openagents_web/ui_contracts_test.exs`) checks inert-control rules
298
only, by design.
299
300
**Smallest change:** narrow the claim to what the proofs cover (the primitive
301
contracts and the catalog), or add a template scan over
302
`lib/openagents_web/**` asserting variant-bearing markup uses the sanctioned
303
class-plus-data-attribute shapes.
304
305
### DATA-001 — the projection-loss clause has no test
306
307
**Claim:** losing PubSub or the LiveView stream must not lose accepted data;
308
rows are persisted before their state is presented as accepted.
309
310
**Reality:** the cited tests prove `create_turn/2` persists and a happy-path
311
turn renders (`test/openagents/conversations_test.exs` lines 28-41,
312
`test/openagents_web/live/chat_live_test.exs` lines 536-589). Nothing kills
313
a subscription, drops a stream, or asserts persist-before-broadcast ordering,
314
so a regression to broadcast-then-persist passes.
315
316
**Smallest change:** narrow the claim to what the tests prove (durable rows
317
for accepted turns), or add an ordering assertion at the delta-persistence
318
seam.
319
320
## Claims the code or the tests contradict
321
322
These are prose defects: the proof is fine or repairable, but the sentence in
323
the ledger is not what the system does.
324
325
- **VOICE-006 and VOICE-009** both state "sending typed input while voice is
326
  active ends the voice generation first." The cited test proves the
327
  opposite by design: `test/openagents_web/live/chat_live_test.exs` lines
328
  282-342 is titled "typing during a live voice call keeps the call open and
329
  hands voice the message" and asserts the session stays `listening`. The
330
  no-parallel-responses property may still hold through injection (no text
331
  turn opens), but the stated mechanism is a superseded design.
332
- **DATA-002** credits "unique indexes and identity-source constraint in
333
  `create_github_users`." No migration by that name exists — the indexes live
334
  in `priv/repo/migrations/20260816214200_create_sarah_conversations.exs` —
335
  and no identity-source constraint exists at all: nothing prevents a
336
  visitors row carrying both a browser key and a user ID. No test inserts a
337
  duplicate or races two creates, so dropping the unique indexes also fails
338
  nothing.
339
- **ADMIN-001** says the `/admin` panel shows recording completeness
340
  metadata; `test/openagents_web/live/admin_live_test.exs` lines 110-119
341
  refute recording strings with a stale comment claiming the product does not
342
  capture call audio, and line 139's model-ID refutation is vacuous because
343
  no call is seeded.
344
- **IDENTITY-007**'s proof index row cites `test/openagents/agents_test.exs`,
345
  which contains no grant test; the lifecycle is actually proven in
346
  `test/openagents/forge/assignment_test.exs` lines 105-131 and the two
347
  controller suites. The index row should follow the tests that exist.
348
- **STACK-001** cites `ops/ci/stack-contracts.sh` as evidence; nothing
349
  invokes it — it appears in no gate stage and wraps the same test file the
350
  suite already runs.
351
- **RELEASE-001**'s "Evidence: Docker `CMD`" points at a command with no
352
  migration step (see above).
353
- **MEMORY-003**'s evidence line lists `test/openagents/profile_memory_test.exs`
354
  twice — cosmetic.
355
356
## Tautologies, wrong chokepoints, and unexercised constraints
357
358
The most consequential "partial" verdicts, each with the violation the proof
359
misses.
360
361
### THREAD-001 — the race proof does not race
362
363
`test/openagents/threads/credit_race_test.exs` line 20 is
364
`use OpenAgents.DataCase, async: false`, and `test/support/data_case.ex`
365
line 40 starts the sandbox owner in shared mode for non-async tests. Every
366
`Task.async_stream` worker therefore serializes on one shared connection —
367
the concurrent opens and mints are savepoints on a single transaction, not
368
competing transactions. Deleting both `FOR UPDATE` locks in
369
`lib/openagents/threads.ex` (the admission-cap lock and the mint-side lock)
370
leaves all four tests green, and THREAD-001's amended text cites this file as
371
proving "the cap holds under concurrency." The token-reach half of THREAD-001
372
(`test/openagents/threads/grant_token_reach_test.exs`) is the strongest
373
enumeration in the repository — four derived sets, both directions, explicit
374
non-emptiness guards, route population from the router. The race clause needs
375
a test that checks out two real connections (unsandboxed or with explicit
376
allowances) before it proves anything.
377
378
### REPUTATION-001 — verification shares the producer's canonicalization
379
380
Every signature and digest check verifies with the same
381
`OpenAgents.Provenance.Canonical` encoding that produced the claim
382
(`test/openagents/reputation_test.exs` lines 38 and 45-49;
383
`lib/openagents/reputation/claim.ex` lines 103 and 112). A canonicalization
384
bug — a dropped field, an unstable ordering — verifies fine on both sides.
385
The tamper test (lines 116-124) catches a signature that ignores content, not
386
an encoding that ignores a field. "No function returns a ranking" is one
387
`function_exported?` check on one name (line 93) with no enumeration of the
388
module's exports. The key-rotation half
389
(`test/openagents/forge/key_rotation_test.exs`) bites hard and is the model
390
to follow. Smallest change: pin one full claim to a hard-coded expected byte
391
encoding and digest, the way `test/openagents/forge/wal_test.exs` lines
392
282-298 pin the WAL encoding.
393
394
### TOOL-005 — the boundary is enumerated at the wrong chokepoint
395
396
`test/openagents/dependency_boundary_test.exs` lines 152-178 enumerate
397
callers of `OpenAgents.Tools.Selector` and require each to name
398
`OpenAgents.Tools.Reach`. But `OpenAgents.Tools.Registry` is itself a public
399
un-narrowed catalog builder — `lib/openagents/tools/registry.ex` line 135
400
forwards whatever options it was handed — and the real catalog builders sit
401
one hop out (`lib/openagents/turns/turn_server.ex` line 113,
402
`lib/openagents/work/job_server.ex` line 138), outside the enumeration. A new
403
caller of the registry's prompt-definitions functions that omits `:reach`
404
offers the whole catalog and stays green — the exact violation the contract
405
says this test catches. The "names Reach" check is also satisfied by the
406
registry's unrelated boot-time `Reach.requirements()` call
407
(`lib/openagents/tools/registry.ex` line 366), so it cannot distinguish
408
resolving a caller from mentioning the module. Smallest change: enumerate
409
callers of the registry's catalog-building functions and require the reach
410
option at those sites.
411
412
### SELF-EDIT-001 — the population is a namespace prefix and the assertion is vacuous on empty
413
414
`test/openagents/dependency_boundary_test.exs` lines 196-198 filter to
415
modules named under the tools namespace, but the contract quantifies over
416
"every tool module." A tool-shaped module elsewhere — the coding work module,
417
a future plugin namespace — can name the hot-loader and stay green. The
418
assertion at line 93 is `offenders == []`, which passes if the module
419
enumeration breaks and returns nothing (unreadable BEAMs are swallowed at
420
line 210), and the fleet-release module list at lines 43-50 is hand-declared
421
with no existence check, so a renamed release module silently empties the
422
check forever. Smallest change: assert the enumerated population and the
423
release-module list are both non-empty, and derive the tool population from
424
the shipped-plus-fixture catalogs rather than a name prefix.
425
426
### SETTLEMENT-001 — the uniqueness ladder is never climbed
427
428
Every "pays once" mechanism is a constraint no test reaches: the payment-hash
429
unique index (`priv/repo/migrations/20260823060000_create_bounty_settlement.exs`
430
line 147) cannot collide because the test gateway returns a constant hash and
431
no second payment ever succeeds; the single-`paid`-intent partial index and
432
the one-receipt-per-intent index are shadowed by earlier context refusals.
433
The invariant calls its schemas "seven append-only schemas," and no
434
append-only trigger exists on any settlement table — the refund test re-reads
435
a receipt and finds it unchanged, but nothing would have refused a rewrite.
436
Smallest change: raw-insert tests against each uniqueness constraint, and
437
either add the append-only triggers or strike the phrase.
438
439
### COMPENSATION-001 — the payout check reads back a literal
440
441
"Never creates payout authority" is proven by
442
`refute function_exported?(..., :payout, 2)` and by asserting
443
`payout_authority == false` in a projection that hard-codes the literal
444
(`lib/openagents/compensation.ex` lines 30, 205, 239). The real defense — the
445
database CHECK on the policy rules
446
(`priv/repo/migrations/20260817010500_create_compensation_accounting.exs`
447
line 162) — is never bitten, and only one of seven append-only triggers is
448
tested. Smallest change: insert a policy with `payout_authority: true` raw
449
and assert PostgreSQL refuses.
450
451
### EFFECT-001 — the production call site's transaction is untested
452
453
The outbox mechanics bite (`test/openagents/effects_test.exs`: real rollback,
454
real payload-digest conflict, real reclaim, genuinely concurrent claims). But
455
`test/openagents/effects/work_launch_test.exs` lines 27-41, titled "commits
456
its launch in the same transaction," asserts only that the effect exists
457
afterward, and the refusal case fails before the enqueue runs — so moving the
458
enqueue outside the job transaction, reintroducing the exact crash window
459
EFFECT-001 exists to close, keeps both tests green. The two schema
460
constraints the contract leans on (`effects_lease_pair_check`,
461
`effects_status_shape_check` in
462
`priv/repo/migrations/20260824204740_create_effects.exs`) are never reached
463
by any test. EFFECT-002's milestone separation therefore also rests on
464
untested constraints. Smallest change: a raw insert against each constraint,
465
and a work-launch test that crashes between the job insert and the launch and
466
asserts the effect row exists.
467
468
### MODULE-001 — the pre-invocation byte check is real and unproven
469
470
The runner verifies the loaded executor before invocation
471
(`lib/openagents/tools/registry.ex` line 190), but the only tamper test
472
mutates an in-memory struct and calls the verify helper directly
473
(`test/openagents/modules/registry_test.exs` lines 171-184). Delete the
474
runner's check and the suite passes; no test unloads or changes module bytes
475
and drives the runner. Smallest change: purge a tool module's code in a test
476
and assert the runner returns the unavailable outcome.
477
478
### EXIT-005 — the sole-writer clause and the publisher
479
480
The chain, the rewrite mutation, and the anchor checks are excellent — the
481
rewrite is a real byte-level rewrite with recomputed keys, and the stranger's
482
anchor is taken from served bytes. Two clauses still have no proof:
483
484
- **"`append_entry/2` is the one function every writer reaches the log
485
  through"** — unchanged since the 2026-08-23 audit recorded it (issue #151).
486
  No test enumerates WAL writers, and the escape is real: `append_entry/2` is
487
  pure; the durable write is the CAS, and the test suite itself writes
488
  indexes without the chain in four places in
489
  `test/openagents/forge/independence_test.exs`. On a log with a pre-contract
490
  prefix, a bypassing writer's `chain_link_missing` is classified as history
491
  (`lib/openagents/forge/verification.ex` lines 330-341), so the bypass can
492
  be absorbed silently. The current writer population is three modules
493
  (`lib/openagents/forge/pushes.ex` line 175, `lib/openagents/forge/git_plane.ex`
494
  line 532, `lib/openagents/repositories/importer.ex` line 450); an
495
  import-table enumeration in the shape of
496
  `test/openagents/threads/grant_token_reach_test.exs` would close it.
497
- **`OpenAgents.Forge.AnchorPublisher` has zero test coverage.** Every anchor
498
  test calls the publish function by hand; nothing proves the scheduled child
499
  runs, reschedules, or that `published_at` advances — the clause the
500
  contract says lets a reader see that publication has stopped. Removing the
501
  supervisor child fails nothing.
502
503
Also: the encoding is verified by the same module that produces it;
504
`test/openagents/forge/wal_test.exs` lines 282-298 mitigate this with a
505
pinned hard-coded digest, which is the right shape and worth extending to a
506
full entry-plus-link vector.
507
508
### EXIT-002 and EXIT-003 — depth-1 import tables and a hand-listed function set
509
510
The independence reads are strict about missing modules (a `MatchError`, not
511
an empty pass), but they read one module's own import table only: a helper
512
module between the verifier and the database, or an `apply/3`, is invisible.
513
The mirror-function refusal at `test/openagents/forge/independence_test.exs`
514
line 564 names three functions as a literal with no completeness assertion
515
against the pushes module's exports — a new mirror-reading export evades it.
516
517
### EXIT-001 — six probes accept 200 with an empty body
518
519
`read_probe/2` (`test/openagents/data_rights/export_inventory_test.exs`
520
lines 529-534) treats any 200 as portable without checking the body, so the
521
six families the contract narrates as previously blocked would still read
522
portable if their routes regressed to returning empty lists for the owner's
523
own records. The issue, project, and repository probes check for the seeded
524
record; these six should too.
525
526
### WORK-001 — two of three bounds and the resume claim
527
528
The tool-call ceiling bites; the continuation ceiling and the ten-minute wall
529
clock have no test (no hit for either reason code in `test/`). The
530
non-advertisement clauses (no `deep_work` in a job's provider request, no
531
`work.delegate` or `memory.write` authority) are structurally invisible to
532
the scripted provider, which ignores the tools field entirely. Recovery
533
"resumes and continues" is asserted only as "not interrupted, generation
534
advanced" — a worker that adopts and immediately fails passes. SCV-001
535
inherits the same shape: "no job may deploy one" (`scv.deploy` never in job
536
authorities) has no test.
537
538
### Route classification — the catch-alls and the operator class
539
540
`test/openagents_web/authenticated_route_gate_test.exs` is the real thing —
541
derived population, non-empty guard, actual anonymous dispatch. Two
542
structural gaps sit beside it. First, the classifier's catch-alls
543
(`lib/openagents_web/route_authority.ex` line 588 for API GETs, lines 695-724
544
for browser path shapes) classify unknown reads as `:public_read` silently —
545
a private GET added in one of those shapes leaves the gate population without
546
failing anything; the box fan-out status route already demonstrates the
547
mislabel (harmless today only because the API inventory independently
548
dispatches it). Second, no test dispatches `:operator` routes anonymously as
549
a class — the gate test filters them out, and
550
`test/openagents_web/operator_surface_test.exs` proves membership, not
551
behavior, checking `pipe_through` for exactly one route. A route correctly
552
classified and correctly listed, but wired without the operator pipeline and
553
without an in-handler recheck, passes everything. FLEETPROMOTE-001's
554
`/admin/forge` is one path-keyed classification away from that shape, saved
555
today by its own LiveView test. Smallest change: dispatch the operator class
556
anonymously the way the authenticated class is dispatched.
557
558
### Scope-boundary scans — one backend is invisible and one guard is missing
559
560
`test/openagents/memory/scope_boundary_test.exs` is two proofs of different
561
strength. The entry-point enumeration bites. The query scan does not guard
562
against emptiness on the recall side (line 196 iterates whatever the AST scan
563
returns), and `OpenAgents.Memory.HybridRecall` reaches the database only
564
through raw SQL the AST scan cannot see — deleting the conversation predicate
565
from `lib/openagents/memory/hybrid_recall.ex` line 70 fails nothing. The scan
566
also accepts a scope column named anywhere in the query, not only in a
567
`where`. Separately, `OpenAgents.Memory.SemanticIndex.rebuild/1`
568
(`lib/openagents/memory/semantic_index.ex` lines 120-126) reads messages
569
across every conversation and sits outside both MEMORY-001's and MEMORY-004's
570
populations. And PRIVACY-001's projector enumeration labels
571
`OpenAgents.Tools.MemoryContract` as reading the category list only, while
572
its `memory_output/1` returns the raw stored claim to the model with no
573
redaction pass (`lib/openagents/tools/memory_contract.ex`) — the one-projector
574
assertion at line 264 compares the declaration to itself, and the redaction
575
check at line 270 is satisfied by any mention of the redaction module.
576
577
### UI-002 — the AST matcher's syntax blind spot
578
579
`test/openagents_web/tool_activity_projection_test.exs` has real anti-vacuity
580
guards and both-direction key sets, but its matcher recognizes only
581
`from ... select:` keyword queries; a projection written as a pipeline
582
(`select/3`, `select_merge`, a `Map.take` after the query) lands in neither
583
set and the exactness still passes.
584
585
### PERSONA-001 — two adapters have no wire probe
586
587
The behaviour and atom-table enumerations bite in both directions. But of the
588
three outbound HTTP adapters, only one is driven against a capturing plug;
589
`OpenAgents.Providers.OpenRouter` and `OpenAgents.Providers.VercelGateway`
590
have no wire probe in the boundary file, the configured-provider backstop
591
reads test-double config rather than production selections, and the
592
gateway's config key is not in the checked set. An OpenRouter payload builder
593
that appended its own system text would pass. (The payload test that exists
594
covers the messages field only, and shows instructions are trimmed — so
595
"byte for byte" is already not literal there.)
596
597
### PROVIDER-002 — lane routing is unverifiable under test config
598
599
The refusal paths bite against the production catalog list. But all three
600
provider lanes are the same test module under `config/test.exs`, so the
601
adapter-lane assertions are `Test == Test`, and the catalog endpoint tests
602
compare the response to the same function that renders it.
603
604
## The gate itself
605
606
Findings that are about whether proofs run, not whether they can fail.
607
608
- **A Markdown-only candidate runs no tests.** `ops/dev/precommit.sh` lines
609
  61-81 skip the test suite when every changed path is documentation, and
610
  `ops/ci/gate.sh` line 116 runs precommit as a stage — the receipt still
611
  records the stage as passed. Tests with no dedicated gate stage are only
612
  reached through precommit, including
613
  `test/openagents/hosted_ci_absence_test.exs`,
614
  `test/openagents/network_status_test.exs`,
615
  `test/openagents_web/transparency_surface_test.exs`,
616
  `test/openagents/transparency/work_disclosure_test.exs`,
617
  `test/openagents/forge/deployment_lane_test.exs`,
618
  `test/openagents/capacity_test.exs`, and both token-vault suites. This is
619
  the most likely mechanism by which the red operator-surface enumeration
620
  reached `main`.
621
- **The gate receipt is forgeable and short-circuits.** `ops/ci/gate.sh`
622
  lines 15-30 and 51-54: an unsigned JSON at a known path skips the gate
623
  entirely. `test/openagents/forge/gate_receipt_test.exs` proves shape and
624
  SHA-binding, not authenticity — RELEASE-004 does not claim authenticity, so
625
  this is a bound to record rather than a defect, but the stage list is
626
  declared in three unlinked places and nothing ties the receipt's list to
627
  the script's.
628
- **`ops/ci/reference-check.sh` passes when `rg` is missing** (see
629
  RELEASE-002 above).
630
- **RELEASE-004's absence test covers hosted providers by list**, and the
631
  list omits `cloudbuild.yaml` — notable because the deployment target is
632
  Google Cloud — plus a handful of others (CodeBuild, Bitbucket, Gitea and
633
  Forgejo workflow paths).
634
- **VAULT-001's third vault has no test.** `OpenAgents.Voice.RecordingVault`
635
  has no test file; nothing proves it does not borrow the GitHub key, and no
636
  enumeration closes the vault population, so a fourth vault that borrows a
637
  key is invisible.
638
639
## Every contract, one verdict
640
641
Verdicts: **can fail** (the cited proof goes red when the claim is violated),
642
**partial** (the core clause can fail; the named clause cannot), **cannot
643
fail** (a load-bearing clause has no proof that can go red). Details above
644
where a section exists; one line here for everything else.
645
646
### Identity and canon
647
648
| Contract | Verdict | Sharpest gap |
649
| --- | --- | --- |
650
| CANON-001 | partial | Content SHA-256 is shape-checked, never compared to any file's bytes (`lib/openagents/persona/source_manifest.ex` line 256) |
651
| CANON-002 | can fail | Strongest enumeration in the set; pattern covers the public schema only, and the audit-kind check is one-directional |
652
| PERSONA-001 | partial | Two of three outbound adapters have no wire probe; config backstop reads test doubles |
653
| PERSONA-002 | can fail | Voice-session role receipt persistence has no cited proof |
654
| PERSONA-003 | partial | The incomplete-results branch is untested; corpus digest computed on both sides, so a weakened corpus moves both |
655
| BLUEPRINT-001 | partial | Deletion of admitted rows and the facts table never attempted; explicit-none pinned only at the composer layer |
656
| PROGRAM-001 | partial | No-effect-authority is four atom names on one module; the in-flight-capture test asserts BEAM immutability |
657
| DEGRADE-001 | can fail | Real trigger tests, default path covered |
658
| PROGRAM-002 | partial | "The live turn never consumes their result" has no test; the synthetic-corpus claim is the fixture describing itself |
659
| PROGRAM-003 | can fail | Generation-pointer proof is genuine |
660
661
### Identity and authorization
662
663
| Contract | Verdict | Sharpest gap |
664
| --- | --- | --- |
665
| IDENTITY-001 | can fail | One-time state, replay, ban, token-never-in-browser all driven |
666
| IDENTITY-002 | partial | Classifier catch-alls mislabel silently; Repo-reach scan has no positive control; public-read LiveViews exempt from the event hook |
667
| IDENTITY-003 | partial | Non-persistence is a schema field-name check; KDF envelope parameters unpinned |
668
| IDENTITY-004 | partial | One-active-link constraint and attribution-across-unlink untested |
669
| IDENTITY-005 | can fail | Route list is 5 of 12 box-control paths |
670
| IDENTITY-006 | cannot fail | See section above |
671
| IDENTITY-007 | partial | Cited file has no grant test; real coverage lives elsewhere |
672
| IDENTITY-008 | can fail | Lock asserted from the live database function; contention window honestly declared unproven |
673
| IDENTITY-009 | can fail | Planted-secret redaction is a real control |
674
| IDENTITY-010 | can fail | Catalog scan with positive control; nested-value and derived-copy residues |
675
| IDENTITY-011 | can fail | Query plan asserted with sequential scans priced out |
676
| IDENTITY-012 | can fail | Residue: a future non-changeset writer of guarded columns |
677
| CAPACITY-002 | can fail | Concurrency tests would fail without the locks |
678
| CAPACITY-003 | partial | Interval test asserts the literal it passed in; 15-second timeout and cost parser untested |
679
| WORK-002 | can fail | Single-probe and cancellation-stamp ordering proven |
680
681
### Data, memory, and privacy
682
683
| Contract | Verdict | Sharpest gap |
684
| --- | --- | --- |
685
| DATA-001 | cannot fail | See section above |
686
| DATA-002 | partial | Cited migration name and identity-source constraint do not exist; no duplicate-insert or race test |
687
| DATA-003 | can fail | Partition test is complete; timestamp-tie determinism unasserted |
688
| MEMORY-001 | can fail | The semantic-index rebuild reads cross-conversation and sits outside the population |
689
| MEMORY-002 | partial | Model-side clauses vacuous against a scripted provider; host-side halves bite |
690
| MEMORY-003 | can fail | Real trigger tests via raw SQL |
691
| MEMORY-004 | partial | Query scan vacuous for the hybrid backend's raw SQL; no non-empty guard on the recall side |
692
| MEMORY-005 | cannot fail | See section above |
693
| MEMORY-006 | partial | Synonym-recall metric measures the test's own regex provider |
694
| MEMORY-007 | can fail | Gate driven end to end with each bypass refused |
695
| MEMORY-008 | partial | Default-off asserted from the eval JSON, not the shipped config |
696
| MEMORY-009 | partial | Same default-off defect; build determinism genuinely proven |
697
| PRIVACY-001 | partial | The claim-projecting tool is mislabeled and skips redaction; projector assertion compares the declaration to itself |
698
699
### Turn, provider, and effects
700
701
| Contract | Verdict | Sharpest gap |
702
| --- | --- | --- |
703
| TURN-001 | can fail | Real second insert against the real index; no concurrent case |
704
| TURN-002 | partial | Single-transaction claim untestable as written — moving inserts out of the transaction passes |
705
| TURN-003 | partial | Non-blocking proven; persist-before-broadcast ordering not |
706
| TURN-004 | can fail | Idempotency claimed, never called twice |
707
| TURN-005 | partial | Parallel-call refusal and continuation ceiling have zero tests |
708
| PROVENANCE-001 | can fail | Five raw-SQL trigger tests; the strongest turn proof |
709
| PROVIDER-001 | partial | Contract test iterates two of four adapters |
710
| PROVIDER-002 | partial | Lane assertions are the same module compared to itself under test config |
711
| EFFECT-001 | partial | See section above |
712
| EFFECT-002 | partial | The status-shape constraint that carries the claim is untested |
713
| TOOL-001 | partial | No registry rebuild mid-turn; digest compared to the live registry |
714
| TOOL-002 | partial | Execution contexts hand-built rather than application-created |
715
| TOOL-003 | can fail | Claim atomicity proven sequentially only |
716
| TOOL-004 | partial | The UI disclosure test runs entirely on literal maps |
717
| TOOL-005 | partial | See section above — wrong chokepoint |
718
| TOOL-006 | can fail | Reads shipped config through the prod reader, not the test fixture; both directions |
719
| DEGRADE-002 | can fail | Honest-degradation asserted on the rendered answer |
720
721
### Collective, settlement, and modules
722
723
| Contract | Verdict | Sharpest gap |
724
| --- | --- | --- |
725
| COLLECTIVE-001 | partial | Cited database constraints never exercised; raw-quote check is six hand-listed fields |
726
| COLLECTIVE-002 | cannot fail | See section above |
727
| COLLECTIVE-003 | can fail | Append-only via real constraint errors |
728
| COMPENSATION-001 | partial | See section above |
729
| REPUTATION-001 | partial | See section above |
730
| SETTLEMENT-001 | partial | See section above |
731
| MODULE-001 | partial | See section above |
732
| MODULE-002 | can fail | Projection key allowlist would strengthen it |
733
| MODULE-003 | can fail | Nine-axis filter matrix is a real enumeration |
734
| MODULE-004 | cannot fail | See section above |
735
736
### Delegated work, threads, and deployment plane
737
738
| Contract | Verdict | Sharpest gap |
739
| --- | --- | --- |
740
| WORK-001 | partial | See section above |
741
| SELF-EDIT-001 | partial | See section above |
742
| SCV-001 | partial | The no-job-may-deploy clause has no test |
743
| OUTCOME-001 | partial | Four of five false-green classes never pass through either path; the pure-grader suite proves fixtures |
744
| THREAD-001 | partial | Token reach is exemplary; the race proof does not race (see above) |
745
| THREAD-002 | can fail | Write routes dispatched as a reader; one subset check is one-directional |
746
| DEPLOYPLANE-001 | partial | Artifact-digest format never validated |
747
| DEPLOYPLANE-002 | can fail | Every bound dimension widened in turn |
748
| DEPLOYPLANE-003 | partial | The digest half of "same SHA and same digest" never varied on the admission path |
749
| DEPLOYPLANE-004 | cannot fail | See section above |
750
| DEPLOYPLANE-005 | partial | Supervisor gating untested; all provider claims carried by the one fake |
751
| FLEETPROMOTE-001 | can fail | Best-proven contract in the ledger; the browser route's pipeline membership rests on its own LiveView test |
752
753
### Voice, admin, and interface
754
755
| Contract | Verdict | Sharpest gap |
756
| --- | --- | --- |
757
| VOICE-001 | can fail | Architecture and provider values never given a bad case |
758
| VOICE-002 | partial | CSRF never omitted; SDP size ceiling untested; header check greps the variable name, not the value |
759
| VOICE-003 | partial | The database-constraint half is unproven; the one-active index has no test and the code path does not use it |
760
| VOICE-004 | can fail | Hostile provider frames through the real decoder |
761
| VOICE-005 | cannot fail | See section above |
762
| VOICE-006 | can fail | But the prose contradicts the cited test (see above) |
763
| VOICE-007 | can fail | Changed-identity refusal has no dedicated test |
764
| VOICE-008 | can fail | Exact durable terminal set asserted |
765
| VOICE-009 | can fail | Real chronology plus a real constraint error; same stale sentence as VOICE-006 |
766
| VOICE-010 | partial | The advisory-lock serialization claim has no concurrent test |
767
| VOICE-011 | partial | The event-kind field is a free-text channel no test constrains; the 64-event cap untested |
768
| VOICE-012 | can fail | The no-opt-out never-claim has no closing mechanism |
769
| ADMIN-001 | can fail | Red right now (see above); membership proven, class-wide behavior not |
770
| DATA-004 | can fail | Cascade seeded broadly with direct-delete refusals first; the voice-summary export branch is vacuous |
771
| UI-001 | partial | The operator class is never dispatched anonymously |
772
| UI-002 | can fail | Pipeline-syntax queries evade the matcher |
773
| UI-003 | cannot fail | See section above |
774
| LEADERBOARD-001 | can fail | Two named exclusions never seeded, so excluded vacuously |
775
| OBSERVABILITY-001 | cannot fail | See section above |
776
777
### Release, status, and transparency
778
779
| Contract | Verdict | Sharpest gap |
780
| --- | --- | --- |
781
| RELEASE-001 | cannot fail | See section above |
782
| RELEASE-002 | cannot fail | See section above |
783
| VAULT-001 | partial | The recording vault has no test; no enumeration over the vault population |
784
| RELEASE-003 | cannot fail | See section above |
785
| RELEASE-004 | partial | Provider path list omits the deployment target's own CI config file |
786
| RELEASE-005 | can fail | Empty-relup refusal is a real positive control |
787
| RELEASE-006 | can fail | Both directions of refusal at target and readiness |
788
| RELEASE-007 | can fail | Parses the real Dockerfile; one silent-skip seam when a variable is undeclared in a stage |
789
| RELEASE-008 | can fail | Runs the real OTP walk against the real running application |
790
| RELEASE-009 | can fail | Fail-closed in every direction; the file has no dedicated gate stage |
791
| STATUS-001 | can fail | Structs contribute one opaque key; degradation enumerated for one section only |
792
| CAPACITY-001 | partial | Redaction proven over nine hand-listed fields, no exact key set on the projection |
793
| TRANSPARENCY-001 | partial | The route population filter misses the anonymous JSON API entirely; the gate check proves a static reference, not a gated path |
794
795
### Repository, exit, and issues
796
797
| Contract | Verdict | Sharpest gap |
798
| --- | --- | --- |
799
| REPOSITORY-001 | can fail | The past insert-the-grant-yourself defect is genuinely closed; regex-evading join forms remain the residue |
800
| API-001 | partial | Governance covers one of four extension objects; one enum is written twice with nothing binding the copies |
801
| CONTRIBUTION-001 | can fail | Cross-binds the document to the router, both authorities, and the real push guard |
802
| REPOSITORY-002 | can fail | Executes the real script against real hostile remotes |
803
| REPOSITORY-003 | can fail | Real pushes, real cache deletion, real replay |
804
| EXIT-001 | can fail | Six probes accept 200 with an empty body (see above) |
805
| EXIT-002 | partial | Depth-1 import tables; helper-module indirection invisible |
806
| EXIT-003 | partial | Mirror-function list is a literal with no completeness check |
807
| EXIT-004 | can fail | Rebuilds the exact #179 failing shape and clones for real |
808
| EXIT-005 | partial | Sole-writer clause still unproven (#151); the anchor publisher has zero coverage (see above) |
809
| EXIT-006 | can fail | Gap list vacuous today in one direction, by honest construction |
810
| STACK-001 | can fail | The cited CI script is invoked by nothing |
811
| ISSUE-001 | can fail | Real pushes over real HTTP through the real auth plug |
812
| FORUM-001 | can fail | The source scan carries its own anti-rot control |
813
| ISSUE-002 | can fail | The lock-based no-clobber clause and the 200-body bound are untested |
814
| ISSUE-003 | can fail | Ambiguous-name and unsettled-name cases covered |
815
| ISSUE-004 | can fail | Crafted events from readers asserted to write nothing |
816
| PROMISE-001 | partial | The LIVE gate is proven on create, not on update into LIVE |
817
| PROMISE-002 | can fail | Proposed status; the append-only trigger is already exercised |
818
| NOTIFY-001 | partial | The "reveals nothing" clause has no proof — no schema enumeration, no content refutation anywhere in the cited tests |
819
| FORGEAPI-001 | can fail | Dispatches every envelope-classified route and pins the exact key set |
820
821
## What to fix first
822
823
1. **Amend ADMIN-001 for the Gym and re-run the enumeration** — the ledger is
824
   wrong today and the proof already says so.
825
2. **MEMORY-005** — decide whether the code or the sentence is right; the two
826
   cannot coexist. If the fallback stays, the invariant must be rewritten and
827
   the consent claim withdrawn.
828
3. **Close the gate's Markdown-only skip** or give the enumeration tests
829
   (operator surface, transparency surface, hosted-CI absence, work
830
   disclosure) a dedicated gate stage, so a red enumeration cannot land with
831
   a passing receipt again.
832
4. **RELEASE-003** — wire the tested module into production or retarget the
833
   proof; today the claim is carried by code nothing cited tests, and the
834
   cited test covers code nothing runs.
835
5. **THREAD-001's race** — a concurrency proof in shared sandbox mode proves
836
   serialization by the test harness, not by the lock. Two real connections
837
   or an honest narrowing.
838
6. **The unexercised constraint set** — settlement uniqueness and append-only,
839
   the effects lease-pair and status-shape checks, compensation's payout
840
   CHECK, the voice one-active index: each is one raw-SQL test in the shape
841
   `test/openagents/threads/grant_fence_test.exs` already uses everywhere.
842
7. **EXIT-005's writer set and publisher** — the one remaining row from the
843
   2026-08-23 audit's closing table, still open, plus a supervised child with
844
   no test.
845
8. **IDENTITY-006** — the credential's branch discipline is the whole point
846
   of the contract and is one real receive-pack test away from proven.
847
848
A closing observation the counts understate: the proofs this repository
849
builds when it decides to enumerate — the operator surface, the grant-token
850
reach, the visibility joins, the WAL rewrite, the fleet-promotion caller
851
sets — are genuinely strong, and three of them caught real defects during
852
this audit (the Gym, the mislabeled fan-out route, the stale admin-panel
853
comment). The recurring weakness is not the enumerations; it is the sentence
854
beside them that names a mechanism ("in one transaction", "under a lock",
855
"append-only", "the only definition", "before the supervision tree starts")
856
that no test has ever watched fail. Where a constraint or an ordering is
857
load-bearing, the proof should reach it raw — the way the fence, provenance,
858
and profile-memory tests already do — or the ledger should stop crediting it.

This page updates live while a promote is in flight · changelog