@plures/praxis 1.1.3 → 1.2.10

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 (74) hide show
  1. package/FRAMEWORK.md +106 -15
  2. package/README.md +194 -119
  3. package/dist/browser/adapter-CIMBGDC7.js +14 -0
  4. package/dist/browser/chunk-K377RW4V.js +230 -0
  5. package/dist/browser/chunk-MBVHLOU2.js +152 -0
  6. package/dist/browser/{chunk-R45WXWKH.js → chunk-VOMLVI6V.js} +1 -149
  7. package/dist/browser/engine-YJZV4SLD.js +8 -0
  8. package/dist/browser/index.d.ts +161 -5
  9. package/dist/browser/index.js +156 -141
  10. package/dist/browser/integrations/svelte.d.ts +2 -2
  11. package/dist/browser/integrations/svelte.js +2 -1
  12. package/dist/browser/{reactive-engine.svelte-C9OpcTHf.d.ts → reactive-engine.svelte-9aS0kTa8.d.ts} +136 -1
  13. package/dist/node/adapter-75ISSMWD.js +15 -0
  14. package/dist/node/chunk-5RH7UAQC.js +486 -0
  15. package/dist/node/chunk-MBVHLOU2.js +152 -0
  16. package/dist/node/chunk-PRPQO6R5.js +85 -0
  17. package/dist/node/chunk-R2PSBPKQ.js +150 -0
  18. package/dist/node/chunk-S54337I5.js +446 -0
  19. package/dist/node/{chunk-R45WXWKH.js → chunk-VOMLVI6V.js} +1 -149
  20. package/dist/node/chunk-WZ6B3LZ6.js +638 -0
  21. package/dist/node/cli/index.cjs +2936 -897
  22. package/dist/node/cli/index.js +27 -0
  23. package/dist/node/components/index.d.cts +3 -2
  24. package/dist/node/components/index.d.ts +3 -2
  25. package/dist/node/docs-JFNYTOJA.js +102 -0
  26. package/dist/node/engine-2DQBKBJC.js +9 -0
  27. package/dist/node/index.cjs +1114 -354
  28. package/dist/node/index.d.cts +388 -5
  29. package/dist/node/index.d.ts +388 -5
  30. package/dist/node/index.js +201 -640
  31. package/dist/node/integrations/svelte.cjs +76 -0
  32. package/dist/node/integrations/svelte.d.cts +2 -2
  33. package/dist/node/integrations/svelte.d.ts +2 -2
  34. package/dist/node/integrations/svelte.js +3 -1
  35. package/dist/node/{reactive-engine.svelte-1M4m_C_v.d.cts → reactive-engine.svelte-BFIZfawz.d.cts} +199 -1
  36. package/dist/node/{reactive-engine.svelte-ChNFn4Hj.d.ts → reactive-engine.svelte-CRNqHlbv.d.ts} +199 -1
  37. package/dist/node/reverse-W7THPV45.js +193 -0
  38. package/dist/node/{terminal-adapter-CWka-yL8.d.ts → terminal-adapter-B-UK_Vdz.d.ts} +28 -3
  39. package/dist/node/{terminal-adapter-CDzxoLKR.d.cts → terminal-adapter-BQSIF5bf.d.cts} +28 -3
  40. package/dist/node/validate-CNHUULQE.js +180 -0
  41. package/docs/core/pluresdb-integration.md +15 -15
  42. package/docs/decision-ledger/BEHAVIOR_LEDGER.md +225 -0
  43. package/docs/decision-ledger/DecisionLedger.tla +180 -0
  44. package/docs/decision-ledger/IMPLEMENTATION_SUMMARY.md +217 -0
  45. package/docs/decision-ledger/LATEST.md +166 -0
  46. package/docs/guides/cicd-pipeline.md +142 -0
  47. package/package.json +2 -2
  48. package/src/__tests__/cli-validate.test.ts +197 -0
  49. package/src/__tests__/decision-ledger.test.ts +485 -0
  50. package/src/__tests__/reverse-generator.test.ts +189 -0
  51. package/src/__tests__/scanner.test.ts +215 -0
  52. package/src/cli/commands/docs.ts +147 -0
  53. package/src/cli/commands/reverse.ts +289 -0
  54. package/src/cli/commands/validate.ts +264 -0
  55. package/src/cli/index.ts +68 -0
  56. package/src/core/pluresdb/adapter.ts +46 -3
  57. package/src/core/reactive-engine.svelte.ts +6 -1
  58. package/src/core/reactive-engine.ts +1 -1
  59. package/src/core/rules.ts +133 -0
  60. package/src/decision-ledger/README.md +400 -0
  61. package/src/decision-ledger/REVERSE_ENGINEERING.md +484 -0
  62. package/src/decision-ledger/facts-events.ts +121 -0
  63. package/src/decision-ledger/index.ts +70 -0
  64. package/src/decision-ledger/ledger.ts +246 -0
  65. package/src/decision-ledger/logic-ledger.ts +158 -0
  66. package/src/decision-ledger/reverse-generator.ts +426 -0
  67. package/src/decision-ledger/scanner.ts +506 -0
  68. package/src/decision-ledger/types.ts +247 -0
  69. package/src/decision-ledger/validation.ts +336 -0
  70. package/src/dsl/index.ts +13 -2
  71. package/src/index.browser.ts +6 -0
  72. package/src/index.ts +40 -0
  73. package/src/integrations/pluresdb.ts +14 -2
  74. package/src/integrations/unified.ts +350 -0
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,10 +30,386 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
33
+ // src/core/protocol.ts
34
+ var PRAXIS_PROTOCOL_VERSION;
35
+ var init_protocol = __esm({
36
+ "src/core/protocol.ts"() {
37
+ "use strict";
38
+ PRAXIS_PROTOCOL_VERSION = "1.0.0";
39
+ }
40
+ });
41
+
42
+ // src/core/engine.ts
43
+ var engine_exports = {};
44
+ __export(engine_exports, {
45
+ LogicEngine: () => LogicEngine,
46
+ createPraxisEngine: () => createPraxisEngine
47
+ });
48
+ function safeClone(value) {
49
+ if (value === null || typeof value !== "object") {
50
+ return value;
51
+ }
52
+ if (typeof globalThis.structuredClone === "function") {
53
+ try {
54
+ return globalThis.structuredClone(value);
55
+ } catch {
56
+ }
57
+ }
58
+ if (Array.isArray(value)) {
59
+ return [...value];
60
+ }
61
+ return { ...value };
62
+ }
63
+ function createPraxisEngine(options) {
64
+ return new LogicEngine(options);
65
+ }
66
+ var LogicEngine;
67
+ var init_engine = __esm({
68
+ "src/core/engine.ts"() {
69
+ "use strict";
70
+ init_protocol();
71
+ LogicEngine = class {
72
+ state;
73
+ registry;
74
+ constructor(options) {
75
+ this.registry = options.registry;
76
+ this.state = {
77
+ context: options.initialContext,
78
+ facts: options.initialFacts ?? [],
79
+ meta: options.initialMeta ?? {},
80
+ protocolVersion: PRAXIS_PROTOCOL_VERSION
81
+ };
82
+ }
83
+ /**
84
+ * Get the current state (immutable copy)
85
+ */
86
+ getState() {
87
+ return {
88
+ context: safeClone(this.state.context),
89
+ facts: [...this.state.facts],
90
+ meta: this.state.meta ? safeClone(this.state.meta) : void 0,
91
+ protocolVersion: this.state.protocolVersion
92
+ };
93
+ }
94
+ /**
95
+ * Get the current context
96
+ */
97
+ getContext() {
98
+ return safeClone(this.state.context);
99
+ }
100
+ /**
101
+ * Get current facts
102
+ */
103
+ getFacts() {
104
+ return [...this.state.facts];
105
+ }
106
+ /**
107
+ * Process events through the engine.
108
+ * Applies all registered rules and checks all registered constraints.
109
+ *
110
+ * @param events Events to process
111
+ * @returns Result with new state and diagnostics
112
+ */
113
+ step(events) {
114
+ const config = {
115
+ ruleIds: this.registry.getRuleIds(),
116
+ constraintIds: this.registry.getConstraintIds()
117
+ };
118
+ return this.stepWithConfig(events, config);
119
+ }
120
+ /**
121
+ * Process events with specific rule and constraint configuration.
122
+ *
123
+ * @param events Events to process
124
+ * @param config Step configuration
125
+ * @returns Result with new state and diagnostics
126
+ */
127
+ stepWithConfig(events, config) {
128
+ const diagnostics = [];
129
+ let newState = { ...this.state };
130
+ const newFacts = [];
131
+ for (const ruleId of config.ruleIds) {
132
+ const rule = this.registry.getRule(ruleId);
133
+ if (!rule) {
134
+ diagnostics.push({
135
+ kind: "rule-error",
136
+ message: `Rule "${ruleId}" not found in registry`,
137
+ data: { ruleId }
138
+ });
139
+ continue;
140
+ }
141
+ try {
142
+ const ruleFacts = rule.impl(newState, events);
143
+ newFacts.push(...ruleFacts);
144
+ } catch (error) {
145
+ diagnostics.push({
146
+ kind: "rule-error",
147
+ message: `Error executing rule "${ruleId}": ${error instanceof Error ? error.message : String(error)}`,
148
+ data: { ruleId, error }
149
+ });
150
+ }
151
+ }
152
+ newState = {
153
+ ...newState,
154
+ facts: [...newState.facts, ...newFacts]
155
+ };
156
+ for (const constraintId of config.constraintIds) {
157
+ const constraint = this.registry.getConstraint(constraintId);
158
+ if (!constraint) {
159
+ diagnostics.push({
160
+ kind: "constraint-violation",
161
+ message: `Constraint "${constraintId}" not found in registry`,
162
+ data: { constraintId }
163
+ });
164
+ continue;
165
+ }
166
+ try {
167
+ const result = constraint.impl(newState);
168
+ if (result === false) {
169
+ diagnostics.push({
170
+ kind: "constraint-violation",
171
+ message: `Constraint "${constraintId}" violated`,
172
+ data: { constraintId, description: constraint.description }
173
+ });
174
+ } else if (typeof result === "string") {
175
+ diagnostics.push({
176
+ kind: "constraint-violation",
177
+ message: result,
178
+ data: { constraintId, description: constraint.description }
179
+ });
180
+ }
181
+ } catch (error) {
182
+ diagnostics.push({
183
+ kind: "constraint-violation",
184
+ message: `Error checking constraint "${constraintId}": ${error instanceof Error ? error.message : String(error)}`,
185
+ data: { constraintId, error }
186
+ });
187
+ }
188
+ }
189
+ this.state = newState;
190
+ return {
191
+ state: newState,
192
+ diagnostics
193
+ };
194
+ }
195
+ /**
196
+ * Update the context directly (for exceptional cases).
197
+ * Generally, context should be updated through rules.
198
+ *
199
+ * @param updater Function that produces new context from old context
200
+ */
201
+ updateContext(updater) {
202
+ this.state = {
203
+ ...this.state,
204
+ context: updater(this.state.context)
205
+ };
206
+ }
207
+ /**
208
+ * Add facts directly (for exceptional cases).
209
+ * Generally, facts should be added through rules.
210
+ *
211
+ * @param facts Facts to add
212
+ */
213
+ addFacts(facts) {
214
+ this.state = {
215
+ ...this.state,
216
+ facts: [...this.state.facts, ...facts]
217
+ };
218
+ }
219
+ /**
220
+ * Clear all facts
221
+ */
222
+ clearFacts() {
223
+ this.state = {
224
+ ...this.state,
225
+ facts: []
226
+ };
227
+ }
228
+ /**
229
+ * Reset the engine to initial state
230
+ */
231
+ reset(options) {
232
+ this.state = {
233
+ context: options.initialContext,
234
+ facts: options.initialFacts ?? [],
235
+ meta: options.initialMeta ?? {},
236
+ protocolVersion: PRAXIS_PROTOCOL_VERSION
237
+ };
238
+ }
239
+ };
240
+ }
241
+ });
242
+
243
+ // src/core/pluresdb/adapter.ts
244
+ var adapter_exports = {};
245
+ __export(adapter_exports, {
246
+ InMemoryPraxisDB: () => InMemoryPraxisDB,
247
+ PluresDBPraxisAdapter: () => PluresDBPraxisAdapter,
248
+ createInMemoryDB: () => createInMemoryDB,
249
+ createPluresDB: () => createPluresDB,
250
+ createPraxisLocalFirst: () => createPraxisLocalFirst
251
+ });
252
+ function createInMemoryDB() {
253
+ return new InMemoryPraxisDB();
254
+ }
255
+ function createPluresDB(config) {
256
+ return new PluresDBPraxisAdapter(config);
257
+ }
258
+ async function createPraxisLocalFirst(options = {}) {
259
+ const { pollInterval, ...localOptions } = options;
260
+ const mod = await import("@plures/pluresdb/local-first");
261
+ const LocalFirstCtor = mod.PluresDBLocalFirst ?? mod.default;
262
+ if (!LocalFirstCtor) {
263
+ throw new Error("Failed to load PluresDBLocalFirst from @plures/pluresdb/local-first");
264
+ }
265
+ const db = new LocalFirstCtor(localOptions);
266
+ return new PluresDBPraxisAdapter({ db, pollInterval });
267
+ }
268
+ var InMemoryPraxisDB, PluresDBPraxisAdapter;
269
+ var init_adapter = __esm({
270
+ "src/core/pluresdb/adapter.ts"() {
271
+ "use strict";
272
+ InMemoryPraxisDB = class {
273
+ store = /* @__PURE__ */ new Map();
274
+ watchers = /* @__PURE__ */ new Map();
275
+ async get(key) {
276
+ return this.store.get(key);
277
+ }
278
+ async set(key, value) {
279
+ this.store.set(key, value);
280
+ const keyWatchers = this.watchers.get(key);
281
+ if (keyWatchers) {
282
+ for (const callback of keyWatchers) {
283
+ callback(value);
284
+ }
285
+ }
286
+ }
287
+ watch(key, callback) {
288
+ if (!this.watchers.has(key)) {
289
+ this.watchers.set(key, /* @__PURE__ */ new Set());
290
+ }
291
+ const watchers = this.watchers.get(key);
292
+ const wrappedCallback = (val) => callback(val);
293
+ watchers.add(wrappedCallback);
294
+ return () => {
295
+ watchers.delete(wrappedCallback);
296
+ if (watchers.size === 0) {
297
+ this.watchers.delete(key);
298
+ }
299
+ };
300
+ }
301
+ /**
302
+ * Get all keys (for testing/debugging)
303
+ */
304
+ keys() {
305
+ return Array.from(this.store.keys());
306
+ }
307
+ /**
308
+ * Clear all data (for testing)
309
+ */
310
+ clear() {
311
+ this.store.clear();
312
+ this.watchers.clear();
313
+ }
314
+ };
315
+ PluresDBPraxisAdapter = class {
316
+ db;
317
+ watchers = /* @__PURE__ */ new Map();
318
+ pollIntervals = /* @__PURE__ */ new Map();
319
+ lastValues = /* @__PURE__ */ new Map();
320
+ pollInterval;
321
+ constructor(config) {
322
+ if ("get" in config && "put" in config) {
323
+ this.db = config;
324
+ this.pollInterval = 1e3;
325
+ } else {
326
+ this.db = config.db;
327
+ this.pollInterval = config.pollInterval ?? 1e3;
328
+ }
329
+ }
330
+ async get(key) {
331
+ try {
332
+ const value = await this.db.get(key);
333
+ return value;
334
+ } catch (error) {
335
+ return void 0;
336
+ }
337
+ }
338
+ async set(key, value) {
339
+ await this.db.put(key, value);
340
+ this.lastValues.set(key, value);
341
+ const keyWatchers = this.watchers.get(key);
342
+ if (keyWatchers) {
343
+ for (const callback of keyWatchers) {
344
+ callback(value);
345
+ }
346
+ }
347
+ }
348
+ watch(key, callback) {
349
+ if (!this.watchers.has(key)) {
350
+ this.watchers.set(key, /* @__PURE__ */ new Set());
351
+ }
352
+ const watchers = this.watchers.get(key);
353
+ const wrappedCallback = (val) => callback(val);
354
+ watchers.add(wrappedCallback);
355
+ if (!this.pollIntervals.has(key)) {
356
+ const interval = setInterval(async () => {
357
+ try {
358
+ const value = await this.db.get(key);
359
+ const lastValue = this.lastValues.get(key);
360
+ if (JSON.stringify(value) !== JSON.stringify(lastValue)) {
361
+ this.lastValues.set(key, value);
362
+ const currentWatchers = this.watchers.get(key);
363
+ if (currentWatchers) {
364
+ for (const cb of currentWatchers) {
365
+ cb(value);
366
+ }
367
+ }
368
+ }
369
+ } catch (error) {
370
+ }
371
+ }, this.pollInterval);
372
+ this.pollIntervals.set(key, interval);
373
+ }
374
+ return () => {
375
+ watchers.delete(wrappedCallback);
376
+ if (watchers.size === 0) {
377
+ this.watchers.delete(key);
378
+ const interval = this.pollIntervals.get(key);
379
+ if (interval) {
380
+ clearInterval(interval);
381
+ this.pollIntervals.delete(key);
382
+ }
383
+ this.lastValues.delete(key);
384
+ }
385
+ };
386
+ }
387
+ /**
388
+ * Clean up all resources
389
+ */
390
+ dispose() {
391
+ for (const interval of this.pollIntervals.values()) {
392
+ clearInterval(interval);
393
+ }
394
+ this.pollIntervals.clear();
395
+ this.watchers.clear();
396
+ this.lastValues.clear();
397
+ }
398
+ };
399
+ }
400
+ });
401
+
30
402
  // src/index.ts
31
403
  var src_exports = {};
32
404
  __export(src_exports, {
405
+ AcknowledgeContractGap: () => AcknowledgeContractGap,
33
406
  ActorManager: () => ActorManager,
407
+ BehaviorLedger: () => BehaviorLedger,
408
+ ContractAdded: () => ContractAdded,
409
+ ContractGapAcknowledged: () => ContractGapAcknowledged,
410
+ ContractMissing: () => ContractMissing,
411
+ ContractUpdated: () => ContractUpdated,
412
+ ContractValidated: () => ContractValidated,
34
413
  FrameworkAgnosticReactiveEngine: () => ReactiveLogicEngine2,
35
414
  InMemoryPraxisDB: () => InMemoryPraxisDB,
36
415
  LogicEngine: () => LogicEngine,
@@ -45,12 +424,15 @@ __export(src_exports, {
45
424
  RegistryIntrospector: () => RegistryIntrospector,
46
425
  StateDocsGenerator: () => StateDocsGenerator,
47
426
  TerminalAdapter: () => TerminalAdapter,
427
+ ValidateContracts: () => ValidateContracts,
428
+ attachAllIntegrations: () => attachAllIntegrations,
48
429
  attachTauriToEngine: () => attachTauriToEngine,
49
430
  attachToEngine: () => attachToEngine,
50
431
  attachUnumToEngine: () => attachUnumToEngine,
51
432
  canvasToMermaid: () => canvasToMermaid,
52
433
  canvasToSchema: () => canvasToSchema,
53
434
  canvasToYaml: () => canvasToYaml,
435
+ createBehaviorLedger: () => createBehaviorLedger,
54
436
  createCanvasEditor: () => createCanvasEditor,
55
437
  createFrameworkAgnosticReactiveEngine: () => createReactiveEngine2,
56
438
  createInMemoryDB: () => createInMemoryDB,
@@ -62,6 +444,7 @@ __export(src_exports, {
62
444
  createPluresDBGenerator: () => createPluresDBGenerator,
63
445
  createPraxisDBStore: () => createPraxisDBStore,
64
446
  createPraxisEngine: () => createPraxisEngine,
447
+ createPraxisLocalFirst: () => createPraxisLocalFirst,
65
448
  createReactiveEngine: () => createReactiveEngine,
66
449
  createSchemaRegistry: () => createSchemaRegistry,
67
450
  createSchemaTemplate: () => createSchemaTemplate,
@@ -69,8 +452,10 @@ __export(src_exports, {
69
452
  createTauriPraxisAdapter: () => createTauriPraxisAdapter,
70
453
  createTerminalAdapter: () => createTerminalAdapter,
71
454
  createTimerActor: () => createTimerActor,
455
+ createUnifiedApp: () => createUnifiedApp,
72
456
  createUnumAdapter: () => createUnumAdapter,
73
457
  defineConstraint: () => defineConstraint,
458
+ defineContract: () => defineContract,
74
459
  defineEvent: () => defineEvent,
75
460
  defineFact: () => defineFact,
76
461
  defineModule: () => defineModule,
@@ -79,12 +464,17 @@ __export(src_exports, {
79
464
  filterFacts: () => filterFacts,
80
465
  findEvent: () => findEvent,
81
466
  findFact: () => findFact,
467
+ formatValidationReport: () => formatValidationReport,
468
+ formatValidationReportJSON: () => formatValidationReportJSON,
469
+ formatValidationReportSARIF: () => formatValidationReportSARIF,
82
470
  generateDocs: () => generateDocs,
83
471
  generateId: () => generateId,
84
472
  generateTauriConfig: () => generateTauriConfig,
473
+ getContract: () => getContract,
85
474
  getEventPath: () => getEventPath,
86
475
  getFactPath: () => getFactPath,
87
476
  getSchemaPath: () => getSchemaPath,
477
+ isContract: () => isContract,
88
478
  loadSchema: () => loadSchema,
89
479
  loadSchemaFromFile: () => loadSchemaFromFile,
90
480
  loadSchemaFromJson: () => loadSchemaFromJson,
@@ -92,19 +482,29 @@ __export(src_exports, {
92
482
  registerSchema: () => registerSchema,
93
483
  runTerminalCommand: () => runTerminalCommand,
94
484
  schemaToCanvas: () => schemaToCanvas,
485
+ validateContracts: () => validateContracts,
95
486
  validateForGeneration: () => validateForGeneration,
96
487
  validateSchema: () => validateSchema,
97
488
  validateWithGuardian: () => validateWithGuardian
98
489
  });
99
490
  module.exports = __toCommonJS(src_exports);
100
-
101
- // src/core/protocol.ts
102
- var PRAXIS_PROTOCOL_VERSION = "1.0.0";
491
+ init_protocol();
103
492
 
104
493
  // src/core/rules.ts
105
494
  var PraxisRegistry = class {
106
495
  rules = /* @__PURE__ */ new Map();
107
496
  constraints = /* @__PURE__ */ new Map();
497
+ compliance;
498
+ contractGaps = [];
499
+ constructor(options = {}) {
500
+ const defaultEnabled = typeof process !== "undefined" ? process.env?.NODE_ENV !== "production" : false;
501
+ this.compliance = {
502
+ enabled: defaultEnabled,
503
+ requiredFields: ["behavior", "examples", "invariants"],
504
+ missingSeverity: "warning",
505
+ ...options.compliance
506
+ };
507
+ }
108
508
  /**
109
509
  * Register a rule
110
510
  */
@@ -113,6 +513,7 @@ var PraxisRegistry = class {
113
513
  throw new Error(`Rule with id "${descriptor.id}" already registered`);
114
514
  }
115
515
  this.rules.set(descriptor.id, descriptor);
516
+ this.trackContractCompliance(descriptor.id, descriptor);
116
517
  }
117
518
  /**
118
519
  * Register a constraint
@@ -122,6 +523,7 @@ var PraxisRegistry = class {
122
523
  throw new Error(`Constraint with id "${descriptor.id}" already registered`);
123
524
  }
124
525
  this.constraints.set(descriptor.id, descriptor);
526
+ this.trackContractCompliance(descriptor.id, descriptor);
125
527
  }
126
528
  /**
127
529
  * Register a module (all its rules and constraints)
@@ -170,199 +572,77 @@ var PraxisRegistry = class {
170
572
  getAllConstraints() {
171
573
  return Array.from(this.constraints.values());
172
574
  }
173
- };
174
-
175
- // src/core/engine.ts
176
- function safeClone(value) {
177
- if (value === null || typeof value !== "object") {
178
- return value;
179
- }
180
- if (typeof globalThis.structuredClone === "function") {
181
- try {
182
- return globalThis.structuredClone(value);
183
- } catch {
184
- }
185
- }
186
- if (Array.isArray(value)) {
187
- return [...value];
188
- }
189
- return { ...value };
190
- }
191
- var LogicEngine = class {
192
- state;
193
- registry;
194
- constructor(options) {
195
- this.registry = options.registry;
196
- this.state = {
197
- context: options.initialContext,
198
- facts: options.initialFacts ?? [],
199
- meta: options.initialMeta ?? {},
200
- protocolVersion: PRAXIS_PROTOCOL_VERSION
201
- };
202
- }
203
- /**
204
- * Get the current state (immutable copy)
205
- */
206
- getState() {
207
- return {
208
- context: safeClone(this.state.context),
209
- facts: [...this.state.facts],
210
- meta: this.state.meta ? safeClone(this.state.meta) : void 0,
211
- protocolVersion: this.state.protocolVersion
212
- };
213
- }
214
575
  /**
215
- * Get the current context
216
- */
217
- getContext() {
218
- return safeClone(this.state.context);
219
- }
220
- /**
221
- * Get current facts
222
- */
223
- getFacts() {
224
- return [...this.state.facts];
576
+ * Get collected contract gaps from registration-time validation.
577
+ */
578
+ getContractGaps() {
579
+ return [...this.contractGaps];
225
580
  }
226
581
  /**
227
- * Process events through the engine.
228
- * Applies all registered rules and checks all registered constraints.
229
- *
230
- * @param events Events to process
231
- * @returns Result with new state and diagnostics
232
- */
233
- step(events) {
234
- const config = {
235
- ruleIds: this.registry.getRuleIds(),
236
- constraintIds: this.registry.getConstraintIds()
237
- };
238
- return this.stepWithConfig(events, config);
582
+ * Clear collected contract gaps.
583
+ */
584
+ clearContractGaps() {
585
+ this.contractGaps = [];
239
586
  }
240
- /**
241
- * Process events with specific rule and constraint configuration.
242
- *
243
- * @param events Events to process
244
- * @param config Step configuration
245
- * @returns Result with new state and diagnostics
246
- */
247
- stepWithConfig(events, config) {
248
- const diagnostics = [];
249
- let newState = { ...this.state };
250
- const newFacts = [];
251
- for (const ruleId of config.ruleIds) {
252
- const rule = this.registry.getRule(ruleId);
253
- if (!rule) {
254
- diagnostics.push({
255
- kind: "rule-error",
256
- message: `Rule "${ruleId}" not found in registry`,
257
- data: { ruleId }
258
- });
259
- continue;
260
- }
261
- try {
262
- const ruleFacts = rule.impl(newState, events);
263
- newFacts.push(...ruleFacts);
264
- } catch (error) {
265
- diagnostics.push({
266
- kind: "rule-error",
267
- message: `Error executing rule "${ruleId}": ${error instanceof Error ? error.message : String(error)}`,
268
- data: { ruleId, error }
269
- });
270
- }
587
+ trackContractCompliance(id, descriptor) {
588
+ if (!this.compliance.enabled) {
589
+ return;
271
590
  }
272
- newState = {
273
- ...newState,
274
- facts: [...newState.facts, ...newFacts]
275
- };
276
- for (const constraintId of config.constraintIds) {
277
- const constraint = this.registry.getConstraint(constraintId);
278
- if (!constraint) {
279
- diagnostics.push({
280
- kind: "constraint-violation",
281
- message: `Constraint "${constraintId}" not found in registry`,
282
- data: { constraintId }
283
- });
284
- continue;
285
- }
286
- try {
287
- const result = constraint.impl(newState);
288
- if (result === false) {
289
- diagnostics.push({
290
- kind: "constraint-violation",
291
- message: `Constraint "${constraintId}" violated`,
292
- data: { constraintId, description: constraint.description }
293
- });
294
- } else if (typeof result === "string") {
295
- diagnostics.push({
296
- kind: "constraint-violation",
297
- message: result,
298
- data: { constraintId, description: constraint.description }
299
- });
300
- }
301
- } catch (error) {
302
- diagnostics.push({
303
- kind: "constraint-violation",
304
- message: `Error checking constraint "${constraintId}": ${error instanceof Error ? error.message : String(error)}`,
305
- data: { constraintId, error }
306
- });
591
+ const gaps = this.validateDescriptorContract(id, descriptor);
592
+ for (const gap of gaps) {
593
+ this.contractGaps.push(gap);
594
+ if (this.compliance.onGap) {
595
+ this.compliance.onGap(gap);
596
+ } else {
597
+ const label = gap.severity === "error" ? "ERROR" : gap.severity === "warning" ? "WARN" : "INFO";
598
+ console.warn(`[Praxis][${label}] Contract gap for "${gap.ruleId}": missing ${gap.missing.join(", ")}`);
307
599
  }
308
600
  }
309
- this.state = newState;
310
- return {
311
- state: newState,
312
- diagnostics
313
- };
314
- }
315
- /**
316
- * Update the context directly (for exceptional cases).
317
- * Generally, context should be updated through rules.
318
- *
319
- * @param updater Function that produces new context from old context
320
- */
321
- updateContext(updater) {
322
- this.state = {
323
- ...this.state,
324
- context: updater(this.state.context)
325
- };
326
601
  }
327
- /**
328
- * Add facts directly (for exceptional cases).
329
- * Generally, facts should be added through rules.
330
- *
331
- * @param facts Facts to add
332
- */
333
- addFacts(facts) {
334
- this.state = {
335
- ...this.state,
336
- facts: [...this.state.facts, ...facts]
337
- };
338
- }
339
- /**
340
- * Clear all facts
341
- */
342
- clearFacts() {
343
- this.state = {
344
- ...this.state,
345
- facts: []
346
- };
347
- }
348
- /**
349
- * Reset the engine to initial state
350
- */
351
- reset(options) {
352
- this.state = {
353
- context: options.initialContext,
354
- facts: options.initialFacts ?? [],
355
- meta: options.initialMeta ?? {},
356
- protocolVersion: PRAXIS_PROTOCOL_VERSION
357
- };
602
+ validateDescriptorContract(id, descriptor) {
603
+ const requiredFields = this.compliance.requiredFields ?? ["behavior", "examples", "invariants"];
604
+ const missingSeverity = this.compliance.missingSeverity ?? "warning";
605
+ const contract = descriptor.contract ?? (descriptor.meta?.contract && typeof descriptor.meta.contract === "object" ? descriptor.meta.contract : void 0);
606
+ if (!contract) {
607
+ return [
608
+ {
609
+ ruleId: id,
610
+ missing: ["contract"],
611
+ severity: missingSeverity,
612
+ message: `Contract missing for "${id}"`
613
+ }
614
+ ];
615
+ }
616
+ const missing = [];
617
+ if (requiredFields.includes("behavior") && (!contract.behavior || contract.behavior.trim() === "")) {
618
+ missing.push("behavior");
619
+ }
620
+ if (requiredFields.includes("examples") && (!contract.examples || contract.examples.length === 0)) {
621
+ missing.push("examples");
622
+ }
623
+ if (requiredFields.includes("invariants") && (!contract.invariants || contract.invariants.length === 0)) {
624
+ missing.push("invariants");
625
+ }
626
+ if (missing.length === 0) {
627
+ return [];
628
+ }
629
+ return [
630
+ {
631
+ ruleId: id,
632
+ missing,
633
+ severity: "warning",
634
+ message: `Contract for "${id}" is incomplete: missing ${missing.join(", ")}`
635
+ }
636
+ ];
358
637
  }
359
638
  };
360
- function createPraxisEngine(options) {
361
- return new LogicEngine(options);
362
- }
639
+
640
+ // src/index.ts
641
+ init_engine();
363
642
 
364
643
  // src/core/reactive-engine.svelte.ts
365
644
  var $ = __toESM(require("svelte/internal/client"), 1);
645
+ init_engine();
366
646
  var ReactiveLogicEngine = class {
367
647
  #state = (
368
648
  // Use Svelte's $state rune for automatic reactivity
@@ -999,19 +1279,25 @@ function defineEvent(tag) {
999
1279
  };
1000
1280
  }
1001
1281
  function defineRule(options) {
1282
+ const contract = options.contract ?? options.meta?.contract;
1283
+ const meta = contract ? { ...options.meta ?? {}, contract } : options.meta;
1002
1284
  return {
1003
1285
  id: options.id,
1004
1286
  description: options.description,
1005
1287
  impl: options.impl,
1006
- meta: options.meta
1288
+ contract,
1289
+ meta
1007
1290
  };
1008
1291
  }
1009
1292
  function defineConstraint(options) {
1293
+ const contract = options.contract ?? options.meta?.contract;
1294
+ const meta = contract ? { ...options.meta ?? {}, contract } : options.meta;
1010
1295
  return {
1011
1296
  id: options.id,
1012
1297
  description: options.description,
1013
1298
  impl: options.impl,
1014
- meta: options.meta
1299
+ contract,
1300
+ meta
1015
1301
  };
1016
1302
  }
1017
1303
  function defineModule(options) {
@@ -1034,6 +1320,461 @@ function findFact(facts, definition) {
1034
1320
  return facts.find(definition.is);
1035
1321
  }
1036
1322
 
1323
+ // src/decision-ledger/types.ts
1324
+ function isContract(obj) {
1325
+ if (typeof obj !== "object" || obj === null) {
1326
+ return false;
1327
+ }
1328
+ const contract = obj;
1329
+ return typeof contract.ruleId === "string" && typeof contract.behavior === "string" && Array.isArray(contract.examples) && contract.examples.length > 0 && contract.examples.every(
1330
+ (ex) => typeof ex === "object" && ex !== null && typeof ex.given === "string" && typeof ex.when === "string" && typeof ex.then === "string"
1331
+ ) && Array.isArray(contract.invariants) && contract.invariants.every((inv) => typeof inv === "string");
1332
+ }
1333
+ function defineContract(options) {
1334
+ if (options.examples.length === 0) {
1335
+ throw new Error("Contract must have at least one example");
1336
+ }
1337
+ if (options.assumptions) {
1338
+ for (const assumption of options.assumptions) {
1339
+ if (assumption.confidence < 0 || assumption.confidence > 1) {
1340
+ throw new Error(
1341
+ `Assumption '${assumption.id}' has invalid confidence value ${assumption.confidence}. Must be between 0.0 and 1.0`
1342
+ );
1343
+ }
1344
+ }
1345
+ }
1346
+ return {
1347
+ ruleId: options.ruleId,
1348
+ behavior: options.behavior,
1349
+ examples: options.examples,
1350
+ invariants: options.invariants,
1351
+ assumptions: options.assumptions,
1352
+ references: options.references,
1353
+ version: options.version || "1.0.0",
1354
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1355
+ };
1356
+ }
1357
+ function getContract(meta) {
1358
+ if (!meta || !meta.contract) {
1359
+ return void 0;
1360
+ }
1361
+ if (isContract(meta.contract)) {
1362
+ return meta.contract;
1363
+ }
1364
+ return void 0;
1365
+ }
1366
+ function getContractFromDescriptor(descriptor) {
1367
+ if (!descriptor) {
1368
+ return void 0;
1369
+ }
1370
+ if (descriptor.contract && isContract(descriptor.contract)) {
1371
+ return descriptor.contract;
1372
+ }
1373
+ return getContract(descriptor.meta);
1374
+ }
1375
+
1376
+ // src/decision-ledger/facts-events.ts
1377
+ var ContractMissing = defineFact("ContractMissing");
1378
+ var ContractValidated = defineFact("ContractValidated");
1379
+ var AcknowledgeContractGap = defineEvent("ACKNOWLEDGE_CONTRACT_GAP");
1380
+ var ValidateContracts = defineEvent("VALIDATE_CONTRACTS");
1381
+ var ContractGapAcknowledged = defineFact("ContractGapAcknowledged");
1382
+ var ContractAdded = defineEvent("CONTRACT_ADDED");
1383
+ var ContractUpdated = defineEvent("CONTRACT_UPDATED");
1384
+ var ContractGapEmitted = defineEvent("CONTRACT_GAP_EMITTED");
1385
+
1386
+ // src/decision-ledger/validation.ts
1387
+ function validateContracts(registry, options = {}) {
1388
+ const {
1389
+ incompleteSeverity = "warning",
1390
+ requiredFields = ["behavior", "examples"],
1391
+ artifactIndex
1392
+ } = options;
1393
+ const complete = [];
1394
+ const incomplete = [];
1395
+ const missing = [];
1396
+ for (const rule of registry.getAllRules()) {
1397
+ const contract = getContractFromDescriptor(rule);
1398
+ if (!contract) {
1399
+ missing.push(rule.id);
1400
+ if (options.missingSeverity) {
1401
+ incomplete.push({
1402
+ ruleId: rule.id,
1403
+ missing: ["contract"],
1404
+ severity: options.missingSeverity,
1405
+ message: `Rule '${rule.id}' has no contract`
1406
+ });
1407
+ }
1408
+ continue;
1409
+ }
1410
+ const gaps = validateContract(contract, requiredFields, artifactIndex);
1411
+ if (gaps.length > 0) {
1412
+ incomplete.push({
1413
+ ruleId: rule.id,
1414
+ missing: gaps,
1415
+ severity: incompleteSeverity,
1416
+ message: `Rule '${rule.id}' contract is incomplete: missing ${gaps.join(", ")}`
1417
+ });
1418
+ } else {
1419
+ complete.push({ ruleId: rule.id, contract });
1420
+ }
1421
+ }
1422
+ for (const constraint of registry.getAllConstraints()) {
1423
+ const contract = getContractFromDescriptor(constraint);
1424
+ if (!contract) {
1425
+ missing.push(constraint.id);
1426
+ if (options.missingSeverity) {
1427
+ incomplete.push({
1428
+ ruleId: constraint.id,
1429
+ missing: ["contract"],
1430
+ severity: options.missingSeverity,
1431
+ message: `Constraint '${constraint.id}' has no contract`
1432
+ });
1433
+ }
1434
+ continue;
1435
+ }
1436
+ const gaps = validateContract(contract, requiredFields, artifactIndex);
1437
+ if (gaps.length > 0) {
1438
+ incomplete.push({
1439
+ ruleId: constraint.id,
1440
+ missing: gaps,
1441
+ severity: incompleteSeverity,
1442
+ message: `Constraint '${constraint.id}' contract is incomplete: missing ${gaps.join(", ")}`
1443
+ });
1444
+ } else {
1445
+ complete.push({ ruleId: constraint.id, contract });
1446
+ }
1447
+ }
1448
+ const total = registry.getAllRules().length + registry.getAllConstraints().length;
1449
+ return {
1450
+ complete,
1451
+ incomplete,
1452
+ missing,
1453
+ total,
1454
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1455
+ };
1456
+ }
1457
+ function validateContract(contract, requiredFields, artifactIndex) {
1458
+ const missing = [];
1459
+ if (requiredFields.includes("behavior") && isFieldEmpty(contract.behavior)) {
1460
+ missing.push("behavior");
1461
+ }
1462
+ if (requiredFields.includes("examples") && (!contract.examples || contract.examples.length === 0)) {
1463
+ missing.push("examples");
1464
+ }
1465
+ if (requiredFields.includes("invariants") && (!contract.invariants || contract.invariants.length === 0)) {
1466
+ missing.push("invariants");
1467
+ }
1468
+ if (artifactIndex?.tests && !artifactIndex.tests.has(contract.ruleId)) {
1469
+ missing.push("tests");
1470
+ }
1471
+ if (artifactIndex?.spec && !artifactIndex.spec.has(contract.ruleId)) {
1472
+ missing.push("spec");
1473
+ }
1474
+ return missing;
1475
+ }
1476
+ function isFieldEmpty(value) {
1477
+ return !value || value.trim() === "";
1478
+ }
1479
+ function formatValidationReport(report) {
1480
+ const lines = [];
1481
+ lines.push("Contract Validation Report");
1482
+ lines.push("=".repeat(50));
1483
+ lines.push("");
1484
+ lines.push(`Total: ${report.total}`);
1485
+ lines.push(`Complete: ${report.complete.length}`);
1486
+ lines.push(`Incomplete: ${report.incomplete.length}`);
1487
+ lines.push(`Missing: ${report.missing.length}`);
1488
+ lines.push("");
1489
+ if (report.complete.length > 0) {
1490
+ lines.push("\u2713 Complete Contracts:");
1491
+ for (const { ruleId, contract } of report.complete) {
1492
+ lines.push(` \u2713 ${ruleId} (v${contract.version || "1.0.0"})`);
1493
+ }
1494
+ lines.push("");
1495
+ }
1496
+ if (report.incomplete.length > 0) {
1497
+ lines.push("\u2717 Incomplete Contracts:");
1498
+ for (const gap of report.incomplete) {
1499
+ const icon = gap.severity === "error" ? "\u2717" : gap.severity === "warning" ? "\u26A0" : "\u2139";
1500
+ lines.push(` ${icon} ${gap.ruleId} - Missing: ${gap.missing.join(", ")}`);
1501
+ if (gap.message) {
1502
+ lines.push(` ${gap.message}`);
1503
+ }
1504
+ }
1505
+ lines.push("");
1506
+ }
1507
+ if (report.missing.length > 0) {
1508
+ lines.push("\u2717 No Contract:");
1509
+ for (const ruleId of report.missing) {
1510
+ lines.push(` \u2717 ${ruleId}`);
1511
+ }
1512
+ lines.push("");
1513
+ }
1514
+ lines.push(`Validated at: ${report.timestamp}`);
1515
+ return lines.join("\n");
1516
+ }
1517
+ function formatValidationReportJSON(report) {
1518
+ return JSON.stringify(report, null, 2);
1519
+ }
1520
+ function formatValidationReportSARIF(report) {
1521
+ const results = report.incomplete.map((gap) => {
1522
+ const primaryMissing = gap.missing.length > 0 ? gap.missing[0] : "contract";
1523
+ return {
1524
+ ruleId: `decision-ledger/${primaryMissing}`,
1525
+ level: gap.severity === "error" ? "error" : gap.severity === "warning" ? "warning" : "note",
1526
+ message: {
1527
+ text: gap.message || `Missing: ${gap.missing.join(", ")}`
1528
+ },
1529
+ locations: [
1530
+ {
1531
+ physicalLocation: {
1532
+ artifactLocation: {
1533
+ uri: "registry"
1534
+ },
1535
+ region: {
1536
+ startLine: 1
1537
+ }
1538
+ }
1539
+ }
1540
+ ],
1541
+ properties: {
1542
+ ruleId: gap.ruleId,
1543
+ missing: gap.missing
1544
+ }
1545
+ };
1546
+ });
1547
+ const sarif = {
1548
+ version: "2.1.0",
1549
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
1550
+ runs: [
1551
+ {
1552
+ tool: {
1553
+ driver: {
1554
+ name: "Praxis Decision Ledger",
1555
+ version: "1.0.0",
1556
+ informationUri: "https://github.com/plures/praxis",
1557
+ rules: [
1558
+ {
1559
+ id: "decision-ledger/contract",
1560
+ shortDescription: {
1561
+ text: "Rule or constraint missing contract"
1562
+ }
1563
+ },
1564
+ {
1565
+ id: "decision-ledger/behavior",
1566
+ shortDescription: {
1567
+ text: "Contract missing behavior description"
1568
+ }
1569
+ },
1570
+ {
1571
+ id: "decision-ledger/examples",
1572
+ shortDescription: {
1573
+ text: "Contract missing examples"
1574
+ }
1575
+ },
1576
+ {
1577
+ id: "decision-ledger/invariants",
1578
+ shortDescription: {
1579
+ text: "Contract missing invariants"
1580
+ }
1581
+ },
1582
+ {
1583
+ id: "decision-ledger/tests",
1584
+ shortDescription: {
1585
+ text: "Contract missing tests"
1586
+ }
1587
+ },
1588
+ {
1589
+ id: "decision-ledger/spec",
1590
+ shortDescription: {
1591
+ text: "Contract missing spec"
1592
+ }
1593
+ }
1594
+ ]
1595
+ }
1596
+ },
1597
+ results
1598
+ }
1599
+ ]
1600
+ };
1601
+ return JSON.stringify(sarif, null, 2);
1602
+ }
1603
+
1604
+ // src/decision-ledger/ledger.ts
1605
+ var BehaviorLedger = class _BehaviorLedger {
1606
+ entries = [];
1607
+ entryMap = /* @__PURE__ */ new Map();
1608
+ /**
1609
+ * Append a new entry to the ledger.
1610
+ *
1611
+ * @param entry The entry to append
1612
+ * @throws Error if entry ID already exists
1613
+ */
1614
+ append(entry) {
1615
+ if (this.entryMap.has(entry.id)) {
1616
+ throw new Error(`Ledger entry with ID '${entry.id}' already exists`);
1617
+ }
1618
+ if (entry.supersedes) {
1619
+ const superseded = this.entryMap.get(entry.supersedes);
1620
+ if (superseded && superseded.status === "active") {
1621
+ const updatedEntry = {
1622
+ ...superseded,
1623
+ status: "superseded"
1624
+ };
1625
+ this.entryMap.set(entry.supersedes, updatedEntry);
1626
+ }
1627
+ }
1628
+ this.entries.push(entry);
1629
+ this.entryMap.set(entry.id, entry);
1630
+ }
1631
+ /**
1632
+ * Get an entry by ID.
1633
+ *
1634
+ * @param id The entry ID
1635
+ * @returns The entry, or undefined if not found
1636
+ */
1637
+ getEntry(id) {
1638
+ return this.entryMap.get(id);
1639
+ }
1640
+ /**
1641
+ * Get all entries (in order of append) with current status.
1642
+ *
1643
+ * @returns Array of all entries with current status from the map
1644
+ */
1645
+ getAllEntries() {
1646
+ return this.entries.map((entry) => this.entryMap.get(entry.id));
1647
+ }
1648
+ /**
1649
+ * Get entries for a specific rule ID.
1650
+ *
1651
+ * @param ruleId The rule ID
1652
+ * @returns Array of entries for this rule with current status
1653
+ */
1654
+ getEntriesForRule(ruleId) {
1655
+ return this.entries.map((entry) => this.entryMap.get(entry.id)).filter((entry) => entry.contract.ruleId === ruleId);
1656
+ }
1657
+ /**
1658
+ * Get the latest active entry for a rule.
1659
+ *
1660
+ * @param ruleId The rule ID
1661
+ * @returns The latest active entry, or undefined if none
1662
+ */
1663
+ getLatestEntry(ruleId) {
1664
+ const entries = this.getEntriesForRule(ruleId);
1665
+ const activeEntries = entries.filter((entry) => entry.status === "active");
1666
+ if (activeEntries.length === 0) {
1667
+ return void 0;
1668
+ }
1669
+ return activeEntries[activeEntries.length - 1];
1670
+ }
1671
+ /**
1672
+ * Get all active assumptions across all entries.
1673
+ *
1674
+ * @returns Map of assumption ID to assumption
1675
+ */
1676
+ getActiveAssumptions() {
1677
+ const assumptions = /* @__PURE__ */ new Map();
1678
+ for (const entry of this.entries) {
1679
+ const currentEntry = this.entryMap.get(entry.id);
1680
+ if (currentEntry.status !== "active") {
1681
+ continue;
1682
+ }
1683
+ for (const assumption of currentEntry.contract.assumptions || []) {
1684
+ if (assumption.status === "active") {
1685
+ assumptions.set(assumption.id, assumption);
1686
+ }
1687
+ }
1688
+ }
1689
+ return assumptions;
1690
+ }
1691
+ /**
1692
+ * Find assumptions that impact a specific artifact type.
1693
+ *
1694
+ * @param impactType The artifact type ('spec', 'tests', 'code')
1695
+ * @returns Array of assumptions
1696
+ */
1697
+ findAssumptionsByImpact(impactType) {
1698
+ const assumptions = [];
1699
+ for (const entry of this.entries) {
1700
+ const currentEntry = this.entryMap.get(entry.id);
1701
+ if (currentEntry.status !== "active") {
1702
+ continue;
1703
+ }
1704
+ for (const assumption of currentEntry.contract.assumptions || []) {
1705
+ if (assumption.status === "active" && assumption.impacts.includes(impactType)) {
1706
+ assumptions.push(assumption);
1707
+ }
1708
+ }
1709
+ }
1710
+ return assumptions;
1711
+ }
1712
+ /**
1713
+ * Get ledger statistics.
1714
+ */
1715
+ getStats() {
1716
+ const currentEntries = this.entries.map((e) => this.entryMap.get(e.id));
1717
+ const active = currentEntries.filter((e) => e.status === "active").length;
1718
+ const superseded = currentEntries.filter((e) => e.status === "superseded").length;
1719
+ const deprecated = currentEntries.filter((e) => e.status === "deprecated").length;
1720
+ const uniqueRules = new Set(currentEntries.map((e) => e.contract.ruleId)).size;
1721
+ return {
1722
+ totalEntries: this.entries.length,
1723
+ activeEntries: active,
1724
+ supersededEntries: superseded,
1725
+ deprecatedEntries: deprecated,
1726
+ uniqueRules
1727
+ };
1728
+ }
1729
+ /**
1730
+ * Export ledger as JSON.
1731
+ *
1732
+ * @returns JSON string with current entry status
1733
+ */
1734
+ toJSON() {
1735
+ return JSON.stringify(
1736
+ {
1737
+ version: "1.0.0",
1738
+ // Export entries with current status from the map
1739
+ entries: this.entries.map((entry) => this.entryMap.get(entry.id)),
1740
+ stats: this.getStats()
1741
+ },
1742
+ null,
1743
+ 2
1744
+ );
1745
+ }
1746
+ /**
1747
+ * Import ledger from JSON.
1748
+ *
1749
+ * Note: The JSON must contain entries in the order they were originally appended.
1750
+ * If a superseding entry appears before the entry it supersedes, the superseding
1751
+ * logic will not work correctly. The toJSON method preserves this order.
1752
+ *
1753
+ * @param json The JSON string
1754
+ * @returns A new BehaviorLedger instance
1755
+ */
1756
+ static fromJSON(json) {
1757
+ const data = JSON.parse(json);
1758
+ const ledger = new _BehaviorLedger();
1759
+ for (const entry of data.entries || []) {
1760
+ ledger.append(entry);
1761
+ }
1762
+ return ledger;
1763
+ }
1764
+ };
1765
+ function createBehaviorLedger() {
1766
+ return new BehaviorLedger();
1767
+ }
1768
+
1769
+ // src/decision-ledger/logic-ledger.ts
1770
+ var import_node_crypto = require("crypto");
1771
+ var import_node_fs = require("fs");
1772
+ var import_node_path = __toESM(require("path"), 1);
1773
+
1774
+ // src/decision-ledger/scanner.ts
1775
+ var import_node_fs2 = require("fs");
1776
+ var import_node_path2 = __toESM(require("path"), 1);
1777
+
1037
1778
  // src/runtime/terminal-adapter.ts
1038
1779
  async function defaultExecutor(command, options) {
1039
1780
  try {
@@ -1154,15 +1895,15 @@ var TerminalAdapter = class {
1154
1895
  * @param path - PluresDB path for storing results
1155
1896
  * @param data - Data to sync
1156
1897
  */
1157
- async syncToPluresDB(path, data) {
1898
+ async syncToPluresDB(path3, data) {
1158
1899
  if (!this.db) return;
1159
1900
  try {
1160
- await this.db.set(path, {
1901
+ await this.db.set(path3, {
1161
1902
  ...data,
1162
1903
  nodeId: this.state.nodeId,
1163
1904
  syncedAt: Date.now()
1164
1905
  });
1165
- const historyPath = `${path}/history`;
1906
+ const historyPath = `${path3}/history`;
1166
1907
  const historyKey = `${historyPath}/${data.timestamp}`;
1167
1908
  await this.db.set(historyKey, data);
1168
1909
  } catch (error) {
@@ -1631,8 +2372,8 @@ var PraxisDBStore = class {
1631
2372
  async persistFact(fact) {
1632
2373
  const payload = fact.payload;
1633
2374
  const id = payload?.id ?? generateId();
1634
- const path = getFactPath(fact.tag, id);
1635
- await this.db.set(path, fact);
2375
+ const path3 = getFactPath(fact.tag, id);
2376
+ await this.db.set(path3, fact);
1636
2377
  }
1637
2378
  /**
1638
2379
  * Get a fact by tag and id
@@ -1642,8 +2383,8 @@ var PraxisDBStore = class {
1642
2383
  * @returns The fact or undefined if not found
1643
2384
  */
1644
2385
  async getFact(factTag, id) {
1645
- const path = getFactPath(factTag, id);
1646
- return this.db.get(path);
2386
+ const path3 = getFactPath(factTag, id);
2387
+ return this.db.get(path3);
1647
2388
  }
1648
2389
  /**
1649
2390
  * Append an event to the event stream
@@ -1653,15 +2394,15 @@ var PraxisDBStore = class {
1653
2394
  * @param event The event to append
1654
2395
  */
1655
2396
  async appendEvent(event) {
1656
- const path = getEventPath(event.tag);
1657
- const existingEvents = await this.db.get(path) ?? [];
2397
+ const path3 = getEventPath(event.tag);
2398
+ const existingEvents = await this.db.get(path3) ?? [];
1658
2399
  const entry = {
1659
2400
  event,
1660
2401
  timestamp: Date.now(),
1661
2402
  sequence: existingEvents.length
1662
2403
  };
1663
2404
  const newEvents = [...existingEvents, entry];
1664
- await this.db.set(path, newEvents);
2405
+ await this.db.set(path3, newEvents);
1665
2406
  await this.triggerRulesForEvents([event]);
1666
2407
  }
1667
2408
  /**
@@ -1676,15 +2417,15 @@ var PraxisDBStore = class {
1676
2417
  eventsByTag.set(event.tag, [...existing, event]);
1677
2418
  }
1678
2419
  for (const [tag, tagEvents] of eventsByTag) {
1679
- const path = getEventPath(tag);
1680
- const existingEvents = await this.db.get(path) ?? [];
2420
+ const path3 = getEventPath(tag);
2421
+ const existingEvents = await this.db.get(path3) ?? [];
1681
2422
  let sequence = existingEvents.length;
1682
2423
  const newEntries = tagEvents.map((event) => ({
1683
2424
  event,
1684
2425
  timestamp: Date.now(),
1685
2426
  sequence: sequence++
1686
2427
  }));
1687
- await this.db.set(path, [...existingEvents, ...newEntries]);
2428
+ await this.db.set(path3, [...existingEvents, ...newEntries]);
1688
2429
  }
1689
2430
  await this.triggerRulesForEvents(events);
1690
2431
  }
@@ -1696,8 +2437,8 @@ var PraxisDBStore = class {
1696
2437
  * @returns Array of event stream entries
1697
2438
  */
1698
2439
  async getEvents(eventTag, options) {
1699
- const path = getEventPath(eventTag);
1700
- const events = await this.db.get(path) ?? [];
2440
+ const path3 = getEventPath(eventTag);
2441
+ const events = await this.db.get(path3) ?? [];
1701
2442
  let result = events;
1702
2443
  if (options?.since !== void 0) {
1703
2444
  const sinceTimestamp = options.since;
@@ -1716,7 +2457,7 @@ var PraxisDBStore = class {
1716
2457
  * @returns Unsubscribe function
1717
2458
  */
1718
2459
  watchFacts(factTag, callback) {
1719
- const path = getFactPath(factTag);
2460
+ const path3 = getFactPath(factTag);
1720
2461
  if (!this.factWatchers.has(factTag)) {
1721
2462
  this.factWatchers.set(factTag, /* @__PURE__ */ new Set());
1722
2463
  }
@@ -1724,7 +2465,7 @@ var PraxisDBStore = class {
1724
2465
  if (watchers) {
1725
2466
  watchers.add(callback);
1726
2467
  }
1727
- const unsubscribe = this.db.watch(path, (fact) => {
2468
+ const unsubscribe = this.db.watch(path3, (fact) => {
1728
2469
  callback([fact]);
1729
2470
  });
1730
2471
  this.subscriptions.push(unsubscribe);
@@ -1830,140 +2571,8 @@ function createPraxisDBStore(db, registry, initialContext, onRuleError) {
1830
2571
  return new PraxisDBStore({ db, registry, initialContext, onRuleError });
1831
2572
  }
1832
2573
 
1833
- // src/core/pluresdb/adapter.ts
1834
- var InMemoryPraxisDB = class {
1835
- store = /* @__PURE__ */ new Map();
1836
- watchers = /* @__PURE__ */ new Map();
1837
- async get(key) {
1838
- return this.store.get(key);
1839
- }
1840
- async set(key, value) {
1841
- this.store.set(key, value);
1842
- const keyWatchers = this.watchers.get(key);
1843
- if (keyWatchers) {
1844
- for (const callback of keyWatchers) {
1845
- callback(value);
1846
- }
1847
- }
1848
- }
1849
- watch(key, callback) {
1850
- if (!this.watchers.has(key)) {
1851
- this.watchers.set(key, /* @__PURE__ */ new Set());
1852
- }
1853
- const watchers = this.watchers.get(key);
1854
- const wrappedCallback = (val) => callback(val);
1855
- watchers.add(wrappedCallback);
1856
- return () => {
1857
- watchers.delete(wrappedCallback);
1858
- if (watchers.size === 0) {
1859
- this.watchers.delete(key);
1860
- }
1861
- };
1862
- }
1863
- /**
1864
- * Get all keys (for testing/debugging)
1865
- */
1866
- keys() {
1867
- return Array.from(this.store.keys());
1868
- }
1869
- /**
1870
- * Clear all data (for testing)
1871
- */
1872
- clear() {
1873
- this.store.clear();
1874
- this.watchers.clear();
1875
- }
1876
- };
1877
- function createInMemoryDB() {
1878
- return new InMemoryPraxisDB();
1879
- }
1880
- var PluresDBPraxisAdapter = class {
1881
- db;
1882
- watchers = /* @__PURE__ */ new Map();
1883
- pollIntervals = /* @__PURE__ */ new Map();
1884
- lastValues = /* @__PURE__ */ new Map();
1885
- pollInterval;
1886
- constructor(config) {
1887
- if ("get" in config && "put" in config) {
1888
- this.db = config;
1889
- this.pollInterval = 1e3;
1890
- } else {
1891
- this.db = config.db;
1892
- this.pollInterval = config.pollInterval ?? 1e3;
1893
- }
1894
- }
1895
- async get(key) {
1896
- try {
1897
- const value = await this.db.get(key);
1898
- return value;
1899
- } catch (error) {
1900
- return void 0;
1901
- }
1902
- }
1903
- async set(key, value) {
1904
- await this.db.put(key, value);
1905
- this.lastValues.set(key, value);
1906
- const keyWatchers = this.watchers.get(key);
1907
- if (keyWatchers) {
1908
- for (const callback of keyWatchers) {
1909
- callback(value);
1910
- }
1911
- }
1912
- }
1913
- watch(key, callback) {
1914
- if (!this.watchers.has(key)) {
1915
- this.watchers.set(key, /* @__PURE__ */ new Set());
1916
- }
1917
- const watchers = this.watchers.get(key);
1918
- const wrappedCallback = (val) => callback(val);
1919
- watchers.add(wrappedCallback);
1920
- if (!this.pollIntervals.has(key)) {
1921
- const interval = setInterval(async () => {
1922
- try {
1923
- const value = await this.db.get(key);
1924
- const lastValue = this.lastValues.get(key);
1925
- if (JSON.stringify(value) !== JSON.stringify(lastValue)) {
1926
- this.lastValues.set(key, value);
1927
- const currentWatchers = this.watchers.get(key);
1928
- if (currentWatchers) {
1929
- for (const cb of currentWatchers) {
1930
- cb(value);
1931
- }
1932
- }
1933
- }
1934
- } catch (error) {
1935
- }
1936
- }, this.pollInterval);
1937
- this.pollIntervals.set(key, interval);
1938
- }
1939
- return () => {
1940
- watchers.delete(wrappedCallback);
1941
- if (watchers.size === 0) {
1942
- this.watchers.delete(key);
1943
- const interval = this.pollIntervals.get(key);
1944
- if (interval) {
1945
- clearInterval(interval);
1946
- this.pollIntervals.delete(key);
1947
- }
1948
- this.lastValues.delete(key);
1949
- }
1950
- };
1951
- }
1952
- /**
1953
- * Clean up all resources
1954
- */
1955
- dispose() {
1956
- for (const interval of this.pollIntervals.values()) {
1957
- clearInterval(interval);
1958
- }
1959
- this.pollIntervals.clear();
1960
- this.watchers.clear();
1961
- this.lastValues.clear();
1962
- }
1963
- };
1964
- function createPluresDB(config) {
1965
- return new PluresDBPraxisAdapter(config);
1966
- }
2574
+ // src/integrations/pluresdb.ts
2575
+ init_adapter();
1967
2576
 
1968
2577
  // src/core/pluresdb/schema-registry.ts
1969
2578
  function getSchemaPath(schemaName) {
@@ -1980,13 +2589,13 @@ var PraxisSchemaRegistry = class {
1980
2589
  * @param schema The schema to register
1981
2590
  */
1982
2591
  async register(schema) {
1983
- const path = getSchemaPath(schema.name);
2592
+ const path3 = getSchemaPath(schema.name);
1984
2593
  const storedSchema = {
1985
2594
  schema,
1986
2595
  registeredAt: Date.now(),
1987
2596
  version: schema.version
1988
2597
  };
1989
- await this.db.set(path, storedSchema);
2598
+ await this.db.set(path3, storedSchema);
1990
2599
  }
1991
2600
  /**
1992
2601
  * Get a schema by name
@@ -1995,8 +2604,8 @@ var PraxisSchemaRegistry = class {
1995
2604
  * @returns The stored schema or undefined if not found
1996
2605
  */
1997
2606
  async get(schemaName) {
1998
- const path = getSchemaPath(schemaName);
1999
- return this.db.get(path);
2607
+ const path3 = getSchemaPath(schemaName);
2608
+ return this.db.get(path3);
2000
2609
  }
2001
2610
  /**
2002
2611
  * Check if a schema is registered
@@ -3304,29 +3913,29 @@ function createMockTauriBridge() {
3304
3913
  tauriVersion: "mock"
3305
3914
  },
3306
3915
  fs: {
3307
- async readFile(path) {
3308
- const data = storage.get(path);
3916
+ async readFile(path3) {
3917
+ const data = storage.get(path3);
3309
3918
  if (data instanceof Uint8Array) return data;
3310
- throw new Error(`File not found: ${path}`);
3919
+ throw new Error(`File not found: ${path3}`);
3311
3920
  },
3312
- async readTextFile(path) {
3313
- const data = storage.get(path);
3921
+ async readTextFile(path3) {
3922
+ const data = storage.get(path3);
3314
3923
  if (typeof data === "string") return data;
3315
- throw new Error(`File not found: ${path}`);
3924
+ throw new Error(`File not found: ${path3}`);
3316
3925
  },
3317
- async writeFile(path, data) {
3318
- storage.set(path, data);
3926
+ async writeFile(path3, data) {
3927
+ storage.set(path3, data);
3319
3928
  },
3320
- async writeTextFile(path, data) {
3321
- storage.set(path, data);
3929
+ async writeTextFile(path3, data) {
3930
+ storage.set(path3, data);
3322
3931
  },
3323
- async exists(path) {
3324
- return storage.has(path);
3932
+ async exists(path3) {
3933
+ return storage.has(path3);
3325
3934
  },
3326
3935
  async mkdir(_path, _options) {
3327
3936
  },
3328
- async remove(path, _options) {
3329
- storage.delete(path);
3937
+ async remove(path3, _options) {
3938
+ storage.delete(path3);
3330
3939
  },
3331
3940
  async rename(oldPath, newPath) {
3332
3941
  const data = storage.get(oldPath);
@@ -3512,9 +4121,148 @@ function generateTauriConfig(config) {
3512
4121
  plugins: Object.fromEntries((config.plugins || []).map((p) => [p.name, p.config || {}]))
3513
4122
  };
3514
4123
  }
4124
+
4125
+ // src/integrations/unified.ts
4126
+ async function createUnifiedApp(config) {
4127
+ const { createPraxisEngine: createPraxisEngine2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
4128
+ const { createInMemoryDB: createInMemoryDB2 } = await Promise.resolve().then(() => (init_adapter(), adapter_exports));
4129
+ const db = config.db || createInMemoryDB2();
4130
+ const pluresdb = createPluresDBAdapter({
4131
+ db,
4132
+ registry: config.registry,
4133
+ initialContext: config.initialContext
4134
+ });
4135
+ const engine = createPraxisEngine2({
4136
+ initialContext: config.initialContext,
4137
+ registry: config.registry
4138
+ });
4139
+ pluresdb.attachEngine(engine);
4140
+ const disposers = [];
4141
+ let unum;
4142
+ let channel;
4143
+ if (config.enableUnum) {
4144
+ const fullIdentity = config.unumIdentity ? {
4145
+ ...config.unumIdentity,
4146
+ id: generateId(),
4147
+ createdAt: Date.now()
4148
+ } : void 0;
4149
+ unum = await createUnumAdapter({
4150
+ db,
4151
+ identity: fullIdentity,
4152
+ realtime: true
4153
+ });
4154
+ channel = await unum.createChannel(
4155
+ config.unumIdentity?.name || "praxis-app",
4156
+ []
4157
+ );
4158
+ const unumDisposer = attachUnumToEngine(engine, unum, channel.id);
4159
+ disposers.push(unumDisposer);
4160
+ }
4161
+ let docs;
4162
+ let generateDocs2;
4163
+ if (config.enableDocs && config.docsConfig) {
4164
+ docs = createStateDocsGenerator({
4165
+ projectTitle: config.docsConfig.projectTitle,
4166
+ target: config.docsConfig.target || "./docs"
4167
+ });
4168
+ generateDocs2 = () => {
4169
+ const module2 = {
4170
+ rules: config.registry.getAllRules(),
4171
+ constraints: config.registry.getAllConstraints()
4172
+ };
4173
+ return docs.generateFromModule(module2);
4174
+ };
4175
+ }
4176
+ let canvas;
4177
+ if (config.schema) {
4178
+ canvas = schemaToCanvas(config.schema, { layout: "hierarchical" });
4179
+ }
4180
+ return {
4181
+ engine,
4182
+ pluresdb,
4183
+ unum,
4184
+ channel,
4185
+ docs,
4186
+ canvas,
4187
+ generateDocs: generateDocs2,
4188
+ dispose: () => {
4189
+ pluresdb.dispose();
4190
+ if (unum) {
4191
+ unum.disconnect().catch((err) => {
4192
+ console.warn("Warning: Error during Unum disconnect:", err);
4193
+ });
4194
+ }
4195
+ for (const disposer of disposers) {
4196
+ disposer();
4197
+ }
4198
+ }
4199
+ };
4200
+ }
4201
+ async function attachAllIntegrations(engine, registry, options = {}) {
4202
+ const { createInMemoryDB: createInMemoryDB2 } = await Promise.resolve().then(() => (init_adapter(), adapter_exports));
4203
+ const db = options.db || createInMemoryDB2();
4204
+ const pluresdb = createPluresDBAdapter({
4205
+ db,
4206
+ registry,
4207
+ initialContext: engine.getContext()
4208
+ });
4209
+ pluresdb.attachEngine(engine);
4210
+ const disposers = [];
4211
+ let unum;
4212
+ let channel;
4213
+ if (options.enableUnum) {
4214
+ const fullIdentity = options.unumIdentity ? {
4215
+ ...options.unumIdentity,
4216
+ id: generateId(),
4217
+ createdAt: Date.now()
4218
+ } : void 0;
4219
+ unum = await createUnumAdapter({
4220
+ db,
4221
+ identity: fullIdentity,
4222
+ realtime: true
4223
+ });
4224
+ channel = await unum.createChannel(
4225
+ options.unumIdentity?.name || "praxis-app",
4226
+ []
4227
+ );
4228
+ const unumDisposer = attachUnumToEngine(engine, unum, channel.id);
4229
+ disposers.push(unumDisposer);
4230
+ }
4231
+ let docs;
4232
+ if (options.enableDocs && options.docsConfig) {
4233
+ docs = createStateDocsGenerator({
4234
+ projectTitle: options.docsConfig.projectTitle,
4235
+ target: options.docsConfig.target || "./docs"
4236
+ });
4237
+ }
4238
+ return {
4239
+ pluresdb,
4240
+ unum,
4241
+ channel,
4242
+ docs,
4243
+ dispose: () => {
4244
+ pluresdb.dispose();
4245
+ if (unum) {
4246
+ unum.disconnect().catch((err) => {
4247
+ console.warn("Warning: Error during Unum disconnect:", err);
4248
+ });
4249
+ }
4250
+ for (const disposer of disposers) {
4251
+ disposer();
4252
+ }
4253
+ }
4254
+ };
4255
+ }
3515
4256
  // Annotate the CommonJS export names for ESM import in node:
3516
4257
  0 && (module.exports = {
4258
+ AcknowledgeContractGap,
3517
4259
  ActorManager,
4260
+ BehaviorLedger,
4261
+ ContractAdded,
4262
+ ContractGapAcknowledged,
4263
+ ContractMissing,
4264
+ ContractUpdated,
4265
+ ContractValidated,
3518
4266
  FrameworkAgnosticReactiveEngine,
3519
4267
  InMemoryPraxisDB,
3520
4268
  LogicEngine,
@@ -3529,12 +4277,15 @@ function generateTauriConfig(config) {
3529
4277
  RegistryIntrospector,
3530
4278
  StateDocsGenerator,
3531
4279
  TerminalAdapter,
4280
+ ValidateContracts,
4281
+ attachAllIntegrations,
3532
4282
  attachTauriToEngine,
3533
4283
  attachToEngine,
3534
4284
  attachUnumToEngine,
3535
4285
  canvasToMermaid,
3536
4286
  canvasToSchema,
3537
4287
  canvasToYaml,
4288
+ createBehaviorLedger,
3538
4289
  createCanvasEditor,
3539
4290
  createFrameworkAgnosticReactiveEngine,
3540
4291
  createInMemoryDB,
@@ -3546,6 +4297,7 @@ function generateTauriConfig(config) {
3546
4297
  createPluresDBGenerator,
3547
4298
  createPraxisDBStore,
3548
4299
  createPraxisEngine,
4300
+ createPraxisLocalFirst,
3549
4301
  createReactiveEngine,
3550
4302
  createSchemaRegistry,
3551
4303
  createSchemaTemplate,
@@ -3553,8 +4305,10 @@ function generateTauriConfig(config) {
3553
4305
  createTauriPraxisAdapter,
3554
4306
  createTerminalAdapter,
3555
4307
  createTimerActor,
4308
+ createUnifiedApp,
3556
4309
  createUnumAdapter,
3557
4310
  defineConstraint,
4311
+ defineContract,
3558
4312
  defineEvent,
3559
4313
  defineFact,
3560
4314
  defineModule,
@@ -3563,12 +4317,17 @@ function generateTauriConfig(config) {
3563
4317
  filterFacts,
3564
4318
  findEvent,
3565
4319
  findFact,
4320
+ formatValidationReport,
4321
+ formatValidationReportJSON,
4322
+ formatValidationReportSARIF,
3566
4323
  generateDocs,
3567
4324
  generateId,
3568
4325
  generateTauriConfig,
4326
+ getContract,
3569
4327
  getEventPath,
3570
4328
  getFactPath,
3571
4329
  getSchemaPath,
4330
+ isContract,
3572
4331
  loadSchema,
3573
4332
  loadSchemaFromFile,
3574
4333
  loadSchemaFromJson,
@@ -3576,6 +4335,7 @@ function generateTauriConfig(config) {
3576
4335
  registerSchema,
3577
4336
  runTerminalCommand,
3578
4337
  schemaToCanvas,
4338
+ validateContracts,
3579
4339
  validateForGeneration,
3580
4340
  validateSchema,
3581
4341
  validateWithGuardian