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):' ;
101111function 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 */
119128function 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