@intentius/chant 0.73.0 → 0.75.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/bin/chant +13 -0
- package/dist/behaviour-graph.d.ts +71 -0
- package/dist/behaviour-graph.d.ts.map +1 -0
- package/dist/build.d.ts +16 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +11 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/graph-ir.d.ts +15 -0
- package/dist/graph-ir.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/behaviour-graph.test.ts +151 -0
- package/src/behaviour-graph.ts +102 -0
- package/src/build.test.ts +54 -1
- package/src/build.ts +42 -21
- package/src/cli/handlers/graph.test.ts +195 -0
- package/src/cli/handlers/graph.ts +78 -0
- package/src/cli/main.test.ts +10 -0
- package/src/cli/main.ts +9 -0
- package/src/cli/registry.ts +11 -0
- package/src/graph-ir.ts +15 -0
package/bin/chant
CHANGED
|
@@ -11,4 +11,17 @@ done
|
|
|
11
11
|
SCRIPT_DIR="$(cd "$(dirname "$SELF")" && pwd)"
|
|
12
12
|
MAIN_TS="$SCRIPT_DIR/../src/cli/main.ts"
|
|
13
13
|
|
|
14
|
+
# tsx is our own dependency, so register its hooks in this node process rather
|
|
15
|
+
# than shelling `npx tsx`, which resolves a bin and then spawns a second node.
|
|
16
|
+
# Worth ~30% of startup (#2481). Nested install first, then hoisted; npx stays
|
|
17
|
+
# as the fallback for any layout neither guess covers.
|
|
18
|
+
for CANDIDATE in \
|
|
19
|
+
"$SCRIPT_DIR/../node_modules/tsx/dist/loader.mjs" \
|
|
20
|
+
"$SCRIPT_DIR/../../../tsx/dist/loader.mjs"
|
|
21
|
+
do
|
|
22
|
+
if [ -f "$CANDIDATE" ]; then
|
|
23
|
+
exec node --import "file://$CANDIDATE" "$MAIN_TS" "$@"
|
|
24
|
+
fi
|
|
25
|
+
done
|
|
26
|
+
|
|
14
27
|
exec npx tsx "$MAIN_TS" "$@"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seam between a graph read and a prediction (#2377, epic #2355).
|
|
3
|
+
*
|
|
4
|
+
* `./behaviour-overlay.ts` projects a {@link BehaviourResult} onto the two
|
|
5
|
+
* channels the IR carries it on. This module is the other half: it builds the
|
|
6
|
+
* request from the graph the read already holds, and puts the projection back
|
|
7
|
+
* onto that graph. Both halves are pure, and neither calls an engine — the
|
|
8
|
+
* plugin call itself lives in `./cli/handlers/graph.ts`, which is the only
|
|
9
|
+
* place that has plugins to call.
|
|
10
|
+
*
|
|
11
|
+
* ## The request is the graph, not a second read
|
|
12
|
+
*
|
|
13
|
+
* The epic's rule is that the engine never sees credentials, and the way this
|
|
14
|
+
* path keeps that true is by never reading anything: `chant graph --live`
|
|
15
|
+
* already observed the estate, and {@link behaviourRequestFromIr} reshapes
|
|
16
|
+
* exactly those nodes and edges into the contract's options. There is no
|
|
17
|
+
* provider call here and nothing to leak, because there is no second read to
|
|
18
|
+
* leak from. `screenBehaviourRequest` (`./behaviour-predict.ts`) still screens
|
|
19
|
+
* what this produces; a node whose observed `attrs` picked up a secret is
|
|
20
|
+
* caught there, on the one path every prediction goes through.
|
|
21
|
+
*
|
|
22
|
+
* ## Why `attrs` are the props verbatim
|
|
23
|
+
*
|
|
24
|
+
* An IR node's `attrs` are what the read resolved — literals, and `{ $ref }`
|
|
25
|
+
* for a reference. The contract's `props` is the same thing, so this passes
|
|
26
|
+
* them across unchanged rather than filtering to a "useful" subset. Filtering
|
|
27
|
+
* would be chant deciding which properties can affect a price, and chant does
|
|
28
|
+
* not price anything; an engine that needs a field chant thought irrelevant
|
|
29
|
+
* would get a silently wrong figure with nothing to say so.
|
|
30
|
+
*/
|
|
31
|
+
import { type BehaviourOverlay } from "./behaviour-overlay.js";
|
|
32
|
+
import type { GraphIR } from "./graph-ir.js";
|
|
33
|
+
import type { PredictBehaviourOptions } from "./behaviour.js";
|
|
34
|
+
/**
|
|
35
|
+
* What the caller knows and the graph does not.
|
|
36
|
+
*
|
|
37
|
+
* `edgeCoverage` is here rather than derived because this module cannot
|
|
38
|
+
* honestly derive it. Whether the edges in hand are the whole graph depends on
|
|
39
|
+
* which anchoring produced them and which lexicons contributed a reference
|
|
40
|
+
* catalog, and both facts live with the caller.
|
|
41
|
+
*/
|
|
42
|
+
export interface BehaviourRequestContext extends Pick<PredictBehaviourOptions, "environment" | "traffic" | "buildOutput" | "edgeCoverage"> {
|
|
43
|
+
stack?: string;
|
|
44
|
+
region?: string;
|
|
45
|
+
owned?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Reshape an already-read graph into `predictBehaviour`'s options.
|
|
49
|
+
*
|
|
50
|
+
* Entity identity is the node's `id` and nothing derived from it. That is the
|
|
51
|
+
* same key the drift overlay writes `_status` under and the same key
|
|
52
|
+
* {@link applyBehaviourOverlay} reads back, so in a multi-stack project where
|
|
53
|
+
* ids are `${stack}::${logicalId}`, the block lands on the node it was
|
|
54
|
+
* predicted for without this module knowing that qualification exists.
|
|
55
|
+
*/
|
|
56
|
+
export declare function behaviourRequestFromIr(ir: GraphIR, ctx: BehaviourRequestContext): PredictBehaviourOptions;
|
|
57
|
+
/**
|
|
58
|
+
* Put a projection back onto the graph: each entity's block on its node's
|
|
59
|
+
* `attrs`, the graph-level half on `ir.meta`.
|
|
60
|
+
*
|
|
61
|
+
* A node with no entry keeps the `attrs` object it already had, untouched —
|
|
62
|
+
* an unpriced entity carries no `_behaviour` key rather than an empty or
|
|
63
|
+
* zeroed one, because zero cost and "not priced" are different claims and a
|
|
64
|
+
* renderer cannot tell them apart after the fact.
|
|
65
|
+
*
|
|
66
|
+
* Returns a new graph and mutates nothing: the caller's `ir` is shared with
|
|
67
|
+
* the lens pipeline downstream, and a prediction is not allowed to be the
|
|
68
|
+
* reason a later pass sees different nodes.
|
|
69
|
+
*/
|
|
70
|
+
export declare function applyBehaviourOverlay(ir: GraphIR, overlay: BehaviourOverlay): GraphIR;
|
|
71
|
+
//# sourceMappingURL=behaviour-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"behaviour-graph.d.ts","sourceRoot":"","sources":["../src/behaviour-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D;;;;;;;GAOG;AACH,MAAM,WAAW,uBACf,SAAQ,IAAI,CAAC,uBAAuB,EAAE,aAAa,GAAG,SAAS,GAAG,aAAa,GAAG,cAAc,CAAC;IACjG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,uBAAuB,GAAG,uBAAuB,CAiBzG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAUrF"}
|
package/dist/build.d.ts
CHANGED
|
@@ -56,6 +56,22 @@ export interface StackGraph {
|
|
|
56
56
|
* referencing an attribute of a resource in lexicon B ⇒ A depends on B). Returns
|
|
57
57
|
* the edge set plus a topological order, parallel-safe waves, and any cycles.
|
|
58
58
|
*/
|
|
59
|
+
/**
|
|
60
|
+
* Is this entity a member of a lexicon stack?
|
|
61
|
+
*
|
|
62
|
+
* Four kinds are not, and every one of them is serializer-neutral: a
|
|
63
|
+
* `LexiconOutput` is collected separately, a secret provenance declaration
|
|
64
|
+
* (#1828) and a plan scenario (#1292) are data lint and the CLI read off the
|
|
65
|
+
* entity map, and an Op (#2118) builds to its own `op.json` rather than
|
|
66
|
+
* through a lexicon serializer.
|
|
67
|
+
*
|
|
68
|
+
* {@link partitionByLexicon} has skipped all four since each landed, and
|
|
69
|
+
* {@link computeStackGraph}'s walk did not — so an entity with no `lexicon`
|
|
70
|
+
* (a `LexiconOutput`) entered `edges`, `order` and `waves` as `undefined`
|
|
71
|
+
* while `nodes`, built from the partition, could never contain it (#2480).
|
|
72
|
+
* One predicate for both sites, so they cannot drift apart again.
|
|
73
|
+
*/
|
|
74
|
+
export declare function isStackMember(entity: Declarable): boolean;
|
|
59
75
|
export declare function computeStackGraph(entities: Map<string, Declarable>, lexiconNames: string[]): StackGraph;
|
|
60
76
|
/**
|
|
61
77
|
* Result of the build process
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAsB,MAAM,UAAU,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAyB,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC3F,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAiC,MAAM,UAAU,CAAC;AACzE,OAAO,EAAE,aAAa,EAAmB,MAAM,kBAAkB,CAAC;AAQlE,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAmB,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAKvF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CACb,MAAM,EACN;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CACtD,CAAC;IACF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,wEAAwE;IACxE,UAAU,EAAE,UAAU,CAAC;CACxB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,gDAAgD;IAChD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3C,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB;;;OAGG;IACH,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;IAClB,yEAAyE;IACzE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACjC,YAAY,EAAE,MAAM,EAAE,GACrB,UAAU,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/graph.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/graph.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAiHlD;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAkDnE"}
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAMA,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AA0F9E;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AAMA,OAAO,EAAkB,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AA0F9E;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA8WpD;AAocD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,UAAU,EAmGvC,CAAC;AAqIF;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB5E"}
|
package/dist/cli/registry.d.ts
CHANGED
|
@@ -158,6 +158,17 @@ export interface ParsedArgs {
|
|
|
158
158
|
* cross-substrate topology) and joins live status per node; `live` keeps the
|
|
159
159
|
* provisioned graph's reconstructed edges (the pre-#821 behaviour). */
|
|
160
160
|
overlayAnchor?: "source" | "live";
|
|
161
|
+
/** `chant graph --live --overlay --traffic "<level>"` (#2377) — predict the
|
|
162
|
+
* overlaid estate's behaviour at this traffic level and carry the result on
|
|
163
|
+
* the IR: each entity's block on `attrs._behaviour`, the report's meta (or a
|
|
164
|
+
* whole refusal report) on `meta._behaviour`.
|
|
165
|
+
*
|
|
166
|
+
* The flag is the trigger, and it has no default. The level is passed to the
|
|
167
|
+
* engine verbatim — chant does not parse it, default it or convert it
|
|
168
|
+
* (`../behaviour.ts`) — so without the flag there is no traffic level to
|
|
169
|
+
* predict *at*, nothing is asked, and neither key appears. A consumer reads
|
|
170
|
+
* that absence as "not looked", never as a refusal. */
|
|
171
|
+
traffic?: string;
|
|
161
172
|
/** `chant list --components` / `chant graph --components` — surface discovered
|
|
162
173
|
* `Component` declarations (#560) instead of/alongside lexicon resources. */
|
|
163
174
|
components?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/cli/registry.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/cli/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iIAAiI;IACjI,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;0DACsD;IACtD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;+CAE2C;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;mFAC+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;oFACgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;2EAGuE;IACvE,aAAa,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAClC;;;;;;;;;2DASuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;iFAC6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;6DAEyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0GAA0G;IAC1G,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,+EAA+E;IAC/E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;uFACmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gGAAgG;IAChG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,mFAAmF;IACnF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kHAAkH;IAClH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sJAAsJ;IACtJ,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uKAAuK;IACvK,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,mPAAmP;IACnP,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8SAA8S;IAC9S,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oTAAoT;IACpT,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wZAAwZ;IACxZ,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oJAAoJ;IACpJ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8RAA8R;IAC9R,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,8NAA8N;IAC9N,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uTAAuT;IACvT,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,geAAge;IAChe,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,wWAAwW;IACxW,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gUAAgU;IAChU,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oYAAoY;IACpY,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,ySAAyS;IACzS,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,uJAAuJ;IACvJ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;yDAIqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qHAAqH;IACrH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sJAAsJ;IACtJ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gJAAgJ;IAChJ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iNAAiN;IACjN,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mQAAmQ;IACnQ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,+VAA+V;IAC/V,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iLAAiL;IACjL,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mGAAmG;IACnG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8GAA8G;IAC9G,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4VAA4V;IAC5V,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,0CAA0C;IAC1C,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,UAAU,CAAC;IAChB,4FAA4F;IAC5F,QAAQ,EAAE,OAAO,CAAC;CACnB;AAWD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,eAAe,GAAG,IAAI,CAiB/F"}
|
package/dist/graph-ir.d.ts
CHANGED
|
@@ -258,6 +258,21 @@ export interface GraphIR {
|
|
|
258
258
|
nodes: IRNode[];
|
|
259
259
|
edges: IREdge[];
|
|
260
260
|
groups: IRGroups;
|
|
261
|
+
/**
|
|
262
|
+
* Graph-level facts that are about the whole read rather than any one node.
|
|
263
|
+
*
|
|
264
|
+
* Deliberately an open bag. A fact lands here when it has no node to sit on
|
|
265
|
+
* — `_behaviour` (#2377) carries the engine, its version, the traffic level
|
|
266
|
+
* the figures are *at*, and the estate's totals, or a whole refusal report
|
|
267
|
+
* when there was no engine to ask. None of that is a property of a single
|
|
268
|
+
* entity, and putting the refusal on nodes would mean writing it N times or
|
|
269
|
+
* dropping it, and dropping it is the one outcome the contract forbids
|
|
270
|
+
* (`./behaviour-overlay.ts`).
|
|
271
|
+
*
|
|
272
|
+
* Absent unless something filled it, and a consumer reads absence as "not
|
|
273
|
+
* looked" rather than as a negative finding.
|
|
274
|
+
*/
|
|
275
|
+
meta?: Record<string, unknown>;
|
|
261
276
|
/** Outputs this stack publishes for other stacks to import (#513). */
|
|
262
277
|
exports?: IRExport[];
|
|
263
278
|
/** Parameters this stack imports from other stacks. A viewer matches an import's
|
package/dist/graph-ir.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-ir.d.ts","sourceRoot":"","sources":["../src/graph-ir.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"graph-ir.d.ts","sourceRoot":"","sources":["../src/graph-ir.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The graph seam (#2377): a request built from the graph in hand, and a
|
|
3
|
+
* projection put back onto it.
|
|
4
|
+
*
|
|
5
|
+
* The three states #2377 names are asserted on the values that reach the IR,
|
|
6
|
+
* because that IR is behold's whole input: a block per predicted node and the
|
|
7
|
+
* meta, nothing at all when no prediction was asked for, and the refusal on
|
|
8
|
+
* the meta with no node carrying a block.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, expect, it } from "vitest";
|
|
12
|
+
import {
|
|
13
|
+
behaviourReport,
|
|
14
|
+
noBehaviourEngineRefusal,
|
|
15
|
+
predictedRate,
|
|
16
|
+
type PredictedBehaviour,
|
|
17
|
+
} from "./behaviour";
|
|
18
|
+
import { BEHAVIOUR_OVERLAY_ATTR, behaviourOverlay } from "./behaviour-overlay";
|
|
19
|
+
import { applyBehaviourOverlay, behaviourRequestFromIr } from "./behaviour-graph";
|
|
20
|
+
import type { GraphIR, IRNode } from "./graph-ir";
|
|
21
|
+
|
|
22
|
+
const node = (id: string, kind: string, attrs: Record<string, unknown> = {}): IRNode => ({
|
|
23
|
+
id,
|
|
24
|
+
kind,
|
|
25
|
+
lexicon: "aws",
|
|
26
|
+
attrs,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const ir = (nodes: IRNode[], edges: GraphIR["edges"] = []): GraphIR => ({
|
|
30
|
+
nodes,
|
|
31
|
+
edges,
|
|
32
|
+
groups: { stacks: [], composites: [] } as unknown as GraphIR["groups"],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const block: PredictedBehaviour = {
|
|
36
|
+
at: { traffic: "100 rps, p50" },
|
|
37
|
+
cost: predictedRate(0.0416, "USD"),
|
|
38
|
+
headroom: { cpu: 0.6 },
|
|
39
|
+
errorRate: 0.0005,
|
|
40
|
+
resilience: { failure: "one zone lost", verdict: "survives" },
|
|
41
|
+
provenance: { engine: "fixture", version: "0.0.1", tolerance: "±20%", basis: "modeled" },
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const ctx = {
|
|
45
|
+
environment: "prod",
|
|
46
|
+
traffic: "100 rps, p50",
|
|
47
|
+
buildOutput: "",
|
|
48
|
+
edgeCoverage: { verdict: "unknown" } as const,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
describe("behaviourRequestFromIr", () => {
|
|
52
|
+
it("names every node and carries its kind and props verbatim", () => {
|
|
53
|
+
const graph = ir([node("web", "AWS::EC2::Instance", { instanceType: "t3.small" }), node("role", "AWS::IAM::Role")]);
|
|
54
|
+
const req = behaviourRequestFromIr(graph, ctx);
|
|
55
|
+
|
|
56
|
+
expect(req.entityNames).toEqual(["web", "role"]);
|
|
57
|
+
expect(req.entities.get("web")).toEqual({
|
|
58
|
+
entityType: "AWS::EC2::Instance",
|
|
59
|
+
props: { instanceType: "t3.small" },
|
|
60
|
+
});
|
|
61
|
+
expect(req.traffic).toBe("100 rps, p50");
|
|
62
|
+
expect(req.environment).toBe("prod");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("passes the graph's own edges through, because the prediction is about paths", () => {
|
|
66
|
+
const edges = [{ from: "web", to: "db", kind: "ref" }] as unknown as GraphIR["edges"];
|
|
67
|
+
const req = behaviourRequestFromIr(ir([node("web", "X"), node("db", "Y")], edges), ctx);
|
|
68
|
+
expect(req.edges).toBe(edges);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("does not filter props down to what chant thinks matters", () => {
|
|
72
|
+
// chant prices nothing, so it cannot know which field moves a figure. A
|
|
73
|
+
// filtered request would hand the engine a silently incomplete entity.
|
|
74
|
+
const attrs = { instanceType: "t3.small", tags: { team: "core" }, somethingChantHasNeverHeardOf: 7 };
|
|
75
|
+
const req = behaviourRequestFromIr(ir([node("web", "AWS::EC2::Instance", attrs)]), ctx);
|
|
76
|
+
expect(req.entities.get("web")?.props).toEqual(attrs);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("omits stack, region and owned rather than sending undefined for them", () => {
|
|
80
|
+
const req = behaviourRequestFromIr(ir([node("web", "X")]), ctx);
|
|
81
|
+
expect("stack" in req).toBe(false);
|
|
82
|
+
expect("region" in req).toBe(false);
|
|
83
|
+
expect("owned" in req).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("applyBehaviourOverlay", () => {
|
|
88
|
+
const report = behaviourReport(
|
|
89
|
+
{ entityNames: ["web", "role"], traffic: "100 rps, p50", edgeCoverage: { verdict: "unknown" } },
|
|
90
|
+
{ engine: "fixture", version: "0.0.1", total: predictedRate(0.0416, "USD") },
|
|
91
|
+
{ web: block },
|
|
92
|
+
{ role: { reason: "unsupported-kind", detail: "a role is a grant" } },
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
it("puts a predicted entity's block on its own node, and the meta on the graph", () => {
|
|
96
|
+
const out = applyBehaviourOverlay(ir([node("web", "X"), node("role", "Y")]), behaviourOverlay(report));
|
|
97
|
+
|
|
98
|
+
expect(out.nodes[0].attrs[BEHAVIOUR_OVERLAY_ATTR]).toBe(block);
|
|
99
|
+
const meta = out.meta?.[BEHAVIOUR_OVERLAY_ATTR] as Record<string, unknown>;
|
|
100
|
+
expect(meta.engine).toBe("fixture");
|
|
101
|
+
expect(meta.at).toEqual({ traffic: "100 rps, p50" });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("leaves an unpredicted node with no _behaviour key at all", () => {
|
|
105
|
+
const out = applyBehaviourOverlay(ir([node("web", "X"), node("role", "Y")]), behaviourOverlay(report));
|
|
106
|
+
// Not a zeroed block: "costs nothing" and "was not priced" are different
|
|
107
|
+
// claims, and a renderer cannot tell them apart after the fact.
|
|
108
|
+
expect(BEHAVIOUR_OVERLAY_ATTR in out.nodes[1].attrs).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("a refusal rides on the meta whole, and no node carries a block", () => {
|
|
112
|
+
const out = applyBehaviourOverlay(
|
|
113
|
+
ir([node("web", "X"), node("role", "Y")]),
|
|
114
|
+
behaviourOverlay(noBehaviourEngineRefusal("terraform")),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const meta = out.meta?.[BEHAVIOUR_OVERLAY_ATTR] as Record<string, unknown>;
|
|
118
|
+
// behold's reader branches on `refusal` before it looks for `engine`.
|
|
119
|
+
expect(meta.refusal).toBeDefined();
|
|
120
|
+
expect(meta.behaviour).toBe("v1");
|
|
121
|
+
for (const n of out.nodes) expect(BEHAVIOUR_OVERLAY_ATTR in n.attrs).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("keeps the drift overlay's attrs on the node beside the new block", () => {
|
|
125
|
+
// `_behaviour` joins `_status` rather than replacing the attrs object;
|
|
126
|
+
// losing the drift paint to gain a prediction would be a regression a
|
|
127
|
+
// renderer shows as every node going uncoloured.
|
|
128
|
+
const graph = ir([node("web", "X", { _status: "good", instanceType: "t3.small" })]);
|
|
129
|
+
const out = applyBehaviourOverlay(graph, behaviourOverlay(report));
|
|
130
|
+
|
|
131
|
+
expect(out.nodes[0].attrs._status).toBe("good");
|
|
132
|
+
expect(out.nodes[0].attrs.instanceType).toBe("t3.small");
|
|
133
|
+
expect(out.nodes[0].attrs[BEHAVIOUR_OVERLAY_ATTR]).toBe(block);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("does not mutate the graph it was given", () => {
|
|
137
|
+
// The caller's `ir` goes on to the lens pipeline; a prediction must not be
|
|
138
|
+
// the reason a later pass sees different nodes.
|
|
139
|
+
const graph = ir([node("web", "X", { _status: "good" })]);
|
|
140
|
+
const before = JSON.stringify(graph);
|
|
141
|
+
applyBehaviourOverlay(graph, behaviourOverlay(report));
|
|
142
|
+
expect(JSON.stringify(graph)).toBe(before);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("keeps meta keys another pass already put on the graph", () => {
|
|
146
|
+
const graph = { ...ir([node("web", "X")]), meta: { _somethingElse: 1 } };
|
|
147
|
+
const out = applyBehaviourOverlay(graph, behaviourOverlay(report));
|
|
148
|
+
expect(out.meta?._somethingElse).toBe(1);
|
|
149
|
+
expect(out.meta?.[BEHAVIOUR_OVERLAY_ATTR]).toBeDefined();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seam between a graph read and a prediction (#2377, epic #2355).
|
|
3
|
+
*
|
|
4
|
+
* `./behaviour-overlay.ts` projects a {@link BehaviourResult} onto the two
|
|
5
|
+
* channels the IR carries it on. This module is the other half: it builds the
|
|
6
|
+
* request from the graph the read already holds, and puts the projection back
|
|
7
|
+
* onto that graph. Both halves are pure, and neither calls an engine — the
|
|
8
|
+
* plugin call itself lives in `./cli/handlers/graph.ts`, which is the only
|
|
9
|
+
* place that has plugins to call.
|
|
10
|
+
*
|
|
11
|
+
* ## The request is the graph, not a second read
|
|
12
|
+
*
|
|
13
|
+
* The epic's rule is that the engine never sees credentials, and the way this
|
|
14
|
+
* path keeps that true is by never reading anything: `chant graph --live`
|
|
15
|
+
* already observed the estate, and {@link behaviourRequestFromIr} reshapes
|
|
16
|
+
* exactly those nodes and edges into the contract's options. There is no
|
|
17
|
+
* provider call here and nothing to leak, because there is no second read to
|
|
18
|
+
* leak from. `screenBehaviourRequest` (`./behaviour-predict.ts`) still screens
|
|
19
|
+
* what this produces; a node whose observed `attrs` picked up a secret is
|
|
20
|
+
* caught there, on the one path every prediction goes through.
|
|
21
|
+
*
|
|
22
|
+
* ## Why `attrs` are the props verbatim
|
|
23
|
+
*
|
|
24
|
+
* An IR node's `attrs` are what the read resolved — literals, and `{ $ref }`
|
|
25
|
+
* for a reference. The contract's `props` is the same thing, so this passes
|
|
26
|
+
* them across unchanged rather than filtering to a "useful" subset. Filtering
|
|
27
|
+
* would be chant deciding which properties can affect a price, and chant does
|
|
28
|
+
* not price anything; an engine that needs a field chant thought irrelevant
|
|
29
|
+
* would get a silently wrong figure with nothing to say so.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { BEHAVIOUR_OVERLAY_ATTR, type BehaviourOverlay } from "./behaviour-overlay";
|
|
33
|
+
import type { GraphIR } from "./graph-ir";
|
|
34
|
+
import type { PredictBehaviourOptions } from "./behaviour";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* What the caller knows and the graph does not.
|
|
38
|
+
*
|
|
39
|
+
* `edgeCoverage` is here rather than derived because this module cannot
|
|
40
|
+
* honestly derive it. Whether the edges in hand are the whole graph depends on
|
|
41
|
+
* which anchoring produced them and which lexicons contributed a reference
|
|
42
|
+
* catalog, and both facts live with the caller.
|
|
43
|
+
*/
|
|
44
|
+
export interface BehaviourRequestContext
|
|
45
|
+
extends Pick<PredictBehaviourOptions, "environment" | "traffic" | "buildOutput" | "edgeCoverage"> {
|
|
46
|
+
stack?: string;
|
|
47
|
+
region?: string;
|
|
48
|
+
owned?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Reshape an already-read graph into `predictBehaviour`'s options.
|
|
53
|
+
*
|
|
54
|
+
* Entity identity is the node's `id` and nothing derived from it. That is the
|
|
55
|
+
* same key the drift overlay writes `_status` under and the same key
|
|
56
|
+
* {@link applyBehaviourOverlay} reads back, so in a multi-stack project where
|
|
57
|
+
* ids are `${stack}::${logicalId}`, the block lands on the node it was
|
|
58
|
+
* predicted for without this module knowing that qualification exists.
|
|
59
|
+
*/
|
|
60
|
+
export function behaviourRequestFromIr(ir: GraphIR, ctx: BehaviourRequestContext): PredictBehaviourOptions {
|
|
61
|
+
const entities = new Map<string, { entityType: string; props: Record<string, unknown> }>();
|
|
62
|
+
for (const node of ir.nodes) {
|
|
63
|
+
entities.set(node.id, { entityType: node.kind, props: node.attrs });
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
environment: ctx.environment,
|
|
67
|
+
buildOutput: ctx.buildOutput,
|
|
68
|
+
entityNames: [...entities.keys()],
|
|
69
|
+
entities,
|
|
70
|
+
edges: ir.edges,
|
|
71
|
+
edgeCoverage: ctx.edgeCoverage,
|
|
72
|
+
traffic: ctx.traffic,
|
|
73
|
+
...(ctx.stack === undefined ? {} : { stack: ctx.stack }),
|
|
74
|
+
...(ctx.region === undefined ? {} : { region: ctx.region }),
|
|
75
|
+
...(ctx.owned === undefined ? {} : { owned: ctx.owned }),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Put a projection back onto the graph: each entity's block on its node's
|
|
81
|
+
* `attrs`, the graph-level half on `ir.meta`.
|
|
82
|
+
*
|
|
83
|
+
* A node with no entry keeps the `attrs` object it already had, untouched —
|
|
84
|
+
* an unpriced entity carries no `_behaviour` key rather than an empty or
|
|
85
|
+
* zeroed one, because zero cost and "not priced" are different claims and a
|
|
86
|
+
* renderer cannot tell them apart after the fact.
|
|
87
|
+
*
|
|
88
|
+
* Returns a new graph and mutates nothing: the caller's `ir` is shared with
|
|
89
|
+
* the lens pipeline downstream, and a prediction is not allowed to be the
|
|
90
|
+
* reason a later pass sees different nodes.
|
|
91
|
+
*/
|
|
92
|
+
export function applyBehaviourOverlay(ir: GraphIR, overlay: BehaviourOverlay): GraphIR {
|
|
93
|
+
const nodes = ir.nodes.map((node) => {
|
|
94
|
+
const block = overlay.attrs[node.id];
|
|
95
|
+
return block === undefined ? node : { ...node, attrs: { ...node.attrs, ...block } };
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
...ir,
|
|
99
|
+
nodes,
|
|
100
|
+
meta: { ...(ir.meta ?? {}), [BEHAVIOUR_OVERLAY_ATTR]: overlay.meta[BEHAVIOUR_OVERLAY_ATTR] },
|
|
101
|
+
};
|
|
102
|
+
}
|
package/src/build.test.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { describe, test, expect, beforeEach, afterEach, vi } from "vitest";
|
|
|
2
2
|
import { build, partitionByLexicon, detectCrossLexiconRefs, collectLexiconOutputs, computeStackGraph } from "./build";
|
|
3
3
|
import { output } from "./lexicon-output";
|
|
4
4
|
import { AttrRef } from "./attrref";
|
|
5
|
-
import { INTRINSIC_MARKER } from "./intrinsic";
|
|
5
|
+
import { INTRINSIC_MARKER, type Intrinsic } from "./intrinsic";
|
|
6
6
|
import type { Serializer } from "./serializer";
|
|
7
7
|
import type { Declarable } from "./declarable";
|
|
8
8
|
import { DECLARABLE_MARKER } from "./declarable";
|
|
@@ -793,6 +793,59 @@ describe("computeStackGraph (#200 — cross-stack apply ordering)", () => {
|
|
|
793
793
|
const ent = (lexicon: string, props: Record<string, unknown> = {}): Declarable =>
|
|
794
794
|
({ lexicon, entityType: `${lexicon}::X`, [DECLARABLE_MARKER]: true, props }) as unknown as Declarable;
|
|
795
795
|
|
|
796
|
+
// #2480: the walk took `entity.lexicon` as the consumer for every entity,
|
|
797
|
+
// and four kinds have no place in a stack graph. A LexiconOutput carries no
|
|
798
|
+
// `lexicon` at all, so it produced an edge with `from: undefined`, and that
|
|
799
|
+
// `undefined` then reached `order` and `waves` — while `nodes`, built from
|
|
800
|
+
// the partition, could never contain it. Reproduced on chant's own
|
|
801
|
+
// fan-out-estate and bedrock-agentcore-agent examples, both of which declare
|
|
802
|
+
// outputs built from intrinsics.
|
|
803
|
+
//
|
|
804
|
+
// The invariant these assert is the one a consumer can actually rely on:
|
|
805
|
+
// `nodes` is the roster, and nothing may appear anywhere else that is not
|
|
806
|
+
// named in it.
|
|
807
|
+
const rosterHolds = (g: ReturnType<typeof computeStackGraph>): void => {
|
|
808
|
+
const roster = new Set(g.nodes);
|
|
809
|
+
expect(g.nodes.every((n) => typeof n === "string")).toBe(true);
|
|
810
|
+
for (const e of g.edges) {
|
|
811
|
+
expect(roster.has(e.from)).toBe(true);
|
|
812
|
+
expect(roster.has(e.to)).toBe(true);
|
|
813
|
+
}
|
|
814
|
+
for (const n of g.order) expect(roster.has(n)).toBe(true);
|
|
815
|
+
for (const wave of g.waves) for (const n of wave) expect(roster.has(n)).toBe(true);
|
|
816
|
+
for (const cycle of g.cycles) for (const n of cycle) expect(roster.has(n)).toBe(true);
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
test("a LexiconOutput is not a stack member, so it cannot reach edges, order or waves (#2480)", () => {
|
|
820
|
+
const vpc = ent("aws");
|
|
821
|
+
// An output built from an intrinsic holds real AttrRefs inside it, which
|
|
822
|
+
// is what carried the walk into `addEdge(undefined, "aws")`.
|
|
823
|
+
// Built from an INTRINSIC holding an AttrRef, which is the shape the two
|
|
824
|
+
// reproducing examples have (`Fn::Sub`/`Ref` over a resource attribute).
|
|
825
|
+
// An output built from a bare AttrRef stores only a WeakRef and carries
|
|
826
|
+
// the walk nowhere, so it does not reproduce this.
|
|
827
|
+
const intrinsic: Intrinsic = {
|
|
828
|
+
[INTRINSIC_MARKER]: true as const,
|
|
829
|
+
ref: new AttrRef(vpc, "id"),
|
|
830
|
+
toJSON: () => ({ "Fn::Sub": "x" }),
|
|
831
|
+
} as Intrinsic;
|
|
832
|
+
const out = output(intrinsic, "vpcId");
|
|
833
|
+
|
|
834
|
+
const g = computeStackGraph(new Map([["vpc", vpc], ["vpcId", out as unknown as Declarable]]), ["aws"]);
|
|
835
|
+
|
|
836
|
+
expect(g.nodes).toEqual(["aws"]);
|
|
837
|
+
expect(g.edges).toEqual([]);
|
|
838
|
+
expect(g.order).toEqual(["aws"]);
|
|
839
|
+
expect(g.waves).toEqual([["aws"]]);
|
|
840
|
+
rosterHolds(g);
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
test("the roster invariant holds for an ordinary cross-lexicon graph too", () => {
|
|
844
|
+
const vpc = ent("aws");
|
|
845
|
+
const svc = ent("k8s", { vpcId: new AttrRef(vpc, "id") });
|
|
846
|
+
rosterHolds(computeStackGraph(new Map([["vpc", vpc], ["svc", svc]]), ["aws", "k8s"]));
|
|
847
|
+
});
|
|
848
|
+
|
|
796
849
|
test("infers a consumer→producer edge from a cross-lexicon AttrRef", () => {
|
|
797
850
|
const vpc = ent("aws");
|
|
798
851
|
const svc = ent("k8s", { vpcId: new AttrRef(vpc, "id") });
|
package/src/build.ts
CHANGED
|
@@ -65,6 +65,30 @@ export interface StackGraph {
|
|
|
65
65
|
* referencing an attribute of a resource in lexicon B ⇒ A depends on B). Returns
|
|
66
66
|
* the edge set plus a topological order, parallel-safe waves, and any cycles.
|
|
67
67
|
*/
|
|
68
|
+
/**
|
|
69
|
+
* Is this entity a member of a lexicon stack?
|
|
70
|
+
*
|
|
71
|
+
* Four kinds are not, and every one of them is serializer-neutral: a
|
|
72
|
+
* `LexiconOutput` is collected separately, a secret provenance declaration
|
|
73
|
+
* (#1828) and a plan scenario (#1292) are data lint and the CLI read off the
|
|
74
|
+
* entity map, and an Op (#2118) builds to its own `op.json` rather than
|
|
75
|
+
* through a lexicon serializer.
|
|
76
|
+
*
|
|
77
|
+
* {@link partitionByLexicon} has skipped all four since each landed, and
|
|
78
|
+
* {@link computeStackGraph}'s walk did not — so an entity with no `lexicon`
|
|
79
|
+
* (a `LexiconOutput`) entered `edges`, `order` and `waves` as `undefined`
|
|
80
|
+
* while `nodes`, built from the partition, could never contain it (#2480).
|
|
81
|
+
* One predicate for both sites, so they cannot drift apart again.
|
|
82
|
+
*/
|
|
83
|
+
export function isStackMember(entity: Declarable): boolean {
|
|
84
|
+
return (
|
|
85
|
+
!isLexiconOutput(entity) &&
|
|
86
|
+
!isSecretDeclaration(entity) &&
|
|
87
|
+
!isScenario(entity) &&
|
|
88
|
+
!isOpEntity(entity)
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
68
92
|
export function computeStackGraph(
|
|
69
93
|
entities: Map<string, Declarable>,
|
|
70
94
|
lexiconNames: string[],
|
|
@@ -72,6 +96,10 @@ export function computeStackGraph(
|
|
|
72
96
|
const edges: Array<{ from: string; to: string }> = [];
|
|
73
97
|
const edgeSet = new Set<string>();
|
|
74
98
|
const addEdge = (from: string, to: string): void => {
|
|
99
|
+
// A structural guard, not a defensive one: `nodes` comes from the
|
|
100
|
+
// partition, so an endpoint the partition would have dropped can never be
|
|
101
|
+
// a legitimate member of this graph (#2480).
|
|
102
|
+
if (typeof from !== "string" || typeof to !== "string") return;
|
|
75
103
|
if (from === to) return;
|
|
76
104
|
const key = `${from}\0${to}`;
|
|
77
105
|
if (edgeSet.has(key)) return;
|
|
@@ -104,6 +132,9 @@ export function computeStackGraph(
|
|
|
104
132
|
};
|
|
105
133
|
|
|
106
134
|
for (const [, entity] of entities) {
|
|
135
|
+
// #2480: the same four kinds `partitionByLexicon` drops. Walking one as a
|
|
136
|
+
// consumer takes its absent `lexicon` as the edge's `from`.
|
|
137
|
+
if (!isStackMember(entity)) continue;
|
|
107
138
|
const consumer = entity.lexicon;
|
|
108
139
|
const visited = new Set<unknown>();
|
|
109
140
|
for (const val of Object.values(entity as unknown as Record<string, unknown>)) {
|
|
@@ -117,10 +148,10 @@ export function computeStackGraph(
|
|
|
117
148
|
// Dependency map: node → producers it depends on.
|
|
118
149
|
const nodes = lexiconNames.length
|
|
119
150
|
? [...lexiconNames]
|
|
120
|
-
: //
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
[...new Set([...entities.values()].filter(
|
|
151
|
+
: // The same predicate, rather than the partial copy of it this branch
|
|
152
|
+
// carried: it filtered secrets and scenarios but not outputs or Ops, so
|
|
153
|
+
// it could mint a node the partition would have dropped (#2480).
|
|
154
|
+
[...new Set([...entities.values()].filter(isStackMember).map((e) => e.lexicon))];
|
|
124
155
|
const deps = new Map<string, Set<string>>();
|
|
125
156
|
for (const n of nodes) deps.set(n, new Set());
|
|
126
157
|
for (const { from, to } of edges) {
|
|
@@ -325,23 +356,13 @@ export function partitionByLexicon(
|
|
|
325
356
|
const partitions = new Map<string, Map<string, Declarable>>();
|
|
326
357
|
|
|
327
358
|
for (const [name, entity] of entities) {
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
if (
|
|
335
|
-
// Plan scenarios (#1292) are serializer-neutral the same way: a checkable
|
|
336
|
-
// expectation the CLI reads off the entity map, never output.
|
|
337
|
-
if (isScenario(entity)) continue;
|
|
338
|
-
// Op declarations (#2118) too. An Op's build output is its `op.json` IR,
|
|
339
|
-
// and core writes that itself from the entity map (`./cli/commands/build.ts`)
|
|
340
|
-
// rather than routing it through a lexicon serializer — the Op model, the
|
|
341
|
-
// executor and the IR are all core's, and no lexicon renders an Op entity
|
|
342
|
-
// into its own manifest. Keeping them out of every partition means no
|
|
343
|
-
// "No serializer found" warning fires for core's own `chant` lexicon.
|
|
344
|
-
if (isOpEntity(entity)) continue;
|
|
359
|
+
// The four serializer-neutral kinds, each explained on {@link isStackMember}:
|
|
360
|
+
// a LexiconOutput is collected separately, and a secret declaration
|
|
361
|
+
// (#1828), a plan scenario (#1292) and an Op (#2118) are read off the
|
|
362
|
+
// entity map rather than routed through a lexicon serializer. Keeping them
|
|
363
|
+
// out of every partition is what stops a "No serializer found" warning
|
|
364
|
+
// firing for a pseudo-lexicon.
|
|
365
|
+
if (!isStackMember(entity)) continue;
|
|
345
366
|
const lexicon = entity.lexicon;
|
|
346
367
|
if (!partitions.has(lexicon)) {
|
|
347
368
|
partitions.set(lexicon, new Map());
|
|
@@ -110,6 +110,7 @@ vi.mock("../../build", async () => {
|
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
const { runGraph } = await import("./graph");
|
|
113
|
+
const { behaviourReport, noBehaviourEngineRefusal, predictedRate } = await import("../../behaviour");
|
|
113
114
|
|
|
114
115
|
function makeArgs(overrides: Partial<ParsedArgs> = {}): ParsedArgs {
|
|
115
116
|
return {
|
|
@@ -626,6 +627,200 @@ describe("runGraph", () => {
|
|
|
626
627
|
expect(out).toContain("web-vpc");
|
|
627
628
|
});
|
|
628
629
|
|
|
630
|
+
// #2377 — the overlay carries the prediction. behold's reader takes
|
|
631
|
+
// `attrs._behaviour` per node and `meta._behaviour` on the graph, and
|
|
632
|
+
// reads the absence of both as "not looked" rather than as a refusal, so
|
|
633
|
+
// all three states are asserted on what actually reaches the IR.
|
|
634
|
+
describe("behaviour overlay (--traffic)", () => {
|
|
635
|
+
const predictingPlugin = (predictBehaviour: unknown) => ({
|
|
636
|
+
name: "aws",
|
|
637
|
+
serializer: {},
|
|
638
|
+
emulator: awsEmulatorStub,
|
|
639
|
+
describeResources: () => Promise.resolve({}),
|
|
640
|
+
predictBehaviour,
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
const liveEstate = () => {
|
|
644
|
+
resolveLexMock.mockResolvedValue(["aws"]);
|
|
645
|
+
// `--overlay` builds the declared canvas to classify against. Empty is
|
|
646
|
+
// enough here: what is under test is the behaviour block, and an empty
|
|
647
|
+
// declared graph leaves the observed nodes in place as foreign.
|
|
648
|
+
discoverMock.mockResolvedValue({ entities: new Map(), errors: [], sourceFiles: [] });
|
|
649
|
+
observeMock.mockResolvedValue({
|
|
650
|
+
observations: [{ lexicon: "aws", resources: {
|
|
651
|
+
web: { type: "AWS::EC2::Instance", status: "OK" },
|
|
652
|
+
role: { type: "AWS::IAM::Role", status: "OK" },
|
|
653
|
+
} }],
|
|
654
|
+
errors: [],
|
|
655
|
+
warnings: [],
|
|
656
|
+
});
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const report = () => behaviourReport(
|
|
660
|
+
{ entityNames: ["web", "role"], traffic: "100 rps, p50", edgeCoverage: { verdict: "unknown" } },
|
|
661
|
+
{ engine: "fixture", version: "0.0.1", total: predictedRate(0.0416, "USD") },
|
|
662
|
+
{ web: {
|
|
663
|
+
at: { traffic: "100 rps, p50" },
|
|
664
|
+
cost: predictedRate(0.0416, "USD"),
|
|
665
|
+
headroom: { cpu: 0.6 },
|
|
666
|
+
errorRate: 0.0005,
|
|
667
|
+
resilience: { failure: "one zone lost", verdict: "survives" },
|
|
668
|
+
provenance: { engine: "fixture", version: "0.0.1", tolerance: "±20%", basis: "modeled" },
|
|
669
|
+
} },
|
|
670
|
+
{ role: { reason: "unsupported-kind", detail: "a role is a grant" } },
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
test("with an engine: the block is on its node and the meta on the graph", async () => {
|
|
674
|
+
liveEstate();
|
|
675
|
+
loadPluginsMock.mockResolvedValue([predictingPlugin(() => Promise.resolve(report()))]);
|
|
676
|
+
|
|
677
|
+
const exit = await runGraph({
|
|
678
|
+
args: makeArgs({ format: "ir", live: true, overlay: true, env: "prod", traffic: "100 rps, p50" }),
|
|
679
|
+
plugins: [], serializers: [],
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
expect(exit).toBe(0);
|
|
683
|
+
const ir = JSON.parse(stdoutBuf.join("\n")) as {
|
|
684
|
+
nodes: Array<{ id: string; attrs: Record<string, unknown> }>;
|
|
685
|
+
meta?: Record<string, { engine?: string; at?: { traffic?: string } }>;
|
|
686
|
+
};
|
|
687
|
+
expect(ir.meta?._behaviour?.engine).toBe("fixture");
|
|
688
|
+
expect(ir.meta?._behaviour?.at?.traffic).toBe("100 rps, p50");
|
|
689
|
+
const web = ir.nodes.find((n) => n.id === "web");
|
|
690
|
+
expect((web?.attrs._behaviour as { cost?: { perHour?: number } })?.cost?.perHour).toBe(0.0416);
|
|
691
|
+
// An entity the engine could not price carries no block rather than a
|
|
692
|
+
// zeroed one — "costs nothing" is a different claim from "not priced".
|
|
693
|
+
expect("_behaviour" in (ir.nodes.find((n) => n.id === "role")?.attrs ?? {})).toBe(false);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
test("the traffic level reaches the engine verbatim, with the graph's own entities", async () => {
|
|
697
|
+
liveEstate();
|
|
698
|
+
const seen: Array<{ traffic: string; entityNames: string[] }> = [];
|
|
699
|
+
loadPluginsMock.mockResolvedValue([predictingPlugin((o: { traffic: string; entityNames: string[] }) => {
|
|
700
|
+
seen.push({ traffic: o.traffic, entityNames: o.entityNames });
|
|
701
|
+
return Promise.resolve(report());
|
|
702
|
+
})]);
|
|
703
|
+
|
|
704
|
+
await runGraph({
|
|
705
|
+
args: makeArgs({ format: "ir", live: true, overlay: true, env: "prod", traffic: "1000 rps, p99" }),
|
|
706
|
+
plugins: [], serializers: [],
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
expect(seen[0]?.traffic).toBe("1000 rps, p99");
|
|
710
|
+
expect(seen[0]?.entityNames).toContain("web");
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
test("without --traffic: nothing is asked and neither key appears", async () => {
|
|
714
|
+
liveEstate();
|
|
715
|
+
let called = false;
|
|
716
|
+
loadPluginsMock.mockResolvedValue([predictingPlugin(() => { called = true; return Promise.resolve(report()); })]);
|
|
717
|
+
|
|
718
|
+
const exit = await runGraph({
|
|
719
|
+
args: makeArgs({ format: "ir", live: true, overlay: true, env: "prod" }),
|
|
720
|
+
plugins: [], serializers: [],
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
expect(exit).toBe(0);
|
|
724
|
+
// Not a refusal: an engine was configured and reachable. behold reads
|
|
725
|
+
// this absence as "not looked", which is why nothing may be written.
|
|
726
|
+
expect(called).toBe(false);
|
|
727
|
+
const ir = JSON.parse(stdoutBuf.join("\n")) as {
|
|
728
|
+
nodes: Array<{ attrs: Record<string, unknown> }>; meta?: Record<string, unknown>;
|
|
729
|
+
};
|
|
730
|
+
expect(ir.meta?._behaviour).toBeUndefined();
|
|
731
|
+
for (const n of ir.nodes) expect("_behaviour" in n.attrs).toBe(false);
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
test("a refusal rides on the meta whole, and no node carries a block", async () => {
|
|
735
|
+
liveEstate();
|
|
736
|
+
loadPluginsMock.mockResolvedValue([
|
|
737
|
+
predictingPlugin(() => Promise.resolve(noBehaviourEngineRefusal("aws"))),
|
|
738
|
+
]);
|
|
739
|
+
|
|
740
|
+
const exit = await runGraph({
|
|
741
|
+
args: makeArgs({ format: "ir", live: true, overlay: true, env: "prod", traffic: "100 rps, p50" }),
|
|
742
|
+
plugins: [], serializers: [],
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
expect(exit).toBe(0);
|
|
746
|
+
const ir = JSON.parse(stdoutBuf.join("\n")) as {
|
|
747
|
+
nodes: Array<{ attrs: Record<string, unknown> }>;
|
|
748
|
+
meta?: { _behaviour?: { refusal?: { reason?: string }; behaviour?: string } };
|
|
749
|
+
};
|
|
750
|
+
// behold branches on `refusal` before it looks for `engine`; the
|
|
751
|
+
// envelope is what makes it findable at all.
|
|
752
|
+
expect(ir.meta?._behaviour?.refusal?.reason).toMatch(/CHANT_BEHAVIOUR_ENGINE/);
|
|
753
|
+
expect(ir.meta?._behaviour?.behaviour).toBe("v1");
|
|
754
|
+
for (const n of ir.nodes) expect("_behaviour" in n.attrs).toBe(false);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
test("no predicting lexicon: the graph is served unchanged, and says so once", async () => {
|
|
758
|
+
liveEstate();
|
|
759
|
+
loadPluginsMock.mockResolvedValue([
|
|
760
|
+
{ name: "aws", serializer: {}, emulator: awsEmulatorStub, describeResources: () => Promise.resolve({}) },
|
|
761
|
+
]);
|
|
762
|
+
|
|
763
|
+
const exit = await runGraph({
|
|
764
|
+
args: makeArgs({ format: "ir", live: true, overlay: true, env: "prod", traffic: "100 rps, p50" }),
|
|
765
|
+
plugins: [], serializers: [],
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
expect(exit).toBe(0);
|
|
769
|
+
expect(stderrBuf.join("\n")).toContain("no installed lexicon implements predictBehaviour");
|
|
770
|
+
expect(JSON.parse(stdoutBuf.join("\n")).meta?._behaviour).toBeUndefined();
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
test("a thrown engine costs the prediction, never the graph", async () => {
|
|
774
|
+
liveEstate();
|
|
775
|
+
loadPluginsMock.mockResolvedValue([
|
|
776
|
+
predictingPlugin(() => Promise.reject(new Error("socket hang up"))),
|
|
777
|
+
]);
|
|
778
|
+
|
|
779
|
+
const exit = await runGraph({
|
|
780
|
+
args: makeArgs({ format: "ir", live: true, overlay: true, env: "prod", traffic: "100 rps, p50" }),
|
|
781
|
+
plugins: [], serializers: [],
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
// The estate graph is what was asked for; a broken engine must not
|
|
785
|
+
// take it away.
|
|
786
|
+
expect(exit).toBe(0);
|
|
787
|
+
expect(stderrBuf.join("\n")).toContain("socket hang up");
|
|
788
|
+
const ir = JSON.parse(stdoutBuf.join("\n")) as { nodes: unknown[]; meta?: Record<string, unknown> };
|
|
789
|
+
expect(ir.nodes.length).toBeGreaterThan(0);
|
|
790
|
+
expect(ir.meta?._behaviour).toBeUndefined();
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
test("--traffic without --live is refused rather than silently ignored", async () => {
|
|
794
|
+
// Accepted-and-ignored would read as "predicted, and everything came
|
|
795
|
+
// back empty", which is the opposite of what happened.
|
|
796
|
+
const exit = await runGraph({
|
|
797
|
+
args: makeArgs({ format: "ir", traffic: "100 rps, p50" }),
|
|
798
|
+
plugins: [], serializers: [],
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
expect(exit).toBe(1);
|
|
802
|
+
expect(stderrBuf.join("\n")).toContain("--traffic needs --live");
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
test("two predicting lexicons is refused rather than attributed to one", async () => {
|
|
806
|
+
liveEstate();
|
|
807
|
+
loadPluginsMock.mockResolvedValue([
|
|
808
|
+
predictingPlugin(() => Promise.resolve(report())),
|
|
809
|
+
{ ...predictingPlugin(() => Promise.resolve(report())), name: "gcp" },
|
|
810
|
+
]);
|
|
811
|
+
|
|
812
|
+
const exit = await runGraph({
|
|
813
|
+
args: makeArgs({ format: "ir", live: true, overlay: true, env: "prod", traffic: "100 rps, p50" }),
|
|
814
|
+
plugins: [], serializers: [],
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// One `meta._behaviour` key and two engines: taking the first would
|
|
818
|
+
// put one engine's figures under the other's provenance.
|
|
819
|
+
expect(exit).toBe(1);
|
|
820
|
+
expect(stderrBuf.join("\n")).toContain("cannot merge two engines");
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
|
|
629
824
|
// #1279 — `graph` had no `--at`, so anyone wanting the raw IR of a recorded
|
|
630
825
|
// estate had to reach for the live endpoint. A snapshot could answer most
|
|
631
826
|
// questions and never all of them.
|
|
@@ -3,6 +3,9 @@ import { discoverOps } from "../../op/discover";
|
|
|
3
3
|
import { discover } from "../../discovery/index";
|
|
4
4
|
import { partitionByLexicon, computeStackGraph, build, mergeBuildRootEntities } from "../../build";
|
|
5
5
|
import { buildGraphIr, buildLiveGraphIr, collectUnobserved, overlayGraphs, sourceOverlayGraphs, type GraphIR, type IRPipeline, type LiveObservation } from "../../graph-ir";
|
|
6
|
+
import { behaviourOverlay } from "../../behaviour-overlay";
|
|
7
|
+
import type { SerializerResult } from "../../serializer";
|
|
8
|
+
import { applyBehaviourOverlay, behaviourRequestFromIr } from "../../behaviour-graph";
|
|
6
9
|
import { buildDeclaredPerStack } from "../../graph-declared";
|
|
7
10
|
import { mergeProjectOps } from "../../graph-ops";
|
|
8
11
|
import { reconstructEdges, mergeCatalogs, containmentGroups, type ReferenceCatalog, type ContainmentPair } from "../../graph-refs";
|
|
@@ -157,6 +160,16 @@ export async function runGraph(ctx: CommandContext): Promise<number> {
|
|
|
157
160
|
}));
|
|
158
161
|
return 1;
|
|
159
162
|
}
|
|
163
|
+
// A prediction is about an estate, and only the live path has one. Without
|
|
164
|
+
// `--live` the flag would be accepted and do nothing at all, which reads as
|
|
165
|
+
// "predicted, and everything came back empty" rather than "never ran".
|
|
166
|
+
if (ctx.args.traffic && !ctx.args.live) {
|
|
167
|
+
console.error(formatError({
|
|
168
|
+
message: "--traffic needs --live: a prediction is made about the observed estate, and there is nothing to predict on the declared graph alone.",
|
|
169
|
+
hint: 'chant graph --format ir --live --env <name> --overlay --traffic "100 rps, p50"',
|
|
170
|
+
}));
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
160
173
|
// `--at` graphs a recorded observation instead of reading the estate (#1279).
|
|
161
174
|
// Two different observations with no rule for which wins, so not both.
|
|
162
175
|
if (ctx.args.at && ctx.args.live) {
|
|
@@ -434,6 +447,71 @@ async function runGraphLive(
|
|
|
434
447
|
}
|
|
435
448
|
}
|
|
436
449
|
|
|
450
|
+
// Behaviour overlay (#2377, epic #2355): carry a prediction on the graph.
|
|
451
|
+
//
|
|
452
|
+
// `--traffic` is the trigger, and it is the trigger because the contract
|
|
453
|
+
// forbids defaulting the level (../../behaviour.ts): every figure an engine
|
|
454
|
+
// returns is *at* a stated traffic, so inventing one here would silently
|
|
455
|
+
// decide what every number on the graph means. No flag: nothing is asked,
|
|
456
|
+
// neither key appears, and a consumer reads that absence as "not looked".
|
|
457
|
+
//
|
|
458
|
+
// Placed after the drift overlay so the prediction lands on the final node
|
|
459
|
+
// set — the canvas is settled by here under either anchoring — and before
|
|
460
|
+
// the lens below, so a lens filters predicted nodes like any other.
|
|
461
|
+
if (args.traffic) {
|
|
462
|
+
const predicting = plugins.filter((p) => p.predictBehaviour);
|
|
463
|
+
if (predicting.length === 0) {
|
|
464
|
+
console.error(formatWarning({
|
|
465
|
+
message:
|
|
466
|
+
"--traffic: no installed lexicon implements predictBehaviour, so nothing was predicted. The graph is unchanged.",
|
|
467
|
+
}));
|
|
468
|
+
} else if (predicting.length > 1) {
|
|
469
|
+
// Two engines would mean two `meta._behaviour` values and one key. Rather
|
|
470
|
+
// than pick, say so: a merged prediction across engines is not defined by
|
|
471
|
+
// the contract, and silently taking the first would attribute one
|
|
472
|
+
// engine's figures to the other's provenance.
|
|
473
|
+
console.error(formatError({
|
|
474
|
+
message: `--traffic: ${predicting.length} lexicons implement predictBehaviour (${predicting.map((p) => p.name).join(", ")}) — chant cannot merge two engines' figures onto one graph.`,
|
|
475
|
+
hint: "Predict one lexicon at a time, or drop --traffic to graph without a prediction",
|
|
476
|
+
}));
|
|
477
|
+
return 1;
|
|
478
|
+
} else {
|
|
479
|
+
// The first output the build produced, or empty. Nothing on this path
|
|
480
|
+
// reads it — the mirror is kept so the four observation methods take the
|
|
481
|
+
// same shape (../../op/activities/predict-behaviour.ts) — so a build
|
|
482
|
+
// result without outputs costs an empty string, never the graph.
|
|
483
|
+
const raw = buildResult.outputs ? [...buildResult.outputs.values()][0] : undefined;
|
|
484
|
+
const buildOutput = raw === undefined ? "" : typeof raw === "string" ? raw : (raw as SerializerResult).primary;
|
|
485
|
+
const request = behaviourRequestFromIr(ir, {
|
|
486
|
+
environment: environment ?? "",
|
|
487
|
+
traffic: args.traffic,
|
|
488
|
+
buildOutput,
|
|
489
|
+
// `unknown`, for the reason the declared path claims it
|
|
490
|
+
// (../../op/activities/predict-behaviour.ts): reference edges resolved,
|
|
491
|
+
// containment absent, and no vocabulary here for "this kind is a
|
|
492
|
+
// boundary whose containment is missing". Claiming `complete` would be
|
|
493
|
+
// true about references and false about the graph; `partial` has to
|
|
494
|
+
// name its gap as `dangling` or `unresolvedKinds`, and this gap is
|
|
495
|
+
// neither.
|
|
496
|
+
edgeCoverage: { verdict: "unknown" },
|
|
497
|
+
...(args.owned === undefined ? {} : { owned: args.owned }),
|
|
498
|
+
});
|
|
499
|
+
try {
|
|
500
|
+
// A refusal is a value, not a throw: it comes back as a result and
|
|
501
|
+
// rides on `meta._behaviour` whole, which is the one shape behold's
|
|
502
|
+
// reader branches on (../../behaviour-overlay.ts).
|
|
503
|
+
const result = await predicting[0].predictBehaviour!(request);
|
|
504
|
+
ir = applyBehaviourOverlay(ir, behaviourOverlay(result));
|
|
505
|
+
} catch (err) {
|
|
506
|
+
// The estate graph is the thing asked for; a broken engine must not
|
|
507
|
+
// take it away. Say what failed and serve the graph without the block.
|
|
508
|
+
console.error(formatWarning({
|
|
509
|
+
message: `--traffic: ${predicting[0].name}'s predictBehaviour failed (${err instanceof Error ? err.message : String(err)}) — showing the graph without a prediction`,
|
|
510
|
+
}));
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
437
515
|
if (args.lens) {
|
|
438
516
|
try {
|
|
439
517
|
ir = applyLens(ir, parseLens(args.lens, { up: args.up, down: args.down }));
|
package/src/cli/main.test.ts
CHANGED
|
@@ -4,6 +4,16 @@ import { parseArgs, waitForStreamDrain } from "./main";
|
|
|
4
4
|
import { resolveCommand, type CommandDef, type ParsedArgs } from "./registry";
|
|
5
5
|
|
|
6
6
|
describe("parseArgs", () => {
|
|
7
|
+
// #2377 — the level reaches the engine verbatim, so parsing must not
|
|
8
|
+
// normalise it, and an empty one must not read as "predict at nothing".
|
|
9
|
+
test("takes --traffic as an opaque level, and refuses an empty one", () => {
|
|
10
|
+
expect(parseArgs(["graph", "--live", "--overlay"]).traffic).toBeUndefined();
|
|
11
|
+
expect(parseArgs(["graph", "--live", "--overlay", "--traffic", "100 rps, p50"]).traffic).toBe("100 rps, p50");
|
|
12
|
+
// Spacing, casing and punctuation are the engine's to interpret.
|
|
13
|
+
expect(parseArgs(["graph", "--traffic", "1000rps p99"]).traffic).toBe("1000rps p99");
|
|
14
|
+
expect(() => parseArgs(["graph", "--traffic"])).toThrow(/--traffic needs a level/);
|
|
15
|
+
});
|
|
16
|
+
|
|
7
17
|
test("--fold and --no-fold set the tri-state fold option (#1134)", () => {
|
|
8
18
|
expect(parseArgs(["build", "src"]).fold).toBeUndefined();
|
|
9
19
|
expect(parseArgs(["build", "src", "--fold"]).fold).toBe(true);
|
package/src/cli/main.ts
CHANGED
|
@@ -195,6 +195,10 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
195
195
|
result.betweenA = args[++i];
|
|
196
196
|
result.betweenB = args[++i];
|
|
197
197
|
if (!result.betweenA || !result.betweenB) throw new Error("--between needs two snapshot refs: --between <refA> <refB>");
|
|
198
|
+
} else if (arg === "--traffic") {
|
|
199
|
+
const v = args[++i];
|
|
200
|
+
if (!v) throw new Error('--traffic needs a level, passed to the engine verbatim: --traffic "100 rps, p50"');
|
|
201
|
+
result.traffic = v;
|
|
198
202
|
} else if (arg === "--overlay-anchor") {
|
|
199
203
|
const v = args[++i];
|
|
200
204
|
if (v !== "source" && v !== "live") throw new Error(`--overlay-anchor must be 'source' or 'live', got '${v}'`);
|
|
@@ -595,6 +599,11 @@ Ops:
|
|
|
595
599
|
add the CI/pipeline projection (stages/jobs/needs) to
|
|
596
600
|
the component-graph IR, from the same generator
|
|
597
601
|
'build --components --generate' uses (#989)
|
|
602
|
+
--live --overlay --traffic "<level>": ask the project's
|
|
603
|
+
predicting lexicon what the overlaid estate does at that
|
|
604
|
+
traffic level, and carry each entity's prediction on the
|
|
605
|
+
IR. The level is passed to the engine verbatim; without
|
|
606
|
+
the flag nothing is asked and nothing is carried (#2377)
|
|
598
607
|
|
|
599
608
|
Lifecycle (alias: lc):
|
|
600
609
|
lifecycle snapshot <env> Query API, save metadata to orphan branch
|
package/src/cli/registry.ts
CHANGED
|
@@ -159,6 +159,17 @@ export interface ParsedArgs {
|
|
|
159
159
|
* cross-substrate topology) and joins live status per node; `live` keeps the
|
|
160
160
|
* provisioned graph's reconstructed edges (the pre-#821 behaviour). */
|
|
161
161
|
overlayAnchor?: "source" | "live";
|
|
162
|
+
/** `chant graph --live --overlay --traffic "<level>"` (#2377) — predict the
|
|
163
|
+
* overlaid estate's behaviour at this traffic level and carry the result on
|
|
164
|
+
* the IR: each entity's block on `attrs._behaviour`, the report's meta (or a
|
|
165
|
+
* whole refusal report) on `meta._behaviour`.
|
|
166
|
+
*
|
|
167
|
+
* The flag is the trigger, and it has no default. The level is passed to the
|
|
168
|
+
* engine verbatim — chant does not parse it, default it or convert it
|
|
169
|
+
* (`../behaviour.ts`) — so without the flag there is no traffic level to
|
|
170
|
+
* predict *at*, nothing is asked, and neither key appears. A consumer reads
|
|
171
|
+
* that absence as "not looked", never as a refusal. */
|
|
172
|
+
traffic?: string;
|
|
162
173
|
/** `chant list --components` / `chant graph --components` — surface discovered
|
|
163
174
|
* `Component` declarations (#560) instead of/alongside lexicon resources. */
|
|
164
175
|
components?: boolean;
|
package/src/graph-ir.ts
CHANGED
|
@@ -295,6 +295,21 @@ export interface GraphIR {
|
|
|
295
295
|
nodes: IRNode[];
|
|
296
296
|
edges: IREdge[];
|
|
297
297
|
groups: IRGroups;
|
|
298
|
+
/**
|
|
299
|
+
* Graph-level facts that are about the whole read rather than any one node.
|
|
300
|
+
*
|
|
301
|
+
* Deliberately an open bag. A fact lands here when it has no node to sit on
|
|
302
|
+
* — `_behaviour` (#2377) carries the engine, its version, the traffic level
|
|
303
|
+
* the figures are *at*, and the estate's totals, or a whole refusal report
|
|
304
|
+
* when there was no engine to ask. None of that is a property of a single
|
|
305
|
+
* entity, and putting the refusal on nodes would mean writing it N times or
|
|
306
|
+
* dropping it, and dropping it is the one outcome the contract forbids
|
|
307
|
+
* (`./behaviour-overlay.ts`).
|
|
308
|
+
*
|
|
309
|
+
* Absent unless something filled it, and a consumer reads absence as "not
|
|
310
|
+
* looked" rather than as a negative finding.
|
|
311
|
+
*/
|
|
312
|
+
meta?: Record<string, unknown>;
|
|
298
313
|
/** Outputs this stack publishes for other stacks to import (#513). */
|
|
299
314
|
exports?: IRExport[];
|
|
300
315
|
/** Parameters this stack imports from other stacks. A viewer matches an import's
|