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/val/MapVal.ts CHANGED
@@ -16,6 +16,7 @@ import { unite } from '../unify'
16
16
 
17
17
  import {
18
18
  propagateMarks,
19
+ canonRiders,
19
20
  walk,
20
21
  explainOpen,
21
22
  ec,
@@ -27,12 +28,15 @@ import { makeNilErr, AontuError } from '../err'
27
28
  import {
28
29
  top
29
30
  } from './top'
31
+ import { pendingMarkWrapper } from './RefVal'
30
32
 
31
33
 
32
34
  import { ConjunctVal } from './ConjunctVal'
33
35
  import { NilVal } from './NilVal'
34
36
  import { BagVal } from './BagVal'
37
+ import { repathInstance, spreadId } from './Val'
35
38
  import { cmpCodePoint } from '../keyorder'
39
+ import { markSpread } from '../provenance'
36
40
 
37
41
 
38
42
  // Structural snapshots of ref spreads (see MapVal.unify), keyed by the
@@ -60,10 +64,21 @@ function snapshotRefSpread(cj: any, ctx: AontuContext): Val | undefined {
60
64
  const sk = spreadSnapKey(cj)
61
65
  let snap: Val | undefined = snapmap.get(sk)
62
66
  if (undefined === snap) {
63
- let tgt: Val | undefined = cj.find(ctx)
67
+ // snap mode: the pending-mark-wrapper defer in find must not
68
+ // apply here — the snapshot WANTS the pre-resolution structure.
69
+ let tgt: Val | undefined = cj.find(ctx, true)
64
70
  // A ref to a type() resolves to its inner template — snapshot that,
65
71
  // so a type-wrapped ref behaves like a plain-map ref spread.
66
72
  if (tgt && (tgt as any).isTypeFunc) tgt = (tgt as any).peg?.[0]
73
+ // A pending type()/hide() CALL is not yet a value to snapshot
74
+ // (ADR-005, the same rule find's non-snap path defers on): cached
75
+ // here it resolves at every destination and STAMPS marks the
76
+ // clearing walk ran too early to clear -- a mutual recursive
77
+ // schema's members vanished from generation this way. No cache;
78
+ // retry once the wrapper has resolved at its own field.
79
+ if (tgt && pendingMarkWrapper(tgt)) {
80
+ return undefined
81
+ }
67
82
  // Only snapshot a found, path-dependent target. If the target is not
68
83
  // present yet (it may be introduced by a later conjunct/merge), do
69
84
  // NOT cache — retry on the next fixpoint pass.
@@ -104,7 +119,12 @@ class MapVal extends BagVal {
104
119
 
105
120
  if (spread) {
106
121
  if ('&' === spread.o) {
107
- // TODO: handle existing spread!
122
+ // Multiple same-level spreads arrive as an array and conjoin;
123
+ // an unequal spread arriving from ANOTHER statement meets this
124
+ // one in unify's spread combination below — sound since the
125
+ // combined template became stateless (pure ExpectVal, BUGS.md
126
+ // §6-§7): each child meets the combined constraint
127
+ // independently and children never meet each other's data.
108
128
  this.spread.cj =
109
129
  Array.isArray(spread.v) ?
110
130
  1 < spread.v.length ?
@@ -120,7 +140,34 @@ class MapVal extends BagVal {
120
140
 
121
141
  // NOTE: order of keys is not preserved!
122
142
  // not possible in any case - consider {a,b} unify {b,a}
143
+ // ALIAS DECLARATIONS MUST SIT AT THE DOCUMENT ROOT. Stated on the
144
+ // VALUE rather than at the parse, because the parse cannot see it: an
145
+ // INCLUDED file's declarations are at the root of their own text, and
146
+ // only once the loaded map is placed does it become apparent that
147
+ // root is not the document's. `%name` is spelled as a reference from
148
+ // the document root, so an included file's own `%b` would otherwise
149
+ // reach the INCLUDER's `%b` -- cross-file capture, the hazard the
150
+ // sigil exists to prevent, one level up.
151
+ //
152
+ // P1 is single-file by construction; carrying a name ACROSS files is
153
+ // what `export` and the destructure are for (P2, not built), and this
154
+ // refusal is what keeps the two from being confused meanwhile. Pathed
155
+ // at the DECLARATION, which is what is wrong, not at the map.
156
+ aliasDeclarationsAreRooted(ctx: AontuContext): Val | undefined {
157
+ if (0 === this.aliasKeys.length || 0 === this.path.length) {
158
+ return undefined
159
+ }
160
+ const nv: any = new NilVal({ why: 'alias_not_toplevel' }, ctx)
161
+ nv.site = this.site
162
+ nv.path = [...this.path, this.aliasKeys[0]]
163
+ return nv
164
+ }
165
+
123
166
  unify(peer: Val, ctx: AontuContext): Val {
167
+ const arooted = this.aliasDeclarationsAreRooted(ctx)
168
+ if (undefined !== arooted) {
169
+ return arooted
170
+ }
124
171
  // console.log('MAPVAL-UNIFY', this.id, this.canon, peer.id, peer.canon)
125
172
 
126
173
  const TOP = top()
@@ -146,9 +193,18 @@ class MapVal extends BagVal {
146
193
 
147
194
  out.closed = this.closed
148
195
  out.optionalKeys = [...this.optionalKeys]
196
+ out.aliasKeys = [...this.aliasKeys]
149
197
  out.spread.cj = this.spread.cj
150
198
  out.site = this.site
151
199
 
200
+ // A rel() peer DRIVES whichever side the fold hands it on: the
201
+ // relation constraint rewrites this container leaf by leaf
202
+ // (RELATIONS.0.md §3.2), exactly as a sizing residual takes the
203
+ // driver's seat above.
204
+ if (true === (peer as any)?.isRel) {
205
+ return peer.unify(this, te ? ctx.clone({ explain: ec(te, 'REL') }) : ctx)
206
+ }
207
+
152
208
  if (peer instanceof MapVal) {
153
209
  if (!this.closed && peer.closed) {
154
210
  out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'PMC') }) : ctx) as MapVal
@@ -184,6 +240,13 @@ class MapVal extends BagVal {
184
240
  // combined here). unite resolves key()/path() at each destination via
185
241
  // spreadClone below, so nested + sibling key() cases stay correct
186
242
  // (test/spec/spread-nested-key, spread-key-all).
243
+ //
244
+ // The combined template must stay STATELESS: this meet wraps a
245
+ // key present in only one side as an ExpectVal, the combined
246
+ // map is shared across destinations when path-independent
247
+ // (spreadClone tier 1), and a stateful expect accumulated the
248
+ // first sibling's data and met it into the next (BUGS.md
249
+ // §6-§7). ExpectVal.unify is pure for exactly this reason.
187
250
  out.spread.cj = null == out.spread.cj ? peer.spread.cj : (
188
251
  null == peer.spread.cj ? out.spread.cj :
189
252
  out.spread.cj.canon === peer.spread.cj.canon ? out.spread.cj :
@@ -243,31 +306,46 @@ class MapVal extends BagVal {
243
306
  // No `undefined !== child` here: propagateMarks above already
244
307
  // dereferenced it, so a missing child would have thrown there.
245
308
  if (!spread_cj.isTop
246
- && (child as any)._spr === (spread_cj as any).id) {
309
+ && (child as any)._spr === spreadId(spread_cj)) {
247
310
  oval = child.done ? child :
248
311
  unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
249
312
  child, TOP, 'map-own')
250
- ; (oval as any)._spr = (spread_cj as any).id
313
+ ; (oval as any)._spr = spreadId(spread_cj)
251
314
  }
252
315
  else {
253
316
  const key_spread_cj = spread_cj.spreadClone(keyctx)
254
317
 
318
+ // The one place a spread is APPLIED, so the one place that
319
+ // knows a contribution came from a template rather than
320
+ // from the key itself (G7 phase 3). Only when someone is
321
+ // recording: the walk is O(template) per key per pass.
322
+ if (undefined !== keyctx.prov) {
323
+ markSpread(key_spread_cj)
324
+ }
325
+
255
326
  // child is non-nullish: propagateMarks above dereferences it.
256
327
  oval =
257
328
  child.isNil ? child :
258
329
  key_spread_cj.isNil ? key_spread_cj :
259
- key_spread_cj.isTop && child.done ? child :
330
+ // The no-op meet is SKIPPED on the normal path (it is the
331
+ // identity) but TAKEN while recording: a value written once
332
+ // and never met is still a contribution the author wants
333
+ // pointed at, and the Go port's unite sees that meet (G7
334
+ // phase 4). Instrumented runs pay knowingly.
335
+ key_spread_cj.isTop && child.done && undefined === keyctx.prov
336
+ ? child :
260
337
  child.isTop && key_spread_cj.done ? key_spread_cj :
261
338
  unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
262
339
  child, key_spread_cj, 'map-own')
263
340
 
264
341
  if (!spread_cj.isTop && !oval.isNil) {
265
- ; (oval as any)._spr = (spread_cj as any).id
342
+ ; (oval as any)._spr = spreadId(spread_cj)
266
343
  }
267
344
  }
268
345
 
269
346
  out.peg[key] = oval
270
347
 
348
+
271
349
  done = (done && DONE === oval.dc)
272
350
  }
273
351
 
@@ -291,12 +369,31 @@ class MapVal extends BagVal {
291
369
  out.optionalKeys.push(peerkey)
292
370
  }
293
371
 
372
+ // ... and so is an alias declaration, for the same reason:
373
+ // two statements for one map are one map, and a name declared
374
+ // in either is declared in the result.
375
+ if (upeer.aliasKeys.includes(peerkey) && !out.aliasKeys.includes(peerkey)) {
376
+ out.aliasKeys.push(peerkey)
377
+ }
378
+
294
379
  let child = out.peg[peerkey]
295
380
 
296
381
  const peerctx = ctx.descend(peerkey)
297
382
 
298
383
  let oval = out.peg[peerkey] =
299
- undefined === child ? this.handleExpectedVal(peerkey, peerchild, this, ctx) :
384
+ // A peer-only key is CARRIED, not met — except on an
385
+ // instrumented run, where the identity meet is taken so
386
+ // the recorder sees where the value came from. The Go port
387
+ // unites a genable peer-only child with TOP unconditionally
388
+ // (go/mapval.go), and the difference was invisible until
389
+ // G4's identity merge brought a peer whose children the
390
+ // recorder counts as WRITTEN: `why $.b.k` on two positions
391
+ // of one entity named the site in Go and answered "nothing
392
+ // met here" in TypeScript.
393
+ undefined === child
394
+ ? (undefined !== peerctx.prov && peerchild.isGenable
395
+ ? unite(peerctx, peerchild, TOP, 'map-peer-only')
396
+ : this.handleExpectedVal(peerkey, peerchild, this, ctx)) :
300
397
  child.isTop && peerchild.done ? peerchild :
301
398
  child.isNil ? child :
302
399
  peerchild.isNil ? peerchild :
@@ -307,15 +404,27 @@ class MapVal extends BagVal {
307
404
  // Same apply-once discipline as the own-key loop: once the
308
405
  // constraint is merged into the value (marked with the
309
406
  // constraint's id), later passes only self-unify.
310
- if ((oval as any)._spr !== (spread_cj as any).id) {
407
+ if ((oval as any)._spr !== spreadId(spread_cj)) {
311
408
  let key_spread_cj = spread_cj.spreadClone(peerctx)
312
409
 
410
+ // A SPREAD IS A SPREAD FROM EITHER SIDE. The own-key loop
411
+ // above marks its template so `why` can say the
412
+ // contribution came from `&:` rather than from the key;
413
+ // this arm did not, so the SAME document reported the
414
+ // role or dropped it depending on whether the template
415
+ // and the keys were written in one statement or two --
416
+ // `services: &: {..}` plus `services: {web:{}}` took the
417
+ // peer path and lost `(spread)` (use-cases/BUGS.md §55).
418
+ if (undefined !== peerctx.prov) {
419
+ markSpread(key_spread_cj)
420
+ }
421
+
313
422
  oval = out.peg[peerkey] =
314
423
  unite(te ? peerctx.clone({ explain: ec(te, 'PSP:' + peerkey) }) : peerctx,
315
424
  oval, key_spread_cj, 'map-peer-spread')
316
425
 
317
426
  if (!spread_cj.isTop && !oval.isNil) {
318
- ; (oval as any)._spr = (spread_cj as any).id
427
+ ; (oval as any)._spr = spreadId(spread_cj)
319
428
  }
320
429
  }
321
430
  }
@@ -325,6 +434,12 @@ class MapVal extends BagVal {
325
434
  done = (done && DONE === oval.dc)
326
435
  }
327
436
  }
437
+ else if (true === (peer as any).isContainerKind) {
438
+ // The container KIND delegates to its own arm, exactly as a
439
+ // scalar delegates to a ScalarKindVal peer: the kind knows to
440
+ // admit this map, and this map knows nothing about kinds.
441
+ out = peer.unify(this, ctx) as any
442
+ }
328
443
  else if (!peer.isTop) {
329
444
  out = makeNilErr(ctx, 'map', this, peer)
330
445
  }
@@ -382,7 +497,11 @@ class MapVal extends BagVal {
382
497
  }
383
498
 
384
499
  if (!allScalarKind) {
385
- return this.clone(ctx)
500
+ // A full instance (`dup`, ADR-005), paths normalised to the
501
+ // destination: see Val.spreadClone and repathInstance.
502
+ const out = this.clone(ctx, { dup: true })
503
+ repathInstance(out, out.path)
504
+ return out
386
505
  }
387
506
 
388
507
  let out = (super.clone(ctx) as MapVal)
@@ -399,6 +518,7 @@ class MapVal extends BagVal {
399
518
 
400
519
  out.closed = this.closed
401
520
  out.optionalKeys = [...this.optionalKeys]
521
+ out.aliasKeys = [...this.aliasKeys]
402
522
 
403
523
  return out
404
524
  }
@@ -414,16 +534,22 @@ class MapVal extends BagVal {
414
534
  // (entry[1] as Val).clone(ctx, spec?.mark ? { mark: spec.mark } : {}) :
415
535
  (entry[1] as Val).clone(ctx, {
416
536
  mark: spec?.mark ?? {},
417
- path: [...out.path, entry[0]]
537
+ path: [...out.path, entry[0]],
538
+ // The instantiation flag descends (ADR-005): a template's
539
+ // children are part of the instance.
540
+ dup: spec?.dup,
418
541
  }) :
419
542
  entry[1]
420
543
  }
421
544
  if (this.spread.cj) {
422
- out.spread.cj = this.spread.cj.clone(ctx, spec?.mark ? { mark: spec.mark } : {})
545
+ out.spread.cj = this.spread.cj.clone(ctx,
546
+ spec?.mark || spec?.dup ?
547
+ { mark: spec?.mark, dup: spec?.dup } : {})
423
548
  }
424
549
 
425
550
  out.closed = this.closed
426
551
  out.optionalKeys = [...this.optionalKeys]
552
+ out.aliasKeys = [...this.aliasKeys]
427
553
 
428
554
  // out.from = this.from
429
555
 
@@ -437,7 +563,14 @@ class MapVal extends BagVal {
437
563
  // independent of insertion/unification order and matches the Go
438
564
  // port. A bare .sort() is UTF-16 code-unit order, which puts an
439
565
  // astral key ahead of everything in U+E000-U+FFFF -- see cmpCodePoint.
440
- let keys = Object.keys(this.peg).sort(cmpCodePoint)
566
+ // An alias declaration is not part of the document, so canon does
567
+ // not render it: a document with aliases and the document with
568
+ // every alias written out longhand must produce the same text and
569
+ // therefore the same `aon1-` hash. That is the sharpest statement
570
+ // of what an alias IS -- a name for a value, and nothing more.
571
+ let keys = Object.keys(this.peg)
572
+ .filter(k => !this.aliasKeys.includes(k))
573
+ .sort(cmpCodePoint)
441
574
  return '' +
442
575
  // this.errcanon() +
443
576
  // (this.mark.type ? '<type>' : '') +
@@ -450,7 +583,13 @@ class MapVal extends BagVal {
450
583
  JSON.stringify(k) +
451
584
  (this.optionalKeys.includes(k) ? '?' : '') +
452
585
  ':' +
453
- (this.peg[k]?.canon ?? this.peg[k])
586
+ // canonRiders, not .canon: a deprecated field renders
587
+ // back as its `deprecate(x, m)` call, reparseably (G3). The
588
+ // guard is the isVal FLAG, never the canon getter: computing
589
+ // canon in the guard and again in the render doubles the
590
+ // recursion per level, which is 2^depth on a nested document.
591
+ (true === this.peg[k]?.isVal
592
+ ? canonRiders(this.peg[k]) : this.peg[k])
454
593
  ])
455
594
  .join(',') +
456
595
  '}' // + '<' + (this.mark.hide ? 'H' : '') + '>'
@@ -0,0 +1,176 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // GENERATION: `match(v, p1, r1, p2, r2, …, d?)` (G8 phase 2,
4
+ // docs/capability-review/g8-generation.md). A BOUNDED conditional:
5
+ // alternating pattern/result arguments and an optional trailing
6
+ // default. The first pattern IN ARGUMENT ORDER that `v` unifies with
7
+ // SELECTS its result, and the result is the answer.
8
+ //
9
+ // The design said the answer was `v & p & r` -- the scrutinee narrowed
10
+ // by the arm rather than replaced by it. That cannot be what a match
11
+ // is for: it makes every arm whose result is not already a `v` a
12
+ // contradiction, and the design's own example
13
+ // (`match($.tier, small, {cpu:1}, …)`, a string scrutinee and map
14
+ // results) cannot be evaluated at all under it. A match MAPS a value
15
+ // to another value; a document that wants the scrutinee kept can say
16
+ // so, because the scrutinee is a value it can name.
17
+ //
18
+ // size: match($.tier, small, {cpu:1}, large, {cpu:8}, {cpu:2})
19
+ //
20
+ // WHAT KEEPS IT FROM BECOMING A CONDITIONAL LANGUAGE. The scrutinee is
21
+ // matched by UNIFIABILITY only: no boolean guards, no comparisons
22
+ // beyond what the constraint atoms already are, no fallthrough (first
23
+ // match wins, in a spec-pinned order), and no way to write a pattern
24
+ // that is not an ordinary Aontu value. The whole form is total.
25
+ //
26
+ // NO MATCH AND NO DEFAULT IS AN ERROR, not an empty answer, and the
27
+ // report names the patterns that were tried -- the admissible-
28
+ // alternatives shape (G2's error contract). A default is how a
29
+ // document says it meant to allow the rest.
30
+ //
31
+ // A RESULT IS NOT EVALUATED UNTIL IT IS SELECTED. Only the scrutinee
32
+ // and the patterns are driven; an unselected result never runs, so a
33
+ // broken arm nobody takes is not an error the document has to carry
34
+ // (which multi-error collection, G2 phase 6, would otherwise report).
35
+
36
+ import type {
37
+ Val,
38
+ ValSpec,
39
+ } from '../type'
40
+
41
+ import {
42
+ AontuContext,
43
+ } from '../ctx'
44
+
45
+ import { makeNilErr } from '../err'
46
+ import { top } from './top'
47
+ import { prefInnerPeg } from './PrefVal'
48
+ import { FuncBaseVal, trialUnify } from './FuncBaseVal'
49
+
50
+
51
+ // THE DEFAULTED-SCRUTINEE RULE (ADR-004, use-cases/BUGS.md §5). The
52
+ // generation-effective view of a settled scrutinee: a preference — or
53
+ // a disjunction carrying one — means "this value unless something
54
+ // overrides it", and by resolve time the model has SETTLED (staging
55
+ // rule), so nothing will. The value generation is about to emit is
56
+ // therefore the value the patterns must be tested against. Testing
57
+ // against the still-open preference instead let a pattern SELECT an
58
+ // arm by overriding the default: `side_effect:*readonly|write|
59
+ // destructive` beside `match(.side_effect, destructive, true, false)`
60
+ // answered `true` while generating "readonly" next to it — a derived
61
+ // value contradicting the very value it derives from, exit 0.
62
+ // A pref-free scrutinee (open disjunction included) is untouched:
63
+ // matching by unifiability is its documented meaning.
64
+ // Exported for the multi-pref unit test (ADR-002, the subsumeNode
65
+ // precedent): rankPrefs leaves a settled disjunct at most one pref, so
66
+ // the min-rank scan below cannot be reached through a document.
67
+ export function effectiveScrutinee(v: Val): Val {
68
+ let out: any = v
69
+ if (true === out?.isDisjunct && Array.isArray(out.peg)) {
70
+ const prefs = out.peg.filter((m: any) => true === m?.isPref)
71
+ if (0 === prefs.length) {
72
+ return v
73
+ }
74
+ // Generation picks the LOWEST rank (effectiveDefault in
75
+ // subsume.ts; `a:**1|*2` generates 2). rankPrefs leaves at most
76
+ // one pref standing in a settled disjunct, so the scan is
77
+ // defensive.
78
+ out = prefs.reduce((a: any, b: any) => b.rank < a.rank ? b : a)
79
+ }
80
+ return prefInnerPeg(out)
81
+ }
82
+
83
+
84
+ class MatchFuncVal extends FuncBaseVal {
85
+ isMatchFunc = true
86
+
87
+ // THE STAGING RULE (G8 phase 0). A scrutinee that is still being
88
+ // narrowed can match an EARLIER pattern than the one it will end up
89
+ // matching, and the arm a match takes is not a thing to guess at.
90
+ staged = true
91
+
92
+ constructor(
93
+ spec: ValSpec,
94
+ ctx?: AontuContext
95
+ ) {
96
+ super(spec, ctx)
97
+ }
98
+
99
+
100
+ funcname() {
101
+ return 'match'
102
+ }
103
+
104
+
105
+ prepare(_ctx: AontuContext, _args: Val[]) {
106
+ return null
107
+ }
108
+
109
+
110
+ // The scrutinee is argument 0 and the patterns are the odd
111
+ // arguments; the results are the even ones after 0, and the last
112
+ // argument is a DEFAULT when the count is even. Written once, read
113
+ // by both the driver below and resolve.
114
+ hasDefault() {
115
+ return 0 === this.peg.length % 2
116
+ }
117
+
118
+
119
+ unify(peer: Val, ctx: AontuContext): Val {
120
+ // The scrutinee and the PATTERNS are driven; the results are not
121
+ // (see the header). driveStagedArgs takes a prefix, so the odd
122
+ // positions are driven one at a time.
123
+ let ready = this.driveStagedArgs(ctx, 1)
124
+ const last = this.peg.length - (this.hasDefault() ? 1 : 0)
125
+ for (let i = 1; i < last; i += 2) {
126
+ const arg: Val = this.peg[i]
127
+ if (!arg.done) {
128
+ this.peg[i] = arg.unify(top(), ctx)
129
+ }
130
+ ready = ready && true === this.peg[i].done
131
+ }
132
+
133
+ if (!ready || !ctx.settle) {
134
+ return this.residuate(peer, ctx)
135
+ }
136
+
137
+ return super.unify(peer, ctx)
138
+ }
139
+
140
+
141
+ resolve(ctx: AontuContext, args: Val[]) {
142
+ const scrutinee: Val = effectiveScrutinee(args[0])
143
+ const dflt: Val | undefined = this.hasDefault() ?
144
+ args[args.length - 1] : undefined
145
+ const last = args.length - (undefined === dflt ? 0 : 1)
146
+
147
+ const tried: string[] = []
148
+
149
+ for (let i = 1; i < last; i += 2) {
150
+ const pattern: Val = args[i]
151
+ tried.push(pattern.canon)
152
+
153
+ // The trial is against CLONES: `unite` refines a bag in place
154
+ // against a TOP peer, and a pattern that failed must be
155
+ // untouched for the next document that reads its canon.
156
+ if (undefined !== trialUnify(ctx, scrutinee.clone(ctx), pattern.clone(ctx))) {
157
+ return args[i + 1].clone(ctx)
158
+ }
159
+ }
160
+
161
+ if (undefined !== dflt) {
162
+ return dflt.clone(ctx)
163
+ }
164
+
165
+ return makeNilErr(ctx, 'match_none', this, undefined, 'resolve', {
166
+ value: scrutinee.canon,
167
+ tried: tried.join(' '),
168
+ })
169
+ }
170
+
171
+ } /* node:coverage ignore next 5 */
172
+
173
+
174
+ export {
175
+ MatchFuncVal,
176
+ }
@@ -12,7 +12,6 @@ import {
12
12
 
13
13
  import { makeNilErr } from '../err'
14
14
 
15
- import { NilVal } from '../val/NilVal'
16
15
 
17
16
  import {
18
17
  walk
@@ -21,7 +20,6 @@ import {
21
20
 
22
21
 
23
22
  import { FuncBaseVal } from './FuncBaseVal'
24
- import { CopyFuncVal } from './CopyFuncVal'
25
23
  import { PrefFuncVal } from './PrefFuncVal'
26
24
 
27
25
 
package/src/val/NilVal.ts CHANGED
@@ -79,6 +79,31 @@ class NilVal extends Val {
79
79
  }
80
80
  }
81
81
 
82
+ // THE PATH IS WHERE THE MEET IS, NOT WHERE THE OPERAND WAS WRITTEN
83
+ // (use-cases/BUGS.md §41). The operand path above decides the SITE
84
+ // correctly and the path only by accident: a value that arrives by
85
+ // REFERENCE is re-pathed to the referring field, and its children
86
+ // are re-pathed to that same field rather than rebased under it, so
87
+ // every conflict inside `q: $.M` reported `$.q` — the enclosing
88
+ // record, not the key to edit, and the SAME path for every one of
89
+ // its fields. The context has descended to the child by the time
90
+ // the meet fails, and Go reads the path off the tree position for
91
+ // exactly this reason.
92
+ //
93
+ // Only EXTENDS, never redirects: the context path is taken when the
94
+ // operand's path is a PREFIX of it, so a nil minted away from the
95
+ // descent (a func resolving an argument, a generator's own probe,
96
+ // an anchored `--at` run whose operand is already correctly placed)
97
+ // keeps the path its operand carries. Taking the context path
98
+ // unconditionally was tried and reverted -- it moved the closed-key,
99
+ // spread-template and every `--at` finding to the driving location,
100
+ // which is not where those belong.
101
+ if (null != ctx?.path && null != nil.path &&
102
+ nil.path.length < ctx.path.length &&
103
+ nil.path.every((p, i) => p === ctx.path[i])) {
104
+ nil.path = [...ctx.path]
105
+ }
106
+
82
107
  if (ctx) {
83
108
  ctx.adderr(nil)
84
109
  }
@@ -31,6 +31,7 @@ import {
31
31
 
32
32
  import { ConjunctVal } from './ConjunctVal'
33
33
  import { FeatureVal } from './FeatureVal'
34
+ import { hasPlace, fillPlace } from './PlaceVal'
34
35
 
35
36
 
36
37
 
@@ -66,6 +67,17 @@ class OpBaseVal extends FeatureVal {
66
67
 
67
68
 
68
69
  unify(peer: Val, ctx: AontuContext): Val {
70
+
71
+ // THE PLACEHOLDER (G8 phase 3, see PlaceVal), on the operator side:
72
+ // `x: {&: {m: _ + 2}}` meeting `1` is `3`. Same rule as
73
+ // FuncBaseVal's -- the peer fills the hole and the operation is
74
+ // what answers.
75
+ if (!peer.isTop && !peer.isNil && this.id !== peer.id && hasPlace(this)) {
76
+ if (hasPlace(peer)) {
77
+ return makeNilErr(ctx, 'place_pair', this, peer)
78
+ }
79
+ return fillPlace(this, peer, ctx).unify(top(), ctx)
80
+ }
69
81
  const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Op:' + this.opname(), this, peer)
70
82
  // Declared without an initial value: every arm below assigns it, and
71
83
  // seeding it with `this` made the two arms that stand the op read as
@@ -165,10 +177,17 @@ class OpBaseVal extends FeatureVal {
165
177
  }
166
178
 
167
179
 
168
- clone(ctx: AontuContext, _spec?: ValSpec): Val {
180
+ clone(ctx: AontuContext, spec?: ValSpec): Val {
169
181
  let out = (super.clone(ctx, {
170
182
  peg: this.peg,
171
183
  }) as OpBaseVal)
184
+ // THE PER-DESTINATION INSTANTIATION RULE (ADR-005): an operation
185
+ // inside a template clones its operands as instances — the
186
+ // default clone shares them (see FuncBaseVal.clone). Every
187
+ // operand is a Val by construction, as in the Go twin's []Val.
188
+ if (true === spec?.dup) {
189
+ out.peg = this.peg.map((a: Val) => a.clone(ctx, { dup: true }))
190
+ }
172
191
  return out
173
192
  }
174
193
 
@@ -14,7 +14,6 @@ import { makeNilErr } from '../err'
14
14
 
15
15
 
16
16
  import { FuncBaseVal } from './FuncBaseVal'
17
- import { NilVal } from '../val/NilVal'
18
17
  import { BagVal } from '../val/BagVal'
19
18
 
20
19
 
@@ -39,13 +38,16 @@ class OpenFuncVal extends FuncBaseVal {
39
38
 
40
39
 
41
40
  resolve(ctx: AontuContext | undefined, args: Val[]) {
42
- const argval: any = args[0]
41
+ let argval: any = args[0]
43
42
 
44
43
  if (null == argval) {
45
44
  return makeNilErr(ctx, 'no_first_arg', this, undefined, 'close')
46
45
  }
47
46
 
48
47
  if (argval.isMap || argval.isList) {
48
+ // In place, for the reason CloseFuncVal.resolve gives: the
49
+ // instantiation rule (ADR-005) makes the argument this call's
50
+ // own wherever the call is multiplied.
49
51
  (argval as BagVal).closed = false
50
52
  }
51
53