Skip to content

Commit 9d7bbca

Browse files
claudehyperpolymath
authored andcommitted
feat(L10): phase 2 — echo residue as a linear undo-capability
Layer 10's reversal residue becomes a first-class linear value. The checker's residue stack now carries `Linear<Echo<T>>` (was a bare `Echo<T>`): `push_echo_residue` wraps the residue in `Type::Linear`, so the consumed-once discipline is explicit in the carried type and the single linear use is discharged by the matching `take_echo_residue` pop in a later `reverse`. Discipline: affine capability, linear consumption. A given reversal log is consumed at most once — a double `reverse` of one residue is rejected (the linear use, structural in the stack) — while a `reversible` block need not be paired with a `reverse` at all (an un-reversed residue is NOT an error; reversibility is a capability, usually unused). The opposite, strict must-reverse linearity, is deliberately rejected as semantically wrong and poor agent ergonomics; three new tests lock the affine drop in so it cannot be silently tightened. Formal model: proofs/idris2/EchoResidueLinear.idr (%default total, zero believe_me, idris2 --check clean) — reverseLinear consumes (1 e : Echo f y); reverseLinearCorrect (recovered input maps back to y, intrinsic); ResidueCell Holding/Spent carries the residue as agent state between handlers; takeForReverse (Spent -> Nothing); holdingReverses / spentDoesNotReverse; cellMode bridges back to the phase-1 mode. Still open (own rung): cross-handler NAMED residues need residue-binding grammar syntax (let undo = reversible {...}; reverse undo); the formal model (ResidueCell) is in place, only Harvard-guarded surface syntax is missing. Tests: 3 new typechecker tests; 883 oo7-core lib tests pass. fmt clean; clippy (CI-equivalent) -D warnings exit 0. Spec (TYPE-SYSTEM-SPEC §11b.4, OPERATIONAL-SEMANTICS §11.4), CHANGELOG, docs/echo-residue-integration.adoc and 6a2 STATE updated in lockstep. https://claude.ai/code/session_018CaSgNjNURC7ocsyjYh9We
1 parent 9ca0909 commit 9d7bbca

9 files changed

Lines changed: 361 additions & 71 deletions

File tree

.machine_readable/6a2/STATE.a2ml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
@state(version="2.0"):
88
phase: "implementation"
9-
next_action: "Phase-2 echo-residue (Linear<Echo<T>> undo-capability + cross-handler reversal); reconcile estate-standardization-20260607 into main; continue parser gap fill (types, imports, locales, choreographies)"
10-
last_action: "echo-types integrated as Kategoria Layer 10 reversibility — Echo residue makes reverse/irreversible type-safe (spec §11.3 now ENFORCED), Idris2 EchoResidue.idr proof, agent-facing L10_REVERSE_WITHOUT_RESIDUE (PR #34 merged); test/E2E/clippy/fmt CI gates greened (PR #38 merged); CI reconciled at root: hypatia ssh→https, A2ML 40→0, K9 7→0, grammar-guard, eclexiaiser merge-conflict, stapeln"
11-
updated: 2026-06-13T07:40:00Z
9+
next_action: "Layer-10 cross-handler named residues: residue-binding grammar syntax (let undo = reversible {…}; reverse undo) carried through agent state — Harvard-guarded grammar change, own rung; then continue parser gap fill (types, imports, locales, choreographies)"
10+
last_action: "Phase-2 echo-residue landed: residue is now Linear<Echo<T>> (affine capability, linear consumption — double-reverse rejected, un-reversed residue not an error); Idris2 EchoResidueLinear.idr (reverseLinear (1 e), ResidueCell Holding/Spent, idris2 --check clean, zero believe_me); 3 new typechecker tests (883 oo7-core passing); spec §11b.4/§11.4 + CHANGELOG + integration doc updated in lockstep. estate-standardization-20260607 reconciled into main (PR #40 merged). Prior: Layer 10 phase-1 (PR #34), CI gates greened (PR #38), 6a2/ecosystem refresh (PR #39)"
11+
updated: 2026-06-14T02:00:00Z
1212

1313
@blockers:
1414
- id: proof-debt
@@ -17,9 +17,6 @@ updated: 2026-06-13T07:40:00Z
1717
- id: ci-billing
1818
description: "GitHub Actions billing/spending-limit rejection — every job fails in 2-3s with no logs; blocks CI validation of all pushes (account-owner action: Settings → Billing & plans)"
1919
since: 2026-06-13
20-
- id: estate-standardization-unmerged
21-
description: "estate-standardization-20260607 (estate-standards catch-up: contractiles, bot_directives, svc/k9→self-validating rename, 24 CI workflows) diverged from main AND conflicts with PR #34/#38 (ci.yml + K9 guards renamed from PRE-#38 content, so the K9! magic/pedigree fixes must be re-applied during reconciliation)"
22-
since: 2026-06-13
2320
@end
2421

2522
@note(id="patacl-relationship", added="2026-04-27"):

.machine_readable/session-log.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@
2727
[2026-06-13 12:06:00] UPDATE: PR #38 MERGED to origin/main. BOTH this session's PRs are now in main (#34 echo-types Layer 10 + CI root-fixes; #38 test/E2E gates + session-log checkpoint). main is the single reconciled line. Branch-reconciliation now reduces to: both feature branches (claude/inspiring-meitner-QHuNU, claude/ci-cleanup-fmt-clippy-examples) are MERGED → safe to delete. Remaining next-actions unchanged: (ii) delete the two merged branches + enumerate any OTHER remote branches; (iii) doc-currency sweep (6a2, ANCHOR realignment, contractiles incl. Bust/Dust, svc/k9, CREATE bot_directives/, README/EXPLAINME, wiki bleeding-edge); (iv) standards/rsr-template drift check (flag divergence immediately); (v) recency-gate nextgen-languages/nextgen-typing/echo-types; (vi) resume main work. NOTE: GitHub Actions billing rejection may still be active — verify before relying on CI.
2828
[2026-06-13 12:25:00] BRANCH DECISION (item 3): chore/hypatia-scan-reusable-wrapper assessed → DROP (superseded, do not re-create). Evidence: (1) main already DELETED its inline hypatia-scan.yml; (2) main's codeql.yml already uses the `actions` extractor (the branch's "codeql actions matrix" commit is redundant); (3) estate-standardization-20260607 redoes BOTH codeql.yml + hypatia-scan.yml, and its hypatia-scan.yml is the same thin standards reusable-wrapper but pinned to a NEWER standards SHA (6cd3772) vs the branch's stale 97df762. The branch (3 wks old, 24 commits behind main, 3 commits ahead) adds nothing not better-done by main + estate-standardization. DELETION BLOCKED from this remote-exec env: the local git proxy rejects push-deletes ("remote end hung up") and the GitHub MCP has no delete-branch/ref tool. ACTION (account owner): delete chore/hypatia-scan-reusable-wrapper, OR it retires naturally when estate-standardization-20260607 lands. Remaining open branch decision: estate-standardization-20260607 (the standards catch-up; conflicts with #34/#38; needs maintainer-driven reconcile — re-apply the K9!/grammar fixes into the renamed self-validating/ guards).
2929

30+
31+
[2026-06-14 01:25:37] Session ended (continuation): item-2 estate-standardization reconcile MERGED (PR #40) → main is one reconciled line. Then resumed ORIGINAL WORK = Layer-10 PHASE 2 (residue as a linear undo-capability). (a) Idris2: proofs/idris2/EchoResidueLinear.idr (NEW, %default total, zero believe_me, idris2 --check clean) — reverseLinear consumes (1 e : Echo f y) (Idris2 quantity 1 = "consumed exactly once" proof, mirroring 007's planned Linear<Echo<T>>); reverseLinearCorrect (recovered input maps back to y, intrinsic via the dependent pair); ResidueCell Holding/Spent = the residue carried as agent STATE between handlers; takeForReverse (later handler consumes the cell; Spent→Nothing); holdingReverses/spentDoesNotReverse (cross-handler reversal succeeds iff a residue was retained); cellMode bridges a cell back to the phase-1 Reversibility mode. (b) Rust typechecker.rs: residue stack now carries Linear<Echo<T>> (push_echo_residue wraps Type::Echo in Type::Linear) — the consumed-once discipline is explicit in the carried type; the single linear use is the take_echo_residue pop in a later reverse. +3 typechecker_tests (l10_phase2_residue_type_is_linear_echo / _double_reverse_of_single_residue_is_error / _retained_residue_is_not_an_error) → 883 oo7-core lib tests passing. CI gates verified LOCALLY: cargo check --offline 0, full oo7-core test suite 922 ok 0 fail, fmt --check 0, clippy (CI-equivalent, no --all-targets) -D warnings exit 0. (c) DESIGN DECISION flagged to user (a plus/interesting): the residue is an AFFINE capability consumed LINEARLY — replaying one log twice is rejected (linear use; structural in the stack), but a reversible block need NOT be paired with a reverse (un-reversed residue is NOT an error; reversibility is a capability, usually unused). The opposite (strict must-reverse / LinearNotConsumed) was deliberately REJECTED as semantically wrong + poor agent ergonomics; the 3 tests lock the affine-drop in so it can't be silently tightened. (d) Docs in lockstep: TYPE-SYSTEM-SPEC §11b.4 + OPERATIONAL-SEMANTICS §11.4 (planned→landed), CHANGELOG phase-2 bullet, docs/echo-residue-integration.adoc (phase-2 section + design-decision subsection + "still open" cross-handler rung), 6a2 STATE.a2ml last/next_action + cleared estate-standardization-unmerged blocker. (e) NOTE — canonical-proof-suite MANIFEST.a2ml NOT touched: phase-1 EchoResidue.idr is not a manifest entry either, so EchoResidueLinear.idr stays alongside it (checked by idris2 --check, not the curated S/E/M suite); the proof-suite workflow ignores unlisted .idr files (no CI risk). STILL OPEN (next rung): cross-handler NAMED residues need residue-binding grammar syntax (let undo = reversible {…}; reverse undo) — Harvard-guarded grammar change, kept as its own rung; formal model (ResidueCell) already in place, only surface syntax missing. Phase-2 work pushed to branch claude/echo-residue-phase2-linear → draft PR. Billing blocker may still gate CI (account-owner action).

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ All notable changes to this project will be documented in this file.
66
## [Unreleased] — 2026-04-10 consolidation pass
77

88
### Added
9-
- **Layer 10 — Reversibility (Echo residue): echo-types integrated into the type system.** `reversible`/`irreversible`/`reverse` are no longer checked identically: `Type::Echo<T>` residues are tracked per body (`reversible` retains one, `irreversible` discards it, `reverse` consumes one), and a `reverse` with no residue is `ReverseWithoutResidue` — enforcing OPERATIONAL-SEMANTICS §11.3, previously unenforced. Grounded in echo-types' keystone `A ≃ Σ B (Echo f)` and mechanised in `proofs/idris2/EchoResidue.idr` (`encodeDecode`, `reverseAfterIrreversibleIllTyped`, `collapseHasNoSection`; `%default total`, zero `believe_me`). Surfaced to agents via `agent_api` code `L10_REVERSE_WITHOUT_RESIDUE` (+`mark_reversible`/`retain_echo` remediations). 4 new typechecker tests (880 `oo7-core` lib tests passing). Spec: TYPE-SYSTEM-SPEC §"Layer 10", OPERATIONAL-SEMANTICS §11.4; design note `docs/echo-residue-integration.adoc` (incl. the phase-2 linear-undo-capability rung).
9+
- **Layer 10 — Reversibility (Echo residue): echo-types integrated into the type system.** `reversible`/`irreversible`/`reverse` are no longer checked identically: `Type::Echo<T>` residues are tracked per body (`reversible` retains one, `irreversible` discards it, `reverse` consumes one), and a `reverse` with no residue is `ReverseWithoutResidue` — enforcing OPERATIONAL-SEMANTICS §11.3, previously unenforced. Grounded in echo-types' keystone `A ≃ Σ B (Echo f)` and mechanised in `proofs/idris2/EchoResidue.idr` (`encodeDecode`, `reverseAfterIrreversibleIllTyped`, `collapseHasNoSection`; `%default total`, zero `believe_me`). Surfaced to agents via `agent_api` code `L10_REVERSE_WITHOUT_RESIDUE` (+`mark_reversible`/`retain_echo` remediations). 4 new typechecker tests. Spec: TYPE-SYSTEM-SPEC §"Layer 10", OPERATIONAL-SEMANTICS §11.4; design note `docs/echo-residue-integration.adoc`.
10+
- **Layer 10 phase 2 — the residue is a linear undo-capability.** The checker's residue stack now carries `Linear<Echo<T>>` (was a bare `Echo<T>`), making the consumed-once discipline explicit in the type. Discipline: *affine capability, linear consumption* — a given reversal log is consumed at most once (a double `reverse` of one residue is rejected) yet a `reversible` block need not be paired with a `reverse` (an un-reversed residue is **not** an error; reversibility is a capability, usually unused). Mechanised in `proofs/idris2/EchoResidueLinear.idr` (`reverseLinear` consumes `(1 e : Echo f y)`; `ResidueCell` Holding/Spent = the residue as agent state; `holdingReverses`/`spentDoesNotReverse`; `%default total`, zero `believe_me`, `idris2 --check` clean). 3 new typechecker tests (883 `oo7-core` lib tests passing). Remaining rung: cross-handler *named* residues (residue-binding grammar syntax, Harvard-guarded — kept separate). Spec/docs updated in lockstep.
1011
- **752 tests** (up from 207 at 0.5.0). CRG-C achieved 2026-04-04 for `oo7-core`.
1112
- **31 multi-language backends** via `mk2_bridge::create_backend_registry()`. Real dispatch to `elixir`, `idris2`, `gleam`, `rescript`, `nickel`, `guile`, `psql`, etc. Tier-5 coprocessor backends (CUDA/Vulkan/Metal/OpenCL/FPGA/TPU/DSP) do phase-1 device discovery with documented "kernel launch is future work" status.
1213
- **Metainterpreter** (`metainterpreter.rs`, ~3,075 LOC): reify/reflect/step/replay, four modes, 40 inline tests, 18 benches.

crates/oo7-core/src/typechecker.rs

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -501,12 +501,24 @@ pub struct TypeEnv {
501501
/// The witness is removed before the `else` branch (where `d` may be zero)
502502
/// and after the `if` statement.
503503
pub nonzero_witnesses: HashSet<String>,
504-
/// L10 reversibility: the stack of Echo residues currently in scope
505-
/// (echo-types residue tracking). A `reversible` block pushes a residue
506-
/// (its reversal log); a `reverse` block consumes one; an `irreversible`
507-
/// block pushes nothing (the residue is discarded). A `reverse` with an
508-
/// empty stack is a ReverseWithoutResidue error — this enforces spec
509-
/// §11.3 ("reverse of an irreversible block is a compile-time error").
504+
/// L10 reversibility: the stack of LINEAR Echo residues currently in
505+
/// scope (echo-types residue tracking). Each entry is a `Linear<Echo<T>>`
506+
/// — a consumed-exactly-once undo-capability. A `reversible` block pushes
507+
/// a residue (its reversal log); a `reverse` block consumes one (the
508+
/// single linear use); an `irreversible` block pushes nothing (the
509+
/// residue is discarded). A `reverse` with an empty stack is a
510+
/// ReverseWithoutResidue error — this enforces spec §11.3 ("reverse of an
511+
/// irreversible block is a compile-time error").
512+
///
513+
/// Discipline (phase 2, mechanised in proofs/idris2/EchoResidueLinear.idr):
514+
/// the residue is an *affine capability* consumed *linearly*. Replaying a
515+
/// given log twice is rejected (the stack has already popped it); retaining
516+
/// a log that is never replayed is NOT an error (a `reversible` block need
517+
/// not be paired with a `reverse` — reversibility is a capability, usually
518+
/// unused), so residues outstanding at a body boundary are dropped, not
519+
/// flagged. Cross-body (cross-handler) reversal — a residue bound by name
520+
/// and consumed by a later handler — is the next rung and needs residue
521+
/// binding syntax in the grammar (cf. `ResidueCell` in the Idris proof).
510522
echo_residues: Vec<Type>,
511523
}
512524

@@ -533,18 +545,28 @@ impl TypeEnv {
533545

534546
// -- Echo residue management (L10 reversibility, echo-types) --
535547

536-
/// A `reversible` block retains a reversal log — model it as pushing an
537-
/// `Echo<T>` residue onto the scope. `inner` is the type the block's
538-
/// reversal restores (Unit for now; the phased "linear undo-capability"
539-
/// rung refines this to a consumed-exactly-once residue value).
548+
/// A `reversible` block retains a reversal log — model it as pushing a
549+
/// LINEAR `Echo<T>` residue onto the scope, i.e. `Linear<Echo<T>>`: the
550+
/// consumed-exactly-once undo-capability that 007's planned
551+
/// `Linear<Echo<T>>` names directly. `inner` is the type the block's
552+
/// reversal restores (`Unit` until the AST carries a restored-value type).
553+
/// Wrapping in `Type::Linear` makes the residue's resource discipline
554+
/// explicit in the carried type; the single linear use is discharged by
555+
/// the matching `take_echo_residue` pop in a later `reverse`. This is the
556+
/// linear-undo rung mechanised in proofs/idris2/EchoResidueLinear.idr,
557+
/// where `reverseLinear` consumes `(1 e : Echo f y)`.
540558
fn push_echo_residue(&mut self, inner: Type) {
541-
self.echo_residues.push(Type::Echo(Box::new(inner)));
559+
self.echo_residues
560+
.push(Type::Linear(Box::new(Type::Echo(Box::new(inner)))));
542561
}
543562

544-
/// A `reverse` block consumes the most recent Echo residue, if any.
545-
/// Returns `None` when no residue is in scope — i.e. the `reverse`
546-
/// targets an `irreversible` block (or has no preceding `reversible`
547-
/// block), which is the ReverseWithoutResidue error.
563+
/// A `reverse` block consumes the most recent linear Echo residue, if any
564+
/// — the single linear use of that `Linear<Echo<T>>`. Returns `None` when
565+
/// no residue is in scope — i.e. the `reverse` targets an `irreversible`
566+
/// block (or has no preceding `reversible` block), which is the
567+
/// ReverseWithoutResidue error. Because the residue is popped, the same
568+
/// reversal log cannot be replayed twice (the linear "use at most once"
569+
/// is structural in the stack).
548570
fn take_echo_residue(&mut self) -> Option<Type> {
549571
self.echo_residues.pop()
550572
}
@@ -1794,8 +1816,11 @@ fn check_function_decl(fd: &FunctionDecl, env: &mut TypeEnv) {
17941816

17951817
// Echo residues (L10) are scoped to this function body: a `reversible`
17961818
// here must not leak its reversal log to a `reverse` in another
1797-
// function. Cross-body (e.g. cross-handler) reversal is agent state,
1798-
// handled by the forthcoming linear-residue rung.
1819+
// function. Each residue is a linear `Linear<Echo<T>>`; residues still
1820+
// outstanding at the body boundary are dropped (the undo capability was
1821+
// simply never exercised — not an error). Cross-body (e.g. cross-handler)
1822+
// reversal carries a residue by name through agent state; that named
1823+
// binding is the next rung (cf. `ResidueCell` in EchoResidueLinear.idr).
17991824
let saved_residues = std::mem::take(&mut env.echo_residues);
18001825
for stmt in &fd.body {
18011826
check_stmt(stmt, &ret_type, env);
@@ -1837,10 +1862,13 @@ fn check_agent_decl(ad: &AgentDecl, env: &mut TypeEnv) {
18371862
let ty = resolve_type_str(ty_str, env);
18381863
env.bind(name.clone(), ty, LinearStatus::Unrestricted);
18391864
}
1840-
// Echo residues (L10) are scoped per handler body. Cross-handler
1841-
// reversal (a `reverse` in one handler replaying a `reversible` in
1842-
// another) is agent state, not a statically-scoped block residue —
1843-
// deferred to the linear-residue rung.
1865+
// Echo residues (L10) are scoped per handler body; each is a linear
1866+
// `Linear<Echo<T>>`. Cross-handler reversal (a `reverse` in one
1867+
// handler replaying a `reversible` in another) carries the residue by
1868+
// name through agent state, not as a statically-scoped block residue —
1869+
// that named binding is the next rung (cf. `ResidueCell` in
1870+
// EchoResidueLinear.idr). Residues outstanding at the handler boundary
1871+
// are dropped: an unexercised undo capability is not an error.
18441872
let saved_residues = std::mem::take(&mut env.echo_residues);
18451873
for stmt in &handler.body {
18461874
check_stmt(stmt, &Type::Unit, env);
@@ -3394,8 +3422,9 @@ fn check_stmt(stmt: &ControlStmt, expected_ret: &Type, env: &mut TypeEnv) {
33943422
check_stmt(s, expected_ret, env);
33953423
}
33963424
env.pop_scope();
3397-
// The block's operations each have an inverse → an Echo residue
3398-
// becomes available to a later `reverse`.
3425+
// The block's operations each have an inverse → a linear Echo
3426+
// residue (`Linear<Echo<T>>`) becomes available to a later
3427+
// `reverse`, which consumes it exactly once.
33993428
env.push_echo_residue(Type::Unit);
34003429
}
34013430

0 commit comments

Comments
 (0)