aontu 0.52.1 → 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 (272) 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 +96 -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 +13 -0
  13. package/dist/ctx.js +43 -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 +38 -7
  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 +167 -4
  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 +512 -69
  33. package/dist/lang.js.map +1 -1
  34. package/dist/lsp.d.ts +9 -2
  35. package/dist/lsp.js +262 -47
  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 +2 -1
  83. package/dist/unify.js +253 -36
  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 +7 -1
  103. package/dist/val/ConstraintVal.js +490 -40
  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.js +27 -4
  120. package/dist/val/ExpectVal.js.map +1 -1
  121. package/dist/val/FeatureVal.js +1 -1
  122. package/dist/val/FeatureVal.js.map +1 -1
  123. package/dist/val/FilterFuncVal.d.ts +15 -0
  124. package/dist/val/FilterFuncVal.js +91 -0
  125. package/dist/val/FilterFuncVal.js.map +1 -0
  126. package/dist/val/FuncBaseVal.d.ts +6 -1
  127. package/dist/val/FuncBaseVal.js +171 -1
  128. package/dist/val/FuncBaseVal.js.map +1 -1
  129. package/dist/val/HideFuncVal.js.map +1 -1
  130. package/dist/val/IdFuncVal.d.ts +13 -0
  131. package/dist/val/IdFuncVal.js +54 -0
  132. package/dist/val/IdFuncVal.js.map +1 -0
  133. package/dist/val/JunctionVal.js +7 -1
  134. package/dist/val/JunctionVal.js.map +1 -1
  135. package/dist/val/KeyFuncVal.d.ts +1 -1
  136. package/dist/val/KeyFuncVal.js +38 -30
  137. package/dist/val/KeyFuncVal.js.map +1 -1
  138. package/dist/val/ListVal.js +101 -14
  139. package/dist/val/ListVal.js.map +1 -1
  140. package/dist/val/LowerFuncVal.js.map +1 -1
  141. package/dist/val/MapVal.js +93 -8
  142. package/dist/val/MapVal.js.map +1 -1
  143. package/dist/val/MatchFuncVal.d.ts +15 -0
  144. package/dist/val/MatchFuncVal.js +107 -0
  145. package/dist/val/MatchFuncVal.js.map +1 -0
  146. package/dist/val/MoveFuncVal.js.map +1 -1
  147. package/dist/val/NilVal.js +24 -0
  148. package/dist/val/NilVal.js.map +1 -1
  149. package/dist/val/OpBaseVal.d.ts +1 -1
  150. package/dist/val/OpBaseVal.js +19 -1
  151. package/dist/val/OpBaseVal.js.map +1 -1
  152. package/dist/val/OpenFuncVal.js +4 -1
  153. package/dist/val/OpenFuncVal.js.map +1 -1
  154. package/dist/val/PackFuncVal.d.ts +15 -0
  155. package/dist/val/PackFuncVal.js +108 -0
  156. package/dist/val/PackFuncVal.js.map +1 -0
  157. package/dist/val/PathFuncVal.js.map +1 -1
  158. package/dist/val/PlaceVal.d.ts +13 -0
  159. package/dist/val/PlaceVal.js +131 -0
  160. package/dist/val/PlaceVal.js.map +1 -0
  161. package/dist/val/PlusOpVal.js +11 -2
  162. package/dist/val/PlusOpVal.js.map +1 -1
  163. package/dist/val/PrefFuncVal.js.map +1 -1
  164. package/dist/val/PrefVal.d.ts +2 -2
  165. package/dist/val/PrefVal.js +78 -23
  166. package/dist/val/PrefVal.js.map +1 -1
  167. package/dist/val/RefVal.d.ts +1 -1
  168. package/dist/val/RefVal.js +89 -7
  169. package/dist/val/RefVal.js.map +1 -1
  170. package/dist/val/ReferFuncVal.d.ts +36 -0
  171. package/dist/val/ReferFuncVal.js +303 -0
  172. package/dist/val/ReferFuncVal.js.map +1 -0
  173. package/dist/val/ScalarKindVal.d.ts +1 -2
  174. package/dist/val/ScalarKindVal.js +0 -11
  175. package/dist/val/ScalarKindVal.js.map +1 -1
  176. package/dist/val/TopVal.js.map +1 -1
  177. package/dist/val/TypeFuncVal.js.map +1 -1
  178. package/dist/val/UpperFuncVal.js.map +1 -1
  179. package/dist/val/Val.d.ts +8 -1
  180. package/dist/val/Val.js +150 -1
  181. package/dist/val/Val.js.map +1 -1
  182. package/dist/val/VarVal.js.map +1 -1
  183. package/dist/val/arith.d.ts +6 -0
  184. package/dist/val/arith.js +170 -0
  185. package/dist/val/arith.js.map +1 -0
  186. package/dist/vet.d.ts +45 -0
  187. package/dist/vet.js +776 -0
  188. package/dist/vet.js.map +1 -0
  189. package/dist/walk.d.ts +2 -0
  190. package/dist/walk.js +91 -0
  191. package/dist/walk.js.map +1 -0
  192. package/grammar/aontu.gbnf +130 -0
  193. package/grammar/aontu.lark +113 -0
  194. package/package.json +21 -6
  195. package/skill/SKILL.md +37 -0
  196. package/skill/error-codes.md +62 -0
  197. package/skill/examples.md +99 -0
  198. package/skill/grammar-card.md +57 -0
  199. package/src/agentsmd.ts +135 -0
  200. package/src/aontu.ts +135 -4
  201. package/src/cli.ts +2858 -71
  202. package/src/ctx.ts +73 -1
  203. package/src/diff.ts +196 -0
  204. package/src/err.ts +42 -7
  205. package/src/graph.ts +135 -0
  206. package/src/hcanon.ts +169 -0
  207. package/src/hints.ts +208 -4
  208. package/src/jsonschema.ts +511 -0
  209. package/src/lang.ts +570 -70
  210. package/src/lsp.ts +281 -48
  211. package/src/mcp-server.ts +187 -0
  212. package/src/mcp.ts +993 -0
  213. package/src/mod-tool.ts +679 -0
  214. package/src/mod.ts +344 -0
  215. package/src/patch.ts +624 -0
  216. package/src/provenance.ts +430 -0
  217. package/src/query.ts +379 -0
  218. package/src/reach.ts +184 -0
  219. package/src/relation.ts +395 -0
  220. package/src/report-sarif.ts +137 -0
  221. package/src/site.ts +36 -1
  222. package/src/std.ts +73 -0
  223. package/src/subsume.ts +690 -0
  224. package/src/trim.ts +195 -0
  225. package/src/tsconfig.json +10 -4
  226. package/src/type.ts +51 -2
  227. package/src/unify.ts +274 -36
  228. package/src/utility.ts +139 -1
  229. package/src/val/AggFuncVal.ts +319 -0
  230. package/src/val/ArithFuncVal.ts +108 -0
  231. package/src/val/BagVal.ts +101 -4
  232. package/src/val/CloseFuncVal.ts +9 -1
  233. package/src/val/ConjunctVal.ts +20 -0
  234. package/src/val/ConstraintVal.ts +542 -43
  235. package/src/val/CopyFuncVal.ts +7 -1
  236. package/src/val/Decimal.ts +15 -0
  237. package/src/val/DeprecateFuncVal.ts +84 -0
  238. package/src/val/DisjunctVal.ts +139 -28
  239. package/src/val/EachFuncVal.ts +133 -0
  240. package/src/val/ExpectVal.ts +28 -6
  241. package/src/val/FeatureVal.ts +1 -1
  242. package/src/val/FilterFuncVal.ts +154 -0
  243. package/src/val/FuncBaseVal.ts +192 -1
  244. package/src/val/HideFuncVal.ts +0 -2
  245. package/src/val/IdFuncVal.ts +91 -0
  246. package/src/val/JunctionVal.ts +7 -1
  247. package/src/val/KeyFuncVal.ts +39 -35
  248. package/src/val/ListVal.ts +108 -15
  249. package/src/val/LowerFuncVal.ts +0 -1
  250. package/src/val/MapVal.ts +99 -8
  251. package/src/val/MatchFuncVal.ts +176 -0
  252. package/src/val/MoveFuncVal.ts +0 -2
  253. package/src/val/NilVal.ts +25 -0
  254. package/src/val/OpBaseVal.ts +20 -1
  255. package/src/val/OpenFuncVal.ts +4 -2
  256. package/src/val/PackFuncVal.ts +175 -0
  257. package/src/val/PathFuncVal.ts +0 -1
  258. package/src/val/PlaceVal.ts +193 -0
  259. package/src/val/PlusOpVal.ts +11 -2
  260. package/src/val/PrefFuncVal.ts +0 -1
  261. package/src/val/PrefVal.ts +79 -36
  262. package/src/val/RefVal.ts +91 -8
  263. package/src/val/ReferFuncVal.ts +387 -0
  264. package/src/val/ScalarKindVal.ts +0 -13
  265. package/src/val/TopVal.ts +0 -1
  266. package/src/val/TypeFuncVal.ts +0 -2
  267. package/src/val/UpperFuncVal.ts +0 -1
  268. package/src/val/Val.ts +205 -2
  269. package/src/val/VarVal.ts +0 -1
  270. package/src/val/arith.ts +316 -0
  271. package/src/vet.ts +992 -0
  272. package/src/walk.ts +99 -0
@@ -0,0 +1,154 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // GENERATION: `filter(data, cond)` (G8 phase 2,
4
+ // docs/capability-review/g8-generation.md). The children of `data`
5
+ // that unify with `cond`, keys preserved for a map and order for a
6
+ // list; the children that do not are DROPPED, not refused.
7
+ //
8
+ // debugged: filter($.services, {debug: true})
9
+ // sidecars: pack($.debugged, {image: "acme/debug:1.0"})
10
+ //
11
+ // A CHILD IS KEPT WHEN THE CONDITION CHANGES NOTHING: the meet
12
+ // succeeds AND its answer is the child itself, which is to say the
13
+ // child ALREADY satisfies the condition. Mere unifiability is not the
14
+ // test, and cannot be: a map is open, so `{p:2}` unifies with
15
+ // `{debug:true}` perfectly well by GAINING the key -- and a filter
16
+ // that keeps everything that could be made to match keeps everything.
17
+ // The design named unifiability; its own example
18
+ // (`filter($.services, {debug:true})`) is unsatisfiable under it.
19
+ //
20
+ // There is no predicate language here and there will not be one:
21
+ // `cond` is an ordinary Aontu value, so the constraint atoms compose
22
+ // with it for free (`filter($.deploy, {replicas: min(3)})`), and a
23
+ // document that can filter can be read by anything that can read a
24
+ // document.
25
+ //
26
+ // A FAILED CHILD IS AN ANSWER, not an error. The meet runs in TRIAL
27
+ // MODE -- the mechanism disjunction already uses to try each member
28
+ // against a peer (trialUnify in FuncBaseVal) -- so a child that does
29
+ // not match costs nothing and reports nothing.
30
+ //
31
+ // TOTALITY. `filter` iterates a finite, settled bag and cannot call
32
+ // itself, exactly as `pack` and `each` cannot.
33
+
34
+ import type {
35
+ Val,
36
+ ValSpec,
37
+ } from '../type'
38
+
39
+ import {
40
+ AontuContext,
41
+ } from '../ctx'
42
+
43
+ import { makeNilErr } from '../err'
44
+ import { MapVal } from './MapVal'
45
+ import { ListVal } from './ListVal'
46
+ import { FuncBaseVal, trialUnify } from './FuncBaseVal'
47
+ import { repathInstance } from './Val'
48
+ import { fillPlace } from './PlaceVal'
49
+
50
+
51
+ class FilterFuncVal extends FuncBaseVal {
52
+ isFilterFunc = true
53
+
54
+ // THE STAGING RULE (G8 phase 0). A subset of a bag that is still
55
+ // being merged into is a subset of the wrong bag.
56
+ staged = true
57
+
58
+ constructor(
59
+ spec: ValSpec,
60
+ ctx?: AontuContext
61
+ ) {
62
+ super(spec, ctx)
63
+ }
64
+
65
+
66
+ funcname() {
67
+ return 'filter'
68
+ }
69
+
70
+
71
+ // Neither argument is driven by the base: `unify` below drives the
72
+ // DATA by hand, because a staged func must advance the argument it
73
+ // is waiting on every pass rather than only on the one it fires.
74
+ //
75
+ // The CONDITION is not driven at all, and that is deliberate: it is
76
+ // a template, tested against each child at that child's position,
77
+ // so it may hold a `_` (G8 phase 3, the child it is being tested
78
+ // against) or a relative reference — neither of which has an answer
79
+ // at the call site. Driving it there would freeze both.
80
+ prepare(_ctx: AontuContext, _args: Val[]) {
81
+ return null
82
+ }
83
+
84
+
85
+ unify(peer: Val, ctx: AontuContext): Val {
86
+ const ready = this.driveStagedArgs(ctx, 1)
87
+
88
+ if (!ready || !ctx.settle) {
89
+ return this.residuate(peer, ctx)
90
+ }
91
+
92
+ return super.unify(peer, ctx)
93
+ }
94
+
95
+
96
+ resolve(ctx: AontuContext, args: Val[]) {
97
+ const data: any = args[0]
98
+ const cond: Val = args[1]
99
+
100
+ // The trial is run against CLONES: `unite` refines a bag in place,
101
+ // and a child that failed the test must reach the result -- when
102
+ // it passes -- exactly as it was written.
103
+ //
104
+ // Canon is the comparison because canon is what "the same value"
105
+ // MEANS in this language: it is the form the two ports agree on,
106
+ // the form `aontu diff` compares, and the form a hash is taken of.
107
+ const keeps = (child: Val, kctx: AontuContext): boolean => {
108
+ // `_` inside the condition binds the child being tested (G8
109
+ // phase 3), so a condition can be about the child as a whole
110
+ // rather than only about its shape. The condition is cloned as
111
+ // a FULL instance per trial (`dup`, ADR-005) — a bare clone
112
+ // shares call/pref innards across trials (see PackFuncVal).
113
+ const inst = cond.clone(kctx, { dup: true })
114
+ repathInstance(inst, inst.path)
115
+ const test = fillPlace(inst, child, kctx)
116
+ const met = trialUnify(kctx, child.clone(kctx), test)
117
+ return undefined !== met && met.canon === child.canon
118
+ }
119
+
120
+ if (true === data?.isMap) {
121
+ const peg: Record<string, Val> = {}
122
+ for (const key of Object.keys(data.peg)) {
123
+ const kctx = ctx.descend(key)
124
+ if (keeps(data.peg[key], kctx)) {
125
+ peg[key] = data.peg[key].clone(kctx)
126
+ }
127
+ }
128
+ return new MapVal({ peg }, ctx)
129
+ }
130
+
131
+ if (true === data?.isList) {
132
+ const peg: Val[] = []
133
+ for (const el of data.peg as Val[]) {
134
+ // The element context is the position it will END UP at, which
135
+ // is its index in the RESULT: dropping the third of five moves
136
+ // the fourth up, and a kept element must be pathed where it
137
+ // lands rather than where it came from.
138
+ const ectx = ctx.descend(String(peg.length))
139
+ if (keeps(el, ectx)) {
140
+ peg.push(el.clone(ectx))
141
+ }
142
+ }
143
+ return new ListVal({ peg }, ctx)
144
+ }
145
+
146
+ return makeNilErr(ctx, 'filter_data', this)
147
+ }
148
+
149
+ } /* node:coverage ignore next 5 */
150
+
151
+
152
+ export {
153
+ FilterFuncVal,
154
+ }
@@ -29,12 +29,53 @@ import {
29
29
 
30
30
  import { ConjunctVal } from '../val/ConjunctVal'
31
31
  import { FeatureVal } from '../val/FeatureVal'
32
+ import { hasPlace, fillPlace } from '../val/PlaceVal'
33
+
34
+
35
+ // A TRIAL meet: does `a` unify with `b`, and if so as what? Failure is
36
+ // an ANSWER here rather than an error, which is exactly what
37
+ // DisjunctVal already needs when it tries each member against a peer —
38
+ // so this is that mechanism (`ctx._trialMode`, which makes makeNilErr
39
+ // return the shared TRIAL_NIL instead of allocating and recording),
40
+ // lent to the combinators that select by unifiability.
41
+ //
42
+ // The error list and the trial flag are saved and restored in a
43
+ // `finally`: a trial that throws must not leave the surrounding
44
+ // evaluation collapsing every later error into the sentinel.
45
+ function trialUnify(ctx: AontuContext, a: Val, b: Val): Val | undefined {
46
+ const savedErr = ctx.err
47
+ const savedTrial = ctx._trialMode
48
+ const trialErr: any[] = []
49
+
50
+ ctx.err = trialErr
51
+ ctx._trialMode = true
52
+
53
+ let out: Val
54
+ try {
55
+ out = unite(ctx, a, b, 'trial')
56
+ }
57
+ finally {
58
+ ctx.err = savedErr
59
+ ctx._trialMode = savedTrial
60
+ }
61
+
62
+ return 0 < trialErr.length || out.isNil ? undefined : out
63
+ }
32
64
 
33
65
 
34
66
  class FuncBaseVal extends FeatureVal {
35
67
  isFunc = true
36
68
  isGenable = true
37
69
 
70
+ // THE STAGING RULE (G8 phase 0, see AontuContext.settle). A func
71
+ // whose answer depends on WHERE IT IS -- `key()`, whose answer is a
72
+ // segment of its own path, and the generation combinators, whose
73
+ // data argument can still be merged into by a sibling -- sets this
74
+ // and residuates until the model stops moving. Everything else
75
+ // resolves as soon as its arguments are done, which is the rule that
76
+ // has always been here.
77
+ staged = false
78
+
38
79
  constructor(
39
80
  spec: ValSpec,
40
81
  ctx?: AontuContext
@@ -58,7 +99,118 @@ class FuncBaseVal extends FeatureVal {
58
99
  }
59
100
 
60
101
 
102
+ // Drive the first `count` arguments IN PLACE, every pass — not only
103
+ // on the settle pass. A staged func waits for the model to settle,
104
+ // and its own arguments are part of that model: leaving them
105
+ // standing until settle would guarantee the model was still moving
106
+ // when settle arrived. Answers whether they are all done, which is
107
+ // the other half of "ready to fire".
108
+ driveStagedArgs(ctx: AontuContext, count: number): boolean {
109
+ const TOP = top()
110
+ let alldone = true
111
+
112
+ // THE SNAPSHOT WAITS FOR THE SOURCE (the spread-then-pack defect,
113
+ // use-cases/BUGS.md pack-refs family). A reference resolving inside
114
+ // a staged argument is this argument's SNAPSHOT of its source, and
115
+ // the snapshot is not part of the tree: a spread-injected relative
116
+ // reference inside a too-early copy dangles at the argument's
117
+ // location (`.containerPort` rebased under the generator, where no
118
+ // root traversal reaches it) and the generator never fires. The
119
+ // `argsnap` flag makes RefVal.find defer until the target has
120
+ // finished resolving IN THE TREE — where its own spreads and
121
+ // relative references answer at their real location — and only then
122
+ // take the copy. Inherited by every descended ctx, so a reference
123
+ // anywhere in the argument subtree waits the same way.
124
+ const actx: AontuContext = ctx.clone({})
125
+ ; (actx as any).argsnap = true
126
+
127
+ for (let i = 0; i < count && i < this.peg.length; i++) {
128
+ const arg: Val = this.peg[i]
129
+ if (!arg.done) {
130
+ // Charged to the depth budget, as FuncBaseVal's own arg loop is:
131
+ // this recurses without going through `unite`.
132
+ this.peg[i] = withDepth(ctx, arg, TOP, () => arg.unify(TOP, actx))
133
+ }
134
+ alldone = alldone && true === this.peg[i].done
135
+ }
136
+
137
+ return alldone
138
+ }
139
+
140
+
141
+ // THE PER-DESTINATION INSTANTIATION RULE (ADR-005). The default
142
+ // clone shares the argument array AND the argument Vals — pinned
143
+ // sharing for the move()/copy() ghost artifacts (test/spec/func.tsv,
144
+ // ghost-*-innard-canon) — but a clone that is a template INSTANCE
145
+ // must own the full inner structure: with the args shared,
146
+ // `pack($.names, close({name: key()}))` resolved key() once inside
147
+ // the one shared inner map and stamped the FIRST child's key on
148
+ // every child (use-cases/BUGS.md §8). The `dup` spec flag asks for
149
+ // that depth; everything else keeps the sharing it has always had.
150
+ clone(ctx: AontuContext, spec?: ValSpec): Val {
151
+ const out = super.clone(ctx, spec) as FuncBaseVal
152
+ if (true === spec?.dup && Array.isArray(this.peg)) {
153
+ // Every argument is a Val by construction (the parser builds
154
+ // them; make() rebuilds from driven Vals), as the Go twin's
155
+ // []Val typing states outright. The instantiation sites then
156
+ // normalise every path in the clone (repathInstance), so the
157
+ // argument-shaped parse paths never leak into an instance.
158
+ out.peg = this.peg.map((a: Val) => a.clone(ctx, { dup: true }))
159
+ }
160
+ return out
161
+ }
162
+
163
+
164
+ // The shape a staged func holds while it waits: not done, so the pass
165
+ // loop keeps going; unchanged against TOP, so nothing reads an answer
166
+ // it has not given; and collapsed against an identical twin at the
167
+ // same position, so `key() & key()` does not grow a conjunct per pass.
168
+ residuate(peer: Val, ctx: AontuContext): Val {
169
+ this.notdone()
170
+
171
+ if (peer.isTop || (peer.id === this.id)) {
172
+ // Cloned rather than returned: a driver that met the same object
173
+ // twice in one pass would charge the revisit budget and report
174
+ // `unify_cycle`.
175
+ return this.clone(ctx)
176
+ }
177
+
178
+ if (peer.isNil) {
179
+ return peer
180
+ }
181
+
182
+ if (peer.isFunc
183
+ && (peer as any).funcname() === this.funcname()
184
+ && peer.path.join('.') === this.path.join('.')
185
+ && peer.canon === this.canon) {
186
+ return this
187
+ }
188
+
189
+ return new ConjunctVal({ peg: [this, peer] }, ctx)
190
+ }
191
+
192
+
61
193
  unify(peer: Val, ctx: AontuContext): Val {
194
+ if (this.staged && !ctx.settle) {
195
+ return this.residuate(peer, ctx)
196
+ }
197
+
198
+ // THE PLACEHOLDER (G8 phase 3, see PlaceVal). A call holding a hole
199
+ // waits for a peer, and the peer is what fills it: the call is
200
+ // rebuilt with the hole replaced and resolved on the spot, so
201
+ // `upper(_) & hello` is `"HELLO"` and not `"HELLO" & "hello"` --
202
+ // the peer went INTO the call, it is not also a constraint on the
203
+ // way out.
204
+ if (!peer.isTop && !peer.isNil && this.id !== peer.id && hasPlace(this)) {
205
+ // TWO HOLES AND NOTHING TO FILL THEM. `upper(_) & lower(_)` has
206
+ // no value on either side, and picking one call to be the other's
207
+ // filling would be inventing an order the language does not have.
208
+ if (hasPlace(peer)) {
209
+ return makeNilErr(ctx, 'place_pair', this, peer)
210
+ }
211
+ return fillPlace(this, peer, ctx).unify(top(), ctx)
212
+ }
213
+
62
214
  const TOP = top()
63
215
  const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Func:' + this.funcname(), this, peer)
64
216
 
@@ -123,7 +275,12 @@ class FuncBaseVal extends FeatureVal {
123
275
  const resolved = this.resolve(ctx, newpeg)
124
276
  // console.log('FUNC-RESOLVED', ctx.cc, resolved?.canon)
125
277
 
126
- out = resolved.done && peer.isTop ? resolved :
278
+ // The TOP peer is DROPPED as the unit it is — unless it
279
+ // carries an identity (G4 phase 1), which is content rather
280
+ // than the unit: `id(x) & id(y)` resolves both sides to a
281
+ // top, and taking this shortcut would silently keep one
282
+ // name and lose the other instead of refusing the pair.
283
+ out = resolved.done && peer.isTop && null == peer.entity ? resolved :
127
284
  unite(te ? ctx.clone({ explain: ec(te, 'PEG') }) : ctx,
128
285
  resolved, peer, 'func-' + this.funcname() + '/' + this.id)
129
286
  propagateMarks(this, out)
@@ -132,6 +289,17 @@ class FuncBaseVal extends FeatureVal {
132
289
  out.site.row = this.site.row
133
290
  out.site.col = this.site.col
134
291
  out.site.url = this.site.url
292
+ // THE SPAN COMES WITH THE POSITION, always. Moving row and
293
+ // col onto the result while leaving its own text behind
294
+ // produced a site that contradicted itself: `close({...})`
295
+ // reported the call's column and the map's `{`, so reading
296
+ // the document at (row, col, len) found `c` where `src` said
297
+ // `{`. A consumer following the verification contract would
298
+ // refuse every such repair; one skipping it would edit the
299
+ // wrong token. Whatever the position names, the text names
300
+ // too. Twin: the same assignment in go/func.go.
301
+ out.site.len = this.site.len
302
+ out.site.src = this.site.src
135
303
  out.path = this.path
136
304
 
137
305
  why += 'pegdone'
@@ -144,6 +312,17 @@ class FuncBaseVal extends FeatureVal {
144
312
  out.site.row = this.site.row
145
313
  out.site.col = this.site.col
146
314
  out.site.url = this.site.url
315
+ // THE SPAN COMES WITH THE POSITION, always. Moving row and
316
+ // col onto the result while leaving its own text behind
317
+ // produced a site that contradicted itself: `close({...})`
318
+ // reported the call's column and the map's `{`, so reading
319
+ // the document at (row, col, len) found `c` where `src` said
320
+ // `{`. A consumer following the verification contract would
321
+ // refuse every such repair; one skipping it would edit the
322
+ // wrong token. Whatever the position names, the text names
323
+ // too. Twin: the same assignment in go/func.go.
324
+ out.site.len = this.site.len
325
+ out.site.src = this.site.src
147
326
  out.path = this.path
148
327
 
149
328
  why += 'top'
@@ -163,6 +342,17 @@ class FuncBaseVal extends FeatureVal {
163
342
  out.site.row = this.site.row
164
343
  out.site.col = this.site.col
165
344
  out.site.url = this.site.url
345
+ // THE SPAN COMES WITH THE POSITION, always. Moving row and
346
+ // col onto the result while leaving its own text behind
347
+ // produced a site that contradicted itself: `close({...})`
348
+ // reported the call's column and the map's `{`, so reading
349
+ // the document at (row, col, len) found `c` where `src` said
350
+ // `{`. A consumer following the verification contract would
351
+ // refuse every such repair; one skipping it would edit the
352
+ // wrong token. Whatever the position names, the text names
353
+ // too. Twin: the same assignment in go/func.go.
354
+ out.site.len = this.site.len
355
+ out.site.src = this.site.src
166
356
  out.path = this.path
167
357
 
168
358
  why += 'defer'
@@ -207,5 +397,6 @@ class FuncBaseVal extends FeatureVal {
207
397
 
208
398
 
209
399
  export {
400
+ trialUnify,
210
401
  FuncBaseVal,
211
402
  }
@@ -15,8 +15,6 @@ import { makeNilErr } from '../err'
15
15
 
16
16
  import {
17
17
  walk,
18
- explainOpen,
19
- explainClose,
20
18
  } from '../utility'
21
19
 
22
20
 
@@ -0,0 +1,91 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // THE IDENTITY MARK (G4 phase 1,
4
+ // docs/capability-review/g4-identity-relations.md): `id(name)`
5
+ // declares that the enclosing value is an independent ENTITY named
6
+ // `name`, and every node in one evaluation carrying that name is
7
+ // unified with every other.
8
+ //
9
+ // It is written as a conjunct — `id(svc/auth) & { … }` — so the
10
+ // function itself resolves to the UNIT (top) carrying the name, and
11
+ // the identity rides the meet onto the value, the same channel G3's
12
+ // deprecation record uses. Declaring two nodes the same entity MEANS
13
+ // unifying them, so a contradiction between them is a located error
14
+ // naming both sites: the anti-`owl:sameAs`.
15
+
16
+ import type {
17
+ Val,
18
+ ValSpec,
19
+ } from '../type'
20
+
21
+ import {
22
+ AontuContext,
23
+ } from '../ctx'
24
+
25
+ import { makeNilErr } from '../err'
26
+
27
+ import { FuncBaseVal } from './FuncBaseVal'
28
+ import { TopVal } from './TopVal'
29
+ import { nextValId } from './Val'
30
+
31
+
32
+ // Letters, digits, `_`, `-`, `/` — and NO DOTS: a dot separates an
33
+ // entity address from a sub-path (G4 phase 2), so a dotted id would
34
+ // make `svc/auth.port` ambiguous between "the entity `svc/auth.port`"
35
+ // and "the port of `svc/auth`".
36
+ const ID_NAME = /^[A-Za-z0-9_/-]+$/
37
+
38
+
39
+ // The name an argument spells, or undefined when it does not spell
40
+ // one. A bare `svc/auth` parses as a string, as does `"svc/auth"`;
41
+ // anything else — a number, a map, an unresolved reference — is not a
42
+ // name, and saying so at once beats an entity nobody can address.
43
+ export function idName(v: any): string | undefined {
44
+ if (true !== v?.isScalar || 'string' !== typeof v.peg) {
45
+ return undefined
46
+ }
47
+ return ID_NAME.test(v.peg) ? v.peg : undefined
48
+ }
49
+
50
+
51
+ class IdFuncVal extends FuncBaseVal {
52
+ isIdFunc = true
53
+
54
+ constructor(
55
+ spec: ValSpec,
56
+ ctx?: AontuContext
57
+ ) {
58
+ super(spec, ctx)
59
+ }
60
+
61
+ make(_ctx: AontuContext, spec: ValSpec): Val {
62
+ return new IdFuncVal(spec)
63
+ }
64
+
65
+ funcname() {
66
+ return 'id'
67
+ }
68
+
69
+ resolve(ctx: AontuContext, args: Val[]) {
70
+ const name = idName(args[0])
71
+ if (undefined === name) {
72
+ return makeNilErr(ctx, 'id_name', this, undefined, 'id')
73
+ }
74
+
75
+ // THE UNIT, carrying the identity: `id(x) & v` must be `v` with an
76
+ // identity, so the function resolves to what unifies with anything
77
+ // and lets the rider in `unite` do the stamping.
78
+ const out = new TopVal({}, ctx)
79
+ // NOT id 0: TopVal pins that, and `unite`'s fast path returns
80
+ // early for two done Vals sharing an id — which would drop one of
81
+ // two identities before the rider could refuse them.
82
+ out.id = nextValId()
83
+ out.entity = name
84
+ return out
85
+ }
86
+ } /* node:coverage ignore next 6 */
87
+
88
+
89
+ export {
90
+ IdFuncVal,
91
+ }
@@ -33,7 +33,13 @@ abstract class JunctionVal extends FeatureVal {
33
33
 
34
34
  clone(ctx: AontuContext, spec?: ValSpec): Val {
35
35
  let out = (super.clone(ctx, spec) as JunctionVal)
36
- out.peg = this.peg.map((entry: Val) => entry.clone(ctx, spec?.mark ? { mark: spec.mark } : {}))
36
+ // The instantiation flag descends with the mark (ADR-005): a
37
+ // junction inside a template clones its members as instances (the
38
+ // instantiation sites re-path the whole clone afterwards — see
39
+ // repathInstance in Val.ts).
40
+ const childspec = spec?.mark || spec?.dup ?
41
+ { mark: spec?.mark, dup: spec?.dup } : {}
42
+ out.peg = this.peg.map((entry: Val) => entry.clone(ctx, childspec))
37
43
  return out
38
44
  }
39
45
 
@@ -12,7 +12,6 @@ import {
12
12
  } from '../ctx'
13
13
 
14
14
  import { StringVal } from '../val/StringVal'
15
- import { ConjunctVal } from '../val/ConjunctVal'
16
15
 
17
16
 
18
17
 
@@ -41,39 +40,13 @@ class KeyFuncVal extends FuncBaseVal {
41
40
  }
42
41
 
43
42
 
44
- unify(peer: Val, ctx: AontuContext): Val {
45
- // TODO: this delay makes keys in spreads and refs work, but it is a hack - find a better way.
46
- let out: Val = this
47
-
48
- if (ctx.cc < 3) {
49
- this.notdone()
50
-
51
- if (peer.isTop || (peer.id === this.id)) {
52
- // TODO: clone needed to avoid triggering unify_cycle - find a better way
53
- out = this.clone(ctx)
54
- }
55
- else if (peer.isNil) {
56
- out = peer
57
- }
58
- else {
59
- if (
60
- peer.isKeyFunc
61
- && peer.path.join('.') === this.path.join('.')
62
- && peer.peg?.[0]?.peg === this.peg?.[0]?.peg
63
- ) {
64
- out = this
65
- }
66
- else {
67
- out = new ConjunctVal({ peg: [this, peer] }, ctx)
68
- }
69
- }
70
- }
71
- else {
72
- out = super.unify(peer, ctx)
73
- }
74
-
75
- return out
76
- }
43
+ // `key()` is the first value to take THE STAGING RULE (G8 phase 0,
44
+ // see AontuContext.settle): its answer is a segment of its own path,
45
+ // so it must not answer while a spread, a reference or a `move` can
46
+ // still move it. It residuates until the model stops changing, and
47
+ // fires on the settle pass. The residuation itself is FuncBaseVal's,
48
+ // shared with the generation combinators.
49
+ staged = true
77
50
 
78
51
 
79
52
  resolve(ctx: AontuContext, _args: Val[]) {
@@ -113,7 +86,38 @@ class KeyFuncVal extends FuncBaseVal {
113
86
  }
114
87
  }
115
88
 
116
- const key = this.path[this.path.length - (1 + move)] ?? ''
89
+ // THE PATH IS THE ONE IT IS BEING DRIVEN AT, not the one it
90
+ // remembers. `this.path` is a cache that the residuation clone
91
+ // refreshes each pass, and it is right for a key() the bag walk
92
+ // reaches directly -- but a key() nested inside a function or
93
+ // operator ARGUMENT is never reached that way: it is driven by its
94
+ // enclosing call, which re-paths nothing, so it kept the position
95
+ // the SOURCE TEXT put it at. Inside a template that is fatal: the
96
+ // source position of `&: {k: "x" + key()}` (or of `pack`'s
97
+ // template) is the template, not any of the destinations, and the
98
+ // parse-time path of a function argument is not a document
99
+ // position at all.
100
+ // A stored path is only usable if it IS one: a path segment that is
101
+ // not a key is not a position, and the parse-time path of a value
102
+ // written inside a function ARGUMENT carries exactly that -- the
103
+ // argument has no key, so its segment is not a string. Those values
104
+ // are also the ones the bag walk never re-paths (a call re-paths
105
+ // nothing it holds), which is why the stored path stays wrong
106
+ // forever rather than being refreshed on the next pass. For them
107
+ // the driving context is the only truth. For everything else the
108
+ // stored path stays authoritative, because a TRANSPLANTED value
109
+ // (move(), a shared clone) must answer for where it was put, not
110
+ // for whichever driver happened to reach it first.
111
+ let positioned = true
112
+ for (const seg of this.path) {
113
+ if ('string' !== typeof seg) {
114
+ positioned = false
115
+ break
116
+ }
117
+ }
118
+
119
+ const here = positioned ? this.path : ctx.path
120
+ const key = here[here.length - (1 + move)] ?? ''
117
121
  // console.log('KEY', this.path, move, key)
118
122
 
119
123
  out = new StringVal({ peg: key })