aontu 0.62.0 → 0.64.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.
- package/README.md +7 -7
- package/dist/agentsmd.js +0 -27
- package/dist/agentsmd.js.map +1 -1
- package/dist/alias.js.map +1 -1
- package/dist/allow.js +0 -92
- package/dist/allow.js.map +1 -1
- package/dist/aontu.d.ts +3 -3
- package/dist/aontu.js +4 -84
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.d.ts +4 -0
- package/dist/aontumodel.js +27 -0
- package/dist/aontumodel.js.map +1 -0
- package/dist/casing.d.ts +5 -0
- package/dist/casing.js +90 -0
- package/dist/casing.js.map +1 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +228 -935
- package/dist/cli.js.map +1 -1
- package/dist/ctx.js +0 -48
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js +0 -32
- package/dist/diff.js.map +1 -1
- package/dist/err.js +0 -40
- package/dist/err.js.map +1 -1
- package/dist/escape.js +0 -45
- package/dist/escape.js.map +1 -1
- package/dist/exactjson.d.ts +0 -35
- package/dist/exactjson.js +0 -131
- package/dist/exactjson.js.map +1 -1
- package/dist/format.js +13 -203
- package/dist/format.js.map +1 -1
- package/dist/grammar.d.ts +9 -0
- package/dist/grammar.js +54 -0
- package/dist/grammar.js.map +1 -0
- package/dist/graph.js +0 -26
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +0 -82
- package/dist/hcanon.js.map +1 -1
- package/dist/helpdoc.js +2 -2
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.d.ts +0 -6
- package/dist/hints.js +57 -55
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +0 -114
- package/dist/jsonschema.js.map +1 -1
- package/dist/keyorder.d.ts +0 -7
- package/dist/keyorder.js +0 -41
- package/dist/keyorder.js.map +1 -1
- package/dist/lang.js +17 -915
- package/dist/lang.js.map +1 -1
- package/dist/lsp-server.js +0 -16
- package/dist/lsp-server.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +12 -159
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +0 -26
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp.js +0 -149
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +16 -131
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.js +0 -162
- package/dist/mod.js.map +1 -1
- package/dist/patch.js +0 -217
- package/dist/patch.js.map +1 -1
- package/dist/profile.d.ts +9 -0
- package/dist/profile.js +28 -0
- package/dist/profile.js.map +1 -0
- package/dist/provenance.js +0 -140
- package/dist/provenance.js.map +1 -1
- package/dist/query.js +0 -75
- package/dist/query.js.map +1 -1
- package/dist/reach.js +0 -43
- package/dist/reach.js.map +1 -1
- package/dist/relation.js +0 -61
- package/dist/relation.js.map +1 -1
- package/dist/report-sarif.d.ts +0 -11
- package/dist/report-sarif.js +0 -28
- package/dist/report-sarif.js.map +1 -1
- package/dist/sig.js +0 -35
- package/dist/sig.js.map +1 -1
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/siggate.js +0 -4
- package/dist/siggate.js.map +1 -1
- package/dist/site.js +3 -29
- package/dist/site.js.map +1 -1
- package/dist/subsume.d.ts +0 -10
- package/dist/subsume.js +0 -137
- package/dist/subsume.js.map +1 -1
- package/dist/template.d.ts +2 -1
- package/dist/template.js +58 -138
- package/dist/template.js.map +1 -1
- package/dist/trace.d.ts +21 -0
- package/dist/trace.js +107 -0
- package/dist/trace.js.map +1 -0
- package/dist/trim.js +0 -41
- package/dist/trim.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.js +13 -248
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +0 -22
- package/dist/utility.js.map +1 -1
- package/dist/val/AbnfFuncVal.d.ts +18 -0
- package/dist/val/AbnfFuncVal.js +132 -0
- package/dist/val/AbnfFuncVal.js.map +1 -0
- package/dist/val/AbsentVal.d.ts +11 -0
- package/dist/val/AbsentVal.js +30 -0
- package/dist/val/AbsentVal.js.map +1 -0
- package/dist/val/AggFuncVal.d.ts +10 -1
- package/dist/val/AggFuncVal.js +104 -116
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/ArithFuncVal.js +0 -12
- package/dist/val/ArithFuncVal.js.map +1 -1
- package/dist/val/BagVal.js +1 -78
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/BigDecimalVal.js +0 -16
- package/dist/val/BigDecimalVal.js.map +1 -1
- package/dist/val/BigIntegerVal.js +0 -16
- package/dist/val/BigIntegerVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +0 -9
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/CmpFuncVal.d.ts +2 -0
- package/dist/val/CmpFuncVal.js +49 -72
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.js +0 -29
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +0 -500
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.js +0 -2
- package/dist/val/ContainerKindVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +0 -3
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.js +0 -179
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +0 -152
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.js +0 -3
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +1 -1
- package/dist/val/EmitFuncVal.js +6 -119
- package/dist/val/EmitFuncVal.js.map +1 -1
- package/dist/val/ExpectVal.js +0 -62
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FilterFuncVal.js +0 -25
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +7 -127
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.js +0 -15
- package/dist/val/GraphAtomVal.js.map +1 -1
- package/dist/val/HideFuncVal.js +0 -13
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IntegerVal.js +0 -61
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/JunctionVal.js +0 -20
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +0 -46
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +0 -57
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js +0 -11
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +0 -151
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.js +0 -27
- package/dist/val/MatchFuncVal.js.map +1 -1
- package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
- package/dist/val/MaybeFuncVal.js +50 -0
- package/dist/val/MaybeFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js +0 -18
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +0 -60
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +18 -54
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/NumberVal.js +0 -15
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -0
- package/dist/val/OpBaseVal.js +3 -15
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/PackFuncVal.js +0 -34
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +0 -6
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.js +0 -41
- package/dist/val/PathVal.js.map +1 -1
- package/dist/val/PlaceVal.js +0 -25
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +1 -7
- package/dist/val/PlusOpVal.js +13 -74
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js +0 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.js +0 -167
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.js +0 -55
- package/dist/val/RecurseVal.js.map +1 -1
- package/dist/val/RefVal.js +0 -282
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +3 -232
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.js +0 -49
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/ScalarVal.js +0 -11
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/StrFuncVal.js +0 -18
- package/dist/val/StrFuncVal.js.map +1 -1
- package/dist/val/SuperFuncVal.js +0 -32
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.js +0 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TranslateFuncVal.js +1 -3
- package/dist/val/TranslateFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js +0 -11
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -0
- package/dist/val/Val.js +2 -133
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +0 -12
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.js +0 -37
- package/dist/val/arith.js.map +1 -1
- package/dist/val/caserange.js +0 -61
- package/dist/val/caserange.js.map +1 -1
- package/dist/val/members.js +0 -6
- package/dist/val/members.js.map +1 -1
- package/dist/val/numcmp.js +0 -11
- package/dist/val/numcmp.js.map +1 -1
- package/dist/val/numkind.js +0 -145
- package/dist/val/numkind.js.map +1 -1
- package/dist/val/valutil.js +0 -16
- package/dist/val/valutil.js.map +1 -1
- package/dist/vet.js +0 -461
- package/dist/vet.js.map +1 -1
- package/dist/view.js +0 -414
- package/dist/view.js.map +1 -1
- package/dist/walk.js +0 -41
- package/dist/walk.js.map +1 -1
- package/grammar/aontu.abnf +8 -6
- package/grammar/aontu.gbnf +4 -4
- package/grammar/aontu.lark +4 -4
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +4 -2
- package/skill/tasks.md +9 -7
- package/src/agentsmd.ts +0 -35
- package/src/alias.ts +0 -39
- package/src/allow.ts +1 -96
- package/src/aontu.ts +4 -116
- package/src/aontumodel.ts +26 -0
- package/src/casing.ts +95 -0
- package/src/cli.ts +258 -1024
- package/src/ctx.ts +0 -103
- package/src/diff.ts +0 -40
- package/src/err.ts +0 -40
- package/src/escape.ts +0 -46
- package/src/exactjson.ts +0 -131
- package/src/format.ts +14 -257
- package/src/grammar.ts +72 -0
- package/src/graph.ts +0 -61
- package/src/hcanon.ts +0 -82
- package/src/helpdoc.ts +2 -2
- package/src/hints.ts +69 -57
- package/src/jsonschema.ts +0 -123
- package/src/keyorder.ts +0 -42
- package/src/lang.ts +19 -931
- package/src/lsp-server.ts +0 -16
- package/src/lsp.ts +12 -180
- package/src/mcp-server.ts +0 -31
- package/src/mcp.ts +0 -169
- package/src/mod-tool.ts +18 -159
- package/src/mod.ts +0 -178
- package/src/patch.ts +0 -232
- package/src/profile.ts +42 -0
- package/src/provenance.ts +0 -183
- package/src/query.ts +0 -84
- package/src/reach.ts +0 -53
- package/src/relation.ts +0 -84
- package/src/report-sarif.ts +0 -48
- package/src/sig.ts +0 -35
- package/src/sigdecl.ts +1 -1
- package/src/siggate.ts +0 -30
- package/src/site.ts +3 -29
- package/src/subsume.ts +1 -161
- package/src/template.ts +69 -140
- package/src/trace.ts +157 -0
- package/src/trim.ts +0 -53
- package/src/type.ts +2 -45
- package/src/unify.ts +14 -257
- package/src/utility.ts +0 -31
- package/src/val/AbnfFuncVal.ts +181 -0
- package/src/val/AbsentVal.ts +54 -0
- package/src/val/AggFuncVal.ts +152 -188
- package/src/val/ArithFuncVal.ts +0 -20
- package/src/val/BagVal.ts +1 -78
- package/src/val/BigDecimalVal.ts +0 -16
- package/src/val/BigIntegerVal.ts +0 -16
- package/src/val/CloseFuncVal.ts +0 -9
- package/src/val/CmpFuncVal.ts +68 -184
- package/src/val/ConjunctVal.ts +0 -33
- package/src/val/ConstraintVal.ts +2 -537
- package/src/val/ContainerKindVal.ts +0 -18
- package/src/val/CopyFuncVal.ts +0 -5
- package/src/val/Decimal.ts +1 -185
- package/src/val/DeprecateFuncVal.ts +0 -10
- package/src/val/DisjunctVal.ts +0 -157
- package/src/val/EachFuncVal.ts +0 -40
- package/src/val/EmitFuncVal.ts +8 -208
- package/src/val/ExpectVal.ts +0 -62
- package/src/val/FilterFuncVal.ts +0 -55
- package/src/val/FuncBaseVal.ts +9 -130
- package/src/val/GraphAtomVal.ts +0 -42
- package/src/val/HideFuncVal.ts +0 -15
- package/src/val/IntegerVal.ts +0 -61
- package/src/val/JunctionVal.ts +0 -20
- package/src/val/KeyFuncVal.ts +0 -48
- package/src/val/ListVal.ts +0 -59
- package/src/val/LowerFuncVal.ts +0 -12
- package/src/val/MapVal.ts +0 -151
- package/src/val/MatchFuncVal.ts +0 -59
- package/src/val/MaybeFuncVal.ts +86 -0
- package/src/val/MoveFuncVal.ts +0 -20
- package/src/val/NilVal.ts +0 -60
- package/src/val/NomFuncVal.ts +10 -99
- package/src/val/NumberVal.ts +0 -16
- package/src/val/OpBaseVal.ts +4 -17
- package/src/val/PackFuncVal.ts +0 -63
- package/src/val/PathFuncVal.ts +0 -32
- package/src/val/PathVal.ts +0 -66
- package/src/val/PlaceVal.ts +0 -45
- package/src/val/PlusOpVal.ts +18 -75
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +0 -179
- package/src/val/RecurseVal.ts +0 -81
- package/src/val/RefVal.ts +1 -285
- package/src/val/ReferFuncVal.ts +4 -255
- package/src/val/ScalarKindVal.ts +0 -50
- package/src/val/ScalarVal.ts +0 -12
- package/src/val/StrFuncVal.ts +0 -44
- package/src/val/SuperFuncVal.ts +0 -42
- package/src/val/TopVal.ts +0 -1
- package/src/val/TranslateFuncVal.ts +1 -51
- package/src/val/UpperFuncVal.ts +0 -12
- package/src/val/Val.ts +3 -192
- package/src/val/VarVal.ts +0 -15
- package/src/val/arith.ts +0 -92
- package/src/val/caserange.ts +0 -62
- package/src/val/members.ts +0 -23
- package/src/val/numcmp.ts +1 -27
- package/src/val/numkind.ts +0 -149
- package/src/val/valutil.ts +0 -16
- package/src/vet.ts +1 -582
- package/src/view.ts +0 -507
- package/src/walk.ts +0 -41
- package/dist/lower.d.ts +0 -23
- package/dist/lower.js +0 -578
- package/dist/lower.js.map +0 -1
- package/dist/render.d.ts +0 -53
- package/dist/render.js +0 -547
- package/dist/render.js.map +0 -1
- package/dist/std.d.ts +0 -3
- package/dist/std.js +0 -672
- package/dist/std.js.map +0 -1
- package/dist/val/FormFuncVal.js +0 -55
- package/dist/val/FormFuncVal.js.map +0 -1
- package/dist/val/NamerFuncVal.d.ts +0 -12
- package/dist/val/NamerFuncVal.js +0 -176
- package/dist/val/NamerFuncVal.js.map +0 -1
- package/src/lower.ts +0 -636
- package/src/render.ts +0 -732
- package/src/std.ts +0 -683
package/src/patch.ts
CHANGED
|
@@ -1,58 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// OVERLAY PATCH (G7 phase 5,
|
|
4
|
-
// docs/capability-review/g7-machine-access.md): change a document by
|
|
5
|
-
// APPENDING to an overlay, not by rewriting the file.
|
|
6
|
-
//
|
|
7
|
-
// This is the stage that needs no rewriter. An overlay entry is just
|
|
8
|
-
// another conjunct, and unification is order-independent, so appending
|
|
9
|
-
// `services: auth: owner: "identity-2"` to a second file and
|
|
10
|
-
// evaluating both is exactly the same value as writing it into the
|
|
11
|
-
// first — with no parsing of the target, no comment or layout damage,
|
|
12
|
-
// and nothing to preserve. The spec pins that equivalence rather than
|
|
13
|
-
// asserting it.
|
|
14
|
-
//
|
|
15
|
-
// What an overlay CANNOT do is change a PINNED value: the lattice
|
|
16
|
-
// refuses 5 against 3, and the report says so with the pinning site,
|
|
17
|
-
// which `why` then locates. That left the loop "set → conflict → why →
|
|
18
|
-
// edit the pinning site" with its last step manual — and since the
|
|
19
|
-
// commonest vet failure of all is "the data pins the wrong value",
|
|
20
|
-
// `set` was unable to repair the very case it existed for.
|
|
21
|
-
//
|
|
22
|
-
// IN-PLACE REPLACE (`--in-place`) closes that. The G7 design deferred
|
|
23
|
-
// it behind two prerequisites: an evaluated-path → contributing-span
|
|
24
|
-
// map, and a comment-and-layout-preserving CST. The first now exists —
|
|
25
|
-
// `why` is that map, and sites carry `len` and `src` since a site was
|
|
26
|
-
// given an extent. The second turns out NOT to be needed for the case
|
|
27
|
-
// that matters, and the reason is worth stating: a CST is what you need
|
|
28
|
-
// to RE-SERIALISE a document, and a targeted span splice serialises
|
|
29
|
-
// nothing. It replaces `len` code units at one offset and leaves every
|
|
30
|
-
// other byte — every comment, every blank line, every alignment space —
|
|
31
|
-
// exactly as the author left it, because it never looks at them.
|
|
32
|
-
//
|
|
33
|
-
// What makes the splice safe rather than merely plausible is that the
|
|
34
|
-
// site carries `src`, the text it claims to cover. The span is VERIFIED
|
|
35
|
-
// against it before a byte is written, so the corrupting arithmetic
|
|
36
|
-
// this repository has already shipped once — `port: 0x1F` reporting
|
|
37
|
-
// canon `"31"` at column 7, and `(col, canon.length)` writing
|
|
38
|
-
// `port: 5x1F` — cannot be reached: `0x1F` is four code units and says
|
|
39
|
-
// so, and if the text at the span is anything else the edit is refused
|
|
40
|
-
// rather than guessed.
|
|
41
|
-
//
|
|
42
|
-
// Replace is never WORSE than append. Where the value is not a single
|
|
43
|
-
// editable literal in this overlay — a spread template governing other
|
|
44
|
-
// keys, a reference whose site is the `$` and not the target, two
|
|
45
|
-
// statements pinning the same path, a literal in an included file — the
|
|
46
|
-
// splice is refused and the assignment is APPENDED exactly as it would
|
|
47
|
-
// have been without the flag, plus one `warning` finding naming the
|
|
48
|
-
// case and the site it came from. Warnings never move a verdict, so
|
|
49
|
-
// `--in-place` cannot turn a run that would have succeeded into one
|
|
50
|
-
// that fails; it can only rewrite where rewriting is safe, and explain
|
|
51
|
-
// itself where it is not.
|
|
52
|
-
//
|
|
53
|
-
// The verdict is G2's, unchanged: `vet(entry, overlay)` already asks
|
|
54
|
-
// exactly the right question — does this document hold against that
|
|
55
|
-
// truth, and if not, where — so `set` adds a writer, not a report.
|
|
56
3
|
|
|
57
4
|
import { vet } from './vet'
|
|
58
5
|
import type { TrustOptions } from './type'
|
|
@@ -67,19 +14,10 @@ export type PatchOptions = {
|
|
|
67
14
|
// them resolve from their own directories (vet's precedent).
|
|
68
15
|
entryPath?: string
|
|
69
16
|
overlayPath?: string
|
|
70
|
-
// Rewrite a pinned literal where the author wrote it, instead of
|
|
71
|
-
// appending a line that contradicts it. Opt-in: appending is
|
|
72
|
-
// non-destructive and in-place editing is not, so the caller says
|
|
73
|
-
// which one they meant.
|
|
74
17
|
inPlace?: boolean
|
|
75
18
|
// The include capability this document evaluates under
|
|
76
19
|
// (G5, docs/trust.md); vet's precedent.
|
|
77
20
|
trust?: TrustOptions
|
|
78
|
-
// The extensions an include additionally reads as text (the CLI's
|
|
79
|
-
// --text-ext). It rides WITH the capability, never beside it: this
|
|
80
|
-
// verb threaded the capability and not the extension, so `set`
|
|
81
|
-
// refused an include -- and wrote nothing -- under a flag the bare
|
|
82
|
-
// command honoured.
|
|
83
21
|
textExt?: string[]
|
|
84
22
|
}
|
|
85
23
|
|
|
@@ -97,13 +35,7 @@ export type PatchReplacement = {
|
|
|
97
35
|
}
|
|
98
36
|
|
|
99
37
|
export type PatchReport = {
|
|
100
|
-
// The overlay text as it would stand after the assignments: the
|
|
101
|
-
// existing text, with any in-place replacements applied, plus one
|
|
102
|
-
// appended line for each assignment that was not replaced. The caller
|
|
103
|
-
// writes it — an engine that touched the filesystem could not be used
|
|
104
|
-
// by a server, and the CLI is the one place that knows about files.
|
|
105
38
|
overlay: string
|
|
106
|
-
// The appended lines alone, in order.
|
|
107
39
|
appended: string[]
|
|
108
40
|
// The in-place replacements made, in the order the assignments were
|
|
109
41
|
// given (NOT the order they were applied to the text, which is
|
|
@@ -133,23 +65,12 @@ export function parseAssignment(
|
|
|
133
65
|
}
|
|
134
66
|
|
|
135
67
|
|
|
136
|
-
// The path-flattened conjunct one assignment becomes:
|
|
137
|
-
// `$.a.b = 1` is `"a": "b": 1`. Keys are QUOTED — a segment may be a
|
|
138
|
-
// word the grammar spells otherwise (`if`), a number, or a name with
|
|
139
|
-
// a space in it, and quoting one key is the same value as writing it
|
|
140
|
-
// bare.
|
|
141
68
|
export function overlayLine(path: string, value: string): string {
|
|
142
69
|
return pathParts(path).map((p) => JSON.stringify(p)).join(': ') +
|
|
143
70
|
': ' + value
|
|
144
71
|
}
|
|
145
72
|
|
|
146
73
|
|
|
147
|
-
// The character offset of a 1-based (row, col) in `src`, or -1 when the
|
|
148
|
-
// text has no such position. Columns are UTF-16 code units, which is
|
|
149
|
-
// what a site carries and what a JavaScript string index already is —
|
|
150
|
-
// so this is the inverse of the site arithmetic, not a reinterpretation
|
|
151
|
-
// of it (go/patch.go converts to a byte offset, because Go strings are
|
|
152
|
-
// bytes; both address the same character).
|
|
153
74
|
export function offsetAt(src: string, row: number, col: number): number {
|
|
154
75
|
if (row < 1 || col < 1) {
|
|
155
76
|
return -1
|
|
@@ -177,27 +98,9 @@ export function spanAt(
|
|
|
177
98
|
}
|
|
178
99
|
|
|
179
100
|
|
|
180
|
-
// DOES THE TEXT AT THIS SITE SAY WHAT THE SITE CLAIMS IT SAYS?
|
|
181
|
-
//
|
|
182
|
-
// The last check before a splice, and the one that makes the write
|
|
183
|
-
// PROVABLE rather than argued. Exported so it can be exercised with a
|
|
184
|
-
// site the engine would never produce — an out-of-range position, a
|
|
185
|
-
// span over different text — which is the only way to test a guard whose
|
|
186
|
-
// whole purpose is to catch a state the rest of the code says cannot
|
|
187
|
-
// happen. (go/patch.go has the twin, tested the same way.)
|
|
188
101
|
export function spanHolds(
|
|
189
102
|
src: string, site: { row: number, col: number, len: number }, expect: string
|
|
190
103
|
): boolean {
|
|
191
|
-
// THE SITE'S OWN LENGTH IS PART OF ITS CLAIM, and is checked before
|
|
192
|
-
// the text is. A site whose `len` disagrees with the text it says it
|
|
193
|
-
// covers CONTRADICTS ITSELF, which is exactly the state this guard
|
|
194
|
-
// exists to catch — and a zero-length span would otherwise compare
|
|
195
|
-
// equal against nothing and then splice nothing, INSERTING the new
|
|
196
|
-
// value rather than replacing anything.
|
|
197
|
-
//
|
|
198
|
-
// Both ports compare in UTF-16 code units, which is what a site's
|
|
199
|
-
// `len` counts. That is free here and is not in Go, where a string is
|
|
200
|
-
// bytes (go/patch.go converts).
|
|
201
104
|
if ('' === expect || site.len !== expect.length) {
|
|
202
105
|
return false
|
|
203
106
|
}
|
|
@@ -205,68 +108,17 @@ export function spanHolds(
|
|
|
205
108
|
}
|
|
206
109
|
|
|
207
110
|
|
|
208
|
-
// WHY IS THE VALUE AT THIS PATH WHAT IT IS, and is exactly one of the
|
|
209
|
-
// answers a literal this overlay can edit in place?
|
|
210
|
-
//
|
|
211
|
-
// The four refusals below are not defensive padding; each is a real
|
|
212
|
-
// document shape that the probe corpus produced, and each would corrupt
|
|
213
|
-
// something different if the splice ran anyway:
|
|
214
|
-
//
|
|
215
|
-
// - a SPREAD contribution's site is inside the template, which
|
|
216
|
-
// governs every other key too, so rewriting it there changes keys
|
|
217
|
-
// the author did not name;
|
|
218
|
-
// - a REFERENCE's site is the `$` that starts the path and has length
|
|
219
|
-
// 1, so splicing over it writes the new value INTO the path
|
|
220
|
-
// expression (`$.base` becomes `5.base`) — and the value the author
|
|
221
|
-
// wants changed lives at the target anyway;
|
|
222
|
-
// - TWO literals at one path (a duplicate key, two files merged) give
|
|
223
|
-
// no single place to edit, and picking either silently is picking
|
|
224
|
-
// for the author;
|
|
225
|
-
// - a literal in an INCLUDED file is editable, but not by
|
|
226
|
-
// `--overlay <this file>`: the write would land in a document the
|
|
227
|
-
// caller did not name.
|
|
228
|
-
//
|
|
229
|
-
// A PREFERENCE is not refused here for the same reason it is not
|
|
230
|
-
// replaced: appending already overrides a default correctly, so the
|
|
231
|
-
// caller loses nothing by falling through to it.
|
|
232
111
|
function editableLiteral(
|
|
233
112
|
overlaySrc: string,
|
|
234
113
|
path: string,
|
|
235
114
|
overlayPath: string | undefined,
|
|
236
115
|
): { site: PatchReplacement | undefined, finding: VetFinding | undefined } {
|
|
237
|
-
// THE AUTHORITY IS THE OVERLAY TEXT ALONE, WITH INCLUDES DENIED.
|
|
238
|
-
//
|
|
239
|
-
// The splice happens in the text this function was handed, so what it
|
|
240
|
-
// has to establish is that the contribution is IN that text — and the
|
|
241
|
-
// site's `file` cannot establish it. Two ways it fails: a caller of
|
|
242
|
-
// the library API need not pass `overlayPath`, leaving nothing to
|
|
243
|
-
// compare against; and the Go port names the ENTRY document for an
|
|
244
|
-
// included value anyway (issue #66), so the comparison is the overlay
|
|
245
|
-
// against itself. Either way an included literal's (row, col, len,
|
|
246
|
-
// src) can COINCIDE with different text at the same coordinates here
|
|
247
|
-
// — an include holding `a: 42` at 1:4 and an overlay holding `x: 42`
|
|
248
|
-
// at 1:4 — and the span verification cannot tell them apart, because
|
|
249
|
-
// the text really does match. The splice then rewrites `x` while
|
|
250
|
-
// reporting a replacement of `$.a`, in both ports.
|
|
251
|
-
//
|
|
252
|
-
// Denying includes removes the ambiguity at its source rather than
|
|
253
|
-
// detecting it: what resolves is what this text says by itself. An
|
|
254
|
-
// overlay that loads other documents therefore cannot be edited in
|
|
255
|
-
// place at all — the conservative answer, and the assignment still
|
|
256
|
-
// appends. It costs nothing in the shape `set` is for, an overlay it
|
|
257
|
-
// owns and appends to, and it does not depend on file attribution, so
|
|
258
|
-
// both ports agree without waiting on #66.
|
|
259
116
|
const alone = why(overlaySrc, path, {
|
|
260
117
|
trust: { include: 'none' },
|
|
261
118
|
...(null == overlayPath ? {} : { path: overlayPath }),
|
|
262
119
|
})
|
|
263
120
|
|
|
264
121
|
if (true !== alone.ok || null == alone.record) {
|
|
265
|
-
// Nothing here BY ITSELF. Two very different reasons, and they earn
|
|
266
|
-
// different answers: the path may simply not be in this overlay, in
|
|
267
|
-
// which case appending is the whole of the answer and nothing has
|
|
268
|
-
// gone wrong — or it may be here only because something was loaded,
|
|
269
|
-
// which is the case above and has to say so.
|
|
270
122
|
const withLoads = why(overlaySrc, path,
|
|
271
123
|
null == overlayPath ? undefined : { path: overlayPath })
|
|
272
124
|
if (true !== withLoads.ok || null == withLoads.record) {
|
|
@@ -290,14 +142,6 @@ function editableLiteral(
|
|
|
290
142
|
// coverage gate says so, an arm nothing can take.
|
|
291
143
|
const conjuncts: WhyConjunct[] = record.conjuncts
|
|
292
144
|
|
|
293
|
-
// A VALUE REACHED THROUGH A REFERENCE IS NOT THIS PATH'S TO EDIT.
|
|
294
|
-
// Provenance travels through clones now, so `n: $.base` against
|
|
295
|
-
// `base: 7` reports the literal `7` -- correctly, and at the site
|
|
296
|
-
// where it was written, which is `base`'s line and not `n`'s. A
|
|
297
|
-
// splice there would rewrite the REFERENT: every other reader of
|
|
298
|
-
// `$.base` changes with it, and the path the caller named does not
|
|
299
|
-
// move at all. The reference is what stands here, so the reference
|
|
300
|
-
// is what has to be edited, wherever it points.
|
|
301
145
|
const refs = conjuncts.filter((c) => 'ref' === c.role)
|
|
302
146
|
if (0 < refs.length) {
|
|
303
147
|
return {
|
|
@@ -343,17 +187,6 @@ function editableLiteral(
|
|
|
343
187
|
}
|
|
344
188
|
|
|
345
189
|
|
|
346
|
-
// THE SPAN MUST CHECK OUT before anything splices. The refusal arm is
|
|
347
|
-
// unreachable through `patch` since ADR-018: the pipe (`x: hello |>
|
|
348
|
-
// upper`) was the one spelling that synthesised a contribution the
|
|
349
|
-
// parser never sited, and denying includes means every WRITTEN
|
|
350
|
-
// contribution's coordinates describe this text by construction. The
|
|
351
|
-
// verification is kept rather than deleted — splicing without it would
|
|
352
|
-
// corrupt the file (a contribution with no `src` would splice ZERO
|
|
353
|
-
// characters, INSERTING the new value into the middle of a line) — and
|
|
354
|
-
// this last step is its own exported seam so the refusal can be tested
|
|
355
|
-
// directly, against conjuncts the engine would never produce, on the
|
|
356
|
-
// same footing as `spanHolds` itself.
|
|
357
190
|
export function verifiedSite(
|
|
358
191
|
overlaySrc: string,
|
|
359
192
|
path: string,
|
|
@@ -371,29 +204,6 @@ export function verifiedSite(
|
|
|
371
204
|
return { site: undefined, finding }
|
|
372
205
|
}
|
|
373
206
|
|
|
374
|
-
// DOES THE SPAN MEAN THE WHOLE CONTRIBUTION?
|
|
375
|
-
//
|
|
376
|
-
// This is the check that `role === 'literal'` looks like it makes and
|
|
377
|
-
// does not. A site names the TOKEN it points at, so a COMPOUND value
|
|
378
|
-
// reports its OPENING token while its canon is the whole thing:
|
|
379
|
-
// `min(1)` is a literal-role contribution whose src is `min`, `1+2`
|
|
380
|
-
// reports `1`, `$.k+1` reports `$`, `{b:1}` reports `{` and `[1,2]`
|
|
381
|
-
// reports `[`. Splicing over any of those writes the new value INTO
|
|
382
|
-
// the expression — `a: 5(1)`, `a: 5+2`, `a: 5.k+1` — which is the
|
|
383
|
-
// same class of corruption as the canon-length arithmetic, reached by
|
|
384
|
-
// a different route.
|
|
385
|
-
//
|
|
386
|
-
// Rather than enumerate the shapes (a list is a thing to be
|
|
387
|
-
// incomplete about), ASK THE ENGINE: parse `src` on its own and
|
|
388
|
-
// require the value it means to be the value the contribution
|
|
389
|
-
// contributed. That is exactly the property a splice needs — this
|
|
390
|
-
// text, alone, is this value — and it is decided by the same unifier
|
|
391
|
-
// that produced the contribution, so it cannot drift from it.
|
|
392
|
-
//
|
|
393
|
-
// It also gets the interesting case right without special-casing it:
|
|
394
|
-
// `0x1F` canons to `31`, which is not its own spelling, but IS the
|
|
395
|
-
// contribution's canon, so a hex literal is editable while `min` is
|
|
396
|
-
// not.
|
|
397
207
|
const span = spanValue(one.src)
|
|
398
208
|
if (null == span || span.canon !== one.canon) {
|
|
399
209
|
return {
|
|
@@ -407,11 +217,6 @@ export function verifiedSite(
|
|
|
407
217
|
}
|
|
408
218
|
}
|
|
409
219
|
|
|
410
|
-
// AN ABSTRACT CONTRIBUTION IS NOT A PIN. `a: integer` and
|
|
411
|
-
// `a: above(0)` state a constraint, and appending already narrows
|
|
412
|
-
// them — that is the one case the status report notes `set` could
|
|
413
|
-
// always repair. Replacing them would silently DISCARD a constraint
|
|
414
|
-
// the author wrote, to no benefit, so this falls through to append.
|
|
415
220
|
if (true !== span.concrete) {
|
|
416
221
|
return {
|
|
417
222
|
site: undefined,
|
|
@@ -436,21 +241,8 @@ export function verifiedSite(
|
|
|
436
241
|
}
|
|
437
242
|
|
|
438
243
|
|
|
439
|
-
// What does this source text mean ON ITS OWN, and is it a value rather
|
|
440
|
-
// than a constraint? Undefined when it does not stand alone at all
|
|
441
|
-
// (`$` from a path, an unbalanced `{`).
|
|
442
|
-
//
|
|
443
|
-
// The wrapper key is arbitrary and the document it makes is thrown
|
|
444
|
-
// away; what is wanted is the unifier's own reading of the fragment.
|
|
445
244
|
export function spanValue(
|
|
446
245
|
src: string): { canon: string, concrete: boolean } | undefined {
|
|
447
|
-
// NO COLLECTING CONTEXT: `unify` THROWS on a source it cannot read,
|
|
448
|
-
// so a ctx.err check here is a branch nothing can reach — the catch
|
|
449
|
-
// below is the only path a bad fragment takes. (A first draft had
|
|
450
|
-
// both, and the coverage gate called the pair what it was.) What the
|
|
451
|
-
// nil test still earns is the fragment that PARSES and means nothing:
|
|
452
|
-
// `$` is a path with no target, and answers a nil rather than
|
|
453
|
-
// throwing.
|
|
454
246
|
let canon: string
|
|
455
247
|
try {
|
|
456
248
|
const root: any = new Aontu().unify('v: ' + src)
|
|
@@ -477,11 +269,6 @@ export function spanValue(
|
|
|
477
269
|
}
|
|
478
270
|
|
|
479
271
|
|
|
480
|
-
// A refusal to replace, as a WARNING: the assignment still appends, so
|
|
481
|
-
// nothing about the run got worse and the verdict must not move
|
|
482
|
-
// (ts/src/vet.ts, "warnings never touch the verdict"). What the finding
|
|
483
|
-
// adds is the reason, which is the whole value of asking for --in-place
|
|
484
|
-
// over plain set.
|
|
485
272
|
function notEditable(
|
|
486
273
|
code: string, path: string, why: string, from: WhyConjunct[]
|
|
487
274
|
): VetFinding {
|
|
@@ -508,12 +295,6 @@ function notEditable(
|
|
|
508
295
|
}
|
|
509
296
|
|
|
510
297
|
|
|
511
|
-
// Append the assignments to the overlay and answer what the result
|
|
512
|
-
// holds. The report's verdict is the vet verdict of the ENTRY against
|
|
513
|
-
// the new overlay: `valid` when it holds and is concrete, `incomplete`
|
|
514
|
-
// when nothing contradicts but the truth is not yet satisfied,
|
|
515
|
-
// `invalid` when the overlay contradicts a pinned value, `error` when
|
|
516
|
-
// the entry itself does not stand up.
|
|
517
298
|
export function patch(
|
|
518
299
|
entrySrc: string,
|
|
519
300
|
overlaySrc: string,
|
|
@@ -555,9 +336,6 @@ export function patch(
|
|
|
555
336
|
notes.push(found.finding)
|
|
556
337
|
}
|
|
557
338
|
if (null != found.site) {
|
|
558
|
-
// Two assignments naming the same path would splice the same
|
|
559
|
-
// span twice. The second is the one the author wrote last, so
|
|
560
|
-
// it wins — and the first is dropped rather than layered.
|
|
561
339
|
const at = offsetAt(overlaySrc, found.site.row, found.site.col)
|
|
562
340
|
const dup = edits.findIndex((e) => e.at === at)
|
|
563
341
|
const edit = { at, len: found.site.from.length, to: a.value }
|
|
@@ -578,10 +356,6 @@ export function patch(
|
|
|
578
356
|
|
|
579
357
|
const overlay = joinOverlay(applyEdits(overlaySrc, edits), appended)
|
|
580
358
|
|
|
581
|
-
// The file names ride as URLs as well as base paths, so a finding
|
|
582
|
-
// names the entry and the overlay rather than vet's generic
|
|
583
|
-
// `schema`/`data` labels — with two documents that both belong to
|
|
584
|
-
// the caller, "which file" is the whole question.
|
|
585
359
|
const report: VetReport = vet(entrySrc, overlay, {
|
|
586
360
|
trust: options.trust,
|
|
587
361
|
textExt: options.textExt,
|
|
@@ -604,8 +378,6 @@ export function patch(
|
|
|
604
378
|
}
|
|
605
379
|
|
|
606
380
|
|
|
607
|
-
// Apply the collected splices back to front, so an earlier edit's
|
|
608
|
-
// offset is never invalidated by a later one having already run.
|
|
609
381
|
function applyEdits(
|
|
610
382
|
src: string, edits: { at: number, len: number, to: string }[]
|
|
611
383
|
): string {
|
|
@@ -620,10 +392,6 @@ function applyEdits(
|
|
|
620
392
|
}
|
|
621
393
|
|
|
622
394
|
|
|
623
|
-
// One line per assignment, after whatever the overlay already said. A
|
|
624
|
-
// trailing newline is kept when the file had one and added when it
|
|
625
|
-
// did not: a file that does not end in a newline is still a file, and
|
|
626
|
-
// appending to it must not join two entries into one line.
|
|
627
395
|
function joinOverlay(overlaySrc: string, appended: string[]): string {
|
|
628
396
|
if (0 === appended.length) {
|
|
629
397
|
return overlaySrc
|
package/src/profile.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/* Copyright (c) 2026 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { Aontu } from './aontu'
|
|
5
|
+
import { vet, failureFinding } from './vet'
|
|
6
|
+
import { hcanon } from './hcanon'
|
|
7
|
+
import { includeOpts } from './utility'
|
|
8
|
+
|
|
9
|
+
import type { IncludeOptions } from './utility'
|
|
10
|
+
import type { VetFinding } from './vet'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export type ProfileOptions = IncludeOptions & {
|
|
14
|
+
// Where the document CAME FROM, so a relative `@"file"` load inside
|
|
15
|
+
// it resolves from its own directory.
|
|
16
|
+
path?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const PROFILE_VOCABULARY = '@"aontu:profile"'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// A language declared as data, vetted, or the findings that refuse it.
|
|
24
|
+
export function loadProfile(src: string, options?: ProfileOptions):
|
|
25
|
+
{ profile?: any, errors?: VetFinding[] } {
|
|
26
|
+
const opts = options ?? {}
|
|
27
|
+
const aontu = new Aontu(includeOpts(opts))
|
|
28
|
+
const actx = aontu.ctx({ collect: true })
|
|
29
|
+
const root: any = aontu.unify(src, { path: opts.path, collect: true }, actx)
|
|
30
|
+
if (0 < actx.err.length || true === root?.isNil) {
|
|
31
|
+
return { errors: [failureFinding(actx, opts.path, root)] }
|
|
32
|
+
}
|
|
33
|
+
const report = vet(PROFILE_VOCABULARY, hcanon(root))
|
|
34
|
+
if ('valid' !== report.verdict) {
|
|
35
|
+
return { errors: report.findings }
|
|
36
|
+
}
|
|
37
|
+
// The meet: the vocabulary requires `lang`, so a value the vet
|
|
38
|
+
// admitted has a `Lang`.
|
|
39
|
+
const instance = new Aontu().generate(
|
|
40
|
+
PROFILE_VOCABULARY + '\naontu: Lang: ' + hcanon(root.peg.aontu.peg.Lang))
|
|
41
|
+
return { profile: instance.aontu.Lang }
|
|
42
|
+
}
|
package/src/provenance.ts
CHANGED
|
@@ -1,33 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// THE PROVENANCE RECORDER (G7 phase 3,
|
|
4
|
-
// docs/capability-review/g7-machine-access.md): what CONTRIBUTED to
|
|
5
|
-
// the value at a path, in order, with the site each contribution was
|
|
6
|
-
// written at. `why` is the positive twin of G2's error report — errors
|
|
7
|
-
// explain what failed to unify, this explains what did.
|
|
8
|
-
//
|
|
9
|
-
// The recorder rides the CONTEXT and is off by default: `unite` pays
|
|
10
|
-
// one property load on the normal path, and an instrumented run pays
|
|
11
|
-
// site materialisation knowingly. It records at `unite` and nowhere
|
|
12
|
-
// else, because that is the one place every meet passes through — the
|
|
13
|
-
// same reason G3's deprecation rider lives there. A meet is where
|
|
14
|
-
// information currently vanishes, so a meet is what to record.
|
|
15
|
-
//
|
|
16
|
-
// Contributions are the operands that were NOT produced by an earlier
|
|
17
|
-
// meet at the same path: a value that a previous meet made is an
|
|
18
|
-
// intermediate, not a source. Deduplicated by (path, val id) — ids are
|
|
19
|
-
// unique per run — so fixpoint revisits across up to `maxcc` passes do
|
|
20
|
-
// not multiply the record.
|
|
21
|
-
//
|
|
22
|
-
// Roles come from the operand itself, with no further instrumentation:
|
|
23
|
-
// a reference is still a RefVal when it meets its peer (its canon is
|
|
24
|
-
// the path it names), a preference is still a PrefVal (its canon is
|
|
25
|
-
// the `*` form), and a spread-applied template is marked once where
|
|
26
|
-
// the spread is applied. Precedence is spread > ref > pref > literal:
|
|
27
|
-
// a preference INSIDE a spread template is a spread contribution,
|
|
28
|
-
// which is what the author needs to be told.
|
|
29
|
-
|
|
30
|
-
|
|
31
3
|
|
|
32
4
|
export type WhyRole = 'literal' | 'spread' | 'ref' | 'pref'
|
|
33
5
|
|
|
@@ -36,8 +8,6 @@ export type WhyRole = 'literal' | 'spread' | 'ref' | 'pref'
|
|
|
36
8
|
export type WhySite = {
|
|
37
9
|
col: number
|
|
38
10
|
file: string
|
|
39
|
-
// The extent in UTF-16 code units, or -1 when unknown. The same
|
|
40
|
-
// field, and the same meaning, as VetSite.len (ts/src/vet.ts).
|
|
41
11
|
len: number
|
|
42
12
|
row: number
|
|
43
13
|
}
|
|
@@ -46,20 +16,7 @@ export type WhyConjunct = {
|
|
|
46
16
|
canon: string
|
|
47
17
|
role: WhyRole
|
|
48
18
|
site: WhySite
|
|
49
|
-
// The SOURCE TEXT this contribution was written as.
|
|
50
|
-
//
|
|
51
|
-
// `canon` is the value; `src` is the spelling. They are not the same
|
|
52
|
-
// thing, and the difference is the whole reason this record exists:
|
|
53
|
-
// `port: 0x1F` contributes canon `31` from source `0x1F`, so a reader
|
|
54
|
-
// told only the canon cannot find, verify or replace what was
|
|
55
|
-
// actually written. Empty when the contribution occupies no source —
|
|
56
|
-
// a value unification minted rather than a document wrote.
|
|
57
19
|
src: string
|
|
58
|
-
// The PREFERENCE RANK, 0-based, when the contribution is one: `*x`
|
|
59
|
-
// is 0, `**x` is 1. Absent for anything else. The engine's own
|
|
60
|
-
// number (PrefVal.rank), so a reader arbitrating between ranked
|
|
61
|
-
// contributions -- the meet ladder -- need not count stars in a
|
|
62
|
-
// canon string.
|
|
63
20
|
rank?: number
|
|
64
21
|
}
|
|
65
22
|
|
|
@@ -75,77 +32,12 @@ export type WhyRecord = {
|
|
|
75
32
|
export const FROM_SPREAD = '_fromSpread'
|
|
76
33
|
|
|
77
34
|
|
|
78
|
-
// THE AUTHORED MARK, and it is a property of the VALUE rather than a
|
|
79
|
-
// set of ids held beside it (the review's finding E). A contribution
|
|
80
|
-
// is a value the author wrote, and the recorder used to decide that by
|
|
81
|
-
// looking the operand's id up in a set stamped over the parsed tree --
|
|
82
|
-
// which is true of the parsed tree and of nothing derived from it. So
|
|
83
|
-
// every value that reached a path through a CLONE was dark: a default
|
|
84
|
-
// flowing into a `pack()`-generated child, a shape carried by a `$ref`,
|
|
85
|
-
// a value a spread template stamped. `why` answered "(no contributions:
|
|
86
|
-
// nothing met at this path)" over a value it had just printed, with
|
|
87
|
-
// exit 0 -- a false statement, and the one an audit surface may not
|
|
88
|
-
// make (use-cases/BUGS.md §23, §24).
|
|
89
|
-
//
|
|
90
|
-
// A clone of a written value IS that written value, re-instantiated
|
|
91
|
-
// somewhere else: it carries the author's site, so it can be pointed
|
|
92
|
-
// at. Val.clone therefore carries this mark, exactly as it carries the
|
|
93
|
-
// site -- provenance is part of the clone contract, not a recorder
|
|
94
|
-
// bolted beside it. Values the engine MINTS (a kind lifted while a
|
|
95
|
-
// disjunct trials its members, a fold's intermediate) are constructed
|
|
96
|
-
// rather than cloned and stay unmarked, which is what keeps the record
|
|
97
|
-
// to what the author can edit.
|
|
98
|
-
//
|
|
99
|
-
// The mark is only ever SET by an instrumented run (`why` calls
|
|
100
|
-
// writtenFrom; nothing else does), so an ordinary evaluation pays one
|
|
101
|
-
// undefined property read per clone.
|
|
102
35
|
export const WRITTEN = '_written'
|
|
103
36
|
|
|
104
37
|
|
|
105
|
-
// THE CONTAINER A VALUE IS PART OF, when the two stand at the SAME
|
|
106
|
-
// path: a junction's members, a preference's inner value, a function's
|
|
107
|
-
// arguments, an operator's operands. `*1|integer` is one thing the
|
|
108
|
-
// author wrote, and `*1` is not a second thing beside it.
|
|
109
|
-
//
|
|
110
|
-
// The recorder already knew that where the container itself met
|
|
111
|
-
// something -- contributing it marked its members `inside` and the
|
|
112
|
-
// filter dropped them. But the container only appears as an operand
|
|
113
|
-
// when the fixpoint happens to meet it whole; where it resolves
|
|
114
|
-
// member-wise instead (a later sibling of a spread, a default reaching
|
|
115
|
-
// a generated child) the members arrived alone and the record split
|
|
116
|
-
// one written value into two contributions at two columns. Which
|
|
117
|
-
// happened was decided by evaluation order, so `why` answered
|
|
118
|
-
// differently for identical statements (use-cases/BUGS.md §22).
|
|
119
|
-
//
|
|
120
|
-
// So the relation is recorded as a fact about the DOCUMENT, at
|
|
121
|
-
// stamping time, and an operand is reported as the outermost written
|
|
122
|
-
// value it is part of. NOT set for a bag's children (they stand at
|
|
123
|
-
// their own, deeper paths) nor for a conjunct's terms (a conjunct is
|
|
124
|
-
// the statement that several things must all hold, and each term is
|
|
125
|
-
// one of them -- which is exactly what the author needs shown).
|
|
126
38
|
export const INNER_OF = '_innerOf'
|
|
127
39
|
|
|
128
40
|
|
|
129
|
-
// Mark a spread clone and everything inside it, so a contribution
|
|
130
|
-
// several levels down a template is still known to have come from the
|
|
131
|
-
// template. ONLY on an instrumented run: the walk is O(template) per
|
|
132
|
-
// key per pass, which is real money on a large model and buys nothing
|
|
133
|
-
// when no one is recording.
|
|
134
|
-
//
|
|
135
|
-
// THE GUARD IS A CYCLE GUARD, NOT A "DONE" FLAG, and that distinction
|
|
136
|
-
// is the whole of the review's finding E for sibling position. A
|
|
137
|
-
// template is applied once per destination, and the fixpoint advances
|
|
138
|
-
// values IN PLACE between those applications (AGENTS.md, the mutation
|
|
139
|
-
// caveat): by the time the second key is spread, the template's
|
|
140
|
-
// `replicas` child is no longer the disjunction the first key saw but
|
|
141
|
-
// the value that meet produced. Skipping the walk because the
|
|
142
|
-
// CONTAINER was already marked left every one of those replacements
|
|
143
|
-
// unmarked, so `why` at the first sibling reported the written
|
|
144
|
-
// `*1|integer` as one contribution and at the second reported `*1` and
|
|
145
|
-
// `integer` as two -- identical statements, different answers, decided
|
|
146
|
-
// by which key the fixpoint reached first (use-cases/BUGS.md §22).
|
|
147
|
-
// Marking is idempotent, so re-walking costs a pass and changes
|
|
148
|
-
// nothing where nothing moved.
|
|
149
41
|
export function markSpread(v: any, seen?: Set<any>): void {
|
|
150
42
|
const marked = seen ?? new Set<any>()
|
|
151
43
|
if (null == v || true !== v.isVal || marked.has(v)) {
|
|
@@ -189,10 +81,6 @@ function samePathKids(v: any): any[] {
|
|
|
189
81
|
}
|
|
190
82
|
|
|
191
83
|
|
|
192
|
-
// Order contributions the way the document reads: by file, then row,
|
|
193
|
-
// then column, with the canon as the last tiebreak so the order is
|
|
194
|
-
// total even for two values written at the same position (which a
|
|
195
|
-
// merged duplicate key can produce).
|
|
196
84
|
function cmpSite(a: Contribution, b: Contribution): number {
|
|
197
85
|
return a.site.file.localeCompare(b.site.file) ||
|
|
198
86
|
a.site.row - b.site.row ||
|
|
@@ -231,20 +119,8 @@ type PathRecord = {
|
|
|
231
119
|
export class Provenance {
|
|
232
120
|
paths: Map<string, PathRecord> = new Map()
|
|
233
121
|
|
|
234
|
-
// id -> the written container it names, for INNER_OF. The mark on a
|
|
235
|
-
// value is the container's ID rather than the container itself: a
|
|
236
|
-
// Val holding another Val as an own property is a reference cycle
|
|
237
|
-
// through the tree, and enough of the engine walks a value's own
|
|
238
|
-
// properties that one is not safe to introduce.
|
|
239
122
|
containers: Map<number, any> = new Map()
|
|
240
123
|
|
|
241
|
-
// Stamp the parsed tree with the AUTHORED mark: everything the
|
|
242
|
-
// author wrote, before unification starts. A value minted during
|
|
243
|
-
// unification — a kind lifted from a leaf while a disjunct trials
|
|
244
|
-
// its members, a fold's intermediate — is the engine's own work, not
|
|
245
|
-
// a contribution the author can be pointed at. A CLONE of a marked
|
|
246
|
-
// value keeps the mark (see WRITTEN above), because it is the same
|
|
247
|
-
// written value somewhere else. Called once, before unify, by `why`.
|
|
248
124
|
writtenFrom(v: any): void {
|
|
249
125
|
if (null == v || true !== v.isVal || true === v[WRITTEN]) {
|
|
250
126
|
return
|
|
@@ -294,9 +170,6 @@ export class Provenance {
|
|
|
294
170
|
}
|
|
295
171
|
|
|
296
172
|
private contribute(rec: PathRecord, v: any): void {
|
|
297
|
-
// TOP is the unit element and a nil is a failure, neither of which
|
|
298
|
-
// is information the author wrote. A value an earlier meet MADE is
|
|
299
|
-
// an intermediate; the source that made it is already recorded.
|
|
300
173
|
if (null == v || true !== v.isVal || true === v.isTop || true === v.isNil ||
|
|
301
174
|
rec.made.has(v.id) || rec.seen.has(v.id)) {
|
|
302
175
|
return
|
|
@@ -318,11 +191,6 @@ export class Provenance {
|
|
|
318
191
|
if (true !== v[WRITTEN] && true !== v[FROM_SPREAD]) {
|
|
319
192
|
return
|
|
320
193
|
}
|
|
321
|
-
// A CONJUNCT is not one contribution, it is the statement that
|
|
322
|
-
// several must all hold — duplicate keys merged at parse, an
|
|
323
|
-
// explicit `a & b`. Its own site is nowhere (the merge has no
|
|
324
|
-
// source position), while its terms each have one, which is what
|
|
325
|
-
// the author needs to be shown.
|
|
326
194
|
if (true === v.isConjunct && Array.isArray(v.peg)) {
|
|
327
195
|
rec.seen.add(v.id)
|
|
328
196
|
for (const term of v.peg) {
|
|
@@ -348,18 +216,6 @@ export class Provenance {
|
|
|
348
216
|
})
|
|
349
217
|
}
|
|
350
218
|
|
|
351
|
-
// THE VALUE THAT STANDS at a path is a contribution when nothing met
|
|
352
|
-
// there and the author wrote it. A meet is where information
|
|
353
|
-
// vanishes, so a meet is what the recorder watches -- but a
|
|
354
|
-
// generator PLACES a value without meeting anything, and `why` then
|
|
355
|
-
// answered "(no contributions: nothing met at this path)" over a
|
|
356
|
-
// value it had just printed. That is literally true and practically
|
|
357
|
-
// false: the author is asking where the value came from, and it came
|
|
358
|
-
// from somewhere they can be shown (use-cases/BUGS.md §23).
|
|
359
|
-
//
|
|
360
|
-
// Only when the record is otherwise EMPTY. Where something did meet,
|
|
361
|
-
// the standing value is that meet's result -- an intermediate, and
|
|
362
|
-
// the recorder's oldest rule is that a result is not a source.
|
|
363
219
|
stands(path: string[], v: any): void {
|
|
364
220
|
const key = path.join('.')
|
|
365
221
|
const rec = this.paths.get(key)
|
|
@@ -369,50 +225,11 @@ export class Provenance {
|
|
|
369
225
|
this.record(path, v, undefined, undefined)
|
|
370
226
|
}
|
|
371
227
|
|
|
372
|
-
// The record at one path. Empty when nothing met there and nothing
|
|
373
|
-
// the author wrote stands there either — which is a true and useful
|
|
374
|
-
// answer rather than an error.
|
|
375
|
-
//
|
|
376
|
-
// ONLY WHOLE WRITTEN VALUES are contributions. A Val's own unify
|
|
377
|
-
// re-enters `unite` at the same path — a disjunct trials each member
|
|
378
|
-
// there, a constraint meets its atoms there — and those members are
|
|
379
|
-
// PARTS OF one written value, not further values beside it. That is
|
|
380
|
-
// settled BEFORE a member is ever pushed, by the INNER_OF fact
|
|
381
|
-
// stamped over the document (see `contribute`), which is why no
|
|
382
|
-
// filter runs here: a per-path "inside" set used to do it, and it
|
|
383
|
-
// could only work where the container itself happened to meet
|
|
384
|
-
// something at the same path — the order-dependence finding E
|
|
385
|
-
// records.
|
|
386
228
|
at(path: string[]): WhyConjunct[] {
|
|
387
229
|
const rec = this.paths.get(path.join('.'))
|
|
388
230
|
if (null == rec) {
|
|
389
231
|
return []
|
|
390
232
|
}
|
|
391
|
-
// SOURCE ORDER, not meet order: the two are the same in simple
|
|
392
|
-
// cases and diverge with the fixpoint's fold order, which is an
|
|
393
|
-
// engine detail and a parity risk. Sites are parse data, identical
|
|
394
|
-
// in both ports, so ordering by them makes the record read as the
|
|
395
|
-
// document reads and pins it across implementations.
|
|
396
|
-
// ONE WRITTEN TOKEN IS ONE CONTRIBUTION, and the SITE is what
|
|
397
|
-
// identifies it -- not the val id, and not the canon.
|
|
398
|
-
//
|
|
399
|
-
// Not the id, because provenance travels through clones now: a
|
|
400
|
-
// written value and a clone of it are the same statement in the
|
|
401
|
-
// same place, and a path that met both would list it twice.
|
|
402
|
-
//
|
|
403
|
-
// Not the canon, because the same written value reaches a path at
|
|
404
|
-
// different stages of narrowing -- `3|(1|2)` as the author wrote
|
|
405
|
-
// it and `3|1|2` after a fold -- and both name one token.
|
|
406
|
-
//
|
|
407
|
-
// Not the role either: the role says how the value REACHED this
|
|
408
|
-
// path, not which value it is, and one written value can reach a
|
|
409
|
-
// path both ways (a template applied to a key whose value is also
|
|
410
|
-
// written there). Keeping the literal would throw away the more
|
|
411
|
-
// informative half, so the roles have a precedence.
|
|
412
|
-
//
|
|
413
|
-
// ONLY WHERE THE SITE IS REAL. An unsited contribution (row -1)
|
|
414
|
-
// cannot be told apart from another unsited one, so those are kept
|
|
415
|
-
// as they come rather than collapsed into whichever arrived first.
|
|
416
233
|
const shown = new Map<string, Contribution>()
|
|
417
234
|
const order = ['spread', 'ref', 'pref', 'literal']
|
|
418
235
|
const out: Contribution[] = []
|