Skip to content

Commit 454818a

Browse files
committed
test(cutover): the ui pin tests assert the merged workspace, and the lockfile-integrity samples move onto merged-lock keys (objectstack#25803)
The ui tree builds against the workspace spec now: every @objectstack/* dependency is a workspace link, so there is no pinned build, no .objectstack-pin snapshot key and no lockfile spec version left to compare. - objectstack-pin: the real-tree cases assert the merged facts instead (workspace: specifiers, no @objectstack/* lock key, no override, no .objectstack-pin), and keep the one-sha case. - ci-cd-pipeline-doc: the three lockfile-comparison cases retire with the doc paragraph and backend.env's header; the rest stay asserted. - check-lockfile-integrity: sample (a) is a second physical ai copy, sample (b) forks the two zod-peer keys under ai, zod 1 -> 2 now that shadcn ^4.22.1 took zod 3 out. The real-run case reads --base-ref HEAD: Test UI checks out at depth 1, where origin/main does not exist. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CUXv64KLMfYv3ia2sWi6te
1 parent bf5c588 commit 454818a

5 files changed

Lines changed: 138 additions & 200 deletions

File tree

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -557,13 +557,12 @@ the list. The lane grows the allowlist a few proven specs at a time (see the wor
557557
comment), so every promotion would stale a hand-copied enumeration here; it already did
558558
(objectui#3488).
559559

560-
Backend pins live in `e2e/live/ci/backend.env` and must match the `@objectstack/spec` version the
561-
pinned build in `pnpm-lock.yaml` declares — bump both in the same PR, or the run proves nothing.
562-
Since objectui#12957 the lockfile resolves `@objectstack/spec` to a directory built from the commit
563-
`.objectstack-sha` names (`.objectstack-pin/spec`), and the version is the one that
564-
directory's committed `package.json` carries (objectui#13008). ⚠️ objectstack moves that string only when it releases, so a
565-
pin between two releases matches the earlier release's backend by number while the console carries
566-
later commits; this lane still runs the published backend, and nothing here closes that gap.
560+
Backend pins live in `e2e/live/ci/backend.env`. Since objectui moved into the objectstack
561+
repository (objectstack#25803), the console builds against the workspace's own `@objectstack/spec`,
562+
which the lockfile records as a workspace link with no version, so there is no lockfile version
563+
for `OBJECTSTACK_VERSION` to match and no test compares the two. ⚠️ This lane still runs the
564+
published backend at that version against a console built from the current tree, and nothing
565+
here closes that gap.
567566

568567
`OBJECTSTACK_VERSION` is the only `@objectstack` value that file declares, and moving it is the
569568
only move. The showcase-app commit the lane sparse-checks-out is **derived**, not pinned:

‎ui/e2e/live/ci/backend.env‎

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
# .github/workflows/live-e2e.yml).
33
#
44
# OBJECTSTACK_VERSION — the published @objectstack/* npm version the backend
5-
# runs on. MUST stay aligned with the @objectstack/spec version resolved in
6-
# this repo's pnpm-lock.yaml: the whole point of the lane is to smoke-test
7-
# "this console x that published backend" as a matched pair. Testing a
8-
# mismatched pair (backend rc.N vs console pinned rc.M) proves nothing —
9-
# bump this in the same PR that bumps the console's @objectstack pins.
10-
# Pinned by scripts/__tests__/ci-cd-pipeline-doc.test.ts, which reads this
11-
# value and the version pnpm-lock.yaml resolves and requires them equal. It
12-
# had drifted two minor versions (17.0.0-rc.2 against a 17.2.0 lockfile)
13-
# before anything compared the two numbers — objectui#7689.
5+
# runs on. Until objectui moved into the objectstack repository it had to
6+
# match the @objectstack/spec version this repo's pnpm-lock.yaml resolved,
7+
# and ci-cd-pipeline-doc.test.ts required the two equal (objectui#7689).
8+
# Since the import (objectstack#25803) the console builds against the
9+
# workspace's own @objectstack/spec, a lockfile link with no version, so
10+
# that comparison is retired and this value is the published backend the
11+
# lane runs, nothing more.
1412
#
1513
# This file declares exactly ONE @objectstack pin, and that is the whole
1614
# design: the commit is derived from it, see below.

‎ui/scripts/__tests__/check-lockfile-integrity.test.ts‎

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
* 1. it is GREEN on this repository's real `pnpm-lock.yaml`;
1010
* 2. it is RED on the two real incident samples, built from that same real
1111
* lockfile by a transformation this file asserts actually landed (the
12-
* first, since objectui#12957 retired the version rule, in its pinned-build
13-
* form);
12+
* first, since objectui#12957 retired the version rule, in its
13+
* merged-workspace form, objectstack#25803);
1414
* 3. it is wired — the workflow runs it, and the Dependabot merge gate
1515
* classifies the check it produces.
1616
*
@@ -77,9 +77,15 @@ describe('green on main — the negative control', () => {
7777
expect(compareLockfiles(idx, idx).findings).toEqual([]);
7878
});
7979

80-
it('is clean against its own merge base in a real run', () => {
81-
// Exercises the git path (`--base-ref`), not just the pure functions.
82-
const out = execFileSync('node', [GATE, '--base-ref', 'origin/main'], {
80+
it('is clean against its own commit in a real run', () => {
81+
// Exercises the git path (`--base-ref` -> `git merge-base`, then `git show
82+
// <rev>:<path>` from the workspace root), not just the pure functions. The
83+
// base is HEAD, not origin/main, since the move into objectstack's tree
84+
// (objectstack#25803): its Test UI job checks out at depth 1, where no
85+
// origin/main exists to take a merge base with, and whether THIS change's
86+
// lock is clean against main is the lockfile-integrity workflow's question,
87+
// a non-blocking one (below), not a unit test's.
88+
const out = execFileSync('node', [GATE, '--base-ref', 'HEAD'], {
8389
encoding: 'utf8',
8490
cwd: repoRoot,
8591
});
@@ -88,40 +94,42 @@ describe('green on main — the negative control', () => {
8894
});
8995

9096
/**
91-
* Sample (a) — what is left of objectui#8326 once `@objectstack/*` resolves by
92-
* commit (objectui#12957). The downgrade on #7058 split the workspace across two
93-
* physical spec copies; the version half of that card (rule 1) is retired,
94-
* because the pinned build carries no version for anything to move backward.
95-
* The duplication half is what a partial fall-back off the pin looks like now: one
96-
* importer resolving the REGISTRY spec beside the pinned build is two snapshot
97-
* keys under one name. Built from the real lockfile by a transformation this file
98-
* asserts landed.
97+
* Sample (a) — what is left of objectui#8326 in the merged workspace
98+
* (objectstack#25803). The downgrade on #7058 split the workspace across two
99+
* physical spec copies; the version half of that card (rule 1) is retired, and
100+
* since the move into objectstack's tree every `@objectstack/*` package is a
101+
* workspace link with no snapshot key at all, so the spec can no longer stand
102+
* in for it (a registry copy beside a link is 0 -> 1, the newly-declared case
103+
* rule 2 leaves alone; objectstack-pin.test.ts asserts no such key exists). The
104+
* duplication half is what a partial downgrade of any declared runtime package
105+
* looks like: `ai`, single-copy in the merged lock and the peer every spec
106+
* consumer resolves through, gaining an older copy beside the current one is
107+
* two snapshot keys under one name. Built from the real lockfile by a
108+
* transformation this file asserts landed.
99109
*/
100-
const PINNED_SPEC = " '@objectstack/spec@file:.objectstack-pin/spec(ai@7.0.118(zod@4.6.7))':";
110+
const AI_KEY = ' ai@7.0.118(zod@4.6.7):';
101111
function sampleA(): string {
102-
expect(realLock, 'the pinned spec snapshot key moved — rebuild this sample before trusting it').toContain(PINNED_SPEC);
103-
return realLock.replace(
104-
PINNED_SPEC,
105-
" '@objectstack/spec@18.0.0-next.3(ai@7.0.118(zod@4.6.7))':\n dependencies:\n zod: 4.6.7\n" + PINNED_SPEC,
106-
);
112+
expect(realLock, 'the ai snapshot key moved — rebuild this sample before trusting it').toContain(AI_KEY);
113+
return realLock.replace(AI_KEY, ' ai@7.0.65(zod@4.6.7):\n dependencies:\n zod: 4.6.7\n' + AI_KEY);
107114
}
108115

109116
/**
110117
* Sample (b) — objectui#8333, re-measured on `da5e4f69e` on 2026-09-08 by
111118
* `pnpm update better-auth --lockfile-only --recursive`, which resolved 1.7.3
112119
* and produced exactly this: the better-auth family forward, `zod@4.5.4` added,
113-
* and every `@objectstack/*` package that sits under `ai` gaining a SECOND peer
114-
* context at the SAME version.
120+
* and every package that sits under `ai` gaining a SECOND peer context at the
121+
* SAME version. Re-based on the merged lock (objectstack#25803): the
122+
* `@objectstack/*` packages it forked then are workspace links now, so the forks
123+
* are the two snapshot keys under `ai` that carry the zod peer.
115124
*
116-
* ⭐ That last part is the discriminator. No `@objectstack/*` version moves, so
117-
* a gate that only asks "did an identity move backward" is green here.
125+
* ⭐ That last part is the discriminator. No version moves, so a gate that only
126+
* asks "did an identity move backward" is green here.
118127
*/
119128
function sampleB(): string {
120129
let out = realLock;
121130
const forked = [
122-
PINNED_SPEC,
123-
" '@objectstack/formula@file:.objectstack-pin/formula(ai@7.0.118(zod@4.6.7))':",
124-
" ai@7.0.118(zod@4.6.7):",
131+
" '@ai-sdk/react@4.0.121(react@19.3.0)(zod@4.6.7)':",
132+
AI_KEY,
125133
];
126134
for (const key of forked) {
127135
expect(out, `snapshot key moved: ${key}`).toContain(key);
@@ -132,18 +140,19 @@ function sampleB(): string {
132140
return out.replace(zodKey, `${zodKey}\n zod@4.5.4: {}`);
133141
}
134142

135-
describe('cause 1 — a registry @objectstack/* copy beside the pinned build (objectui#8326, objectui#12957)', () => {
143+
describe('cause 1 — a second physical copy of a declared runtime package (objectui#8326, objectstack#25803)', () => {
136144
const reading = compareLockfiles(indexLockfile(realLock, 'base'), indexLockfile(sampleA(), 'a'));
137145

138146
it('the sample really differs from the base (the mutation landed)', () => {
139147
expect(sampleA()).not.toEqual(realLock);
140-
expect(sampleA()).toContain("'@objectstack/spec@18.0.0-next.3(ai@7.0.118(zod@4.6.7))':");
148+
expect(sampleA()).toContain(' ai@7.0.65(zod@4.6.7):');
141149
});
142150

143-
it('goes red, naming the second physical spec', () => {
144-
const spec = reading.duplicated.find((f) => f.name === '@objectstack/spec');
145-
expect(spec?.headCount).toBe(2);
146-
expect(spec?.added).toEqual(['@objectstack/spec@18.0.0-next.3(ai@7.0.118(zod@4.6.7))']);
151+
it('goes red, naming the second physical ai', () => {
152+
const ai = reading.duplicated.find((f) => f.name === 'ai');
153+
expect(ai?.baseCount).toBe(1);
154+
expect(ai?.headCount).toBe(2);
155+
expect(ai?.added).toEqual(['ai@7.0.65(zod@4.6.7)']);
147156
});
148157

149158
it('⛔ no longer orders versions: the retired rule 1 reports nothing of its own', () => {
@@ -159,24 +168,25 @@ describe('cause 2 — a declared dependency forks with no version moving (object
159168
expect(sampleB()).toContain('zod@4.5.4');
160169
});
161170

162-
it('⭐ still goes red: zod gained a copy, though it was never single-copy', () => {
171+
it('⭐ still goes red: zod gained a copy', () => {
163172
const zod = reading.duplicated.find((f) => f.name === 'zod');
164173
expect(zod, 'the gate must not be blind to objectui#8333').toBeTruthy();
165-
// ⛔ 2 -> 3, not 1 -> 2. Both cards say "forks a single-copy dependency";
166-
// measured on `main`, zod is ALREADY two copies (3.25.76 + 4.4.3 then;
167-
// 3.25.76 + 4.6.5 since objectui#11073, 3.25.76 + 4.6.7 since objectui#12626, re-measured), so a
168-
// rule keyed on "was one, is now two" would be green here.
169-
expect(zod?.baseCount).toBe(2);
170-
expect(zod?.headCount).toBe(3);
174+
// 1 -> 2 on the merged lock. objectui's own lock carried two zod copies
175+
// (3.25.76 + 4.6.7, the 3 line through shadcn 4.18.0), and this assertion
176+
// was 2 -> 3 so that a rule keyed on "was one, is now two" would be green
177+
// here; shadcn ^4.22.1 (objectstack#25803) took zod 3 out, re-measured. The
178+
// 2 -> 3 case stays held on a synthetic lock by the gate's own --self-test.
179+
expect(zod?.baseCount).toBe(1);
180+
expect(zod?.headCount).toBe(2);
171181
});
172182

173-
it('⭐ names the second physical @objectstack/spec, at one unchanged version', () => {
174-
const spec = reading.duplicated.find((f) => f.name === '@objectstack/spec');
175-
expect(spec?.headCount).toBe(2);
183+
it('⭐ names the second physical ai, at one unchanged version', () => {
184+
const ai = reading.duplicated.find((f) => f.name === 'ai');
185+
expect(ai?.headCount).toBe(2);
176186
// This is #8326's exact bundling mechanism — two real paths, nothing
177-
// dedupes them — arriving with no version change anywhere: both are the
178-
// pinned build, under two peer contexts.
179-
expect(spec?.head.every((k) => k.startsWith('@objectstack/spec@file:.objectstack-pin/spec('))).toBe(true);
187+
// dedupes them — arriving with no version change anywhere: both are
188+
// ai@7.0.118, under two peer contexts.
189+
expect(ai?.head.every((k) => k.startsWith('ai@7.0.118('))).toBe(true);
180190
});
181191
});
182192

0 commit comments

Comments
 (0)