@kunlv/atdc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +58 -0
- package/GO.md +245 -0
- package/bin/add-command.py +144 -0
- package/bin/atdc.js +460 -0
- package/bin/cls.js +98 -0
- package/central/link.sh +60 -0
- package/central/ticket_center_server/.handoffs/TASK-238389_QYinVipLimit.md +128 -0
- package/central/ticket_center_server/.test_plans/TASK-227254-SupportMembership-self-test-plan.md +566 -0
- package/central/ticket_center_server/.test_plans/task-238389-qyin-vip-limit-self-test-plan.md +210 -0
- package/commands/claude/atd.md +3 -0
- package/commands/claude/atdc/1-spec.md +5 -0
- package/commands/claude/atdc/1-update.md +5 -0
- package/commands/claude/atdc/2-qa.md +3 -0
- package/commands/claude/atdc/3-impl-skip-spec.md +5 -0
- package/commands/claude/atdc/3-impl.md +3 -0
- package/commands/claude/atdc/4-verify.md +3 -0
- package/commands/claude/atdc/5-done.md +3 -0
- package/commands/claude/atdc/add-command.md +4 -0
- package/commands/claude/atdc/add-to-hc.md +3 -0
- package/commands/claude/atdc/analysis-cls-log.md +4 -0
- package/commands/claude/atdc/article.md +6 -0
- package/commands/claude/atdc/bugfix.md +4 -0
- package/commands/claude/atdc/code-simplifier.md +8 -0
- package/commands/claude/atdc/commit.md +4 -0
- package/commands/claude/atdc/compound.md +5 -0
- package/commands/claude/atdc/context.md +3 -0
- package/commands/claude/atdc/diagnose.md +8 -0
- package/commands/claude/atdc/git-guardrails.md +18 -0
- package/commands/claude/atdc/grill-with-docs.md +8 -0
- package/commands/claude/atdc/handoff.md +8 -0
- package/commands/claude/atdc/health.md +3 -0
- package/commands/claude/atdc/learn.md +4 -0
- package/commands/claude/atdc/review.md +10 -0
- package/commands/claude/atdc/search.md +4 -0
- package/commands/claude/atdc/strategy.md +3 -0
- package/commands/claude/atdc/table2xlsx.md +4 -0
- package/commands/claude/atdc/test-plan.md +5 -0
- package/commands/claude/atdc/travel-planner.md +4 -0
- package/commands/claude/atdc/xlsx2sql-insert.md +4 -0
- package/commands/claude/atdc/xlsx2sql-update.md +4 -0
- package/commands/claude/atdc/zoom-out.md +8 -0
- package/commands/codex/atdc/SKILL.md +8 -0
- package/commands/codex/atdc-1-spec/SKILL.md +8 -0
- package/commands/codex/atdc-1-update/SKILL.md +8 -0
- package/commands/codex/atdc-2-qa/SKILL.md +8 -0
- package/commands/codex/atdc-3-impl/SKILL.md +8 -0
- package/commands/codex/atdc-3-impl-skip-spec/SKILL.md +8 -0
- package/commands/codex/atdc-4-verify/SKILL.md +8 -0
- package/commands/codex/atdc-5-done/SKILL.md +8 -0
- package/commands/codex/atdc-add-command/SKILL.md +7 -0
- package/commands/codex/atdc-add-to-hc/SKILL.md +8 -0
- package/commands/codex/atdc-analysis-cls-log/SKILL.md +5 -0
- package/commands/codex/atdc-article/SKILL.md +8 -0
- package/commands/codex/atdc-bugfix/SKILL.md +5 -0
- package/commands/codex/atdc-code-simplifier/SKILL.md +7 -0
- package/commands/codex/atdc-commit/SKILL.md +7 -0
- package/commands/codex/atdc-compound/SKILL.md +8 -0
- package/commands/codex/atdc-context/SKILL.md +8 -0
- package/commands/codex/atdc-diagnose/SKILL.md +9 -0
- package/commands/codex/atdc-git-guardrails/SKILL.md +19 -0
- package/commands/codex/atdc-grill-with-docs/SKILL.md +9 -0
- package/commands/codex/atdc-handoff/SKILL.md +9 -0
- package/commands/codex/atdc-health/SKILL.md +8 -0
- package/commands/codex/atdc-learn/SKILL.md +7 -0
- package/commands/codex/atdc-review/SKILL.md +49 -0
- package/commands/codex/atdc-search/SKILL.md +5 -0
- package/commands/codex/atdc-strategy/SKILL.md +8 -0
- package/commands/codex/atdc-table2xlsx/SKILL.md +8 -0
- package/commands/codex/atdc-test-plan/SKILL.md +13 -0
- package/commands/codex/atdc-travel-planner/SKILL.md +7 -0
- package/commands/codex/atdc-xlsx2sql-insert/SKILL.md +8 -0
- package/commands/codex/atdc-xlsx2sql-update/SKILL.md +8 -0
- package/commands/codex/atdc-zoom-out/SKILL.md +9 -0
- package/commands/cursor/atdc/SKILL.md +8 -0
- package/commands/cursor/atdc-1-spec/SKILL.md +8 -0
- package/commands/cursor/atdc-1-update/SKILL.md +8 -0
- package/commands/cursor/atdc-2-qa/SKILL.md +8 -0
- package/commands/cursor/atdc-3-impl/SKILL.md +8 -0
- package/commands/cursor/atdc-3-impl-skip-spec/SKILL.md +8 -0
- package/commands/cursor/atdc-4-verify/SKILL.md +8 -0
- package/commands/cursor/atdc-5-done/SKILL.md +8 -0
- package/commands/cursor/atdc-add-command/SKILL.md +7 -0
- package/commands/cursor/atdc-add-to-hc/SKILL.md +8 -0
- package/commands/cursor/atdc-analysis-cls-log/SKILL.md +5 -0
- package/commands/cursor/atdc-article/SKILL.md +8 -0
- package/commands/cursor/atdc-bugfix/SKILL.md +5 -0
- package/commands/cursor/atdc-code-simplifier/SKILL.md +7 -0
- package/commands/cursor/atdc-commit/SKILL.md +7 -0
- package/commands/cursor/atdc-compound/SKILL.md +8 -0
- package/commands/cursor/atdc-context/SKILL.md +8 -0
- package/commands/cursor/atdc-diagnose/SKILL.md +8 -0
- package/commands/cursor/atdc-git-guardrails/SKILL.md +18 -0
- package/commands/cursor/atdc-grill-with-docs/SKILL.md +8 -0
- package/commands/cursor/atdc-handoff/SKILL.md +8 -0
- package/commands/cursor/atdc-health/SKILL.md +8 -0
- package/commands/cursor/atdc-learn/SKILL.md +7 -0
- package/commands/cursor/atdc-review/SKILL.md +49 -0
- package/commands/cursor/atdc-search/SKILL.md +5 -0
- package/commands/cursor/atdc-strategy/SKILL.md +8 -0
- package/commands/cursor/atdc-table2xlsx/SKILL.md +8 -0
- package/commands/cursor/atdc-test-plan/SKILL.md +7 -0
- package/commands/cursor/atdc-travel-planner/SKILL.md +7 -0
- package/commands/cursor/atdc-xlsx2sql-insert/SKILL.md +8 -0
- package/commands/cursor/atdc-xlsx2sql-update/SKILL.md +8 -0
- package/commands/cursor/atdc-zoom-out/SKILL.md +8 -0
- package/commands/opencode/atd.md +6 -0
- package/commands/opencode/atdc/1-spec.md +8 -0
- package/commands/opencode/atdc/1-update.md +8 -0
- package/commands/opencode/atdc/2-qa.md +6 -0
- package/commands/opencode/atdc/3-impl-skip-spec.md +8 -0
- package/commands/opencode/atdc/3-impl.md +6 -0
- package/commands/opencode/atdc/4-verify.md +6 -0
- package/commands/opencode/atdc/5-done.md +6 -0
- package/commands/opencode/atdc/add-command.md +6 -0
- package/commands/opencode/atdc/add-to-hc.md +6 -0
- package/commands/opencode/atdc/analysis-cls-log.md +6 -0
- package/commands/opencode/atdc/article.md +6 -0
- package/commands/opencode/atdc/bugfix.md +6 -0
- package/commands/opencode/atdc/code-simplifier.md +8 -0
- package/commands/opencode/atdc/commit.md +6 -0
- package/commands/opencode/atdc/compound.md +8 -0
- package/commands/opencode/atdc/context.md +6 -0
- package/commands/opencode/atdc/diagnose.md +8 -0
- package/commands/opencode/atdc/git-guardrails.md +18 -0
- package/commands/opencode/atdc/grill-with-docs.md +8 -0
- package/commands/opencode/atdc/handoff.md +8 -0
- package/commands/opencode/atdc/health.md +6 -0
- package/commands/opencode/atdc/learn.md +6 -0
- package/commands/opencode/atdc/review.md +7 -0
- package/commands/opencode/atdc/search.md +6 -0
- package/commands/opencode/atdc/strategy.md +6 -0
- package/commands/opencode/atdc/table2xlsx.md +6 -0
- package/commands/opencode/atdc/test-plan.md +6 -0
- package/commands/opencode/atdc/travel-planner.md +6 -0
- package/commands/opencode/atdc/xlsx2sql-insert.md +6 -0
- package/commands/opencode/atdc/xlsx2sql-update.md +6 -0
- package/commands/opencode/atdc/zoom-out.md +8 -0
- package/config/cls-defaults.json +19 -0
- package/hooks/atd-archive.sh +86 -0
- package/hooks/atd-state.sh +165 -0
- package/hooks/evidence-hook.sh +88 -0
- package/hooks/evidence-log.sh +54 -0
- package/hooks/guardrails.sh +65 -0
- package/hooks/handoff-compress.sh +104 -0
- package/hooks/phase-guard.sh +134 -0
- package/hooks/phase-write-guard.sh +96 -0
- package/hooks/ponytail-fix.sh +179 -0
- package/hooks/ponytail-guard.sh +66 -0
- package/hooks/ponytail-scan.sh +62 -0
- package/install.sh +276 -0
- package/package.json +20 -0
- package/packages/cls/bin/cls.js +146 -0
- package/packages/cls/config/defaults.json +17 -0
- package/packages/cls/mcp/server.js +142 -0
- package/packages/cls/package.json +29 -0
- package/packages/cls/scripts/postinstall.js +19 -0
- package/packages/cls/src/client.js +262 -0
- package/prompts/1-spec.md +271 -0
- package/prompts/1-update.md +198 -0
- package/prompts/2-qa.md +164 -0
- package/prompts/3-impl-bugfix.md +271 -0
- package/prompts/3-impl.md +419 -0
- package/prompts/4-verify.md +160 -0
- package/prompts/5-done.md +227 -0
- package/prompts/6-article.md +571 -0
- package/prompts/X-add-command.md +60 -0
- package/prompts/X-add-to-hc.md +178 -0
- package/prompts/X-analysis-cls-log.md +68 -0
- package/prompts/X-bugfix.md +10 -0
- package/prompts/X-code-simplifier.md +38 -0
- package/prompts/X-commit.md +116 -0
- package/prompts/X-compound.md +188 -0
- package/prompts/X-context-scan.md +101 -0
- package/prompts/X-diagnose.md +116 -0
- package/prompts/X-grill-with-docs.md +120 -0
- package/prompts/X-handoff.md +80 -0
- package/prompts/X-health.md +78 -0
- package/prompts/X-learn.md +151 -0
- package/prompts/X-review.md +417 -0
- package/prompts/X-search.md +74 -0
- package/prompts/X-strategy.md +95 -0
- package/prompts/X-table2xlsx.md +46 -0
- package/prompts/X-test-plan.md +436 -0
- package/prompts/X-travel-planner.md +22 -0
- package/prompts/X-xlsx2sql-insert.md +34 -0
- package/prompts/X-xlsx2sql-update.md +40 -0
- package/prompts/X-zoom-out.md +119 -0
- package/reference/diagnose-loop.md +66 -0
- package/reference/gates.md +90 -0
- package/reference/git-guardrails.md +71 -0
- package/reference/guards.md +275 -0
- package/reference/learn.md +100 -0
- package/reference/memory.md +190 -0
- package/reference/risk.md +74 -0
- package/reference/tdd.md +40 -0
- package/scripts/svg2png.js +46 -0
- package/src/cls/client.js +244 -0
- package/src/constants.js +49 -0
- package/src/evidence/ai-draft-metrics.js +157 -0
- package/src/evidence/canvas.js +184 -0
- package/src/evidence/index.js +98 -0
- package/src/evidence/sink-events.js +165 -0
- package/src/evidence/stage-timing.js +112 -0
- package/src/installers/claude.js +79 -0
- package/src/installers/codex.js +62 -0
- package/src/installers/opencode.js +72 -0
- package/src/manifest/ponytail-patterns.json +185 -0
- package/src/mcp/cls-mcp-server.js +142 -0
- package/src/utils.js +79 -0
- package/src/validators/state-validate.js +123 -0
- package/templates/.phase-state.json +13 -0
- package/templates/adr.md +62 -0
- package/templates/changelog.md +21 -0
- package/templates/concepts.md +35 -0
- package/templates/design.md +47 -0
- package/templates/handoff.md +64 -0
- package/templates/learn-skill.md +43 -0
- package/templates/progress.json +17 -0
- package/templates/proposal.md +29 -0
- package/templates/solution.md +36 -0
- package/templates/strategy.md +39 -0
- package/templates/tasks.md +41 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# prompts/X-test-plan · 自测计划生成
|
|
2
|
+
|
|
3
|
+
从此文件读取后直接开始执行,无需用户再次确认。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Step 0:检测目标 change
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
ls openspec/changes/ | grep -v archive
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- 无活跃 change → 提示"当前无活跃 change。请先使用 `/atdc:1-spec` 创建需求或直接说'生成自测计划 {分支名/功能名}'",停止。
|
|
14
|
+
- 有多个活跃 change → 让用户指定目标 change 名,或提示输入分支名/功能名。
|
|
15
|
+
- 有且只有一个 → 自动选中。
|
|
16
|
+
|
|
17
|
+
确认目标上下文:分支名/功能名/change 名。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Step 1:读取变更上下文
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
CHANGE_NAME="<目标>"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
读取以下文件(如存在):
|
|
28
|
+
- `openspec/changes/$CHANGE_NAME/tasks.md` — 理解有哪些任务和涉及文件
|
|
29
|
+
- `openspec/changes/$CHANGE_NAME/proposal.md` — 理解功能背景
|
|
30
|
+
- `openspec/changes/$CHANGE_NAME/design.md` — 理解技术方案和接口定义
|
|
31
|
+
|
|
32
|
+
如果 change 目录不存在(直接指定分支/功能名),则:
|
|
33
|
+
- 扫描 `git diff` 变更文件
|
|
34
|
+
- 识别 controller、service、router 中的接口和方法
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Step 2:扫描涉及接口
|
|
39
|
+
|
|
40
|
+
基于 tasks.md 的 `**Files**` 声明和 git diff,识别本次变更涉及的:
|
|
41
|
+
|
|
42
|
+
1. **HTTP 路由**(`router/router.go`):方法 + 路径 + 中间件
|
|
43
|
+
2. **Service 方法签名**(`service/biz/`):接口方法 + 参数
|
|
44
|
+
3. **CLI 命令**(`cli/register.go`):命令名 + 用途
|
|
45
|
+
4. **RPC 调用**:外部依赖的 RPC 接口
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Step 3:Phase 1 — 生成自测计划文档
|
|
50
|
+
|
|
51
|
+
**输出位置**:`.test_plans/{change名|分支名}-self-test-plan.md`
|
|
52
|
+
|
|
53
|
+
> `.test_plans/` 通过 `~/atdc/central/link.sh` 自动软链到主仓库,所有分支+worktree 共享,不入 git。
|
|
54
|
+
|
|
55
|
+
**文档必须包含**:
|
|
56
|
+
|
|
57
|
+
1. **测试前准备**:DDL 脚本、Apollo 配置、预置 SQL、环境变量
|
|
58
|
+
2. **按业务链路拆分的自测表格**:
|
|
59
|
+
|
|
60
|
+
```markdown
|
|
61
|
+
| # | 测试场景 | 触发方式 | 预期结果 | 验证方法 | 状态 |
|
|
62
|
+
|---|---------|---------|---------|---------|:--:|
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
3. **触发方式必须是可执行命令**:curl / SQL / cli 命令,不写描述性文字
|
|
66
|
+
4. **执行顺序建议**:按业务依赖排列(如先下单才能支付)
|
|
67
|
+
5. **快速回归命令**:`go build` + `go test` + grep 日志关键词
|
|
68
|
+
|
|
69
|
+
**输出后暂停,等用户确认。**
|
|
70
|
+
|
|
71
|
+
> 确认方式:用户说"继续"/"下一步"/"生成测试文件"进入 Phase 2,说"改一下"则修改文档。
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Step 4:用户确认后,Phase 2 — 生成集成测试文件
|
|
76
|
+
|
|
77
|
+
**输出位置**:`service/biz/{主模块名}/{主模块名}_integration_test.go`
|
|
78
|
+
|
|
79
|
+
### 4a. 文件结构
|
|
80
|
+
|
|
81
|
+
必须包含五部分,按顺序:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
1. 包声明 + imports
|
|
85
|
+
2. 环境变量 + 包级变量(var block)
|
|
86
|
+
3. HTTP helpers:doGet / doPost / doPostInner(从模板复制)
|
|
87
|
+
4. kugo 签名 helpers:kugoBuildSignature / kugoSign / signQuery / signQueryWithBody / mapToQuery / parseQuery
|
|
88
|
+
5. 测试函数(每条链路一组),每个场景一个独立顶层函数
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 4b. 可直接复制的通用模板(无需改动)
|
|
92
|
+
|
|
93
|
+
**以下代码块必须原样包含在生成的测试文件中。**
|
|
94
|
+
|
|
95
|
+
> ⚠️ **签名校验(最高优先级,历次踩坑点)**
|
|
96
|
+
>
|
|
97
|
+
> 本项目外部路由挂载 `middleware.CheckClientSignature`,算法来自 kugo `httpclient/signature.go:buildSignature`。
|
|
98
|
+
>
|
|
99
|
+
> | 方法 | 签名串 |
|
|
100
|
+
> |------|--------|
|
|
101
|
+
> | GET | `appkey + sorted(key=value...) + "" + appkey` |
|
|
102
|
+
> | POST | `appkey + sorted(key=value...) + **原始 JSON body** + appkey` |
|
|
103
|
+
>
|
|
104
|
+
> - key 按字典序排列;`signature` 自身**不参与**签名;key/value 之间无 `&` 分隔符(直接拼接 `key=value`)。
|
|
105
|
+
> - POST 时:**先 `json.Marshal` body,用同一字节串既签名又发送**;body 与 signature 不一致必报 `errcode=20006 invalid signature`。
|
|
106
|
+
> - 响应 `data.RawSignatureStr` 可用于对照调试。
|
|
107
|
+
> - 内部路由(`/inner/...`)用 `doPostInner`,**不签名**。
|
|
108
|
+
|
|
109
|
+
#### 1. imports
|
|
110
|
+
|
|
111
|
+
```go
|
|
112
|
+
import (
|
|
113
|
+
"bytes"
|
|
114
|
+
"crypto/md5"
|
|
115
|
+
"encoding/hex"
|
|
116
|
+
"encoding/json"
|
|
117
|
+
"fmt"
|
|
118
|
+
"io"
|
|
119
|
+
"net/http"
|
|
120
|
+
"os"
|
|
121
|
+
"sort"
|
|
122
|
+
"strings"
|
|
123
|
+
"testing"
|
|
124
|
+
"time"
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### 2. 变量与 init
|
|
129
|
+
|
|
130
|
+
```go
|
|
131
|
+
var (
|
|
132
|
+
host string
|
|
133
|
+
client *http.Client
|
|
134
|
+
testAppID string
|
|
135
|
+
testAppKey string
|
|
136
|
+
testToken string
|
|
137
|
+
// 跨场景共享状态(按需新增)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
func init() {
|
|
141
|
+
host = envOr("MEMBERSHIP_TEST_HOST", "http://127.0.0.1:80")
|
|
142
|
+
testAppID = envOr("MEMBERSHIP_TEST_APPID", "3492")
|
|
143
|
+
testAppKey = envOr("MEMBERSHIP_TEST_APPKEY", "")
|
|
144
|
+
testToken = envOr("MEMBERSHIP_TEST_TOKEN", "")
|
|
145
|
+
client = &http.Client{Timeout: 30 * time.Second}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
func envOr(key, defaultVal string) string {
|
|
149
|
+
if v := os.Getenv(key); v != "" { return v }
|
|
150
|
+
return defaultVal
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### 3. kugo 签名算法(精确复刻 kugo CheckClientSignature v0.1,不可修改)
|
|
155
|
+
|
|
156
|
+
```go
|
|
157
|
+
// kugoBuildSignature 复刻 kugo httpclient buildSignature
|
|
158
|
+
// GET: appkey + sorted(key=value...) + "" + appkey
|
|
159
|
+
// POST: appkey + sorted(key=value...) + body + appkey
|
|
160
|
+
// 源码:kugo@v1.6.35/httpclient/signature.go:buildSignature
|
|
161
|
+
func kugoBuildSignature(params map[string]string, appkey, body string) string {
|
|
162
|
+
keys := make([]string, 0, len(params))
|
|
163
|
+
for k := range params {
|
|
164
|
+
if k == "signature" { continue }
|
|
165
|
+
keys = append(keys, k)
|
|
166
|
+
}
|
|
167
|
+
sort.Strings(keys)
|
|
168
|
+
raw := appkey
|
|
169
|
+
for _, k := range keys { raw += k + "=" + params[k] }
|
|
170
|
+
raw += body
|
|
171
|
+
raw += appkey
|
|
172
|
+
hash := md5.Sum([]byte(raw))
|
|
173
|
+
return hex.EncodeToString(hash[:])
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// kugoSign GET 请求签名(body 为空)
|
|
177
|
+
func kugoSign(params map[string]string, appkey string) string {
|
|
178
|
+
return kugoBuildSignature(params, appkey, "")
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### 4. signQuery / signQueryWithBody(公参加签名)
|
|
183
|
+
|
|
184
|
+
```go
|
|
185
|
+
// signQuery GET 或无 body 场景
|
|
186
|
+
func signQuery(params map[string]string) map[string]string {
|
|
187
|
+
return signQueryWithBody(params, "")
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// signQueryWithBody POST 必须传入与请求体完全一致的原始 JSON 字符串
|
|
191
|
+
func signQueryWithBody(params map[string]string, body string) map[string]string {
|
|
192
|
+
result := make(map[string]string, len(params)+8)
|
|
193
|
+
for k, v := range params { result[k] = v }
|
|
194
|
+
result["appid"] = testAppID
|
|
195
|
+
result["clienttime"] = fmt.Sprintf("%d", time.Now().Unix())
|
|
196
|
+
result["clientver"] = "1.0"
|
|
197
|
+
result["mid"] = "1"
|
|
198
|
+
result["dfid"] = "-"
|
|
199
|
+
result["uuid"] = "test-integration-uuid"
|
|
200
|
+
if testToken != "" { result["token"] = testToken }
|
|
201
|
+
result["signature"] = kugoBuildSignature(result, testAppKey, body)
|
|
202
|
+
return result
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### 5. parseQuery / mapToQuery
|
|
207
|
+
|
|
208
|
+
```go
|
|
209
|
+
func parseQuery(qs string) map[string]string {
|
|
210
|
+
m := map[string]string{}
|
|
211
|
+
for _, kv := range strings.Split(qs, "&") {
|
|
212
|
+
if kv == "" { continue }
|
|
213
|
+
pair := strings.SplitN(kv, "=", 2)
|
|
214
|
+
if len(pair) == 2 { m[pair[0]] = pair[1] } else { m[pair[0]] = "" }
|
|
215
|
+
}
|
|
216
|
+
return m
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
func mapToQuery(m map[string]string) string {
|
|
220
|
+
keys := make([]string, 0, len(m))
|
|
221
|
+
for k := range m { keys = append(keys, k) }
|
|
222
|
+
sort.Strings(keys)
|
|
223
|
+
res := ""
|
|
224
|
+
for _, k := range keys {
|
|
225
|
+
if res != "" { res += "&" }
|
|
226
|
+
res += fmt.Sprintf("%s=%s", k, m[k])
|
|
227
|
+
}
|
|
228
|
+
return res
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### 6. HTTP helpers:doGet / doPost / doPostInner
|
|
233
|
+
|
|
234
|
+
```go
|
|
235
|
+
func doGet(t *testing.T, path string) map[string]any {
|
|
236
|
+
t.Helper()
|
|
237
|
+
parts := strings.SplitN(path, "?", 2)
|
|
238
|
+
basePath := parts[0]
|
|
239
|
+
params := map[string]string{}
|
|
240
|
+
if len(parts) > 1 { params = parseQuery(parts[1]) }
|
|
241
|
+
if params["userid"] == "" { params["userid"] = getDefaultUserID(t) }
|
|
242
|
+
signed := signQuery(params)
|
|
243
|
+
url := host + basePath + "?" + mapToQuery(signed)
|
|
244
|
+
req, _ := http.NewRequest("GET", url, nil)
|
|
245
|
+
resp, err := client.Do(req)
|
|
246
|
+
if err != nil { t.Fatalf("GET %s failed: %v", url, err) }
|
|
247
|
+
defer resp.Body.Close()
|
|
248
|
+
body, _ := io.ReadAll(resp.Body)
|
|
249
|
+
var m map[string]any
|
|
250
|
+
json.Unmarshal(body, &m)
|
|
251
|
+
t.Logf("GET %s → status=%d body=%s", url, resp.StatusCode, string(body))
|
|
252
|
+
return m
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
func doPost(t *testing.T, path string, payload map[string]any) map[string]any {
|
|
256
|
+
t.Helper()
|
|
257
|
+
b, _ := json.Marshal(payload)
|
|
258
|
+
params := map[string]string{}
|
|
259
|
+
if uid, ok := payload["userid"]; ok {
|
|
260
|
+
params["userid"] = fmt.Sprintf("%v", uid)
|
|
261
|
+
} else {
|
|
262
|
+
params["userid"] = getDefaultUserID(t)
|
|
263
|
+
}
|
|
264
|
+
querySigned := signQueryWithBody(params, string(b)) // POST: body 参与签名
|
|
265
|
+
url := host + path + "?" + mapToQuery(querySigned)
|
|
266
|
+
req, _ := http.NewRequest("POST", url, bytes.NewReader(b))
|
|
267
|
+
req.Header.Set("Content-Type", "application/json")
|
|
268
|
+
resp, err := client.Do(req)
|
|
269
|
+
if err != nil { t.Fatalf("POST %s failed: %v", url, err) }
|
|
270
|
+
defer resp.Body.Close()
|
|
271
|
+
respBody, _ := io.ReadAll(resp.Body)
|
|
272
|
+
var m map[string]any
|
|
273
|
+
json.Unmarshal(respBody, &m)
|
|
274
|
+
t.Logf("POST %s body=%s → status=%d resp=%s", url, string(b), resp.StatusCode, string(respBody))
|
|
275
|
+
return m
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// doPostSigned 带额外 query 参数(如 client_code=h5_quanmin_ios)的 POST,body 同样参与签名
|
|
279
|
+
func doPostSigned(t *testing.T, path string, payload map[string]any, extraQuery map[string]string) map[string]any {
|
|
280
|
+
t.Helper()
|
|
281
|
+
b, _ := json.Marshal(payload)
|
|
282
|
+
params := map[string]string{}
|
|
283
|
+
if uid, ok := payload["userid"]; ok {
|
|
284
|
+
params["userid"] = fmt.Sprintf("%v", uid)
|
|
285
|
+
} else {
|
|
286
|
+
params["userid"] = getDefaultUserID(t)
|
|
287
|
+
}
|
|
288
|
+
for k, v := range extraQuery { params[k] = v }
|
|
289
|
+
querySigned := signQueryWithBody(params, string(b))
|
|
290
|
+
url := host + path + "?" + mapToQuery(querySigned)
|
|
291
|
+
req, _ := http.NewRequest("POST", url, bytes.NewReader(b))
|
|
292
|
+
req.Header.Set("Content-Type", "application/json")
|
|
293
|
+
resp, err := client.Do(req)
|
|
294
|
+
if err != nil { t.Fatalf("POST %s failed: %v", url, err) }
|
|
295
|
+
defer resp.Body.Close()
|
|
296
|
+
respBody, _ := io.ReadAll(resp.Body)
|
|
297
|
+
var m map[string]any
|
|
298
|
+
json.Unmarshal(respBody, &m)
|
|
299
|
+
t.Logf("POST %s body=%s → status=%d resp=%s", url, string(b), resp.StatusCode, string(respBody))
|
|
300
|
+
return m
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
func doPostInner(t *testing.T, path string, payload map[string]any) string {
|
|
304
|
+
t.Helper()
|
|
305
|
+
b, _ := json.Marshal(payload)
|
|
306
|
+
url := host + path
|
|
307
|
+
resp, err := client.Post(url, "application/json", bytes.NewReader(b))
|
|
308
|
+
if err != nil { t.Fatalf("POST %s failed: %v", url, err) }
|
|
309
|
+
defer resp.Body.Close()
|
|
310
|
+
respBody, _ := io.ReadAll(resp.Body)
|
|
311
|
+
txt := strings.TrimSpace(string(respBody))
|
|
312
|
+
t.Logf("POST %s body=%s → status=%d text=%s", url, string(b), resp.StatusCode, txt)
|
|
313
|
+
return txt
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
#### 7. 响应解析辅助函数
|
|
318
|
+
|
|
319
|
+
```go
|
|
320
|
+
func getString(m map[string]any, key string) string {
|
|
321
|
+
if v, ok := m[key]; ok { s, _ := v.(string); return s }
|
|
322
|
+
if d, ok := m["data"]; ok {
|
|
323
|
+
if dm, ok := d.(map[string]any); ok {
|
|
324
|
+
if v, ok := dm[key]; ok { s, _ := v.(string); return s }
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return ""
|
|
328
|
+
}
|
|
329
|
+
func getFloat(m map[string]any, key string) float64 { v, _ := m[key].(float64); return v }
|
|
330
|
+
func getCode(m map[string]any) float64 { return getFloat(m, "errcode") }
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### 8. getDefaultUserID(按需调整返回哪个 userID)
|
|
334
|
+
|
|
335
|
+
```go
|
|
336
|
+
func getDefaultUserID(t *testing.T) string {
|
|
337
|
+
t.Helper()
|
|
338
|
+
return os.Getenv("MEMBERSHIP_TEST_USERID")
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
#### 9. 签名单测(必须生成,防止 POST 漏签 body)
|
|
343
|
+
|
|
344
|
+
```go
|
|
345
|
+
func TestKugoBuildSignature_PostIncludesBody(t *testing.T) {
|
|
346
|
+
if testAppKey == "" {
|
|
347
|
+
t.Skip("MEMBERSHIP_TEST_APPKEY not set")
|
|
348
|
+
}
|
|
349
|
+
body := `{"userid":1,"app_type":4}`
|
|
350
|
+
params := map[string]string{
|
|
351
|
+
"appid": testAppID, "clienttime": "1000000", "clientver": "1.0",
|
|
352
|
+
"mid": "1", "dfid": "-", "uuid": "test-integration-uuid", "userid": "1",
|
|
353
|
+
}
|
|
354
|
+
withBody := kugoBuildSignature(params, testAppKey, body)
|
|
355
|
+
withoutBody := kugoBuildSignature(params, testAppKey, "")
|
|
356
|
+
if withBody == withoutBody {
|
|
357
|
+
t.Fatal("POST signature must include body; got same hash as GET")
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### 4c. ⚠️ 测试函数组织规则(关键)
|
|
363
|
+
|
|
364
|
+
**每个场景必须是独立的顶层 `TestXxx` 函数,禁止用 `t.Run` 子测试。**
|
|
365
|
+
|
|
366
|
+
原因:`-run` 正则匹配时,父函数名会导致子测试连带执行,无法隔离。
|
|
367
|
+
|
|
368
|
+
```go
|
|
369
|
+
// ❌ 错误:t.Run 子测试,-run "场景1" 会连带执行场景2
|
|
370
|
+
func TestXxx_链1(t *testing.T) {
|
|
371
|
+
t.Run("场景1", func(t *testing.T) { ... })
|
|
372
|
+
t.Run("场景2", func(t *testing.T) { ... })
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ✅ 正确:每个场景独立顶层函数
|
|
376
|
+
func TestMembInt_L1S1_场景1(t *testing.T) { skipIfNoHost(t); l1s1(t) }
|
|
377
|
+
func TestMembInt_L1S2_场景2(t *testing.T) { skipIfNoHost(t); l1s2(t) }
|
|
378
|
+
|
|
379
|
+
func l1s1(t *testing.T) { ... }
|
|
380
|
+
func l1s2(t *testing.T) { ... }
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
**命名规范**:`Test{缩写前缀}_L{链号}S{场景号}_{中文场景名}`
|
|
384
|
+
|
|
385
|
+
- 前缀统一用 `TestMembInt` 或其他简短前缀
|
|
386
|
+
- 实际逻辑放在小写 `lXsY` 函数中,方便复用和跳测
|
|
387
|
+
- `skipIfNoHost` 在顶层函数调用,不在小写函数内
|
|
388
|
+
|
|
389
|
+
**运行粒度**:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
go test -run "TestMembInt_L1S1" # 单个场景
|
|
393
|
+
go test -run "TestMembInt_L1" # 整条链路
|
|
394
|
+
go test -run "TestMembInt_L[23]" # 链2+链3
|
|
395
|
+
go test -run "TestMembInt" # 全部
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## Step 5:质量自查
|
|
401
|
+
|
|
402
|
+
生成完成后运行:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
go build ./...
|
|
406
|
+
go test -run TestKugoBuildSignature ./service/biz/{module}/... -count=1
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
必须通过。有编译错误则修复。
|
|
410
|
+
|
|
411
|
+
额外检查:
|
|
412
|
+
- [ ] 所有 `TestXxx` 函数都是独立顶层函数(无 `t.Run` 嵌套)
|
|
413
|
+
- [ ] **POST 使用 `signQueryWithBody(params, string(b))`,GET 使用 `signQuery(params)`**
|
|
414
|
+
- [ ] **`TestKugoBuildSignature_PostIncludesBody` 已生成且通过**(POST 签名 ≠ GET 签名)
|
|
415
|
+
- [ ] kugo 参数名用的是 `signature` 不是 `sign`
|
|
416
|
+
- [ ] `host` 默认值不含路径后缀(如 `http://127.0.0.1:80`,完整 path 在请求里写 `/quanmin/v1/...`)
|
|
417
|
+
- [ ] `CheckUserToken` 所需的 `userid` 和 `token` 已注入 query
|
|
418
|
+
- [ ] 内嵌 H5 链路 extra query(如 `client_code=h5_quanmin_ios`)通过 `doPostSigned` 注入且参与签名
|
|
419
|
+
- [ ] 内部接口用 `doPostInner`(不签名)
|
|
420
|
+
- [ ] `testAppKey` / `testToken` 默认值为空,不写死密钥到代码
|
|
421
|
+
- [ ] 需要 DB/内网环境的场景用 `t.Skip` 跳过
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Step 6:输出完成摘要
|
|
426
|
+
|
|
427
|
+
```
|
|
428
|
+
✅ 自测计划生成完成:
|
|
429
|
+
|
|
430
|
+
📄 文档:.test_plans/{name}-self-test-plan.md({N} 条链路,{M} 个场景)
|
|
431
|
+
🧪 测试:service/biz/{module}/{module}_integration_test.go({K} 个独立 Test 函数)
|
|
432
|
+
|
|
433
|
+
运行:
|
|
434
|
+
go test -run TestKugoBuildSignature ./service/biz/{module}/... -count=1 # 先验证签名
|
|
435
|
+
go test -run "TestXxx_L1S1" ./service/biz/{module}/... -count=1 -v
|
|
436
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# prompts/X-travel-planner · 旅行规划
|
|
2
|
+
|
|
3
|
+
横向命令,任意时刻可触发:`/atdc:travel-planner <需求>`
|
|
4
|
+
|
|
5
|
+
## 执行步骤
|
|
6
|
+
|
|
7
|
+
### 1. 加载 Skill
|
|
8
|
+
|
|
9
|
+
用 skill 工具加载 `travel-planner` skill(路径:`~/.config/opencode/skills/travel-planner/SKILL.md`)。
|
|
10
|
+
|
|
11
|
+
### 2. 解析需求
|
|
12
|
+
|
|
13
|
+
从 $ARGUMENTS 中提取旅行要素(目的地、天数、出发地、人员、偏好等)。
|
|
14
|
+
|
|
15
|
+
### 3. 执行规划
|
|
16
|
+
|
|
17
|
+
按照 SKILL.md 定义的工作流执行:
|
|
18
|
+
- 需求澄清 → 信息研究(小红书+公开搜索)→ 智能规划(人流+人群适配)→ 攻略输出(HTML/文字)
|
|
19
|
+
|
|
20
|
+
### 4. 交付
|
|
21
|
+
|
|
22
|
+
根据用户需求输出:快速推荐(文字)或完整攻略(HTML页面)。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# prompts/X-xlsx2sql-insert · Excel → INSERT SQL
|
|
2
|
+
|
|
3
|
+
横向命令,任意时刻可触发:`/atdc:xlsx2sql-insert <excel_file>`
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 执行步骤
|
|
8
|
+
|
|
9
|
+
### 1. 确定输入
|
|
10
|
+
|
|
11
|
+
检查用户参数:
|
|
12
|
+
- Excel 文件路径
|
|
13
|
+
- `--table <name>` 目标表名(必填)
|
|
14
|
+
- `--sheet <name>` Sheet 名称(可选,默认第一个)
|
|
15
|
+
- `--header-row <N>` 表头行号(可选,默认 2)
|
|
16
|
+
- `--data-start <N>` 数据起始行(可选,默认 header+1)
|
|
17
|
+
- `--output <path>` 输出文件路径(可选,默认 stdout)
|
|
18
|
+
- 其他参数参考 `xlsx2sql --help`
|
|
19
|
+
|
|
20
|
+
### 2. 调用工具
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
${ATDC_DIR}/tools/xlsx2sql <excel_file> --table <table> [options]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 3. 输出
|
|
27
|
+
|
|
28
|
+
生成 INSERT SQL 语句,可直接用于 `membership_init_sku --sql=xxx.sql`。
|
|
29
|
+
|
|
30
|
+
### 4. 使用示例
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
/atdc:xlsx2sql-insert template.xlsx --table membership_product_sku --output init.sql
|
|
34
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# prompts/X-xlsx2sql-update · Excel → UPDATE SQL
|
|
2
|
+
|
|
3
|
+
横向命令,任意时刻可触发:`/atdc:xlsx2sql-update <excel_file>`
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 执行步骤
|
|
8
|
+
|
|
9
|
+
### 1. 确定输入
|
|
10
|
+
|
|
11
|
+
检查用户参数:
|
|
12
|
+
- Excel 文件路径
|
|
13
|
+
- `--table <name>` 目标表名(必填)
|
|
14
|
+
- `--key <col1,col2>` 主键列(必填,逗号分隔)
|
|
15
|
+
- `--set <col1,col2>` 限定更新列(可选,不指定则更新全部非key列)
|
|
16
|
+
- `--sheet <name>` Sheet 名称(可选)
|
|
17
|
+
- `--output <path>` 输出文件路径(可选,默认 stdout)
|
|
18
|
+
|
|
19
|
+
### 2. 调用工具
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
${ATDC_DIR}/tools/xlsx2sql <excel_file> --table <table> --mode update --key <cols> [--set <cols>]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 3. 输出
|
|
26
|
+
|
|
27
|
+
生成 UPDATE SQL 语句,每行 Excel 数据对应一条 `UPDATE ... SET ... WHERE ...`。
|
|
28
|
+
|
|
29
|
+
### 4. 使用示例
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 更新全部非key列
|
|
33
|
+
/atdc:xlsx2sql-update template.xlsx --table membership_product_sku --key id
|
|
34
|
+
|
|
35
|
+
# 只更新指定列
|
|
36
|
+
/atdc:xlsx2sql-update template.xlsx --table membership_product_sku --key id --set list_price,sale_price
|
|
37
|
+
|
|
38
|
+
# 输出到文件
|
|
39
|
+
/atdc:xlsx2sql-update template.xlsx --table membership_product_sku --key id --output update.sql
|
|
40
|
+
```
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atd-zoom-out
|
|
3
|
+
description: >
|
|
4
|
+
Tell the agent to zoom out and give broader context or a higher-level
|
|
5
|
+
perspective on code. Use when unfamiliar with a section of code, need to
|
|
6
|
+
understand how it fits into the bigger picture, or before modifying code
|
|
7
|
+
in a module you haven't worked with before.
|
|
8
|
+
argument-hint: "Which file, function, or module do you want perspective on?"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# prompts/X-zoom-out · 代码全景分析
|
|
12
|
+
|
|
13
|
+
横向命令:`/atdc:zoom-out <文件/函数/模块>`
|
|
14
|
+
也由 `3-impl Step 5b0` 自动建议触发。
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Step 1:确认目标
|
|
19
|
+
|
|
20
|
+
从用户输入或 3-impl 上下文中确定分析目标:
|
|
21
|
+
- 单个文件路径
|
|
22
|
+
- 单个函数/方法名
|
|
23
|
+
- 整个模块目录
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Step 2:系统定位
|
|
28
|
+
|
|
29
|
+
用 CodeGraph 分析目标在系统中的位置:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
codegraph_context "<目标符号或文件>"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
回答以下问题:
|
|
36
|
+
1. 这个模块/文件在项目目录树中的位置和角色?
|
|
37
|
+
2. 它的上游调用方是谁?(谁依赖它)
|
|
38
|
+
3. 它的下游依赖是谁?(它依赖谁)
|
|
39
|
+
4. 它属于哪个架构层?(handler / service / repository / domain)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Step 3:数据流分析
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
codegraph_trace from:"<入口>" to:"<目标>"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
追踪从入口到目标的数据流:
|
|
50
|
+
- 数据从哪里进入系统?
|
|
51
|
+
- 经过哪些变换/校验?
|
|
52
|
+
- 最终落到哪里(DB / 缓存 / 外部服务)?
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Step 4:影响面评估
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
codegraph_impact "<目标符号>"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
1-hop / 2-hop blast radius:
|
|
63
|
+
- 修改此代码会直接影响哪些调用方?
|
|
64
|
+
- 间接影响哪些用户流程?
|
|
65
|
+
- 是否有测试覆盖这些路径?
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Step 5:与 CONTEXT.md 对比
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
grep -A5 "<模块名>" CONTEXT.md 2>/dev/null
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- 此代码是否符合 CONTEXT.md 中描述的模块边界和职责?
|
|
76
|
+
- 是否发现了 CONTEXT.md 未记录的模块关系?(如果是 → 建议更新 CONTEXT.md)
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Step 6:输出全景报告
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
🔭 Zoom-Out 全景报告 — <目标>
|
|
84
|
+
|
|
85
|
+
### 位置
|
|
86
|
+
- 目录:<路径>
|
|
87
|
+
- 架构层:<handler/service/repository/domain>
|
|
88
|
+
- 所属模块:<模块名>
|
|
89
|
+
|
|
90
|
+
### 上游调用方(谁在用)
|
|
91
|
+
| 调用方 | 文件:行 | 用途 |
|
|
92
|
+
|--------|---------|------|
|
|
93
|
+
| ... | ... | ... |
|
|
94
|
+
|
|
95
|
+
### 下游依赖(用到了什么)
|
|
96
|
+
| 被调用方 | 文件:行 | 用途 |
|
|
97
|
+
|----------|---------|------|
|
|
98
|
+
| ... | ... | ... |
|
|
99
|
+
|
|
100
|
+
### 数据流
|
|
101
|
+
<入口> → <步骤1> → <步骤2> → ... → <目标> → <结果>
|
|
102
|
+
|
|
103
|
+
### 修改影响
|
|
104
|
+
- 直接影响:<N> 个调用方
|
|
105
|
+
- 间接影响流程:<列表>
|
|
106
|
+
- 测试覆盖:<有/缺口>
|
|
107
|
+
|
|
108
|
+
### 架构一致性
|
|
109
|
+
- CONTEXT.md 一致:✅ / ⚠️ <不一致项>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 衔接 3-impl
|
|
115
|
+
|
|
116
|
+
当由 `3-impl Step 5b0` 触发时:
|
|
117
|
+
- 目标从 `**Files**:` 字段自动提取
|
|
118
|
+
- 报告重点:修改影响 + 架构一致性
|
|
119
|
+
- 完成后自动回到 3-impl 继续执行
|