@plures/praxis 1.2.12 → 1.2.41

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 (85) hide show
  1. package/README.md +63 -0
  2. package/dist/browser/{chunk-VOMLVI6V.js → chunk-BBP2F7TT.js} +70 -1
  3. package/dist/browser/{chunk-K377RW4V.js → chunk-FCEH7WMH.js} +1 -1
  4. package/dist/browser/{engine-YJZV4SLD.js → engine-65QDGCAN.js} +1 -1
  5. package/dist/browser/index.d.ts +104 -2
  6. package/dist/browser/index.js +181 -5
  7. package/dist/browser/integrations/svelte.d.ts +2 -2
  8. package/dist/browser/integrations/svelte.js +2 -2
  9. package/dist/browser/{reactive-engine.svelte-9aS0kTa8.d.ts → reactive-engine.svelte-Cqd8Mod2.d.ts} +56 -1
  10. package/dist/node/{chunk-PRPQO6R5.js → chunk-32YFEEML.js} +1 -1
  11. package/dist/node/{chunk-VOMLVI6V.js → chunk-BBP2F7TT.js} +70 -1
  12. package/dist/node/{chunk-5RH7UAQC.js → chunk-PTH6MD6P.js} +1 -0
  13. package/dist/node/cli/index.cjs +1553 -839
  14. package/dist/node/cli/index.js +39 -2
  15. package/dist/node/cloud/index.d.cts +1 -1
  16. package/dist/node/cloud/index.d.ts +1 -1
  17. package/dist/node/components/index.d.cts +2 -2
  18. package/dist/node/components/index.d.ts +2 -2
  19. package/dist/node/conversations-KQBXTP3N.js +596 -0
  20. package/dist/node/{engine-2DQBKBJC.js → engine-7CXQV6RC.js} +1 -1
  21. package/dist/node/index.cjs +408 -3
  22. package/dist/node/index.d.cts +308 -7
  23. package/dist/node/index.d.ts +308 -7
  24. package/dist/node/index.js +336 -6
  25. package/dist/node/integrations/svelte.cjs +70 -1
  26. package/dist/node/integrations/svelte.d.cts +3 -3
  27. package/dist/node/integrations/svelte.d.ts +3 -3
  28. package/dist/node/integrations/svelte.js +2 -2
  29. package/dist/node/{protocol-Qek7ebBl.d.ts → protocol-BocKczNv.d.cts} +1 -1
  30. package/dist/node/{protocol-Qek7ebBl.d.cts → protocol-BocKczNv.d.ts} +1 -1
  31. package/dist/node/{reactive-engine.svelte-CRNqHlbv.d.ts → reactive-engine.svelte-CGe8SpVE.d.cts} +57 -2
  32. package/dist/node/{reactive-engine.svelte-BFIZfawz.d.cts → reactive-engine.svelte-D-xTDxT5.d.ts} +57 -2
  33. package/dist/node/{terminal-adapter-B-UK_Vdz.d.ts → terminal-adapter-CvIvgTo4.d.ts} +1 -1
  34. package/dist/node/{terminal-adapter-BQSIF5bf.d.cts → terminal-adapter-Db-snPJ3.d.cts} +1 -1
  35. package/dist/node/{validate-CNHUULQE.js → validate-EN3M4FUR.js} +1 -1
  36. package/dist/node/{verify-KLJRXVJS.js → verify-7VZRP2WS.js} +2 -2
  37. package/docs/BOT_UPDATE_POLICY.md +125 -0
  38. package/docs/DOGFOODING_CHECKLIST.md +254 -0
  39. package/docs/DOGFOODING_INDEX.md +169 -0
  40. package/docs/DOGFOODING_QUICK_START.md +140 -0
  41. package/docs/KNO_ENG_EXTRACTION_PLAN.md +577 -0
  42. package/docs/PLURES_TOOLS_INVENTORY.md +170 -0
  43. package/docs/README.md +12 -0
  44. package/docs/TESTING_BOT_WORKFLOWS.md +154 -0
  45. package/docs/conversations/INTEGRATION_POINTS.md +719 -0
  46. package/docs/conversations/README.md +168 -0
  47. package/docs/core/extending-praxis-core.md +604 -0
  48. package/docs/core/praxis-core-api.md +385 -0
  49. package/docs/decision-ledger/contract-index.json +2 -2
  50. package/docs/decision-ledger/decisions/2026-02-01-monorepo-organization.md +130 -0
  51. package/docs/examples/DOGFOODING_WORKFLOW_EXAMPLE.md +295 -0
  52. package/docs/examples/README.md +41 -0
  53. package/docs/workflows/pr-overlap-guard.md +50 -0
  54. package/package.json +7 -2
  55. package/src/__tests__/chronicle.test.ts +512 -0
  56. package/src/__tests__/conversations.test.ts +312 -0
  57. package/src/__tests__/edge-cases.test.ts +1 -1
  58. package/src/__tests__/engine-dx.test.ts +355 -0
  59. package/src/cli/commands/conversations.ts +252 -0
  60. package/src/cli/index.ts +73 -0
  61. package/src/conversations/README.md +230 -0
  62. package/src/conversations/candidate.schema.json +123 -0
  63. package/src/conversations/candidates.ts +114 -0
  64. package/src/conversations/capture.ts +56 -0
  65. package/src/conversations/classify.ts +110 -0
  66. package/src/conversations/conversation.schema.json +106 -0
  67. package/src/conversations/emitters/fs.ts +65 -0
  68. package/src/conversations/emitters/github.ts +115 -0
  69. package/src/conversations/gate.ts +102 -0
  70. package/src/conversations/index.ts +28 -0
  71. package/src/conversations/normalize.ts +51 -0
  72. package/src/conversations/redact.ts +57 -0
  73. package/src/conversations/types.ts +96 -0
  74. package/src/core/chronicle/chronicle.ts +227 -0
  75. package/src/core/chronicle/context.ts +80 -0
  76. package/src/core/chronicle/index.ts +53 -0
  77. package/src/core/chronicle/mcp.ts +135 -0
  78. package/src/core/chronicle/types.ts +61 -0
  79. package/src/core/engine.ts +99 -1
  80. package/src/core/pluresdb/index.ts +22 -0
  81. package/src/core/pluresdb/store.ts +162 -5
  82. package/src/core/rules.ts +12 -0
  83. package/src/dsl/index.ts +6 -0
  84. package/src/index.ts +18 -0
  85. package/src/integrations/pluresdb.ts +22 -0
@@ -265,6 +265,18 @@ interface RuleDescriptor<TContext = unknown> {
265
265
  description: string;
266
266
  /** Implementation function */
267
267
  impl: RuleFn<TContext>;
268
+ /**
269
+ * Optional event type filter — only evaluate this rule when at least one
270
+ * event in the batch has a matching `tag`. When omitted, the rule runs on
271
+ * every step (catch-all).
272
+ *
273
+ * Accepts a single tag string or an array of tags.
274
+ *
275
+ * @example
276
+ * { id: 'sprint-behind', eventTypes: ['sprint.update'], impl: ... }
277
+ * { id: 'note-check', eventTypes: 'note.update', impl: ... }
278
+ */
279
+ eventTypes?: string | string[];
268
280
  /** Optional contract for rule behavior */
269
281
  contract?: Contract;
270
282
  /** Optional metadata */
@@ -393,6 +405,20 @@ interface PraxisEngineOptions<TContext = unknown> {
393
405
  initialFacts?: PraxisFact[];
394
406
  /** Initial metadata (optional) */
395
407
  initialMeta?: Record<string, unknown>;
408
+ /**
409
+ * Fact deduplication strategy (default: 'last-write-wins').
410
+ *
411
+ * - 'none': facts accumulate without dedup (original behavior)
412
+ * - 'last-write-wins': only keep the latest fact per tag (most common)
413
+ * - 'append': keep all facts but cap at maxFacts
414
+ */
415
+ factDedup?: 'none' | 'last-write-wins' | 'append';
416
+ /**
417
+ * Maximum number of facts to retain (default: 1000).
418
+ * When exceeded, oldest facts are evicted (FIFO).
419
+ * Set to 0 for unlimited (not recommended).
420
+ */
421
+ maxFacts?: number;
396
422
  }
397
423
  /**
398
424
  * The Praxis Logic Engine
@@ -403,6 +429,8 @@ interface PraxisEngineOptions<TContext = unknown> {
403
429
  declare class LogicEngine<TContext = unknown> {
404
430
  private state;
405
431
  private readonly registry;
432
+ private readonly factDedup;
433
+ private readonly maxFacts;
406
434
  constructor(options: PraxisEngineOptions<TContext>);
407
435
  /**
408
436
  * Get the current state (immutable copy)
@@ -441,6 +469,23 @@ declare class LogicEngine<TContext = unknown> {
441
469
  * @param updater Function that produces new context from old context
442
470
  */
443
471
  updateContext(updater: (context: TContext) => TContext): void;
472
+ /**
473
+ * Atomically update context AND process events in a single call.
474
+ *
475
+ * This avoids the fragile pattern of calling updateContext() then step()
476
+ * separately, where rules could see stale context if the ordering is wrong.
477
+ *
478
+ * @param updater Function that produces new context from old context
479
+ * @param events Events to process after context is updated
480
+ * @returns Result with new state and diagnostics
481
+ *
482
+ * @example
483
+ * engine.stepWithContext(
484
+ * ctx => ({ ...ctx, sprintName: sprint.name, items: sprint.items }),
485
+ * [{ tag: 'sprint.update', payload: { name: sprint.name } }]
486
+ * );
487
+ */
488
+ stepWithContext(updater: (context: TContext) => TContext, events: PraxisEvent[]): PraxisStepResult;
444
489
  /**
445
490
  * Add facts directly (for exceptional cases).
446
491
  * Generally, facts should be added through rules.
@@ -448,6 +493,16 @@ declare class LogicEngine<TContext = unknown> {
448
493
  * @param facts Facts to add
449
494
  */
450
495
  addFacts(facts: PraxisFact[]): void;
496
+ /**
497
+ * Check all constraints without processing any events.
498
+ *
499
+ * Useful for validation-only scenarios (e.g., form validation,
500
+ * pre-save checks) where you want constraint diagnostics without
501
+ * triggering any rules.
502
+ *
503
+ * @returns Array of constraint violation diagnostics (empty = all passing)
504
+ */
505
+ checkConstraints(): PraxisDiagnostics[];
451
506
  /**
452
507
  * Clear all facts
453
508
  */
@@ -551,4 +606,4 @@ declare class ReactiveLogicEngine<TContext extends object> {
551
606
  */
552
607
  declare function createReactiveEngine<TContext extends object>(options: ReactiveEngineOptions<TContext>): ReactiveLogicEngine<TContext>;
553
608
 
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 };
609
+ export { type ConstraintDescriptor as C, LogicEngine as L, type PraxisState as P, type RuleDescriptor as R, type PraxisEvent as a, PraxisRegistry as b, type ConstraintFn as c, type Contract as d, type RuleFn as e, type PraxisFact as f, type PraxisModule as g, type ConstraintId as h, PRAXIS_PROTOCOL_VERSION as i, type PraxisDiagnostics as j, type PraxisEngineOptions as k, type PraxisStepConfig as l, type PraxisStepFn as m, type PraxisStepResult as n, type ReactiveEngineOptions as o, ReactiveLogicEngine as p, type RuleId as q, createPraxisEngine as r, createReactiveEngine as s };
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-R2PSBPKQ.js";
4
4
  import {
5
5
  createPraxisEngine
6
- } from "./chunk-VOMLVI6V.js";
6
+ } from "./chunk-BBP2F7TT.js";
7
7
 
8
8
  // src/core/reactive-engine.svelte.ts
9
9
  import * as $ from "svelte/internal/client";
@@ -20,8 +20,12 @@ function safeClone(value) {
20
20
  var LogicEngine = class {
21
21
  state;
22
22
  registry;
23
+ factDedup;
24
+ maxFacts;
23
25
  constructor(options) {
24
26
  this.registry = options.registry;
27
+ this.factDedup = options.factDedup ?? "last-write-wins";
28
+ this.maxFacts = options.maxFacts ?? 1e3;
25
29
  this.state = {
26
30
  context: options.initialContext,
27
31
  facts: options.initialFacts ?? [],
@@ -77,6 +81,7 @@ var LogicEngine = class {
77
81
  const diagnostics = [];
78
82
  let newState = { ...this.state };
79
83
  const newFacts = [];
84
+ const eventTags = new Set(events.map((e) => e.tag));
80
85
  for (const ruleId of config.ruleIds) {
81
86
  const rule = this.registry.getRule(ruleId);
82
87
  if (!rule) {
@@ -87,6 +92,12 @@ var LogicEngine = class {
87
92
  });
88
93
  continue;
89
94
  }
95
+ if (rule.eventTypes) {
96
+ const filterTags = Array.isArray(rule.eventTypes) ? rule.eventTypes : [rule.eventTypes];
97
+ if (!filterTags.some((t) => eventTags.has(t))) {
98
+ continue;
99
+ }
100
+ }
90
101
  try {
91
102
  const ruleFacts = rule.impl(newState, events);
92
103
  newFacts.push(...ruleFacts);
@@ -98,9 +109,29 @@ var LogicEngine = class {
98
109
  });
99
110
  }
100
111
  }
112
+ let mergedFacts;
113
+ switch (this.factDedup) {
114
+ case "last-write-wins": {
115
+ const factMap = /* @__PURE__ */ new Map();
116
+ for (const f of newState.facts) factMap.set(f.tag, f);
117
+ for (const f of newFacts) factMap.set(f.tag, f);
118
+ mergedFacts = Array.from(factMap.values());
119
+ break;
120
+ }
121
+ case "append":
122
+ mergedFacts = [...newState.facts, ...newFacts];
123
+ break;
124
+ case "none":
125
+ default:
126
+ mergedFacts = [...newState.facts, ...newFacts];
127
+ break;
128
+ }
129
+ if (this.maxFacts > 0 && mergedFacts.length > this.maxFacts) {
130
+ mergedFacts = mergedFacts.slice(mergedFacts.length - this.maxFacts);
131
+ }
101
132
  newState = {
102
133
  ...newState,
103
- facts: [...newState.facts, ...newFacts]
134
+ facts: mergedFacts
104
135
  };
105
136
  for (const constraintId of config.constraintIds) {
106
137
  const constraint = this.registry.getConstraint(constraintId);
@@ -153,6 +184,29 @@ var LogicEngine = class {
153
184
  context: updater(this.state.context)
154
185
  };
155
186
  }
187
+ /**
188
+ * Atomically update context AND process events in a single call.
189
+ *
190
+ * This avoids the fragile pattern of calling updateContext() then step()
191
+ * separately, where rules could see stale context if the ordering is wrong.
192
+ *
193
+ * @param updater Function that produces new context from old context
194
+ * @param events Events to process after context is updated
195
+ * @returns Result with new state and diagnostics
196
+ *
197
+ * @example
198
+ * engine.stepWithContext(
199
+ * ctx => ({ ...ctx, sprintName: sprint.name, items: sprint.items }),
200
+ * [{ tag: 'sprint.update', payload: { name: sprint.name } }]
201
+ * );
202
+ */
203
+ stepWithContext(updater, events) {
204
+ this.state = {
205
+ ...this.state,
206
+ context: updater(this.state.context)
207
+ };
208
+ return this.step(events);
209
+ }
156
210
  /**
157
211
  * Add facts directly (for exceptional cases).
158
212
  * Generally, facts should be added through rules.
@@ -165,6 +219,21 @@ var LogicEngine = class {
165
219
  facts: [...this.state.facts, ...facts]
166
220
  };
167
221
  }
222
+ /**
223
+ * Check all constraints without processing any events.
224
+ *
225
+ * Useful for validation-only scenarios (e.g., form validation,
226
+ * pre-save checks) where you want constraint diagnostics without
227
+ * triggering any rules.
228
+ *
229
+ * @returns Array of constraint violation diagnostics (empty = all passing)
230
+ */
231
+ checkConstraints() {
232
+ return this.stepWithConfig([], {
233
+ ruleIds: [],
234
+ constraintIds: this.registry.getConstraintIds()
235
+ }).diagnostics;
236
+ }
168
237
  /**
169
238
  * Clear all facts
170
239
  */
@@ -32,6 +32,7 @@ function defineRule(options) {
32
32
  id: options.id,
33
33
  description: options.description,
34
34
  impl: options.impl,
35
+ eventTypes: options.eventTypes,
35
36
  contract,
36
37
  meta
37
38
  };