@canonical/pragma-cli 0.32.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 (295) hide show
  1. package/README.md +72 -0
  2. package/dist/pragma +0 -0
  3. package/package.json +19 -15
  4. package/pragma.conf.ts +664 -0
  5. package/src/bin.ts +15 -6
  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.test.ts +10 -1
  9. package/src/capabilities/capabilities/capabilities.verb.ts +32 -14
  10. package/src/capabilities/capabilities/catalog.ts +20 -8
  11. package/src/capabilities/capabilities/hints.ts +21 -36
  12. package/src/capabilities/capabilities/types.ts +2 -0
  13. package/src/capabilities/colophon/collectColophon.ts +34 -19
  14. package/src/capabilities/colophon/colophon.render.test.ts +31 -4
  15. package/src/capabilities/colophon/colophon.render.ts +13 -4
  16. package/src/capabilities/colophon/colophon.test.ts +3 -3
  17. package/src/capabilities/colophon/colophon.verb.ts +11 -4
  18. package/src/capabilities/colophon/markdownTerminal.ts +33 -14
  19. package/src/capabilities/colophon/types.ts +16 -7
  20. package/src/capabilities/config/collectConfigShow.test.ts +57 -0
  21. package/src/capabilities/config/collectConfigShow.ts +41 -4
  22. package/src/capabilities/config/fields.ts +10 -108
  23. package/src/capabilities/config/set.test.ts +6 -6
  24. package/src/capabilities/config/set.verb.ts +12 -8
  25. package/src/capabilities/config/show.render.test.ts +137 -0
  26. package/src/capabilities/config/show.render.ts +57 -16
  27. package/src/capabilities/config/show.verb.ts +9 -8
  28. package/src/capabilities/config/types.ts +8 -0
  29. package/src/capabilities/create/byteEquality.subprocess.test.ts +1 -1
  30. package/src/capabilities/create/byteEquality.test.ts +82 -101
  31. package/src/capabilities/create/compiledCreate.subprocess.test.ts +53 -16
  32. package/src/capabilities/create/constants.ts +75 -0
  33. package/src/capabilities/create/create.mcp.test.ts +2 -2
  34. package/src/capabilities/create/create.test.ts +192 -16
  35. package/src/capabilities/create/create.verb.ts +208 -79
  36. package/src/capabilities/create/index.ts +2 -10
  37. package/src/capabilities/create/pickGenerator.ts +34 -28
  38. package/src/capabilities/create/templates.embedded.generated.ts +4 -29
  39. package/src/capabilities/create/types.ts +10 -6
  40. package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
  41. package/src/capabilities/distribution.test.ts +120 -0
  42. package/src/capabilities/distribution.ts +59 -0
  43. package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
  44. package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
  45. package/src/capabilities/doctor/checks/checkMcpCommands.ts +6 -0
  46. package/src/capabilities/doctor/checks/checkMcpConfigured.ts +15 -8
  47. package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
  48. package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
  49. package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
  50. package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
  51. package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
  52. package/src/capabilities/doctor/checks/deriveBand.test.ts +42 -0
  53. package/src/capabilities/doctor/checks/deriveBand.ts +28 -0
  54. package/src/capabilities/doctor/doctor.render.test.ts +170 -0
  55. package/src/capabilities/doctor/doctor.render.ts +117 -34
  56. package/src/capabilities/doctor/doctor.test.ts +68 -9
  57. package/src/capabilities/doctor/doctor.verb.ts +6 -2
  58. package/src/capabilities/doctor/runChecks.ts +33 -9
  59. package/src/capabilities/doctor/types.ts +16 -1
  60. package/src/capabilities/emptyRecovery.test.ts +32 -15
  61. package/src/capabilities/graph/inspect.verb.ts +3 -2
  62. package/src/capabilities/graph/query.verb.ts +3 -2
  63. package/src/capabilities/graph/runQuery.ts +8 -5
  64. package/src/capabilities/index.ts +22 -18
  65. package/src/capabilities/info/collectInfo.ts +10 -6
  66. package/src/capabilities/info/info.render.ts +3 -2
  67. package/src/capabilities/info/info.verb.ts +3 -2
  68. package/src/capabilities/lazy.test.ts +93 -2
  69. package/src/capabilities/ontology/index.ts +10 -3
  70. package/src/capabilities/ontology/ontology.test.ts +157 -5
  71. package/src/capabilities/ontology/verbs.ts +203 -104
  72. package/src/capabilities/prompt/prompt.render.test.ts +94 -0
  73. package/src/capabilities/prompt/prompt.render.ts +23 -6
  74. package/src/capabilities/prompt/prompt.test.ts +68 -1
  75. package/src/capabilities/prompt/verbs.ts +24 -11
  76. package/src/capabilities/reference.test.ts +32 -0
  77. package/src/capabilities/resources/provider.ts +58 -25
  78. package/src/capabilities/resources/resources.test.ts +176 -21
  79. package/src/capabilities/scopeTypeSync.test.ts +37 -0
  80. package/src/capabilities/setup/index.ts +7 -1
  81. package/src/capabilities/setup/operations/setupCompletions.ts +49 -16
  82. package/src/capabilities/setup/operations/setupGenerator.ts +166 -63
  83. package/src/capabilities/setup/operations/setupLsp.ts +56 -12
  84. package/src/capabilities/setup/operations/setupMcp.ts +216 -39
  85. package/src/capabilities/setup/operations/setupSkills.ts +10 -4
  86. package/src/capabilities/setup/setup.render.test.ts +109 -0
  87. package/src/capabilities/setup/setup.render.ts +61 -14
  88. package/src/capabilities/setup/setup.test.ts +352 -1
  89. package/src/capabilities/setup/setup.verb.ts +68 -9
  90. package/src/capabilities/setup/shell.ts +29 -3
  91. package/src/capabilities/setup/types.ts +59 -2
  92. package/src/capabilities/shared/bands.ts +16 -0
  93. package/src/capabilities/skill/discover.ts +11 -5
  94. package/src/capabilities/skill/render.ts +4 -5
  95. package/src/capabilities/skill/verbs.ts +7 -7
  96. package/src/capabilities/sources/collectStatus.ts +69 -49
  97. package/src/capabilities/sources/index.ts +1 -1
  98. package/src/capabilities/sources/installSkills.ts +2 -2
  99. package/src/capabilities/sources/runUpdate.ts +146 -98
  100. package/src/capabilities/sources/sources.test.ts +264 -84
  101. package/src/capabilities/sources/status.render.test.ts +94 -0
  102. package/src/capabilities/sources/status.render.ts +55 -33
  103. package/src/capabilities/sources/status.verb.ts +15 -8
  104. package/src/capabilities/sources/types.ts +18 -25
  105. package/src/capabilities/sources/update.render.ts +9 -2
  106. package/src/capabilities/sources/update.verb.ts +12 -23
  107. package/src/capabilities/surface.test.ts +65 -17
  108. package/src/capabilities/tier.parity.test.ts +144 -0
  109. package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
  110. package/src/constants.test.ts +9 -7
  111. package/src/constants.ts +49 -23
  112. package/src/identity.test.ts +333 -0
  113. package/src/index.ts +2 -2
  114. package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +18 -16
  115. package/src/kernel/completion/complete.test.ts +3 -5
  116. package/src/kernel/completion/emitScripts.test.ts +1 -1
  117. package/src/kernel/completion/emitScripts.ts +4 -2
  118. package/src/kernel/completion/entitySource.test.ts +185 -41
  119. package/src/kernel/completion/entitySource.ts +131 -113
  120. package/src/kernel/completion/model.test.ts +3 -3
  121. package/src/kernel/completion/model.ts +11 -1
  122. package/src/kernel/completion/parse.test.ts +2 -4
  123. package/src/kernel/completion/safety.test.ts +82 -36
  124. package/src/kernel/completion/shellDrive.test.ts +592 -0
  125. package/src/kernel/completion/templates/fish.ts +28 -6
  126. package/src/kernel/completion/types.ts +3 -3
  127. package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
  128. package/src/kernel/config/defaults.test.ts +85 -0
  129. package/src/kernel/config/defaults.ts +12 -23
  130. package/src/kernel/config/evaluateProjectConfig.ts +2 -3
  131. package/src/kernel/config/findProjectConfig.ts +4 -3
  132. package/src/kernel/config/firstRun.ts +12 -5
  133. package/src/kernel/config/globalConfig.ts +2 -2
  134. package/src/kernel/config/index.ts +4 -5
  135. package/src/kernel/config/paths.ts +17 -17
  136. package/src/kernel/config/readConfig.test.ts +242 -6
  137. package/src/kernel/config/readConfig.ts +8 -8
  138. package/src/kernel/config/schema.test.ts +50 -0
  139. package/src/kernel/config/schema.ts +97 -10
  140. package/src/kernel/config/types.ts +73 -19
  141. package/src/kernel/copy.test.ts +453 -0
  142. package/src/kernel/error/PragmaError.test.ts +5 -5
  143. package/src/kernel/error/constants.ts +3 -2
  144. package/src/kernel/error/index.ts +1 -1
  145. package/src/kernel/error/recovery.test.ts +22 -45
  146. package/src/kernel/error/recovery.ts +22 -27
  147. package/src/kernel/error/renderError.ts +1 -1
  148. package/src/kernel/error/types.ts +5 -4
  149. package/src/kernel/packs/collect.test.ts +195 -11
  150. package/src/kernel/packs/collect.ts +213 -58
  151. package/src/kernel/packs/compile.test.ts +163 -7
  152. package/src/kernel/packs/compile.ts +15 -12
  153. package/src/kernel/packs/disclosure.test.ts +5 -2
  154. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
  155. package/src/kernel/packs/graphqlSource.test.ts +6 -3
  156. package/src/kernel/packs/iri.ts +5 -7
  157. package/src/kernel/packs/renderPack.ts +5 -3
  158. package/src/kernel/packs/resolveEntity.ts +21 -17
  159. package/src/kernel/packs/runBodies.ts +15 -14
  160. package/src/kernel/packs/schema.ts +96 -10
  161. package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
  162. package/src/kernel/packs/sparql/runSelect.ts +73 -34
  163. package/src/kernel/packs/toolDescription.test.ts +10 -2
  164. package/src/kernel/packs/types.ts +39 -4
  165. package/src/kernel/packs/uniqueness.ts +4 -4
  166. package/src/kernel/project/cli/buildProgram.test.ts +60 -11
  167. package/src/kernel/project/cli/buildProgram.ts +11 -0
  168. package/src/kernel/project/cli/dispatch.test.ts +67 -0
  169. package/src/kernel/project/cli/dispatch.ts +34 -12
  170. package/src/kernel/project/cli/globalFlags.test.ts +12 -4
  171. package/src/kernel/project/cli/globalFlags.ts +14 -14
  172. package/src/kernel/project/cli/helpFormat.ts +1 -1
  173. package/src/kernel/project/cli/rootHelp.ts +109 -107
  174. package/src/kernel/project/mcp/buildServer.ts +11 -6
  175. package/src/kernel/project/mcp/instructions.ts +12 -7
  176. package/src/kernel/project/mcp/mcpError.ts +52 -0
  177. package/src/kernel/project/mcp/parity.test.ts +39 -1
  178. package/src/kernel/project/mcp/prompts/prompts.test.ts +128 -5
  179. package/src/kernel/project/mcp/prompts/provider.ts +51 -7
  180. package/src/kernel/project/mcp/prompts/source.ts +68 -67
  181. package/src/kernel/project/mcp/registerVerb.ts +25 -6
  182. package/src/kernel/project/mcp/serve.ts +11 -3
  183. package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
  184. package/src/kernel/render/disclosure.ts +4 -2
  185. package/src/kernel/render/formatters.ts +5 -4
  186. package/src/kernel/render/prefixes.test.ts +36 -0
  187. package/src/kernel/render/prefixes.ts +40 -13
  188. package/src/kernel/render/renderers.test.ts +35 -1
  189. package/src/kernel/render/renderers.ts +25 -7
  190. package/src/kernel/render/style.test.ts +31 -0
  191. package/src/kernel/render/style.ts +74 -0
  192. package/src/kernel/runtime/graphpack/build.ts +57 -3
  193. package/src/kernel/runtime/graphpack/buildIndex.test.ts +149 -0
  194. package/src/kernel/runtime/graphpack/buildIndex.ts +173 -50
  195. package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -8
  196. package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -4
  197. package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
  198. package/src/kernel/runtime/graphpack/embedded.ts +37 -11
  199. package/src/kernel/runtime/graphpack/graphpack.test.ts +274 -6
  200. package/src/kernel/runtime/graphpack/hash.ts +3 -2
  201. package/src/kernel/runtime/graphpack/index.ts +1 -1
  202. package/src/kernel/runtime/graphpack/manifest.ts +114 -12
  203. package/src/kernel/runtime/graphpack/read.ts +32 -22
  204. package/src/kernel/runtime/graphpack/schemas.ts +74 -0
  205. package/src/kernel/runtime/graphpack/stories.ts +84 -0
  206. package/src/kernel/runtime/graphpack/types.ts +72 -45
  207. package/src/kernel/runtime/loadSession.ts +4 -12
  208. package/src/kernel/runtime/paths.ts +64 -15
  209. package/src/kernel/runtime/probe.test.ts +8 -4
  210. package/src/kernel/runtime/readEntity.ts +6 -5
  211. package/src/kernel/runtime/refs/gitOps.ts +1 -19
  212. package/src/kernel/runtime/refs/parseRef.ts +7 -7
  213. package/src/kernel/runtime/refs/resolve.ts +141 -65
  214. package/src/kernel/runtime/resolveSources.test.ts +63 -24
  215. package/src/kernel/runtime/resolveSources.ts +36 -25
  216. package/src/kernel/runtime/storeReadiness.ts +62 -0
  217. package/src/kernel/runtime/storeless.test.ts +1 -1
  218. package/src/kernel/runtime/types.ts +1 -1
  219. package/src/kernel/runtime/wasmEmbed.test.ts +25 -20
  220. package/src/kernel/spec/emitReference.test.ts +196 -0
  221. package/src/kernel/spec/emitReference.ts +624 -0
  222. package/src/kernel/spec/emitSurface.ts +13 -7
  223. package/src/kernel/spec/index.ts +2 -0
  224. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  225. package/src/kernel/spec/surfaceConformance.ts +8 -0
  226. package/src/kernel/spec/types.ts +29 -9
  227. package/src/kernel/spec/validate.ts +2 -1
  228. package/src/kernel/vocabulary.test.ts +164 -0
  229. package/src/kernel/vocabulary.ts +146 -0
  230. package/src/testing/PARITY_GAPS.ts +9 -3
  231. package/src/testing/behavioral/README.md +3 -3
  232. package/src/testing/behavioral/autoLlm.e2e.test.ts +12 -2
  233. package/src/testing/behavioral/completion.test.ts +21 -8
  234. package/src/testing/behavioral/disclosure.test.ts +12 -0
  235. package/src/testing/behavioral/docExamples.test.ts +283 -0
  236. package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
  237. package/src/testing/behavioral/errorMatrix.storeless.test.ts +3 -2
  238. package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
  239. package/src/testing/behavioral/harness.foundation.test.ts +14 -5
  240. package/src/testing/behavioral/journeys.cli.test.ts +116 -73
  241. package/src/testing/behavioral/journeys.defaultPack.test.ts +477 -0
  242. package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
  243. package/src/testing/behavioral/journeys.interactiveTty.e2e.test.ts +66 -0
  244. package/src/testing/behavioral/journeys.livePack.e2e.test.ts +129 -0
  245. package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +179 -0
  246. package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
  247. package/src/testing/behavioral/liveReadSurface.ts +4 -3
  248. package/src/testing/behavioral/mcpSurface.test.ts +10 -5
  249. package/src/testing/behavioral/staleCommands.test.ts +86 -0
  250. package/src/testing/eval/__snapshots__/eval.test.ts.snap +9 -9
  251. package/src/testing/eval/cases/assembly.ts +16 -9
  252. package/src/testing/eval/cases/effectful.ts +31 -16
  253. package/src/testing/eval/cases/readNouns.ts +23 -18
  254. package/src/testing/eval/cases/stable.ts +5 -1
  255. package/src/testing/eval/eval.test.ts +7 -1
  256. package/src/testing/fixtures/fixtureCapability.ts +201 -1
  257. package/src/testing/fixtures/graph/canonical.ts +6 -3
  258. package/src/testing/fixtures/graph/defaultPack.ts +179 -0
  259. package/src/testing/fixtures/graph/owlExport.ts +97 -0
  260. package/src/testing/helpers/extractSnippets.ts +29 -0
  261. package/src/testing/helpers/fixtureGraph.ts +33 -16
  262. package/src/testing/helpers/golden.ts +1 -1
  263. package/src/testing/helpers/packRuntime.ts +2 -3
  264. package/src/testing/helpers/projectCli.ts +2 -1
  265. package/src/testing/helpers/projectMcp.ts +6 -0
  266. package/src/testing/perf/budgets.test.ts +4 -0
  267. package/src/testing/perf/budgets.ts +22 -7
  268. package/src/testing/perf/globalSetup.ts +121 -6
  269. package/src/types.ts +2 -2
  270. package/src/capabilities/block/blockList.verb.ts +0 -150
  271. package/src/capabilities/block/index.ts +0 -25
  272. package/src/capabilities/block/pack.ts +0 -155
  273. package/src/capabilities/block/parity.test.ts +0 -180
  274. package/src/capabilities/block/tierChain.ts +0 -78
  275. package/src/capabilities/colophon/pragmaColophon.ts +0 -66
  276. package/src/capabilities/config/field.test.ts +0 -188
  277. package/src/capabilities/modifier/index.ts +0 -15
  278. package/src/capabilities/modifier/pack.ts +0 -64
  279. package/src/capabilities/standard/index.ts +0 -18
  280. package/src/capabilities/standard/pack.ts +0 -128
  281. package/src/capabilities/tier/index.ts +0 -19
  282. package/src/capabilities/tier/lookup.render.ts +0 -34
  283. package/src/capabilities/tier/lookup.test.ts +0 -68
  284. package/src/capabilities/tier/lookup.verb.ts +0 -44
  285. package/src/capabilities/tier/pack.ts +0 -28
  286. package/src/capabilities/tier/runLookup.ts +0 -59
  287. package/src/capabilities/token/addConfig.render.ts +0 -27
  288. package/src/capabilities/token/addConfig.test.ts +0 -62
  289. package/src/capabilities/token/addConfig.verb.ts +0 -40
  290. package/src/capabilities/token/index.ts +0 -19
  291. package/src/capabilities/token/pack.ts +0 -58
  292. package/src/capabilities/token/runAddConfig.ts +0 -95
  293. package/src/kernel/completion/bashDrive.test.ts +0 -73
  294. package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
  295. package/src/kernel/runtime/lock.ts +0 -90
@@ -5,18 +5,19 @@
5
5
  *
6
6
  * - Preview (CLI `--dry-run`, or MCP without `confirm`): NETWORK-FREE. It reads
7
7
  * only the config, then hands back a plan-only Task listing the refs it would
8
- * resolve+build and the lock it would write — no git fetch, no compile, no
8
+ * resolve+build and the pointer it would write — no git fetch, no compile, no
9
9
  * cache write. This is what a dry-run / agent "preview" must be: side-effect
10
10
  * free and offline-safe.
11
- * - Real execution: the heavy work — resolve every configured package (git
11
+ * - Real execution: the heavy work — resolve every configured pack (git
12
12
  * clone/fetch, file verify, npm resolve), then build the ONE combined
13
13
  * content-addressed pack — runs eagerly (it is not expressible as a task
14
14
  * effect: the effect set is fs + exec, and the in-process compile is not an
15
- * effect at all). The returned Task models the one project mutation, the
16
- * `pragma.lock.json` write, with an undo that restores (or removes) the prior
17
- * lock. Under `--frozen` each package re-resolves to the lock's pinned
18
- * revision and keeps its `resolvedAt`, so an unchanged update rewrites a
19
- * byte-identical lock.
15
+ * effect at all). The returned Task models the mutations: the active-pack
16
+ * pointer write (undo restores or removes the prior pointer) and the package
17
+ * skill symlinks.
18
+ *
19
+ * Pinning a revision is the ref's job, not a flag's: put a SHA in the source
20
+ * (`git+https://…#<sha>`) and every update resolves to exactly that commit.
20
21
  */
21
22
 
22
23
  import {
@@ -29,23 +30,18 @@ import {
29
30
  type Task,
30
31
  writeFile,
31
32
  } from "@canonical/task";
32
- import { RECOVERY_CLI_PREFIX, VERSION } from "../../constants.js";
33
- import type { PackageEntry } from "../../kernel/config/types.js";
33
+ import { VERSION } from "../../constants.js";
34
34
  import { PragmaError } from "../../kernel/error/PragmaError.js";
35
35
  import { cliRecovery } from "../../kernel/error/recovery.js";
36
36
  import { buildPack } from "../../kernel/runtime/graphpack/build.js";
37
- import {
38
- type PragmaLock,
39
- readLock,
40
- serializeLock,
41
- } from "../../kernel/runtime/lock.js";
42
- import { LOCK_BASENAME, lockPath } from "../../kernel/runtime/paths.js";
37
+ import { activePackPath, readActivePack } from "../../kernel/runtime/paths.js";
43
38
  import type { PackageRef } from "../../kernel/runtime/refs/parseRef.js";
44
39
  import {
45
- parsePackageEntry,
40
+ parsePackDeclaration,
46
41
  redactUrl,
47
42
  } from "../../kernel/runtime/refs/parseRef.js";
48
43
  import {
44
+ detectPrefixClashes,
49
45
  harvestPrefixes,
50
46
  type ResolvedPackage,
51
47
  resolvePackage,
@@ -66,17 +62,43 @@ const CORE_PREFIXES: Readonly<Record<string, string>> = {
66
62
  dcterms: "http://purl.org/dc/terms/",
67
63
  };
68
64
 
69
- const entryName = (entry: PackageEntry): string =>
70
- typeof entry === "string" ? entry : entry.name;
65
+ /**
66
+ * The prefix map a pack is built with: core < the packs' own `@prefix` < config.
67
+ *
68
+ * A resolved package's own declarations are harvested from its TTL (ke does not
69
+ * fold parsed-Turtle prefixes into `store.prefixes`) and merged beneath config
70
+ * so the index compacts pack URIs to `pfx:Local` — the FROZEN `{name,type}`
71
+ * token contract — while config still wins any clash. The merged map is
72
+ * persisted in the manifest, so boot reads the same names.
73
+ *
74
+ * Exported because the bundler (`scripts/bundle.ts`) compiles the embedded pack
75
+ * through this exact precedence. If the two pipelines computed prefixes
76
+ * separately, the embed could compact entities differently from what the SAME
77
+ * config produces on a user's machine — a silent divergence in entity names.
78
+ *
79
+ * @param inputs - The resolved packages' labelled RDF sources.
80
+ * @param configPrefixes - The config layer's `prefixes`, if any.
81
+ * @returns The merged prefix map to build with.
82
+ */
83
+ export function buildPackPrefixes(
84
+ inputs: readonly { readonly content: string }[],
85
+ configPrefixes: Readonly<Record<string, string>> | undefined,
86
+ ): Record<string, string> {
87
+ return {
88
+ ...CORE_PREFIXES,
89
+ ...harvestPrefixes(inputs),
90
+ ...(configPrefixes ?? {}),
91
+ };
92
+ }
71
93
 
72
94
  /**
73
95
  * Build the NETWORK-FREE preview plan for `sources update`.
74
96
  *
75
97
  * A plan-only Task: it reads the config to list the refs a real update would
76
- * resolve and build, then models the single project mutation (the lock write)
77
- * as effects the dry-run interpreter can describe WITHOUT running them. No git,
78
- * no compile, no cache write — so `--dry-run` and an MCP plan-first call are
79
- * offline-safe. Config refs are parsed (a cheap, offline validity check) but
98
+ * resolve and build, then models the project mutation (the active-pack pointer
99
+ * write) as effects the dry-run interpreter can describe WITHOUT running them.
100
+ * No git, no compile, no cache write — so `--dry-run` and an MCP plan-first call
101
+ * are offline-safe. Config refs are parsed (a cheap, offline validity check) but
80
102
  * never resolved.
81
103
  *
82
104
  * @param runtime - The per-invocation runtime.
@@ -86,57 +108,51 @@ async function buildUpdatePlan(
86
108
  runtime: PragmaRuntime,
87
109
  ): Promise<Task<SourcesUpdateData>> {
88
110
  const layers = await runtime.loadConfig();
89
- const entries = layers.config.packages ?? [];
90
- const refs = entries.map(parsePackageEntry);
91
- const path = lockPath(runtime.cwd);
111
+ const entries = layers.config.packs ?? [];
112
+ const refs = entries.map(parsePackDeclaration);
92
113
 
93
114
  const data: SourcesUpdateData = {
94
115
  contentHash: "",
95
116
  reused: false,
96
- lockPath: path,
97
- packs: refs.map((ref) => ({ name: ref.pkg, resolved: "", sourceCount: 0 })),
98
- };
99
- // A representative (never-written) lock, so the previewed write shows a
100
- // plausible shape/size. Placeholders make clear nothing was resolved.
101
- const previewLock: PragmaLock = {
102
- version: 1,
103
- contentHash: "(resolved on execute)",
104
117
  packs: refs.map((ref) => ({
105
118
  name: ref.pkg,
106
- source: ref.source,
107
- resolved: "(resolved on execute)",
108
- resolvedAt: "(resolved on execute)",
119
+ resolved: "",
120
+ sourceCount: 0,
121
+ storyCount: 0,
109
122
  })),
110
123
  };
111
- const previewContent = serializeLock(previewLock);
112
124
 
113
125
  return gen(function* () {
114
126
  yield* $(
115
127
  info(
116
128
  refs.length > 0
117
- ? `Resolve and build ${refs.length} package(s): ${refs.map((ref) => ref.source).join(", ")}`
118
- : "No packages configured — the embedded pack answers store reads",
129
+ ? `Resolve and build ${refs.length} pack(s): ${refs.map((ref) => ref.source).join(", ")}`
130
+ : "No packs configured — the embedded pack answers store reads",
119
131
  ),
120
132
  );
121
- // The one project mutation, previewed (dry-run never executes it).
122
- yield* $(writeFile(path, previewContent));
133
+ // The project mutation, previewed (dry-run never executes it). The content
134
+ // is a placeholder: the real hash only exists once the build has run.
135
+ yield* $(
136
+ writeFile(activePackPath(runtime.cwd), "(the pack hash, on execute)"),
137
+ );
123
138
  return data;
124
139
  });
125
140
  }
126
141
 
127
142
  /**
128
- * Resolve, build, and produce the lock-writing Task — or, for a preview, the
143
+ * Resolve, build, and produce the pointer-writing Task — or, for a preview, the
129
144
  * network-free plan.
130
145
  *
131
146
  * @param runtime - The per-invocation runtime.
132
- * @param frozen - When true, re-resolve to the lock's pinned revisions only.
133
- * @returns A Task that writes `pragma.lock.json` and returns the update result.
147
+ * @param skipInvalid - Drop sources that fail to parse (warning about each)
148
+ * rather than failing the whole update.
149
+ * @returns A Task that points the project at the built pack and returns the
150
+ * update result.
134
151
  * @note Impure — resolves packages (may hit git) and builds the pack eagerly,
135
152
  * UNLESS `runtime.mutation.preview` is set, in which case it stays offline.
136
153
  */
137
154
  export async function buildUpdateTask(
138
155
  runtime: PragmaRuntime,
139
- frozen: boolean,
140
156
  skipInvalid = false,
141
157
  ): Promise<Task<SourcesUpdateData>> {
142
158
  if (runtime.mutation?.preview) return buildUpdatePlan(runtime);
@@ -148,37 +164,59 @@ export async function buildUpdateTask(
148
164
  const verbose = runtime.globalFlags.verbose;
149
165
 
150
166
  const layers = await runtime.loadConfig();
151
- const entries = layers.config.packages ?? [];
152
- const existing = readLock(runtime.cwd);
153
- const priorContent = existing ? serializeLock(existing) : undefined;
167
+ const entries = layers.config.packs ?? [];
168
+ const priorHash = readActivePack(runtime.cwd);
154
169
 
155
- const resolved: ResolvedPackage[] = [];
170
+ const resolved: (ResolvedPackage & { readonly kind: PackageRef["kind"] })[] =
171
+ [];
156
172
  for (const entry of entries) {
157
- const ref = parsePackageEntry(entry);
173
+ const ref = parsePackDeclaration(entry);
158
174
  report?.(resolveProgress(ref));
159
- const pinned = existing?.packs.find((pack) => pack.name === ref.pkg);
160
- resolved.push(
161
- await resolvePackage(ref, {
162
- cwd: runtime.cwd,
163
- frozen,
164
- pinned: pinned?.resolved,
165
- }),
166
- );
175
+ resolved.push({
176
+ kind: ref.kind,
177
+ ...(await resolvePackage(ref, { cwd: runtime.cwd })),
178
+ });
167
179
  }
168
180
 
169
- // Prefix precedence: core < pack < config. A resolved package's own
170
- // `@prefix` declarations are harvested from its TTL and merged beneath config
171
- // so the index compacts pack URIs to `pfx:Local` (the FROZEN {name,type}
172
- // token contract); config still wins any clash. The merged map is persisted
173
- // in the manifest, so boot reads the same names.
174
181
  const inputs = resolved.flatMap((pkg) => pkg.sources);
175
- const prefixes = {
176
- ...CORE_PREFIXES,
177
- ...harvestPrefixes(inputs),
178
- ...(layers.config.prefixes ?? {}),
179
- };
182
+ // The packages' own `stories/*.json`, carried into the pack verbatim (see
183
+ // graphpack/types.STORIES_FILE). Raw text: nothing interprets them here.
184
+ const stories = resolved.flatMap((pkg) => pkg.stories);
185
+
186
+ // Refuse to build an empty store (A4). A package that ships no `.ttl` (or no
187
+ // configured packs at all) would build a 0-triple pack whose empty
188
+ // `data.nq` fails the completeness gate — so the "successful" update boots to
189
+ // a PERMANENT `STORE_UNAVAILABLE` loop. Fail loudly here, leaving the embedded
190
+ // pack (or the prior build) answering reads, instead of pointing at that one.
191
+ if (inputs.length === 0) {
192
+ throw PragmaError.configError(
193
+ entries.length === 0
194
+ ? "No packs are configured, so there are no sources to build a store from. The embedded pack answers reads until you add a pack."
195
+ : `The ${entries.length} configured pack(s) resolved 0 RDF sources (no definitions/**.ttl or data/**.ttl). Refusing to build an empty store.`,
196
+ {
197
+ recovery: cliRecovery(
198
+ "sources update --verbose",
199
+ "Add a pack that ships `.ttl` under definitions/ or data/, then re-run.",
200
+ ),
201
+ },
202
+ );
203
+ }
204
+
205
+ const prefixes = buildPackPrefixes(inputs, layers.config.prefixes);
206
+
207
+ // Warn on conflicting `@prefix` declarations across packages (A5): last-wins
208
+ // is silent otherwise, compacting the losing package's URIs to the wrong
209
+ // prefix. Display-only, so a warning (not a hard failure) is the right call.
210
+ for (const clash of detectPrefixClashes(inputs)) {
211
+ report?.(
212
+ `Prefix "${clash.label}:" is declared with conflicting IRIs across packages (${clash.iris.join(" vs ")}); last wins, so some entities may compact to the wrong prefix.`,
213
+ );
214
+ }
215
+
180
216
  report?.(`Building store from ${inputs.length} source(s)`);
181
217
  if (verbose) for (const input of inputs) report?.(` parse ${input.path}`);
218
+ if (stories.length > 0)
219
+ report?.(`Carrying ${stories.length} package read story file(s)`);
182
220
 
183
221
  // Build the pack. On a parse/build failure, classify it as a NAMED data error
184
222
  // (U6) — not INTERNAL_ERROR's "please report this issue" — identifying the
@@ -186,7 +224,20 @@ export async function buildUpdateTask(
186
224
  // With `--skip-invalid`, drop the unparseable sources (warning LOUDLY about
187
225
  // each — never a silent partial graph) and build from the rest instead of
188
226
  // failing the whole update.
189
- const sourceRef = entries.map(entryName).join(",") || "embedded";
227
+ // `<name>@<kind>:<resolved>` — the SAME provenance label the bundler writes
228
+ // for the embed, so the manifest field means one thing whoever built the pack,
229
+ // and `sources status` / `doctor` can still answer "which revision is my store
230
+ // built from?" now that no lock records it. A `file:` pack resolves to a local
231
+ // PATH rather than a revision, so it contributes its name alone: a machine
232
+ // path is not provenance, and `sources status` already lists the ref.
233
+ const sourceRef =
234
+ resolved
235
+ .map((pkg) =>
236
+ pkg.kind === "file"
237
+ ? pkg.name
238
+ : `${pkg.name}@${pkg.kind}:${pkg.resolved}`,
239
+ )
240
+ .join(", ") || "embedded";
190
241
  let built: Awaited<ReturnType<typeof buildPack>>;
191
242
  try {
192
243
  built = await buildPack(inputs, {
@@ -194,6 +245,7 @@ export async function buildUpdateTask(
194
245
  version: VERSION,
195
246
  sourceRef,
196
247
  prefixes,
248
+ stories,
197
249
  });
198
250
  } catch (error) {
199
251
  if (!skipInvalid) throw await classifySourceBuildError(error, inputs);
@@ -213,7 +265,7 @@ export async function buildUpdateTask(
213
265
  `All ${inputs.length} configured source(s) failed to parse — nothing to build.`,
214
266
  {
215
267
  recovery: cliRecovery(
216
- `${RECOVERY_CLI_PREFIX}sources update --verbose`,
268
+ "sources update --verbose",
217
269
  "Fix the reported sources, then re-run.",
218
270
  ),
219
271
  },
@@ -225,11 +277,8 @@ export async function buildUpdateTask(
225
277
  sourceRef,
226
278
  // Re-harvest prefixes from only the sources that survived, so a dropped
227
279
  // file's declarations don't skew compaction.
228
- prefixes: {
229
- ...CORE_PREFIXES,
230
- ...harvestPrefixes(usableInputs),
231
- ...(layers.config.prefixes ?? {}),
232
- },
280
+ prefixes: buildPackPrefixes(usableInputs, layers.config.prefixes),
281
+ stories,
233
282
  });
234
283
  } catch (rebuildError) {
235
284
  throw await classifySourceBuildError(rebuildError, usableInputs);
@@ -239,38 +288,37 @@ export async function buildUpdateTask(
239
288
  `${built.reused ? "Reused" : "Built"} store ${built.contentHash.slice(0, 12)}`,
240
289
  );
241
290
 
242
- const now = new Date().toISOString();
243
- const lock: PragmaLock = {
244
- version: 1,
245
- contentHash: built.contentHash,
246
- packs: resolved.map((pkg) => {
247
- const prev = existing?.packs.find((entry) => entry.name === pkg.name);
248
- return {
249
- name: pkg.name,
250
- source: pkg.source,
251
- resolved: pkg.resolved,
252
- resolvedAt: frozen && prev ? prev.resolvedAt : now,
253
- };
254
- }),
255
- };
256
- const newContent = serializeLock(lock);
257
- const path = lockPath(runtime.cwd);
291
+ // Refuse a 0-triple build (A4): non-empty sources can still parse to no
292
+ // triples (e.g. comment-only TTL, or a file of only `@prefix` lines). Pointing
293
+ // the project at an empty pack boots to the same `STORE_UNAVAILABLE` loop, so
294
+ // treat it as a data error rather than a silent "success". (A manifest
295
+ // predating the triple count is left alone — `undefined` never trips this.)
296
+ if (built.manifest.tripleCount === 0) {
297
+ throw PragmaError.configError(
298
+ `The configured sources parsed to 0 RDF triples, so the store would be empty. Refusing to build an empty store.`,
299
+ {
300
+ recovery: cliRecovery(
301
+ "sources update --verbose",
302
+ "Check that the package sources actually contain RDF triples, then re-run.",
303
+ ),
304
+ },
305
+ );
306
+ }
258
307
 
308
+ const path = activePackPath(runtime.cwd);
259
309
  const data: SourcesUpdateData = {
260
310
  contentHash: built.contentHash,
261
311
  reused: built.reused,
262
- lockPath: path,
263
312
  packs: resolved.map((pkg) => ({
264
313
  name: pkg.name,
265
314
  resolved: pkg.resolved,
266
315
  sourceCount: pkg.sources.length,
316
+ storyCount: pkg.stories.length,
267
317
  })),
268
318
  };
269
319
 
270
320
  const undo =
271
- priorContent !== undefined
272
- ? writeFile(path, priorContent)
273
- : deleteFile(path);
321
+ priorHash !== undefined ? writeFile(path, priorHash) : deleteFile(path);
274
322
 
275
323
  // Install package-provided skills (U10): symlink each resolved package's
276
324
  // `skills/*` into the installed-skills root, so `skill list` / `setup skills`
@@ -282,9 +330,9 @@ export async function buildUpdateTask(
282
330
  if (skillLinks.length > 0)
283
331
  report?.(`Installing ${skillLinks.length} skill(s)`);
284
332
 
285
- report?.(`Writing ${LOCK_BASENAME}`);
333
+ report?.(`Pointing ${runtime.cwd} at pack ${built.contentHash.slice(0, 12)}`);
286
334
  return gen(function* () {
287
- yield* $(writeFile(path, newContent, { undo }));
335
+ yield* $(writeFile(path, built.contentHash, { undo }));
288
336
  if (skillLinks.length > 0) {
289
337
  yield* $(mkdir(installedSkillsDir(), true));
290
338
  for (const link of skillLinks) {
@@ -341,7 +389,7 @@ export async function classifySourceBuildError(
341
389
  : "The configured package sources could not be built into a store";
342
390
  return PragmaError.configError(`${where}: ${detail}`, {
343
391
  recovery: cliRecovery(
344
- `${RECOVERY_CLI_PREFIX}sources update --verbose`,
392
+ "sources update --verbose",
345
393
  "Re-run with --verbose to see each file as it parses. If a package ships malformed RDF, report it to that package's maintainer.",
346
394
  ),
347
395
  });