interaqt 2.0.0 → 2.0.2

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 (94) hide show
  1. package/README.md +1 -0
  2. package/agent/agentspace/knowledge/generator/api-reference.md +28 -5
  3. package/agent/agentspace/knowledge/generator/computation-implementation.md +61 -14
  4. package/agent/agentspace/knowledge/usage/00-mindset-shift.md +2 -0
  5. package/agent/agentspace/knowledge/usage/04-reactive-computations.md +158 -101
  6. package/agent/agentspace/knowledge/usage/05-interactions.md +67 -22
  7. package/agent/agentspace/knowledge/usage/08-activities.md +10 -0
  8. package/agent/agentspace/knowledge/usage/09-filtered-entities.md +1 -0
  9. package/agent/agentspace/knowledge/usage/13-testing.md +52 -99
  10. package/agent/agentspace/knowledge/usage/14-api-reference.md +22 -5
  11. package/agent/agentspace/knowledge/usage/15-entity-crud-patterns.md +89 -28
  12. package/agent/agentspace/knowledge/usage/18-api-exports-reference.md +10 -8
  13. package/agent/agentspace/knowledge/usage/19-common-anti-patterns.md +11 -2
  14. package/dist/builtins/interaction/Action.d.ts.map +1 -1
  15. package/dist/builtins/interaction/Activity.d.ts.map +1 -1
  16. package/dist/builtins/interaction/Attributive.d.ts.map +1 -1
  17. package/dist/builtins/interaction/Condition.d.ts.map +1 -1
  18. package/dist/builtins/interaction/Conditions.d.ts.map +1 -1
  19. package/dist/builtins/interaction/Data.d.ts.map +1 -1
  20. package/dist/builtins/interaction/Event.d.ts.map +1 -1
  21. package/dist/builtins/interaction/Gateway.d.ts.map +1 -1
  22. package/dist/builtins/interaction/Interaction.d.ts.map +1 -1
  23. package/dist/builtins/interaction/Payload.d.ts.map +1 -1
  24. package/dist/builtins/interaction/PayloadItem.d.ts.map +1 -1
  25. package/dist/builtins/interaction/activity/ActivityCall.d.ts.map +1 -1
  26. package/dist/core/Entity.d.ts.map +1 -1
  27. package/dist/core/EventSource.d.ts +37 -0
  28. package/dist/core/EventSource.d.ts.map +1 -1
  29. package/dist/core/Relation.d.ts.map +1 -1
  30. package/dist/core/init.d.ts.map +1 -1
  31. package/dist/core/utils.d.ts.map +1 -1
  32. package/dist/drivers/Mysql.d.ts +4 -3
  33. package/dist/drivers/Mysql.d.ts.map +1 -1
  34. package/dist/drivers/PGLite.d.ts +2 -2
  35. package/dist/drivers/PGLite.d.ts.map +1 -1
  36. package/dist/drivers/PostgreSQL.d.ts +2 -2
  37. package/dist/drivers/PostgreSQL.d.ts.map +1 -1
  38. package/dist/drivers/SQLite.d.ts +3 -3
  39. package/dist/drivers/SQLite.d.ts.map +1 -1
  40. package/dist/drivers.d.ts +2 -0
  41. package/dist/drivers.js +984 -0
  42. package/dist/drivers.js.map +1 -0
  43. package/dist/index.js +5930 -5736
  44. package/dist/index.js.map +1 -1
  45. package/dist/runtime/ComputationSourceMap.d.ts +11 -0
  46. package/dist/runtime/ComputationSourceMap.d.ts.map +1 -1
  47. package/dist/runtime/Controller.d.ts.map +1 -1
  48. package/dist/runtime/Scheduler.d.ts +21 -3
  49. package/dist/runtime/Scheduler.d.ts.map +1 -1
  50. package/dist/runtime/System.d.ts +1 -0
  51. package/dist/runtime/System.d.ts.map +1 -1
  52. package/dist/runtime/computations/Any.d.ts +17 -52
  53. package/dist/runtime/computations/Any.d.ts.map +1 -1
  54. package/dist/runtime/computations/Average.d.ts +15 -49
  55. package/dist/runtime/computations/Average.d.ts.map +1 -1
  56. package/dist/runtime/computations/Computation.d.ts +8 -0
  57. package/dist/runtime/computations/Computation.d.ts.map +1 -1
  58. package/dist/runtime/computations/Count.d.ts +21 -64
  59. package/dist/runtime/computations/Count.d.ts.map +1 -1
  60. package/dist/runtime/computations/Custom.d.ts.map +1 -1
  61. package/dist/runtime/computations/Every.d.ts +18 -52
  62. package/dist/runtime/computations/Every.d.ts.map +1 -1
  63. package/dist/runtime/computations/StateMachine.d.ts +2 -2
  64. package/dist/runtime/computations/StateMachine.d.ts.map +1 -1
  65. package/dist/runtime/computations/Summation.d.ts +15 -49
  66. package/dist/runtime/computations/Summation.d.ts.map +1 -1
  67. package/dist/runtime/computations/Transform.d.ts.map +1 -1
  68. package/dist/runtime/computations/WeightedSummation.d.ts +18 -60
  69. package/dist/runtime/computations/WeightedSummation.d.ts.map +1 -1
  70. package/dist/runtime/computations/aggregationTemplate.d.ts +163 -0
  71. package/dist/runtime/computations/aggregationTemplate.d.ts.map +1 -0
  72. package/dist/runtime/migration.d.ts.map +1 -1
  73. package/dist/storage/erstorage/AttributeQuery.d.ts.map +1 -1
  74. package/dist/storage/erstorage/CreationExecutor.d.ts.map +1 -1
  75. package/dist/storage/erstorage/DeletionExecutor.d.ts.map +1 -1
  76. package/dist/storage/erstorage/EntityQueryHandle.d.ts +13 -0
  77. package/dist/storage/erstorage/EntityQueryHandle.d.ts.map +1 -1
  78. package/dist/storage/erstorage/EntityToTableMap.d.ts +2 -0
  79. package/dist/storage/erstorage/EntityToTableMap.d.ts.map +1 -1
  80. package/dist/storage/erstorage/LinkInfo.d.ts +1 -0
  81. package/dist/storage/erstorage/LinkInfo.d.ts.map +1 -1
  82. package/dist/storage/erstorage/MatchExp.d.ts +3 -2
  83. package/dist/storage/erstorage/MatchExp.d.ts.map +1 -1
  84. package/dist/storage/erstorage/MergedItemProcessor.d.ts +12 -0
  85. package/dist/storage/erstorage/MergedItemProcessor.d.ts.map +1 -1
  86. package/dist/storage/erstorage/NewRecordData.d.ts +1 -0
  87. package/dist/storage/erstorage/NewRecordData.d.ts.map +1 -1
  88. package/dist/storage/erstorage/QueryExecutor.d.ts.map +1 -1
  89. package/dist/storage/erstorage/RecordQueryAgent.d.ts +11 -0
  90. package/dist/storage/erstorage/RecordQueryAgent.d.ts.map +1 -1
  91. package/dist/storage/erstorage/Setup.d.ts +25 -0
  92. package/dist/storage/erstorage/Setup.d.ts.map +1 -1
  93. package/dist/storage/erstorage/UpdateExecutor.d.ts.map +1 -1
  94. package/package.json +5 -1
@@ -629,8 +629,12 @@ const User = Entity.create({
629
629
  StateTransfer.create({
630
630
  current: NameUpdatedState,
631
631
  next: NameUpdatedState,
632
- trigger: UpdateUserProfile,
633
- computeTarget: (event) => ({ id: event.payload.userId })
632
+ trigger: {
633
+ recordName: InteractionEventEntity.name,
634
+ type: 'create',
635
+ record: { interactionName: UpdateUserProfile.name }
636
+ },
637
+ computeTarget: (event) => ({ id: event.record.payload.userId })
634
638
  })
635
639
  ],
636
640
  initialState: NameUpdatedState
@@ -645,8 +649,12 @@ const User = Entity.create({
645
649
  StateTransfer.create({
646
650
  current: BioUpdatedState,
647
651
  next: BioUpdatedState,
648
- trigger: UpdateUserProfile,
649
- computeTarget: (event) => ({ id: event.payload.userId })
652
+ trigger: {
653
+ recordName: InteractionEventEntity.name,
654
+ type: 'create',
655
+ record: { interactionName: UpdateUserProfile.name }
656
+ },
657
+ computeTarget: (event) => ({ id: event.record.payload.userId })
650
658
  })
651
659
  ],
652
660
  initialState: BioUpdatedState
@@ -704,20 +712,32 @@ const Post = Entity.create({
704
712
  StateTransfer.create({
705
713
  current: draftState,
706
714
  next: publishedState,
707
- trigger: PublishPost,
708
- computeTarget: (event) => ({ id: event.payload.postId })
715
+ trigger: {
716
+ recordName: InteractionEventEntity.name,
717
+ type: 'create',
718
+ record: { interactionName: PublishPost.name }
719
+ },
720
+ computeTarget: (event) => ({ id: event.record.payload.postId })
709
721
  }),
710
722
  StateTransfer.create({
711
723
  current: publishedState,
712
724
  next: deletedState,
713
- trigger: DeletePost,
714
- computeTarget: (event) => ({ id: event.payload.postId })
725
+ trigger: {
726
+ recordName: InteractionEventEntity.name,
727
+ type: 'create',
728
+ record: { interactionName: DeletePost.name }
729
+ },
730
+ computeTarget: (event) => ({ id: event.record.payload.postId })
715
731
  }),
716
732
  StateTransfer.create({
717
733
  current: draftState,
718
734
  next: deletedState,
719
- trigger: DeletePost,
720
- computeTarget: (event) => ({ id: event.payload.postId })
735
+ trigger: {
736
+ recordName: InteractionEventEntity.name,
737
+ type: 'create',
738
+ record: { interactionName: DeletePost.name }
739
+ },
740
+ computeTarget: (event) => ({ id: event.record.payload.postId })
721
741
  })
722
742
  ]
723
743
  })
@@ -821,26 +841,42 @@ const Order = Entity.create({
821
841
  StateTransfer.create({
822
842
  current: pendingState,
823
843
  next: confirmedState,
824
- trigger: ConfirmPayment,
825
- computeTarget: (event) => ({ id: event.payload.orderId })
844
+ trigger: {
845
+ recordName: InteractionEventEntity.name,
846
+ type: 'create',
847
+ record: { interactionName: ConfirmPayment.name }
848
+ },
849
+ computeTarget: (event) => ({ id: event.record.payload.orderId })
826
850
  }),
827
851
  StateTransfer.create({
828
852
  current: confirmedState,
829
853
  next: shippedState,
830
- trigger: ShipOrder,
831
- computeTarget: (event) => ({ id: event.payload.orderId })
854
+ trigger: {
855
+ recordName: InteractionEventEntity.name,
856
+ type: 'create',
857
+ record: { interactionName: ShipOrder.name }
858
+ },
859
+ computeTarget: (event) => ({ id: event.record.payload.orderId })
832
860
  }),
833
861
  StateTransfer.create({
834
862
  current: shippedState,
835
863
  next: deliveredState,
836
- trigger: ConfirmDelivery,
837
- computeTarget: (event) => ({ id: event.payload.orderId })
864
+ trigger: {
865
+ recordName: InteractionEventEntity.name,
866
+ type: 'create',
867
+ record: { interactionName: ConfirmDelivery.name }
868
+ },
869
+ computeTarget: (event) => ({ id: event.record.payload.orderId })
838
870
  }),
839
871
  StateTransfer.create({
840
872
  current: pendingState,
841
873
  next: cancelledState,
842
- trigger: CancelOrder,
843
- computeTarget: (event) => ({ id: event.payload.orderId })
874
+ trigger: {
875
+ recordName: InteractionEventEntity.name,
876
+ type: 'create',
877
+ record: { interactionName: CancelOrder.name }
878
+ },
879
+ computeTarget: (event) => ({ id: event.record.payload.orderId })
844
880
  })
845
881
  ]
846
882
  })
@@ -1130,14 +1166,22 @@ const OrderStateMachine = StateMachine.create({
1130
1166
  StateTransfer.create({
1131
1167
  current: PendingState,
1132
1168
  next: PaidState,
1133
- trigger: PayOrder,
1134
- computeTarget: (event) => ({ id: event.payload.orderId })
1169
+ trigger: {
1170
+ recordName: InteractionEventEntity.name,
1171
+ type: 'create',
1172
+ record: { interactionName: PayOrder.name }
1173
+ },
1174
+ computeTarget: (event) => ({ id: event.record.payload.orderId })
1135
1175
  }),
1136
1176
  StateTransfer.create({
1137
1177
  current: PaidState,
1138
1178
  next: ShippedState,
1139
- trigger: ShipOrder,
1140
- computeTarget: (event) => ({ id: event.payload.orderId })
1179
+ trigger: {
1180
+ recordName: InteractionEventEntity.name,
1181
+ type: 'create',
1182
+ record: { interactionName: ShipOrder.name }
1183
+ },
1184
+ computeTarget: (event) => ({ id: event.record.payload.orderId })
1141
1185
  })
1142
1186
  ]
1143
1187
  });
@@ -1300,6 +1344,7 @@ const Order = Entity.create({
1300
1344
  computation: Every.create({
1301
1345
  record: OrderItemRelation,
1302
1346
  relationDirection: 'source',
1347
+ attributeQuery: ['quantity', ['product', { attributeQuery: ['stock'] }]], // Required when callback reads fields
1303
1348
  callback: function(orderItem) {
1304
1349
  // Check if each order item has sufficient product inventory
1305
1350
  return orderItem.product.stock >= orderItem.quantity;
@@ -1,5 +1,15 @@
1
1
  # How to Use Activities for Process Management
2
2
 
3
+ > **⚠️ Maintenance note (2026-07-09)**: parts of this guide are outdated. The REAL Activity API is:
4
+ > `Activity.create({ name, interactions, transfers, groups? })` with `Transfer.create({ name, source, target })`,
5
+ > where `source`/`target` are Interaction / ActivityGroup instances. **There is no string-based
6
+ > `Transfer.create({ from, to, interaction, condition })` form** — examples below using that shape are
7
+ > historical sketches and do not run. Activities are dispatched through `ActivityManager`:
8
+ > compile with `new ActivityManager(activities).getOutput()`, pass the output entities/relations/eventSources
9
+ > to the Controller, then `controller.dispatch(controller.findEventSourceByName('ActivityName:interactionName'), { user, activityId? })`.
10
+ > For a runnable end-to-end example see `agent/agentspace/knowledge/usage/13-testing.md` §12.2.2
11
+ > and `tests/runtime/activity.spec.ts`. A full rewrite of this guide is tracked as follow-up work.
12
+
3
13
  Activities are the core mechanism in interaqt for managing complex business processes. They allow you to define multi-step, multi-role business processes and control the execution order and conditions of processes through a state machine model.
4
14
 
5
15
  ## Understanding Activity Concepts
@@ -373,6 +373,7 @@ const User = Entity.create({
373
373
  defaultValue: () => 0,
374
374
  computation: WeightedSummation.create({
375
375
  record: UserPublishedPosts,
376
+ attributeQuery: [['target', { attributeQuery: ['viewCount'] }]], // Required when callback reads fields
376
377
  callback: (relation) => ({
377
378
  weight: 1,
378
379
  value: relation.target.viewCount
@@ -26,7 +26,8 @@ storage.create('Entity', data) // ✅ Create record
26
26
 
27
27
  ```typescript
28
28
  import { describe, test, expect, beforeEach } from 'vitest'
29
- import { Controller, MonoSystem, KlassByName, PGLiteDB, MatchExp } from 'interaqt'
29
+ import { Controller, MonoSystem, KlassByName, MatchExp } from 'interaqt'
30
+ import { PGLiteDB } from 'interaqt/drivers'
30
31
  import { entities, relations, interactions, activities } from '../backend'
31
32
  // If you need UUID, install and import it:
32
33
  // npm install uuid @types/uuid
@@ -574,121 +575,72 @@ describe('User Interactions', () => {
574
575
 
575
576
  ### 12.2.2 Activity Testing
576
577
 
578
+ An Activity is an ordered composition of Interactions (`Activity.create({ name, interactions, transfers })`, with `Transfer.create({ name, source, target })` connecting them — NOT StateNode/StateTransfer, which belong to StateMachine). Use `ActivityManager` to compile activities into dispatchable event sources named `activityName:interactionName`, and drive them through `controller.dispatch`:
579
+
577
580
  ```typescript
578
581
  // tests/activities/approvalProcess.spec.ts
582
+ import {
583
+ Activity, ActivityManager, Transfer, Interaction, Action,
584
+ Controller, MonoSystem, KlassByName
585
+ } from 'interaqt';
586
+ import { PGLiteDB } from '@drivers';
587
+
579
588
  describe('Approval Process Activity', () => {
580
589
  test('should handle complete approval workflow', async () => {
581
590
  const system = new MonoSystem(new PGLiteDB());
582
-
583
- // Create request entity
584
- const requestEntity = Entity.create({
585
- name: 'Request',
586
- properties: [
587
- Property.create({ name: 'title', type: 'string' }),
588
- Property.create({ name: 'status', type: 'string' }),
589
- Property.create({ name: 'submitterId', type: 'string' })
590
- ]
591
- });
592
-
593
- // Create activity states
594
- const submittedState = StateNode.create({ name: 'submitted' });
595
- const reviewingState = StateNode.create({ name: 'reviewing' });
596
- const approvedState = StateNode.create({ name: 'approved' });
597
- const rejectedState = StateNode.create({ name: 'rejected' });
598
-
599
- // Create interactions
600
- const submitInteraction = Interaction.create({
591
+ system.conceptClass = KlassByName;
592
+
593
+ // 1. Declare the interactions of the workflow
594
+ const submit = Interaction.create({
601
595
  name: 'submit',
602
596
  action: Action.create({ name: 'submit' })
603
597
  });
604
-
605
- const approveInteraction = Interaction.create({
598
+ const approve = Interaction.create({
606
599
  name: 'approve',
607
600
  action: Action.create({ name: 'approve' })
608
601
  });
609
-
610
- const rejectInteraction = Interaction.create({
611
- name: 'reject',
612
- action: Action.create({ name: 'reject' })
613
- });
614
-
615
- // Create state transfers
616
- const submitTransfer = StateTransfer.create({
617
- trigger: submitInteraction,
618
- current: submittedState,
619
- next: reviewingState
620
- });
621
-
622
- const approveTransfer = StateTransfer.create({
623
- trigger: approveInteraction,
624
- current: reviewingState,
625
- next: approvedState
626
- });
627
-
628
- const rejectTransfer = StateTransfer.create({
629
- trigger: rejectInteraction,
630
- current: reviewingState,
631
- next: rejectedState
632
- });
633
-
634
- // Create activity
602
+
603
+ // 2. Compose them into an activity: submit → approve
635
604
  const approvalActivity = Activity.create({
636
605
  name: 'ApprovalProcess',
637
- states: [submittedState, reviewingState, approvedState, rejectedState],
638
- transfers: [submitTransfer, approveTransfer, rejectTransfer],
639
- initialState: submittedState
606
+ interactions: [submit, approve],
607
+ transfers: [
608
+ Transfer.create({ name: 'submitToApprove', source: submit, target: approve })
609
+ ]
640
610
  });
641
-
611
+
612
+ // 3. ActivityManager compiles the activity into event sources / records
613
+ const activityManager = new ActivityManager([approvalActivity]);
614
+ const activityOutput = activityManager.getOutput();
615
+
642
616
  const controller = new Controller({
643
617
  system,
644
- entities: [requestEntity],
645
- relations: [],
646
- activities: [approvalActivity], // activities
647
- interactions: [submitInteraction, approveInteraction, rejectInteraction], // interactions
648
- dict: []
618
+ entities: [...activityOutput.entities],
619
+ relations: [...activityOutput.relations],
620
+ eventSources: [...activityOutput.eventSources]
649
621
  });
650
622
  await controller.setup(true);
651
-
652
- // Create request
653
- const request = await system.storage.create('Request', {
654
- title: 'Test Request',
655
- status: 'submitted',
656
- submitterId: 'user123'
657
- });
658
-
659
- // Execute submit interaction
660
- await controller.callInteraction(submitInteraction.name, {
661
- user: { id: 'user123' }, // Add user object
662
- payload: {
663
- requestId: request.id
664
- }
665
- });
666
-
667
- // Verify state transition
668
- let updatedRequest = await system.storage.findOne(
669
- 'Request',
670
- MatchExp.atom({ key: 'id', value: ['=', request.id] }),
671
- undefined,
672
- ['id', 'title', 'status', 'submitterId'] // Specify fields
673
- );
674
- expect(updatedRequest.status).toBe('reviewing');
675
-
676
- // Execute approve interaction
677
- await controller.callInteraction(approveInteraction.name, {
678
- user: { id: 'admin-user' }, // Add user object
679
- payload: {
680
- requestId: request.id
681
- }
682
- });
683
-
684
- // Verify final state
685
- updatedRequest = await system.storage.findOne(
686
- 'Request',
687
- MatchExp.atom({ key: 'id', value: ['=', request.id] }),
688
- undefined,
689
- ['id', 'title', 'status', 'submitterId'] // Specify fields
690
- );
691
- expect(updatedRequest.status).toBe('approved');
623
+
624
+ const submitES = controller.findEventSourceByName('ApprovalProcess:submit')!;
625
+ const approveES = controller.findEventSourceByName('ApprovalProcess:approve')!;
626
+
627
+ // 4. The head interaction creates the activity instance
628
+ const res1 = await controller.dispatch(submitES, { user: { id: 'user123' } });
629
+ expect(res1.error).toBeUndefined();
630
+ const activityId = res1.context!.activityId as string;
631
+
632
+ // 5. Subsequent interactions must carry the activityId
633
+ const res2 = await controller.dispatch(approveES, { user: { id: 'admin-user' }, activityId });
634
+ expect(res2.error).toBeUndefined();
635
+
636
+ // 6. The activity is complete: no further interaction is available
637
+ const res3 = await controller.dispatch(approveES, { user: { id: 'admin-user' }, activityId });
638
+ expect(res3.error).toBeDefined();
639
+
640
+ // 7. Verify the activity state directly
641
+ const activityCall = activityManager.getActivityCallByName('ApprovalProcess')!;
642
+ const state = await activityCall.getState(controller, activityId);
643
+ expect(state.current).toBeUndefined(); // undefined = completed
692
644
  });
693
645
  });
694
646
  ```
@@ -724,7 +676,8 @@ Permission testing is an important component of interaqt application testing, re
724
676
 
725
677
  ```typescript
726
678
  import { describe, test, expect, beforeEach } from 'vitest';
727
- import { Controller, MonoSystem, KlassByName, PGLiteDB } from 'interaqt';
679
+ import { Controller, MonoSystem, KlassByName } from 'interaqt';
680
+ import { PGLiteDB } from 'interaqt/drivers';
728
681
 
729
682
  describe('Permission Testing', () => {
730
683
  let system: MonoSystem;
@@ -299,6 +299,7 @@ const userTotalScore = Property.create({
299
299
  defaultValue: () => 0, // Must provide default value
300
300
  computation: WeightedSummation.create({
301
301
  record: UserScoreRelation,
302
+ attributeQuery: ['multiplier', 'points'], // Required when callback reads fields
302
303
  callback: function(scoreRecord) {
303
304
  return {
304
305
  weight: scoreRecord.multiplier || 1,
@@ -311,6 +312,7 @@ const userTotalScore = Property.create({
311
312
  // Global weighted summation
312
313
  const globalWeightedScore = WeightedSummation.create({
313
314
  record: ScoreRecord,
315
+ attributeQuery: ['difficulty', 'score'], // Required when callback reads fields
314
316
  callback: function(record) {
315
317
  return {
316
318
  weight: record.difficulty,
@@ -460,6 +462,7 @@ const completedAllRequired = Property.create({
460
462
  defaultValue: () => false, // Must provide default value
461
463
  computation: Every.create({
462
464
  record: UserCourseRelation,
465
+ attributeQuery: ['status'], // Required when callback reads fields
463
466
  callback: function(courseRelation) {
464
467
  return courseRelation.status === 'completed'
465
468
  },
@@ -491,6 +494,7 @@ const hasPendingTasks = Property.create({
491
494
  defaultValue: () => false, // Must provide default value
492
495
  computation: Any.create({
493
496
  record: UserTaskRelation,
497
+ attributeQuery: ['status'], // Required when callback reads fields
494
498
  callback: function(taskRelation) {
495
499
  return taskRelation.status === 'pending'
496
500
  }
@@ -530,6 +534,7 @@ Custom callbacks may be replayed after retryable transaction failures. Do not pe
530
534
 
531
535
  **Incremental planning**
532
536
  - If `incrementalCompute` or `incrementalPatchCompute` is provided, the computation must also provide `incrementalDataDeps` or `planIncremental`.
537
+ - Mutation event snapshot shapes differ by type: `create` events carry the written fields in `record` (`oldRecord` is `undefined`); `update` events carry **only the changed fields** (+ `id`) in `record` and the full pre-update snapshot in `oldRecord`; `delete` events carry the full deleted snapshot in `record` (`oldRecord` is `undefined`). For updates, reconstruct the full new state with `{ ...mutationEvent.oldRecord, ...mutationEvent.record }` or re-fetch the record — reading unchanged fields off `mutationEvent.record` yields `undefined`.
533
538
  - `incrementalDataDeps: string[]` lists exactly which `dataDeps` keys should be resolved for the incremental callback; use `[]` when none are needed.
534
539
  - `planIncremental(event, record, context)` is the advanced form. Return `{ type: 'incremental', dataDepKeys, needsLastValue? }`, `{ type: 'fullRecompute', reason }`, or `{ type: 'skip', reason }`.
535
540
  - Incremental callbacks receive only planned partial `dataDeps`; the runtime no longer resolves all `dataDeps` before incremental execution.
@@ -621,7 +626,11 @@ const OrderStateMachine = StateMachine.create({
621
626
  StateTransfer.create({
622
627
  current: pendingState,
623
628
  next: confirmedState,
624
- trigger: ConfirmOrderInteraction
629
+ trigger: {
630
+ recordName: InteractionEventEntity.name,
631
+ type: 'create',
632
+ record: { interactionName: ConfirmOrderInteraction.name }
633
+ }
625
634
  })
626
635
  ],
627
636
  initialState: pendingState
@@ -927,23 +936,31 @@ StateTransfer.create(config: StateTransferConfig): KlassInstance<typeof StateTra
927
936
  ```
928
937
 
929
938
  **Parameters**
930
- - `config.trigger` (any, required): Trigger for the state transfer (usually an Interaction)
939
+ - `config.trigger` (RecordMutationEventPattern, required): Pattern matched against record mutation events: `{ recordName, type, keys?, record?, oldRecord? }`. For interaction-triggered transfers use `{ recordName: InteractionEventEntity.name, type: 'create', record: { interactionName: SomeInteraction.name } }`. `keys` uses subset semantics (fires when the update touched all listed fields)
931
940
  - `config.current` (StateNode, required): Current state node
932
941
  - `config.next` (StateNode, required): Next state node
933
- - `config.computeTarget` (function, optional): Function to dynamically compute the target state
942
+ - `config.computeTarget` (function, optional for global-level, **required for property-level** state machines): Maps a matched mutation event to the record(s) whose property should transition, e.g. `(event) => ({ id: event.record.payload.id })`
934
943
 
935
944
  **Examples**
936
945
  ```typescript
937
946
  // Simple state transfer
938
947
  const approveTransfer = StateTransfer.create({
939
- trigger: ApproveInteraction,
948
+ trigger: {
949
+ recordName: InteractionEventEntity.name,
950
+ type: 'create',
951
+ record: { interactionName: ApproveInteraction.name }
952
+ },
940
953
  current: pendingState,
941
954
  next: approvedState
942
955
  });
943
956
 
944
957
  // State transfer with dynamic target computation
945
958
  const conditionalTransfer = StateTransfer.create({
946
- trigger: ProcessInteraction,
959
+ trigger: {
960
+ recordName: InteractionEventEntity.name,
961
+ type: 'create',
962
+ record: { interactionName: ProcessInteraction.name }
963
+ },
947
964
  current: pendingState,
948
965
  next: approvedState, // Default next state
949
966
  computeTarget: (context) => {
@@ -363,14 +363,22 @@ const ArticleStatusStateMachine = StateMachine.create({
363
363
  StateTransfer.create({
364
364
  current: ActiveState,
365
365
  next: DeletedState,
366
- trigger: DeleteArticle,
367
- computeTarget: (event) => ({ id: event.payload.articleId })
366
+ trigger: {
367
+ recordName: InteractionEventEntity.name,
368
+ type: 'create',
369
+ record: { interactionName: DeleteArticle.name }
370
+ },
371
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
368
372
  }),
369
373
  StateTransfer.create({
370
374
  current: DeletedState,
371
375
  next: ActiveState,
372
- trigger: RestoreArticle,
373
- computeTarget: (event) => ({ id: event.payload.articleId })
376
+ trigger: {
377
+ recordName: InteractionEventEntity.name,
378
+ type: 'create',
379
+ record: { interactionName: RestoreArticle.name }
380
+ },
381
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
374
382
  })
375
383
  ]
376
384
  });
@@ -418,14 +426,22 @@ const Article = Entity.create({
418
426
  StateTransfer.create({
419
427
  current: activeState,
420
428
  next: deletedState,
421
- trigger: DeleteArticle,
422
- computeTarget: (event) => ({ id: event.payload.articleId })
429
+ trigger: {
430
+ recordName: InteractionEventEntity.name,
431
+ type: 'create',
432
+ record: { interactionName: DeleteArticle.name }
433
+ },
434
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
423
435
  }),
424
436
  StateTransfer.create({
425
437
  current: deletedState,
426
438
  next: activeState,
427
- trigger: RestoreArticle,
428
- computeTarget: (event) => ({ id: event.payload.articleId })
439
+ trigger: {
440
+ recordName: InteractionEventEntity.name,
441
+ type: 'create',
442
+ record: { interactionName: RestoreArticle.name }
443
+ },
444
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
429
445
  })
430
446
  ],
431
447
  initialState: activeState
@@ -476,11 +492,15 @@ deletionProperty.computation = StateMachine.create({
476
492
  initialState: NON_DELETED_STATE,
477
493
  transfers: [
478
494
  StateTransfer.create({
479
- trigger: DeleteArticle,
495
+ trigger: {
496
+ recordName: InteractionEventEntity.name,
497
+ type: 'create',
498
+ record: { interactionName: DeleteArticle.name }
499
+ },
480
500
  current: NON_DELETED_STATE,
481
501
  next: DELETED_STATE,
482
502
  computeTarget: function(event) {
483
- return { id: event.payload.articleId };
503
+ return { id: event.record.payload.articleId };
484
504
  }
485
505
  })
486
506
  ]
@@ -611,14 +631,22 @@ const ArticlePublishStateMachine = StateMachine.create({
611
631
  StateTransfer.create({
612
632
  current: DraftState,
613
633
  next: PublishedState,
614
- trigger: PublishArticle,
615
- computeTarget: (event) => ({ id: event.payload.articleId })
634
+ trigger: {
635
+ recordName: InteractionEventEntity.name,
636
+ type: 'create',
637
+ record: { interactionName: PublishArticle.name }
638
+ },
639
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
616
640
  }),
617
641
  StateTransfer.create({
618
642
  current: PublishedState,
619
643
  next: DraftState,
620
- trigger: UnpublishArticle,
621
- computeTarget: (event) => ({ id: event.payload.articleId })
644
+ trigger: {
645
+ recordName: InteractionEventEntity.name,
646
+ type: 'create',
647
+ record: { interactionName: UnpublishArticle.name }
648
+ },
649
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
622
650
  })
623
651
  ]
624
652
  });
@@ -722,8 +750,12 @@ const TimestampStateMachine = StateMachine.create({
722
750
  StateTransfer.create({
723
751
  current: TimestampState,
724
752
  next: TimestampState,
725
- trigger: RecordActivity,
726
- computeTarget: (event) => ({ id: event.payload.entityId })
753
+ trigger: {
754
+ recordName: InteractionEventEntity.name,
755
+ type: 'create',
756
+ record: { interactionName: RecordActivity.name }
757
+ },
758
+ computeTarget: (event) => ({ id: event.record.payload.entityId })
727
759
  })
728
760
  ]
729
761
  });
@@ -767,7 +799,11 @@ const User = Entity.create({
767
799
  StateTransfer.create({
768
800
  current: activeState,
769
801
  next: activeState,
770
- trigger: UserActivityInteraction,
802
+ trigger: {
803
+ recordName: InteractionEventEntity.name,
804
+ type: 'create',
805
+ record: { interactionName: UserActivityInteraction.name }
806
+ },
771
807
  computeTarget: (event) => ({ id: event.user.id })
772
808
  })
773
809
  ],
@@ -801,8 +837,12 @@ const Article = Entity.create({
801
837
  StateTransfer.create({
802
838
  current: modifiedState,
803
839
  next: modifiedState,
804
- trigger: UpdateArticleInteraction,
805
- computeTarget: (event) => ({ id: event.payload.articleId })
840
+ trigger: {
841
+ recordName: InteractionEventEntity.name,
842
+ type: 'create',
843
+ record: { interactionName: UpdateArticleInteraction.name }
844
+ },
845
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
806
846
  })
807
847
  ],
808
848
  initialState: modifiedState
@@ -834,8 +874,12 @@ const Sensor = Entity.create({
834
874
  StateTransfer.create({
835
875
  current: triggeredState,
836
876
  next: triggeredState,
837
- trigger: SensorTriggerInteraction,
838
- computeTarget: (event) => ({ id: event.payload.sensorId })
877
+ trigger: {
878
+ recordName: InteractionEventEntity.name,
879
+ type: 'create',
880
+ record: { interactionName: SensorTriggerInteraction.name }
881
+ },
882
+ computeTarget: (event) => ({ id: event.record.payload.sensorId })
839
883
  })
840
884
  ],
841
885
  initialState: triggeredState
@@ -896,7 +940,11 @@ Property.create({
896
940
  StateTransfer.create({
897
941
  current: activeState,
898
942
  next: activeState,
899
- trigger: UserActivityInteraction,
943
+ trigger: {
944
+ recordName: InteractionEventEntity.name,
945
+ type: 'create',
946
+ record: { interactionName: UserActivityInteraction.name }
947
+ },
900
948
  computeTarget: (event) => ({ id: event.user.id })
901
949
  })
902
950
  ]
@@ -925,6 +973,7 @@ const User = Entity.create({
925
973
  computation: Count.create({
926
974
  record: UserArticleRelation,
927
975
  direction: 'target',
976
+ attributeQuery: [['source', { attributeQuery: ['status'] }]], // Required when callback reads fields
928
977
  callback: (relation) => relation.source.status !== 'deleted'
929
978
  })
930
979
  })
@@ -1033,20 +1082,32 @@ const ArticleLifecycleStateMachine = StateMachine.create({
1033
1082
  StateTransfer.create({
1034
1083
  current: DraftState,
1035
1084
  next: PublishedState,
1036
- trigger: PublishArticle,
1037
- computeTarget: (event) => ({ id: event.payload.articleId })
1085
+ trigger: {
1086
+ recordName: InteractionEventEntity.name,
1087
+ type: 'create',
1088
+ record: { interactionName: PublishArticle.name }
1089
+ },
1090
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
1038
1091
  }),
1039
1092
  StateTransfer.create({
1040
1093
  current: PublishedState,
1041
1094
  next: DeletedState,
1042
- trigger: DeleteArticle,
1043
- computeTarget: (event) => ({ id: event.payload.articleId })
1095
+ trigger: {
1096
+ recordName: InteractionEventEntity.name,
1097
+ type: 'create',
1098
+ record: { interactionName: DeleteArticle.name }
1099
+ },
1100
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
1044
1101
  }),
1045
1102
  StateTransfer.create({
1046
1103
  current: DraftState,
1047
1104
  next: DeletedState,
1048
- trigger: DeleteArticle,
1049
- computeTarget: (event) => ({ id: event.payload.articleId })
1105
+ trigger: {
1106
+ recordName: InteractionEventEntity.name,
1107
+ type: 'create',
1108
+ record: { interactionName: DeleteArticle.name }
1109
+ },
1110
+ computeTarget: (event) => ({ id: event.record.payload.articleId })
1050
1111
  })
1051
1112
  ]
1052
1113
  });