Every serious defect this session survived a green suite. The sharpest:
THREE.WebGLRenderer throws on a device with no WebGL, the exception escaped
its effect, React unmounted the whole root, and every page rendered blank.
432 frontend tests passed throughout, because jsdom never builds a WebGL
context and nothing exercised a real page in a real browser.
scripts/smoke.py closes that blind spot. It drives Chrome over the DevTools
Protocol — no framework, no npm dependency — and checks each of the 13
routes for the four failures that have actually happened here: a page that
renders nothing, horizontal scroll on a phone, an input under 16px (iOS
zooms on focus and never zooms back), and any uncaught error during load.
Non-zero exit, so it can gate a deploy.
All four checks are PROVEN to fire, by pointing the script at a page built
with each defect on purpose — which is how I found that my own
horizontal-scroll check was broken. It compared scrollWidth against
window.innerWidth, but under Chrome's mobile emulation innerWidth expands to
the CONTENT width when a page overflows: 1200 on a 390px viewport, so the
difference was always 0 and the check could never fire. It reported "ok" on
a deliberately 1200px-wide page. Now compares against
documentElement.clientWidth, which stays at the true 390, and reports
"H-SCROLL +810px" on that same page.
Worth stating plainly: the earlier clean sweep I reported was measured with
that broken check, so its horizontal-scroll result was meaningless. Re-run
with the fix, all 13 routes still pass at 390x844 and 1440x900 — but that is
the run that counts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The worst of it: the ghost-log HUD is a fixed 300px panel pinned bottom-left
on every route. On a 390px screen it covered the seance ask-field and its
buttons outright — you could not reach the input to type a question. It now
collapses, and spans the width instead of blocking it under 560px.
- 16 uses of `100vh` meant every page was taller than iOS Safari's visible
viewport, hiding the last row behind the toolbar. Now `100dvh` with the
`100vh` line kept first as the fallback.
- Six inputs under 16px triggered iOS zoom-on-focus, which never zooms back
out — you were left panning a zoomed layout after tapping login. All six
at 16px, plus a global floor.
- No safe-area insets existed anywhere, so enabling viewport-fit=cover
would have put content under the notch and home indicator. Added,
including the fixed-position shells that ignore body padding.
- The codex voice table forced horizontal page scroll; it scrolls in its
own container now.
- Tap targets under 44px raised, including a transcript replay button that
was ~20x14px and named only by a `title` tooltip, which touch never shows.
Decorative micro-labels left alone deliberately — bumping them wholesale
would reintroduce the overflow this fixes.
README: the documented test command cannot work. conftest APPENDS `_test`,
so the documented DATABASE_URL derived `quantumancy_test_test` and errored
every test at setup. Corrected, with the two guards explained. Test counts
were stale (328/366 -> 403/385).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The old one predated roughly half the system: entropy, astronomy,
geomagnetic, manifest speech, voice archetypes, rituals/judgment/essence,
the ESP32 node, guest access, the Ghost Log, conditions, PWA, phone
layouts. Test counts corrected (54->328 backend, 110->366 frontend), the
protocol tables now list every real frame and endpoint, and Current State
distinguishes verified-live from unverified-on-hardware from
built-but-unreachable honestly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Renaming: "Armory" read too militaristic for a séance app. Landed on "The
Reliquary" (not "The Threshold" — that name was already taken by the
landing-page back-link).
Spec addendum: added a visible essence currency (earned per summon, spent
on unlocks — distinct from the hidden favor score) and a fourth judgment
verdict, cross_over, for compassionately helping a genuinely benevolent
"stuck" spirit move on rather than just trusting or banishing it. Updates
workstreams B/C/E/F accordingly before any of them are dispatched.
The gap-g-readme merge commit (57a8914) staged this fix but never
re-staged it after editing, so the merge landed with the pre-fix content —
the working tree had the correction but git didn't. No functional change,
just closing the gap between what was intended and what was committed.