@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
@@ -1,6 +1,93 @@
1
1
  // src/core/protocol.ts
2
2
  var PRAXIS_PROTOCOL_VERSION = "1.0.0";
3
3
 
4
+ // src/core/rule-result.ts
5
+ var RuleResult = class _RuleResult {
6
+ /** The kind of result */
7
+ kind;
8
+ /** Facts produced (only for 'emit') */
9
+ facts;
10
+ /** Fact tags to retract (only for 'retract') */
11
+ retractTags;
12
+ /** Optional reason (for noop/skip/retract — useful for debugging) */
13
+ reason;
14
+ /** The rule ID that produced this result (set by engine) */
15
+ ruleId;
16
+ constructor(kind, facts, retractTags, reason) {
17
+ this.kind = kind;
18
+ this.facts = facts;
19
+ this.retractTags = retractTags;
20
+ this.reason = reason;
21
+ }
22
+ /**
23
+ * Rule produced facts.
24
+ *
25
+ * @example
26
+ * return RuleResult.emit([
27
+ * { tag: 'sprint.behind', payload: { deficit: 5 } }
28
+ * ]);
29
+ */
30
+ static emit(facts) {
31
+ if (facts.length === 0) {
32
+ throw new Error(
33
+ "RuleResult.emit() requires at least one fact. Use RuleResult.noop() or RuleResult.skip() when a rule has nothing to say."
34
+ );
35
+ }
36
+ return new _RuleResult("emit", facts, []);
37
+ }
38
+ /**
39
+ * Rule evaluated but had nothing to report.
40
+ * Unlike returning [], this is explicit and traceable.
41
+ *
42
+ * @example
43
+ * if (ctx.completedHours >= expectedHours) {
44
+ * return RuleResult.noop('Sprint is on pace');
45
+ * }
46
+ */
47
+ static noop(reason) {
48
+ return new _RuleResult("noop", [], [], reason);
49
+ }
50
+ /**
51
+ * Rule decided to skip because preconditions were not met.
52
+ * Distinct from noop: skip means "I can't evaluate", noop means "I evaluated and found nothing".
53
+ *
54
+ * @example
55
+ * if (!ctx.sprintName) {
56
+ * return RuleResult.skip('No active sprint');
57
+ * }
58
+ */
59
+ static skip(reason) {
60
+ return new _RuleResult("skip", [], [], reason);
61
+ }
62
+ /**
63
+ * Rule retracts previously emitted facts by tag.
64
+ * Used when a condition that previously produced facts is no longer true.
65
+ *
66
+ * @example
67
+ * // Sprint was behind, but caught up
68
+ * if (ctx.completedHours >= expectedHours) {
69
+ * return RuleResult.retract(['sprint.behind'], 'Sprint caught up');
70
+ * }
71
+ */
72
+ static retract(tags, reason) {
73
+ if (tags.length === 0) {
74
+ throw new Error("RuleResult.retract() requires at least one tag.");
75
+ }
76
+ return new _RuleResult("retract", [], tags, reason);
77
+ }
78
+ /** Whether this result produced facts */
79
+ get hasFacts() {
80
+ return this.facts.length > 0;
81
+ }
82
+ /** Whether this result retracts facts */
83
+ get hasRetractions() {
84
+ return this.retractTags.length > 0;
85
+ }
86
+ };
87
+ function fact(tag, payload) {
88
+ return { tag, payload };
89
+ }
90
+
4
91
  // src/core/engine.ts
5
92
  function safeClone(value) {
6
93
  if (value === null || typeof value !== "object") {
@@ -80,7 +167,13 @@ var LogicEngine = class {
80
167
  stepWithConfig(events, config) {
81
168
  const diagnostics = [];
82
169
  let newState = { ...this.state };
170
+ const stateWithEvents = {
171
+ ...newState,
172
+ events
173
+ // current batch — rules can read state.events
174
+ };
83
175
  const newFacts = [];
176
+ const retractions = [];
84
177
  const eventTags = new Set(events.map((e) => e.tag));
85
178
  for (const ruleId of config.ruleIds) {
86
179
  const rule = this.registry.getRule(ruleId);
@@ -99,8 +192,31 @@ var LogicEngine = class {
99
192
  }
100
193
  }
101
194
  try {
102
- const ruleFacts = rule.impl(newState, events);
103
- newFacts.push(...ruleFacts);
195
+ const rawResult = rule.impl(stateWithEvents, events);
196
+ if (rawResult instanceof RuleResult) {
197
+ rawResult.ruleId = ruleId;
198
+ switch (rawResult.kind) {
199
+ case "emit":
200
+ newFacts.push(...rawResult.facts);
201
+ break;
202
+ case "retract":
203
+ retractions.push(...rawResult.retractTags);
204
+ break;
205
+ case "noop":
206
+ case "skip":
207
+ if (rawResult.reason) {
208
+ diagnostics.push({
209
+ kind: "rule-error",
210
+ // reused kind — could add 'rule-trace' in protocol v2
211
+ message: `[${rawResult.kind}] ${ruleId}: ${rawResult.reason}`,
212
+ data: { ruleId, resultKind: rawResult.kind, reason: rawResult.reason }
213
+ });
214
+ }
215
+ break;
216
+ }
217
+ } else if (Array.isArray(rawResult)) {
218
+ newFacts.push(...rawResult);
219
+ }
104
220
  } catch (error) {
105
221
  diagnostics.push({
106
222
  kind: "rule-error",
@@ -109,21 +225,26 @@ var LogicEngine = class {
109
225
  });
110
226
  }
111
227
  }
228
+ let existingFacts = newState.facts;
229
+ if (retractions.length > 0) {
230
+ const retractSet = new Set(retractions);
231
+ existingFacts = existingFacts.filter((f) => !retractSet.has(f.tag));
232
+ }
112
233
  let mergedFacts;
113
234
  switch (this.factDedup) {
114
235
  case "last-write-wins": {
115
236
  const factMap = /* @__PURE__ */ new Map();
116
- for (const f of newState.facts) factMap.set(f.tag, f);
237
+ for (const f of existingFacts) factMap.set(f.tag, f);
117
238
  for (const f of newFacts) factMap.set(f.tag, f);
118
239
  mergedFacts = Array.from(factMap.values());
119
240
  break;
120
241
  }
121
242
  case "append":
122
- mergedFacts = [...newState.facts, ...newFacts];
243
+ mergedFacts = [...existingFacts, ...newFacts];
123
244
  break;
124
245
  case "none":
125
246
  default:
126
- mergedFacts = [...newState.facts, ...newFacts];
247
+ mergedFacts = [...existingFacts, ...newFacts];
127
248
  break;
128
249
  }
129
250
  if (this.maxFacts > 0 && mergedFacts.length > this.maxFacts) {
@@ -261,6 +382,8 @@ function createPraxisEngine(options) {
261
382
 
262
383
  export {
263
384
  PRAXIS_PROTOCOL_VERSION,
385
+ RuleResult,
386
+ fact,
264
387
  LogicEngine,
265
388
  createPraxisEngine
266
389
  };
@@ -0,0 +1,59 @@
1
+ // src/decision-ledger/types.ts
2
+ function isContract(obj) {
3
+ if (typeof obj !== "object" || obj === null) {
4
+ return false;
5
+ }
6
+ const contract = obj;
7
+ return typeof contract.ruleId === "string" && typeof contract.behavior === "string" && Array.isArray(contract.examples) && contract.examples.length > 0 && contract.examples.every(
8
+ (ex) => typeof ex === "object" && ex !== null && typeof ex.given === "string" && typeof ex.when === "string" && typeof ex.then === "string"
9
+ ) && Array.isArray(contract.invariants) && contract.invariants.every((inv) => typeof inv === "string");
10
+ }
11
+ function defineContract(options) {
12
+ if (options.examples.length === 0) {
13
+ throw new Error("Contract must have at least one example");
14
+ }
15
+ if (options.assumptions) {
16
+ for (const assumption of options.assumptions) {
17
+ if (assumption.confidence < 0 || assumption.confidence > 1) {
18
+ throw new Error(
19
+ `Assumption '${assumption.id}' has invalid confidence value ${assumption.confidence}. Must be between 0.0 and 1.0`
20
+ );
21
+ }
22
+ }
23
+ }
24
+ return {
25
+ ruleId: options.ruleId,
26
+ behavior: options.behavior,
27
+ examples: options.examples,
28
+ invariants: options.invariants,
29
+ assumptions: options.assumptions,
30
+ references: options.references,
31
+ version: options.version || "1.0.0",
32
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
33
+ };
34
+ }
35
+ function getContract(meta) {
36
+ if (!meta || !meta.contract) {
37
+ return void 0;
38
+ }
39
+ if (isContract(meta.contract)) {
40
+ return meta.contract;
41
+ }
42
+ return void 0;
43
+ }
44
+ function getContractFromDescriptor(descriptor) {
45
+ if (!descriptor) {
46
+ return void 0;
47
+ }
48
+ if (descriptor.contract && isContract(descriptor.contract)) {
49
+ return descriptor.contract;
50
+ }
51
+ return getContract(descriptor.meta);
52
+ }
53
+
54
+ export {
55
+ isContract,
56
+ defineContract,
57
+ getContract,
58
+ getContractFromDescriptor
59
+ };