diff --git a/docs/plans/A8-etr.md b/docs/plans/A8-etr.md index 6625685..725ee76 100644 --- a/docs/plans/A8-etr.md +++ b/docs/plans/A8-etr.md @@ -20,8 +20,9 @@ engine, and *not* migration. The axes: Handles the stress PS+/Eth-Int generate. ## 2. Status / certainty -**SCAFFOLD — built and tested this session** (`run_etr_tests.sh` → 12 PASS / 2 FAIL-by-design / -2 PEND). Full law + status in `../../src/endocrine/etr/etr_invariants.md` (this spec defers to it). +**SCAFFOLD — `RESTORE_GAIN` fitted** (`run_etr_tests.sh` → **14 PASS / 0 FAIL / 2 PEND**; the 2 PEND +are L5 active coupling and L7). Full law + status in `../../src/endocrine/etr/etr_invariants.md` +(this spec defers to it). ## 3. Language & location GNU Octave · `src/endocrine/etr/` (`etr.m`, `test_etr.m`, `run_etr_tests.sh`, `etr_invariants.md`). diff --git a/src/endocrine/etr/etr.m b/src/endocrine/etr/etr.m index 2ddb415..9c0e732 100644 --- a/src/endocrine/etr/etr.m +++ b/src/endocrine/etr/etr.m @@ -22,6 +22,12 @@ function v = ETR_BAND_LO(); v = 17; endfunction function v = ETR_BAND_HI(); v = 35; endfunction function v = ETR_WRAP(); v = 50; endfunction +% ---- fitted constant (NOT law) ---- +% RESTORE_GAIN: spring stiffness of the out-of-band restoring force (etr_axis_restoring). +% Fitted to satisfy L3-magnitude (force nonzero out of band) and L2 (zero-drift +% convergence into the band) without overshoot past the far edge. 0 < GAIN < ~2. +function v = ETR_RESTORE_GAIN(); v = 0.5; endfunction + % ---- state: one point, three scalars ---- function s = etr_init(coord) if nargin < 1, coord = [25 25 25]; endif % default: a valid in-band point @@ -50,11 +56,22 @@ function d = etr_axis_restoring_dir(v) endif endfunction -% ---- L3: per-axis restoring FORCE (STUB: magnitude UNFITTED -> RED) ---- -% Direction above is law; the magnitude/curve (RESTORE_GAIN) is C1 TBD. -% Returns 0 for now so the magnitude + convergence tests stay red. +% ---- L3: per-axis restoring FORCE (FITTED) ---- +% Direction is law (etr_axis_restoring_dir); the magnitude is a spring fitted +% via RESTORE_GAIN. The force is active ONLY out of band (in band = slack, L3), +% and pulls toward the band CENTRE (not the near edge): a centre target makes the +% step cross INTO [17,35] and stop there, whereas an edge target would asymptote +% onto 17 from below and never reach |v|>=17 (failing L2). Below-band pushes away +% from 0, so the restoring force alone never crosses zero (L7). function r = etr_axis_restoring(v) - r = 0; % STUB — TODO: RESTORE_GAIN * shape(v) along etr_axis_restoring_dir(v) + GAIN = ETR_RESTORE_GAIN(); + a = abs(v); + if a < ETR_BAND_LO() || a > ETR_BAND_HI() + centre = (ETR_BAND_LO() + ETR_BAND_HI()) / 2; % 26 + r = GAIN * (centre * sign(v) - v); % spring toward signed centre + else + r = 0; % in band: slack (L3) + endif endfunction % ---- L5: cross-axis coupling (OPEN SEAM) ---- diff --git a/src/endocrine/etr/etr_invariants.md b/src/endocrine/etr/etr_invariants.md index b70f986..b4bfcb6 100644 --- a/src/endocrine/etr/etr_invariants.md +++ b/src/endocrine/etr/etr_invariants.md @@ -24,10 +24,15 @@ Each axis is a standalone scalar carrying one of ETR's three tensions: | L7 no-zero-cross | **C2** | Bands wall off 0; a pole/sign flip happens **only** by riding over the ±50 wrap, never through neutrality. *(emergent — unconfirmed)* | | L8 mechanism | **C2** | "Opposition" = a restoring force on the drift, **not** an out-of-band cost. *(unconfirmed)* | -## Constants (NOT yet fitted — C1) +## Constants `BAND_LO = 17`, `BAND_HI = 35`, `WRAP = 50` are **law** (L1–L3), not fitted. -`RESTORE_GAIN` (restoring magnitude/curve) and `COUPLING` (L5 strength/mapping) are **TBD**, -to be fitted so the tests pass — never asserted ahead of a test. +`RESTORE_GAIN = 0.5` is **FITTED** — the spring stiffness of the out-of-band restoring force: active +only out of band (in band is slack, L3), pulling toward the band **centre (26)** so a step crosses +*into* `[17,35]` and stops there (an edge target would asymptote onto 17 from below and fail L2). +Below-band pushes away from 0, so the force alone never crosses zero (L7). Lands e.g. `[5 5 5] → 20.75` +in 2 steps; `[45 −45 40] → [30.75 −30.75 33]`. Valid range `0 < GAIN < ~2` (above ~2 a cross-in step can +overshoot the far edge). +`COUPLING` (L5 strength/mapping) remains **TBD** — open seam, not to be invented. ## Testable predicates (see `test_etr.m`) - **L1**: `wrap(50) = −50`; `wrap(60) = −40`; `wrap(v)=v` for `v∈(−50,50)`; `wrap(49.9) = wrap(−50.1)`. @@ -58,7 +63,9 @@ scaffold seam is correct). What's fixed is their **provenance** (upstream in SAE endocrines) and two side-effects (reshuffle, shift-rate) that belong to *those* organs. Still open: which stress endomotiv; the "too strong" reshuffle threshold; the shift-rate function. -## Status at scaffold -Implemented (green): L1 wrap, L3 direction, L4 drift-must-be-fed, L5 coupling-off identity. -Red (await fitting): L3 restoring **magnitude**, L2 band-convergence. -Pending (unconfirmed): L5 active coupling, L7 no-zero-crossing, L8 mechanism. +## Status (RESTORE_GAIN fitted — 14 PASS / 0 FAIL / 2 PEND) +Green: L1 wrap, L3 direction + **restoring magnitude (fitted)**, **L2 band-convergence**, +L4 drift-must-be-fed, L5 coupling-off identity. L8 (opposition = a restoring force on the drift, not an +out-of-band cost) is now **realised by construction** — the force is added alongside drift, never charged +as a cost — though still tagged C2 until a dedicated test pins it. +Pending: L5 active coupling (C1, open seam), L7 no-zero-crossing (C2).