etr(R): re-port driver_etr.R to the five-zone torus (kill disowned port)

Replaces the Euclidean-magnitude R port (DREAD/BLUR/INCOHERENT, inverted
Z-path) with a faithful native port of etr.m: three independent toroidal
axes, five zones, snap-across flips, basin spring toward centre 26
(SOFT 0.25 / INCOH 0.5), L6 Z-path corrected (z<0 alimentation/lattice,
z>=0 transmutation/evolution). Drive-Box now renders per-axis ETR status.
R suite green (etr 31, drive_box 34) and Octave 26/0/1; both pinned to
etr_invariants.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hmgREHNsxYCuim33yUF2c
This commit is contained in:
Claude 2026-06-18 07:14:51 +00:00
parent e9088c98c9
commit 7117d61386
No known key found for this signature in database
5 changed files with 187 additions and 126 deletions

View File

@ -27,7 +27,9 @@ implemented and tested, incl. L7 snap-flips. Full law + status in
## 3. Language & location
GNU Octave · `src/endocrine/etr/` (`etr.m`, `test_etr.m`, `run_etr_tests.sh`, `etr_invariants.md`).
R wrapper `src/endocrine/driver_etr.R` bridges into the Drive-Box.
R port `src/endocrine/driver_etr.R` (+ `test_etr.R`) feeds the Drive-Box — a **faithful native port**
of `etr.m` (five-zone torus, per-axis), pinned to the same invariants doc. The disowned Euclidean-
magnitude port is gone. A single-source R↔Octave IPC bridge is still optional/future (transport C1).
## 4. Does / does-not
- **Does:** hold the point; wrap each axis at ±50; apply per-axis restoring toward [17,35]; take
@ -54,8 +56,11 @@ L4 AI-drift C4 · L5 cross-axis coupling via stress C1 · L6 Z-path C3 · L7 sna
## 8. Build steps (remaining)
1. ~~Fit the restoring force~~ **DONE** — five-zone law (SOFT/INCOH gains + snap landings) green; L7 + L8 realised.
2. Define **L5** coupling (the stress→cross-axis mapping; G1) → replace the identity stub (the lone PEND).
3. Wire R↔Octave (driver_etr.R ↔ etr.m) for the Drive-Box (A1) — and the larger medium (D2).
2. ~~Reconcile the R port~~ **DONE**`driver_etr.R` is now a faithful native port of `etr.m`
(five-zone, per-axis, L6 Z-path corrected); R suite + drive-box green.
3. Define **L5** coupling (the stress→cross-axis mapping; G1) → replace the identity stub (the lone PEND).
4. *(optional/future)* collapse the dual R+Octave implementations via an R↔Octave IPC bridge once the
transport (C1, shared with A1/C3/D2) is decided.
## 9. Tests
`bash src/endocrine/etr/run_etr_tests.sh` (now **26/0/1** — the lone PEND is L5 active coupling).

View File

@ -39,7 +39,7 @@ drive_snapshot <- function(state) {
alive = is_alive(state$energy),
existential_load = reality$existential_load,
arguments = reality$arguments,
etr_status = evaluate_status(state$etr),
etr_status = paste(etr_status(state$etr), collapse = "/"),
update_path = determine_system_update_path(state$etr)
)
}
@ -89,7 +89,7 @@ drive_box_evaluate <- function(state, action_tags = character(0),
true_cost = true_cost,
existential_load = ps_load,
eth_penalty = eth_penalty,
etr_status = evaluate_status(state$etr),
etr_status = paste(etr_status(state$etr), collapse = "/"),
update_path = determine_system_update_path(state$etr),
arguments = reality$arguments
)

View File

@ -1,65 +1,118 @@
# --- High-Fidelity Implementation for Driver 4: Existential Temporality Relief (ETR) ---
# --- Driver 4: Existential Temporality Relief (ETR) — R port of the torus ---
#
# ETR situates the Drive-Box within a 3-axis temporality space. The agent's
# position is a coordinate (X, Y, Z) whose distance from the origin (the
# magnitude) maps onto an existential status band, while the Z-axis selects the
# system's generative update path. Movement through the space wraps toroidally
# so the agent can never leave the bounded temporal manifold.
# ETR is a SINGLE POINT on three INDEPENDENT toroidal axes (X, Y, Z). This R
# module is a faithful port of the Octave source of truth
# (src/endocrine/etr/etr.m) and its laws (src/endocrine/etr/etr_invariants.md);
# both implementations are pinned to that invariants doc. The earlier Euclidean-
# magnitude port (DREAD/BLUR/INCOHERENT, inverted Z-path) is DISOWNED.
#
# State is a plain list with a single field:
# coordinate - length-3 numeric vector: X, Y, Z
# Each axis is a BISTABLE torus with five zones per pass and unstable watersheds
# at |v| = 7 (inner) and |v| = 45 (outer):
#
# State-mutating helpers (shift_coordinate) return a new (modified copy of the)
# list rather than mutating in place, matching the reference semantics of the
# other Drive-Box drivers.
# |v| < 7 SNAP_IN : snaps ACROSS 0 to the opposite pole
# 7 .. 17 SOFT : weak restoring pull up into the band
# 17 .. 35 IN_BAND : stable (slack)
# 35 .. 45 INCOH : firmer restoring pull down into the band
# |v| > 45 SNAP_OUT : snaps ACROSS the ±50 wrap to the opposite pole
#
# A snap flips the pole and lands JUST PAST the opposite watershed (inner ->
# opposite SOFT; outer -> opposite INCOH), then that zone recovers it.
#
# State is a plain list with one field: coordinate - length-3 numeric (X,Y,Z).
# Constructor helper so tests and other modules can build a clean state.
init_etr_state <- function(coordinate = c(0, 0, 0)) {
# ---- law constants (NOT fitted) ----
ETR_BAND_LO <- 17
ETR_BAND_HI <- 35
ETR_WRAP <- 50
ETR_SNAP_INNER <- 7 # inner watershed (Anja)
ETR_SNAP_OUTER <- 45 # outer watershed (Anja)
# ---- fitted constants (NOT law) ----
ETR_GAIN_SOFT <- 0.25 # weak (SOFT, 7..17)
ETR_GAIN_INCOH <- 0.5 # firmer (INCOH, 35..45)
ETR_SNAP_MARGIN <- 2 # how far past the opposite watershed a snap lands
# Constructor: default to a valid in-band point (mirrors etr_init's [25 25 25]).
init_etr_state <- function(coordinate = c(25, 25, 25)) {
list(coordinate = coordinate)
}
get_magnitude <- function(etr_state) {
# Euclidean distance from origin: sqrt(x^2 + y^2 + z^2)
coords <- etr_state$coordinate
return(sqrt(sum(coords^2)))
# ---- L1: per-axis toroidal wrap onto [-50, 50) ----
etr_axis_wrap <- function(v) {
P <- 2 * ETR_WRAP
((v + ETR_WRAP) %% P) - ETR_WRAP
}
evaluate_status <- function(etr_state) {
magnitude <- get_magnitude(etr_state)
if (magnitude < 5.0) {
return("DISASTROUS")
} else if (magnitude >= 5.0 && magnitude < 15.0) {
return("DREAD")
} else if (magnitude >= 15.0 && magnitude < 35.0) {
return("FUNCTIONAL")
} else if (magnitude >= 35.0 && magnitude < 50.0) {
return("BLUR")
# ---- five-zone classification ----
etr_axis_zone <- function(v) {
a <- abs(v)
if (a < ETR_SNAP_INNER) "SNAP_IN"
else if (a < ETR_BAND_LO) "SOFT"
else if (a <= ETR_BAND_HI) "IN_BAND"
else if (a <= ETR_SNAP_OUTER) "INCOH"
else "SNAP_OUT"
}
# ---- L3: per-axis restoring force (spring toward band centre 26) ----
# Weak in SOFT, firmer in INCOH; zero in band (slack) and in snap zones (those
# flip, they do not restore).
etr_axis_restoring <- function(v) {
a <- abs(v)
centre <- (ETR_BAND_LO + ETR_BAND_HI) / 2 # 26
if (a < ETR_SNAP_INNER || a > ETR_SNAP_OUTER) {
0
} else if (a >= ETR_BAND_LO && a <= ETR_BAND_HI) {
0
} else if (a < ETR_BAND_LO) {
ETR_GAIN_SOFT * (centre * sign(v) - v) # SOFT — weak
} else {
return("INCOHERENT")
ETR_GAIN_INCOH * (centre * sign(v) - v) # INCOH — firmer
}
}
determine_system_update_path <- function(etr_state) {
# Axis 2 (Z-axis, index 3) determines the generative path.
z_coord <- etr_state$coordinate[3]
if (z_coord >= 0) {
return("LATTICE_REINFORCEMENT")
# ---- L7: snap resolution — a flip ACROSS to the opposite pole ----
# Precondition: |v| < 7 or |v| > 45. Lands just past the opposite watershed,
# sign flipped: inner -> opposite SOFT (±9); outer -> opposite INCOH (±43).
etr_axis_snap <- function(v) {
s <- sign(v); if (s == 0) s <- 1 # 0 has no pole; pick one
if (abs(v) < ETR_SNAP_INNER) {
-s * (ETR_SNAP_INNER + ETR_SNAP_MARGIN) # inner -> opposite SOFT
} else {
return("EXPERIMENTAL_EVOLUTION")
-s * (ETR_SNAP_OUTER - ETR_SNAP_MARGIN) # outer -> opposite INCOH
}
}
shift_coordinate <- function(etr_state, delta_vector, bound = 50.0) {
# Toroidal wrap-around: keep each axis within [-bound, bound].
new_coords <- etr_state$coordinate + delta_vector
# ---- one ETR step: AI drift -> (snap | restoring) -> wrap ----
# drift is AI-originated (L4); ETR never invents motion. stress is the L5
# coupling mediator (open seam -> identity for now).
etr_step <- function(etr_state, drift, stress = 0) {
if (missing(drift)) {
stop("etr_step: AI-originated drift must be supplied (L4 — ETR does not invent motion)")
}
coord <- etr_state$coordinate # coupling identity (L5 open)
for (i in 1:3) {
while (new_coords[i] > bound) {
new_coords[i] <- new_coords[i] - (2 * bound)
}
while (new_coords[i] < -bound) {
new_coords[i] <- new_coords[i] + (2 * bound)
v <- etr_axis_wrap(coord[i] + drift[i])
a <- abs(v)
if (a < ETR_SNAP_INNER || a > ETR_SNAP_OUTER) {
v <- etr_axis_snap(v)
} else {
v <- etr_axis_wrap(v + etr_axis_restoring(v))
}
coord[i] <- v
}
etr_state$coordinate <- new_coords
return(etr_state)
etr_state$coordinate <- coord
etr_state
}
# ---- per-axis zone status (length-3 character vector) ----
etr_status <- function(etr_state) {
vapply(etr_state$coordinate, etr_axis_zone, character(1))
}
# ---- L6 Z-path: the generative update path selected by the Z-axis sign ----
# z < 0 -> alimentation (maintain self) -> LATTICE_REINFORCEMENT
# z >= 0 -> transmutation (evolve self) -> EXPERIMENTAL_EVOLUTION
determine_system_update_path <- function(etr_state) {
z <- etr_state$coordinate[3]
if (z < 0) "LATTICE_REINFORCEMENT" else "EXPERIMENTAL_EVOLUTION"
}

View File

@ -29,8 +29,8 @@ test_case("drive_snapshot reports a coherent read-only aggregate", function() {
expect_false(snap$tool_locked, "not tool-locked at full energy")
expect_true(snap$existential_load > 0, "primed body has positive load")
expect_true(length(snap$arguments) > 0, "arguments emitted")
expect_equal(snap$etr_status, "DISASTROUS", label = "origin coordinate -> DISASTROUS")
expect_equal(snap$update_path, "LATTICE_REINFORCEMENT", label = "z=0 -> lattice")
expect_equal(snap$etr_status, "IN_BAND/IN_BAND/IN_BAND", label = "default coord -> all axes in band")
expect_equal(snap$update_path, "EXPERIMENTAL_EVOLUTION", label = "z=25 (>=0) -> transmutation/evolution")
})
test_case("aligned action is far cheaper than its antithetical mirror", function() {
@ -110,7 +110,7 @@ test_case("input slot: empty body still emits driver + soul signals", function()
res <- drive_box_input_slot(db, input_text = "", tarot_spread = character(0))
expect_true(grepl("[SOUL: SOUL.md]", res$slot, fixed = TRUE), "soul present")
expect_true(grepl("[E ratio=1.00", res$slot, fixed = TRUE), "energy present at full")
expect_true(grepl("[ETR status=DISASTROUS", res$slot, fixed = TRUE), "etr present")
expect_true(grepl("[ETR status=IN_BAND", res$slot, fixed = TRUE), "etr present")
expect_equal(res$input, res$slot, label = "no input_text -> input == slot")
})

View File

@ -1,95 +1,98 @@
# --- Tests for Driver 4: Existential Temporality Relief (ETR) ---
# Run from repo root: Rscript src/endocrine/test_etr.R
# Exit 0 => all pass.
# test_etr.R — invariants tests for the R port of the ETR five-zone torus.
# Mirrors src/endocrine/etr/test_etr.m (same laws, same source of truth). Run
# from the repo root via run_tests.sh.
source("src/endocrine/test_framework.R")
source("src/endocrine/driver_etr.R")
# --- init_etr_state constructor ---
test_case("init_etr_state builds state with default origin coordinate", function() {
s <- init_etr_state()
expect_equal(s$coordinate, c(0, 0, 0))
# --- L1 wrap ----------------------------------------------------------
test_case("etr_axis_wrap: +50 wraps to -50", function() {
expect_equal(etr_axis_wrap(50), -50, tol = 1e-9)
})
test_case("etr_axis_wrap: 60 -> -40, -60 -> 40", function() {
expect_equal(etr_axis_wrap(60), -40, tol = 1e-9)
expect_equal(etr_axis_wrap(-60), 40, tol = 1e-9)
})
test_case("etr_axis_wrap: in-range unchanged; edge continuity", function() {
expect_equal(etr_axis_wrap(25), 25, tol = 1e-9)
expect_equal(etr_axis_wrap(49.9), etr_axis_wrap(-50.1), tol = 1e-9)
})
test_case("init_etr_state honors a custom coordinate", function() {
s <- init_etr_state(c(1, 2, 3))
expect_equal(s$coordinate, c(1, 2, 3))
# --- zones ------------------------------------------------------------
test_case("etr_axis_zone: five zones at 5/10/25/40/47", function() {
expect_equal(etr_axis_zone(5), "SNAP_IN")
expect_equal(etr_axis_zone(10), "SOFT")
expect_equal(etr_axis_zone(25), "IN_BAND")
expect_equal(etr_axis_zone(40), "INCOH")
expect_equal(etr_axis_zone(47), "SNAP_OUT")
})
# --- get_magnitude ---
test_case("get_magnitude of c(3,4,0) is 5.0", function() {
expect_equal(get_magnitude(init_etr_state(c(3, 4, 0))), 5.0, tol = 1e-9)
# --- L3 restoring -----------------------------------------------------
test_case("etr_axis_restoring: SOFT pulls up, INCOH pulls down, band slack", function() {
expect_true(etr_axis_restoring(10) > 0, "SOFT (+v) pulls toward band")
expect_true(etr_axis_restoring(-10) < 0, "SOFT (-v) pulls toward band")
expect_true(etr_axis_restoring(40) < 0, "INCOH (+v) pulls toward band")
expect_true(etr_axis_restoring(-40) > 0, "INCOH (-v) pulls toward band")
expect_equal(etr_axis_restoring(25), 0)
})
test_case("etr_axis_restoring: zero in snap zones", function() {
expect_equal(etr_axis_restoring(5), 0)
expect_equal(etr_axis_restoring(47), 0)
})
test_case("etr_axis_restoring: soft-pull weaker than incoherency", function() {
expect_true(abs(etr_axis_restoring(8)) < abs(etr_axis_restoring(44)),
"weak soft-pull")
})
test_case("get_magnitude of origin is 0.0", function() {
expect_equal(get_magnitude(init_etr_state(c(0, 0, 0))), 0.0, tol = 1e-9)
# --- L2 convergence (same pole) ---------------------------------------
test_case("L2: SOFT start settles into band without flipping sign", function() {
s <- init_etr_state(c(10, 10, 10))
for (k in 1:200) s <- etr_step(s, c(0, 0, 0), 0)
a <- abs(s$coordinate)
expect_true(all(a >= ETR_BAND_LO & a <= ETR_BAND_HI), "in band")
expect_true(all(s$coordinate > 0), "no flip")
})
# --- evaluate_status: exact boundary bands ---
test_case("evaluate_status magnitude 0 -> DISASTROUS", function() {
expect_equal(evaluate_status(init_etr_state(c(0, 0, 0))), "DISASTROUS")
# --- L7 snap-across flips ---------------------------------------------
test_case("L7: inner snap lands in opposite SOFT, settles in opposite band", function() {
s <- init_etr_state(c(5, 5, 5))
s1 <- etr_step(s, c(0, 0, 0), 0)
a1 <- abs(s1$coordinate)
expect_true(all(s1$coordinate < 0), "flipped sign")
expect_true(all(a1 > ETR_SNAP_INNER & a1 < ETR_BAND_LO), "lands in SOFT")
for (k in 1:200) s <- etr_step(s, c(0, 0, 0), 0)
a <- abs(s$coordinate)
expect_true(all(s$coordinate < 0) && all(a >= ETR_BAND_LO & a <= ETR_BAND_HI),
"settles in opposite band")
})
test_case("L7: outer snap lands in opposite INCOH, settles in opposite band", function() {
s <- init_etr_state(c(47, 47, 47))
s1 <- etr_step(s, c(0, 0, 0), 0)
a1 <- abs(s1$coordinate)
expect_true(all(s1$coordinate < 0), "flipped sign")
expect_true(all(a1 > ETR_BAND_HI & a1 <= ETR_SNAP_OUTER), "lands in INCOH")
for (k in 1:200) s <- etr_step(s, c(0, 0, 0), 0)
a <- abs(s$coordinate)
expect_true(all(s$coordinate < 0) && all(a >= ETR_BAND_LO & a <= ETR_BAND_HI),
"settles in opposite band")
})
test_case("evaluate_status magnitude exactly 5.0 -> DREAD", function() {
expect_equal(evaluate_status(init_etr_state(c(5, 0, 0))), "DREAD")
# --- L4 drift required ------------------------------------------------
test_case("L4: etr_step refuses to invent drift", function() {
expect_error(etr_step(init_etr_state()), "drift must be supplied")
})
test_case("evaluate_status magnitude exactly 15.0 -> FUNCTIONAL", function() {
expect_equal(evaluate_status(init_etr_state(c(15, 0, 0))), "FUNCTIONAL")
# --- L6 Z-path --------------------------------------------------------
test_case("L6: z<0 -> alimentation (lattice); z>=0 -> transmutation (evolution)", function() {
expect_equal(determine_system_update_path(init_etr_state(c(0, 0, -3))), "LATTICE_REINFORCEMENT")
expect_equal(determine_system_update_path(init_etr_state(c(0, 0, 0))), "EXPERIMENTAL_EVOLUTION")
expect_equal(determine_system_update_path(init_etr_state(c(0, 0, 7))), "EXPERIMENTAL_EVOLUTION")
})
test_case("evaluate_status magnitude exactly 35.0 -> BLUR", function() {
expect_equal(evaluate_status(init_etr_state(c(35, 0, 0))), "BLUR")
})
test_case("evaluate_status magnitude exactly 50.0 -> INCOHERENT", function() {
expect_equal(evaluate_status(init_etr_state(c(50, 0, 0))), "INCOHERENT")
})
# --- evaluate_status: mid-band values ---
test_case("evaluate_status magnitude 10 -> DREAD", function() {
expect_equal(evaluate_status(init_etr_state(c(10, 0, 0))), "DREAD")
})
test_case("evaluate_status magnitude 25 -> FUNCTIONAL", function() {
expect_equal(evaluate_status(init_etr_state(c(25, 0, 0))), "FUNCTIONAL")
})
test_case("evaluate_status magnitude 40 -> BLUR", function() {
expect_equal(evaluate_status(init_etr_state(c(40, 0, 0))), "BLUR")
})
test_case("evaluate_status magnitude 60 -> INCOHERENT", function() {
expect_equal(evaluate_status(init_etr_state(c(60, 0, 0))), "INCOHERENT")
})
# --- determine_system_update_path: Z-axis governs the generative path ---
test_case("determine_system_update_path z > 0 -> LATTICE_REINFORCEMENT", function() {
expect_equal(determine_system_update_path(init_etr_state(c(0, 0, 7))), "LATTICE_REINFORCEMENT")
})
test_case("determine_system_update_path z exactly 0 -> LATTICE_REINFORCEMENT", function() {
expect_equal(determine_system_update_path(init_etr_state(c(0, 0, 0))), "LATTICE_REINFORCEMENT")
})
test_case("determine_system_update_path z < 0 -> EXPERIMENTAL_EVOLUTION", function() {
expect_equal(determine_system_update_path(init_etr_state(c(0, 0, -3))), "EXPERIMENTAL_EVOLUTION")
})
# --- shift_coordinate: toroidal wrap-around within [-bound, bound] ---
test_case("shift_coordinate wraps positive overflow on X (45 + 10 -> -45)", function() {
s <- shift_coordinate(init_etr_state(c(45, 0, 0)), c(10, 0, 0))
expect_equal(s$coordinate, c(-45, 0, 0))
})
test_case("shift_coordinate wraps negative overflow on X (-45 + -10 -> 45)", function() {
s <- shift_coordinate(init_etr_state(c(-45, 0, 0)), c(-10, 0, 0))
expect_equal(s$coordinate, c(45, 0, 0))
})
test_case("shift_coordinate does not wrap an in-bounds shift", function() {
s <- shift_coordinate(init_etr_state(c(10, 5, -5)), c(5, 5, 5))
expect_equal(s$coordinate, c(15, 10, 0))
# --- status -----------------------------------------------------------
test_case("etr_status: per-axis zone vector", function() {
st <- etr_status(init_etr_state(c(25, 10, 40)))
expect_equal(st, c("IN_BAND", "SOFT", "INCOH"))
})
test_summary()