@suss/checker-intent 0.33.1 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -31
- package/dist/index.cjs +626 -79
- package/dist/index.d.cts +170 -30
- package/dist/index.d.ts +170 -30
- package/dist/index.js +608 -65
- package/package.json +6 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,87 @@
|
|
|
1
|
-
import { BehavioralSummary } from '@suss/behavioral-ir';
|
|
2
|
-
import { IntentFinding, IntentSummary } from '@suss/intent-ir';
|
|
1
|
+
import { BehavioralSummary, ReceiverClass, TypeShape, ProvenanceEntry, BoundaryGuard, Interaction, Transition, Deployment } from '@suss/behavioral-ir';
|
|
2
|
+
import { CoveringTestSpelling, IntentFinding, IntentSummary, IntentEffect, IntentOutcome } from '@suss/intent-ir';
|
|
3
3
|
export { IntentFinding } from '@suss/intent-ir';
|
|
4
|
-
import { SuppressionRule, Semantics } from '@suss/ir-core';
|
|
4
|
+
import { Relation, BoundaryBinding, SuppressionRule, Semantics } from '@suss/ir-core';
|
|
5
|
+
import { CallFacts, ReachTarget } from '@suss/checker';
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Checks the tests a PRD scenario lists under `coveredBy`: that each
|
|
9
|
+
* one exists, that it runs, and that its calls reach what the scenario
|
|
10
|
+
* is about without going through a mock.
|
|
11
|
+
*
|
|
12
|
+
* Turning a spelled test or subject into summaries is the caller's
|
|
13
|
+
* job, through the resolver its commands already use, and arrives here
|
|
14
|
+
* as a `CoveringTestLookup`. The reach question is asked once for every
|
|
15
|
+
* test, through the call facts in @suss/checker, rather than once per
|
|
16
|
+
* scenario. It is asked a second time without honouring mocks only for
|
|
17
|
+
* the tests that missed, to tell a test that never reaches its subject
|
|
18
|
+
* from one that reaches it only through something it replaced.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
type FoundTest = {
|
|
22
|
+
found: true;
|
|
23
|
+
unit: BehavioralSummary;
|
|
24
|
+
} | {
|
|
25
|
+
found: false;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
type FoundSubject = {
|
|
29
|
+
found: true;
|
|
30
|
+
target: ReachTarget;
|
|
31
|
+
label: string;
|
|
32
|
+
/**
|
|
33
|
+
* The classes the subject is, when it was spelled as a class or as
|
|
34
|
+
* a file a class is written in. A call a test sends to one of them
|
|
35
|
+
* reaches the subject. Empty for a single unit or a boundary.
|
|
36
|
+
*/
|
|
37
|
+
classes?: ReadonlyArray<ReceiverClass>;
|
|
38
|
+
} | {
|
|
39
|
+
found: false;
|
|
40
|
+
message: string;
|
|
41
|
+
};
|
|
42
|
+
/** How the check finds the summaries a PRD's spellings mean. */
|
|
43
|
+
interface CoveringTestLookup {
|
|
44
|
+
/** The call facts over the summaries being checked. */
|
|
45
|
+
facts: CallFacts;
|
|
46
|
+
/** The test unit a `coveredBy` entry means, or why there is none. */
|
|
47
|
+
test(spelled: CoveringTestSpelling): FoundTest;
|
|
48
|
+
/** What a boundary or unit spelling means, the way `suss ask` reads it. */
|
|
49
|
+
subject(spelledAs: string): FoundSubject;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Compares the intent a team wrote with the summaries derived from code.
|
|
54
|
+
*
|
|
55
|
+
* This is kept apart from @suss/checker because the inputs and outputs
|
|
56
|
+
* differ: one side is an `IntentSummary`, and a finding says the code
|
|
57
|
+
* does not do what the team declared. It takes only the call facts from
|
|
58
|
+
* @suss/checker, to see whether a test reaches what a scenario is about.
|
|
59
|
+
*
|
|
60
|
+
* A boundary intent is paired with the code at its boundary key. A PRD
|
|
61
|
+
* has its scenario links resolved and its covering tests checked. Which
|
|
62
|
+
* severity each finding gets, and why a finding against inferred intent
|
|
63
|
+
* drops a level, is in the package design notes.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/** A code transition's terminal, reduced to the dimensions intent compares. */
|
|
67
|
+
interface CodeOutcome {
|
|
68
|
+
kind: "response" | "return" | "throw" | "exit";
|
|
69
|
+
/** A response's status, or the code an `exit` output ends the process with. */
|
|
70
|
+
status: number | null;
|
|
71
|
+
body: TypeShape | null;
|
|
72
|
+
errorType: string | null;
|
|
73
|
+
/**
|
|
74
|
+
* The literal a return hands back when the unit's return becomes the
|
|
75
|
+
* process's exit code, so `exits: 1` can match `return 1` there.
|
|
76
|
+
*/
|
|
77
|
+
exitCode: number | null;
|
|
78
|
+
/** What the transition that ends this way did at other boundaries. */
|
|
79
|
+
effects: CodeEffect[];
|
|
80
|
+
/** The boundaries the branch leading here turned on. */
|
|
81
|
+
turnsOn: BoundaryGuard[];
|
|
82
|
+
/** The line the transition starts on, for a finding about one transition. */
|
|
83
|
+
line: number;
|
|
84
|
+
}
|
|
6
85
|
/** A boundary intent that was paired and compared against code. */
|
|
7
86
|
interface CheckedBoundaryIntent {
|
|
8
87
|
kind: "boundary";
|
|
@@ -22,34 +101,46 @@ interface CheckedPrd {
|
|
|
22
101
|
scenarios: number;
|
|
23
102
|
/** Scenarios whose every link resolved to a declared outcome (had >=1 link). */
|
|
24
103
|
resolved: number;
|
|
25
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Scenarios whose every covering test exists, runs and reaches its
|
|
106
|
+
* subject. Zero when the check was given no way to look tests up.
|
|
107
|
+
*/
|
|
108
|
+
covered: number;
|
|
109
|
+
/** Scenarios with neither a link nor a covering test. */
|
|
26
110
|
unlinked: number;
|
|
27
111
|
}
|
|
28
112
|
/**
|
|
29
113
|
* A declared intent that was compared, either a boundary intent paired
|
|
30
114
|
* against code or a PRD whose scenario links were resolved. Discriminated
|
|
31
|
-
* on `kind
|
|
32
|
-
* third doc kind (workflow) ships it extends this union.
|
|
115
|
+
* on `kind`, so a caller renders both from one list.
|
|
33
116
|
*/
|
|
34
117
|
type CheckedIntent = CheckedBoundaryIntent | CheckedPrd;
|
|
35
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Intent that was loaded and not compared, and why. `unkeyable` is a
|
|
120
|
+
* whole document whose boundary cannot be paired. `unreadValue` is one
|
|
121
|
+
* `from` source the code gave no answer to compare against.
|
|
122
|
+
* `unfollowedCall` is a covering test that never reaches its subject
|
|
123
|
+
* through the calls suss followed, and makes calls suss did not follow.
|
|
124
|
+
*/
|
|
36
125
|
interface UncheckedIntent {
|
|
37
126
|
/** The intent doc's `name` (boundary) or `title` (prd). */
|
|
38
127
|
intent: string;
|
|
39
|
-
reason: "unkeyable";
|
|
128
|
+
reason: "unkeyable" | "unreadValue" | "unfollowedCall";
|
|
129
|
+
/** The outcome the claim is on, for a claim narrower than the document. */
|
|
130
|
+
outcomeId?: string;
|
|
131
|
+
/** The scenario the claim is on, by its title or its position, for a covering test. */
|
|
132
|
+
scenario?: string;
|
|
133
|
+
/** The covering test, as the scenario lists it. */
|
|
134
|
+
coveredBy?: string;
|
|
40
135
|
/** Human-readable explanation, render-ready. */
|
|
41
136
|
detail: string;
|
|
42
137
|
}
|
|
43
138
|
/**
|
|
44
|
-
* The
|
|
45
|
-
* checker's
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* checker compared: boundary intents and PRDs both land in `checked`
|
|
50
|
-
* (a discriminated union). `unchecked` is reserved for intent that
|
|
51
|
-
* couldn't be compared at all (an unkeyable boundary), never silently
|
|
52
|
-
* dropped.
|
|
139
|
+
* The result of an intent-agreement pass, laid out like the behavioural
|
|
140
|
+
* checker's `checkAll` result. `findings` are what to fix. `checked`
|
|
141
|
+
* lists every declared intent that was compared, boundary intents and
|
|
142
|
+
* PRDs alike. `unchecked` lists intent that could not be compared at
|
|
143
|
+
* all, such as an unkeyable boundary, so nothing is dropped silently.
|
|
53
144
|
*/
|
|
54
145
|
interface CheckIntentResult {
|
|
55
146
|
findings: IntentFinding[];
|
|
@@ -59,33 +150,82 @@ interface CheckIntentResult {
|
|
|
59
150
|
/**
|
|
60
151
|
* Compare every loaded intent doc against what's known. Boundary intents
|
|
61
152
|
* pair against the code summaries sharing their boundary key; PRDs resolve
|
|
62
|
-
* each scenario's link against the loaded boundary intents
|
|
63
|
-
*
|
|
64
|
-
*
|
|
153
|
+
* each scenario's link against the loaded boundary intents, and check the
|
|
154
|
+
* tests it lists under `coveredBy` through `tests`. Without `tests`, those
|
|
155
|
+
* scenarios count as backed and their tests go unchecked. An unkeyable
|
|
156
|
+
* boundary is reported in `unchecked`, never silently dropped.
|
|
65
157
|
*
|
|
66
158
|
* Findings against `source: "inferred"` intent are downgraded one severity
|
|
67
159
|
* level: the intent describes what the code did when the inference ran, so
|
|
68
160
|
* a divergence is most likely a code change since, not an authoring error.
|
|
69
161
|
* Curation (`"inferred, curated"`) restores full severity.
|
|
70
162
|
*/
|
|
71
|
-
declare function checkIntentAgreement(intents: IntentSummary[], code: BehavioralSummary[]): CheckIntentResult;
|
|
163
|
+
declare function checkIntentAgreement(intents: IntentSummary[], code: BehavioralSummary[], tests?: CoveringTestLookup): CheckIntentResult;
|
|
72
164
|
/**
|
|
73
|
-
* Apply .sussignore rules to intent findings,
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* an intent finding
|
|
165
|
+
* Apply .sussignore rules to intent findings, through the same pipeline
|
|
166
|
+
* in @suss/ir-core that the behavioural checker's `applySuppressions`
|
|
167
|
+
* uses. A rule's `kind` matches the finding kind, and its `boundary`
|
|
168
|
+
* matches the finding's boundary key, exactly for `fn:` and `gql:` keys
|
|
169
|
+
* and path-normalized for REST. A rule's `scenario` matches the title
|
|
170
|
+
* of the PRD scenario the finding is about. A rule that gives
|
|
171
|
+
* `consumer` or `provider` never matches, since an intent finding has
|
|
172
|
+
* neither side.
|
|
80
173
|
*/
|
|
81
174
|
declare function applyIntentSuppressions(findings: IntentFinding[], rules: SuppressionRule[], opts?: {
|
|
82
175
|
keepHidden?: boolean;
|
|
83
176
|
}): IntentFinding[];
|
|
177
|
+
/**
|
|
178
|
+
* Whether an effect the code makes is the one the intent declared. The
|
|
179
|
+
* boundary is resolved with `namesBoundary`, the matcher that resolves
|
|
180
|
+
* what somebody types at `suss ask`, so a document and a question that
|
|
181
|
+
* spell a store the same way pick out the same one.
|
|
182
|
+
*/
|
|
183
|
+
declare function effectMatches(declared: IntentEffect, made: CodeEffect): boolean;
|
|
184
|
+
/** How a transition ends, in the terms intent states an ending in. */
|
|
185
|
+
type CodeEnding = Pick<CodeOutcome, "kind" | "status" | "body" | "errorType" | "exitCode">;
|
|
186
|
+
/**
|
|
187
|
+
* How a transition ends, or null for an ending intent has no word for,
|
|
188
|
+
* such as a render. A status the code computes has no literal, so it
|
|
189
|
+
* comes back null and matches no declared status. `returnIsExitCode`
|
|
190
|
+
* says the unit's return becomes the process's exit code.
|
|
191
|
+
*/
|
|
192
|
+
declare function endingOf(t: Transition, returnIsExitCode?: boolean): CodeEnding | null;
|
|
193
|
+
/** One verb and one boundary this transition reaches. */
|
|
194
|
+
interface CodeEffect {
|
|
195
|
+
does: Relation;
|
|
196
|
+
binding: BoundaryBinding;
|
|
197
|
+
/** How a report writes that boundary, for a message about it. */
|
|
198
|
+
label: string;
|
|
199
|
+
/** The columns the access states, empty when it states none. */
|
|
200
|
+
fields: string[];
|
|
201
|
+
/** What the access picks the item out by, empty when it states none. */
|
|
202
|
+
by: string[];
|
|
203
|
+
/** The shape of what a write put out, when the code says. */
|
|
204
|
+
payload?: TypeShape;
|
|
205
|
+
/** Where the values in this effect's slots came from, when the summary says. */
|
|
206
|
+
slots?: ProvenanceEntry[];
|
|
207
|
+
/** The line the transition that has this effect starts on. */
|
|
208
|
+
line?: number;
|
|
209
|
+
}
|
|
210
|
+
/** Every verb and boundary the transition reaches, grounded against the deployment. */
|
|
211
|
+
declare function codeEffectsOf(t: Transition, deployment: Deployment): CodeEffect[];
|
|
212
|
+
/**
|
|
213
|
+
* The fields an access states: a storage access's columns and key, or
|
|
214
|
+
* the variable a config read takes, which is a field of the runtime's
|
|
215
|
+
* contract. Nothing for any other class.
|
|
216
|
+
*/
|
|
217
|
+
declare function accessDetail(interaction: Interaction): {
|
|
218
|
+
fields: string[];
|
|
219
|
+
by: string[];
|
|
220
|
+
payload?: TypeShape;
|
|
221
|
+
};
|
|
222
|
+
/** Whether the code ends the way the intent says. The body is compared elsewhere. */
|
|
223
|
+
declare function outcomeMatches(intent: Pick<IntentOutcome, "kind" | "status" | "errorType">, code: CodeEnding): boolean;
|
|
84
224
|
/**
|
|
85
225
|
* What each protocol needs before an intent doc written against one of
|
|
86
226
|
* its boundaries can be paired, in the doc author's terms. The
|
|
87
|
-
* drafter
|
|
227
|
+
* drafter gives the same reason for a boundary it could not write.
|
|
88
228
|
*/
|
|
89
229
|
declare function whatWouldKeyIt(protocol: Semantics["name"]): string;
|
|
90
230
|
|
|
91
|
-
export { type CheckIntentResult, type CheckedBoundaryIntent, type CheckedIntent, type CheckedPrd, type UncheckedIntent, applyIntentSuppressions, checkIntentAgreement, whatWouldKeyIt };
|
|
231
|
+
export { type CheckIntentResult, type CheckedBoundaryIntent, type CheckedIntent, type CheckedPrd, type CodeEffect, type CodeEnding, type CoveringTestLookup, type FoundSubject, type FoundTest, type UncheckedIntent, accessDetail, applyIntentSuppressions, checkIntentAgreement, codeEffectsOf, effectMatches, endingOf, outcomeMatches, whatWouldKeyIt };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,87 @@
|
|
|
1
|
-
import { BehavioralSummary } from '@suss/behavioral-ir';
|
|
2
|
-
import { IntentFinding, IntentSummary } from '@suss/intent-ir';
|
|
1
|
+
import { BehavioralSummary, ReceiverClass, TypeShape, ProvenanceEntry, BoundaryGuard, Interaction, Transition, Deployment } from '@suss/behavioral-ir';
|
|
2
|
+
import { CoveringTestSpelling, IntentFinding, IntentSummary, IntentEffect, IntentOutcome } from '@suss/intent-ir';
|
|
3
3
|
export { IntentFinding } from '@suss/intent-ir';
|
|
4
|
-
import { SuppressionRule, Semantics } from '@suss/ir-core';
|
|
4
|
+
import { Relation, BoundaryBinding, SuppressionRule, Semantics } from '@suss/ir-core';
|
|
5
|
+
import { CallFacts, ReachTarget } from '@suss/checker';
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Checks the tests a PRD scenario lists under `coveredBy`: that each
|
|
9
|
+
* one exists, that it runs, and that its calls reach what the scenario
|
|
10
|
+
* is about without going through a mock.
|
|
11
|
+
*
|
|
12
|
+
* Turning a spelled test or subject into summaries is the caller's
|
|
13
|
+
* job, through the resolver its commands already use, and arrives here
|
|
14
|
+
* as a `CoveringTestLookup`. The reach question is asked once for every
|
|
15
|
+
* test, through the call facts in @suss/checker, rather than once per
|
|
16
|
+
* scenario. It is asked a second time without honouring mocks only for
|
|
17
|
+
* the tests that missed, to tell a test that never reaches its subject
|
|
18
|
+
* from one that reaches it only through something it replaced.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
type FoundTest = {
|
|
22
|
+
found: true;
|
|
23
|
+
unit: BehavioralSummary;
|
|
24
|
+
} | {
|
|
25
|
+
found: false;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
28
|
+
type FoundSubject = {
|
|
29
|
+
found: true;
|
|
30
|
+
target: ReachTarget;
|
|
31
|
+
label: string;
|
|
32
|
+
/**
|
|
33
|
+
* The classes the subject is, when it was spelled as a class or as
|
|
34
|
+
* a file a class is written in. A call a test sends to one of them
|
|
35
|
+
* reaches the subject. Empty for a single unit or a boundary.
|
|
36
|
+
*/
|
|
37
|
+
classes?: ReadonlyArray<ReceiverClass>;
|
|
38
|
+
} | {
|
|
39
|
+
found: false;
|
|
40
|
+
message: string;
|
|
41
|
+
};
|
|
42
|
+
/** How the check finds the summaries a PRD's spellings mean. */
|
|
43
|
+
interface CoveringTestLookup {
|
|
44
|
+
/** The call facts over the summaries being checked. */
|
|
45
|
+
facts: CallFacts;
|
|
46
|
+
/** The test unit a `coveredBy` entry means, or why there is none. */
|
|
47
|
+
test(spelled: CoveringTestSpelling): FoundTest;
|
|
48
|
+
/** What a boundary or unit spelling means, the way `suss ask` reads it. */
|
|
49
|
+
subject(spelledAs: string): FoundSubject;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Compares the intent a team wrote with the summaries derived from code.
|
|
54
|
+
*
|
|
55
|
+
* This is kept apart from @suss/checker because the inputs and outputs
|
|
56
|
+
* differ: one side is an `IntentSummary`, and a finding says the code
|
|
57
|
+
* does not do what the team declared. It takes only the call facts from
|
|
58
|
+
* @suss/checker, to see whether a test reaches what a scenario is about.
|
|
59
|
+
*
|
|
60
|
+
* A boundary intent is paired with the code at its boundary key. A PRD
|
|
61
|
+
* has its scenario links resolved and its covering tests checked. Which
|
|
62
|
+
* severity each finding gets, and why a finding against inferred intent
|
|
63
|
+
* drops a level, is in the package design notes.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/** A code transition's terminal, reduced to the dimensions intent compares. */
|
|
67
|
+
interface CodeOutcome {
|
|
68
|
+
kind: "response" | "return" | "throw" | "exit";
|
|
69
|
+
/** A response's status, or the code an `exit` output ends the process with. */
|
|
70
|
+
status: number | null;
|
|
71
|
+
body: TypeShape | null;
|
|
72
|
+
errorType: string | null;
|
|
73
|
+
/**
|
|
74
|
+
* The literal a return hands back when the unit's return becomes the
|
|
75
|
+
* process's exit code, so `exits: 1` can match `return 1` there.
|
|
76
|
+
*/
|
|
77
|
+
exitCode: number | null;
|
|
78
|
+
/** What the transition that ends this way did at other boundaries. */
|
|
79
|
+
effects: CodeEffect[];
|
|
80
|
+
/** The boundaries the branch leading here turned on. */
|
|
81
|
+
turnsOn: BoundaryGuard[];
|
|
82
|
+
/** The line the transition starts on, for a finding about one transition. */
|
|
83
|
+
line: number;
|
|
84
|
+
}
|
|
6
85
|
/** A boundary intent that was paired and compared against code. */
|
|
7
86
|
interface CheckedBoundaryIntent {
|
|
8
87
|
kind: "boundary";
|
|
@@ -22,34 +101,46 @@ interface CheckedPrd {
|
|
|
22
101
|
scenarios: number;
|
|
23
102
|
/** Scenarios whose every link resolved to a declared outcome (had >=1 link). */
|
|
24
103
|
resolved: number;
|
|
25
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Scenarios whose every covering test exists, runs and reaches its
|
|
106
|
+
* subject. Zero when the check was given no way to look tests up.
|
|
107
|
+
*/
|
|
108
|
+
covered: number;
|
|
109
|
+
/** Scenarios with neither a link nor a covering test. */
|
|
26
110
|
unlinked: number;
|
|
27
111
|
}
|
|
28
112
|
/**
|
|
29
113
|
* A declared intent that was compared, either a boundary intent paired
|
|
30
114
|
* against code or a PRD whose scenario links were resolved. Discriminated
|
|
31
|
-
* on `kind
|
|
32
|
-
* third doc kind (workflow) ships it extends this union.
|
|
115
|
+
* on `kind`, so a caller renders both from one list.
|
|
33
116
|
*/
|
|
34
117
|
type CheckedIntent = CheckedBoundaryIntent | CheckedPrd;
|
|
35
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Intent that was loaded and not compared, and why. `unkeyable` is a
|
|
120
|
+
* whole document whose boundary cannot be paired. `unreadValue` is one
|
|
121
|
+
* `from` source the code gave no answer to compare against.
|
|
122
|
+
* `unfollowedCall` is a covering test that never reaches its subject
|
|
123
|
+
* through the calls suss followed, and makes calls suss did not follow.
|
|
124
|
+
*/
|
|
36
125
|
interface UncheckedIntent {
|
|
37
126
|
/** The intent doc's `name` (boundary) or `title` (prd). */
|
|
38
127
|
intent: string;
|
|
39
|
-
reason: "unkeyable";
|
|
128
|
+
reason: "unkeyable" | "unreadValue" | "unfollowedCall";
|
|
129
|
+
/** The outcome the claim is on, for a claim narrower than the document. */
|
|
130
|
+
outcomeId?: string;
|
|
131
|
+
/** The scenario the claim is on, by its title or its position, for a covering test. */
|
|
132
|
+
scenario?: string;
|
|
133
|
+
/** The covering test, as the scenario lists it. */
|
|
134
|
+
coveredBy?: string;
|
|
40
135
|
/** Human-readable explanation, render-ready. */
|
|
41
136
|
detail: string;
|
|
42
137
|
}
|
|
43
138
|
/**
|
|
44
|
-
* The
|
|
45
|
-
* checker's
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* checker compared: boundary intents and PRDs both land in `checked`
|
|
50
|
-
* (a discriminated union). `unchecked` is reserved for intent that
|
|
51
|
-
* couldn't be compared at all (an unkeyable boundary), never silently
|
|
52
|
-
* dropped.
|
|
139
|
+
* The result of an intent-agreement pass, laid out like the behavioural
|
|
140
|
+
* checker's `checkAll` result. `findings` are what to fix. `checked`
|
|
141
|
+
* lists every declared intent that was compared, boundary intents and
|
|
142
|
+
* PRDs alike. `unchecked` lists intent that could not be compared at
|
|
143
|
+
* all, such as an unkeyable boundary, so nothing is dropped silently.
|
|
53
144
|
*/
|
|
54
145
|
interface CheckIntentResult {
|
|
55
146
|
findings: IntentFinding[];
|
|
@@ -59,33 +150,82 @@ interface CheckIntentResult {
|
|
|
59
150
|
/**
|
|
60
151
|
* Compare every loaded intent doc against what's known. Boundary intents
|
|
61
152
|
* pair against the code summaries sharing their boundary key; PRDs resolve
|
|
62
|
-
* each scenario's link against the loaded boundary intents
|
|
63
|
-
*
|
|
64
|
-
*
|
|
153
|
+
* each scenario's link against the loaded boundary intents, and check the
|
|
154
|
+
* tests it lists under `coveredBy` through `tests`. Without `tests`, those
|
|
155
|
+
* scenarios count as backed and their tests go unchecked. An unkeyable
|
|
156
|
+
* boundary is reported in `unchecked`, never silently dropped.
|
|
65
157
|
*
|
|
66
158
|
* Findings against `source: "inferred"` intent are downgraded one severity
|
|
67
159
|
* level: the intent describes what the code did when the inference ran, so
|
|
68
160
|
* a divergence is most likely a code change since, not an authoring error.
|
|
69
161
|
* Curation (`"inferred, curated"`) restores full severity.
|
|
70
162
|
*/
|
|
71
|
-
declare function checkIntentAgreement(intents: IntentSummary[], code: BehavioralSummary[]): CheckIntentResult;
|
|
163
|
+
declare function checkIntentAgreement(intents: IntentSummary[], code: BehavioralSummary[], tests?: CoveringTestLookup): CheckIntentResult;
|
|
72
164
|
/**
|
|
73
|
-
* Apply .sussignore rules to intent findings,
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* an intent finding
|
|
165
|
+
* Apply .sussignore rules to intent findings, through the same pipeline
|
|
166
|
+
* in @suss/ir-core that the behavioural checker's `applySuppressions`
|
|
167
|
+
* uses. A rule's `kind` matches the finding kind, and its `boundary`
|
|
168
|
+
* matches the finding's boundary key, exactly for `fn:` and `gql:` keys
|
|
169
|
+
* and path-normalized for REST. A rule's `scenario` matches the title
|
|
170
|
+
* of the PRD scenario the finding is about. A rule that gives
|
|
171
|
+
* `consumer` or `provider` never matches, since an intent finding has
|
|
172
|
+
* neither side.
|
|
80
173
|
*/
|
|
81
174
|
declare function applyIntentSuppressions(findings: IntentFinding[], rules: SuppressionRule[], opts?: {
|
|
82
175
|
keepHidden?: boolean;
|
|
83
176
|
}): IntentFinding[];
|
|
177
|
+
/**
|
|
178
|
+
* Whether an effect the code makes is the one the intent declared. The
|
|
179
|
+
* boundary is resolved with `namesBoundary`, the matcher that resolves
|
|
180
|
+
* what somebody types at `suss ask`, so a document and a question that
|
|
181
|
+
* spell a store the same way pick out the same one.
|
|
182
|
+
*/
|
|
183
|
+
declare function effectMatches(declared: IntentEffect, made: CodeEffect): boolean;
|
|
184
|
+
/** How a transition ends, in the terms intent states an ending in. */
|
|
185
|
+
type CodeEnding = Pick<CodeOutcome, "kind" | "status" | "body" | "errorType" | "exitCode">;
|
|
186
|
+
/**
|
|
187
|
+
* How a transition ends, or null for an ending intent has no word for,
|
|
188
|
+
* such as a render. A status the code computes has no literal, so it
|
|
189
|
+
* comes back null and matches no declared status. `returnIsExitCode`
|
|
190
|
+
* says the unit's return becomes the process's exit code.
|
|
191
|
+
*/
|
|
192
|
+
declare function endingOf(t: Transition, returnIsExitCode?: boolean): CodeEnding | null;
|
|
193
|
+
/** One verb and one boundary this transition reaches. */
|
|
194
|
+
interface CodeEffect {
|
|
195
|
+
does: Relation;
|
|
196
|
+
binding: BoundaryBinding;
|
|
197
|
+
/** How a report writes that boundary, for a message about it. */
|
|
198
|
+
label: string;
|
|
199
|
+
/** The columns the access states, empty when it states none. */
|
|
200
|
+
fields: string[];
|
|
201
|
+
/** What the access picks the item out by, empty when it states none. */
|
|
202
|
+
by: string[];
|
|
203
|
+
/** The shape of what a write put out, when the code says. */
|
|
204
|
+
payload?: TypeShape;
|
|
205
|
+
/** Where the values in this effect's slots came from, when the summary says. */
|
|
206
|
+
slots?: ProvenanceEntry[];
|
|
207
|
+
/** The line the transition that has this effect starts on. */
|
|
208
|
+
line?: number;
|
|
209
|
+
}
|
|
210
|
+
/** Every verb and boundary the transition reaches, grounded against the deployment. */
|
|
211
|
+
declare function codeEffectsOf(t: Transition, deployment: Deployment): CodeEffect[];
|
|
212
|
+
/**
|
|
213
|
+
* The fields an access states: a storage access's columns and key, or
|
|
214
|
+
* the variable a config read takes, which is a field of the runtime's
|
|
215
|
+
* contract. Nothing for any other class.
|
|
216
|
+
*/
|
|
217
|
+
declare function accessDetail(interaction: Interaction): {
|
|
218
|
+
fields: string[];
|
|
219
|
+
by: string[];
|
|
220
|
+
payload?: TypeShape;
|
|
221
|
+
};
|
|
222
|
+
/** Whether the code ends the way the intent says. The body is compared elsewhere. */
|
|
223
|
+
declare function outcomeMatches(intent: Pick<IntentOutcome, "kind" | "status" | "errorType">, code: CodeEnding): boolean;
|
|
84
224
|
/**
|
|
85
225
|
* What each protocol needs before an intent doc written against one of
|
|
86
226
|
* its boundaries can be paired, in the doc author's terms. The
|
|
87
|
-
* drafter
|
|
227
|
+
* drafter gives the same reason for a boundary it could not write.
|
|
88
228
|
*/
|
|
89
229
|
declare function whatWouldKeyIt(protocol: Semantics["name"]): string;
|
|
90
230
|
|
|
91
|
-
export { type CheckIntentResult, type CheckedBoundaryIntent, type CheckedIntent, type CheckedPrd, type UncheckedIntent, applyIntentSuppressions, checkIntentAgreement, whatWouldKeyIt };
|
|
231
|
+
export { type CheckIntentResult, type CheckedBoundaryIntent, type CheckedIntent, type CheckedPrd, type CodeEffect, type CodeEnding, type CoveringTestLookup, type FoundSubject, type FoundTest, type UncheckedIntent, accessDetail, applyIntentSuppressions, checkIntentAgreement, codeEffectsOf, effectMatches, endingOf, outcomeMatches, whatWouldKeyIt };
|