1212 * So these cases build real host roots on disk (real `node_modules`, a real
1313 * stand-in package, nothing mocked) and pin all three verdicts: available,
1414 * unavailable, and declared-but-missing.
15+ *
16+ * ── #16539 — why the subject below is a `@fixture/*` name ────────────────────
17+ *
18+ * Every verdict here is a statement about what a host root HAS and, just as
19+ * load-bearing, what it has NOT got. Until #16215 the second half came free:
20+ * `@objectstack/organizations` was cloud-private, so a temp host that declared
21+ * it and did not install it was unresolvable by construction. ADR-0132 (#16215)
22+ * brought the package into this workspace; pnpm's hoisted store carries it, and every
23+ * `pnpm exec`-launched runner (vitest's bin shim included) exports a `NODE_PATH`
24+ * that reaches that store. From then on the CONTROL's verdict was a function of
25+ * whether an unrelated package had been BUILT: green on CI, whose task graph
26+ * never builds it, red on any tree that had run a full `pnpm build`.
27+ *
28+ * The visible half of that is a false red. The half that matters is the quiet
29+ * one — a control whose subject is reachable is no longer controlling the thing
30+ * its name claims, and nothing says so. So the cases below drive a name this
31+ * workspace can never contain, and PROVE its absence instead of assuming it;
32+ * `ORGANIZATIONS_PKG` stays pinned as the probe's default subject by its own
33+ * case. #16723 made exactly this repair to `packages/types/src/node.test.ts`
34+ * for the same landing, and this file reuses its `@fixture/*` scope.
1535 */
1636
1737import { describe , it , expect , beforeAll , afterAll } from 'vitest' ;
1838import { mkdirSync , mkdtempSync , rmSync , writeFileSync } from 'node:fs' ;
1939import { tmpdir } from 'node:os' ;
2040import { join } from 'node:path' ;
41+ import { createHostRequire } from '@objectstack/types/node' ;
2142import { probeOrganizations , MULTI_ORG_ENV , ORGANIZATIONS_PKG } from './enterprise-organizations.js' ;
2243
44+ /**
45+ * The package name every fixture host below is built around: modelled on the
46+ * real enterprise plugin (its export is the class `serve` / `bootStack`
47+ * construct), fixture-only in NAME.
48+ *
49+ * ⚠️ #16539 — it must stay a `@fixture/*` name. A name this workspace owns
50+ * cannot state "this host root does not have it", because the hoisted store and
51+ * the launcher's `NODE_PATH` answer that question instead of the fixture; and no
52+ * workspace name is safe from becoming one (`@objectstack/organizations` was
53+ * cloud-private when these cases were written). Only a name the workspace can
54+ * never contain is, and the PREMISE cases below prove this one still is not.
55+ */
56+ const HOST_ONLY = '@fixture/enterprise-organizations' ;
57+
2358let hostWithPkg : string ;
2459let hostWithoutPkg : string ;
2560/**
@@ -39,6 +74,9 @@ let hostDeclaredNoLoadableEntry: string;
3974/**
4075 * The `declared-unresolvable` CONTROL: declared and NOT installed. #14270 left
4176 * this arm alone, so its wording must come out byte-identical.
77+ *
78+ * ⚠️ #16539 — "NOT installed" is a property of THIS directory, and only a
79+ * subject the workspace can never supply keeps it one. See {@link HOST_ONLY}.
4280 */
4381let hostDeclaredNotInstalled : string ;
4482
@@ -55,12 +93,12 @@ function writeHost(
5593 name : 'dogfood-host-fixture' ,
5694 private : true ,
5795 type : 'module' ,
58- ...( declare ? { dependencies : { [ ORGANIZATIONS_PKG ] : '*' } } : { } ) ,
96+ ...( declare ? { dependencies : { [ HOST_ONLY ] : '*' } } : { } ) ,
5997 } ) ,
6098 'utf8' ,
6199 ) ;
62100 if ( withPkg ) {
63- const pkgDir = join ( dir , 'node_modules' , ...ORGANIZATIONS_PKG . split ( '/' ) ) ;
101+ const pkgDir = join ( dir , 'node_modules' , ...HOST_ONLY . split ( '/' ) ) ;
64102 mkdirSync ( pkgDir , { recursive : true } ) ;
65103 if ( opts . typesOnly ) {
66104 // No `require` condition (the CJS resolver throws) and no `import`
@@ -69,7 +107,7 @@ function writeHost(
69107 writeFileSync (
70108 join ( pkgDir , 'package.json' ) ,
71109 JSON . stringify ( {
72- name : ORGANIZATIONS_PKG ,
110+ name : HOST_ONLY ,
73111 version : '0.0.0-fixture' ,
74112 type : 'module' ,
75113 exports : { '.' : { types : './index.d.ts' } } ,
@@ -86,7 +124,7 @@ function writeHost(
86124 writeFileSync (
87125 join ( pkgDir , 'package.json' ) ,
88126 JSON . stringify ( {
89- name : ORGANIZATIONS_PKG ,
127+ name : HOST_ONLY ,
90128 version : '0.0.0-fixture' ,
91129 type : 'module' ,
92130 main : 'index.js' ,
@@ -127,13 +165,13 @@ describe('enterprise multi-org probe (#4700)', () => {
127165 // The verdict the old probe could never reach, no matter what any app or CI
128166 // had installed. This is what makes `describe.skipIf(!organizationsAvailable)`
129167 // a real gate rather than an unconditional skip.
130- const probe = await probeOrganizations ( hostWithPkg , false ) ;
168+ const probe = await probeOrganizations ( hostWithPkg , false , HOST_ONLY ) ;
131169 expect ( probe . available ) . toBe ( true ) ;
132170 expect ( probe . reason ) . toBeUndefined ( ) ;
133171 } ) ;
134172
135173 it ( 'reports UNAVAILABLE, with an actionable reason, when the app lacks it' , async ( ) => {
136- const probe = await probeOrganizations ( hostWithoutPkg , false ) ;
174+ const probe = await probeOrganizations ( hostWithoutPkg , false , HOST_ONLY ) ;
137175 expect ( probe . available ) . toBe ( false ) ;
138176 // The reason has to name the switch, or the skip stays folklore.
139177 expect ( probe . reason ) . toContain ( MULTI_ORG_ENV ) ;
@@ -145,13 +183,13 @@ describe('enterprise multi-org probe (#4700)', () => {
145183 // failure a CI operator cannot miss (Prime Directive #10 / "absence must be
146184 // loud"). Without this, a cloud run that lost the package would look exactly
147185 // like a cloud run that has it.
148- await expect ( probeOrganizations ( hostWithoutPkg , true ) ) . rejects . toThrow (
186+ await expect ( probeOrganizations ( hostWithoutPkg , true , HOST_ONLY ) ) . rejects . toThrow (
149187 new RegExp ( `${ MULTI_ORG_ENV } =1 declares` ) ,
150188 ) ;
151189 } ) ;
152190
153191 it ( 'does not throw when the run declares the package AND it is there' , async ( ) => {
154- await expect ( probeOrganizations ( hostWithPkg , true ) ) . resolves . toEqual ( { available : true } ) ;
192+ await expect ( probeOrganizations ( hostWithPkg , true , HOST_ONLY ) ) . resolves . toEqual ( { available : true } ) ;
155193 } ) ;
156194
157195 it ( 'reports UNAVAILABLE when the package is merely PRESENT but not declared (#4719)' , async ( ) => {
@@ -161,26 +199,26 @@ describe('enterprise multi-org probe (#4700)', () => {
161199 // asked for the enterprise runtime. Worse, in a real pnpm workspace the
162200 // "present" half arrives via the bin shim's NODE_PATH, so the verdict moved
163201 // with the launcher.
164- const probe = await probeOrganizations ( hostInstalledButUndeclared , false ) ;
202+ const probe = await probeOrganizations ( hostInstalledButUndeclared , false , HOST_ONLY ) ;
165203 expect ( probe . available ) . toBe ( false ) ;
166204 expect ( probe . reason ) . toContain ( "package.json" ) ;
167205 } ) ;
168206
169207 it ( 'THROWS with a DECLARE-it remedy when the run declares it but the app does not (#4719)' , async ( ) => {
170208 // The remedy has to be the one that works. "Install it" is unfollowable
171209 // advice here — it is already installed; the missing act is declaring it.
172- await expect ( probeOrganizations ( hostInstalledButUndeclared , true ) ) . rejects . toThrow (
173- new RegExp ( `declare ${ ORGANIZATIONS_PKG . replace ( '/' , '\\/' ) } in .* package\\.json` ) ,
210+ await expect ( probeOrganizations ( hostInstalledButUndeclared , true , HOST_ONLY ) ) . rejects . toThrow (
211+ new RegExp ( `declare ${ HOST_ONLY . replace ( '/' , '\\/' ) } in .* package\\.json` ) ,
174212 ) ;
175213 } ) ;
176214
177215 it ( 'CONTROL — the `declared-unresolvable` remedy is unchanged: declared, not installed' , async ( ) => {
178216 // The arm #14270 did NOT touch. Pinned here so the three-way rewrite is a
179217 // measurement: this text has to be byte-identical either side of it.
180- const probe = await probeOrganizations ( hostDeclaredNotInstalled , false ) ;
218+ const probe = await probeOrganizations ( hostDeclaredNotInstalled , false , HOST_ONLY ) ;
181219 expect ( probe . available ) . toBe ( false ) ;
182220 expect ( probe . reason ) . toContain (
183- `${ hostDeclaredNotInstalled } DECLARES ${ ORGANIZATIONS_PKG } , so repair its INSTALL there `
221+ `${ hostDeclaredNotInstalled } DECLARES ${ HOST_ONLY } , so repair its INSTALL there `
184222 + '(`pnpm install`, un-prune, rebuild its dist)' ,
185223 ) ;
186224 } ) ;
@@ -190,15 +228,15 @@ describe('enterprise multi-org probe (#4700)', () => {
190228 // two, so `declared-no-loadable-entry` fell into the else leg and told an
191229 // operator whose app DECLARES the package and HAS it installed to declare
192230 // it and install it. No install action can change what a package publishes.
193- const probe = await probeOrganizations ( hostDeclaredNoLoadableEntry , false ) ;
231+ const probe = await probeOrganizations ( hostDeclaredNoLoadableEntry , false , HOST_ONLY ) ;
194232 expect ( probe . available ) . toBe ( false ) ;
195233 // Which arm fired: the deferral names the two things that are NOT the
196234 // problem and hands the remedy to the importer's message, which this
197235 // reason interpolates at the end.
198236 expect ( probe . reason ) . toContain ( 'and it IS installed, so neither is the problem' ) ;
199237 expect ( probe . reason ) . toContain ( 'publishes no entry Node can load' ) ;
200238 // ⛔ Neither of the other two arms — both are unfollowable for this kind.
201- expect ( probe . reason ) . not . toContain ( `declare ${ ORGANIZATIONS_PKG } in` ) ;
239+ expect ( probe . reason ) . not . toContain ( `declare ${ HOST_ONLY } in` ) ;
202240 expect ( probe . reason ) . not . toContain ( 'repair its INSTALL' ) ;
203241 // The message deferred TO has to actually arrive.
204242 expect ( probe . reason ) . toContain ( 'publishes no entry that Node can load' ) ;
@@ -207,12 +245,67 @@ describe('enterprise multi-org probe (#4700)', () => {
207245 it ( 'THROWS with that same deferral when the run declares the package (#14270)' , async ( ) => {
208246 // The loud half: MULTI_ORG=1 says the package is there, and it IS — it just
209247 // cannot be loaded. The refusal must still name the right remedy.
210- const err = await probeOrganizations ( hostDeclaredNoLoadableEntry , true ) . then (
248+ const err = await probeOrganizations ( hostDeclaredNoLoadableEntry , true , HOST_ONLY ) . then (
211249 ( ) => new Error ( 'probeOrganizations resolved; MULTI_ORG=1 must make this a failure' ) ,
212250 ( e : unknown ) => e as Error ,
213251 ) ;
214252 expect ( err . message ) . toContain ( MULTI_ORG_ENV ) ;
215253 expect ( err . message ) . toContain ( 'and it IS installed, so neither is the problem' ) ;
216- expect ( err . message ) . not . toContain ( `declare ${ ORGANIZATIONS_PKG } in` ) ;
254+ expect ( err . message ) . not . toContain ( `declare ${ HOST_ONLY } in` ) ;
255+ } ) ;
256+ } ) ;
257+
258+ /**
259+ * #16539 — the premises the cases above rest on, asserted instead of assumed.
260+ *
261+ * Both legs are load-bearing and they fail in opposite directions. Without leg 1
262+ * a name that exists nowhere at all satisfies leg 2 and every verdict above is
263+ * vacuous; without leg 2 the fixture stops deciding what the host root has, and
264+ * the CONTROL silently starts measuring the ambient workspace — which is exactly
265+ * how this file broke.
266+ */
267+ describe ( 'PREMISE — the fixture subject is host-only (#16539)' , ( ) => {
268+ it ( 'resolves from a host app that installs it' , ( ) => {
269+ // Leg 1. The fixture host really can see it, so "unresolvable" elsewhere is
270+ // a statement about the resolver's anchor and not about a typo.
271+ const fromHost = createHostRequire ( hostWithPkg ) . resolve ( HOST_ONLY ) ;
272+ expect ( fromHost ) . toContain ( hostWithPkg ) ;
273+ } ) ;
274+
275+ it ( 'is absent from every ambient store the runner exposes' , ( ) => {
276+ // Leg 2, and the guard that would have caught this card. Asserted on the
277+ // BARE SPECIFIER, ⛔ never on `/Cannot find module/` alone: a package the
278+ // runner CAN see whose entry file merely is not on disk throws
279+ // MODULE_NOT_FOUND too, naming `<store>/<pkg>/dist/index.js` instead of the
280+ // specifier. That second throw is what kept the CONTROL above green while
281+ // the property went unguarded — `@objectstack/organizations` was reachable
282+ // through the pnpm bin shim's NODE_PATH and simply unbuilt on CI's graph.
283+ // Read as a bare-specifier failure, the premise can no longer be satisfied
284+ // by an unbuilt workspace package, in either build state.
285+ expect ( ( ) => createHostRequire ( hostDeclaredNotInstalled ) . resolve ( HOST_ONLY ) ) . toThrow (
286+ new RegExp ( `Cannot find module '${ HOST_ONLY } '` ) ,
287+ ) ;
288+ } ) ;
289+
290+ it ( 'and the probe still binds the ENTERPRISE package as its default subject' , ( ) => {
291+ // What the fixture name must NOT quietly become: the subject. Production
292+ // callers pass no specifier, and the one they get is the real package.
293+ expect ( ORGANIZATIONS_PKG ) . toBe ( '@objectstack/organizations' ) ;
294+ expect ( HOST_ONLY ) . not . toBe ( ORGANIZATIONS_PKG ) ;
295+ } ) ;
296+
297+ it ( "and this package's own resolution still cannot see it" , async ( ) => {
298+ // The sentence that used to sit in `enterprise-organizations.ts` as prose —
299+ // "resolvable from nowhere in the framework workspace" — died with #16215
300+ // and nothing noticed. It is an assertion now, on the default subject, over
301+ // the arm that actually uses this module's ESM base.
302+ //
303+ // ⚠️ A red here is NOT a fixture problem: it means `@objectstack/dogfood`
304+ // can now resolve the enterprise package, so `organizationsAvailable` is
305+ // true in the framework repo and the multi-org gates have started running
306+ // here. Read the verdict, then decide — do not re-point this case.
307+ const probe = await probeOrganizations ( hostWithoutPkg , false ) ;
308+ expect ( probe . available ) . toBe ( false ) ;
309+ expect ( probe . reason ) . toContain ( `declare ${ ORGANIZATIONS_PKG } in` ) ;
217310 } ) ;
218311} ) ;
0 commit comments