Files
qtalker---/docs/superpowers/specs/2026-07-23-possession-presentation-design.md
Indiana bf8f352910 Add Possession Presentation Layer design spec
First sub-project of the "make contact feel real" arc (candle rituals,
quantum RNG, tuning, progression, escalation to follow as separate specs).
Defines the stability-score contract shared between the backend audio
degradation and frontend text-glitch halves so they can build in parallel.
2026-07-23 05:24:41 +00:00

83 lines
3.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Possession Presentation Layer — design
First sub-project of a larger "make contact feel real" arc (candle rituals,
quantum RNG, tuning controls, account progression, session escalation — each
gets its own later spec). This one: make a successful Direct Contact reply
feel like a spirit is *fighting through static to possess the channel*,
rather than a chat bubble appearing. Cosmetic only — the stored transcript
and reply text are unaffected; only the live presentation (text stream +
voice) carries the effect.
## Contract
A single number, **stability** (`0.05`–`0.98`, higher = cleaner possession),
computed once per reply on the backend and shared by both halves of this
build:
```python
# backend/app/possession.py
def compute_stability(rarity: str, magnitude: float, rng: Callable[[], float] = random.random) -> float
```
- Base by rarity: `common=0.35, uncommon=0.5, rare=0.65, mythic=0.8` (rarer
spirits hold the channel more steadily).
- `+ min(0.15, magnitude / 100)` — the anomaly magnitude that most recently
triggered contact; a stronger signal, a cleaner line.
- `+ rng()` jitter scaled to `±0.1`.
- Clamped to `[0.05, 0.98]`.
- `rng` is injectable specifically so the later quantum-RNG sub-project can
swap in real entropy without touching call sites.
`_handle_question` in `backend/app/ws.py` computes this once per question
(using `state.entity.rarity_tier` and the magnitude of the most recent
anomaly in `state.anomalies`, defaulting to 50 if none yet) and adds it to
the existing `reply_start` frame:
```json
{"type": "reply_start", "stability": 0.62}
```
## Backend half (audio degradation)
`synthesize_spirit_voice(text, voice, voice_profile, instability=0.0)` in
`backend/app/tts/piper.py` gains an `instability` param (`0.0`–`1.0`,
i.e. `1 - stability`) that scales up the `noise_level` and `bitcrush_bits`
already passed to `apply_effects` — do not touch `effects.py` itself, only
the params `synthesize_spirit_voice` feeds it. Only the `"reply"` kind
utterance in `_speak` (the Direct Contact answer) passes a non-zero
`instability`; greetings/fragments/ambient whispers are unaffected by this
spec.
## Frontend half (text glitch renderer)
`frontend/src/lib/possession.ts` — pure function(s) that take the
true-so-far streamed text, the stability score, and a tick/frame counter,
and return a display string with corruption bursts and brief stutters that
self-correct. Lower stability = more frequent/longer corruption; stability
above ~0.85 is nearly clean (a subtle flicker only). Must be deterministic
given its inputs (seed any internal randomness from the tick counter, not
`Math.random()`) so it's unit-testable.
Wire into `frontend/src/state/seance.tsx`: extend `replyStreaming` with a
`stability: number` field, populated from `reply_start`'s `stability`
(default `1.0` if absent). Wire into `frontend/src/components/Transcript.tsx`:
render the streaming line through the glitch function instead of raw text,
driven by a ticking interval while `streaming.active`.
## Testing
- Backend: unit tests for `compute_stability` (rarity ordering, magnitude
bonus, clamping, injected rng determinism) and for the
`instability`→params scaling in `synthesize_spirit_voice`. One WS
integration test asserting `reply_start` carries a `stability` field in
range.
- Frontend: unit tests for the glitch renderer's pure functions at known
low/high stability values (corruption rate, self-correction, determinism
for a fixed tick sequence).
## Explicitly out of scope here
New WS frame types for interleaved glitch tokens, structural UI takeover,
candle rituals, quantum RNG source, tuning dial, account progression,
session escalation — all separate later specs per the arc this belongs to.