@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/README.md
CHANGED
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and says where the two disagree.
|
|
4
4
|
|
|
5
|
-
Pairs
|
|
6
|
-
code
|
|
5
|
+
Pairs the intent a team wrote with the summaries derived from code, and
|
|
6
|
+
reports where the code does not do what the team declared.
|
|
7
7
|
|
|
8
8
|
## What this package is
|
|
9
9
|
|
|
10
|
-
This
|
|
11
|
-
separate from `@suss/checker
|
|
12
|
-
differ
|
|
13
|
-
`
|
|
14
|
-
is one-sided coverage
|
|
15
|
-
|
|
10
|
+
This package does the intent half of suss's checking. It is kept
|
|
11
|
+
separate from `@suss/checker`, the behavioral peer checker, on purpose,
|
|
12
|
+
because the inputs differ. One side is an `IntentSummary` (from
|
|
13
|
+
`@suss/contract-intent`) and the other is a `BehavioralSummary`. The
|
|
14
|
+
output differs too. An `IntentFinding` is one-sided coverage: "the team
|
|
15
|
+
declared X; does the code do it?" A behavioral finding is a symmetric
|
|
16
|
+
provider↔consumer mismatch.
|
|
16
17
|
|
|
17
18
|
```ts
|
|
18
19
|
import { checkIntentAgreement } from "@suss/checker-intent";
|
|
@@ -20,7 +21,7 @@ import { checkIntentAgreement } from "@suss/checker-intent";
|
|
|
20
21
|
const findings = checkIntentAgreement(intentSummaries, codeSummaries);
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
For each boundary intent, it pairs
|
|
24
|
+
For each boundary intent, it pairs with the code summaries that share
|
|
24
25
|
the same boundary key, and emits:
|
|
25
26
|
|
|
26
27
|
- `unimplementedBoundary`: the intent declares a boundary no code produces.
|
|
@@ -29,14 +30,23 @@ the same boundary key, and emits:
|
|
|
29
30
|
- `undeclaredOutcome`: code produces a REST status, or reaches a boundary, that the intent doesn't declare (info; intent under-specifies).
|
|
30
31
|
- `unkeyableBoundary`: the intent's boundary has no key to pair on, so nothing was compared (warning).
|
|
31
32
|
- `undescribedOutcome`: a declared outcome no PRD scenario links to (info).
|
|
32
|
-
- `renamedBoundary`: a declared store the unit never touches, paired with an undeclared store of the same system
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
- `renamedBoundary`: a declared store the unit never touches, paired with an undeclared store of the same system that the unit does touch, with the same verbs on the same outcomes.
|
|
34
|
+
- `pathWithoutEffect`: a transition producing a declared outcome lacks an effect the document's `always` block says every outcome has.
|
|
35
|
+
- `valueFromElsewhere`: a column a `results` line gives a source for under `from` takes its value from somewhere else. A source the code gives no answer for goes under `unchecked` as `unreadValue` instead.
|
|
36
|
+
|
|
37
|
+
For each PRD, it resolves every scenario's links against the loaded
|
|
38
|
+
boundary intents, and checks the tests a scenario lists under
|
|
39
|
+
`coveredBy` through the `CoveringTestLookup` passed as the third
|
|
40
|
+
argument. That lookup turns a spelled test or subject into summaries,
|
|
41
|
+
and the CLI builds it with the resolver `suss ask` uses. Whether a test
|
|
42
|
+
reaches its subject is asked of `@suss/checker`'s call facts, once for
|
|
43
|
+
every test. It emits `unlinkedScenario`, `danglingScenarioLink`,
|
|
44
|
+
`ambiguousScenarioLink`, `missingCoveringTest`, `testMissesSubject` and
|
|
45
|
+
`coveringTestSkipped`, each a warning.
|
|
36
46
|
|
|
37
47
|
## How a declared effect is compared
|
|
38
48
|
|
|
39
|
-
An outcome can declare
|
|
49
|
+
An outcome can declare its effects, written the same way `suss ask` asks about one:
|
|
40
50
|
|
|
41
51
|
```yaml
|
|
42
52
|
results:
|
|
@@ -44,49 +54,49 @@ results:
|
|
|
44
54
|
- invokes: unit:lambda ArchiveWorker
|
|
45
55
|
```
|
|
46
56
|
|
|
47
|
-
|
|
57
|
+
The pass checks both directions, and both resolve the boundary with `namesBoundary` in `@suss/ir-core`. That is the same matcher `suss ask` uses for what somebody types. So `writes: Invoices` and `writes: aws.dynamodb:Invoices` both pick out the table, as the two spellings do at the command line, and this package has no second parser that could drift from that one.
|
|
48
58
|
|
|
49
|
-
For each declared effect, the pass walks what the matching code transitions reached
|
|
59
|
+
For each declared effect, the pass walks what the matching code transitions reached. Each of those is a verb from `relationsOf` plus the binding it reached. When none of them matches the intent, the pass reports `uncoveredOutcome`. An outcome that declares an ending narrows to the transitions that end that way. An outcome that declares only effects is checked against every transition of the unit.
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
In the other direction, the pass walks every boundary the unit's transitions reach, and reports `undeclaredOutcome` for each one that no outcome declares. If an intent lists three writes on a unit that does four, one write was never written down. The finding is info, for the same reason an undeclared status is: an intent doc declares a minimum and is not a closed list, so code that goes beyond it usually means somebody has not documented that part yet.
|
|
52
62
|
|
|
53
|
-
|
|
63
|
+
When the code cannot settle an access's container, as with a wrapper that receives its table as an argument, the access's name has a hole in it and matches nothing here. Grounding that name is the storage pass's job, and this pass does not load contracts. An invoke that reads its callee out of an env var is in the same position. The code writes it as `unit:lambda {ARCHIVE_WORKER_FUNCTION}`, and resolving that chain is the unit-invocation pass's job. For the two to match, a document has to write the callee the same way the code does.
|
|
54
64
|
|
|
55
65
|
## How a declared condition is compared
|
|
56
66
|
|
|
57
|
-
|
|
67
|
+
When a `when` clause states which boundary the branch read, the pass compares that too. This is what makes `when` more than prose.
|
|
58
68
|
|
|
59
|
-
`boundaryGuardsOf` in `@suss/behavioral-ir`
|
|
69
|
+
For each branch of the code, `boundaryGuardsOf` in `@suss/behavioral-ir` returns which boundary its guards depended on, and whether the guard passed because something was there. A declared outcome then narrows to the branches whose guards match every boundary clause the intent stated. `uncoveredOutcome` reports an outcome whose ending the code produces under a different condition. So an intent saying "404 when a read of `aws.dynamodb:Invoices` finds nothing" fails when the code's 404 depends on the row being present. When the unit has no effect against that boundary in any transition, the message points at the boundary as the cause instead.
|
|
60
70
|
|
|
61
|
-
The boundary resolves through `namesBoundary` again, and
|
|
71
|
+
The boundary resolves through `namesBoundary` again, and when the clause states `finds`, it has to agree. A clause about an input, and a clause left as a sentence, are treated as prose and not compared, because the summary has nothing that corresponds to those paths or words.
|
|
62
72
|
|
|
63
73
|
## When a store was renamed
|
|
64
74
|
|
|
65
|
-
|
|
75
|
+
When a store is renamed in code and the intent doc is not updated, one cause produces a pile of findings. The old name is declared and never touched, and the new one is touched and never declared. An `uncoveredOutcome` for the store that vanished and an `undeclaredOutcome` for the one that appeared fire for every verb and every outcome that used it. `renamedBoundary` folds all of those into one finding when the pairing is unambiguous. That requires four things: the two boundaries share a system prefix, their verbs match exactly, the new one satisfies every declared use the old one had, and each side has exactly one candidate on the other. The folded finding is still an error, like the findings it replaces. The document and the code still disagree, and folding is only a guess about the cause. It does not change whether the disagreement matters.
|
|
66
76
|
|
|
67
|
-
Pairing only considers reads and writes. A queue channel or a deployed unit is addressed by name, so a different callee produces its own uncoveredOutcome and undeclaredOutcome findings
|
|
77
|
+
Pairing only considers reads and writes. A queue channel or a deployed unit is addressed by name, so a different callee produces its own uncoveredOutcome and undeclaredOutcome findings, and they are not folded.
|
|
68
78
|
|
|
69
79
|
## The coverage question, both ways
|
|
70
80
|
|
|
71
|
-
The three scenario kinds
|
|
81
|
+
The three scenario kinds check whether a scenario points at an outcome that exists. `undescribedOutcome` checks the reverse: which declared behaviour has no written reason. A product reader asks that question, and the same two documents answer it.
|
|
72
82
|
|
|
73
|
-
|
|
83
|
+
The check stays quiet until at least one PRD is loaded, because before that every outcome would be reported. `suss infer prd` writes a scenario per outcome, so a fresh set of drafts starts with no such findings. They appear as the boundary documents gain outcomes that the PRDs do not cover.
|
|
74
84
|
|
|
75
85
|
## Which summaries count as the code
|
|
76
86
|
|
|
77
|
-
Two rules narrow the summaries an intent doc is compared against
|
|
87
|
+
Two rules narrow the summaries an intent doc is compared against. Both keep suss from reporting a document for something the document was never meant to cover.
|
|
78
88
|
|
|
79
|
-
A consumer at the same key is a caller. A client calling `GET /users/{id}` shares the key with the route
|
|
89
|
+
A consumer at the same key is a caller. A client calling `GET /users/{id}` shares the key with the route but provides nothing, so comparing outcomes against its returns would report every declared outcome as uncovered.
|
|
80
90
|
|
|
81
|
-
A summary a manifest
|
|
91
|
+
A summary produced from a manifest is a declaration. A CloudFormation queue resource declares that the channel exists and nothing more, so it has no transition that could satisfy an outcome. The handler in the same run is the code that satisfies it. So summaries with `confidence.source: "declared"` are left out. A boundary with nothing else behind it still reports `unimplementedBoundary`, which matches its state: declared and not written yet.
|
|
82
92
|
|
|
83
|
-
The handler behind a queue needs the declaration for its identity
|
|
93
|
+
The handler behind a queue still needs the declaration for its identity. A SAM template decides which queue delivers to it, and its own summary has no channel. `withDeclaredDelivery` in `@suss/behavioral-ir` combines the two through the deployable unit they both record. Both this pass and `suss infer intent` call it before they index anything by boundary key. It fills in only what a summary left null, and only from a declaration, so a channel the code stated for itself stays as it is.
|
|
84
94
|
|
|
85
95
|
## Which boundaries can be paired
|
|
86
96
|
|
|
87
|
-
`whatWouldKeyIt`
|
|
97
|
+
`whatWouldKeyIt` returns, for each protocol, what an intent doc needs before it can be paired. The message on an `unkeyableBoundary` finding comes from it, and so does the reason `suss infer intent` gives for a boundary it skipped.
|
|
88
98
|
|
|
89
|
-
A store is the case
|
|
99
|
+
A store is the case to know about. By design in `@suss/ir-core` it has no identity key at all, so you can write a `kind: boundary` doc for one, but it can never be paired. What pairs today is naming the store as the target of an effect on the boundary that touches it.
|
|
90
100
|
|
|
91
101
|
## Where it fits in suss
|
|
92
102
|
|
|
@@ -100,6 +110,7 @@ It does **not** depend on
|
|
|
100
110
|
|
|
101
111
|
## More
|
|
102
112
|
|
|
113
|
+
- [How intent findings are graded](./DESIGN.md)
|
|
103
114
|
- [Documentation](https://suss.sh/)
|
|
104
115
|
- [Every package and pack suss ships](https://suss.sh/packs/catalog)
|
|
105
116
|
- [Source and issues](https://github.com/nimbuscloud-ai/suss)
|