aontu 0.52.1 → 0.54.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 (310) hide show
  1. package/README.md +88 -0
  2. package/bin/aontu-mcp.js +4 -0
  3. package/dist/agentsmd.d.ts +16 -0
  4. package/dist/agentsmd.js +107 -0
  5. package/dist/agentsmd.js.map +1 -0
  6. package/dist/aontu.d.ts +15 -3
  7. package/dist/aontu.js +126 -5
  8. package/dist/aontu.js.map +1 -1
  9. package/dist/cli.d.ts +45 -1
  10. package/dist/cli.js +2845 -69
  11. package/dist/cli.js.map +1 -1
  12. package/dist/ctx.d.ts +18 -0
  13. package/dist/ctx.js +44 -0
  14. package/dist/ctx.js.map +1 -1
  15. package/dist/diff.d.ts +22 -0
  16. package/dist/diff.js +141 -0
  17. package/dist/diff.js.map +1 -0
  18. package/dist/err.d.ts +3 -1
  19. package/dist/err.js +45 -8
  20. package/dist/err.js.map +1 -1
  21. package/dist/graph.d.ts +13 -0
  22. package/dist/graph.js +110 -0
  23. package/dist/graph.js.map +1 -0
  24. package/dist/hcanon.d.ts +3 -0
  25. package/dist/hcanon.js +145 -0
  26. package/dist/hcanon.js.map +1 -0
  27. package/dist/hints.js +235 -8
  28. package/dist/hints.js.map +1 -1
  29. package/dist/jsonschema.d.ts +20 -0
  30. package/dist/jsonschema.js +425 -0
  31. package/dist/jsonschema.js.map +1 -0
  32. package/dist/lang.js +955 -110
  33. package/dist/lang.js.map +1 -1
  34. package/dist/lsp.d.ts +9 -2
  35. package/dist/lsp.js +343 -48
  36. package/dist/lsp.js.map +1 -1
  37. package/dist/mcp-server.d.ts +20 -0
  38. package/dist/mcp-server.js +147 -0
  39. package/dist/mcp-server.js.map +1 -0
  40. package/dist/mcp.d.ts +42 -0
  41. package/dist/mcp.js +941 -0
  42. package/dist/mcp.js.map +1 -0
  43. package/dist/mod-tool.d.ts +58 -0
  44. package/dist/mod-tool.js +532 -0
  45. package/dist/mod-tool.js.map +1 -0
  46. package/dist/mod.d.ts +35 -0
  47. package/dist/mod.js +345 -0
  48. package/dist/mod.js.map +1 -0
  49. package/dist/patch.d.ts +49 -0
  50. package/dist/patch.js +506 -0
  51. package/dist/patch.js.map +1 -0
  52. package/dist/provenance.d.ts +41 -0
  53. package/dist/provenance.js +336 -0
  54. package/dist/provenance.js.map +1 -0
  55. package/dist/query.d.ts +27 -0
  56. package/dist/query.js +294 -0
  57. package/dist/query.js.map +1 -0
  58. package/dist/reach.d.ts +15 -0
  59. package/dist/reach.js +168 -0
  60. package/dist/reach.js.map +1 -0
  61. package/dist/relation.d.ts +23 -0
  62. package/dist/relation.js +230 -0
  63. package/dist/relation.js.map +1 -0
  64. package/dist/report-sarif.d.ts +14 -0
  65. package/dist/report-sarif.js +102 -0
  66. package/dist/report-sarif.js.map +1 -0
  67. package/dist/sig.d.ts +25 -0
  68. package/dist/sig.js +277 -0
  69. package/dist/sig.js.map +1 -0
  70. package/dist/sigdecl.d.ts +2 -0
  71. package/dist/sigdecl.js +11 -0
  72. package/dist/sigdecl.js.map +1 -0
  73. package/dist/siggate.d.ts +4 -0
  74. package/dist/siggate.js +90 -0
  75. package/dist/siggate.js.map +1 -0
  76. package/dist/site.d.ts +4 -0
  77. package/dist/site.js +31 -0
  78. package/dist/site.js.map +1 -1
  79. package/dist/std.d.ts +1 -0
  80. package/dist/std.js +129 -0
  81. package/dist/std.js.map +1 -0
  82. package/dist/subsume.d.ts +39 -0
  83. package/dist/subsume.js +573 -0
  84. package/dist/subsume.js.map +1 -0
  85. package/dist/trim.d.ts +19 -0
  86. package/dist/trim.js +155 -0
  87. package/dist/trim.js.map +1 -0
  88. package/dist/tsconfig.tsbuildinfo +1 -1
  89. package/dist/type.d.ts +17 -1
  90. package/dist/type.js.map +1 -1
  91. package/dist/unify.d.ts +2 -1
  92. package/dist/unify.js +234 -38
  93. package/dist/unify.js.map +1 -1
  94. package/dist/utility.d.ts +8 -1
  95. package/dist/utility.js +68 -1
  96. package/dist/utility.js.map +1 -1
  97. package/dist/val/AggFuncVal.d.ts +44 -0
  98. package/dist/val/AggFuncVal.js +364 -0
  99. package/dist/val/AggFuncVal.js.map +1 -0
  100. package/dist/val/ArithFuncVal.d.ts +31 -0
  101. package/dist/val/ArithFuncVal.js +62 -0
  102. package/dist/val/ArithFuncVal.js.map +1 -0
  103. package/dist/val/BagVal.d.ts +7 -0
  104. package/dist/val/BagVal.js +150 -11
  105. package/dist/val/BagVal.js.map +1 -1
  106. package/dist/val/CloseFuncVal.js +9 -1
  107. package/dist/val/CloseFuncVal.js.map +1 -1
  108. package/dist/val/ConjunctVal.d.ts +1 -1
  109. package/dist/val/ConjunctVal.js +19 -0
  110. package/dist/val/ConjunctVal.js.map +1 -1
  111. package/dist/val/ConstraintVal.d.ts +7 -1
  112. package/dist/val/ConstraintVal.js +523 -45
  113. package/dist/val/ConstraintVal.js.map +1 -1
  114. package/dist/val/ContainerKindVal.d.ts +35 -0
  115. package/dist/val/ContainerKindVal.js +99 -0
  116. package/dist/val/ContainerKindVal.js.map +1 -0
  117. package/dist/val/CopyFuncVal.d.ts +1 -2
  118. package/dist/val/CopyFuncVal.js.map +1 -1
  119. package/dist/val/Decimal.d.ts +1 -0
  120. package/dist/val/Decimal.js +13 -0
  121. package/dist/val/Decimal.js.map +1 -1
  122. package/dist/val/DeprecateFuncVal.d.ts +11 -0
  123. package/dist/val/DeprecateFuncVal.js +47 -0
  124. package/dist/val/DeprecateFuncVal.js.map +1 -0
  125. package/dist/val/DisjunctVal.d.ts +1 -2
  126. package/dist/val/DisjunctVal.js +269 -53
  127. package/dist/val/DisjunctVal.js.map +1 -1
  128. package/dist/val/EachFuncVal.d.ts +15 -0
  129. package/dist/val/EachFuncVal.js +75 -0
  130. package/dist/val/EachFuncVal.js.map +1 -0
  131. package/dist/val/ExpectVal.js +62 -4
  132. package/dist/val/ExpectVal.js.map +1 -1
  133. package/dist/val/FeatureVal.js +1 -1
  134. package/dist/val/FeatureVal.js.map +1 -1
  135. package/dist/val/FilterFuncVal.d.ts +15 -0
  136. package/dist/val/FilterFuncVal.js +91 -0
  137. package/dist/val/FilterFuncVal.js.map +1 -0
  138. package/dist/val/FuncBaseVal.d.ts +7 -1
  139. package/dist/val/FuncBaseVal.js +183 -2
  140. package/dist/val/FuncBaseVal.js.map +1 -1
  141. package/dist/val/GraphAtomVal.d.ts +39 -0
  142. package/dist/val/GraphAtomVal.js +184 -0
  143. package/dist/val/GraphAtomVal.js.map +1 -0
  144. package/dist/val/HideFuncVal.js.map +1 -1
  145. package/dist/val/JunctionVal.js +24 -1
  146. package/dist/val/JunctionVal.js.map +1 -1
  147. package/dist/val/KeyFuncVal.d.ts +1 -1
  148. package/dist/val/KeyFuncVal.js +38 -30
  149. package/dist/val/KeyFuncVal.js.map +1 -1
  150. package/dist/val/ListVal.js +94 -14
  151. package/dist/val/ListVal.js.map +1 -1
  152. package/dist/val/LowerFuncVal.js.map +1 -1
  153. package/dist/val/MapVal.d.ts +1 -0
  154. package/dist/val/MapVal.js +146 -14
  155. package/dist/val/MapVal.js.map +1 -1
  156. package/dist/val/MatchFuncVal.d.ts +15 -0
  157. package/dist/val/MatchFuncVal.js +107 -0
  158. package/dist/val/MatchFuncVal.js.map +1 -0
  159. package/dist/val/MoveFuncVal.js.map +1 -1
  160. package/dist/val/NilVal.js +24 -0
  161. package/dist/val/NilVal.js.map +1 -1
  162. package/dist/val/OpBaseVal.d.ts +1 -1
  163. package/dist/val/OpBaseVal.js +19 -1
  164. package/dist/val/OpBaseVal.js.map +1 -1
  165. package/dist/val/OpenFuncVal.js +4 -1
  166. package/dist/val/OpenFuncVal.js.map +1 -1
  167. package/dist/val/PackFuncVal.d.ts +15 -0
  168. package/dist/val/PackFuncVal.js +108 -0
  169. package/dist/val/PackFuncVal.js.map +1 -0
  170. package/dist/val/PathFuncVal.d.ts +2 -2
  171. package/dist/val/PathFuncVal.js +75 -16
  172. package/dist/val/PathFuncVal.js.map +1 -1
  173. package/dist/val/PathVal.d.ts +25 -0
  174. package/dist/val/PathVal.js +150 -0
  175. package/dist/val/PathVal.js.map +1 -0
  176. package/dist/val/PlaceVal.d.ts +13 -0
  177. package/dist/val/PlaceVal.js +131 -0
  178. package/dist/val/PlaceVal.js.map +1 -0
  179. package/dist/val/PlusOpVal.d.ts +2 -1
  180. package/dist/val/PlusOpVal.js +61 -37
  181. package/dist/val/PlusOpVal.js.map +1 -1
  182. package/dist/val/PrefFuncVal.js.map +1 -1
  183. package/dist/val/PrefVal.d.ts +4 -2
  184. package/dist/val/PrefVal.js +223 -41
  185. package/dist/val/PrefVal.js.map +1 -1
  186. package/dist/val/RecurseVal.d.ts +19 -0
  187. package/dist/val/RecurseVal.js +217 -0
  188. package/dist/val/RecurseVal.js.map +1 -0
  189. package/dist/val/RefVal.d.ts +3 -2
  190. package/dist/val/RefVal.js +181 -66
  191. package/dist/val/RefVal.js.map +1 -1
  192. package/dist/val/ReferFuncVal.d.ts +59 -0
  193. package/dist/val/ReferFuncVal.js +604 -0
  194. package/dist/val/ReferFuncVal.js.map +1 -0
  195. package/dist/val/ScalarKindVal.d.ts +4 -3
  196. package/dist/val/ScalarKindVal.js +12 -12
  197. package/dist/val/ScalarKindVal.js.map +1 -1
  198. package/dist/val/SuperFuncVal.d.ts +4 -2
  199. package/dist/val/SuperFuncVal.js +118 -14
  200. package/dist/val/SuperFuncVal.js.map +1 -1
  201. package/dist/val/TopVal.d.ts +1 -1
  202. package/dist/val/TopVal.js.map +1 -1
  203. package/dist/val/TypeFuncVal.js.map +1 -1
  204. package/dist/val/UpperFuncVal.js.map +1 -1
  205. package/dist/val/Val.d.ts +7 -1
  206. package/dist/val/Val.js +170 -2
  207. package/dist/val/Val.js.map +1 -1
  208. package/dist/val/VarVal.js.map +1 -1
  209. package/dist/val/arith.d.ts +6 -0
  210. package/dist/val/arith.js +173 -0
  211. package/dist/val/arith.js.map +1 -0
  212. package/dist/vet.d.ts +45 -0
  213. package/dist/vet.js +802 -0
  214. package/dist/vet.js.map +1 -0
  215. package/dist/view.d.ts +85 -0
  216. package/dist/view.js +1948 -0
  217. package/dist/view.js.map +1 -0
  218. package/dist/walk.d.ts +2 -0
  219. package/dist/walk.js +91 -0
  220. package/dist/walk.js.map +1 -0
  221. package/grammar/aontu.gbnf +138 -0
  222. package/grammar/aontu.lark +118 -0
  223. package/grammar/aontu.tmLanguage.json +184 -0
  224. package/package.json +30 -8
  225. package/skill/SKILL.md +37 -0
  226. package/skill/error-codes.md +62 -0
  227. package/skill/examples.md +99 -0
  228. package/skill/grammar-card.md +56 -0
  229. package/src/agentsmd.ts +135 -0
  230. package/src/aontu.ts +166 -5
  231. package/src/cli.ts +3324 -72
  232. package/src/ctx.ts +93 -1
  233. package/src/diff.ts +198 -0
  234. package/src/err.ts +50 -8
  235. package/src/graph.ts +185 -0
  236. package/src/hcanon.ts +167 -0
  237. package/src/hints.ts +291 -8
  238. package/src/jsonschema.ts +552 -0
  239. package/src/lang.ts +1049 -110
  240. package/src/lsp.ts +351 -49
  241. package/src/mcp-server.ts +187 -0
  242. package/src/mcp.ts +1130 -0
  243. package/src/mod-tool.ts +718 -0
  244. package/src/mod.ts +453 -0
  245. package/src/patch.ts +628 -0
  246. package/src/provenance.ts +437 -0
  247. package/src/query.ts +381 -0
  248. package/src/reach.ts +213 -0
  249. package/src/relation.ts +298 -0
  250. package/src/report-sarif.ts +137 -0
  251. package/src/sig.ts +345 -0
  252. package/src/sigdecl.ts +11 -0
  253. package/src/siggate.ts +144 -0
  254. package/src/site.ts +36 -1
  255. package/src/std.ts +131 -0
  256. package/src/subsume.ts +739 -0
  257. package/src/trim.ts +195 -0
  258. package/src/tsconfig.json +10 -4
  259. package/src/type.ts +51 -2
  260. package/src/unify.ts +253 -38
  261. package/src/utility.ts +79 -1
  262. package/src/val/AggFuncVal.ts +546 -0
  263. package/src/val/ArithFuncVal.ts +108 -0
  264. package/src/val/BagVal.ts +158 -12
  265. package/src/val/CloseFuncVal.ts +9 -1
  266. package/src/val/ConjunctVal.ts +20 -0
  267. package/src/val/ConstraintVal.ts +576 -48
  268. package/src/val/ContainerKindVal.ts +158 -0
  269. package/src/val/CopyFuncVal.ts +0 -1
  270. package/src/val/Decimal.ts +15 -0
  271. package/src/val/DeprecateFuncVal.ts +84 -0
  272. package/src/val/DisjunctVal.ts +282 -59
  273. package/src/val/EachFuncVal.ts +133 -0
  274. package/src/val/ExpectVal.ts +65 -8
  275. package/src/val/FeatureVal.ts +1 -1
  276. package/src/val/FilterFuncVal.ts +154 -0
  277. package/src/val/FuncBaseVal.ts +206 -2
  278. package/src/val/GraphAtomVal.ts +264 -0
  279. package/src/val/HideFuncVal.ts +0 -2
  280. package/src/val/JunctionVal.ts +24 -1
  281. package/src/val/KeyFuncVal.ts +39 -35
  282. package/src/val/ListVal.ts +101 -15
  283. package/src/val/LowerFuncVal.ts +0 -1
  284. package/src/val/MapVal.ts +153 -14
  285. package/src/val/MatchFuncVal.ts +176 -0
  286. package/src/val/MoveFuncVal.ts +0 -2
  287. package/src/val/NilVal.ts +25 -0
  288. package/src/val/OpBaseVal.ts +20 -1
  289. package/src/val/OpenFuncVal.ts +4 -2
  290. package/src/val/PackFuncVal.ts +175 -0
  291. package/src/val/PathFuncVal.ts +107 -20
  292. package/src/val/PathVal.ts +221 -0
  293. package/src/val/PlaceVal.ts +193 -0
  294. package/src/val/PlusOpVal.ts +67 -39
  295. package/src/val/PrefFuncVal.ts +0 -1
  296. package/src/val/PrefVal.ts +251 -60
  297. package/src/val/RecurseVal.ts +285 -0
  298. package/src/val/RefVal.ts +183 -78
  299. package/src/val/ReferFuncVal.ts +732 -0
  300. package/src/val/ScalarKindVal.ts +12 -13
  301. package/src/val/SuperFuncVal.ts +137 -13
  302. package/src/val/TopVal.ts +1 -2
  303. package/src/val/TypeFuncVal.ts +0 -2
  304. package/src/val/UpperFuncVal.ts +0 -1
  305. package/src/val/Val.ts +216 -4
  306. package/src/val/VarVal.ts +0 -1
  307. package/src/val/arith.ts +319 -0
  308. package/src/vet.ts +1025 -0
  309. package/src/view.ts +2563 -0
  310. package/src/walk.ts +99 -0
package/src/unify.ts CHANGED
@@ -8,8 +8,10 @@ import { AontuContext } from './ctx'
8
8
  import { DONE } from './type'
9
9
 
10
10
  import { makeNilErr } from './err'
11
+ import { findAt } from './val/ReferFuncVal'
11
12
 
12
13
  import { NilVal } from './val/NilVal'
14
+ import { hasPlace } from './val/PlaceVal'
13
15
 
14
16
  import {
15
17
  Lang
@@ -26,27 +28,27 @@ import {
26
28
  } from './val/top'
27
29
 
28
30
 
29
- // Per-pass revisit bound: how many times one (Val, path) pair may be
30
- // re-unified within a single fixpoint pass before the evaluator calls it
31
- // non-convergence (`unify_cycle`). The old false positive here -- a
32
- // legal model with more than MAXCYCLE sibling conjunct terms at one
33
- // path, each re-running the TOP self-unify -- is fixed by the per-pass
34
- // memo below (_tcc/_tpi); test/spec/budget.tsv drives 1200 sibling
35
- // terms through both engines as the regression guard.
36
- const MAXCYCLE = 999
37
-
38
- // Structural recursion budget: how deep `unite` may nest before the
39
- // evaluator reports `unify_cycle`. SHARED LANGUAGE SURFACE -- Go's
40
- // maxUniteDepth (go/unify.go) carries the same number, and
41
- // test/spec/budget.tsv pins the boundary in both, so changing it is a
42
- // spec-visible change in both ports at once.
31
+ // The evaluation budgets live on the context (ctx.budget: passes,
32
+ // revisits, depth), defaulted there to the shared spec-visible
33
+ // constants test/spec/budget.tsv pins in both ports (9 / 999 / 1000)
34
+ // and configurable through the trust profile (G5, docs/trust.md) —
35
+ // deterministically: a budget is an integer count of engine events,
36
+ // never wall-clock.
43
37
  //
44
- // Why 1000: the whole shared suite peaks at 603 (the deliberately
45
- // extreme 1200-sibling-term fixture; ordinary documents are two orders
46
- // below), and V8 exhausts its call stack somewhere past depth ~1500 in
47
- // this evaluator. 1000 sits above every real document and below the
48
- // host limit, so the budget -- not the host -- decides the verdict.
49
- const MAXDEPTH = 1000
38
+ // Why the revisit default is 999: how many times one (Val, path) pair
39
+ // may be re-unified within a single fixpoint pass before the evaluator
40
+ // calls it non-convergence (`unify_cycle`). The old false positive here
41
+ // -- a legal model with many sibling conjunct terms at one path, each
42
+ // re-running the TOP self-unify -- is fixed by the per-pass memo below
43
+ // (_tcc/_tpi); test/spec/budget.tsv drives 1200 sibling terms through
44
+ // both engines as the regression guard.
45
+ //
46
+ // Why the depth default is 1000: the whole shared suite peaks at 603
47
+ // (the deliberately extreme 1200-sibling-term fixture; ordinary
48
+ // documents are two orders below), and V8 exhausts its call stack
49
+ // somewhere past depth ~1500 in this evaluator. 1000 sits above every
50
+ // real document and below the host limit, so the budget -- not the
51
+ // host -- decides the verdict.
50
52
 
51
53
  // Charge a DIRECT `Val.unify` recursion to the same depth budget that
52
54
  // `unite` enforces. Function and operator arguments evaluate through
@@ -59,7 +61,7 @@ const MAXDEPTH = 1000
59
61
  const withDepth = (
60
62
  ctx: AontuContext, a: any, b: any, run: () => any
61
63
  ): any => {
62
- if (MAXDEPTH <= ctx._depth.n) {
64
+ if (ctx.budget.depth <= ctx._depth.n) {
63
65
  return makeNilErr(ctx, 'unify_cycle', a, b)
64
66
  }
65
67
  ctx._depth.n++
@@ -89,14 +91,46 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
89
91
  if (a === b) {
90
92
  if (a.done) return a
91
93
  }
92
- else if (b !== undefined && b !== null) {
94
+ // ... and NOT on an instrumented run (G7 provenance). Both arms
95
+ // below answer with one operand and never reach the recorder at
96
+ // the tail, so an equal pair — two positions of one entity that
97
+ // agree, a clone meeting its source — contributed silently and
98
+ // `why` named one site where the Go port, whose recorder wraps the
99
+ // whole dispatcher, named both. Instrumented runs pay the slow
100
+ // path knowingly; uninstrumented ones pay one undefined check.
101
+ else if (b !== undefined && b !== null && undefined === ctx.prov) {
93
102
  if (a.done && b.done) {
94
- if (a.id === b.id) return a
103
+ if (a.id === b.id) {
104
+ // The deprecation record survives the fast path (G3).
105
+ if (null == a.deprecation && null != b.deprecation) {
106
+ a.deprecation = b.deprecation
107
+ }
108
+ return a
109
+ }
95
110
  if (a.constructor === b.constructor && a.peg === b.peg
96
111
  && !a.isNil && !b.isNil
97
112
  && !a.isMap && !a.isList
98
113
  && !a.isConjunct && !a.isDisjunct
99
- && !a.isRef && !a.isPref && !a.isFunc && !a.isExpect) {
114
+ && !a.isRef && !a.isPref && !a.isFunc && !a.isExpect
115
+ // NOT two TOPs: every top has the same (absent) peg, so
116
+ // this path would treat any two as the same value and
117
+ // return one of them whole — dropping a rider the other
118
+ // carries. The slow path answers the same thing for two
119
+ // plain tops, so nothing is lost by declining the shortcut.
120
+ && !a.isTop && !b.isTop
121
+ // NOT two rel residuals (RELATIONS P1) for the same
122
+ // reason: a settled rel is DONE with an absent peg, so
123
+ // any two matched here — dropping one side's type and
124
+ // held constraints. RelVal.unify merges them instead.
125
+ // NOT two graph atoms (P2) either: acyclic() and
126
+ // inverse(x) share a constructor and an absent peg
127
+ // without being the same declaration.
128
+ && !a.isRel && !a.isGraphAtom && !a.isRecurse) {
129
+ // The deprecation record survives the fast path too (G3):
130
+ // `deprecate(5) & 5` short-circuits here.
131
+ if (null == a.deprecation && null != b.deprecation) {
132
+ a.deprecation = b.deprecation
133
+ }
100
134
  return a
101
135
  }
102
136
  }
@@ -119,7 +153,7 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
119
153
  // NOTE: if this error occurs "unreasonably", attemp to avoid unnecesary unification
120
154
  // See for example PrefVal peg.id equality inspection.
121
155
  const sawCount = ctx.seen[saw] ?? 0
122
- if (MAXDEPTH <= ctx._depth.n) {
156
+ if (ctx.budget.depth <= ctx._depth.n) {
123
157
  // Structural recursion budget. Without it, deep nesting exhausts the
124
158
  // V8 call stack and the catch-all below reports a RangeError as
125
159
  // `internal` — a verdict that depends on the host's stack size
@@ -128,7 +162,7 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
128
162
  // makes it a stated budget error, like the pass budget.
129
163
  out = makeNilErr(ctx, 'unify_cycle', a, b)
130
164
  }
131
- else if (MAXCYCLE < sawCount) {
165
+ else if (ctx.budget.revisits < sawCount) {
132
166
  // console.log('SAW', sawCount, saw, a?.id, a?.canon, b?.id, b?.canon, ctx.cc)
133
167
  out = makeNilErr(ctx, 'unify_cycle', a, b)
134
168
  }
@@ -184,14 +218,36 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
184
218
  || b.isVar
185
219
  || b.isFunc
186
220
  || b.isExpect
221
+ // The refer residual (G4 phase 2) DRIVES, like the other
222
+ // residuals here: its peer is a plain string, which knows
223
+ // nothing about entity addresses, so letting the string drive
224
+ // dropped the address and left the constraint standing.
225
+ || b.isRefer
226
+ // An operator holding a HOLE (G8 phase 3) drives for the same
227
+ // reason: its peer is what FILLS it, and a scalar asked to
228
+ // unify with `_ + 2` sees an operator rather than a hole and
229
+ // refuses it on kind. Narrow to placeheld operators on
230
+ // purpose -- every other operator meets its peer the way it
231
+ // always has, through the conjunct fold that drives it.
232
+ || (b.isOp && hasPlace(b))
233
+ // A graph atom DRIVES (RELATIONS P2): its peer is the value
234
+ // it rides beside -- a container, a rel, a scalar -- and none
235
+ // of them know the atom; the atom knows to residuate.
236
+ || b.isGraphAtom
237
+ // The recursive residual DRIVES for the same reason: its peer
238
+ // is the concrete structure it expands against.
239
+ || b.isRecurse
187
240
  ) {
188
241
  out = b.unify(a, te ? ctx.clone({ explain: ec(te, 'BW') }) : ctx)
189
242
  unified = true
190
243
  why = 'bv'
191
244
  }
192
245
  // Exactly equal scalars (not caught by early fast-path — e.g.
193
- // because a or b isn't .done yet).
194
- else if (a.constructor === b.constructor && a.peg === b.peg) {
246
+ // because a or b isn't .done yet). Rel residuals are excluded
247
+ // exactly as in the fast path: their pegs are equally absent
248
+ // without the values being the same relation.
249
+ else if (a.constructor === b.constructor && a.peg === b.peg
250
+ && !a.isRel && !a.isGraphAtom && !a.isRecurse) {
195
251
  out = update(a, b)
196
252
  why = 'up'
197
253
  }
@@ -251,6 +307,29 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
251
307
 
252
308
  ctx.explain && explainClose(te, out)
253
309
 
310
+ // The provenance record (G7 phase 3), at the one place every meet
311
+ // passes through — the same reason the deprecation rider below lives
312
+ // here. Off by default: an uninstrumented run pays this one property
313
+ // load, and an instrumented one pays site materialisation knowingly.
314
+ if (undefined !== ctx.prov) {
315
+ ctx.prov.record(ctx.path, a, b, out)
316
+ }
317
+
318
+ // The deprecation record survives EVERY meet (G3 phase 4): the
319
+ // boolean marks have their own sweeps (ConjunctVal, the bag walks),
320
+ // but a record lost in one meet shape is a use the tooling never
321
+ // warns about, so it rides here, at the one place all meets pass
322
+ // through. First record wins; TOP and nil stay clean (TOP is the
323
+ // unit, and an error needs no deprecation).
324
+ if (null != out && true === (out as any).isVal &&
325
+ !out.isTop && !out.isNil && null == out.deprecation) {
326
+ const dep = (null != a ? a.deprecation : undefined) ??
327
+ (null != b ? b.deprecation : undefined)
328
+ if (null != dep) {
329
+ out.deprecation = dep
330
+ }
331
+ }
332
+
254
333
  return out
255
334
  }
256
335
 
@@ -285,6 +364,72 @@ function residuePaths(v: Val, max: number): string[] {
285
364
  }
286
365
 
287
366
 
367
+ // THE TYPE FLOW, APPLIED (G4 phase 2). `refer(t)` unifies `t` INTO the
368
+ // node it addresses, which is a write at a position the meet is not
369
+ // currently at -- the one non-local effect in the evaluator.
370
+ //
371
+ // It cannot be only a write made during the pass. A pass BUILDS a new
372
+ // tree from the old one, and `ctx.root` during pass N is pass N-1's
373
+ // result; a subtree rebuilt by pass N (which is exactly what happens
374
+ // when the link sits inside its own target, or when two nodes link at
375
+ // each other) drops a write made into the previous one. So each flow is
376
+ // also RECORDED, keyed by the target's path, and re-applied to the
377
+ // pass's own result here.
378
+ //
379
+ // Keyed by PATH, so there is no registry of names to collide in
380
+ // (ADR-014) -- the key is the position the address resolved to, and
381
+ // re-uniting the same type at the same position is idempotent, which is
382
+ // what makes replaying every recorded flow every pass correct rather
383
+ // than merely cheap.
384
+ //
385
+ // The pass loop is its only caller; it is EXPORTED for the test that
386
+ // pins the unresolved-path guard below (see there).
387
+ function applyFlows(ctx: AontuContext, root: Val): Val {
388
+ const flows: Map<string, Val> | undefined = (ctx as any).referflows
389
+ // NOTHING TO APPLY is the common case -- a document with no links
390
+ // pays one property load per pass, and the walk never runs.
391
+ if (null == flows || 0 === flows.size) {
392
+ return root
393
+ }
394
+ // Sorted, so two flows landing at overlapping positions arrive in the
395
+ // same order in both ports.
396
+ for (const key of [...flows.keys()].sort()) {
397
+ const path = key.split('\x00')
398
+ // The SHARED resolver, the one `refer` itself uses: a second
399
+ // descent written here would be a second answer to "what does this
400
+ // path name", and the two would drift (the Go twin calls the same
401
+ // findAt, arm for arm -- ADR-001).
402
+ //
403
+ // A RECORDED PATH THAT NO LONGER RESOLVES is skipped rather than
404
+ // refused: the record outliving its position is a question about
405
+ // the tree, and the link that named it answers it
406
+ // (test/spec/refer.tsv, `flow-target-moved-away`).
407
+ //
408
+ // NO DOCUMENT REACHES IT: a record is written only for a path that
409
+ // HAD resolved, and unification never takes a node back out of the
410
+ // tree — `move` copies and hides its source rather than removing
411
+ // it, which is the one rearrangement that looked like it would
412
+ // (probed in both ports: the flow still resolves on every pass,
413
+ // `flow-lands-then-its-parent-moves`). The guard is the contract
414
+ // for a rearrangement that does, and it is pinned by a direct call
415
+ // (`apply-flows-skips-a-record-that-stops-resolving` in
416
+ // ts/test/coverage3.test.ts) rather than by an ignore marker: node's
417
+ // `coverage ignore` drops LINES, and the gate reads BRANCH records,
418
+ // which survive it. The Go twin in go/unify.go can use the marker
419
+ // because that gate counts statements.
420
+ const found = findAt(root, path)
421
+ if (undefined === found) {
422
+ continue
423
+ }
424
+ const { parent, key: pkey, val: node } = found
425
+ const merged = unite(ctx.descend(pkey as string), node,
426
+ flows.get(key) as Val, 'refer-flow')
427
+ parent.peg[pkey as string] = merged
428
+ }
429
+ return root
430
+ }
431
+
432
+
288
433
  class Unify {
289
434
  root: Val
290
435
  res: Val
@@ -339,27 +484,96 @@ class Unify {
339
484
  // keyed by ref canon + source site, shared across all passes.
340
485
  ; (uctx as any).snapmap = new Map()
341
486
 
487
+ // The RECORDED TYPE FLOWS (G4 phase 2): target path -> the type
488
+ // `refer(t)` unified into it, replayed onto each pass's result by
489
+ // applyFlows. Seeded HERE, on the unify root, for the reason the
490
+ // snapshot map above is: a descended context is Object.create'd
491
+ // from its parent, so a `??=` in the descendant would make its
492
+ // OWN map and the root would never see what was recorded.
493
+ ; (uctx as any).referflows = new Map()
494
+
495
+ // The re-entrancy guard for those flows: the set of target paths
496
+ // a flow is currently inside. Same placement, same reason.
497
+ ; (uctx as any)._referflow = new Set()
498
+
342
499
  const explain = null == ctx?.explain ? undefined : ctx?.explain
343
500
  const te = explain && explainOpen(uctx, explain, 'root', res)
344
501
 
345
502
  // NOTE: if true === res.done already, then this loop never needs to run.
346
- let maxcc = 9 // 99
503
+ let maxcc = uctx.budget.passes
347
504
  let prevCanon: string | undefined = undefined
505
+ let lastCanon: string | undefined = undefined
506
+ let settle = false
348
507
  for (; this.cc < maxcc && DONE !== res.dc; this.cc++) {
349
508
  // console.log('CC', this.cc, res.canon)
350
509
  uctx.cc = this.cc
351
510
  uctx.seen = {}
352
- res = unite(te ? uctx.clone({ explain: ec(te, 'run') }) : uctx, res, top(), 'unify')
353
511
 
354
- if (0 < uctx.err.length) {
355
- break
512
+ // THE STAGING RULE (G8 phase 0,
513
+ // docs/capability-review/g8-generation.md), stated once, here,
514
+ // for every value whose answer depends on WHERE IT IS. Such a
515
+ // value residuates while the model is still moving and fires on
516
+ // the first pass whose input is IDENTICAL to the previous
517
+ // pass's input: nothing moved, so nothing will move it again,
518
+ // and the position it reports is the position it ends at.
519
+ //
520
+ // Why the whole model and not the value's own path. A spread, a
521
+ // reference or a `move` can place a value under a path it has
522
+ // already been driven at and THEN change what encloses it --
523
+ // `move` hides its source one pass AFTER it copies it, and a
524
+ // `key()` that answered on the strength of its path alone would
525
+ // answer for the ghost. Stability of the model is the only
526
+ // signal that says every such rearrangement is finished.
527
+ uctx.settle = settle
528
+
529
+ // Snapshot BEFORE the final pass (the loop condition has
530
+ // already established the tree is not done), so exhaustion can
531
+ // tell "still refining" from "stable residue" below. Taken at
532
+ // the final pass's ENTRY rather than the previous pass's exit
533
+ // — the same value when the budget allows two passes, and the
534
+ // only possible value when the trust profile sets passes to 1,
535
+ // where the old placement (cc === maxcc - 2, never true) made
536
+ // exhaustion silent, exactly the truncation docs/trust.md
537
+ // forbids. `lastCanon` IS that entry canon whenever a previous
538
+ // pass rendered one, so this costs nothing extra.
539
+ if (this.cc === maxcc - 1) {
540
+ prevCanon = lastCanon ?? res.canon
356
541
  }
357
542
 
358
- // Snapshot the second-to-last pass's result, so exhaustion can
359
- // tell "still refining" from "stable residue" below. Only paid
360
- // by models still unresolved this late.
361
- if (this.cc === maxcc - 2 && DONE !== res.dc) {
362
- prevCanon = res.canon
543
+ res = unite(te ? uctx.clone({ explain: ec(te, 'run') }) : uctx, res, top(), 'unify')
544
+
545
+ // MULTI-ERROR COLLECTION (G2 phase 6): the pass loop CONTINUES
546
+ // past an erroring pass, so independent failures a later pass
547
+ // would reach are collected in the same run — the break that
548
+ // stood here made every multi-error report truncated at the
549
+ // first erroring pass.
550
+ //
551
+ // What controls the cascade the design feared: a nil is
552
+ // ABSORBING (unite's isNil arms return the existing nil, no new
553
+ // error), so one failure stays ONE NilVal however many later
554
+ // meets touch it — a reference resolving to a failed target
555
+ // takes the same nil identity, which is exactly what lets the
556
+ // report layer dedup by identity. The probes that established
557
+ // this (fan-in refs, spread templates, disjunct trials, nested
558
+ // conjuncts) are pinned as vet.tsv's multi-* rows in both
559
+ // ports.
560
+
561
+ // The recorded type flows, re-applied to the tree THIS pass
562
+ // built: a pass rebuilds subtrees, and a flow written into the
563
+ // previous pass's tree does not survive that.
564
+ res = applyFlows(uctx, res)
565
+
566
+ // The staging signal for the NEXT pass, rendered here rather
567
+ // than at the top of the loop so a model that is FINISHED is
568
+ // never rendered at all: canon walks references, and the only
569
+ // trees that close a cycle are hand-built ones (the pass loop
570
+ // is what a test drives them through), which converge in one
571
+ // pass and must not be walked to decide a question that no
572
+ // longer arises.
573
+ if (DONE !== res.dc) {
574
+ const nowCanon = res.canon
575
+ settle = undefined !== lastCanon && lastCanon === nowCanon
576
+ lastCanon = nowCanon
363
577
  }
364
578
 
365
579
  uctx = uctx.clone({ root: res })
@@ -389,7 +603,7 @@ class Unify {
389
603
 
390
604
  this.res = res
391
605
  }
392
- } /* node:coverage ignore next 9 */
606
+ } /* node:coverage ignore next 10 */
393
607
 
394
608
 
395
609
 
@@ -398,4 +612,5 @@ export {
398
612
  Unify,
399
613
  unite,
400
614
  withDepth,
615
+ applyFlows,
401
616
  }
package/src/utility.ts CHANGED
@@ -25,6 +25,80 @@ function propagateMarks(source: Val, target: Val): void {
25
25
  }
26
26
 
27
27
 
28
+ // Collect every value in the tree carrying the deprecation record (G3
29
+ // phase 4), with its path — the one walk behind vet's `deprecated`
30
+ // warnings and the LSP's Deprecated tags. The record travels on meets
31
+ // (the unite rider) and clones, so this sees the declaration and every
32
+ // use resolving through it. The non-Val guard is for a bag's raw peg
33
+ // entries, which degenerate parses can leave behind.
34
+ function collectDeprecations(
35
+ root: Val): Array<{ val: Val, path: string[] }> {
36
+ const out: Array<{ val: Val, path: string[] }> = []
37
+ walkBagVals(root, (v: any, path) => {
38
+ if (null != v.deprecation) {
39
+ out.push({ val: v, path })
40
+ }
41
+ })
42
+ return out
43
+ }
44
+
45
+
46
+ // Visit every Val reachable through bag children, with its path — the
47
+ // walk under collectDeprecations and vet's default-validity lint. The
48
+ // non-Val guard is for a bag's raw peg entries, which degenerate
49
+ // parses can leave behind (pinned by the collect-deprecations direct
50
+ // test, ts/test/coverage3.test.ts).
51
+ function walkBagVals(
52
+ root: Val, fn: (v: Val, path: string[]) => void): void {
53
+ const walk = (v: any, path: string[]): void => {
54
+ if (null == v || true !== v.isVal) {
55
+ return
56
+ }
57
+ fn(v, path)
58
+ if ((true === v.isMap || true === v.isList) && null != v.peg) {
59
+ for (const k of Object.keys(v.peg)) {
60
+ walk(v.peg[k], [...path, k])
61
+ }
62
+ }
63
+ }
64
+ walk(root, [])
65
+ }
66
+
67
+
68
+ // The one-line prose for a deprecation record, shared by vet's warning
69
+ // findings and the LSP's tagged diagnostics.
70
+ function deprecationMessage(d: Record<string, string>): string {
71
+ const msg = 'string' === typeof d.msg ? d.msg : ''
72
+ return 'deprecated' + ('' === msg ? '' : ': ' + msg) +
73
+ ('string' === typeof d.use ? ' (use ' + d.use + ')' : '') +
74
+ ('string' === typeof d.since ? ' (since ' + d.since + ')' : '')
75
+ }
76
+
77
+
78
+ // The canonical form of a value, wrapped in the RIDER it carries —
79
+ // the deprecation record (G3 phase 4) — reparseably, so
80
+ // `deprecate(x, m)` survives canon. Bags render their children through
81
+ // this (MapVal/ListVal canon), which is where a marked FIELD — the
82
+ // realistic case — lives.
83
+ //
84
+ // The rider renders HERE and not in the value's own `canon` for the
85
+ // same reason the guard at the MapVal call site tests the isVal flag:
86
+ // a bag's canon recursion visits each child once, and a child that
87
+ // wrapped itself as well would render its subtree twice per level —
88
+ // 2^depth on a nested document.
89
+ function canonRiders(v: Val): string {
90
+ const c = v.canon
91
+ const d = v.deprecation
92
+ if (null == d) {
93
+ return c
94
+ }
95
+ const keys = Object.keys(d).sort()
96
+ const rec = keys.map((k) =>
97
+ JSON.stringify(k) + ':' + JSON.stringify(d[k])).join(',')
98
+ return 'deprecate(' + c + ('' === rec ? '' : ',{' + rec + '}') + ')'
99
+ }
100
+
101
+
28
102
  function formatPath(path: Val | string[], absolute?: boolean) {
29
103
  let parts: string[]
30
104
  if (Array.isArray(path)) {
@@ -188,12 +262,16 @@ function items(o: any) {
188
262
  else {
189
263
  return []
190
264
  }
191
- } /* node:coverage ignore next 15 */
265
+ } /* node:coverage ignore next 18 */
192
266
 
193
267
 
194
268
  export {
195
269
  items,
196
270
  propagateMarks,
271
+ canonRiders,
272
+ collectDeprecations,
273
+ walkBagVals,
274
+ deprecationMessage,
197
275
  formatPath,
198
276
  walk,
199
277
  WalkApply,