Skip to content

Commit 7721c91

Browse files
feat: EchoResidueCell — Agda mirror of 007's Holding/Spent once-only discipline (#250)
## What A new module `proofs/agda/EchoResidueCell.agda` — the Agda mirror of **007's `ResidueCell`** (`Holding`/`Spent`) once-only reversal discipline, the *operational* half that `EchoReversibilityBridge` (#249) deferred as "runtime bookkeeping". 007's Idris2 spec (`EchoResidueLinear.idr`) enforces once-only via the **linear quantity** `(1 e : Echo f y)`. Agda has no such quantity under `--safe`, so this module enforces it via the **state machine** instead — which is exactly how 007's *runtime* (the rung-3b residue cell) does it. ## The mirror | This module | Idris `EchoResidueLinear.idr` | |---|---| | `ResidueCell f y = holding (Echo f y) \| spent` | `data ResidueCell … = Holding (1 e) \| Spent` | | `takeForReverse : ResidueCell f y → Maybe A` | `takeForReverse … : Maybe a` | | `consume : ResidueCell f y → ResidueCell f y` (Holding→Spent) | the cell-state effect of `reverse` | | `holding-reverses` | `holdingReverses` | | `spent-does-not-reverse` | `spentDoesNotReverse` | | **`once-only`** — `takeForReverse (consume c) ≡ nothing` | the `(1 e)` linear-consumption guarantee | `once-only` is the headline: after `consume`, a residue replays **at most once** — the state-machine form of linear consumption, and the operational content of 007's cross-handler once-only property that the rung-3b *static* check could only approximate by presence. All proofs are `refl` (definitional). ## Honest scope Captures the once-**per-cell** discipline. It does **not** model linear NON-DUPLICATION (nothing stops a caller copying a `holding` cell before consuming) — that needs the `(1 e)` quantity / an `@0`-style erasure encoding, and remains the documented next slice. Stated explicitly in the module preamble and the MAP.adoc scope note. ## Discipline - `--safe --without-K`, **zero postulates**, no funext. - Wired into `All.agda` (next to `EchoReversibilityBridge`); 7 headlines pinned in `Smoke.agda`. - Classified up front in `echo-kernel-note.adoc` (Tier 2) + `MAP.adoc` (the 007-bridge section) — `sh scripts/kernel-guard.sh` PASS (applying the lesson from #249). - **Verified on latest `main`:** `EchoResidueCell.agda`, `Smoke.agda`, and `All.agda` all `agda --safe --without-K` exit 0 (captured locally). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018CaSgNjNURC7ocsyjYh9We --- _Generated by [Claude Code](https://claude.ai/code/session_018CaSgNjNURC7ocsyjYh9We)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent e6a15a6 commit 7721c91

5 files changed

Lines changed: 119 additions & 3 deletions

File tree

docs/echo-types/MAP.adoc

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,18 @@ postulates.
483483
`EchoTotalCompletion.A↔ΣEcho`), and `capability-can-be-dropped` /
484484
`no-recovery-once-dropped` (007 phase-2 "affine capability, linear
485485
consumption" via `EchoLinear.weaken` / `no-section-weaken`).
486+
* Proofs: `proofs/agda/EchoResidueCell.agda` — the Agda mirror of 007's
487+
`ResidueCell` (`holding` / `spent`) operational once-only discipline.
488+
`takeForReverse : ResidueCell f y → Maybe A` (= the Idris partial form),
489+
`consume` (Holding→Spent), `holding-reverses` / `spent-does-not-reverse`
490+
(↔ Idris `holdingReverses` / `spentDoesNotReverse`), and the headline
491+
`once-only` — after `consume`, a further `takeForReverse` is always
492+
`nothing` (the state-machine form of the `(1 e)` linear consumption).
486493
* Scope: the type-level totality + recovery + linear→affine no-section
487-
content. 007's linear `(1 e : Echo f y)` quantity is NOT re-proved in
488-
Agda (the full quantity-semantics port is the documented next slice).
494+
content, plus the once-PER-CELL replay discipline as a state machine.
495+
007's linear NON-DUPLICATION quantity `(1 e : Echo f y)` is NOT re-proved
496+
in Agda (nothing stops copying a `holding` cell) — the full
497+
quantity-semantics port (`@0`-style erasure) is the documented next slice.
489498

490499
=== Tropical / Dyadic `[REAL*]`
491500
Bridge domains enumerated in `docs/EchoBridges.md` (JanusKey / CNO /

docs/echo-types/echo-kernel-note.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ kernel** — the boundary is real and lives outside this core.
8282
`EchoEpistemicResidue`, `EchoExamples`, `EchoGraded`,
8383
`EchoLinear`, `EchoPullback`, `EchoRelModel`, `EchoScope`,
8484
`EchoStabilityTests`, `EchoThermodynamics`, `EchoCNOBridge`,
85-
`EchoEphapaxBridge`, `EchoReversibilityBridge`,
85+
`EchoEphapaxBridge`, `EchoReversibilityBridge`, `EchoResidueCell`,
8686
`EchoThermodynamicsFinite`, `EchoIntegration`, `EchoAccess`,
8787
`EchoCost`, `EchoCostInstance`, `EchoSearch`, `EchoSearchInstance`,
8888
`EchoExampleAbsInt`, `EchoExampleParser`, `EchoExampleProvenance`,

proofs/agda/All.agda

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ open import AntiEchoTropicalGeneric
3737
open import EchoIntegration
3838
open import EchoCNOBridge
3939
open import EchoReversibilityBridge
40+
open import EchoResidueCell
4041

4142
open import EchoApprox
4243
open import EchoApproxInstance

proofs/agda/EchoResidueCell.agda

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
-- SPDX-License-Identifier: MPL-2.0
2+
-- SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
3+
--
4+
-- EchoResidueCell: the Agda mirror of 007's `ResidueCell` (Holding / Spent)
5+
-- operational once-only reversal discipline, under --safe --without-K.
6+
--
7+
-- 007's Layer-10 reversibility runtime (and its Idris2 spec
8+
-- `proofs/idris2/EchoResidueLinear.idr`) represents an armed undo as a
9+
-- `ResidueCell f y` that is either `Holding` a residue or `Spent`.
10+
-- `takeForReverse` replays a Holding cell (recovering the input) and returns
11+
-- `nothing` on a Spent cell; consuming a cell transitions it Holding → Spent,
12+
-- so a residue reverses AT MOST ONCE.
13+
--
14+
-- The Idris version enforces once-only via the LINEAR quantity `(1 e : Echo f
15+
-- y)`. Agda has no such quantity under `--safe`, so this module enforces it
16+
-- via the STATE MACHINE instead: `consume` makes the cell `Spent`, and
17+
-- `once-only` proves that a second `takeForReverse` after `consume` is always
18+
-- `nothing`. This is the operational half that `EchoReversibilityBridge`
19+
-- deferred as "runtime Holding/Spent bookkeeping": the bridge's
20+
-- `takeForReverse` is the *total* `Just`-branch (a bare `Echo f y → A`); here
21+
-- it is the partial `ResidueCell f y → Maybe A` with the cell carrying the
22+
-- Holding/Spent state.
23+
--
24+
-- Honest scope: this captures the once-PER-CELL discipline operationally. It
25+
-- does NOT model linear NON-DUPLICATION (nothing stops a caller copying a
26+
-- `holding` cell before consuming) — that needs the `(1 e)` quantity / an
27+
-- `@0`-style erasure encoding and remains the documented next slice.
28+
29+
{-# OPTIONS --safe --without-K #-}
30+
31+
module EchoResidueCell where
32+
33+
open import Level using (Level; _⊔_)
34+
open import Data.Product.Base using (proj₁)
35+
open import Data.Maybe.Base using (Maybe; just; nothing)
36+
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
37+
38+
open import Echo using (Echo; echo-intro)
39+
40+
private variable
41+
a b : Level
42+
A : Set a
43+
B : Set b
44+
45+
-- A residue cell over the visible output `y`: either `holding` an echo
46+
-- residue, or `spent`. (= the Idris `ResidueCell`; `holding` takes a bare
47+
-- `Echo f y` rather than a linear `(1 e)` — see the honest-scope note above.)
48+
data ResidueCell {a b} {A : Set a} {B : Set b} (f : A B) (y : B)
49+
: Set (a ⊔ b) where
50+
holding : Echo f y ResidueCell f y
51+
spent : ResidueCell f y
52+
53+
-- Replay the cell: a `holding` cell yields the recovered input; a `spent`
54+
-- cell yields `nothing`. (= the Idris `takeForReverse`.)
55+
takeForReverse : {f : A B} {y : B} ResidueCell f y Maybe A
56+
takeForReverse (holding e) = just (proj₁ e)
57+
takeForReverse spent = nothing
58+
59+
-- Consuming a cell transitions `holding → spent` (`spent` stays `spent`).
60+
-- This is the cell-state effect of a `reverse <name>` in 007.
61+
consume : {f : A B} {y : B} ResidueCell f y ResidueCell f y
62+
consume (holding _) = spent
63+
consume spent = spent
64+
65+
-- A `holding` cell armed from `x` reverses to exactly `x`
66+
-- (= Idris `holdingReverses`).
67+
holding-reverses : (f : A B) (x : A)
68+
takeForReverse (holding (echo-intro f x)) ≡ just x
69+
holding-reverses f x = refl
70+
71+
-- A `spent` cell never reverses (= Idris `spentDoesNotReverse`).
72+
spent-does-not-reverse : {f : A B} {y : B}
73+
takeForReverse {f = f} {y} spent ≡ nothing
74+
spent-does-not-reverse = refl
75+
76+
-- Consuming a `holding` cell spends it.
77+
consume-spends : {f : A B} {y : B} (e : Echo f y)
78+
consume (holding e) ≡ spent
79+
consume-spends _ = refl
80+
81+
-- THE once-only theorem: after consuming ANY cell, a further `takeForReverse`
82+
-- is always `nothing` — a residue reverses at most once. This is the
83+
-- state-machine form of the Idris `(1 e)` linear-consumption guarantee, and
84+
-- the operational content of 007's cross-handler "once-only" property that
85+
-- the rung-3b *static* check could only approximate by presence.
86+
once-only : {f : A B} {y : B} (c : ResidueCell f y)
87+
takeForReverse (consume c) ≡ nothing
88+
once-only (holding _) = refl
89+
once-only spent = refl

proofs/agda/Smoke.agda

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ open import EchoReversibilityBridge using
107107
; no-recovery-once-dropped
108108
)
109109

110+
-- EchoResidueCell — the Agda mirror of 007's `ResidueCell` (Holding/Spent)
111+
-- operational once-only reversal discipline. `takeForReverse` replays a
112+
-- `holding` cell / fails on `spent`; `consume` transitions Holding→Spent;
113+
-- `once-only` is the headline — after `consume`, a further `takeForReverse`
114+
-- is always `nothing` (the state-machine form of the Idris `(1 e)` linear
115+
-- consumption). `holding-reverses` / `spent-does-not-reverse` mirror the
116+
-- Idris `holdingReverses` / `spentDoesNotReverse`.
117+
open import EchoResidueCell using
118+
( ResidueCell
119+
; takeForReverse
120+
; consume
121+
; holding-reverses
122+
; spent-does-not-reverse
123+
; consume-spends
124+
; once-only
125+
)
126+
110127
-- EchoNoSectionGeneric — raises the example-level
111128
-- `no-section-collapse-to-residue` to a uniform structural theorem.
112129
-- Generic headline `no-section-of-collapsing-map`: any collapsing

0 commit comments

Comments
 (0)