@suss/intent-ir 0.23.0 → 0.24.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.
Files changed (2) hide show
  1. package/README.md +9 -111
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @suss/intent-ir
2
2
 
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
+
3
5
  The team-authored side of the loop: what the code was *meant* to do, in a form that pairs against the derived `BehavioralSummary` of what it *does*.
4
6
 
5
7
  ## What this package is
@@ -9,117 +11,6 @@ Two citizens, discriminated by `kind`, both built on `@suss/ir-core` so intent a
9
11
  - **System intent** (`kind: boundary`): what one boundary should do, as named outcomes. The boundary is REST, function-call, message-bus, storage or unit-invocation, and every field of one comes off the `@suss/ir-core` schema for that protocol.
10
12
  - **Outcome intent** (`kind: prd`): human `when` / `expect` scenarios, each with an optional `link` to a system-intent outcome (`<intent-name>.<outcome-id>`). A scenario with no `link` is a valid state to be in: it reads fully, and nothing has linked it to an outcome yet.
11
13
 
12
- ## What an outcome is
13
-
14
- A transition says three things: what it turned on, how it ends, and what it did.
15
-
16
- **What it turned on**: `when`, a list of clauses in the same verbs `results` uses.
17
-
18
- ```yaml
19
- transitions:
20
- - id: invoice-not-found
21
- when:
22
- - reads: aws.dynamodb:Invoices
23
- finds: nothing
24
- response:
25
- status: 404
26
-
27
- - id: invoice-settled
28
- when:
29
- - reads: aws.dynamodb:Invoices
30
- finds: something
31
- where: settledAt is set
32
- response:
33
- status: 409
34
-
35
- - id: invoice-returned
36
- when:
37
- - reads: aws.dynamodb:Invoices
38
- finds: something
39
- where: settledAt is missing
40
- response:
41
- status: 200
42
- results:
43
- - reads: aws.dynamodb:Invoices
44
- by: invoiceId
45
- ```
46
-
47
- A clause says which subject, then at most one thing about it.
48
-
49
- The subject is a boundary verb (`reads`, `writes`) whose value is the boundary's name, or `input:` with the path the caller sent. Those come from the IR's own subject types: a guard on the result of a call that crossed a boundary becomes the boundary form, one on an input becomes `input:`, and anything else falls back.
50
-
51
- The check is one of `finds` (`nothing` or `something`, what a lookup came back with), `is` (`set`, `missing`, `null`, `a string`), `equals`, or `has`. `where` narrows the clause with whatever the guard said about a deeper read of the same result.
52
-
53
- A fall-through branch states its own condition rather than pointing at the ones above it, since the summary records that branch's guards as the negations of theirs. `otherwise` is left for the one case that has none to state, which is a default branch the summary recorded nothing for. A word that means "not the branches above" would change what it claims the moment somebody inserts a transition over it, in a file people hand-edit.
54
-
55
- A guard that maps to none of this keeps a sentence, and `when` written as one plain string stays valid, which is what existing authored documents use.
56
-
57
- The point of naming the boundary is that the line survives a rename of the variable the source used, and that the checker can compare it. `when: "!doc.send().Item"` could do neither.
58
-
59
- **How it ends**: at most one of `response` (a status and a body), `returns` (a function or handler return value), or `throws` (an error).
60
-
61
- **What it did**: `results`, a list of the effects the outcome has, written in the verbs `suss ask` asks with.
62
-
63
- ```yaml
64
- transitions:
65
- - id: invoice-recorded
66
- when: the message gives an invoice id we have not recorded
67
- returns:
68
- body:
69
- properties:
70
- recorded: { type: boolean }
71
- results:
72
- - writes: aws.dynamodb:Invoices
73
- ```
74
-
75
- `suss ask "what writes aws.dynamodb:Invoices"` is the question and this is the assertion, spelled the same way. The key is the verb, `reads`, `writes` or `invokes`, which are the three `relationsOf` in `@suss/behavioral-ir` gives an effect. The value is the boundary's own name, the string every report writes and `namesBoundary` in `@suss/ir-core` resolves, so what you type in a document and what you type at `ask` pick out the same boundary. Nothing here is per protocol, so a queue consumer and a table writer say what they do without a fourth outcome shape each.
76
-
77
- `invokes` is the verb for calling a deployed unit by name, which is how a service built out of Lambdas hangs together:
78
-
79
- ```yaml
80
- results:
81
- - invokes: unit:lambda ArchiveWorker
82
- ```
83
-
84
- A run that reads the deployment template gives a unit its name, so the boundary a document declares is `unit:lambda ReportBuilder`. The callee on a `results` line is spelled the same way, even when the invoking code reaches it through an env var: `deploymentOf` in `@suss/behavioral-ir` says which resource the template points that variable at, and the drafter and the intent checker both go through it, so a document says which function rather than which variable. A run with no template in it has nothing to put in, and then the line keeps the code's own spelling, `unit:lambda {ARCHIVE_WORKER_FUNCTION}`, and the document's header says which variable it is waiting on. A store the code reaches through a variable works the same way.
85
-
86
- A clause can go further, since the summary does. `fields` is the columns the access touches and `by` is what it picks the item out by:
87
-
88
- ```yaml
89
- results:
90
- - reads: aws.dynamodb:Accounts
91
- by: accountId
92
- - writes: aws.dynamodb:Profiles
93
- fields: [email, phone, address]
94
- ```
95
-
96
- Both are optional and a clause with only the verb and the name means what it always did. What they buy is that "the customer's contact details are erased" stops being satisfied by any write at all to that table. The checker asks that the access cover every column the clause stated; an access that states none is unread rather than empty (no pack parses a DynamoDB `UpdateExpression`), and one that asked for every column covers whatever the clause stated.
97
-
98
- The outcome id stays free-form on purpose. That is where a person writes what the outcome means, and it is what a PRD scenario links to: `invoice-intake.invoice-recorded`, read as "a duplicate delivery changes nothing". A `kind: prd` document is the level where a scenario reads as prose; `kind: boundary` is system intent for engineers, so it stays structural.
99
-
100
- Adding `results` changes nothing about `response`, `returns` and `throws`. A doc that states only those parses and pairs the way it always did.
101
-
102
- ## Which boundaries pair, and which are pending
103
-
104
- A boundary intent pairs when its boundary has a key. REST has one from its method and path, a function-call export has one from its package and export path, a message-bus boundary has one from its channel, and an invoked unit has one from its deployment target and the name the platform knows it by. A unit whose name only the runtime settles has none, so a document for one is authorable and reported `unkeyableBoundary`.
105
-
106
- A store has none. `storage.ts` in `@suss/ir-core` returns null for a storage identity key on purpose: a container name can be a pattern with holes that only a caller or the deployment settles, and the storage pass in `@suss/checker` grounds it before pairing. So a `kind: boundary` doc whose boundary is a store is authorable, and the checker reports it as `unkeyableBoundary` and puts it in `unchecked` rather than pretending to have compared it. That is the same pending state a module-level function-call boundary is in.
107
-
108
- What does pair today is a store named as the target of an effect: put `- writes: aws.dynamodb:Invoices` on an outcome of the boundary that touches the store, and the checker compares it against the storage accesses on that boundary's transitions.
109
-
110
- Every boundary kind has a name of its own in `@suss/ir-core`, `storageLabel` being the one this change added, so an effect always takes a label and never a structured block. A binding with no name at all, a REST call whose method and path the code never settles, is left out of a draft rather than written as a string nobody would type back.
111
-
112
- ```ts
113
- import { IntentDocSchema, intentDocToSummary } from "@suss/intent-ir";
114
-
115
- const doc = IntentDocSchema.parse(/* parsed YAML / JSON */);
116
- const summary = intentDocToSummary(doc); // normalised, checker-ready
117
- ```
118
-
119
- `schema.ts` is the authoring surface (friendly to write); `summary.ts` is the normalized form the checker consumes (boundaries as `ir-core` `BoundaryBinding`s, bodies as `TypeShape`s, one flat outcome list) plus the transform between them. `source` provenance (`author` / `inferred` / `inferred, curated`) travels along with it for the inference path.
120
-
121
- The design is documented in [`design/proposals/intent-specs.md`](../../design/proposals/intent-specs.md).
122
-
123
14
  ## Where it fits in suss
124
15
 
125
16
  It is a peer of `@suss/behavioral-ir`; both build on `@suss/ir-core`. Readers (e.g. `@suss/contract-intent`) parse files into `IntentDoc` and call `intentDocToSummary`; the checker pairs the result against derived code summaries.
@@ -128,6 +19,13 @@ It is a peer of `@suss/behavioral-ir`; both build on `@suss/ir-core`. Readers (e
128
19
 
129
20
  v0: REST, function-call, message-bus, storage and unit-invocation system intent, effects as outcomes, PRD outcome intent with optional links. GraphQL, runtime-config and metric boundaries have no block yet.
130
21
 
22
+ ## More
23
+
24
+ - [What an intent document states](./DESIGN.md)
25
+ - [Documentation](https://nimbuscloud-ai.github.io/suss/)
26
+ - [Every package and pack suss ships](https://nimbuscloud-ai.github.io/suss/reference/packages)
27
+ - [Source and issues](https://github.com/nimbuscloud-ai/suss)
28
+
131
29
  ## Coverage
132
30
 
133
31
  ![coverage](../../.github/badges/coverage-intent-ir.svg)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/intent-ir",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "type": "module",
5
5
  "description": "The types for the intent your team writes down, product outcomes and system behavior, in a shape suss can check the code against.",
6
6
  "keywords": [
@@ -49,7 +49,7 @@
49
49
  "zod": "^4.0.0"
50
50
  },
51
51
  "dependencies": {
52
- "@suss/ir-core": "0.23.0"
52
+ "@suss/ir-core": "0.24.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "tsup": "*",