@plures/praxis 1.2.41 → 1.4.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 (64) hide show
  1. package/dist/browser/{chunk-BBP2F7TT.js → chunk-MJK3IYTJ.js} +123 -5
  2. package/dist/browser/{chunk-FCEH7WMH.js → chunk-N63K4KWS.js} +1 -1
  3. package/dist/browser/{engine-65QDGCAN.js → engine-YIEGSX7U.js} +1 -1
  4. package/dist/browser/index.d.ts +2 -2
  5. package/dist/browser/index.js +10 -5
  6. package/dist/browser/integrations/svelte.d.ts +2 -2
  7. package/dist/browser/integrations/svelte.js +2 -2
  8. package/dist/browser/{reactive-engine.svelte-Cqd8Mod2.d.ts → reactive-engine.svelte-DjynI82A.d.ts} +83 -4
  9. package/dist/node/chunk-2IUFZBH3.js +87 -0
  10. package/dist/node/{chunk-WZ6B3LZ6.js → chunk-7CSWBDFL.js} +3 -56
  11. package/dist/node/{chunk-32YFEEML.js → chunk-7M3HV4XR.js} +4 -4
  12. package/dist/node/{chunk-PTH6MD6P.js → chunk-FWOXU4MM.js} +1 -1
  13. package/dist/node/{chunk-BBP2F7TT.js → chunk-KMJWAFZV.js} +128 -5
  14. package/dist/node/chunk-PGVSB6NR.js +59 -0
  15. package/dist/node/cli/index.cjs +1078 -211
  16. package/dist/node/cli/index.js +21 -2
  17. package/dist/node/cloud/index.d.cts +1 -1
  18. package/dist/node/cloud/index.d.ts +1 -1
  19. package/dist/node/{engine-7CXQV6RC.js → engine-FEN5IYZ5.js} +1 -1
  20. package/dist/node/index.cjs +1633 -59
  21. package/dist/node/index.d.cts +769 -5
  22. package/dist/node/index.d.ts +769 -5
  23. package/dist/node/index.js +1375 -45
  24. package/dist/node/integrations/svelte.cjs +123 -5
  25. package/dist/node/integrations/svelte.d.cts +3 -3
  26. package/dist/node/integrations/svelte.d.ts +3 -3
  27. package/dist/node/integrations/svelte.js +3 -3
  28. package/dist/node/{protocol-BocKczNv.d.ts → protocol-DcyGMmWY.d.cts} +7 -0
  29. package/dist/node/{protocol-BocKczNv.d.cts → protocol-DcyGMmWY.d.ts} +7 -0
  30. package/dist/node/{reactive-engine.svelte-CGe8SpVE.d.cts → reactive-engine.svelte-Cg0Yc2Hs.d.cts} +90 -6
  31. package/dist/node/{reactive-engine.svelte-D-xTDxT5.d.ts → reactive-engine.svelte-DekxqFu0.d.ts} +90 -6
  32. package/dist/node/{reverse-W7THPV45.js → reverse-YD3CWIGM.js} +3 -2
  33. package/dist/node/rules-4DAJ4Z4N.js +7 -0
  34. package/dist/node/server-SYZPDULV.js +361 -0
  35. package/dist/node/{validate-EN3M4FUR.js → validate-TQGVIG7G.js} +4 -3
  36. package/package.json +29 -3
  37. package/src/__tests__/engine-v2.test.ts +532 -0
  38. package/src/__tests__/expectations.test.ts +364 -0
  39. package/src/__tests__/factory.test.ts +426 -0
  40. package/src/__tests__/mcp-server.test.ts +310 -0
  41. package/src/__tests__/project.test.ts +396 -0
  42. package/src/cli/index.ts +28 -0
  43. package/src/core/completeness.ts +274 -0
  44. package/src/core/engine.ts +47 -5
  45. package/src/core/pluresdb/store.ts +9 -3
  46. package/src/core/protocol.ts +7 -0
  47. package/src/core/rule-result.ts +130 -0
  48. package/src/core/rules.ts +12 -5
  49. package/src/core/ui-rules.ts +340 -0
  50. package/src/expectations/expectations.ts +471 -0
  51. package/src/expectations/index.ts +29 -0
  52. package/src/expectations/types.ts +95 -0
  53. package/src/factory/factory.ts +634 -0
  54. package/src/factory/index.ts +27 -0
  55. package/src/factory/types.ts +64 -0
  56. package/src/index.ts +84 -0
  57. package/src/mcp/index.ts +33 -0
  58. package/src/mcp/server.ts +485 -0
  59. package/src/mcp/types.ts +161 -0
  60. package/src/project/index.ts +31 -0
  61. package/src/project/project.ts +423 -0
  62. package/src/project/types.ts +87 -0
  63. package/src/vite/completeness-plugin.ts +72 -0
  64. /package/dist/node/{chunk-R2PSBPKQ.js → chunk-TEMFJOIH.js} +0 -0
@@ -0,0 +1,471 @@
1
+ /**
2
+ * Expectations DSL — Core
3
+ *
4
+ * Behavioral declarations for Praxis rules. Instead of writing test
5
+ * assertions, you declare what behaviors you expect from your system.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { expectBehavior, ExpectationSet, verify } from '@plures/praxis/expectations';
10
+ *
11
+ * const expectations = new ExpectationSet({ name: 'settings' });
12
+ *
13
+ * expectations.add(
14
+ * expectBehavior('settings-saved-toast')
15
+ * .onlyWhen('settings.diff is non-empty')
16
+ * .never('when settings panel opens without changes')
17
+ * .never('when save fails')
18
+ * .always('includes which settings changed')
19
+ * );
20
+ *
21
+ * const report = verify(registry, expectations);
22
+ * ```
23
+ */
24
+
25
+ import type {
26
+ ExpectationCondition,
27
+ ConditionResult,
28
+ ExpectationResult,
29
+ VerificationReport,
30
+ ExpectationSetOptions,
31
+ VerifiableRegistry,
32
+ VerifiableDescriptor,
33
+ } from './types.js';
34
+
35
+ // ─── Expectation Class ──────────────────────────────────────────────────────
36
+
37
+ /**
38
+ * A behavioral expectation declaration.
39
+ *
40
+ * Chainable API for declaring conditions under which a behavior
41
+ * should or should not occur.
42
+ */
43
+ export class Expectation {
44
+ readonly name: string;
45
+ private _conditions: ExpectationCondition[] = [];
46
+
47
+ constructor(name: string) {
48
+ this.name = name;
49
+ }
50
+
51
+ /**
52
+ * Declare that this behavior should ONLY occur when a condition is true.
53
+ * If the condition is false, the behavior should NOT occur.
54
+ */
55
+ onlyWhen(condition: string): this {
56
+ this._conditions.push({ description: condition, type: 'onlyWhen' });
57
+ return this;
58
+ }
59
+
60
+ /**
61
+ * Declare that this behavior should NEVER occur under a given condition.
62
+ */
63
+ never(condition: string): this {
64
+ this._conditions.push({ description: condition, type: 'never' });
65
+ return this;
66
+ }
67
+
68
+ /**
69
+ * Declare that this behavior should ALWAYS have a certain property.
70
+ */
71
+ always(condition: string): this {
72
+ this._conditions.push({ description: condition, type: 'always' });
73
+ return this;
74
+ }
75
+
76
+ /** Get all declared conditions. */
77
+ get conditions(): ReadonlyArray<ExpectationCondition> {
78
+ return this._conditions;
79
+ }
80
+ }
81
+
82
+ // ─── ExpectationSet ─────────────────────────────────────────────────────────
83
+
84
+ /**
85
+ * A collection of expectations for a specific domain.
86
+ */
87
+ export class ExpectationSet {
88
+ readonly name: string;
89
+ readonly description: string;
90
+ private _expectations: Expectation[] = [];
91
+
92
+ constructor(options: ExpectationSetOptions) {
93
+ this.name = options.name;
94
+ this.description = options.description ?? '';
95
+ }
96
+
97
+ /** Add an expectation to the set. */
98
+ add(expectation: Expectation): this {
99
+ this._expectations.push(expectation);
100
+ return this;
101
+ }
102
+
103
+ /** Get all expectations in this set. */
104
+ get expectations(): ReadonlyArray<Expectation> {
105
+ return this._expectations;
106
+ }
107
+
108
+ /** Number of expectations. */
109
+ get size(): number {
110
+ return this._expectations.length;
111
+ }
112
+ }
113
+
114
+ // ─── Builder Function ───────────────────────────────────────────────────────
115
+
116
+ /**
117
+ * Create a new behavioral expectation.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * expectBehavior('settings-saved-toast')
122
+ * .onlyWhen('settings.diff is non-empty')
123
+ * .never('when save fails')
124
+ * .always('includes which settings changed');
125
+ * ```
126
+ */
127
+ export function expectBehavior(name: string): Expectation {
128
+ return new Expectation(name);
129
+ }
130
+
131
+ // ─── Verification Engine ────────────────────────────────────────────────────
132
+
133
+ /**
134
+ * Verify expectations against a rule registry.
135
+ *
136
+ * Walks the rule graph to determine if expectations are satisfied,
137
+ * violated, or unverifiable given the registered rules and contracts.
138
+ */
139
+ export function verify(
140
+ registry: VerifiableRegistry,
141
+ expectations: ExpectationSet,
142
+ ): VerificationReport {
143
+ const rules = registry.getAllRules();
144
+ const constraints = registry.getAllConstraints();
145
+ const ruleIds = registry.getRuleIds();
146
+ const constraintIds = registry.getConstraintIds();
147
+
148
+ const allDescriptors: VerifiableDescriptor[] = [...rules, ...constraints];
149
+
150
+ const expectationResults: ExpectationResult[] = [];
151
+
152
+ for (const exp of expectations.expectations) {
153
+ const result = verifyExpectation(exp, allDescriptors, ruleIds, constraintIds);
154
+ expectationResults.push(result);
155
+ }
156
+
157
+ const satisfied = expectationResults.filter(r => r.status === 'satisfied').length;
158
+ const violated = expectationResults.filter(r => r.status === 'violated').length;
159
+ const partial = expectationResults.filter(r => r.status === 'partial').length;
160
+
161
+ const allEdgeCases = expectationResults.flatMap(r => r.edgeCases);
162
+ const allMitigations = expectationResults.flatMap(r => r.mitigations);
163
+
164
+ return {
165
+ setName: expectations.name,
166
+ timestamp: new Date().toISOString(),
167
+ status: violated > 0 ? 'violated' : partial > 0 ? 'partial' : 'satisfied',
168
+ expectations: expectationResults,
169
+ summary: {
170
+ total: expectationResults.length,
171
+ satisfied,
172
+ violated,
173
+ partial,
174
+ },
175
+ allEdgeCases,
176
+ allMitigations,
177
+ };
178
+ }
179
+
180
+ // ─── Internal Verification Logic ────────────────────────────────────────────
181
+
182
+ function verifyExpectation(
183
+ expectation: Expectation,
184
+ descriptors: VerifiableDescriptor[],
185
+ ruleIds: string[],
186
+ _constraintIds: string[],
187
+ ): ExpectationResult {
188
+ const conditionResults: ConditionResult[] = [];
189
+ const edgeCases: string[] = [];
190
+ const mitigations: string[] = [];
191
+
192
+ // Find rules/constraints that might be related to this expectation
193
+ const related = findRelatedDescriptors(expectation.name, descriptors);
194
+
195
+ for (const condition of expectation.conditions) {
196
+ const result = verifyCondition(condition, expectation.name, related, ruleIds);
197
+ conditionResults.push(result);
198
+
199
+ // Discover edge cases
200
+ if (result.status === 'unverifiable') {
201
+ edgeCases.push(`Cannot verify "${condition.description}" for "${expectation.name}" — no covering rule/contract found`);
202
+ mitigations.push(`Add a rule or constraint that explicitly covers: ${condition.description}`);
203
+ } else if (result.status === 'violated') {
204
+ edgeCases.push(`"${expectation.name}" may fire incorrectly: ${condition.description}`);
205
+ mitigations.push(`Review rule logic for "${expectation.name}" regarding: ${condition.description}`);
206
+ }
207
+ }
208
+
209
+ const satisfiedCount = conditionResults.filter(r => r.status === 'satisfied').length;
210
+ const violatedCount = conditionResults.filter(r => r.status === 'violated').length;
211
+ const total = conditionResults.length;
212
+
213
+ let status: 'satisfied' | 'violated' | 'partial';
214
+ if (total === 0) {
215
+ status = 'satisfied'; // no conditions = vacuously true
216
+ } else if (violatedCount > 0) {
217
+ status = 'violated';
218
+ } else if (satisfiedCount === total) {
219
+ status = 'satisfied';
220
+ } else {
221
+ status = 'partial';
222
+ }
223
+
224
+ return {
225
+ name: expectation.name,
226
+ status,
227
+ conditions: conditionResults,
228
+ edgeCases,
229
+ mitigations,
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Check if two text strings share enough semantic overlap.
235
+ * Extracts significant words and checks overlap ratio.
236
+ */
237
+ function textOverlaps(a: string, b: string): boolean {
238
+ const stopWords = new Set(['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been',
239
+ 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
240
+ 'should', 'may', 'might', 'shall', 'can', 'to', 'of', 'in', 'for', 'on', 'with',
241
+ 'at', 'by', 'from', 'as', 'into', 'through', 'during', 'before', 'after', 'when',
242
+ 'that', 'this', 'it', 'its', 'and', 'or', 'but', 'not', 'no', 'if', 'then',
243
+ 'than', 'so', 'up', 'out', 'about', 'just', 'must']);
244
+
245
+ const wordsA = a.toLowerCase().replace(/[^a-z0-9\s]/g, '').split(/\s+/).filter(w => w.length > 2 && !stopWords.has(w));
246
+ const wordsB = b.toLowerCase().replace(/[^a-z0-9\s]/g, '').split(/\s+/).filter(w => w.length > 2 && !stopWords.has(w));
247
+
248
+ if (wordsA.length === 0 || wordsB.length === 0) return false;
249
+
250
+ // Check for stem overlap (simple: one word starts with the other's prefix)
251
+ const matches = wordsA.filter(wa =>
252
+ wordsB.some(wb => wa.startsWith(wb.slice(0, 4)) || wb.startsWith(wa.slice(0, 4)))
253
+ );
254
+
255
+ const minWords = Math.min(wordsA.length, wordsB.length);
256
+ return matches.length >= Math.max(1, Math.ceil(minWords * 0.5));
257
+ }
258
+
259
+ function verifyCondition(
260
+ condition: ExpectationCondition,
261
+ expectationName: string,
262
+ relatedDescriptors: VerifiableDescriptor[],
263
+ _ruleIds: string[],
264
+ ): ConditionResult {
265
+ if (relatedDescriptors.length === 0) {
266
+ return {
267
+ condition,
268
+ status: 'unverifiable',
269
+ explanation: `No rules or constraints found related to "${expectationName}"`,
270
+ relatedRules: [],
271
+ };
272
+ }
273
+
274
+ const relatedIds = relatedDescriptors.map(d => d.id);
275
+ const condLower = condition.description.toLowerCase();
276
+
277
+ /** Check if text matches against a target string (includes or fuzzy overlap). */
278
+ const matches = (target: string): boolean =>
279
+ target.toLowerCase().includes(condLower) ||
280
+ condLower.includes(target.toLowerCase()) ||
281
+ textOverlaps(condLower, target);
282
+
283
+ switch (condition.type) {
284
+ case 'onlyWhen': {
285
+ // Check if any related rule's contract mentions this precondition
286
+ const coveringRule = relatedDescriptors.find(d =>
287
+ d.contract?.examples.some(ex =>
288
+ matches(ex.given) || matches(ex.when),
289
+ ) ||
290
+ d.contract?.invariants.some(inv => matches(inv)),
291
+ );
292
+
293
+ if (coveringRule) {
294
+ return {
295
+ condition,
296
+ status: 'satisfied',
297
+ explanation: `Rule "${coveringRule.id}" contract covers precondition: ${condition.description}`,
298
+ relatedRules: relatedIds,
299
+ };
300
+ }
301
+
302
+ // Check description match
303
+ const descMatch = relatedDescriptors.find(d => matches(d.description));
304
+ if (descMatch) {
305
+ return {
306
+ condition,
307
+ status: 'satisfied',
308
+ explanation: `Rule "${descMatch.id}" description addresses: ${condition.description}`,
309
+ relatedRules: relatedIds,
310
+ };
311
+ }
312
+
313
+ return {
314
+ condition,
315
+ status: 'unverifiable',
316
+ explanation: `No rule contract explicitly covers the precondition: ${condition.description}`,
317
+ relatedRules: relatedIds,
318
+ };
319
+ }
320
+
321
+ case 'never': {
322
+ // Check if any constraint/rule prevents this condition via invariants or negative examples
323
+ const preventingDescriptor = relatedDescriptors.find(d =>
324
+ d.contract?.invariants.some(inv => matches(inv)) ||
325
+ d.contract?.examples.some(ex =>
326
+ (matches(ex.given) || matches(ex.when)) &&
327
+ (ex.then.toLowerCase().includes('retract') ||
328
+ ex.then.toLowerCase().includes('fail') ||
329
+ ex.then.toLowerCase().includes('violation') ||
330
+ ex.then.toLowerCase().includes('skip') ||
331
+ ex.then.toLowerCase().includes('block') ||
332
+ ex.then.toLowerCase().includes('no ')),
333
+ ) ||
334
+ d.contract?.behavior.toLowerCase().includes('block') ||
335
+ d.contract?.behavior.toLowerCase().includes('prevent'),
336
+ );
337
+
338
+ if (preventingDescriptor) {
339
+ return {
340
+ condition,
341
+ status: 'satisfied',
342
+ explanation: `Constraint/rule "${preventingDescriptor.id}" prevents: ${condition.description}`,
343
+ relatedRules: relatedIds,
344
+ };
345
+ }
346
+
347
+ return {
348
+ condition,
349
+ status: 'unverifiable',
350
+ explanation: `No rule or constraint explicitly prevents: ${condition.description}`,
351
+ relatedRules: relatedIds,
352
+ };
353
+ }
354
+
355
+ case 'always': {
356
+ // Check if any rule's contract guarantees this property
357
+ const guaranteeing = relatedDescriptors.find(d =>
358
+ d.contract?.invariants.some(inv => matches(inv)) ||
359
+ d.contract?.behavior && matches(d.contract.behavior),
360
+ );
361
+
362
+ if (guaranteeing) {
363
+ return {
364
+ condition,
365
+ status: 'satisfied',
366
+ explanation: `Rule "${guaranteeing.id}" guarantees: ${condition.description}`,
367
+ relatedRules: relatedIds,
368
+ };
369
+ }
370
+
371
+ // Check examples for the guarantee
372
+ const exampleMatch = relatedDescriptors.find(d =>
373
+ d.contract?.examples.some(ex => matches(ex.then)),
374
+ );
375
+
376
+ if (exampleMatch) {
377
+ return {
378
+ condition,
379
+ status: 'satisfied',
380
+ explanation: `Rule "${exampleMatch.id}" example demonstrates: ${condition.description}`,
381
+ relatedRules: relatedIds,
382
+ };
383
+ }
384
+
385
+ return {
386
+ condition,
387
+ status: 'unverifiable',
388
+ explanation: `No rule contract guarantees: ${condition.description}`,
389
+ relatedRules: relatedIds,
390
+ };
391
+ }
392
+ }
393
+ }
394
+
395
+ /**
396
+ * Find descriptors related to an expectation by name matching.
397
+ * Uses fuzzy matching against rule IDs, descriptions, contracts, and event types.
398
+ */
399
+ function findRelatedDescriptors(
400
+ expectationName: string,
401
+ descriptors: VerifiableDescriptor[],
402
+ ): VerifiableDescriptor[] {
403
+ const nameLower = expectationName.toLowerCase();
404
+ const nameParts = nameLower.split(/[-_./\s]+/);
405
+
406
+ return descriptors.filter(d => {
407
+ const idLower = d.id.toLowerCase();
408
+ const descLower = d.description.toLowerCase();
409
+ const behaviorLower = d.contract?.behavior?.toLowerCase() ?? '';
410
+
411
+ // Direct match
412
+ if (idLower.includes(nameLower) || nameLower.includes(idLower)) return true;
413
+ if (descLower.includes(nameLower)) return true;
414
+ if (behaviorLower.includes(nameLower)) return true;
415
+
416
+ // Part-based match (at least 2 parts must match for multi-part names)
417
+ const minParts = Math.min(2, nameParts.length);
418
+ const matchingParts = nameParts.filter(part =>
419
+ part.length > 2 && (idLower.includes(part) || descLower.includes(part) || behaviorLower.includes(part)),
420
+ );
421
+ if (matchingParts.length >= minParts) return true;
422
+
423
+ // Event type match
424
+ if (d.eventTypes) {
425
+ const eventStr = Array.isArray(d.eventTypes) ? d.eventTypes.join(' ') : d.eventTypes;
426
+ if (eventStr.toLowerCase().includes(nameLower)) return true;
427
+ }
428
+
429
+ return false;
430
+ });
431
+ }
432
+
433
+ /**
434
+ * Format a verification report as human-readable text.
435
+ */
436
+ export function formatVerificationReport(report: VerificationReport): string {
437
+ const lines: string[] = [];
438
+ const icon = report.status === 'satisfied' ? '✅' : report.status === 'partial' ? '🟡' : '🔴';
439
+
440
+ lines.push(`${icon} Expectations: ${report.setName} — ${report.status.toUpperCase()}`);
441
+ lines.push(` ${report.summary.satisfied}/${report.summary.total} satisfied, ${report.summary.violated} violated, ${report.summary.partial} partial`);
442
+ lines.push('');
443
+
444
+ for (const exp of report.expectations) {
445
+ const expIcon = exp.status === 'satisfied' ? '✅' : exp.status === 'partial' ? '🟡' : '🔴';
446
+ lines.push(`${expIcon} ${exp.name}`);
447
+
448
+ for (const cond of exp.conditions) {
449
+ const condIcon = cond.status === 'satisfied' ? ' ✓' : cond.status === 'violated' ? ' ✗' : ' ?';
450
+ lines.push(`${condIcon} [${cond.condition.type}] ${cond.condition.description}`);
451
+ lines.push(` ${cond.explanation}`);
452
+ }
453
+
454
+ if (exp.edgeCases.length > 0) {
455
+ lines.push(' Edge cases:');
456
+ for (const ec of exp.edgeCases) {
457
+ lines.push(` ⚠️ ${ec}`);
458
+ }
459
+ }
460
+ lines.push('');
461
+ }
462
+
463
+ if (report.allMitigations.length > 0) {
464
+ lines.push('Suggested mitigations:');
465
+ for (const m of report.allMitigations) {
466
+ lines.push(` 💡 ${m}`);
467
+ }
468
+ }
469
+
470
+ return lines.join('\n');
471
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Expectations DSL
3
+ *
4
+ * Public API for behavioral expectations.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { expectBehavior, ExpectationSet, verify } from '@plures/praxis/expectations';
9
+ * ```
10
+ */
11
+
12
+ export {
13
+ Expectation,
14
+ ExpectationSet,
15
+ expectBehavior,
16
+ verify,
17
+ formatVerificationReport,
18
+ } from './expectations.js';
19
+
20
+ export type {
21
+ ExpectationCondition,
22
+ ConditionStatus,
23
+ ConditionResult,
24
+ ExpectationResult,
25
+ VerificationReport,
26
+ ExpectationSetOptions,
27
+ VerifiableRegistry,
28
+ VerifiableDescriptor,
29
+ } from './types.js';
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Expectations DSL — Types
3
+ *
4
+ * Types for declaring behavioral expectations about rules.
5
+ * Expectations replace traditional tests with behavioral declarations.
6
+ */
7
+
8
+ // ─── Core Types ─────────────────────────────────────────────────────────────
9
+
10
+ /** A condition under which a behavior should or should not occur. */
11
+ export interface ExpectationCondition {
12
+ /** Human-readable condition description */
13
+ description: string;
14
+ /** Type of expectation */
15
+ type: 'onlyWhen' | 'never' | 'always';
16
+ }
17
+
18
+ /** Verification status for a single expectation condition. */
19
+ export type ConditionStatus = 'satisfied' | 'violated' | 'unverifiable';
20
+
21
+ /** Detailed result for a single condition check. */
22
+ export interface ConditionResult {
23
+ condition: ExpectationCondition;
24
+ status: ConditionStatus;
25
+ /** Explanation of how the condition was verified or why it couldn't be */
26
+ explanation: string;
27
+ /** Related rule IDs that informed this check */
28
+ relatedRules: string[];
29
+ }
30
+
31
+ /** Verification result for a single Expectation. */
32
+ export interface ExpectationResult {
33
+ /** The expectation name/ID */
34
+ name: string;
35
+ /** Overall status: satisfied if ALL conditions pass */
36
+ status: 'satisfied' | 'violated' | 'partial';
37
+ /** Per-condition results */
38
+ conditions: ConditionResult[];
39
+ /** Edge cases discovered */
40
+ edgeCases: string[];
41
+ /** Suggested mitigations for violated/partial expectations */
42
+ mitigations: string[];
43
+ }
44
+
45
+ /** Full verification report for an ExpectationSet. */
46
+ export interface VerificationReport {
47
+ /** Set name */
48
+ setName: string;
49
+ /** Timestamp of verification */
50
+ timestamp: string;
51
+ /** Overall status: satisfied if ALL expectations are satisfied */
52
+ status: 'satisfied' | 'violated' | 'partial';
53
+ /** Per-expectation results */
54
+ expectations: ExpectationResult[];
55
+ /** Summary stats */
56
+ summary: {
57
+ total: number;
58
+ satisfied: number;
59
+ violated: number;
60
+ partial: number;
61
+ };
62
+ /** All edge cases found across all expectations */
63
+ allEdgeCases: string[];
64
+ /** All mitigations suggested */
65
+ allMitigations: string[];
66
+ }
67
+
68
+ /** Options for creating an ExpectationSet */
69
+ export interface ExpectationSetOptions {
70
+ /** Name/domain for this set of expectations */
71
+ name: string;
72
+ /** Optional description */
73
+ description?: string;
74
+ }
75
+
76
+ /** Interface describing a rule or constraint for verification */
77
+ export interface VerifiableDescriptor {
78
+ id: string;
79
+ description: string;
80
+ eventTypes?: string | string[];
81
+ contract?: {
82
+ behavior: string;
83
+ examples: Array<{ given: string; when: string; then: string }>;
84
+ invariants: string[];
85
+ ruleId: string;
86
+ };
87
+ }
88
+
89
+ /** Registry-like interface for verification */
90
+ export interface VerifiableRegistry {
91
+ getAllRules(): VerifiableDescriptor[];
92
+ getAllConstraints(): VerifiableDescriptor[];
93
+ getRuleIds(): string[];
94
+ getConstraintIds(): string[];
95
+ }