aontu 0.61.0 → 0.63.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 (371) hide show
  1. package/README.md +4 -4
  2. package/dist/agentsmd.d.ts +1 -0
  3. package/dist/agentsmd.js +7 -28
  4. package/dist/agentsmd.js.map +1 -1
  5. package/dist/alias.js.map +1 -1
  6. package/dist/allow.d.ts +23 -0
  7. package/dist/allow.js +138 -0
  8. package/dist/allow.js.map +1 -0
  9. package/dist/aontu.d.ts +4 -2
  10. package/dist/aontu.js +4 -80
  11. package/dist/aontu.js.map +1 -1
  12. package/dist/aontumodel.d.ts +4 -0
  13. package/dist/aontumodel.js +33 -0
  14. package/dist/aontumodel.js.map +1 -0
  15. package/dist/cli.d.ts +10 -1
  16. package/dist/cli.js +877 -479
  17. package/dist/cli.js.map +1 -1
  18. package/dist/ctx.js +0 -48
  19. package/dist/ctx.js.map +1 -1
  20. package/dist/diff.js +0 -32
  21. package/dist/diff.js.map +1 -1
  22. package/dist/err.js +0 -40
  23. package/dist/err.js.map +1 -1
  24. package/dist/escape.js +0 -45
  25. package/dist/escape.js.map +1 -1
  26. package/dist/exactjson.d.ts +0 -35
  27. package/dist/exactjson.js +0 -131
  28. package/dist/exactjson.js.map +1 -1
  29. package/dist/format.js +55 -189
  30. package/dist/format.js.map +1 -1
  31. package/dist/grammar.d.ts +9 -0
  32. package/dist/grammar.js +54 -0
  33. package/dist/grammar.js.map +1 -0
  34. package/dist/graph.js +0 -26
  35. package/dist/graph.js.map +1 -1
  36. package/dist/hcanon.js +0 -82
  37. package/dist/hcanon.js.map +1 -1
  38. package/dist/helpdoc.d.ts +16 -0
  39. package/dist/helpdoc.js +59 -0
  40. package/dist/helpdoc.js.map +1 -0
  41. package/dist/hints.d.ts +0 -6
  42. package/dist/hints.js +59 -47
  43. package/dist/hints.js.map +1 -1
  44. package/dist/jsonschema.js +0 -114
  45. package/dist/jsonschema.js.map +1 -1
  46. package/dist/keyorder.d.ts +0 -7
  47. package/dist/keyorder.js +0 -41
  48. package/dist/keyorder.js.map +1 -1
  49. package/dist/lang.js +32 -877
  50. package/dist/lang.js.map +1 -1
  51. package/dist/lower.d.ts +3 -0
  52. package/dist/lower.js +14 -61
  53. package/dist/lower.js.map +1 -1
  54. package/dist/lsp-server.js +0 -16
  55. package/dist/lsp-server.js.map +1 -1
  56. package/dist/lsp.d.ts +1 -1
  57. package/dist/lsp.js +12 -159
  58. package/dist/lsp.js.map +1 -1
  59. package/dist/mcp-server.js +0 -26
  60. package/dist/mcp-server.js.map +1 -1
  61. package/dist/mcp.js +0 -113
  62. package/dist/mcp.js.map +1 -1
  63. package/dist/mod-tool.js +0 -130
  64. package/dist/mod-tool.js.map +1 -1
  65. package/dist/mod.js +0 -162
  66. package/dist/mod.js.map +1 -1
  67. package/dist/patch.js +0 -217
  68. package/dist/patch.js.map +1 -1
  69. package/dist/provenance.js +0 -140
  70. package/dist/provenance.js.map +1 -1
  71. package/dist/query.js +0 -75
  72. package/dist/query.js.map +1 -1
  73. package/dist/reach.js +0 -43
  74. package/dist/reach.js.map +1 -1
  75. package/dist/relation.d.ts +2 -0
  76. package/dist/relation.js +3 -58
  77. package/dist/relation.js.map +1 -1
  78. package/dist/render.js +33 -143
  79. package/dist/render.js.map +1 -1
  80. package/dist/report-sarif.d.ts +0 -11
  81. package/dist/report-sarif.js +0 -28
  82. package/dist/report-sarif.js.map +1 -1
  83. package/dist/sig.js +0 -35
  84. package/dist/sig.js.map +1 -1
  85. package/dist/sigdecl.js +1 -1
  86. package/dist/sigdecl.js.map +1 -1
  87. package/dist/siggate.js +0 -4
  88. package/dist/siggate.js.map +1 -1
  89. package/dist/site.js +3 -29
  90. package/dist/site.js.map +1 -1
  91. package/dist/subsume.d.ts +0 -10
  92. package/dist/subsume.js +0 -137
  93. package/dist/subsume.js.map +1 -1
  94. package/dist/template.d.ts +2 -1
  95. package/dist/template.js +58 -138
  96. package/dist/template.js.map +1 -1
  97. package/dist/trim.js +0 -41
  98. package/dist/trim.js.map +1 -1
  99. package/dist/tsconfig.tsbuildinfo +1 -1
  100. package/dist/type.js.map +1 -1
  101. package/dist/unify.js +12 -242
  102. package/dist/unify.js.map +1 -1
  103. package/dist/utility.js +0 -22
  104. package/dist/utility.js.map +1 -1
  105. package/dist/val/AbnfFuncVal.d.ts +18 -0
  106. package/dist/val/AbnfFuncVal.js +132 -0
  107. package/dist/val/AbnfFuncVal.js.map +1 -0
  108. package/dist/val/AbsentVal.d.ts +11 -0
  109. package/dist/val/AbsentVal.js +30 -0
  110. package/dist/val/AbsentVal.js.map +1 -0
  111. package/dist/val/AggFuncVal.d.ts +10 -1
  112. package/dist/val/AggFuncVal.js +104 -116
  113. package/dist/val/AggFuncVal.js.map +1 -1
  114. package/dist/val/ArithFuncVal.js +0 -12
  115. package/dist/val/ArithFuncVal.js.map +1 -1
  116. package/dist/val/BagVal.js +1 -78
  117. package/dist/val/BagVal.js.map +1 -1
  118. package/dist/val/BigDecimalVal.js +0 -16
  119. package/dist/val/BigDecimalVal.js.map +1 -1
  120. package/dist/val/BigIntegerVal.js +0 -16
  121. package/dist/val/BigIntegerVal.js.map +1 -1
  122. package/dist/val/CloseFuncVal.js +0 -9
  123. package/dist/val/CloseFuncVal.js.map +1 -1
  124. package/dist/val/CmpFuncVal.d.ts +20 -0
  125. package/dist/val/CmpFuncVal.js +188 -0
  126. package/dist/val/CmpFuncVal.js.map +1 -0
  127. package/dist/val/ConjunctVal.js +0 -29
  128. package/dist/val/ConjunctVal.js.map +1 -1
  129. package/dist/val/ConstraintVal.js +0 -500
  130. package/dist/val/ConstraintVal.js.map +1 -1
  131. package/dist/val/ContainerKindVal.js +0 -2
  132. package/dist/val/ContainerKindVal.js.map +1 -1
  133. package/dist/val/CopyFuncVal.js +0 -3
  134. package/dist/val/CopyFuncVal.js.map +1 -1
  135. package/dist/val/Decimal.js +0 -179
  136. package/dist/val/Decimal.js.map +1 -1
  137. package/dist/val/DeprecateFuncVal.js.map +1 -1
  138. package/dist/val/DisjunctVal.js +0 -152
  139. package/dist/val/DisjunctVal.js.map +1 -1
  140. package/dist/val/EachFuncVal.d.ts +1 -2
  141. package/dist/val/EachFuncVal.js +12 -29
  142. package/dist/val/EachFuncVal.js.map +1 -1
  143. package/dist/val/EmitFuncVal.d.ts +1 -1
  144. package/dist/val/EmitFuncVal.js +6 -119
  145. package/dist/val/EmitFuncVal.js.map +1 -1
  146. package/dist/val/ExpectVal.js +0 -62
  147. package/dist/val/ExpectVal.js.map +1 -1
  148. package/dist/val/FilterFuncVal.js +0 -25
  149. package/dist/val/FilterFuncVal.js.map +1 -1
  150. package/dist/val/FuncBaseVal.d.ts +1 -0
  151. package/dist/val/FuncBaseVal.js +7 -127
  152. package/dist/val/FuncBaseVal.js.map +1 -1
  153. package/dist/val/GraphAtomVal.js +0 -15
  154. package/dist/val/GraphAtomVal.js.map +1 -1
  155. package/dist/val/HideFuncVal.js +0 -13
  156. package/dist/val/HideFuncVal.js.map +1 -1
  157. package/dist/val/IntegerVal.js +0 -61
  158. package/dist/val/IntegerVal.js.map +1 -1
  159. package/dist/val/JunctionVal.js +0 -20
  160. package/dist/val/JunctionVal.js.map +1 -1
  161. package/dist/val/KeyFuncVal.js +0 -46
  162. package/dist/val/KeyFuncVal.js.map +1 -1
  163. package/dist/val/ListVal.js +0 -57
  164. package/dist/val/ListVal.js.map +1 -1
  165. package/dist/val/LowerFuncVal.js +11 -6
  166. package/dist/val/LowerFuncVal.js.map +1 -1
  167. package/dist/val/MapVal.js +0 -151
  168. package/dist/val/MapVal.js.map +1 -1
  169. package/dist/val/MatchFuncVal.js +0 -27
  170. package/dist/val/MatchFuncVal.js.map +1 -1
  171. package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
  172. package/dist/val/MaybeFuncVal.js +50 -0
  173. package/dist/val/MaybeFuncVal.js.map +1 -0
  174. package/dist/val/MoveFuncVal.js +0 -18
  175. package/dist/val/MoveFuncVal.js.map +1 -1
  176. package/dist/val/NilVal.js +2 -36
  177. package/dist/val/NilVal.js.map +1 -1
  178. package/dist/val/NomFuncVal.d.ts +12 -0
  179. package/dist/val/NomFuncVal.js +153 -0
  180. package/dist/val/NomFuncVal.js.map +1 -0
  181. package/dist/val/NumberVal.js +0 -15
  182. package/dist/val/NumberVal.js.map +1 -1
  183. package/dist/val/OpBaseVal.d.ts +1 -0
  184. package/dist/val/OpBaseVal.js +3 -15
  185. package/dist/val/OpBaseVal.js.map +1 -1
  186. package/dist/val/PackFuncVal.js +0 -34
  187. package/dist/val/PackFuncVal.js.map +1 -1
  188. package/dist/val/PathFuncVal.js +0 -6
  189. package/dist/val/PathFuncVal.js.map +1 -1
  190. package/dist/val/PathVal.js +0 -41
  191. package/dist/val/PathVal.js.map +1 -1
  192. package/dist/val/PlaceVal.js +0 -25
  193. package/dist/val/PlaceVal.js.map +1 -1
  194. package/dist/val/PlusOpVal.d.ts +1 -7
  195. package/dist/val/PlusOpVal.js +13 -74
  196. package/dist/val/PlusOpVal.js.map +1 -1
  197. package/dist/val/PrefFuncVal.js +0 -1
  198. package/dist/val/PrefFuncVal.js.map +1 -1
  199. package/dist/val/PrefVal.js +0 -167
  200. package/dist/val/PrefVal.js.map +1 -1
  201. package/dist/val/RecurseVal.js +0 -55
  202. package/dist/val/RecurseVal.js.map +1 -1
  203. package/dist/val/RefVal.js +0 -282
  204. package/dist/val/RefVal.js.map +1 -1
  205. package/dist/val/ReferFuncVal.js +0 -232
  206. package/dist/val/ReferFuncVal.js.map +1 -1
  207. package/dist/val/ScalarKindVal.js +0 -49
  208. package/dist/val/ScalarKindVal.js.map +1 -1
  209. package/dist/val/ScalarVal.js +0 -11
  210. package/dist/val/ScalarVal.js.map +1 -1
  211. package/dist/val/StrFuncVal.js +0 -18
  212. package/dist/val/StrFuncVal.js.map +1 -1
  213. package/dist/val/SuperFuncVal.js +0 -32
  214. package/dist/val/SuperFuncVal.js.map +1 -1
  215. package/dist/val/TopVal.js +0 -1
  216. package/dist/val/TopVal.js.map +1 -1
  217. package/dist/val/TranslateFuncVal.d.ts +12 -0
  218. package/dist/val/TranslateFuncVal.js +99 -0
  219. package/dist/val/TranslateFuncVal.js.map +1 -0
  220. package/dist/val/UpperFuncVal.js +11 -6
  221. package/dist/val/UpperFuncVal.js.map +1 -1
  222. package/dist/val/Val.d.ts +1 -0
  223. package/dist/val/Val.js +2 -133
  224. package/dist/val/Val.js.map +1 -1
  225. package/dist/val/VarVal.js +0 -12
  226. package/dist/val/VarVal.js.map +1 -1
  227. package/dist/val/arith.js +0 -37
  228. package/dist/val/arith.js.map +1 -1
  229. package/dist/val/caserange.d.ts +3 -0
  230. package/dist/val/caserange.js +49 -0
  231. package/dist/val/caserange.js.map +1 -0
  232. package/dist/val/members.js +0 -6
  233. package/dist/val/members.js.map +1 -1
  234. package/dist/val/numcmp.js +0 -11
  235. package/dist/val/numcmp.js.map +1 -1
  236. package/dist/val/numkind.js +0 -145
  237. package/dist/val/numkind.js.map +1 -1
  238. package/dist/val/valutil.js +0 -16
  239. package/dist/val/valutil.js.map +1 -1
  240. package/dist/vet.d.ts +12 -0
  241. package/dist/vet.js +159 -412
  242. package/dist/vet.js.map +1 -1
  243. package/dist/view.js +0 -414
  244. package/dist/view.js.map +1 -1
  245. package/dist/walk.js +0 -41
  246. package/dist/walk.js.map +1 -1
  247. package/grammar/aontu.abnf +9 -7
  248. package/grammar/aontu.gbnf +5 -5
  249. package/grammar/aontu.lark +5 -5
  250. package/grammar/aontu.tmLanguage.json +1 -1
  251. package/package.json +4 -2
  252. package/skill/SKILL.md +8 -0
  253. package/skill/init/check.sh +28 -0
  254. package/skill/init/data.aon +12 -0
  255. package/skill/init/model.aon +19 -0
  256. package/skill/tasks.md +151 -0
  257. package/src/agentsmd.ts +8 -32
  258. package/src/alias.ts +0 -39
  259. package/src/allow.ts +221 -0
  260. package/src/aontu.ts +10 -108
  261. package/src/aontumodel.ts +32 -0
  262. package/src/cli.ts +1009 -540
  263. package/src/ctx.ts +0 -103
  264. package/src/diff.ts +0 -40
  265. package/src/err.ts +0 -40
  266. package/src/escape.ts +0 -46
  267. package/src/exactjson.ts +0 -131
  268. package/src/format.ts +63 -234
  269. package/src/grammar.ts +72 -0
  270. package/src/graph.ts +0 -61
  271. package/src/hcanon.ts +0 -82
  272. package/src/helpdoc.ts +77 -0
  273. package/src/hints.ts +72 -49
  274. package/src/jsonschema.ts +0 -123
  275. package/src/keyorder.ts +0 -42
  276. package/src/lang.ts +39 -895
  277. package/src/lower.ts +15 -65
  278. package/src/lsp-server.ts +0 -16
  279. package/src/lsp.ts +12 -180
  280. package/src/mcp-server.ts +0 -31
  281. package/src/mcp.ts +0 -130
  282. package/src/mod-tool.ts +0 -158
  283. package/src/mod.ts +0 -178
  284. package/src/patch.ts +0 -232
  285. package/src/provenance.ts +0 -183
  286. package/src/query.ts +0 -84
  287. package/src/reach.ts +0 -53
  288. package/src/relation.ts +7 -71
  289. package/src/render.ts +33 -180
  290. package/src/report-sarif.ts +0 -48
  291. package/src/sig.ts +0 -35
  292. package/src/sigdecl.ts +1 -1
  293. package/src/siggate.ts +0 -30
  294. package/src/site.ts +3 -29
  295. package/src/subsume.ts +1 -161
  296. package/src/template.ts +69 -140
  297. package/src/trim.ts +0 -53
  298. package/src/type.ts +2 -45
  299. package/src/unify.ts +13 -251
  300. package/src/utility.ts +0 -31
  301. package/src/val/AbnfFuncVal.ts +181 -0
  302. package/src/val/AbsentVal.ts +54 -0
  303. package/src/val/AggFuncVal.ts +152 -188
  304. package/src/val/ArithFuncVal.ts +0 -20
  305. package/src/val/BagVal.ts +1 -78
  306. package/src/val/BigDecimalVal.ts +0 -16
  307. package/src/val/BigIntegerVal.ts +0 -16
  308. package/src/val/CloseFuncVal.ts +0 -9
  309. package/src/val/CmpFuncVal.ts +249 -0
  310. package/src/val/ConjunctVal.ts +0 -33
  311. package/src/val/ConstraintVal.ts +2 -537
  312. package/src/val/ContainerKindVal.ts +0 -18
  313. package/src/val/CopyFuncVal.ts +0 -5
  314. package/src/val/Decimal.ts +1 -185
  315. package/src/val/DeprecateFuncVal.ts +0 -10
  316. package/src/val/DisjunctVal.ts +0 -157
  317. package/src/val/EachFuncVal.ts +12 -53
  318. package/src/val/EmitFuncVal.ts +8 -208
  319. package/src/val/ExpectVal.ts +0 -62
  320. package/src/val/FilterFuncVal.ts +0 -55
  321. package/src/val/FuncBaseVal.ts +9 -130
  322. package/src/val/GraphAtomVal.ts +0 -42
  323. package/src/val/HideFuncVal.ts +0 -15
  324. package/src/val/IntegerVal.ts +0 -61
  325. package/src/val/JunctionVal.ts +0 -20
  326. package/src/val/KeyFuncVal.ts +0 -48
  327. package/src/val/ListVal.ts +0 -59
  328. package/src/val/LowerFuncVal.ts +12 -7
  329. package/src/val/MapVal.ts +0 -151
  330. package/src/val/MatchFuncVal.ts +0 -59
  331. package/src/val/MaybeFuncVal.ts +86 -0
  332. package/src/val/MoveFuncVal.ts +0 -20
  333. package/src/val/NilVal.ts +2 -36
  334. package/src/val/NomFuncVal.ts +200 -0
  335. package/src/val/NumberVal.ts +0 -16
  336. package/src/val/OpBaseVal.ts +4 -17
  337. package/src/val/PackFuncVal.ts +0 -63
  338. package/src/val/PathFuncVal.ts +0 -32
  339. package/src/val/PathVal.ts +0 -66
  340. package/src/val/PlaceVal.ts +0 -45
  341. package/src/val/PlusOpVal.ts +18 -75
  342. package/src/val/PrefFuncVal.ts +0 -1
  343. package/src/val/PrefVal.ts +0 -179
  344. package/src/val/RecurseVal.ts +0 -81
  345. package/src/val/RefVal.ts +1 -285
  346. package/src/val/ReferFuncVal.ts +0 -255
  347. package/src/val/ScalarKindVal.ts +0 -50
  348. package/src/val/ScalarVal.ts +0 -12
  349. package/src/val/StrFuncVal.ts +0 -44
  350. package/src/val/SuperFuncVal.ts +0 -42
  351. package/src/val/TopVal.ts +0 -1
  352. package/src/val/TranslateFuncVal.ts +132 -0
  353. package/src/val/UpperFuncVal.ts +12 -7
  354. package/src/val/Val.ts +3 -192
  355. package/src/val/VarVal.ts +0 -15
  356. package/src/val/arith.ts +0 -92
  357. package/src/val/caserange.ts +53 -0
  358. package/src/val/members.ts +0 -23
  359. package/src/val/numcmp.ts +1 -27
  360. package/src/val/numkind.ts +0 -149
  361. package/src/val/valutil.ts +0 -16
  362. package/src/vet.ts +209 -504
  363. package/src/view.ts +0 -507
  364. package/src/walk.ts +0 -41
  365. package/dist/std.d.ts +0 -3
  366. package/dist/std.js +0 -637
  367. package/dist/std.js.map +0 -1
  368. package/dist/val/FormFuncVal.js +0 -55
  369. package/dist/val/FormFuncVal.js.map +0 -1
  370. package/src/std.ts +0 -648
  371. package/src/val/FormFuncVal.ts +0 -119
package/src/graph.ts CHANGED
@@ -1,22 +1,5 @@
1
1
  /* Copyright (c) 2025 Richard Rodger, MIT License */
2
2
 
3
- // THE DERIVED STRUCTURE (G4 phase 3,
4
- // docs/capability-review/g4-identity-relations.md): an evaluated
5
- // document has, besides its value, a GRAPH — the set of checked links,
6
- // each from one tree position to another.
7
- //
8
- // The graph is PATH-NATIVE (ADR-014). There is no second namespace to
9
- // index: a node's address is its path, so the entity index the first
10
- // design carried is exactly the set of paths already in the edges, and
11
- // the node a link starts at is derived from where the link sits rather
12
- // than declared by a mark.
13
- //
14
- // G4's deliverable is that this exists and is DETERMINISTIC. What is
15
- // built on it — impact analysis ("what reaches $.services.auth?"),
16
- // reachability, context-window-sized slices — is a traversal, and its
17
- // exposure as verbs and projections belongs to G7. Relation properties
18
- // (acyclicity, inverse consistency) are G4 phase 5's, and consume
19
- // exactly this edge set.
20
3
 
21
4
  import type { Val } from './type'
22
5
 
@@ -24,35 +7,17 @@ import { cmpCodePoint } from './keyorder'
24
7
 
25
8
 
26
9
  export type Edge = {
27
- // The node the link starts at, as a `$.dotted.path`: the link's own
28
- // position with the relation key and any list indices stripped. `$`
29
- // when the link sits at the top of the document.
30
10
  from: string
31
- // The RELATION: the key the link hangs under, so a link inside a
32
- // list (`dependsOn: [refer() & "$.a"]`) is an edge under `dependsOn`
33
- // rather than under `0`. A rel()-minted link carries its predicate
34
- // declared rather than inferred.
35
11
  key: string
36
- // The address, as the link spells it.
37
12
  to: string
38
13
  // Where the link is, as a `$.dotted.path`, so a report can point at
39
14
  // it.
40
15
  at: string
41
- // Set when the link sits inside a `hide()`-marked subtree. The link
42
- // is still checked and still an edge, but a figure that draws it
43
- // DISCLOSES what the document hides, so the view extractors skip it
44
- // and report it as `hidden_contribution`.
45
16
  hidden?: true
46
17
  }
47
18
 
48
19
  export type Graph = {
49
20
  edges: Edge[]
50
- // The positions of links written under an UNRESOLVED DISJUNCTION.
51
- // ADR-007: an unresolved disjunction is not a value, so a link
52
- // beneath one of its arms is not a fact and is not an edge -- but it
53
- // is not nothing either, and a figure that silently dropped it would
54
- // be the failure the views exist to avoid. Absent when there are
55
- // none, so a graph of a decided document is the shape it always was.
56
21
  disjunct?: string[]
57
22
  }
58
23
 
@@ -66,14 +31,6 @@ const formatPath = (path: string[]): string =>
66
31
  const isIndex = (seg: string): boolean => /^[0-9]+$/.test(seg)
67
32
 
68
33
 
69
- // The node a link starts at and the relation it hangs under, derived
70
- // from the link's own position.
71
- //
72
- // A DECLARED predicate (rel()-minted) is authoritative: the link is cut
73
- // at the key the rel() sat on, wherever that is on the way down, which
74
- // is what makes a MAP-valued relation report the relation rather than
75
- // the inner label. Without one the relation is INFERRED: strip the list
76
- // indices, and the first real key above the link is it.
77
34
  const cut = (
78
35
  at: string[], relkey: string | undefined
79
36
  ): { from: string, key: string } => {
@@ -92,11 +49,6 @@ const cut = (
92
49
  }
93
50
 
94
51
 
95
- // The graph of an evaluated tree. Walks POSITIONS, not values: a
96
- // reference or a spread can put one value object at several positions,
97
- // and a walk guarded by object identity would find the first and miss
98
- // every other place it is reached. The guard is therefore the ancestor
99
- // chain — which is what a cycle actually is.
100
52
  export function graphOf(root: Val): Graph {
101
53
  const edges: Edge[] = []
102
54
  const disjunct: string[] = []
@@ -136,11 +88,6 @@ export function graphOf(root: Val): Graph {
136
88
  visit(node.held, path, ancestors, hidden, undecided)
137
89
  }
138
90
 
139
- // An unresolved conjunction (a link waiting on a peer that never
140
- // came, a constraint still open) holds its terms at the SAME
141
- // position; every link among them is written there, and is an
142
- // edge. This is what a match-selected branch or a deferred refer()
143
- // looks like after evaluation.
144
91
  if (true === node.isConjunct && Array.isArray(node.peg)) {
145
92
  ancestors.add(node)
146
93
  for (const term of node.peg) {
@@ -149,10 +96,6 @@ export function graphOf(root: Val): Graph {
149
96
  ancestors.delete(node)
150
97
  }
151
98
 
152
- // AN UNRESOLVED DISJUNCTION IS NOT A VALUE (ADR-007), so a link
153
- // under one of its arms is not an edge. Its POSITION is collected
154
- // instead, so a figure can report what the document leaves
155
- // undecided rather than drawing it or dropping it in silence.
156
99
  if (true === node.isDisjunct && Array.isArray(node.peg)) {
157
100
  ancestors.add(node)
158
101
  for (const arm of node.peg) {
@@ -172,10 +115,6 @@ export function graphOf(root: Val): Graph {
172
115
 
173
116
  visit(root, [], new Set(), false, false)
174
117
 
175
- // DETERMINISTIC by construction, not by luck: edges by the position
176
- // they are written at, which is unique — one link, one place. That
177
- // holds through a conjunction too: its terms share the position, and
178
- // two links there would have had to unify into one.
179
118
  edges.sort((a, b) => cmpCodePoint(a.at, b.at))
180
119
 
181
120
  if (0 < disjunct.length) {
package/src/hcanon.ts CHANGED
@@ -1,44 +1,5 @@
1
1
  /* Copyright (c) 2025 Richard Rodger, MIT License */
2
2
 
3
- // The HASH FORM (G6 phase 0, docs/capability-review/g6-distribution.md):
4
- // exactly the unify-level canon with the additions that close its
5
- // semantic gaps, so that two documents with the same hash form have the
6
- // same meaning:
7
- //
8
- // - a CLOSED map or list renders wrapped: close({...}), close([...])
9
- // (canon drops closedness);
10
- // - the type/hide MARKS render as their builtin wrappers: type(x),
11
- // hide(x) (canon drops marks).
12
- //
13
- // Both additions reuse existing parseable syntax, so the hash form
14
- // remains valid Aontu source and round-trips:
15
- // hcanon(unify(parse(hcanon(v)))) == hcanon(v) is a spec-suite property
16
- // (test/spec/hcanon.tsv). User-facing canon is UNCHANGED — hcanon is a
17
- // separate rendering.
18
- //
19
- // An ALIAS REFERENCE left standing in a spread template renders its
20
- // EXPANSION through this same walk rather than through the reference's
21
- // own canon, so a close() or a mark on the aliased value survives into
22
- // the hash. Without that the wrappers above are absent exactly where
23
- // the alias filter has already erased the declaration that carried
24
- // them (BUGS.md §60).
25
- //
26
- // The marks PROPAGATE to every descendant at unification (walkMark), so
27
- // a wrapper is emitted only where a mark STARTS: the walk carries the
28
- // inherited marks down and a child whose mark the parent already
29
- // carries renders bare. Without that, hide({a:1}) would render every
30
- // leaf re-wrapped — still correct, never minimal, and not what the
31
- // source said.
32
- //
33
- // canonHash (G6 phase 1) is the pin built on it:
34
- // "aon1-" + base64url( SHA-256( UTF-8( hcanon(v) ) ) )
35
- // (unpadded base64url, RFC 4648 section 5). The "aon1-" scheme id
36
- // exists so a future semantically-stronger normal form is an upgrade,
37
- // not a breakage. This is a CANONICAL-TEXT hash, not a semantic
38
- // equivalence class: the failure direction is the safe one — a false
39
- // "changed" forces a needless re-review; a false "unchanged" would need
40
- // two hash forms with the same bytes and different meanings, which is
41
- // exactly the gap the close/mark wrappers exist to shut.
42
3
 
43
4
  import { createHash } from 'node:crypto'
44
5
 
@@ -53,15 +14,6 @@ type HMarks = {
53
14
  }
54
15
 
55
16
 
56
- // One node's rendering, carrying the marks the ANCESTORS already
57
- // wrapped. Bags and junctions recurse structurally (their canon getters
58
- // render children through plain canon, which would drop a nested
59
- // close), and so does an expanded alias reference, for the reason its
60
- // own arm gives. Everything else — scalars, kinds, funcs, constraints,
61
- // and a reference with nothing to expand — delegates to its own canon,
62
- // whose text is already in cross-port parity. The non-Val arm mirrors
63
- // MapVal.canon's raw-peg fallback and is unreachable through an
64
- // evaluated tree (direct-tested, ADR-002).
65
17
  function render(v: any, inh: HMarks): string {
66
18
  if (true !== v?.isVal) {
67
19
  return String(v)
@@ -76,12 +28,6 @@ function render(v: any, inh: HMarks): string {
76
28
 
77
29
  let s: string
78
30
  if (true === v.isMap) {
79
- // Alias declarations are dropped here for the same reason
80
- // MapVal.canon drops them, and this is the surface where it counts:
81
- // `aon1-` pins MEANING, so a document written with aliases and the
82
- // same document written longhand must hash to one string. Two
83
- // renderers, one rule -- hcanon is not canon and does not inherit
84
- // the filter (docs/design/ALIASES.0.md §4).
85
31
  const keys = Object.keys(v.peg)
86
32
  .filter((k) => !v.aliasKeys.includes(k))
87
33
  .sort(cmpCodePoint)
@@ -115,22 +61,6 @@ function render(v: any, inh: HMarks): string {
115
61
  else if (true === v.isConjunct || true === v.isDisjunct) {
116
62
  s = junctionText(v, true === v.isConjunct ? '&' : '|', inner)
117
63
  }
118
- // AN EXPANDED ALIAS REFERENCE IS RENDERED, NOT DELEGATED (BUGS.md
119
- // §60). A reference standing after unification is one inside a
120
- // spread template, and `RefVal.canon` answers with the EXPANSION's
121
- // plain canon -- which drops exactly what this renderer exists to
122
- // keep. The declaration carrying `close()` or a mark is erased by
123
- // the alias filter above, so a `close()` lost here is lost from the
124
- // hash entirely: `%A = close({n:string})` and `%A = {n:string}`,
125
- // used as `box: [&: %A]`, hashed to ONE STRING while refusing and
126
- // admitting `{n:"x",z:1}` respectively -- a change of meaning the
127
- // pin reported as no change, in the unsafe direction. Recursing
128
- // through `inner` is what makes the alias form and its longhand
129
- // twin agree again, which is ALIASES.0.md §4's own requirement.
130
- //
131
- // A reference with NO expansion still spells its name: a plain
132
- // `$.A` names a key the hash form still carries in full, and the
133
- // knot of a recursive alias inside its own template never gets one.
134
64
  else if (true === v.isRef && undefined !== v.expansion) {
135
65
  s = render(v.expansion, inner)
136
66
  }
@@ -160,14 +90,6 @@ function render(v: any, inh: HMarks): string {
160
90
  }
161
91
 
162
92
 
163
- // The JunctionVal.canon parenthesisation rule, kept: a member that is
164
- // itself a junction with more than one term keeps its parens so the
165
- // text reparses with the same structure (`(1|2)&3`, not the
166
- // differently-parsing `1|2&3`). Post-unification junctions are
167
- // flattened by norm, so no SOURCE reaches the wrapping arm -- it is
168
- // pinned by direct tests over constructed Vals in both ports, because
169
- // a hash form that could render ambiguously would be a pin that
170
- // silently agrees with a document it should not.
171
93
  function junctionText(v: any, sym: string, inner: HMarks): string {
172
94
  return v.peg.map((m: any) =>
173
95
  true === m?.isJunction && 1 < m.peg.length
@@ -185,10 +107,6 @@ export function hcanon(v: Val): string {
185
107
  }
186
108
 
187
109
 
188
- // The canon-hash pin. Scoped to the module evaluated STANDALONE: its
189
- // own include closure resolved and unified at its own root, before any
190
- // consumer context — which is what makes the pin transitive (an edit
191
- // two includes deep changes the unified root, hence the hash).
192
110
  export function canonHash(v: Val): string {
193
111
  return 'aon1-' +
194
112
  createHash('sha256').update(hcanon(v), 'utf8').digest('base64url')
package/src/helpdoc.ts ADDED
@@ -0,0 +1,77 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // GENERATED by ts/scripts/helpdoc.cjs (`make helpdoc`) from
4
+ // docs/skill/*.md and grammar/aontu.abnf — DO NOT EDIT. The skill
5
+ // sources are the source; ts/test/helpdoc.test.ts asserts this copy
6
+ // is identical with them.
7
+
8
+ type HelpTopic = {
9
+ topic: string
10
+ summary: string
11
+ source: string
12
+ text: string
13
+ }
14
+
15
+ const HELPDOC: HelpTopic[] = [
16
+ {
17
+ "topic": "tasks",
18
+ "summary": "what do you want to do? — the verb for a job",
19
+ "source": "docs/skill/tasks.md",
20
+ "text": "# What do you want to do?\n\nThe verb for a job, found by the word you arrived with. aontu's own\nvocabulary is on the right; yours is probably on the left.\n\n## Start from a document that works\n\nNothing written yet? Do not invent the first document, edit one:\n\n```\naontu init # into the working directory\naontu init model/ # or into a named one\nsh check.sh # the four questions, on what was just written\n```\n\n`init` writes `model.aon` (an entity map constrained with `&:`),\n`data.aon` (an instance of it that holds) and `check.sh` (the four\nchecks to run after every edit). It never overwrites: if any\nof the three already stands there, it refuses and writes none of them.\n\n## Describe a domain\n\nAn **ontology**, a **schema**, a **data model**, a **contract** — in\naontu these are all one thing: a document. Write the entities as a\n**map keyed by name**, and say what every entry must satisfy with the\n`&:` template:\n\n```\nentity: {\n &: {\n table: string\n fields: { &: { type: string, required: *false | boolean } }\n }\n planet: { table: \"planets\", fields: { name: { type: \"string\" } } }\n moon: { table: \"moons\", fields: { name: { type: \"string\" } } }\n}\n```\n\n`&:` is the construct to reach for first, and the one most easily\nmissed. It meets **every** key of the map it sits in. A quoted `\"*\"`\nis not a wildcard — it is a key named `*`, and a schema written that\nway constrains nothing while still reporting `valid`.\n\nPrefer a named map to a list unless the order is a fact (a migration\nsequence, a rule table tried in order). A key is an address, a name\nother parts can refer to, and a diff that shows one insertion instead\nof every following element renumbered.\n\n## Check data against a model\n\n```\naontu vet model.aon data.aon\n```\n\nExit `0` valid, `1` a contradiction, `3` incomplete (nothing\ncontradicts, but the model is not yet satisfied), `4` the model does\nnot stand up on its own. `--format json` for the machine-readable\nreport, `--closed` to refuse keys the model does not declare,\n`--max-errors <n>` to cap the list.\n\n**Make the check prove it checked something.** A check that examined\nnothing answers exactly like one that passed:\n\n```\naontu vet --coverage model.aon data.aon # what did it examine?\naontu vet --strict-coverage model.aon data.aon # exit 1 if nothing\n```\n\n`--coverage` reports how many data leaves a declaration constrained,\nthe data paths none did, and the declarations no data met.\n`--strict-coverage` exits 1 when the answer is nothing. The usual\ncause is the `\"*\"` mistake above: reach for `&:`.\n\n## Check the model is coherent with itself\n\n```\naontu relations model.aon # declared entity edges: targets resolve, no cycles\naontu reaches planet moon model.aon # does one entity reach another, at any remove?\naontu trim --check model.aon # entries whose removal changes nothing\n```\n\n## Check code against a model\n\n```\naontu render --check src/ --profile go.aon model.aon\n```\n\n`render` writes code from the model; `--check` writes nothing and\nlists what on disk differs from what the model implies. That is the\ngate: the model is the truth, the code is the claim, and drift is a\nfinding. `--coverage` reports the other direction — model paths no\noutput consumed, and rendered declarations no rule produced.\n\n## Ask what a model says, and why\n\n```\naontu get $.entity.planet.table model.aon\naontu why $.entity.planet.table model.aon\naontu get $.entity --keys model.aon\naontu get $.entity --types model.aon\n```\n\n`why` names **every** contribution to a value, with the file and line\neach was written on. It is the first thing to run when a value is not\nwhat you expected, and the second thing to run when `vet` refuses.\n\n## Change a value without editing the file\n\n```\naontu set '$.entity.planet.table=planet_v2' --entry model.aon --overlay local.aon\n```\n\nThe change is checked before it is written; a change that contradicts\na pinned value is refused, and the file is left alone.\n\n## Gate a change to the model itself\n\n```\naontu subsume old.aon new.aon # does the general admit every specific?\naontu breaking --against git#HEAD~1 model.aon\naontu hash model.aon # a pin that survives reformatting\n```\n\n## Hand a model to another agent\n\n```\naontu agentsmd --write AGENTS.md model.aon\n```\n\nSplices a derived stanza — the pin, the root keys, the shape, and the\ncommands spelled with paths that exist — between two markers, and\nleaves the rest of the file alone. Re-run it in the commit that\nchanges the model.\n\n## When something refuses\n\nRead the code in the brackets, then look it up:\n\n```\naontu explain no_scalar_unify\n```\n\nEvery finding carries a `code`, a `class`, a `path` and the **two\nsites** that disagree — the value and the constraint it failed, each\nwith its file and line. The class says what kind of answer it is:\n`conflict` (two things cannot both hold), `incomplete` (nothing is\nwrong yet, something is missing), `parse`, `reference`, `budget`.\n\nFull index: [`error-codes.md`](error-codes.md). The language on one\npage: [`grammar-card.md`](grammar-card.md). The worked ladder from\nplain JSON upward: [`examples.md`](examples.md).\n"
21
+ },
22
+ {
23
+ "topic": "language",
24
+ "summary": "the grammar card — everything the language spells, on one page",
25
+ "source": "docs/skill/grammar-card.md",
26
+ "text": "# aontu grammar card\n\nEverything the language spells, on one page. The published machine\ngrammars are [`grammar/aontu.abnf`](../../grammar/aontu.abnf) — the\nnotation a person reads, with railroad diagrams — and\n[`aontu.gbnf`](../../grammar/aontu.gbnf), `aontu.lark` and\n`aontu.tmLanguage.json` for constrained decoding, parsing and\nhighlighting; this is their human twin.\n\n## Values\n\n| Write | Means |\n|-------|-------|\n| `{\"a\":1}`, `a: 1` | a map; keys may be bare or quoted |\n| `[1,2]` | a list |\n| `\"s\"`, `1`, `1.5`, `true`, `null` | scalars, as JSON |\n| `0d9007199254740993` | an EXACT number, at any magnitude |\n| `string integer number float boolean null` | kinds: any value of that kind |\n| `top` | anything at all; `nil` is the failure |\n\n## Combining\n\n| Write | Means |\n|-------|-------|\n| `a & b` | both must hold — the meet |\n| `a \\| b` | either may hold — the join |\n| `*1 \\| integer` | either, with `1` the DEFAULT a generation picks |\n| | the branch keeps its kind: a later `1.5` is refused, not admitted |\n| `x: 1` twice | the two meet; `1 & 2` is an error, `1 & integer` is `1` |\n| `$.a.b` | the value at that path of this document |\n| `1 + 2` | arithmetic on concrete numbers |\n\n## Maps that say more\n\n| Write | Means |\n|-------|-------|\n| `{&: {k: integer}}` | a TEMPLATE every key must satisfy |\n| `close({a:1})` | no key beyond those named |\n| `{a?: 1}` | optional: dropped if unresolved |\n| `hide(x)` | evaluated, then dropped from the output |\n| `type(x)` | a definition, not a value: it generates nothing |\n| `deprecate(x, {msg:\"…\"})` | still works, and says so |\n| `refer(t) & path($.a.b)` | a checked LINK: the tree address must resolve, and `t` flows into it |\n\n## Constraints\n\n`min(n) max(n) above(n) below(n)` bound a number;\n`length(n)` and `unique()` bound a list or string;\n`re(\"^…$\")` matches a string; `neq(v)` refuses one value;\n`must(cond, \"why\")` is the escape hatch.\n\nBounds compose: `integer & min(1) & max(10)` is a range, and two\nranges meet to their overlap.\n\n## Files\n\n`@\"other.aon\"` includes another document — it unifies in, it does not\nconcatenate. (A constrained decoder is not given this: generated\ndocuments describe values, they do not load files.)\n"
27
+ },
28
+ {
29
+ "topic": "examples",
30
+ "summary": "the ladder — plain JSON, then one addition at a time",
31
+ "source": "docs/skill/examples.md",
32
+ "text": "# aontu by example\n\nThe ladder: start with the JSON you already know, add one thing at a\ntime. Every rung is a valid document.\n\n## 1. It is JSON\n\n```\n{\"service\": {\"name\": \"auth\", \"port\": 8080}}\n```\n\nBare keys and no commas also parse, so this is the same document:\n\n```\nservice: {\n name: \"auth\"\n port: 8080\n}\n```\n\n## 2. Say what a value MAY be\n\n```\nservice: {\n name: string\n port: integer\n}\n```\n\nNothing generates yet — this is a truth, not a value. `aontu vet\nservice.aon deploy.aon` says whether a deployment satisfies it.\n\n## 3. Say what it SHOULD be\n\n```\nservice: {\n name: string\n port: *8080 | integer # 8080 unless told otherwise\n replicas: *1 | integer\n}\n```\n\nNow it generates: `{\"service\":{\"name\":…,\"port\":8080,\"replicas\":1}}` —\nexcept `name`, which nothing has supplied. Defaults are picked at\ngeneration; they do not stop a caller from choosing something else.\n\n`*8080 | integer` means what it reads as: a caller's `9090` overrides\nit, and a caller's `1.5` is refused, because the branch admits only the\nkind it names. Write `*8080 | number` if any numeric value should be\nallowed to win.\n\n## 4. Say it once, for every key\n\n```\nservices: {\n &: { port: integer, replicas: *1 | integer }\n auth: { port: 8080 }\n db: { port: 5432, replicas: 3 }\n}\n```\n\nThe `&:` template meets EVERY key. `aontu why $.services.db.replicas`\nreports both contributions — the template's `*1|integer` and the\nliteral `3` — with the line each was written on.\n\n## 5. Bound it\n\n```\nservices: {\n &: {\n port: integer & min(1) & max(65535)\n name: string & re(\"^[a-z][a-z0-9-]*$\")\n }\n}\n```\n\nA port of `0` is now an error with a path, not a runtime surprise.\n\n## 6. Layer it\n\n```\n# base.aon\nservice: { image: string, replicas: *1 | integer }\n\n# prod.aon\n@\"base.aon\"\nservice: { image: \"auth:v2.3\", replicas: 5 }\n```\n\nUnification is order-independent: `base & prod` is `prod & base`. To\nchange a value without editing the file, append to an overlay:\n\n```\naontu set '$.service.replicas=7' --entry prod.aon --overlay local.aon\n```\n\nIf the value is PINNED rather than open, that command refuses and\nnames the pinning site — which is the honest answer, and where\n`aontu why` takes you next.\n"
33
+ },
34
+ {
35
+ "topic": "codes",
36
+ "summary": "what a refusal means, and what to do about it",
37
+ "source": "docs/skill/error-codes.md",
38
+ "text": "# What a refusal means\n\nEvery aontu error carries a **code**, and every code has a **class**.\nThe class says what kind of thing went wrong, which is what decides\nyour next move. The full registry — every code, its class, and the\nversion it appeared in — is\n[`test/spec/errcodes.tsv`](../../test/spec/errcodes.tsv), which the\ntest suite holds to the engine in both implementations.\n\n| Class | Meaning | What to do |\n|-------|---------|------------|\n| `parse` | the text is not a document | fix the syntax at the site the frame points at |\n| `conflict` | two values cannot both hold | one of them is wrong: `aontu why <path>` names both and where they were written |\n| `incomplete` | nothing contradicts, but the value is not concrete | supply what is missing, or accept it with `--partial` |\n| `reference` | a path names nothing | check the spelling; `aontu get $ --keys` lists what is there |\n| `compat` | a change breaks an earlier version | that is `aontu subsume` / `aontu breaking` talking: widen the change or version it |\n| `budget` | evaluation hit a deterministic limit | usually a cycle; simplify, or raise the budget deliberately |\n| `internal` | the engine surprised itself | a bug worth reporting |\n\n## The repair loop\n\n```\naontu vet schema.aon mine.aon --format json\n```\n\nThe exit code branches for you: `0` valid, `1` invalid (fix the\ndata), `3` incomplete (supply more), `4` the schema itself is\nunusable (fix the truth, not the data). Every finding carries\n`path`, `code`, and the sites on both sides — the data site first,\nbecause that is the one to edit. A site names **the file whose text\nit excerpts**, so its row and column are safe to edit at even when\nthe document loads others.\n\nRead `hint` before guessing. `message` is the one-line headline;\n`hint` is the engine's own explanation of the failure class with the\noffending values filled in, and for several codes it names the fix\noutright — `lossy_integer_literal` tells you to write the literal as\n`0d…`. It is absent for codes that have no hint text.\n\nFor a conflict, `aontu why <path> mine.aon` lists every contribution\nto that path with its role and source line, which turns \"these\ndisagree\" into \"these two lines disagree\".\n\nThen fix it:\n\n```\naontu set '$.replicas=5' --entry schema.aon --overlay mine.aon --in-place\n```\n\n`--in-place` rewrites the pinned literal **where it was written**, so\ncomments and layout survive. Without it, `set` APPENDS — which is the\nright thing when the document left a hole, and cannot work when it\npinned the wrong value, because unification only narrows.\n\nThe edit is verified before a byte is written: a site carries the\nsource text it covers, and the text at the span must match it. Where\nthat cannot be established — the value comes from a `&:` template or a\n`$ref`, two statements pin the path, the site names the opening token\nof a compound like `min(1)`, or the overlay `@\"includes\"` another\ndocument — the assignment is appended as usual and a **warning** says\nwhich case it hit. A warning never changes the verdict, so asking for\n`--in-place` cannot make a run fail that would have succeeded.\n"
39
+ },
40
+ {
41
+ "topic": "grammar",
42
+ "summary": "the published ABNF, for a parser or a constrained decoder",
43
+ "source": "grammar/aontu.abnf",
44
+ "text": "; Aontu --- published grammar (ABNF, RFC 5234 + RFC 7405).\n;\n; The same language as grammar/aontu.gbnf, rule for rule and name for\n; name, in the notation an RFC reader expects. The GBNF form is for a\n; constrained decoder; THIS form is for a person, and for the railroad\n; diagram in the language reference, which is generated from this file\n; (ts/scripts/figures.cjs).\n;\n; It covers the DOCUMENTED EMISSION SURFACE: what a model should be\n; allowed to write, which is a superset of JSON plus the operators,\n; constraints and marks the language's own canonical form emits. It is\n; CONSERVATIVE BY CONSTRUCTION --- it may accept less than the parser\n; does, never more. The same two deliberate exclusions the GBNF has:\n;\n; - `@\"...\"` includes. A generated document should describe values,\n; not reach for files.\n; - Unquoted keys and the other spellings the parser tolerates.\n; Canon quotes every key, and one spelling is what a grammar is\n; for.\n;\n; TWO NOTATION CHOICES worth stating, because both are load-bearing.\n;\n; EVERY LITERAL IS CASE-SENSITIVE, spelled `%s\"...\"` (RFC 7405). A\n; plain `\"...\"` is case-INSENSITIVE in RFC 5234, and Aontu is not:\n; `TRUE` is a bare word and `true` is a boolean. The reader in\n; ts/scripts/abnf.cjs refuses a bare quoted string rather than guess\n; which was meant.\n;\n; ALTERNATION IS ORDERED. RFC 5234's `/` is formally unordered, and\n; the consumers of this file (a PEG-style matcher here, a railroad\n; renderer) take the first branch that matches. Where one spelling is\n; a prefix of another the longer comes first --- `exact` before\n; `number`, `refer` before `rem` before `re`, `biginteger` before\n; `boolean` --- so the ordered reading and the unordered one accept\n; the same language. ts/test/grammar.test.ts holds this file to the\n; whole canon corpus of test/spec/*.tsv, which is what proves it.\n\nroot = ws value ws\n\n; No pipe. `|>` was parse-time sugar; it was removed, `x |> f(a)`\n; being written `f(x, a)`.\nvalue = disjunct\n\ndisjunct = conjunct *( ws %s\"|\" ws conjunct )\n\nconjunct = prefixed *( ws %s\"&\" ws prefixed )\n\n; A preference marks the alternative a generation picks.\nprefixed = %s\"*\" ws prefixed / sum\n\nsum = atom *( ws %s\"+\" ws atom )\n\natom = map\n / list\n / func\n / ref\n / kind\n / place\n / scalar\n / %s\"(\" ws value ws %s\")\"\n\nmap = %s\"{\" ws [ entry *( ws %s\",\" ws entry ) ws ] %s\"}\"\n\nentry = spread / pair\n\n; The template every key of the bag must also satisfy.\nspread = %s\"&\" ws %s\":\" ws value\n\npair = string ws [ %s\"?\" ] ws %s\":\" ws value\n\n; A list ELEMENT is a value, not a pair --- only the template is keyed.\nlist = %s\"[\" ws [ element *( ws %s\",\" ws element ) ws ] %s\"]\"\n\nelement = spread / value\n\n; The builtins, applied. The name set is closed on purpose: an unknown\n; function is a parse-time error in the engine, so a grammar that\n; allowed any name would over-approximate.\nfunc = name %s\"(\" ws [ value *( ws %s\",\" ws value ) ws ] %s\")\"\n\n; ORDER MATTERS where one name is a prefix of another: `refer` is\n; listed before `re`, and `rem` before `re` as well.\nname = %s\"abnf\" / %s\"above\" / %s\"acyclic\" / %s\"add\" / %s\"below\" / %s\"close\"\n / %s\"copyfiles\" / %s\"copy\" / %s\"deprecate\" / %s\"div\"\n / %s\"each\" / %s\"emit\" / %s\"esc\" / %s\"filter\" / %s\"greatest\"\n / %s\"hide\"\n / %s\"inverse\" / %s\"join\" / %s\"key\" / %s\"least\"\n / %s\"length\" / %s\"listitems\" / %s\"list\" / %s\"lower\" / %s\"map\" / %s\"match\"\n / %s\"maybe\" / %s\"max\" / %s\"min\" / %s\"mod\" / %s\"move\"\n / %s\"mul\" / %s\"must\"\n / %s\"neq\"\n / %s\"open\" / %s\"pack\" / %s\"parse\" / %s\"path\" / %s\"pick\" / %s\"pref\"\n / %s\"refer\" / %s\"rel\" / %s\"rem\" / %s\"rep\" / %s\"re\" / %s\"sort\" / %s\"split\"\n / %s\"super\"\n / %s\"content\" / %s\"file\" / %s\"folder\" / %s\"fragment\" / %s\"inject\"\n / %s\"line\" / %s\"nom\" / %s\"project\" / %s\"slot\" / %s\"translate\"\n / %s\"sub\" / %s\"sum\"\n / %s\"type\"\n / %s\"unique\" / %s\"upper\" / %s\"usc\"\n\n; A path reference, absolute from the document root. At least one\n; segment: a bare `$` is an incomplete expression, not a reference.\n; A RELATIVE reference has no `$`: `.n` names a sibling, resolved\n; against the enclosing map rather than the document root. Canon emits\n; one wherever a spread template survives unresolved, so a grammar\n; without it cannot parse this engine's own output. No conflict with\n; `number`: a numeric literal always starts with a digit or `-`.\nref = %s\"$\" 1*( %s\".\" segment ) / 1*( %s\".\" segment )\n\n; No `-`: it is not a bare-text character (test/spec/op-chars.tsv pins\n; `a:6-2` as a parse error), so admitting it would over-approximate.\nsegment = 1*( ALPHA / DIGIT / %s\"_\" )\n\n; The placeholder: a hole a call is filled through. BARE only --- `\"_\"`\n; is an ordinary string, and a longer bare word containing it is\n; ordinary text.\nplace = %s\"_\"\n\n; `biginteger` before `bigdecimal` is not required (they differ at the\n; fourth character), but `biginteger` before `boolean` is: see the\n; ordered-alternation note above.\nkind = %s\"biginteger\" / %s\"bigdecimal\" / %s\"boolean\" / %s\"float\"\n / %s\"integer\" / %s\"number\" / %s\"string\" / %s\"top\" / %s\"nil\"\n\n; `exact` before `number`, or `0d5` would match `number` as the single\n; digit `0` and leave `d5` unconsumed.\nscalar = string / exact / number / %s\"true\" / %s\"false\" / %s\"null\"\n\nstring = DQUOTE *char DQUOTE\n\nchar = unescaped / %x5C escape\n\n; Every code point but the quote and the backslash, which is what the\n; GBNF's `[^\"\\\\]` says. Control characters included: canon writes them\n; escaped, and a grammar that refused them would refuse less than the\n; parser accepts in the one direction this file may not.\nunescaped = %x00-21 / %x23-5B / %x5D-10FFFF\n\nescape = DQUOTE / %x5C / %s\"/\" / %s\"b\" / %s\"f\" / %s\"n\" / %s\"r\"\n / %s\"t\" / %s\"u\" hex hex hex hex\n\nhex = DIGIT / %x41-46 / %x61-66\n\n; The exact leaves: arbitrary precision, spelled with the 0d marker.\nexact = [ %s\"-\" ] %s\"0d\" digits [ %s\".\" digits ] [ exponent ]\n\nnumber = [ %s\"-\" ] digits [ %s\".\" digits ] [ exponent ]\n\nexponent = ( %s\"e\" / %s\"E\" ) [ %s\"-\" / %s\"+\" ] digits\n\ndigits = 1*DIGIT\n\nws = *( %x20 / %x09 / %x0A / %x0D )\n\n; RFC 5234's core rules, written out so this file stands alone.\nALPHA = %x41-5A / %x61-7A\nDIGIT = %x30-39\nDQUOTE = %x22\n"
45
+ }
46
+ ]
47
+
48
+ type InitFile = {
49
+ file: string
50
+ mode: number
51
+ name: string
52
+ text: string
53
+ }
54
+
55
+ const INITDOC: InitFile[] = [
56
+ {
57
+ "file": "docs/skill/init/model.aon",
58
+ "name": "model.aon",
59
+ "mode": 420,
60
+ "text": "# model.aon --- the truth. `aontu vet model.aon data.aon` asks whether\n# a document satisfies it.\n#\n# `&:` is the construct to learn first: it meets EVERY key of the map\n# it sits in, so one statement constrains every entity. A quoted \"*\" is\n# not a wildcard -- it is a key named `*`, it meets nothing, and a\n# schema written that way reports `valid` over data that violates it.\n# `aontu vet --strict-coverage` refuses to pass such a check.\nentity: {\n &: {\n table: string & re(\"^[a-z][a-z0-9_]*$\")\n fields: {\n &: {\n type: \"string\" | \"integer\" | \"boolean\"\n required: *false | boolean\n }\n }\n }\n}\n"
61
+ },
62
+ {
63
+ "file": "docs/skill/init/data.aon",
64
+ "name": "data.aon",
65
+ "mode": 420,
66
+ "text": "# data.aon --- an instance of the truth in model.aon. Edit it and run\n# check.sh: every change is checked against the model, and a change the\n# model refuses is named with the line it is on.\nentity: {\n planet: {\n table: \"planets\"\n fields: {\n name: { type: \"string\", required: true }\n diameter: { type: \"integer\" }\n }\n }\n}\n"
67
+ },
68
+ {
69
+ "file": "docs/skill/init/check.sh",
70
+ "name": "check.sh",
71
+ "mode": 493,
72
+ "text": "#!/usr/bin/env sh\n# check.sh --- the four questions to ask of a model. Run it after\n# every edit to model.aon or data.aon.\n#\n# `aontu help tasks` maps a job to a verb; `aontu explain <code>` says\n# what a refusal means; `aontu help language` is the whole grammar on\n# one page. None of them needs a network.\nset -eu\n\nAONTU=\"${AONTU:-aontu}\"\ncd \"$(dirname \"$0\")\"\n\n# 1. Does the data satisfy the truth -- and did the check examine\n# anything? --strict-coverage exits 1 on a check that constrained no\n# value, which is the failure a passing gate hides.\n$AONTU vet --strict-coverage model.aon data.aon\n\n# 2. What does it say at a path?\n$AONTU get '$.entity.planet.table' data.aon\n\n# 3. Why does it say that? Every contribution, with the line it is on.\n$AONTU why '$.entity.planet.table' data.aon\n\n# 4. A pin for the truth: it survives reformatting and moves on any\n# change of meaning.\n$AONTU hash model.aon\n\necho \"ok --- model.aon and data.aon agree\"\n"
73
+ }
74
+ ]
75
+
76
+ export type { HelpTopic, InitFile }
77
+ export { HELPDOC, INITDOC }
package/src/hints.ts CHANGED
@@ -1,11 +1,5 @@
1
1
  /* Copyright (c) 2025 Richard Rodger, MIT License */
2
2
 
3
- /**
4
- * Error code hints for Aontu unification errors.
5
- *
6
- * Each key is an error code that can be passed to makeNilErr.
7
- * Each value is a human-readable explanation of what the error means.
8
- */
9
3
 
10
4
  const hints: Record<string, string> = {
11
5
 
@@ -40,7 +34,6 @@ const hints: Record<string, string> = {
40
34
  'when nothing else does.',
41
35
 
42
36
 
43
- // TODO: extend errors to have details so we can name the key
44
37
  mapval_required: 'This map value is required.',
45
38
 
46
39
  mapval_no_gen:
@@ -108,6 +101,31 @@ const hints: Record<string, string> = {
108
101
  ' # with "tier";\n' +
109
102
  ' min(0) & must(integer,"whole") & 3 -> 3 # Bands compose.',
110
103
 
104
+ abnf_grammar:
105
+ 'This ABNF grammar could not be compiled:\n' +
106
+ '{reason}\n' +
107
+ ' \n' +
108
+ 'abnf() takes RFC 5234 ABNF -- `=` and `/`, not `::=`. The\n' +
109
+ 'compiler reports the first thing it could not read; a rule\n' +
110
+ 'referenced but never defined is the usual cause, after a\n' +
111
+ 'quantifier written the EBNF way.',
112
+
113
+ parse_arg:
114
+ 'parse(grammar, text) takes two strings: a grammar, normally the\n' +
115
+ 'answer of an abnf() call, and the text to parse.\n' +
116
+ ' \n' +
117
+ 'Examples:\n' +
118
+ ' G: abnf("v = 1*DIGIT")\n' +
119
+ ' a: parse($.G, "12") # the AST\n' +
120
+ ' b: parse($.G, 12) # parse_arg: the text is not a string',
121
+
122
+ parse_failed:
123
+ 'The text does not parse under this grammar:\n' +
124
+ '{reason}\n' +
125
+ ' \n' +
126
+ 'A failure to parse is a failure to unify, so the field is\n' +
127
+ 'refused rather than set to a value meaning "no".',
128
+
111
129
  constraint_pattern:
112
130
  'This re() pattern is outside the supported subset. It uses\n' +
113
131
  '{reason}.\n' +
@@ -176,8 +194,6 @@ const hints: Record<string, string> = {
176
194
  bare_punct: 'A bare string holds letters, digits, `-` and `_`, and nothing else.\nThis one holds `{char}`, in `{text}`. Every other punctuation\ncharacter is either syntax or an error, never silently part of a\nstring: a value that needs one is written quoted, and a `>` or `<`\nthat was meant as a bound is written as min(x), max(x), above(x) or\nbelow(x).\n \nExamples:\n a: team-payments -> "team-payments" # `-` and `_` are text;\n a: 2026-09-05 -> "2026-09-05" # ... digits included;\n a: x=y -> nil # `=` is not;\n a: "x=y" -> "x=y" # ... so quote it;\n a: >10 -> nil # Not an operator: write above(10).',
177
195
 
178
196
 
179
-
180
-
181
197
  recursion_unexpanded: 'A schema refers to itself here, and no data reached this position\nto expand it against. Guard the recursion -- an optional key\n(next?:) drops when nothing arrives, and a preferred alternative\n(*null | $.Node) generates -- or supply the data.\n \nExamples:\n Node: {v: integer, next?: $.Node}\n t: $.Node & {v: 1} -> {..} # next? drops;\n Node: {v: integer, next: $.Node}\n t: $.Node & {v: 1} -> nil # ... required refuses.',
182
198
  recursion_budget: 'A recursive schema expanded past the evaluation depth budget\nwithout meeting concrete data. Expansion is driven by the data --\nfinite data always terminates -- so a chain this deep means two\ndefinitions feeding each other, or data deeper than the budget\n(docs/trust.md raises it deliberately).',
183
199
  list_length: 'A literal list alternative in a disjunction admits only a list of\nits own length -- a spread (&:) makes it variadic. Outside a\ndisjunction two statements of one list still merge elementwise.\n \nExamples:\n x: [] | [&: integer]\n x: [1, 2] -> [1,2] # The variadic arm;\n x: [] -> [] # ... or exactly empty;\n y: [a] | [b]\n y: [a, extra] -> nil # ... a literal arm is its length.',
@@ -203,6 +219,8 @@ const hints: Record<string, string> = {
203
219
  view_profile_unknown: 'The figure kind does not render into the profile asked for: there is no\ntext form of a node-link drawing and no Mermaid form of a matrix. The\nnote lists the profiles the kind declares; the first is its default.',
204
220
 
205
221
  format_check: 'The formatted text is not the same document, so nothing was written.\nThis is a formatter defect: please report it, with the source.',
222
+
223
+ decimal_syntax: 'This 0d literal is not a valid exact number.',
206
224
  view_style_profile: 'Each profile has ONE way to carry the meaning of a figure\'s marks:\nSGR escapes for text, CSS classes for svg. Asking for the other one is\na usage error rather than a silent no-op. `none` works everywhere.',
207
225
  view_style_unknown: 'The styles are none, ansi and css, plus `auto` at the command line,\nwhich the command resolves before the library runs: whether the\ndestination is a terminal is not something a library can see.',
208
226
 
@@ -225,7 +243,7 @@ const hints: Record<string, string> = {
225
243
 
226
244
  pack_key: 'A list packed by pack() holds something that is not a string. The\nelements of a packed list ARE the generated keys, and only a string\nis a key — an element keyed by its position would churn every\ngenerated child the moment the list was reordered.\n \nExamples:\n pack([a,b], {x:1}) -> {..} # Names;\n pack(["a b"], {x:1}) -> {..} # ... a quoted name is still a name;\n pack([1,2], {x:1}) -> nil # ... but a number is not one.',
227
245
 
228
- each_data: 'The first argument to each() is not a bag. `each` makes one list\nelement per child of its DATA, so the data has to have children: a\nlist, or a map whose values become the elements in sorted-key order.\n \nExamples:\n each([1,2]) -> [..] # A list, in source order;\n each({b:2,a:1}) -> [..] # ... a map, in sorted-key order;\n each(1) -> nil # ... but a scalar has no children.',
246
+ each_data: 'The first argument to each() is not a bag. `each` makes one list\nelement per child of its DATA, so the data has to have children: a\nlist, or a map whose values are taken in sorted-key order.\n \nExamples:\n each([a,b], upper(_)) -> [..] # A list, in source order;\n each({b:2,a:1}, _) -> [..] # ... a map, in sorted-key order;\n each(1, _) -> nil # ... but a scalar has no children.',
229
247
 
230
248
  filter_data: 'The first argument to filter() is not a bag. `filter` keeps the\nchildren of its DATA that already satisfy a condition, so the data\nhas to have children: a list, or a map.\n \nExamples:\n filter([1,x], integer) -> [..] # A list;\n filter({a:1,b:x}, integer) -> {..} # ... or a map, keys kept;\n filter(1, integer) -> nil # ... but a scalar has none.',
231
249
 
@@ -249,6 +267,9 @@ const hints: Record<string, string> = {
249
267
 
250
268
  replace_value: 'A replacement value is not text: the `replace` key {key} came to\n{value} at the node. A value reaches the body as a string -- a number\nor a boolean spells itself, as it does after `+` -- and it has to\nhave settled by the time the dispatch fires, so a map, a list, a\nnull or a value still unresolved is refused rather than written into\na file as something else.\n \nExamples:\n replace: {PIN: .pin} # A string field;\n replace: {PORT: .port} # ... or a number, as digits;\n replace: {ALL: _} # ... but the node itself is a map.',
251
269
 
270
+ // RETIRED with the name `form` (ADR-027): the function is `each` now
271
+ // and answers `each_data`. Registered still, errcodes.tsv being
272
+ // append-only, so a code a released engine raised keeps its meaning.
252
273
  form_data: 'The first argument to form() is not a bag. `form` makes one list\nelement per child of its DATA, so the data has to have children: a\nlist, or a map whose values are taken in sorted-key order.\n \nExamples:\n form([a,b], upper(_)) -> [..] # A list, in source order;\n form({b:2,a:1}, _) -> [..] # ... a map, in sorted-key order;\n form(1, _) -> nil # ... but a scalar has no children.',
253
274
 
254
275
  esc_variant: 'esc(), usc() or a template\'s `esc:` key were given a variant that\nnames no convention.\nA variant names a CONVENTION rather than a language, because several\nlanguages share one and one language has several. The names are `sq`,\n`sql`, `shell`, `xml`, `uri` and `regex`; written with no variant at\nall it is the C escape, JSON canonical, which covers the double-quoted\nliteral of every C-family language.\n \nExamples:\n esc(text) -> ... # C / JSON, the default;\n esc(text, sq) -> ... # ... single-quoted C-family;\n esc(text, pascal) -> nil # ... but that is not a convention.',
@@ -331,6 +352,41 @@ const hints: Record<string, string> = {
331
352
  ' pick([{a:1},{b:2}], a) -> nil # ... the second does not;\n' +
332
353
  ' pick([[9],[8]], 0) -> [9,8] # A list child takes an index.',
333
354
 
355
+ sort_key:
356
+ 'A child of this bag has no key `{key}` to order by. Ordering\n' +
357
+ 'refuses rather than skipping, for the reason `pick` does: a\n' +
358
+ 'shorter list orders a DIFFERENT set of records than the one the\n' +
359
+ 'author named. Give every child the key, or filter the bag first.\n' +
360
+ ' \n' +
361
+ 'Examples:\n' +
362
+ ' sort([{a:2},{a:1}], a) -> [{a:1},{a:2}] # Every child has it;\n' +
363
+ ' sort([{a:1},{b:2}], a) -> nil # ... the second does not;\n' +
364
+ ' sort([[9],[8]], 0) -> [[8],[9]] # A list child takes an index.',
365
+
366
+ sort_domain:
367
+ 'This bag cannot be ordered: `{member}`. There are two orders and no\n' +
368
+ 'third -- text by code point, numbers by the exact comparator -- so a\n' +
369
+ 'bag that mixes them, or holds a boolean, a null or a container, has\n' +
370
+ 'no order to be put in. Project a field that is all one kind, or\n' +
371
+ 'filter the bag first.\n' +
372
+ ' \n' +
373
+ 'Examples:\n' +
374
+ ' sort([3,1,2]) -> [1,2,3] # All numbers;\n' +
375
+ ' sort([b,a]) -> ["a","b"] # ... or all text;\n' +
376
+ ' sort([1,a]) -> nil # ... never both.',
377
+
378
+ sort_dir:
379
+ 'A sort direction names no direction: `{dir}`. The third argument is\n' +
380
+ '`asc` or `desc`, and omitting it is `asc`. The second argument is\n' +
381
+ 'the field to order by, so a keyless descending sort writes the\n' +
382
+ 'empty projector: the member itself.\n' +
383
+ ' \n' +
384
+ 'Examples:\n' +
385
+ ' sort($.rows, n) # Ascending by field `n`;\n' +
386
+ ' sort($.rows, n, desc) # ... descending;\n' +
387
+ ' sort($.tags) # The members themselves, ascending;\n' +
388
+ ' sort($.tags, "", desc) # ... descending.',
389
+
334
390
  join_member:
335
391
  'A member of this bag is not text and never will be: `{member}`.\n' +
336
392
  '`join` folds with `+` seeded with the empty string, and `+` with a\n' +
@@ -465,8 +521,6 @@ const hints: Record<string, string> = {
465
521
  'empty': 'Empty disjunction. The disjunction has no valid alternatives.',
466
522
  'empty-dist': 'Empty disjunction distribution. All alternatives in the disjunction are invalid.',
467
523
 
468
- // ADR-011 R2: two DEFAULTS of equal rank that cannot agree. The
469
- // fix is a rank, so the hint names it.
470
524
  'pref_rank_clash': 'Two defaults of the same rank disagree.' +
471
525
  ' Rank one of them (`**x`) to say which is the weaker layer,' +
472
526
  ' or give them the same value.',
@@ -494,15 +548,6 @@ const hints: Record<string, string> = {
494
548
  }
495
549
 
496
550
 
497
- // codeClasses assigns every error code a CLASS: conflict | incomplete |
498
- // reference | parse | budget | internal. The contract lives in
499
- // test/spec/errcodes.tsv (mode `errcode`): the spec suite executes one
500
- // row per code against this table and asserts SET EQUALITY between the
501
- // file and these keys, in both implementations (go/hints.go mirrors
502
- // this map exactly). Codes are append-only and never renamed; a class
503
- // change is a breaking change. Class rulings (why decimal_budget and
504
- // lossy_integer_literal are conflict, not budget; why unknown_function
505
- // is reference) are documented in the tsv header.
506
551
  const codeClasses: Record<string, string> = {
507
552
  // parse -- the source text is malformed or unusable
508
553
  parse: 'parse',
@@ -537,10 +582,6 @@ const codeClasses: Record<string, string> = {
537
582
  patch_ambiguous: 'reference',
538
583
  patch_span_mismatch: 'internal',
539
584
 
540
- // G4 phase 2 -- the checked link: a string that is not a tree
541
- // address (class `parse`, the text is wrong), and an address that
542
- // names nothing in this evaluation (class `reference`, the same
543
- // class as `no_path`, because it is the same kind of miss).
544
585
  refer_address: 'parse',
545
586
  rel_address: 'parse',
546
587
  refer_unresolved: 'reference',
@@ -581,10 +622,6 @@ const codeClasses: Record<string, string> = {
581
622
  // class as refer_address, because it is the same mistake.
582
623
  path_address: 'parse',
583
624
 
584
- // G8 phase 1 -- the generation combinators. All three are class
585
- // `parse`: what is wrong is the CALL as written (data that is not a
586
- // bag, a list element that is not a name), not any pair of values a
587
- // meet brought together.
588
625
  pack_data: 'parse',
589
626
  pack_key: 'parse',
590
627
  each_data: 'parse',
@@ -597,24 +634,12 @@ const codeClasses: Record<string, string> = {
597
634
  filter_data: 'parse',
598
635
  match_none: 'conflict',
599
636
 
600
- // G9 phase 6 -- the string builtins. All five are class `parse`:
601
- // what is wrong is the CALL as written -- a variant that names no
602
- // convention, a pattern outside the subset, a substitution naming a
603
- // group that does not exist, a separator that is neither string nor
604
- // pattern. `usc_malformed` is the odd one and still `parse`: the
605
- // TEXT the call was given has no inverse, which is a fact about the
606
- // argument rather than about any meet.
607
637
  esc_variant: 'parse',
608
638
  usc_malformed: 'parse',
609
639
  rep_pattern: 'parse',
610
640
  rep_sub: 'parse',
611
641
  split_sep: 'parse',
612
642
 
613
- // G9 phase 6 -- apply-templates. The four shape codes are class
614
- // `parse`: what is wrong is the CALL as written -- a selection with
615
- // no children, a table that is not one, a rule missing a half.
616
- // `emit_none` is class `conflict` for `match_none`'s reason: the
617
- // node and every pattern written for it disagreed.
618
643
  emit_data: 'parse',
619
644
  emit_table: 'parse',
620
645
  emit_template: 'parse',
@@ -622,19 +647,11 @@ const codeClasses: Record<string, string> = {
622
647
  emit_none: 'conflict',
623
648
  emit_ref: 'conflict',
624
649
 
625
- // RENDER P6 -- `replace` on a template, and `form`. The two
626
- // template checks are class `parse`: what is wrong is the TEMPLATE
627
- // as written, before any node. `replace_value` is class `conflict`:
628
- // the node's value and the body that wanted text disagreed.
629
- // `form_data` is `each_data`'s twin.
630
650
  replace_overlap: 'parse',
631
651
  replace_unused: 'parse',
632
652
  replace_value: 'conflict',
633
653
  form_data: 'parse',
634
654
 
635
- // G8 phase 3 -- the placeholder. Class `conflict`: two values met
636
- // and neither could answer for the other, which is what every
637
- // conflict is.
638
655
  place_pair: 'conflict',
639
656
 
640
657
  // G6 phase 2 -- modules. Both are class `parse`: a module import is
@@ -675,6 +692,9 @@ const codeClasses: Record<string, string> = {
675
692
  constraint: 'conflict',
676
693
  must: 'conflict',
677
694
  constraint_pattern: 'conflict',
695
+ abnf_grammar: 'parse',
696
+ parse_arg: 'parse',
697
+ parse_failed: 'conflict',
678
698
  scalar_value: 'conflict',
679
699
  scalar_kind: 'conflict',
680
700
  no_scalar_unify: 'conflict',
@@ -692,6 +712,9 @@ const codeClasses: Record<string, string> = {
692
712
  exact_float_mix: 'conflict',
693
713
  inexact_integer_sum: 'conflict',
694
714
  pick_key: 'conflict',
715
+ sort_key: 'conflict',
716
+ sort_domain: 'conflict',
717
+ sort_dir: 'parse',
695
718
  aggregate_data: 'conflict',
696
719
  aggregate_empty: 'conflict',
697
720
  join_member: 'conflict',