@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
@@ -119,6 +119,105 @@ interface PraxisStepResult {
119
119
  */
120
120
  type PraxisStepFn = (state: PraxisState, events: PraxisEvent[], config: PraxisStepConfig) => PraxisStepResult;
121
121
 
122
+ /**
123
+ * Decision Ledger - Contract Types
124
+ *
125
+ * Types for defining and validating contracts for rules and constraints.
126
+ * All types are JSON-serializable for cross-language compatibility.
127
+ */
128
+ /**
129
+ * A single assumption made during contract definition.
130
+ */
131
+ interface Assumption {
132
+ /** Stable unique identifier for the assumption */
133
+ id: string;
134
+ /** The assumption statement */
135
+ statement: string;
136
+ /** Confidence level (0.0 to 1.0) */
137
+ confidence: number;
138
+ /** Justification for the assumption */
139
+ justification: string;
140
+ /** What this assumption was derived from */
141
+ derivedFrom?: string;
142
+ /** What artifacts this assumption impacts */
143
+ impacts: Array<'spec' | 'tests' | 'code'>;
144
+ /** Current status of the assumption */
145
+ status: 'active' | 'revised' | 'invalidated';
146
+ }
147
+ /**
148
+ * A reference to external documentation or resources.
149
+ */
150
+ interface Reference {
151
+ /** Type of reference (e.g., 'doc', 'ticket', 'issue') */
152
+ type: string;
153
+ /** URL to the reference */
154
+ url?: string;
155
+ /** Human-readable description */
156
+ description?: string;
157
+ }
158
+ /**
159
+ * A Given/When/Then example for a contract.
160
+ */
161
+ interface Example {
162
+ /** Initial state or preconditions */
163
+ given: string;
164
+ /** Triggering event or action */
165
+ when: string;
166
+ /** Expected outcome or postconditions */
167
+ then: string;
168
+ }
169
+ /**
170
+ * Contract for a rule or constraint.
171
+ * Documents the expected behavior, test cases, invariants, and assumptions.
172
+ */
173
+ interface Contract {
174
+ /** ID of the rule or constraint this contract applies to */
175
+ ruleId: string;
176
+ /** Canonical behavior description */
177
+ behavior: string;
178
+ /** Given/When/Then examples (become test vectors) */
179
+ examples: Example[];
180
+ /** TLA+-friendly invariants or Praxis-level invariants */
181
+ invariants: string[];
182
+ /** Explicit assumptions with confidence levels */
183
+ assumptions?: Assumption[];
184
+ /** References to docs, tickets, links */
185
+ references?: Reference[];
186
+ /** Contract version (for evolution tracking) */
187
+ version?: string;
188
+ /** Timestamp of contract creation */
189
+ timestamp?: string;
190
+ }
191
+ /**
192
+ * Severity levels for contract gaps.
193
+ */
194
+ type Severity = 'warning' | 'error' | 'info';
195
+ /**
196
+ * Types of missing contract artifacts.
197
+ */
198
+ /**
199
+ * Types of artifacts that can be missing from a contract.
200
+ *
201
+ * Note: 'tests' and 'spec' are included in this type for future extensibility
202
+ * and SARIF reporting compatibility, but are not currently validated by the
203
+ * validateContract function. To check for these, implement custom validation
204
+ * logic that scans for test files or spec files in your codebase.
205
+ */
206
+ type MissingArtifact = 'behavior' | 'examples' | 'invariants' | 'tests' | 'spec' | 'contract';
207
+ /**
208
+ * A gap in contract coverage.
209
+ */
210
+ interface ContractGap {
211
+ /** ID of the rule or constraint */
212
+ ruleId: string;
213
+ /** What is missing */
214
+ missing: MissingArtifact[];
215
+ /** Severity of the gap */
216
+ severity: Severity;
217
+ /** Optional human-readable message */
218
+ message?: string;
219
+ }
220
+
122
221
  /**
123
222
  * Rules and Constraints System
124
223
  *
@@ -166,6 +265,8 @@ interface RuleDescriptor<TContext = unknown> {
166
265
  description: string;
167
266
  /** Implementation function */
168
267
  impl: RuleFn<TContext>;
268
+ /** Optional contract for rule behavior */
269
+ contract?: Contract;
169
270
  /** Optional metadata */
170
271
  meta?: Record<string, unknown>;
171
272
  }
@@ -179,6 +280,8 @@ interface ConstraintDescriptor<TContext = unknown> {
179
280
  description: string;
180
281
  /** Implementation function */
181
282
  impl: ConstraintFn<TContext>;
283
+ /** Optional contract for constraint behavior */
284
+ contract?: Contract;
182
285
  /** Optional metadata */
183
286
  meta?: Record<string, unknown>;
184
287
  }
@@ -194,6 +297,25 @@ interface PraxisModule<TContext = unknown> {
194
297
  /** Optional module metadata */
195
298
  meta?: Record<string, unknown>;
196
299
  }
300
+ /**
301
+ * Compliance validation options for rule/constraint registration.
302
+ */
303
+ interface RegistryComplianceOptions {
304
+ /** Enable contract checks during registration (default: true in dev) */
305
+ enabled?: boolean;
306
+ /** Required contract fields to be present */
307
+ requiredFields?: Array<'behavior' | 'examples' | 'invariants'>;
308
+ /** Severity to use for missing contracts */
309
+ missingSeverity?: Severity;
310
+ /** Callback for contract gaps (e.g., to emit facts) */
311
+ onGap?: (gap: ContractGap) => void;
312
+ }
313
+ /**
314
+ * PraxisRegistry configuration options.
315
+ */
316
+ interface PraxisRegistryOptions {
317
+ compliance?: RegistryComplianceOptions;
318
+ }
197
319
  /**
198
320
  * Registry for rules and constraints.
199
321
  * Maps IDs to their descriptors.
@@ -201,6 +323,9 @@ interface PraxisModule<TContext = unknown> {
201
323
  declare class PraxisRegistry<TContext = unknown> {
202
324
  private rules;
203
325
  private constraints;
326
+ private readonly compliance;
327
+ private contractGaps;
328
+ constructor(options?: PraxisRegistryOptions);
204
329
  /**
205
330
  * Register a rule
206
331
  */
@@ -237,6 +362,16 @@ declare class PraxisRegistry<TContext = unknown> {
237
362
  * Get all constraints
238
363
  */
239
364
  getAllConstraints(): ConstraintDescriptor<TContext>[];
365
+ /**
366
+ * Get collected contract gaps from registration-time validation.
367
+ */
368
+ getContractGaps(): ContractGap[];
369
+ /**
370
+ * Clear collected contract gaps.
371
+ */
372
+ clearContractGaps(): void;
373
+ private trackContractCompliance;
374
+ private validateDescriptorContract;
240
375
  }
241
376
 
242
377
  /**
@@ -416,4 +551,4 @@ declare class ReactiveLogicEngine<TContext extends object> {
416
551
  */
417
552
  declare function createReactiveEngine<TContext extends object>(options: ReactiveEngineOptions<TContext>): ReactiveLogicEngine<TContext>;
418
553
 
419
- export { type ConstraintDescriptor as C, LogicEngine as L, type PraxisState as P, type RuleDescriptor as R, type PraxisEvent as a, PraxisRegistry as b, type PraxisFact as c, type RuleFn as d, type ConstraintFn as e, type PraxisModule as f, type PraxisDiagnostics as g, type PraxisStepConfig as h, type PraxisStepResult as i, type PraxisStepFn as j, PRAXIS_PROTOCOL_VERSION as k, type RuleId as l, type ConstraintId as m, type PraxisEngineOptions as n, createPraxisEngine as o, type ReactiveEngineOptions as p, ReactiveLogicEngine as q, createReactiveEngine as r };
554
+ export { type ConstraintDescriptor as C, LogicEngine as L, type PraxisState as P, type RuleDescriptor as R, type PraxisEvent as a, PraxisRegistry as b, type PraxisFact as c, type RuleFn as d, type Contract as e, type ConstraintFn as f, type PraxisModule as g, type PraxisDiagnostics as h, type PraxisStepConfig as i, type PraxisStepResult as j, type PraxisStepFn as k, PRAXIS_PROTOCOL_VERSION as l, type RuleId as m, type ConstraintId as n, type PraxisEngineOptions as o, createPraxisEngine as p, type ReactiveEngineOptions as q, ReactiveLogicEngine as r, createReactiveEngine as s };
@@ -0,0 +1,15 @@
1
+ import {
2
+ InMemoryPraxisDB,
3
+ PluresDBPraxisAdapter,
4
+ createInMemoryDB,
5
+ createPluresDB,
6
+ createPraxisLocalFirst
7
+ } from "./chunk-MBVHLOU2.js";
8
+ import "./chunk-QGM4M3NI.js";
9
+ export {
10
+ InMemoryPraxisDB,
11
+ PluresDBPraxisAdapter,
12
+ createInMemoryDB,
13
+ createPluresDB,
14
+ createPraxisLocalFirst
15
+ };
@@ -0,0 +1,486 @@
1
+ import {
2
+ getContractFromDescriptor
3
+ } from "./chunk-WZ6B3LZ6.js";
4
+
5
+ // src/dsl/index.ts
6
+ function defineFact(tag) {
7
+ return {
8
+ tag,
9
+ create(payload) {
10
+ return { tag, payload };
11
+ },
12
+ is(fact) {
13
+ return fact.tag === tag;
14
+ }
15
+ };
16
+ }
17
+ function defineEvent(tag) {
18
+ return {
19
+ tag,
20
+ create(payload) {
21
+ return { tag, payload };
22
+ },
23
+ is(event) {
24
+ return event.tag === tag;
25
+ }
26
+ };
27
+ }
28
+ function defineRule(options) {
29
+ const contract = options.contract ?? options.meta?.contract;
30
+ const meta = contract ? { ...options.meta ?? {}, contract } : options.meta;
31
+ return {
32
+ id: options.id,
33
+ description: options.description,
34
+ impl: options.impl,
35
+ contract,
36
+ meta
37
+ };
38
+ }
39
+ function defineConstraint(options) {
40
+ const contract = options.contract ?? options.meta?.contract;
41
+ const meta = contract ? { ...options.meta ?? {}, contract } : options.meta;
42
+ return {
43
+ id: options.id,
44
+ description: options.description,
45
+ impl: options.impl,
46
+ contract,
47
+ meta
48
+ };
49
+ }
50
+ function defineModule(options) {
51
+ return {
52
+ rules: options.rules ?? [],
53
+ constraints: options.constraints ?? [],
54
+ meta: options.meta
55
+ };
56
+ }
57
+ function filterEvents(events, definition) {
58
+ return events.filter(definition.is);
59
+ }
60
+ function filterFacts(facts, definition) {
61
+ return facts.filter(definition.is);
62
+ }
63
+ function findEvent(events, definition) {
64
+ return events.find(definition.is);
65
+ }
66
+ function findFact(facts, definition) {
67
+ return facts.find(definition.is);
68
+ }
69
+
70
+ // src/decision-ledger/facts-events.ts
71
+ var ContractMissing = defineFact("ContractMissing");
72
+ var ContractValidated = defineFact("ContractValidated");
73
+ var AcknowledgeContractGap = defineEvent("ACKNOWLEDGE_CONTRACT_GAP");
74
+ var ValidateContracts = defineEvent("VALIDATE_CONTRACTS");
75
+ var ContractGapAcknowledged = defineFact("ContractGapAcknowledged");
76
+ var ContractAdded = defineEvent("CONTRACT_ADDED");
77
+ var ContractUpdated = defineEvent("CONTRACT_UPDATED");
78
+ var ContractGapEmitted = defineEvent("CONTRACT_GAP_EMITTED");
79
+
80
+ // src/decision-ledger/validation.ts
81
+ function validateContracts(registry, options = {}) {
82
+ const {
83
+ incompleteSeverity = "warning",
84
+ requiredFields = ["behavior", "examples"],
85
+ artifactIndex
86
+ } = options;
87
+ const complete = [];
88
+ const incomplete = [];
89
+ const missing = [];
90
+ for (const rule of registry.getAllRules()) {
91
+ const contract = getContractFromDescriptor(rule);
92
+ if (!contract) {
93
+ missing.push(rule.id);
94
+ if (options.missingSeverity) {
95
+ incomplete.push({
96
+ ruleId: rule.id,
97
+ missing: ["contract"],
98
+ severity: options.missingSeverity,
99
+ message: `Rule '${rule.id}' has no contract`
100
+ });
101
+ }
102
+ continue;
103
+ }
104
+ const gaps = validateContract(contract, requiredFields, artifactIndex);
105
+ if (gaps.length > 0) {
106
+ incomplete.push({
107
+ ruleId: rule.id,
108
+ missing: gaps,
109
+ severity: incompleteSeverity,
110
+ message: `Rule '${rule.id}' contract is incomplete: missing ${gaps.join(", ")}`
111
+ });
112
+ } else {
113
+ complete.push({ ruleId: rule.id, contract });
114
+ }
115
+ }
116
+ for (const constraint of registry.getAllConstraints()) {
117
+ const contract = getContractFromDescriptor(constraint);
118
+ if (!contract) {
119
+ missing.push(constraint.id);
120
+ if (options.missingSeverity) {
121
+ incomplete.push({
122
+ ruleId: constraint.id,
123
+ missing: ["contract"],
124
+ severity: options.missingSeverity,
125
+ message: `Constraint '${constraint.id}' has no contract`
126
+ });
127
+ }
128
+ continue;
129
+ }
130
+ const gaps = validateContract(contract, requiredFields, artifactIndex);
131
+ if (gaps.length > 0) {
132
+ incomplete.push({
133
+ ruleId: constraint.id,
134
+ missing: gaps,
135
+ severity: incompleteSeverity,
136
+ message: `Constraint '${constraint.id}' contract is incomplete: missing ${gaps.join(", ")}`
137
+ });
138
+ } else {
139
+ complete.push({ ruleId: constraint.id, contract });
140
+ }
141
+ }
142
+ const total = registry.getAllRules().length + registry.getAllConstraints().length;
143
+ return {
144
+ complete,
145
+ incomplete,
146
+ missing,
147
+ total,
148
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
149
+ };
150
+ }
151
+ function validateContract(contract, requiredFields, artifactIndex) {
152
+ const missing = [];
153
+ if (requiredFields.includes("behavior") && isFieldEmpty(contract.behavior)) {
154
+ missing.push("behavior");
155
+ }
156
+ if (requiredFields.includes("examples") && (!contract.examples || contract.examples.length === 0)) {
157
+ missing.push("examples");
158
+ }
159
+ if (requiredFields.includes("invariants") && (!contract.invariants || contract.invariants.length === 0)) {
160
+ missing.push("invariants");
161
+ }
162
+ if (artifactIndex?.tests && !artifactIndex.tests.has(contract.ruleId)) {
163
+ missing.push("tests");
164
+ }
165
+ if (artifactIndex?.spec && !artifactIndex.spec.has(contract.ruleId)) {
166
+ missing.push("spec");
167
+ }
168
+ return missing;
169
+ }
170
+ function isFieldEmpty(value) {
171
+ return !value || value.trim() === "";
172
+ }
173
+ function formatValidationReport(report) {
174
+ const lines = [];
175
+ lines.push("Contract Validation Report");
176
+ lines.push("=".repeat(50));
177
+ lines.push("");
178
+ lines.push(`Total: ${report.total}`);
179
+ lines.push(`Complete: ${report.complete.length}`);
180
+ lines.push(`Incomplete: ${report.incomplete.length}`);
181
+ lines.push(`Missing: ${report.missing.length}`);
182
+ lines.push("");
183
+ if (report.complete.length > 0) {
184
+ lines.push("\u2713 Complete Contracts:");
185
+ for (const { ruleId, contract } of report.complete) {
186
+ lines.push(` \u2713 ${ruleId} (v${contract.version || "1.0.0"})`);
187
+ }
188
+ lines.push("");
189
+ }
190
+ if (report.incomplete.length > 0) {
191
+ lines.push("\u2717 Incomplete Contracts:");
192
+ for (const gap of report.incomplete) {
193
+ const icon = gap.severity === "error" ? "\u2717" : gap.severity === "warning" ? "\u26A0" : "\u2139";
194
+ lines.push(` ${icon} ${gap.ruleId} - Missing: ${gap.missing.join(", ")}`);
195
+ if (gap.message) {
196
+ lines.push(` ${gap.message}`);
197
+ }
198
+ }
199
+ lines.push("");
200
+ }
201
+ if (report.missing.length > 0) {
202
+ lines.push("\u2717 No Contract:");
203
+ for (const ruleId of report.missing) {
204
+ lines.push(` \u2717 ${ruleId}`);
205
+ }
206
+ lines.push("");
207
+ }
208
+ lines.push(`Validated at: ${report.timestamp}`);
209
+ return lines.join("\n");
210
+ }
211
+ function formatValidationReportJSON(report) {
212
+ return JSON.stringify(report, null, 2);
213
+ }
214
+ function formatValidationReportSARIF(report) {
215
+ const results = report.incomplete.map((gap) => {
216
+ const primaryMissing = gap.missing.length > 0 ? gap.missing[0] : "contract";
217
+ return {
218
+ ruleId: `decision-ledger/${primaryMissing}`,
219
+ level: gap.severity === "error" ? "error" : gap.severity === "warning" ? "warning" : "note",
220
+ message: {
221
+ text: gap.message || `Missing: ${gap.missing.join(", ")}`
222
+ },
223
+ locations: [
224
+ {
225
+ physicalLocation: {
226
+ artifactLocation: {
227
+ uri: "registry"
228
+ },
229
+ region: {
230
+ startLine: 1
231
+ }
232
+ }
233
+ }
234
+ ],
235
+ properties: {
236
+ ruleId: gap.ruleId,
237
+ missing: gap.missing
238
+ }
239
+ };
240
+ });
241
+ const sarif = {
242
+ version: "2.1.0",
243
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
244
+ runs: [
245
+ {
246
+ tool: {
247
+ driver: {
248
+ name: "Praxis Decision Ledger",
249
+ version: "1.0.0",
250
+ informationUri: "https://github.com/plures/praxis",
251
+ rules: [
252
+ {
253
+ id: "decision-ledger/contract",
254
+ shortDescription: {
255
+ text: "Rule or constraint missing contract"
256
+ }
257
+ },
258
+ {
259
+ id: "decision-ledger/behavior",
260
+ shortDescription: {
261
+ text: "Contract missing behavior description"
262
+ }
263
+ },
264
+ {
265
+ id: "decision-ledger/examples",
266
+ shortDescription: {
267
+ text: "Contract missing examples"
268
+ }
269
+ },
270
+ {
271
+ id: "decision-ledger/invariants",
272
+ shortDescription: {
273
+ text: "Contract missing invariants"
274
+ }
275
+ },
276
+ {
277
+ id: "decision-ledger/tests",
278
+ shortDescription: {
279
+ text: "Contract missing tests"
280
+ }
281
+ },
282
+ {
283
+ id: "decision-ledger/spec",
284
+ shortDescription: {
285
+ text: "Contract missing spec"
286
+ }
287
+ }
288
+ ]
289
+ }
290
+ },
291
+ results
292
+ }
293
+ ]
294
+ };
295
+ return JSON.stringify(sarif, null, 2);
296
+ }
297
+
298
+ // src/decision-ledger/ledger.ts
299
+ var BehaviorLedger = class _BehaviorLedger {
300
+ entries = [];
301
+ entryMap = /* @__PURE__ */ new Map();
302
+ /**
303
+ * Append a new entry to the ledger.
304
+ *
305
+ * @param entry The entry to append
306
+ * @throws Error if entry ID already exists
307
+ */
308
+ append(entry) {
309
+ if (this.entryMap.has(entry.id)) {
310
+ throw new Error(`Ledger entry with ID '${entry.id}' already exists`);
311
+ }
312
+ if (entry.supersedes) {
313
+ const superseded = this.entryMap.get(entry.supersedes);
314
+ if (superseded && superseded.status === "active") {
315
+ const updatedEntry = {
316
+ ...superseded,
317
+ status: "superseded"
318
+ };
319
+ this.entryMap.set(entry.supersedes, updatedEntry);
320
+ }
321
+ }
322
+ this.entries.push(entry);
323
+ this.entryMap.set(entry.id, entry);
324
+ }
325
+ /**
326
+ * Get an entry by ID.
327
+ *
328
+ * @param id The entry ID
329
+ * @returns The entry, or undefined if not found
330
+ */
331
+ getEntry(id) {
332
+ return this.entryMap.get(id);
333
+ }
334
+ /**
335
+ * Get all entries (in order of append) with current status.
336
+ *
337
+ * @returns Array of all entries with current status from the map
338
+ */
339
+ getAllEntries() {
340
+ return this.entries.map((entry) => this.entryMap.get(entry.id));
341
+ }
342
+ /**
343
+ * Get entries for a specific rule ID.
344
+ *
345
+ * @param ruleId The rule ID
346
+ * @returns Array of entries for this rule with current status
347
+ */
348
+ getEntriesForRule(ruleId) {
349
+ return this.entries.map((entry) => this.entryMap.get(entry.id)).filter((entry) => entry.contract.ruleId === ruleId);
350
+ }
351
+ /**
352
+ * Get the latest active entry for a rule.
353
+ *
354
+ * @param ruleId The rule ID
355
+ * @returns The latest active entry, or undefined if none
356
+ */
357
+ getLatestEntry(ruleId) {
358
+ const entries = this.getEntriesForRule(ruleId);
359
+ const activeEntries = entries.filter((entry) => entry.status === "active");
360
+ if (activeEntries.length === 0) {
361
+ return void 0;
362
+ }
363
+ return activeEntries[activeEntries.length - 1];
364
+ }
365
+ /**
366
+ * Get all active assumptions across all entries.
367
+ *
368
+ * @returns Map of assumption ID to assumption
369
+ */
370
+ getActiveAssumptions() {
371
+ const assumptions = /* @__PURE__ */ new Map();
372
+ for (const entry of this.entries) {
373
+ const currentEntry = this.entryMap.get(entry.id);
374
+ if (currentEntry.status !== "active") {
375
+ continue;
376
+ }
377
+ for (const assumption of currentEntry.contract.assumptions || []) {
378
+ if (assumption.status === "active") {
379
+ assumptions.set(assumption.id, assumption);
380
+ }
381
+ }
382
+ }
383
+ return assumptions;
384
+ }
385
+ /**
386
+ * Find assumptions that impact a specific artifact type.
387
+ *
388
+ * @param impactType The artifact type ('spec', 'tests', 'code')
389
+ * @returns Array of assumptions
390
+ */
391
+ findAssumptionsByImpact(impactType) {
392
+ const assumptions = [];
393
+ for (const entry of this.entries) {
394
+ const currentEntry = this.entryMap.get(entry.id);
395
+ if (currentEntry.status !== "active") {
396
+ continue;
397
+ }
398
+ for (const assumption of currentEntry.contract.assumptions || []) {
399
+ if (assumption.status === "active" && assumption.impacts.includes(impactType)) {
400
+ assumptions.push(assumption);
401
+ }
402
+ }
403
+ }
404
+ return assumptions;
405
+ }
406
+ /**
407
+ * Get ledger statistics.
408
+ */
409
+ getStats() {
410
+ const currentEntries = this.entries.map((e) => this.entryMap.get(e.id));
411
+ const active = currentEntries.filter((e) => e.status === "active").length;
412
+ const superseded = currentEntries.filter((e) => e.status === "superseded").length;
413
+ const deprecated = currentEntries.filter((e) => e.status === "deprecated").length;
414
+ const uniqueRules = new Set(currentEntries.map((e) => e.contract.ruleId)).size;
415
+ return {
416
+ totalEntries: this.entries.length,
417
+ activeEntries: active,
418
+ supersededEntries: superseded,
419
+ deprecatedEntries: deprecated,
420
+ uniqueRules
421
+ };
422
+ }
423
+ /**
424
+ * Export ledger as JSON.
425
+ *
426
+ * @returns JSON string with current entry status
427
+ */
428
+ toJSON() {
429
+ return JSON.stringify(
430
+ {
431
+ version: "1.0.0",
432
+ // Export entries with current status from the map
433
+ entries: this.entries.map((entry) => this.entryMap.get(entry.id)),
434
+ stats: this.getStats()
435
+ },
436
+ null,
437
+ 2
438
+ );
439
+ }
440
+ /**
441
+ * Import ledger from JSON.
442
+ *
443
+ * Note: The JSON must contain entries in the order they were originally appended.
444
+ * If a superseding entry appears before the entry it supersedes, the superseding
445
+ * logic will not work correctly. The toJSON method preserves this order.
446
+ *
447
+ * @param json The JSON string
448
+ * @returns A new BehaviorLedger instance
449
+ */
450
+ static fromJSON(json) {
451
+ const data = JSON.parse(json);
452
+ const ledger = new _BehaviorLedger();
453
+ for (const entry of data.entries || []) {
454
+ ledger.append(entry);
455
+ }
456
+ return ledger;
457
+ }
458
+ };
459
+ function createBehaviorLedger() {
460
+ return new BehaviorLedger();
461
+ }
462
+
463
+ export {
464
+ defineFact,
465
+ defineEvent,
466
+ defineRule,
467
+ defineConstraint,
468
+ defineModule,
469
+ filterEvents,
470
+ filterFacts,
471
+ findEvent,
472
+ findFact,
473
+ ContractMissing,
474
+ ContractValidated,
475
+ AcknowledgeContractGap,
476
+ ValidateContracts,
477
+ ContractGapAcknowledged,
478
+ ContractAdded,
479
+ ContractUpdated,
480
+ validateContracts,
481
+ formatValidationReport,
482
+ formatValidationReportJSON,
483
+ formatValidationReportSARIF,
484
+ BehaviorLedger,
485
+ createBehaviorLedger
486
+ };