@canonical/pragma-cli 0.33.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/README.md +7 -7
  2. package/dist/pragma +0 -0
  3. package/package.json +18 -15
  4. package/pragma.conf.ts +664 -0
  5. package/src/bin.ts +13 -4
  6. package/src/capabilities/block.parity.test.ts +250 -0
  7. package/src/capabilities/capabilities/capabilities.render.ts +3 -2
  8. package/src/capabilities/capabilities/capabilities.verb.ts +31 -13
  9. package/src/capabilities/capabilities/catalog.ts +18 -10
  10. package/src/capabilities/capabilities/hints.ts +13 -19
  11. package/src/capabilities/colophon/collectColophon.ts +34 -19
  12. package/src/capabilities/colophon/colophon.verb.ts +8 -4
  13. package/src/capabilities/colophon/types.ts +16 -7
  14. package/src/capabilities/config/collectConfigShow.test.ts +57 -0
  15. package/src/capabilities/config/collectConfigShow.ts +41 -4
  16. package/src/capabilities/config/fields.ts +0 -27
  17. package/src/capabilities/config/set.verb.ts +7 -3
  18. package/src/capabilities/config/show.render.test.ts +77 -5
  19. package/src/capabilities/config/show.render.ts +7 -12
  20. package/src/capabilities/config/show.verb.ts +4 -3
  21. package/src/capabilities/config/types.ts +8 -0
  22. package/src/capabilities/create/byteEquality.test.ts +78 -100
  23. package/src/capabilities/create/compiledCreate.subprocess.test.ts +52 -15
  24. package/src/capabilities/create/constants.ts +75 -0
  25. package/src/capabilities/create/create.mcp.test.ts +2 -2
  26. package/src/capabilities/create/create.test.ts +162 -15
  27. package/src/capabilities/create/create.verb.ts +140 -69
  28. package/src/capabilities/create/index.ts +2 -10
  29. package/src/capabilities/create/pickGenerator.ts +34 -28
  30. package/src/capabilities/create/templates.embedded.generated.ts +4 -29
  31. package/src/capabilities/create/types.ts +10 -6
  32. package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
  33. package/src/capabilities/distribution.test.ts +120 -0
  34. package/src/capabilities/distribution.ts +59 -0
  35. package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
  36. package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
  37. package/src/capabilities/doctor/checks/checkMcpConfigured.ts +7 -6
  38. package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
  39. package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
  40. package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
  41. package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
  42. package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
  43. package/src/capabilities/doctor/doctor.render.test.ts +6 -6
  44. package/src/capabilities/doctor/doctor.render.ts +2 -1
  45. package/src/capabilities/doctor/doctor.test.ts +37 -8
  46. package/src/capabilities/doctor/doctor.verb.ts +6 -2
  47. package/src/capabilities/doctor/runChecks.ts +5 -5
  48. package/src/capabilities/doctor/types.ts +1 -1
  49. package/src/capabilities/emptyRecovery.test.ts +32 -15
  50. package/src/capabilities/graph/inspect.verb.ts +3 -2
  51. package/src/capabilities/graph/query.verb.ts +3 -2
  52. package/src/capabilities/graph/runQuery.ts +8 -5
  53. package/src/capabilities/index.ts +22 -18
  54. package/src/capabilities/info/collectInfo.ts +10 -6
  55. package/src/capabilities/info/info.render.ts +3 -2
  56. package/src/capabilities/info/info.verb.ts +3 -2
  57. package/src/capabilities/lazy.test.ts +93 -2
  58. package/src/capabilities/ontology/verbs.ts +21 -21
  59. package/src/capabilities/prompt/prompt.test.ts +12 -6
  60. package/src/capabilities/prompt/verbs.ts +24 -11
  61. package/src/capabilities/resources/provider.ts +30 -11
  62. package/src/capabilities/resources/resources.test.ts +85 -29
  63. package/src/capabilities/setup/operations/setupGenerator.ts +16 -9
  64. package/src/capabilities/setup/operations/setupMcp.ts +2 -2
  65. package/src/capabilities/setup/operations/setupSkills.ts +3 -3
  66. package/src/capabilities/setup/setup.verb.ts +12 -5
  67. package/src/capabilities/setup/shell.ts +29 -3
  68. package/src/capabilities/skill/discover.ts +11 -5
  69. package/src/capabilities/skill/render.ts +4 -5
  70. package/src/capabilities/skill/verbs.ts +7 -7
  71. package/src/capabilities/sources/collectStatus.ts +55 -50
  72. package/src/capabilities/sources/index.ts +1 -1
  73. package/src/capabilities/sources/installSkills.ts +2 -2
  74. package/src/capabilities/sources/runUpdate.ts +113 -122
  75. package/src/capabilities/sources/sources.test.ts +111 -117
  76. package/src/capabilities/sources/status.render.test.ts +53 -24
  77. package/src/capabilities/sources/status.render.ts +24 -29
  78. package/src/capabilities/sources/status.verb.ts +15 -8
  79. package/src/capabilities/sources/types.ts +18 -25
  80. package/src/capabilities/sources/update.render.ts +9 -2
  81. package/src/capabilities/sources/update.verb.ts +12 -23
  82. package/src/capabilities/surface.test.ts +33 -10
  83. package/src/capabilities/tier.parity.test.ts +144 -0
  84. package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
  85. package/src/constants.test.ts +8 -6
  86. package/src/constants.ts +47 -21
  87. package/src/identity.test.ts +333 -0
  88. package/src/index.ts +2 -2
  89. package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +5 -2
  90. package/src/kernel/completion/emitScripts.test.ts +1 -1
  91. package/src/kernel/completion/emitScripts.ts +4 -2
  92. package/src/kernel/completion/entitySource.test.ts +185 -41
  93. package/src/kernel/completion/entitySource.ts +117 -107
  94. package/src/kernel/completion/model.test.ts +1 -1
  95. package/src/kernel/completion/model.ts +11 -0
  96. package/src/kernel/completion/safety.test.ts +82 -36
  97. package/src/kernel/completion/shellDrive.test.ts +592 -0
  98. package/src/kernel/completion/templates/fish.ts +28 -6
  99. package/src/kernel/completion/types.ts +3 -3
  100. package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
  101. package/src/kernel/config/defaults.test.ts +85 -0
  102. package/src/kernel/config/defaults.ts +12 -23
  103. package/src/kernel/config/evaluateProjectConfig.ts +2 -3
  104. package/src/kernel/config/findProjectConfig.ts +4 -3
  105. package/src/kernel/config/firstRun.ts +12 -5
  106. package/src/kernel/config/globalConfig.ts +2 -2
  107. package/src/kernel/config/index.ts +4 -5
  108. package/src/kernel/config/paths.ts +17 -17
  109. package/src/kernel/config/readConfig.test.ts +242 -6
  110. package/src/kernel/config/readConfig.ts +8 -8
  111. package/src/kernel/config/schema.test.ts +50 -0
  112. package/src/kernel/config/schema.ts +97 -10
  113. package/src/kernel/config/types.ts +73 -19
  114. package/src/kernel/copy.test.ts +453 -0
  115. package/src/kernel/error/PragmaError.test.ts +1 -1
  116. package/src/kernel/error/constants.ts +3 -2
  117. package/src/kernel/error/index.ts +1 -1
  118. package/src/kernel/error/recovery.test.ts +22 -45
  119. package/src/kernel/error/recovery.ts +22 -27
  120. package/src/kernel/error/types.ts +5 -4
  121. package/src/kernel/packs/collect.test.ts +195 -11
  122. package/src/kernel/packs/collect.ts +213 -58
  123. package/src/kernel/packs/compile.test.ts +163 -7
  124. package/src/kernel/packs/compile.ts +15 -12
  125. package/src/kernel/packs/disclosure.test.ts +5 -2
  126. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
  127. package/src/kernel/packs/graphqlSource.test.ts +6 -3
  128. package/src/kernel/packs/iri.ts +5 -7
  129. package/src/kernel/packs/renderPack.ts +5 -3
  130. package/src/kernel/packs/resolveEntity.ts +21 -17
  131. package/src/kernel/packs/runBodies.ts +15 -14
  132. package/src/kernel/packs/schema.ts +96 -10
  133. package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
  134. package/src/kernel/packs/sparql/runSelect.ts +73 -34
  135. package/src/kernel/packs/toolDescription.test.ts +10 -2
  136. package/src/kernel/packs/types.ts +39 -4
  137. package/src/kernel/packs/uniqueness.ts +4 -4
  138. package/src/kernel/project/cli/buildProgram.test.ts +9 -4
  139. package/src/kernel/project/cli/dispatch.test.ts +67 -0
  140. package/src/kernel/project/cli/dispatch.ts +34 -12
  141. package/src/kernel/project/cli/helpFormat.ts +1 -1
  142. package/src/kernel/project/cli/rootHelp.ts +105 -105
  143. package/src/kernel/project/mcp/buildServer.ts +11 -6
  144. package/src/kernel/project/mcp/instructions.ts +11 -6
  145. package/src/kernel/project/mcp/parity.test.ts +39 -1
  146. package/src/kernel/project/mcp/prompts/prompts.test.ts +120 -5
  147. package/src/kernel/project/mcp/prompts/provider.ts +22 -8
  148. package/src/kernel/project/mcp/prompts/source.ts +68 -67
  149. package/src/kernel/project/mcp/registerVerb.ts +25 -6
  150. package/src/kernel/project/mcp/serve.ts +11 -3
  151. package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
  152. package/src/kernel/render/disclosure.ts +4 -2
  153. package/src/kernel/render/prefixes.test.ts +36 -0
  154. package/src/kernel/render/prefixes.ts +40 -13
  155. package/src/kernel/render/renderers.test.ts +1 -1
  156. package/src/kernel/runtime/graphpack/build.ts +29 -3
  157. package/src/kernel/runtime/graphpack/buildIndex.test.ts +46 -0
  158. package/src/kernel/runtime/graphpack/buildIndex.ts +52 -17
  159. package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -5
  160. package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -3
  161. package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
  162. package/src/kernel/runtime/graphpack/embedded.ts +37 -11
  163. package/src/kernel/runtime/graphpack/graphpack.test.ts +205 -5
  164. package/src/kernel/runtime/graphpack/hash.ts +3 -2
  165. package/src/kernel/runtime/graphpack/index.ts +1 -1
  166. package/src/kernel/runtime/graphpack/manifest.ts +114 -12
  167. package/src/kernel/runtime/graphpack/read.ts +4 -9
  168. package/src/kernel/runtime/graphpack/schemas.ts +74 -0
  169. package/src/kernel/runtime/graphpack/stories.ts +84 -0
  170. package/src/kernel/runtime/graphpack/types.ts +61 -48
  171. package/src/kernel/runtime/paths.ts +64 -15
  172. package/src/kernel/runtime/probe.test.ts +8 -4
  173. package/src/kernel/runtime/readEntity.ts +6 -5
  174. package/src/kernel/runtime/refs/gitOps.ts +1 -19
  175. package/src/kernel/runtime/refs/parseRef.ts +7 -7
  176. package/src/kernel/runtime/refs/resolve.ts +78 -62
  177. package/src/kernel/runtime/resolveSources.test.ts +63 -24
  178. package/src/kernel/runtime/resolveSources.ts +36 -25
  179. package/src/kernel/runtime/storeReadiness.ts +9 -8
  180. package/src/kernel/runtime/storeless.test.ts +1 -1
  181. package/src/kernel/runtime/wasmEmbed.test.ts +22 -19
  182. package/src/kernel/spec/emitReference.test.ts +3 -3
  183. package/src/kernel/spec/emitReference.ts +144 -11
  184. package/src/kernel/spec/emitSurface.ts +9 -5
  185. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  186. package/src/kernel/spec/surfaceConformance.ts +8 -0
  187. package/src/kernel/spec/types.ts +29 -9
  188. package/src/kernel/spec/validate.ts +2 -1
  189. package/src/kernel/vocabulary.test.ts +164 -0
  190. package/src/kernel/vocabulary.ts +146 -0
  191. package/src/testing/PARITY_GAPS.ts +8 -2
  192. package/src/testing/behavioral/README.md +3 -3
  193. package/src/testing/behavioral/completion.test.ts +19 -6
  194. package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
  195. package/src/testing/behavioral/errorMatrix.storeless.test.ts +1 -1
  196. package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
  197. package/src/testing/behavioral/harness.foundation.test.ts +14 -5
  198. package/src/testing/behavioral/journeys.cli.test.ts +116 -75
  199. package/src/testing/behavioral/journeys.defaultPack.test.ts +82 -176
  200. package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
  201. package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +9 -6
  202. package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
  203. package/src/testing/behavioral/liveReadSurface.ts +4 -3
  204. package/src/testing/behavioral/mcpSurface.test.ts +9 -4
  205. package/src/testing/behavioral/staleCommands.test.ts +10 -0
  206. package/src/testing/eval/__snapshots__/eval.test.ts.snap +7 -7
  207. package/src/testing/eval/cases/assembly.ts +13 -6
  208. package/src/testing/eval/cases/effectful.ts +25 -13
  209. package/src/testing/eval/cases/readNouns.ts +22 -20
  210. package/src/testing/eval/cases/stable.ts +5 -1
  211. package/src/testing/eval/eval.test.ts +7 -1
  212. package/src/testing/fixtures/fixtureCapability.ts +45 -1
  213. package/src/testing/fixtures/graph/canonical.ts +6 -3
  214. package/src/testing/fixtures/graph/defaultPack.ts +6 -5
  215. package/src/testing/fixtures/graph/owlExport.ts +7 -5
  216. package/src/testing/helpers/fixtureGraph.ts +33 -16
  217. package/src/testing/helpers/packRuntime.ts +2 -3
  218. package/src/testing/helpers/projectCli.ts +2 -1
  219. package/src/testing/helpers/projectMcp.ts +6 -0
  220. package/src/testing/perf/budgets.test.ts +4 -0
  221. package/src/testing/perf/budgets.ts +22 -7
  222. package/src/testing/perf/globalSetup.ts +121 -6
  223. package/src/types.ts +2 -2
  224. package/src/capabilities/block/blockList.test.ts +0 -79
  225. package/src/capabilities/block/blockList.verb.ts +0 -163
  226. package/src/capabilities/block/index.ts +0 -25
  227. package/src/capabilities/block/pack.ts +0 -166
  228. package/src/capabilities/block/parity.test.ts +0 -187
  229. package/src/capabilities/block/tierChain.ts +0 -78
  230. package/src/capabilities/colophon/pragmaColophon.ts +0 -66
  231. package/src/capabilities/modifier/index.ts +0 -15
  232. package/src/capabilities/modifier/pack.ts +0 -64
  233. package/src/capabilities/standard/index.ts +0 -18
  234. package/src/capabilities/standard/pack.ts +0 -128
  235. package/src/capabilities/tier/index.ts +0 -19
  236. package/src/capabilities/tier/lookup.render.test.ts +0 -72
  237. package/src/capabilities/tier/lookup.render.ts +0 -59
  238. package/src/capabilities/tier/lookup.test.ts +0 -83
  239. package/src/capabilities/tier/lookup.verb.ts +0 -44
  240. package/src/capabilities/tier/pack.ts +0 -28
  241. package/src/capabilities/tier/runLookup.ts +0 -87
  242. package/src/capabilities/token/addConfig.render.ts +0 -27
  243. package/src/capabilities/token/addConfig.test.ts +0 -62
  244. package/src/capabilities/token/addConfig.verb.ts +0 -40
  245. package/src/capabilities/token/index.ts +0 -19
  246. package/src/capabilities/token/pack.ts +0 -58
  247. package/src/capabilities/token/runAddConfig.ts +0 -95
  248. package/src/kernel/completion/bashDrive.test.ts +0 -73
  249. package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
  250. package/src/kernel/runtime/lock.ts +0 -90
@@ -40,6 +40,17 @@ import type {
40
40
  * The allowlist every inlined completion token must match: an alphanumeric
41
41
  * head, then word/URI-ish characters. No whitespace, quotes, `$`, backticks,
42
42
  * semicolons, or globs — nothing a shell could expand or split.
43
+ *
44
+ * KNOWN GAP, deliberately not closed: it admits shell RESERVED WORDS — `esac`,
45
+ * `in`, `do`, `done`, `fi`. A noun so named emits a `case` arm bash and zsh both
46
+ * refuse to parse, so `_pragma` is never defined and the user gets NO completion
47
+ * at all rather than a wrong candidate (`fish -n` does not catch it either). The
48
+ * gap is owned by the syntax gates, not by this regex: `shellDrive.test.ts`
49
+ * runs `bash -n`/`zsh -n` over the emitted scripts, so the day a live noun is
50
+ * named `done` the build fails loudly and names the file. A denylist here would
51
+ * be new production code guarding a case nothing in the tree exhibits — and a
52
+ * partial one (reserved words differ across families and versions) reads as
53
+ * more protection than it gives.
43
54
  */
44
55
  export const SAFE_TOKEN_RE = /^[A-Za-z0-9][A-Za-z0-9@/:._+-]*$/;
45
56
 
@@ -1,7 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, mkdtempSync, readdirSync, readFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
- import { dirname, join, resolve } from "node:path";
4
+ import { dirname, join, relative, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { createStore } from "@canonical/ke";
7
7
  import { describe, expect, it, vi } from "vitest";
@@ -207,6 +207,8 @@ function staticImportGraph(
207
207
  }
208
208
 
209
209
  const here = dirname(fileURLToPath(import.meta.url));
210
+ /** The package root, so a failure names `src/…` instead of an absolute path. */
211
+ const pkgRoot = resolve(here, "../../..");
210
212
  const has = (graph: Set<string>, fragment: string): boolean =>
211
213
  [...graph].some((file) => file.includes(fragment));
212
214
 
@@ -224,16 +226,25 @@ describe("storeless guarantee (PROTECTED)", () => {
224
226
  // Runtime boot (bootRuntime) — never on the completion path.
225
227
  expect(has(graph, "kernel/runtime/boot.ts")).toBe(false);
226
228
  // The config LAYER (reader, evaluator, paths, schema) — storeless means
227
- // no read. PR2's pure-leaf `config/types.ts` (the ConfigLayers type,
228
- // imported type-only via VerbSpec.run's PragmaRuntime and erased at
229
- // runtime) is the lone tolerated seam — as spec/types.ts and
230
- // runtime/types.ts already sit in this graph as type-only.
231
- const configModules = [...graph].filter(
232
- (file) =>
233
- file.includes("kernel/config/") &&
234
- !file.endsWith("kernel/config/types.ts"),
235
- );
236
- expect(configModules).toEqual([]);
229
+ // no read. Its tolerated seams are POSITIVE-LISTED, not filtered out by a
230
+ // path fragment: the distribution config sits at the package root, so a
231
+ // `kernel/config/`-shaped filter would not see it and this guard would
232
+ // keep passing after it stopped being true.
233
+ // - `kernel/config/types.ts` — the ConfigLayers type, imported type-only
234
+ // via VerbSpec.run's PragmaRuntime and erased at runtime, as
235
+ // spec/types.ts and runtime/types.ts already are in this graph.
236
+ // - `pragma.conf.ts` — the distribution config, statically imported by
237
+ // `constants.ts` (identity), `render/prefixes.ts` (the domain
238
+ // namespaces) and `kernel/vocabulary.ts` (the domain terms). Safe only
239
+ // while it stays inert data; `capabilities/lazy.test.ts` pins that.
240
+ const configModules = [...graph]
241
+ .filter((file) => /config|\.conf\./.test(file))
242
+ .map((file) => relative(pkgRoot, file))
243
+ .sort();
244
+ expect(configModules).toEqual([
245
+ "pragma.conf.ts",
246
+ "src/kernel/config/types.ts",
247
+ ]);
237
248
  // zod stays off the fast path (validate.ts is the registration seam).
238
249
  expect(has(graph, "kernel/spec/validate.ts")).toBe(false);
239
250
  // No store/graph modules, present or future.
@@ -266,7 +277,7 @@ describe("storeless guarantee (PROTECTED)", () => {
266
277
  required: true,
267
278
  complete: {
268
279
  kind: "names",
269
- source: { from: "index", type: "ex:Component" },
280
+ source: { from: "index", type: "ds:Component" },
270
281
  },
271
282
  },
272
283
  ],
@@ -286,23 +297,23 @@ describe("storeless guarantee (PROTECTED)", () => {
286
297
  } as VerbSpec,
287
298
  ],
288
299
  };
289
- // Fresh cwd → no lock → the reader falls back to the embedded pack index.
300
+ // Fresh cwd → no pointer → the reader falls back to the embedded index.
290
301
  const cwd = mkdtempSync(join(tmpdir(), "pragma-storespy-"));
291
302
  const matches = await runComplete(
292
- ["block", "lookup", "ex:B"],
303
+ ["block", "lookup", "ds:global.component.but"],
293
304
  [lookupModule],
294
305
  indexCompletionEnv(cwd),
295
306
  );
296
307
  // It DID read the index (a real name came back)…
297
- expect(matches).toEqual(["ex:Button"]);
308
+ expect(matches).toContain("ds:global.component.button");
298
309
  // …and it did so without ever constructing the store.
299
310
  expect(vi.mocked(createStore)).not.toHaveBeenCalled();
300
311
  });
301
312
 
302
- it("the non-index name sources resolve without constructing the store", async () => {
303
- // skills (filesystem), prompts/tiers (index filter), prefixes (index ∪
304
- // default map) — every family completes storelessly against the live
305
- // capabilities. A fresh cwd → no lock → the embedded fallback index.
313
+ it("every declared name source resolves without constructing the store", async () => {
314
+ // skills (filesystem), prefixes (index ∪ default map) and the index's own
315
+ // `label`/`altNames` fields — every family completes storelessly against
316
+ // the live capabilities. A fresh cwd → no pointer → the embedded index.
306
317
  const cwd = mkdtempSync(join(tmpdir(), "pragma-storeless-src-"));
307
318
  const env = indexCompletionEnv(cwd);
308
319
 
@@ -311,16 +322,23 @@ describe("storeless guarantee (PROTECTED)", () => {
311
322
  runComplete(["ontology", "show", "d"], capabilities, env),
312
323
  ).resolves.toContain("ds");
313
324
 
314
- // skills / prompts / tiers: no data here, but the point is they never boot
315
- // the store — an empty list is the correct storeless answer.
325
+ // index (derived, no field): the tier lookup is a declared pack lookup
326
+ // (L-OPEN-9), so its candidates are the index's entity NAMES for the
327
+ // declared type — prefixed IRIs, like every other declared lookup's
328
+ // (D9-A). `ds:apps` is a real `ds:Tier` subject in the embedded index,
329
+ // and the lookup resolves a prefixed-IRI candidate directly.
330
+ await expect(
331
+ runComplete(["tier", "lookup", "ap"], capabilities, env),
332
+ ).resolves.toContain("ds:apps");
333
+
334
+ // skills (no skills root here) and prompt labels (this graph carries no
335
+ // prompt entities) have nothing to offer, and the honest storeless answer
336
+ // is an empty list rather than a store boot.
316
337
  for (const words of [
317
338
  ["skill", "lookup", "do"],
318
339
  ["prompt", "lookup", "bu"],
319
- ["tier", "lookup", "ap"],
320
340
  ]) {
321
- await expect(
322
- runComplete(words, capabilities, env),
323
- ).resolves.toBeInstanceOf(Array);
341
+ await expect(runComplete(words, capabilities, env)).resolves.toEqual([]);
324
342
  }
325
343
 
326
344
  expect(vi.mocked(createStore)).not.toHaveBeenCalled();
@@ -328,31 +346,59 @@ describe("storeless guarantee (PROTECTED)", () => {
328
346
 
329
347
  it("the spawned __complete fast path answers without touching any state", () => {
330
348
  // The perf globalSetup guarantees dist/pragma exists.
349
+ // A FRESH INSTALL, reproduced: nothing is inherited from this process's
350
+ // environment, the cwd holds no config, and $HOME plus all three XDG roots
351
+ // are empty temps. Anything the binary answers here it answered from
352
+ // itself. (The suite before this inherited `...process.env`, so it could
353
+ // not tell a compiled-in answer from an ambient one.)
331
354
  const binary = fileURLToPath(
332
355
  new URL("../../../dist/pragma", import.meta.url),
333
356
  );
357
+ const home = mkdtempSync(join(tmpdir(), "pragma-storeless-home-"));
334
358
  const xdgConfig = mkdtempSync(join(tmpdir(), "pragma-storeless-cfg-"));
335
359
  const xdgState = mkdtempSync(join(tmpdir(), "pragma-storeless-state-"));
336
360
  const xdgCache = mkdtempSync(join(tmpdir(), "pragma-storeless-cache-"));
361
+ const emptyCwd = mkdtempSync(join(tmpdir(), "pragma-storeless-cwd-"));
362
+ const env = {
363
+ HOME: home,
364
+ XDG_CONFIG_HOME: xdgConfig,
365
+ XDG_STATE_HOME: xdgState,
366
+ XDG_CACHE_HOME: xdgCache,
367
+ };
337
368
 
338
- const result = spawnSync(binary, ["__complete", "--", "co"], {
369
+ const nouns = spawnSync(binary, ["__complete", "--", "co"], {
339
370
  encoding: "utf-8",
340
- env: {
341
- ...process.env,
342
- XDG_CONFIG_HOME: xdgConfig,
343
- XDG_STATE_HOME: xdgState,
344
- XDG_CACHE_HOME: xdgCache,
345
- },
371
+ cwd: emptyCwd,
372
+ env,
346
373
  });
374
+ expect(nouns.status).toBe(0);
375
+ expect(nouns.stdout).toBe("colophon\nconfig\n");
376
+ expect(nouns.stderr).toBe("");
377
+
378
+ // The headline guarantee of the embedded pack: a user who has installed
379
+ // the binary and nothing else gets ENTITY candidates on the first TAB.
380
+ // Every other pin on the embedded index runs in-process, where a bundler
381
+ // change that made `pack.index.generated.ts` unreachable FROM THE BINARY
382
+ // would leave the whole suite green.
383
+ const entities = spawnSync(
384
+ binary,
385
+ ["__complete", "--", "block", "lookup", "ds:global.component.but"],
386
+ { encoding: "utf-8", cwd: emptyCwd, env },
387
+ );
388
+ expect(entities.status).toBe(0);
389
+ expect(entities.stderr).toBe("");
390
+ expect(entities.stdout.split("\n").at(0)).toBe(
391
+ "ds:global.component.button",
392
+ );
347
393
 
348
- expect(result.status).toBe(0);
349
- expect(result.stdout).toBe("colophon\nconfig\n");
350
- expect(result.stderr).toBe("");
351
394
  // Storeless and config-free: no first-run marker, no config cache, no
352
- // store artifacts — the fast path must leave the environment untouched.
395
+ // store artifacts — the fast path must leave the environment untouched,
396
+ // after BOTH kinds of context.
397
+ expect(readdirSync(home)).toEqual([]);
353
398
  expect(readdirSync(xdgConfig)).toEqual([]);
354
399
  expect(readdirSync(xdgState)).toEqual([]);
355
400
  expect(readdirSync(xdgCache)).toEqual([]);
401
+ expect(readdirSync(emptyCwd)).toEqual([]);
356
402
  });
357
403
 
358
404
  it("the spawned fast path emits zero bytes for zero candidates, exit 0", () => {