chore: dead-code + duplicate sweep; fix moon-flaky re-contact test

Dead code: removed an unused `settings` import (main.py), an unused
`RITUAL_HOLD_MS` import (RitualPanel), and an unused `beforeEach`
(SigilDesigner test). The `_refs` keep-alive in sdr.ts is deliberate
(holds hardware-pass constants) and stays; the orphaned .evp-scope /
.radio-waterfall CSS was already removed in an earlier lint pass.

Duplicate: coldSpot.ts and baseline.ts each carried the same time-aware
EMA alpha formula. Extracted it as baseline.emaAlpha(dtMs, tauMs) and
pointed both at it. coldSpot's pure-function core is deliberately NOT
merged into the stateful ThresholdBaseline class — different contract
(immutable-state-threaded vs internal-threshold), and forcing them
together would be an overhaul that risks the tested cold-spot logic.

Determinism fix: test_familiar_presence_answers_again_on_a_known_channel
pinned RETURN_CHANCE=1.0 but not the sky. Since the astronomy wiring made
the real return chance RETURN_CHANCE*(1 - veil_thinness*PULL), and
veil_thinness reads the *actual current moon phase*, a full-moon test run
dragged the effective chance to ~0.55 and the test failed ~45% of the
time. Now also pins VEIL_THINNESS_PULL=0 to isolate re-contact from the
veil influence (which has its own tests). Verified 12/12 consecutive
passes; it was ~7/12 before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Indiana
2026-07-29 02:14:39 +00:00
parent a5ee2d57cd
commit 2a67684df4
6 changed files with 33 additions and 12 deletions

View File

@@ -9,7 +9,6 @@ from fastapi.staticfiles import StaticFiles
from sqlalchemy import text
import app.models # noqa: F401 — registers models on Base.metadata before create_all
from app.config import settings
from app.db import Base, async_session_maker, engine
from app.routes.auth import router as auth_router
from app.routes.codex import router as codex_router

View File

@@ -169,6 +169,14 @@ async def test_familiar_presence_answers_again_on_a_known_channel(sync_client, m
(`ws.RETURN_CHANCE`), so this pins the probability to 1.0 rather than
relying on the default — at 0.72 this assertion would otherwise pass
only ~72% of the time, which is worse than failing.
It ALSO pins VEIL_THINNESS_PULL to 0. The real return chance is
`RETURN_CHANCE * (1 - veil_thinness * PULL)`, and veil_thinness is
driven by the *actual current moon phase and geomagnetic Kp*. On a
full-moon test run, thinness ≈ 1 drags even a pinned RETURN_CHANCE=1.0
down to ~0.55 — so without this, the test is silently date-dependent
and fails ~45% of the time in the wrong week. The veil-thinness
influence has its own dedicated tests; this one isolates re-contact.
"""
# Scoped limiters: the module-level ones are shared singletons that
# accumulate across the whole session, and this test summons more than
@@ -180,6 +188,7 @@ async def test_familiar_presence_answers_again_on_a_known_channel(sync_client, m
app.ws, "summon_ip_limiter", RateLimiter(max_requests=100, window_seconds=60)
)
monkeypatch.setattr(app.ws, "RETURN_CHANCE", 1.0)
monkeypatch.setattr(app.ws, "VEIL_THINNESS_PULL", 0.0)
_login(sync_client, "mediumx")
anomalies = [
{"type": "anomaly", "source": "radio", "frequency": 101.0 + i, "magnitude": 5.0 + i}

View File

@@ -18,7 +18,6 @@ import type { CSSProperties } from 'react'
import { useTranslation } from 'react-i18next'
import { useSeance } from '../state/seance'
import {
RITUAL_HOLD_MS,
RITUAL_PROMPTS,
holdProgress,
nextStep,

View File

@@ -1,6 +1,6 @@
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { afterEach, describe, expect, it, vi } from 'vitest'
import '../i18n'
import { SigilDesigner } from './SigilDesigner'
import { RUNES, SIGIL_MAX_POINTS, SIGIL_SLOT_COUNT } from '../lib/sigil'

View File

@@ -36,6 +36,25 @@ export type BaselineEvent = {
at: number
}
/**
* EMA smoothing factor from elapsed wall-clock time, not sample count.
*
* Sensor intervals here are irregular — a BLE beacon may advertise every
* 100ms or every 2s, a device can drop offline and reconnect — so a fixed
* per-sample alpha would weight a burst and a long gap identically. The
* exponential-decay form degrades gracefully at both ends: a long gap
* pushes alpha toward 1 (the old baseline is stale, trust the new reading),
* a rapid burst toward 0 (barely move it).
*
* A non-positive dt (duplicate timestamps, clock skew, two readings in one
* tick) can't be trusted as an elapsed time, so it falls back to a flat
* step rather than dividing by it. Shared by ThresholdBaseline and
* coldSpot's pure-function core so the one formula lives in one place.
*/
export function emaAlpha(dtMs: number, tauMs: number): number {
return dtMs > 0 ? 1 - Math.exp(-dtMs / tauMs) : 0.15
}
export type ThresholdBaselineOptions = {
/** Deviation magnitude that counts as an event, in the sensor's own unit. */
threshold: number
@@ -91,11 +110,8 @@ export class ThresholdBaseline {
const deviation = value - this.mean
const warm = this.warm
// Guard against zero/negative dt (duplicate timestamps, clock skew, two
// readings in one tick) with a flat fallback rather than dividing by an
// elapsed time that isn't trustworthy.
const dt = this.lastAt === null ? 0 : atMs - this.lastAt
const alpha = dt > 0 ? 1 - Math.exp(-dt / this.opts.tauMs) : 0.15
const alpha = emaAlpha(dt, this.opts.tauMs)
this.mean = this.mean + alpha * deviation
this.lastAt = atMs
this.samples++

View File

@@ -22,6 +22,8 @@
// why that's an EMA keyed on elapsed wall-clock time rather than sample
// count — real hardware does not report on a fixed schedule.
import { emaAlpha } from './baseline'
// ---------------------------------------------------------------------------
// Shared baseline core
// ---------------------------------------------------------------------------
@@ -113,12 +115,8 @@ export function applyReading(
const deviation = value - state.mean
const warm = state.sampleCount >= config.minSamples
// Guard against zero/negative/out-of-order dt (duplicate timestamps,
// clock skew, or two readings racing in the same tick) with a flat
// fallback step rather than dividing by an elapsed time that isn't
// trustworthy.
const dtMs = state.lastAt === null ? 0 : atMs - state.lastAt
const alpha = dtMs > 0 ? 1 - Math.exp(-dtMs / config.tauMs) : 0.15
const alpha = emaAlpha(dtMs, config.tauMs)
const nextMean = state.mean + alpha * deviation
return {