From 76af1939e6c4ffa120765b8bf93d8add290e4487 Mon Sep 17 00:00:00 2001 From: Indiana Date: Fri, 31 Jul 2026 04:26:05 +0000 Subject: [PATCH] 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 --- ...-07-31-passage-doctrine-firmware-design.md | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-31-passage-doctrine-firmware-design.md diff --git a/docs/superpowers/specs/2026-07-31-passage-doctrine-firmware-design.md b/docs/superpowers/specs/2026-07-31-passage-doctrine-firmware-design.md new file mode 100644 index 0000000..b086470 --- /dev/null +++ b/docs/superpowers/specs/2026-07-31-passage-doctrine-firmware-design.md @@ -0,0 +1,153 @@ +# 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/ -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, "")` 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.