Commit Graph

3 Commits

Author SHA1 Message Date
Indiana
cf602ab3de feat: the moon shapes who answers; wire magnetometer; drop dead CSS
MOON INFLUENCE ON SUMMONING

Astronomy previously only decided whether a channel's familiar spirit
returned. It now shapes *who comes through*:

  - Rarity skews with real moon illumination. At full moon the rare and
    mythic weights roughly triple while common recedes, so a mythic
    summoning becomes a reason to go out on the right night rather than a
    flat lottery. Deliberately a skew and never a gate — every tier stays
    reachable on every night, because someone who can only play midweek
    should not be locked out of the good spirits.
  - Hidden traits take a small moonlit nudge: power and volatility rise,
    alignment drifts slightly darker. Capped at 0.12 and clamped to [0,1],
    so a full moon intensifies what a spirit already is instead of
    rewriting it. Deceptiveness is untouched — whether a spirit lies is its
    own nature, not the sky's doing.
  - The mint prompt is told the phase, and explicitly told the entity must
    never mention or seem aware of it. It shapes who they are, not their
    dialogue; a ghost remarking on the moonlight would break the illusion
    instantly.

Tests assert the outcomes shift in practice (mythic rate over 4000 draws,
rare-tier counts across 300 fallback profiles), not merely that the code
runs. test_mint_prompt_never_receives_traits now allows `sky` while still
forbidding `traits`: moon phase is public, observable state anyone can look
up, hidden ground truth is not.

GEOMAGNETIC (app/geomagnetic.py)

Real NOAA SWPC planetary K-index, verified against the live endpoint —
which caught a real bug: I had written the parser against an
array-of-arrays shape, and the actual feed serves a list of objects
(`estimated_kp` float, `kp_index` int, `kp` a display string with a letter
suffix). Fixed, and the tests now use the real captured shape. Cached,
never blocking, and a failed refresh keeps serving the last real value —
an hour-old genuine measurement beats nothing, and geomagnetic conditions
do not change fast enough for that to mislead.

MAGNETOMETER WIRED

MagnetometerListener existed but was never connected. The EMF panel now
runs it alongside the motion listener where the hardware exists, so the
"EMF meter" measures actual magnetic field in µT rather than only
inferring disturbance from movement. Additive: the motion path is
untouched and remains the only option on iOS. Its field jitter also feeds
the entropy pool.

DEAD CODE

Removed .evp-scope and .radio-waterfall, orphaned when both panels moved to
the shared SpectrumScope. Audited every other flagged export first and left
them alone — they are used internally, and "not imported elsewhere" is not
the same as dead.

Adds docs/CHANNELS.md recording what each channel measures and, honestly,
what has actually been verified against hardware versus only written
carefully.

311 backend + 355 frontend tests pass; i18n parity gate passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 13:31:15 +00:00
Indiana
e5bc253105 feat: add hidden entity traits (Workstream A) + idempotent migration
Entity.traits (alignment/power/volatility/deceptiveness, 0.0-1.0 each) is
rolled once at mint time in entities.py, seeded from the entity's
signature via random.Random(f"traits:{signature}") — a separate rng
namespace from normalize_profile's existing "norm:" rng, and never fed
into mint_prompt, so persona text stays fully decoupled from ground
truth. normalize_profile now includes "traits" in its returned dict;
fallback_profile inherits it for free since it already delegates to
normalize_profile.

Adds the new JSONB column to the Entity model (default {}) and the
idempotent `ALTER TABLE entities ADD COLUMN IF NOT EXISTS traits ...`
migration line to main.py's lifespan, per the live-Postgres migration
convention this spec introduces (no Alembic in this repo).

Tests cover trait value ranges, signature-determinism, and
persona/trait independence (same persona template pairs with a wide
spread of alignment rolls across signatures), plus a regression check
that mint_prompt's signature never grows a traits parameter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 11:15:25 +00:00
Indiana
b9110f45de feat: spirit engine — seance WS, entity minting/Codex, Piper TTS voices, wire telemetry
- WS /ws/session: modes, summon, anomaly fragments, streaming direct contact,
  passive wire-ghost ambient loop, per-user rate limits, event transcript
- entities: anomaly-signature fingerprinting, LLM minting + procedural
  fallback, Codex matching with contact counts and sightings
- tts: 8 local Piper voices (EN/ES), per-entity voice profiles, numpy
  effects chain (pitch/rate/bitcrush/echo/static)
- llm: streaming client, submit_stream in bounded queue, SpiritService
  with offline fallbacks for every channel
- routes: public /api/codex, /api/codex/{id}, /api/stats; /audio static mount
- models: Entity, EntitySighting, Event, ContactSession(entity_id, language)
2026-07-20 20:13:28 +00:00