Skip to content

Commit 189f6ed

Browse files
Merge branch 'main' into claude/gallant-faraday-LSAGJ
2 parents f67fbe6 + f89a3aa commit 189f6ed

7 files changed

Lines changed: 501 additions & 1 deletion

File tree

docs/echo-types/MAP.adoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,24 @@ honest-bound matched-negative block.
253253
the `affine` mode in `EchoObservationalEquivalence`. Honest-bound:
254254
NOT side-channel-safe / cryptographic deniability / adaptive
255255
adversary. `proofs/agda/EchoDeniability.agda`. `[REAL]`
256+
* *Transaction (rollback safety, issue #174)* — `WriteSet` (staged
257+
mutations) → `RollbackLog` collapsing audit boundary; `rollback`
258+
admits no section, so a discarded write-set is unrecoverable from the
259+
receipt alone (#DB-2.1). Packaged as a `Security` instance
260+
(`transaction-security`), reducing to `no-section-of-collapsing-map`.
261+
Honest-bound: NOT durability / isolation / commit-semantics (the
262+
dual) / savepoint-locality / runtime-memory-zeroed.
263+
`proofs/agda/EchoTransaction.agda`. `[REAL]`
264+
* *Selective projection (σ–π commutativity, issue #176)* — the Codd law
265+
σ_p(π_S(R)) = π_S(σ_p(R)) for column-safe predicates, as an Echo-fibre
266+
set-equality (`_⇔_` of the two result relations per projected value).
267+
`SelectiveProjection` carries the column-safe factoring `p b ⇔ q
268+
(projection b)`; `select-project-commute` is the commutativity;
269+
`no-column-safe-lift` is the counterexample (a projected-away-column
270+
predicate has no column-restricted lift, so σ/π do not commute).
271+
Honest-bound: `_⇔_` is set-equality (full `_↔_` would need
272+
proof-irrelevant predicates); NOT a runtime query planner.
273+
`proofs/agda/EchoSelectiveProjection.agda`. `[REAL]`
256274
* *Aggregation (general, issue #175)* — aggregation-as-fold over a
257275
`Monoid`, with a `GroupAggregator` and the monoid-homomorphism law
258276
`aggregation-as-fold` (aggregating a concatenation = combining the

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ kernel** — the boundary is real and lives outside this core.
125125
*Application/extension modules* (also added via the same sweep):
126126
`EchoEntropy`, `EchoLLEncoding`, `EchoProvenance`, `EchoSecurity`,
127127
`EchoProbabilisticSupport`, `EchoDifferential`, `EchoDeniability`,
128-
`EchoAggregation`, `EchoVariance`
128+
`EchoAggregation`, `EchoVariance`, `EchoTransaction`,
129+
`EchoSelectiveProjection`
129130
are derived domain applications mapped under their own headings in
130131
MAP.adoc. `EchoDeniability` formalises residue deniability: the
131132
perfect (constant / no-section) case and the partial (injective /

proofs/agda/All.agda

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ open import EchoDifferential -- Perturbation tracking (audience-facing sensi
9696
-- and the injective/section-exists story (partial case), with the
9797
-- IsConstantOpener boundary tying deniability to the affine mode.
9898
open import EchoDeniability
99+
open import EchoTransaction -- Transaction rollback safety (issue #174; Security instance)
100+
open import EchoSelectiveProjection -- σ–π commutativity (issue #176; relational-algebra carrier)
99101

100102
-- Narrative deliverable: curated index of "why Echo deserves a name".
101103
open import EchoCanonicalIdentitySuite
@@ -127,6 +129,7 @@ open import Ordinal.Brouwer.VeblenPhiNormal -- φ₁ a normal function; next-ε
127129
open import Ordinal.Brouwer.VeblenBinary -- binary Veblen φ_α(β) + the diagonal Γ₀
128130
open import Ordinal.Brouwer.VeblenBinaryNormal -- every φ_α a normal function; φ_{α+1} enumerates fixed points of φ_α
129131
open import Ordinal.Brouwer.VeblenBinaryMono -- first-arg monotonicity; Γ₀ ≤′ φ_Γ₀(0) (diagonal pre-fixed point)
132+
open import Ordinal.Brouwer.VeblenBinaryLeast -- nextFix is the LEAST pre-fixed point; reverse-Γ₀ reduced to one closure
130133
open import Ordinal.Brouwer.StrictLeftMonoRefuted
131134
open import Ordinal.Brouwer.AdditivePrincipalGenericRefuted
132135
open import Ordinal.Buchholz.Syntax
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
{-# OPTIONS --safe --without-K #-}
2+
-- SPDX-License-Identifier: MPL-2.0
3+
-- SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
5+
-- EchoSelectiveProjection: the relational-algebra selection/projection
6+
-- commutativity (the subset case) as an Echo-fibre statement. Closes
7+
-- echo-types#174's sibling echo-types#176 (consumer: affinescript
8+
-- db-theory #4, `stdlib/Select.affine`).
9+
--
10+
-- The classical law (Codd): for selection σ_p (predicate-driven row
11+
-- filter) and projection π_S (column-subset),
12+
--
13+
-- σ_p(π_S(R)) = π_S(σ_p(R)) when p references only columns in S,
14+
-- σ_p(π_S(R)) ≠ π_S(σ_p(R)) when p references a column NOT in S.
15+
--
16+
-- *Honest level.* "=" between relations is SET equality: same rows.
17+
-- The Echo formalisation states it as a per-projected-value LOGICAL
18+
-- equivalence `_⇔_` of the two result fibres (co-membership), which is
19+
-- exactly set-equality of the result relations. A full `_↔_` (with
20+
-- round-trips) would additionally require the predicate family to be
21+
-- proof-irrelevant (`is-prop`); set-equality does not, and `_⇔_` is the
22+
-- faithful level. See the companion remark.
23+
--
24+
-- *The column-safe condition.* "p references only columns in S" is
25+
-- formalised as: p factors through the projection — there is a
26+
-- predicate `q : S → Set` on the projected columns with `p b ⇔ q
27+
-- (projection b)` for every record `b`. This `factors` field IS the
28+
-- "uses only columns in S" hypothesis.
29+
--
30+
-- *The Echo content.* With `pf = projection ∘ f` (the projected map),
31+
-- `Echo pf s` is π_S(R) at projected value `s` — the rows projecting to
32+
-- `s`. The two composites:
33+
--
34+
-- ProjectSelect s = Σ (Echo pf s) (λ e → p (f (proj₁ e)))
35+
-- -- π_S(σ_p(R)) at s: rows projecting to s that satisfy p
36+
-- SelectProject s = Σ (Echo pf s) (λ _ → q s)
37+
-- -- σ_q(π_S(R)) at s: rows projecting to s, kept iff q holds at s
38+
--
39+
-- `select-project-commute` is `∀ s → ProjectSelect s ⇔ SelectProject s`.
40+
-- The proof is K-free: pattern-matching the fibre witness `pf x ≡ s` as
41+
-- `refl` (s a free variable, so --without-K-safe) collapses the
42+
-- transport, and the predicate move is exactly the `factors` field.
43+
--
44+
-- *The counterexample.* `no-column-safe-lift` shows a predicate that
45+
-- reads a projected-away column (`proj₂`, with projection `= proj₁`)
46+
-- admits NO column-restricted lift `q` at all: the records `(true,true)`
47+
-- and `(true,false)` share projection `true` but disagree on the
48+
-- predicate, so any `q true` would be both inhabited and empty. This is
49+
-- the "≠" direction: σ and π do not commute for such a predicate.
50+
--
51+
-- Headlines (pinned in Smoke.agda):
52+
-- * SelectiveProjection -- the column-safe setup record
53+
-- * select-project-commute -- σ–π commute (column-safe), per s
54+
-- * column-safe-example -- worked column-safe instance
55+
-- * no-column-safe-lift -- counterexample (non-column-safe ⇒ ✗)
56+
57+
module EchoSelectiveProjection where
58+
59+
open import Echo using (Echo)
60+
open import Data.Product.Base using (Σ; _,_; proj₁; proj₂; _×_)
61+
open import Data.Bool.Base using (Bool; true; false)
62+
open import Relation.Binary.PropositionalEquality using (_≡_; refl)
63+
open import Relation.Nullary using (¬_)
64+
65+
----------------------------------------------------------------------
66+
-- Logical equivalence (set-equality level). Kept local and minimal —
67+
-- the σ–π law is co-membership of the two result relations, not a
68+
-- structure-carrying bijection.
69+
----------------------------------------------------------------------
70+
71+
record _⇔_ (P Q : Set) : Set where
72+
constructor equiv
73+
field
74+
to : P Q
75+
from : Q P
76+
77+
----------------------------------------------------------------------
78+
-- The column-safe selection/projection setup.
79+
----------------------------------------------------------------------
80+
81+
record SelectiveProjection {A B : Set} (f : A B) (S : Set) : Set₁ where
82+
field
83+
projection : B S -- π_S
84+
p : B Set -- σ predicate on full records
85+
q : S Set -- its restriction to the projected columns
86+
-- "p uses only columns in S": p factors through projection as q.
87+
factors : b p b ⇔ q (projection b)
88+
89+
-- The projected map; `Echo pf s` = π_S(R) at projected value s.
90+
pf : A S
91+
pf a = projection (f a)
92+
93+
-- π_S(σ_p(R)) at s : rows projecting to s that satisfy p.
94+
ProjectSelect : S Set
95+
ProjectSelect s = Σ (Echo pf s) (λ e p (f (proj₁ e)))
96+
97+
-- σ_q(π_S(R)) at s : rows projecting to s, kept iff q holds at s.
98+
SelectProject : S Set
99+
SelectProject s = Σ (Echo pf s) (λ _ q s)
100+
101+
----------------------------------------------------------------------
102+
-- σ–π commute (column-safe): the two result relations co-inhabit at
103+
-- every projected value. Set-equality of σ_p(π_S(R)) and π_S(σ_p(R)).
104+
----------------------------------------------------------------------
105+
106+
select-project-commute :
107+
{A B : Set} {f : A B} {S : Set} (sp : SelectiveProjection f S) (s : S)
108+
SelectiveProjection.ProjectSelect sp s ⇔ SelectiveProjection.SelectProject sp s
109+
select-project-commute {f = f} sp s = equiv to from
110+
where
111+
open SelectiveProjection sp
112+
to : ProjectSelect s SelectProject s
113+
to ((x , refl) , pe) = (x , refl) , _⇔_.to (factors (f x)) pe
114+
from : SelectProject s ProjectSelect s
115+
from ((x , refl) , qs) = (x , refl) , _⇔_.from (factors (f x)) qs
116+
117+
----------------------------------------------------------------------
118+
-- Worked column-safe instance: 2-column Bool records, project to
119+
-- column 0, predicate "column 0 is true" — uses only the projected
120+
-- column, so it factors (the identity equivalence).
121+
----------------------------------------------------------------------
122+
123+
column-safe-example : SelectiveProjection {Bool × Bool} {Bool × Bool} (λ b b) Bool
124+
column-safe-example = record
125+
{ projection = proj₁
126+
; p = λ b proj₁ b ≡ true
127+
; q = λ s s ≡ true
128+
; factors = λ b equiv (λ z z) (λ z z)
129+
}
130+
131+
----------------------------------------------------------------------
132+
-- Counterexample (the "≠" direction): a predicate reading a
133+
-- projected-away column has NO column-restricted lift. `(true,true)`
134+
-- and `(true,false)` share projection `true` but disagree on
135+
-- `proj₂ · ≡ true`, so any candidate `q true` is both inhabited and
136+
-- empty. Hence σ and π do not commute for such a predicate.
137+
----------------------------------------------------------------------
138+
139+
no-column-safe-lift :
140+
¬ Σ (Bool Set) (λ q b (proj₂ b ≡ true) ⇔ q (proj₁ b))
141+
no-column-safe-lift (q , fac)
142+
with _⇔_.from (fac (true , false)) (_⇔_.to (fac (true , true)) refl)
143+
... | ()
144+
145+
----------------------------------------------------------------------
146+
-- Companion remark.
147+
--
148+
-- `_⇔_` (set-equality of the result relations) is the faithful level:
149+
-- relational-algebra equality is membership equality. Upgrading to a
150+
-- structure-preserving `_↔_` would need the predicate family to be
151+
-- proof-irrelevant (an `is-prop (p b)` field); under `--safe
152+
-- --without-K` that is an extra hypothesis, not free, and it adds no
153+
-- relational-algebra content. NOT claimed: σ–π commutativity for
154+
-- arbitrary (non-column-safe) predicates (refuted by
155+
-- `no-column-safe-lift`); any runtime query-planner behaviour (the
156+
-- decision of whether a predicate is column-safe lives in the
157+
-- consumer). The carrier encodes only the property.
158+
----------------------------------------------------------------------
159+
160+
NotProved-arbitrary-predicate-commute : Set
161+
NotProved-arbitrary-predicate-commute = Bool
162+
163+
NotProved-runtime-query-planner : Set
164+
NotProved-runtime-query-planner = Bool

proofs/agda/EchoTransaction.agda

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
{-# OPTIONS --safe --without-K #-}
2+
-- SPDX-License-Identifier: MPL-2.0
3+
-- SPDX-FileCopyrightText: 2025-2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
5+
-- EchoTransaction: SQL transaction rollback safety as an instance of the
6+
-- generic no-section theorem. Closes echo-types#174 (consumer:
7+
-- affinescript db-theory #2, `stdlib/Transaction.affine`,
8+
-- `docs/academic/proofs/db-theory-2-transaction-safety.md` #DB-2.1).
9+
--
10+
-- !! HONEST BOUND, STATED UP FRONT !!
11+
--
12+
-- `rollback-discards-writes` is a TYPE-LEVEL guarantee that no pure Agda
13+
-- function reconstructs a discarded write-set from the rollback receipt
14+
-- alone (factored through
15+
-- `EchoNoSectionGeneric.no-section-of-collapsing-map`). It is NOT:
16+
-- * `durability` — a claim about writes surviving a crash;
17+
-- * `isolation` — a claim about concurrent-transaction visibility;
18+
-- * `commit-semantics` — #DB-2.2 commit-promotes-writes is the DUAL
19+
-- (commit PRESERVES writes, so it is not a
20+
-- no-section statement at all);
21+
-- * `savepoint-locality` — #DB-2.3 is a structural/runtime nesting
22+
-- claim outside this carrier;
23+
-- * `runtime-memory-zeroed` — a claim about physical memory contents.
24+
-- Conflating any of these with `rollback-discards-writes` is the category
25+
-- error the matched-negative block at the bottom heads off. Same
26+
-- honest-bound discipline as `EchoSecurity` / `RegionExitAudit`
27+
-- (R-2026-05-18).
28+
--
29+
-- *Structural content (#DB-2.1).* A transaction's staged-but-uncommitted
30+
-- writes are an affine resource; `rollback` consumes that resource and
31+
-- emits a receipt that records only THAT a rollback occurred, never which
32+
-- writes were discarded. Two distinct write-sets therefore collapse to
33+
-- the same receipt, so `rollback` admits no section: a post-rollback
34+
-- caller holding only the receipt cannot type-check a function returning
35+
-- the discarded write-set. This is precisely "the one consumption that
36+
-- discards is observationally equivalent to never having started".
37+
--
38+
-- This is the first `Security` instance outside the original region-exit
39+
-- audit setting (`EchoSecurity.region-exit-audit-instance`), establishing
40+
-- that any bracketed-mutation resource — transactions, scoped
41+
-- capabilities, scoped logs — reduces to the same generic lemma.
42+
--
43+
-- Headlines (pinned in Smoke.agda):
44+
--
45+
-- * Mutation / WriteSet / RollbackLog -- the carrier types
46+
-- * rollback -- the collapsing audit boundary
47+
-- * rollback-discards-writes -- #DB-2.1, direct no-section
48+
-- * transaction-security -- the `Security` instance
49+
-- * rollback-no-recovery -- #DB-2.1 via the abstract theorem
50+
--
51+
-- Scope guardrail (Echo-vs-Σ clearance). `rollback-discards-writes` uses
52+
-- `no-section-of-collapsing-map` directly, whose three-line trans/sym/cong
53+
-- proof is the canonical non-trivial Echo-typed content; replacing it with
54+
-- a bare `Σ` + `_≡_` would lose the collapse/no-section content. The
55+
-- `NotProved-*` aliases pin the honest scope.
56+
57+
module EchoTransaction where
58+
59+
open import EchoNoSectionGeneric using (no-section-of-collapsing-map)
60+
open import EchoSecurity using (Security; module SecurityTheorems)
61+
62+
open import Data.Nat.Base using (ℕ)
63+
open import Data.List.Base using (List; []; _∷_)
64+
open import Data.Product.Base using (Σ)
65+
open import Data.Unit.Base using (⊤; tt)
66+
open import Relation.Binary.PropositionalEquality using (_≡_; _≢_; refl)
67+
open import Relation.Nullary using (¬_)
68+
69+
----------------------------------------------------------------------
70+
-- The carrier.
71+
--
72+
-- A `Mutation` is an opaque cell-write (address, value); a `WriteSet` is
73+
-- the ordered list a transaction has staged but not committed. The
74+
-- `RollbackLog` receipt is information-free: it records only that a
75+
-- rollback happened.
76+
----------------------------------------------------------------------
77+
78+
data Mutation : Set where
79+
set-cell : Mutation
80+
81+
WriteSet : Set
82+
WriteSet = List Mutation
83+
84+
data RollbackLog : Set where
85+
rolled-back : RollbackLog
86+
87+
-- The audit boundary: rollback collapses every write-set to the single
88+
-- receipt, discarding the staged mutations.
89+
rollback : WriteSet RollbackLog
90+
rollback _ = rolled-back
91+
92+
----------------------------------------------------------------------
93+
-- The audit witnesses: two distinct write-sets that collapse identically.
94+
----------------------------------------------------------------------
95+
96+
ws₁ ws₂ : WriteSet
97+
ws₁ = []
98+
ws₂ = set-cell 0 1 ∷ []
99+
100+
ws₁≢ws₂ : ws₁ ≢ ws₂
101+
ws₁≢ws₂ ()
102+
103+
rollback-collapses : rollback ws₁ ≡ rollback ws₂
104+
rollback-collapses = refl
105+
106+
----------------------------------------------------------------------
107+
-- #DB-2.1 rollback-discards-writes (headline): no pure function
108+
-- reconstructs a discarded write-set from the rollback receipt alone.
109+
----------------------------------------------------------------------
110+
111+
rollback-discards-writes :
112+
¬ Σ (RollbackLog WriteSet) (λ recover ws recover (rollback ws) ≡ ws)
113+
rollback-discards-writes =
114+
no-section-of-collapsing-map rollback ws₁ ws₂ ws₁≢ws₂ rollback-collapses
115+
116+
----------------------------------------------------------------------
117+
-- The same guarantee as a `Security` instance, so the transaction
118+
-- carrier joins the EchoSecurity audience family. A single transaction
119+
-- scope is the (trivial) `RegionId`; `audit-no-recovery-at` re-derives
120+
-- #DB-2.1 through the abstract theorem, witnessing that the instance is
121+
-- real (not merely structurally similar).
122+
----------------------------------------------------------------------
123+
124+
transaction-security : Security
125+
transaction-security = record
126+
{ RegionId =
127+
; Resource = λ _ WriteSet
128+
; Receipt = λ _ RollbackLog
129+
; exit = λ _ rollback
130+
; res₁ = λ _ ws₁
131+
; res₂ = λ _ ws₂
132+
; res-distinct = λ _ ws₁≢ws₂
133+
; exit-collapses = λ _ rollback-collapses
134+
}
135+
136+
rollback-no-recovery :
137+
(r : ⊤)
138+
¬ Σ (RollbackLog WriteSet) (λ recover ws recover (rollback ws) ≡ ws)
139+
rollback-no-recovery = SecurityTheorems.audit-no-recovery-at transaction-security
140+
141+
----------------------------------------------------------------------
142+
-- Matched-negative block (HONEST BOUND, per R-2026-05-18 discipline).
143+
--
144+
-- `⊤`-aliased so `grep NotProved` catches them. A consumer citing
145+
-- `rollback-discards-writes` beyond these scopes is making a category
146+
-- error: the guarantee is exactly "no pure Agda function reconstructs
147+
-- the discarded write-set from the receipt alone, inside this model".
148+
----------------------------------------------------------------------
149+
150+
NotProved-durability : Set
151+
NotProved-durability =
152+
153+
NotProved-isolation : Set
154+
NotProved-isolation =
155+
156+
NotProved-commit-semantics : Set -- #DB-2.2 is the dual, not a no-section
157+
NotProved-commit-semantics =
158+
159+
NotProved-savepoint-locality : Set -- #DB-2.3 is structural/runtime
160+
NotProved-savepoint-locality =
161+
162+
NotProved-runtime-memory-zeroed : Set
163+
NotProved-runtime-memory-zeroed =
164+
165+
----------------------------------------------------------------------
166+
-- Companion remark.
167+
--
168+
-- #DB-2.2 (commit-promotes-writes) is the dual of #DB-2.1: commit
169+
-- PRESERVES the write-set, so it is an equality/section statement, not a
170+
-- no-section one — it belongs in a separate carrier and is deliberately
171+
-- not modelled here. #DB-2.3 (savepoint-locality) is a nesting/structural
172+
-- property of the transaction tree; it too is out of scope for this
173+
-- minimal rollback-safety carrier. The `Mutation` / `RollbackLog` types
174+
-- are placeholders chosen for inhabitability + a clean distinctness
175+
-- witness; the proof goes through for any carrier with two distinct
176+
-- write-sets collapsing to the same receipt.
177+
----------------------------------------------------------------------

0 commit comments

Comments
 (0)