interaqt 0.7.4 → 0.8.1
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/agent/.claude/agents/bug-fix-handler.md +242 -0
- package/agent/.claude/agents/code-generation-handler.md +235 -86
- package/agent/.claude/agents/computation-generation-handler.md +236 -47
- package/agent/.claude/agents/error-check-handler.md +1251 -0
- package/agent/.claude/agents/frontend-generation-handler.md +397 -0
- package/agent/.claude/agents/implement-design-handler.md +76 -15
- package/agent/.claude/agents/implement-integration-handler.md +1689 -0
- package/agent/.claude/agents/permission-generation-handler.md +22 -11
- package/agent/.claude/agents/requirements-analysis-handler.md +812 -82
- package/agent/.claude/settings.local.json +36 -1
- package/agent/CLAUDE.md +53 -13
- package/agent/agentspace/knowledge/generator/computation-analysis.md +105 -21
- package/agent/agentspace/knowledge/generator/data-analysis.md +211 -17
- package/agent/agentspace/prompt/integration_sub_agent_refactor.md +19 -0
- package/dist/index.js +345 -399
- package/dist/index.js.map +1 -1
- package/dist/shared/Data.d.ts +30 -57
- package/dist/shared/Data.d.ts.map +1 -1
- package/dist/shared/Interaction.d.ts +6 -6
- package/dist/shared/Interaction.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -10,6 +10,14 @@ color: blue
|
|
|
10
10
|
|
|
11
11
|
## START: Select Next Uncompleted Item
|
|
12
12
|
|
|
13
|
+
**📖 FIRST: Determine current module and confirm context.**
|
|
14
|
+
|
|
15
|
+
**🔴 STEP 0: Determine Current Module**
|
|
16
|
+
1. Read module name from `.currentmodule` file in project root
|
|
17
|
+
2. If file doesn't exist, STOP and ask user which module to work on
|
|
18
|
+
3. Use this module name for all subsequent file operations
|
|
19
|
+
4. Module status file location: `docs/{module}.status.json`
|
|
20
|
+
|
|
13
21
|
**📖 Reference**
|
|
14
22
|
- `./backend/crud.example.ts` + `./tests/crud.example.test.ts` - Simple CRUD operations with state machines, relations, and permissions
|
|
15
23
|
- `./backend/versionControl.example.ts` + `./tests/versionControl.example.test.ts` - Version control with soft delete pattern
|
|
@@ -17,9 +25,13 @@ color: blue
|
|
|
17
25
|
- `./agentspace/knowledge/generator/computation-implementation.md` - Detailed computation implementation patterns and examples
|
|
18
26
|
|
|
19
27
|
|
|
28
|
+
**🔴 CRITICAL: Module-Based File Naming**
|
|
29
|
+
- Read module name from `.currentmodule` and use it as file prefix
|
|
30
|
+
- All file references must use `{module}.` prefix format
|
|
31
|
+
|
|
20
32
|
**🔴 CRITICAL: Implement ONLY ONE computation per session, then STOP and wait for user confirmation.**
|
|
21
33
|
|
|
22
|
-
1. **Read `docs/computation-implementation-plan.json`** to find the FIRST item with `completed: false`
|
|
34
|
+
1. **Read `docs/{module}.computation-implementation-plan.json`** to find the FIRST item with `completed: false`
|
|
23
35
|
- ALWAYS select the FIRST item where `completed` field is `false`
|
|
24
36
|
- NEVER skip ahead - dependencies must be completed in order
|
|
25
37
|
- Phase 1 must complete before Phase 2, etc.
|
|
@@ -35,12 +47,11 @@ color: blue
|
|
|
35
47
|
2. **Analyze Root Cause**:
|
|
36
48
|
- Verify implementation code correctness
|
|
37
49
|
- Check all `expandedDependencies` are properly handled
|
|
38
|
-
- Cross-reference with `requirements/interaction-matrix.md` for business logic
|
|
39
50
|
- Confirm test expectations match business requirements
|
|
40
51
|
- Review similar successful computations for patterns
|
|
41
52
|
|
|
42
53
|
3. **Apply Fix Based on Analysis**:
|
|
43
|
-
- **Implementation Issue** → Fix computation code in backend/
|
|
54
|
+
- **Implementation Issue** → Fix computation code in `backend/{module}.ts` (refer to API reference)
|
|
44
55
|
- **Test Issue** → Fix test case logic or expectations
|
|
45
56
|
- **Dependency Issue** → Fix data creation order
|
|
46
57
|
- **Business Logic Issue** → Re-read requirements and adjust
|
|
@@ -58,14 +69,84 @@ color: blue
|
|
|
58
69
|
|
|
59
70
|
1. **Implement the Computation** (following API Reference)
|
|
60
71
|
- **📖 MANDATORY FIRST STEP: Completely read `./agentspace/knowledge/generator/api-reference.md` to understand all API usage before writing any code**
|
|
61
|
-
- **📖 MANDATORY SECOND STEP: Completely read `./backend/
|
|
72
|
+
- **📖 MANDATORY SECOND STEP: Completely read `./backend/{module}.ts` to understand all existing implementations from previous tasks**
|
|
62
73
|
- **📖 MANDATORY THIRD STEP: Study the reference example files above to understand the standard code structure and computation patterns**
|
|
74
|
+
- **🔴 CRITICAL: Recognize Split Computation Nodes** - If the current item's ID contains `@InteractionName` (e.g., `Entity@CreateInteraction`, `Entity@UpdateInteraction`):
|
|
75
|
+
- This indicates the entity/relation can be created through multiple interaction paths
|
|
76
|
+
- Multiple nodes with same entity name but different `@InteractionName` suffixes represent ONE unified computation with multiple trigger paths
|
|
77
|
+
- **First node implementation**: Design the Transform to support ALL interactions from the start, even if you're only implementing the first path. Check the plan file for other nodes with the same entity name to see all interaction paths.
|
|
78
|
+
- **Subsequent nodes**: Add new interaction branch to the existing Transform, don't create separate computation
|
|
79
|
+
- Use `event.interactionName` or match conditions to branch between different interaction paths within a single Transform callback
|
|
80
|
+
- All nodes with same entity name share the same `ownerProperties` and `createdWithRelations` - implement them once in the unified Transform
|
|
81
|
+
- Example: `User@CreateUser` and `User@ImportUser` should result in ONE `User.computation` Transform with two branches
|
|
63
82
|
- **🔴 CRITICAL: Check for existing computations** - If the target entity/relation already has computation code:
|
|
64
83
|
- **NEVER overwrite** existing computation logic
|
|
65
84
|
- **ADD branch logic** to handle the new interaction/scenario within existing Transform callback
|
|
66
85
|
- **PRESERVE all existing branches** to ensure previous test cases continue to pass
|
|
67
86
|
- Example: Add `else if` conditions or extend existing conditions in Transform callback
|
|
68
87
|
- **⚠️ Regression Prevention**: All previous tests must continue passing after adding new computation branches
|
|
88
|
+
- **Note**: The backend file is `./backend/{module}.ts` where {module} is from `.currentmodule`
|
|
89
|
+
- **🔴 CRITICAL: NO DATA MUTATIONS IN COMPUTATIONS** - Computations must NEVER directly use `this.system.storage.create()`, `this.system.storage.update()`, or `this.system.storage.delete()`:
|
|
90
|
+
- ❌ **FORBIDDEN**: `await this.system.storage.create('Entity', {...})` in computation code
|
|
91
|
+
- ❌ **FORBIDDEN**: `await this.system.storage.update('Entity', MatchExp..., {...})` in computation code
|
|
92
|
+
- ❌ **FORBIDDEN**: `await this.system.storage.delete('Entity', MatchExp...)` in computation code
|
|
93
|
+
- ✅ **ALLOWED**: `await this.system.storage.find()` or `await this.system.storage.findOne()` to READ data
|
|
94
|
+
- **WHY**: Computations are reactive and should only compute values based on existing data. Data mutations should happen through Interactions or RecordMutationSideEffect, NOT in computations
|
|
95
|
+
- **IF YOU NEED TO MUTATE DATA**: Use Interactions (for user-triggered actions) or RecordMutationSideEffect (for reactive side effects)
|
|
96
|
+
- **🔴 CRITICAL: NO MOCK DATA OR PLACEHOLDERS** - The reactive framework is complete and powerful. Every computation must be fully implemented:
|
|
97
|
+
- ❌ **FORBIDDEN**: `return 100 // TODO: implement later`
|
|
98
|
+
- ❌ **FORBIDDEN**: `return 0 // Placeholder`
|
|
99
|
+
- ❌ **FORBIDDEN**: `if (record.id === 'test') return mockValue`
|
|
100
|
+
- ✅ **REQUIRED**: Complete implementation with real data queries and calculations
|
|
101
|
+
- **IF DATA IS MISSING**: Add it to the data model (entities/relations/properties)
|
|
102
|
+
- **IF CALCULATION IS COMPLEX**: Break it down into steps, but implement all steps
|
|
103
|
+
- **🔴 CRITICAL: NO SIDE EFFECTS IN COMPUTATIONS** - Side effects (email, AI calls, external APIs) must be in integrations:
|
|
104
|
+
- ❌ **FORBIDDEN**: `await sendEmail(...)` in computation code
|
|
105
|
+
- ❌ **FORBIDDEN**: `await callOpenAI(...)` in computation code
|
|
106
|
+
- ❌ **FORBIDDEN**: `await stripeAPI.charge(...)` in computation code
|
|
107
|
+
- ✅ **CORRECT**: Create separate integration (see `implement-integration-handler`)
|
|
108
|
+
- **COMPUTATION ROLE**: Read data → Calculate → Return value (pure reactive computation)
|
|
109
|
+
- **INTEGRATION ROLE**: Handle all side effects triggered by data changes
|
|
110
|
+
- **🔴 CRITICAL: API Call Entity Property Computations** - If implementing Property computation for API Call entities that depend on Integration Event entities:
|
|
111
|
+
- **Key Matching Pattern**: Find the affected API Call record by matching `externalId` fields
|
|
112
|
+
- **Why**: Both API Call entity and Integration Event entity have `externalId` field that records the external API task/job ID
|
|
113
|
+
- **Pattern**: Use `event.externalId` to query and find the corresponding API Call record with matching `externalId`
|
|
114
|
+
- **Example**: When VolcTTSEvent arrives, find the corresponding VolcTTSCall record where `VolcTTSCall.externalId === event.externalId`
|
|
115
|
+
- **🔴 Special Case - externalId Property**:
|
|
116
|
+
- The `externalId` property is computed from the integration event with `eventType: 'initialized'`
|
|
117
|
+
- The 'initialized' event ALWAYS contains both `entityId` (the API Call's id) and `externalId` (from external system)
|
|
118
|
+
- This event establishes the link between API Call entity and external task/job ID
|
|
119
|
+
- Use `event.entityId` to find the API Call record when processing 'initialized' events
|
|
120
|
+
- Subsequent events (processing/completed/failed) use `externalId` for matching
|
|
121
|
+
- This pattern enables reactive updates to API Call status/result properties based on external system events
|
|
122
|
+
- **🔴 CRITICAL: Entity nodes with ownerProperties** - If the current item has `ownerProperties` field:
|
|
123
|
+
- These are `_owner` type properties that must be set in the entity's creation computation
|
|
124
|
+
- Include ALL ownerProperties in the owner entity computation return value when creating the entity
|
|
125
|
+
- **🔴 CRITICAL: Entity nodes with createdWithRelations** - If the current item has `createdWithRelations` field:
|
|
126
|
+
- These are `created-with-entity` type relations that must be created together with the entity
|
|
127
|
+
- Read `docs/{module}.data-design.json` to find the `sourceProperty` or `targetProperty` name for each relation
|
|
128
|
+
- In the entity's computation, return these property names with corresponding data to create relations
|
|
129
|
+
- Framework will automatically create relation records based on these property values. Example:
|
|
130
|
+
```typescript
|
|
131
|
+
// Entity with createdWithRelations (create relations via property names)
|
|
132
|
+
Order.computation = Transform.create({
|
|
133
|
+
eventDeps: {
|
|
134
|
+
orderInteraction: {
|
|
135
|
+
recordName: InteractionEventEntity.name,
|
|
136
|
+
type: 'create',
|
|
137
|
+
record: {
|
|
138
|
+
interactionName: 'createOrder'
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
callback: async function(event) {
|
|
143
|
+
return {
|
|
144
|
+
orderNumber: event.payload.orderNumber,
|
|
145
|
+
owner: event.user // Creates OrderOwner relations via 'owner' sourceProperty
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
```
|
|
69
150
|
- **🔴 SPECIAL CASE 1: `_parent:[parent]` notation**
|
|
70
151
|
- If the computation name contains `_parent:[parent]` (e.g., `_parent:[User]`), this means:
|
|
71
152
|
- You should modify the PARENT entity's computation, not the current entity
|
|
@@ -89,42 +170,14 @@ color: blue
|
|
|
89
170
|
}
|
|
90
171
|
});
|
|
91
172
|
```
|
|
92
|
-
- **🔴 SPECIAL CASE 2: `_owner` notation**
|
|
93
|
-
- If the computation decision is `_owner`, this means:
|
|
94
|
-
- The property's value is fully controlled by its owner entity/relation's computation
|
|
95
|
-
- You should modify the OWNER entity/relation's creation or derivation logic, not add a separate property computation. Add the property assignment logic in the entity/relation's creation Transform
|
|
96
|
-
- Example: For a `createdAt` property with `_owner`, add timestamp assignment in the entity's Transform that creates it
|
|
97
173
|
- Add computation code using assignment pattern at end of file:
|
|
98
174
|
```typescript
|
|
99
|
-
// At end of backend/
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
175
|
+
// At end of backend/{module}.ts, after exports:
|
|
176
|
+
// Property computation
|
|
177
|
+
const userProperty = User.properties.find(p => p.name === 'postCount')!
|
|
178
|
+
userProperty.computation = Count.create({
|
|
103
179
|
property: 'posts'
|
|
104
180
|
})
|
|
105
|
-
|
|
106
|
-
// For _owner properties, modify the owner entity's computation instead:
|
|
107
|
-
Post.computationTarget = Transform.create({
|
|
108
|
-
items: [
|
|
109
|
-
TransformItem.create({
|
|
110
|
-
from: 'InteractionEventEntity',
|
|
111
|
-
name: 'event',
|
|
112
|
-
transform: async function(this: Controller, event: InteractionEventEntity) {
|
|
113
|
-
if (event.interaction === 'CreatePost') {
|
|
114
|
-
// Create the Post entity with _owner properties
|
|
115
|
-
return {
|
|
116
|
-
title: event.payload.title,
|
|
117
|
-
content: event.payload.content,
|
|
118
|
-
createdAt: Math.floor(Date.now() / 1000), // _owner property set here
|
|
119
|
-
status: 'draft' // _owner property set here
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return null
|
|
123
|
-
}
|
|
124
|
-
})
|
|
125
|
-
]
|
|
126
|
-
})
|
|
127
|
-
```
|
|
128
181
|
- Remove any `defaultValue` if adding computation to that property
|
|
129
182
|
- Never use Transform in Property computation
|
|
130
183
|
- For `_owner` properties, always set them in the owner's creation/derivation logic
|
|
@@ -134,10 +187,13 @@ color: blue
|
|
|
134
187
|
- Fix all type errors before proceeding to tests
|
|
135
188
|
|
|
136
189
|
3. **Create Test Case Plan**
|
|
137
|
-
- Read item details from `docs/computation-implementation-plan.json`
|
|
190
|
+
- Read item details from `docs/{module}.computation-implementation-plan.json`
|
|
138
191
|
- Check `expandedDependencies` to understand all required dependencies
|
|
139
192
|
- Write test plan comment with: dependencies, test steps, business logic notes
|
|
140
|
-
- Cross-reference with `
|
|
193
|
+
- Cross-reference with `docs/{module}.data-design.json`
|
|
194
|
+
- **🔴 For split computation nodes** (ID contains `@InteractionName`): Write a separate test for the specific interaction path this node represents, even though the implementation is a unified Transform. Each node's test verifies its particular trigger path works correctly.
|
|
195
|
+
- **🔴 For entity nodes with `ownerProperties`**: Test entity creation AND verify ALL ownerProperties are correctly set
|
|
196
|
+
- **🔴 For entity nodes with `createdWithRelations`**: Test entity creation AND query each relation to verify it was created with correct source/target
|
|
141
197
|
- **🔴 For `_parent:[parent]` computations**: Test the parent entity's behavior that creates/manages the child entities
|
|
142
198
|
- **🔴 For `_owner` computations**: Test that the property is correctly set when the owner entity/relation is created
|
|
143
199
|
|
|
@@ -152,6 +208,48 @@ color: blue
|
|
|
152
208
|
// Implementation...
|
|
153
209
|
})
|
|
154
210
|
|
|
211
|
+
// For split computation nodes (same entity, different interaction paths):
|
|
212
|
+
test('VolcanoEngineStreamURLCall entity via CreateLivestreamRoom (Node: VolcanoEngineStreamURLCall@CreateLivestreamRoom)', async () => {
|
|
213
|
+
/**
|
|
214
|
+
* Test Plan for: VolcanoEngineStreamURLCall@CreateLivestreamRoom
|
|
215
|
+
* Note: This tests ONE trigger path of the unified Transform computation
|
|
216
|
+
* Steps: 1) Trigger CreateLivestreamRoom 2) Verify VolcanoEngineStreamURLCall created with correct properties
|
|
217
|
+
* Business Logic: API call entity auto-created when room is created
|
|
218
|
+
*/
|
|
219
|
+
// Implementation...
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test('VolcanoEngineStreamURLCall entity via RetryStreamURLGeneration (Node: VolcanoEngineStreamURLCall@RetryStreamURLGeneration)', async () => {
|
|
223
|
+
/**
|
|
224
|
+
* Test Plan for: VolcanoEngineStreamURLCall@RetryStreamURLGeneration
|
|
225
|
+
* Note: This tests ANOTHER trigger path of the SAME unified Transform computation
|
|
226
|
+
* Steps: 1) Trigger RetryStreamURLGeneration 2) Verify new VolcanoEngineStreamURLCall created
|
|
227
|
+
* Business Logic: New API call created when user retries URL generation
|
|
228
|
+
*/
|
|
229
|
+
// Implementation...
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
// For entity with ownerProperties:
|
|
233
|
+
test('Post entity creation with ownerProperties', async () => {
|
|
234
|
+
/**
|
|
235
|
+
* Test Plan for: Post entity (with ownerProperties: [createdAt, status])
|
|
236
|
+
* This tests the entity creation AND all ownerProperties
|
|
237
|
+
* Steps: 1) Create Post via interaction 2) Verify entity exists 3) Verify ALL ownerProperties are set
|
|
238
|
+
* Business Logic: Post creation sets createdAt timestamp and initial status
|
|
239
|
+
*/
|
|
240
|
+
// Implementation...
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
// For entity with createdWithRelations:
|
|
244
|
+
test('Order entity with createdWithRelations', async () => {
|
|
245
|
+
/**
|
|
246
|
+
* Test Plan for: Order entity (with createdWithRelations: [OrderItemRelation])
|
|
247
|
+
* Steps: 1) Create Order 2) Verify Order exists 3) Query each relation to verify creation
|
|
248
|
+
* Note: Check relation source/target are correctly linked
|
|
249
|
+
*/
|
|
250
|
+
// Implementation...
|
|
251
|
+
})
|
|
252
|
+
|
|
155
253
|
// For _parent:[parent] computations:
|
|
156
254
|
test('Post creation through User Transform (_parent:[User])', async () => {
|
|
157
255
|
/**
|
|
@@ -176,7 +274,7 @@ color: blue
|
|
|
176
274
|
```
|
|
177
275
|
|
|
178
276
|
4. **Write Test Implementation**
|
|
179
|
-
- Add test to `tests/
|
|
277
|
+
- Add test to `tests/{module}.business.test.ts` in 'Basic Functionality' describe group
|
|
180
278
|
- Follow the test plan created above
|
|
181
279
|
- **📖 Reference the example test files above for testing patterns and structure**
|
|
182
280
|
- For StateMachine computations, test ALL StateTransfer transitions
|
|
@@ -187,6 +285,15 @@ color: blue
|
|
|
187
285
|
- NEVER hardcode relation names: `storage.find('UserPostRelation', ...)` ❌
|
|
188
286
|
- This ensures tests work regardless of whether relation names are manually specified or auto-generated
|
|
189
287
|
|
|
288
|
+
**⚠️ Testing Integration Event Entity Computations:**
|
|
289
|
+
- When testing computations based on **Integration Event Entities** (NOT InteractionEventEntity):
|
|
290
|
+
- Use `controller.system.storage.create('${EventEntityName}', {...})` to directly create event records
|
|
291
|
+
- Do NOT use `controller.callInteraction()` - integration events are created by external systems
|
|
292
|
+
- Example: For StripePaymentEvent entity, use `controller.system.storage.create(StripePaymentEvent.name, { transactionId: '...', paymentStatus: 'success', ... })`
|
|
293
|
+
- This simulates the webhook/callback from external systems that creates these events
|
|
294
|
+
- When testing computations based on **InteractionEventEntity**:
|
|
295
|
+
- Use `controller.callInteraction()` as normal - these are triggered by user interactions
|
|
296
|
+
|
|
190
297
|
Example patterns:
|
|
191
298
|
```typescript
|
|
192
299
|
test('User.status has correct default value', async () => {
|
|
@@ -205,6 +312,53 @@ color: blue
|
|
|
205
312
|
expect(foundUser.status).toBe('active')
|
|
206
313
|
})
|
|
207
314
|
|
|
315
|
+
test('Post entity with ownerProperties (createdAt, status)', async () => {
|
|
316
|
+
// Create Post - ownerProperties should be set by entity's Transform
|
|
317
|
+
const result = await controller.callInteraction('CreatePost', {
|
|
318
|
+
user: testUser,
|
|
319
|
+
payload: { title: 'Test Post', content: 'Content' }
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
// Verify entity creation AND all ownerProperties
|
|
323
|
+
const post = await system.storage.findOne(
|
|
324
|
+
'Post',
|
|
325
|
+
MatchExp.atom({ key: 'id', value: ['=', result.data.id] }),
|
|
326
|
+
undefined,
|
|
327
|
+
['id', 'title', 'createdAt', 'status'] // Include ALL ownerProperties in attributeQuery
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
expect(post.title).toBe('Test Post')
|
|
331
|
+
expect(post.createdAt).toBeGreaterThan(0) // ownerProperty: createdAt
|
|
332
|
+
expect(post.status).toBe('draft') // ownerProperty: status
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
test('Order entity with createdWithRelations (OrderItemRelation)', async () => {
|
|
336
|
+
// Create Order with items - relations created automatically via sourceProperty
|
|
337
|
+
const result = await controller.callInteraction('CreateOrder', {
|
|
338
|
+
user: testUser,
|
|
339
|
+
payload: {
|
|
340
|
+
orderNumber: 'ORD001',
|
|
341
|
+
items: [{ productId: 'P1', quantity: 2 }, { productId: 'P2', quantity: 1 }]
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
// Verify Order exists
|
|
346
|
+
const order = await system.storage.findOne('Order',
|
|
347
|
+
MatchExp.atom({ key: 'id', value: ['=', result.data.id] }),
|
|
348
|
+
undefined, ['id', 'orderNumber']
|
|
349
|
+
)
|
|
350
|
+
expect(order.orderNumber).toBe('ORD001')
|
|
351
|
+
|
|
352
|
+
// Verify ALL relations in createdWithRelations are created
|
|
353
|
+
const relations = await system.storage.find(OrderItemRelation.name,
|
|
354
|
+
MatchExp.atom({ key: 'source.id', value: ['=', order.id] }),
|
|
355
|
+
undefined,
|
|
356
|
+
['id', ['source', { attributeQuery: ['id'] }], ['target', { attributeQuery: ['id', 'productId'] }]]
|
|
357
|
+
)
|
|
358
|
+
expect(relations.length).toBe(2)
|
|
359
|
+
expect(relations[0].target.productId).toBe('P1')
|
|
360
|
+
})
|
|
361
|
+
|
|
208
362
|
test('Article.state transitions correctly', async () => {
|
|
209
363
|
// Create article in draft state
|
|
210
364
|
const result = await controller.callInteraction('CreateArticle', {
|
|
@@ -256,6 +410,40 @@ color: blue
|
|
|
256
410
|
|
|
257
411
|
expect(relations.length).toBe(1)
|
|
258
412
|
})
|
|
413
|
+
|
|
414
|
+
// Example: Testing Integration Event Entity computations
|
|
415
|
+
test('Order.paymentStatus computed from StripePaymentEvent', async () => {
|
|
416
|
+
/**
|
|
417
|
+
* Test Plan: Testing computation based on Integration Event Entity
|
|
418
|
+
* This tests computations triggered by external system events (StripePaymentEvent)
|
|
419
|
+
* Use storage.create() to simulate webhook data, NOT callInteraction()
|
|
420
|
+
*/
|
|
421
|
+
|
|
422
|
+
// Create an order first (via interaction)
|
|
423
|
+
const orderResult = await controller.callInteraction('CreateOrder', {
|
|
424
|
+
user: testUser,
|
|
425
|
+
payload: { orderNumber: 'ORD001', amount: 100 }
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
// Simulate webhook from payment gateway by directly creating StripePaymentEvent
|
|
429
|
+
// This mimics external system (Stripe/Alipay) sending payment confirmation
|
|
430
|
+
await controller.system.storage.create(StripePaymentEvent.name, {
|
|
431
|
+
transactionId: 'txn_123456',
|
|
432
|
+
paymentStatus: 'success',
|
|
433
|
+
orderId: orderResult.data.id,
|
|
434
|
+
timestamp: Math.floor(Date.now() / 1000)
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
// Verify the computation triggered by StripePaymentEvent updated Order
|
|
438
|
+
const order = await system.storage.findOne(
|
|
439
|
+
'Order',
|
|
440
|
+
MatchExp.atom({ key: 'id', value: ['=', orderResult.data.id] }),
|
|
441
|
+
undefined,
|
|
442
|
+
['id', 'paymentStatus']
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
expect(order.paymentStatus).toBe('paid')
|
|
446
|
+
})
|
|
259
447
|
```
|
|
260
448
|
|
|
261
449
|
5. **Type Check Test Code**
|
|
@@ -264,29 +452,30 @@ color: blue
|
|
|
264
452
|
- Do NOT run actual tests until type checking passes
|
|
265
453
|
|
|
266
454
|
6. **Run Test**
|
|
267
|
-
- Run full test suite: `npm run test tests/
|
|
455
|
+
- Run full test suite: `npm run test tests/{module}.business.test.ts`
|
|
268
456
|
- Must fix any failures (new tests or regressions) before proceeding
|
|
269
457
|
|
|
270
458
|
**If test fails:**
|
|
271
459
|
- Review test plan - are dependencies properly set up?
|
|
272
|
-
- Verify against `
|
|
460
|
+
- Verify against `docs/{module}.data-design.json`
|
|
273
461
|
- Check if test data matches `expandedDependencies`
|
|
274
462
|
- Common issues: missing dependencies, wrong operation order, incorrect expectations
|
|
275
463
|
|
|
276
464
|
**Error handling:**
|
|
277
465
|
- After 10 fix attempts, STOP IMMEDIATELY and wait for user guidance
|
|
278
|
-
- Create error document in `docs/errors
|
|
279
|
-
- Update `lastError` field in computation-implementation-plan.json with error doc path
|
|
466
|
+
- Create error document in `docs/errors/{module}.{error-name}.md` with test plan, code, error, and attempts
|
|
467
|
+
- Update `lastError` field in `docs/{module}.computation-implementation-plan.json` with error doc path
|
|
280
468
|
- Never skip tests or fake data to pass
|
|
281
469
|
|
|
282
470
|
7. **Document Progress**
|
|
283
|
-
- **🔴 CRITICAL: Update `docs/computation-implementation-plan.json` based on test results:**
|
|
284
|
-
- **If ALL tests pass** (`npm run test tests/
|
|
285
|
-
- Set `"completed": true`
|
|
471
|
+
- **🔴 CRITICAL: Update `docs/{module}.computation-implementation-plan.json` based on test results:**
|
|
472
|
+
- **If ALL tests pass** (`npm run test tests/{module}.business.test.ts` shows ALL tests passing):
|
|
473
|
+
- Set `"completed": true` for the CURRENT node being worked on
|
|
286
474
|
- Remove `lastError` field if it exists
|
|
475
|
+
- **For split computation nodes**: Mark completed only after the specific interaction path is implemented and tested. The unified Transform implementation happens progressively across nodes, but each node tracks completion of its trigger path.
|
|
287
476
|
- **If ANY test fails** (including regression tests):
|
|
288
477
|
- Keep `"completed": false` - the computation is NOT done
|
|
289
|
-
- Add/update `lastError` field with path to error document in `docs/errors
|
|
478
|
+
- Add/update `lastError` field with path to error document in `docs/errors/{module}.{error-name}.md`
|
|
290
479
|
- The computation remains incomplete and needs fixing
|
|
291
480
|
|
|
292
481
|
8. **Commit Changes (only if tests pass)**
|