@canonical/pragma-cli 0.36.0 → 0.38.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 (312) hide show
  1. package/README.md +3 -2
  2. package/bundled-skills/adoption-a1-styles/SKILL.md +24 -0
  3. package/bundled-skills/adoption-a2-components/SKILL.md +22 -6
  4. package/bundled-skills/anatomy-author/ANATOMY_DSL_SPEC.md +1397 -327
  5. package/bundled-skills/anatomy-author/SKILL.md +840 -137
  6. package/bundled-skills/design-auditor/SKILL.md +133 -9
  7. package/bundled-skills/implement-component/SKILL.md +192 -23
  8. package/bundled-skills/specify-component/SKILL.md +124 -12
  9. package/bundled-skills/specify-pattern/SKILL.md +47 -8
  10. package/dist/package.json +23 -20
  11. package/dist/pragma.conf.js +963 -40
  12. package/dist/pragma.conf.js.map +1 -1
  13. package/dist/src/capabilities/capabilities/catalog.js +14 -5
  14. package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
  15. package/dist/src/capabilities/capabilities/hints.js +33 -5
  16. package/dist/src/capabilities/capabilities/hints.js.map +1 -1
  17. package/dist/src/capabilities/config/show.render.js +30 -2
  18. package/dist/src/capabilities/config/show.render.js.map +1 -1
  19. package/dist/src/capabilities/create/create.verb.js +2 -1
  20. package/dist/src/capabilities/create/create.verb.js.map +1 -1
  21. package/dist/src/capabilities/create/createSurface.generated.js +23 -1
  22. package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
  23. package/dist/src/capabilities/doctor/checks/checkNodeVersion.js +11 -6
  24. package/dist/src/capabilities/doctor/checks/checkNodeVersion.js.map +1 -1
  25. package/dist/src/capabilities/doctor/checks/harnessInventory.js +1 -1
  26. package/dist/src/capabilities/doctor/checks/harnessInventory.js.map +1 -1
  27. package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js +38 -0
  28. package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js.map +1 -0
  29. package/dist/src/capabilities/doctor/checks/targetHealth.js +104 -14
  30. package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
  31. package/dist/src/capabilities/graph/connect.render.js +101 -0
  32. package/dist/src/capabilities/graph/connect.render.js.map +1 -0
  33. package/dist/src/capabilities/graph/connect.types.js +21 -0
  34. package/dist/src/capabilities/graph/connect.types.js.map +1 -0
  35. package/dist/src/capabilities/graph/connect.verb.js +147 -0
  36. package/dist/src/capabilities/graph/connect.verb.js.map +1 -0
  37. package/dist/src/capabilities/graph/index.js +5 -3
  38. package/dist/src/capabilities/graph/index.js.map +1 -1
  39. package/dist/src/capabilities/graph/queryText.js +206 -0
  40. package/dist/src/capabilities/graph/queryText.js.map +1 -0
  41. package/dist/src/capabilities/graph/relationIndex.js +302 -0
  42. package/dist/src/capabilities/graph/relationIndex.js.map +1 -0
  43. package/dist/src/capabilities/graph/runConnect.js +456 -0
  44. package/dist/src/capabilities/graph/runConnect.js.map +1 -0
  45. package/dist/src/capabilities/graph/runQuery.js +21 -4
  46. package/dist/src/capabilities/graph/runQuery.js.map +1 -1
  47. package/dist/src/capabilities/setup/buildPlan.js +6 -2
  48. package/dist/src/capabilities/setup/buildPlan.js.map +1 -1
  49. package/dist/src/capabilities/setup/operations/index.js +7 -2
  50. package/dist/src/capabilities/setup/operations/index.js.map +1 -1
  51. package/dist/src/capabilities/setup/operations/setupCompletions.js +6 -2
  52. package/dist/src/capabilities/setup/operations/setupCompletions.js.map +1 -1
  53. package/dist/src/capabilities/setup/operations/setupConfig.js +4 -2
  54. package/dist/src/capabilities/setup/operations/setupConfig.js.map +1 -1
  55. package/dist/src/capabilities/setup/operations/setupGenerator.js +72 -14
  56. package/dist/src/capabilities/setup/operations/setupGenerator.js.map +1 -1
  57. package/dist/src/capabilities/setup/operations/setupLsp.js +372 -62
  58. package/dist/src/capabilities/setup/operations/setupLsp.js.map +1 -1
  59. package/dist/src/capabilities/setup/operations/setupMcp.js +150 -5
  60. package/dist/src/capabilities/setup/operations/setupMcp.js.map +1 -1
  61. package/dist/src/capabilities/setup/operations/setupSkills.js +4 -2
  62. package/dist/src/capabilities/setup/operations/setupSkills.js.map +1 -1
  63. package/dist/src/capabilities/setup/operations/writability.js +114 -0
  64. package/dist/src/capabilities/setup/operations/writability.js.map +1 -0
  65. package/dist/src/capabilities/setup/plan.js.map +1 -1
  66. package/dist/src/capabilities/setup/setup.verb.js +80 -31
  67. package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
  68. package/dist/src/capabilities/setup/targets.js +124 -42
  69. package/dist/src/capabilities/setup/targets.js.map +1 -1
  70. package/dist/src/capabilities/shared/index.js +1 -1
  71. package/dist/src/capabilities/shared/index.js.map +1 -1
  72. package/dist/src/constants.js +7 -1
  73. package/dist/src/constants.js.map +1 -1
  74. package/dist/src/kernel/error/renderError.js +3 -2
  75. package/dist/src/kernel/error/renderError.js.map +1 -1
  76. package/dist/src/kernel/error/validOptions.js +41 -0
  77. package/dist/src/kernel/error/validOptions.js.map +1 -0
  78. package/dist/src/kernel/packs/compile.js +90 -6
  79. package/dist/src/kernel/packs/compile.js.map +1 -1
  80. package/dist/src/kernel/packs/cursor.js +135 -0
  81. package/dist/src/kernel/packs/cursor.js.map +1 -0
  82. package/dist/src/kernel/packs/graphql/buildLookupDocument.js +9 -2
  83. package/dist/src/kernel/packs/graphql/buildLookupDocument.js.map +1 -1
  84. package/dist/src/kernel/packs/paging.js +108 -0
  85. package/dist/src/kernel/packs/paging.js.map +1 -0
  86. package/dist/src/kernel/packs/renderPack.js +154 -5
  87. package/dist/src/kernel/packs/renderPack.js.map +1 -1
  88. package/dist/src/kernel/packs/resolveEntity.js +118 -19
  89. package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
  90. package/dist/src/kernel/packs/runBodies.js +156 -18
  91. package/dist/src/kernel/packs/runBodies.js.map +1 -1
  92. package/dist/src/kernel/packs/schema.js +160 -12
  93. package/dist/src/kernel/packs/schema.js.map +1 -1
  94. package/dist/src/kernel/packs/sparql/authorQuery.js +227 -0
  95. package/dist/src/kernel/packs/sparql/authorQuery.js.map +1 -0
  96. package/dist/src/kernel/packs/sparql/buildListQuery.js +251 -0
  97. package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -0
  98. package/dist/src/kernel/packs/sparql/buildLookupQuery.js +125 -22
  99. package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
  100. package/dist/src/kernel/packs/sparql/filterValues.js +145 -0
  101. package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -0
  102. package/dist/src/kernel/packs/sparql/index.js +24 -14
  103. package/dist/src/kernel/packs/sparql/index.js.map +1 -1
  104. package/dist/src/kernel/packs/sparql/runSelect.js +10 -4
  105. package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
  106. package/dist/src/kernel/packs/sparql/searchTerm.js +25 -0
  107. package/dist/src/kernel/packs/sparql/searchTerm.js.map +1 -0
  108. package/dist/src/kernel/packs/storyRules.js +151 -0
  109. package/dist/src/kernel/packs/storyRules.js.map +1 -0
  110. package/dist/src/kernel/packs/tierScope.js +310 -0
  111. package/dist/src/kernel/packs/tierScope.js.map +1 -0
  112. package/dist/src/kernel/packs/types.js +80 -0
  113. package/dist/src/kernel/packs/types.js.map +1 -1
  114. package/dist/src/kernel/project/cli/buildProgram.js +10 -1
  115. package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
  116. package/dist/src/kernel/project/cli/dispatch.js +104 -3
  117. package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
  118. package/dist/src/kernel/project/cli/rootHelp.js +8 -5
  119. package/dist/src/kernel/project/cli/rootHelp.js.map +1 -1
  120. package/dist/src/kernel/project/cli/suggestNames.js +31 -7
  121. package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
  122. package/dist/src/kernel/project/mcp/registerVerb.js +5 -1
  123. package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
  124. package/dist/src/kernel/render/renderers.js +34 -2
  125. package/dist/src/kernel/render/renderers.js.map +1 -1
  126. package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js +2 -3
  127. package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js.map +1 -1
  128. package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js +1 -1
  129. package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js.map +1 -1
  130. package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js +4 -0
  131. package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js.map +1 -0
  132. package/dist/src/kernel/runtime/graphpack/embedded.js +13 -6
  133. package/dist/src/kernel/runtime/graphpack/embedded.js.map +1 -1
  134. package/dist/src/kernel/runtime/graphpack/read.js +17 -10
  135. package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
  136. package/dist/src/kernel/runtime/loadSession.js +3 -1
  137. package/dist/src/kernel/runtime/loadSession.js.map +1 -1
  138. package/dist/src/kernel/runtime/probe.js +1 -1
  139. package/dist/src/kernel/runtime/probe.js.map +1 -1
  140. package/dist/src/kernel/runtime/readEntity.js +19 -4
  141. package/dist/src/kernel/runtime/readEntity.js.map +1 -1
  142. package/dist/src/kernel/runtime/refs/parseRef.js +17 -1
  143. package/dist/src/kernel/runtime/refs/parseRef.js.map +1 -1
  144. package/dist/src/kernel/runtime/refs/resolve.js +38 -5
  145. package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
  146. package/dist/src/kernel/spec/emitReference.js +1 -1
  147. package/dist/src/kernel/spec/emitReference.js.map +1 -1
  148. package/package.json +23 -20
  149. package/pragma.conf.ts +983 -40
  150. package/src/capabilities/block.parity.test.ts +207 -27
  151. package/src/capabilities/block.shipped.exec.test.ts +156 -14
  152. package/src/capabilities/block.tierRank.exec.test.ts +46 -9
  153. package/src/capabilities/capabilities/catalog.ts +14 -5
  154. package/src/capabilities/capabilities/hints.ts +40 -5
  155. package/src/capabilities/config/collectConfigShow.test.ts +1 -0
  156. package/src/capabilities/config/set.test.ts +24 -25
  157. package/src/capabilities/config/show.render.test.ts +9 -4
  158. package/src/capabilities/config/show.render.ts +30 -2
  159. package/src/capabilities/create/byteEquality.test.ts +28 -1
  160. package/src/capabilities/create/create.test.ts +1 -0
  161. package/src/capabilities/create/create.verb.ts +2 -1
  162. package/src/capabilities/create/createGrammar.test.ts +4 -1
  163. package/src/capabilities/create/createSurface.generated.ts +23 -1
  164. package/src/capabilities/create/crossCli.subprocess.test.ts +10 -3
  165. package/src/capabilities/distribution.parity.test.ts +12 -11
  166. package/src/capabilities/distribution.test.ts +77 -7
  167. package/src/capabilities/doctor/checks/checkNodeVersion.test.ts +49 -0
  168. package/src/capabilities/doctor/checks/checkNodeVersion.ts +11 -7
  169. package/src/capabilities/doctor/checks/harnessInventory.test.ts +13 -6
  170. package/src/capabilities/doctor/checks/harnessInventory.ts +8 -4
  171. package/src/capabilities/doctor/checks/isSupportedNodeVersion.test.ts +111 -0
  172. package/src/capabilities/doctor/checks/isSupportedNodeVersion.ts +34 -0
  173. package/src/capabilities/doctor/checks/mcpCommand.test.ts +7 -1
  174. package/src/capabilities/doctor/checks/targetHealth.ts +114 -13
  175. package/src/capabilities/doctor/doctor.test.ts +327 -36
  176. package/src/capabilities/emptyRecovery.test.ts +84 -28
  177. package/src/capabilities/graph/connect.render.ts +141 -0
  178. package/src/capabilities/graph/connect.shipped.exec.test.ts +370 -0
  179. package/src/capabilities/graph/connect.test.ts +361 -0
  180. package/src/capabilities/graph/connect.types.ts +136 -0
  181. package/src/capabilities/graph/connect.verb.ts +165 -0
  182. package/src/capabilities/graph/index.ts +5 -3
  183. package/src/capabilities/graph/query.shipped.exec.test.ts +66 -0
  184. package/src/capabilities/graph/query.test.ts +17 -0
  185. package/src/capabilities/graph/queryText.test.ts +121 -0
  186. package/src/capabilities/graph/queryText.ts +240 -0
  187. package/src/capabilities/graph/relationIndex.ts +372 -0
  188. package/src/capabilities/graph/runConnect.ts +577 -0
  189. package/src/capabilities/graph/runQuery.ts +21 -6
  190. package/src/capabilities/lazy.test.ts +32 -2
  191. package/src/capabilities/listBudget.shipped.exec.test.ts +239 -0
  192. package/src/capabilities/listLookup.shipped.exec.test.ts +387 -0
  193. package/src/capabilities/listQuery.shipped.exec.test.ts +400 -0
  194. package/src/capabilities/resources/resources.test.ts +38 -2
  195. package/src/capabilities/setup/buildPlan.ts +7 -1
  196. package/src/capabilities/setup/operations/index.ts +14 -0
  197. package/src/capabilities/setup/operations/setupCompletions.ts +9 -2
  198. package/src/capabilities/setup/operations/setupConfig.ts +7 -2
  199. package/src/capabilities/setup/operations/setupGenerator.ts +93 -15
  200. package/src/capabilities/setup/operations/setupLsp.ts +544 -85
  201. package/src/capabilities/setup/operations/setupMcp.ts +212 -4
  202. package/src/capabilities/setup/operations/setupSkills.ts +7 -2
  203. package/src/capabilities/setup/operations/writability.test.ts +151 -0
  204. package/src/capabilities/setup/operations/writability.ts +135 -0
  205. package/src/capabilities/setup/plan.ts +11 -0
  206. package/src/capabilities/setup/setup.test.ts +992 -16
  207. package/src/capabilities/setup/setup.verb.ts +84 -36
  208. package/src/capabilities/setup/targets.ts +152 -48
  209. package/src/capabilities/setup/types.ts +41 -5
  210. package/src/capabilities/shared/index.ts +1 -0
  211. package/src/capabilities/sources/sources.test.ts +55 -0
  212. package/src/capabilities/surface.test.ts +59 -11
  213. package/src/capabilities/tier.parity.test.ts +2 -2
  214. package/src/capabilities/token.parity.test.ts +784 -0
  215. package/src/capabilities/tokenGraph.cli.test.ts +279 -0
  216. package/src/capabilities/tokenGraph.shipped.exec.test.ts +653 -0
  217. package/src/capabilities/variable.parity.test.ts +448 -0
  218. package/src/constants.ts +8 -0
  219. package/src/kernel/completion/shellDrive.test.ts +32 -30
  220. package/src/kernel/config/defaults.test.ts +21 -2
  221. package/src/kernel/config/readConfig.test.ts +1 -1
  222. package/src/kernel/error/renderError.ts +3 -2
  223. package/src/kernel/error/validOptions.test.ts +57 -0
  224. package/src/kernel/error/validOptions.ts +45 -0
  225. package/src/kernel/packs/collect.test.ts +2 -0
  226. package/src/kernel/packs/compile.test.ts +188 -15
  227. package/src/kernel/packs/compile.ts +109 -14
  228. package/src/kernel/packs/cursor.ts +135 -0
  229. package/src/kernel/packs/filteredEmpty.test.ts +213 -0
  230. package/src/kernel/packs/graphql/buildLookupDocument.ts +16 -9
  231. package/src/kernel/packs/graphqlSource.test.ts +152 -0
  232. package/src/kernel/packs/listNotice.test.ts +156 -0
  233. package/src/kernel/packs/paging.ts +110 -0
  234. package/src/kernel/packs/renderPack.ts +186 -13
  235. package/src/kernel/packs/resolveEntity.test.ts +62 -3
  236. package/src/kernel/packs/resolveEntity.ts +186 -26
  237. package/src/kernel/packs/runBodies.ts +196 -26
  238. package/src/kernel/packs/schema.ts +180 -13
  239. package/src/kernel/packs/sparql/authorQuery.test.ts +179 -0
  240. package/src/kernel/packs/sparql/authorQuery.ts +257 -0
  241. package/src/kernel/packs/sparql/buildListQuery.test.ts +344 -0
  242. package/src/kernel/packs/sparql/buildListQuery.ts +344 -0
  243. package/src/kernel/packs/sparql/buildLookupQuery.test.ts +138 -0
  244. package/src/kernel/packs/sparql/buildLookupQuery.ts +140 -20
  245. package/src/kernel/packs/sparql/filterValues.test.ts +86 -0
  246. package/src/kernel/packs/sparql/filterValues.ts +182 -0
  247. package/src/kernel/packs/sparql/index.ts +30 -14
  248. package/src/kernel/packs/sparql/runSelect.test.ts +63 -0
  249. package/src/kernel/packs/sparql/runSelect.ts +42 -2
  250. package/src/kernel/packs/sparql/searchTerm.ts +29 -0
  251. package/src/kernel/packs/storyRules.ts +180 -0
  252. package/src/kernel/packs/tierScope.test.ts +487 -0
  253. package/src/kernel/packs/tierScope.ts +409 -0
  254. package/src/kernel/packs/toolDescription.test.ts +13 -10
  255. package/src/kernel/packs/types.ts +311 -9
  256. package/src/kernel/project/cli/buildProgram.ts +17 -1
  257. package/src/kernel/project/cli/dispatch.test.ts +87 -0
  258. package/src/kernel/project/cli/dispatch.ts +114 -4
  259. package/src/kernel/project/cli/rootHelp.ts +8 -5
  260. package/src/kernel/project/cli/suggest.test.ts +35 -0
  261. package/src/kernel/project/cli/suggestNames.ts +34 -7
  262. package/src/kernel/project/mcp/registerVerb.ts +5 -1
  263. package/src/kernel/render/contracts.ts +2 -0
  264. package/src/kernel/render/prefixes.test.ts +2 -0
  265. package/src/kernel/render/renderers.test.ts +43 -0
  266. package/src/kernel/render/renderers.ts +34 -1
  267. package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +2 -3
  268. package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +1 -1
  269. package/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.ts +3 -0
  270. package/src/kernel/runtime/graphpack/embedded.ts +14 -6
  271. package/src/kernel/runtime/graphpack/graphpack.test.ts +31 -5
  272. package/src/kernel/runtime/graphpack/read.ts +17 -13
  273. package/src/kernel/runtime/loadSession.ts +3 -1
  274. package/src/kernel/runtime/probe.ts +1 -1
  275. package/src/kernel/runtime/readEntity.ts +19 -4
  276. package/src/kernel/runtime/refs/parseRef.test.ts +49 -1
  277. package/src/kernel/runtime/refs/parseRef.ts +25 -1
  278. package/src/kernel/runtime/refs/resolve.ts +39 -5
  279. package/src/kernel/runtime/types.ts +30 -0
  280. package/src/kernel/spec/emitReference.ts +1 -1
  281. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  282. package/src/kernel/spec/types.ts +17 -0
  283. package/src/testing/PARITY_GAPS.ts +4 -4
  284. package/src/testing/behavioral/agentSession.mcp.test.ts +43 -39
  285. package/src/testing/behavioral/completion.test.ts +7 -6
  286. package/src/testing/behavioral/disclosure.test.ts +39 -37
  287. package/src/testing/behavioral/errorMatrix.mcp.test.ts +82 -64
  288. package/src/testing/behavioral/errorMatrix.storeless.test.ts +11 -10
  289. package/src/testing/behavioral/journeys.cli.test.ts +71 -32
  290. package/src/testing/behavioral/journeys.defaultPack.test.ts +6 -4
  291. package/src/testing/behavioral/journeys.firstInstall.test.ts +70 -5
  292. package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +4 -3
  293. package/src/testing/behavioral/journeys.packageStories.test.ts +2 -1
  294. package/src/testing/behavioral/mcpSurface.test.ts +28 -25
  295. package/src/testing/behavioral/parity.test.ts +25 -23
  296. package/src/testing/eval/__snapshots__/eval.test.ts.snap +20 -0
  297. package/src/testing/eval/cases/effectful.ts +7 -1
  298. package/src/testing/eval/cases/readNouns.ts +100 -11
  299. package/src/testing/fixtures/blockGraph.ts +89 -5
  300. package/src/testing/fixtures/connectGraph.ts +159 -0
  301. package/src/testing/fixtures/graph/canonical.ts +128 -32
  302. package/src/testing/helpers/fsProbe.ts +88 -0
  303. package/src/testing/helpers/runCli.test.ts +44 -0
  304. package/src/testing/helpers/runCli.ts +27 -3
  305. package/src/testing/perf/budgets.test.ts +3 -1
  306. package/src/testing/perf/budgets.ts +138 -54
  307. package/dist/src/kernel/packs/sparql/applyFilters.js +0 -167
  308. package/dist/src/kernel/packs/sparql/applyFilters.js.map +0 -1
  309. package/dist/src/kernel/packs/sparql/applySearch.js +0 -25
  310. package/dist/src/kernel/packs/sparql/applySearch.js.map +0 -1
  311. package/src/kernel/packs/sparql/applyFilters.ts +0 -203
  312. package/src/kernel/packs/sparql/applySearch.ts +0 -33
@@ -146,17 +146,25 @@ Start with a broad inventory, then drill into specific concerns:
146
146
  pragma block list # every component, pattern, layout and subcomponent, with type and tier
147
147
  pragma modifier list # every modifier family with its values
148
148
  pragma tier list # the tiers blocks can live in
149
+ pragma token list # the design-token symbols, by their dotted names
150
+ pragma variable list # the CSS custom properties those symbols are emitted as
149
151
  pragma graph query "…" # the audit queries below
150
152
  pragma graph inspect <IRI-or-ds:name> # every triple on one entity
153
+ pragma sources status # which pack is answering, and how many entities it holds
151
154
  ```
152
155
 
156
+ Run `sources status` before the first query and record what it says in the report's
157
+ scope line. Every count in an audit is a count from one pack at one version; a finding
158
+ that does not say which pack answered cannot be compared against the next audit.
159
+
153
160
  Two graph-query mechanics to know (shared with the sibling skills):
154
161
 
155
- - Common prefixes (`ds:`, `cs:`) are applied automatically — no PREFIX preamble.
156
- - Inside a SPARQL body, a local name with more than one dot
157
- (`ds:global.component.button`) does not parse — use the full IRI there
158
- (`<https://ds.canonical.com/global.component.button>`). Single-dot and dot-free
159
- prefixed names (`ds:global`, `ds:Component`) work as-is.
162
+ - Common prefixes (`ds:`, `cs:`, `dt:`) are applied automatically — no PREFIX preamble.
163
+ - A prefixed name carrying dots or slashes parses inside a SPARQL body, so write the
164
+ name the graph prints: `ds:global.component.button` and
165
+ `dt:s4/web/cond/layer-ds-modifiers/root` both resolve. The full IRI
166
+ (`<https://ds.canonical.com/global.component.button>`) is accepted too and answers
167
+ identically — reach for it only when the namespace has no declared prefix.
160
168
 
161
169
  Note that `block list` deliberately omits the `ds:Group` class — an audit that counts
162
170
  only the list under-counts. The queries below go to the graph directly, so they see
@@ -166,10 +174,21 @@ groups too.
166
174
  > output (counts, names, ratios) into documentation, PRs, or this skill.
167
175
 
168
176
  One more live-data rule: before trusting any query, confirm its predicates against the
169
- current ontology (`pragma ontology lookup ds`). The vocabulary evolves — for example,
170
- `ds:usage` subsumed the former `whenToUse`/`whenNotToUse` predicates, and queries
171
- against retired names return empty tables, not errors. An empty result must be
172
- distinguished from a wrong query before it becomes a finding.
177
+ current ontology. The properties are a section of their own and the flag asks for it —
178
+ `pragma ontology lookup ds` alone prints the classes:
179
+
180
+ ```bash
181
+ pragma ontology lookup ds --properties # ds: predicates: usage, summary, hasModifierFamily, …
182
+ pragma ontology lookup dt --properties # dt: predicates: covers, channelOf, …
183
+ ```
184
+
185
+ Both namespaces matter, and they split along a line worth knowing: the block-side
186
+ predicates live in `ds:`, and the token-side ones — `dt:covers`, `dt:channelOf` — live
187
+ in `dt:`, so a token audit that only checks `ds:` never finds the predicate it needs.
188
+ The vocabulary evolves — for example, `ds:usage` subsumed the former
189
+ `whenToUse`/`whenNotToUse` predicates, and queries against retired names return empty
190
+ tables, not errors. An empty result must be distinguished from a wrong query before it
191
+ becomes a finding.
173
192
 
174
193
  ## Audit Dimensions
175
194
 
@@ -198,6 +217,84 @@ where the team's practice is right and the standard is not, the finding becomes
198
217
  proposed change to the standard — the standards are open to contribution, and audits
199
218
  are where their gaps surface.
200
219
 
220
+ ### 6. Token Audit
221
+ Which symbols exist, which families rebind them, and which blocks consume them?
222
+
223
+ The token graph is queryable, and a coverage question about tokens is answered there
224
+ rather than by reading stylesheets. The inventory reads first:
225
+
226
+ ```bash
227
+ pragma token list --search focus # which symbols exist around a word
228
+ pragma token list --type color # the population of one type
229
+ pragma token list --channel-of color.text # the channels that provision one symbol
230
+ pragma token consumers --symbol color.text # which blocks consume a symbol, at which key and state
231
+ ```
232
+
233
+ **`token list`** is the inventory. `--search <word>` matches name and description
234
+ (`--search focus` answers `## Token (7)`, the focus-ring family and its channel);
235
+ `--type <type>` filters by the symbol's own type; and `--limit` defaults to 300, so a
236
+ count is a PAGE until the output says otherwise. It says so plainly — "Showing 300
237
+ token entries, and more exist", with an `--after` cursor — so never read a count of
238
+ 300 as a population; raise `--limit` and read the real one (`--type color` is 473,
239
+ not the 300 the default shows). `--channel-of` takes a SYMBOL, not a family name:
240
+ `--channel-of color.text` answers with `modifier.color.text` and
241
+ `surface.color.text`, the two channels that provision it. A family name there is an `INVALID_INPUT` error that lists the symbols
242
+ it would accept.
243
+
244
+ **A symbol's coverage — which families may rebind it — is `token lookup`'s
245
+ `### Covered by` section**, not `modifier lookup`'s. `pragma modifier lookup
246
+ Criticality` answers with the family's `### Values` (Error, Information, Success,
247
+ Warning) and is the read for what a family offers; `pragma token lookup color.text`
248
+ answers with the families that reach it. Audit from both ends: a family with values
249
+ nothing covers, and a symbol no family covers, are different findings.
250
+
251
+ The family end has no verb of its own — the predicate is `dt:covers`, and the query
252
+ below is the read (the Family Coverage query under Key Queries lists it per family):
253
+
254
+ ```bash
255
+ pragma graph query "SELECT ?symbol WHERE { ds:global.modifier_family.criticality dt:covers ?symbol }"
256
+ ```
257
+
258
+ `modifier lookup` renders the family's values and nothing else, so the family's OWN
259
+ documentation fields never appear in it — a family with a blank `ds:summary` looks
260
+ identical to a documented one. For a quality finding about the family itself, inspect
261
+ it: `pragma graph inspect ds:global.modifier_family.criticality`. This is the
262
+ present-but-blank trap again, one level up from the blocks.
263
+
264
+ `pragma variable lookup modifier-color-text` is the third view of the same fact, and
265
+ the most concrete one: one declaration row per modifier, each naming the selector, the
266
+ cascade layer, what it emits and the `file:line` — seventeen rows for that channel,
267
+ which is the family coverage as the stylesheet actually implements it.
268
+
269
+ **`token consumers` answers empty today, and that is a real finding, not a broken
270
+ command.** No pack records which block consumes which symbol so far: the bindings come
271
+ from the anatomies, the anatomies are being rewritten by hand, and the rows appear
272
+ once the authored anatomies are applied to the document and the pack is bumped. Until
273
+ then, do not report "no block consumes this symbol" as a coverage gap — the honest
274
+ finding is that consumption is not yet measurable, and `anatomy-author` is where the
275
+ work that makes it measurable happens.
276
+
277
+ Read the empty answer carefully, because under a `--symbol` filter it is ambiguous by
278
+ construction: it means either that nobody consumes THAT symbol, or that the store holds
279
+ no bindings at all. The filtered message does not distinguish them, and its wording is
280
+ in flux — do not learn it by heart. Run the verb unfiltered to tell them apart:
281
+
282
+ ```bash
283
+ pragma token consumers # the store-wide answer: are there any bindings at all?
284
+ pragma token consumers --symbol color.text # then the one symbol
285
+ ```
286
+
287
+ An unfiltered zero settles it — the store has no bindings, so no per-symbol result can
288
+ mean anything yet. Only once the unfiltered read returns rows does a filtered zero
289
+ become a statement about that symbol. A symbol name the store does not know is a
290
+ different outcome again: `INVALID_INPUT`, with the accepted symbols listed, and it
291
+ exits non-zero.
292
+
293
+ The same reads are available as MCP tools — `token_list`, `token_lookup`,
294
+ `token_consumers`, `variable_lookup`, `variable_chain`, `token_values` among them. Take
295
+ the current list from `pragma capabilities` rather than from this paragraph: the
296
+ catalog grows between releases.
297
+
201
298
  ## Key Queries
202
299
 
203
300
  Documentation-completeness queries must catch BOTH the absent predicate and the empty
@@ -301,6 +398,23 @@ ORDER BY DESC(?usage)"
301
398
  **What it asks**: which families are core and which are unused. A zero-usage family is
302
399
  a question — premature abstraction, or adoption that has not landed yet?
303
400
 
401
+ ### Consistency: Family Coverage — how many symbols each family rebinds
402
+
403
+ ```bash
404
+ pragma graph query "SELECT ?familyName (COUNT(?symbol) as ?symbols) WHERE {
405
+ ?family a ds:ModifierFamily ;
406
+ ds:name ?familyName .
407
+ OPTIONAL { ?family dt:covers ?symbol }
408
+ } GROUP BY ?familyName
409
+ ORDER BY DESC(?symbols)"
410
+ ```
411
+
412
+ **What it asks**: the family end of the token audit — which families actually rebind
413
+ symbols, and which declare values that reach no symbol at all. Keep the `OPTIONAL` as
414
+ written: without it a family covering nothing drops out of the table, and those zero
415
+ rows are the finding. Pair this with the per-component usage count above — a family can
416
+ be widely covered in the token graph and carried by almost no component, or the reverse.
417
+
304
418
  ### Consistency: Components Without Modifier Families
305
419
 
306
420
  ```bash
@@ -433,6 +547,16 @@ they MEAN is judged against the team's intent:
433
547
  "looks" off) and label them differently in the report.
434
548
  - Follow up drill-downs with `pragma graph inspect <IRI>` on the specific blocks a
435
549
  query surfaced, and `pragma block lookup <Name>` for the human-readable view.
550
+ - `block lookup` takes a DISPLAY name, a `ds:`-prefixed IRI, or a glob — not the bare
551
+ dotted name. A `block list` row prints `ds:apps_lxd.component.meter`; paste it with
552
+ the prefix and it resolves, drop the prefix and it is `ENTITY_NOT_FOUND`.
553
+ - A display name shared across tiers answers with EVERY tier's block, concatenated —
554
+ one `## <Name>` heading and one `- Tier:` line each, and they are DIFFERENT blocks,
555
+ independently authored, not repeats of one. `pragma block lookup Badge` returns the
556
+ global, launchpad and portal Badges in a single answer. Read the `- Tier:` lines,
557
+ count the headings, and pick; an audit that reads only the first heading silently
558
+ scopes itself to one tier. To go straight to one, use its `ds:` IRI
559
+ (`pragma block lookup ds:global.component.badge`).
436
560
 
437
561
  ## Limitations
438
562
 
@@ -126,19 +126,29 @@ Then, and only then, the breakdown.
126
126
  1. **Read the spec.**
127
127
  - Documented block: `pragma block lookup <Name>` — anatomy, modifiers, properties in
128
128
  one read (MCP: block_lookup); `pragma graph inspect <uri>` for the full triple view.
129
- A bare name can match blocks in several tiers, and `block lookup` silently picks
130
- one — it resolves `ds:name` globally and cannot be steered to a tier. Take the
131
- picked block's tier from the lookup's own `- Tier:` line, and check for other
132
- tiers carrying the name with the name query below, then address the tier you
133
- want by its IRI.
129
+ A bare name resolves `ds:name` globally, so where several tiers carry the name
130
+ the lookup answers with EVERY one of them — the full writeups concatenated, each
131
+ under its own `## <Name>` heading with its own `- Tier:` line. They are different
132
+ blocks, separately authored, not repeats of one: the global Badge and the
133
+ launchpad Badge have different summaries, properties and anatomies. So the read is
134
+ not "confirm the tier it picked" but "count the headings and pick": scroll the
135
+ whole answer, read every `- Tier:` line, and decide which block you are
136
+ implementing. Stopping at the first heading is how a reader silently implements
137
+ another tier's component.
138
+
139
+ To skip the disambiguation, address one block directly by its `ds:` IRI, which
140
+ `block lookup` accepts:
134
141
 
135
142
  ```bash
143
+ pragma block lookup ds:global.component.badge # exactly one block
136
144
  pragma graph query "SELECT ?b WHERE { ?b ds:name ?n . FILTER(LCASE(?n) = LCASE('<Name>')) }" # every tier carrying the name
137
145
  ```
138
146
 
139
- If the lookup picked the wrong tier's block, read the one you want with
140
- `pragma graph inspect <IRI from that row>` instead — step 2 derives the package
141
- from the tier of the block confirmed HERE.
147
+ The bare dotted name is NOT a key: `block list` prints
148
+ `ds:apps_lxd.component.meter`, and pasting that row's name without its `ds:`
149
+ prefix comes back `ENTITY_NOT_FOUND`. Keep the prefix, or use the display name.
150
+ Step 2 derives the package from the tier of the block confirmed HERE, so make
151
+ that choice explicit before moving on.
142
152
 
143
153
  **Check the entry is a spec before building on it — whatever class it is.**
144
154
  A row resolving is not the same as a block being documented: many entries
@@ -175,18 +185,37 @@ Then, and only then, the breakdown.
175
185
  The inspect renders `ds:hasProperty` only as opaque blank-node labels; for the
176
186
  property content use the lookup's Properties section — only when the lookup
177
187
  resolved the block you want — or bind the property query to the block confirmed
178
- above. (A `block list` row prints the `ds:` form — `graph inspect` takes it
179
- as-is, but a SPARQL body needs the full IRI: join `https://ds.canonical.com/`
180
- with the row's dotted name, e.g. for the global Button,
181
- `<https://ds.canonical.com/global.component.button>`; the name query above
182
- prints that full form directly. The bracketed prefixed form `<ds:…>` silently
183
- returns an EMPTY table, not an error.)
188
+ above. (A `block list` row prints the `ds:` form, and both `graph inspect` and a
189
+ SPARQL body take it as-is — a prefixed name carrying dots parses. The full IRI
190
+ (`<https://ds.canonical.com/global.component.button>`) is accepted too, and is
191
+ what `graph query` prints back. What does NOT work is the bracketed prefixed
192
+ form `<ds:…>`: it silently returns an EMPTY table, not an error.)
184
193
 
185
194
  ```bash
186
- pragma graph query "SELECT ?p ?v WHERE { <https://ds.canonical.com/<dotted name from that row>> ds:hasProperty ?x . ?x ?p ?v }"
195
+ pragma graph query "SELECT ?p ?v WHERE { ds:global.component.button ds:hasProperty ?x . ?x ?p ?v }"
187
196
  ```
188
197
  - New block: the `specs/…` file its specify flow produced.
189
- 2. **Confirm the target package and tier.** The spec names its tier; `pragma tier list`
198
+ 2. **Check whether it is already implemented, before scaffolding anything.** The graph
199
+ records implementations, and a block that already has one is not a scaffolding job:
200
+
201
+ ```bash
202
+ pragma implementation list --search <Name> # which library implements it, on which platform, and the source file
203
+ ```
204
+
205
+ A row names the library, the platform and a link to the head of the source. Read
206
+ what it points at before deciding: an implementation on the target platform means
207
+ the work is an EXTENSION of that file, not a new component next to it — and running
208
+ `pragma create` anyway leaves two components for one block. An implementation on
209
+ another platform (a react one when the target is svelte) is still the reference to
210
+ read for API and structure. Only when the search returns nothing for the target
211
+ platform is a fresh scaffold the right move. Search by display name: a match on the
212
+ block name also surfaces the groups built from it, so read the block name on each
213
+ row rather than taking the first.
214
+
215
+ If a same-platform implementation exists and the request was to build a new one,
216
+ that is a decision to raise with the person, not to resolve by scaffolding.
217
+
218
+ 3. **Confirm the target package and tier.** The spec names its tier; `pragma tier list`
190
219
  shows what exists. Where a package exists for the tier, its name follows a
191
220
  convention — the graph carries no package field: `@canonical/<framework>-ds-global`
192
221
  for the `global` tier, `@canonical/<framework>-ds-app` for the shared `apps` tier,
@@ -197,7 +226,7 @@ Then, and only then, the breakdown.
197
226
  resolves in the workspace before installing or scaffolding into it. Tiers outside
198
227
  `global`/`apps*` have no package convention today: there the target package is a
199
228
  decision to raise, not to derive.
200
- 3. **Pull the applicable standards NOW.**
229
+ 4. **Pull the applicable standards NOW.**
201
230
 
202
231
  ```bash
203
232
  pragma standard categories # what categories exist today
@@ -243,11 +272,22 @@ Then, and only then, the breakdown.
243
272
  pragma graph query "SELECT ?s ?kind ?caption ?code WHERE { ?s cs:hasCategory <http://pragma.canonical.com/codestandards#testing.coverage> . { ?s cs:do ?x . BIND('do' AS ?kind) } UNION { ?s cs:dont ?x . BIND('dont' AS ?kind) } ?x cs:description ?caption . OPTIONAL { ?x cs:code ?code } }"
244
273
  ```
245
274
 
246
- (Inside a SPARQL body a local name with more than one dot does not parse; a
247
- single dot — `cs:testing.unit` — is fine.) `pragma standard lookup <name>` and
248
- `pragma standard sample` resolve only the standards that carry a `cs:name`
249
- title — today only a few do — so for the rest the graph query above is the read
250
- path. (MCP: standard_categories / standard_list / graph_query / graph_inspect.)
275
+ Prefixed names with dots parse inside a SPARQL body, so `cs:testing.coverage` and
276
+ `cs:react.component.barrel_exports` can be written in place of the full `<…>` form
277
+ and answer identically. The full form is never wrong, and is what `graph query`
278
+ prints back.
279
+
280
+ **`pragma standard lookup <id>` is not the read path for most standards, so do not
281
+ start there.** It resolves only the standards carrying a `cs:name` title — today a
282
+ few do — and an id taken straight out of `standard list`'s own output
283
+ (`css.selectors.namespace`) comes back `ENTITY_NOT_FOUND` with the standard's title
284
+ offered as a "suggestion", which reads like a typo rather than like the tool
285
+ answering a different question. The reliable pair is the two reads above:
286
+ `pragma standard list --category <c>` for what exists, and the Do/Don't graph query
287
+ for the pairs. `pragma standard sample` has the same title-only limit. Treat a
288
+ `lookup` miss as saying nothing about whether the standard exists — the category
289
+ listing is what says that. (MCP: standard_categories / standard_list / graph_query /
290
+ graph_inspect.)
251
291
 
252
292
  ## Scaffold
253
293
 
@@ -258,7 +298,7 @@ pragma create component react src/components/Button
258
298
  - The framework is a tree segment — `pragma create component <framework> <path>` with
259
299
  `react`, `svelte`, or `lit` as the segment; the path's final segment is the
260
300
  PascalCase component name.
261
- - Run it from inside the target package confirmed in pre-flight step 2 — the path is
301
+ - Run it from inside the target package confirmed in pre-flight step 3 — the path is
262
302
  package-relative (in the ds packages, components live at `src/lib/component/<Name>`,
263
303
  so the path there is `src/lib/component/Button`, not the generic example above).
264
304
  - Plan-first: preview with `--dry-run`, apply non-interactively with `--yes`, reverse
@@ -294,6 +334,135 @@ standards either way; when no pairs exist, work from those standards' descriptio
294
334
  A standard you disagree with is feedback for the standards repo, not a license to
295
335
  deviate silently.
296
336
 
337
+ ### The variable to write, and what it stands for
338
+
339
+ The stylesheet is where the component meets the tokens, and the seam is queryable — so
340
+ no custom property goes into a stylesheet on a guess. What the reads below cannot do is
341
+ guarantee an answer: a symbol the anatomy names may have no counterpart in the live
342
+ token graph, and that is a finding to raise, not a blank to fill. The rule that follows
343
+ from it is the one to carry through this whole section: a name that does not resolve
344
+ stops the work on that declaration; it never licenses a value.
345
+
346
+ Six reads, each answering one question:
347
+
348
+ ```bash
349
+ pragma block lookup Button # the anatomy the implementation owes: its nodes, keys and symbols
350
+ pragma token list --search radius # which symbols exist around a word, when the exact name is unknown
351
+ pragma token lookup color.text # one symbol: its type, description and every definition behind it
352
+ pragma token values --symbol modifier.color.text # what it resolves to at each position
353
+ pragma variable lookup color-text # which CSS variable, declared where, emitting what
354
+ pragma variable chain --variable color-text # what that variable finally reaches
355
+ ```
356
+
357
+ **`block lookup`** carries the `### Anatomy (DSL)` section — the structure and the
358
+ style bindings the implementation owes. Read it as the contract, but read it with one
359
+ caveat: the anatomies are being rewritten by hand and written back to the document, and
360
+ until a block's rewrite has landed and the pack has been rebuilt, that section still
361
+ answers with the retired notation (slash paths like `color/text/muted`, `stack`,
362
+ `flow`, and `sth` placeholders). A slash path there is not a symbol to look up — the
363
+ dotted name is, and `anatomy-author` carries the notation in full. The section that
364
+ will list a block's token bindings directly arrives with those bindings; it is not in
365
+ the lookup's output yet.
366
+
367
+ Swapping the slashes for dots is a spelling change, and it is only the first thing to
368
+ try. It succeeds where the symbol kept its name and fails where the taxonomy moved
369
+ under it, and those two failures look identical at the prompt — both are
370
+ `ENTITY_NOT_FOUND`. So a miss on the dotted name is not the end of the read; it is the
371
+ start of a search:
372
+
373
+ ```bash
374
+ pragma token list --search radius # the family, under whatever name it has now
375
+ pragma token list --type dimension --limit 1000
376
+ ```
377
+
378
+ Three outcomes, and they are different findings. The search turns up the same decision
379
+ under a new name — use it, and say in the PR that the anatomy still carries the old
380
+ one. The search turns up a family that was restructured rather than renamed, so the
381
+ decision the anatomy named no longer exists in that shape — that is a design question
382
+ for the token owners, and it belongs in the PR as one. Or the search turns up nothing
383
+ at all — also a finding, and the same answer: raise it. In none of the three do you
384
+ pick a near-miss symbol or write a raw value, because a token-typed property in the
385
+ anatomy is a design decision already made, and `cs:css.properties.values` — pulled in
386
+ pre-flight — bans a raw value in exactly that place. A blocked declaration named in the
387
+ PR is a better outcome than a stylesheet that resolves and is wrong.
388
+
389
+ **`token list`** is the search read, and it needs its filters to be trusted. It pages
390
+ at 300 rows sorted by name, so the first page of a bare `pragma token list` is colour
391
+ tokens and nothing else — every dimension, number, fontWeight and typography symbol is
392
+ off it. The output says so, in a heading that ends "and more exist" and a closing line
393
+ offering an `--after` cursor, but a bare list piped into `grep` swallows both and reads
394
+ as a system-wide absence. Never conclude "the graph has no such token" from an
395
+ unnarrowed list. Narrow instead: `--search <word>` over name and description,
396
+ `--type <type>` for one type's population, `--channel-of <symbol>` for the channels
397
+ provisioning one symbol (it takes a SYMBOL, not a family name — a family there is an
398
+ `INVALID_INPUT` error that lists what it would accept), and `--limit` raised past 300
399
+ when a full population is what you actually want.
400
+
401
+ **`token lookup <symbol>`** answers with the symbol's `- Type:` and `- Description:`, a
402
+ `### Definitions` list naming the file each definition comes from, a `### Covered by`
403
+ list of the modifier families that may rebind it, and a `### Values` list. That is what
404
+ tells you whether the symbol you are about to consume is the right one — the
405
+ description is written for exactly this decision. It does not tell you which STYLE KEY
406
+ the symbol is legal on: each key admits symbols from its own namespaces, and neither
407
+ `token lookup` nor `token list` knows about that rule. A symbol that resolves here can
408
+ still be the wrong symbol for the property you are writing — `anatomy-author` carries
409
+ the per-key namespaces, and `anatomies validate --authored` is what enforces them.
410
+
411
+ **`token values --symbol <symbol>`** is one row per position. For a channel it shows
412
+ the routing: `modifier.color.text | criticality.error | color.text.error` says that
413
+ under `.error`, reading the channel gets you the error text colour. For a flat symbol
414
+ it shows the resolved value per coordinate, `mode.dark` included.
415
+
416
+ **`variable lookup <name>`** takes the custom property WITHOUT its leading dashes and
417
+ is the read that tells you what to write in CSS. It answers with the `- Symbol:` the
418
+ variable stands for, its `- Tier:` and `- Visibility:`, and a `### Declarations` list:
419
+ one row per declaration, each naming the `selector` it is declared under, the
420
+ `inAtRule` cascade layer, what it `emits`, and the `file:line` it is declared at. A
421
+ `- Visibility: dt:visibility.internal` variable is not a component's to read directly
422
+ — `modifier-color-text` is internal, and a component reads it only as the head of the
423
+ chain the anatomy gives it.
424
+
425
+ The verb has THREE outcomes, and the third is the one that catches people out:
426
+
427
+ 1. **It resolves and carries a `- Symbol:` line.** The variable stands for that symbol.
428
+ This is the ordinary case, and the symbol is what you check against the anatomy.
429
+ 2. **It resolves and carries NO `- Symbol:` line at all.** The variable is a derived
430
+ one — a state product or a delta, autogenerated rather than authored — and it stands
431
+ for no symbol. `hover--color-foreground-secondary` is one: it resolves, its `emits`
432
+ is an `oklch(from …)` expression, and there is no symbol behind it. Do not read the
433
+ missing line as a failed lookup, and do not treat the variable as a token. Ask what
434
+ it reaches instead:
435
+
436
+ ```bash
437
+ pragma variable chain --variable hover--color-foreground-secondary
438
+ ```
439
+
440
+ 3. **`ENTITY_NOT_FOUND`.** The graph has never heard of the name.
441
+
442
+ That third outcome does NOT mean "yours to invent". It means one of two things, and
443
+ which one depends on the anatomy. For a property the anatomy never mentions —
444
+ `button-gap`, `icon-size`, a local alias the stylesheet declares for its own
445
+ convenience — it is genuinely component-local: yours to declare, standing for no token,
446
+ and that is the end of it. But for a token-typed property the anatomy DOES name, an
447
+ unresolved name is a gap between the anatomy and the token graph: either the anatomy is
448
+ still in the retired notation, and the rewrite has not landed for that block yet, or
449
+ the token was retired. Neither is a licence to write a value. Stop that declaration and
450
+ raise it, by the rule three paragraphs up.
451
+
452
+ So the question to ask of every `ENTITY_NOT_FOUND` is not "is it in the graph" but
453
+ "does the anatomy name it". The anatomy names it: raise it. The anatomy is silent:
454
+ declare it locally.
455
+
456
+ **`variable chain --variable <name>`** is the transitive walk — every
457
+ `variable | variable | symbol` triple the variable reaches through what its
458
+ declarations reference. Use it when `lookup`'s `emits` is another `var()` and you need
459
+ to know where the indirection ends: `color-text` reaches `color.palette.black` and
460
+ `color.palette.white`, which is `light-dark()` spelled out.
461
+
462
+ The same reads are MCP tools — `block_lookup`, `token_list`, `token_lookup`,
463
+ `token_values`, `variable_lookup`, `variable_chain` — taking the same arguments.
464
+ `pragma capabilities` prints the current catalog, which grows between releases.
465
+
297
466
  ## Post-flight
298
467
 
299
468
  - Fill in the stories and tests the scaffold stubbed — the spec's properties, modifiers,
@@ -162,12 +162,29 @@ Before anything is invented, find what already exists:
162
162
 
163
163
  ```bash
164
164
  pragma block list # every component, pattern, layout and subcomponent, with its type and tier
165
- pragma block lookup <Name-or-glob> # full spec of candidates: anatomy, modifiers, properties (MCP: block_lookup); a glob repeats a multi-tier name once per IRI and every repeat is the SAME block — the fragment query below lists the tiers
165
+ pragma block lookup <Name-or-glob> # full spec of candidates: anatomy, modifiers, properties (MCP: block_lookup)
166
166
  pragma block sample # real entry shapes — read these BEFORE writing queries
167
167
  pragma modifier list # the modifier families blocks draw from
168
168
  pragma tier list # the tiers a block can live in
169
169
  ```
170
170
 
171
+ **`block lookup` fans a shared name out across tiers, and the repeats are not
172
+ repeats.** A display name several tiers carry — or a glob matching them — answers with
173
+ one full writeup per tier, each under its own `## <Name>` heading with its own
174
+ `- Tier:` line, and those are DIFFERENT blocks: `Badge` returns the global, launchpad
175
+ and portal Badges, three independently authored specs with different summaries,
176
+ properties and anatomies. For this step that matters twice over. The answer is the
177
+ prior art, so skipping past the second and third headings as noise is how an
178
+ apps-tier block that already does the job goes unnoticed — read every one. And an
179
+ apps-tier block carrying the name is a different verdict from a global one: the first
180
+ may be a promotion candidate, the second means the gap is not a gap.
181
+
182
+ Three key forms, and only three: the display name, the `ds:`-prefixed IRI
183
+ (`pragma block lookup ds:global.component.badge`, which answers with exactly one
184
+ block), and a glob. The bare dotted name is not one of them — `block list` prints
185
+ `ds:apps_lxd.component.meter`, and pasting that row without its `ds:` prefix comes
186
+ back `ENTITY_NOT_FOUND`.
187
+
171
188
  Search by name fragment when the naming is uncertain:
172
189
 
173
190
  ```bash
@@ -177,6 +194,16 @@ pragma graph query "SELECT ?b ?name WHERE { ?b ds:name ?name . FILTER(CONTAINS(L
177
194
  > The covered set is whatever the graph answers today. Query it live — never copy its
178
195
  > output into documentation, PRs, or this skill.
179
196
 
197
+ That query matches `ds:name` on ANY entity, so its rows are not necessarily blocks —
198
+ a modifier value (`ds:global.modifier.in_progress`) and a documentation tag will come
199
+ back alongside them, and neither is prior art. A non-empty table can still mean "no
200
+ block of this name exists". Read each row's IRI, or bind the type when the name is
201
+ noisy:
202
+
203
+ ```bash
204
+ pragma graph query "SELECT ?b ?name ?type WHERE { VALUES ?type { ds:Component ds:Pattern ds:Layout ds:Subcomponent ds:Group } ?b a ?type ; ds:name ?name . FILTER(CONTAINS(LCASE(?name), 'crumb')) }"
205
+ ```
206
+
180
207
  Outcome gate: **either "exists — stop or extend it" or a NAMED gap.** No spec is written
181
208
  without one of those two sentences, naming the blocks that were checked.
182
209
 
@@ -185,11 +212,17 @@ without one of those two sentences, naming the blocks that were checked.
185
212
  Read the shape the entry must satisfy:
186
213
 
187
214
  ```bash
188
- pragma ontology lookup ds # the design-system vocabulary
215
+ pragma ontology lookup ds # the design-system vocabulary: its CLASSES
216
+ pragma ontology lookup ds --properties # the predicates an entry carries — a section of its own, behind the flag
189
217
  pragma ontology lookup ds --class UIBlock # the block-level properties every entry carries
190
218
  pragma ontology lookup ds --class Component # what Component itself declares
191
219
  ```
192
220
 
221
+ The bare `ontology lookup ds` prints the classes and stops there; the predicates come
222
+ only with `--properties`. So a field name checked against the bare output looks absent
223
+ when it is not — confirm `ds:usage`, `ds:guidelines`, `ds:hasProperty` and the rest
224
+ against the properties section before writing a query or a spec field around them.
225
+
193
226
  `--class` shows only what a class declares itself — usage, guidelines, properties,
194
227
  and tier are declared on `ds:UIBlock` and inherited by its subclasses; `summary` and
195
228
  `name` sit further up the chain on `ds:Entity`
@@ -281,19 +314,98 @@ Delegate to the `anatomy-author` skill:
281
314
  pragma skill lookup anatomy-author
282
315
  ```
283
316
 
317
+ **Read the repository's copy, not only the printed one, and prefer it where they
318
+ differ.** `skills/anatomy-author/SKILL.md` in canonical/design-system is the authority;
319
+ the copy `pragma skill lookup` prints is bundled into a pragma release and lags the
320
+ repository until the next one. So a section this step names and the printout does not
321
+ show is old text in the printout, not a section that is missing — open the repository
322
+ file before concluding anything is absent.
323
+
284
324
  The skill covers named and anonymous nodes, edges with cardinality, slot names, and
285
325
  CTI-inspired style keys; the full ANATOMY_DSL_SPEC ships beside it as
286
326
  `ANATOMY_DSL_SPEC.md` in the installed `anatomy-author` skill folder (the lookup
287
- renders SKILL.md only — open that file directly for the complete spec). The produced
288
- DSL lands in the step-6 spec file
289
- under `specs/` — in a `.md` spec as its anatomy section, in a `.ttl` spec as the
290
- block's `ds:anatomyDsl` string literal — never under `data/`; entry into Coda stays
291
- the human step.
327
+ renders SKILL.md only — open that file directly for the complete spec).
328
+
329
+ **A modifier family the spec claims is expressed in the anatomy by CONSUMING its
330
+ channel, never by naming it.** There is no style key, `@state` or `switch`
331
+ discriminator that takes a family name, so an anatomy that supports Criticality on its
332
+ fill colour says so by binding the channel of the symbol the fill reads —
333
+ `appearance.background: [modifier.color.foreground.primary, color.foreground.primary]`
334
+ for a filled surface, `typography.color: [modifier.color.text, color.text]` for text.
335
+ Which channels exist is the token graph's answer, not a guess:
336
+ `pragma token list --channel-of color.background` reports a surface channel and no
337
+ modifier one, so `modifier.color.background` is not a symbol and cannot be bound. The
338
+ family lands in
339
+ the channel: `pragma token values --symbol modifier.color.text` shows
340
+ `anticipation.caution` routing to `color.text.warning`, which is how Button's
341
+ Anticipation support reaches its text without Button's anatomy ever saying
342
+ "Anticipation". So when step 6 recorded a Modifier Families section, this step's job is
343
+ to name, per family, WHICH key carries it and which channel that key reads — and to
344
+ check the family actually covers that symbol (`pragma token lookup <symbol>`, the
345
+ `### Covered by` list). A family covering none of the symbols the anatomy binds is a
346
+ finding for the token owners, not something to express in the tree.
347
+
348
+ **Where the produced DSL lands.** An anatomy is a file of its own, not a section of
349
+ this spec: it goes to `anatomies/authored/<tier>/<uri>.yaml` in canonical/design-system,
350
+ with the file name spelling the dotted uri. There it is checked offline with
351
+ `bun src/cli.ts anatomies validate --authored`, reviewed as a file in a pull request,
352
+ and written to the block's `anatomy_dsl` cell by `anatomies write`; after that write the
353
+ document is the source of record. Never write into design-system `data/`, which the
354
+ pull sync regenerates. `anatomy-author`'s "Where the anatomy lands" section is the
355
+ authority on that path — this step's job is to hand it a specified component and take
356
+ back the file name it wrote.
357
+
358
+ ### 8. Pair with tokens
359
+
360
+ Tokens here means token symbols — `color.text`, `dimension.200` — never CSS variables.
361
+ A variable such as `--color-text` is the web platform's name for a symbol, one name per
362
+ platform, and it is the implementer's concern: the anatomy names the symbol, and
363
+ `implement-component` reads `pragma variable lookup` to find what to write in CSS. A spec
364
+ that named variables would be tied to one platform and would miss every symbol the web
365
+ has not emitted yet.
366
+
367
+ The spec does not bind tokens — that is the anatomy's job, one style key at a time. What
368
+ this step owes is the check that the symbols the component will need actually exist, so
369
+ a specification does not promise a colour or a spacing role the token graph has never
370
+ declared:
292
371
 
293
- ### 8. Pair with tokens — placeholder
372
+ ```bash
373
+ pragma token list --search focus # which symbols exist around a word
374
+ pragma token lookup color.text # one symbol in full: type, description, definitions
375
+ ```
376
+
377
+ `token list` pages at 300 rows sorted by name, so a bare `pragma token list` returns
378
+ colour tokens and nothing else — every dimension, number and typography symbol is off
379
+ the first page, behind an "and more exist" heading and an `--after` cursor that a pipe
380
+ into `grep` swallows. Always narrow (`--search`, `--type`) or raise `--limit` before
381
+ concluding a family is absent; an unnarrowed list is not evidence.
382
+
383
+ A name that comes back `ENTITY_NOT_FOUND` is a finding for the spec — either the
384
+ component reads an existing symbol instead, or a new token is a decision to raise with
385
+ the token owners, and either answer belongs in the spec rather than in the anatomy that
386
+ follows. (MCP: `token_list`, `token_lookup`.)
387
+
388
+ **`token lookup` resolving is the FIRST check, not the whole one.** It answers whether a
389
+ symbol exists; it says nothing about which style key the symbol may go on. Each key
390
+ admits symbols from one namespace only, and a symbol that reads as exactly on-topic can
391
+ still be outside it: `typography.text.tertiary` is a real composite typography token and
392
+ "12px tertiary text" is exactly what a small label wants, yet `typography.size` admits
393
+ `dimension.` symbols alone, so the binding is unlawful and
394
+ `dimension.size.fontSize.250` is what belongs there. Neither `token lookup` nor
395
+ `token list` knows that rule.
396
+
397
+ So the pairing has two checks, in order: `token lookup <symbol>` for existence, then the
398
+ anatomy gate for admissibility.
399
+
400
+ ```bash
401
+ bun src/cli.ts anatomies validate --authored # in canonical/design-system
402
+ ```
294
403
 
295
- **Left blank for now** — token pairing content lands with the token work. Do not invent
296
- token guidance.
404
+ It names the file, the node, the key, the symbol and the namespaces the key admits. Run
405
+ it before reporting step 8 as done, and treat a namespace rejection the way you treat an
406
+ `ENTITY_NOT_FOUND`: a finding to resolve against the roster
407
+ (`node_modules/@canonical/anatomy-dsl/definitions/style-keys.yaml`, which states the
408
+ `valueKind` and the namespace per key), never a value to force through.
297
409
 
298
410
  ## Response format
299
411
 
@@ -309,8 +421,8 @@ Spec: specs/<tier>.<type>.<snake_name>.md
309
421
  - [ ] 4. Benchmarked against: … (adopt/reject decisions stated)
310
422
  - [ ] 5. State gate: fired/not fired (States & interaction section: yes/no)
311
423
  - [ ] 6. Documentation written to the sync-safe location
312
- - [ ] 7. Anatomy DSL written via anatomy-author
313
- - [ ] 8. Token pairing: placeholder (deferred)
424
+ - [ ] 7. Anatomy DSL written via anatomy-author (file: anatomies/authored/…)
425
+ - [ ] 8. Token pairing: symbols checked against the token graph AND the anatomy gate run (findings: …)
314
426
  ```
315
427
 
316
428
  ## Related skills