The published Terms directly contradicted two locked safety decisions and the
shipping app copy, and the in-app Privacy Policy link 404'd. Both are legal
exposure under CA SB 243 (companion chatbots) and the AI-companion litigation.
Terms (§1, §11):
- Add an explicit "you must be 18 or older" eligibility statement. The old §11
permitted "Children 13 and over" independently and under-13 via family
accounts — the exact minor path the app's age gate forbids and the litigation
epicenter targets.
- Replace §11 "Children and Family Accounts" with an honest "Safety Features"
section. Removes the Hard Bell auto-notify block ("emergency services and
trusted contacts are notified first", "cannot be changed") and the
mandatory-reporting clause ("emergency services or relevant authorities may
be contacted", "cannot opt out") — both promised an automatic escalation the
product does not perform. New copy matches the app: Neuron shows you 988 and
how to reach a contact you chose; it never contacts anyone on your behalf;
there is no automatic escalation.
Privacy Policy (new):
- Add src/privacy.el + register it in main.el (generation, /legal/privacy route,
state pointer, sitemap). Fixes the in-app link, which pointed at
/legal/privacy (404 — no route, no doc existed).
- Ported from docs/legal/privacy-policy-companion-DRAFT.md (2026-07-14):
local-first, 18+, and never-auto-contact stated verbatim from the locked draft.
- Wire privacy.html through Dockerfile.stage / Dockerfile / deploy.yaml; add the
secondary /legal/privacy route to server.el for parity.
PENDING ATTORNEY (Daniel) SIGN-OFF before merge/publish. Bracketed legal
decisions (governing law, effective date, retention periods) left for counsel.
Audit found two website claims with no backing implementation:
- '3 marketplace plugins included' (free tier) -> 'Plugin marketplace access
(coming soon)'. The marketplace is not built; there are no free plugins.
- 'Connectors - day one' -> 'Connectors (rolling out)'. Only a few connectors
are available; Slack is coming-soon, Gmail/Drive not yet shipped.
String-content only; elc syntax-checks clean. Three other audit flags (no-card
wording, crisis-line copy, post-quantum 'everything encrypted') intentionally
NOT touched - left for human review (sensitive / conflicting evidence).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
Each provider row now has a collapsible details panel with accurate
step-by-step instructions and a direct link to the key creation page.
Includes billing notes for OpenAI and Anthropic (easy to miss gotchas),
free tier note for Gemini, and credits note for Grok.
All three pricing CTA buttons now share the same solid navy background,
white text, and blue hover state. Previously only anchor-element rules
existed for the solid variant; the button elements had no explicit
background so all three appeared unstyled.
chat-widget.el: apostrophe in El native_js double-quoted strings caused
the El compiler to drop the backslash, producing broken JS single-quoted
strings. Switched those four string literals to double-quoted JS strings
using \" escaping so the compiled output is valid.
main.el: /api/supabase-config was returning 403 for all stage Cloud Run
origins. Added marketing-stage-* prefix to the allowed list so the
checkout page can initialise Supabase during CI E2E runs.
El's native_js() compiles to eval(). checkout-auth.el uses native_js()
to embed the auth logic, so all window globals (showSignIn, initStripe,
etc.) live inside an eval call. Stage CSP was blocking it, leaving the
page with no auth functions defined.
Stage CSP blocks 'unsafe-eval' which javascript-obfuscator introduces.
checkout-auth.js IIFE was crashing before assigning window globals,
causing all checkout E2E tests to fail.