fix(ci): align the live-e2e backend pin with the lockfile, and pin the rule that says it must - #7970
Merged
baozhoutao merged 2 commits intoSep 6, 2026
Conversation
`e2e/live/ci/backend.env` pinned `OBJECTSTACK_VERSION=17.0.0-rc.2` while `pnpm-lock.yaml` resolved `@objectstack/spec` 17.2.0. Both the file's own header and `content/docs/guide/ci-cd-pipeline.md` state the two must match, and that a mismatched pair "proves nothing" -- so for two minor versions the `Live E2E (informational)` lane produced a green signal its own contract declares meaningless. Aligns the pair to the value the lockfile actually resolves (17.2.0, not the `^17.0.0` range) and moves `OBJECTSTACK_REF` with it, to the commit the `@objectstack/cli@17.2.0` release tag points at -- the pairing rule the file header already states, and the one the previous pin honoured for rc.2. Adds the missing comparison to `scripts/__tests__/ci-cd-pipeline-doc.test.ts`, whose charter is pinning that page's claims to CI reality: it reads `OBJECTSTACK_VERSION`, reads every `@objectstack/spec` version the lockfile resolves (two independent readings, union must be one), and requires equality, with both anti-vacuity floors the file's culture asks for. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013uAaxiwgYDybsTNV9xwa1M
…eral `e2e/live/saved-view-filter.spec.ts`'s header stated the measurement was taken on `@objectstack/*@17.0.0-rc.2` -- the pair the previous commit just moved off. It was the second reader of that literal on the fenced surfaces, and it went stale the moment the pin did. Names `OBJECTSTACK_VERSION` / `OBJECTSTACK_REF` in `e2e/live/ci/backend.env` instead, and says the pair has moved since the measurement rather than implying it has not. A comment that carries the value is a copy of the pin that nothing updates; a comment that carries the file name cannot go stale behind it. Comment only -- no assertion, import or code line is touched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013uAaxiwgYDybsTNV9xwa1M
baozhoutao
marked this pull request as ready for review
September 6, 2026 05:16
baozhoutao
deleted the
claude/issue-7689-backend-env-pin-matches-lockfile
branch
September 6, 2026 05:41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7689
e2e/live/ci/backend.envpinnedOBJECTSTACK_VERSION=17.0.0-rc.2whilepnpm-lock.yamlresolved@objectstack/spec17.2.0. Three separate places state that the two MUST match and that a mismatched pair “proves nothing”: the file's own header,content/docs/guide/ci-cd-pipeline.md:420, and.github/workflows/live-e2e.yml's header at:27. For two minor versionsLive E2E (informational)therefore published a green signal its own contract declares meaningless — because nothing compared the two numbers.The pair pinned, and why these values
OBJECTSTACK_VERSION17.0.0-rc.217.2.0^17.0.0range it declares. Verified on this branch's base:@objectstack/spec@appears inpnpm-lock.yamlat exactly one version, in both spellings (the barepackages:key and the peer-suffixedsnapshots:key), and all 10 workspace importers resolve17.2.0.OBJECTSTACK_REF89d2a4eb3f3b…e7d2cc67fdef…@objectstack/cli@+ that version release tag points at. Measured, not guessed —@objectstack/cli@17.2.0dereferences toe7d2cc67fdef7fee9d2c6d65d7363fe1c78ce6a4. The rule was being honoured before: the outgoing ref is byte-for-byte the commit@objectstack/cli@17.0.0-rc.2points at.Registry probe before pinning: every
@objectstack/*packagestart-backend.shinstalls — the nine runtime deps the showcase app declares at that ref, plus@objectstack/cli— publishes17.2.0. None had to be substituted, so this is not aneeds_decision.Deliberately not
17.3.0: that is PR #7685's lockfile, not this branch's.The check
scripts/__tests__/ci-cd-pipeline-doc.test.ts, in a newdescribe. That file's charter is pinning this exact page's claims to CI reality, and the sentence atci-cd-pipeline.md:420is a claim of exactly the shape it was written for — its header already says a doc advertising a guardrail CI does not have is worse than no doc.The version-claim ledger
doc-version-claims.test.tswas the dispatch's first candidate and was ruled out on measurement, not taste: itsSCAN_ROOTSarecontent/docs,packages/*/README.mdandskills,.md/.mdxonly, sobackend.envis not on a scanned surface and noKNOWN_CLAIMSrow holds the17.0.0-rc.2literal. Its own rule — one rule judges one kind of fact — argues against bolting a two-non-doc-file comparison onto it.Four assertions: the doc sentence is still on the page (this whole block is its enforcement); the lockfile resolves exactly one
@objectstack/specversion, read two independent ways whose union must be a singleton;OBJECTSTACK_VERSIONequals it; andOBJECTSTACK_REFis a full 40-character object name, becausegit fetch --depth 1refuses an abbreviated one and the lane only says so 300 seconds later.Both anti-vacuity floors this file's culture asks for are in: an empty lockfile match set is a failure, not a silent pass, and a missing doc sentence is a failure rather than a quiet retirement.
content/docs/guide/ci-cd-pipeline.mdis unchanged: the sentence names no version number, so it did not misstate anything after the pin moved.Second commit, after review. The reader sweep for the outgoing literal (
git grep -n '17\.0\.0-rc\.2' -- scripts/ content/ e2e/ .github/) returns exactly two hits, and the first is the pin itself; the second wase2e/live/saved-view-filter.spec.ts:10, whose header restated the pair as a value and went stale the moment the pin moved. It now namesOBJECTSTACK_VERSION/OBJECTSTACK_REFine2e/live/ci/backend.envinstead, and says the pair has moved since that measurement rather than implying it has not — comment only, with no assertion, import or code line touched (git diff -U0on that file yields no changed line that is not a comment line).The half deliberately not asserted, stated in both the env file and the test rather than implied as covered: whether
OBJECTSTACK_REFreally is the tag's commit. Reading that tag needs the objectstack repository over the network, which the unit lane has not got. Filed separately as #7964.Ablation, on the committed tree
Three legs, each proving on disk that the mutation landed (injected/removed line counts and
git hash-object) before reading the run, with atraprestoring fromHEADby absolute path:17.0.0-rc.2e2e/live/ci/backend.env OBJECTSTACK_VERSION=17.0.0-rc.2againstpnpm-lock.yaml @objectstack/spec 17.2.0e7d2cc6db35d5ef…back to theHEADblob) and an emptygit diff HEAD, not by an exit codeNo dist leg: the test reads
backend.envandpnpm-lock.yamlstraight off the working tree, so there is no build output for a mutation to fail to reach.The live lane will go red here, and that is the point
Per the triage ruling on the card — 「一条失败的匹配对携带的信息,严格多于一条绿色的不匹配对」 — a red
Live E2E (informational)after alignment is the expected result, not a regression, and ⛔ the repair is never to put the pin back.Measured in this container before opening the PR, so it is no longer a prediction. The matched pair prepares cleanly — sparse checkout at the new ref,
npm installof the ten published@objectstack/*@17.2.0packages, 521 packages — and the server starts, but never seeds: the system tables are never created (no such table: sys_user,sys_organization,sys_permission_set,sys_position), so the seeded sign-instart-backend.shpolls never answers and the script exits 1 at its 300-second deadline.The cause is upstream of this repository and was traced to a single declaration, from the registry rather than by inference:
@objectstack/plugin-auth@better-auth/core17.0.0-rc.2(the outgoing pin)1.7.0-rc.2— exact1.7.0-rc.217.1.0^1.7.11.7.317.2.0(the incoming pin)^1.7.11.7.317.3.0(PR #7685's target)^1.7.21.7.3@objectstack/plugin-auth@17.2.0importscreateLocalAccountIssuerfrom@better-auth/core/db;@better-auth/core@1.7.3does not export it (verified by grep over the installed package: zero hits), and the CLI logs exactly that as a load failure before the schema step. So the outgoing pin booted only because rc.2 happened to pin its auth dependency exactly, and every published version since has floated into a range that is now broken.A control leg run minutes later in the same container, through the same
start-backend.sh, boots the outgoing pair (@objectstack/*@17.0.0-rc.2at ref89d2a4eb…) to a seeded sign-in in 30 seconds, with zerocreateLocalAccountIssuerlines and zerono such tablelines. That is the load-bearing half of the measurement: it rules out "this container cannot boot the lane at all" and attributes the failure to the published version rather than to the harness or the alignment.That is precisely the class of breakage this lane exists to catch, and precisely what an unmatched pair could not tell anyone. It is not a regression introduced here and ⛔ must not be repaired by reverting the pin. Filed upstream, where the change belongs, as objectstack-ai/objectstack#16186.
⛔ Out of scope on purpose, per the triage: the lane's tier.
informationalstays; promoting it is the manual floor and its own decision card. No change to.github/workflows/live-e2e.yml,pnpm-lock.yamlor anypackage.json.For the
domain:specseat merging mainPR #7685 (the 17.3.0 lockfile bump) does not move
backend.env; this check will red there until it does.Verification
pnpm exec vitest run --maxWorkers=2 scripts/__tests__/ci-cd-pipeline-doc.test.ts— 40 passed (36 before, 4 added)pnpm exec vitest run --maxWorkers=2 scripts/__tests__/{doc-version-claims,check-control-bytes,e2e-type-check,scripts-type-check}.test.ts— 94 passedpnpm type-check:scripts— exit 0;tsc --listFilesconfirmsscripts/__tests__/ci-cd-pipeline-doc.test.tsis in the program, so this is a measurement and not an exclusionpnpm lint:root— exit 0, 0 errors (32 pre-existing warnings, none in a changed file). Full repo-root scan, not narrowedpnpm check:control-bytes— OK, 6415 files;pnpm check:shell-escape-residue— OKnode scripts/check-governed-queue-guard.mjs --teston both changed paths —NOT GOVERNEDnode scripts/check-changeset-presence.mjs— no changeset owed (0 published sources, 0 published contracts changed)pnpm type-check:e2e(tsconfig.e2e.json, the program that owns that spec) — exit 0;--listFilesputse2e/live/saved-view-filter.spec.tsin the program (1 hit), so it is measured and not excludedpnpm exec eslint --format jsonon the changed spec — exit 0, 0 errors, 0 warnings, 1 file reported (a count of 0 would have meant out-of-scope, which is not a pass); the wholee2e/**/*.tsglob lints at 0 errors across 31 filese2e/is now measured, not asserted: with the literal injected into that spec,doc-version-claims.test.tsstays green (exit 0), and with the same literal on a throwaway page undercontent/docs/it goes red (exit 1). The negative reading is a blind spot, not a dead instrument. Both probes restored; tree verified clean afterwards.Heavy runs went through the shared verify lock; every verdict above was read from the gate's own printed line with the exit code captured before any pipe.
Authored by a Claude Code developer seat, session
session_013uAaxiwgYDybsTNV9xwa1M(repeated here as prose because a footer link does not reliably survive an edit to this body).Judgement, on the four axes
start-backend.shsources this file at:25, builds its cache stamp from both keys at:32and installs fromOBJECTSTACK_VERSIONat:41and:65;live-e2e.ymlkeys the backend cache on the file's hash at:133and:302. Two live readers, and the lane they serve is the only one that can see cross-version breakage at all.@objectstack/specversions is named as a split, not silently reduced to one to compare against; an empty match set fails instead of passing; and the failure text forbids by name the repair that would restore green while restoring meaninglessness.describein an existing file. No new script, no new workflow, no new declaration surface, and the two expansions available here were both declined: the tier change (the card's own out-of-scope half) and a network check for the ref (a real fork between checking the pin and deriving it, filed as finding(ci): the live-e2e lane's OTHER pin —OBJECTSTACK_REF— states a MUST that nothing can check, and it fails silently in the same way objectui#7689 did #7964 for the maintainer rather than picked here).Generated by Claude Code