@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
package/README.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Praxis
2
2
 
3
+ <!-- plures-readme-banner -->
4
+ [![CI](https://github.com/plures/praxis/actions/workflows/ci.yml/badge.svg)](https://github.com/plures/praxis/actions/workflows/ci.yml)
5
+ [![npm](https://img.shields.io/npm/v/%40plures%2Fpraxis.svg)](https://www.npmjs.com/package/@plures/praxis)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+
3
9
  **Typed, visual-first application logic for Svelte, Node, and the browser.**
4
10
 
5
11
  [![npm version](https://img.shields.io/npm/v/@plures/praxis.svg)](https://www.npmjs.com/package/@plures/praxis)
@@ -312,6 +318,8 @@ See [src/decision-ledger/README.md](./src/decision-ledger/README.md) for complet
312
318
  ## Documentation
313
319
  - [Getting Started](./GETTING_STARTED.md)
314
320
  - [Framework Guide](./FRAMEWORK.md)
321
+ - [Praxis-Core API](./docs/core/praxis-core-api.md) - Stable API surface & guarantees
322
+ - [Extending Praxis-Core](./docs/core/extending-praxis-core.md) - Extension guidelines
315
323
  - [Decision Ledger Guide](./src/decision-ledger/README.md)
316
324
  - [Examples](./examples/)
317
325
 
@@ -452,6 +460,44 @@ This protocol is:
452
460
 
453
461
  ## Framework Architecture
454
462
 
463
+ Praxis is organized as a **monorepo** with clearly separated packages. See [MONOREPO.md](./MONOREPO.md) for the complete organization plan.
464
+
465
+ ### Target Monorepo Structure
466
+
467
+ ```
468
+ praxis/
469
+ ├── packages/ # Published npm packages
470
+ │ ├── praxis-core/ # Core logic library (zero dependencies)
471
+ │ │ └── src/
472
+ │ │ ├── logic/ # Facts, events, rules, constraints, engine
473
+ │ │ ├── schema/ # Schema definitions and validation
474
+ │ │ ├── decision-ledger/ # Contracts and behavior specifications
475
+ │ │ └── protocol/ # Core protocol types
476
+ │ ├── praxis-cli/ # Command-line interface
477
+ │ │ └── src/
478
+ │ │ ├── commands/ # CLI commands
479
+ │ │ └── generators/ # Code generators
480
+ │ ├── praxis-svelte/ # Svelte 5 integration
481
+ │ │ └── src/
482
+ │ │ ├── components/ # Reactive Svelte components
483
+ │ │ ├── generators/ # Component generators
484
+ │ │ └── runtime/ # Svelte runtime integration
485
+ │ ├── praxis-cloud/ # Cloud sync and relay
486
+ │ │ └── src/
487
+ │ │ ├── relay/ # Cloud relay server
488
+ │ │ └── sync/ # Sync protocol
489
+ │ └── praxis/ # Main package (re-exports all)
490
+ ├── apps/ # Example applications
491
+ ├── tools/ # Development tools
492
+ ├── ui/ # UI components and tools
493
+ ├── docs/ # Documentation
494
+ └── examples/ # Simple examples and demos
495
+ ```
496
+
497
+ ### Current Structure (In Transition)
498
+
499
+ The existing code is currently located in:
500
+
455
501
  ```
456
502
  /praxis
457
503
  ├── core/ # Core framework
@@ -1110,6 +1156,10 @@ MIT License - see [LICENSE](./LICENSE) for details.
1110
1156
 
1111
1157
  Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) to get started.
1112
1158
 
1159
+ **Automated Updates**: This repository uses batched bot updates to reduce commit churn. Dependency updates are grouped weekly and include audit trails. See the [Bot Update Policy](./docs/BOT_UPDATE_POLICY.md) for details.
1160
+
1161
+ **Dogfooding Plures Tools**: We actively dogfood all Plures tools during development. If you encounter friction while using any tool, please file a [Dogfooding Friction Report](https://github.com/plures/praxis/issues/new/choose). See the [Dogfooding Quick Start](./docs/DOGFOODING_QUICK_START.md) for details.
1162
+
1113
1163
  - 🐛 [Report a bug](https://github.com/plures/praxis/issues/new?template=bug_report.yml)
1114
1164
  - 💡 [Request a feature](https://github.com/plures/praxis/issues/new?template=enhancement.yml)
1115
1165
  - 📖 [Improve documentation](https://github.com/plures/praxis/issues/new?template=bug_report.yml)
@@ -1131,3 +1181,16 @@ Please review our [Code of Conduct](./CODE_OF_CONDUCT.md) before participating.
1131
1181
  ---
1132
1182
 
1133
1183
  Built with ❤️ by the plures team
1184
+
1185
+ ---
1186
+
1187
+ <!-- plures-readme-standard-sections -->
1188
+ ## Overview
1189
+
1190
+ ## Install
1191
+
1192
+ ## Development
1193
+
1194
+ ## Contributing
1195
+
1196
+ ## License
@@ -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
  */
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createPraxisEngine
3
- } from "./chunk-VOMLVI6V.js";
3
+ } from "./chunk-BBP2F7TT.js";
4
4
 
5
5
  // src/core/rules.ts
6
6
  var PraxisRegistry = class {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  LogicEngine,
3
3
  createPraxisEngine
4
- } from "./chunk-VOMLVI6V.js";
4
+ } from "./chunk-BBP2F7TT.js";
5
5
  export {
6
6
  LogicEngine,
7
7
  createPraxisEngine
@@ -1,5 +1,5 @@
1
- import { L as LogicEngine, P as PraxisState, a as PraxisEvent, b as PraxisRegistry, R as RuleDescriptor, C as ConstraintDescriptor, c as PraxisFact, d as RuleFn, e as Contract, f as ConstraintFn, g as PraxisModule } from './reactive-engine.svelte-9aS0kTa8.js';
2
- export { n as ConstraintId, l as PRAXIS_PROTOCOL_VERSION, h as PraxisDiagnostics, o as PraxisEngineOptions, i as PraxisStepConfig, k as PraxisStepFn, j as PraxisStepResult, q as ReactiveEngineOptions, r as ReactiveLogicEngine, m as RuleId, p as createPraxisEngine, s as createReactiveEngine } from './reactive-engine.svelte-9aS0kTa8.js';
1
+ import { L as LogicEngine, P as PraxisState, a as PraxisEvent, b as PraxisRegistry, R as RuleDescriptor, C as ConstraintDescriptor, c as ConstraintFn, d as Contract, e as RuleFn, f as PraxisFact, g as PraxisModule } from './reactive-engine.svelte-Cqd8Mod2.js';
2
+ export { h as ConstraintId, i as PRAXIS_PROTOCOL_VERSION, j as PraxisDiagnostics, k as PraxisEngineOptions, l as PraxisStepConfig, m as PraxisStepFn, n as PraxisStepResult, o as ReactiveEngineOptions, p as ReactiveLogicEngine, q as RuleId, r as createPraxisEngine, s as createReactiveEngine } from './reactive-engine.svelte-Cqd8Mod2.js';
3
3
  import { LocalFirstOptions } from '@plures/pluresdb/local-first';
4
4
 
5
5
  /**
@@ -435,6 +435,11 @@ interface DefineRuleOptions<TContext = unknown> {
435
435
  id: string;
436
436
  description: string;
437
437
  impl: RuleFn<TContext>;
438
+ /**
439
+ * Optional event type filter — only evaluate this rule when at least one
440
+ * event in the batch has a matching `tag`. Accepts a single tag or array.
441
+ */
442
+ eventTypes?: string | string[];
438
443
  contract?: Contract;
439
444
  meta?: Record<string, unknown>;
440
445
  }
@@ -1103,6 +1108,86 @@ interface PraxisLocalFirstOptions extends LocalFirstOptions {
1103
1108
  */
1104
1109
  declare function createPraxisLocalFirst(options?: PraxisLocalFirstOptions): Promise<PluresDBPraxisAdapter>;
1105
1110
 
1111
+ /**
1112
+ * Chronicle Types
1113
+ *
1114
+ * Core types for the Chronicle causal tracking system.
1115
+ * Records state transitions as a causal graph stored in PluresDB.
1116
+ */
1117
+ /**
1118
+ * Direction for traversing causal chains.
1119
+ */
1120
+ type TraceDirection = 'backward' | 'forward' | 'both';
1121
+ /**
1122
+ * A recorded state transition event passed to Chronicle.
1123
+ */
1124
+ interface ChronicleEvent {
1125
+ /** Path to the changed value (fact or event stream path) */
1126
+ path: string;
1127
+ /** Value before the change (undefined for creates) */
1128
+ before?: unknown;
1129
+ /** Value after the change */
1130
+ after?: unknown;
1131
+ /** Parent span/node ID that caused this change */
1132
+ cause?: string;
1133
+ /** Session or request ID grouping related changes */
1134
+ context?: string;
1135
+ /** Additional metadata key-value pairs */
1136
+ metadata: Record<string, string>;
1137
+ }
1138
+ /**
1139
+ * A Chronicle node representing a single recorded state transition.
1140
+ */
1141
+ interface ChronicleNode {
1142
+ /** Unique node ID: `chronos:{timestamp}-{counter}` */
1143
+ id: string;
1144
+ /** Timestamp (ms since epoch) when this node was recorded */
1145
+ timestamp: number;
1146
+ /** The recorded state transition */
1147
+ event: ChronicleEvent;
1148
+ }
1149
+
1150
+ /**
1151
+ * Chronicle Interface and PluresDbChronicle Implementation
1152
+ *
1153
+ * Records state transitions as a causal graph in PluresDB.
1154
+ * Attached to PraxisDBStore via `.withChronicle()` for zero-effort observability.
1155
+ */
1156
+
1157
+ /**
1158
+ * Chronicle interface — records state transitions as a causal graph.
1159
+ *
1160
+ * Automatically attached to any PraxisDBStore at runtime via `.withChronicle()`.
1161
+ * Records state diffs as graph nodes with causal edges.
1162
+ */
1163
+ interface Chronicle {
1164
+ /**
1165
+ * Record a state transition and return the created node.
1166
+ */
1167
+ record(event: ChronicleEvent): Promise<ChronicleNode>;
1168
+ /**
1169
+ * Trace causality backward or forward from a node.
1170
+ *
1171
+ * @param nodeId Starting node ID
1172
+ * @param direction `'backward'` follows incoming edges, `'forward'` follows outgoing edges
1173
+ * @param maxDepth Maximum traversal depth (prevents cycles / infinite loops)
1174
+ */
1175
+ trace(nodeId: string, direction: TraceDirection, maxDepth: number): Promise<ChronicleNode[]>;
1176
+ /**
1177
+ * Return all Chronicle nodes recorded within a timestamp range.
1178
+ *
1179
+ * @param start Inclusive start timestamp (ms)
1180
+ * @param end Inclusive end timestamp (ms)
1181
+ */
1182
+ range(start: number, end: number): Promise<ChronicleNode[]>;
1183
+ /**
1184
+ * Return all Chronicle nodes belonging to a context (session/request).
1185
+ *
1186
+ * @param contextId The context identifier
1187
+ */
1188
+ subgraph(contextId: string): Promise<ChronicleNode[]>;
1189
+ }
1190
+
1106
1191
  /**
1107
1192
  * PraxisDB Store
1108
1193
  *
@@ -1182,9 +1267,26 @@ declare class PraxisDBStore<TContext = unknown> {
1182
1267
  private subscriptions;
1183
1268
  private factWatchers;
1184
1269
  private onRuleError;
1270
+ private chronicle?;
1185
1271
  constructor(options: PraxisDBStoreOptions<TContext> & {
1186
1272
  onRuleError?: RuleErrorHandler;
1187
1273
  });
1274
+ /**
1275
+ * Attach a Chronicle observer to this store.
1276
+ *
1277
+ * Every subsequent `storeFact` and `appendEvent` call will be recorded as a
1278
+ * causal graph node in PluresDB, enabling full observability for free.
1279
+ *
1280
+ * @param chronicle Chronicle implementation to attach
1281
+ * @returns `this` for fluent chaining
1282
+ *
1283
+ * @example
1284
+ * ```typescript
1285
+ * const store = createPraxisDBStore(db, registry)
1286
+ * .withChronicle(createChronicle(db));
1287
+ * ```
1288
+ */
1289
+ withChronicle(chronicle: Chronicle): this;
1188
1290
  /**
1189
1291
  * Store a fact in PluresDB
1190
1292
  *
@@ -2,12 +2,12 @@ import {
2
2
  PraxisRegistry,
3
3
  ReactiveLogicEngine,
4
4
  createReactiveEngine
5
- } from "./chunk-K377RW4V.js";
5
+ } from "./chunk-FCEH7WMH.js";
6
6
  import {
7
7
  LogicEngine,
8
8
  PRAXIS_PROTOCOL_VERSION,
9
9
  createPraxisEngine
10
- } from "./chunk-VOMLVI6V.js";
10
+ } from "./chunk-BBP2F7TT.js";
11
11
  import {
12
12
  InMemoryPraxisDB,
13
13
  PluresDBPraxisAdapter,
@@ -586,6 +586,7 @@ function defineRule(options) {
586
586
  id: options.id,
587
587
  description: options.description,
588
588
  impl: options.impl,
589
+ eventTypes: options.eventTypes,
589
590
  contract,
590
591
  meta
591
592
  };
@@ -884,6 +885,53 @@ function validateForGeneration(schema) {
884
885
  };
885
886
  }
886
887
 
888
+ // src/core/chronicle/context.ts
889
+ var ChronicleContext = class {
890
+ static _stack = [];
891
+ /**
892
+ * Get the current active span, if any.
893
+ */
894
+ static get current() {
895
+ return this._stack[this._stack.length - 1];
896
+ }
897
+ /**
898
+ * Run a synchronous function within a causal span.
899
+ * The span is automatically popped when the function returns.
900
+ */
901
+ static run(span, fn) {
902
+ this._stack.push(span);
903
+ try {
904
+ return fn();
905
+ } finally {
906
+ this._stack.pop();
907
+ }
908
+ }
909
+ /**
910
+ * Run an async function within a causal span.
911
+ * The span is popped after the promise settles.
912
+ */
913
+ static async runAsync(span, fn) {
914
+ this._stack.push(span);
915
+ try {
916
+ return await fn();
917
+ } finally {
918
+ this._stack.pop();
919
+ }
920
+ }
921
+ /**
922
+ * Create a child span that inherits the current contextId.
923
+ *
924
+ * @param spanId ID for the new span
925
+ * @returns A new ChronicleSpan with the current contextId
926
+ */
927
+ static childSpan(spanId) {
928
+ return {
929
+ spanId,
930
+ contextId: this.current?.contextId
931
+ };
932
+ }
933
+ };
934
+
887
935
  // src/core/pluresdb/store.ts
888
936
  var PRAXIS_PATHS = {
889
937
  /** Base path for all Praxis data */
@@ -919,12 +967,32 @@ var PraxisDBStore = class {
919
967
  subscriptions = [];
920
968
  factWatchers = /* @__PURE__ */ new Map();
921
969
  onRuleError;
970
+ chronicle;
922
971
  constructor(options) {
923
972
  this.db = options.db;
924
973
  this.registry = options.registry;
925
974
  this.context = options.initialContext ?? {};
926
975
  this.onRuleError = options.onRuleError ?? defaultErrorHandler;
927
976
  }
977
+ /**
978
+ * Attach a Chronicle observer to this store.
979
+ *
980
+ * Every subsequent `storeFact` and `appendEvent` call will be recorded as a
981
+ * causal graph node in PluresDB, enabling full observability for free.
982
+ *
983
+ * @param chronicle Chronicle implementation to attach
984
+ * @returns `this` for fluent chaining
985
+ *
986
+ * @example
987
+ * ```typescript
988
+ * const store = createPraxisDBStore(db, registry)
989
+ * .withChronicle(createChronicle(db));
990
+ * ```
991
+ */
992
+ withChronicle(chronicle) {
993
+ this.chronicle = chronicle;
994
+ return this;
995
+ }
928
996
  /**
929
997
  * Store a fact in PluresDB
930
998
  *
@@ -939,7 +1007,31 @@ var PraxisDBStore = class {
939
1007
  if (!constraintResult.valid) {
940
1008
  throw new Error(`Constraint violation: ${constraintResult.errors.join(", ")}`);
941
1009
  }
1010
+ let before;
1011
+ if (this.chronicle) {
1012
+ const payload = fact.payload;
1013
+ const id = payload?.id;
1014
+ if (id) {
1015
+ before = await this.getFact(fact.tag, id);
1016
+ }
1017
+ }
942
1018
  await this.persistFact(fact);
1019
+ if (this.chronicle) {
1020
+ const payload = fact.payload;
1021
+ const id = payload?.id ?? "";
1022
+ const span = ChronicleContext.current;
1023
+ try {
1024
+ await this.chronicle.record({
1025
+ path: getFactPath(fact.tag, id),
1026
+ before,
1027
+ after: fact,
1028
+ cause: span?.spanId,
1029
+ context: span?.contextId,
1030
+ metadata: { factTag: fact.tag, operation: "storeFact" }
1031
+ });
1032
+ } catch {
1033
+ }
1034
+ }
943
1035
  await this.triggerRules([fact]);
944
1036
  }
945
1037
  /**
@@ -953,7 +1045,31 @@ var PraxisDBStore = class {
953
1045
  throw new Error(`Constraint violation: ${constraintResult.errors.join(", ")}`);
954
1046
  }
955
1047
  for (const fact of facts) {
1048
+ let before;
1049
+ if (this.chronicle) {
1050
+ const payload = fact.payload;
1051
+ const id = payload?.id;
1052
+ if (id) {
1053
+ before = await this.getFact(fact.tag, id);
1054
+ }
1055
+ }
956
1056
  await this.persistFact(fact);
1057
+ if (this.chronicle) {
1058
+ const payload = fact.payload;
1059
+ const id = payload?.id ?? "";
1060
+ const span = ChronicleContext.current;
1061
+ try {
1062
+ await this.chronicle.record({
1063
+ path: getFactPath(fact.tag, id),
1064
+ before,
1065
+ after: fact,
1066
+ cause: span?.spanId,
1067
+ context: span?.contextId,
1068
+ metadata: { factTag: fact.tag, operation: "storeFacts" }
1069
+ });
1070
+ } catch {
1071
+ }
1072
+ }
957
1073
  }
958
1074
  await this.triggerRules(facts);
959
1075
  }
@@ -995,7 +1111,29 @@ var PraxisDBStore = class {
995
1111
  };
996
1112
  const newEvents = [...existingEvents, entry];
997
1113
  await this.db.set(path, newEvents);
998
- await this.triggerRulesForEvents([event]);
1114
+ let eventNodeId;
1115
+ if (this.chronicle) {
1116
+ const span = ChronicleContext.current;
1117
+ try {
1118
+ const node = await this.chronicle.record({
1119
+ path,
1120
+ before: existingEvents.length > 0 ? existingEvents[existingEvents.length - 1] : void 0,
1121
+ after: entry,
1122
+ cause: span?.spanId,
1123
+ context: span?.contextId,
1124
+ metadata: { eventTag: event.tag, sequence: String(entry.sequence), operation: "appendEvent" }
1125
+ });
1126
+ eventNodeId = node.id;
1127
+ } catch {
1128
+ }
1129
+ }
1130
+ const outerSpan = ChronicleContext.current;
1131
+ const ruleSpan = eventNodeId ? { spanId: eventNodeId, contextId: outerSpan?.contextId } : outerSpan;
1132
+ if (ruleSpan && this.chronicle) {
1133
+ await ChronicleContext.runAsync(ruleSpan, () => this.triggerRulesForEvents([event]));
1134
+ } else {
1135
+ await this.triggerRulesForEvents([event]);
1136
+ }
999
1137
  }
1000
1138
  /**
1001
1139
  * Append multiple events to their respective streams
@@ -1008,6 +1146,7 @@ var PraxisDBStore = class {
1008
1146
  const existing = eventsByTag.get(event.tag) ?? [];
1009
1147
  eventsByTag.set(event.tag, [...existing, event]);
1010
1148
  }
1149
+ let lastEventNodeId;
1011
1150
  for (const [tag, tagEvents] of eventsByTag) {
1012
1151
  const path = getEventPath(tag);
1013
1152
  const existingEvents = await this.db.get(path) ?? [];
@@ -1018,8 +1157,30 @@ var PraxisDBStore = class {
1018
1157
  sequence: sequence++
1019
1158
  }));
1020
1159
  await this.db.set(path, [...existingEvents, ...newEntries]);
1160
+ if (this.chronicle) {
1161
+ const span = ChronicleContext.current;
1162
+ for (const entry of newEntries) {
1163
+ try {
1164
+ const node = await this.chronicle.record({
1165
+ path,
1166
+ after: entry,
1167
+ cause: span?.spanId,
1168
+ context: span?.contextId,
1169
+ metadata: { eventTag: tag, sequence: String(entry.sequence), operation: "appendEvents" }
1170
+ });
1171
+ lastEventNodeId = node.id;
1172
+ } catch {
1173
+ }
1174
+ }
1175
+ }
1176
+ }
1177
+ const outerSpan = ChronicleContext.current;
1178
+ const ruleSpan = lastEventNodeId ? { spanId: lastEventNodeId, contextId: outerSpan?.contextId } : outerSpan;
1179
+ if (ruleSpan && this.chronicle) {
1180
+ await ChronicleContext.runAsync(ruleSpan, () => this.triggerRulesForEvents(events));
1181
+ } else {
1182
+ await this.triggerRulesForEvents(events);
1021
1183
  }
1022
- await this.triggerRulesForEvents(events);
1023
1184
  }
1024
1185
  /**
1025
1186
  * Get events from a stream
@@ -1132,6 +1293,21 @@ var PraxisDBStore = class {
1132
1293
  if (constraintResult.valid) {
1133
1294
  for (const fact of derivedFacts) {
1134
1295
  await this.persistFact(fact);
1296
+ if (this.chronicle) {
1297
+ const payload = fact.payload;
1298
+ const id = payload?.id ?? "";
1299
+ const span = ChronicleContext.current;
1300
+ try {
1301
+ await this.chronicle.record({
1302
+ path: getFactPath(fact.tag, id),
1303
+ after: fact,
1304
+ cause: span?.spanId,
1305
+ context: span?.contextId,
1306
+ metadata: { factTag: fact.tag, operation: "derivedFact" }
1307
+ });
1308
+ } catch {
1309
+ }
1310
+ }
1135
1311
  }
1136
1312
  }
1137
1313
  }
@@ -2713,7 +2889,7 @@ function generateTauriConfig(config) {
2713
2889
 
2714
2890
  // src/integrations/unified.ts
2715
2891
  async function createUnifiedApp(config) {
2716
- const { createPraxisEngine: createPraxisEngine2 } = await import("./engine-YJZV4SLD.js");
2892
+ const { createPraxisEngine: createPraxisEngine2 } = await import("./engine-65QDGCAN.js");
2717
2893
  const { createInMemoryDB: createInMemoryDB2 } = await import("./adapter-CIMBGDC7.js");
2718
2894
  const db = config.db || createInMemoryDB2();
2719
2895
  const pluresdb = createPluresDBAdapter({
@@ -1,5 +1,5 @@
1
- import { L as LogicEngine, P as PraxisState, a as PraxisEvent } from '../reactive-engine.svelte-9aS0kTa8.js';
2
- export { q as ReactiveEngineOptions, r as ReactiveLogicEngine, s as createReactiveEngine } from '../reactive-engine.svelte-9aS0kTa8.js';
1
+ import { P as PraxisState, a as PraxisEvent, L as LogicEngine } from '../reactive-engine.svelte-Cqd8Mod2.js';
2
+ export { o as ReactiveEngineOptions, p as ReactiveLogicEngine, s as createReactiveEngine } from '../reactive-engine.svelte-Cqd8Mod2.js';
3
3
 
4
4
  /**
5
5
  * Svelte v5 Integration
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  ReactiveLogicEngine,
3
3
  createReactiveEngine
4
- } from "../chunk-K377RW4V.js";
5
- import "../chunk-VOMLVI6V.js";
4
+ } from "../chunk-FCEH7WMH.js";
5
+ import "../chunk-BBP2F7TT.js";
6
6
 
7
7
  // src/integrations/svelte.ts
8
8
  function createPraxisStore(engine) {