MannaLedger: real anti-hoarding enforcement — d'var yom b'yomo (Step 26)

Shemot 16:4 — d'var yom b'yomo — the day's matter in its day.
Shemot 16:19 — al yoter mimenu ad boker — do not leave surplus.
Shemot 16:20 — vayotiru... vayarum tola'im vayiv'ash — hoarded manna rots.

- MannaLedger GenServer tracks consumption against daily provision
- load_provision/0 reads manna file, resets consumption to zero
- consume/2 deducts from allocated resource, rejects when exhausted
- remaining/1 and available?/2 for balance queries
- enforce_cycle_boundary/0 zeroes out all allocation, returns released amounts
- Chatzotzrot assembly_leaders signal when provision runs low (< 10%)
- Manna.enforce_anti_hoarding/0 now delegates to MannaLedger
- Added to supervision tree in application.ex
- 17 new tests with proper isolation (reset between tests)
- All 177 Elixir tests pass, 0 failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott KarpandClaude Opus 4.6 committed 2026-03-18 09:27:09 -04:00
1 parent 671d45625c
commit 6a879fcf8b
4 files changed
+566 -4

No files matched your search

@@ -8,6 +8,7 @@ defmodule VavPipeline.Application do
- AreiMiklat (cities of refuge — protection protocol)
- EglahArufah (accountability for unknown harm)
- Bikurim (first fruits — release protocol)
- MannaLedger (consumption tracking and anti-hoarding enforcement)
- ShabbatTimer (mandatory cessation monitor)
- ShoreshEnginePort (Erlang Port to Haskell engine, if binary found)
- TalmudicEngineSocket (Unix socket to Prolog engine)
@@ -33,6 +34,7 @@ defmodule VavPipeline.Application do
{VavPipeline.AreiMiklat, []},
{VavPipeline.EglahArufah, []},
{VavPipeline.Bikurim, []},
{VavPipeline.MannaLedger, []},
{VavPipeline.ShabbatTimer, []}
]
|> maybe_add_repo()
+15 -4
View File
@@ -98,14 +98,25 @@ defmodule VavPipeline.Manna do
Enforce anti-hoarding — manna from today cannot be used tomorrow.
Called at erev_voker (cycle boundary).
In production this would enforce resource deallocation.
In Phase 2, it logs the boundary.
Shemot 16:19 — al yoter mimenu ad boker.
Delegates to MannaLedger.enforce_cycle_boundary/0 for real enforcement.
"""
@spec enforce_anti_hoarding() :: :ok
def enforce_anti_hoarding do
require Logger
Logger.info("Manna anti-hoarding enforced — surplus released at cycle boundary")
:ok
case VavPipeline.MannaLedger.enforce_cycle_boundary() do
{:ok, %{released: released}} ->
total = released |> Map.values() |> Enum.sum()
if total > 0 do
Logger.info("Manna anti-hoarding enforced — surplus released: #{inspect(released)}")
else
Logger.info("Manna anti-hoarding enforced — no surplus to release")
end
:ok
end
end
@doc """
@@ -0,0 +1,308 @@
defmodule VavPipeline.MannaLedger do
@moduledoc """
Manna consumption ledger — d'var yom b'yomo.
Shemot 16:4 — "The people shall go out and gather the matter
of a day in its day." Each cycle receives exactly what it needs.
Shemot 16:19 — "Let no one leave any of it until morning."
Root ותר (yoter) — do not hoard. At cycle boundary, surplus
is released. Not rolled over. Not saved.
Shemot 16:20 — "Some left over... and it bred worms and stank."
Root באש (ba'ash) — hoarded resources breed corruption.
This GenServer tracks:
1. Today's allocated provision (from the manna file)
2. Consumption against each resource category
3. Remaining balance
4. Cycle boundary enforcement (zeroing out surplus)
Architecture §4, Step 26: Manna anti-hoarding enforcement
and cycle-boundary surplus release.
"""
use GenServer
require Logger
alias VavPipeline.{Manna, Chatzotzrot}
@type resource_key :: :compute_units | :memory_mb | :api_calls_external |
:storage_delta_mb | :sanhedrin_panels_max
@type ledger_state :: %{
cycle_date: String.t() | nil,
allocated: map(),
consumed: map(),
cycle_count: non_neg_integer(),
last_enforcement: DateTime.t() | nil,
hoarding_violations: non_neg_integer()
}
@zero_provision %{
compute_units: 0,
memory_mb: 0,
api_calls_external: 0,
storage_delta_mb: 0,
sanhedrin_panels_max: 0
}
# ============================================================
# Client API
# ============================================================
def start_link(opts \\ []) do
name = Keyword.get(opts, :name, __MODULE__)
GenServer.start_link(__MODULE__, opts, name: name)
end
@doc """
Load today's provision from the manna file into the ledger.
Called at the start of each cycle. Resets consumption to zero.
If provision was already loaded for today, returns {:ok, :already_loaded}.
"""
@spec load_provision() :: {:ok, :loaded | :already_loaded | :no_provision} | {:error, String.t()}
def load_provision do
GenServer.call(__MODULE__, :load_provision)
end
@doc """
Consume resources from today's manna provision.
D'var yom b'yomo — take only what the day requires.
Parameters:
- resource: which resource to consume (:compute_units, :api_calls_external, etc.)
- amount: how much to consume
Returns:
- {:ok, remaining} — consumption recorded, remaining balance returned
- {:error, :provision_exhausted} — no more of this resource available
- {:error, :no_provision} — no provision loaded for today
"""
@spec consume(resource_key(), pos_integer()) ::
{:ok, non_neg_integer()} | {:error, atom()}
def consume(resource, amount) when is_atom(resource) and is_integer(amount) and amount > 0 do
GenServer.call(__MODULE__, {:consume, resource, amount})
end
@doc """
Check remaining balance for a resource.
"""
@spec remaining(resource_key()) :: non_neg_integer()
def remaining(resource) when is_atom(resource) do
GenServer.call(__MODULE__, {:remaining, resource})
end
@doc """
Check if a resource has sufficient provision for the requested amount.
"""
@spec available?(resource_key(), pos_integer()) :: boolean()
def available?(resource, amount) when is_atom(resource) and is_integer(amount) do
GenServer.call(__MODULE__, {:available?, resource, amount})
end
@doc """
Get the full ledger state — allocated, consumed, remaining.
"""
@spec ledger() :: ledger_state()
def ledger do
GenServer.call(__MODULE__, :ledger)
end
@doc """
Enforce anti-hoarding — release all surplus at cycle boundary.
Shemot 16:19 — al yoter mimenu ad boker.
"Let no one leave any of it until morning."
This zeroes out remaining allocation and records the enforcement.
Called by BereishitCycle.erev_voker/2 at cycle boundary.
Returns {:ok, %{released: map()}} with the amounts released (what was left over).
"""
@spec enforce_cycle_boundary() :: {:ok, %{released: map()}}
def enforce_cycle_boundary do
GenServer.call(__MODULE__, :enforce_cycle_boundary)
end
@doc """
Reset the ledger to initial state. For testing only.
"""
@spec reset() :: :ok
def reset do
GenServer.call(__MODULE__, :reset)
end
# ============================================================
# GenServer callbacks
# ============================================================
@impl true
def init(_opts) do
state = %{
cycle_date: nil,
allocated: @zero_provision,
consumed: @zero_provision,
cycle_count: 0,
last_enforcement: nil,
hoarding_violations: 0
}
Logger.info("MannaLedger: ledger ready — d'var yom b'yomo")
{:ok, state}
end
@impl true
def handle_call(:reset, _from, _state) do
{:reply, :ok,
%{
cycle_date: nil,
allocated: @zero_provision,
consumed: @zero_provision,
cycle_count: 0,
last_enforcement: nil,
hoarding_violations: 0
}}
end
@impl true
def handle_call(:load_provision, _from, state) do
today = Date.utc_today() |> Date.to_iso8601()
if state.cycle_date == today do
{:reply, {:ok, :already_loaded}, state}
else
case Manna.check() do
{:ok, :no_provision} ->
new_state = %{
state
| cycle_date: today,
allocated: @zero_provision,
consumed: @zero_provision,
cycle_count: state.cycle_count + 1
}
Logger.warning("MannaLedger: no provision for #{today} — system will wait")
{:reply, {:ok, :no_provision}, new_state}
{:ok, manna} ->
new_state = %{
state
| cycle_date: today,
allocated: manna.provision,
consumed: @zero_provision,
cycle_count: state.cycle_count + 1
}
Logger.info(
"MannaLedger: provision loaded for #{today} (#{manna.type}) — " <>
"compute: #{manna.provision.compute_units}, " <>
"api_calls: #{manna.provision.api_calls_external}, " <>
"panels: #{manna.provision.sanhedrin_panels_max}"
)
{:reply, {:ok, :loaded}, new_state}
{:error, reason} ->
{:reply, {:error, reason}, state}
end
end
end
@impl true
def handle_call({:consume, resource, amount}, _from, state) do
allocated = Map.get(state.allocated, resource, 0)
consumed = Map.get(state.consumed, resource, 0)
remaining = allocated - consumed
cond do
state.cycle_date == nil ->
{:reply, {:error, :no_provision}, state}
remaining <= 0 ->
Logger.warning(
"MannaLedger: #{resource} exhausted — " <>
"allocated #{allocated}, consumed #{consumed}"
)
{:reply, {:error, :provision_exhausted}, state}
amount > remaining ->
# Partial consumption not allowed — don't take more than what remains
Logger.warning(
"MannaLedger: #{resource} insufficient — " <>
"requested #{amount}, remaining #{remaining}"
)
{:reply, {:error, :provision_exhausted}, state}
true ->
new_consumed = Map.put(state.consumed, resource, consumed + amount)
new_remaining = remaining - amount
new_state = %{state | consumed: new_consumed}
# Signal when provision runs low (< 10% remaining)
if new_remaining < allocated * 0.1 and new_remaining > 0 do
Chatzotzrot.signal(:assembly_leaders,
reason: "Manna low: #{resource} at #{new_remaining}/#{allocated}"
)
end
{:reply, {:ok, new_remaining}, new_state}
end
end
@impl true
def handle_call({:remaining, resource}, _from, state) do
allocated = Map.get(state.allocated, resource, 0)
consumed = Map.get(state.consumed, resource, 0)
{:reply, max(allocated - consumed, 0), state}
end
@impl true
def handle_call({:available?, resource, amount}, _from, state) do
allocated = Map.get(state.allocated, resource, 0)
consumed = Map.get(state.consumed, resource, 0)
{:reply, (allocated - consumed) >= amount, state}
end
@impl true
def handle_call(:ledger, _from, state) do
{:reply, state, state}
end
@impl true
def handle_call(:enforce_cycle_boundary, _from, state) do
now = DateTime.utc_now()
# Calculate what was left over — the surplus that would breed worms
released =
Map.new(@zero_provision, fn {key, _} ->
allocated = Map.get(state.allocated, key, 0)
consumed = Map.get(state.consumed, key, 0)
{key, max(allocated - consumed, 0)}
end)
total_released = released |> Map.values() |> Enum.sum()
if total_released > 0 do
Logger.info(
"MannaLedger: cycle boundary — releasing surplus: #{inspect(released)}"
)
end
new_state = %{
state
| allocated: @zero_provision,
consumed: @zero_provision,
cycle_date: nil,
last_enforcement: now,
hoarding_violations: state.hoarding_violations
}
{:reply, {:ok, %{released: released}}, new_state}
end
end