aontu 0.53.0 → 0.55.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 (197) hide show
  1. package/dist/aontu.d.ts +3 -2
  2. package/dist/aontu.js +36 -7
  3. package/dist/aontu.js.map +1 -1
  4. package/dist/cli.d.ts +2 -1
  5. package/dist/cli.js +500 -8
  6. package/dist/cli.js.map +1 -1
  7. package/dist/ctx.d.ts +5 -0
  8. package/dist/ctx.js +1 -0
  9. package/dist/ctx.js.map +1 -1
  10. package/dist/diff.js.map +1 -1
  11. package/dist/err.js +7 -1
  12. package/dist/err.js.map +1 -1
  13. package/dist/graph.d.ts +2 -5
  14. package/dist/graph.js +83 -46
  15. package/dist/graph.js.map +1 -1
  16. package/dist/hcanon.js +9 -10
  17. package/dist/hcanon.js.map +1 -1
  18. package/dist/hints.js +94 -26
  19. package/dist/hints.js.map +1 -1
  20. package/dist/jsonschema.js +34 -0
  21. package/dist/jsonschema.js.map +1 -1
  22. package/dist/lang.js +593 -191
  23. package/dist/lang.js.map +1 -1
  24. package/dist/lsp.d.ts +1 -1
  25. package/dist/lsp.js +86 -6
  26. package/dist/lsp.js.map +1 -1
  27. package/dist/mcp.js +153 -6
  28. package/dist/mcp.js.map +1 -1
  29. package/dist/mod-tool.js +42 -8
  30. package/dist/mod-tool.js.map +1 -1
  31. package/dist/mod.d.ts +4 -0
  32. package/dist/mod.js +97 -2
  33. package/dist/mod.js.map +1 -1
  34. package/dist/patch.d.ts +5 -0
  35. package/dist/patch.js +25 -25
  36. package/dist/patch.js.map +1 -1
  37. package/dist/provenance.d.ts +1 -0
  38. package/dist/provenance.js +2 -1
  39. package/dist/provenance.js.map +1 -1
  40. package/dist/query.js.map +1 -1
  41. package/dist/reach.d.ts +1 -0
  42. package/dist/reach.js +49 -21
  43. package/dist/reach.js.map +1 -1
  44. package/dist/relation.d.ts +4 -0
  45. package/dist/relation.js +125 -200
  46. package/dist/relation.js.map +1 -1
  47. package/dist/sig.d.ts +25 -0
  48. package/dist/sig.js +277 -0
  49. package/dist/sig.js.map +1 -0
  50. package/dist/sigdecl.d.ts +2 -0
  51. package/dist/sigdecl.js +11 -0
  52. package/dist/sigdecl.js.map +1 -0
  53. package/dist/siggate.d.ts +4 -0
  54. package/dist/siggate.js +90 -0
  55. package/dist/siggate.js.map +1 -0
  56. package/dist/std.js +75 -16
  57. package/dist/std.js.map +1 -1
  58. package/dist/subsume.js +57 -10
  59. package/dist/subsume.js.map +1 -1
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/dist/unify.d.ts +2 -2
  62. package/dist/unify.js +93 -114
  63. package/dist/unify.js.map +1 -1
  64. package/dist/utility.d.ts +1 -2
  65. package/dist/utility.js +7 -61
  66. package/dist/utility.js.map +1 -1
  67. package/dist/val/AggFuncVal.d.ts +12 -1
  68. package/dist/val/AggFuncVal.js +165 -3
  69. package/dist/val/AggFuncVal.js.map +1 -1
  70. package/dist/val/BagVal.d.ts +2 -0
  71. package/dist/val/BagVal.js +56 -8
  72. package/dist/val/BagVal.js.map +1 -1
  73. package/dist/val/ConstraintVal.js +33 -5
  74. package/dist/val/ConstraintVal.js.map +1 -1
  75. package/dist/val/ContainerKindVal.d.ts +35 -0
  76. package/dist/val/ContainerKindVal.js +99 -0
  77. package/dist/val/ContainerKindVal.js.map +1 -0
  78. package/dist/val/CopyFuncVal.js +0 -7
  79. package/dist/val/CopyFuncVal.js.map +1 -1
  80. package/dist/val/DisjunctVal.d.ts +1 -2
  81. package/dist/val/DisjunctVal.js +146 -39
  82. package/dist/val/DisjunctVal.js.map +1 -1
  83. package/dist/val/ExpectVal.js +37 -2
  84. package/dist/val/ExpectVal.js.map +1 -1
  85. package/dist/val/FuncBaseVal.d.ts +1 -0
  86. package/dist/val/FuncBaseVal.js +19 -8
  87. package/dist/val/FuncBaseVal.js.map +1 -1
  88. package/dist/val/GraphAtomVal.d.ts +39 -0
  89. package/dist/val/GraphAtomVal.js +184 -0
  90. package/dist/val/GraphAtomVal.js.map +1 -0
  91. package/dist/val/JunctionVal.js +22 -5
  92. package/dist/val/JunctionVal.js.map +1 -1
  93. package/dist/val/ListVal.js +27 -34
  94. package/dist/val/ListVal.js.map +1 -1
  95. package/dist/val/MapVal.d.ts +1 -0
  96. package/dist/val/MapVal.js +82 -35
  97. package/dist/val/MapVal.js.map +1 -1
  98. package/dist/val/PathFuncVal.d.ts +2 -2
  99. package/dist/val/PathFuncVal.js +75 -16
  100. package/dist/val/PathFuncVal.js.map +1 -1
  101. package/dist/val/PathVal.d.ts +25 -0
  102. package/dist/val/PathVal.js +150 -0
  103. package/dist/val/PathVal.js.map +1 -0
  104. package/dist/val/PlusOpVal.d.ts +2 -1
  105. package/dist/val/PlusOpVal.js +50 -35
  106. package/dist/val/PlusOpVal.js.map +1 -1
  107. package/dist/val/PrefVal.d.ts +2 -0
  108. package/dist/val/PrefVal.js +159 -32
  109. package/dist/val/PrefVal.js.map +1 -1
  110. package/dist/val/RecurseVal.d.ts +19 -0
  111. package/dist/val/RecurseVal.js +217 -0
  112. package/dist/val/RecurseVal.js.map +1 -0
  113. package/dist/val/RefVal.d.ts +2 -1
  114. package/dist/val/RefVal.js +105 -72
  115. package/dist/val/RefVal.js.map +1 -1
  116. package/dist/val/ReferFuncVal.d.ts +30 -7
  117. package/dist/val/ReferFuncVal.js +395 -94
  118. package/dist/val/ReferFuncVal.js.map +1 -1
  119. package/dist/val/ScalarKindVal.d.ts +4 -2
  120. package/dist/val/ScalarKindVal.js +12 -1
  121. package/dist/val/ScalarKindVal.js.map +1 -1
  122. package/dist/val/SuperFuncVal.d.ts +4 -2
  123. package/dist/val/SuperFuncVal.js +118 -14
  124. package/dist/val/SuperFuncVal.js.map +1 -1
  125. package/dist/val/TopVal.d.ts +1 -1
  126. package/dist/val/Val.d.ts +2 -3
  127. package/dist/val/Val.js +39 -20
  128. package/dist/val/Val.js.map +1 -1
  129. package/dist/val/arith.js +4 -1
  130. package/dist/val/arith.js.map +1 -1
  131. package/dist/vet.d.ts +1 -0
  132. package/dist/vet.js +32 -1
  133. package/dist/vet.js.map +1 -1
  134. package/dist/view.d.ts +90 -0
  135. package/dist/view.js +2168 -0
  136. package/dist/view.js.map +1 -0
  137. package/grammar/aontu.gbnf +18 -10
  138. package/grammar/aontu.lark +15 -10
  139. package/grammar/aontu.tmLanguage.json +184 -0
  140. package/package.json +10 -3
  141. package/skill/grammar-card.md +1 -2
  142. package/src/aontu.ts +37 -7
  143. package/src/cli.ts +553 -8
  144. package/src/ctx.ts +20 -0
  145. package/src/diff.ts +4 -2
  146. package/src/err.ts +8 -1
  147. package/src/graph.ts +125 -75
  148. package/src/hcanon.ts +9 -11
  149. package/src/hints.ts +112 -29
  150. package/src/jsonschema.ts +41 -0
  151. package/src/lang.ts +642 -203
  152. package/src/lsp.ts +75 -6
  153. package/src/mcp.ts +164 -6
  154. package/src/mod-tool.ts +48 -9
  155. package/src/mod.ts +110 -1
  156. package/src/patch.ts +31 -27
  157. package/src/provenance.ts +8 -1
  158. package/src/query.ts +4 -2
  159. package/src/reach.ts +52 -23
  160. package/src/relation.ts +139 -236
  161. package/src/sig.ts +345 -0
  162. package/src/sigdecl.ts +11 -0
  163. package/src/siggate.ts +144 -0
  164. package/src/std.ts +77 -16
  165. package/src/subsume.ts +59 -10
  166. package/src/unify.ts +102 -125
  167. package/src/utility.ts +7 -67
  168. package/src/val/AggFuncVal.ts +231 -4
  169. package/src/val/BagVal.ts +58 -9
  170. package/src/val/ConstraintVal.ts +34 -5
  171. package/src/val/ContainerKindVal.ts +158 -0
  172. package/src/val/CopyFuncVal.ts +0 -7
  173. package/src/val/DisjunctVal.ts +152 -40
  174. package/src/val/ExpectVal.ts +39 -4
  175. package/src/val/FuncBaseVal.ts +21 -8
  176. package/src/val/GraphAtomVal.ts +264 -0
  177. package/src/val/JunctionVal.ts +23 -6
  178. package/src/val/ListVal.ts +30 -37
  179. package/src/val/MapVal.ts +87 -39
  180. package/src/val/PathFuncVal.ts +107 -19
  181. package/src/val/PathVal.ts +221 -0
  182. package/src/val/PlusOpVal.ts +56 -37
  183. package/src/val/PrefVal.ts +186 -38
  184. package/src/val/RecurseVal.ts +285 -0
  185. package/src/val/RefVal.ts +105 -83
  186. package/src/val/ReferFuncVal.ts +445 -100
  187. package/src/val/ScalarKindVal.ts +12 -0
  188. package/src/val/SuperFuncVal.ts +137 -13
  189. package/src/val/TopVal.ts +1 -1
  190. package/src/val/Val.ts +44 -35
  191. package/src/val/arith.ts +4 -1
  192. package/src/vet.ts +41 -4
  193. package/src/view.ts +2882 -0
  194. package/dist/val/IdFuncVal.d.ts +0 -13
  195. package/dist/val/IdFuncVal.js +0 -54
  196. package/dist/val/IdFuncVal.js.map +0 -1
  197. package/src/val/IdFuncVal.ts +0 -91
@@ -64,6 +64,15 @@ class BigDecimal { }
64
64
  // A ScalarKind for null.
65
65
  class Null { }
66
66
 
67
+ // A ScalarKind for tree addresses (docs/design/PATHS.0.md): the value
68
+ // a `path(p)` call captures. Sits UNDER string -- an address is a
69
+ // string with more structure, so string-kinded schemas over address
70
+ // fields keep admitting -- while staying a distinct leaf, so a plain
71
+ // string literal and a path value refuse each other exactly as the
72
+ // number tower's leaves do. Reached only by the `path()` builtin, as
73
+ // the exact numeric leaves are reached only by `0d`.
74
+ class Path { }
75
+
67
76
 
68
77
  // The immediate lattice parent of each kind marker. A marker absent from
69
78
  // this table sits directly under top (string, boolean, null -- and
@@ -75,6 +84,7 @@ const KIND_PARENT = new Map<any, any>([
75
84
  [Float, Number],
76
85
  [BigInteger, Number],
77
86
  [BigDecimal, Number],
87
+ [Path, String],
78
88
  ])
79
89
 
80
90
 
@@ -107,6 +117,7 @@ type ScalarConstructor =
107
117
  (typeof BigInteger) |
108
118
  (typeof BigDecimal) |
109
119
  (typeof Null) |
120
+ (typeof Path) |
110
121
  (typeof Integer.constructor)
111
122
 
112
123
 
@@ -216,6 +227,7 @@ export {
216
227
  Float,
217
228
  Integer,
218
229
  Null,
230
+ Path,
219
231
  ScalarConstructor,
220
232
  ScalarKindVal,
221
233
  kindParent,
@@ -1,6 +1,16 @@
1
1
  /* Copyright (c) 2021-2025 Richard Rodger, MIT License */
2
2
 
3
3
 
4
+ // THE IMMEDIATE PARENT TYPE (docs/design/SUPER.0.md): super(x) answers
5
+ // the type one step above its argument, structurally. The lattice
6
+ // primitive Val.superior() stays what it always was -- the preference
7
+ // override gate and query's kind lift -- and answers here only for the
8
+ // forms it already served (scalars, kinds, top). Everything structural
9
+ // is this file's walk: maps and lists lift child by child, preferences
10
+ // unwrap, disjunctions distribute, constraints answer the kind they
11
+ // constrain, and a recursion residual stays a symbolic call -- the
12
+ // finite spelling of a lift that is itself recursive.
13
+
4
14
  import type {
5
15
  Val,
6
16
  ValSpec,
@@ -12,6 +22,11 @@ import {
12
22
 
13
23
 
14
24
  import { FuncBaseVal } from './FuncBaseVal'
25
+ import { MapVal } from './MapVal'
26
+ import { ListVal } from './ListVal'
27
+ import { DisjunctVal } from './DisjunctVal'
28
+ import { ScalarKindVal } from './ScalarKindVal'
29
+ import { top } from './top'
15
30
 
16
31
 
17
32
  class SuperFuncVal extends FuncBaseVal {
@@ -34,26 +49,135 @@ class SuperFuncVal extends FuncBaseVal {
34
49
  }
35
50
 
36
51
 
37
- resolve(_ctx: AontuContext, args: Val[]) {
38
- // super(x) is the lattice-superior of its ARGUMENT, not of the
39
- // super() call itself: super(1) -> integer, super(1.5) -> number,
40
- // super(a) -> string, super(true) -> boolean. Returning the
41
- // function's own superior (top) is what made super() inert.
42
- const arg: any = args?.[0]
43
- if (arg?.isVal) {
44
- const sup = arg.superior()
45
- // Where the argument has no meaningful superior (superior()
46
- // defaults to top), fall back to the previous behaviour.
47
- if (null != sup && true !== sup.isTop) {
48
- return this.place(sup)
52
+ // A DIRECT residual rides the residuate path rather than resolving:
53
+ // the pending call IS the answer (SUPER.0.md, the phase boundary),
54
+ // printing as written and refusing at generation exactly as an
55
+ // unexpanded recursion does. Residuals met DURING descent are minted
56
+ // as pending child calls by superOf below; only the top-level
57
+ // argument needs the defer, or resolve would re-mint the same call
58
+ // inside one pass forever.
59
+ deferResolve(_ctx: AontuContext, args?: Val[]): boolean {
60
+ return true === (args?.[0] as any)?.isRecurse
61
+ }
62
+
63
+
64
+ resolve(ctx: AontuContext, args: Val[]) {
65
+ // One argument, always a Val: funcArity pins super at [1,1]
66
+ // before any resolve, and the parser builds arguments as Vals --
67
+ // a guarded fallback here is dead code under ADR-002.
68
+ return this.place(superOf(ctx, args[0]))
69
+ }
70
+
71
+ }
72
+
73
+
74
+ // superOf answers the immediate parent type of a RESOLVED value (the
75
+ // caller drives arguments before resolve fires, so pending forms --
76
+ // held conjuncts, unresolved references, holes -- never arrive).
77
+ function superOf(ctx: AontuContext, v: any): Val {
78
+ // The residual's lift is itself recursive, so the finite answer is
79
+ // the symbolic call (SUPER.0.md): a fresh pending super() holding a
80
+ // clone of the residual, standing wherever the residual stood --
81
+ // the `next?` slot of a lifted recursive body prints
82
+ // `super($.Node)` and drops under an optional key at generation.
83
+ if (true === v.isRecurse) {
84
+ const call = new SuperFuncVal({ peg: [v.clone(ctx)] }, ctx)
85
+ return v.place(call)
86
+ }
87
+
88
+ // Maps and lists lift child by child. Shape is carried, not lifted:
89
+ // key optionality and closedness describe the container, and the
90
+ // spread template lifts so the result admits at the lifted level
91
+ // for future keys exactly as the original admitted at its own.
92
+ // A fresh bag (ADR-005 instantiation): type/hide marks are not
93
+ // copied -- the lift of a hidden definition is output.
94
+ if (true === v.isMap) {
95
+ const peg: Record<string, Val> = {}
96
+ for (const k of Object.keys(v.peg)) {
97
+ peg[k] = superOf(ctx, v.peg[k])
98
+ }
99
+ const out: any = new MapVal({ peg }, ctx)
100
+ out.optionalKeys = [...v.optionalKeys]
101
+ out.closed = v.closed
102
+ if (null != v.spread?.cj) {
103
+ out.spread.cj = superOf(ctx, v.spread.cj)
104
+ }
105
+ return v.place(out)
106
+ }
107
+
108
+ if (true === v.isList) {
109
+ const peg: Val[] = v.peg.map((e: Val) => superOf(ctx, e))
110
+ const out: any = new ListVal({ peg }, ctx)
111
+ out.closed = v.closed
112
+ if (null != v.spread?.cj) {
113
+ out.spread.cj = superOf(ctx, v.spread.cj)
114
+ }
115
+ return v.place(out)
116
+ }
117
+
118
+ // The parent TYPE of a soft value is the parent of the value --
119
+ // softness does not survive typing. Deliberately NOT superpeg,
120
+ // whose top-for-a-kind answer is override-gate semantics.
121
+ if (true === v.isPref) {
122
+ return superOf(ctx, v.peg)
123
+ }
124
+
125
+ // A choice lifts arm by arm: super(1|2) is integer, super(1|"a") is
126
+ // integer|string. An arm whose lift is top absorbs the whole answer
127
+ // -- a disjunct carrying top says nothing -- and duplicate lifts
128
+ // collapse so the common case answers as the one kind it is.
129
+ if (true === v.isDisjunct) {
130
+ const arms: Val[] = []
131
+ const seen: Record<string, boolean> = {}
132
+ for (const a of v.peg) {
133
+ const lift = superOf(ctx, a)
134
+ if (true === (lift as any).isTop) {
135
+ return v.place(top())
136
+ }
137
+ if (true !== seen[lift.canon]) {
138
+ seen[lift.canon] = true
139
+ arms.push(lift)
49
140
  }
50
141
  }
51
- return this.place(this.superior())
142
+ if (1 === arms.length) {
143
+ return v.place(arms[0])
144
+ }
145
+ return v.place(new DisjunctVal({ peg: arms }, ctx))
52
146
  }
53
147
 
148
+ // A constraint's parent is the kind it constrains: the absorbed
149
+ // leaf kind when it has one (integer & min(3) -> integer), else the
150
+ // domain its atoms compare in (min(3) -> number, min("a") ->
151
+ // string). length() constrains strings, lists and maps alike, so
152
+ // with neither it falls through to top.
153
+ if (true === v.isConstraint) {
154
+ if (null != v.kind) {
155
+ return v.place(new ScalarKindVal({ peg: v.kind }))
156
+ }
157
+ if ('number' === v.domain) {
158
+ return v.place(new ScalarKindVal({ peg: Number }))
159
+ }
160
+ if ('string' === v.domain) {
161
+ return v.place(new ScalarKindVal({ peg: String }))
162
+ }
163
+ return v.place(top())
164
+ }
165
+
166
+ // The lattice primitive answers for the forms it always served:
167
+ // a concrete scalar lifts to its leaf kind, a kind to its parent,
168
+ // top to itself, and a NIL to itself -- which is how a failed
169
+ // argument stays the failure: `super(1 & 2)` reports the conflict
170
+ // rather than typing it. Where it has no meaningful answer (superior()
171
+ // defaults to top for features), top is the honest remainder.
172
+ const sup = v.superior()
173
+ if (null != sup && true !== (sup as any).isTop) {
174
+ return sup
175
+ }
176
+ return v.place(top())
54
177
  } /* node:coverage ignore next 6 */
55
178
 
56
179
 
57
180
  export {
58
181
  SuperFuncVal,
182
+ superOf,
59
183
  }
package/src/val/TopVal.ts CHANGED
@@ -52,7 +52,7 @@ class TopVal extends Val {
52
52
  return this
53
53
  }
54
54
 
55
- clone(_ctx: AontuContext, _spec?: ValSpec) {
55
+ clone(_ctx: AontuContext, _spec?: ValSpec): Val {
56
56
  return this
57
57
  }
58
58
 
package/src/val/Val.ts CHANGED
@@ -80,17 +80,6 @@ const EMPTY_ERR: any[] = Object.freeze([]) as unknown as any[]
80
80
  let ID = 1000
81
81
 
82
82
 
83
- // A fresh Val id, for the one carrier that cannot take the one its
84
- // class fixes: TopVal pins `id = 0` (there is only one top), and the
85
- // identity mark (G4 phase 1) resolves to a top that must NOT collide
86
- // with it — the fast path in `unite` returns early on two done Vals
87
- // with the same id, which would drop an identity before the rider
88
- // could carry it.
89
- export function nextValId(): number {
90
- return ID++
91
- }
92
-
93
-
94
83
  abstract class Val {
95
84
  // Type-discriminator flags: defaults live on Val.prototype (see
96
85
  // bottom of this file). Each subclass overrides only its own
@@ -172,25 +161,16 @@ abstract class Val {
172
161
  // marks are.
173
162
  deprecation?: Record<string, string>
174
163
 
175
- // The IDENTITY (G4 phase 1, `id(name)`): the entity this value IS.
176
- // A separate slot for the same reason the deprecation record has
177
- // one — a boolean ValMark cannot hold a name — and carried through
178
- // meets by the same rider in `unite`. Unlike the marks, canon
179
- // RENDERS it: identity is semantic content, and G6's hash must see
180
- // it.
181
- entity?: string
182
-
183
- // The LINK (G4 phase 2/3): the entity address a `refer` resolved to,
164
+ // The LINK (G4 phase 2/3): the tree address a `refer` resolved to,
184
165
  // stamped on the string it answers. The string IS the value — a
185
166
  // link, not an embedding — so nothing downstream could otherwise
186
167
  // tell a checked link from a literal that happens to look like one,
187
168
  // and the edge set (ts/src/graph.ts) is exactly the set of these.
188
169
  link?: string
189
170
 
190
- // The GRAPH of an evaluated document (G4 phase 3): the entity index
191
- // and the edge set, stamped on the result by Aontu.unify the way the
192
- // include manifest is. Absent on every Val that is not a unify
193
- // result.
171
+ // The GRAPH of an evaluated document (G4 phase 3): the edge set,
172
+ // stamped on the result by Aontu.unify the way the include manifest
173
+ // is. Absent on every Val that is not a unify result.
194
174
  graph?: any
195
175
 
196
176
  // Actual native value.
@@ -311,20 +291,34 @@ abstract class Val {
311
291
  out.mark.type = this.mark.type && (fullspec.mark?.type ?? true)
312
292
  out.mark.hide = this.mark.hide && (fullspec.mark?.hide ?? true)
313
293
 
314
- // The two IDENTITY riders travel together, under one test: the
315
- // entity a value IS, and the address a resolved link POINTS AT.
316
- // One guard rather than two because the pair is what a clone
317
- // either carries or does not — and because a second test for the
318
- // link alone would be a branch no document takes, the reference
319
- // clone catching the pending residual before it ever resolves.
320
- if (null != this.entity || null != this.link) {
321
- out.entity = this.entity
294
+ // The LINK rider travels with the clone: the address a resolved
295
+ // link POINTS AT is part of what the value is, and a copy of a
296
+ // link is still a link.
297
+ if (null != this.link) {
322
298
  out.link = this.link
323
299
  }
324
300
  if (null != this.deprecation) {
325
301
  out.deprecation = this.deprecation
326
302
  }
327
303
 
304
+ // THE APPLY-ONCE MARK TRAVELS WITH THE CLONE. `_spr` records which
305
+ // spread template has already been merged into this value, and the
306
+ // bag loops read it to keep a template from being applied twice
307
+ // (MapVal.unify, ListVal.unify). A clone that dropped it looked
308
+ // un-spread, so a REFERENCE resolving to a templated bag had the
309
+ // template applied a second time -- over the value the first
310
+ // application had already produced. With `n: key()` that meant
311
+ // meeting the answered `"x"` as though it were a map and asking
312
+ // `key()` again, which answered `"n"`: `$.a.b.f.x.n.n`, "n"
313
+ // against "x" (use-cases/BUGS.md §50). The Go port carries it and
314
+ // answered correctly; this is the canonical side catching up.
315
+ if (null != (this as any)._spr) {
316
+ ; (out as any)._spr = (this as any)._spr
317
+ }
318
+ if (null != (this as any)._sid) {
319
+ ; (out as any)._sid = (this as any)._sid
320
+ }
321
+
328
322
  // PROVENANCE TRAVELS WITH THE CLONE, exactly as the site does, and
329
323
  // for the same reason: a clone of a value the author wrote IS that
330
324
  // written value somewhere else, and it carries the author's site,
@@ -361,7 +355,7 @@ abstract class Val {
361
355
  // once per destination child, and each application must own its
362
356
  // path-dependent innards — a bare clone shared a call's arguments
363
357
  // and a preference's inner value across destinations, so a spread
364
- // like `&: id(key(0)) & $.schema.C` resolved its one shared key()
358
+ // like `&: {k: key(0)} & $.schema.C` resolved its one shared key()
365
359
  // at the first child it met (use-cases/BUGS.md §12's id_name form).
366
360
  spreadClone(ctx: AontuContext): Val {
367
361
  const out = this.clone(ctx, { dup: true })
@@ -656,14 +650,27 @@ function pretty(s: string) {
656
650
  }
657
651
 
658
652
 
653
+ // THE STABLE IDENTITY OF A SPREAD TEMPLATE, across clones. Every Val
654
+ // takes a fresh `id` when it is constructed, so the bag loops'
655
+ // apply-once mark -- which records WHICH template has already been
656
+ // merged into a value -- could never match after a clone: a reference
657
+ // resolving to a templated bag clones the bag AND its template, and the
658
+ // fresh template's id matched nothing, so the template was applied a
659
+ // second time over the value the first application had produced
660
+ // (use-cases/BUGS.md §50). The first call fixes the identity to the
661
+ // original's own id; Val.clone carries `_sid`, so every clone of that
662
+ // template answers with it.
663
+ function spreadId(cj: any): number {
664
+ return cj._sid ?? (cj._sid = cj.id)
665
+ }
666
+
659
667
  function empty(o: any) {
660
668
  return (
661
669
  (Array.isArray(o) && 0 === o.length)
662
670
  || (null != o && 'object' === typeof o && 0 === Object.keys(o).length)
663
671
  || false
664
672
  )
665
- } /* node:coverage ignore next 16 */
666
-
673
+ } /* node:coverage ignore next 17 */
667
674
 
668
675
 
669
676
  export type {
@@ -671,7 +678,9 @@ export type {
671
678
  ValSpec,
672
679
  }
673
680
 
681
+
674
682
  export {
683
+ spreadId,
675
684
  Val,
676
685
  DONE,
677
686
  SPREAD,
package/src/val/arith.ts CHANGED
@@ -105,7 +105,10 @@ function unpref(v: any): any {
105
105
 
106
106
 
107
107
  function arithKind(v: any): ArithKind | undefined {
108
- if (!(v?.isVal && v.isScalar)) {
108
+ // Every caller hands a driven Val (an operand past the signature
109
+ // gate, or a bag member from the aggregate fold), so scalarhood is
110
+ // the one question -- a container member lands here.
111
+ if (true !== v?.isScalar) {
109
112
  return undefined
110
113
  }
111
114
  if (v.isBigInteger) {
package/src/vet.ts CHANGED
@@ -146,8 +146,10 @@ export type VetOptions = {
146
146
  // Vet's whole job is to evaluate a document its caller did not
147
147
  // write, so the caller must be able to say what that document is
148
148
  // allowed to reach: without this the include chain is the default
149
- // one, and `@"x.js"` in hostile data is arbitrary code execution in
150
- // the validating process. A server passes `{include:'none'}`.
149
+ // one, and `@"../../etc/passwd.aon"` in hostile data reads whatever
150
+ // the validating process can. (`@"x.js"` no longer executes --
151
+ // ADR-012 refuses the extension -- but reading is enough.) A server
152
+ // passes `{include:'none'}`.
151
153
  trust?: TrustOptions
152
154
  }
153
155
 
@@ -571,7 +573,11 @@ export function anchorAt(root: any, at: string): Val | undefined {
571
573
 
572
574
 
573
575
  // The container inside a settled sizing residue, or the value itself.
574
- function throughResidue(v: any): any {
576
+ // EXPORTED for the `doc` figure, which walks the same shape the anchor
577
+ // does: a list still carrying a `unique()` is a list, and a drawing
578
+ // that stopped at the residue would omit keys the document plainly
579
+ // has.
580
+ export function throughResidue(v: any): any {
575
581
  return sizingResidue(v)?.bag ?? v
576
582
  }
577
583
 
@@ -696,7 +702,18 @@ export function vet(
696
702
  findings: [findingOf(failure, { data: new Set([dataUrl]) })],
697
703
  }
698
704
  }
699
- stampUrl(anchor, schemaUrl)
705
+ // STAMP THE WHOLE SETTLED SCHEMA, not just the lifted anchor.
706
+ // Without `--at` these are the same tree. With it, the anchor is a
707
+ // subtree and the rest of the schema is still REACHABLE from inside
708
+ // it -- a `%alias` declaration (`[&: %U]`, target `$.%U`) or a
709
+ // recursive residual's `$.spec.Step`, both of which the meet
710
+ // resolves through _fixroot (RefVal.find, RecurseVal.body). A node
711
+ // reached that way but never stamped carries no url, so its site
712
+ // named no file while excerpting the schema's text -- against the
713
+ // invariant that every site names the file whose text it shows
714
+ // (finding F, §25). stampUrl only fills a url that is EMPTY, so
715
+ // stamping the superset never renames a value read from an include.
716
+ stampUrl(schemaVal, schemaUrl)
700
717
  const dataUrls = stampUrl(dataVal, dataUrl)
701
718
  // The projection every site in this report goes through: roles by
702
719
  // url-set membership, names by how the caller reached each document.
@@ -820,6 +837,26 @@ export function vet(
820
837
  stampUrl(meetAnchor, schemaUrl)
821
838
  }
822
839
  }
840
+ else {
841
+ // A RECURSIVE residual inside the lifted anchor still names its
842
+ // definition by absolute path (`then?: $.spec.Step` -- the
843
+ // fixpoint, RECURSION.0.md), and the meet's root is the anchored
844
+ // subtree, which does not contain `$.spec`. Without a tree to
845
+ // walk, the residual held its peer forever and everything under a
846
+ // recursive field vetted VALID unchecked. The settled schema root
847
+ // is kept on the meet context for exactly that walk
848
+ // (AontuContext._fixroot; RecurseVal.body and RefVal.find fall
849
+ // back to it).
850
+ ; (ctx as any)._fixroot = schemaVal
851
+ // And the meet DRIVES AT the anchor's own path, so every finding
852
+ // minted live during it sits in the schema's namespace
853
+ // ($.spec.Step.then.approver), exactly where the findings carried
854
+ // on the settled anchor's stored paths already sit -- the Go port
855
+ // gets this for free from its clone discipline, and the shared
856
+ // anchored-vet rows pin the agreement.
857
+ ; (ctx as any).path = options.at.replace(/^\$\.?/, '')
858
+ .split('.').filter((s: string) => '' !== s)
859
+ }
823
860
  const pair = new ConjunctVal({ peg: [meetAnchor, dataVal] }, ctx)
824
861
  const unified: any = aontu.unify(pair, undefined, ctx)
825
862