Skip to content

Commit 88f4df9

Browse files
claudehyperpolymath
authored andcommitted
feat: scaffold Elixir project with core reversibility infrastructure
Phase 1 foundation - the Hypervisor pattern: Core modules: - Valence.Command - 4-callback behaviour for reversible operations - describe/1 → :safe | :warn | :danger - execute/2 → {:ok, result, compensation} - compensate/2 → undo the operation - verify/1 → detect drift (Two Generals) - Valence.History.Zipper - O(1) undo/redo without state copying - push/back/forward in constant time - Functional, immutable structure - Valence.Journal - Idempotency for crash recovery - Track pending/completed/compensated states - Same key = cached result (no re-execution) - Valence.Saga - Compensating transaction coordinator - Multi-step operations as atomic units - Automatic rollback on failure Sample commands: - Directory.Mkdir / Directory.Rmdir - FileOps.Touch / FileOps.Rm / FileOps.Write Proofs: - proofs/coq/rmr.v - Reversibility axiom F⁻¹(F(s)) = s Tests: - Zipper unit tests - Property-based tests with StreamData
1 parent 3d473c6 commit 88f4df9

File tree

15 files changed

+1336
-0
lines changed

15 files changed

+1336
-0
lines changed

.formatter.exs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[
2+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
3+
]

lib/valence.ex

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
defmodule Valence do
2+
@moduledoc """
3+
Valence Shell - The Thermodynamic Shell
4+
5+
Every command is a reversible transaction. Every mutation is accountable.
6+
7+
## Core Principle
8+
9+
F⁻¹(F(s)) = s
10+
11+
Full reversibility without sacrificing POSIX compliance.
12+
13+
## Architecture
14+
15+
- `Valence.Command` - Behaviour for reversible operations
16+
- `Valence.History.Zipper` - O(1) undo/redo structure
17+
- `Valence.Saga` - Compensating transaction orchestration
18+
- `Valence.Journal` - Idempotency and crash recovery
19+
"""
20+
21+
@doc """
22+
Execute a command with full transaction semantics.
23+
24+
Returns `{:ok, result}` on success, `{:error, reason}` on failure.
25+
On failure, any partial mutations are automatically compensated.
26+
"""
27+
def execute(command, args) do
28+
Valence.Saga.execute(command, args)
29+
end
30+
31+
@doc """
32+
Undo the last operation.
33+
34+
Returns `{:ok, previous_state}` or `{:error, :nothing_to_undo}`.
35+
"""
36+
def undo do
37+
Valence.History.undo()
38+
end
39+
40+
@doc """
41+
Redo the last undone operation.
42+
43+
Returns `{:ok, restored_state}` or `{:error, :nothing_to_redo}`.
44+
"""
45+
def redo do
46+
Valence.History.redo()
47+
end
48+
end

lib/valence/application.ex

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
defmodule Valence.Application do
2+
@moduledoc """
3+
OTP Application for Valence Shell.
4+
5+
Supervises:
6+
- History (Zipper state for undo/redo)
7+
- Journal (Idempotency log for crash recovery)
8+
- Saga (Compensating transaction coordinator)
9+
"""
10+
use Application
11+
12+
@impl true
13+
def start(_type, _args) do
14+
children = [
15+
# In-memory undo/redo history
16+
Valence.History,
17+
18+
# Idempotency journal for crash recovery
19+
Valence.Journal,
20+
21+
# Saga coordinator for multi-step transactions
22+
Valence.Saga
23+
]
24+
25+
opts = [strategy: :one_for_one, name: Valence.Supervisor]
26+
Supervisor.start_link(children, opts)
27+
end
28+
end

lib/valence/command.ex

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
defmodule Valence.Command do
2+
@moduledoc """
3+
Behaviour for reversible shell commands.
4+
5+
Every command in Valence Shell must implement these four callbacks,
6+
ensuring full reversibility and accountability.
7+
8+
## The Contract
9+
10+
```elixir
11+
@callback describe(args) :: :safe | :warn | :danger
12+
@callback execute(args, idempotency_key) :: {:ok, result, compensation} | {:error, reason}
13+
@callback compensate(args, idempotency_key) :: :ok | {:error, reason}
14+
@callback verify(args) :: :ok | {:drift, expected, actual}
15+
```
16+
17+
## Example Implementation
18+
19+
```elixir
20+
defmodule Valence.Commands.Mkdir do
21+
@behaviour Valence.Command
22+
23+
@impl true
24+
def describe(%{path: _path}), do: :safe
25+
26+
@impl true
27+
def execute(%{path: path}, idempotency_key) do
28+
case File.mkdir(path) do
29+
:ok ->
30+
compensation = %{module: __MODULE__, action: :rmdir, args: %{path: path}}
31+
{:ok, :created, compensation}
32+
{:error, reason} ->
33+
{:error, reason}
34+
end
35+
end
36+
37+
@impl true
38+
def compensate(%{path: path}, _idempotency_key) do
39+
File.rmdir(path)
40+
end
41+
42+
@impl true
43+
def verify(%{path: path}) do
44+
if File.dir?(path), do: :ok, else: {:drift, :exists, :missing}
45+
end
46+
end
47+
```
48+
"""
49+
50+
@type args :: map()
51+
@type idempotency_key :: binary()
52+
@type compensation :: %{module: module(), action: atom(), args: map()}
53+
@type risk_level :: :safe | :warn | :danger
54+
55+
@doc """
56+
Assess the thermodynamic cost of this operation.
57+
58+
- `:safe` - Reversible with no external effects
59+
- `:warn` - Reversible but may have observable side effects
60+
- `:danger` - May be irreversible or affect external systems
61+
"""
62+
@callback describe(args()) :: risk_level()
63+
64+
@doc """
65+
Execute the command and return its compensation.
66+
67+
The idempotency key ensures crash recovery - if this key has already
68+
been executed, return the cached result instead of re-executing.
69+
70+
Returns:
71+
- `{:ok, result, compensation}` - Success with undo information
72+
- `{:error, reason}` - Failure (no compensation needed)
73+
"""
74+
@callback execute(args(), idempotency_key()) ::
75+
{:ok, term(), compensation()} | {:error, term()}
76+
77+
@doc """
78+
Undo the effects of a previous execution.
79+
80+
This is the inverse function: if `execute` did F(s), then
81+
`compensate` does F⁻¹(F(s)) = s.
82+
"""
83+
@callback compensate(args(), idempotency_key()) :: :ok | {:error, term()}
84+
85+
@doc """
86+
Verify the expected state matches reality.
87+
88+
Used for Two Generals problem detection - did the operation actually
89+
complete, or is there drift between expected and actual state?
90+
"""
91+
@callback verify(args()) :: :ok | {:drift, expected :: term(), actual :: term()}
92+
93+
@doc """
94+
Check if a module implements the Command behaviour.
95+
"""
96+
def command?(module) when is_atom(module) do
97+
behaviours = module.module_info(:attributes)[:behaviour] || []
98+
__MODULE__ in behaviours
99+
end
100+
end

lib/valence/commands/directory.ex

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
defmodule Valence.Commands.Directory do
2+
@moduledoc """
3+
Reversible directory operations.
4+
5+
Implements:
6+
- `mkdir` / `rmdir` (proven reversible: rmdir(mkdir(p)) = identity)
7+
"""
8+
9+
defmodule Mkdir do
10+
@moduledoc "Create a directory. Compensation: remove it."
11+
@behaviour Valence.Command
12+
13+
@impl true
14+
def describe(%{path: _path}), do: :safe
15+
16+
@impl true
17+
def execute(%{path: path}, _idempotency_key) do
18+
case File.mkdir_p(path) do
19+
:ok ->
20+
compensation = %{
21+
module: Rmdir,
22+
action: :compensate,
23+
args: %{path: path}
24+
}
25+
{:ok, :created, compensation}
26+
27+
{:error, reason} ->
28+
{:error, reason}
29+
end
30+
end
31+
32+
@impl true
33+
def compensate(%{path: path}, _idempotency_key) do
34+
case File.rmdir(path) do
35+
:ok -> :ok
36+
{:error, reason} -> {:error, reason}
37+
end
38+
end
39+
40+
@impl true
41+
def verify(%{path: path}) do
42+
if File.dir?(path) do
43+
:ok
44+
else
45+
{:drift, :directory_exists, :missing}
46+
end
47+
end
48+
end
49+
50+
defmodule Rmdir do
51+
@moduledoc "Remove an empty directory. Compensation: recreate it."
52+
@behaviour Valence.Command
53+
54+
@impl true
55+
def describe(%{path: _path}), do: :warn # Could fail if not empty
56+
57+
@impl true
58+
def execute(%{path: path}, _idempotency_key) do
59+
# Capture state for compensation
60+
case File.rmdir(path) do
61+
:ok ->
62+
compensation = %{
63+
module: Mkdir,
64+
action: :compensate,
65+
args: %{path: path}
66+
}
67+
{:ok, :removed, compensation}
68+
69+
{:error, reason} ->
70+
{:error, reason}
71+
end
72+
end
73+
74+
@impl true
75+
def compensate(%{path: path}, _idempotency_key) do
76+
File.mkdir_p(path)
77+
end
78+
79+
@impl true
80+
def verify(%{path: path}) do
81+
if File.dir?(path) do
82+
{:drift, :removed, :still_exists}
83+
else
84+
:ok
85+
end
86+
end
87+
end
88+
end

0 commit comments

Comments
 (0)