@intentius/chant 0.64.0 → 0.66.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/dist/behaviour-delta.d.ts +181 -0
- package/dist/behaviour-delta.d.ts.map +1 -0
- package/dist/behaviour-http.d.ts +106 -0
- package/dist/behaviour-http.d.ts.map +1 -0
- package/dist/behaviour-overlay.d.ts +61 -0
- package/dist/behaviour-overlay.d.ts.map +1 -0
- package/dist/behaviour.d.ts +1174 -0
- package/dist/behaviour.d.ts.map +1 -0
- package/dist/cli/handlers/scenario.d.ts.map +1 -1
- package/dist/identity.d.ts +28 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon.d.ts +45 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/scenario-eval.d.ts +23 -5
- package/dist/lifecycle/scenario-eval.d.ts.map +1 -1
- package/dist/lifecycle/scenario.d.ts +45 -3
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/types.d.ts +17 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +42 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/predict-behaviour.d.ts +207 -0
- package/dist/op/activities/predict-behaviour.d.ts.map +1 -0
- package/dist/op/activities/reconcile.d.ts +86 -16
- package/dist/op/activities/reconcile.d.ts.map +1 -1
- package/dist/op/composites/behaviour-op.d.ts +57 -0
- package/dist/op/composites/behaviour-op.d.ts.map +1 -0
- package/dist/op/composites/index.d.ts +2 -0
- package/dist/op/composites/index.d.ts.map +1 -1
- package/dist/op/index.d.ts +2 -2
- package/dist/op/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/behaviour-delta.test.ts +331 -0
- package/src/behaviour-delta.ts +564 -0
- package/src/behaviour-http.test.ts +456 -0
- package/src/behaviour-http.ts +252 -0
- package/src/behaviour-overlay.test.ts +149 -0
- package/src/behaviour-overlay.ts +76 -0
- package/src/behaviour.test.ts +2011 -0
- package/src/behaviour.ts +2127 -0
- package/src/cli/handlers/scenario.test.ts +108 -0
- package/src/cli/handlers/scenario.ts +63 -16
- package/src/fold/subset-doc-parity.test.ts +60 -0
- package/src/identity.ts +31 -2
- package/src/index.ts +3 -0
- package/src/lexicon.ts +46 -0
- package/src/lifecycle/scenario-cost.test.ts +183 -0
- package/src/lifecycle/scenario-eval.ts +133 -6
- package/src/lifecycle/scenario.ts +72 -4
- package/src/lifecycle/types.ts +17 -0
- package/src/lint/rules/op/ops012-activity-contract.test.ts +31 -0
- package/src/op/activities/activity-contracts.ts +49 -0
- package/src/op/activities/index.ts +24 -0
- package/src/op/activities/predict-behaviour.test.ts +255 -0
- package/src/op/activities/predict-behaviour.ts +468 -0
- package/src/op/activities/reconcile.test.ts +84 -0
- package/src/op/activities/reconcile.ts +97 -24
- package/src/op/activity-contract-registry.test.ts +3 -0
- package/src/op/composites/behaviour-op.test.ts +56 -0
- package/src/op/composites/behaviour-op.ts +99 -0
- package/src/op/composites/index.ts +2 -0
- package/src/op/index.ts +2 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The predicted delta between two behaviour results, and how it is shown
|
|
3
|
+
* (#2358, contract #2356).
|
|
4
|
+
*
|
|
5
|
+
* `behaviour.ts` ships the invariant and deliberately not the presentation:
|
|
6
|
+
* {@link compareFigures} says on which axes two figures fail to be a delta of
|
|
7
|
+
* like things, and binds every consumer to mark such a pair wherever it is
|
|
8
|
+
* shown. This module is the consumer. It takes the base side and the head
|
|
9
|
+
* side of a pull request as two {@link BehaviourResult}s and produces one
|
|
10
|
+
* finding, under four rules that are the contract's and #2358's rather than
|
|
11
|
+
* this file's:
|
|
12
|
+
*
|
|
13
|
+
* 1. A whole-run refusal on either side is a finding that says **no
|
|
14
|
+
* prediction**, with the refusal's own text and remedy. Never a delta and
|
|
15
|
+
* never a partial one: the five refusal causes are the engine being
|
|
16
|
+
* absent, unreachable, out of credit, over quota, or a credential in the
|
|
17
|
+
* request, and none of them is a statement about the estate.
|
|
18
|
+
* 2. A delta is computed only over entities predicted on **both** sides. An
|
|
19
|
+
* entity declined on either side — `unpredicted`, whatever the reason —
|
|
20
|
+
* is a row in the finding saying so, not an absent row and not a zero. An
|
|
21
|
+
* estate that gained an unmapped kind between the two runs did not gain a
|
|
22
|
+
* cost, and a finding that read it as one would be the faked number the
|
|
23
|
+
* epic forbids.
|
|
24
|
+
* 3. Before any two figures are differenced, {@link compareFigures} runs. A
|
|
25
|
+
* non-empty mismatch set means the pair is **marked** with every label in
|
|
26
|
+
* it, in {@link FIGURE_MISMATCHES}' display order, and no number is
|
|
27
|
+
* subtracted. `modeled` minus `validated` is not a change in the estate.
|
|
28
|
+
* 4. Every figure shown carries its provenance — engine, version, tolerance,
|
|
29
|
+
* basis — and a rate is rendered as a rate: so much per hour, at the
|
|
30
|
+
* stated traffic level, never an amount.
|
|
31
|
+
*
|
|
32
|
+
* Resilience has a fifth rule, from #2360's third comment: a verdict is
|
|
33
|
+
* computed over the graph the engine was handed, and the declared path hands
|
|
34
|
+
* over reference edges with no containment. When either side's
|
|
35
|
+
* `meta.edgeCoverage` is `partial` or `unknown`, the two verdicts were reached
|
|
36
|
+
* over graphs of different completeness, and the finding shows each side's
|
|
37
|
+
* verdict and says why they are not compared rather than drawing an arrow
|
|
38
|
+
* between them.
|
|
39
|
+
*
|
|
40
|
+
* Pure: no I/O, no clock, no environment. The Op activity that posts the
|
|
41
|
+
* result (`./op/activities/predict-behaviour.ts`) is where the sides come
|
|
42
|
+
* from.
|
|
43
|
+
*/
|
|
44
|
+
import { type BehaviourProvenance, type BehaviourRefusal, type BehaviourReportMeta, type BehaviourResult, type FigureMismatch, type PredictedBehaviour, type PredictedRate, type UnpredictedEntity } from "./behaviour.js";
|
|
45
|
+
/**
|
|
46
|
+
* Hold a {@link BehaviourResult} to the contract, on arrival.
|
|
47
|
+
*
|
|
48
|
+
* `behaviourReport` checks a report as the ordinary route builds it, and its
|
|
49
|
+
* own doc says plainly that this is a check and not a proof: the report type
|
|
50
|
+
* is a plain interface, `isBehaviourResult` accepts a hand-built one, and a
|
|
51
|
+
* lexicon calling the builder with `entityNames: Object.keys(entities)`
|
|
52
|
+
* self-certifies. A consumer that needs the guarantee runs this instead, with
|
|
53
|
+
* the names it asked about, and the two consumers that need it are the delta
|
|
54
|
+
* (each side arrives from a plugin) and a scenario's fixture (the block
|
|
55
|
+
* arrives from JSON on disk).
|
|
56
|
+
*
|
|
57
|
+
* Throws, naming what is wrong. The refusals are the builder's, applied to a
|
|
58
|
+
* result rather than to its parts: an entity in neither map or in both, a
|
|
59
|
+
* figure for a name nobody asked about, a block priced at a level the run did
|
|
60
|
+
* not ask for, an unpredicted reason outside the closed set, an edge-coverage
|
|
61
|
+
* claim that names no gap, and any block failing `validateBehaviourBlock`. A
|
|
62
|
+
* refusal arm is held to having a legal cause and a non-empty reason and
|
|
63
|
+
* remedy, since those two strings are what a consumer prints.
|
|
64
|
+
*/
|
|
65
|
+
export declare function validateBehaviourResult(result: unknown, askedFor: readonly string[]): BehaviourResult;
|
|
66
|
+
/** One side of the delta: the result, and what to call it in the finding. */
|
|
67
|
+
export interface BehaviourDeltaSide {
|
|
68
|
+
/** `base` or `head`, or whatever the caller calls the two sides. */
|
|
69
|
+
label: string;
|
|
70
|
+
/** What the side was predicted from — a branch, a ref, a pull request. Free text. */
|
|
71
|
+
ref?: string;
|
|
72
|
+
result: BehaviourResult;
|
|
73
|
+
}
|
|
74
|
+
/** Why a row carries no plain difference. */
|
|
75
|
+
export type BehaviourDeltaRowKind =
|
|
76
|
+
/** Predicted on both sides and comparable: `deltaPerHour` is a difference in the estate. */
|
|
77
|
+
"comparable"
|
|
78
|
+
/** Predicted on both sides and not a delta of like things: `mismatches` says on which axes. */
|
|
79
|
+
| "marked"
|
|
80
|
+
/** Declined on one side or both: `baseDeclined`/`headDeclined` say why. */
|
|
81
|
+
| "declined"
|
|
82
|
+
/** Present on the base side only — removed by the change. */
|
|
83
|
+
| "only-base"
|
|
84
|
+
/** Present on the head side only — added by the change. */
|
|
85
|
+
| "only-head";
|
|
86
|
+
/** One entity's row in the finding. */
|
|
87
|
+
export interface BehaviourDeltaRow {
|
|
88
|
+
name: string;
|
|
89
|
+
/** Declared entity type, when either side knows it. */
|
|
90
|
+
type?: string;
|
|
91
|
+
kind: BehaviourDeltaRowKind;
|
|
92
|
+
base?: PredictedBehaviour;
|
|
93
|
+
head?: PredictedBehaviour;
|
|
94
|
+
baseDeclined?: UnpredictedEntity;
|
|
95
|
+
headDeclined?: UnpredictedEntity;
|
|
96
|
+
/** Every axis the pair disagrees on, in display order. Non-empty exactly when `kind` is `marked`. */
|
|
97
|
+
mismatches?: FigureMismatch[];
|
|
98
|
+
/** `head.cost.perHour - base.cost.perHour`, present exactly when `kind` is `comparable`. */
|
|
99
|
+
deltaPerHour?: number;
|
|
100
|
+
/** The currency both comparable figures share. */
|
|
101
|
+
currency?: string;
|
|
102
|
+
}
|
|
103
|
+
/** chant's own sum of the comparable rows' deltas, per currency, labelled as such wherever it is shown. */
|
|
104
|
+
export interface ComparableDeltaSum {
|
|
105
|
+
currency: string;
|
|
106
|
+
perHour: number;
|
|
107
|
+
/** How many comparable pairs the sum is over. */
|
|
108
|
+
pairs: number;
|
|
109
|
+
}
|
|
110
|
+
/** A finding with figures on both sides. */
|
|
111
|
+
export interface BehaviourDeltaReport {
|
|
112
|
+
kind: "delta";
|
|
113
|
+
base: {
|
|
114
|
+
label: string;
|
|
115
|
+
ref?: string;
|
|
116
|
+
meta: BehaviourReportMeta;
|
|
117
|
+
};
|
|
118
|
+
head: {
|
|
119
|
+
label: string;
|
|
120
|
+
ref?: string;
|
|
121
|
+
meta: BehaviourReportMeta;
|
|
122
|
+
};
|
|
123
|
+
/** Every entity either side named, one row each, sorted by name. */
|
|
124
|
+
rows: BehaviourDeltaRow[];
|
|
125
|
+
/** The comparable rows' deltas summed by chant, per currency. Empty when no row is comparable. */
|
|
126
|
+
sums: ComparableDeltaSum[];
|
|
127
|
+
/**
|
|
128
|
+
* Whether the two sides' resilience verdicts were reached over graphs of the
|
|
129
|
+
* same completeness — both `meta.edgeCoverage.verdict === "complete"`. When
|
|
130
|
+
* false the finding shows each verdict and does not compare them.
|
|
131
|
+
*/
|
|
132
|
+
resilienceComparable: boolean;
|
|
133
|
+
}
|
|
134
|
+
/** A finding with no figures, because one side or both refused. */
|
|
135
|
+
export interface BehaviourDeltaRefused {
|
|
136
|
+
kind: "no-prediction";
|
|
137
|
+
base: {
|
|
138
|
+
label: string;
|
|
139
|
+
ref?: string;
|
|
140
|
+
refusal?: BehaviourRefusal;
|
|
141
|
+
};
|
|
142
|
+
head: {
|
|
143
|
+
label: string;
|
|
144
|
+
ref?: string;
|
|
145
|
+
refusal?: BehaviourRefusal;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export type BehaviourDelta = BehaviourDeltaReport | BehaviourDeltaRefused;
|
|
149
|
+
/**
|
|
150
|
+
* Compute the delta. Pure, and never subtracts two figures it has not first
|
|
151
|
+
* put through {@link compareFigures}.
|
|
152
|
+
*/
|
|
153
|
+
export declare function behaviourDelta(base: BehaviourDeltaSide, head: BehaviourDeltaSide): BehaviourDelta;
|
|
154
|
+
/** What the rendered finding is about — the words in its heading. */
|
|
155
|
+
export interface BehaviourFindingContext {
|
|
156
|
+
/** The environment the prediction is for. */
|
|
157
|
+
env: string;
|
|
158
|
+
/** The Op that produced it, named in the heading so two Ops' findings read apart. */
|
|
159
|
+
op: string;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Four significant fractional digits at most, trailing zeros dropped. A rate
|
|
163
|
+
* of `0.272` renders as `0.272`, of `3` as `3`, and of `0.00230000001` as
|
|
164
|
+
* `0.0023`. No locale: the finding is the same bytes on every runner.
|
|
165
|
+
*/
|
|
166
|
+
export declare function formatPerHour(n: number): string;
|
|
167
|
+
/** A rate as a rate: `0.272 USD/hour`, never an amount. */
|
|
168
|
+
export declare function renderRate(rate: PredictedRate): string;
|
|
169
|
+
/** `acme-sim 1.4.2 · ±15% · modeled` — the four provenance fields, always together. */
|
|
170
|
+
export declare function renderProvenance(p: BehaviourProvenance): string;
|
|
171
|
+
/**
|
|
172
|
+
* The finding as Markdown, for a pull-request comment or a merge-request
|
|
173
|
+
* note. Deterministic for a given delta.
|
|
174
|
+
*
|
|
175
|
+
* The first paragraph says what the numbers are, before any of them appear:
|
|
176
|
+
* a modeled rate for one hypothetical hour, at the level the run was asked
|
|
177
|
+
* for, from a named engine at a stated tolerance. The paragraph is not
|
|
178
|
+
* decoration; it is rule 1 of the contract applied to prose.
|
|
179
|
+
*/
|
|
180
|
+
export declare function renderBehaviourFinding(delta: BehaviourDelta, ctx: BehaviourFindingContext): string;
|
|
181
|
+
//# sourceMappingURL=behaviour-delta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"behaviour-delta.d.ts","sourceRoot":"","sources":["../src/behaviour-delta.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first engine adapter (#2359): a {@link BehaviourTransport} that dials a
|
|
3
|
+
* URL with a bearer token, and maps what the wire says onto the contract's
|
|
4
|
+
* refusals.
|
|
5
|
+
*
|
|
6
|
+
* Unnamed on purpose. Nothing here is a vendor's API; it is the shape any
|
|
7
|
+
* metered HTTP engine has — an address, an account, a request body, and four
|
|
8
|
+
* ways the account or the wire can say no — so a second engine behind the
|
|
9
|
+
* same shape is a second address and a second token, not a second adapter.
|
|
10
|
+
* What this file knows about the engine is that it accepts a JSON body by
|
|
11
|
+
* `POST`, answers with a JSON body, and uses the HTTP status the way HTTP
|
|
12
|
+
* says to: `402` for an account with nothing left on it, `429` for a limit
|
|
13
|
+
* that is spent, `401`/`403` for a token it does not accept.
|
|
14
|
+
*
|
|
15
|
+
* ## The token is on the wire and nowhere else
|
|
16
|
+
*
|
|
17
|
+
* Rule 3 of the contract — the engine is never handed a credential — is about
|
|
18
|
+
* the request body, and `screenBehaviourRequest` has already walked the body
|
|
19
|
+
* by the time a transport exists. The bearer token here is a different thing:
|
|
20
|
+
* it is how the engine knows whose account to bill, it travels in the
|
|
21
|
+
* `authorization` header, and this module is the only code that holds its
|
|
22
|
+
* value. Four things follow, and each has a test:
|
|
23
|
+
*
|
|
24
|
+
* - it is resolved from {@link behaviourTokenFrom}'s chain, never from the
|
|
25
|
+
* address chain, so no message that prints the address can print it;
|
|
26
|
+
* - it is never interpolated into a refusal, a `detail`, or an error — the
|
|
27
|
+
* variable's *name* is what a refusal carries;
|
|
28
|
+
* - whatever the engine writes back is passed through {@link concealing}
|
|
29
|
+
* before it becomes a `detail`, because an engine that echoes its request
|
|
30
|
+
* headers into an error page would otherwise put the token in a
|
|
31
|
+
* merge-request comment (#2358 posts refusals publicly);
|
|
32
|
+
* - the request is sent with `redirect: "error"`, so a `3xx` from the
|
|
33
|
+
* address named is a refusal rather than a second request carrying the
|
|
34
|
+
* header to whatever host the redirect names.
|
|
35
|
+
*
|
|
36
|
+
* ## No token, nothing sent
|
|
37
|
+
*
|
|
38
|
+
* This adapter is for an engine that bills an account: `engine-out-of-credit`
|
|
39
|
+
* and `engine-over-quota` are statements about an account, and an engine
|
|
40
|
+
* with no account has neither. So a token is required, and a missing one is
|
|
41
|
+
* refused before anything is sent — {@link noBehaviourTokenRefusal}, naming
|
|
42
|
+
* the chain — rather than sent bare and reported as whatever the engine's
|
|
43
|
+
* 401 page said. An engine that needs no token is not this adapter's, and
|
|
44
|
+
* the address chain still reaches it through a command on `PATH`.
|
|
45
|
+
*
|
|
46
|
+
* ## What the wire says, and what it becomes
|
|
47
|
+
*
|
|
48
|
+
* | Wire | Cause | Why |
|
|
49
|
+
* |---|---|---|
|
|
50
|
+
* | `2xx` | — | the body is the answer; the lexicon validates it |
|
|
51
|
+
* | `401`, `403` | `no-engine` | the token was rejected; the remedy is to set the variable |
|
|
52
|
+
* | `402` | `engine-out-of-credit` | the address answered; the account is empty |
|
|
53
|
+
* | `429` | `engine-over-quota` | the address answered; a limit is spent; `retry-after` is echoed |
|
|
54
|
+
* | any other status | `engine-unreachable` | the engine did not answer the question — a `5xx`, a `404`, a `3xx` |
|
|
55
|
+
* | `fetch` threw | `engine-unreachable` | connection refused, no such host, TLS, or the timeout |
|
|
56
|
+
*
|
|
57
|
+
* `4xx` outside the three named is deliberately `engine-unreachable` rather
|
|
58
|
+
* than a fifth cause. The contract's four causes are four remedies, and "the
|
|
59
|
+
* engine rejected this request as malformed" has no remedy an operator can
|
|
60
|
+
* apply — it is a bug on one side of the wire or the other, and the detail
|
|
61
|
+
* names the status so whoever reads it can tell which side.
|
|
62
|
+
*/
|
|
63
|
+
import { type BehaviourEngineEndpoint, type BehaviourEngineToken, type BehaviourRefusalReport, type BehaviourTransport } from "./behaviour.js";
|
|
64
|
+
/** What {@link httpBehaviourTransport} can be handed instead of the process's own. */
|
|
65
|
+
export interface HttpBehaviourTransportDeps {
|
|
66
|
+
/** The `fetch` to send with. Defaults to the global, and a test hands in a fake. */
|
|
67
|
+
fetch?: typeof fetch;
|
|
68
|
+
/** How long the engine has before it is treated as unreachable. */
|
|
69
|
+
timeoutMs?: number;
|
|
70
|
+
}
|
|
71
|
+
/** The default deadline: the same one augur's command transport gives a child. */
|
|
72
|
+
export declare const HTTP_BEHAVIOUR_TIMEOUT_MS = 30000;
|
|
73
|
+
/** True when an address is one this adapter dials. `grpc://` and friends are not. */
|
|
74
|
+
export declare function isHttpBehaviourAddress(value: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Every occurrence of the token's value replaced, in text that came from the
|
|
77
|
+
* engine or from an error. Exported so a test can assert on the one function
|
|
78
|
+
* every `detail` passes through.
|
|
79
|
+
*
|
|
80
|
+
* `split`/`join` rather than a `RegExp`, so a token containing `.` or `+` is
|
|
81
|
+
* matched as itself. Nothing shorter than four characters is concealed: a
|
|
82
|
+
* one-letter "token" would blank every occurrence of that letter, which is a
|
|
83
|
+
* detail nobody can read and a false sense that something was protected.
|
|
84
|
+
*/
|
|
85
|
+
export declare function concealing(token: BehaviourEngineToken, text: string): string;
|
|
86
|
+
/**
|
|
87
|
+
* The refusal an HTTP status earns, or `undefined` for a status that carries
|
|
88
|
+
* an answer. Pure and exported so the whole table in the module doc is one
|
|
89
|
+
* function a test can walk.
|
|
90
|
+
*
|
|
91
|
+
* `detail` is the first non-empty line of the response body, already
|
|
92
|
+
* concealed; this adds the status in front of it so a `detail` never reads as
|
|
93
|
+
* the engine's prose alone.
|
|
94
|
+
*/
|
|
95
|
+
export declare function httpStatusRefusal(lexicon: string, endpoint: BehaviourEngineEndpoint, token: BehaviourEngineToken, status: number, detail: string, retryAfter?: string): BehaviourRefusalReport | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* Build the transport for one lexicon against one URL.
|
|
98
|
+
*
|
|
99
|
+
* The token is resolved here, once, from `env` — never from `process.env`
|
|
100
|
+
* unless that is what was passed — so a test can drive the whole wire with an
|
|
101
|
+
* environment of its own. A missing token makes a transport whose every
|
|
102
|
+
* `send` refuses by name and sends nothing; see the module doc for why that is
|
|
103
|
+
* the honest shape for an adapter whose engine bills an account.
|
|
104
|
+
*/
|
|
105
|
+
export declare function httpBehaviourTransport(lexicon: string, endpoint: BehaviourEngineEndpoint, env: Record<string, string | undefined>, deps?: HttpBehaviourTransportDeps): BehaviourTransport;
|
|
106
|
+
//# sourceMappingURL=behaviour-http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"behaviour-http.d.ts","sourceRoot":"","sources":["../src/behaviour-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAEH,OAAO,EAKL,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EAExB,MAAM,aAAa,CAAC;AAGrB,sFAAsF;AACtF,MAAM,WAAW,0BAA0B;IACzC,oFAAoF;IACpF,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,kFAAkF;AAClF,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAKhD,qFAAqF;AACrF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,uBAAuB,EACjC,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,sBAAsB,GAAG,SAAS,CAgCpC;AAQD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,uBAAuB,EACjC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,IAAI,GAAE,0BAA+B,GACpC,kBAAkB,CAoDpB"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A behaviour result, in the shape the overlay carries it (#2360, epic #2355).
|
|
3
|
+
*
|
|
4
|
+
* behold reads a prediction off `chant graph --live --overlay`'s IR and never
|
|
5
|
+
* calls an engine itself: one block per entity on `attrs._behaviour`, the
|
|
6
|
+
* channel every other live fact rides on (`_status`, `_release`, `_carve`),
|
|
7
|
+
* and the graph-level half on `meta._behaviour`. Its reader
|
|
8
|
+
* (`behold/src/behaviour.ts`, `validateBehaviourMeta`) takes `meta._behaviour`
|
|
9
|
+
* as `{ engine?, version?, at?, total?, refusal? }` and short-circuits on
|
|
10
|
+
* `refusal`: a refusal is present *instead of* the engine, and a meta with
|
|
11
|
+
* neither is dropped as "meta.engine missing".
|
|
12
|
+
*
|
|
13
|
+
* That last sentence is the whole reason this module exists. What goes on
|
|
14
|
+
* `meta._behaviour` for a refusal is the **entire** {@link BehaviourRefusalReport}
|
|
15
|
+
* — `{ behaviour: "v1", refusal: { cause, reason, remedy, source? } }` — and
|
|
16
|
+
* not the bare {@link BehaviourRefusal} inside it. A bare refusal has `reason`
|
|
17
|
+
* and `remedy` at its top level and no `refusal` key, so behold's reader finds
|
|
18
|
+
* no refusal, then finds no engine, and drops the meta with a diagnostic that
|
|
19
|
+
* names the wrong thing. The contract doc said the wrong type once
|
|
20
|
+
* (#2360's second review comment); this is written against the reader.
|
|
21
|
+
*
|
|
22
|
+
* For a report, `meta._behaviour` is the report's own `meta`: engine, version,
|
|
23
|
+
* `at`, an optional `total`, and `edgeCoverage`, which behold's reader ignores
|
|
24
|
+
* and a consumer that wants to know what a resilience verdict was computed
|
|
25
|
+
* over reads. Each entity's block goes on its node verbatim — the contract's
|
|
26
|
+
* {@link PredictedBehaviour} is field for field the block behold validates.
|
|
27
|
+
*
|
|
28
|
+
* Pure. Nothing here calls an engine; the result is whatever a lexicon's
|
|
29
|
+
* `predictBehaviour` returned.
|
|
30
|
+
*/
|
|
31
|
+
import { type BehaviourRefusalReport, type BehaviourReportMeta, type BehaviourResult, type PredictedBehaviour } from "./behaviour.js";
|
|
32
|
+
/** The attribute a behaviour block rides on, on a node and on the graph's meta. */
|
|
33
|
+
export declare const BEHAVIOUR_OVERLAY_ATTR = "_behaviour";
|
|
34
|
+
/** What `meta._behaviour` holds: the report's meta, or the whole refusal report. */
|
|
35
|
+
export type BehaviourOverlayMeta = BehaviourReportMeta | BehaviourRefusalReport;
|
|
36
|
+
/** A behaviour result projected onto the overlay's two channels. */
|
|
37
|
+
export interface BehaviourOverlay {
|
|
38
|
+
/** Goes on the IR's `meta`, keyed {@link BEHAVIOUR_OVERLAY_ATTR}. */
|
|
39
|
+
meta: {
|
|
40
|
+
[BEHAVIOUR_OVERLAY_ATTR]: BehaviourOverlayMeta;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* One entry per priced entity, keyed by entity name: what goes on that
|
|
44
|
+
* node's `attrs`, keyed {@link BEHAVIOUR_OVERLAY_ATTR}. Empty for a refusal,
|
|
45
|
+
* because a refusal is present instead of every figure, and empty for an
|
|
46
|
+
* entity that was `unpredicted` — an unpriced node carries no block rather
|
|
47
|
+
* than a block full of zeroes.
|
|
48
|
+
*/
|
|
49
|
+
attrs: Record<string, {
|
|
50
|
+
[BEHAVIOUR_OVERLAY_ATTR]: PredictedBehaviour;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Project a result onto the overlay's channels.
|
|
55
|
+
*
|
|
56
|
+
* A refusal keeps its envelope. behold reads `meta._behaviour.refusal`, and
|
|
57
|
+
* the envelope is also what makes the value self-describing to anything else
|
|
58
|
+
* reading the IR: `behaviour: "v1"` says which contract the refusal is in.
|
|
59
|
+
*/
|
|
60
|
+
export declare function behaviourOverlay(result: BehaviourResult): BehaviourOverlay;
|
|
61
|
+
//# sourceMappingURL=behaviour-overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"behaviour-overlay.d.ts","sourceRoot":"","sources":["../src/behaviour-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAErB,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD,oFAAoF;AACpF,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GAAG,sBAAsB,CAAC;AAEhF,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,IAAI,EAAE;QAAE,CAAC,sBAAsB,CAAC,EAAE,oBAAoB,CAAA;KAAE,CAAC;IACzD;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,sBAAsB,CAAC,EAAE,kBAAkB,CAAA;KAAE,CAAC,CAAC;CACzE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAS1E"}
|