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
@@ -0,0 +1,285 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // THE RECURSIVE RESIDUAL (docs/design/RECURSION.0.md): where RefVal's
4
+ // prefix test used to answer `path_cycle` for a self-reference --
5
+ // `$.Node` written anywhere inside `Node` -- it now answers this
6
+ // value: a deferred reference carrying the target path, exactly as a
7
+ // constraint atom carries its bound. No new syntax: the reference the
8
+ // author wrote simply MEANS the fixpoint.
9
+ //
10
+ // The three moments of every residual:
11
+ //
12
+ // - At unification, EXPAND ONE LEVEL PER MEET WITH STRUCTURE: met by
13
+ // a map, list, or scalar, the residual clones the schema body one
14
+ // level -- per destination, under ADR-005's clone discipline -- and
15
+ // unifies the clone with the peer. The clone's own self-reference
16
+ // is again this residual, so each expansion consumes one level of
17
+ // concrete data; data is finite, so expansion terminates
18
+ // (structural descent), with the depth budget as the backstop
19
+ // (recursion_budget).
20
+ // - In canon and the aon1- hash, SYMBOLIC: the residual prints as the
21
+ // reference the author wrote, so a recursive schema's canon is
22
+ // finite and round-trips, and the hash pins the mu-form rather than
23
+ // any unrolling.
24
+ // - At generation, an unexpanded residual REFUSES with
25
+ // recursion_unexpanded. Guardedness is therefore EMERGENT: under
26
+ // `next?:` the refusal is isolated and the optional key drops;
27
+ // under `*null | $.Node` the preference generates; a REQUIRED
28
+ // recursive field with no data refuses at the instance.
29
+
30
+ import type {
31
+ Val,
32
+ ValSpec,
33
+ } from '../type'
34
+
35
+ import {
36
+ DONE,
37
+ } from '../type'
38
+
39
+ import {
40
+ AontuContext,
41
+ } from '../ctx'
42
+
43
+ import { makeNilErr } from '../err'
44
+
45
+ import { FeatureVal } from './FeatureVal'
46
+ import { ConjunctVal } from './ConjunctVal'
47
+ import { unite } from '../unify'
48
+ import { propagateMarks, walk } from '../utility'
49
+
50
+
51
+ class RecurseVal extends FeatureVal {
52
+ isRecurse = true
53
+ isGenable = true
54
+ // LAST in a conjunct fold, after even the graph atoms: the residual
55
+ // wants to see the assembled concrete structure it expands against.
56
+ cjo = 47000
57
+
58
+ // The target path, absolute from the root, as the reference spelled
59
+ // it.
60
+ target: string[]
61
+ // Expansion depth so far along this chain, charged against the
62
+ // depth budget (the T-1 backstop).
63
+ xc: number
64
+
65
+ constructor(spec: ValSpec, ctx?: AontuContext) {
66
+ super(spec, ctx)
67
+ this.target = (spec as any).target ?? []
68
+ this.xc = (spec as any).xc ?? 0
69
+ // A settled residual: a type() body carrying one must settle, and
70
+ // an unmet recursion is its own value until data arrives.
71
+ this.dc = DONE
72
+ }
73
+
74
+ clone(ctx: AontuContext, spec?: ValSpec): Val {
75
+ const out: any = super.clone(ctx, spec)
76
+ out.target = this.target
77
+ out.xc = this.xc
78
+ return out
79
+ }
80
+
81
+ // The schema body the target names, from the ROOT: the fixpoint is
82
+ // over the finished definition, and the definition's own residual
83
+ // keeps it finite. When the root does not contain the target -- the
84
+ // residual was LIFTED out of its defining tree, as vet's anchored
85
+ // meet does -- fall back to ctx._fixroot, the settled tree the
86
+ // lifter kept for exactly this walk.
87
+ private body(ctx: AontuContext): Val | undefined {
88
+ return walkTarget(ctx.root, this.target)
89
+ ?? walkTarget((ctx as any)._fixroot, this.target)
90
+ }
91
+
92
+ unify(peer: Val, ctx: AontuContext): Val {
93
+ const p: any = peer
94
+
95
+ // The self-drive: nothing to advance -- the residual waits for
96
+ // structure. (A null/nil peer never arrives; unite's ladder
97
+ // absorbs both.)
98
+ if (null == peer || true === p.isTop) {
99
+ return this
100
+ }
101
+
102
+ // The same fixpoint twice is one fixpoint.
103
+ if (true === p.isRecurse) {
104
+ if (this.target.length === p.target.length
105
+ && this.target.every((s, i) => s === p.target[i])) {
106
+ return this
107
+ }
108
+ // Different targets -- mutual recursion meeting -- are BOTH
109
+ // held: each expands as data arrives, through the fold that
110
+ // keeps a conjunct's members separate.
111
+ const out = new ConjunctVal({ peg: [this, peer] }, ctx)
112
+ propagateMarks(this, out)
113
+ out.path = this.path
114
+ return out
115
+ }
116
+
117
+ // CONCRETE STRUCTURE: expand one level against it.
118
+ if (true === p.isMap || true === p.isList || true === p.isScalar) {
119
+ if (ctx.budget.depth <= this.xc) {
120
+ return makeNilErr(ctx, 'recursion_budget', this, peer, 'recurse',
121
+ { target: '$.' + this.target.join('.') })
122
+ }
123
+ const body = this.body(ctx)
124
+ if (undefined === body) {
125
+ // The definition has not assembled yet (an early pass): hold
126
+ // the peer beside the residual and try again when it has.
127
+ const out = new ConjunctVal({ peg: [this, peer] }, ctx)
128
+ propagateMarks(this, out)
129
+ out.path = this.path
130
+ return out
131
+ }
132
+ // ADR-005's clone discipline: the expansion is a per-destination
133
+ // instantiation, so the definition itself is never written
134
+ // into. The clone's type/hide marks are CLEARED at every depth,
135
+ // exactly as a plain reference copy clears them (concreteFlow's
136
+ // rule): the schema is hidden, the instances it expands into
137
+ // are the output.
138
+ // The clone is REBASED to the DRIVE path explicitly (spec.path;
139
+ // the Go twin passes cp(r.path), whose stored paths its clone
140
+ // arms keep clean): the default rebase slices the definition's
141
+ // path by the destination's length, which leaks definition
142
+ // segments into instance paths whenever the destination is
143
+ // shallower -- a residual carried inside a copied definition
144
+ // body (`chain: $.spec.Step` inside Policy, copied to a slot
145
+ // shallower than the definition) reported
146
+ // `$.payments_policy.Policy.chain.then.approver` for the
147
+ // finding Go placed at `$.payments_policy.chain.then.approver`.
148
+ // The drive path is also right under vet's anchored meet, which
149
+ // drives AT the anchor's own path (see vet.ts), so anchored
150
+ // findings land in the schema's namespace in both ports.
151
+ const level: any = body.clone(ctx, {
152
+ dup: true, path: [...ctx.path],
153
+ } as any)
154
+ walk(level, (_key: string | number | undefined, v: Val) => {
155
+ v.mark.type = false
156
+ v.mark.hide = false
157
+ return v
158
+ })
159
+ bumpRecurse(level, this.xc + 1)
160
+ return unite(ctx, level, peer, 'recurse-expand')
161
+ }
162
+
163
+ // Anything else -- a func still resolving, a reference, a
164
+ // constraint -- waits beside the residual.
165
+ const out = new ConjunctVal({ peg: [this, peer] }, ctx)
166
+ propagateMarks(this, out)
167
+ out.path = this.path
168
+ return out
169
+ }
170
+
171
+ get canon(): string {
172
+ return '$.' + this.target.join('.')
173
+ }
174
+
175
+ gen(ctx: AontuContext) {
176
+ // An unexpanded residual in a demanded position refuses;
177
+ // guardedness is emergent -- under an optional key the bag's
178
+ // isolated context swallows this and drops the key.
179
+ makeNilErr(ctx, 'recursion_unexpanded', this, undefined, 'recurse',
180
+ { target: '$.' + this.target.join('.') })
181
+ return undefined
182
+ }
183
+ }
184
+
185
+
186
+ // walkTarget descends a tree by the residual's absolute target path,
187
+ // answering the definition node or undefined.
188
+ function walkTarget(root: any, target: string[]): Val | undefined {
189
+ let node: any = root
190
+ for (const seg of target) {
191
+ node = node?.peg?.[seg]
192
+ }
193
+ return null != node && true === node.isVal ? node : undefined
194
+ }
195
+
196
+
197
+ // bumpRecurse stamps the expansion depth onto every residual inside a
198
+ // freshly cloned level, so descent is charged along the chain.
199
+ function bumpRecurse(v: any, xc: number): void {
200
+ if (null == v || true !== v.isVal) {
201
+ return
202
+ }
203
+ if (true === v.isRecurse) {
204
+ v.xc = Math.max(v.xc, xc)
205
+ return
206
+ }
207
+ const peg: any = v.peg
208
+ if (true === v.isMap && null != peg) {
209
+ for (const k of Object.keys(peg)) {
210
+ bumpRecurse(peg[k], xc)
211
+ }
212
+ }
213
+ else if (true === v.isList && Array.isArray(peg)) {
214
+ for (const e of peg) {
215
+ bumpRecurse(e, xc)
216
+ }
217
+ }
218
+ else if (true === v.isConjunct && Array.isArray(peg)) {
219
+ for (const e of peg) {
220
+ bumpRecurse(e, xc)
221
+ }
222
+ }
223
+ if (null != v.spread?.cj) {
224
+ bumpRecurse(v.spread.cj, xc)
225
+ }
226
+ }
227
+
228
+
229
+ // containsRecurseOf answers whether a definition holds a residual of
230
+ // the given target -- i.e. the definition is (transitively) the
231
+ // fixpoint that target names. A reference RESOLVING to such a
232
+ // definition must itself answer the residual: cloned instead, every
233
+ // reparse of a canon unrolled the schema one more level and canon
234
+ // never converged.
235
+ function containsRecurseOf(v: any, target: string[], depth?: number): boolean {
236
+ const d = depth ?? 0
237
+ if (null == v || true !== v.isVal || 8 < d) {
238
+ return false
239
+ }
240
+ if (true === v.isRecurse) {
241
+ return v.target.length === target.length
242
+ && v.target.every((s: string, i: number) => s === target[i])
243
+ }
244
+ // A RAW REFERENCE to the target IS the recursion, minted or not:
245
+ // the answer must not depend on whether the definition's own
246
+ // prefix position has been visited yet. Without this arm the
247
+ // answer was ORDER-DEPENDENT -- reparsing a generated canon puts
248
+ // the instance before the definition, its trailing `$.spec.Step`
249
+ // leaves resolved before `Step.then` had minted, and each resolve
250
+ // cloned one more unrolled level until the unify depth guard
251
+ // (unify_cycle) killed the document.
252
+ if (true === v.isRef && Array.isArray(v.peg)) {
253
+ if (v.peg.length === target.length
254
+ && v.peg.every((s: any, i: number) => s === target[i])) {
255
+ return true
256
+ }
257
+ }
258
+ const peg: any = v.peg
259
+ if (true === v.isMap && null != peg) {
260
+ for (const k of Object.keys(peg)) {
261
+ if (containsRecurseOf(peg[k], target, d + 1)) {
262
+ return true
263
+ }
264
+ }
265
+ }
266
+ else if ((true === v.isList || true === v.isConjunct || true === v.isDisjunct)
267
+ && Array.isArray(peg)) {
268
+ for (const e of peg) {
269
+ if (containsRecurseOf(e, target, d + 1)) {
270
+ return true
271
+ }
272
+ }
273
+ }
274
+ if (null != v.spread?.cj && containsRecurseOf(v.spread.cj, target, d + 1)) {
275
+ return true
276
+ }
277
+ return false
278
+ } /* node:coverage ignore next 7 */
279
+
280
+
281
+ export {
282
+ RecurseVal,
283
+ bumpRecurse,
284
+ containsRecurseOf,
285
+ }
package/src/val/RefVal.ts CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  import { AontuContext } from '../ctx'
23
23
 
24
24
  import { makeNilErr } from '../err'
25
+ import { RecurseVal, containsRecurseOf } from './RecurseVal'
25
26
  import { unite } from '../unify'
26
27
 
27
28
 
@@ -279,15 +280,36 @@ class RefVal extends FeatureVal {
279
280
  // let descent = ''
280
281
 
281
282
  if (isprefixpath) {
282
- // console.log('SELFPATH', selfpath, 'PEGPATH', pegpath)
283
- out = makeNilErr(ctx, 'path_cycle', this)
283
+ // THE DETECTOR'S ANSWER IS A RESIDUAL (RECURSION.0.md): a
284
+ // self-reference under a guarded shape is the fixpoint the
285
+ // author wrote, so the prefix hit mints the recursive residual
286
+ // instead of refusing -- except the degenerate all-empty
287
+ // spelling (path("")), which names nothing and keeps its
288
+ // path_cycle. Only all-string paths recurse; anything else
289
+ // keeps the conservative refusal.
290
+ let degenerate = 0 === this.path.length
291
+ let target: string[] = []
292
+ for (let i = 0; i < this.peg.length && !degenerate; i++) {
293
+ if ('string' !== typeof this.peg[i] || '' === this.peg[i]) {
294
+ degenerate = true
295
+ break
296
+ }
297
+ target.push(this.peg[i] as string)
298
+ }
299
+ if (degenerate) {
300
+ out = makeNilErr(ctx, 'path_cycle', this)
301
+ }
302
+ else {
303
+ const rec: any = new RecurseVal({ target } as any, ctx)
304
+ rec.site = this.site
305
+ rec.path = [...this.path]
306
+ out = rec
307
+ }
284
308
  }
285
309
  else {
286
310
 
287
311
  let parts: string[] = []
288
312
 
289
- let modes: string[] = []
290
-
291
313
  for (let pI = 0; pI < this.peg.length; pI++) {
292
314
  let part = this.peg[pI]
293
315
  // An unspellable segment MISSES BEFORE ANY LOOKUP. The marker
@@ -301,38 +323,13 @@ class RefVal extends FeatureVal {
301
323
  return makeNilErr(ctx, 'no_path', this)
302
324
  }
303
325
  if (part instanceof VarVal) {
304
- let strval = (part as VarVal).peg
305
- let name = strval ? '' + strval.peg : ''
306
-
307
- if ('KEY' === name) {
308
- if (pI === this.peg.length - 1) {
309
- modes.push(name)
310
- }
311
- else {
312
- // TODO: return a Nil explaining error
313
- return
314
- }
315
- }
316
-
317
- if ('SELF' === name) {
318
- if (pI === 0) {
319
- modes.push(name)
320
- }
321
- else {
322
- // TODO: return a Nil explaining error
323
- return
324
- }
325
- }
326
- else if ('PARENT' === name) {
327
- if (pI === 0) {
328
- modes.push(name)
329
- }
330
- else {
331
- // TODO: return a Nil explaining error
332
- return
333
- }
334
- }
335
- else if (0 === modes.length) {
326
+ // EVERY `$name` IN A PATH IS AN ORDINARY VARIABLE. `$KEY`,
327
+ // `$SELF` and `$PARENT` used to be intercepted here by name;
328
+ // they are gone (ADR-009). `key()` is the replacement for
329
+ // `$KEY` and answers where a value LANDS rather than where it
330
+ // was written, `$SELF.x` was only ever `$.x`, and `$PARENT.x`
331
+ // was only ever `.x`.
332
+ {
336
333
  part = (part as VarVal).unify(top(), ctx)
337
334
  if (part.isNil) {
338
335
  // TODO: var not found, so can't find path
@@ -367,15 +364,9 @@ class RefVal extends FeatureVal {
367
364
  refpath = parts
368
365
  }
369
366
  else {
370
- // TODO: deprecate $KEY, etc
371
- refpath = this.path.slice(
372
- 0,
373
- (
374
- modes.includes('SELF') ? 0 :
375
- modes.includes('PARENT') ? -1 :
376
- -1 // siblings
377
- )
378
- ).concat(parts)
367
+ // A relative reference reads from the SIBLING scope: drop this
368
+ // node's own key and append the written segments.
369
+ refpath = this.path.slice(0, -1).concat(parts)
379
370
  }
380
371
 
381
372
  let sep = '.'
@@ -383,29 +374,6 @@ class RefVal extends FeatureVal {
383
374
  .reduce(((a: string[], p: string) =>
384
375
  (p === sep ? a.length = a.length - 1 : a.push(p), a)), [])
385
376
 
386
- if (modes.includes('KEY')) {
387
- // STRINGIFY. A LIST index arrives here as a JS NUMBER (jsonic puts
388
- // it in the path as one, and lang.ts copies the path wholesale),
389
- // so `.$KEY` inside a list built a StringVal whose peg was the
390
- // number 0 -- an ill-formed value, not a design choice: it canoned
391
- // as a bare 0, generated a JSON number, satisfied `number` and
392
- // failed `string`. Go stringifies, and key() already agreed with
393
- // Go, so this port disagreed with itself.
394
- //
395
- // Coerced HERE, at the consumption site, rather than by
396
- // normalising Val.path: the numeric segment originates in jsonic's
397
- // own r.k.path and every other path consumer (find's descent,
398
- // key(), the clone/spread machinery) already handles it.
399
- let key = this.path[this.path.length - 2]
400
- let sv = new StringVal({ peg: null == key ? '' : '' + key }, ctx)
401
-
402
- // TODO: other props?
403
- sv.dc = DONE
404
- sv.path = this.path
405
-
406
- return sv
407
- }
408
-
409
377
  let node = ctx.root as Val
410
378
 
411
379
  let nopath = false
@@ -423,6 +391,21 @@ class RefVal extends FeatureVal {
423
391
  else if (node.isList) {
424
392
  node = node.peg[part]
425
393
  }
394
+ // A PENDING MARK WRAPPER IS TRANSPARENT TO THE WALK: hide()
395
+ // and type() only mark, and their argument is the structure
396
+ // the path names. Without this, two sibling schemas in one
397
+ // hide() bag deadlock -- the wrapper waits for its argument,
398
+ // the argument's members wait for references that walk into
399
+ // the unresolved wrapper (BUGS.md §53's family; the
400
+ // recursive Policy/Step pair found it again). The found node
401
+ // is cloned and mark-cleared exactly as any reference target
402
+ // is, and the wrapper still marks its own field.
403
+ else if (true === (node as any).isFunc
404
+ && ((node as any).isHideFunc || (node as any).isTypeFunc)
405
+ && (true === (node as any).peg?.[0]?.isMap
406
+ || true === (node as any).peg?.[0]?.isList)) {
407
+ node = (node as any).peg[0].peg[part]
408
+ }
426
409
  else if (node.done) {
427
410
  nopath = true
428
411
  break;
@@ -439,8 +422,41 @@ class RefVal extends FeatureVal {
439
422
  }
440
423
  }
441
424
 
442
- // console.log('REFPATH', ctx.cc, pI, refpath, nopath, ctx.root, node)
443
-
425
+ // THE ANCHORED-MEET FALLBACK (vet --at): an absolute reference
426
+ // inside a lifted subtree names the SCHEMA's namespace, and the
427
+ // meet's root does not contain it. The settled schema root the
428
+ // lifter kept (AontuContext._fixroot; see RecurseVal.body) is
429
+ // the tree such a reference means. Without this, a recursion
430
+ // re-entering through a list spread died as no_path at the
431
+ // first element under vet --at, while the map-tail form (whose
432
+ // residual was minted during schema settling) worked; the Go
433
+ // port answers the anchored meet from settled structures and
434
+ // never sees the gap.
435
+ const fixroot: any = (ctx as any)._fixroot
436
+ if (this.absolute && null != fixroot
437
+ && (nopath || pI !== refpath.length)) {
438
+ nopath = false
439
+ pI = 0
440
+ let fnode: any = fixroot
441
+ for (; pI < refpath.length; pI++) {
442
+ const part = refpath[pI]
443
+ if (true === fnode.isMap || true === fnode.isList) {
444
+ fnode = fnode.peg[part]
445
+ }
446
+ else {
447
+ break
448
+ }
449
+ if (null == fnode) {
450
+ break
451
+ }
452
+ }
453
+ if (null != fnode && pI === refpath.length) {
454
+ node = fnode
455
+ }
456
+ else {
457
+ nopath = true
458
+ }
459
+ }
444
460
 
445
461
  if (nopath) {
446
462
  out = makeNilErr(ctx, 'no_path', this)
@@ -494,6 +510,18 @@ class RefVal extends FeatureVal {
494
510
  !out.done) {
495
511
  out = undefined
496
512
  }
513
+ // A REFERENCE TO A RECURSIVE DEFINITION IS THE FIXPOINT
514
+ // REFERENCE (RECURSION.0.md): resolving it to a clone
515
+ // unrolled the schema one level, and every reparse of a
516
+ // canon then unrolled one more -- canon never converged. The
517
+ // residual is the resolved form, exactly as at the prefix
518
+ // positions inside the definition.
519
+ else if (null != out && !snap && containsRecurseOf(out, this.peg as any)) {
520
+ const rec: any = new RecurseVal({ target: [...this.peg] } as any, ctx)
521
+ rec.site = this.site
522
+ rec.path = [...this.path]
523
+ out = rec
524
+ }
497
525
  // Types and hidden values are cloned and made concrete
498
526
  else if (null != out) { // && (out.mark.type || out.mark.hide)) {
499
527
 
@@ -522,19 +550,12 @@ class RefVal extends FeatureVal {
522
550
  walk(out, (_key: string | number | undefined, val: Val) => {
523
551
  val.mark.type = false
524
552
  val.mark.hide = false
525
- // REFERENCES DO NOT CARRY IDENTITY (G4 phase 1, clearing
526
- // rule 1). The clone is a copy of an entity, not the
527
- // entity: without this, `w:b:$.q.a & {y:2,z:3}` (row
528
- // `ref-and-merge`, test/spec/ref.tsv) would push `y:2`
529
- // back into `q.a` through the identity merge — pinned
530
- // behaviour, silently changed by a mark the author never
531
- // wrote at the reference site.
532
- //
533
- // The LINK is NOT cleared (G4 phase 3): an identity says
534
- // what a value IS, so a copy must not be that entity; a
535
- // link says what a value POINTS AT, and a copy of a link
536
- // points at the same thing.
537
- val.entity = undefined
553
+ // THE LINK IS NOT CLEARED (G4 phase 3): a link says what a
554
+ // value POINTS AT, and a copy of a link points at the same
555
+ // thing. An ABSOLUTE address still names the same node from
556
+ // the copy; a RELATIVE one is read from the copy's own
557
+ // position, which is what makes a referenced model resolve
558
+ // its internal links inside the copy (ADR-014).
538
559
  return val
539
560
  })
540
561
  //}
@@ -719,5 +740,6 @@ class RefVal extends FeatureVal {
719
740
 
720
741
 
721
742
  export {
743
+ pendingMarkWrapper,
722
744
  RefVal,
723
745
  }