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>
This commit is contained in:
@@ -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/<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.
|
||||
Reference in New Issue
Block a user