@plures/praxis 1.2.13 → 1.3.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.
- package/README.md +44 -0
- package/dist/browser/chunk-MJK3IYTJ.js +384 -0
- package/dist/browser/{chunk-K377RW4V.js → chunk-N63K4KWS.js} +1 -1
- package/dist/browser/{engine-YJZV4SLD.js → engine-YIEGSX7U.js} +1 -1
- package/dist/browser/index.d.ts +104 -2
- package/dist/browser/index.js +188 -7
- package/dist/browser/integrations/svelte.d.ts +2 -2
- package/dist/browser/integrations/svelte.js +2 -2
- package/dist/browser/{reactive-engine.svelte-9aS0kTa8.d.ts → reactive-engine.svelte-DjynI82A.d.ts} +139 -5
- package/dist/node/{chunk-PRPQO6R5.js → chunk-5JQJZADT.js} +1 -1
- package/dist/node/chunk-KMJWAFZV.js +389 -0
- package/dist/node/{chunk-5RH7UAQC.js → chunk-PTH6MD6P.js} +1 -0
- package/dist/node/cli/index.cjs +1553 -839
- package/dist/node/cli/index.js +39 -2
- package/dist/node/cloud/index.d.cts +1 -1
- package/dist/node/cloud/index.d.ts +1 -1
- package/dist/node/components/index.d.cts +2 -2
- package/dist/node/components/index.d.ts +2 -2
- package/dist/node/conversations-KQBXTP3N.js +596 -0
- package/dist/node/{engine-2DQBKBJC.js → engine-FEN5IYZ5.js} +1 -1
- package/dist/node/index.cjs +911 -43
- package/dist/node/index.d.cts +574 -7
- package/dist/node/index.d.ts +574 -7
- package/dist/node/index.js +672 -26
- package/dist/node/integrations/svelte.cjs +190 -3
- package/dist/node/integrations/svelte.d.cts +3 -3
- package/dist/node/integrations/svelte.d.ts +3 -3
- package/dist/node/integrations/svelte.js +2 -2
- package/dist/node/{protocol-Qek7ebBl.d.ts → protocol-DcyGMmWY.d.cts} +8 -1
- package/dist/node/{protocol-Qek7ebBl.d.cts → protocol-DcyGMmWY.d.ts} +8 -1
- package/dist/node/{reactive-engine.svelte-CRNqHlbv.d.ts → reactive-engine.svelte-Cg0Yc2Hs.d.cts} +145 -6
- package/dist/node/{reactive-engine.svelte-BFIZfawz.d.cts → reactive-engine.svelte-DekxqFu0.d.ts} +145 -6
- package/dist/node/{terminal-adapter-B-UK_Vdz.d.ts → terminal-adapter-CvIvgTo4.d.ts} +1 -1
- package/dist/node/{terminal-adapter-BQSIF5bf.d.cts → terminal-adapter-Db-snPJ3.d.cts} +1 -1
- package/dist/node/{validate-CNHUULQE.js → validate-EN3M4FUR.js} +1 -1
- package/dist/node/{verify-KLJRXVJS.js → verify-7VZRP2WS.js} +2 -2
- package/docs/BOT_UPDATE_POLICY.md +125 -0
- package/docs/DOGFOODING_CHECKLIST.md +254 -0
- package/docs/DOGFOODING_INDEX.md +169 -0
- package/docs/DOGFOODING_QUICK_START.md +140 -0
- package/docs/KNO_ENG_EXTRACTION_PLAN.md +577 -0
- package/docs/PLURES_TOOLS_INVENTORY.md +170 -0
- package/docs/README.md +12 -0
- package/docs/TESTING_BOT_WORKFLOWS.md +154 -0
- package/docs/conversations/INTEGRATION_POINTS.md +719 -0
- package/docs/conversations/README.md +168 -0
- package/docs/core/extending-praxis-core.md +604 -0
- package/docs/core/praxis-core-api.md +385 -0
- package/docs/decision-ledger/contract-index.json +2 -2
- package/docs/decision-ledger/decisions/2026-02-01-monorepo-organization.md +130 -0
- package/docs/examples/DOGFOODING_WORKFLOW_EXAMPLE.md +295 -0
- package/docs/examples/README.md +41 -0
- package/docs/workflows/pr-overlap-guard.md +50 -0
- package/package.json +8 -3
- package/src/__tests__/chronicle.test.ts +512 -0
- package/src/__tests__/conversations.test.ts +312 -0
- package/src/__tests__/edge-cases.test.ts +1 -1
- package/src/__tests__/engine-dx.test.ts +355 -0
- package/src/__tests__/engine-v2.test.ts +532 -0
- package/src/cli/commands/conversations.ts +252 -0
- package/src/cli/index.ts +73 -0
- package/src/conversations/README.md +230 -0
- package/src/conversations/candidate.schema.json +123 -0
- package/src/conversations/candidates.ts +114 -0
- package/src/conversations/capture.ts +56 -0
- package/src/conversations/classify.ts +110 -0
- package/src/conversations/conversation.schema.json +106 -0
- package/src/conversations/emitters/fs.ts +65 -0
- package/src/conversations/emitters/github.ts +115 -0
- package/src/conversations/gate.ts +102 -0
- package/src/conversations/index.ts +28 -0
- package/src/conversations/normalize.ts +51 -0
- package/src/conversations/redact.ts +57 -0
- package/src/conversations/types.ts +96 -0
- package/src/core/chronicle/chronicle.ts +227 -0
- package/src/core/chronicle/context.ts +80 -0
- package/src/core/chronicle/index.ts +53 -0
- package/src/core/chronicle/mcp.ts +135 -0
- package/src/core/chronicle/types.ts +61 -0
- package/src/core/completeness.ts +274 -0
- package/src/core/engine.ts +143 -3
- package/src/core/pluresdb/index.ts +22 -0
- package/src/core/pluresdb/store.ts +171 -8
- package/src/core/protocol.ts +7 -0
- package/src/core/rule-result.ts +130 -0
- package/src/core/rules.ts +24 -5
- package/src/core/ui-rules.ts +340 -0
- package/src/dsl/index.ts +6 -0
- package/src/index.ts +45 -0
- package/src/integrations/pluresdb.ts +22 -0
- package/src/vite/completeness-plugin.ts +72 -0
- package/dist/browser/chunk-VOMLVI6V.js +0 -197
- package/dist/node/chunk-VOMLVI6V.js +0 -197
|
@@ -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") {
|
|
@@ -215,8 +299,12 @@ function safeClone(value) {
|
|
|
215
299
|
var LogicEngine = class {
|
|
216
300
|
state;
|
|
217
301
|
registry;
|
|
302
|
+
factDedup;
|
|
303
|
+
maxFacts;
|
|
218
304
|
constructor(options) {
|
|
219
305
|
this.registry = options.registry;
|
|
306
|
+
this.factDedup = options.factDedup ?? "last-write-wins";
|
|
307
|
+
this.maxFacts = options.maxFacts ?? 1e3;
|
|
220
308
|
this.state = {
|
|
221
309
|
context: options.initialContext,
|
|
222
310
|
facts: options.initialFacts ?? [],
|
|
@@ -271,7 +359,14 @@ var LogicEngine = class {
|
|
|
271
359
|
stepWithConfig(events, config) {
|
|
272
360
|
const diagnostics = [];
|
|
273
361
|
let newState = { ...this.state };
|
|
362
|
+
const stateWithEvents = {
|
|
363
|
+
...newState,
|
|
364
|
+
events
|
|
365
|
+
// current batch — rules can read state.events
|
|
366
|
+
};
|
|
274
367
|
const newFacts = [];
|
|
368
|
+
const retractions = [];
|
|
369
|
+
const eventTags = new Set(events.map((e) => e.tag));
|
|
275
370
|
for (const ruleId of config.ruleIds) {
|
|
276
371
|
const rule = this.registry.getRule(ruleId);
|
|
277
372
|
if (!rule) {
|
|
@@ -282,9 +377,38 @@ var LogicEngine = class {
|
|
|
282
377
|
});
|
|
283
378
|
continue;
|
|
284
379
|
}
|
|
380
|
+
if (rule.eventTypes) {
|
|
381
|
+
const filterTags = Array.isArray(rule.eventTypes) ? rule.eventTypes : [rule.eventTypes];
|
|
382
|
+
if (!filterTags.some((t) => eventTags.has(t))) {
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
285
386
|
try {
|
|
286
|
-
const
|
|
287
|
-
|
|
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
|
+
}
|
|
288
412
|
} catch (error) {
|
|
289
413
|
diagnostics.push({
|
|
290
414
|
kind: "rule-error",
|
|
@@ -293,9 +417,34 @@ var LogicEngine = class {
|
|
|
293
417
|
});
|
|
294
418
|
}
|
|
295
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
|
+
}
|
|
425
|
+
let mergedFacts;
|
|
426
|
+
switch (this.factDedup) {
|
|
427
|
+
case "last-write-wins": {
|
|
428
|
+
const factMap = /* @__PURE__ */ new Map();
|
|
429
|
+
for (const f of existingFacts) factMap.set(f.tag, f);
|
|
430
|
+
for (const f of newFacts) factMap.set(f.tag, f);
|
|
431
|
+
mergedFacts = Array.from(factMap.values());
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
case "append":
|
|
435
|
+
mergedFacts = [...existingFacts, ...newFacts];
|
|
436
|
+
break;
|
|
437
|
+
case "none":
|
|
438
|
+
default:
|
|
439
|
+
mergedFacts = [...existingFacts, ...newFacts];
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
if (this.maxFacts > 0 && mergedFacts.length > this.maxFacts) {
|
|
443
|
+
mergedFacts = mergedFacts.slice(mergedFacts.length - this.maxFacts);
|
|
444
|
+
}
|
|
296
445
|
newState = {
|
|
297
446
|
...newState,
|
|
298
|
-
facts:
|
|
447
|
+
facts: mergedFacts
|
|
299
448
|
};
|
|
300
449
|
for (const constraintId of config.constraintIds) {
|
|
301
450
|
const constraint = this.registry.getConstraint(constraintId);
|
|
@@ -348,6 +497,29 @@ var LogicEngine = class {
|
|
|
348
497
|
context: updater(this.state.context)
|
|
349
498
|
};
|
|
350
499
|
}
|
|
500
|
+
/**
|
|
501
|
+
* Atomically update context AND process events in a single call.
|
|
502
|
+
*
|
|
503
|
+
* This avoids the fragile pattern of calling updateContext() then step()
|
|
504
|
+
* separately, where rules could see stale context if the ordering is wrong.
|
|
505
|
+
*
|
|
506
|
+
* @param updater Function that produces new context from old context
|
|
507
|
+
* @param events Events to process after context is updated
|
|
508
|
+
* @returns Result with new state and diagnostics
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* engine.stepWithContext(
|
|
512
|
+
* ctx => ({ ...ctx, sprintName: sprint.name, items: sprint.items }),
|
|
513
|
+
* [{ tag: 'sprint.update', payload: { name: sprint.name } }]
|
|
514
|
+
* );
|
|
515
|
+
*/
|
|
516
|
+
stepWithContext(updater, events) {
|
|
517
|
+
this.state = {
|
|
518
|
+
...this.state,
|
|
519
|
+
context: updater(this.state.context)
|
|
520
|
+
};
|
|
521
|
+
return this.step(events);
|
|
522
|
+
}
|
|
351
523
|
/**
|
|
352
524
|
* Add facts directly (for exceptional cases).
|
|
353
525
|
* Generally, facts should be added through rules.
|
|
@@ -360,6 +532,21 @@ var LogicEngine = class {
|
|
|
360
532
|
facts: [...this.state.facts, ...facts]
|
|
361
533
|
};
|
|
362
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* Check all constraints without processing any events.
|
|
537
|
+
*
|
|
538
|
+
* Useful for validation-only scenarios (e.g., form validation,
|
|
539
|
+
* pre-save checks) where you want constraint diagnostics without
|
|
540
|
+
* triggering any rules.
|
|
541
|
+
*
|
|
542
|
+
* @returns Array of constraint violation diagnostics (empty = all passing)
|
|
543
|
+
*/
|
|
544
|
+
checkConstraints() {
|
|
545
|
+
return this.stepWithConfig([], {
|
|
546
|
+
ruleIds: [],
|
|
547
|
+
constraintIds: this.registry.getConstraintIds()
|
|
548
|
+
}).diagnostics;
|
|
549
|
+
}
|
|
363
550
|
/**
|
|
364
551
|
* Clear all facts
|
|
365
552
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { L as LogicEngine } from '../reactive-engine.svelte-
|
|
2
|
-
export {
|
|
3
|
-
import { P as PraxisState, a as PraxisEvent } from '../protocol-
|
|
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-
|
|
2
|
-
export {
|
|
3
|
-
import { P as PraxisState, a as PraxisEvent } from '../protocol-
|
|
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-
|
|
4
|
+
} from "../chunk-5JQJZADT.js";
|
|
5
5
|
import "../chunk-R2PSBPKQ.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-KMJWAFZV.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) */
|
|
@@ -119,4 +126,4 @@ interface PraxisStepResult {
|
|
|
119
126
|
*/
|
|
120
127
|
type PraxisStepFn = (state: PraxisState, events: PraxisEvent[], config: PraxisStepConfig) => PraxisStepResult;
|
|
121
128
|
|
|
122
|
-
export { type PraxisState as P, type PraxisEvent as a, type PraxisFact as b, type PraxisStepResult as c, type PraxisStepConfig as d, type PraxisDiagnostics as e,
|
|
129
|
+
export { type PraxisState as P, type PraxisEvent as a, type PraxisFact as b, type PraxisStepResult as c, type PraxisStepConfig as d, type PraxisDiagnostics as e, PRAXIS_PROTOCOL_VERSION as f, type PraxisStepFn as g };
|
|
@@ -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) */
|
|
@@ -119,4 +126,4 @@ interface PraxisStepResult {
|
|
|
119
126
|
*/
|
|
120
127
|
type PraxisStepFn = (state: PraxisState, events: PraxisEvent[], config: PraxisStepConfig) => PraxisStepResult;
|
|
121
128
|
|
|
122
|
-
export { type PraxisState as P, type PraxisEvent as a, type PraxisFact as b, type PraxisStepResult as c, type PraxisStepConfig as d, type PraxisDiagnostics as e,
|
|
129
|
+
export { type PraxisState as P, type PraxisEvent as a, type PraxisFact as b, type PraxisStepResult as c, type PraxisStepConfig as d, type PraxisDiagnostics as e, PRAXIS_PROTOCOL_VERSION as f, type PraxisStepFn as g };
|
package/dist/node/{reactive-engine.svelte-CRNqHlbv.d.ts → reactive-engine.svelte-Cg0Yc2Hs.d.cts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
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
|
-
|
|
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.
|
|
@@ -209,6 +293,18 @@ interface RuleDescriptor<TContext = unknown> {
|
|
|
209
293
|
description: string;
|
|
210
294
|
/** Implementation function */
|
|
211
295
|
impl: RuleFn<TContext>;
|
|
296
|
+
/**
|
|
297
|
+
* Optional event type filter — only evaluate this rule when at least one
|
|
298
|
+
* event in the batch has a matching `tag`. When omitted, the rule runs on
|
|
299
|
+
* every step (catch-all).
|
|
300
|
+
*
|
|
301
|
+
* Accepts a single tag string or an array of tags.
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* { id: 'sprint-behind', eventTypes: ['sprint.update'], impl: ... }
|
|
305
|
+
* { id: 'note-check', eventTypes: 'note.update', impl: ... }
|
|
306
|
+
*/
|
|
307
|
+
eventTypes?: string | string[];
|
|
212
308
|
/** Optional contract for rule behavior */
|
|
213
309
|
contract?: Contract;
|
|
214
310
|
/** Optional metadata */
|
|
@@ -337,6 +433,20 @@ interface PraxisEngineOptions<TContext = unknown> {
|
|
|
337
433
|
initialFacts?: PraxisFact[];
|
|
338
434
|
/** Initial metadata (optional) */
|
|
339
435
|
initialMeta?: Record<string, unknown>;
|
|
436
|
+
/**
|
|
437
|
+
* Fact deduplication strategy (default: 'last-write-wins').
|
|
438
|
+
*
|
|
439
|
+
* - 'none': facts accumulate without dedup (original behavior)
|
|
440
|
+
* - 'last-write-wins': only keep the latest fact per tag (most common)
|
|
441
|
+
* - 'append': keep all facts but cap at maxFacts
|
|
442
|
+
*/
|
|
443
|
+
factDedup?: 'none' | 'last-write-wins' | 'append';
|
|
444
|
+
/**
|
|
445
|
+
* Maximum number of facts to retain (default: 1000).
|
|
446
|
+
* When exceeded, oldest facts are evicted (FIFO).
|
|
447
|
+
* Set to 0 for unlimited (not recommended).
|
|
448
|
+
*/
|
|
449
|
+
maxFacts?: number;
|
|
340
450
|
}
|
|
341
451
|
/**
|
|
342
452
|
* The Praxis Logic Engine
|
|
@@ -347,6 +457,8 @@ interface PraxisEngineOptions<TContext = unknown> {
|
|
|
347
457
|
declare class LogicEngine<TContext = unknown> {
|
|
348
458
|
private state;
|
|
349
459
|
private readonly registry;
|
|
460
|
+
private readonly factDedup;
|
|
461
|
+
private readonly maxFacts;
|
|
350
462
|
constructor(options: PraxisEngineOptions<TContext>);
|
|
351
463
|
/**
|
|
352
464
|
* Get the current state (immutable copy)
|
|
@@ -385,6 +497,23 @@ declare class LogicEngine<TContext = unknown> {
|
|
|
385
497
|
* @param updater Function that produces new context from old context
|
|
386
498
|
*/
|
|
387
499
|
updateContext(updater: (context: TContext) => TContext): void;
|
|
500
|
+
/**
|
|
501
|
+
* Atomically update context AND process events in a single call.
|
|
502
|
+
*
|
|
503
|
+
* This avoids the fragile pattern of calling updateContext() then step()
|
|
504
|
+
* separately, where rules could see stale context if the ordering is wrong.
|
|
505
|
+
*
|
|
506
|
+
* @param updater Function that produces new context from old context
|
|
507
|
+
* @param events Events to process after context is updated
|
|
508
|
+
* @returns Result with new state and diagnostics
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* engine.stepWithContext(
|
|
512
|
+
* ctx => ({ ...ctx, sprintName: sprint.name, items: sprint.items }),
|
|
513
|
+
* [{ tag: 'sprint.update', payload: { name: sprint.name } }]
|
|
514
|
+
* );
|
|
515
|
+
*/
|
|
516
|
+
stepWithContext(updater: (context: TContext) => TContext, events: PraxisEvent[]): PraxisStepResult;
|
|
388
517
|
/**
|
|
389
518
|
* Add facts directly (for exceptional cases).
|
|
390
519
|
* Generally, facts should be added through rules.
|
|
@@ -392,6 +521,16 @@ declare class LogicEngine<TContext = unknown> {
|
|
|
392
521
|
* @param facts Facts to add
|
|
393
522
|
*/
|
|
394
523
|
addFacts(facts: PraxisFact[]): void;
|
|
524
|
+
/**
|
|
525
|
+
* Check all constraints without processing any events.
|
|
526
|
+
*
|
|
527
|
+
* Useful for validation-only scenarios (e.g., form validation,
|
|
528
|
+
* pre-save checks) where you want constraint diagnostics without
|
|
529
|
+
* triggering any rules.
|
|
530
|
+
*
|
|
531
|
+
* @returns Array of constraint violation diagnostics (empty = all passing)
|
|
532
|
+
*/
|
|
533
|
+
checkConstraints(): PraxisDiagnostics[];
|
|
395
534
|
/**
|
|
396
535
|
* Clear all facts
|
|
397
536
|
*/
|
|
@@ -495,4 +634,4 @@ declare class ReactiveLogicEngine<TContext extends object> {
|
|
|
495
634
|
*/
|
|
496
635
|
declare function createReactiveEngine<TContext extends object>(options: ReactiveEngineOptions<TContext>): ReactiveLogicEngine<TContext>;
|
|
497
636
|
|
|
498
|
-
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,
|
|
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 };
|