aontu 0.62.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 (356) hide show
  1. package/README.md +1 -1
  2. package/dist/agentsmd.js +0 -27
  3. package/dist/agentsmd.js.map +1 -1
  4. package/dist/alias.js.map +1 -1
  5. package/dist/allow.js +0 -92
  6. package/dist/allow.js.map +1 -1
  7. package/dist/aontu.d.ts +1 -1
  8. package/dist/aontu.js +1 -79
  9. package/dist/aontu.js.map +1 -1
  10. package/dist/aontumodel.d.ts +4 -0
  11. package/dist/aontumodel.js +33 -0
  12. package/dist/aontumodel.js.map +1 -0
  13. package/dist/cli.js +101 -620
  14. package/dist/cli.js.map +1 -1
  15. package/dist/ctx.js +0 -48
  16. package/dist/ctx.js.map +1 -1
  17. package/dist/diff.js +0 -32
  18. package/dist/diff.js.map +1 -1
  19. package/dist/err.js +0 -40
  20. package/dist/err.js.map +1 -1
  21. package/dist/escape.js +0 -45
  22. package/dist/escape.js.map +1 -1
  23. package/dist/exactjson.d.ts +0 -35
  24. package/dist/exactjson.js +0 -131
  25. package/dist/exactjson.js.map +1 -1
  26. package/dist/format.js +13 -203
  27. package/dist/format.js.map +1 -1
  28. package/dist/grammar.d.ts +9 -0
  29. package/dist/grammar.js +54 -0
  30. package/dist/grammar.js.map +1 -0
  31. package/dist/graph.js +0 -26
  32. package/dist/graph.js.map +1 -1
  33. package/dist/hcanon.js +0 -82
  34. package/dist/hcanon.js.map +1 -1
  35. package/dist/helpdoc.js +1 -1
  36. package/dist/helpdoc.js.map +1 -1
  37. package/dist/hints.d.ts +0 -6
  38. package/dist/hints.js +54 -55
  39. package/dist/hints.js.map +1 -1
  40. package/dist/jsonschema.js +0 -114
  41. package/dist/jsonschema.js.map +1 -1
  42. package/dist/keyorder.d.ts +0 -7
  43. package/dist/keyorder.js +0 -41
  44. package/dist/keyorder.js.map +1 -1
  45. package/dist/lang.js +17 -915
  46. package/dist/lang.js.map +1 -1
  47. package/dist/lower.js +11 -61
  48. package/dist/lower.js.map +1 -1
  49. package/dist/lsp-server.js +0 -16
  50. package/dist/lsp-server.js.map +1 -1
  51. package/dist/lsp.d.ts +1 -1
  52. package/dist/lsp.js +12 -159
  53. package/dist/lsp.js.map +1 -1
  54. package/dist/mcp-server.js +0 -26
  55. package/dist/mcp-server.js.map +1 -1
  56. package/dist/mcp.js +0 -113
  57. package/dist/mcp.js.map +1 -1
  58. package/dist/mod-tool.js +0 -130
  59. package/dist/mod-tool.js.map +1 -1
  60. package/dist/mod.js +0 -162
  61. package/dist/mod.js.map +1 -1
  62. package/dist/patch.js +0 -217
  63. package/dist/patch.js.map +1 -1
  64. package/dist/provenance.js +0 -140
  65. package/dist/provenance.js.map +1 -1
  66. package/dist/query.js +0 -75
  67. package/dist/query.js.map +1 -1
  68. package/dist/reach.js +0 -43
  69. package/dist/reach.js.map +1 -1
  70. package/dist/relation.js +0 -61
  71. package/dist/relation.js.map +1 -1
  72. package/dist/render.js +20 -135
  73. package/dist/render.js.map +1 -1
  74. package/dist/report-sarif.d.ts +0 -11
  75. package/dist/report-sarif.js +0 -28
  76. package/dist/report-sarif.js.map +1 -1
  77. package/dist/sig.js +0 -35
  78. package/dist/sig.js.map +1 -1
  79. package/dist/sigdecl.js +1 -1
  80. package/dist/sigdecl.js.map +1 -1
  81. package/dist/siggate.js +0 -4
  82. package/dist/siggate.js.map +1 -1
  83. package/dist/site.js +3 -29
  84. package/dist/site.js.map +1 -1
  85. package/dist/subsume.d.ts +0 -10
  86. package/dist/subsume.js +0 -137
  87. package/dist/subsume.js.map +1 -1
  88. package/dist/template.d.ts +2 -1
  89. package/dist/template.js +58 -138
  90. package/dist/template.js.map +1 -1
  91. package/dist/trim.js +0 -41
  92. package/dist/trim.js.map +1 -1
  93. package/dist/tsconfig.tsbuildinfo +1 -1
  94. package/dist/type.js.map +1 -1
  95. package/dist/unify.js +12 -242
  96. package/dist/unify.js.map +1 -1
  97. package/dist/utility.js +0 -22
  98. package/dist/utility.js.map +1 -1
  99. package/dist/val/AbnfFuncVal.d.ts +18 -0
  100. package/dist/val/AbnfFuncVal.js +132 -0
  101. package/dist/val/AbnfFuncVal.js.map +1 -0
  102. package/dist/val/AbsentVal.d.ts +11 -0
  103. package/dist/val/AbsentVal.js +30 -0
  104. package/dist/val/AbsentVal.js.map +1 -0
  105. package/dist/val/AggFuncVal.d.ts +10 -1
  106. package/dist/val/AggFuncVal.js +104 -116
  107. package/dist/val/AggFuncVal.js.map +1 -1
  108. package/dist/val/ArithFuncVal.js +0 -12
  109. package/dist/val/ArithFuncVal.js.map +1 -1
  110. package/dist/val/BagVal.js +1 -78
  111. package/dist/val/BagVal.js.map +1 -1
  112. package/dist/val/BigDecimalVal.js +0 -16
  113. package/dist/val/BigDecimalVal.js.map +1 -1
  114. package/dist/val/BigIntegerVal.js +0 -16
  115. package/dist/val/BigIntegerVal.js.map +1 -1
  116. package/dist/val/CloseFuncVal.js +0 -9
  117. package/dist/val/CloseFuncVal.js.map +1 -1
  118. package/dist/val/CmpFuncVal.js +2 -59
  119. package/dist/val/CmpFuncVal.js.map +1 -1
  120. package/dist/val/ConjunctVal.js +0 -29
  121. package/dist/val/ConjunctVal.js.map +1 -1
  122. package/dist/val/ConstraintVal.js +0 -500
  123. package/dist/val/ConstraintVal.js.map +1 -1
  124. package/dist/val/ContainerKindVal.js +0 -2
  125. package/dist/val/ContainerKindVal.js.map +1 -1
  126. package/dist/val/CopyFuncVal.js +0 -3
  127. package/dist/val/CopyFuncVal.js.map +1 -1
  128. package/dist/val/Decimal.js +0 -179
  129. package/dist/val/Decimal.js.map +1 -1
  130. package/dist/val/DeprecateFuncVal.js.map +1 -1
  131. package/dist/val/DisjunctVal.js +0 -152
  132. package/dist/val/DisjunctVal.js.map +1 -1
  133. package/dist/val/EachFuncVal.js +0 -3
  134. package/dist/val/EachFuncVal.js.map +1 -1
  135. package/dist/val/EmitFuncVal.d.ts +1 -1
  136. package/dist/val/EmitFuncVal.js +6 -119
  137. package/dist/val/EmitFuncVal.js.map +1 -1
  138. package/dist/val/ExpectVal.js +0 -62
  139. package/dist/val/ExpectVal.js.map +1 -1
  140. package/dist/val/FilterFuncVal.js +0 -25
  141. package/dist/val/FilterFuncVal.js.map +1 -1
  142. package/dist/val/FuncBaseVal.d.ts +1 -0
  143. package/dist/val/FuncBaseVal.js +7 -127
  144. package/dist/val/FuncBaseVal.js.map +1 -1
  145. package/dist/val/GraphAtomVal.js +0 -15
  146. package/dist/val/GraphAtomVal.js.map +1 -1
  147. package/dist/val/HideFuncVal.js +0 -13
  148. package/dist/val/HideFuncVal.js.map +1 -1
  149. package/dist/val/IntegerVal.js +0 -61
  150. package/dist/val/IntegerVal.js.map +1 -1
  151. package/dist/val/JunctionVal.js +0 -20
  152. package/dist/val/JunctionVal.js.map +1 -1
  153. package/dist/val/KeyFuncVal.js +0 -46
  154. package/dist/val/KeyFuncVal.js.map +1 -1
  155. package/dist/val/ListVal.js +0 -57
  156. package/dist/val/ListVal.js.map +1 -1
  157. package/dist/val/LowerFuncVal.js +0 -11
  158. package/dist/val/LowerFuncVal.js.map +1 -1
  159. package/dist/val/MapVal.js +0 -151
  160. package/dist/val/MapVal.js.map +1 -1
  161. package/dist/val/MatchFuncVal.js +0 -27
  162. package/dist/val/MatchFuncVal.js.map +1 -1
  163. package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
  164. package/dist/val/MaybeFuncVal.js +50 -0
  165. package/dist/val/MaybeFuncVal.js.map +1 -0
  166. package/dist/val/MoveFuncVal.js +0 -18
  167. package/dist/val/MoveFuncVal.js.map +1 -1
  168. package/dist/val/NilVal.js +0 -60
  169. package/dist/val/NilVal.js.map +1 -1
  170. package/dist/val/NomFuncVal.js +8 -42
  171. package/dist/val/NomFuncVal.js.map +1 -1
  172. package/dist/val/NumberVal.js +0 -15
  173. package/dist/val/NumberVal.js.map +1 -1
  174. package/dist/val/OpBaseVal.d.ts +1 -0
  175. package/dist/val/OpBaseVal.js +3 -15
  176. package/dist/val/OpBaseVal.js.map +1 -1
  177. package/dist/val/PackFuncVal.js +0 -34
  178. package/dist/val/PackFuncVal.js.map +1 -1
  179. package/dist/val/PathFuncVal.js +0 -6
  180. package/dist/val/PathFuncVal.js.map +1 -1
  181. package/dist/val/PathVal.js +0 -41
  182. package/dist/val/PathVal.js.map +1 -1
  183. package/dist/val/PlaceVal.js +0 -25
  184. package/dist/val/PlaceVal.js.map +1 -1
  185. package/dist/val/PlusOpVal.d.ts +1 -7
  186. package/dist/val/PlusOpVal.js +13 -74
  187. package/dist/val/PlusOpVal.js.map +1 -1
  188. package/dist/val/PrefFuncVal.js +0 -1
  189. package/dist/val/PrefFuncVal.js.map +1 -1
  190. package/dist/val/PrefVal.js +0 -167
  191. package/dist/val/PrefVal.js.map +1 -1
  192. package/dist/val/RecurseVal.js +0 -55
  193. package/dist/val/RecurseVal.js.map +1 -1
  194. package/dist/val/RefVal.js +0 -282
  195. package/dist/val/RefVal.js.map +1 -1
  196. package/dist/val/ReferFuncVal.js +0 -232
  197. package/dist/val/ReferFuncVal.js.map +1 -1
  198. package/dist/val/ScalarKindVal.js +0 -49
  199. package/dist/val/ScalarKindVal.js.map +1 -1
  200. package/dist/val/ScalarVal.js +0 -11
  201. package/dist/val/ScalarVal.js.map +1 -1
  202. package/dist/val/StrFuncVal.js +0 -18
  203. package/dist/val/StrFuncVal.js.map +1 -1
  204. package/dist/val/SuperFuncVal.js +0 -32
  205. package/dist/val/SuperFuncVal.js.map +1 -1
  206. package/dist/val/TopVal.js +0 -1
  207. package/dist/val/TopVal.js.map +1 -1
  208. package/dist/val/TranslateFuncVal.js +1 -3
  209. package/dist/val/TranslateFuncVal.js.map +1 -1
  210. package/dist/val/UpperFuncVal.js +0 -11
  211. package/dist/val/UpperFuncVal.js.map +1 -1
  212. package/dist/val/Val.d.ts +1 -0
  213. package/dist/val/Val.js +2 -133
  214. package/dist/val/Val.js.map +1 -1
  215. package/dist/val/VarVal.js +0 -12
  216. package/dist/val/VarVal.js.map +1 -1
  217. package/dist/val/arith.js +0 -37
  218. package/dist/val/arith.js.map +1 -1
  219. package/dist/val/caserange.js +0 -61
  220. package/dist/val/caserange.js.map +1 -1
  221. package/dist/val/members.js +0 -6
  222. package/dist/val/members.js.map +1 -1
  223. package/dist/val/numcmp.js +0 -11
  224. package/dist/val/numcmp.js.map +1 -1
  225. package/dist/val/numkind.js +0 -145
  226. package/dist/val/numkind.js.map +1 -1
  227. package/dist/val/valutil.js +0 -16
  228. package/dist/val/valutil.js.map +1 -1
  229. package/dist/vet.js +0 -461
  230. package/dist/vet.js.map +1 -1
  231. package/dist/view.js +0 -414
  232. package/dist/view.js.map +1 -1
  233. package/dist/walk.js +0 -41
  234. package/dist/walk.js.map +1 -1
  235. package/grammar/aontu.abnf +8 -6
  236. package/grammar/aontu.gbnf +4 -4
  237. package/grammar/aontu.lark +4 -4
  238. package/grammar/aontu.tmLanguage.json +1 -1
  239. package/package.json +4 -2
  240. package/src/agentsmd.ts +0 -35
  241. package/src/alias.ts +0 -39
  242. package/src/allow.ts +1 -96
  243. package/src/aontu.ts +3 -110
  244. package/src/aontumodel.ts +32 -0
  245. package/src/cli.ts +113 -682
  246. package/src/ctx.ts +0 -103
  247. package/src/diff.ts +0 -40
  248. package/src/err.ts +0 -40
  249. package/src/escape.ts +0 -46
  250. package/src/exactjson.ts +0 -131
  251. package/src/format.ts +14 -257
  252. package/src/grammar.ts +72 -0
  253. package/src/graph.ts +0 -61
  254. package/src/hcanon.ts +0 -82
  255. package/src/helpdoc.ts +1 -1
  256. package/src/hints.ts +66 -57
  257. package/src/jsonschema.ts +0 -123
  258. package/src/keyorder.ts +0 -42
  259. package/src/lang.ts +19 -931
  260. package/src/lower.ts +12 -62
  261. package/src/lsp-server.ts +0 -16
  262. package/src/lsp.ts +12 -180
  263. package/src/mcp-server.ts +0 -31
  264. package/src/mcp.ts +0 -130
  265. package/src/mod-tool.ts +0 -158
  266. package/src/mod.ts +0 -178
  267. package/src/patch.ts +0 -232
  268. package/src/provenance.ts +0 -183
  269. package/src/query.ts +0 -84
  270. package/src/reach.ts +0 -53
  271. package/src/relation.ts +0 -84
  272. package/src/render.ts +20 -172
  273. package/src/report-sarif.ts +0 -48
  274. package/src/sig.ts +0 -35
  275. package/src/sigdecl.ts +1 -1
  276. package/src/siggate.ts +0 -30
  277. package/src/site.ts +3 -29
  278. package/src/subsume.ts +1 -161
  279. package/src/template.ts +69 -140
  280. package/src/trim.ts +0 -53
  281. package/src/type.ts +2 -45
  282. package/src/unify.ts +13 -251
  283. package/src/utility.ts +0 -31
  284. package/src/val/AbnfFuncVal.ts +181 -0
  285. package/src/val/AbsentVal.ts +54 -0
  286. package/src/val/AggFuncVal.ts +152 -188
  287. package/src/val/ArithFuncVal.ts +0 -20
  288. package/src/val/BagVal.ts +1 -78
  289. package/src/val/BigDecimalVal.ts +0 -16
  290. package/src/val/BigIntegerVal.ts +0 -16
  291. package/src/val/CloseFuncVal.ts +0 -9
  292. package/src/val/CmpFuncVal.ts +4 -166
  293. package/src/val/ConjunctVal.ts +0 -33
  294. package/src/val/ConstraintVal.ts +2 -537
  295. package/src/val/ContainerKindVal.ts +0 -18
  296. package/src/val/CopyFuncVal.ts +0 -5
  297. package/src/val/Decimal.ts +1 -185
  298. package/src/val/DeprecateFuncVal.ts +0 -10
  299. package/src/val/DisjunctVal.ts +0 -157
  300. package/src/val/EachFuncVal.ts +0 -40
  301. package/src/val/EmitFuncVal.ts +8 -208
  302. package/src/val/ExpectVal.ts +0 -62
  303. package/src/val/FilterFuncVal.ts +0 -55
  304. package/src/val/FuncBaseVal.ts +9 -130
  305. package/src/val/GraphAtomVal.ts +0 -42
  306. package/src/val/HideFuncVal.ts +0 -15
  307. package/src/val/IntegerVal.ts +0 -61
  308. package/src/val/JunctionVal.ts +0 -20
  309. package/src/val/KeyFuncVal.ts +0 -48
  310. package/src/val/ListVal.ts +0 -59
  311. package/src/val/LowerFuncVal.ts +0 -12
  312. package/src/val/MapVal.ts +0 -151
  313. package/src/val/MatchFuncVal.ts +0 -59
  314. package/src/val/MaybeFuncVal.ts +86 -0
  315. package/src/val/MoveFuncVal.ts +0 -20
  316. package/src/val/NilVal.ts +0 -60
  317. package/src/val/NomFuncVal.ts +8 -95
  318. package/src/val/NumberVal.ts +0 -16
  319. package/src/val/OpBaseVal.ts +4 -17
  320. package/src/val/PackFuncVal.ts +0 -63
  321. package/src/val/PathFuncVal.ts +0 -32
  322. package/src/val/PathVal.ts +0 -66
  323. package/src/val/PlaceVal.ts +0 -45
  324. package/src/val/PlusOpVal.ts +18 -75
  325. package/src/val/PrefFuncVal.ts +0 -1
  326. package/src/val/PrefVal.ts +0 -179
  327. package/src/val/RecurseVal.ts +0 -81
  328. package/src/val/RefVal.ts +1 -285
  329. package/src/val/ReferFuncVal.ts +0 -255
  330. package/src/val/ScalarKindVal.ts +0 -50
  331. package/src/val/ScalarVal.ts +0 -12
  332. package/src/val/StrFuncVal.ts +0 -44
  333. package/src/val/SuperFuncVal.ts +0 -42
  334. package/src/val/TopVal.ts +0 -1
  335. package/src/val/TranslateFuncVal.ts +1 -51
  336. package/src/val/UpperFuncVal.ts +0 -12
  337. package/src/val/Val.ts +3 -192
  338. package/src/val/VarVal.ts +0 -15
  339. package/src/val/arith.ts +0 -92
  340. package/src/val/caserange.ts +0 -62
  341. package/src/val/members.ts +0 -23
  342. package/src/val/numcmp.ts +1 -27
  343. package/src/val/numkind.ts +0 -149
  344. package/src/val/valutil.ts +0 -16
  345. package/src/vet.ts +1 -582
  346. package/src/view.ts +0 -507
  347. package/src/walk.ts +0 -41
  348. package/dist/std.d.ts +0 -3
  349. package/dist/std.js +0 -672
  350. package/dist/std.js.map +0 -1
  351. package/dist/val/FormFuncVal.js +0 -55
  352. package/dist/val/FormFuncVal.js.map +0 -1
  353. package/dist/val/NamerFuncVal.d.ts +0 -12
  354. package/dist/val/NamerFuncVal.js +0 -176
  355. package/dist/val/NamerFuncVal.js.map +0 -1
  356. package/src/std.ts +0 -683
package/dist/cli.js CHANGED
@@ -32,13 +32,6 @@ exports.runFmt = runFmt;
32
32
  exports.watchChange = watchChange;
33
33
  exports.watchSignature = watchSignature;
34
34
  exports.deprecatedAt = deprecatedAt;
35
- // Command-line interface for Aontu.
36
- //
37
- // aontu [options] [file]
38
- //
39
- // With a file argument, the file is evaluated and the result printed.
40
- // With no file on an interactive terminal, a REPL is started. With no
41
- // file and piped input, the source is read from stdin. See HELP below.
42
35
  // Named imports, not `import * as`: the namespace form makes tsc emit the
43
36
  const query_1 = require("./query");
44
37
  // __importStar downlevel helper, whose branches no supported Node takes.
@@ -77,7 +70,8 @@ const HELP = `Usage: aontu [options] [file]
77
70
  aontu render [--at <path>] [--profile <file>]... [--unit <path>]
78
71
  [--stdout | --out <dir> | --check <dir> | --coverage]
79
72
  [--coverage-at <path>] [--strict] <file>
80
- aontu template [--resugar] [--check] [--marker <token>] <file>
73
+ aontu template [--resugar] [--check] [--marker <token>]
74
+ [--profile <file>] <file>
81
75
  aontu hash [options] <file>
82
76
  aontu mod tidy|verify|vendor|manifest [options] [dir]
83
77
  aontu get <path> [options] <file>
@@ -85,7 +79,8 @@ const HELP = `Usage: aontu [options] [file]
85
79
  aontu set <path>=<value>... --entry <file> --overlay <file>
86
80
  aontu allow --role <role> [--at <path>] <roles-file> <path>...
87
81
  aontu agentsmd [--write <AGENTS.md>] [--depth <n>] <file>
88
- aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>] <file>...
82
+ aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>]
83
+ [--profile <file>] <file>...
89
84
  aontu help [topic] [--format text|json]
90
85
  aontu explain <code> | --list [--format text|json]
91
86
  aontu init [dir]
@@ -325,8 +320,9 @@ up on its own, or a relation, root or path that names nothing.
325
320
  Render options:
326
321
  --at <path> Render the value at this path ($.a.b); the root by
327
322
  default
328
- --profile <file> A profile document, profile: {lang, ...}, vetted
329
- against aontu:profile; repeatable, one per language
323
+ --profile <file> A profile document, aontu: render: Lang: {lang,
324
+ ...}, vetted against aontu:render; repeatable,
325
+ one per language
330
326
  --unit <path> Render only the unit with this path
331
327
  --stdout One unit's bytes and nothing else (with --unit when
332
328
  the instance has several)
@@ -350,8 +346,8 @@ does not stand up or the instance is not aontu:code.
350
346
  A render entry file whose extension is not .aon is a TEMPLATE: a
351
347
  generator in the target's own syntax, whose marker lines carry aontu
352
348
  and whose other lines are output. It is desugared before it is
353
- evaluated, and --marker names the marker for a language the table does
354
- not know.
349
+ evaluated, and a language the table does not know names its marker with
350
+ --marker, or declares it once in a profile file that --profile reads.
355
351
 
356
352
  Template options:
357
353
  --resugar The file is the canonical aontu; print the template
@@ -359,7 +355,9 @@ Template options:
359
355
  --check Desugar and resugar, and exit 1 if the file is not
360
356
  what the round trip answers
361
357
  --marker <t> The marker, when the extension does not name it
362
- (default //-, and #- --- /*- by extension)
358
+ (default //-, and #- --- /*- <!--- by extension)
359
+ --profile <f> A profile file, whose template.ext names the
360
+ extensions it marks and template.marker the marker
363
361
 
364
362
  The template verb prints the canonical aontu form of a generator
365
363
  written in the target's own syntax: a marked line is aontu source, and
@@ -452,7 +450,9 @@ Fmt options:
452
450
  shapes, on standard error, and print nothing else
453
451
  --strict With --lint, and exit 1 when there is a finding
454
452
  --marker <t> The file is a generator, and this is its marker
455
- (default //-, and #- --- /*- by extension)
453
+ (default //-, and #- --- /*- <!--- by extension)
454
+ --profile <f> A profile file, whose template.ext names the
455
+ extensions it marks and template.marker the marker
456
456
 
457
457
  The fmt verb prints one document in the agreed form; with no file it
458
458
  reads standard input. Several files need one of the options above.
@@ -495,30 +495,7 @@ function version() {
495
495
  return '0.0.0';
496
496
  }
497
497
  }
498
- // The terminal colour escapes the parser puts in its message text. A
499
- // machine-readable report is no place for them, which is the rule
500
- // findingOf states in ts/src/vet.ts; the twin here rather than an
501
- // import because go/cmd/aontu carries its own for the same reason (the
502
- // engine's is not exported to its command).
503
498
  const EVAL_ANSI = new RegExp('\u001b\\[[0-9;]*m', 'g');
504
- // THE ENGINE'S DIAGNOSIS AS A FINDING (G11 phase 7). The bare command
505
- // was the one verb whose failure had no machine-readable form, so the
506
- // default entry point was the one an agent had to parse with a regular
507
- // expression.
508
- //
509
- // THE HEADLINE ONLY, and no `hint`. Both are parity decisions rather
510
- // than economies: the frames under the headline are drawn for a person
511
- // reading a terminal and only the first line is held to byte parity
512
- // between the ports (the rule findingOf states), and the hint TABLES
513
- // are deliberately not in parity while the code registry is -- so a
514
- // hint here would make the two ports answer differently for a code
515
- // only one of them explains. `aontu explain <code>` is where the hint
516
- // lives, which is what phase 3 built it for.
517
- //
518
- // The CLASS comes from the registry rather than from the nil, because
519
- // the registry is what both ports hold set-equal
520
- // (test/spec/errcodes.tsv). Mirrors evalFinding in
521
- // go/cmd/aontu/main.go.
522
499
  function evalFinding(code, text) {
523
500
  return {
524
501
  class: (0, hints_1.codeClass)(code),
@@ -526,10 +503,6 @@ function evalFinding(code, text) {
526
503
  message: text.split('\n')[0].replace(EVAL_ANSI, ''),
527
504
  path: '$',
528
505
  severity: 'error',
529
- // NO SITE. The bare command's failure is the whole document not
530
- // standing up, and the two sites a conflict names are in the
531
- // frames the text form prints; naming one of them here would be a
532
- // choice the engine has not made.
533
506
  sites: [],
534
507
  };
535
508
  }
@@ -537,12 +510,6 @@ function evalFinding(code, text) {
537
510
  // message, and the failure in the finding shape. Never throws.
538
511
  function evalSource(aontu, src, mode) {
539
512
  try {
540
- // exactJSON, not JSON.stringify: a document using the `0d` exact
541
- // leaves generates bigints and Decimals, which JSON.stringify cannot
542
- // write (D9). The CLI prints INDENTED JSON and the shared suite's
543
- // `gens` mode prints COMPACT JSON, but both go through this one
544
- // emitter -- an indent argument rather than a second implementation,
545
- // so the two cannot drift from each other or from the Go port.
546
513
  const text = 'canon' === mode
547
514
  ? aontu.unify(src).canon
548
515
  : (0, aontu_1.exactJSON)(aontu.generate(src), 2);
@@ -552,13 +519,6 @@ function evalSource(aontu, src, mode) {
552
519
  const msg = (err instanceof aontu_1.AontuError || true === err?.aontu)
553
520
  ? err.message
554
521
  : String(err?.message ?? err);
555
- // WHAT THE ENGINE COLLECTED, when it collected anything: an
556
- // AontuError carries the NilVals the run failed on, already
557
- // materialised (handleErrors in ts/src/aontu.ts), and their first
558
- // is the diagnosis every other verb reports. An error raised
559
- // outside the engine's own collection -- exactJSON's circular
560
- // refusal, a foreign object claiming to be one -- carries none,
561
- // and answers with the text alone rather than an invented code.
562
522
  const errs = 'function' === typeof err?.errs ? err.errs() : [];
563
523
  const first = errs[0];
564
524
  return {
@@ -623,16 +583,6 @@ function trustOpts(trust, entryRoot) {
623
583
  return { ...text, trustWarn: makeTrustWarn(), trustWarnRoot: entryRoot };
624
584
  }
625
585
  }
626
- // EVERY VERB honours the include capability, not just the bare
627
- // command. G5 wired `--trust`/`--include-root` to `aontu <file>` alone,
628
- // so `aontu vet schema.aon data.json` -- the surface an agent actually
629
- // scripts -- ran the full system resolver with no flag to confine it
630
- // and no warning (use-cases/REVIEW.md finding G). The flags are
631
- // stripped here, before each verb parses its own tail, so a verb only
632
- // has to pass the profile on to its engine.
633
- //
634
- // Returns undefined when the spelling is wrong, with the message
635
- // already printed: the caller answers the usage class.
636
586
  function takeTrust(argv) {
637
587
  const rest = [];
638
588
  let trust = { kind: 'system-warn', textExt: [] };
@@ -727,17 +677,6 @@ function runFile(file, mode, format, trust) {
727
677
  src = (0, node_fs_1.readFileSync)(file, 'utf8');
728
678
  }
729
679
  catch (err) {
730
- // A MISTYPED VERB READS AS A FILE NAME, and until G11 phase 2 that
731
- // was only said when there were TWO of them. The one-argument case
732
- // is the one an agent actually produces -- `aontu help`, `aontu
733
- // init`, `aontu ontology` -- and it answered `cannot read help:
734
- // ...`, which describes the symptom and hides the cause.
735
- //
736
- // The test is SHAPE, not existence: a bare word (no separator, no
737
- // extension) that cannot be read was meant as a verb, while
738
- // `./help`, `help.aon` and `/tmp/help` were meant as paths and keep
739
- // the file diagnosis and its exit 1. That is the same escape hatch
740
- // the subcommand dispatch documents. Mirrors go/cmd/aontu/main.go.
741
680
  if (looksLikeVerb(file)) {
742
681
  process.stderr.write(`aontu: \`${file}\` is not a file, and not a verb this port knows\n`);
743
682
  const near = nearestVerb(file, KNOWN_VERBS);
@@ -751,12 +690,6 @@ function runFile(file, mode, format, trust) {
751
690
  return 1;
752
691
  }
753
692
  const path = (0, node_path_1.resolve)(file);
754
- // `fs` IS WHAT MAKES A FRAME EXCERPT THE FILE IT NAMES. Without it,
755
- // err.ts's resolveSrc falls back to the ENTRY text, so a frame whose
756
- // arrow says `lib/types.aon:2:6` printed the entry's line 2 under it
757
- // -- a real file name over another file's line, which
758
- // docs/reference-api.md forbids in the same words it uses to require
759
- // the name.
760
693
  const aontu = new aontu_1.Aontu({
761
694
  path,
762
695
  errfs: { existsSync: node_fs_1.existsSync, readFileSync: node_fs_1.readFileSync },
@@ -892,14 +825,6 @@ function runRepl(initialMode, jsonl, trust) {
892
825
  rl.prompt();
893
826
  });
894
827
  rl.on('close', () => {
895
- // The closing newline is for a HUMAN, so it is written only for
896
- // one: it moves the terminal off the prompt line that `rl` left
897
- // hanging. In `--jsonl` there is no prompt, every answer already
898
- // ends in its own newline, and this one appended a bare empty line
899
- // to the stream -- a record that is not JSON, at the end of a
900
- // protocol whose whole contract is one JSON object per line. A
901
- // harness parsing every line it receives failed on it, after the
902
- // commands had all succeeded. Mirrors go/cmd/aontu/repl.go.
903
828
  if (!jsonl) {
904
829
  process.stdout.write('\n');
905
830
  }
@@ -909,15 +834,6 @@ function runRepl(initialMode, jsonl, trust) {
909
834
  process.exitCode = 0;
910
835
  });
911
836
  }
912
- // THE VET VERB (G2 phase 3).
913
- //
914
- // Exit codes are VERDICT CLASSES, not a pass/fail bit: an agent loop
915
- // branches on "the data contradicts the truth" (1) differently from
916
- // "the data has not supplied everything the truth requires" (3), and
917
- // differently again from "the schema itself is broken" (4), which is
918
- // never the data's fault. 2 stays what it already was for this CLI --
919
- // the caller got the invocation wrong -- which is why an unreadable
920
- // file is a 2 rather than a 4.
921
837
  const VET_EXIT = {
922
838
  valid: 0,
923
839
  invalid: 1,
@@ -961,14 +877,6 @@ function parseVetArgs(argv) {
961
877
  format = f;
962
878
  }
963
879
  else if ('--max-errors' === arg) {
964
- // ONE GRAMMAR, spelled the same way in both ports: decimal
965
- // digits, one to nine of them, at least 1. `Number()` alone
966
- // accepted `1.0`, `1e2`, `0x10` and ` 3`, which Go's parser
967
- // refuses -- so the same documented invocation meant different
968
- // things in the two shipped commands. The nine-digit ceiling is
969
- // where the ports would part company again: beyond it Go's
970
- // integer conversion saturates, and a cap nobody can reach is
971
- // not worth a divergence.
972
880
  const raw = argv[++i];
973
881
  if (!/^[0-9]{1,9}$/.test(raw ?? '') || 1 > Number(raw)) {
974
882
  return { err: 'aontu: --max-errors needs a positive whole number' };
@@ -1046,10 +954,6 @@ function renderFinding(f) {
1046
954
  out.push(` actual: ${f.actual}`);
1047
955
  }
1048
956
  for (const s of f.sites) {
1049
- // Every site carries the canon of the value it stands for: that is
1050
- // what makes the two sides of a conflict readable side by side. A
1051
- // site's file is always a string -- empty when the value belongs to
1052
- // neither document -- so there is nothing to coalesce here.
1053
957
  out.push(` ${s.role}: ${s.file}:${s.row}:${s.col} (${s.value})`);
1054
958
  }
1055
959
  return out.join('\n');
@@ -1141,18 +1045,9 @@ function vetOnce(args, trust) {
1141
1045
  process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
1142
1046
  return 2;
1143
1047
  }
1144
- // Each data file is vetted on its own, because a parsed tree is
1145
- // single-use (docs/reference-api.md) -- and because two data files
1146
- // are two candidates for the same truth, not one merged candidate.
1147
1048
  let verdict = 'valid';
1148
1049
  let truncated = false;
1149
1050
  const findings = [];
1150
- // COVERAGE ACROSS SEVERAL DATA FILES (G11 phase 5). Two data files
1151
- // are two candidates for one truth, so the schema side is the SAME
1152
- // for each: `declared` is taken once, and a declaration is unused
1153
- // only when NO file met it -- the intersection, because a
1154
- // declaration one file exercised is exercised. The data side adds
1155
- // up: leaves and checked leaves sum, and `unchecked` is the union.
1156
1051
  let cov;
1157
1052
  // Initialised rather than left undefined: it is filled in the same
1158
1053
  // block that sets `cov`, so a fallback at the read below would be an
@@ -1170,12 +1065,6 @@ function vetOnce(args, trust) {
1170
1065
  maxErrors: args.maxErrors,
1171
1066
  schemaUrl: args.schema,
1172
1067
  dataUrl: source.file,
1173
- // The paths as well as the labels: a relative `@"file"` load
1174
- // inside either document resolves from ITS OWN directory, the
1175
- // way `aontu <file>` already resolves one (runFile above). The
1176
- // path is passed AS TYPED, not resolved: it doubles as the
1177
- // label above, and a report that mixed the typed path with an
1178
- // absolute one would name the same file two ways.
1179
1068
  schemaPath: args.schema,
1180
1069
  dataPath: source.file,
1181
1070
  coverage: args.coverage,
@@ -1204,26 +1093,10 @@ function vetOnce(args, trust) {
1204
1093
  ? new Set([...unusedEvery].filter((u) => mine.has(u))) : mine;
1205
1094
  unusedSeen = true;
1206
1095
  }
1207
- // A SCHEMA-SIDE FAULT IS THE SAME FAULT FOR EVERY DATA FILE, so it
1208
- // is reported ONCE. `error` means exactly that -- the run could not
1209
- // be set up from the truth's side, never the data's (the exit table
1210
- // in docs/reference-api.md) -- so the report the first file
1211
- // produced is the report every later file would produce, character
1212
- // for character. Concatenating them repeated one broken schema N
1213
- // times and, past the cap, marked the report `truncated` over a
1214
- // single underlying fault. It only became visible once the `error`
1215
- // verdict started carrying findings at all: while the list was
1216
- // empty there was nothing to duplicate.
1217
1096
  if ('error' === report.verdict) {
1218
1097
  break;
1219
1098
  }
1220
1099
  }
1221
- // The cap is on the REPORT, not on each file. Capping every file's
1222
- // list and then concatenating them let `--max-errors 1` emit one
1223
- // finding PER FILE -- and leave `truncated` false while doing it,
1224
- // because no single file had been cut. The engine still caps each
1225
- // run, so a pathological file cannot flood the aggregate before it
1226
- // gets here; this is the second, honest cut.
1227
1100
  const cap = args.maxErrors ?? vet_1.VET_MAX_ERRORS;
1228
1101
  const kept = cap < findings.length ? findings.slice(0, cap) : findings;
1229
1102
  if (null != cov) {
@@ -1241,12 +1114,6 @@ function vetOnce(args, trust) {
1241
1114
  'sarif' === args.format ? renderVetSarif(report) :
1242
1115
  renderVetText(report);
1243
1116
  process.stdout.write(text + '\n');
1244
- // A VACUOUS CHECK IS A FAILED GATE UNDER `--strict-coverage`, and
1245
- // only under it: the verdict WORD is unchanged, so nothing that
1246
- // passes today starts failing, and a caller who wants the stronger
1247
- // gate asks for it. The reason goes to stderr, because stdout is a
1248
- // report contract -- a JSON consumer reads `coverage.vacuous` and a
1249
- // person reads this.
1250
1117
  if (true === args.strictCoverage && true === report.coverage?.vacuous) {
1251
1118
  process.stderr.write('aontu: no data leaf was constrained by the schema:' +
1252
1119
  ' this run checked nothing\n' +
@@ -1273,19 +1140,6 @@ function watchSignature(files) {
1273
1140
  function sleep(ms) {
1274
1141
  return new Promise((done) => setTimeout(done, ms));
1275
1142
  }
1276
- // Resolve true when any watched file's signature moves off `before`.
1277
- // This is the real waiter: it never resolves false, so a real watch
1278
- // runs until the process is interrupted; tests inject their own waiter
1279
- // to bound the loop, and pass a short pollMs when they drive this one
1280
- // directly. The interval is a required argument (the command passes
1281
- // WATCH_POLL_MS) so there is no defaulting branch a test could never
1282
- // take.
1283
- //
1284
- // The BASELINE is an argument, not a snapshot taken here: the loop
1285
- // records it BEFORE each vet run, so a save landing between the run's
1286
- // reads and the wait still compares as a change. A waiter that
1287
- // snapshotted on entry would adopt that unvetted save as its baseline
1288
- // and wait indefinitely on a stale report.
1289
1143
  async function watchChange(files, before, pollMs) {
1290
1144
  for (;;) {
1291
1145
  await sleep(pollMs);
@@ -1314,9 +1168,6 @@ async function watchVet(args, wait, trust) {
1314
1168
  }
1315
1169
  return code;
1316
1170
  }
1317
- // The vet verb. Non-watch runs are synchronous and return the exit
1318
- // class directly; `--watch` returns a promise that resolves only when
1319
- // the waiter says stop (never, for the real one).
1320
1171
  function runVet(argv, wait) {
1321
1172
  const trusted = takeTrust(argv);
1322
1173
  if (null == trusted) {
@@ -1527,30 +1378,7 @@ function parseBreakingArgs(argv) {
1527
1378
  },
1528
1379
  };
1529
1380
  }
1530
- // A source file the include resolver can actually load. `git#<rev>`
1531
- // materialises these and nothing else: an include names an Aontu
1532
- // document (`.aon`/`.aontu`, the two extensions `@"foo"` tries) or a
1533
- // JSON one, so the rest of a revision's tree cannot be part of any
1534
- // include closure and copying it would be pure cost.
1535
1381
  const INCLUDABLE = /\.(aon|aontu|jsonic|json)$/;
1536
- // Resolve one --against spelling to an old version.
1537
- //
1538
- // A `git#<rev>` spelling is the old version of the WHOLE TREE, not of
1539
- // the entry file alone. It used to be `git show <rev>:./<file>`, whose
1540
- // text was then evaluated with `generalPath`/`specificPath` pointing at
1541
- // the WORKING file -- so every `@"..."` include in the old document
1542
- // resolved against the working tree, and the "old" side was old entry
1543
- // text meeting new includes. A breaking change inside an included file
1544
- // therefore compared against itself and answered `compatible`: the
1545
- // documented CI gate silently un-gated every non-entry file of the
1546
- // multi-file layout real models use (use-cases/BUGS.md §26). The old
1547
- // tree's includable sources are copied into a temporary directory and
1548
- // the old document is evaluated from THERE.
1549
- //
1550
- // Sources outside the revision -- package includes under node_modules,
1551
- // the bundled `std/system` -- still resolve as they do today: they are
1552
- // not in the tree, and their versions travel with the lockfile rather
1553
- // than with this comparison.
1554
1382
  function oldVersion(spec, file) {
1555
1383
  if (!spec.startsWith('git#')) {
1556
1384
  try {
@@ -1578,23 +1406,9 @@ function oldVersion(spec, file) {
1578
1406
  // that only some failures take.
1579
1407
  const temp = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'aontu-against-'));
1580
1408
  try {
1581
- // THE REPO-RELATIVE PATH COMES FROM GIT, not from path arithmetic.
1582
- // Relativising `rev-parse --show-toplevel` against `resolve(file)`
1583
- // puts two DIFFERENT COORDINATE SYSTEMS on either side of the
1584
- // subtraction: git prints the real path, while the caller's is
1585
- // whatever they typed. On macOS a temp file under /var is
1586
- // /private/var to git, and on Windows a TMP short name
1587
- // (RUNNER~1) is the long form to git -- so the subtraction gave a
1588
- // `../..` climb, the entry was "not in that revision", and the
1589
- // documented CI spelling failed on both platforms while passing on
1590
- // Linux (this PR's own CI). `--show-prefix` is the same question
1591
- // asked in git's coordinates: the repo-relative directory of the
1592
- // cwd, already slash-separated and already normalised.
1593
1409
  const prefix = git(['rev-parse', '--show-prefix'], dir).trim();
1594
1410
  const entryRel = prefix + (0, node_path_1.basename)(file);
1595
1411
  const top = git(['rev-parse', '--show-toplevel'], dir).trim();
1596
- // `-z` so a path with a newline or a quote cannot be mistaken for
1597
- // two paths (git otherwise quotes such names).
1598
1412
  const listed = git(['ls-tree', '-r', '-z', '--name-only', rev], top)
1599
1413
  .split('\0').filter((p) => '' !== p);
1600
1414
  if (!listed.includes(entryRel)) {
@@ -1624,13 +1438,6 @@ function oldVersion(spec, file) {
1624
1438
  function policyCompat(newSrc, path, include) {
1625
1439
  const aontu = new aontu_1.Aontu();
1626
1440
  const ctx = aontu.ctx({ collect: true });
1627
- // The declaration is read by EVALUATING the document, so this leg
1628
- // runs the include resolver too and has to run it under BOTH of the
1629
- // verb's include options -- a `breaking --trust none` that read its
1630
- // own mode through an unconfined resolver would confine the
1631
- // comparison and not the question (use-cases/REVIEW.md finding G),
1632
- // and one that took the capability alone read no mode at all when
1633
- // the declaration arrived through a `--text-ext` include.
1634
1441
  const v = aontu.unify(newSrc, { path, ...(0, utility_1.includeOpts)(include) }, ctx);
1635
1442
  if (0 < ctx.err.length || true === v?.isNil) {
1636
1443
  return undefined;
@@ -1649,11 +1456,6 @@ function policyCompat(newSrc, path, include) {
1649
1456
  return 'backward' === m || 'forward' === m || 'full' === m || 'none' === m
1650
1457
  ? m : undefined;
1651
1458
  }
1652
- // Is the evaluated old version's value at the finding path deprecated?
1653
- // The --allow-deprecated-removal downgrade (G3 phase 4): removing (or
1654
- // otherwise changing) a value the old version already deprecated warns
1655
- // instead of breaking. The Go port exports the same reader as
1656
- // aontu.DeprecatedAt.
1657
1459
  function deprecatedAt(oldSrc, path, filePath) {
1658
1460
  const aontu = new aontu_1.Aontu();
1659
1461
  const ctx = aontu.ctx({ collect: true });
@@ -1720,9 +1522,6 @@ function runBreaking(argv) {
1720
1522
  process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
1721
1523
  return 2;
1722
1524
  }
1723
- // The declared mode: --mode overrides the document's own policy;
1724
- // neither means backward, the index's framing (v1-valid documents
1725
- // stay valid).
1726
1525
  const mode = args.mode ??
1727
1526
  policyCompat(newSrc, args.file, verbOpts(trust, entryRootOf(args.file))) ??
1728
1527
  'backward';
@@ -1755,8 +1554,6 @@ function runBreaking(argv) {
1755
1554
  if (null != old.temp) {
1756
1555
  temps.push(old.temp);
1757
1556
  }
1758
- // backward: the NEW document is the general side — every old
1759
- // instance must still be admitted. forward: the old one is.
1760
1557
  const checks = [];
1761
1558
  if ('backward' === mode || 'full' === mode) {
1762
1559
  checks.push({ general: [newSrc, args.file], specific: [oldSrc, spec] });
@@ -1771,17 +1568,9 @@ function runBreaking(argv) {
1771
1568
  at: args.at,
1772
1569
  generalUrl: check.general[1],
1773
1570
  specificUrl: check.specific[1],
1774
- // The old side's relative loads resolve from ITS own tree --
1775
- // the materialised revision for a git spelling, the named
1776
- // file's directory otherwise -- so an included file's change
1777
- // is part of the comparison rather than invisible to it.
1778
1571
  generalPath: check.general[1] === spec ? oldPath : args.file,
1779
1572
  specificPath: check.specific[1] === spec ? oldPath : args.file,
1780
1573
  });
1781
- // The deprecated-removal downgrade: a finding about a value the
1782
- // OLD version already deprecated becomes a warning, and warnings
1783
- // do not move the verdict. Deprecate-then-remove is the
1784
- // supported rename path (the design's own sequencing).
1785
1574
  let verdict = report.verdict;
1786
1575
  if (args.allowDeprecatedRemoval) {
1787
1576
  let liveFindings = 0;
@@ -1832,12 +1621,6 @@ function renderBreakingJson(report, mode) {
1832
1621
  findings: report.findings,
1833
1622
  }, 2);
1834
1623
  }
1835
- // ---------------------------------------------------------------------
1836
- // The trim reporter (G3 phase 6): report redundant entries as paths.
1837
- // Report-only — REWRITING needs G7's format-preserving patch surface —
1838
- // which is why --check is REQUIRED rather than defaulted: `aontu trim
1839
- // f.aon` reads as "trim this file", and doing something else silently
1840
- // is worse than saying so.
1841
1624
  const TRIM_HELP = 'aontu trim --check <file> (try --help)';
1842
1625
  const TRIM_EXIT = {
1843
1626
  clean: 0,
@@ -1907,8 +1690,6 @@ function runTrim(argv) {
1907
1690
  }
1908
1691
  function renderTrimText(report) {
1909
1692
  const head = `verdict: ${report.verdict}`;
1910
- // WHY, when the document could not be evaluated at all: rendered as
1911
- // vet renders a finding, because it IS one (the review's finding F).
1912
1693
  const errors = report.errors ?? [];
1913
1694
  if (0 < errors.length) {
1914
1695
  return [head, ''].concat(errors.map(renderFinding)).join('\n');
@@ -1957,27 +1738,10 @@ const VIEW_EDGES = ['upward', 'all', 'none'];
1957
1738
  // is a terminal, which is the CLI's to know and the library's never --
1958
1739
  // the same division err.ts already draws for the error frames.
1959
1740
  const VIEW_STYLES = ['auto', 'none', 'ansi', 'css'];
1960
- // `--style auto` resolved, which only the CLI can do. The mechanism is
1961
- // the PROFILE's and the library knows it -- an SVG carries its
1962
- // stylesheet unless told not to, which is what makes a figure stand
1963
- // alone. What the library cannot know is whether the DESTINATION is a
1964
- // terminal, so that is the only thing decided here: escapes on the
1965
- // text profile when stdout is a terminal and NO_COLOR is unset, the
1966
- // same two conditions the error frames use. `undefined` leaves the
1967
- // profile's own default in place.
1968
1741
  function viewStyleOf(asked, as) {
1969
1742
  if (undefined !== asked && 'auto' !== asked) {
1970
1743
  return asked;
1971
1744
  }
1972
- // STDOUT'S OWN TERMINAL-NESS, and NO_COLOR read here rather than
1973
- // through colorActive(). The figure goes to STDOUT and the error
1974
- // frames go to STDERR, and they are not the same destination: main()
1975
- // has already called setColor for stderr, so asking colorActive()
1976
- // would answer the wrong question twice --- no escapes for
1977
- // `aontu view tree m.aon 2>/dev/null` at a terminal, and escapes
1978
- // into the pipe for `aontu view tree m.aon | less`. The NO_COLOR
1979
- // rule is the one no-color.org states and err.ts implements:
1980
- // set, to anything but empty, means no colour.
1981
1745
  const no = process.env.NO_COLOR;
1982
1746
  return 'text' === as && true === process.stdout.isTTY
1983
1747
  && (null == no || '' === no) ? 'ansi' : undefined;
@@ -1999,24 +1763,6 @@ const VIEW_USAGE_CODES = [
1999
1763
  'view_document_shape', 'view_style_profile', 'view_style_unknown',
2000
1764
  ];
2001
1765
  const MOD_HELP = 'aontu mod tidy|verify|vendor|manifest [dir] (try --help)';
2002
- // The module tooling (G6 phase 3, ts/src/mod-tool.ts). All LOCAL:
2003
- // `tidy` resolves the closure from what is in the stores and rewrites
2004
- // the lockfile, `verify` asks whether the stores still mean what the
2005
- // lockfile pins and changes nothing, `vendor` materialises the locked
2006
- // closure into the project, `manifest` prints what a publish would
2007
- // push.
2008
- //
2009
- // TIDY AND VERIFY ARE DIFFERENT QUESTIONS, and that is why both exist.
2010
- // Tidy recomputes and rewrites by design -- a pin is what a module
2011
- // means NOW -- so it makes the lockfile agree with whatever the store
2012
- // holds, tampering included. Verify is the gate: a CI job runs it
2013
- // BEFORE tidy, or instead of it.
2014
- //
2015
- // `get` and `publish` are the NETWORK half of the design and are not in
2016
- // this build. They are named here rather than left to fall out as an
2017
- // unknown subcommand, because a reader of the design will type them and
2018
- // deserves to be told which half is missing rather than that the word
2019
- // is wrong.
2020
1766
  function runMod(argv) {
2021
1767
  const rest = [];
2022
1768
  let format = 'text';
@@ -2063,10 +1809,6 @@ function runMod(argv) {
2063
1809
  process.stderr.write(`aontu: mod needs tidy, verify, vendor or manifest\n${MOD_HELP}\n`);
2064
1810
  return 2;
2065
1811
  }
2066
- // THE OLD LAYOUT IS NAMED, NOT READ. The lockfile and the vendored
2067
- // closure moved under aontu_meta/; a project that still carries them
2068
- // at its root would otherwise look untouched by any of these verbs,
2069
- // which is the one silence worth breaking.
2070
1812
  if ((0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'aon_vendor')) || (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'mod-lock.aon'))) {
2071
1813
  process.stderr.write('aontu: aon_vendor/ and mod-lock.aon now live under aontu_meta/: ' +
2072
1814
  'move them, or run aontu mod tidy and aontu mod vendor\n');
@@ -2090,9 +1832,6 @@ const MOD_SUBS = ['tidy', 'verify', 'vendor', 'manifest'];
2090
1832
  const MOD_EXIT = {
2091
1833
  ok: 0,
2092
1834
  missing: 1,
2093
- // A REFUSED GATE, with `breaking`: a store that no longer means what
2094
- // the lockfile pins is the integrity check saying no, and a CI job
2095
- // reading exit codes should not have to learn a third class for it.
2096
1835
  mismatch: 1,
2097
1836
  // Likewise a lockfile that does not cover the project: the gate has
2098
1837
  // nothing to check, which is a refusal and not a pass.
@@ -2138,11 +1877,6 @@ function modText(sub, report) {
2138
1877
  for (const f of report.findings) {
2139
1878
  lines.push(f.path + ': ' + f.message);
2140
1879
  }
2141
- // What a manifest lacks is a declaration the module does not make
2142
- // or an entry file that is not there, and neither is something a
2143
- // fetch would supply -- so this is not the tail the other two
2144
- // subcommands share. The name says which kind it is: `mod.version`
2145
- // is a declaration, `service.aon` is a file.
2146
1880
  for (const miss of report.missing) {
2147
1881
  lines.push(miss + ': missing');
2148
1882
  }
@@ -2152,8 +1886,6 @@ function modText(sub, report) {
2152
1886
  for (const mod of report.verified) {
2153
1887
  lines.push(mod + ': verified');
2154
1888
  }
2155
- // BOTH HASHES, because the useful question is which way it moved:
2156
- // an empty `got` is a module that no longer stands up at all.
2157
1889
  for (const m of report.mismatched) {
2158
1890
  lines.push(m.mod + ': pinned ' + m.want + ' but the store means ' +
2159
1891
  ('' === m.got ? 'nothing (it does not evaluate)' : m.got));
@@ -2186,26 +1918,6 @@ function modText(sub, report) {
2186
1918
  }
2187
1919
  return lines.join('\n');
2188
1920
  }
2189
- // VACUITY SIGNALS (G11 phase 4,
2190
- // docs/capability-review/g11-agent-onramp.md).
2191
- //
2192
- // The same principle phase 5 applied to `vet`: a verb that did NOTHING
2193
- // and a verb that did its job answer the same. `aontu view tree` over a
2194
- // document declaring no relations printed one newline and exited 0;
2195
- // `aontu render` with no profile printed nothing and exited 0; `aontu
2196
- // relations` over a document declaring none answered `verdict: pass`.
2197
- // For a person at a terminal that is a shrug. For an unattended agent
2198
- // it is a green check mark on an empty box.
2199
- //
2200
- // ON STDERR, ALWAYS. stdout is a report contract -- a `--format json`
2201
- // consumer parses it -- and the exit code is a verdict class that
2202
- // callers already branch on. Neither changes here: what changes is
2203
- // that the caller is TOLD. A caller who wants it to be fatal has
2204
- // `vet --strict-coverage`, and the same argument would give the other
2205
- // verbs a flag of their own if one is ever asked for.
2206
- //
2207
- // The repository already ruled this for one verb, in G8 phase 6 on
2208
- // `trim`: "doing something else silently is worse than refusing".
2209
1921
  function vacuous(what, why) {
2210
1922
  process.stderr.write(`aontu: ${what}: ${why}\n`);
2211
1923
  }
@@ -2469,12 +2181,6 @@ function runView(argv) {
2469
2181
  rest.push(arg);
2470
2182
  }
2471
2183
  }
2472
- // ESCAPES NEVER GO INTO A FILE. A pinned golden holding terminal
2473
- // control codes is not a golden anybody can read, and a byte
2474
- // comparison against one would fail on the reader's terminal
2475
- // settings. `auto` resolves to `none` there on its own; asking for
2476
- // `ansi` explicitly is a usage error rather than a silent downgrade,
2477
- // so a script that wanted colour is told where it went.
2478
2184
  if ('ansi' === style && (undefined !== out || undefined !== opts.views)) {
2479
2185
  process.stderr.write('aontu: --style ansi writes to a terminal, not to a file\n');
2480
2186
  return 2;
@@ -2482,10 +2188,6 @@ function runView(argv) {
2482
2188
  // THE VIEW DOCUMENT draws every figure a document declares, so it
2483
2189
  // names no kind: the declarations do, one each.
2484
2190
  if (undefined !== opts.views) {
2485
- // A declaration names its own profile, so the style is left to
2486
- // each figure's own default; `--style none` still reaches every
2487
- // one of them, which is how a host page that binds the CSS
2488
- // variables asks for eight figures without eight stylesheets.
2489
2191
  opts.style = viewStyleOf(style, undefined);
2490
2192
  return runViewSet(rest, opts, trust, { format, check, strict, out });
2491
2193
  }
@@ -2553,17 +2255,6 @@ function runView(argv) {
2553
2255
  docs: files.slice(1).map((path, i) => ({ src: srcs[i + 1], path })),
2554
2256
  };
2555
2257
  const report = (0, view_1.view)(srcs[0], viewOpts);
2556
- // AN EMPTY FIGURE IS THE SAME BYTES AS A DRAWN ONE MINUS ITS
2557
- // CONTENT, and every profile spells "empty" differently: text draws
2558
- // nothing at all, mermaid still draws its `flowchart LR` header, the
2559
- // matrix still prints its count line. Rather than teach this one
2560
- // place each of those spellings -- a list that goes stale the first
2561
- // time a profile gains a header -- ASK THE SAME KIND TO DRAW AN
2562
- // EMPTY DOCUMENT and compare. Equal texts mean this document
2563
- // contributed nothing to the figure, whatever the profile.
2564
- //
2565
- // It costs one drawing of `{}`, which is the cheapest document
2566
- // there is, and only on a run that produced a figure at all.
2567
2258
  if ('error' !== report.verdict && null != report.text) {
2568
2259
  const bare = (0, view_1.view)('{}', viewOpts);
2569
2260
  if ('error' !== bare.verdict && bare.text === report.text) {
@@ -2612,13 +2303,6 @@ function runView(argv) {
2612
2303
  }
2613
2304
  return strict && 'lossy' === report.verdict ? 1 : VIEW_EXIT[report.verdict];
2614
2305
  }
2615
- // `aontu view --views <path> <file>`: every figure the document
2616
- // declares, from one evaluation, all or nothing.
2617
- //
2618
- // A declared `out` is resolved against the DOCUMENT's own directory,
2619
- // not the caller's: a view document is committed beside the figures it
2620
- // gates, and a gate that only passes from one working directory is not
2621
- // a gate.
2622
2306
  function runViewSet(rest, opts, trust, how) {
2623
2307
  if (1 !== rest.length) {
2624
2308
  process.stderr.write('aontu: view --views takes one file\n');
@@ -2742,8 +2426,6 @@ function renderViewJson(report) {
2742
2426
  }
2743
2427
  function renderRelationsText(report) {
2744
2428
  const head = `verdict: ${report.verdict}`;
2745
- // WHY, when the document could not be evaluated at all: rendered as
2746
- // vet renders a finding, because it IS one (the review's finding F).
2747
2429
  const errors = report.errors ?? [];
2748
2430
  if (0 < errors.length) {
2749
2431
  return [head, ''].concat(errors.map(renderFinding)).join('\n');
@@ -2765,19 +2447,6 @@ function renderRelationsJson(report) {
2765
2447
  ...(null == report.errors ? {} : { errors: report.errors }),
2766
2448
  }, 2);
2767
2449
  }
2768
- // ---------------------------------------------------------------------
2769
- // JSON SCHEMA EXPORT (SUPPORT.md act 2, the review's finding I): the
2770
- // bridge to every structured-output API, which constrains generation to
2771
- // JSON Schema and nothing else. Export the model, let the provider
2772
- // generate under it, then `vet` the result against the model itself --
2773
- // the hybrid an enterprise actually deploys, and impossible without
2774
- // this verb.
2775
- //
2776
- // THE SCHEMA GOES TO STDOUT AND THE LOSSES TO STDERR, so `aontu
2777
- // jsonschema x.aon > schema.json` writes a schema and still tells the
2778
- // reader what it could not carry. `--strict` makes a loss a refusal,
2779
- // for the CI job that would rather fail than ship a schema weaker than
2780
- // its model.
2781
2450
  const JSONSCHEMA_HELP = 'aontu jsonschema [--at <path>] [--strict] <file> (try --help)';
2782
2451
  function runJsonSchema(argv) {
2783
2452
  const trusted = takeTrust(argv);
@@ -2861,14 +2530,6 @@ function runJsonSchema(argv) {
2861
2530
  return 'error' === report.verdict ? 4 :
2862
2531
  strict && 'lossy' === report.verdict ? 1 : 0;
2863
2532
  }
2864
- // ---------------------------------------------------------------------
2865
- // THE RENDER VERB (docs/design/RENDER.0.md D8): evaluate a document,
2866
- // vet the value at --at against aontu:code, fold code.units into bytes,
2867
- // and put them where the flag says -- one unit on stdout, every unit
2868
- // below --out (all or nothing), or compared against --check. Exit codes
2869
- // mirror jsonschema's: 0 ok; 1 lossy under --strict or drift under
2870
- // --check; 2 usage or I/O, a refused unit path included; 4 the
2871
- // document does not stand up or the instance is not aontu:code.
2872
2533
  const RENDER_HELP = 'aontu render [--at <path>] [--profile <file>]... [--unit <path>] ' +
2873
2534
  '[--stdout | --out <dir> | --check <dir> | --coverage] ' +
2874
2535
  '[--coverage-at <path>] [--strict] [--marker <token>] <file> (try --help)';
@@ -2997,44 +2658,14 @@ function runRender(argv) {
2997
2658
  process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
2998
2659
  return 2;
2999
2660
  }
3000
- // THE ENTRY MAY BE A TEMPLATE (TEMPLATE.0.md; P8), and its EXTENSION
3001
- // decides, as an include's extension decides what the include is
3002
- // (ADR-012): a generator is a file in the target's own syntax, so it
3003
- // carries the target's extension and never `.aon`. Desugared here
3004
- // rather than anywhere deeper, because a template is an entry
3005
- // spelling and not a value: an include is still aontu.
3006
- if (!files[0].endsWith('.aon')) {
3007
- src = (0, template_1.desugarTemplate)(src, marker ?? (0, template_1.markerFor)(files[0]));
2661
+ const loadedProfiles = loadProfiles(profileFiles, trust);
2662
+ if ('number' === typeof loadedProfiles) {
2663
+ return loadedProfiles;
3008
2664
  }
3009
- // THE PROFILES (D5): each --profile file is a document whose root is
3010
- // `profile: {lang, ...}`, evaluated under the verb's trust and vetted
3011
- // against aontu:profile as a settled value before the fold reads it
3012
- // (renderProfile, which also fills the defaults). Two files claiming
3013
- // one lang is a usage error: the fold could not choose.
3014
- const profiles = [];
3015
- const langs = new Map();
3016
- for (const pf of profileFiles) {
3017
- let text;
3018
- try {
3019
- text = (0, node_fs_1.readFileSync)(pf, 'utf8');
3020
- }
3021
- catch (err) {
3022
- process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
3023
- return 2;
3024
- }
3025
- const loaded = (0, aontu_1.renderProfile)(text, { path: (0, node_path_1.resolve)(pf), ...verbOpts(trust, entryRootOf(pf)) });
3026
- if (undefined !== loaded.errors) {
3027
- process.stderr.write(loaded.errors.map(renderFinding).join('\n') + '\n');
3028
- return 4;
3029
- }
3030
- const profile = loaded.profile;
3031
- const prev = langs.get(profile.lang);
3032
- if (undefined !== prev) {
3033
- process.stderr.write(`aontu: two profiles claim ${profile.lang}: ${prev} and ${pf}\n`);
3034
- return 2;
3035
- }
3036
- langs.set(profile.lang, pf);
3037
- profiles.push(profile);
2665
+ const profiles = loadedProfiles;
2666
+ if (!files[0].endsWith('.aon')) {
2667
+ src = (0, template_1.desugarTemplate)(src, marker ??
2668
+ (0, template_1.markerFromProfiles)(profiles, files[0]) ?? (0, template_1.markerFor)(files[0]));
3038
2669
  }
3039
2670
  // A RENDER WITH NO PROFILE PRODUCES NO UNITS, and said so with zero
3040
2671
  // bytes and exit 0. The profile is what maps a model onto a
@@ -3174,18 +2805,19 @@ function renderExit(report, drift) {
3174
2805
  }
3175
2806
  return 0 < drift ? 1 : 0;
3176
2807
  }
3177
- // ---------------------------------------------------------------------
3178
- // THE TEMPLATE SURFACE (docs/design/TEMPLATE.0.md; RENDER.0.md P8): the
3179
- // two transforms and the round trip between them. `render` reads a
3180
- // template directly, by its extension; this verb is for seeing the
3181
- // canonical form, for writing one by hand and sugaring it, and for the
3182
- // check that keeps a committed template and its meaning in agreement.
3183
2808
  const TEMPLATE_HELP = 'aontu template [--resugar] [--check] [--marker <token>] <file> (try --help)';
3184
2809
  function runTemplate(argv) {
2810
+ const trusted = takeTrust(argv);
2811
+ if (null == trusted) {
2812
+ return 2;
2813
+ }
2814
+ argv = trusted.argv;
2815
+ const trust = trusted.trust;
3185
2816
  const files = [];
3186
2817
  let resugar = false;
3187
2818
  let check = false;
3188
2819
  let marker = undefined;
2820
+ const profileFiles = [];
3189
2821
  for (let i = 0; i < argv.length; i++) {
3190
2822
  const arg = argv[i];
3191
2823
  if ('-h' === arg || '--help' === arg) {
@@ -3205,6 +2837,14 @@ function runTemplate(argv) {
3205
2837
  return 2;
3206
2838
  }
3207
2839
  }
2840
+ else if ('--profile' === arg) {
2841
+ const pf = argv[++i];
2842
+ if (null == pf) {
2843
+ process.stderr.write('aontu: --profile needs a file\n');
2844
+ return 2;
2845
+ }
2846
+ profileFiles.push(pf);
2847
+ }
3208
2848
  else if (arg.startsWith('-')) {
3209
2849
  process.stderr.write(`aontu: unknown template option ${arg} (try --help)\n`);
3210
2850
  return 2;
@@ -3217,10 +2857,6 @@ function runTemplate(argv) {
3217
2857
  process.stderr.write(`aontu: template needs one file\n${TEMPLATE_HELP}\n`);
3218
2858
  return 2;
3219
2859
  }
3220
- // THE TWO ARE DIRECTIONS, NOT MODES THAT COMPOSE: `--check` reads a
3221
- // template and asks whether the round trip answers it back, and
3222
- // `--resugar` reads the canonical form instead. A run cannot be both
3223
- // at once, because the file is one thing or the other.
3224
2860
  if (resugar && check) {
3225
2861
  process.stderr.write('aontu: template takes one of --resugar or --check\n');
3226
2862
  return 2;
@@ -3233,18 +2869,13 @@ function runTemplate(argv) {
3233
2869
  process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
3234
2870
  return 2;
3235
2871
  }
3236
- const mark = marker ?? (0, template_1.markerFor)(files[0]);
2872
+ const declared = loadProfiles(profileFiles, trust);
2873
+ if ('number' === typeof declared) {
2874
+ return declared;
2875
+ }
2876
+ const mark = marker ?? (0, template_1.markerFromProfiles)(declared, files[0]) ??
2877
+ (0, template_1.markerFor)(files[0]);
3237
2878
  if (check) {
3238
- // THE ROUND TRIP IS THE CHECK (D6): the file held to the spelling
3239
- // the two transforms answer. What that names is a marker line the
3240
- // transform would not have written -- one without its space, or one
3241
- // whose aontu is indented after the marker rather than before it,
3242
- // since the marker keeps its own indentation. It does NOT name a
3243
- // changed body line: a template's whitespace is output, so a
3244
- // trimmed trailing space is still a valid template and it is
3245
- // `render --check` against the committed files that catches it.
3246
- // The first line that differs is the report, since a whole diff of
3247
- // a generator is the file again.
3248
2879
  const back = (0, template_1.resugarTemplate)((0, template_1.desugarTemplate)(src, mark), mark);
3249
2880
  if (back === src) {
3250
2881
  return 0;
@@ -3255,12 +2886,6 @@ function runTemplate(argv) {
3255
2886
  while (n < want.length && n < have.length && want[n] === have[n]) {
3256
2887
  n++;
3257
2888
  }
3258
- // THE TWO ARE THE SAME LENGTH, always: each transform maps one
3259
- // line to one line and applies the same trailing-newline rule, so
3260
- // `back` has as many lines as `src`. The loop above therefore stops
3261
- // at a real difference rather than by running out of either -- an
3262
- // equal prefix all the way to the end IS `back === src`, which
3263
- // returned above. So both indexes are in range here.
3264
2889
  process.stderr.write(`aontu: ${files[0]}:${n + 1} is not what the round trip answers\n` +
3265
2890
  ` have: ${JSON.stringify(have[n])}\n` +
3266
2891
  ` want: ${JSON.stringify(want[n])}\n`);
@@ -3270,13 +2895,38 @@ function runTemplate(argv) {
3270
2895
  (0, template_1.resugarTemplate)(src, mark) : (0, template_1.desugarTemplate)(src, mark));
3271
2896
  return 0;
3272
2897
  }
3273
- // ---------------------------------------------------------------------
3274
- // The canon-hash (G6 phase 1): the pin an agent, a lockfile or a
3275
- // registry stores for "this module, this meaning". The hash covers the
3276
- // module evaluated STANDALONE -- its own include closure resolved and
3277
- // unified at its own root, before any consumer context -- which is what
3278
- // makes the pin transitive: an edit two includes deep changes the
3279
- // unified root, hence the hash.
2898
+ // The profiles named by --profile, vetted, or the exit code that says
2899
+ // why not. A profile is a language declared as data: `render` matches
2900
+ // one to a unit by `lang`, and `template` and `fmt` match one to a file
2901
+ // by the extensions its `template.ext` names.
2902
+ function loadProfiles(profileFiles, trust) {
2903
+ const profiles = [];
2904
+ const langs = new Map();
2905
+ for (const pf of profileFiles) {
2906
+ let text;
2907
+ try {
2908
+ text = (0, node_fs_1.readFileSync)(pf, 'utf8');
2909
+ }
2910
+ catch (err) {
2911
+ process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
2912
+ return 2;
2913
+ }
2914
+ const loaded = (0, aontu_1.renderProfile)(text, { path: (0, node_path_1.resolve)(pf), ...verbOpts(trust, entryRootOf(pf)) });
2915
+ if (undefined !== loaded.errors) {
2916
+ process.stderr.write(loaded.errors.map(renderFinding).join('\n') + '\n');
2917
+ return 4;
2918
+ }
2919
+ const profile = loaded.profile;
2920
+ const prev = langs.get(profile.lang);
2921
+ if (undefined !== prev) {
2922
+ process.stderr.write(`aontu: two profiles claim ${profile.lang}: ${prev} and ${pf}\n`);
2923
+ return 2;
2924
+ }
2925
+ langs.set(profile.lang, pf);
2926
+ profiles.push(profile);
2927
+ }
2928
+ return profiles;
2929
+ }
3280
2930
  const HASH_HELP = 'aontu hash <file> (try --help)';
3281
2931
  function runHash(argv) {
3282
2932
  const trusted = takeTrust(argv);
@@ -3331,14 +2981,6 @@ function runHash(argv) {
3331
2981
  const ctx = aontu.ctx({ collect: true });
3332
2982
  const v = aontu.unify(src, { path: files[0] }, ctx);
3333
2983
  if (0 < ctx.err.length || true === v?.isNil) {
3334
- // A document that does not stand up on its own has no meaning to
3335
- // pin, and a hash of a broken evaluation would be a pin that
3336
- // silently agrees with every other broken evaluation.
3337
- // WHY it does not stand up, not just that it does not: the same
3338
- // diagnosis `aontu <file>` prints (the review's finding F).
3339
- // evalFailure unconditionally, as every other call site does: it
3340
- // owns the "ctx.err is never empty here" contract, and a guard
3341
- // that pretends otherwise is a dead arm asserting nothing.
3342
2984
  process.stderr.write(`aontu: ${files[0]} does not evaluate on its own; nothing to hash\n` +
3343
2985
  renderFinding((0, query_1.evalFailure)(ctx)) + '\n');
3344
2986
  return 4;
@@ -3353,12 +2995,6 @@ function runHash(argv) {
3353
2995
  process.stdout.write(text + '\n');
3354
2996
  return 0;
3355
2997
  }
3356
- // ---------------------------------------------------------------------
3357
- // The query surface (G7 phase 1): one node of an evaluated document,
3358
- // selected by path and rendered. Evaluation is still GLOBAL -- what
3359
- // `get` buys is the size of the ANSWER, not the cost of producing it --
3360
- // and the projections are lattice abstractions, each a valid Aontu
3361
- // document that subsumes the truth it summarises.
3362
2998
  const GET_HELP = 'aontu get <path> <file> (try --help)';
3363
2999
  function runGet(argv) {
3364
3000
  const trusted = takeTrust(argv);
@@ -3527,11 +3163,6 @@ function runWhy(argv) {
3527
3163
  }
3528
3164
  return 'no_path' === report.findings[0]?.code ? 1 : 4;
3529
3165
  }
3530
- // One contribution per line, numbered in source order, each with what
3531
- // was written, where, and how it got here. A siteless contribution
3532
- // prints no location rather than a `-1:-1` that means nothing —
3533
- // exported for the direct test, because the site SHAPE allows one
3534
- // while no document has yet produced one (ADR-002).
3535
3166
  function renderWhyText(record) {
3536
3167
  const head = `${record.path} = ${record.value}`;
3537
3168
  if (0 === record.conjuncts.length) {
@@ -3547,12 +3178,6 @@ function renderWhyText(record) {
3547
3178
  ('literal' === c.role ? '' : ` (${c.role})`);
3548
3179
  })).join('\n');
3549
3180
  }
3550
- // ---------------------------------------------------------------------
3551
- // The overlay patch verb (G7 phase 5): change a document by APPENDING
3552
- // to an overlay, not by rewriting it. An overlay entry is just another
3553
- // conjunct and unification is order-independent, so this needs no
3554
- // rewriter — the format-preserving in-place edit is stage 2, and needs
3555
- // a comment-preserving CST the parser stack does not have.
3556
3181
  const SET_HELP = 'aontu set <path>=<value> --entry <file> --overlay <file> (try --help)';
3557
3182
  function runSet(argv) {
3558
3183
  const trusted = takeTrust(argv);
@@ -3659,27 +3284,10 @@ function runSet(argv) {
3659
3284
  }, 2) + '\n');
3660
3285
  }
3661
3286
  else {
3662
- // A replacement is REPORTED as the edit it is, not left for the
3663
- // reader to infer from a changed file: `where: what -> what`, in
3664
- // source spelling, because the spelling is what changed.
3665
- //
3666
- // PAST TENSE ONLY WHERE IT HAPPENED. A refused write leaves the
3667
- // file exactly as it was, and one assignment can be replaceable
3668
- // while another makes the whole run invalid — so `replaced:` there
3669
- // tells an operator the pin was changed when it was not, and unlike
3670
- // `--dry-run` there is nothing else on the line to say otherwise.
3671
3287
  const verb = wrote ? 'replaced' : 'would replace';
3672
3288
  const edits = report.replaced.map((r) => `${verb}: ${r.file}:${r.row}:${r.col} ${r.from} -> ${r.to}`);
3673
3289
  const head = [`verdict: ${report.verdict}`].concat(edits).join('\n') +
3674
3290
  (wrote ? `\nwrote: ${overlayFile}` : dryRun ? '\n(dry run)' : '');
3675
- // A SUCCESSFUL COMMAND WRITES ITS STATUS TO STDOUT, findings or
3676
- // not. Routing on `findings.length` was right while every finding
3677
- // this verb could produce was an ERROR; `--in-place` made a WARNING
3678
- // possible, and a run that held, wrote the file and exited 0 then
3679
- // sent its whole report to stderr — leaving stdout empty, so
3680
- // `$(aontu set ...)` captured nothing and only the JSON form
3681
- // behaved like a success. The verdict decides the stream; warnings
3682
- // are diagnostics and go to stderr beside it.
3683
3291
  const failed = 'invalid' === report.verdict || 'error' === report.verdict;
3684
3292
  const findingText = report.findings.map(renderFinding);
3685
3293
  if (failed) {
@@ -3698,14 +3306,6 @@ function runSet(argv) {
3698
3306
  }
3699
3307
  return VET_EXIT[report.verdict];
3700
3308
  }
3701
- // ---------------------------------------------------------------------
3702
- // The role gate (docs/design/ALLOW.0.md): may the role the caller is
3703
- // operating under modify these subtrees? Asked before `set`, by an
3704
- // agent whose skill names its role, and answered from a role model
3705
- // that is itself an aontu document. The verdict is the exit code, as
3706
- // it is for every gate here: 0 is yes, 1 is no, 4 is "the model that
3707
- // was to decide does not stand up", and an agent branches on nothing
3708
- // else.
3709
3309
  const ALLOW_HELP = 'aontu allow --role <role> <roles-file> <path> [more-paths...] (try --help)';
3710
3310
  const ALLOW_EXIT = {
3711
3311
  allowed: 0,
@@ -3808,14 +3408,6 @@ function runAllow(argv) {
3808
3408
  process.stderr.write('aontu: --role needs one key, without dots\n');
3809
3409
  return 2;
3810
3410
  }
3811
- // A path may arrive in `set`'s spelling, `$.a.b=1`, so a skill can
3812
- // hand the gate the very arguments the write will get. The text up
3813
- // to the first `=` is the path, and it starts with `$`: an empty
3814
- // argument, or a second file name, would otherwise read as a path
3815
- // and be answered. The VALUE is checked to be one value. `set`
3816
- // appends it as source after the flattened path, so a value carrying
3817
- // a second pair -- `3 secrets: key: "x"` -- writes a sibling of the
3818
- // overlay root, a subtree the gate was never asked about.
3819
3411
  const paths = [];
3820
3412
  for (const arg of asked) {
3821
3413
  const eq = arg.indexOf('=');
@@ -3949,28 +3541,17 @@ function runAgentsMd(argv) {
3949
3541
  process.stdout.write(`wrote: ${write}\n`);
3950
3542
  return 0;
3951
3543
  }
3952
- // Exit without truncating output.
3953
- //
3954
- // process.exit() terminates immediately, discarding anything still
3955
- // queued on stdout. A write to a PIPE is asynchronous once it exceeds
3956
- // the pipe buffer, so `write(big); exit(0)` silently truncated output at
3957
- // 65536 bytes — while a write to a TTY or a file, being synchronous,
3958
- // looked fine. Setting exitCode instead lets the process end naturally,
3959
- // after the queue drains.
3960
- //
3961
- // This predates the exact leaves but they make it trivially reachable
3962
- // (one long biginteger canon exceeds the buffer), and it lands squarely
3963
- // on the parity-probe discipline in AGENTS.md, which derives expected
3964
- // spec values by piping BOTH CLIs and comparing. A truncated pipe there
3965
- // reads as a port divergence.
3966
- // ---------------------------------------------------------------------
3967
- // The source formatter (docs/design/FMT.0.md): one agreed form, in the
3968
- // tradition of gofmt. The verb prints, lists, checks, diffs or rewrites;
3969
- // the form itself is the library's (ts/src/format.ts), and the two
3970
- // ports agree on it row by row in test/spec/fmt.tsv.
3971
- const FMT_HELP = 'aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>] <file>... (try --help)';
3544
+ const FMT_HELP = 'aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>] ' +
3545
+ '[--profile <file>] <file>... (try --help)';
3972
3546
  function runFmt(argv) {
3547
+ const trusted = takeTrust(argv);
3548
+ if (null == trusted) {
3549
+ return 2;
3550
+ }
3551
+ argv = trusted.argv;
3552
+ const trust = trusted.trust;
3973
3553
  const files = [];
3554
+ const profileFiles = [];
3974
3555
  let marker = undefined;
3975
3556
  const flags = {
3976
3557
  write: false, list: false, check: false, diff: false, lint: false, strict: false,
@@ -4010,6 +3591,14 @@ function runFmt(argv) {
4010
3591
  return 2;
4011
3592
  }
4012
3593
  }
3594
+ else if ('--profile' === arg) {
3595
+ const pf = argv[++i];
3596
+ if (null == pf) {
3597
+ process.stderr.write('aontu: --profile needs a file\n');
3598
+ return 2;
3599
+ }
3600
+ profileFiles.push(pf);
3601
+ }
4013
3602
  else if (arg.startsWith('-')) {
4014
3603
  process.stderr.write(`aontu: unknown fmt option ${arg} (try --help)\n`);
4015
3604
  return 2;
@@ -4033,6 +3622,10 @@ function runFmt(argv) {
4033
3622
  process.stdin.on('end', () => resolve(fmtOne('<stdin>', src, flags, marker)));
4034
3623
  });
4035
3624
  }
3625
+ const declared = loadProfiles(profileFiles, trust);
3626
+ if ('number' === typeof declared) {
3627
+ return declared;
3628
+ }
4036
3629
  // Several files onto standard output would be one stream nobody can
4037
3630
  // split again (the note's X-6): the verb refuses unless an option
4038
3631
  // says what to do with each.
@@ -4051,32 +3644,18 @@ function runFmt(argv) {
4051
3644
  process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`);
4052
3645
  return 2;
4053
3646
  }
4054
- const mark = fmtMarker(file, src, marker);
3647
+ const mark = fmtMarker(file, src, marker ?? (0, template_1.markerFromProfiles)(declared, file));
4055
3648
  if (false === mark) {
4056
3649
  process.stderr.write(`aontu: ${file} is not aontu source (.aon, .aontu) and carries no ` +
4057
3650
  `${(0, template_1.markerFor)(file)} marker line, so there is no aontu in it to ` +
4058
- 'format; --marker names the marker for a language the table does ' +
4059
- 'not know\n');
3651
+ 'format; --marker names the marker for a language the table ' +
3652
+ 'does not know, and --profile reads one that declares it\n');
4060
3653
  return 2;
4061
3654
  }
4062
3655
  worst = Math.max(worst, fmtOne(file, src, flags, mark));
4063
3656
  }
4064
3657
  return worst;
4065
3658
  }
4066
- // WHAT A FILE IS, BY ITS EXTENSION (ADR-012's rule, and the one
4067
- // `render` reads an entry by): `.aon` and `.aontu` are aontu source,
4068
- // and anything else is a GENERATOR written in the target's own syntax
4069
- // (docs/design/TEMPLATE.0.md), whose marker lines carry the document
4070
- // this formats and whose other lines are output. `undefined` is aontu,
4071
- // a string is the generator's marker, and `false` is neither.
4072
- //
4073
- // A FILE WITH NO MARKER LINE IN IT IS NEITHER, and that is what keeps
4074
- // FMT.0.md §9's boundary where it stood: a `.json`, `.yaml` or `.toml`
4075
- // include is another language's file, and reading one as a generator
4076
- // would answer it back unchanged having understood none of it. The
4077
- // marker is the evidence that a file was written to carry aontu at
4078
- // all. `--marker` says so outright, and then the file is a generator
4079
- // whatever it is called.
4080
3659
  function fmtMarker(file, src, marker) {
4081
3660
  if (undefined !== marker) {
4082
3661
  return marker;
@@ -4132,11 +3711,8 @@ function fmtOne(name, src, flags, marker) {
4132
3711
  }
4133
3712
  return flags.check ? 1 : strict;
4134
3713
  }
4135
- // The real pair takes the process's own stdin and stdout, which no
4136
- // in-process test can lend it; the executable-entry tests in
4137
- // cli.test.ts run each through a child process instead, so these two
4138
- // lines are excluded from the in-process count, as the stdio wiring
4139
- // of lsp-server.ts is.
3714
+ // Excluded: the real pair takes the process stdio, so ts/test/cli.test.ts
3715
+ // drives each server through a child process instead.
4140
3716
  /* node:coverage ignore next 4 */
4141
3717
  const SERVERS = {
4142
3718
  lsp: () => void (0, lsp_server_1.main)(),
@@ -4176,19 +3752,6 @@ function parseTrustArg(value) {
4176
3752
  }
4177
3753
  return undefined;
4178
3754
  }
4179
- // THE TEACHING PACK, SERVED FROM THE COMMAND (G11 phase 1,
4180
- // docs/capability-review/g11-agent-onramp.md; mirrors
4181
- // go/cmd/aontu/help.go).
4182
- //
4183
- // HELP documents the TOOLCHAIN and says nothing about the LANGUAGE:
4184
- // `&`, the map template and the one construct an ontology cannot be
4185
- // written without, occurs zero times in it, while `template` occurs
4186
- // fourteen times and names an unrelated verb every time. docs/skill/
4187
- // was already the right content and already gated; the gap was
4188
- // DELIVERY, since it reached an installation as
4189
- // node_modules/aontu/skill/ where nothing looks. ts/src/helpdoc.ts is
4190
- // generated from those sources by ts/scripts/helpdoc.cjs and asserted
4191
- // byte-identical with them by ts/test/helpdoc.test.ts.
4192
3755
  const HELP_VERB_HELP = 'aontu help [topic] (try `aontu help` for the topics)';
4193
3756
  const EXPLAIN_HELP = 'aontu explain <code> (try `aontu explain --list`)';
4194
3757
  function helpIndexText(index) {
@@ -4260,22 +3823,6 @@ function runHelp(argv) {
4260
3823
  `aontu: topics are ${helpdoc_1.HELPDOC.map((t) => t.topic).join(', ')}\n`);
4261
3824
  return 2;
4262
3825
  }
4263
- // `aontu explain <code>` (G11 phase 3; mirrors
4264
- // go/cmd/aontu/explain.go).
4265
- //
4266
- // THE REGISTRY IS THE LIST, NOT THE HINT TABLE. test/spec/errcodes.tsv
4267
- // registers 157 codes and the spec suite asserts set equality between
4268
- // the file and codeClasses IN BOTH PORTS, so listing from codeClasses
4269
- // is listing the shared contract. The hint tables are smaller and are
4270
- // NOT in parity -- 130 entries here against 131 in Go, the extra being
4271
- // decimal_syntax, which this port never raises -- so listing from them
4272
- // would make `aontu explain --list` differ between ports over a
4273
- // difference that is not about what either port can report.
4274
- //
4275
- // A REGISTERED CODE WITH NO HINT ANSWERS WITH ITS CLASS AND SAYS SO.
4276
- // Twenty-seven registered codes carry no explanation text here; before
4277
- // this verb their absence was invisible, because a hint is only ever
4278
- // seen beside the error that raises it.
4279
3826
  // The dynamic prefixes a generated code extends (`func:upper`,
4280
3827
  // `op[+]`). Mirrors CODE_PREFIXES in ts/src/hints.ts, which is not
4281
3828
  // exported; a code that extends one is registered through its prefix
@@ -4399,20 +3946,6 @@ function runExplain(argv) {
4399
3946
  process.stdout.write(`code: ${code}\nclass: ${cls}\n\n${body}\n`);
4400
3947
  return 0;
4401
3948
  }
4402
- // `aontu init` (G11 phase 6,
4403
- // docs/capability-review/g11-agent-onramp.md).
4404
- //
4405
- // NOT SCAFFOLDING CONVENIENCE. The agent's most expensive failure is
4406
- // writing a FIRST document at all: the measurement that opened G11
4407
- // found one reaching for the wildcard its neighbours use and getting
4408
- // `verdict: valid` over data that violates it. A known-good starting
4409
- // document turns generation into editing, which is the operation a
4410
- // model is reliably good at.
4411
- //
4412
- // The trio is real, runnable and tested where it lives
4413
- // (docs/skill/init/, run by ts/test/helpdoc.test.ts), and staged into
4414
- // both ports by the same generator that stages the teaching pack, so
4415
- // the two write the same bytes.
4416
3949
  const INIT_HELP = 'aontu init [dir] (try --help)';
4417
3950
  function runInit(argv) {
4418
3951
  const dirs = [];
@@ -4432,10 +3965,6 @@ function runInit(argv) {
4432
3965
  return 2;
4433
3966
  }
4434
3967
  const dir = dirs[0] ?? '.';
4435
- // REFUSES TO OVERWRITE, and checks every member BEFORE writing any of
4436
- // them: a scaffold that wrote two files and then refused the third
4437
- // would leave a directory in a state neither the caller nor a re-run
4438
- // can reason about.
4439
3968
  const standing = helpdoc_1.INITDOC.filter((f) => (0, node_fs_1.existsSync)((0, node_path_1.join)(dir, f.name)));
4440
3969
  if (0 < standing.length) {
4441
3970
  process.stderr.write(`aontu: ${dir} already holds ${standing.map((f) => f.name).join(', ')}\n` +
@@ -4459,12 +3988,6 @@ function runInit(argv) {
4459
3988
  'Learn the language: aontu help language\n');
4460
3989
  return 0;
4461
3990
  }
4462
- // EVERY VERB THIS PORT DISPATCHES, for the nearest-verb suggestion
4463
- // G11 phase 2 prints. A separate list from the if-chain in main()
4464
- // because the chain's arms have three different shapes and cannot be
4465
- // a table; ts/test/cli-help.test.ts keeps the two from drifting by
4466
- // running each name and requiring it not to fall through to the bare
4467
- // command.
4468
3991
  const KNOWN_VERBS = [
4469
3992
  'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
4470
3993
  'help', 'init', 'jsonschema', 'lsp', 'mcp', 'mod', 'reaches',
@@ -4481,13 +4004,6 @@ function looksLikeVerb(arg) {
4481
4004
  !/[/\\.]/.test(arg) &&
4482
4005
  !arg.startsWith('-');
4483
4006
  }
4484
- // NEAREST-VERB SUGGESTION (G11 phase 2). Restricted
4485
- // Damerau-Levenshtein with a cap that grows with the word and stops at
4486
- // three: one edit is a convincing suggestion on any length, three is
4487
- // the most that can be believed on a long one, and an UNCAPPED
4488
- // nearest match on a three-letter typo names something unrelated with
4489
- // confidence. Mirrors go/cmd/aontu/help.go, including the sort, so
4490
- // the two ports suggest the same verb on a tie.
4491
4007
  function nearestVerb(word, verbs) {
4492
4008
  let best = '';
4493
4009
  let bestDist = Infinity;
@@ -4501,8 +4017,6 @@ function nearestVerb(word, verbs) {
4501
4017
  }
4502
4018
  return bestDist > limit ? '' : best;
4503
4019
  }
4504
- // Levenshtein with a transposition, iterative over two rows. Mirrors
4505
- // editDistance in go/cmd/aontu/help.go exactly.
4506
4020
  function editDistance(a, b) {
4507
4021
  const ar = [...a];
4508
4022
  const br = [...b];
@@ -4527,25 +4041,12 @@ function editDistance(a, b) {
4527
4041
  return prev[br.length];
4528
4042
  }
4529
4043
  function main(argv, servers = SERVERS) {
4530
- // COLOUR OFF WHEN THE DESTINATION IS NOT A TERMINAL. Error frames
4531
- // hardcoded their ANSI escapes, so a piped report and a `--jsonl`
4532
- // answer carried terminal control codes into whatever read them (the
4533
- // review's finding F). `NO_COLOR` is honoured by the library itself;
4534
- // only the CLI can see whether its stderr is a terminal, so only the
4535
- // CLI can make this call. `undefined` means "leave it to NO_COLOR".
4536
4044
  (0, aontu_1.setColor)(true === process.stderr.isTTY ? undefined : false);
4537
4045
  let mode = 'json';
4538
4046
  // THE REPORT FORM (G11 phase 7), default text: every existing caller
4539
4047
  // reads exactly what it always read, and a caller that asks for json
4540
4048
  // gets the failure in the finding shape every other verb reports.
4541
4049
  let format = 'text';
4542
- // A LIST, though the bare command evaluates exactly one document.
4543
- // It used to be one variable and the last argument won, which made a
4544
- // MISTYPED VERB a silent success: `aontu vet2 schema.aon good.json`
4545
- // printed good.json and exited 0, because `vet2` matched no
4546
- // subcommand, fell through to this loop as a file name, and was
4547
- // overwritten twice. In a tool loop that reads as a passing
4548
- // validation. Counting them is what lets the refusal below happen.
4549
4050
  const files = [];
4550
4051
  let trust = { kind: 'system-warn', textExt: [] };
4551
4052
  let textExt = [];
@@ -4554,15 +4055,6 @@ function main(argv, servers = SERVERS) {
4554
4055
  // than the design's --json, which would read as the `:json` output
4555
4056
  // mode the REPL already has.
4556
4057
  let jsonl = false;
4557
- // Subcommand dispatch, and deliberately only for a FIRST argument:
4558
- // `aontu vet` is the verb, while `aontu somefile vet` keeps meaning
4559
- // what it always did. A file named `vet` is still reachable as
4560
- // `aontu ./vet`.
4561
- //
4562
- // Promise.resolve either way: a non-watch run returns its exit class
4563
- // synchronously (and has already written its report), while `--watch`
4564
- // resolves only when the watch ends — so one await-shaped line serves
4565
- // both without a branch to keep covered.
4566
4058
  if ('vet' === argv[2]) {
4567
4059
  return void Promise.resolve(runVet(argv.slice(3))).then(finish);
4568
4060
  }
@@ -4699,23 +4191,12 @@ function main(argv, servers = SERVERS) {
4699
4191
  files.push(arg);
4700
4192
  }
4701
4193
  }
4702
- // ONE DOCUMENT. The bare form has always been `aontu [options]
4703
- // [file]`, singular, and anything past the first was silently
4704
- // discarded rather than refused -- so every way of getting the verb
4705
- // wrong (a typo, a verb this port does not have, a verb spelled for
4706
- // another tool) ended in a plausible answer about the wrong file.
4707
- // Exit 2, the usage class, and the message names the cause rather
4708
- // than the symptom: nothing here can tell a mistyped verb from a
4709
- // second file, but the reader can.
4710
4194
  if (1 < files.length) {
4711
4195
  process.stderr.write(`aontu: the bare command evaluates one document, and ${files.length}` +
4712
4196
  ' were given\naontu: a mistyped verb reads as a file name' +
4713
4197
  ' (try --help)\n');
4714
4198
  return finish(2);
4715
4199
  }
4716
- // The extensions ride with the capability from here on, so the three
4717
- // entry shapes below (file, REPL, stdin) each get them by threading
4718
- // the one value they already thread.
4719
4200
  trust = { ...trust, textExt };
4720
4201
  const file = files[0];
4721
4202
  if (null != file) {