Files
qtalker---/docs/superpowers/specs/2026-07-31-passage-doctrine-firmware-design.md
Indiana 76af1939e6 docs: spec for provable firmware, the layered Passage, and the Doctrine
Three workstreams: host-testable firmware logic (closing the gap that let
the RD03E frame bug ship), crossing over as a five-layer rite with
trait-driven twists, and a /doctrine page whose every arcane claim maps to
a real mechanism in the source.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 04:26:05 +00:00

7.7 KiB

The Passage, the Doctrine, and provable firmware

Three parallel workstreams.

Binding rules (all workstreams)

  • Additive only. Nothing removed. No restructuring beyond your files.
  • Guests stay first-class; never gate the séance.
  • i18n parity is a hard gate. Strings via t(), keys in BOTH src/i18n/en.json and es.json; npm run pretest must pass. Template keys need a domain rule in src/i18n/coverage-check.mjs (base names only — it derives *Title itself).
  • Backend tests: cd backend && set -a && source ../.env && set +a && source venv/bin/activate && python -m pytest tests/<file> -q -p no:cacheprovider (they use quantumancy_test automatically — never the live DB).
  • Frontend: npx tsc --noEmit -p ., npm run pretest, npx vitest run.
  • Mobile: 390px, 44px targets, honour prefers-reduced-motion.
  • Keep diffs to shared files (main.py, App.tsx, ws.py, SeancePage.tsx) as small as possible — the integrator merges several workstreams.

Workstream F — provable firmware (highest priority)

The firmware has NEVER been flashed. A real bug already slipped through (RD03E_FRAME_LEN was 5 for a 6-byte frame, making every distance reading garbage) — it was pure logic and should have been catchable without hardware. Close that gap.

Files: firmware/esp32p4-sensor-node/main/ and a new firmware/esp32p4-sensor-node/test/ directory.

  1. Extract pure logic from the drivers into ESP-IDF-free units so it can compile on a host with plain gcc:

    • rd03e_parse.c/h — the frame scanner: given a byte buffer + length, find the newest valid frame and return gesture + distance. No UART, no ESP headers. rd03e.c then calls it.
    • bmp280_compensate.c/h — the temperature/pressure compensation maths transcribed from the Bosch datasheet. No I2C. bmp280.c calls it.
    • mems_level.c/h — the RMS → dBFS conversion. No I2S. These are moves, not rewrites: keep the existing behaviour byte for byte, and keep the explanatory comments with the code they explain.
  2. Host test harness: a Makefile (or small shell script) that builds the extracted units with gcc -Wall -Wextra -Werror plus a test main, and a run_tests.sh that returns non-zero on failure. No frameworks — plain asserts are fine and keep it dependency-free.

  3. Tests that would have caught the real bug, and more:

    • rd03e: a well-formed frame parses to the exact expected gesture and distance; a 6-byte frame is consumed as 6 bytes; a truncated trailing frame is ignored; garbage before a valid frame is skipped; a frame with a bad footer is rejected; TWO frames in one buffer yield the NEWEST; a distance byte pair of e.g. 0x2C 0x01 yields 300cm (little-endian — this is precisely the class of bug that shipped).
    • bmp280: verify against the Bosch datasheet's own worked reference values if you can derive them, else assert physically-sane invariants (a known raw ADC + known calibration yields a temperature in a plausible band; pressure decreases monotonically with altitude proxy; the var1 == 0 divide-by-zero guard returns 0 rather than crashing).
    • mems: full-scale input → ~0 dBFS; silence → the -120 floor, not -inf or NaN.
  4. Update firmware/esp32p4-sensor-node/README.md "what's verified vs not" to state precisely what the host tests now prove (logic) and what still requires hardware (wiring, timing, real register behaviour). Be honest — do not overclaim.

Deliverable: ./run_tests.sh passes from a clean checkout with only gcc.


Workstream L — the Passage (layered crossing over)

Today cross_over is a single verdict with one outcome. Make helping a spirit pass a layered rite — the "unwrapping" the owner asked for: each layer reveals more, rewards, and can twist.

Files: backend/app/passage.py (new, PURE — no DB/IO, fully unit-tested), backend/app/ws.py (new passage_* frames + handlers), frontend/src/components/PassagePanel.tsx + css, mount in SeancePage.tsx, i18n, tests.

Design (implementer owns the details, but honour these):

  • Layers, in order: listen → name → unbind → open → release. Each is a distinct beat with its own prompt and its own reveal.
  • Each completed layer reveals something true about the spirit that the seeker did not have: one hidden trait, a fragment of what binds them, the era they died in. Use the entity's REAL traits — never invent a second hidden state.
  • Each layer rewards essence (small, escalating) and the final one pays the existing CROSS_OVER_ESSENCE.
  • Twists must be driven by the entity's real traits, not random flavour:
    • high deceptiveness → a layer can lie (reveal a false trait, marked as unreliable only in hindsight at the next layer).
    • high volatility → a layer can collapse the rite, forcing a restart from listen (keep prior essence — losing it would feel cheap).
    • low alignment (a demon) → release resists, exactly as the current resisted consequence does. Do not let a demon cross.
  • Draws use veil_float(state.entropy, "<context>") from app/entropy.py with a distinct context per decision — never random — so the room's physical noise drives the twists like everything else here.
  • Passage must not bypass the existing judgment economy: reuse credit_essence, and rate-limit like ritual_limiter.
  • passage.py is pure: given (traits, layer, draw) → outcome. All twist logic unit-tested with pinned draws; no flakiness.

UI: a vertical five-layer track showing which are sealed/open/failed, the reveal text as each opens, and the reward. Spooky but legible; reduced motion respected.


Workstream D — the Doctrine (how it actually works)

A new public page at /doctrine explaining, in the voice of an insufferably erudite occultist, how this app genuinely channels, summons and passes spirits — using the device and the human.

Files: frontend/src/pages/DoctrinePage.tsx + css, route in App.tsx, a link from the landing page, i18n, usePageMeta for SEO.

The constraint that makes this good: every arcane claim must be TRUE. This app runs on real physics, so the mystical register can describe real mechanisms without inventing anything. Read the source and describe what is actually there:

  • app/entropy.py — Von Neumann debiasing, SHA-256 conditioning, HMAC with fresh server secret; the client can only ADD unpredictability.
  • app/entities.py — signature_from_anomalies (frequency-bucket + magnitude fingerprint = the "channel"), RETURN_CHANCE.
  • app/celestial.py — mean synodic month, true solar midnight from longitude; VEIL_THINNESS_PULL.
  • app/geomagnetic.py — NOAA planetary K-index.
  • frontend/src/lib/baseline.ts — the time-aware EMA (1 - exp(-dt/tau)) every sensor shares.
  • lib/bluetooth.ts — 2.4GHz body absorption; lib/magnetometer.ts — µT.
  • SpiritService.manifest / scry — entropy-seeded sampling, vision.

Structure it as numbered "articles" with an ornate voice, but each article should carry a plain-language margin note ("in the profane tongue: …") that states the mechanism plainly. That way it is genuinely educational AND fits the fiction — and nobody is misled into thinking invented physics is real.

Include the honest limits too (an article on what the instrument CANNOT do: no WebUSB/Bluetooth/magnetometer on iOS, unflashed firmware, etc.) — a doctrine that admits its own boundaries reads smarter than one that doesn't.


Integration (controller)

Merge F, then L, then D. Resolve ws.py / App.tsx / SeancePage.tsx overlaps, run both suites + i18n gate + the firmware host tests, build, deploy, restart, verify live, push.