@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
@@ -196,6 +196,90 @@ var PraxisRegistry = class {
196
196
  // src/core/protocol.ts
197
197
  var PRAXIS_PROTOCOL_VERSION = "1.0.0";
198
198
 
199
+ // src/core/rule-result.ts
200
+ var RuleResult = class _RuleResult {
201
+ /** The kind of result */
202
+ kind;
203
+ /** Facts produced (only for 'emit') */
204
+ facts;
205
+ /** Fact tags to retract (only for 'retract') */
206
+ retractTags;
207
+ /** Optional reason (for noop/skip/retract — useful for debugging) */
208
+ reason;
209
+ /** The rule ID that produced this result (set by engine) */
210
+ ruleId;
211
+ constructor(kind, facts, retractTags, reason) {
212
+ this.kind = kind;
213
+ this.facts = facts;
214
+ this.retractTags = retractTags;
215
+ this.reason = reason;
216
+ }
217
+ /**
218
+ * Rule produced facts.
219
+ *
220
+ * @example
221
+ * return RuleResult.emit([
222
+ * { tag: 'sprint.behind', payload: { deficit: 5 } }
223
+ * ]);
224
+ */
225
+ static emit(facts) {
226
+ if (facts.length === 0) {
227
+ throw new Error(
228
+ "RuleResult.emit() requires at least one fact. Use RuleResult.noop() or RuleResult.skip() when a rule has nothing to say."
229
+ );
230
+ }
231
+ return new _RuleResult("emit", facts, []);
232
+ }
233
+ /**
234
+ * Rule evaluated but had nothing to report.
235
+ * Unlike returning [], this is explicit and traceable.
236
+ *
237
+ * @example
238
+ * if (ctx.completedHours >= expectedHours) {
239
+ * return RuleResult.noop('Sprint is on pace');
240
+ * }
241
+ */
242
+ static noop(reason) {
243
+ return new _RuleResult("noop", [], [], reason);
244
+ }
245
+ /**
246
+ * Rule decided to skip because preconditions were not met.
247
+ * Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
248
+ *
249
+ * @example
250
+ * if (!ctx.sprintName) {
251
+ * return RuleResult.skip('No active sprint');
252
+ * }
253
+ */
254
+ static skip(reason) {
255
+ return new _RuleResult("skip", [], [], reason);
256
+ }
257
+ /**
258
+ * Rule retracts previously emitted facts by tag.
259
+ * Used when a condition that previously produced facts is no longer true.
260
+ *
261
+ * @example
262
+ * // Sprint was behind, but caught up
263
+ * if (ctx.completedHours >= expectedHours) {
264
+ * return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
265
+ * }
266
+ */
267
+ static retract(tags, reason) {
268
+ if (tags.length === 0) {
269
+ throw new Error("RuleResult.retract() requires at least one tag.");
270
+ }
271
+ return new _RuleResult("retract", [], tags, reason);
272
+ }
273
+ /** Whether this result produced facts */
274
+ get hasFacts() {
275
+ return this.facts.length > 0;
276
+ }
277
+ /** Whether this result retracts facts */
278
+ get hasRetractions() {
279
+ return this.retractTags.length > 0;
280
+ }
281
+ };
282
+
199
283
  // src/core/engine.ts
200
284
  function safeClone(value) {
201
285
  if (value === null || typeof value !== "object") {
@@ -275,7 +359,13 @@ var LogicEngine = class {
275
359
  stepWithConfig(events, config) {
276
360
  const diagnostics = [];
277
361
  let newState = { ...this.state };
362
+ const stateWithEvents = {
363
+ ...newState,
364
+ events
365
+ // current batch — rules can read state.events
366
+ };
278
367
  const newFacts = [];
368
+ const retractions = [];
279
369
  const eventTags = new Set(events.map((e) => e.tag));
280
370
  for (const ruleId of config.ruleIds) {
281
371
  const rule = this.registry.getRule(ruleId);
@@ -294,8 +384,31 @@ var LogicEngine = class {
294
384
  }
295
385
  }
296
386
  try {
297
- const ruleFacts = rule.impl(newState, events);
298
- newFacts.push(...ruleFacts);
387
+ const rawResult = rule.impl(stateWithEvents, events);
388
+ if (rawResult instanceof RuleResult) {
389
+ rawResult.ruleId = ruleId;
390
+ switch (rawResult.kind) {
391
+ case "emit":
392
+ newFacts.push(...rawResult.facts);
393
+ break;
394
+ case "retract":
395
+ retractions.push(...rawResult.retractTags);
396
+ break;
397
+ case "noop":
398
+ case "skip":
399
+ if (rawResult.reason) {
400
+ diagnostics.push({
401
+ kind: "rule-error",
402
+ // reused kind — could add 'rule-trace' in protocol v2
403
+ message: `[${rawResult.kind}] ${ruleId}: ${rawResult.reason}`,
404
+ data: { ruleId, resultKind: rawResult.kind, reason: rawResult.reason }
405
+ });
406
+ }
407
+ break;
408
+ }
409
+ } else if (Array.isArray(rawResult)) {
410
+ newFacts.push(...rawResult);
411
+ }
299
412
  } catch (error) {
300
413
  diagnostics.push({
301
414
  kind: "rule-error",
@@ -304,21 +417,26 @@ var LogicEngine = class {
304
417
  });
305
418
  }
306
419
  }
420
+ let existingFacts = newState.facts;
421
+ if (retractions.length > 0) {
422
+ const retractSet = new Set(retractions);
423
+ existingFacts = existingFacts.filter((f) => !retractSet.has(f.tag));
424
+ }
307
425
  let mergedFacts;
308
426
  switch (this.factDedup) {
309
427
  case "last-write-wins": {
310
428
  const factMap = /* @__PURE__ */ new Map();
311
- for (const f of newState.facts) factMap.set(f.tag, f);
429
+ for (const f of existingFacts) factMap.set(f.tag, f);
312
430
  for (const f of newFacts) factMap.set(f.tag, f);
313
431
  mergedFacts = Array.from(factMap.values());
314
432
  break;
315
433
  }
316
434
  case "append":
317
- mergedFacts = [...newState.facts, ...newFacts];
435
+ mergedFacts = [...existingFacts, ...newFacts];
318
436
  break;
319
437
  case "none":
320
438
  default:
321
- mergedFacts = [...newState.facts, ...newFacts];
439
+ mergedFacts = [...existingFacts, ...newFacts];
322
440
  break;
323
441
  }
324
442
  if (this.maxFacts > 0 && mergedFacts.length > this.maxFacts) {
@@ -1,6 +1,6 @@
1
- import { L as LogicEngine } from '../reactive-engine.svelte-CGe8SpVE.cjs';
2
- export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-CGe8SpVE.cjs';
3
- import { P as PraxisState, a as PraxisEvent } from '../protocol-BocKczNv.cjs';
1
+ import { L as LogicEngine } from '../reactive-engine.svelte-Cg0Yc2Hs.cjs';
2
+ export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-Cg0Yc2Hs.cjs';
3
+ import { P as PraxisState, a as PraxisEvent } from '../protocol-DcyGMmWY.cjs';
4
4
 
5
5
  /**
6
6
  * Svelte v5 Integration
@@ -1,6 +1,6 @@
1
- import { L as LogicEngine } from '../reactive-engine.svelte-D-xTDxT5.js';
2
- export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-D-xTDxT5.js';
3
- import { P as PraxisState, a as PraxisEvent } from '../protocol-BocKczNv.js';
1
+ import { L as LogicEngine } from '../reactive-engine.svelte-DekxqFu0.js';
2
+ export { R as ReactiveEngineOptions, a as ReactiveLogicEngine, c as createReactiveEngine } from '../reactive-engine.svelte-DekxqFu0.js';
3
+ import { P as PraxisState, a as PraxisEvent } from '../protocol-DcyGMmWY.js';
4
4
 
5
5
  /**
6
6
  * Svelte v5 Integration
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  ReactiveLogicEngine,
3
3
  createReactiveEngine
4
- } from "../chunk-32YFEEML.js";
5
- import "../chunk-R2PSBPKQ.js";
6
- import "../chunk-BBP2F7TT.js";
4
+ } from "../chunk-7M3HV4XR.js";
5
+ import "../chunk-KMJWAFZV.js";
6
+ import "../chunk-TEMFJOIH.js";
7
7
  import "../chunk-QGM4M3NI.js";
8
8
 
9
9
  // src/integrations/svelte.ts
@@ -71,6 +71,13 @@ interface PraxisState {
71
71
  context: unknown;
72
72
  /** Current facts about the domain */
73
73
  facts: PraxisFact[];
74
+ /**
75
+ * Events currently being processed in this step.
76
+ * Available to rules during execution — guaranteed to contain the exact
77
+ * events passed to step()/stepWithContext().
78
+ * Empty outside of step execution.
79
+ */
80
+ events?: PraxisEvent[];
74
81
  /** Optional metadata (timestamps, version, etc.) */
75
82
  meta?: Record<string, unknown>;
76
83
  /** Protocol version (for cross-language compatibility) */
@@ -71,6 +71,13 @@ interface PraxisState {
71
71
  context: unknown;
72
72
  /** Current facts about the domain */
73
73
  facts: PraxisFact[];
74
+ /**
75
+ * Events currently being processed in this step.
76
+ * Available to rules during execution — guaranteed to contain the exact
77
+ * events passed to step()/stepWithContext().
78
+ * Empty outside of step execution.
79
+ */
80
+ events?: PraxisEvent[];
74
81
  /** Optional metadata (timestamps, version, etc.) */
75
82
  meta?: Record<string, unknown>;
76
83
  /** Protocol version (for cross-language compatibility) */
@@ -1,4 +1,4 @@
1
- import { P as PraxisState, a as PraxisEvent, b as PraxisFact, c as PraxisStepResult, d as PraxisStepConfig, e as PraxisDiagnostics } from './protocol-BocKczNv.cjs';
1
+ import { b as PraxisFact, P as PraxisState, a as PraxisEvent, c as PraxisStepResult, d as PraxisStepConfig, e as PraxisDiagnostics } from './protocol-DcyGMmWY.cjs';
2
2
 
3
3
  /**
4
4
  * Decision Ledger - Contract Types
@@ -162,6 +162,83 @@ interface ValidationReport {
162
162
  timestamp: string;
163
163
  }
164
164
 
165
+ /**
166
+ * The result of evaluating a rule. Every rule MUST return one of:
167
+ * - `RuleResult.emit(facts)` — rule produced facts
168
+ * - `RuleResult.noop(reason?)` — rule evaluated but had nothing to say
169
+ * - `RuleResult.skip(reason?)` — rule decided to skip (preconditions not met)
170
+ * - `RuleResult.retract(tags)` — rule retracts previously emitted facts
171
+ */
172
+ declare class RuleResult {
173
+ /** The kind of result */
174
+ readonly kind: 'emit' | 'noop' | 'skip' | 'retract';
175
+ /** Facts produced (only for 'emit') */
176
+ readonly facts: PraxisFact[];
177
+ /** Fact tags to retract (only for 'retract') */
178
+ readonly retractTags: string[];
179
+ /** Optional reason (for noop/skip/retract — useful for debugging) */
180
+ readonly reason?: string;
181
+ /** The rule ID that produced this result (set by engine) */
182
+ ruleId?: string;
183
+ private constructor();
184
+ /**
185
+ * Rule produced facts.
186
+ *
187
+ * @example
188
+ * return RuleResult.emit([
189
+ * { tag: 'sprint.behind', payload: { deficit: 5 } }
190
+ * ]);
191
+ */
192
+ static emit(facts: PraxisFact[]): RuleResult;
193
+ /**
194
+ * Rule evaluated but had nothing to report.
195
+ * Unlike returning [], this is explicit and traceable.
196
+ *
197
+ * @example
198
+ * if (ctx.completedHours >= expectedHours) {
199
+ * return RuleResult.noop('Sprint is on pace');
200
+ * }
201
+ */
202
+ static noop(reason?: string): RuleResult;
203
+ /**
204
+ * Rule decided to skip because preconditions were not met.
205
+ * Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
206
+ *
207
+ * @example
208
+ * if (!ctx.sprintName) {
209
+ * return RuleResult.skip('No active sprint');
210
+ * }
211
+ */
212
+ static skip(reason?: string): RuleResult;
213
+ /**
214
+ * Rule retracts previously emitted facts by tag.
215
+ * Used when a condition that previously produced facts is no longer true.
216
+ *
217
+ * @example
218
+ * // Sprint was behind, but caught up
219
+ * if (ctx.completedHours >= expectedHours) {
220
+ * return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
221
+ * }
222
+ */
223
+ static retract(tags: string[], reason?: string): RuleResult;
224
+ /** Whether this result produced facts */
225
+ get hasFacts(): boolean;
226
+ /** Whether this result retracts facts */
227
+ get hasRetractions(): boolean;
228
+ }
229
+ /**
230
+ * A rule function that returns a typed RuleResult.
231
+ * New API — replaces the old PraxisFact[] return type.
232
+ */
233
+ type TypedRuleFn<TContext = unknown> = (state: PraxisState & {
234
+ context: TContext;
235
+ events: PraxisEvent[];
236
+ }, events: PraxisEvent[]) => RuleResult;
237
+ /**
238
+ * Convenience: create a fact object (just a shorthand)
239
+ */
240
+ declare function fact(tag: string, payload: unknown): PraxisFact;
241
+
165
242
  /**
166
243
  * Rules and Constraints System
167
244
  *
@@ -182,13 +259,20 @@ type ConstraintId = string;
182
259
  * A rule function derives new facts or transitions from context + input facts/events.
183
260
  * Rules must be pure - no side effects.
184
261
  *
185
- * @param state Current Praxis state
186
- * @param events Events to process
187
- * @returns Array of new facts to add to the state
262
+ * Returns either:
263
+ * - `RuleResult` (new API — typed, traceable, supports retraction)
264
+ * - `PraxisFact[]` (legacy backward compatible, will be deprecated)
265
+ *
266
+ * The state parameter includes `events` — the current batch being processed.
267
+ *
268
+ * @param state Current Praxis state (includes state.events for current batch)
269
+ * @param events Events to process (same as state.events, provided for convenience)
270
+ * @returns RuleResult or array of new facts
188
271
  */
189
272
  type RuleFn<TContext = unknown> = (state: PraxisState & {
190
273
  context: TContext;
191
- }, events: PraxisEvent[]) => PraxisFact[];
274
+ events: PraxisEvent[];
275
+ }, events: PraxisEvent[]) => RuleResult | PraxisFact[];
192
276
  /**
193
277
  * A constraint function checks that an invariant holds.
194
278
  * Constraints must be pure - no side effects.
@@ -550,4 +634,4 @@ declare class ReactiveLogicEngine<TContext extends object> {
550
634
  */
551
635
  declare function createReactiveEngine<TContext extends object>(options: ReactiveEngineOptions<TContext>): ReactiveLogicEngine<TContext>;
552
636
 
553
- export { type Assumption as A, type ConstraintDescriptor as C, type DefineContractOptions as D, type Example as E, LogicEngine as L, type MissingArtifact as M, PraxisRegistry as P, type ReactiveEngineOptions as R, type Severity as S, type ValidationReport as V, ReactiveLogicEngine as a, type RuleDescriptor as b, createReactiveEngine as c, type ConstraintFn as d, type Contract as e, type RuleFn as f, type PraxisModule as g, type ConstraintId as h, type ContractGap as i, type PraxisEngineOptions as j, type Reference as k, type RuleId as l, createPraxisEngine as m, defineContract as n, getContract as o, isContract as p };
637
+ export { type Assumption as A, type ConstraintDescriptor as C, type DefineContractOptions as D, type Example as E, LogicEngine as L, type MissingArtifact as M, PraxisRegistry as P, type ReactiveEngineOptions as R, type Severity as S, type TypedRuleFn as T, type ValidationReport as V, ReactiveLogicEngine as a, type RuleDescriptor as b, createReactiveEngine as c, type ConstraintFn as d, type Contract as e, type RuleFn as f, type PraxisModule as g, type ConstraintId as h, type ContractGap as i, type PraxisEngineOptions as j, type Reference as k, type RuleId as l, RuleResult as m, createPraxisEngine as n, defineContract as o, fact as p, getContract as q, isContract as r };
@@ -1,4 +1,4 @@
1
- import { P as PraxisState, a as PraxisEvent, b as PraxisFact, c as PraxisStepResult, d as PraxisStepConfig, e as PraxisDiagnostics } from './protocol-BocKczNv.js';
1
+ import { b as PraxisFact, P as PraxisState, a as PraxisEvent, c as PraxisStepResult, d as PraxisStepConfig, e as PraxisDiagnostics } from './protocol-DcyGMmWY.js';
2
2
 
3
3
  /**
4
4
  * Decision Ledger - Contract Types
@@ -162,6 +162,83 @@ interface ValidationReport {
162
162
  timestamp: string;
163
163
  }
164
164
 
165
+ /**
166
+ * The result of evaluating a rule. Every rule MUST return one of:
167
+ * - `RuleResult.emit(facts)` — rule produced facts
168
+ * - `RuleResult.noop(reason?)` — rule evaluated but had nothing to say
169
+ * - `RuleResult.skip(reason?)` — rule decided to skip (preconditions not met)
170
+ * - `RuleResult.retract(tags)` — rule retracts previously emitted facts
171
+ */
172
+ declare class RuleResult {
173
+ /** The kind of result */
174
+ readonly kind: 'emit' | 'noop' | 'skip' | 'retract';
175
+ /** Facts produced (only for 'emit') */
176
+ readonly facts: PraxisFact[];
177
+ /** Fact tags to retract (only for 'retract') */
178
+ readonly retractTags: string[];
179
+ /** Optional reason (for noop/skip/retract — useful for debugging) */
180
+ readonly reason?: string;
181
+ /** The rule ID that produced this result (set by engine) */
182
+ ruleId?: string;
183
+ private constructor();
184
+ /**
185
+ * Rule produced facts.
186
+ *
187
+ * @example
188
+ * return RuleResult.emit([
189
+ * { tag: 'sprint.behind', payload: { deficit: 5 } }
190
+ * ]);
191
+ */
192
+ static emit(facts: PraxisFact[]): RuleResult;
193
+ /**
194
+ * Rule evaluated but had nothing to report.
195
+ * Unlike returning [], this is explicit and traceable.
196
+ *
197
+ * @example
198
+ * if (ctx.completedHours >= expectedHours) {
199
+ * return RuleResult.noop('Sprint is on pace');
200
+ * }
201
+ */
202
+ static noop(reason?: string): RuleResult;
203
+ /**
204
+ * Rule decided to skip because preconditions were not met.
205
+ * Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
206
+ *
207
+ * @example
208
+ * if (!ctx.sprintName) {
209
+ * return RuleResult.skip('No active sprint');
210
+ * }
211
+ */
212
+ static skip(reason?: string): RuleResult;
213
+ /**
214
+ * Rule retracts previously emitted facts by tag.
215
+ * Used when a condition that previously produced facts is no longer true.
216
+ *
217
+ * @example
218
+ * // Sprint was behind, but caught up
219
+ * if (ctx.completedHours >= expectedHours) {
220
+ * return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
221
+ * }
222
+ */
223
+ static retract(tags: string[], reason?: string): RuleResult;
224
+ /** Whether this result produced facts */
225
+ get hasFacts(): boolean;
226
+ /** Whether this result retracts facts */
227
+ get hasRetractions(): boolean;
228
+ }
229
+ /**
230
+ * A rule function that returns a typed RuleResult.
231
+ * New API — replaces the old PraxisFact[] return type.
232
+ */
233
+ type TypedRuleFn<TContext = unknown> = (state: PraxisState & {
234
+ context: TContext;
235
+ events: PraxisEvent[];
236
+ }, events: PraxisEvent[]) => RuleResult;
237
+ /**
238
+ * Convenience: create a fact object (just a shorthand)
239
+ */
240
+ declare function fact(tag: string, payload: unknown): PraxisFact;
241
+
165
242
  /**
166
243
  * Rules and Constraints System
167
244
  *
@@ -182,13 +259,20 @@ type ConstraintId = string;
182
259
  * A rule function derives new facts or transitions from context + input facts/events.
183
260
  * Rules must be pure - no side effects.
184
261
  *
185
- * @param state Current Praxis state
186
- * @param events Events to process
187
- * @returns Array of new facts to add to the state
262
+ * Returns either:
263
+ * - `RuleResult` (new API — typed, traceable, supports retraction)
264
+ * - `PraxisFact[]` (legacy backward compatible, will be deprecated)
265
+ *
266
+ * The state parameter includes `events` — the current batch being processed.
267
+ *
268
+ * @param state Current Praxis state (includes state.events for current batch)
269
+ * @param events Events to process (same as state.events, provided for convenience)
270
+ * @returns RuleResult or array of new facts
188
271
  */
189
272
  type RuleFn<TContext = unknown> = (state: PraxisState & {
190
273
  context: TContext;
191
- }, events: PraxisEvent[]) => PraxisFact[];
274
+ events: PraxisEvent[];
275
+ }, events: PraxisEvent[]) => RuleResult | PraxisFact[];
192
276
  /**
193
277
  * A constraint function checks that an invariant holds.
194
278
  * Constraints must be pure - no side effects.
@@ -550,4 +634,4 @@ declare class ReactiveLogicEngine<TContext extends object> {
550
634
  */
551
635
  declare function createReactiveEngine<TContext extends object>(options: ReactiveEngineOptions<TContext>): ReactiveLogicEngine<TContext>;
552
636
 
553
- export { type Assumption as A, type ConstraintDescriptor as C, type DefineContractOptions as D, type Example as E, LogicEngine as L, type MissingArtifact as M, PraxisRegistry as P, type ReactiveEngineOptions as R, type Severity as S, type ValidationReport as V, ReactiveLogicEngine as a, type RuleDescriptor as b, createReactiveEngine as c, type ConstraintFn as d, type Contract as e, type RuleFn as f, type PraxisModule as g, type ConstraintId as h, type ContractGap as i, type PraxisEngineOptions as j, type Reference as k, type RuleId as l, createPraxisEngine as m, defineContract as n, getContract as o, isContract as p };
637
+ export { type Assumption as A, type ConstraintDescriptor as C, type DefineContractOptions as D, type Example as E, LogicEngine as L, type MissingArtifact as M, PraxisRegistry as P, type ReactiveEngineOptions as R, type Severity as S, type TypedRuleFn as T, type ValidationReport as V, ReactiveLogicEngine as a, type RuleDescriptor as b, createReactiveEngine as c, type ConstraintFn as d, type Contract as e, type RuleFn as f, type PraxisModule as g, type ConstraintId as h, type ContractGap as i, type PraxisEngineOptions as j, type Reference as k, type RuleId as l, RuleResult as m, createPraxisEngine as n, defineContract as o, fact as p, getContract as q, isContract as r };
@@ -2,10 +2,11 @@ import {
2
2
  generateContractFromRule,
3
3
  scanRepository,
4
4
  writeLogicLedgerEntry
5
- } from "./chunk-WZ6B3LZ6.js";
5
+ } from "./chunk-7CSWBDFL.js";
6
+ import "./chunk-PGVSB6NR.js";
6
7
  import {
7
8
  PraxisRegistry
8
- } from "./chunk-R2PSBPKQ.js";
9
+ } from "./chunk-TEMFJOIH.js";
9
10
  import "./chunk-QGM4M3NI.js";
10
11
 
11
12
  // src/cli/commands/reverse.ts
@@ -0,0 +1,7 @@
1
+ import {
2
+ PraxisRegistry
3
+ } from "./chunk-TEMFJOIH.js";
4
+ import "./chunk-QGM4M3NI.js";
5
+ export {
6
+ PraxisRegistry
7
+ };