aontu 0.52.0 → 0.53.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 (273) 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 +14 -3
  7. package/dist/aontu.js +145 -4
  8. package/dist/aontu.js.map +1 -1
  9. package/dist/cli.d.ts +44 -1
  10. package/dist/cli.js +2401 -44
  11. package/dist/cli.js.map +1 -1
  12. package/dist/ctx.d.ts +16 -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 +48 -8
  20. package/dist/err.js.map +1 -1
  21. package/dist/graph.d.ts +16 -0
  22. package/dist/graph.js +73 -0
  23. package/dist/graph.js.map +1 -0
  24. package/dist/hcanon.d.ts +3 -0
  25. package/dist/hcanon.js +146 -0
  26. package/dist/hcanon.js.map +1 -0
  27. package/dist/hints.js +223 -5
  28. package/dist/hints.js.map +1 -1
  29. package/dist/jsonschema.d.ts +20 -0
  30. package/dist/jsonschema.js +391 -0
  31. package/dist/jsonschema.js.map +1 -0
  32. package/dist/lang.js +698 -35
  33. package/dist/lang.js.map +1 -1
  34. package/dist/lsp.d.ts +9 -2
  35. package/dist/lsp.js +262 -46
  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 +814 -0
  42. package/dist/mcp.js.map +1 -0
  43. package/dist/mod-tool.d.ts +58 -0
  44. package/dist/mod-tool.js +498 -0
  45. package/dist/mod-tool.js.map +1 -0
  46. package/dist/mod.d.ts +31 -0
  47. package/dist/mod.js +250 -0
  48. package/dist/mod.js.map +1 -0
  49. package/dist/patch.d.ts +44 -0
  50. package/dist/patch.js +506 -0
  51. package/dist/patch.js.map +1 -0
  52. package/dist/provenance.d.ts +40 -0
  53. package/dist/provenance.js +335 -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 +14 -0
  59. package/dist/reach.js +140 -0
  60. package/dist/reach.js.map +1 -0
  61. package/dist/relation.d.ts +19 -0
  62. package/dist/relation.js +305 -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/site.d.ts +4 -0
  68. package/dist/site.js +31 -0
  69. package/dist/site.js.map +1 -1
  70. package/dist/std.d.ts +1 -0
  71. package/dist/std.js +73 -0
  72. package/dist/std.js.map +1 -0
  73. package/dist/subsume.d.ts +39 -0
  74. package/dist/subsume.js +526 -0
  75. package/dist/subsume.js.map +1 -0
  76. package/dist/trim.d.ts +19 -0
  77. package/dist/trim.js +155 -0
  78. package/dist/trim.js.map +1 -0
  79. package/dist/tsconfig.tsbuildinfo +1 -1
  80. package/dist/type.d.ts +17 -1
  81. package/dist/type.js.map +1 -1
  82. package/dist/unify.d.ts +3 -1
  83. package/dist/unify.js +287 -18
  84. package/dist/unify.js.map +1 -1
  85. package/dist/utility.d.ts +9 -1
  86. package/dist/utility.js +122 -1
  87. package/dist/utility.js.map +1 -1
  88. package/dist/val/AggFuncVal.d.ts +33 -0
  89. package/dist/val/AggFuncVal.js +202 -0
  90. package/dist/val/AggFuncVal.js.map +1 -0
  91. package/dist/val/ArithFuncVal.d.ts +31 -0
  92. package/dist/val/ArithFuncVal.js +62 -0
  93. package/dist/val/ArithFuncVal.js.map +1 -0
  94. package/dist/val/BagVal.d.ts +5 -0
  95. package/dist/val/BagVal.js +96 -5
  96. package/dist/val/BagVal.js.map +1 -1
  97. package/dist/val/CloseFuncVal.js +9 -1
  98. package/dist/val/CloseFuncVal.js.map +1 -1
  99. package/dist/val/ConjunctVal.d.ts +1 -1
  100. package/dist/val/ConjunctVal.js +19 -0
  101. package/dist/val/ConjunctVal.js.map +1 -1
  102. package/dist/val/ConstraintVal.d.ts +48 -1
  103. package/dist/val/ConstraintVal.js +1501 -110
  104. package/dist/val/ConstraintVal.js.map +1 -1
  105. package/dist/val/CopyFuncVal.d.ts +1 -2
  106. package/dist/val/CopyFuncVal.js +7 -0
  107. package/dist/val/CopyFuncVal.js.map +1 -1
  108. package/dist/val/Decimal.d.ts +1 -0
  109. package/dist/val/Decimal.js +13 -0
  110. package/dist/val/Decimal.js.map +1 -1
  111. package/dist/val/DeprecateFuncVal.d.ts +11 -0
  112. package/dist/val/DeprecateFuncVal.js +47 -0
  113. package/dist/val/DeprecateFuncVal.js.map +1 -0
  114. package/dist/val/DisjunctVal.js +130 -21
  115. package/dist/val/DisjunctVal.js.map +1 -1
  116. package/dist/val/EachFuncVal.d.ts +15 -0
  117. package/dist/val/EachFuncVal.js +75 -0
  118. package/dist/val/EachFuncVal.js.map +1 -0
  119. package/dist/val/ExpectVal.d.ts +1 -0
  120. package/dist/val/ExpectVal.js +41 -4
  121. package/dist/val/ExpectVal.js.map +1 -1
  122. package/dist/val/FeatureVal.js +1 -1
  123. package/dist/val/FeatureVal.js.map +1 -1
  124. package/dist/val/FilterFuncVal.d.ts +15 -0
  125. package/dist/val/FilterFuncVal.js +91 -0
  126. package/dist/val/FilterFuncVal.js.map +1 -0
  127. package/dist/val/FuncBaseVal.d.ts +6 -1
  128. package/dist/val/FuncBaseVal.js +178 -2
  129. package/dist/val/FuncBaseVal.js.map +1 -1
  130. package/dist/val/HideFuncVal.js.map +1 -1
  131. package/dist/val/IdFuncVal.d.ts +13 -0
  132. package/dist/val/IdFuncVal.js +54 -0
  133. package/dist/val/IdFuncVal.js.map +1 -0
  134. package/dist/val/JunctionVal.js +7 -1
  135. package/dist/val/JunctionVal.js.map +1 -1
  136. package/dist/val/KeyFuncVal.d.ts +1 -1
  137. package/dist/val/KeyFuncVal.js +38 -30
  138. package/dist/val/KeyFuncVal.js.map +1 -1
  139. package/dist/val/ListVal.js +117 -17
  140. package/dist/val/ListVal.js.map +1 -1
  141. package/dist/val/LowerFuncVal.js.map +1 -1
  142. package/dist/val/MapVal.js +102 -8
  143. package/dist/val/MapVal.js.map +1 -1
  144. package/dist/val/MatchFuncVal.d.ts +15 -0
  145. package/dist/val/MatchFuncVal.js +107 -0
  146. package/dist/val/MatchFuncVal.js.map +1 -0
  147. package/dist/val/MoveFuncVal.js.map +1 -1
  148. package/dist/val/NilVal.js +24 -0
  149. package/dist/val/NilVal.js.map +1 -1
  150. package/dist/val/OpBaseVal.d.ts +1 -1
  151. package/dist/val/OpBaseVal.js +24 -2
  152. package/dist/val/OpBaseVal.js.map +1 -1
  153. package/dist/val/OpenFuncVal.js +4 -1
  154. package/dist/val/OpenFuncVal.js.map +1 -1
  155. package/dist/val/PackFuncVal.d.ts +15 -0
  156. package/dist/val/PackFuncVal.js +108 -0
  157. package/dist/val/PackFuncVal.js.map +1 -0
  158. package/dist/val/PathFuncVal.js.map +1 -1
  159. package/dist/val/PlaceVal.d.ts +13 -0
  160. package/dist/val/PlaceVal.js +131 -0
  161. package/dist/val/PlaceVal.js.map +1 -0
  162. package/dist/val/PlusOpVal.js +11 -2
  163. package/dist/val/PlusOpVal.js.map +1 -1
  164. package/dist/val/PrefFuncVal.js.map +1 -1
  165. package/dist/val/PrefVal.d.ts +2 -2
  166. package/dist/val/PrefVal.js +78 -23
  167. package/dist/val/PrefVal.js.map +1 -1
  168. package/dist/val/RefVal.d.ts +1 -1
  169. package/dist/val/RefVal.js +158 -30
  170. package/dist/val/RefVal.js.map +1 -1
  171. package/dist/val/ReferFuncVal.d.ts +36 -0
  172. package/dist/val/ReferFuncVal.js +303 -0
  173. package/dist/val/ReferFuncVal.js.map +1 -0
  174. package/dist/val/ScalarKindVal.d.ts +1 -2
  175. package/dist/val/ScalarKindVal.js +0 -11
  176. package/dist/val/ScalarKindVal.js.map +1 -1
  177. package/dist/val/TopVal.js.map +1 -1
  178. package/dist/val/TypeFuncVal.js.map +1 -1
  179. package/dist/val/UpperFuncVal.js.map +1 -1
  180. package/dist/val/Val.d.ts +9 -2
  181. package/dist/val/Val.js +150 -4
  182. package/dist/val/Val.js.map +1 -1
  183. package/dist/val/VarVal.js.map +1 -1
  184. package/dist/val/arith.d.ts +6 -0
  185. package/dist/val/arith.js +170 -0
  186. package/dist/val/arith.js.map +1 -0
  187. package/dist/vet.d.ts +45 -0
  188. package/dist/vet.js +776 -0
  189. package/dist/vet.js.map +1 -0
  190. package/dist/walk.d.ts +2 -0
  191. package/dist/walk.js +91 -0
  192. package/dist/walk.js.map +1 -0
  193. package/grammar/aontu.gbnf +130 -0
  194. package/grammar/aontu.lark +113 -0
  195. package/package.json +30 -15
  196. package/skill/SKILL.md +37 -0
  197. package/skill/error-codes.md +62 -0
  198. package/skill/examples.md +99 -0
  199. package/skill/grammar-card.md +57 -0
  200. package/src/agentsmd.ts +135 -0
  201. package/src/aontu.ts +192 -4
  202. package/src/cli.ts +2858 -71
  203. package/src/ctx.ts +81 -0
  204. package/src/diff.ts +196 -0
  205. package/src/err.ts +52 -8
  206. package/src/graph.ts +135 -0
  207. package/src/hcanon.ts +169 -0
  208. package/src/hints.ts +271 -5
  209. package/src/jsonschema.ts +511 -0
  210. package/src/lang.ts +779 -37
  211. package/src/lsp.ts +281 -47
  212. package/src/mcp-server.ts +187 -0
  213. package/src/mcp.ts +993 -0
  214. package/src/mod-tool.ts +679 -0
  215. package/src/mod.ts +344 -0
  216. package/src/patch.ts +624 -0
  217. package/src/provenance.ts +430 -0
  218. package/src/query.ts +379 -0
  219. package/src/reach.ts +184 -0
  220. package/src/relation.ts +395 -0
  221. package/src/report-sarif.ts +137 -0
  222. package/src/site.ts +36 -1
  223. package/src/std.ts +73 -0
  224. package/src/subsume.ts +690 -0
  225. package/src/trim.ts +195 -0
  226. package/src/tsconfig.json +10 -4
  227. package/src/type.ts +51 -2
  228. package/src/unify.ts +311 -16
  229. package/src/utility.ts +139 -1
  230. package/src/val/AggFuncVal.ts +319 -0
  231. package/src/val/ArithFuncVal.ts +108 -0
  232. package/src/val/BagVal.ts +101 -4
  233. package/src/val/CloseFuncVal.ts +9 -1
  234. package/src/val/ConjunctVal.ts +20 -0
  235. package/src/val/ConstraintVal.ts +1699 -116
  236. package/src/val/CopyFuncVal.ts +7 -1
  237. package/src/val/Decimal.ts +15 -0
  238. package/src/val/DeprecateFuncVal.ts +84 -0
  239. package/src/val/DisjunctVal.ts +139 -28
  240. package/src/val/EachFuncVal.ts +133 -0
  241. package/src/val/ExpectVal.ts +43 -6
  242. package/src/val/FeatureVal.ts +1 -1
  243. package/src/val/FilterFuncVal.ts +154 -0
  244. package/src/val/FuncBaseVal.ts +200 -3
  245. package/src/val/HideFuncVal.ts +0 -2
  246. package/src/val/IdFuncVal.ts +91 -0
  247. package/src/val/JunctionVal.ts +7 -1
  248. package/src/val/KeyFuncVal.ts +39 -35
  249. package/src/val/ListVal.ts +125 -18
  250. package/src/val/LowerFuncVal.ts +0 -1
  251. package/src/val/MapVal.ts +110 -8
  252. package/src/val/MatchFuncVal.ts +176 -0
  253. package/src/val/MoveFuncVal.ts +0 -2
  254. package/src/val/NilVal.ts +25 -0
  255. package/src/val/OpBaseVal.ts +26 -3
  256. package/src/val/OpenFuncVal.ts +4 -2
  257. package/src/val/PackFuncVal.ts +175 -0
  258. package/src/val/PathFuncVal.ts +0 -1
  259. package/src/val/PlaceVal.ts +193 -0
  260. package/src/val/PlusOpVal.ts +11 -2
  261. package/src/val/PrefFuncVal.ts +0 -1
  262. package/src/val/PrefVal.ts +79 -36
  263. package/src/val/RefVal.ts +163 -30
  264. package/src/val/ReferFuncVal.ts +387 -0
  265. package/src/val/ScalarKindVal.ts +0 -13
  266. package/src/val/TopVal.ts +0 -1
  267. package/src/val/TypeFuncVal.ts +0 -2
  268. package/src/val/UpperFuncVal.ts +0 -1
  269. package/src/val/Val.ts +213 -3
  270. package/src/val/VarVal.ts +0 -1
  271. package/src/val/arith.ts +316 -0
  272. package/src/vet.ts +992 -0
  273. package/src/walk.ts +99 -0
@@ -17,7 +17,7 @@ import {
17
17
  } from '../err'
18
18
 
19
19
  import { AontuContext } from '../ctx'
20
- import { unite } from '../unify'
20
+ import { unite, withDepth } from '../unify'
21
21
 
22
22
  import {
23
23
  explainOpen,
@@ -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
@@ -83,7 +95,11 @@ class OpBaseVal extends FeatureVal {
83
95
 
84
96
  for (let arg of this.peg) {
85
97
  if (!arg.done) {
86
- arg = arg.unify(top(), ctx, ec(te, 'ARG'))
98
+ // Charged to the depth budget: this recurses without going
99
+ // through `unite`, so the counter would otherwise stay flat
100
+ // while the stack grows (see withDepth in unify.ts).
101
+ const a = arg
102
+ arg = withDepth(ctx, a, top(), () => a.unify(top(), ctx, ec(te, 'ARG')))
87
103
  }
88
104
  pegdone &&= arg.done
89
105
  newpeg.push(arg)
@@ -161,10 +177,17 @@ class OpBaseVal extends FeatureVal {
161
177
  }
162
178
 
163
179
 
164
- clone(ctx: AontuContext, _spec?: ValSpec): Val {
180
+ clone(ctx: AontuContext, spec?: ValSpec): Val {
165
181
  let out = (super.clone(ctx, {
166
182
  peg: this.peg,
167
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
+ }
168
191
  return out
169
192
  }
170
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
 
@@ -0,0 +1,175 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // GENERATION: `pack(data, tmpl)` (G8 phase 1,
4
+ // docs/capability-review/g8-generation.md). One child of the result map
5
+ // per child of `data`, each of them `tmpl` cloned at the destination.
6
+ //
7
+ // names: [web, auth, billing]
8
+ // deploy: pack($.names, {
9
+ // image: "acme/" + key() + ":1.4.2"
10
+ // replicas: *2 | integer
11
+ // })
12
+ //
13
+ // WHY A FUNCTION AND NOT SYNTAX. A comprehension would need keywords,
14
+ // a scoping rule, and string interpolation for the computed name; the
15
+ // language already has functions, spread templates, and `key()`, and
16
+ // `pack` is those three composed. A call is also DATA -- a named node
17
+ // in the tree -- so a document that generates stays as analysable as
18
+ // one that does not.
19
+ //
20
+ // KEYS ARE DATA, NOT POSITION. For a list, the strings themselves are
21
+ // the keys (a non-string element is refused: `pack_key`); for a map,
22
+ // its keys. Terraform's `count` keyed by index is the counter-example
23
+ // the review names: reordering the data churns every downstream child.
24
+ // Duplicate keys are not an error -- the colliding children unify,
25
+ // exactly as duplicate source keys already merge.
26
+ //
27
+ // TOTALITY. `pack` iterates a finite, settled bag and cannot call
28
+ // itself: the number of children it can produce is fixed by data that
29
+ // already exists. Nothing here can recurse, which is the guarantee the
30
+ // combinators exist to keep.
31
+
32
+ import type {
33
+ Val,
34
+ ValSpec,
35
+ } from '../type'
36
+
37
+ import {
38
+ AontuContext,
39
+ } from '../ctx'
40
+
41
+ import { unite } from '../unify'
42
+ import { makeNilErr } from '../err'
43
+ import { MapVal } from './MapVal'
44
+ import { FuncBaseVal } from './FuncBaseVal'
45
+ import { repathInstance } from './Val'
46
+ import { fillPlace } from './PlaceVal'
47
+
48
+
49
+ // The keys a data bag names, in the order the result must carry them,
50
+ // or a code naming what is wrong with it. Shared with `each`, which
51
+ // asks the same question of the same argument and answers it with the
52
+ // values rather than the keys.
53
+ function dataKeys(data: Val | undefined): string[] | string {
54
+ const d: any = data
55
+
56
+ if (true === d?.isMap) {
57
+ return Object.keys(d.peg)
58
+ }
59
+
60
+ if (true === d?.isList) {
61
+ const out: string[] = []
62
+ for (const el of d.peg as Val[]) {
63
+ const e: any = el
64
+ // A key is a NAME, and only a string is one. A number would
65
+ // key by position under another spelling, which is the failure
66
+ // mode the data-keyed rule exists to refuse.
67
+ if (true !== e?.isScalar || 'string' !== typeof e.peg) {
68
+ return 'pack_key'
69
+ }
70
+ out.push(e.peg)
71
+ }
72
+ return out
73
+ }
74
+
75
+ return 'pack_data'
76
+ }
77
+
78
+
79
+ class PackFuncVal extends FuncBaseVal {
80
+ isPackFunc = true
81
+
82
+ // THE STAGING RULE (G8 phase 0, see AontuContext.settle). A
83
+ // generator's data argument is not settled merely by being `done`
84
+ // once: a sibling conjunct, an include or a spread can still merge
85
+ // keys into it, and children generated from the half-merged bag
86
+ // would be missing. It fires on the settle pass and only then.
87
+ staged = true
88
+
89
+ constructor(
90
+ spec: ValSpec,
91
+ ctx?: AontuContext
92
+ ) {
93
+ super(spec, ctx)
94
+ }
95
+
96
+
97
+
98
+ funcname() {
99
+ return 'pack'
100
+ }
101
+
102
+
103
+ // The TEMPLATE IS NOT AN ARGUMENT TO DRIVE. Driving it would resolve
104
+ // its `key()` at the CALL SITE -- the one position the template is
105
+ // never used at -- and freeze whatever else in it is path-dependent
106
+ // there too. The data argument is driven by `unify` below instead,
107
+ // one argument by hand rather than all of them by the base.
108
+ prepare(_ctx: AontuContext, _args: Val[]) {
109
+ return null
110
+ }
111
+
112
+
113
+ unify(peer: Val, ctx: AontuContext): Val {
114
+ // ONE argument is driven: the data. The template is not (see
115
+ // prepare above), and driveStagedArgs answers whether the data has
116
+ // settled -- the other half of "ready to fire".
117
+ const ready = this.driveStagedArgs(ctx, 1)
118
+
119
+ if (!ready || !ctx.settle) {
120
+ return this.residuate(peer, ctx)
121
+ }
122
+
123
+ return super.unify(peer, ctx)
124
+ }
125
+
126
+
127
+ resolve(ctx: AontuContext, args: Val[]) {
128
+ const keys = dataKeys(args?.[0])
129
+ if ('string' === typeof keys) {
130
+ return makeNilErr(ctx, keys, this)
131
+ }
132
+
133
+ // Arity is checked at parse (funcArity), so both arguments are here.
134
+ const tmpl: Val = args[1]
135
+ const peg: Record<string, Val> = {}
136
+
137
+ const src: any = args[0]
138
+
139
+ for (const key of keys) {
140
+ const keyctx = ctx.descend(key)
141
+ // THE PLACEHOLDER BINDS THE SOURCE CHILD (G8 phase 3): inside a
142
+ // generator's template `_` is the datum this child is being made
143
+ // FROM. For a map that is the child's value; for a list of names
144
+ // it is the name, which is also the key -- so `_` and `key()`
145
+ // agree there, and differ the moment the data is a map.
146
+ const source: Val = true === src?.isMap ? src.peg[key] : src.peg[keys.indexOf(key)]
147
+ // CLONED, never shared. A spread may share a template that holds
148
+ // nothing path-dependent (MapVal.spreadClone), because a spread
149
+ // CONSTRAINS a child that exists; a generator's template IS the
150
+ // child, and a child is a position. Sharing left every generated
151
+ // child pointing at the template's own parse-time location, which
152
+ // is the position the template is never used at -- visible as the
153
+ // site an error inside a generated child reports.
154
+ //
155
+ // A FULL INSTANCE, to the leaves (`dup`, ADR-005): a bare clone
156
+ // shares the inner structure of any call, preference or
157
+ // operation in the template, so the first child's resolution of
158
+ // a shared key()/ref answered for every child (BUGS.md §8, §9).
159
+ const inst = tmpl.clone(keyctx, { dup: true })
160
+ repathInstance(inst, inst.path)
161
+ const child = fillPlace(inst, source, keyctx)
162
+ peg[key] = undefined === peg[key] ? child :
163
+ unite(keyctx, peg[key], child, 'pack')
164
+ }
165
+
166
+ return new MapVal({ peg }, ctx)
167
+ }
168
+
169
+ } /* node:coverage ignore next 7 */
170
+
171
+
172
+ export {
173
+ dataKeys,
174
+ PackFuncVal,
175
+ }
@@ -12,7 +12,6 @@ import {
12
12
 
13
13
  import { makeNilErr } from '../err'
14
14
 
15
- import { NilVal } from '../val/NilVal'
16
15
  import { RefVal } from '../val/RefVal'
17
16
  import { FuncBaseVal } from './FuncBaseVal'
18
17
 
@@ -0,0 +1,193 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // THE PLACEHOLDER `_` (G8 phase 3,
4
+ // docs/capability-review/g8-generation.md): a HOLE in a call, filled
5
+ // by whatever the call is unified with.
6
+ //
7
+ // x: {&: {m: _ + 2}} x: a: m: 1 -> a: m: 3
8
+ // greeting: upper(_) & hello -> "HELLO"
9
+ //
10
+ // WHY A HOLE AND NOT A FUNCTION PARAMETER. Aontu has no user-defined
11
+ // functions and will not get them (G8's Boundary): a function value
12
+ // crossing into the data plane is the step that makes a configuration
13
+ // language a programming language. A hole is not a parameter -- it
14
+ // cannot be named, passed, or partially applied. It says only "the
15
+ // value that arrives here", which is what a template needs to compute
16
+ // from the value it lands on.
17
+ //
18
+ // ALONE, `_` IS TOP WITH A MARK: it admits everything and is filled by
19
+ // its peer, exactly as TOP is dropped by one. What makes it different
20
+ // is that a CALL can see it, and a call that holds one waits for a
21
+ // peer to fill it instead of resolving around it. Unfilled at
22
+ // generation it is an error, as TOP is -- a hole is not a value.
23
+
24
+ import type {
25
+ Val,
26
+ ValSpec,
27
+ } from '../type'
28
+
29
+ import {
30
+ AontuContext,
31
+ } from '../ctx'
32
+
33
+ import { Val as ValBase } from './Val'
34
+
35
+
36
+ class PlaceVal extends ValBase {
37
+ isPlace = true
38
+
39
+ constructor(
40
+ spec: ValSpec,
41
+ ctx?: AontuContext
42
+ ) {
43
+ super(spec, ctx)
44
+ }
45
+
46
+
47
+ unify(peer: Val, _ctx: AontuContext): Val {
48
+ // The peer FILLS the hole. Against TOP there is nothing to fill it
49
+ // with, so it waits -- and waiting is not done, or a call holding
50
+ // it would resolve around it.
51
+ if (peer.isTop) {
52
+ this.notdone()
53
+ return this
54
+ }
55
+
56
+ return peer
57
+ }
58
+
59
+
60
+ get canon() {
61
+ return '_'
62
+ }
63
+
64
+
65
+ // A hole admits everything, so nothing sits above it -- the same
66
+ // answer TOP gives, for the same reason.
67
+ superior(): Val {
68
+ return this
69
+ }
70
+
71
+
72
+ }
73
+
74
+
75
+ // A HOLE BELONGS TO ITS NEAREST ENCLOSING GENERATOR. A `_` inside a
76
+ // generator's template (pack/each, arg 1) or condition (filter, arg 1)
77
+ // is that generator's to bind — "_ is the source child" — so neither
78
+ // the hole test nor the fill walk may cross into those arguments from
79
+ // outside. Before this boundary, `close(pack(d, _ & t))` reported a
80
+ // hole to the OUTER call, so an ordinary overlay statement was
81
+ // absorbed into the template instead of merging with the generated
82
+ // child (use-cases/BUGS.md §10), and an outer pack's fill pass
83
+ // captured a NESTED pack's hole lexically, binding it to the outer
84
+ // source (§34). The data argument (arg 0) is not a binding position,
85
+ // so it stays visible: a hole there is an outer hole as before.
86
+ function boundArgStart(v: any): number {
87
+ return true === v.isPackFunc || true === v.isEachFunc ||
88
+ true === v.isFilterFunc ? 1 : Infinity
89
+ }
90
+
91
+
92
+ // Does this value CONTAIN a hole? Asked of a call before it resolves:
93
+ // a call holding one must wait for a peer to fill it. Holes inside a
94
+ // generator's own binding arguments are NOT this value's holes — see
95
+ // boundArgStart above.
96
+ function hasPlace(v: Val): boolean {
97
+ if (true === (v as any).isPlace) {
98
+ return true
99
+ }
100
+
101
+ const peg: any = (v as any).peg
102
+ const bound = boundArgStart(v)
103
+
104
+ if (Array.isArray(peg)) {
105
+ for (let cI = 0; cI < peg.length && cI < bound; cI++) {
106
+ const c = peg[cI]
107
+ if (true === c?.isVal && hasPlace(c)) {
108
+ return true
109
+ }
110
+ }
111
+ }
112
+ else if (true === peg?.isVal) {
113
+ return hasPlace(peg)
114
+ }
115
+ else if (null != peg && 'object' === typeof peg) {
116
+ for (const k of Object.keys(peg)) {
117
+ if (true === peg[k]?.isVal && hasPlace(peg[k])) {
118
+ return true
119
+ }
120
+ }
121
+ }
122
+
123
+ return false
124
+ }
125
+
126
+
127
+ // The same tree with every hole filled by `fill`. Answers the value
128
+ // UNCHANGED when it holds no hole, so a caller can test identity to
129
+ // know whether anything was filled -- and so a tree with no hole is
130
+ // never needlessly cloned. A nested generator's binding arguments are
131
+ // left untouched (boundArgStart): those holes are the inner
132
+ // generator's to fill with its OWN source children when it fires.
133
+ function fillPlace(v: Val, fill: Val, ctx: AontuContext): Val {
134
+ if (true === (v as any).isPlace) {
135
+ return fill
136
+ }
137
+
138
+ const peg: any = (v as any).peg
139
+ const bound = boundArgStart(v)
140
+
141
+ if (Array.isArray(peg)) {
142
+ let changed = false
143
+ const out = peg.map((c: any, cI: number) => {
144
+ if (true !== c?.isVal || bound <= cI) {
145
+ return c
146
+ }
147
+ const f = fillPlace(c, fill, ctx)
148
+ changed = changed || f !== c
149
+ return f
150
+ })
151
+ return changed ? rebuild(v, out, ctx) : v
152
+ }
153
+
154
+ if (true === peg?.isVal) {
155
+ const f = fillPlace(peg, fill, ctx)
156
+ return f === peg ? v : rebuild(v, f, ctx)
157
+ }
158
+
159
+ if (null != peg && 'object' === typeof peg) {
160
+ let changed = false
161
+ const out: Record<string, Val> = {}
162
+ for (const k of Object.keys(peg)) {
163
+ const c = peg[k]
164
+ // No isVal guard: a slot holding something that is not a Val
165
+ // answers itself, because fillPlace's own first tests -- is it a
166
+ // hole, has it a peg -- are both false for one.
167
+ const f = fillPlace(c, fill, ctx)
168
+ changed = changed || f !== c
169
+ out[k] = f
170
+ }
171
+ return changed ? rebuild(v, out, ctx) : v
172
+ }
173
+
174
+ return v
175
+ }
176
+
177
+
178
+ // A clone carrying a new peg. `clone` shares the peg by reference (see
179
+ // Val.clone), which is exactly what must NOT happen here: the tree
180
+ // being filled is a template, and the fill is one destination's.
181
+ function rebuild(v: Val, peg: any, ctx: AontuContext): Val {
182
+ const out: any = v.clone(ctx)
183
+ out.peg = peg
184
+ out.dc = 0
185
+ return out
186
+ } /* node:coverage ignore next 8 */
187
+
188
+
189
+ export {
190
+ hasPlace,
191
+ fillPlace,
192
+ PlaceVal,
193
+ }
@@ -157,9 +157,18 @@ class PlusOpVal extends OpBaseVal {
157
157
  }
158
158
 
159
159
  // Float with float or integer: unchanged R5 contagion, binary64
160
- // addition, float result (`1 + 2.0` is `3.0`).
160
+ // addition, float result (`1 + 2.0` is `3.0`) -- but a sum that
161
+ // leaves binary64's finite range is NOT a value. Aontu is a JSON
162
+ // superset and there is no notation for an infinity, so one here
163
+ // used to escape as `[aontu/internal]` in TypeScript and as Go's
164
+ // raw `json: unsupported value: +Inf` with no code at all
165
+ // (use-cases/BUGS.md 39). The same check governs the arithmetic
166
+ // family (float_overflow, ts/src/val/arith.ts).
161
167
  if ('float' === ak || 'float' === bk) {
162
- return new NumberVal({ peg: av.peg + bv.peg })
168
+ const sum = av.peg + bv.peg
169
+ return Number.isFinite(sum) ?
170
+ new NumberVal({ peg: sum }) :
171
+ makeNilErr(ctx, 'float_overflow', this, undefined, 'add')
163
172
  }
164
173
 
165
174
  // Both operands are on the exact ladder: promote to the widest.
@@ -16,7 +16,6 @@ import {
16
16
  walk
17
17
  } from '../utility'
18
18
 
19
- import { NilVal } from '../val/NilVal'
20
19
  import { PrefVal } from '../val/PrefVal'
21
20
  import { FuncBaseVal } from './FuncBaseVal'
22
21
 
@@ -23,7 +23,20 @@ import {
23
23
  import { top } from './top'
24
24
 
25
25
  import { FeatureVal } from './FeatureVal'
26
- import { ScalarKindVal, kindFamily } from './ScalarKindVal'
26
+
27
+
28
+ // The innermost preferred value under every pref layer: the value a
29
+ // preference of ANY rank ultimately defends, and the one generation
30
+ // emits for it. Shared by the disjunct admission gate (DisjunctVal),
31
+ // the defaulted-scrutinee rule (MatchFuncVal) and the effective-default
32
+ // walk (subsume.ts) so "the default's value" cannot mean three things.
33
+ function prefInnerPeg(v: Val): Val {
34
+ let out: any = v
35
+ while (true === out?.isPref) {
36
+ out = out.peg
37
+ }
38
+ return out
39
+ }
27
40
 
28
41
 
29
42
  class PrefVal extends FeatureVal {
@@ -37,19 +50,28 @@ class PrefVal extends FeatureVal {
37
50
  // Both are assigned by resuper(), called from the constructor.
38
51
  superpeg!: Val
39
52
 
40
- // The gate an overriding peer must pass. A preference is a DEFAULT, so
41
- // a concrete peer replaces it -- but only within the preferred value's
42
- // FAMILY: `*lower(1.1) & a` is a conflict, not an override.
53
+ // THE GATE AN OVERRIDING PEER MUST PASS IS `superpeg` ITSELF — the
54
+ // preferred value's own KIND, not its family.
43
55
  //
44
- // Family and not leaf, because the number tower made `integer` and
45
- // `float` disjoint siblings under `number`. Gating on the leaf would
46
- // turn `*lower(2.2) & 3` (a float default overridden by an integer)
47
- // into an error, which is a tightening no document asked for; gating
48
- // on the family keeps it working and makes the mirror case
49
- // (`*2 & 3.0`, an error before the tower) work too. For every
50
- // non-numeric value the family root IS the leaf, so this is the
51
- // preferred value's type as before.
52
- familypeg!: Val
56
+ // A preference is a DEFAULT, so a concrete peer replaces it, but only
57
+ // where the peer is the same kind of thing: `*lower(1.1) & a` is a
58
+ // conflict, not an override. The number tower made `integer` and
59
+ // `float` disjoint siblings under `number`, and this gate used to
60
+ // widen to that family — so `*2.2 & 3` worked, and so did
61
+ // `*8080 & 3.5`. Those are ONE rule in two directions; no kind-based
62
+ // gate can keep the first and refuse the second.
63
+ //
64
+ // Refusing both is the choice, because the second is the documented
65
+ // default idiom: `port: *8080 | integer` accepted 3.5 in both ports,
66
+ // silently widening every key written that way from integer to
67
+ // number — the finding §6 of the 2026-08-21 status report calls the
68
+ // most consequential, precisely because docs/skill/examples.md
69
+ // teaches the idiom to agents.
70
+ //
71
+ // What the tightening costs is named rather than hidden: mixing the
72
+ // numeric leaves around a preference is now an error instead of a
73
+ // silent widening. `*1.5 & integer` used to answer `integer` and
74
+ // DISCARD a default that could never apply; it now says so.
53
75
 
54
76
  rank: number = 0
55
77
 
@@ -74,7 +96,21 @@ class PrefVal extends FeatureVal {
74
96
  // Recompute the type yardstick and the override gate from the current
75
97
  // peg. Called again whenever the peg resolves (e.g. a ref).
76
98
  private resuper() {
77
- const peg: any = this.peg
99
+ // THE RANK-UNIFORM MEET (ADR-004). The yardstick is the INNERMOST
100
+ // preferred value's kind, whatever the preference's rank: `**1.5`
101
+ // defends `float` exactly as `*1.5` does. The old rule read the
102
+ // immediate peg, and a rank>=2 peg is itself a PrefVal whose
103
+ // superior is top -- so ANY conjunct overrode a ranked default
104
+ // (`**1.5 & float` dropped the default and died as mapval_no_gen;
105
+ // `**2|integer` met by a bare `integer` lost the default the
106
+ // spelling exists to carry -- use-cases/BUGS.md §3), while the
107
+ // rank-1 spelling of the same document kept it. One rule, every
108
+ // rank. Pinned by test/spec/pref.tsv (pref-rank2-* rows, and the
109
+ // flipped pref-nested-concrete-wins).
110
+ let peg: any = this.peg
111
+ while (true === peg?.isPref) {
112
+ peg = peg.peg
113
+ }
78
114
 
79
115
  // A preference whose peg is ITSELF a kind (`*integer`) constrains
80
116
  // nothing: there is no type-of-a-type in this lattice, so any peer
@@ -83,28 +119,13 @@ class PrefVal extends FeatureVal {
83
119
  // now that a leaf kind lifts to `number`, it has to be said.)
84
120
  if (true === peg.isScalarKind) {
85
121
  this.superpeg = top()
86
- this.familypeg = this.superpeg
87
122
  return
88
123
  }
89
124
 
90
- const sup: any = peg.superior()
91
- this.superpeg = sup
92
-
93
125
  // No optional chain: superior() is contractually non-null (every
94
126
  // Val returns one, a NilVal returning itself), so guarding against
95
127
  // nullish here would claim a possibility the type does not have.
96
- if (true === sup.isScalarKind) {
97
- const family = kindFamily(sup.peg)
98
- // The gate stands in for the preferred value in any conflict it
99
- // reports, so it must carry the same site and path as the type it
100
- // widens -- otherwise NilVal.make picks a different primary and
101
- // the error moves to the wrong path.
102
- this.familypeg = family === sup.peg ?
103
- sup : sup.place(new ScalarKindVal({ peg: family, path: sup.path }))
104
- }
105
- else {
106
- this.familypeg = sup
107
- }
128
+ this.superpeg = peg.superior()
108
129
  }
109
130
 
110
131
 
@@ -163,13 +184,21 @@ class PrefVal extends FeatureVal {
163
184
  why += 'super-'
164
185
 
165
186
  out = unite(te ? ctx.clone({ explain: ec(te, 'SUPER') }) : ctx,
166
- this.familypeg, peer, 'pref-super/' + this.id)
187
+ this.superpeg, peer, 'pref-super/' + this.id)
167
188
 
168
189
  // The peer added nothing beyond a type the preferred value already
169
190
  // satisfies (`*1 & integer`, `*1 & number`), so the preference
170
- // stands. Anything else is a concrete override and wins.
171
- if (out.same(this.superpeg) || out.same(this.familypeg)) {
172
- out = this.peg
191
+ // stands — as ITSELF, rank intact (ADR-004). Returning the peg
192
+ // here (the old rule) demoted the preference to a concrete value
193
+ // at rank 0 and to a lower rank above it, which both destroyed
194
+ // overridability (`*1 & integer` then `2` was a conflict) and
195
+ // broke the layered-defaults ladder: a team's `**debug|string`
196
+ // meeting an env's `string` branch produced a rank-0 `*debug`
197
+ // that then fought the env's own rank-0 `*warn` as an equal.
198
+ // Anything else is a concrete override and wins (subject to the
199
+ // disjunct admission gate in DisjunctVal).
200
+ if (out.same(this.superpeg)) {
201
+ out = this
173
202
  why += 'same'
174
203
  }
175
204
 
@@ -205,7 +234,20 @@ class PrefVal extends FeatureVal {
205
234
 
206
235
  clone(ctx: AontuContext, spec?: ValSpec): Val {
207
236
  let out = (super.clone(ctx, spec) as PrefVal)
208
- // out.pref = this.pref.clone(null, ctx)
237
+ // THE PER-DESTINATION INSTANTIATION RULE (ADR-005). The default
238
+ // clone shares the preferred value (`peg: this.peg` in Val.clone)
239
+ // — a cloned pref spread template resolves its inner value at the
240
+ // template's own location, pinned behaviour. But a template
241
+ // INSTANCE must own it: with the peg shared, a rank-2 default
242
+ // (`**key(1) | string`) in a pack template resolved its one
243
+ // shared inner key() at the first destination and every child got
244
+ // the first child's key (use-cases/BUGS.md §9 — rank 1 escaped
245
+ // only because its unify builds a fresh PrefVal per meet). The
246
+ // superpeg yardstick from the shared peg still holds: the clone's
247
+ // innermost value is the same kind.
248
+ if (true === spec?.dup && true === (this.peg as any)?.isVal) {
249
+ out.peg = this.peg.clone(ctx, { dup: true })
250
+ }
209
251
  return out
210
252
  }
211
253
 
@@ -227,9 +269,10 @@ class PrefVal extends FeatureVal {
227
269
 
228
270
  return val.gen(ctx)
229
271
  }
230
- } /* node:coverage ignore next 6 */
272
+ } /* node:coverage ignore next 7 */
231
273
 
232
274
 
233
275
  export {
234
276
  PrefVal,
277
+ prefInnerPeg,
235
278
  }