elb's flag_val only matches --key=value, not --key value.
All three workflows were passing flags space-separated which
elb silently ignored, causing 'cannot locate el_runtime.c'.
- stage.yaml and deploy.yaml now extract El SDK from ci-base (docker cp /opt/el) and run elb build to produce dist/neuron-landing
- JS El sources compiled via elc --target=js in a dedicated step, matching dev.yaml exactly
- build-stage.sh replaced with thin elb wrapper for local dev use
- Removes the broken "Set up El SDK" stub (echo EL_HOME) and old build-stage.sh invocation from both workflows
elb compiles each .el source independently (per-file codegen),
avoiding the exponential memory growth from concatenating all sources
into main-combined.el and feeding it to elc in one shot.
- dev.yaml: replace build-stage.sh with elb build + per-file JS elc
- Dockerfile.stage: COPY dist/neuron-landing (elb binary) directly
instead of compiling from pre-generated main.c. soul-demo stays as
cc compilation (small file, no risk).
The El repo only has a darwin arm64 elc binary. The v1.2.1 linux
binary predates native HTML template syntax. Compile elc.c (the
committed C source of the El compiler) on linux/amd64 in CI to
get a native binary that supports the new syntax.
v1.2.1 elc (282KB) cannot compile native HTML template syntax
introduced in feat/native-el-templates. Clone El repo depth=1
to get the latest elc (486KB) that supports it. Set EL_HOME
to lang/ subdir.
El repo is organized under lang/ — runtime and dist/platform binaries
are at lang/el-compiler/runtime/ and lang/dist/platform/, not at root.
Setting EL_HOME=$DEST/lang makes RUNTIME_SRC resolve correctly so
build-stage.sh can cp el_runtime.{c,h,js} from the right location.
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
- Gitea branch protection enabled on stage and main:
- Direct pushes disabled (non-admin)
- stage requires "Dev — Build & local smoke test / build-smoke" to pass
- main requires "Stage — Build, push & deploy to marketing-stage / deploy-stage" to pass
- Enforcement step added to stage.yaml and deploy.yaml:
- stage only accepts merges from dev
- main only accepts merges from stage
- workflow_dispatch exempt (allows manual redeploy)
- Direct non-admin pushes are blocked at the Gitea layer before CI runs
- dev.yaml: build + local docker smoke test only (no push, no deploy)
- stage.yaml: build + push + deploy to marketing-stage + smoke test (stops here)
- deploy.yaml: add HTML placeholder touch step before docker build
Proper human gate between stage and prod: the stage→main merge decision.
CI: gitea runner ships docker without the buildx plugin, so
`docker buildx build --platform linux/amd64 --load` exits 125
("unknown flag: --platform") in both the full build (build-stage.sh)
and the asset-only fast path (deploy.yaml). Runner host is already
linux/amd64, so explicit --platform is redundant. Switch both call
sites to plain `docker build`. This unblocks the pipeline — every
run since the workflow was added (~26 runs) has failed at this
exact step.
Chat: the live chat bubble renders marked.parse() output via
innerHTML, but .demo-msg-ai .demo-msg-bubble only had CSS rules
for p/ul/ol/li/strong. Fenced code blocks rendered as <pre><code>
with no styling — they appeared as wrapped plaintext, hard to
distinguish from prose. Add rules for code (inline and block),
pre, blockquote, em, h1-h4, and a, mirroring the share-card
styling (which always had them) so chat code blocks finally get
the monospace + tinted-background treatment users expect.
Replaces the three-step apt install + GCS cache probe + gcc build sequence
with a single curl download of the pre-built binary. Eliminates build-time
C toolchain dependency and shaves ~2-3 minutes off every full build.
Nothing reaches prod without deploying to marketing-stage first and
passing a 90s HTTP smoke test. Stage uses test Stripe keys
(stripe-secret-key-stage) so checkout can be exercised safely.
Set STRIPE_PUBLISHABLE_KEY on the stage service manually once:
gcloud run services update marketing-stage --region us-central1 \
--project neuron-785695 \
--update-env-vars STRIPE_PUBLISHABLE_KEY=pk_test_...
- Detect asset-only changes (src/assets/, src/shares/, static HTML, llms.txt)
and skip El compilation, C build deps, and Docker full build entirely
- Fast path pulls :latest as base and rebuilds only the assets layer
- Gate clone-el, install-C-deps, elc-cache, build-elc, build-image, push-image
behind asset_only != 'true'; deploy steps run unconditionally
- Switch build-stage.sh from registry cache driver (requires docker-container
buildx driver) to inline cache backed by :latest — compatible with default
docker driver on the runner
- 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
elc-bootstrap.c isn't committed to engram-lang; the actual C source
is dist/platform/elc.c. Add libcurl4-openssl-dev/libssl-dev install
step so cc has the right headers.
The committed dist/platform/elc was an arm64 binary from the local dev
box; runner is linux/amd64 and got 'cannot execute binary file: Exec
format error'. Always rebuild.
Gitea Actions doesn't currently inject ACTIONS_ID_TOKEN_REQUEST_TOKEN /
ACTIONS_ID_TOKEN_REQUEST_URL into job env, so google-github-actions/auth
can't mint a federated token. The WIF infrastructure stays in Terraform
so we can flip back once that gap closes; the JSON key in GCP_SA_KEY is
the working path today.
act_runner v0.6 host-mode hits a 'permission denied' error on
.git/objects/pack/*.idx when running two checkout steps in the same
job. Drop down to a plain git clone of engram-lang and pin EL_HOME
outside the workspace.
The auto-issued GITHUB_TOKEN is scoped to the current repo only, so
cross-repo actions/checkout needs an explicit token. CHECKOUT_TOKEN
holds an admin-scoped Gitea API token; long-term we should switch to
a dedicated read-only PAT.
Push to main triggers build-stage.sh, push to Artifact Registry,
parallel deploy to all 3 marketing prod regions, traffic flip,
verify. Auth via Workload Identity Federation against the
Gitea OIDC provider — no long-lived keys on the runner.
Falls back to GCP_SA_KEY repo secret if WIF doesn't work end
to end against this Gitea instance.