@suss/intent-ir 0.0.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/dist/index.js ADDED
@@ -0,0 +1,320 @@
1
+ // src/findings.ts
2
+ import { z } from "zod";
3
+ var IntentFindingKindSchema = z.enum([
4
+ // System intent (kind: boundary) vs code:
5
+ "uncoveredOutcome",
6
+ // intent declares an outcome the code never produces
7
+ "unimplementedBoundary",
8
+ // intent boundary has no implementing code at all
9
+ "outcomeShapeMismatch",
10
+ // a matched outcome whose body shapes disagree
11
+ "undeclaredOutcome",
12
+ // code produces a REST status the intent doesn't declare
13
+ "unkeyableBoundary",
14
+ // intent boundary can't be keyed, so it can't be checked
15
+ // Outcome intent (kind: prd) — scenario link coverage against system
16
+ // intent. These concretise the proposal's "scenario not linked /
17
+ // dangling / ambiguous" set (docs/internal/proposals/intent-specs.md);
18
+ // the proposal deferred concrete names to implementation time.
19
+ "unlinkedScenario",
20
+ // scenario carries no structured link (info — a valid pending state)
21
+ "danglingScenarioLink",
22
+ // link names an intent / outcome no boundary intent declares
23
+ "ambiguousScenarioLink"
24
+ // link resolves to two or more boundary intents sharing the name
25
+ ]);
26
+ var IntentFindingSeveritySchema = z.enum(["error", "warning", "info"]);
27
+ var IntentRefSchema = z.object({
28
+ /** The intent doc's `name` (boundary) or `title` (prd). */
29
+ name: z.string(),
30
+ /** The declared outcome id, when the finding is outcome-specific. */
31
+ outcomeId: z.string().optional()
32
+ });
33
+ var IntentFindingSuppressionSchema = z.object({
34
+ /** The rule's human-written justification. */
35
+ reason: z.string(),
36
+ effect: z.enum(["mark", "downgrade", "hide"]),
37
+ /** Original severity, present only when effect is "downgrade". */
38
+ originalSeverity: IntentFindingSeveritySchema.optional()
39
+ });
40
+ var IntentFindingSchema = z.object({
41
+ kind: IntentFindingKindSchema,
42
+ severity: IntentFindingSeveritySchema,
43
+ /**
44
+ * Human-readable boundary label — e.g. `GET /users/:id` or
45
+ * `fn:@suss/cli::contract`. The key the intent and code were paired on.
46
+ */
47
+ boundary: z.string(),
48
+ /** The intent side of the finding. */
49
+ intent: IntentRefSchema,
50
+ /**
51
+ * The matched code summary as `${file}::${name}`, when the finding
52
+ * concerns a specific implementation. Absent for unimplementedBoundary.
53
+ */
54
+ code: z.string().optional(),
55
+ /**
56
+ * PRD-scenario extension — present only on outcome-intent findings
57
+ * (unlinkedScenario / danglingScenarioLink / ambiguousScenarioLink).
58
+ * Identifies the scenario (by its optional title) and the qualified
59
+ * outcome ref (`<intent-name>.<outcome-id>`) that failed to resolve.
60
+ * The peer / boundary findings leave it unset — this is the "intent
61
+ * finding extension" decision 2 of the proposal anticipated.
62
+ */
63
+ scenario: z.object({
64
+ title: z.string().optional(),
65
+ link: z.string().optional()
66
+ }).optional(),
67
+ message: z.string(),
68
+ /** Present when a .sussignore rule matched this finding. */
69
+ suppressed: IntentFindingSuppressionSchema.optional()
70
+ });
71
+
72
+ // src/schema.ts
73
+ import { z as z2 } from "zod";
74
+ var IntentSourceSchema = z2.enum(["author", "inferred", "inferred, curated"]).default("author");
75
+ var PrimitiveTypeName = z2.enum([
76
+ "string",
77
+ "integer",
78
+ "number",
79
+ "boolean",
80
+ "null",
81
+ "unknown"
82
+ ]);
83
+ var ShapeSchema = z2.lazy(
84
+ () => z2.union([
85
+ z2.object({ type: PrimitiveTypeName }),
86
+ z2.object({ type: z2.literal("array"), items: ShapeSchema.optional() }),
87
+ z2.object({
88
+ type: z2.literal("object"),
89
+ properties: z2.record(z2.string(), ShapeSchema).optional(),
90
+ required: z2.array(z2.string()).optional()
91
+ })
92
+ ])
93
+ );
94
+ var BodyShapeSchema = z2.union([
95
+ ShapeSchema,
96
+ z2.object({
97
+ properties: z2.record(z2.string(), ShapeSchema).optional(),
98
+ required: z2.array(z2.string()).optional()
99
+ })
100
+ ]);
101
+ var RestBoundarySchema = z2.object({
102
+ transport: z2.literal("http").default("http"),
103
+ semantics: z2.literal("rest"),
104
+ method: z2.string().min(1),
105
+ path: z2.string().min(1)
106
+ });
107
+ var FunctionCallBoundarySchema = z2.object({
108
+ transport: z2.string().default("in-process"),
109
+ semantics: z2.literal("function-call"),
110
+ /** Repo-relative module path, when the boundary is an intra-repo unit. */
111
+ module: z2.string().optional(),
112
+ /** Named export within the module / package. */
113
+ exportName: z2.string().optional(),
114
+ /** Package name when the boundary is a public package export. */
115
+ package: z2.string().optional(),
116
+ /** Path to the export within the package (sub-path + nested names). */
117
+ exportPath: z2.array(z2.string()).optional()
118
+ });
119
+ var BoundarySchema = z2.discriminatedUnion("semantics", [
120
+ RestBoundarySchema,
121
+ FunctionCallBoundarySchema
122
+ ]);
123
+ var ResponseOutcomeSchema = z2.object({
124
+ status: z2.number().int().min(100).max(599),
125
+ body: BodyShapeSchema.optional()
126
+ });
127
+ var ReturnsOutcomeSchema = z2.object({
128
+ body: BodyShapeSchema.optional()
129
+ });
130
+ var ThrowsOutcomeSchema = z2.object({
131
+ errorType: z2.string().optional()
132
+ });
133
+ function emptyIfNull(schema) {
134
+ return z2.preprocess((v) => v === null ? {} : v, schema);
135
+ }
136
+ var BoundaryTransitionSchema = z2.object({
137
+ id: z2.string().min(1),
138
+ when: z2.string().min(1),
139
+ response: emptyIfNull(ResponseOutcomeSchema).optional(),
140
+ returns: emptyIfNull(ReturnsOutcomeSchema).optional(),
141
+ throws: emptyIfNull(ThrowsOutcomeSchema).optional()
142
+ }).refine(
143
+ (t) => [t.response, t.returns, t.throws].filter((o) => o !== void 0).length === 1,
144
+ {
145
+ message: "each transition must declare exactly one outcome: response, returns, or throws"
146
+ }
147
+ );
148
+ var BoundaryIntentSchema = z2.object({
149
+ kind: z2.literal("boundary"),
150
+ /** Name PRDs reference outcomes through: `<name>.<transition-id>`. */
151
+ name: z2.string().min(1),
152
+ purpose: z2.string().min(1),
153
+ audience: z2.string().min(1),
154
+ source: IntentSourceSchema,
155
+ boundary: BoundarySchema,
156
+ transitions: z2.array(BoundaryTransitionSchema).min(1)
157
+ });
158
+ var PrdScenarioSchema = z2.object({
159
+ title: z2.string().min(1).optional(),
160
+ /** The condition, in the author's terms. */
161
+ when: z2.string().min(1),
162
+ /** The expected outcome, in the author's terms. Always present. */
163
+ expect: z2.string().min(1),
164
+ /**
165
+ * Optional structured link(s) to system-intent outcomes
166
+ * (`<intent-name>.<outcome-id>`). A scenario without `link` is a
167
+ * valid pending-link state: fully human-readable, not yet machine-
168
+ * linked. The link is filled in later by a facilitator (a person, a
169
+ * platform, or an LLM at authoring time) — never required to author.
170
+ *
171
+ * (The human-readable parts are `when` / `expect`; the field is named
172
+ * `link`, not `then`, because a data object with a `then` property is
173
+ * treated as a thenable by Promise resolution — a latent footgun.)
174
+ */
175
+ link: z2.union([z2.string().min(1), z2.array(z2.string().min(1)).min(1)]).optional()
176
+ });
177
+ var PrdSchema = z2.object({
178
+ kind: z2.literal("prd"),
179
+ title: z2.string().min(1),
180
+ purpose: z2.string().min(1),
181
+ audience: z2.string().min(1),
182
+ source: IntentSourceSchema,
183
+ scenarios: z2.array(PrdScenarioSchema).min(1)
184
+ });
185
+ var IntentDocSchema = z2.discriminatedUnion("kind", [
186
+ BoundaryIntentSchema,
187
+ PrdSchema
188
+ ]);
189
+
190
+ // src/summary.ts
191
+ import {
192
+ functionCallBinding,
193
+ restBinding
194
+ } from "@suss/ir-core";
195
+ function intentDocToSummary(doc) {
196
+ if (doc.kind === "prd") {
197
+ return prdToSummary(doc);
198
+ }
199
+ return boundaryIntentToSummary(doc);
200
+ }
201
+ function boundaryIntentToSummary(doc) {
202
+ return {
203
+ kind: "boundary",
204
+ name: doc.name,
205
+ purpose: doc.purpose,
206
+ audience: doc.audience,
207
+ source: doc.source,
208
+ boundary: toBoundaryBinding(doc.boundary),
209
+ outcomes: doc.transitions.map(toOutcome)
210
+ };
211
+ }
212
+ function prdToSummary(doc) {
213
+ return {
214
+ kind: "prd",
215
+ title: doc.title,
216
+ purpose: doc.purpose,
217
+ audience: doc.audience,
218
+ source: doc.source,
219
+ scenarios: doc.scenarios.map((s) => ({
220
+ title: s.title ?? null,
221
+ when: s.when,
222
+ expect: s.expect,
223
+ link: s.link === void 0 ? [] : Array.isArray(s.link) ? s.link : [s.link]
224
+ }))
225
+ };
226
+ }
227
+ function toBoundaryBinding(boundary) {
228
+ if (boundary.semantics === "rest") {
229
+ return restBinding({
230
+ transport: boundary.transport,
231
+ method: boundary.method,
232
+ path: boundary.path,
233
+ recognition: "intent"
234
+ });
235
+ }
236
+ return functionCallBinding({
237
+ transport: boundary.transport,
238
+ recognition: "intent",
239
+ ...boundary.module !== void 0 ? { module: boundary.module } : {},
240
+ ...boundary.exportName !== void 0 ? { exportName: boundary.exportName } : {},
241
+ ...boundary.package !== void 0 ? { package: boundary.package } : {},
242
+ ...boundary.exportPath !== void 0 ? { exportPath: boundary.exportPath } : {}
243
+ });
244
+ }
245
+ function toOutcome(t) {
246
+ const base = { id: t.id, when: t.when };
247
+ if (t.response !== void 0) {
248
+ return {
249
+ ...base,
250
+ kind: "response",
251
+ status: t.response.status,
252
+ body: t.response.body ? bodyToTypeShape(t.response.body) : null,
253
+ errorType: null
254
+ };
255
+ }
256
+ if (t.returns !== void 0) {
257
+ return {
258
+ ...base,
259
+ kind: "return",
260
+ status: null,
261
+ body: t.returns.body ? bodyToTypeShape(t.returns.body) : null,
262
+ errorType: null
263
+ };
264
+ }
265
+ return {
266
+ ...base,
267
+ kind: "throw",
268
+ status: null,
269
+ body: null,
270
+ errorType: t.throws?.errorType ?? null
271
+ };
272
+ }
273
+ function bodyToTypeShape(body) {
274
+ if (!("type" in body)) {
275
+ if (body.properties === void 0) {
276
+ return null;
277
+ }
278
+ return recordShape(body.properties);
279
+ }
280
+ return shapeToTypeShape(body);
281
+ }
282
+ function shapeToTypeShape(shape) {
283
+ if (shape.type === "array") {
284
+ return {
285
+ type: "array",
286
+ items: shape.items !== void 0 ? shapeToTypeShape(shape.items) : { type: "unknown" }
287
+ };
288
+ }
289
+ if (shape.type === "object") {
290
+ return recordShape(shape.properties ?? {});
291
+ }
292
+ return PRIMITIVE_TYPE_SHAPES[shape.type];
293
+ }
294
+ function recordShape(authored) {
295
+ const properties = {};
296
+ for (const [name, prop] of Object.entries(authored)) {
297
+ properties[name] = shapeToTypeShape(prop);
298
+ }
299
+ return { type: "record", properties };
300
+ }
301
+ var PRIMITIVE_TYPE_SHAPES = {
302
+ string: { type: "text" },
303
+ integer: { type: "integer" },
304
+ number: { type: "number" },
305
+ boolean: { type: "boolean" },
306
+ null: { type: "null" },
307
+ unknown: { type: "unknown" }
308
+ };
309
+ export {
310
+ BodyShapeSchema,
311
+ BoundarySchema,
312
+ IntentDocSchema,
313
+ IntentFindingKindSchema,
314
+ IntentFindingSchema,
315
+ IntentFindingSeveritySchema,
316
+ IntentFindingSuppressionSchema,
317
+ IntentRefSchema,
318
+ IntentSourceSchema,
319
+ intentDocToSummary
320
+ };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@suss/intent-ir",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "Intent IR for suss — the team-authored side of the loop. PRD (outcome) intent and system intent, in shapes that pair against derived behavioral summaries.",
6
+ "keywords": [
7
+ "suss",
8
+ "intent",
9
+ "prd",
10
+ "ir"
11
+ ],
12
+ "license": "Apache-2.0",
13
+ "author": "Nimbus Cloud AI LLC",
14
+ "homepage": "https://github.com/nimbuscloud-ai/suss/tree/main/packages/intent-ir#readme",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/nimbuscloud-ai/suss.git",
18
+ "directory": "packages/intent-ir"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/nimbuscloud-ai/suss/issues"
22
+ },
23
+ "main": "./dist/index.cjs",
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "require": "./dist/index.cjs"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "test": "vitest run",
41
+ "test:coverage": "vitest run --coverage",
42
+ "lint": "biome check .",
43
+ "lint:fix": "biome check --write .",
44
+ "typecheck": "tsc --noEmit"
45
+ },
46
+ "peerDependencies": {
47
+ "zod": "^4.0.0"
48
+ },
49
+ "dependencies": {
50
+ "@suss/ir-core": "0.0.1"
51
+ },
52
+ "devDependencies": {
53
+ "tsup": "*",
54
+ "typescript": "*",
55
+ "vitest": "*",
56
+ "zod": "^4.3.6"
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ }
61
+ }