interaqt 0.8.3 → 0.8.4
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/code-generation-handler.md +31 -17
- package/agent/.claude/agents/error-check-handler.md +17 -0
- package/agent/.claude/agents/frontend-generation-handler.md +34 -1
- package/agent/.claude/agents/implement-integration-handler.md +21 -0
- package/agent/.claude/agents/requirements-analysis-handler.md +16 -0
- package/package.json +1 -1
- package/agent/agentspace/prompt/integration_sub_agent_refactor.md +0 -19
- package/agent/agentspace/prompt/requirement_analysis_refactor.md +0 -34
|
@@ -162,27 +162,41 @@ git commit -m "feat: Task 3.1.2 - Complete entity and relation implementation"
|
|
|
162
162
|
- [ ] Ensure all payloads match the documented fields
|
|
163
163
|
- [ ] **🔴 CRITICAL: For query interactions (action: GetAction):**
|
|
164
164
|
- **MUST declare `data` field** - specify the Entity or Relation to query
|
|
165
|
-
- **SHOULD declare `
|
|
166
|
-
-
|
|
165
|
+
- **SHOULD declare `dataPolicy` field** if there are predefined filters/fields or access restrictions
|
|
166
|
+
- **⚠️ IMPORTANT: Data Access Scope vs Business Rules**
|
|
167
|
+
- If `dataConstraints` express **data access scope restrictions** (e.g., "can only view own entities", "can only view specific fields"), use `dataPolicy` NOT `condition`
|
|
168
|
+
- `dataPolicy` controls what data can be accessed AFTER the operation is permitted
|
|
169
|
+
- `condition` controls WHETHER the operation can execute (permissions/business rules)
|
|
170
|
+
- Example of data policy: Restricting visible fields, filtering by ownership
|
|
171
|
+
- Example with dynamic data policy (user-based filtering):
|
|
172
|
+
```typescript
|
|
173
|
+
const ViewMyOrders = Interaction.create({
|
|
174
|
+
name: 'ViewMyOrders',
|
|
175
|
+
action: GetAction,
|
|
176
|
+
data: Order,
|
|
177
|
+
dataPolicy: DataPolicy.create({
|
|
178
|
+
match: function(this: Controller, event: any) {
|
|
179
|
+
// Only show user's own orders
|
|
180
|
+
return MatchExp.atom({key: 'owner.id', value:['=', event.user.id]})
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
- Example with combined data policy (filtering + field restrictions + pagination):
|
|
167
186
|
```typescript
|
|
168
187
|
const ViewMyFollowers = Interaction.create({
|
|
169
|
-
name: '
|
|
188
|
+
name: 'ViewMyFollowers',
|
|
170
189
|
action: GetAction,
|
|
171
190
|
data: User, // REQUIRED: specify what to query
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
value: function(this:Controller, event:any) {
|
|
182
|
-
return MatchExp.atom({key: 'follow.id', value:['=', event.user.id]})
|
|
183
|
-
}
|
|
184
|
-
})
|
|
185
|
-
]
|
|
191
|
+
dataPolicy: DataPolicy.create({
|
|
192
|
+
// Dynamic filter: only users who follow the current user
|
|
193
|
+
match: function(this: Controller, event: any) {
|
|
194
|
+
return MatchExp.atom({key: 'following.id', value:['=', event.user.id]})
|
|
195
|
+
},
|
|
196
|
+
// Field restrictions: only expose specific fields
|
|
197
|
+
attributeQuery: ['id', 'name', 'email'],
|
|
198
|
+
// Default pagination
|
|
199
|
+
modifier: { limit: 20, orderBy: { name: 'asc' } }
|
|
186
200
|
})
|
|
187
201
|
})
|
|
188
202
|
```
|
|
@@ -1015,6 +1015,23 @@ const GetUserDonations = Interaction.create({
|
|
|
1015
1015
|
]
|
|
1016
1016
|
})
|
|
1017
1017
|
})
|
|
1018
|
+
|
|
1019
|
+
// Query interaction with dataPolicy for access control
|
|
1020
|
+
const GetMyDonations = Interaction.create({
|
|
1021
|
+
name: 'GetMyDonations',
|
|
1022
|
+
action: GetAction,
|
|
1023
|
+
data: Donation, // ✅ Entity reference
|
|
1024
|
+
dataPolicy: DataPolicy.create({
|
|
1025
|
+
// ✅ Dynamic filter: users can only see their own donations
|
|
1026
|
+
match: function(this: Controller, event: any) {
|
|
1027
|
+
return MatchExp.atom({key: 'donor.id', value: ['=', event.user.id]})
|
|
1028
|
+
},
|
|
1029
|
+
// ✅ Field restrictions: limit exposed fields
|
|
1030
|
+
attributeQuery: ['id', 'amount', 'createdAt', 'status'],
|
|
1031
|
+
// ✅ Default pagination
|
|
1032
|
+
modifier: { limit: 20, orderBy: { createdAt: 'desc' } }
|
|
1033
|
+
})
|
|
1034
|
+
})
|
|
1018
1035
|
```
|
|
1019
1036
|
|
|
1020
1037
|
### Pattern 5: Test Error Checking
|
|
@@ -231,7 +231,40 @@ npm run generate-frontend-api
|
|
|
231
231
|
}
|
|
232
232
|
```
|
|
233
233
|
|
|
234
|
-
2. **
|
|
234
|
+
2. **Query Interactions Always Return Arrays:**
|
|
235
|
+
- Backend query-type interactions always return arrays in `response.data`
|
|
236
|
+
- To query a specific entity/relation, use the `match` field in the query options (2nd parameter)
|
|
237
|
+
- Do NOT put match criteria in the payload (1st parameter)
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// ✅ Correct: Use match in query options
|
|
241
|
+
const response = await apiClient.ViewVideoGenerationStatus(
|
|
242
|
+
{ videoGenerationRequestId: videoId }, // payload
|
|
243
|
+
{
|
|
244
|
+
attributeQuery: ['id', 'status', 'videoUrl'],
|
|
245
|
+
match: {
|
|
246
|
+
key: 'id',
|
|
247
|
+
value: ['=', videoId] // Match condition here
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
const item = response.data[0]; // Extract first item from array
|
|
252
|
+
|
|
253
|
+
// ❌ Wrong: Don't rely on payload for filtering
|
|
254
|
+
const response = await apiClient.ViewVideoGenerationStatus(
|
|
255
|
+
{ videoGenerationRequestId: videoId } // This won't filter results
|
|
256
|
+
);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
3. **Handling Asynchronous External System Tasks:**
|
|
260
|
+
- For asynchronous tasks that call external systems, backend typically does NOT implement polling unless explicitly specified in requirements
|
|
261
|
+
- Backend usually provides a separate API endpoint to trigger status updates
|
|
262
|
+
- Frontend can call this API to trigger backend status updates
|
|
263
|
+
- Frontend implementation options:
|
|
264
|
+
- **Manual trigger**: Add a button in the component for users to manually trigger the status update API
|
|
265
|
+
- **Automatic polling**: Implement polling in the component (on mount) until the task reaches a completion state
|
|
266
|
+
|
|
267
|
+
4. **Reference Existing Components for Patterns:**
|
|
235
268
|
- Look at `frontend/src/components/*.tsx` files
|
|
236
269
|
- Follow the same patterns for API client usage
|
|
237
270
|
- Check how error handling is implemented
|
|
@@ -45,6 +45,27 @@ This applies to BOTH async APIs (with task IDs) and sync APIs (immediate results
|
|
|
45
45
|
- All integration documentation files MUST be prefixed with current module name from `.currentmodule`
|
|
46
46
|
- Format: `docs/{module}.{integration-name}.integration-design.md`
|
|
47
47
|
|
|
48
|
+
**🔴 CRITICAL PRINCIPLE: Status Polling Strategy**
|
|
49
|
+
|
|
50
|
+
**Default Approach: Frontend Polling with Manual Query API**
|
|
51
|
+
|
|
52
|
+
Backend polling consumes significant server resources. Follow this priority order:
|
|
53
|
+
|
|
54
|
+
1. **Default (ALWAYS implement)**: Provide manual query API for frontend
|
|
55
|
+
- Create API endpoint to query external status
|
|
56
|
+
- Frontend can poll this API at its own pace
|
|
57
|
+
- Even if polling is needed, frontend handles it unless explicitly stated otherwise
|
|
58
|
+
|
|
59
|
+
2. **Backend Polling (ONLY if explicitly required)**: Implement server-side polling
|
|
60
|
+
- ONLY implement if user explicitly requests "backend polling" in requirements
|
|
61
|
+
- Use with caution due to resource consumption
|
|
62
|
+
- Example: volcjmeng integration (only because explicitly required)
|
|
63
|
+
|
|
64
|
+
3. **Webhook (ONLY if both conditions met)**: Implement webhook endpoint
|
|
65
|
+
- ONLY if external service supports webhook registration
|
|
66
|
+
- AND user can register webhook themselves
|
|
67
|
+
- Requires exposing public endpoint for external callbacks
|
|
68
|
+
|
|
48
69
|
# Core Concepts
|
|
49
70
|
|
|
50
71
|
## Interaqt Framework
|
|
@@ -1280,6 +1280,22 @@ git commit -m "feat: Task 1.4 - Complete data concept extraction"
|
|
|
1280
1280
|
- Interaction IDs must be semantic names (e.g., "BorrowBook", "ViewAvailableBooks") not codes (e.g., "I001")
|
|
1281
1281
|
- ❌ If a requirement has role="System", it was incorrectly created - SKIP it, do NOT create interaction
|
|
1282
1282
|
|
|
1283
|
+
**⚠️ IMPORTANT: Distinguishing Data Access Constraints**
|
|
1284
|
+
|
|
1285
|
+
For read-type interaction requirements with access restrictions, distinguish between:
|
|
1286
|
+
|
|
1287
|
+
1. **Business Rules** - Constraints on whether the read operation can execute
|
|
1288
|
+
- Example: "Only administrators can view XXX entity"
|
|
1289
|
+
- Controls who can perform the action
|
|
1290
|
+
- Should be specified in the `conditions` field
|
|
1291
|
+
|
|
1292
|
+
2. **Data Policy** - Constraints on the scope of data returned
|
|
1293
|
+
- Example: "Can only view own XXX entities" or "Can only view YYY fields of XXX entity"
|
|
1294
|
+
- Controls what data is accessible after the operation is permitted
|
|
1295
|
+
- Should be specified in the `dataConstraints` field
|
|
1296
|
+
|
|
1297
|
+
These must be separated as they are implemented differently in subsequent phases.
|
|
1298
|
+
|
|
1283
1299
|
**⚠️ IMPORTANT: External Integration Interactions**
|
|
1284
1300
|
|
|
1285
1301
|
If Task 1.4 includes API Call entities, design error handling interactions:
|
package/package.json
CHANGED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
我们的项目所使用的框架是一个叫做 Interaqt 的后端响应式数据框架,它会自动根据应用中的数据变化及响应式数据的定义执行相应的数据变化。它只负责处理一般的业务逻辑中表达的数据逻辑,例如一般业务逻辑中会用到 平均/综合 等计算,还有常见的基于状态机等业务逻辑表达等。对于一些非一般的能力需求,例如 大模型生图、大模型生视频、tts、发送邮件、发送信息、完整支付系统等。它需要借助外部系统/api 来完成。
|
|
2
|
-
我们设计了一个叫做 integration 的概念,专门用来对接当前系统和外部的api/系统。它通过 interaqt 框架提供的数据观察机制,来观察数据变化,根据数据变化来决定如何调用外部的 api。同时通过 webhook 等机制来接受外部的事件,将外部事件同步回系统中,触发响应式的业务逻辑。
|
|
3
|
-
|
|
4
|
-
我们将 integration 需要集成的功能分成了三种类别:
|
|
5
|
-
1. 调用外部的 api,为了获得一个具体的返回。例如 tts,大模型生图等。
|
|
6
|
-
2. 执行某种副作用,例如发送邮件、发送 im 消息等。
|
|
7
|
-
3. 对接其他有状态的系统,例如支付系统等。
|
|
8
|
-
|
|
9
|
-
现在我们需要指导 claude code 的 sub agent 合理地识别需要的外部服务以及如何自己实现 integration。
|
|
10
|
-
1. 指导 `.claude/agents/requirements-analysis-handler.md` 在需求分析阶段,正确分析出 integration 的类型。并在相应的输出的文档中,设计一个字段来表达 integration 的类型。
|
|
11
|
-
2. 指导 `.claude/agents/implement-design-handler.md` 在设计数据的时候,根据如下原则进行设计:
|
|
12
|
-
2.1. 不管是哪种类型,都会涉及到对外部 api 的调用,例如执行副作用,也会有副作用 api 的调用。所以我们应该对每一个 api 的调用都设计一个 `{xxx}APICall` 的 entity,它负责记录这次 api 调用的参数、状态、返回值、调用时间等。
|
|
13
|
-
2.2. 同时设计一个相应的 integraion event entity,当我们通过 webhook 或者自己通过接口查询到 api 调用状态的变化时,在系统内创建相应的 api call result event 事件。并且将上一步创建的 `{xxx}APICall` entity 的 status 和 data 字段写成基于 integration event entity 的 computation,这样就完整符合了框架的响应式范式。也记录了所有应该记录的数据,增强了系统的健壮性。
|
|
14
|
-
2.3. 如果当前场景是希望基于这个 integration 获得具体的返回值,那么意味着我们系统内的业务数据对这个 `{xxx}APICall` 的 entity 是有依赖的,应该写成基于 `{xxx}APICall` 的 computation。例如我们的有一个 `Greeting` entity,其中有个 `voiceUrl` property 是需要利用外部 tts 能力将文本转化为语音。那么 `Greeting.voiceUrl` 就应该表达为基于 `{ttsAPICall}` entity 的 computation。如果是纯副作用类型等的调用,就不需要了。注意,这种情况下,还需要建立相应的 entity 和 api call entity 之间的关系,才能查找到正确的数据。
|
|
15
|
-
2.4. `.claude/agents/implement-design-handler.md` 在做 data-design 的时候,应该明确表达出来:1. 设计的哪些实体是 api call 类型的 entity,哪些实体是 api call result event 实体。2. 系统内的业务数据如果需要 api 的返回结果,那么应该依赖正确的 api call entity。
|
|
16
|
-
3. 指导 `.claude/agents/code-generation-handler.md` 在实现阶段,在写测试用例时,完全可以通过创建正确的 api call result event 来模拟 api 的调用,完整验证系统的内部逻辑的正确性。不需要等到 integration 的真实实现。
|
|
17
|
-
4. 指导 `.claude/agents/error-check-handler.md` 在合适的阶段对 integration 相关的设计做错误检查。
|
|
18
|
-
|
|
19
|
-
你充分理解上面的所有思路,并且修改相应的 sub agent 文件来达成目标。
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
在需求分析的过程中,我们已经在 `.claude/agents/requirements-analysis-handle.md` 中提出了一种以最终"读需求"为起点,反向衍生出 创建/修改/删除 需求的方法。
|
|
2
|
-
在实际的探索中发现:用户可能会在输入阶段就按照自己的真实需要描述了一部分流程了,这个流程里面包含了
|
|
3
|
-
|
|
4
|
-
- 进行增删改查交互的步骤,通常是有依赖顺序的
|
|
5
|
-
- 进行交互的角色
|
|
6
|
-
- 交互对数据产生的具体增删改变化
|
|
7
|
-
例子:在 `requirements/requirements.md` 中,用户直接在描述中叙述如何创建版本 rollback 时数据应该如何变化。这里面就已经包含了:"创建版本-回退版本"的具体流程。
|
|
8
|
-
|
|
9
|
-
这个流程本身也是用户的一种需求,我们需要严格遵照他的指示实现。
|
|
10
|
-
现在,你来将 `.claude/agents/requirements-analysis-handle.md` 完全重写。要求:
|
|
11
|
-
1. 先将 `.claude/agents/requirements-analysis-handle.md` 关于目标补充、数据分析、交互动作定义的步骤、设计的数据结构完全提取出来,这些部分已经很稳定,可以留作复用。
|
|
12
|
-
|
|
13
|
-
用下面的这些步骤作为重写的分析步骤:
|
|
14
|
-
1. 对用户的输入进行分析,识别出其中的:
|
|
15
|
-
- 目标。通常是和具体软件功能无关,和现实中真实目标相关的描述。例如“管理图书”,“管理员工”,“和好友实时交流”等。
|
|
16
|
-
- 流程。例如 "发出好友申请-收到申请后同意/收到申请后拒绝"。
|
|
17
|
-
- 数据概念。(使用原文档里数据概念)。
|
|
18
|
-
2. 进行常见的目标补充(使用原文档里的方法)。
|
|
19
|
-
3. 进行完整的数据概念设计(复用原文档中的方法)。
|
|
20
|
-
4. 进行流程和交互动作设计。
|
|
21
|
-
3.1. 优先整合用户描述中的流程。并且看流程达到了哪些目标。
|
|
22
|
-
3.2. 为没有达到的目标设计流程。
|
|
23
|
-
3.2.1. 流程是一系列有顺序的交互的总和,其中可以包含可能的分支或者循环。交互的具体定义仍然采用原文档中的定义。
|
|
24
|
-
3.2.2. 流程的最后仍然应该是一个"读需求"作为结尾,来真正满足目标。但前面需要补充常见的创建等逻辑。
|
|
25
|
-
3.3. 注意,流程中的每一步应该都是一个交互动作,交互动作要使用原文档中 interactions-design.json 中的数据结构表示。要包含完整 `data.creates`/`data.updates`/`data.deletes`。从用户的输入中直接提取的出来的流程也要完善这些信息。
|
|
26
|
-
5. 设计完流程后。从数据的角度来补充用户可能需要的其他增删改查需求:
|
|
27
|
-
4.1. 为每一个修改和删除的交互考虑用户作为人类,是否需要经过查看/搜索再进行决策的需求。
|
|
28
|
-
4.2. 为每一个创建的交互动作考虑,数据在现实中是否允许修改/删除。如果允许就应该增加相应的需求。
|
|
29
|
-
6. 最终产出的 interactions-design.json 仍然使用原文档里的 interaction 数据结构,但以流程为组来组织。
|
|
30
|
-
|
|
31
|
-
注意,在每一个步骤中,都要给出清晰的数据结构定义,用 json 来写。
|
|
32
|
-
整体用简洁的英语完成文档重写。注意原本文档中的在关键步骤 update docs/{module}.status.json 仍然按照原文档的方式写。
|
|
33
|
-
|
|
34
|
-
|