Commit Graph

282 Commits

Author SHA1 Message Date
will.anderson 69f348d48b Fix free plan checkout: use SetupIntent instead of $0 PaymentIntent
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m40s
Stripe rejects amount=0 PaymentIntents. Free plan age verification should
use a SetupIntent (no charge, saves payment method). The JS already handles
setup_mode:true by calling stripe.confirmSetup instead of confirmPayment.
Mirrors the existing professional-later SetupIntent path.
2026-05-13 12:12:10 -05:00
will.anderson 675c467a74 Fix about page rendering: restore raw string syntax to fix El tokenizer mangling
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m45s
The El HTML template parser (native { } syntax introduced in 5cb13d6) strips
spaces from text nodes, drops & from HTML entities (' → 39;), and breaks
hyphenated attribute names (aria-label → aria - label). All other component
files were already converted to the extern el_*() function style in 2553a6b
which is immune to this issue. about.el was the only page still using the
broken template syntax. Restoring the raw string return style fixes all
rendering defects on /about.
2026-05-13 11:46:13 -05:00
will.anderson b6aecd7d89 Fix magic-link sign-in: implicit flow + redirect to /account
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m42s
account-auth.el was using flowType:'pkce' while account-dashboard.el
uses 'implicit'. After the OTP redirect, the dashboard's implicit
client couldn't exchange the PKCE code — so the sign-in silently
failed. Fix: match implicit flow across both clients.

Also adds emailRedirectTo so the link lands on /account instead of
the site root.
2026-05-12 14:32:39 -05:00
will.anderson 0fdbba82e0 Fix duplicate Stripe customers and attestation plan bypass
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m29s
Two bugs:

1. Double-Bearer auth on Stripe customer search. Both checkout paths
   were passing "Bearer sk_..." to http_get_auth(), which prepends
   another "Bearer " — producing "Bearer Bearer sk_..." which Stripe
   rejects as 401. Customer lookup always failed, so a new Stripe
   customer was created on every checkout page load. Fix: pass the
   raw key to http_get_auth(), letting it handle the prefix.

2. /api/attest blindly wrote whatever plan the client submitted to
   the waitlist, letting anyone POST plan=founding and get founding
   member access without paying. Fix: server ignores the client-
   submitted plan and always writes plan=waitlist. Founding access
   requires Stripe payment — the attestation form is waitlist-only.
2026-05-12 14:10:04 -05:00
will.anderson 9e0451be41 Merge pull request 'Fix initStripe load order, subscription webhook email, chat textarea' (#134) from fix/webhook-initstripe-textarea into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m11s
Merge PR #134: Fix initStripe load order, webhook user_metadata, chat textarea
2026-05-12 17:49:26 +00:00
will.anderson 99ed8b85f7 Fix webhook failing to update plan for pre-existing Supabase users
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m17s
supabase_admin_invite re-sends a magic link for users who already have
an account (e.g. signed up via attestation before paying) but does not
touch their user_metadata — leaving plan as "free" after purchase.

Fix: add supabase_admin_update_user (PUT /auth/v1/admin/users/{id})
and call it after every invite so user_metadata is always stamped with
the correct plan, name, and stripe_customer_id. Idempotent for new and
returning users.

Also fix waitlist_upsert to use on_conflict=email,plan so the upsert
works for users who already have a waitlist row from attestation,
rather than silently failing on duplicate key.
2026-05-12 12:31:45 -05:00
will.anderson c72127032e Fix initStripe load order, subscription webhook email extraction, chat textarea UX
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m18s
- checkout.el: swap stripe_el_script before auth_script so initStripe is
  defined when Supabase auth fires onAuthStateChange on page load
- main.el: fix Stripe webhook email extraction for checkout.session.completed
  (subscription) events — customer_details is nested at data.object level,
  not at root; previous code only worked for payment_intent.succeeded
- page_close.c: replace <input type="text"> with <textarea rows="1"> in
  the chat widget input row so long questions are visible as you type
- page_css.c: update #neuron-demo-text CSS for textarea (resize:none,
  overflow:hidden, min/max-height, align-items:flex-end on row)
- chat-widget.el: add auto-resize event listener (grows up to ~4 lines),
  reset height to auto on send
2026-05-12 12:22:59 -05:00
will.anderson 869dcec0bb Merge pull request 'Fix intro greeting and load history on return visits' (#132) from fix/greeting-history-load into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m8s
2026-05-12 14:03:11 +00:00
will.anderson 1786aeeff6 Fix intro greeting tone and load history on return visits
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m36s
- soul-demo.c: rewrite intro system prompt — remove 'to see if you are real'
  and 'say something true about who you are' which were producing alive/sentient
  language. New prompt: friendly hello, ask how they're doing, explicitly no
  alive/sentient/experiencing anything lines.
- chat-widget: Turnstile callback now replays existing session history instead
  of always firing a new greeting — returning users within the same day see
  their conversation, not a duplicate hello.
2026-05-12 09:02:55 -05:00
will.anderson e938cb69fc Merge pull request 'Fix question counter, rate-limit timer, admin reset API, pricing clarity' (#130) from fix/question-timer-pricing-clarity into dev
Dev — Build & local smoke test / build-smoke (push) Failing after 1m12s
2026-05-12 13:53:04 +00:00
will.anderson 4f6df973cb Fix question counter daily reset, rate-limit timer, and founding member pricing clarity
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 2m0s
- chat-widget: session.count now resets on new UTC day (keeps client in sync with server's daily quota reset)
- chat-widget: fix rate-limit timer — wrong element IDs (neuron-demo-msgs → neuron-demo-messages) and wrong class (.neuron-msg-ai → .demo-msg-ai) meant the countdown never updated
- chat-widget: remove btn.disabled=false that immediately re-enabled the send button after rate-limiting
- main.el: add POST /api/admin/reset-rate-limits endpoint (requires NEURON_ADMIN_TOKEN, deletes all demo_rate_limits rows)
- pricing.el: clarify founding member card — software updates are free forever, inference is pay-per-use at founding member rate
2026-05-12 08:52:34 -05:00
will.anderson be849c608e Merge pull request 'fix: binary asset serving + checkout centering' (#128) from fix/binary-assets-checkout-layout into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m7s
2026-05-12 01:10:58 +00:00
will.anderson 5ce5f4a8be fix: binary asset serving + checkout centering
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m34s
el_runtime: http_response() JSON-encoded the body via jb_emit_escaped(),
which stops at the first null byte. PNG/binary files contain null bytes
at byte 8 (IHDR chunk length), so only 8 bytes were served — browsers
received a corrupt/truncated image and showed broken icons.

Fix: when _tl_fs_read_len > 0 (binary fs_read), copy raw bytes into a
thread-local side-channel (_tl_binary_body/_tl_binary_size) and write
the sentinel "__el_binary__" into the envelope body field. http_send_response()
detects the sentinel and substitutes the real bytes for sending.

checkout.el: .checkout-shell, .checkout-summary, and .checkout-form-wrap
had no CSS, leaving the page left-aligned and single-column. Added grid
layout (2-col desktop, 1-col mobile), max-width centering, and sticky
order summary.
2026-05-11 20:10:19 -05:00
will.anderson 6e425da63e Merge pull request 'fix: remove setup_future_usage from $0 PaymentIntent' (#127) from fix/zero-pi-setup-future-usage into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m2s
2026-05-12 00:55:56 +00:00
will.anderson 37c7dca30d Fix $0 PaymentIntent: remove setup_future_usage (invalid with amount=0)
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m32s
2026-05-11 19:55:44 -05:00
will.anderson 73c435eb90 Merge pull request 'fix: free plan $0 PaymentIntent for age verification' (#125) from fix/free-plan-payment-intent into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m14s
2026-05-12 00:46:01 +00:00
will.anderson 7be2b49300 Free plan: use $0 PaymentIntent instead of SetupIntent for age verification
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m45s
All plans now use the same payment intent flow. Free creates a $0 PI
with payment_method_types[]=card and setup_future_usage=off_session.
No charge, card saved. Removes setup_mode=true for free plan.

Fix submit button label: show 'Verify age & get started' for free
instead of 'Complete purchase'. Retire checkout-free.el.
2026-05-11 19:45:39 -05:00
will.anderson e5c05cbece Merge branch 'dev' of git.neuralplatform.ai:neuron-technologies/neuron-web into dev 2026-05-11 19:16:18 -05:00
will.anderson c7f4d0248c Merge pull request 'fix: free checkout Stripe SetupIntent + remove no-card-required copy' (#123) from fix/free-checkout-stripe into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m14s
2026-05-12 00:16:12 +00:00
will.anderson 4c5d67c321 fix: free checkout requires Stripe SetupIntent for age verification; update copy
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m43s
2026-05-11 19:15:57 -05:00
will.anderson 9feb9e24b6 Merge branch 'dev' of git.neuralplatform.ai:neuron-technologies/neuron-web into dev 2026-05-11 18:56:43 -05:00
will.anderson 941faccb3f Merge pull request 'fix: force full build when no diff or stage-latest missing' (#121) from fix/stage-full-build into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m16s
2026-05-11 23:56:35 +00:00
will.anderson 6a040afcc5 fix: force full build when no diff or stage-latest missing
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m43s
2026-05-11 18:56:18 -05:00
will.anderson a346a2197e Merge branch 'dev' of git.neuralplatform.ai:neuron-technologies/neuron-web into dev 2026-05-11 18:46:33 -05:00
will.anderson e268b424f5 Merge pull request 'ci: touch dist to trigger stage rebuild' (#119) from ci/touch-dist into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m2s
2026-05-11 23:46:17 +00:00
will.anderson 20029d36df ci: touch dist to trigger stage rebuild
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m35s
2026-05-11 18:45:57 -05:00
will.anderson 54d48ed679 ci: trigger rebuild after registry cleanup 2026-05-11 17:33:53 -05:00
will.anderson 28f9ecd1a3 Merge pull request 'fix: heading and button elements pass children unescaped' (#113) from fix/force-full-rebuild into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m0s
2026-05-11 22:21:41 +00:00
will.anderson b6bb25e79e fix: heading and button elements pass children unescaped
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m32s
el_h1/h2/h3/h4 and el_button were calling el_escape() on their
content, converting any HTML children (e.g. <span class="gold">)
into literal entity text on screen.

These functions accept composed HTML children, not raw text — they
should pass the argument through like el_div/el_p/el_span do.
el_text, el_attr, el_title, el_textarea, and el_img keep escaping
(they handle actual text/attribute values, not HTML children).
2026-05-11 17:21:19 -05:00
will.anderson 5812cb0452 Merge pull request 'Force full El rebuild — strip CGI content from base image' (#110) from fix/force-full-rebuild into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m13s
2026-05-11 21:43:09 +00:00
will.anderson c99923da1b Force full El rebuild — strip CGI content from base image
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m39s
2026-05-11 16:42:41 -05:00
will.anderson 4e35cbe841 Merge pull request 'Also skip El rebuild for workflow-only changes' (#107) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m52s
2026-05-11 20:46:51 +00:00
will.anderson 62385b53c2 Also skip El rebuild for .gitea/ workflow-only changes
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 2m16s
Workflow file changes don't require rebuilding the El binary. Without
this, merging workflow fixes to main triggers a full El build which
hits a codegen issue in the CI version of elb.
2026-05-11 15:46:37 -05:00
will.anderson 952b03737b Merge pull request 'Skip El rebuild for migration/script/test-only changes' (#105) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m54s
2026-05-11 20:45:14 +00:00
will.anderson d2628ec42e Skip El rebuild for migration/script/test-only changes
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 2m17s
migrations/, scripts/, tests/ changes don't require rebuilding the El
binary. Classifying them as asset-only avoids spurious full builds that
regenerate dist/*.c and can hit codegen incompatibilities.
2026-05-11 15:44:59 -05:00
will.anderson d598fb7b10 Merge pull request 'Update CORS test: no-Origin requests are allowed' (#103) from fix/stage-ci-paths into dev 2026-05-11 20:22:30 +00:00
will.anderson 1eeb8df04b Update CORS test: no-Origin requests are allowed (same-origin fix)
Same-origin browser fetches don't send Origin. The server correctly
allows them — blocking was the bug that broke checkout. Update the
test to match the fixed behavior.
2026-05-11 15:22:22 -05:00
will.anderson 9e5d7e55ab Merge pull request 'Fix stage source guard: fetch origin/dev before ancestry check' (#101) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m48s
2026-05-11 19:09:33 +00:00
will.anderson 5d3b1a3e20 Fix stage source guard: fetch origin/dev before ancestry check
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 2m22s
The shallow clone (fetch-depth: 2) doesn't include origin/dev, so
git merge-base --is-ancestor was silently failing. Fetch dev with
depth=1 first so custom merge commit titles still pass the check.
2026-05-11 14:09:18 -05:00
will.anderson 1264e32577 Fix: idempotent migration policy creation 2026-05-11 18:56:36 +00:00
will.anderson 7f88414b40 Make migration policy creation idempotent
DROP POLICY IF EXISTS before CREATE POLICY so migrations can be
re-applied to a DB that already has the policy (e.g. demo_config
was manually applied before migration tracking was set up).
2026-05-11 13:56:12 -05:00
will.anderson b3ce6c3e64 Merge pull request 'Fix CI migration step: script file instead of heredoc' (#97) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m3s
Merge PR #97
2026-05-11 18:34:01 +00:00
will.anderson adbdfd3e90 Fix CI migration step: extract Python to scripts/run_migrations.py
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m36s
go-yaml (Gitea's parser) mishandles << inside block scalars, treating the
bash heredoc delimiter as a YAML merge key. Move the migration logic to a
standalone script called via python3 scripts/run_migrations.py.
2026-05-11 13:33:44 -05:00
will.anderson dd5fd2b3ce Merge pull request 'Fix supabase-config CORS: treat absent Origin as allowed' (#95) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 1m59s
Merge PR #95
2026-05-11 18:30:44 +00:00
will.anderson 617916134f Fix supabase-config CORS: treat absent Origin header as allowed
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m30s
map_get returns null (0) for missing headers. str_eq(null, "") is false
because EL_CSTR(0) is NULL != "". Same-origin browser fetches don't send
Origin at all, so the missing-origin case was incorrectly being denied.

Fix: use str_starts_with(req_origin, "http") to detect a present origin.
If no origin header (null first arg → str_starts_with returns false),
origin_present is false and the request is allowed unconditionally.
2026-05-11 13:30:22 -05:00
will.anderson 924c0804e7 Merge pull request 'Wire Supabase migrations into CI/CD' (#93) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m0s
Merge PR #93
2026-05-11 18:22:01 +00:00
will.anderson 4a915c1a11 Wire Supabase migrations into CI/CD
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m30s
Adds a "Run database migrations" step to both stage.yaml and deploy.yaml.
Uses the Supabase Management API (access token from GCP Secret Manager)
to apply pending migrations tracked in a schema_migrations table.
Migrations run unconditionally before every deploy — asset-only or full.

Also adds migrations/** to paths filter so a migrations-only commit
triggers the pipeline.
2026-05-11 13:21:42 -05:00
will.anderson 4a3ede98f7 Merge pull request 'Stage: pricing buttons, API keys, reasoning note, enterprise contacts' (#91) from fix/stage-ci-paths into dev
Dev — Build & local smoke test / build-smoke (push) Successful in 2m19s
Merge PR #91: dev stage batch
2026-05-11 18:05:33 +00:00
will.anderson a6b75b9abf Add direct sales and security contact block to enterprise section
Dev — Build & local smoke test / build-smoke (pull_request) Successful in 1m45s
Two-card grid above the enterprise box — sales (enterprise@) and
security (security@) — with email links and one-line descriptions.
Visible without filling out the form, which is what enterprise and
security teams look for first.
2026-05-11 12:58:25 -05:00
will.anderson 21a7c07547 Add reasoning model recommendation to API Keys card
Callout above the provider list recommends o4-mini/o3, Claude Sonnet 4,
Gemini 2.5 Pro, or Grok-3 for best performance, notes that model choice
happens in the app, and points to Neuron Inference launching Q3 2026.
2026-05-11 12:54:28 -05:00