Skip to content

ci(netlify): build @coasys/ad4m from source (dev branch) - #194

Merged
jhweir merged 5 commits into
devfrom
ci/netlify-ad4m-dev
Sep 9, 2026
Merged

ci(netlify): build @coasys/ad4m from source (dev branch)#194
jhweir merged 5 commits into
devfrom
ci/netlify-ad4m-dev

Conversation

@HexaField

@HexaField HexaField commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

ci(netlify): build @coasys/ad4m from source on deploy previews

What

Netlify deploy previews can clone an ad4m branch, build the SDK from source, and link it into
the WE workspace before the normal build. The preview site picks up SDK work — batch RPC endpoints,
performance fixes, new handlers — without waiting for a new pre-release tag. Which ad4m branch is
decided per pull request; left alone it tracks dev.

The published site is unaffected and keeps installing the version pnpm.overrides pins.

Why

WE's pnpm override pins a published pre-release tag (0.13.0-test-model-layer). New SDK features
land on ad4m's dev branch well before a tag gets cut, and the tag only moves when somebody
hand-publishes one from an ad4m commit. The gap means the preview site runs stale SDK code even
when the executor already supports the new endpoints.

At time of writing ad4m dev is 19 commits ahead of 54a3fd956, the commit the current pin was
published from, with nothing behind — so this is the ordinary state of things rather than a moment
that happened to be bad.

Concrete example: PerspectiveProxy.subjectClassTargetClasses() (ad4m#979, merged 8 Sept) is a
single-call presence check for a set of models, replacing one queryLinks per model — ~4.5s on a
16-model space switch against a 300ms-RTT executor. It is in those 19 commits and not in the pin.
That is the same ground #190 covered from this side.

Correction to the original description: getAllShacl() was the example given, and it is
already in the pinned tag as a single perspective.getAllShacl RPC — which is why
getForeignShacl typechecks on dev. The argument holds; the method named was the wrong one.

How

  • scripts/netlify-build.sh — shallow-clones ad4m, builds @coasys/ad4m from source (3 deps,
    fast), rewrites the pnpm override to link: the local build, then runs pnpm build.
  • netlify.toml — points [context.deploy-preview] at the script. [build] keeps
    pnpm build, so production, branch deploys and every other context stay on the pin.
  • Which ad4m, in the order the answers are consulted:
    1. AD4M_BRANCH in the Netlify UI — site-wide, overrides everything below.
    2. A preview:ad4m@<ref> label on the pull request. preview:ad4m@pin means "use the pinned
      version", for a branch whose whole point is that pin.
    3. An ad4m branch of the same name as the WE branch — the cross-repo case.
    4. dev.
  • The build says what it built: the resolved ref and SHA are written to /build-info.json and
    announced in the console, plus a we:ad4m-build meta tag.
  • GitHub Actions CI stays untouched — it keeps building against the published SDK version.

Why the context scoping

[build] covers every deploy context, so as originally written the change governed the published
site too. The pin is a claim about which executor a build requires — VERIFIED_AGAINST_AD4M in
@we/backend-ad4m names both a version and an ad4m commit, and the Rust half is published nowhere
at all — and the published site has to keep making that claim, because the people opening it are
running whatever executor they installed. A preview is the opposite case: it is tried against an
executor somebody has in front of them, which during cross-repo work is a build rather than a
release.

Why per-PR rather than hardcoded dev

Hardcoding the branch reproduces, on the preview surface, what 23927712 took out of CI in August:
an artefact defined by whatever another repository held at the moment the job ran. Same-name branch
matching is revived from the old build-with-ad4m-link.sh, and is what ad4m-compat.yaml still
does.

A label rather than a committed marker file because a label cannot merge. A file saying "build
from dev" would land on dev and go on governing every later preview until somebody noticed.

The branch name is read from the pull request rather than from BRANCH, which Netlify sets to
pull/N/head on a preview — one request, and the labels come back with it.

Test plan

  • Deploy preview builds green with the script running the source path end to end.
  • /build-info.json on the preview reports "ad4mSource": "source", "ad4mRef": "dev",
    "ad4mSha": "cf1f69597…" — the #979 merge commit.
  • The we:ad4m-build meta tag and console line are present in the served page.
  • Ref resolution exercised for all four answers, including REVIEW_ID=194 against the live
    GitHub API (resolves ci/netlify-ad4m-dev, no label, no matching ad4m branch → dev).
  • Build-info injection tested for the source path, the pin path, and a missing dist.
  • bash -n clean. Prettier does not parse .toml or .sh, so format:check skips both.
  • preview:ad4m@pin — the path a deploy preview cannot exercise on its own. Tested by labelling
    the PR and running the script locally with REVIEW_ID=194, so the label was read off the live
    API: resolves to pin, skips the clone (/tmp/ad4m-sdk never created), builds against
    @coasys+ad4m@0.13.0-test-model-layer (named in the bundler's own module resolution), reports
    "ad4mSource": "pin" with a null SHA, and leaves the working tree clean — the pin path does
    not rewrite package.json, so it cannot trip the build-drift gate. Label since removed.

Known follow-ups

  • link: is kept rather than file:. The linked package's deps were installed in place by npm;
    file: would have pnpm re-resolve them against WE's lockfile.
  • No caching. Every preview build re-clones and re-compiles ad4m core; ad4m-compat.yaml caches on
    the ad4m SHA and this could too.
  • The executor half is still unaddressed. This picks an SDK; the pin also names an ad4m commit for
    the Rust side, and nothing here builds or checks one.

🤖 Generated with Claude Code

Netlify previews now clone ad4m's dev branch, build the SDK from source,
and link it into the WE workspace before the normal build runs. This
closes the gap between SDK features landing on dev and a new pre-release
tag being published — the deployed preview site always picks up the
latest batch RPC endpoints and performance fixes.

GitHub Actions CI is unaffected and continues to build against the
published SDK version.

The branch defaults to dev and can be overridden via the AD4M_BRANCH
env var in the Netlify UI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@HexaField
HexaField requested a review from jhweir as a code owner September 9, 2026 11:07
@netlify

netlify Bot commented Sep 9, 2026

Copy link
Copy Markdown

Deploy Preview for coasys-we ready!

Name Link
🔨 Latest commit c0fce79
🔍 Latest deploy log https://app.netlify.com/projects/coasys-we/deploys/6aa1bf254bb23c000893a4fb
😎 Deploy Preview https://deploy-preview-194--coasys-we.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

HexaField and others added 4 commits September 9, 2026 21:12
- Capture WE_ROOT from PWD instead of NETLIFY_BUILD_BASE (not reliably
  set, caused immediate failure with set -u).
- Use npm instead of pnpm for ad4m core — avoids pnpm 9 vs 10 version
  conflict and workspace install issues.
- npm install --ignore-scripts skips postinstall hooks that assume the
  full workspace; npx runs patch-package/tsc/rollup directly.
- Drop ::group:: markers (GitHub Actions syntax, not Netlify).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
npm walks up from core/ and finds the root package.json with a
workspaces field, then tries to resolve all sibling packages — including
ones that use pnpm's workspace:* protocol. Remove the root package.json
and pnpm-workspace.yaml so npm treats core/ as a standalone package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The preview site had no way to exercise SDK work that has landed on an ad4m
branch but not in a published tag, and the gap is real rather than theoretical:
ad4m `dev` is 19 commits ahead of 54a3fd956, the commit 0.13.0-test-model-layer
was hand-published from, and one of those commits is #979's
`subjectClassTargetClasses()` — a bulk presence check that replaces one
`queryLinks` per model during a space switch, which is the same ground #190
covered from this side. A pin only moves when somebody publishes a tag by hand,
so the lag is structural.

Two things follow from closing it in the build command rather than the manifest.

The command moves to `[context.deploy-preview]`. `[build]` covers every context,
so as written it governed the published site too — and the published site must
keep installing what the repo pins, because the pin is a claim about the
executor its readers are running: VERIFIED_AGAINST_AD4M names a version and an
ad4m commit, and the Rust half is published nowhere at all. A preview is the
opposite case, tried against whatever executor is in front of the person
testing, which during cross-repo work is a build rather than a release.

And which ad4m a preview gets becomes a per-PR answer instead of always `dev`.
Hardcoding the branch reproduces, on the preview surface, what 2392771 took out
of CI in August: an artefact defined by whatever another repository held at the
moment the job ran. Four answers, in order — AD4M_BRANCH in the Netlify UI, a
`preview:ad4m@<ref>` label on the pull request, an ad4m branch of the same name
as the WE branch, then `dev`. `preview:ad4m@pin` is the way for a branch whose
point is the pin to say so. A label rather than a committed marker because a
label cannot merge: a file saying "build from dev" would land on `dev` and go on
governing every later preview until somebody noticed. Same-name matching is
revived from build-with-ad4m-link.sh, and is what ad4m-compat.yaml still does.

The branch name is read from the pull request rather than from BRANCH, which is
`pull/N/head` on a preview — one request, and the labels come back with it.

Last, the build says what it built. A page made from "dev at the time" is
unexplainable a week later, and somebody hitting an RPC failure against a
released executor has no way to tell that from a WE bug. The resolved ref and
SHA are written to /build-info.json and announced in the console.

Nothing here reaches the required build, which still installs the lockfile and
stops.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jhweir jhweir added preview:ad4m@pin Deploy preview: build against the pinned @coasys/ad4m instead of a branch and removed preview:ad4m@pin Deploy preview: build against the pinned @coasys/ad4m instead of a branch labels Sep 9, 2026
@jhweir
jhweir merged commit 821ef76 into dev Sep 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants