@canonical/pragma-cli 0.38.0 → 0.42.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 (365) hide show
  1. package/README.md +3 -3
  2. package/dist/package.json +16 -16
  3. package/dist/pragma.conf.js +273 -176
  4. package/dist/pragma.conf.js.map +1 -1
  5. package/dist/src/capabilities/capabilities/capabilities.verb.js +2 -0
  6. package/dist/src/capabilities/capabilities/capabilities.verb.js.map +1 -1
  7. package/dist/src/capabilities/capabilities/catalog.js +47 -51
  8. package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
  9. package/dist/src/capabilities/capabilities/index.js +2 -0
  10. package/dist/src/capabilities/capabilities/index.js.map +1 -1
  11. package/dist/src/capabilities/capabilities/types.js +5 -5
  12. package/dist/src/capabilities/colophon/colophon.render.js +7 -4
  13. package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
  14. package/dist/src/capabilities/colophon/colophon.verb.js +1 -0
  15. package/dist/src/capabilities/colophon/colophon.verb.js.map +1 -1
  16. package/dist/src/capabilities/colophon/markdownTerminal.js.map +1 -1
  17. package/dist/src/capabilities/config/get.verb.js +2 -0
  18. package/dist/src/capabilities/config/get.verb.js.map +1 -1
  19. package/dist/src/capabilities/config/runField.js +2 -2
  20. package/dist/src/capabilities/config/runField.js.map +1 -1
  21. package/dist/src/capabilities/config/set.verb.js +2 -0
  22. package/dist/src/capabilities/config/set.verb.js.map +1 -1
  23. package/dist/src/capabilities/config/show.render.js.map +1 -1
  24. package/dist/src/capabilities/config/show.verb.js +1 -0
  25. package/dist/src/capabilities/config/show.verb.js.map +1 -1
  26. package/dist/src/capabilities/config/unset.verb.js +2 -0
  27. package/dist/src/capabilities/config/unset.verb.js.map +1 -1
  28. package/dist/src/capabilities/create/constants.js +8 -5
  29. package/dist/src/capabilities/create/constants.js.map +1 -1
  30. package/dist/src/capabilities/create/create.render.js.map +1 -1
  31. package/dist/src/capabilities/create/create.verb.js +36 -7
  32. package/dist/src/capabilities/create/create.verb.js.map +1 -1
  33. package/dist/src/capabilities/create/createSurface.generated.js +22 -0
  34. package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
  35. package/dist/src/capabilities/create/index.js +1 -1
  36. package/dist/src/capabilities/create/pickGenerator.js +1 -0
  37. package/dist/src/capabilities/create/pickGenerator.js.map +1 -1
  38. package/dist/src/capabilities/distribution.js +3 -1
  39. package/dist/src/capabilities/distribution.js.map +1 -1
  40. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +10 -2
  41. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
  42. package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
  43. package/dist/src/capabilities/doctor/doctor.verb.js +2 -0
  44. package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
  45. package/dist/src/capabilities/graph/connect.verb.js +2 -0
  46. package/dist/src/capabilities/graph/connect.verb.js.map +1 -1
  47. package/dist/src/capabilities/graph/inspect.render.js +7 -1
  48. package/dist/src/capabilities/graph/inspect.render.js.map +1 -1
  49. package/dist/src/capabilities/graph/inspect.verb.js +2 -0
  50. package/dist/src/capabilities/graph/inspect.verb.js.map +1 -1
  51. package/dist/src/capabilities/graph/query.render.js +37 -5
  52. package/dist/src/capabilities/graph/query.render.js.map +1 -1
  53. package/dist/src/capabilities/graph/query.verb.js +2 -0
  54. package/dist/src/capabilities/graph/query.verb.js.map +1 -1
  55. package/dist/src/capabilities/graph/queryText.js +28 -0
  56. package/dist/src/capabilities/graph/queryText.js.map +1 -1
  57. package/dist/src/capabilities/graph/runConnect.js +3 -3
  58. package/dist/src/capabilities/graph/runConnect.js.map +1 -1
  59. package/dist/src/capabilities/graph/runQuery.js +8 -6
  60. package/dist/src/capabilities/graph/runQuery.js.map +1 -1
  61. package/dist/src/capabilities/info/info.verb.js +2 -0
  62. package/dist/src/capabilities/info/info.verb.js.map +1 -1
  63. package/dist/src/capabilities/meta/mcp.verb.js +1 -0
  64. package/dist/src/capabilities/meta/mcp.verb.js.map +1 -1
  65. package/dist/src/capabilities/ontology/verbs.js +7 -10
  66. package/dist/src/capabilities/ontology/verbs.js.map +1 -1
  67. package/dist/src/capabilities/prompt/verbs.js +5 -4
  68. package/dist/src/capabilities/prompt/verbs.js.map +1 -1
  69. package/dist/src/capabilities/resources/provider.js +6 -3
  70. package/dist/src/capabilities/resources/provider.js.map +1 -1
  71. package/dist/src/capabilities/setup/operations/writability.js.map +1 -1
  72. package/dist/src/capabilities/setup/plan.render.js.map +1 -1
  73. package/dist/src/capabilities/setup/setup.verb.js +13 -5
  74. package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
  75. package/dist/src/capabilities/shared/calls.js +16 -0
  76. package/dist/src/capabilities/shared/calls.js.map +1 -0
  77. package/dist/src/capabilities/shared/packageManager.js.map +1 -1
  78. package/dist/src/capabilities/skill/discover.js.map +1 -1
  79. package/dist/src/capabilities/skill/render.js +7 -5
  80. package/dist/src/capabilities/skill/render.js.map +1 -1
  81. package/dist/src/capabilities/skill/verbs.js +5 -4
  82. package/dist/src/capabilities/skill/verbs.js.map +1 -1
  83. package/dist/src/capabilities/sources/collectStatus.js +6 -1
  84. package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
  85. package/dist/src/capabilities/sources/index.js +4 -3
  86. package/dist/src/capabilities/sources/index.js.map +1 -1
  87. package/dist/src/capabilities/sources/reset.render.js +37 -0
  88. package/dist/src/capabilities/sources/reset.render.js.map +1 -0
  89. package/dist/src/capabilities/sources/reset.verb.js +51 -0
  90. package/dist/src/capabilities/sources/reset.verb.js.map +1 -0
  91. package/dist/src/capabilities/sources/runReset.js +59 -0
  92. package/dist/src/capabilities/sources/runReset.js.map +1 -0
  93. package/dist/src/capabilities/sources/runUpdate.js +7 -6
  94. package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
  95. package/dist/src/capabilities/sources/status.render.js +24 -8
  96. package/dist/src/capabilities/sources/status.render.js.map +1 -1
  97. package/dist/src/capabilities/sources/status.verb.js +1 -0
  98. package/dist/src/capabilities/sources/status.verb.js.map +1 -1
  99. package/dist/src/capabilities/sources/update.verb.js +2 -0
  100. package/dist/src/capabilities/sources/update.verb.js.map +1 -1
  101. package/dist/src/capabilities/upgrade/upgrade.verb.js +1 -0
  102. package/dist/src/capabilities/upgrade/upgrade.verb.js.map +1 -1
  103. package/dist/src/capabilities/version/version.verb.js +1 -0
  104. package/dist/src/capabilities/version/version.verb.js.map +1 -1
  105. package/dist/src/constants.js +4 -3
  106. package/dist/src/constants.js.map +1 -1
  107. package/dist/src/kernel/completion/complete.js.map +1 -1
  108. package/dist/src/kernel/completion/emitScripts.js.map +1 -1
  109. package/dist/src/kernel/completion/parse.js.map +1 -1
  110. package/dist/src/kernel/completion/rank.js.map +1 -1
  111. package/dist/src/kernel/completion/resolve.js.map +1 -1
  112. package/dist/src/kernel/config/readConfig.js.map +1 -1
  113. package/dist/src/kernel/error/PragmaError.js.map +1 -1
  114. package/dist/src/kernel/error/index.js +1 -1
  115. package/dist/src/kernel/error/index.js.map +1 -1
  116. package/dist/src/kernel/error/recovery.js +24 -42
  117. package/dist/src/kernel/error/recovery.js.map +1 -1
  118. package/dist/src/kernel/packs/collect.js +123 -9
  119. package/dist/src/kernel/packs/collect.js.map +1 -1
  120. package/dist/src/kernel/packs/compile.js +74 -15
  121. package/dist/src/kernel/packs/compile.js.map +1 -1
  122. package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js +3 -3
  123. package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
  124. package/dist/src/kernel/packs/iri.js +2 -4
  125. package/dist/src/kernel/packs/iri.js.map +1 -1
  126. package/dist/src/kernel/packs/renderPack.js +90 -50
  127. package/dist/src/kernel/packs/renderPack.js.map +1 -1
  128. package/dist/src/kernel/packs/resolveEntity.js +202 -90
  129. package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
  130. package/dist/src/kernel/packs/runBodies.js +89 -30
  131. package/dist/src/kernel/packs/runBodies.js.map +1 -1
  132. package/dist/src/kernel/packs/schema.js +66 -12
  133. package/dist/src/kernel/packs/schema.js.map +1 -1
  134. package/dist/src/kernel/packs/sparql/buildListQuery.js +80 -13
  135. package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -1
  136. package/dist/src/kernel/packs/sparql/buildLookupQuery.js +41 -13
  137. package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
  138. package/dist/src/kernel/packs/sparql/filterValues.js +32 -23
  139. package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -1
  140. package/dist/src/kernel/packs/sparql/runSelect.js +3 -5
  141. package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
  142. package/dist/src/kernel/packs/storyRules.js +31 -4
  143. package/dist/src/kernel/packs/storyRules.js.map +1 -1
  144. package/dist/src/kernel/packs/tierScope.js +3 -3
  145. package/dist/src/kernel/packs/tierScope.js.map +1 -1
  146. package/dist/src/kernel/packs/types.js +4 -0
  147. package/dist/src/kernel/packs/types.js.map +1 -1
  148. package/dist/src/kernel/project/cli/buildProgram.js +4 -0
  149. package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
  150. package/dist/src/kernel/project/cli/dispatch.js +32 -8
  151. package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
  152. package/dist/src/kernel/project/cli/globalFlags.js.map +1 -1
  153. package/dist/src/kernel/project/cli/suggestNames.js +44 -1
  154. package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
  155. package/dist/src/kernel/project/cli/verbHelp.js +17 -18
  156. package/dist/src/kernel/project/cli/verbHelp.js.map +1 -1
  157. package/dist/src/kernel/project/mcp/buildServer.js +11 -4
  158. package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
  159. package/dist/src/kernel/project/mcp/envelope.js +3 -2
  160. package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
  161. package/dist/src/kernel/project/mcp/instructions.js +101 -31
  162. package/dist/src/kernel/project/mcp/instructions.js.map +1 -1
  163. package/dist/src/kernel/project/mcp/registerVerb.js +59 -23
  164. package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
  165. package/dist/src/kernel/render/envelope.js.map +1 -1
  166. package/dist/src/kernel/render/renderers.js +3 -3
  167. package/dist/src/kernel/render/renderers.js.map +1 -1
  168. package/dist/src/kernel/render/turtle.js +21 -4
  169. package/dist/src/kernel/render/turtle.js.map +1 -1
  170. package/dist/src/kernel/runtime/boot.js.map +1 -1
  171. package/dist/src/kernel/runtime/graphpack/build.js +54 -1
  172. package/dist/src/kernel/runtime/graphpack/build.js.map +1 -1
  173. package/dist/src/kernel/runtime/graphpack/manifest.js +14 -0
  174. package/dist/src/kernel/runtime/graphpack/manifest.js.map +1 -1
  175. package/dist/src/kernel/runtime/graphpack/read.js +3 -5
  176. package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
  177. package/dist/src/kernel/runtime/graphpack/schemas.js +1 -0
  178. package/dist/src/kernel/runtime/graphpack/schemas.js.map +1 -1
  179. package/dist/src/kernel/runtime/loadSession.js +8 -1
  180. package/dist/src/kernel/runtime/loadSession.js.map +1 -1
  181. package/dist/src/kernel/runtime/packVersion.js +75 -0
  182. package/dist/src/kernel/runtime/packVersion.js.map +1 -0
  183. package/dist/src/kernel/runtime/readEntity.js +43 -4
  184. package/dist/src/kernel/runtime/readEntity.js.map +1 -1
  185. package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
  186. package/dist/src/kernel/runtime/resolveSources.js +61 -9
  187. package/dist/src/kernel/runtime/resolveSources.js.map +1 -1
  188. package/dist/src/kernel/runtime/storeReadiness.js +3 -2
  189. package/dist/src/kernel/runtime/storeReadiness.js.map +1 -1
  190. package/dist/src/kernel/spec/call.js +167 -0
  191. package/dist/src/kernel/spec/call.js.map +1 -0
  192. package/dist/src/kernel/spec/emitReference.js +9 -13
  193. package/dist/src/kernel/spec/emitReference.js.map +1 -1
  194. package/dist/src/kernel/spec/emitSurface.js +8 -1
  195. package/dist/src/kernel/spec/emitSurface.js.map +1 -1
  196. package/dist/src/kernel/spec/guidance.js +63 -0
  197. package/dist/src/kernel/spec/guidance.js.map +1 -0
  198. package/dist/src/kernel/spec/wireType.js +36 -0
  199. package/dist/src/kernel/spec/wireType.js.map +1 -0
  200. package/package.json +16 -16
  201. package/pragma.conf.ts +294 -177
  202. package/src/capabilities/block.parity.test.ts +1 -1
  203. package/src/capabilities/callRule.test.ts +147 -0
  204. package/src/capabilities/capabilities/capabilities.test.ts +47 -19
  205. package/src/capabilities/capabilities/capabilities.verb.ts +2 -0
  206. package/src/capabilities/capabilities/catalog.ts +60 -55
  207. package/src/capabilities/capabilities/index.ts +2 -0
  208. package/src/capabilities/capabilities/types.ts +16 -19
  209. package/src/capabilities/colophon/colophon.render.ts +9 -5
  210. package/src/capabilities/colophon/colophon.verb.ts +1 -0
  211. package/src/capabilities/config/get.verb.ts +2 -0
  212. package/src/capabilities/config/runField.ts +3 -3
  213. package/src/capabilities/config/set.verb.ts +3 -0
  214. package/src/capabilities/config/show.verb.ts +2 -0
  215. package/src/capabilities/config/unset.verb.ts +2 -0
  216. package/src/capabilities/create/constants.ts +8 -5
  217. package/src/capabilities/create/create.mcp.test.ts +173 -1
  218. package/src/capabilities/create/create.test.ts +4 -2
  219. package/src/capabilities/create/create.verb.ts +45 -3
  220. package/src/capabilities/create/createGrammar.test.ts +41 -5
  221. package/src/capabilities/create/createSurface.generated.ts +22 -0
  222. package/src/capabilities/create/crossCli.subprocess.test.ts +53 -14
  223. package/src/capabilities/create/index.ts +1 -1
  224. package/src/capabilities/create/pickGenerator.ts +1 -0
  225. package/src/capabilities/create/shippedCreate.subprocess.test.ts +28 -7
  226. package/src/capabilities/distribution.test.ts +6 -3
  227. package/src/capabilities/distribution.ts +6 -0
  228. package/src/capabilities/doctor/checks/checkPackageRefs.ts +14 -2
  229. package/src/capabilities/doctor/doctor.test.ts +38 -1
  230. package/src/capabilities/doctor/doctor.verb.ts +2 -0
  231. package/src/capabilities/emptyRecovery.test.ts +5 -0
  232. package/src/capabilities/graph/connect.verb.ts +2 -0
  233. package/src/capabilities/graph/inspect.render.ts +9 -1
  234. package/src/capabilities/graph/inspect.test.ts +73 -0
  235. package/src/capabilities/graph/inspect.verb.ts +2 -0
  236. package/src/capabilities/graph/query.render.test.ts +35 -0
  237. package/src/capabilities/graph/query.render.ts +45 -7
  238. package/src/capabilities/graph/query.shipped.exec.test.ts +44 -0
  239. package/src/capabilities/graph/query.verb.ts +2 -0
  240. package/src/capabilities/graph/queryText.test.ts +55 -1
  241. package/src/capabilities/graph/queryText.ts +47 -0
  242. package/src/capabilities/graph/runConnect.ts +4 -5
  243. package/src/capabilities/graph/runQuery.ts +23 -9
  244. package/src/capabilities/info/info.verb.ts +2 -0
  245. package/src/capabilities/links.shipped.exec.test.ts +398 -0
  246. package/src/capabilities/listBudget.shipped.exec.test.ts +2 -2
  247. package/src/capabilities/listLookup.shipped.exec.test.ts +122 -13
  248. package/src/capabilities/listQuery.shipped.exec.test.ts +39 -16
  249. package/src/capabilities/lookupBudget.shipped.exec.test.ts +208 -0
  250. package/src/capabilities/meta/mcp.verb.ts +2 -0
  251. package/src/capabilities/ontology/ontology.test.ts +24 -0
  252. package/src/capabilities/ontology/verbs.ts +16 -12
  253. package/src/capabilities/prompt/verbs.ts +10 -4
  254. package/src/capabilities/resources/provider.ts +7 -3
  255. package/src/capabilities/resources/resources.test.ts +2 -1
  256. package/src/capabilities/setup/setup.verb.ts +16 -0
  257. package/src/capabilities/shared/calls.ts +20 -0
  258. package/src/capabilities/skill/render.ts +9 -6
  259. package/src/capabilities/skill/verbs.ts +8 -4
  260. package/src/capabilities/sources/collectStatus.ts +6 -1
  261. package/src/capabilities/sources/index.ts +4 -3
  262. package/src/capabilities/sources/reset.render.test.ts +33 -0
  263. package/src/capabilities/sources/reset.render.ts +43 -0
  264. package/src/capabilities/sources/reset.verb.ts +59 -0
  265. package/src/capabilities/sources/runReset.ts +65 -0
  266. package/src/capabilities/sources/runUpdate.ts +12 -11
  267. package/src/capabilities/sources/sources.test.ts +158 -3
  268. package/src/capabilities/sources/status.render.test.ts +63 -1
  269. package/src/capabilities/sources/status.render.ts +25 -8
  270. package/src/capabilities/sources/status.verb.ts +2 -0
  271. package/src/capabilities/sources/types.ts +26 -0
  272. package/src/capabilities/sources/update.verb.ts +3 -0
  273. package/src/capabilities/surface.test.ts +29 -4
  274. package/src/capabilities/token.parity.test.ts +3 -2
  275. package/src/capabilities/tokenGraph.shipped.exec.test.ts +14 -2
  276. package/src/capabilities/upgrade/upgrade.verb.ts +1 -0
  277. package/src/capabilities/variable.parity.test.ts +2 -1
  278. package/src/capabilities/version/version.verb.ts +1 -0
  279. package/src/capabilities/wireContract.test.ts +110 -0
  280. package/src/constants.ts +4 -3
  281. package/src/kernel/completion/shellDrive.test.ts +2 -2
  282. package/src/kernel/copy.test.ts +1 -1
  283. package/src/kernel/error/PragmaError.test.ts +7 -3
  284. package/src/kernel/error/index.ts +1 -1
  285. package/src/kernel/error/recovery.test.ts +52 -18
  286. package/src/kernel/error/recovery.ts +25 -46
  287. package/src/kernel/error/types.ts +1 -1
  288. package/src/kernel/packs/collect.test.ts +79 -0
  289. package/src/kernel/packs/collect.ts +151 -11
  290. package/src/kernel/packs/compile.test.ts +107 -10
  291. package/src/kernel/packs/compile.ts +100 -11
  292. package/src/kernel/packs/filteredEmpty.test.ts +22 -5
  293. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +5 -6
  294. package/src/kernel/packs/graphqlSource.test.ts +42 -0
  295. package/src/kernel/packs/iri.ts +5 -4
  296. package/src/kernel/packs/listNotice.test.ts +1 -1
  297. package/src/kernel/packs/nounLink.test.ts +385 -0
  298. package/src/kernel/packs/renderPack.ts +137 -57
  299. package/src/kernel/packs/resolveEntity.test.ts +170 -1
  300. package/src/kernel/packs/resolveEntity.ts +281 -101
  301. package/src/kernel/packs/runBodies.ts +133 -31
  302. package/src/kernel/packs/schema.ts +77 -12
  303. package/src/kernel/packs/sparql/buildListQuery.test.ts +78 -1
  304. package/src/kernel/packs/sparql/buildListQuery.ts +106 -16
  305. package/src/kernel/packs/sparql/buildLookupQuery.ts +44 -13
  306. package/src/kernel/packs/sparql/filterValues.test.ts +36 -4
  307. package/src/kernel/packs/sparql/filterValues.ts +50 -31
  308. package/src/kernel/packs/sparql/runSelect.ts +4 -6
  309. package/src/kernel/packs/storyRules.ts +37 -4
  310. package/src/kernel/packs/tierScope.test.ts +120 -6
  311. package/src/kernel/packs/tierScope.ts +5 -7
  312. package/src/kernel/packs/toolDescription.test.ts +59 -74
  313. package/src/kernel/packs/types.ts +93 -11
  314. package/src/kernel/project/cli/buildProgram.ts +4 -0
  315. package/src/kernel/project/cli/dispatch.ts +34 -8
  316. package/src/kernel/project/cli/suggest.test.ts +77 -0
  317. package/src/kernel/project/cli/suggestNames.ts +47 -1
  318. package/src/kernel/project/cli/verbHelp.ts +16 -19
  319. package/src/kernel/project/mcp/buildServer.ts +15 -4
  320. package/src/kernel/project/mcp/envelope.ts +3 -2
  321. package/src/kernel/project/mcp/instructions.ts +123 -35
  322. package/src/kernel/project/mcp/prompts/prompts.test.ts +136 -20
  323. package/src/kernel/project/mcp/registerVerb.test.ts +28 -0
  324. package/src/kernel/project/mcp/registerVerb.ts +67 -29
  325. package/src/kernel/render/__snapshots__/renderers.test.ts.snap +1 -1
  326. package/src/kernel/render/renderers.test.ts +2 -2
  327. package/src/kernel/render/renderers.ts +3 -3
  328. package/src/kernel/render/turtle.ts +24 -4
  329. package/src/kernel/runtime/graphpack/build.ts +58 -1
  330. package/src/kernel/runtime/graphpack/graphpack.test.ts +20 -0
  331. package/src/kernel/runtime/graphpack/manifest.ts +15 -0
  332. package/src/kernel/runtime/graphpack/read.ts +4 -6
  333. package/src/kernel/runtime/graphpack/schemas.ts +1 -0
  334. package/src/kernel/runtime/graphpack/types.ts +13 -0
  335. package/src/kernel/runtime/loadSession.ts +8 -1
  336. package/src/kernel/runtime/packVersion.test.ts +61 -0
  337. package/src/kernel/runtime/packVersion.ts +84 -0
  338. package/src/kernel/runtime/readEntity.ts +61 -6
  339. package/src/kernel/runtime/resolveSources.test.ts +90 -7
  340. package/src/kernel/runtime/resolveSources.ts +91 -10
  341. package/src/kernel/runtime/storeReadiness.ts +4 -4
  342. package/src/kernel/runtime/types.ts +12 -0
  343. package/src/kernel/spec/call.test.ts +95 -0
  344. package/src/kernel/spec/call.ts +186 -0
  345. package/src/kernel/spec/emitReference.ts +9 -13
  346. package/src/kernel/spec/emitSurface.ts +9 -1
  347. package/src/kernel/spec/guidance.test.ts +49 -0
  348. package/src/kernel/spec/guidance.ts +68 -0
  349. package/src/kernel/spec/index.ts +4 -0
  350. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  351. package/src/kernel/spec/types.ts +64 -7
  352. package/src/kernel/spec/wireType.ts +53 -0
  353. package/src/testing/behavioral/README.md +1 -1
  354. package/src/testing/behavioral/errorMatrix.mcp.test.ts +2 -1
  355. package/src/testing/behavioral/journeys.firstInstall.test.ts +72 -2
  356. package/src/testing/behavioral/parity.test.ts +40 -0
  357. package/src/testing/eval/__snapshots__/eval.test.ts.snap +1 -1
  358. package/src/testing/eval/cases/assembly.ts +4 -3
  359. package/src/testing/eval/cases/stable.ts +2 -3
  360. package/src/testing/helpers/callSchema.ts +40 -0
  361. package/src/testing/helpers/parity.ts +35 -2
  362. package/src/testing/setupCallChecking.ts +30 -0
  363. package/dist/src/capabilities/capabilities/hints.js +0 -223
  364. package/dist/src/capabilities/capabilities/hints.js.map +0 -1
  365. package/src/capabilities/capabilities/hints.ts +0 -266
@@ -28,9 +28,11 @@ import type { ConfigLayers } from "../config/index.js";
28
28
  import { PragmaError } from "../error/index.js";
29
29
  import type { PackStoryRecord } from "../runtime/graphpack/stories.js";
30
30
  import type { CapabilityModule } from "../spec/index.js";
31
- import { compileStoryModule } from "./compile.js";
31
+ import { compileStoryModule, storyOf } from "./compile.js";
32
32
  import { parsePackDefinition } from "./schema.js";
33
- import type { PackDefinition, PackEntry } from "./types.js";
33
+ import { nounsNamed } from "./storyRules.js";
34
+ import type { NounLookups, PackDefinition, PackEntry } from "./types.js";
35
+ import { VERB_PATH_PATTERN } from "./types.js";
34
36
  import { assertUniqueVerbs } from "./uniqueness.js";
35
37
 
36
38
  /** One package-declared story that could not be used, and why. */
@@ -83,10 +85,14 @@ export function validateStories(
83
85
  const problems: StoryProblem[] = [];
84
86
  for (const record of records) {
85
87
  try {
86
- const definition = parsePackDefinition(
87
- JSON.parse(record.content),
88
- record.source,
89
- );
88
+ const migrated = migrateLegacyRecoveries(JSON.parse(record.content));
89
+ for (const hint of migrated.dropped) {
90
+ problems.push({
91
+ source: record.source,
92
+ message: `its story is kept, but its emptyRecovery.cli hint "${hint}" is not a verb path and was dropped; the key is now emptyRecovery.call.`,
93
+ });
94
+ }
95
+ const definition = parsePackDefinition(migrated.raw, record.source);
90
96
  if (reserved.has(definition.noun)) {
91
97
  problems.push({
92
98
  source: record.source,
@@ -112,6 +118,55 @@ export function validateStories(
112
118
  return { entries: [...byNoun.values()], problems };
113
119
  }
114
120
 
121
+ /**
122
+ * Carry a PACKAGE story written against the old grammar forward.
123
+ *
124
+ * `emptyRecovery.cli: "sources update"` became `emptyRecovery.call: { verb }`. A
125
+ * project's own config gets the hard error that names the change — its author
126
+ * can fix it. A package is third-party data, often already built into a pack on
127
+ * disk, and refusing the key would cost the user the whole noun for the sake of
128
+ * a hint. So the old string becomes a call when it IS a verb path, and
129
+ * otherwise only the hint is dropped, reported once under `doctor`.
130
+ *
131
+ * @param raw - The parsed story JSON (untrusted; returned unchanged when it
132
+ * carries no legacy key).
133
+ * @returns The story to validate, and the legacy hints that could not be kept.
134
+ */
135
+ export function migrateLegacyRecoveries(raw: unknown): {
136
+ raw: unknown;
137
+ dropped: string[];
138
+ } {
139
+ const dropped: string[] = [];
140
+ const migrateHalf = (half: unknown): unknown => {
141
+ const recovery = (
142
+ half as { emptyRecovery?: Record<string, unknown> } | null
143
+ )?.emptyRecovery;
144
+ if (typeof recovery?.cli !== "string") return half;
145
+ const { cli, ...rest } = recovery;
146
+ const keep = VERB_PATH_PATTERN.test(cli);
147
+ if (!keep) dropped.push(cli);
148
+ return {
149
+ ...(half as object),
150
+ emptyRecovery:
151
+ keep && rest.call === undefined
152
+ ? { ...rest, call: { verb: cli } }
153
+ : rest,
154
+ };
155
+ };
156
+ if (typeof raw !== "object" || raw === null) return { raw, dropped };
157
+ const story = raw as { list?: unknown; verbs?: unknown };
158
+ return {
159
+ raw: {
160
+ ...story,
161
+ ...(story.list === undefined ? {} : { list: migrateHalf(story.list) }),
162
+ ...(Array.isArray(story.verbs)
163
+ ? { verbs: story.verbs.map(migrateHalf) }
164
+ : {}),
165
+ },
166
+ dropped,
167
+ };
168
+ }
169
+
115
170
  /**
116
171
  * The PROJECT's config-declared stories, weakest tier first.
117
172
  *
@@ -147,8 +202,8 @@ function projectStoryTiers(layers: ConfigLayers): readonly unknown[][] {
147
202
  * carries (see {@link validateStories}); weaker than either config tier.
148
203
  * @returns The effective modules, uniqueness-checked.
149
204
  * @throws PragmaError CONFIG_ERROR on an invalid CONFIG story, a config story
150
- * claiming an authored non-story noun, or a duplicate noun within one config
151
- * tier. Package stories were already screened and never throw here.
205
+ * claiming an authored non-story noun, a duplicate noun within one config
206
+ * tier, or a config or shipped story naming a noun no story declares. Package stories were already screened and never throw here.
152
207
  */
153
208
  export function assembleEffectiveModules(
154
209
  staticModules: readonly CapabilityModule[],
@@ -171,6 +226,19 @@ export function assembleEffectiveModules(
171
226
  // Keyed by noun so the stronger tier REPLACES the weaker one — declaring a
172
227
  // story both on its pack and at the top level is a refinement, not an error.
173
228
  const dynamic = new Map<string, CapabilityModule>();
229
+ // The EFFECTIVE lookup of a noun, for shipped and project stories alike.
230
+ // Read at run time, so it sees every tier whatever order they compile in.
231
+ const nouns: NounLookups = (noun) => {
232
+ const module =
233
+ dynamic.get(noun) ?? staticModules.find((m) => m.name === noun);
234
+ const story = module && storyOf(module);
235
+ if (story && !story.definition.lookup) {
236
+ throw PragmaError.configError(
237
+ `The "${noun}" story in ${story.source.label} declares no lookup, so a filter that takes a ${noun} cannot resolve its value. Add a lookup to that story.`,
238
+ );
239
+ }
240
+ return story?.definition.lookup;
241
+ };
174
242
  for (const entry of packageStories) {
175
243
  dynamic.set(
176
244
  entry.definition.noun,
@@ -178,6 +246,7 @@ export function assembleEffectiveModules(
178
246
  entry.definition,
179
247
  { label: entry.source, origin: "package" },
180
248
  prefixes,
249
+ nouns,
181
250
  ),
182
251
  );
183
252
  }
@@ -205,6 +274,7 @@ export function assembleEffectiveModules(
205
274
  definition,
206
275
  { label: "config", origin: "config" },
207
276
  prefixes,
277
+ nouns,
208
278
  ),
209
279
  );
210
280
  }
@@ -212,12 +282,79 @@ export function assembleEffectiveModules(
212
282
 
213
283
  // Drop the static module for any noun a config story overrides, then append
214
284
  // the dynamic modules.
215
- const kept = staticModules.filter((module) => !dynamic.has(module.name));
285
+ const kept = staticModules
286
+ .filter((module) => !dynamic.has(module.name))
287
+ // Recompiled so a shipped filter naming an overridden noun resolves
288
+ // through the project's lookup, as the project's own stories do.
289
+ .map((module) => {
290
+ const story = storyOf(module);
291
+ return story
292
+ ? compileStoryModule(
293
+ story.definition,
294
+ story.source,
295
+ story.prefixes,
296
+ nouns,
297
+ )
298
+ : module;
299
+ });
216
300
  const effective = [...kept, ...dynamic.values()];
301
+ // A noun some story declares without a lookup is refused when a filter on it
302
+ // is used, not here: only a noun NO story declares is a declaration error.
303
+ // Package stories never throw; {@link screenPackageStories} drops them.
304
+ for (const module of effective) {
305
+ const story = storyOf(module);
306
+ const unknown =
307
+ story && story.source.origin !== "package"
308
+ ? undeclaredNoun(story.definition, effective)
309
+ : undefined;
310
+ if (story && unknown) {
311
+ throw PragmaError.configError(
312
+ `Invalid story in ${story.source.label}: "${module.name}" names the noun "${unknown}", and no story declares it.`,
313
+ );
314
+ }
315
+ }
217
316
  assertUniqueVerbs(effective.flatMap((module) => [...module.verbs]));
218
317
  return effective;
219
318
  }
220
319
 
320
+ /** The first noun a story names that no effective story declares. */
321
+ function undeclaredNoun(
322
+ definition: PackDefinition,
323
+ effective: readonly CapabilityModule[],
324
+ ): string | undefined {
325
+ return nounsNamed(definition).find(
326
+ (noun) => !effective.some((m) => m.name === noun && storyOf(m)),
327
+ );
328
+ }
329
+
330
+ /**
331
+ * Drop each PACKAGE story naming a noun no effective story declares, reporting
332
+ * it — judged over the assembled set, so the order of declaration is nothing.
333
+ *
334
+ * @param effective - The assembled modules.
335
+ * @returns The modules kept, and one problem per story dropped.
336
+ */
337
+ export function screenPackageStories(effective: readonly CapabilityModule[]): {
338
+ readonly modules: readonly CapabilityModule[];
339
+ readonly problems: readonly StoryProblem[];
340
+ } {
341
+ const problems: StoryProblem[] = [];
342
+ const modules = effective.filter((module) => {
343
+ const story = storyOf(module);
344
+ const unknown =
345
+ story?.source.origin === "package"
346
+ ? undeclaredNoun(story.definition, effective)
347
+ : undefined;
348
+ if (!story || unknown === undefined) return true;
349
+ problems.push({
350
+ source: story.source.label,
351
+ message: `it names the noun "${unknown}", and no story declares it.`,
352
+ });
353
+ return false;
354
+ });
355
+ return { modules: problems.length === 0 ? effective : modules, problems };
356
+ }
357
+
221
358
  /**
222
359
  * Load the effective modules for a real invocation: read the layered config and
223
360
  * the answering pack's carried stories, and merge both into the static
@@ -252,8 +389,11 @@ export async function loadEffectiveModules(
252
389
  activeStories(resolveSources(layers, cwd)),
253
390
  staticModules,
254
391
  );
392
+ const screened = screenPackageStories(
393
+ assembleEffectiveModules(staticModules, layers, entries),
394
+ );
255
395
  return {
256
- modules: assembleEffectiveModules(staticModules, layers, entries),
257
- problems,
396
+ modules: screened.modules,
397
+ problems: [...problems, ...screened.problems],
258
398
  };
259
399
  }
@@ -12,6 +12,7 @@ import { RECOVERY_CLI_PREFIX } from "../../constants.js";
12
12
  import { buildFixtureRuntime } from "../../testing/helpers/packRuntime.js";
13
13
  import { PragmaError } from "../error/index.js";
14
14
  import type { PragmaRuntime } from "../runtime/types.js";
15
+ import { declareVerbs } from "../spec/call.js";
15
16
  import { kebabCase } from "../spec/emitSurface.js";
16
17
  import type { VerbSpec } from "../spec/types.js";
17
18
  import { compileListable, compilePack, compileStoryModule } from "./compile.js";
@@ -362,19 +363,28 @@ describe("the grammar rejects what the compiler cannot build (PROTECTED)", () =>
362
363
  );
363
364
  });
364
365
 
365
- it("rejects an emptyRecovery.cli that names a binary, and names the change", () => {
366
- // The hint is rendered as `<consuming distribution> <cli>`, so a story
367
- // carrying the old prefixed form would render `pragma pragma sources
368
- // update`. Third-party packs are third-party DATA: they get an error that
369
- // names the grammar change, not a silently doubled string.
370
- const withCli = (cli: string) => ({
366
+ it("rejects the retired emptyRecovery.cli and a call that is a command line, naming the change", () => {
367
+ // A recovery names a CALL — a verb path and a param bag — and the consuming
368
+ // distribution spells it per surface. Third-party packs are third-party
369
+ // DATA: the old `cli` string gets an error that names the grammar change,
370
+ // and a command line in `verb` would render `pragma pragma sources update`.
371
+ const withRecovery = (recovery: Record<string, unknown>) => ({
371
372
  noun: "widget",
372
- list: { ...listShape, emptyRecovery: { message: "None.", cli } },
373
+ list: { ...listShape, emptyRecovery: { message: "None.", ...recovery } },
373
374
  });
374
- expect(parse(withCli(`${RECOVERY_CLI_PREFIX}sources update`))).toThrow(
375
- /WITHOUT the binary name/,
375
+ expect(parse(withRecovery({ cli: "sources update" }))).toThrow(
376
+ /emptyRecovery\.cli is now emptyRecovery\.call/,
376
377
  );
377
- expect(parse(withCli("sources update"))).not.toThrow();
378
+ expect(
379
+ parse(
380
+ withRecovery({
381
+ call: { verb: `${RECOVERY_CLI_PREFIX}sources update` },
382
+ }),
383
+ ),
384
+ ).toThrow(/a call names a verb path/);
385
+ expect(
386
+ parse(withRecovery({ call: { verb: "sources update" } })),
387
+ ).not.toThrow();
378
388
  });
379
389
  });
380
390
 
@@ -387,6 +397,11 @@ describe("pack compiler — SPARQL fetch path (PROTECTED)", () => {
387
397
  prefixes: PREFIXES,
388
398
  detail: "detailed",
389
399
  }));
400
+ // A miss recovers to `widget list`; the suite checks every call it renders
401
+ // against the declared verbs, and a fixture's are not the distribution's.
402
+ declareVerbs(
403
+ compilePack(WIDGET_PACK, distributionSource("bundled:widget"), PREFIXES),
404
+ );
390
405
  });
391
406
 
392
407
  afterAll(async () => {
@@ -563,6 +578,88 @@ describe("pack compiler — SPARQL fetch path (PROTECTED)", () => {
563
578
  * once, for the name resolve, and the resource listing reads that same
564
579
  * declaration. Nothing to keep in sync because there is nothing written twice.
565
580
  */
581
+ describe("a lookup that declares no disclosure can still be asked for less", () => {
582
+ /** The widget lookup with every trace of disclosure removed. */
583
+ const BARE_PACK: PackDefinition = {
584
+ noun: "widget",
585
+ lookup: {
586
+ source: "sparql",
587
+ by: "ex:name",
588
+ type: "ex:Widget",
589
+ fields: [{ name: "description", property: "ex:description" }],
590
+ expand: [
591
+ {
592
+ name: "parts",
593
+ relation: "ex:hasPart",
594
+ select: [{ name: "name", property: "ex:name" }],
595
+ },
596
+ ],
597
+ },
598
+ };
599
+ const lookupVerb = compilePack(
600
+ BARE_PACK,
601
+ distributionSource("bundled:widget"),
602
+ PREFIXES,
603
+ ).find((v) => verbKey(v.path) === "widget lookup") as VerbSpec;
604
+
605
+ const lookupAt = async (options: {
606
+ detail?: "summary" | "standard" | "detailed";
607
+ configDetail?: "summary";
608
+ }) => {
609
+ const { rt } = await buildFixtureRuntime({
610
+ ttl: TTL,
611
+ prefixes: PREFIXES,
612
+ ...(options.detail ? { detail: options.detail } : {}),
613
+ ...(options.configDetail
614
+ ? { configDetail: options.configDetail, detailOrigin: "project" }
615
+ : {}),
616
+ });
617
+ try {
618
+ const out = (await lookupVerb.run(
619
+ { name: ["Button"] },
620
+ rt,
621
+ )) as LookupOutput;
622
+ return out.results.at(0);
623
+ } finally {
624
+ (await rt.store.get()).store.dispose();
625
+ }
626
+ };
627
+
628
+ it("advertises the canonical levels, defaulting to the fullest", () => {
629
+ expect(lookupVerb.disclosure).toEqual({
630
+ levels: ["summary", "standard", "detailed"],
631
+ default: "detailed",
632
+ });
633
+ });
634
+
635
+ it("answers exactly as before when nothing is asked for", async () => {
636
+ expect(await lookupAt({})).toEqual(await lookupAt({ detail: "detailed" }));
637
+ expect((await lookupAt({}))?.parts).toEqual([{ name: "Label" }]);
638
+ });
639
+
640
+ it("is the fields alone at summary, and everything from standard", async () => {
641
+ const summary = await lookupAt({ detail: "summary" });
642
+ expect(summary).toMatchObject({ description: "A button." });
643
+ expect(summary).not.toHaveProperty("parts");
644
+ expect(await lookupAt({ detail: "standard" })).toHaveProperty("parts");
645
+ });
646
+
647
+ it("follows a configured detail, as a declared disclosure always did", async () => {
648
+ expect(await lookupAt({ configDetail: "summary" })).not.toHaveProperty(
649
+ "parts",
650
+ );
651
+ });
652
+
653
+ it("leaves a declared disclosure exactly as its author wrote it", () => {
654
+ const declared = compilePack(
655
+ WIDGET_PACK,
656
+ distributionSource("bundled:widget"),
657
+ PREFIXES,
658
+ ).find((v) => verbKey(v.path) === "widget lookup");
659
+ expect(declared?.disclosure?.default).toBe("summary");
660
+ });
661
+ });
662
+
566
663
  describe("declared listing (derived from the lookup's types)", () => {
567
664
  it("derives one collection per declared type, unweighted types at 1", () => {
568
665
  expect(
@@ -14,7 +14,7 @@
14
14
  * facade).
15
15
  */
16
16
 
17
- import { BIN_NAME } from "../../constants.js";
17
+ import { BIN_NAME, DETAIL_LEVELS } from "../../constants.js";
18
18
  import { PragmaError } from "../error/index.js";
19
19
  import { compactUri, DEFAULT_PREFIX_MAP } from "../render/index.js";
20
20
  import type { PragmaRuntime } from "../runtime/index.js";
@@ -42,8 +42,10 @@ import {
42
42
  import { storyIssues } from "./storyRules.js";
43
43
  import {
44
44
  EVERY_TIER,
45
+ type NounLookups,
45
46
  type PackDefinition,
46
47
  type PackFilter,
48
+ type PackGuidance,
47
49
  type PackList,
48
50
  type PackLookup,
49
51
  type PackPage,
@@ -74,17 +76,22 @@ const READ_CAPABILITY = {
74
76
  * @param definition - A validated pack definition.
75
77
  * @param source - Where the definition came from, for diagnostics.
76
78
  * @param prefixes - The merged prefix map used for display compaction.
79
+ * @param nouns - The other stories' lookups, which a filter naming a noun
80
+ * resolves its values through. Read at run time only.
77
81
  * @returns The compiled verbs (list, extra verbs, lookup, sample), in order.
78
82
  */
79
83
  export function compilePack(
80
84
  definition: PackDefinition,
81
85
  source: StorySource,
82
86
  prefixes: Readonly<Record<string, string>>,
87
+ nouns?: NounLookups,
83
88
  ): VerbSpec[] {
84
89
  const { noun } = definition;
85
90
  const verbs: VerbSpec[] = [];
86
91
 
87
92
  const tierScope = definition.tierScope;
93
+ // A story may declare a lookup alone; a miss must not point at a `list` it lacks.
94
+ const hasList = definition.list !== undefined;
88
95
 
89
96
  if (definition.list) {
90
97
  verbs.push(
@@ -93,8 +100,10 @@ export function compilePack(
93
100
  verb: "list",
94
101
  summary: definition.description ?? `List ${noun} entries.`,
95
102
  doc: definition.toolDescription,
103
+ ...guidanceOf(definition),
96
104
  source,
97
105
  prefixes,
106
+ ...(nouns ? { nouns } : {}),
98
107
  ...(tierScope ? { tierScope } : {}),
99
108
  }),
100
109
  );
@@ -107,19 +116,24 @@ export function compilePack(
107
116
  verb: verb.verb,
108
117
  summary: verb.description ?? `List ${noun} ${verb.verb}.`,
109
118
  doc: verb.toolDescription,
119
+ ...guidanceOf(verb),
110
120
  source,
111
121
  prefixes,
122
+ ...(nouns ? { nouns } : {}),
112
123
  ...(tierScope ? { tierScope } : {}),
113
124
  }),
114
125
  );
115
126
  }
116
127
 
117
128
  if (definition.lookup) {
129
+ // Normalised ONCE, here, so the lookup verb, its run body, both fetch lanes
130
+ // and the sample all read the same declaration.
131
+ const lookup = withDisclosure(definition.lookup);
118
132
  verbs.push(
119
- compileLookupVerb(definition.lookup, noun, source, prefixes, tierScope),
133
+ compileLookupVerb(lookup, noun, source, prefixes, hasList, tierScope),
120
134
  );
121
- if (definition.lookup.sample) {
122
- verbs.push(compileSampleVerb(definition.lookup, noun, source, prefixes));
135
+ if (lookup.sample) {
136
+ verbs.push(compileSampleVerb(lookup, noun, source, prefixes, hasList));
123
137
  }
124
138
  }
125
139
 
@@ -195,6 +209,7 @@ export function compileListable(
195
209
  * @param definition - A validated pack definition.
196
210
  * @param source - Where the definition came from, for diagnostics.
197
211
  * @param prefixes - The merged prefix map used for display compaction.
212
+ * @param nouns - As {@link compilePack}.
198
213
  * @returns The module: the compiled verbs plus the story's module-level data.
199
214
  * @throws PragmaError CONFIG_ERROR when the definition cannot be compiled,
200
215
  * naming the field and the rule.
@@ -203,6 +218,7 @@ export function compileStoryModule(
203
218
  definition: PackDefinition,
204
219
  source: StorySource,
205
220
  prefixes: Readonly<Record<string, string>>,
221
+ nouns?: NounLookups,
206
222
  ): CapabilityModule {
207
223
  const issue = storyIssues(definition)[0];
208
224
  if (issue) {
@@ -211,17 +227,47 @@ export function compileStoryModule(
211
227
  );
212
228
  }
213
229
  const listable = compileListable(definition);
214
- return {
230
+ const module: CapabilityModule = {
215
231
  name: definition.noun,
216
232
  story: true,
217
- verbs: compilePack(definition, source, prefixes),
233
+ verbs: compilePack(definition, source, prefixes, nouns),
218
234
  colophon: definition.colophon,
219
235
  ...(listable ? { mcpListable: listable } : {}),
220
236
  };
237
+ compiledFrom.set(module, { definition, source, prefixes });
238
+ return module;
239
+ }
240
+
241
+ /** What each story module was compiled from. */
242
+ const compiledFrom = new WeakMap<
243
+ CapabilityModule,
244
+ {
245
+ readonly definition: PackDefinition;
246
+ readonly source: StorySource;
247
+ readonly prefixes: Readonly<Record<string, string>>;
248
+ }
249
+ >();
250
+
251
+ /**
252
+ * The story a module was compiled from by {@link compileStoryModule}, with the
253
+ * source and prefixes it was compiled under; `undefined` for any other module.
254
+ */
255
+ export function storyOf(
256
+ module: CapabilityModule,
257
+ ): ReturnType<(typeof compiledFrom)["get"]> {
258
+ return compiledFrom.get(module);
259
+ }
260
+
261
+ /** Carry a story half's declared guidance onto its verb, omitting what is absent. */
262
+ function guidanceOf(half: PackGuidance): Pick<VerbSpec, "useWhen" | "example"> {
263
+ return {
264
+ ...(half.useWhen ? { useWhen: half.useWhen } : {}),
265
+ ...(half.example ? { example: half.example } : {}),
266
+ };
221
267
  }
222
268
 
223
269
  /** Presentation facts for one compiled list-shaped verb. */
224
- interface ListVerbMeta {
270
+ interface ListVerbMeta extends PackGuidance {
225
271
  readonly noun: string;
226
272
  readonly verb: string;
227
273
  readonly summary: string;
@@ -229,6 +275,8 @@ interface ListVerbMeta {
229
275
  readonly doc?: string;
230
276
  readonly source: StorySource;
231
277
  readonly prefixes: Readonly<Record<string, string>>;
278
+ /** The other stories' lookups, for a filter that names a noun. */
279
+ readonly nouns?: NounLookups;
232
280
  /** The noun's declared tier hierarchy, when its entities are tiered. */
233
281
  readonly tierScope?: PackTierScope;
234
282
  }
@@ -246,6 +294,7 @@ function compileListVerb(shape: PackList, meta: ListVerbMeta): VerbSpec {
246
294
  path: [meta.noun, meta.verb],
247
295
  summary: meta.summary,
248
296
  ...(meta.doc ? { doc: meta.doc } : {}),
297
+ ...guidanceOf(meta),
249
298
  params,
250
299
  output: {
251
300
  formatters: listFormatters(shape, {
@@ -272,6 +321,8 @@ function compileListVerb(shape: PackList, meta: ListVerbMeta): VerbSpec {
272
321
  runBodies().then((m) =>
273
322
  m.makeListRun(shape, {
274
323
  source: meta.source,
324
+ prefixes: meta.prefixes,
325
+ ...(meta.nouns ? { nouns: meta.nouns } : {}),
275
326
  ...(meta.tierScope ? { tierScope: meta.tierScope } : {}),
276
327
  })(params, rt),
277
328
  ),
@@ -285,6 +336,7 @@ function compileLookupVerb(
285
336
  noun: string,
286
337
  source: StorySource,
287
338
  prefixes: Readonly<Record<string, string>>,
339
+ hasList: boolean,
288
340
  tierScope?: PackTierScope,
289
341
  ): VerbSpec {
290
342
  // Derive-by-default: every lookup completes its `<name>` from the pack index
@@ -319,6 +371,7 @@ function compileLookupVerb(
319
371
  summary:
320
372
  lookup.description ?? `Look up ${noun} details by name, IRI, or glob.`,
321
373
  ...(lookup.toolDescription ? { doc: lookup.toolDescription } : {}),
374
+ ...guidanceOf(lookup),
322
375
  params: [nameParam, ...tierParams(tierScope)],
323
376
  output: { formatters: lookupFormatters(lookup, prefixes) },
324
377
  examples: [
@@ -332,13 +385,18 @@ function compileLookupVerb(
332
385
  ]
333
386
  : []),
334
387
  ],
335
- ...(lookup.disclosure
336
- ? { disclosure: disclosureSpec(lookup.disclosure) }
337
- : {}),
388
+ disclosure: disclosureSpec(lookup.disclosure),
338
389
  capability: READ_CAPABILITY,
339
390
  run: (params: Record<string, unknown>, rt: PragmaRuntime) =>
340
391
  runBodies().then((m) =>
341
- m.makeLookupRun(lookup, noun, source, prefixes, tierScope)(params, rt),
392
+ m.makeLookupRun(
393
+ lookup,
394
+ noun,
395
+ source,
396
+ prefixes,
397
+ hasList,
398
+ tierScope,
399
+ )(params, rt),
342
400
  ),
343
401
  };
344
402
  return asVerb(verb);
@@ -374,6 +432,7 @@ function compileSampleVerb(
374
432
  noun: string,
375
433
  source: StorySource,
376
434
  prefixes: Readonly<Record<string, string>>,
435
+ hasList: boolean,
377
436
  ): VerbSpec {
378
437
  const defaultCount = sampleDefaultCount(lookup);
379
438
  const config = lookup.sample === true ? undefined : lookup.sample;
@@ -396,6 +455,7 @@ function compileSampleVerb(
396
455
  config?.description ??
397
456
  `Return randomly selected complete ${noun} entries as exemplars.`,
398
457
  ...(config?.toolDescription ? { doc: config.toolDescription } : {}),
458
+ ...guidanceOf(config ?? {}),
399
459
  params: countParam,
400
460
  output: { formatters: sampleFormatters(lookup, noun, prefixes) },
401
461
  examples: [
@@ -411,12 +471,41 @@ function compileSampleVerb(
411
471
  source,
412
472
  prefixes,
413
473
  defaultCount,
474
+ hasList,
414
475
  )(params, rt),
415
476
  ),
416
477
  };
417
478
  return asVerb(verb);
418
479
  }
419
480
 
481
+ /**
482
+ * Give a lookup that declares no disclosure the canonical ladder: default
483
+ * `detailed` (so its default answer is unchanged) and untagged expands from
484
+ * `standard` (so `summary` is the fields alone, and `standard` equals
485
+ * `detailed`). A declared disclosure is returned as written.
486
+ */
487
+ function withDisclosure(lookup: PackLookup): PackLookup {
488
+ if (lookup.disclosure) return lookup;
489
+ return {
490
+ ...lookup,
491
+ disclosure: { levels: [...DETAIL_LEVELS], default: IMPUTED_DEFAULT_LEVEL },
492
+ ...(lookup.expand
493
+ ? {
494
+ expand: lookup.expand.map((expand) => ({
495
+ ...expand,
496
+ level: expand.level ?? IMPUTED_EXPAND_LEVEL,
497
+ })),
498
+ }
499
+ : {}),
500
+ };
501
+ }
502
+
503
+ /** The level an imputed disclosure answers at when none is asked for: all of it. */
504
+ const IMPUTED_DEFAULT_LEVEL = "detailed";
505
+
506
+ /** The level from which an imputed disclosure shows an untagged expand. */
507
+ const IMPUTED_EXPAND_LEVEL = "standard";
508
+
420
509
  /** Normalize a pack disclosure into a {@link DisclosureSpec} (default → base). */
421
510
  function disclosureSpec(disclosure: PackLookup["disclosure"]): DisclosureSpec {
422
511
  const levels = disclosure?.levels ?? [];