# R30 Phase-1 — φ0 PAD-point decay measurement (demo v1.0.0b14, A1 anchor fix) Executor: ratatoskr-dev · coordinator: worldtree-dev · owner: brokkr-smithy-dev. Method: **joint two-timescale fit (b)** (brokkr-ruled) + **empty-tail direct fit** as mutual validation, both run off the same trajectory. Measured against the **corrected anchor** (`decay_anchor = baseline_pad()`, positive_p_cap removed; worldtree v1.0.0b14 `e1cdf82`). ## Method - 4 base agents × {pos, neg} impulse, fresh `end_user` each, 26 turns, "Please continue." neutrals + ~16s spacing (so one trajectory yields both an emotion-free tail AND the full joint-fit series). Natural per-turn cadence for the point-decay; spacing only lets the wall-clock emotion fade land in-window. - **Capture:** `affect_update` `current` snapshot (live mood) + `emotions_active` + wall-clock, per turn. - **push_t** (the covariate): only NEWLY-appraised, new-type-or-strictly-stronger emotions push (dedup-gated; confirmed source-authoritative by worldtree-dev vs `registry.py::post_turn` L307-324 — the persistent `emotions_active` decays for render/goals but never re-pushes mood). Reconstructed from `emotions_active` deltas: a type new or strictly-stronger-than-the-prior(decayed)-snapshot pushed at its new intensity. `push_t[axis] = Σ OCC_PAD_IMPULSES[type]·intensity·0.15`. - **Model** (per axis, push-then-decay): `y_t=(axis_{t+1}−anchor)`, `X_t=(axis_t+push_t−anchor)`, regress `y=φ·X+c` → φ=slope, decay_rate=1−φ, anchor=`baseline_pad`. Validated on b9 first (φ_P 0.959, c −0.003, r² 0.997). ## Result — CONFIG FAITHFUL **φ0 (PAD-point decay retention) ≈ 0.95–0.97, config-faithful.** Both reads agree: | agent | baseline P | joint φ_P | c_P | r²_P | empty-tail φ0_P | |---|---:|---:|---:|---:|---:| | lofn | 0.809 | 0.954 / 0.967 (pos/neg) | ~0.000 | 0.93–0.94 | — (excursion faded pre-empty) | | mimir | 0.615 | 0.964 / 0.977 (pos/neg) | −0.012 / −0.015 | 0.97–0.99 | — (never emptied) | | mask | 0.000 | 0.983 / 0.982 (pos/neg) | +0.004 / +0.006 | 0.90 / 0.95 | — | | forseti | 0.239 | signal-poor (r²=0.46/0.61, excluded) | — | — | **0.95** (neg, r²=1.0) | - **Joint fit, 6 well-conditioned runs (3 baselines; all 8 probes in): φ_P = 0.971 ± 0.01** (range 0.954–0.983). Consistently a hair above config 0.95 — the empty-tail read (no push modeling) nails **0.95 exactly**, so the joint fit's ~0.97 carries a small upward bias from push under-attribution; true φ0 ≈ 0.95. - **Empty-tail cross-check** (forseti_neg's clean pure-decay tail): **φ0_P = 0.95**, r²=1.0 — independently confirms the joint fit. - **config cross-check:** measured φ0 ≈ 0.95–0.97 vs config `1−0.05 = 0.95` → **config faithfully applied.** The R29 "net ~0.90" was **continuous re-appraisal** (a new dedup-gated push most turns), NOT re-push contamination. - **intercept c ≈ 0** across the well-conditioned runs (−0.015..+0.004) → the push_t reconstruction + push-then-decay order are correct on the deployed engine. ### Diagnostics for brokkr - **(i) residual/c:** c≈0 → push_t + order correctly specified. ✓ - **(ii) φ≈0.95:** YES → config faithful; kills the config≠behavior worry. ✓ - **(iii) A/P decay ratio: NO clean ~1.9.** The per-axis mood-point decay is roughly uniform (matches the single `decay_rate` in `model.py` L175-179). CAVEAT: the per-axis **A/D** fits are noisy — small A/D excursions yield some φ_A>1 artifacts (e.g. mask A/P −1.66 is a fit artifact), so the ratio is not cleanly determined here; but there is **no support for 1.9×** in the mood-decay layer. If the S2 ~1.9× chronometry is real it lives elsewhere (the OCC impulse vectors' per-axis asymmetry, or emotion wall-clock decay), not in point-decay. A dedicated per-axis run with larger A/D excursions would pin it. - **trait-flat:** φ_P ~equal across baselines 0.0 / 0.615 / 0.809 → the current decay is **trait-FLAT** (the pre-R30 baseline R30's `dynamics_from_ocean()` will modulate). ✓ ### φ_max recommendation (preserve-persistence rule, Vuong-locked) Measured φ0 ≈ 0.96 (> 0.90) → **RELAX φ_max to ≈0.96** — the measured good operating point defines the cap; do NOT re-base the agents faster. ## Caveats - **forseti (baseline 0.239) is signal-poor** in the joint fit — a negative impulse from a low baseline has little down-room → tiny excursion → ill- conditioned regression (r² 0.46/0.61). Correctly excluded (its empty-tail still gave a clean 0.95). High/mid-baseline agents (lofn/mimir/mask) carry the joint-fit signal. - **mask_neg** recovered from a demo-side SSE stall (turn 14) and completed — all 8/8 in. It is well-conditioned (φ_P 0.982, r² 0.95) and confirms the verdict (didn't move it). `stream_turn_resilient`'s reconnect handled the transient hang. Series: `r30-phi0-{lofn,mimir,forseti,mask}-{pos,neg}.json` (per-turn pad + emotions_active + wall-clock). sec/turn ≈ 19–25 (16s spacing + processing).