Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

Commit 272ea03

Browse files
claude[bot]claude
andauthored
fix(live-e2e): derive the showcase-app commit from the release tag at boot (#8276)
`e2e/live/ci/backend.env` carried two pins that a header MUST said must agree: `OBJECTSTACK_VERSION` (checked against `pnpm-lock.yaml` since objectui#7689) and `OBJECTSTACK_REF`, a hand-moved sha whose stated rule — "always the commit the `@objectstack/cli@${OBJECTSTACK_VERSION}` release tag points at" — nothing could check. The file said so in as many words: "NOT machine-checked, and deliberately so", on the reasoning that resolving the tag needs the objectstack repository and the unit lane cannot reach it. True of the unit lane, and beside the point: the live lane clones that very repository to get the app. Retire the pin instead of adding a check nobody would read (the lane is `informational`). `start-backend.sh` now resolves the commit at boot from the release tag named by `OBJECTSTACK_VERSION` — `git ls-remote --tags`, taking the peeled `^{}` sha of the annotated tag — and refuses to start, naming the tag, when it does not resolve to a 40-character sha. `git ls-remote` reports a missing tag as empty output and exit 0, so that shape check is the guard, not a formality. The resolved sha stays in the reuse stamp so a re-pointed tag cannot be served from a cached fixture in a reused scratch dir. The pair the header describes then cannot disagree by construction: the sparse-checkout of `examples/app-showcase` and the installed published packages always come from the same release, and there is one value to move, not two. Tests: the `keeps OBJECTSTACK_REF in the one shape start-backend.sh can fetch` pin is retired deliberately — it asserted the shape of a value that no longer exists — and replaced by three pins that now hold: backend.env declares no `OBJECTSTACK_REF`; start-backend.sh still resolves the tag, still builds it from `OBJECTSTACK_VERSION`, and still refuses on a bad shape; and backend.env still explains the absence, so nobody restores the pin thinking it was dropped by accident. Prose updated in the CI/CD page, `better-auth-pin.mjs` and the live saved-view spec, all of which described a two-pin pair. Refs objectui#7964, objectui#7689. Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3c6394c commit 272ea03

6 files changed

Lines changed: 199 additions & 56 deletions

File tree

‎content/docs/guide/ci-cd-pipeline.md‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,15 @@ comment), so every promotion would stale a hand-copied enumeration here; it alre
420420
Backend pins live in `e2e/live/ci/backend.env` and must match the `@objectstack/spec` version in
421421
`pnpm-lock.yaml` — bump both in the same PR, or the run proves nothing.
422422

423-
That file carries a **third** pin, `BETTER_AUTH_VERSION`, and it is a different kind of thing:
423+
`OBJECTSTACK_VERSION` is the only `@objectstack` value that file declares, and moving it is the
424+
only move. The showcase-app commit the lane sparse-checks-out is **derived**, not pinned:
425+
`start-backend.sh` resolves the `@objectstack/cli@$OBJECTSTACK_VERSION` release tag at boot
426+
(`git ls-remote --tags`, peeled sha) and refuses to start when it does not resolve. It used to be
427+
a second key, `OBJECTSTACK_REF`, moved by hand under a MUST that nothing could check — the same
428+
shape as the version drift above, and retired for the same reason (objectui#7964). Deriving it
429+
means the app source and the published packages it runs on come from one release by construction.
430+
431+
That file carries a **second** pin, `BETTER_AUTH_VERSION`, and it is a different kind of thing:
424432
not a matched-pair pin but a workaround for a break inside the published packages themselves.
425433
`@objectstack/plugin-auth` imports `createLocalAccountIssuer` from `@better-auth/core/db` and
426434
declares `@better-auth/core` with a caret; `@better-auth/core@1.7.3` removed that export in a
@@ -431,7 +439,7 @@ its 300-second readiness timeout having run zero specs (objectstack#16186, objec
431439
pinning a dependency to turn a lane green is a gate weakening — `e2e/live/ci/better-auth-pin.mjs`
432440
runs on **every** start, cache hits included, and fails by name if the override was not declared,
433441
did not resolve, or resolved and still lacks the export. ⛔ It is not, and must not become, a
434-
repair of the two pins above: objectui#7689's triage forbids repairing this lane by moving those.
442+
repair of the version pin above: objectui#7689's triage forbids repairing this lane by moving it.
435443
Retire the pin and its guard together in the PR that bumps `OBJECTSTACK_VERSION` past the
436444
upstream fix.
437445

‎e2e/live/ci/backend.env‎

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,38 @@
1212
# had drifted two minor versions (17.0.0-rc.2 against a 17.2.0 lockfile)
1313
# before anything compared the two numbers — objectui#7689.
1414
#
15-
# OBJECTSTACK_REF — the objectstack-ai/objectstack commit the showcase app
16-
# metadata is checked out from. Always the commit the release tag
17-
# `@objectstack/cli@${OBJECTSTACK_VERSION}` points at, so the app source and
18-
# the published packages it runs on come from the same tree. (Mirror image
19-
# of the framework repo's `.objectui-sha` console pin.)
20-
# NOT machine-checked, and deliberately so: resolving that tag needs the
21-
# objectstack repository, which the unit lane cannot reach. Move this by
22-
# hand whenever the version above moves, from the tag's own commit.
15+
# This file declares exactly ONE @objectstack pin, and that is the whole
16+
# design: the commit is derived from it, see below.
2317
OBJECTSTACK_VERSION=17.3.0
24-
OBJECTSTACK_REF=8a1bad8b8ee7189a54229368400b5b427e3ad5e2
18+
19+
# There is deliberately no OBJECTSTACK_REF key here. The objectstack-ai/objectstack
20+
# commit the showcase app metadata is sparse-checked-out from is DERIVED at
21+
# boot by start-backend.sh, from the release tag
22+
# `@objectstack/cli@${OBJECTSTACK_VERSION}` — `git ls-remote --tags`, taking
23+
# the peeled `^{}` sha of the annotated tag — and the script refuses to start
24+
# when that tag does not resolve to a 40-character sha. The app source and the
25+
# published packages it runs on therefore come from the same release BY
26+
# CONSTRUCTION; there is no second value to move, so the two cannot disagree.
27+
# (Mirror image of the framework repo's `.objectui-sha` console pin.)
28+
#
29+
# It used to be a hand-moved sha here, carrying a stated MUST — "always the
30+
# commit the release tag points at" — that nothing could check, on the
31+
# reasoning that resolving the tag needs the objectstack repository and the
32+
# unit lane cannot reach it. True of the unit lane, and beside the point: the
33+
# live lane clones that very repository at boot to get the app, so the lane
34+
# that CONSUMES the value could always resolve it. objectui#7964 retired the
35+
# pin rather than add a check inside an `informational` lane nobody reads.
36+
#
37+
# ⚠️ One consequence, stated rather than papered over. The workflow caches the
38+
# prepared fixture under `live-backend-<os>-<hashFiles(backend.env)>`, and the
39+
# resolved sha is no longer in this file. A version bump still moves the key,
40+
# because the version is what the sha is derived from. But if the
41+
# `@objectstack/cli@${OBJECTSTACK_VERSION}` tag were ever re-pointed at another
42+
# commit after a fixture had been cached, that stale fixture would still be
43+
# served until OBJECTSTACK_VERSION itself moved. start-backend.sh's own stamp
44+
# carries the RESOLVED sha, so a re-point is caught in a reused scratch dir;
45+
# the Actions cache is the one path it is not caught on. Published release tags
46+
# are not expected to move, and moving one is the anomaly, not this lane.
2547

2648
# BETTER_AUTH_VERSION — the version the whole floating `better-auth` family is
2749
# pinned to in the backend's install path, via an npm `overrides` block that

‎e2e/live/ci/better-auth-pin.mjs‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@
3232
* binds the port, loads 42 plugins and prints `Server is ready` while auth is
3333
* absent (objectui#8084).
3434
*
35-
* ⛔ The pin is NOT a repair of `OBJECTSTACK_VERSION` / `OBJECTSTACK_REF`.
36-
* objectui#7689's triage forbids repairing this lane by moving those, and this
37-
* does not move them. It pins a TRANSITIVE dependency of the published artifact
35+
* ⛔ The pin is NOT a repair of `OBJECTSTACK_VERSION` (nor of the showcase-app
36+
* commit start-backend.sh derives from it — objectui#7964 retired the second,
37+
* hand-moved `OBJECTSTACK_REF` key that used to sit beside it).
38+
* objectui#7689's triage forbids repairing this lane by moving that, and this
39+
* does not move it. It pins a TRANSITIVE dependency of the published artifact
3840
* to the version that artifact's own manifest was authored against, restoring
3941
* the resolution the publisher intended. When objectstack#16186 lands upstream
4042
* — plugin-auth pinning `@better-auth/core` itself, or moving off the removed

‎e2e/live/ci/start-backend.sh‎

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
# @objectstack/* packages, serving the showcase app the live specs target.
44
#
55
# What it does (idempotent; a stamp file skips prepare when pins are unchanged):
6-
# 1. Sparse-checkout `examples/app-showcase` from objectstack-ai/objectstack
7-
# at the pinned OBJECTSTACK_REF (backend.env).
6+
# 1. Resolve the `@objectstack/cli@$OBJECTSTACK_VERSION` release tag to a
7+
# commit, and sparse-checkout `examples/app-showcase` from
8+
# objectstack-ai/objectstack at it. The commit is DERIVED, never pinned:
9+
# see the note above OBJECTSTACK_VERSION in backend.env (objectui#7964).
810
# 2. Rewrite its package.json: every `@objectstack/*` workspace dep -> the
911
# pinned published OBJECTSTACK_VERSION; dev-only tooling dropped.
1012
# 3. `npm install` (published tarballs only — nothing is built from source,
@@ -37,10 +39,53 @@ STAMP="$BACKEND_DIR/.prepared"
3739
# floating family is the exact outcome this pin exists to prevent, and it costs
3840
# 300 seconds to discover downstream (objectstack#16186).
3941
: "${BETTER_AUTH_VERSION:?backend.env must declare BETTER_AUTH_VERSION — see its header and objectstack#16186}"
40-
# The pin is IN the stamp: without it, a fixture prepared before a pin change
41-
# satisfies the reuse test and the new pin is never installed. The workflow's
42-
# fixture cache key is the hash of backend.env, which carries the pin for the
43-
# same reason.
42+
43+
# The showcase-app commit is DERIVED here, and is declared nowhere: it is
44+
# whatever the `@objectstack/cli@$OBJECTSTACK_VERSION` release tag points at, so
45+
# the metadata checked out below and the published packages installed on top of
46+
# it cannot come from different trees. backend.env used to carry it as a second,
47+
# hand-moved pin whose stated MUST ("always the commit the release tag points
48+
# at") nothing could check — and a pair that can disagree eventually does
49+
# (objectui#7964, objectui#7689 for the version half).
50+
#
51+
# Annotated tags resolve in two lines: the tag object, then the peeled `^{}`
52+
# commit. Take the peeled sha when present, else the tag's own (lightweight tags
53+
# do not peel). Both patterns are passed explicitly — asking for the tag alone
54+
# does NOT return its peeled line.
55+
OBJECTSTACK_TAG="@objectstack/cli@$OBJECTSTACK_VERSION"
56+
# GIT_TERMINAL_PROMPT=0 so an unreachable/private remote fails in seconds with
57+
# the message below instead of blocking the job on a credential prompt.
58+
if ! TAG_LINES="$(GIT_TERMINAL_PROMPT=0 git ls-remote --tags "$REPO_URL" \
59+
"refs/tags/$OBJECTSTACK_TAG" "refs/tags/$OBJECTSTACK_TAG^{}" 2>&1)"; then
60+
echo "[live-backend] cannot reach $REPO_URL to resolve the release tag" >&2
61+
echo "[live-backend] $TAG_LINES" >&2
62+
exit 1
63+
fi
64+
OBJECTSTACK_REF="$(printf '%s\n' "$TAG_LINES" | awk -v t="refs/tags/$OBJECTSTACK_TAG" '
65+
$2 == t "^{}" { peeled = $1 }
66+
$2 == t { plain = $1 }
67+
END { print (peeled != "" ? peeled : plain) }
68+
')"
69+
# A missing tag is not an error to `git ls-remote` — it prints nothing and exits
70+
# 0 — so the shape check below is the actual guard, not a formality. Refuse
71+
# loudly and by name: the alternative is `git fetch --depth 1 origin ""` failing
72+
# 300 seconds downstream, in a log nobody reads until the job goes red.
73+
if [[ ! "$OBJECTSTACK_REF" =~ ^[0-9a-f]{40}$ ]]; then
74+
echo "[live-backend] release tag '$OBJECTSTACK_TAG' does not resolve to a commit in" >&2
75+
echo "[live-backend] $REPO_URL" >&2
76+
echo "[live-backend] OBJECTSTACK_VERSION=$OBJECTSTACK_VERSION (backend.env) names a version" >&2
77+
echo "[live-backend] with no published release tag, or the tag scheme moved. Refusing to" >&2
78+
echo "[live-backend] start: there is no showcase-app commit matching that release." >&2
79+
exit 1
80+
fi
81+
echo "[live-backend] resolved $OBJECTSTACK_TAG -> $OBJECTSTACK_REF"
82+
83+
# The RESOLVED sha, not the version, is in the stamp: without it, a fixture
84+
# prepared before a pin change satisfies the reuse test and the new pin is never
85+
# installed — and a re-pointed tag is exactly such a change with no key of its
86+
# own to hash. The workflow's fixture cache key is the hash of backend.env,
87+
# which carries the version for the same reason; see the ⚠️ note there for the
88+
# one path that key can no longer see.
4489
WANT_STAMP="$OBJECTSTACK_REF $OBJECTSTACK_VERSION better-auth@$BETTER_AUTH_VERSION"
4590

4691
mkdir -p "$BACKEND_DIR"
@@ -50,10 +95,10 @@ prepare() {
5095
echo "[live-backend] prepare: pins unchanged ($WANT_STAMP), reusing $APP_DIR"
5196
return
5297
fi
53-
echo "[live-backend] prepare: showcase@${OBJECTSTACK_REF:0:12} on published @objectstack/*@$OBJECTSTACK_VERSION"
98+
echo "[live-backend] prepare: showcase@${OBJECTSTACK_REF:0:12} (from $OBJECTSTACK_TAG) on published @objectstack/*@$OBJECTSTACK_VERSION"
5499
rm -rf "$APP_DIR" "$BACKEND_DIR/src" "$STAMP"
55100

56-
# Shallow, sparse fetch of the pinned commit — metadata source only.
101+
# Shallow, sparse fetch of the resolved commit — metadata source only.
57102
git init -q "$BACKEND_DIR/src"
58103
git -C "$BACKEND_DIR/src" remote add origin "$REPO_URL"
59104
git -C "$BACKEND_DIR/src" sparse-checkout set examples/app-showcase

‎e2e/live/saved-view-filter.spec.ts‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import { test, expect } from '@playwright/test';
77
* This is the empirical pin for the defect, run against the REAL stack because
88
* only the server can answer the question the issue asked: does it accept a
99
* rule array in `$filter`? It does not. Measured against the published backend
10-
* pair this lane pins — `OBJECTSTACK_VERSION` and `OBJECTSTACK_REF` in
11-
* `e2e/live/ci/backend.env`, which move together and are now held to the
12-
* lockfile by `scripts/__tests__/ci-cd-pipeline-doc.test.ts` (objectui#7689).
13-
* That pair has moved since this measurement was first taken, which is why it
14-
* is named by FILE and not by value: a version literal written here goes stale
15-
* behind the pin silently, and the whole point of objectui#7689 is that it did
16-
* — for two minor versions, in the pin itself. The showcase app is the one that
17-
* ref checks out:
10+
* backend this lane pins — `OBJECTSTACK_VERSION` in `e2e/live/ci/backend.env`,
11+
* held to the lockfile by `scripts/__tests__/ci-cd-pipeline-doc.test.ts`
12+
* (objectui#7689), with the showcase-app commit derived from it at boot from
13+
* the matching `@objectstack/cli` release tag (objectui#7964). That pin has
14+
* moved since this measurement was first taken, which is why it is named by
15+
* FILE and not by value: a version literal written here goes stale behind the
16+
* pin silently, and the whole point of objectui#7689 is that it did — for two
17+
* minor versions, in the pin itself. The showcase app is the one that release
18+
* checks out:
1819
*
1920
* GET /api/v1/data/showcase_task
2021
* ?$filter=[{"field":"status","operator":"equals","value":"in_progress"}]

0 commit comments

Comments
 (0)