soul-demo.c was previously an older compiled artifact that triggered an
undefined reference to emit_metric/emit_log/trace_span_* at link time in CI.
Two fixes:
1. Rebuild soul-demo.c from soul-demo.el using current elc — cleaner
codegen (no double-wrapped el_from_float), same logic, unix_timestamp
collision with el_runtime.c removed.
2. Dockerfile.stage: compile soul-demo against el_runtime.c directly
(not el_runtime.o) so all runtime symbols are always resolved from the
staged source, bypassing any Docker layer cache divergence on el_runtime.o.
Recovers original JS from git history and ports it into proper El source
files under src/js/. Each file wraps the original JS in a native_js call
inside a main() function, making it valid El that compiles to a
self-contained IIFE via elc --target=js --bundle.
Files added:
src/js/account-auth.el - Supabase OTP magic-link (sendMagicLink)
src/js/account-dashboard.el - Account dashboard: session, plan card, family
src/js/chat-widget.el - Demo chat widget (neuronDemoToggle/Send/Reset)
src/js/checkout-auth.el - Checkout auth: OAuth, email sign-in/up
src/js/checkout-free.el - Free plan: auth-badge watch -> payment reveal
src/js/checkout-stripe.el - Stripe Payment Element (reads NEURON_CFG)
src/js/enterprise.el - Enterprise inquiry form + headcount filter
src/js/environmental.el - Efficiency calculator slider
src/js/gallery.el - Gallery nav, search/sort, Supabase voting
src/js/main.el - Share page voting + copyForPlatform
src/js/marketplace.el - Developer interest form
src/js/nav.el - Nav hamburger + Mission dropdown
src/js/styles.el - Landing: nav scroll, reveal, founding counter
- deploy.yaml: restore elc from GCS (gs://neuron-ci-cache) keyed on
source SHA; only compile on cache miss, then upload for future runs
- Dockerfile.stage: pre-compile el_runtime.o as its own layer so the
expensive object is cached when only main.c changes between runs
- build-stage.sh: add --cache-from/--cache-to pointing at Artifact
Registry so apt-get + compilation layers survive across cold builds
Build pipeline
- build-stage.sh replaces the old in-Dockerfile bootstrap.py path. Host
pre-compiles src/*.el into dist/main.c via the canonical native elc at
foundation/el/dist/platform/elc and applies the stub-decl sed before
docker buildx runs.
- Dockerfile.stage drops bootstrap.py + python3 from the builder stage
and just runs cc on the host-supplied dist/main.c.
- Pre-rendered HTML shells under /srv/landing/ are now chowned to the
landing user so the El page-builder's fs_write at startup can rewrite
them — without that, post-COPY edits never reach the served HTML and
the served page stays as the stale build-time fallback.
Chat restore
- session.verified + session.verifiedAt persist through localStorage so
a return visit within 24h skips the Turnstile gate and lands directly
in the restored conversation.
- restoreOrGreet() is the single source of truth for what shows up in
the message pane after the gate clears: replays prior messages with
skipSave, else drops the canned hello once and remembers it.
- applyVerifiedDom() hides the gate / reveals the chat row, called both
from the verified-on-load path (DOMContentLoaded if loading, else
immediate) and from the Turnstile callback.
- neuronDemoReset clears verified + verifiedAt so the gate returns next
open.
Extracted JS assets (src/assets/js/*.js + manifest.json) and the
extract-js.py helper land here too — they match what the new build-stage
flow produces and removes the inline <script> blobs from the served HTML.
build-local.sh is no longer needed - bootstrap.py resolves imports
natively now. Dockerfile.stage already runs bootstrap.py on
dist/main-combined.el; the next image rev will switch to running
it directly on src/main.el.
Also: COPY src/llms.txt + the four prerendered HTML shells (about /
terms / enterprise-terms / index) into /srv/landing. The El handler
does fs_read(src_dir + "/llms.txt") which returned empty because the
file didn't exist in the container.
The /account "Loading..." spinner stayed on forever because the
browser-side waitlist read went through the anon key and didn't reach
the row. Replaced it with a POST /api/my-plan: the server verifies
the user's access_token via Supabase /auth/v1/user, then reads the
waitlist row with the service key. Bypasses RLS without exposing the
service key to the browser.
Stripped implementation comments from the served JS so the browser
doesn't broadcast how internals are shaped.
Build pipeline: declared the supabase_auth_user stub for both
build-local.sh and Dockerfile.stage so the bootstrap-injected forward
declarations match what's actually linked.
The auth-first flow blocked Stripe Elements from initialising for any
visitor without an existing Supabase session. Users hit the checkout
page, saw "Sign in to continue", and could not get to a card field at
all. Restored the inline-JS path (HEAD before extraction broke it),
flipped payment-section visible by default, kept the sign-in panel
behind an explicit "Already have an account? Sign in" link.
Build pipeline: added supabase_get stub injection and -lssl/-lcrypto
linker flags (web_stubs.c uses EVP for the AES-256-GCM transport).
Without those the Docker build aborts at link time.