Compare commits
15 Commits
22107fa7b8
...
feat/devfl
| Author | SHA1 | Date | |
|---|---|---|---|
| 23ea8fdca5 | |||
| bfe3815626 | |||
| 3706d7f32d | |||
|
|
31c2d5a474 | ||
|
|
ccbdfd7eb3 | ||
|
|
f0e5735ffa | ||
|
|
52b8c85b94 | ||
|
|
0e24828a6d | ||
|
|
d564e6dbf9 | ||
|
|
a58dc39795 | ||
|
|
b5f44ac6aa | ||
| b6bd4bbfed | |||
| 187f5621c9 | |||
| b9c808cce0 | |||
| e3924e6b2b |
@@ -47,6 +47,44 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "db-migration-plugin",
|
||||
"source": "./skills-dev/db-migration-plugin",
|
||||
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时激活。",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "deploy-rollback-plugin",
|
||||
"source": "./skills-dev/deploy-rollback-plugin",
|
||||
"description": "回滚方案插件。部署后发现问题时的回滚策略、数据修复、灰度回退。挂载在 deploy 阶段。",
|
||||
"version": "1.0.0",
|
||||
"category": "devops",
|
||||
"keywords": [
|
||||
"devops",
|
||||
"deployment",
|
||||
"operations"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-android-plugin",
|
||||
"source": "./skills-dev/dev-android-plugin",
|
||||
"description": "Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。按需加载。",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-arch-plugin",
|
||||
"source": "./skills-dev/dev-arch-plugin",
|
||||
@@ -60,10 +98,127 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-cicd-plugin",
|
||||
"source": "./skills-dev/dev-cicd-plugin",
|
||||
"description": "Plugin for dev-cicd",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-coding-plugin",
|
||||
"source": "./skills-dev/dev-coding-plugin",
|
||||
"description": "Plugin for dev-coding",
|
||||
"description": "软件编码开发技能。Go 后端 + Vue/React 前端编码实现,集成 ai-proj 任务管理。",
|
||||
"version": "2.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-commit-plugin",
|
||||
"source": "./skills-dev/dev-commit-plugin",
|
||||
"description": "智能 /commit 命令:分支保护 + 自动建功能分支 + Conventional Commits 生成",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-deploy-plugin",
|
||||
"source": "./skills-dev/dev-deploy-plugin",
|
||||
"description": "Plugin for dev-deploy",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-integration-plugin",
|
||||
"source": "./skills-dev/dev-integration-plugin",
|
||||
"description": "前后端联调技能。API 契约验证、联调报告、纯后端需求自动跳过。对应 req 流程 integration 阶段。",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-ios-plugin",
|
||||
"source": "./skills-dev/dev-ios-plugin",
|
||||
"description": "iOS 开发插件。Swift/SwiftUI + MVVM 架构、TestFlight 部署、Xcode 构建。按需加载。",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-mcp-plugin",
|
||||
"source": "./skills-dev/dev-mcp-plugin",
|
||||
"description": "MCP Bridge 开发插件。TypeScript MCP 服务开发、Token 管理、HTTP 客户端模式。按需加载。",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-pda-plugin",
|
||||
"source": "./skills-dev/dev-pda-plugin",
|
||||
"description": "PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先模式。按需加载。",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-review-plugin",
|
||||
"source": "./skills-dev/dev-review-plugin",
|
||||
"description": "代码评审技能。五视角对抗性扫描法(攻击者/泄露者/并发者/边界者/依赖者),CR 报告生成,独立于 req 工作流可单独使用。",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
"development",
|
||||
"coding",
|
||||
"workflow"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "dev-scaffold-plugin",
|
||||
"source": "./skills-dev/dev-scaffold-plugin",
|
||||
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架(Model/Repository/Service/Handler/Route)。挂载在 dev 阶段。",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
@@ -99,10 +254,48 @@
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "review-checklist-plugin",
|
||||
"source": "./skills-dev/review-checklist-plugin",
|
||||
"description": "项目级代码评审检查清单。按项目积累的特定检查项,挂载在 dev-review 下自动加载。",
|
||||
"version": "1.0.0",
|
||||
"category": "utility",
|
||||
"keywords": [
|
||||
"utility",
|
||||
"tools"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-compare-plugin",
|
||||
"source": "./skills-req/req-compare-plugin",
|
||||
"description": "对比式需求分析插件。系统平移、竞品借鉴、版本升级时的参考对象对比分析。挂载在 analysis 阶段。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-design-plugin",
|
||||
"source": "./skills-req/req-design-plugin",
|
||||
"description": "需求开发设计技能。PRD 到开发设计的转换:API 契约、数据模型变更、任务拆分、风险评估。",
|
||||
"version": "2.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-dev-plugin",
|
||||
"source": "./skills-req/req-dev-plugin",
|
||||
"description": "Plugin for req-dev",
|
||||
"description": "[已废弃] 请使用 req-design-plugin。需求开发设计功能已迁移。",
|
||||
"version": "1.0.0",
|
||||
"category": "development",
|
||||
"keywords": [
|
||||
@@ -128,7 +321,46 @@
|
||||
{
|
||||
"name": "req-prd-plugin",
|
||||
"source": "./skills-req/req-prd-plugin",
|
||||
"description": "Plugin for req-prd",
|
||||
"description": "产品需求设计技能。PRD 文档编写、需求分析、用户故事、对比式分析。纯产品视角,不含技术实现。",
|
||||
"version": "2.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-prototype-plugin",
|
||||
"source": "./skills-req/req-prototype-plugin",
|
||||
"description": "Stitch 原型生成与迭代。基于 PRD 文档自动生成 UI 原型。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-research-plugin",
|
||||
"source": "./skills-req/req-research-plugin",
|
||||
"description": "需求调研插件。代码审计、数据库分析、现有功能调研。挂载在 analysis 阶段,需要深度调研时激活。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-review-plugin",
|
||||
"source": "./skills-req/req-review-plugin",
|
||||
"description": "PRD 评审方法论。用于需求评审、PRD 文档审查、评审意见编写。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
@@ -141,7 +373,20 @@
|
||||
{
|
||||
"name": "req-test-gate-plugin",
|
||||
"source": "./skills-req/req-test-gate-plugin",
|
||||
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5,含前后端联调+视觉验证)、部署级验证(Deploy Gates)、持续回归(Regression)。",
|
||||
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
"project-management",
|
||||
"tasks",
|
||||
"requirements"
|
||||
],
|
||||
"strict": false
|
||||
},
|
||||
{
|
||||
"name": "req-workflow-plugin",
|
||||
"source": "./skills-req/req-workflow-plugin",
|
||||
"description": "需求完整工作流。从创建到归档的完整流程、Hook 自动同步、测试环境流程。",
|
||||
"version": "1.0.0",
|
||||
"category": "productivity",
|
||||
"keywords": [
|
||||
|
||||
253
docs/design/release-draft-gate.md
Normal file
253
docs/design/release-draft-gate.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# Release Draft 闸门机制 — 设计文档
|
||||
|
||||
**REQ**: REQ-20260416-0017 P0-3
|
||||
**状态**: 设计阶段
|
||||
**创建时间**: 2026-04-16
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景
|
||||
|
||||
### 当前问题
|
||||
|
||||
ai-proj 当前 CI/CD 流程(`.gitea/workflows/build.yaml`):
|
||||
|
||||
```
|
||||
push 到 main → 自动 build 镜像 → 自动部署生产
|
||||
```
|
||||
|
||||
**这个链路没有"最后一道人工闸门"**。merge develop→main 的 PR 一合并,生产就开始部署。一旦合并,只能事后回滚。
|
||||
|
||||
**典型事故场景**:PR 评审时漏了某个改动,merge 后立即推到线上,5 分钟后发现问题,但已经影响用户 5 分钟。
|
||||
|
||||
### 借鉴方案
|
||||
|
||||
**devflow-claude `/req:release` 命令的核心做法:**
|
||||
|
||||
1. merge PR 不直接部署
|
||||
2. 创建一个 **draft release**(草稿)
|
||||
3. **需要人工在 Gitea/GitHub 平台点 "Publish" 按钮** 才触发部署
|
||||
4. Draft 可以审查 SQL migration、回滚脚本、changelog 等"产物清单"
|
||||
5. Publish 后才打 tag + 触发 build.yaml
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标
|
||||
|
||||
- 在 merge main 和"真正部署"之间插入 **人工 publish 闸门**
|
||||
- 提供 **release 产物清单预览**(SQL migration / 回滚脚本 / changelog)
|
||||
- 支持 **一键回滚**(从 draft release 追溯回滚脚本)
|
||||
- 与现有 ai-proj MCP 工具链集成
|
||||
|
||||
---
|
||||
|
||||
## 3. 架构设计
|
||||
|
||||
### 3.1 流程对比
|
||||
|
||||
**当前流程:**
|
||||
```
|
||||
[feat/xxx] → PR → merge develop → 测试
|
||||
[develop] → PR → merge main → ✗ 立即触发 build.yaml → 生产部署
|
||||
```
|
||||
|
||||
**新流程:**
|
||||
```
|
||||
[feat/xxx] → PR → merge develop → 测试
|
||||
[develop] → PR → merge main → Gitea draft release(待审查)
|
||||
↓
|
||||
人工 publish
|
||||
↓
|
||||
tag 推送 + 触发 build.yaml → 生产部署
|
||||
```
|
||||
|
||||
### 3.2 组件职责
|
||||
|
||||
| 组件 | 新增/改造 | 职责 |
|
||||
|------|---------|------|
|
||||
| **ai-proj backend** | 新增 | `/api/v1/releases` REST API(创建 draft、publish、查询、回滚) |
|
||||
| **mcp-task-bridge** | 新增工具 | `create_release_draft` / `publish_release` / `list_releases` / `get_release` / `rollback_release` |
|
||||
| **Gitea Actions** | 改造 | `build.yaml` 触发条件改为 `release.published` 事件 |
|
||||
| **`release` skill** | 新增 | AI 侧命令封装(`/release new`, `/release publish`, `/release rollback`) |
|
||||
| **release-draft.sh** | 新增脚本 | 本地命令行工具,用于在 CI 外手动创建 draft |
|
||||
|
||||
### 3.3 数据模型
|
||||
|
||||
**新增 `releases` 表:**
|
||||
|
||||
```sql
|
||||
CREATE TABLE releases (
|
||||
id SERIAL PRIMARY KEY,
|
||||
display_id VARCHAR(64) UNIQUE NOT NULL, -- e.g. "RELEASE-20260416-001"
|
||||
version VARCHAR(64) NOT NULL, -- e.g. "v1.2.0"
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(32) NOT NULL, -- draft / published / rolled_back
|
||||
git_base_ref VARCHAR(255) NOT NULL, -- e.g. previous tag "v1.1.9"
|
||||
git_head_ref VARCHAR(255) NOT NULL, -- e.g. commit sha after merge
|
||||
git_tag VARCHAR(64), -- 生成的 tag,publish 后才有
|
||||
changelog_md TEXT NOT NULL, -- 自动生成的 changelog
|
||||
sql_migration_paths TEXT[], -- 合并的 SQL 文件路径列表
|
||||
sql_rollback_md TEXT, -- 回滚脚本
|
||||
gitea_release_id BIGINT, -- Gitea 上的 release ID
|
||||
gitea_release_url VARCHAR(512),
|
||||
created_by INT REFERENCES users(id),
|
||||
published_by INT REFERENCES users(id),
|
||||
published_at TIMESTAMP,
|
||||
rolled_back_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### 3.4 API 设计
|
||||
|
||||
| Endpoint | 作用 | 权限 |
|
||||
|----------|------|------|
|
||||
| `POST /api/v1/releases/drafts` | 创建 draft release(自动推导版本、生成 changelog) | 开发者 |
|
||||
| `POST /api/v1/releases/:id/publish` | 发布(推 tag、触发部署) | 需确认 |
|
||||
| `GET /api/v1/releases` | 列表 | 所有人 |
|
||||
| `GET /api/v1/releases/:id` | 详情 | 所有人 |
|
||||
| `POST /api/v1/releases/:id/rollback` | 回滚到上一版本 | 管理员 |
|
||||
|
||||
### 3.5 Gitea 集成
|
||||
|
||||
**创建 draft release(调 Gitea API):**
|
||||
```http
|
||||
POST /api/v1/repos/{owner}/{repo}/releases
|
||||
{
|
||||
"tag_name": "v1.2.0",
|
||||
"target_commitish": "main",
|
||||
"name": "v1.2.0",
|
||||
"body": "...<changelog>...",
|
||||
"draft": true
|
||||
}
|
||||
```
|
||||
|
||||
**build.yaml 改造:**
|
||||
|
||||
```yaml
|
||||
# Before
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
# After
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch: # 保留手动触发
|
||||
```
|
||||
|
||||
**关键变化**:`release.published` 事件只在草稿被 publish 时触发,merge main 不再自动触发。
|
||||
|
||||
---
|
||||
|
||||
## 4. 用户流程(典型场景)
|
||||
|
||||
### 4.1 开发者发版
|
||||
|
||||
```
|
||||
1. 合并 PR 到 main(照旧)
|
||||
2. 在 Claude Code 里:/release new
|
||||
- AI 读 git log,自动推导版本号
|
||||
- 汇总 SQL migration
|
||||
- 生成 changelog
|
||||
- 生成回滚脚本
|
||||
- 展示产物清单,等用户确认
|
||||
3. 确认后:MCP create_release_draft → 后端 API → Gitea draft
|
||||
4. 收到 Slack/企微通知:"draft release v1.2.0 已创建,请审查"
|
||||
5. 打开 Gitea 查看产物清单
|
||||
6. 点 "Publish release" 按钮
|
||||
7. CI/CD 自动触发 → 生产部署
|
||||
```
|
||||
|
||||
### 4.2 回滚
|
||||
|
||||
```
|
||||
1. /release rollback v1.2.0
|
||||
2. MCP rollback_release → 后端执行回滚 SQL → 重新部署上一版本镜像
|
||||
3. 记录到 releases 表:status=rolled_back
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 渐进式落地路径(P0-3 拆分子任务)
|
||||
|
||||
由于 P0-3 完整实现涉及后端 + MCP + CI/CD + skill 四个层面,**不建议单会话完成**。拆分为:
|
||||
|
||||
| 子任务 | 范围 | 风险 |
|
||||
|-------|------|------|
|
||||
| **P0-3.1** | 后端 release model + migration | 低(只是建表) |
|
||||
| **P0-3.2** | 后端 /api/v1/releases API(draft + publish) | 中 |
|
||||
| **P0-3.3** | MCP 工具 create_release_draft / publish_release | 中 |
|
||||
| **P0-3.4** | `release` skill 创建 | 低 |
|
||||
| **P0-3.5** | Gitea draft release 集成 | 中 |
|
||||
| **P0-3.6** | build.yaml 改为 release.published 触发 | **高**(影响所有人的发版流程) |
|
||||
| **P0-3.7** | 回滚能力 | 中 |
|
||||
|
||||
**推荐落地节奏:**
|
||||
- 先做 P0-3.1 ~ P0-3.5(构建基础能力)
|
||||
- **保留旧 push-to-main 流程作为 fallback**
|
||||
- 跑 2 周观察
|
||||
- 再做 P0-3.6(切换 CI/CD 触发源)
|
||||
- 最后 P0-3.7(回滚能力)
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与应对
|
||||
|
||||
### R1: build.yaml 触发源切换影响所有开发者
|
||||
|
||||
**影响**:目前大家习惯 "merge main 即部署",切换后需等 publish
|
||||
|
||||
**应对**:
|
||||
- 上线前 1 周发邮件 + 企微通知
|
||||
- 提供手动触发(workflow_dispatch)作为应急入口
|
||||
- 文档化新流程
|
||||
|
||||
### R2: 回滚脚本自动生成不够可靠
|
||||
|
||||
**影响**:复杂 migration(如数据迁移)的回滚无法自动推导
|
||||
|
||||
**应对**:
|
||||
- AI 只生成简单反向 DDL(CREATE→DROP 等)
|
||||
- 复杂情况标记 `⚠️ 需手工补全`
|
||||
- 回滚 SQL 由人工审查后纳入 draft release
|
||||
|
||||
### R3: draft release 被忘记 publish
|
||||
|
||||
**影响**:功能开发完但生产没部署
|
||||
|
||||
**应对**:
|
||||
- 企微机器人每天检查超 24h 未 publish 的 draft,发提醒
|
||||
- 管理员 dashboard 显示 draft 列表
|
||||
|
||||
---
|
||||
|
||||
## 7. 验收标准
|
||||
|
||||
- [ ] `releases` 表创建,migration 落地
|
||||
- [ ] `/api/v1/releases/drafts` 创建 draft(至少支持最小字段)
|
||||
- [ ] MCP `create_release_draft` 工具可用
|
||||
- [ ] 能从 Claude Code 一键创建 Gitea draft release
|
||||
- [ ] build.yaml 支持 `release.published` 触发
|
||||
- [ ] 至少跑通 1 次完整流程(draft → 人工 publish → 自动部署)
|
||||
- [ ] 回滚脚本自动生成覆盖 >80% 的 DDL 场景
|
||||
|
||||
---
|
||||
|
||||
## 8. 参考
|
||||
|
||||
- devflow-claude: `plugins/req/commands/release.md`
|
||||
- REQ-20260416-0017(母需求)
|
||||
- 当前 build.yaml: `/Users/donglinlai/coding/qiudl/new-ai-proj/.gitea/workflows/build.yaml`
|
||||
- Gitea Release API: https://docs.gitea.com/api/next/#tag/repository/operation/repoCreateRelease
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一步行动
|
||||
|
||||
- [ ] 本文档提交评审
|
||||
- [ ] 评审通过后拆 7 个子任务(P0-3.1 ~ P0-3.7)并分别关联到 REQ-20260416-0017
|
||||
- [ ] 从 P0-3.1(数据模型)开始实施
|
||||
100
hooks/README.md
Normal file
100
hooks/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Claude Code Hooks
|
||||
|
||||
本目录包含 ai-proj-helper 体系的 Claude Code 钩子脚本。
|
||||
|
||||
**REQ-20260416-0017 P0 批 — 源自 devflow-claude 借鉴**
|
||||
|
||||
## 脚本清单
|
||||
|
||||
| 脚本 | 事件 | 作用 |
|
||||
|------|------|------|
|
||||
| `session-context.sh` | SessionStart | 从分支名解析 REQ-ID,注入需求上下文到会话 |
|
||||
| `pre-tool-confirm.sh` | PreToolUse (Bash) | 拦截生产发布、force push、docker 生产容器、reset --hard 等危险操作 |
|
||||
|
||||
## 安装(用户级,一次即可)
|
||||
|
||||
### 方式 1:编辑 `~/.claude/settings.json`
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
|
||||
"timeout": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/pre-tool-confirm.sh",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方式 2:一键安装脚本
|
||||
|
||||
```bash
|
||||
bash /Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/install.sh
|
||||
```
|
||||
|
||||
## 验证
|
||||
|
||||
### SessionStart hook
|
||||
|
||||
1. 切到一个带 REQ-ID 的分支:`git checkout feat/REQ-20260416-0017-xxx`
|
||||
2. 打开新的 Claude Code 会话
|
||||
3. 会话开头应看到 `# 需求上下文(SessionStart Hook)` 块
|
||||
|
||||
### PreToolUse hook
|
||||
|
||||
让 AI 尝试执行这些命令中的任一条,应弹出原生确认对话框:
|
||||
|
||||
- `git push origin main`
|
||||
- `git push --force`
|
||||
- `tea pr merge --base main`
|
||||
- `docker rm ai_postgres_prod`
|
||||
- `git reset --hard`
|
||||
|
||||
## 依赖
|
||||
|
||||
- `bash` (macOS / Linux 默认)
|
||||
- `jq` (PreToolUse hook 需要,`brew install jq`)
|
||||
- `python3` (SessionStart hook 解析 JSON)
|
||||
- `curl` (SessionStart hook 调 MCP API)
|
||||
|
||||
## 自定义
|
||||
|
||||
### 修改 MCP API 地址
|
||||
|
||||
在项目根目录创建 `.ai-proj-env`:
|
||||
|
||||
```bash
|
||||
export AI_PROJ_API_BASE="https://api.ai-proj.example.com"
|
||||
export AI_PROJ_MCP_KEY="your-mcp-api-key"
|
||||
```
|
||||
|
||||
或设置全局环境变量。
|
||||
|
||||
### 拦截更多命令
|
||||
|
||||
编辑 `pre-tool-confirm.sh`,在 `REASON` 赋值的 elif 链中增加规则。
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **快速失败退出**:不处理的命令立即 `exit 0`,不影响性能
|
||||
2. **非侵入性**:网络/依赖缺失时静默退出,不阻塞正常工作流
|
||||
3. **可复现**:hook 脚本跟随仓库分发,方便团队一致部署
|
||||
81
hooks/install.sh
Executable file
81
hooks/install.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
# install.sh
|
||||
# 一键把 ai-proj-helper hooks 注册到 ~/.claude/settings.json
|
||||
#
|
||||
# 用法: bash hooks/install.sh
|
||||
|
||||
set -e
|
||||
|
||||
HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SETTINGS_FILE="$HOME/.claude/settings.json"
|
||||
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo "❌ 需要 python3(用于合并 JSON)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "⚠️ 未安装 jq,PreToolUse hook 将无法正常工作"
|
||||
echo " 请执行: brew install jq"
|
||||
fi
|
||||
|
||||
mkdir -p "$HOME/.claude"
|
||||
|
||||
python3 << EOF
|
||||
import json
|
||||
import os
|
||||
|
||||
settings_file = "$SETTINGS_FILE"
|
||||
hooks_dir = "$HOOKS_DIR"
|
||||
|
||||
# 读已有配置
|
||||
if os.path.exists(settings_file):
|
||||
with open(settings_file) as f:
|
||||
data = json.load(f)
|
||||
else:
|
||||
data = {}
|
||||
|
||||
# 合并 hooks
|
||||
hooks = data.setdefault("hooks", {})
|
||||
|
||||
# SessionStart
|
||||
session_start = hooks.setdefault("SessionStart", [])
|
||||
session_cmd = f"{hooks_dir}/session-context.sh"
|
||||
already_registered = any(
|
||||
any(h.get("command") == session_cmd for h in entry.get("hooks", []))
|
||||
for entry in session_start
|
||||
)
|
||||
if not already_registered:
|
||||
session_start.append({
|
||||
"hooks": [{"type": "command", "command": session_cmd, "timeout": 10}]
|
||||
})
|
||||
print("✅ 注册 SessionStart hook")
|
||||
else:
|
||||
print("⏭️ SessionStart hook 已存在")
|
||||
|
||||
# PreToolUse (Bash)
|
||||
pre_tool = hooks.setdefault("PreToolUse", [])
|
||||
pre_cmd = f"{hooks_dir}/pre-tool-confirm.sh"
|
||||
already_registered = any(
|
||||
entry.get("matcher") == "Bash" and any(h.get("command") == pre_cmd for h in entry.get("hooks", []))
|
||||
for entry in pre_tool
|
||||
)
|
||||
if not already_registered:
|
||||
pre_tool.append({
|
||||
"matcher": "Bash",
|
||||
"hooks": [{"type": "command", "command": pre_cmd, "timeout": 30}]
|
||||
})
|
||||
print("✅ 注册 PreToolUse (Bash) hook")
|
||||
else:
|
||||
print("⏭️ PreToolUse hook 已存在")
|
||||
|
||||
# 写回
|
||||
with open(settings_file, "w") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n📝 已更新: {settings_file}")
|
||||
print("\n下一步:")
|
||||
print(" 1. 重启 Claude Code 会话")
|
||||
print(" 2. 切到含 REQ-ID 的分支测试 SessionStart hook")
|
||||
print(" 3. 让 AI 尝试 'git push origin main' 测试 PreToolUse hook")
|
||||
EOF
|
||||
94
hooks/pre-tool-confirm.sh
Executable file
94
hooks/pre-tool-confirm.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# pre-tool-confirm.sh
|
||||
# PreToolUse Hook: 拦截危险操作,强制原生确认对话框
|
||||
#
|
||||
# 输入格式(stdin JSON):
|
||||
# { "tool_name": "Bash", "tool_input": { "command": "..." } }
|
||||
#
|
||||
# 输出格式(stdout JSON):
|
||||
# { "hookSpecificOutput": { "hookEventName": "PreToolUse",
|
||||
# "permissionDecision": "ask",
|
||||
# "permissionDecisionReason": "..." } }
|
||||
#
|
||||
# 安装方式:在 ~/.claude/settings.json 配置
|
||||
# hooks.PreToolUse:
|
||||
# - matcher: "Bash"
|
||||
# hooks:
|
||||
# - type: command
|
||||
# command: "<path>/hooks/pre-tool-confirm.sh"
|
||||
# timeout: 30
|
||||
#
|
||||
# 参考:devflow-claude confirm-before-commit.sh + ai-proj memory 规则
|
||||
# REQ-20260416-0017 P0-2
|
||||
|
||||
set -e
|
||||
|
||||
INPUT=$(cat)
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
# 没有 jq 就不处理
|
||||
exit 0
|
||||
fi
|
||||
|
||||
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
|
||||
|
||||
if [ -z "$COMMAND" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REASON=""
|
||||
|
||||
# ============ 1. 生产分支推送 ============
|
||||
if echo "$COMMAND" | grep -qE '\bgit\s+push\b.*\b(origin\s+)?(main|master)\b'; then
|
||||
REASON="⚠️ 即将推送到 main/master 生产分支。确认已过 PR 评审?"
|
||||
|
||||
# ============ 2. 强制推送 ============
|
||||
elif echo "$COMMAND" | grep -qE '\bgit\s+push\b.*(--force|--force-with-lease|-f\b)'; then
|
||||
REASON="⛔ 危险:force push 会覆盖远程历史,可能丢失他人提交。确认继续?"
|
||||
|
||||
# ============ 3. tea pr merge --base main ============
|
||||
elif echo "$COMMAND" | grep -qE '\btea\s+pr\s+merge\b.*--base\s+main\b'; then
|
||||
REASON="⚠️ 即将合并 PR 到 main 分支,合并后将触发生产部署。确认 PR 已测试?"
|
||||
|
||||
# ============ 4. docker rm/stop 生产容器 ============
|
||||
elif echo "$COMMAND" | grep -qE '\bdocker\s+(rm|stop|kill)\b.*\b(ai_postgres_prod|ai_backend_prod|ai_frontend_prod|ai_redis_prod)\b'; then
|
||||
REASON="⛔ 危险:即将停止/删除生产容器!2026-04-05 曾因此宕机 15 分钟。确认是灾难恢复?"
|
||||
|
||||
# ============ 5. docker rm/stop 其他 prod 相关 ============
|
||||
elif echo "$COMMAND" | grep -qE '\bdocker\s+(rm|stop|kill)\b.*_prod\b'; then
|
||||
REASON="⚠️ 即将停止/删除含 _prod 的容器。确认是生产环境?"
|
||||
|
||||
# ============ 6. git reset --hard / clean -fd ============
|
||||
elif echo "$COMMAND" | grep -qE '\bgit\s+reset\s+--hard\b'; then
|
||||
REASON="⚠️ git reset --hard 会丢弃所有未提交改动。确认继续?"
|
||||
|
||||
elif echo "$COMMAND" | grep -qE '\bgit\s+clean\s+.*-f'; then
|
||||
REASON="⚠️ git clean -f 会删除未跟踪文件。确认继续?"
|
||||
|
||||
# ============ 7. rm -rf / 系统路径 ============
|
||||
elif echo "$COMMAND" | grep -qE '\brm\s+.*-[rf]+.*\s+(/|/\*|~|\$HOME)'; then
|
||||
REASON="⛔ 危险:rm -rf 指向系统根或家目录。确认继续?"
|
||||
|
||||
# ============ 8. ssh 生产服务器 + 破坏性命令 ============
|
||||
elif echo "$COMMAND" | grep -qE 'ssh\s+\S*prod\S*.*\b(rm|drop|truncate|delete)\b'; then
|
||||
REASON="⛔ 危险:在生产服务器执行破坏性命令。确认继续?"
|
||||
|
||||
# ============ 9. psql/mysql 生产数据库 + DROP/TRUNCATE/DELETE ============
|
||||
elif echo "$COMMAND" | grep -qiE '(psql|mysql).*prod.*\b(DROP|TRUNCATE|DELETE FROM)\b'; then
|
||||
REASON="⛔ 危险:生产数据库 DROP/TRUNCATE/DELETE。确认已备份?"
|
||||
|
||||
# ============ 10. MCP advance_delivery_stage force=true ============
|
||||
# 这种会走 MCP 而不是 Bash,本 hook 不好拦,留给另一个 matcher 处理
|
||||
fi
|
||||
|
||||
if [ -n "$REASON" ]; then
|
||||
jq -n --arg reason "$REASON" '{
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "PreToolUse",
|
||||
permissionDecision: "ask",
|
||||
permissionDecisionReason: $reason
|
||||
}
|
||||
}'
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
161
hooks/release-draft.sh
Executable file
161
hooks/release-draft.sh
Executable file
@@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
# release-draft.sh
|
||||
# 最小可用版:从本地 git 仓库创建 Gitea draft release
|
||||
#
|
||||
# 用法:
|
||||
# export GITEA_TOKEN=$(bw get password "Gitea - qiudl Token")
|
||||
# bash release-draft.sh v1.2.0 [--from v1.1.9]
|
||||
#
|
||||
# 输出: 创建的 draft release URL(待人工 publish)
|
||||
# REQ-20260416-0017 P0-3 最小可用脚本
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="$1"
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "❌ 用法: $0 <version> [--from <previous_tag>]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FROM_TAG=""
|
||||
if [ "$2" = "--from" ]; then
|
||||
FROM_TAG="$3"
|
||||
fi
|
||||
|
||||
if ! git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
echo "❌ 不在 git 仓库内"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REPO_SLUG=$(git remote get-url origin 2>/dev/null | \
|
||||
sed -E 's|.*[:/]([^/]+/[^/]+)\.git$|\1|' | \
|
||||
sed -E 's|.*[:/]([^/]+/[^/]+)$|\1|')
|
||||
|
||||
if [ -z "$REPO_SLUG" ]; then
|
||||
echo "❌ 无法从 git remote 推断 OWNER/REPO"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "❌ 需要 GITEA_TOKEN 环境变量"
|
||||
echo " export GITEA_TOKEN=\$(bw get password 'Gitea - qiudl Token')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
GITEA_URL="${GITEA_URL:-https://gitea.pipexerp.com}"
|
||||
|
||||
# 推断 from
|
||||
if [ -z "$FROM_TAG" ]; then
|
||||
FROM_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
TO_REF="HEAD"
|
||||
|
||||
# 生成 changelog 内容
|
||||
echo "📋 生成 changelog..."
|
||||
|
||||
CHANGELOG=""
|
||||
if [ -n "$FROM_TAG" ]; then
|
||||
COMMITS=$(git log --pretty=format:'- %s (%h)' "${FROM_TAG}..${TO_REF}" 2>/dev/null || echo "")
|
||||
else
|
||||
COMMITS=$(git log --pretty=format:'- %s (%h)' "${TO_REF}" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
if [ -z "$COMMITS" ]; then
|
||||
echo "⚠️ 无 commit,放弃"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 按类型分组
|
||||
FEATS=$(echo "$COMMITS" | grep -iE 'feat(\(|:)|新功能' || true)
|
||||
FIXES=$(echo "$COMMITS" | grep -iE 'fix(\(|:)|修复' || true)
|
||||
CHORES=$(echo "$COMMITS" | grep -iE 'chore(\(|:)' || true)
|
||||
OTHERS=$(echo "$COMMITS" | grep -vE 'feat(\(|:)|fix(\(|:)|chore(\(|:)|新功能|修复' || true)
|
||||
|
||||
CHANGELOG="## 发布内容
|
||||
|
||||
**版本**: \`${VERSION}\`
|
||||
**区间**: \`${FROM_TAG:-init}..${TO_REF}\`
|
||||
|
||||
"
|
||||
|
||||
if [ -n "$FEATS" ]; then
|
||||
CHANGELOG="${CHANGELOG}### 新功能
|
||||
|
||||
${FEATS}
|
||||
|
||||
"
|
||||
fi
|
||||
|
||||
if [ -n "$FIXES" ]; then
|
||||
CHANGELOG="${CHANGELOG}### Bug 修复
|
||||
|
||||
${FIXES}
|
||||
|
||||
"
|
||||
fi
|
||||
|
||||
if [ -n "$CHORES" ]; then
|
||||
CHANGELOG="${CHANGELOG}### 杂项
|
||||
|
||||
${CHORES}
|
||||
|
||||
"
|
||||
fi
|
||||
|
||||
if [ -n "$OTHERS" ]; then
|
||||
CHANGELOG="${CHANGELOG}### 其他
|
||||
|
||||
${OTHERS}
|
||||
|
||||
"
|
||||
fi
|
||||
|
||||
CHANGELOG="${CHANGELOG}
|
||||
|
||||
---
|
||||
|
||||
⚠️ **这是 draft release**,审查无误后点击 'Publish release' 按钮才会触发生产部署。
|
||||
|
||||
📋 审查要点:
|
||||
- [ ] 所有改动已过 PR 评审
|
||||
- [ ] SQL migration 已验证(如有)
|
||||
- [ ] 回滚方案已确认(如有)
|
||||
- [ ] 生产环境准备就绪"
|
||||
|
||||
# 创建 draft release
|
||||
echo "🚀 创建 Gitea draft release..."
|
||||
|
||||
BODY_JSON=$(python3 -c "
|
||||
import json
|
||||
print(json.dumps({
|
||||
'tag_name': '$VERSION',
|
||||
'target_commitish': 'main',
|
||||
'name': '$VERSION',
|
||||
'body': '''$CHANGELOG''',
|
||||
'draft': True,
|
||||
'prerelease': False,
|
||||
}))
|
||||
")
|
||||
|
||||
RESP=$(curl -s -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$BODY_JSON" \
|
||||
"${GITEA_URL}/api/v1/repos/${REPO_SLUG}/releases")
|
||||
|
||||
HTML_URL=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('html_url',''))" 2>/dev/null)
|
||||
|
||||
if [ -n "$HTML_URL" ]; then
|
||||
echo "✅ Draft release 已创建"
|
||||
echo "🔗 $HTML_URL"
|
||||
echo ""
|
||||
echo "⏭️ 下一步:"
|
||||
echo " 1. 打开链接审查产物清单"
|
||||
echo " 2. 确认无误后点 'Publish release' 按钮"
|
||||
echo " 3. CI/CD 将自动触发生产部署"
|
||||
else
|
||||
echo "❌ 创建失败"
|
||||
echo "$RESP" | head -20
|
||||
exit 1
|
||||
fi
|
||||
130
hooks/session-context.sh
Executable file
130
hooks/session-context.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/bin/bash
|
||||
# session-context.sh
|
||||
# SessionStart Hook: 会话启动时自动注入需求上下文
|
||||
#
|
||||
# 从当前 Git 分支名解析 REQ-ID,调用 ai-proj MCP API 查询需求详情,
|
||||
# 把标题 / 状态 / delivery_stage / reviewer / 进行中需求数注入 system-reminder。
|
||||
#
|
||||
# 安装方式:
|
||||
# 在 ~/.claude/settings.json 的 hooks.SessionStart 配置:
|
||||
# {
|
||||
# "command": "/Users/donglinlai/coding/qiudl/ai-proj-helper/hooks/session-context.sh",
|
||||
# "timeout": 10
|
||||
# }
|
||||
#
|
||||
# 参考:devflow-claude 同名脚本 + ai-proj MCP 适配
|
||||
# REQ-20260416-0017 P0-1
|
||||
|
||||
set -e
|
||||
|
||||
# 仅在 git 仓库内执行
|
||||
if ! git rev-parse --git-dir >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||
if [ -z "$REPO_ROOT" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
# ============ 1. 当前分支 → REQ ID ============
|
||||
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
|
||||
REQ_ID=""
|
||||
|
||||
if [ -n "$BRANCH" ]; then
|
||||
# 匹配 feat/REQ-20260416-0017-xxx / fix/REQ-20260416-0017 / feature/req-20260416-0017
|
||||
REQ_ID=$(echo "$BRANCH" | grep -oiE 'REQ-[0-9]{8}-[0-9]{4}' | head -1 | tr '[:lower:]' '[:upper:]')
|
||||
fi
|
||||
|
||||
# ============ 2. 无 REQ 时仅输出分支信息(静默退出条件) ============
|
||||
if [ -z "$REQ_ID" ]; then
|
||||
# 只要不在 main/develop 上就提示一下
|
||||
case "$BRANCH" in
|
||||
main|master|develop|"") exit 0 ;;
|
||||
esac
|
||||
echo "# 会话上下文"
|
||||
echo ""
|
||||
echo "- 当前分支: \`${BRANCH}\`(未检测到 REQ ID)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ============ 3. 查询 MCP API ============
|
||||
# MCP API 通过 localhost:8080 直连(ai-proj 本地后端)或 ai-proj-prod
|
||||
# 这里优先读项目根的 .ai-proj-env 决定环境
|
||||
API_BASE="${AI_PROJ_API_BASE:-}"
|
||||
API_TOKEN="${AI_PROJ_MCP_KEY:-}"
|
||||
|
||||
if [ -f "$REPO_ROOT/.ai-proj-env" ]; then
|
||||
# shellcheck disable=SC1091
|
||||
source "$REPO_ROOT/.ai-proj-env"
|
||||
fi
|
||||
|
||||
if [ -z "$API_BASE" ]; then
|
||||
# 默认走本地 dev
|
||||
API_BASE="http://localhost:8080"
|
||||
fi
|
||||
|
||||
# 查询需求
|
||||
RESP=""
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
if [ -n "$API_TOKEN" ]; then
|
||||
RESP=$(curl -s --max-time 3 -H "X-MCP-API-Key: $API_TOKEN" \
|
||||
"${API_BASE}/api/v1/mcp/requirements/by-display-id/${REQ_ID}" 2>/dev/null || echo "")
|
||||
else
|
||||
RESP=$(curl -s --max-time 3 \
|
||||
"${API_BASE}/api/v1/mcp/requirements/by-display-id/${REQ_ID}" 2>/dev/null || echo "")
|
||||
fi
|
||||
fi
|
||||
|
||||
# ============ 4. 解析并输出 ============
|
||||
echo "# 需求上下文(SessionStart Hook)"
|
||||
echo ""
|
||||
echo "- 分支: \`${BRANCH}\`"
|
||||
echo "- 需求: **${REQ_ID}**"
|
||||
|
||||
if [ -n "$RESP" ] && command -v python3 >/dev/null 2>&1; then
|
||||
# 尝试用 python 解析
|
||||
PARSED=$(python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
d = json.loads('''$RESP''')
|
||||
data = d.get('data', {})
|
||||
if not data:
|
||||
sys.exit(0)
|
||||
title = data.get('title', '?')
|
||||
status = data.get('status', '?')
|
||||
stage = data.get('delivery_stage', '?')
|
||||
priority = data.get('priority', '?')
|
||||
project = data.get('project_name', '?')
|
||||
print(f'title={title}')
|
||||
print(f'status={status}')
|
||||
print(f'stage={stage}')
|
||||
print(f'priority={priority}')
|
||||
print(f'project={project}')
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null)
|
||||
|
||||
if [ -n "$PARSED" ]; then
|
||||
TITLE=$(echo "$PARSED" | grep '^title=' | sed 's/^title=//')
|
||||
STATUS=$(echo "$PARSED" | grep '^status=' | sed 's/^status=//')
|
||||
STAGE=$(echo "$PARSED" | grep '^stage=' | sed 's/^stage=//')
|
||||
PRIORITY=$(echo "$PARSED" | grep '^priority=' | sed 's/^priority=//')
|
||||
PROJECT=$(echo "$PARSED" | grep '^project=' | sed 's/^project=//')
|
||||
|
||||
[ -n "$TITLE" ] && echo "- 标题: ${TITLE}"
|
||||
[ -n "$PROJECT" ] && echo "- 项目: ${PROJECT}"
|
||||
[ -n "$STATUS" ] && echo "- 状态: ${STATUS}"
|
||||
[ -n "$STAGE" ] && echo "- 交付阶段: ${STAGE}"
|
||||
[ -n "$PRIORITY" ] && echo "- 优先级: ${PRIORITY}"
|
||||
else
|
||||
echo "- 📡 MCP API 响应为空或未授权(API_BASE=${API_BASE})"
|
||||
fi
|
||||
else
|
||||
echo "- ⚠️ 无法连接 MCP API(${API_BASE}),仅显示分支信息"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "💡 相关命令:\`/req get ${REQ_ID}\` 查看详情 · \`/commit\` 智能提交"
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "agent-browser-plugin",
|
||||
"description": "浏览器自动化技能。用于网页交互、E2E冒烟测试、需求验收验证、前端开发验证、截图对比。基于 Vercel agent-browser CLI。",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
186
skills-dev/agent-browser-plugin/skills/SKILL.md
Normal file
186
skills-dev/agent-browser-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,186 @@
|
||||
---
|
||||
name: agent-browser
|
||||
description: 浏览器自动化技能。用于网页交互、E2E冒烟测试、需求验收验证、前端开发验证、截图对比。基于 Vercel agent-browser CLI。
|
||||
---
|
||||
|
||||
# agent-browser
|
||||
|
||||
浏览器自动化 CLI,专为 AI Agent 设计。当用户需要与网页交互时使用:导航页面、填写表单、点击按钮、截图、提取数据、测试 Web 应用、自动化浏览器任务。
|
||||
|
||||
当用户提到以下关键词时自动激活:浏览器自动化、打开网页、截图、网页测试、冒烟测试、E2E 验证、页面检查、agent-browser。
|
||||
|
||||
## 前置条件
|
||||
|
||||
```bash
|
||||
npm i -g agent-browser # 安装 CLI
|
||||
agent-browser install # 下载 Chrome
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 核心工作流(snapshot-ref 模式)
|
||||
|
||||
1. **导航** 到 URL
|
||||
2. **snapshot** 获取可交互元素(返回 `@e1`, `@e2` 等引用)
|
||||
3. **交互** 使用元素引用
|
||||
4. **重新 snapshot** DOM 变化后刷新引用
|
||||
|
||||
```bash
|
||||
agent-browser open https://example.com/form
|
||||
agent-browser snapshot -i
|
||||
agent-browser fill @e1 "user@example.com"
|
||||
agent-browser click @e3
|
||||
agent-browser wait --load networkidle
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 命令参考
|
||||
|
||||
### 导航与控制
|
||||
- `open <url>` – 打开网页
|
||||
- `goto <url>` – 在会话内跳转
|
||||
- `close` – 关闭浏览器
|
||||
- `wait <condition>` – 等待元素/网络/JS 条件
|
||||
|
||||
### 页面检查
|
||||
- `snapshot -i` – 获取可交互元素及引用(推荐)
|
||||
- `snapshot` – 完整无障碍树
|
||||
- `get text|html|value @e1` – 提取元素内容
|
||||
- `get title|url` – 获取页面标题/URL
|
||||
- `screenshot [path]` – 截图
|
||||
- `screenshot --annotate` – 带元素标注的截图
|
||||
|
||||
### 交互操作
|
||||
- `click @e1` / `dblclick @e1` – 单击/双击
|
||||
- `fill @e1 "text"` – 清空并输入
|
||||
- `type @e1 "text"` – 追加输入
|
||||
- `select @e1 "option"` – 下拉选择
|
||||
- `check @e1` – 勾选/取消勾选
|
||||
- `press Enter` – 按键
|
||||
- `hover @e1` – 悬停
|
||||
- `scroll down 500` – 滚动
|
||||
- `upload @e1 /path` – 上传文件
|
||||
- `drag @e1 @e2` – 拖放
|
||||
|
||||
### 高级功能
|
||||
- `eval 'code'` – 执行 JavaScript
|
||||
- `diff snapshot` – 对比前后页面状态
|
||||
- `set viewport 1920 1080` – 设置视口
|
||||
- `set device "iPhone 14"` – 移动端模拟
|
||||
- `find role <role> click --name "Label"` – 语义定位器
|
||||
- `network mock <pattern> --body '...'` – 网络请求模拟
|
||||
- `cookies get/set/clear` – Cookie 管理
|
||||
- `storage get/set/clear` – localStorage 管理
|
||||
|
||||
---
|
||||
|
||||
## 认证与会话持久化
|
||||
|
||||
### Auth Vault(推荐)
|
||||
```bash
|
||||
agent-browser auth save myapp
|
||||
agent-browser open https://myapp.com --auth myapp
|
||||
```
|
||||
|
||||
### 持久化 Profile
|
||||
```bash
|
||||
agent-browser open https://site.com --profile ~/.myapp-profile
|
||||
```
|
||||
|
||||
### 命名会话
|
||||
```bash
|
||||
agent-browser open https://site.com --session-name myapp
|
||||
```
|
||||
|
||||
### 状态文件
|
||||
```bash
|
||||
agent-browser open https://site.com --state ./auth.json --save-state
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 命令链式调用
|
||||
|
||||
不需要中间输出时用 `&&` 串联:
|
||||
```bash
|
||||
agent-browser open https://example.com && \
|
||||
agent-browser wait --load networkidle && \
|
||||
agent-browser screenshot page.png
|
||||
```
|
||||
|
||||
需要解析输出(如 snapshot 获取 ref)时分开执行。
|
||||
|
||||
---
|
||||
|
||||
## 与其他技能结合
|
||||
|
||||
### 结合 dev-test(E2E 冒烟测试)
|
||||
|
||||
在 dev-test 的 Gate 4(E2E 冒烟测试)中使用:
|
||||
|
||||
```bash
|
||||
# 1. 打开本地前端
|
||||
agent-browser open http://localhost:3000 --session-name e2e-test
|
||||
|
||||
# 2. 验证页面加载
|
||||
agent-browser snapshot -i
|
||||
agent-browser screenshot /tmp/e2e-home.png
|
||||
|
||||
# 3. 测试登录流程
|
||||
agent-browser fill @e1 "admin@example.com"
|
||||
agent-browser fill @e2 "password"
|
||||
agent-browser click @e3
|
||||
agent-browser wait --load networkidle
|
||||
|
||||
# 4. 验证登录成功
|
||||
agent-browser get title # 应包含 "Dashboard"
|
||||
agent-browser screenshot /tmp/e2e-dashboard.png
|
||||
|
||||
# 5. 清理
|
||||
agent-browser close
|
||||
```
|
||||
|
||||
### 结合 req(需求验收验证)
|
||||
|
||||
PRD 评审后实际操作验证需求是否实现:
|
||||
|
||||
```bash
|
||||
# 根据 PRD 验收标准逐项检查
|
||||
agent-browser open http://localhost:3000/feature-page
|
||||
agent-browser snapshot -i
|
||||
# 按验收标准操作对应元素...
|
||||
agent-browser screenshot /tmp/req-verify.png
|
||||
```
|
||||
|
||||
### 结合 dev-coding(前端开发即时验证)
|
||||
|
||||
开发完组件后立即打开页面验证:
|
||||
|
||||
```bash
|
||||
agent-browser open http://localhost:3000/new-page
|
||||
agent-browser snapshot -i # 检查渲染的元素
|
||||
agent-browser screenshot --annotate /tmp/dev-check.png # 带标注截图
|
||||
```
|
||||
|
||||
### 结合 ops-tools(部署后验证)
|
||||
|
||||
部署后快速检查页面是否正常:
|
||||
|
||||
```bash
|
||||
agent-browser open https://ai.pipexerp.com
|
||||
agent-browser wait --load networkidle
|
||||
agent-browser get title
|
||||
agent-browser screenshot /tmp/deploy-check.png
|
||||
agent-browser close
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
- **Ref 生命周期**:导航或 DOM 变化后引用失效,必须重新 snapshot
|
||||
- **超时**:默认 25 秒,通过 `AGENT_BROWSER_DEFAULT_TIMEOUT` 环境变量调整
|
||||
- **会话隔离**:用 `--session-name` 实现并行自动化
|
||||
- **安全**:支持域名白名单、操作策略、内容边界
|
||||
- **输出格式**:用 `--json` 获取机器可读输出
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "db-migration-plugin",
|
||||
"description": "数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时激活。",
|
||||
"version": "1.0.0",
|
||||
"author": { "name": "qiudl" }
|
||||
}
|
||||
105
skills-dev/db-migration-plugin/skills/SKILL.md
Normal file
105
skills-dev/db-migration-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
name: db-migration
|
||||
description: 数据库变更方案插件。Migration 脚本生成、数据迁移策略、回滚方案。挂载在 design 阶段,涉及数据库变更时由 req-design 推荐激活。
|
||||
---
|
||||
|
||||
# 数据库变更方案插件 (db-migration)
|
||||
|
||||
## 概述
|
||||
|
||||
当需求涉及数据库结构变更时使用,确保变更安全、可回滚。
|
||||
|
||||
**触发条件**:
|
||||
- 新增/修改/删除表或字段
|
||||
- 数据迁移(旧数据转换)
|
||||
- 索引优化
|
||||
|
||||
## Migration 规范
|
||||
|
||||
### 文件命名
|
||||
|
||||
```
|
||||
backend/migrations/YYYYMMDDHHMMSS_description.up.sql # 正向迁移
|
||||
backend/migrations/YYYYMMDDHHMMSS_description.down.sql # 回滚迁移
|
||||
```
|
||||
|
||||
### 安全规则
|
||||
|
||||
| 操作 | 风险等级 | 注意事项 |
|
||||
|------|---------|---------|
|
||||
| ADD COLUMN (nullable) | 低 | 安全,无锁表 |
|
||||
| ADD COLUMN (NOT NULL + DEFAULT) | 中 | PG 12+ 不锁表,旧版本锁表 |
|
||||
| DROP COLUMN | 高 | 确认无代码引用,先标记废弃 |
|
||||
| ALTER COLUMN TYPE | 高 | 可能锁表,大表慎用 |
|
||||
| ADD INDEX | 中 | 使用 CONCURRENTLY 避免锁表 |
|
||||
| DROP TABLE | 极高 | 必须确认无依赖 |
|
||||
|
||||
### Migration 模板
|
||||
|
||||
**新增表**:
|
||||
```sql
|
||||
-- up.sql
|
||||
CREATE TABLE IF NOT EXISTS xxx (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL,
|
||||
-- 业务字段
|
||||
name VARCHAR(255) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'active',
|
||||
-- 审计字段
|
||||
created_by BIGINT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_xxx_tenant_id ON xxx(tenant_id);
|
||||
CREATE INDEX idx_xxx_deleted_at ON xxx(deleted_at);
|
||||
|
||||
-- down.sql
|
||||
DROP TABLE IF EXISTS xxx;
|
||||
```
|
||||
|
||||
**新增字段**:
|
||||
```sql
|
||||
-- up.sql
|
||||
ALTER TABLE xxx ADD COLUMN yyy VARCHAR(255);
|
||||
-- 如果需要索引
|
||||
CREATE INDEX CONCURRENTLY idx_xxx_yyy ON xxx(yyy);
|
||||
|
||||
-- down.sql
|
||||
DROP INDEX IF EXISTS idx_xxx_yyy;
|
||||
ALTER TABLE xxx DROP COLUMN IF EXISTS yyy;
|
||||
```
|
||||
|
||||
**数据迁移**:
|
||||
```sql
|
||||
-- up.sql
|
||||
-- 1. 先添加新字段
|
||||
ALTER TABLE xxx ADD COLUMN new_field VARCHAR(255);
|
||||
|
||||
-- 2. 迁移数据
|
||||
UPDATE xxx SET new_field = old_field WHERE new_field IS NULL;
|
||||
|
||||
-- 3. 添加约束(数据迁移完成后)
|
||||
ALTER TABLE xxx ALTER COLUMN new_field SET NOT NULL;
|
||||
|
||||
-- down.sql
|
||||
ALTER TABLE xxx ALTER COLUMN new_field DROP NOT NULL;
|
||||
ALTER TABLE xxx DROP COLUMN IF EXISTS new_field;
|
||||
```
|
||||
|
||||
## 大表变更策略
|
||||
|
||||
当表数据量 > 100 万行时:
|
||||
|
||||
1. **添加索引**:必须使用 `CREATE INDEX CONCURRENTLY`
|
||||
2. **修改字段类型**:分步执行(新增列→迁移数据→切换引用→删除旧列)
|
||||
3. **添加 NOT NULL**:先添加 DEFAULT,再 SET NOT NULL
|
||||
4. **数据迁移**:分批处理,每批 1000-10000 行
|
||||
|
||||
## 回滚检查
|
||||
|
||||
每个 Migration 必须有可执行的 down.sql:
|
||||
- [ ] down.sql 存在且语法正确
|
||||
- [ ] down.sql 可以完全撤销 up.sql 的变更
|
||||
- [ ] down.sql 不会丢失业务数据(除非是 DROP TABLE)
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "deploy-rollback-plugin",
|
||||
"description": "回滚方案插件。部署后发现问题时的回滚策略、数据修复、灰度回退。挂载在 deploy 阶段。",
|
||||
"version": "1.0.0",
|
||||
"author": { "name": "qiudl" }
|
||||
}
|
||||
102
skills-dev/deploy-rollback-plugin/skills/SKILL.md
Normal file
102
skills-dev/deploy-rollback-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
name: deploy-rollback
|
||||
description: 回滚方案插件。部署后发现问题时的回滚策略和执行步骤。挂载在 deploy 阶段,部署出问题时激活。
|
||||
---
|
||||
|
||||
# 回滚方案插件 (deploy-rollback)
|
||||
|
||||
## 概述
|
||||
|
||||
当生产部署后发现问题时,提供结构化的回滚决策和执行步骤。
|
||||
|
||||
**触发条件**:
|
||||
- 部署后健康检查失败
|
||||
- 部署后用户报告问题
|
||||
- 部署后监控告警
|
||||
|
||||
## 回滚决策树
|
||||
|
||||
```
|
||||
问题发现
|
||||
├── 服务完全不可用?
|
||||
│ └── YES → 立即回滚(紧急)
|
||||
├── 核心功能异常?
|
||||
│ └── YES → 评估影响范围 → 回滚或热修复
|
||||
├── 非核心功能异常?
|
||||
│ └── 评估修复时间
|
||||
│ ├── < 30 分钟 → 热修复
|
||||
│ └── > 30 分钟 → 回滚
|
||||
└── 性能下降?
|
||||
├── 严重(>5x) → 回滚
|
||||
└── 轻微(<2x) → 监控 + 排期修复
|
||||
```
|
||||
|
||||
## 回滚类型
|
||||
|
||||
### 1. Docker 镜像回滚(最常用)
|
||||
|
||||
```bash
|
||||
# 查看历史镜像
|
||||
docker images | grep ai-proj
|
||||
|
||||
# 回滚到上一版本
|
||||
cd deploy
|
||||
# 修改 docker-compose.prod.yml 中的镜像 tag
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# 验证
|
||||
curl -s http://localhost:8080/health | jq .
|
||||
```
|
||||
|
||||
### 2. 数据库回滚
|
||||
|
||||
```bash
|
||||
# 执行 down migration
|
||||
cd backend
|
||||
migrate -path migrations -database "$DB_URL" down 1
|
||||
|
||||
# 验证表结构
|
||||
psql -U user -d db -c "\d affected_table"
|
||||
```
|
||||
|
||||
**注意**:数据库回滚可能导致数据丢失,必须先评估影响。
|
||||
|
||||
### 3. 配置回滚
|
||||
|
||||
```bash
|
||||
# 恢复旧配置
|
||||
git checkout HEAD~1 -- deploy/config/
|
||||
docker compose -f docker-compose.prod.yml restart
|
||||
```
|
||||
|
||||
## 回滚检查清单
|
||||
|
||||
- [ ] 确认问题现象和影响范围
|
||||
- [ ] 通知相关人员(用户需知道在维护中)
|
||||
- [ ] 执行回滚操作
|
||||
- [ ] 验证服务恢复正常
|
||||
- [ ] 验证数据完整性
|
||||
- [ ] 记录回滚原因和过程
|
||||
- [ ] 创建修复任务
|
||||
|
||||
## 回滚记录模板
|
||||
|
||||
```markdown
|
||||
## 回滚记录
|
||||
|
||||
**时间**: YYYY-MM-DD HH:mm
|
||||
**触发原因**: [问题描述]
|
||||
**影响范围**: [受影响的功能/用户]
|
||||
**回滚类型**: Docker 镜像 / 数据库 / 配置
|
||||
**回滚操作**: [具体步骤]
|
||||
**恢复确认**: [验证结果]
|
||||
**根因分析**: [问题根因]
|
||||
**修复计划**: [后续修复方案]
|
||||
```
|
||||
|
||||
## 预防措施
|
||||
|
||||
- 部署前确保 Migration 有 down.sql
|
||||
- 部署前确保 Docker 保留上一版本镜像
|
||||
- 大变更使用灰度发布
|
||||
- 监控关键指标(错误率、延迟、CPU/内存)
|
||||
1
skills-dev/dev-android-plugin/.claude-plugin/plugin.json
Normal file
1
skills-dev/dev-android-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"dev-android-plugin","description":"Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
|
||||
54
skills-dev/dev-android-plugin/skills/SKILL.md
Normal file
54
skills-dev/dev-android-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
name: dev-android
|
||||
description: Android 开发插件。Kotlin + Jetpack Compose + Hilt 依赖注入。当涉及 Android 开发任务时按需加载。
|
||||
---
|
||||
|
||||
# Android 开发插件 (dev-android)
|
||||
|
||||
## 架构:MVVM + Hilt
|
||||
|
||||
```
|
||||
android-app/app/src/main/
|
||||
├── java/com/project/
|
||||
│ ├── ui/ # Compose 屏幕 + 组件
|
||||
│ ├── data/ # API + Repository + 本地存储
|
||||
│ ├── domain/ # 业务逻辑
|
||||
│ └── di/ # Hilt 依赖注入
|
||||
└── res/ # 资源文件
|
||||
```
|
||||
|
||||
## 代码规范
|
||||
|
||||
```kotlin
|
||||
@HiltViewModel
|
||||
class TaskViewModel @Inject constructor(
|
||||
private val taskRepository: TaskRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val _tasks = MutableStateFlow<List<Task>>(emptyList())
|
||||
val tasks: StateFlow<List<Task>> = _tasks.asStateFlow()
|
||||
|
||||
fun fetchTasks() {
|
||||
viewModelScope.launch {
|
||||
taskRepository.getTasks()
|
||||
.collect { _tasks.value = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TaskListScreen(viewModel: TaskViewModel = hiltViewModel()) {
|
||||
val tasks by viewModel.tasks.collectAsState()
|
||||
LazyColumn {
|
||||
items(tasks) { task -> TaskItem(task = task) }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 构建
|
||||
|
||||
```bash
|
||||
./gradlew assembleDebug # Debug 构建
|
||||
./gradlew assembleRelease # Release 构建
|
||||
./gradlew test # 测试
|
||||
```
|
||||
8
skills-dev/dev-cicd-plugin/.claude-plugin/plugin.json
Normal file
8
skills-dev/dev-cicd-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "dev-cicd-plugin",
|
||||
"description": "Plugin for dev-cicd",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
599
skills-dev/dev-cicd-plugin/skills/SKILL.md
Normal file
599
skills-dev/dev-cicd-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,599 @@
|
||||
---
|
||||
name: dev-cicd
|
||||
description: CI/CD 流水线设计、优化与排查。适配 Gitea Actions + Go/Swift/Next.js/Docker 栈。当用户提到 CI、CD、流水线、pipeline、workflow、构建失败、runner 相关任务时自动激活。
|
||||
---
|
||||
|
||||
# CI/CD 流水线技能 (dev-cicd)
|
||||
|
||||
## 概述
|
||||
|
||||
管理 Gitea Actions CI/CD 流水线的设计、优化和故障排查。适配技术栈:
|
||||
- **Git**: Gitea (self-hosted, GitHub Actions YAML 兼容)
|
||||
- **Backend**: Go (Gin + GORM)
|
||||
- **iOS**: Swift 6 + SwiftUI + TCA
|
||||
- **Web**: Next.js (React)
|
||||
- **Container**: Docker + Docker Compose
|
||||
- **Registry**: Aliyun ACR
|
||||
- **Runners**: self-hosted (Linux) + macos-arm64 (iOS)
|
||||
|
||||
---
|
||||
|
||||
## 命令参考
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `/cicd analyze` | 分析当前 workflow 找优化点 |
|
||||
| `/cicd troubleshoot` | 诊断流水线失败原因 |
|
||||
| `/cicd template [go\|ios\|web\|docker]` | 生成 workflow 模板 |
|
||||
| `/cicd status` | 查看最近 workflow 运行状态 |
|
||||
|
||||
---
|
||||
|
||||
## 1. Pipeline 设计
|
||||
|
||||
### 1.1 Monorepo 路径过滤
|
||||
|
||||
仓库包含多个子项目,用 `paths` 只触发相关构建:
|
||||
|
||||
```yaml
|
||||
# .gitea/workflows/ci-cd.yml — Go + Web + Docker
|
||||
on:
|
||||
push:
|
||||
branches: [develop, main]
|
||||
paths:
|
||||
- 'gateway/**'
|
||||
- 'web/**'
|
||||
- 'docker/**'
|
||||
- 'scripts/**'
|
||||
|
||||
# .gitea/workflows/ios-testflight.yml — iOS 独立
|
||||
on:
|
||||
push:
|
||||
branches: [develop, main]
|
||||
paths:
|
||||
- 'ios/**'
|
||||
```
|
||||
|
||||
### 1.2 Pipeline 结构原则
|
||||
|
||||
```
|
||||
快速反馈优先:
|
||||
1. 静态检查 (lint/vet) — 秒级
|
||||
2. 单元测试 (test) — 1-5 分钟
|
||||
3. 构建 (build) — 2-10 分钟
|
||||
4. 集成测试 (可选) — 5-15 分钟
|
||||
5. 发布 (deploy) — 5-15 分钟
|
||||
```
|
||||
|
||||
### 1.3 Go 后端模板
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout
|
||||
run: |
|
||||
cd ${{ github.workspace }}
|
||||
if [ -d .git ]; then
|
||||
git fetch --depth 1 origin ${{ github.ref_name }}
|
||||
git reset --hard origin/${{ github.ref_name }}
|
||||
else
|
||||
git clone --depth 1 --branch ${{ github.ref_name }} \
|
||||
http://xiaoqu:${{ secrets.REPO_TOKEN }}@localhost:3000/<org>/<repo>.git .
|
||||
fi
|
||||
|
||||
- name: Go Vet
|
||||
run: cd gateway && go vet ./...
|
||||
|
||||
- name: Go Test
|
||||
run: cd gateway && go test ./... -count=1 -timeout 120s
|
||||
|
||||
- name: Go Build
|
||||
run: cd gateway && go build ./cmd/gateway/
|
||||
```
|
||||
|
||||
### 1.4 iOS 模板
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
ios:
|
||||
runs-on: macos-arm64
|
||||
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
||||
steps:
|
||||
- name: Checkout
|
||||
run: git clone --depth 1 --branch ${{ github.ref_name }} <repo-url> .
|
||||
|
||||
- name: xcodegen
|
||||
run: /opt/homebrew/bin/xcodegen generate
|
||||
working-directory: ios
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
set -o pipefail
|
||||
swift test 2>&1 | tee /tmp/test.log | tail -20
|
||||
working-directory: ios
|
||||
|
||||
- name: Deploy TestFlight
|
||||
env:
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
|
||||
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
|
||||
run: ./scripts/ios-testflight.sh
|
||||
```
|
||||
|
||||
### 1.5 Web (Next.js) 模板
|
||||
|
||||
```yaml
|
||||
- name: Web Install
|
||||
run: cd web && npm ci --legacy-peer-deps
|
||||
|
||||
- name: Web Build
|
||||
run: cd web && npm run build
|
||||
|
||||
- name: Docker Build Web
|
||||
run: |
|
||||
docker build -t $REGISTRY/$WEB_IMAGE:${{ github.sha }} \
|
||||
-t $REGISTRY/$WEB_IMAGE:latest ./web
|
||||
```
|
||||
|
||||
### 1.6 单 Job vs 多 Job
|
||||
|
||||
| 场景 | 选择 | 原因 |
|
||||
|------|------|------|
|
||||
| Runner capacity=1 | 单 Job | 多 Job 串行 + 多次 checkout = 更慢 |
|
||||
| 多 Runner 可用 | 多 Job + needs | 并行加速 |
|
||||
| 不同 OS (Linux+macOS) | 分 Workflow | 不同 runner label |
|
||||
|
||||
**当前推荐**:Linux runner 单 Job(Go+Web+Docker),macOS runner 单 Job(iOS)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 优化
|
||||
|
||||
### 2.1 浅克隆
|
||||
|
||||
```yaml
|
||||
# 首次 clone
|
||||
git clone --depth 1 --branch ${{ github.ref_name }} <url> .
|
||||
|
||||
# 增量 fetch
|
||||
git fetch --depth 1 origin ${{ github.ref_name }}
|
||||
git reset --hard origin/${{ github.ref_name }}
|
||||
```
|
||||
|
||||
**效果**:仓库含大量二进制文件时,clone 时间从 30s+ 降到 3-5s。
|
||||
|
||||
**注意**:需要 push 时先 `git fetch --unshallow`。
|
||||
|
||||
### 2.2 依赖缓存
|
||||
|
||||
Gitea Actions 不支持 `actions/cache`,但 self-hosted runner 可利用本地磁盘:
|
||||
|
||||
```yaml
|
||||
# Go modules — runner 上全局缓存
|
||||
env:
|
||||
GOMODCACHE: /opt/runner-cache/go/mod
|
||||
GOCACHE: /opt/runner-cache/go/build
|
||||
|
||||
# npm — 利用 node_modules 持久化
|
||||
# self-hosted runner 的 workspace 在两次运行间保留
|
||||
- run: |
|
||||
if [ -f web/node_modules/.cache-hash ] && \
|
||||
[ "$(cat web/node_modules/.cache-hash)" = "$(md5sum web/package-lock.json | cut -d' ' -f1)" ]; then
|
||||
echo "npm cache hit, skip install"
|
||||
else
|
||||
cd web && npm ci --legacy-peer-deps
|
||||
md5sum package-lock.json | cut -d' ' -f1 > node_modules/.cache-hash
|
||||
fi
|
||||
|
||||
# SPM — Xcode 自动缓存到 DerivedData,self-hosted runner 保留
|
||||
```
|
||||
|
||||
### 2.3 并发取消
|
||||
|
||||
避免同一分支多次 push 排队等待:
|
||||
|
||||
```yaml
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
```
|
||||
|
||||
### 2.4 条件跳过
|
||||
|
||||
```yaml
|
||||
# 跳过 CI Bot 的自动提交
|
||||
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
||||
|
||||
# 只在 develop 分支部署
|
||||
if: github.ref == 'refs/heads/develop'
|
||||
```
|
||||
|
||||
### 2.5 构建产物复用
|
||||
|
||||
```yaml
|
||||
# Build once, use in deploy
|
||||
- name: Build
|
||||
run: go build -o /tmp/gateway ./cmd/gateway/
|
||||
|
||||
- name: Docker Build
|
||||
run: |
|
||||
# 用已编译的二进制,不在 Docker 内重新编译
|
||||
cp /tmp/gateway docker/
|
||||
docker build -f docker/gateway.prebuilt.Dockerfile -t $IMAGE .
|
||||
```
|
||||
|
||||
### 2.6 Docker Context 瘦身
|
||||
|
||||
**问题**:`docker build` 会将整个 context 目录发送到 daemon。缺少 `.dockerignore` 时,`node_modules`(数百 MB)、`.next/`、`.git/` 等全部传入,导致 `transferring context: 768MB` 耗时 30s+。
|
||||
|
||||
**诊断**:
|
||||
```bash
|
||||
# 检查 context 大小(模拟 docker build 发送量)
|
||||
du -sh --exclude=.git <project-dir>
|
||||
|
||||
# 检查是否有 .dockerignore
|
||||
cat <project-dir>/.dockerignore 2>/dev/null || echo "缺少 .dockerignore!"
|
||||
```
|
||||
|
||||
**`/cicd analyze` 必查项**:对每个有 Dockerfile 的目录检查 `.dockerignore` 是否存在。缺失则告警。
|
||||
|
||||
**标准 .dockerignore 模板**:
|
||||
|
||||
```
|
||||
# Node.js
|
||||
node_modules
|
||||
.next
|
||||
.turbo
|
||||
coverage
|
||||
|
||||
# Common
|
||||
.git
|
||||
.gitignore
|
||||
.env*
|
||||
*.md
|
||||
.vscode
|
||||
.idea
|
||||
```
|
||||
|
||||
**效果**:Web 项目 context 从 768MB → ~10MB,Docker build 加速 10x。
|
||||
|
||||
**经验教训**:`.gitignore` 不等于 `.dockerignore`。Git 忽略的文件可能在 runner workspace 中存在(如 self-hosted runner 保留的 `node_modules` 缓存),docker build 会把它们全部打包传入。每个有 Dockerfile 的子目录**必须有 `.dockerignore`**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 故障排查
|
||||
|
||||
### 3.1 决策树
|
||||
|
||||
```
|
||||
Pipeline 失败
|
||||
├── Workflow 没触发
|
||||
│ ├── 检查 paths 过滤 → 改动不在匹配路径下
|
||||
│ ├── 检查 branch 过滤 → 分支名不匹配
|
||||
│ ├── 检查 [skip ci] → commit message 含跳过标记
|
||||
│ └── Runner 离线 → Gitea Admin > Runners 检查状态
|
||||
│
|
||||
├── Checkout 失败
|
||||
│ ├── "Authentication failed" → REPO_TOKEN secret 过期/无效
|
||||
│ ├── "Connection refused :3000" → Gitea 服务未运行
|
||||
│ └── Checkout 很慢 → 加 --depth 1 浅克隆
|
||||
│
|
||||
├── Go 构建失败
|
||||
│ ├── "module not found" → GOPROXY 设置 / go mod tidy
|
||||
│ ├── "cannot find package" → go.sum 不完整
|
||||
│ └── "go: version mismatch" → runner 上 Go 版本与 go.mod 不匹配
|
||||
│
|
||||
├── iOS 构建失败
|
||||
│ ├── "Macro must be enabled" → 加 -skipMacroValidation
|
||||
│ ├── "cannot find type" → xcodegen generate 未运行
|
||||
│ ├── "errSecInternalComponent" → unlock-keychain + set-key-partition-list
|
||||
│ ├── "No signing certificate" → Xcode > Accounts 登录下载证书
|
||||
│ ├── "Redundant Binary Upload" → 递增 CURRENT_PROJECT_VERSION
|
||||
│ └── "Missing required icon" → Assets.xcassets 缺 1024x1024 icon
|
||||
│
|
||||
├── Docker 构建失败/慢
|
||||
│ ├── "Cannot connect to daemon" → Docker Desktop 未启动
|
||||
│ ├── "unauthorized" / "denied" → docker login 凭据过期 或 ACR namespace 缺失
|
||||
│ ├── "no space left" → docker system prune
|
||||
│ ├── "transferring context: XXX MB" 很慢 → 缺少 .dockerignore(node_modules 被传入)
|
||||
│ ├── build 成功但 push denied → 镜像路径缺 namespace(registry/namespace/image)
|
||||
│ ├── docker compose pull 超时 → 不带参数会拉 Docker Hub 上的 postgres/redis,只拉业务镜像
|
||||
│ └── docker compose up -d 也会 pull → 加 `--no-deps gateway web` 只重启业务容器
|
||||
│
|
||||
└── 部署失败
|
||||
├── "Connection refused" (SSH) → 目标服务器 SSH 端口/密钥
|
||||
├── "health check failed" → 应用启动慢,增加重试等待
|
||||
├── "port already in use" → docker compose down 先停旧容器
|
||||
├── "no such service: xxx" → 服务器 compose 与 CI 配置不一致
|
||||
├── health check 失败但容器在跑 → curl URL 的端口与实际服务端口不匹配
|
||||
├── --no-deps 跳过了 nginx → health check 走 port 80 但 nginx 未启动
|
||||
├── gateway 无端口映射 → prod compose 不暴露端口,用 docker exec 检查
|
||||
└── nginx crash "upstream not allowed" → nginx.conf mount 到 /etc/nginx/nginx.conf 覆盖主配置,改 /etc/nginx/conf.d/default.conf
|
||||
```
|
||||
|
||||
### 3.2 常见错误速查
|
||||
|
||||
| 错误 | 原因 | 修复 |
|
||||
|------|------|------|
|
||||
| `errSecInternalComponent` | SSH 会话无法访问 Keychain | `security unlock-keychain` + `set-key-partition-list` |
|
||||
| `Macro "X" must be enabled` | Swift Macros 安全限制 | `-skipMacroValidation` |
|
||||
| `cannot find type 'Foo'` | xcodeproj 未包含新文件 | `xcodegen generate` |
|
||||
| `Redundant Binary Upload` | build number 重复 | 递增 `CURRENT_PROJECT_VERSION` |
|
||||
| `Cloud signing permission error` | API Key 权限不足或 Issuer ID 错误 | 用手动签名 + 本地 profile |
|
||||
| `HTTP 401 Unauthorized` (ASC API) | JWT 缺少 `kid` header | `headers={"kid": KEY_ID}` |
|
||||
| `No profiles for bundle id` | 无 distribution profile | 在 Apple Developer 创建并安装 |
|
||||
| `transferring context: 768MB` | 缺 .dockerignore | 创建 .dockerignore 排除 node_modules/.next/.git |
|
||||
| `denied: requested access` (push) | ACR 镜像路径缺 namespace | registry/**namespace**/image |
|
||||
| `docker compose pull` 超时 | 拉了 Docker Hub 的 postgres/redis | `docker compose pull gateway web` 只拉业务镜像 |
|
||||
| `docker compose up -d` 也超时 | up 隐含 pull 所有 service | `docker compose up -d --no-deps gateway web` |
|
||||
| health check 失败但容器在跑 | curl URL 端口 ≠ 服务端口 | 检查 nginx(80) vs gateway(8080),直接 `curl :8080/health` |
|
||||
| `--no-deps` 后 nginx 没启动 | nginx 被 no-deps 跳过 | 显式加 `--no-deps gateway web nginx` |
|
||||
| `no such service: xxx` | 服务器 compose 缺 service | SSH 检查实际 compose 文件 |
|
||||
| gateway healthy 但 curl 不通 | prod compose 无端口映射 | `docker exec <container> wget -q -O- localhost:8080/health` |
|
||||
| nginx `upstream not allowed` | nginx.conf mount 到 /etc/nginx/nginx.conf | 改 mount 到 `/etc/nginx/conf.d/default.conf` |
|
||||
| `missing icon file 120x120` | 无 App Icon asset | 创建 Assets.xcassets + AppIcon |
|
||||
| `UIInterfaceOrientation` iPad | 缺 iPad 方向声明 | 四方向 + `UIRequiresFullScreen` |
|
||||
|
||||
### 3.3 调试技巧
|
||||
|
||||
```bash
|
||||
# 查看 Gitea runner 状态
|
||||
curl -s -H "Authorization: token <TOKEN>" \
|
||||
http://<gitea>/api/v1/repos/<org>/<repo>/actions/runners
|
||||
|
||||
# 查看最近 workflow 运行
|
||||
curl -s -H "Authorization: token <TOKEN>" \
|
||||
http://<gitea>/api/v1/repos/<org>/<repo>/actions/runs?limit=5
|
||||
|
||||
# 本地模拟 CI 环境
|
||||
# Go
|
||||
docker run -v $(pwd):/app -w /app golang:1.25 go build ./cmd/gateway/
|
||||
|
||||
# iOS — 只能在 macOS 上
|
||||
ssh bjwework "cd ~/workspace/xiaoqu-ai/ios && swift test"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 安全
|
||||
|
||||
### 4.1 Secrets 管理
|
||||
|
||||
```bash
|
||||
# 通过 Gitea API 配置 secrets(不要手动编辑 workflow 文件)
|
||||
curl -X PUT -H "Authorization: token <ADMIN_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
"http://<gitea>/api/v1/repos/<org>/<repo>/actions/secrets/<NAME>" \
|
||||
-d '{"data": "<VALUE>"}'
|
||||
```
|
||||
|
||||
**必需 Secrets 清单**:
|
||||
|
||||
| Secret | 用途 | 轮换周期 |
|
||||
|--------|------|---------|
|
||||
| `REPO_TOKEN` | Git clone 认证 | 按需 |
|
||||
| `ACR_USERNAME` / `ACR_PASSWORD` | Docker 镜像推送 | 90 天 |
|
||||
| `SSH_PRIVATE_KEY` | 服务器部署 | 按需 |
|
||||
| `KEYCHAIN_PASSWORD` | macOS 签名解锁 | 改密码时 |
|
||||
| `ASC_KEY_ID` / `ASC_ISSUER_ID` | App Store Connect | 按需 |
|
||||
| `FEISHU_WEBHOOK` | 通知 | 不过期 |
|
||||
|
||||
### 4.2 防泄漏检查清单
|
||||
|
||||
- [ ] `.gitignore` 包含 `.env`、`*.p8`、`*.pem`、`*.mobileprovision`
|
||||
- [ ] Workflow 中无硬编码密码/token(全走 `${{ secrets.* }}`)
|
||||
- [ ] 脚本用 `${VAR:?error}` 强制要求环境变量(不用默认值暴露凭据)
|
||||
- [ ] Docker 镜像不包含 `.env` 文件(Dockerfile 有 `.dockerignore`)
|
||||
- [ ] Git remote URL 不含 token(用 secrets 注入)
|
||||
|
||||
### 4.3 提交前检查
|
||||
|
||||
```bash
|
||||
# 扫描即将提交的文件是否含密钥
|
||||
git diff --cached --name-only | xargs grep -lE \
|
||||
'(PRIVATE KEY|password|secret|token|apikey)' 2>/dev/null
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 监控
|
||||
|
||||
### 5.1 查看 Pipeline 状态
|
||||
|
||||
```bash
|
||||
# 最近运行
|
||||
curl -s -H "Authorization: token <TOKEN>" \
|
||||
"http://<gitea>/api/v1/repos/<org>/<repo>/actions/runs?limit=5" | \
|
||||
python3 -c "
|
||||
import json, sys
|
||||
for r in json.load(sys.stdin).get('workflow_runs', []):
|
||||
print(f\"{r['id']} | {r['display_title'][:40]} | {r['status']} | {r['conclusion']}\")
|
||||
"
|
||||
```
|
||||
|
||||
### 5.2 飞书通知模板
|
||||
|
||||
```yaml
|
||||
# 成功/失败通知(在 workflow 最后一步 if: always())
|
||||
- name: Notify
|
||||
if: always()
|
||||
run: |
|
||||
STATUS="${{ job.status }}"
|
||||
EMOJI=$([ "$STATUS" = "success" ] && echo "✅" || echo "❌")
|
||||
COLOR=$([ "$STATUS" = "success" ] && echo "green" || echo "red")
|
||||
cat > /tmp/notify.json << EOF
|
||||
{
|
||||
"msg_type": "interactive",
|
||||
"card": {
|
||||
"header": {
|
||||
"title": {"tag": "plain_text", "content": "$EMOJI <App> $STATUS"},
|
||||
"template": "$COLOR"
|
||||
},
|
||||
"elements": [{
|
||||
"tag": "div",
|
||||
"text": {"tag": "lark_md", "content": "**分支**: ${{ github.ref_name }}\n**提交**: ${{ github.sha }}\n**触发**: ${{ github.event.head_commit.message }}"}
|
||||
}]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
curl -s -X POST "${{ secrets.FEISHU_WEBHOOK }}" \
|
||||
-H "Content-Type: application/json" -d @/tmp/notify.json || true
|
||||
```
|
||||
|
||||
### 5.3 构建时间追踪
|
||||
|
||||
在 workflow 首尾加时间戳:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: Start Timer
|
||||
run: echo "BUILD_START=$(date +%s)" >> $GITHUB_ENV
|
||||
|
||||
# ... 构建步骤 ...
|
||||
|
||||
- name: Report Duration
|
||||
if: always()
|
||||
run: |
|
||||
DURATION=$(( $(date +%s) - $BUILD_START ))
|
||||
echo "Build duration: ${DURATION}s"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Runner 管理
|
||||
|
||||
### 6.1 Runner 类型
|
||||
|
||||
| Runner | 标签 | 用途 | 位置 |
|
||||
|--------|------|------|------|
|
||||
| xiaoqu-runner | `self-hosted` | Go + Web + Docker | 阿里云 39.104.65.241 |
|
||||
| bjwework-macos | `macos-arm64` | iOS + Swift | Tailscale 100.69.230.116 |
|
||||
|
||||
### 6.2 新增 Runner
|
||||
|
||||
```bash
|
||||
# 1. 获取注册 token
|
||||
curl -s -H "Authorization: token <ADMIN_TOKEN>" \
|
||||
"http://<gitea>/api/v1/repos/<org>/<repo>/actions/runners/registration-token"
|
||||
|
||||
# 2. 注册
|
||||
./act_runner register --no-interactive \
|
||||
--instance http://<gitea> \
|
||||
--token <TOKEN> \
|
||||
--name <NAME> \
|
||||
--labels <LABEL>:host
|
||||
|
||||
# 3. 启动(macOS 用 launchd)
|
||||
launchctl load ~/Library/LaunchAgents/com.gitea.act-runner.plist
|
||||
```
|
||||
|
||||
### 6.3 Runner 健康检查
|
||||
|
||||
```bash
|
||||
# 检查 runner 进程
|
||||
ssh bjwework "launchctl list | grep act-runner"
|
||||
|
||||
# 检查 runner 日志
|
||||
ssh bjwework "tail -20 ~/act_runner/runner.log"
|
||||
|
||||
# 检查 Gitea 上的 runner 状态
|
||||
curl -s -H "Authorization: token <TOKEN>" \
|
||||
"http://<gitea>/api/v1/repos/<org>/<repo>/actions/runners" | \
|
||||
python3 -c "import json,sys; [print(f\"{r['name']} | {r['status']}\") for r in json.load(sys.stdin)]"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Workflow 模板生成
|
||||
|
||||
### `/cicd analyze` 检查清单
|
||||
|
||||
执行时自动扫描以下项目:
|
||||
|
||||
1. **Workflow YAML** — 语法检查、路径过滤、并发取消、[skip ci]
|
||||
2. **Docker context** — 每个有 Dockerfile 的目录是否有 `.dockerignore`(**必查**)
|
||||
3. **Secrets** — workflow 中是否有硬编码凭据、路径
|
||||
4. **缓存** — 是否利用了依赖缓存(npm/Go/SPM)
|
||||
5. **浅克隆** — checkout 是否用了 `--depth 1`
|
||||
6. **镜像命名** — ACR/registry 路径是否包含 namespace
|
||||
|
||||
```bash
|
||||
# 快速扫描命令
|
||||
echo "=== .dockerignore 检查 ==="
|
||||
find . -name Dockerfile -exec sh -c 'DIR=$(dirname "{}"); [ -f "$DIR/.dockerignore" ] && echo "✅ $DIR" || echo "❌ $DIR 缺少 .dockerignore"' \;
|
||||
|
||||
echo "=== 硬编码凭据检查 ==="
|
||||
grep -rn 'password\|secret\|token' .gitea/workflows/ | grep -v 'secrets\.' | grep -v '#'
|
||||
```
|
||||
|
||||
### `/cicd template go`
|
||||
|
||||
生成 Go 后端 CI workflow,含 vet → test → build → docker → deploy。
|
||||
|
||||
### `/cicd template ios`
|
||||
|
||||
生成 iOS TestFlight workflow,含 xcodegen → test → archive → upload → notify。
|
||||
|
||||
### `/cicd template web`
|
||||
|
||||
生成 Next.js CI workflow,含 install → build → docker → deploy。
|
||||
|
||||
### `/cicd template docker`
|
||||
|
||||
生成 Docker multi-service build+push workflow,含 ACR 登录 → 多镜像构建 → SSH 部署。
|
||||
|
||||
---
|
||||
|
||||
## 8. CD 部署前验证清单
|
||||
|
||||
**每次修改 deploy 步骤前必须逐项确认:**
|
||||
|
||||
```
|
||||
1. 服务器 compose 有哪些 service?
|
||||
→ ssh <server> "docker compose -f <file> config --services"
|
||||
|
||||
2. CI deploy 启动了哪些 service?
|
||||
→ grep "up -d" .gitea/workflows/ci-cd.yml
|
||||
|
||||
3. health check URL 指向哪个端口?
|
||||
→ grep "curl.*health" .gitea/workflows/ci-cd.yml
|
||||
|
||||
4. 该端口由哪个 service 服务?
|
||||
→ port 80 = nginx, port 8080 = gateway, port 3001 = web
|
||||
|
||||
5. 该 service 是否在 deploy 启动列表中?
|
||||
→ 如果 health check 走 nginx:80,deploy 必须包含 nginx
|
||||
|
||||
6. 基础服务(postgres/redis)是否已运行?
|
||||
→ docker compose ps 检查,不要在 CI 中重启它们
|
||||
|
||||
7. Docker Hub 可达吗?
|
||||
→ 国内服务器必须配镜像源,或只拉 ACR 镜像
|
||||
```
|
||||
|
||||
**部署命令标准模板:**
|
||||
```bash
|
||||
# 只拉业务镜像(不触碰 Docker Hub)
|
||||
docker compose -f docker-compose.prod.yml pull gateway web
|
||||
|
||||
# 只重启业务容器 + nginx(不动 postgres/redis)
|
||||
docker compose -f docker-compose.prod.yml up -d --no-deps gateway web nginx
|
||||
|
||||
# 直接检查 gateway 端口(不依赖 nginx)
|
||||
sleep 10
|
||||
curl -sf http://localhost:8080/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 与其他技能的关系
|
||||
|
||||
| 技能 | 协作点 |
|
||||
|------|--------|
|
||||
| `dev-deploy` | `/deploy ios` 执行 TestFlight 部署,`/deploy docker` 执行容器部署 |
|
||||
| `dev-coding` | 开发完成后触发 CI |
|
||||
| `req` | `/req deploy` 项目级批量部署 |
|
||||
| `pull-request` | PR 触发 CI 检查 |
|
||||
| `req-test-gate` | CI 中的测试门禁 |
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "dev-coding-plugin",
|
||||
"description": "Plugin for dev-coding",
|
||||
"version": "1.0.0",
|
||||
"description": "软件编码开发技能。Go 后端 + Vue/React 前端编码实现,集成 ai-proj 任务管理。",
|
||||
"version": "2.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
|
||||
@@ -1,43 +1,24 @@
|
||||
---
|
||||
name: dev-coding
|
||||
description: 软件编码开发技能。用于代码编写、功能实现、代码审查、重构优化。集成 ai-proj CLI 进行任务管理和进度跟踪。支持 Go、Vue、React、iOS、Android、小程序等全栈开发。
|
||||
description: 软件编码开发技能。用于代码编写、功能实现、重构优化。集成 ai-proj CLI 进行任务管理和进度跟踪。核心支持 Go 后端 + Vue/React 前端开发。
|
||||
---
|
||||
|
||||
# 软件编码开发 Skill (dev-coding)
|
||||
|
||||
## ⚠️ REQ 任务自动工作流
|
||||
|
||||
**当收到 REQ 任务(包含 REQ-YYYYMMDD-XXXX)需要开发时,必须严格按以下顺序执行:**
|
||||
|
||||
1. **读取 ticket** — 从 ai-proj 获取需求详情和关联文档
|
||||
```
|
||||
mcp__ai-proj-dev__get_detailed_task_info (通过 REQ 号查找)
|
||||
mcp__ai-proj-dev__get_task_document (如果有 PRD 文档)
|
||||
```
|
||||
|
||||
2. **进入 Plan Mode** — 调用 `EnterPlanMode` 工具
|
||||
- 分析需求,探索代码库,设计实现方案
|
||||
- 输出实现计划(涉及的文件、改动范围、测试策略)
|
||||
- 等待用户审批后再开始编码
|
||||
|
||||
3. **执行计划** — 用户批准后按计划编码 + 写测试
|
||||
|
||||
**禁止跳过 plan mode 直接编码。**
|
||||
|
||||
---
|
||||
|
||||
## 概述
|
||||
|
||||
本技能用于软件编码开发工作,支持多种项目类型:
|
||||
本技能用于软件编码实现,核心覆盖:
|
||||
- Go 后端 (Gin + GORM)
|
||||
- Vue 3 / React 前端
|
||||
- iOS (Swift/SwiftUI)
|
||||
- Android (Kotlin/Jetpack Compose)
|
||||
- PDA 应用
|
||||
- MCP 桥接服务
|
||||
- 微服务架构
|
||||
|
||||
核心集成 **ai-proj CLI** 进行任务管理。
|
||||
集成 **ai-proj CLI** 进行任务管理。
|
||||
|
||||
**不包含**(由其他技能/插件负责):
|
||||
- 开发设计(API 契约、任务拆分)→ `req-design`
|
||||
- 代码评审 → `dev-review`(批次2)
|
||||
- iOS 开发 → `dev-ios`(插件)
|
||||
- Android 开发 → `dev-android`(插件)
|
||||
- MCP 开发 → `dev-mcp`(插件)
|
||||
|
||||
---
|
||||
|
||||
@@ -84,11 +65,11 @@ ai-proj task append-doc --id <taskId> --content "实现说明"
|
||||
|
||||
### 当前项目生态
|
||||
|
||||
| 项目 | 类型 | 后端 | 前端 | 移动端 |
|
||||
|------|------|------|------|--------|
|
||||
| TWMS | 仓储物流 | Go+Gin+MySQL | Vue 3 | - |
|
||||
| AI-Proj | 项目管理 | Go+Gin+PostgreSQL | React 18 | iOS+Android |
|
||||
| DICIAI | 进销存SaaS | Go+Gin+MySQL | Vue 3 | Android PDA |
|
||||
| 项目 | 类型 | 后端 | 前端 |
|
||||
|------|------|------|------|
|
||||
| TWMS | 仓储物流 | Go+Gin+MySQL | Vue 3 |
|
||||
| AI-Proj | 项目管理 | Go+Gin+PostgreSQL | React 18 |
|
||||
| DICIAI | 进销存SaaS | Go+Gin+MySQL | Vue 3 |
|
||||
|
||||
---
|
||||
|
||||
@@ -349,309 +330,6 @@ npm run test:e2e
|
||||
|
||||
---
|
||||
|
||||
## iOS 开发 (Swift/SwiftUI)
|
||||
|
||||
### 项目结构
|
||||
|
||||
```
|
||||
AI-Proj-iOS/
|
||||
├── Core/ # 核心服务
|
||||
│ ├── Network/ # 网络层
|
||||
│ ├── Storage/ # 本地存储
|
||||
│ └── Auth/ # 认证
|
||||
├── Features/ # 功能模块
|
||||
│ ├── Dashboard/
|
||||
│ ├── Tasks/
|
||||
│ └── Settings/
|
||||
├── Models/ # 数据模型
|
||||
└── UI/ # UI 组件
|
||||
```
|
||||
|
||||
### 代码规范
|
||||
|
||||
```swift
|
||||
// MVVM 架构
|
||||
class TaskViewModel: ObservableObject {
|
||||
@Published var tasks: [Task] = []
|
||||
@Published var isLoading = false
|
||||
|
||||
private let taskService: TaskServiceProtocol
|
||||
|
||||
init(taskService: TaskServiceProtocol = TaskService()) {
|
||||
self.taskService = taskService
|
||||
}
|
||||
|
||||
func fetchTasks() async {
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
tasks = try await taskService.getTasks()
|
||||
} catch {
|
||||
// 错误处理
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SwiftUI 视图
|
||||
struct TaskListView: View {
|
||||
@StateObject private var viewModel = TaskViewModel()
|
||||
|
||||
var body: some View {
|
||||
List(viewModel.tasks) { task in
|
||||
TaskRow(task: task)
|
||||
}
|
||||
.task {
|
||||
await viewModel.fetchTasks()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 构建命令
|
||||
|
||||
```bash
|
||||
# Xcode 构建
|
||||
xcodebuild -scheme AI-Proj-iOS -configuration Debug
|
||||
|
||||
# 测试
|
||||
xcodebuild test -scheme AI-Proj-iOS
|
||||
```
|
||||
|
||||
### 常见问题排查
|
||||
|
||||
#### SwiftLint 沙盒错误
|
||||
|
||||
**问题描述**:
|
||||
构建时出现错误:
|
||||
```
|
||||
Sandbox: swiftlint(xxxx) deny(1) file-read-data /path/to/.swiftlint.yml
|
||||
```
|
||||
|
||||
**原因**:
|
||||
Xcode 15+ 默认启用 User Script Sandboxing,限制脚本访问文件系统。
|
||||
|
||||
**解决方案**:
|
||||
|
||||
方案 1 - 修改项目配置(推荐):
|
||||
1. 打开 Xcode → 选择项目 → Build Settings
|
||||
2. 搜索 "User Script Sandboxing"
|
||||
3. 将 `ENABLE_USER_SCRIPT_SANDBOXING` 设置为 `NO`
|
||||
|
||||
方案 2 - 命令行构建时禁用:
|
||||
```bash
|
||||
xcodebuild -scheme AI-Proj-iOS -configuration Debug \
|
||||
ENABLE_USER_SCRIPT_SANDBOXING=NO
|
||||
```
|
||||
|
||||
方案 3 - 直接修改 project.pbxproj:
|
||||
```bash
|
||||
sed -i '' 's/ENABLE_USER_SCRIPT_SANDBOXING = YES/ENABLE_USER_SCRIPT_SANDBOXING = NO/g' \
|
||||
AI-Proj-iOS.xcodeproj/project.pbxproj
|
||||
```
|
||||
|
||||
#### Personal Development Team 功能限制
|
||||
|
||||
**问题描述**:
|
||||
使用免费 Personal Team 签名时报错:
|
||||
```
|
||||
Cannot create iOS App Development provisioning profile...
|
||||
Personal development teams do not support the Associated Domains,
|
||||
Push Notifications and App Groups capabilities.
|
||||
```
|
||||
|
||||
**原因**:
|
||||
Personal Team(免费账户)不支持以下 Entitlements:
|
||||
- Associated Domains (`com.apple.developer.associated-domains`)
|
||||
- Push Notifications (`aps-environment`)
|
||||
- App Groups (`com.apple.security.application-groups`)
|
||||
|
||||
**解决方案**:
|
||||
|
||||
1. 从 Entitlements 文件中移除不支持的功能:
|
||||
|
||||
```xml
|
||||
<!-- AI-Proj-iOS.entitlements -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- 仅保留 Personal Team 支持的功能 -->
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.yourcompany.app</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
2. Personal Team 支持的功能:
|
||||
- Keychain Access Groups ✓
|
||||
- In-App Purchase ✓
|
||||
- Game Center ✓
|
||||
|
||||
3. 需要付费 Apple Developer Program 的功能:
|
||||
- Push Notifications ✗
|
||||
- Associated Domains ✗
|
||||
- App Groups ✗
|
||||
- CloudKit ✗
|
||||
- Sign in with Apple ✗
|
||||
|
||||
---
|
||||
|
||||
## Android 开发 (Kotlin)
|
||||
|
||||
### 项目结构
|
||||
|
||||
```
|
||||
android-app/app/src/main/
|
||||
├── java/com/project/
|
||||
│ ├── ui/ # UI 层
|
||||
│ │ ├── screens/ # Compose 屏幕
|
||||
│ │ └── components/ # 可复用组件
|
||||
│ ├── data/ # 数据层
|
||||
│ │ ├── api/ # 网络接口
|
||||
│ │ ├── repository/ # 仓库模式
|
||||
│ │ └── local/ # 本地存储
|
||||
│ ├── domain/ # 业务逻辑
|
||||
│ └── di/ # 依赖注入
|
||||
└── res/ # 资源文件
|
||||
```
|
||||
|
||||
### 代码规范
|
||||
|
||||
```kotlin
|
||||
// Hilt 依赖注入
|
||||
@HiltViewModel
|
||||
class TaskViewModel @Inject constructor(
|
||||
private val taskRepository: TaskRepository
|
||||
) : ViewModel() {
|
||||
|
||||
private val _tasks = MutableStateFlow<List<Task>>(emptyList())
|
||||
val tasks: StateFlow<List<Task>> = _tasks.asStateFlow()
|
||||
|
||||
fun fetchTasks() {
|
||||
viewModelScope.launch {
|
||||
taskRepository.getTasks()
|
||||
.collect { _tasks.value = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Jetpack Compose
|
||||
@Composable
|
||||
fun TaskListScreen(
|
||||
viewModel: TaskViewModel = hiltViewModel()
|
||||
) {
|
||||
val tasks by viewModel.tasks.collectAsState()
|
||||
|
||||
LazyColumn {
|
||||
items(tasks) { task ->
|
||||
TaskItem(task = task)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 构建命令
|
||||
|
||||
```bash
|
||||
# 构建 Debug
|
||||
./gradlew assembleDebug
|
||||
|
||||
# 构建 Release
|
||||
./gradlew assembleRelease
|
||||
|
||||
# 测试
|
||||
./gradlew test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PDA 应用开发
|
||||
|
||||
### 特点
|
||||
|
||||
- Android 原生开发
|
||||
- 扫码枪集成
|
||||
- 离线优先
|
||||
- 简洁 UI
|
||||
|
||||
### 常见功能
|
||||
|
||||
```kotlin
|
||||
// 扫码处理
|
||||
class ScanReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val barcode = intent.getStringExtra("SCAN_BARCODE")
|
||||
// 处理扫码结果
|
||||
}
|
||||
}
|
||||
|
||||
// 离线存储
|
||||
@Entity(tableName = "inventory")
|
||||
data class Inventory(
|
||||
@PrimaryKey val id: Long,
|
||||
val barcode: String,
|
||||
val quantity: Int,
|
||||
@ColumnInfo(name = "sync_status")
|
||||
val syncStatus: SyncStatus = SyncStatus.PENDING
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCP 桥接开发
|
||||
|
||||
### 项目结构
|
||||
|
||||
```
|
||||
mcp-task-bridge/
|
||||
├── index.ts # 入口
|
||||
├── task-service.ts # 任务服务
|
||||
├── document-service.ts # 文档服务
|
||||
├── base-client.ts # HTTP 基类
|
||||
├── types.ts # 类型定义
|
||||
└── token-storage.ts # Token 管理
|
||||
```
|
||||
|
||||
### 代码规范
|
||||
|
||||
```typescript
|
||||
// 服务类模式
|
||||
export class TaskService extends BaseClient {
|
||||
async createTask(
|
||||
title: string,
|
||||
projectId: number = 1,
|
||||
options: CreateTaskOptions = {}
|
||||
): Promise<ApiResponse<Task>> {
|
||||
try {
|
||||
const response = await this.makeRequest<Task>(
|
||||
'POST',
|
||||
`/projects/${projectId}/tasks`,
|
||||
{ title, project_id: projectId, ...options }
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
return {
|
||||
success: true,
|
||||
data: response.data,
|
||||
message: `✅ 任务 "${title}" 创建成功`
|
||||
};
|
||||
}
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
error: `创建任务失败: ${error.message}`
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 通用开发规范
|
||||
|
||||
### API 响应格式
|
||||
@@ -695,13 +373,6 @@ try {
|
||||
} catch (error) {
|
||||
message.error(error.message);
|
||||
}
|
||||
|
||||
// Swift
|
||||
do {
|
||||
let result = try await service.fetch()
|
||||
} catch {
|
||||
// 处理错误
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
@@ -808,3 +479,66 @@ fi
|
||||
4. **小步提交** - 频繁提交,每次做一件事
|
||||
5. **测试覆盖** - 核心逻辑必须有测试
|
||||
6. **文档同步** - 代码变更同步更新文档
|
||||
|
||||
## 相关技能
|
||||
|
||||
| 技能 | 用途 |
|
||||
|------|------|
|
||||
| `req-design` | 开发设计(API 契约、任务拆分)— dev-coding 的输入 |
|
||||
| `dev-test` | 测试与质量门禁 |
|
||||
| `dev-review` | 代码评审(五视角扫描)|
|
||||
| `dev-ios` | iOS 开发(插件,按需加载)|
|
||||
| `dev-android` | Android 开发(插件,按需加载)|
|
||||
| `dev-mcp` | MCP bridge 开发(插件,按需加载)|
|
||||
|
||||
---
|
||||
|
||||
## CLAUDE.md 架构检查机制(REQ-20260416-0017 P0-5)
|
||||
|
||||
**原则:本 skill 不硬编码任何项目的架构细节,从项目 CLAUDE.md 读取**。
|
||||
|
||||
### 为什么
|
||||
|
||||
同一套 skill 要支持多个技术栈(Go+Gin / React+AntD / Vue+Element / Python+FastAPI)。如果把分层、命名、目录结构写死在 SKILL.md 里,跨项目就会冲突。
|
||||
|
||||
devflow-claude 的做法(借鉴):skill 只管**流程和模板**,项目架构由 CLAUDE.md 的 "Architecture" / "项目架构" 章节定义。
|
||||
|
||||
### 执行前检查
|
||||
|
||||
开始编码任务前,skill 先检查项目根 `CLAUDE.md`:
|
||||
|
||||
```bash
|
||||
# 检查 CLAUDE.md 是否含架构关键词
|
||||
if [ -f "CLAUDE.md" ]; then
|
||||
if grep -qiE "(架构|分层|目录结构|tech stack|architecture|project structure)" CLAUDE.md; then
|
||||
echo "✅ 检测到项目架构信息"
|
||||
else
|
||||
echo "⚠️ CLAUDE.md 缺少架构描述"
|
||||
echo " dev-coding 需要架构信息来生成准确的文件路径和分层顺序"
|
||||
echo ""
|
||||
echo " 📋 建议操作:"
|
||||
echo " - 查看预置架构片段: ai-proj-helper/skills-dev/dev-coding-plugin/templates/claude-md-snippets/"
|
||||
echo " - 选择匹配技术栈的片段,补充到 CLAUDE.md 的 '## Architecture' 章节"
|
||||
echo ""
|
||||
echo " ⚠️ 继续执行,但生成的文件路径可能不够准确"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ 未找到项目 CLAUDE.md,建议创建"
|
||||
fi
|
||||
```
|
||||
|
||||
### 架构片段模板库
|
||||
|
||||
位于 `skills-dev/dev-coding-plugin/templates/claude-md-snippets/`:
|
||||
|
||||
| 文件 | 适用场景 |
|
||||
|------|---------|
|
||||
| `go-gin-gorm.md` | Go + Gin + GORM 后端(ai-proj backend 风格) |
|
||||
| `react-antd.md` | React + TypeScript + Ant Design(ai-proj frontend 风格) |
|
||||
| `vue-element.md` | Vue 3 + Element Plus(coolbuy-paas 风格) |
|
||||
| `mcp-typescript.md` | MCP bridge TypeScript(mcp-task-bridge 风格) |
|
||||
| `generic.md` | 通用空白骨架 |
|
||||
|
||||
### 非阻断原则
|
||||
|
||||
架构信息缺失时**仅警告不阻止**。用户仍可继续,但会被告知"生成的建议可能不够准确"。
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节,按实际情况填写 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 技术栈
|
||||
|
||||
- **语言**: _TODO_
|
||||
- **框架**: _TODO_
|
||||
- **数据库**: _TODO_
|
||||
- **缓存**: _TODO_
|
||||
- **部署**: _TODO_
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
project-root/
|
||||
├── ???/ # _TODO: 说明_
|
||||
├── ???/ # _TODO_
|
||||
└── ???/
|
||||
```
|
||||
|
||||
### 分层 / 模块规则
|
||||
|
||||
1. _TODO: 依赖方向_
|
||||
2. _TODO: 允许/禁止的跨层调用_
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| _TODO_ | _TODO_ | _TODO_ |
|
||||
|
||||
### 错误处理
|
||||
|
||||
_TODO_
|
||||
|
||||
### 日志
|
||||
|
||||
_TODO_
|
||||
|
||||
### 测试
|
||||
|
||||
_TODO_
|
||||
|
||||
### 其他关键约定
|
||||
|
||||
- _TODO_
|
||||
@@ -0,0 +1,56 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 分层结构(Go + Gin + GORM)
|
||||
|
||||
```
|
||||
backend/
|
||||
├── routes/ # HTTP 路由定义(按模块拆分)
|
||||
├── handlers/ # 请求解析 + 响应组装(薄层,不含业务)
|
||||
├── services/ # 业务逻辑(事务、组合、校验)
|
||||
├── models/ # GORM 数据模型
|
||||
├── database/ # Repository 层(SQL、查询)
|
||||
├── middleware/ # 认证、CORS、日志、限流
|
||||
├── migrations/ # SQL 迁移文件
|
||||
└── utils/ # 通用工具(密码、签名等)
|
||||
```
|
||||
|
||||
### 分层规则(强制)
|
||||
|
||||
1. **请求流向**:Route → Handler → Service → Database → Models
|
||||
2. **Handler 禁止直接访问 database**:必须走 Service 层
|
||||
3. **Service 禁止调用 Handler 或 Route**:单向依赖
|
||||
4. **Model 仅定义结构 + GORM tag**:不含业务方法
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| 文件名 | snake_case | `user_service.go` |
|
||||
| 包名 | lowercase | `services`, `handlers` |
|
||||
| 导出函数/类型 | PascalCase | `CreateUser`, `UserRepository` |
|
||||
| 内部函数 | camelCase | `validatePassword` |
|
||||
| 常量 | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` |
|
||||
|
||||
### 错误处理
|
||||
|
||||
- 使用 `errors.New()` 或自定义 error type
|
||||
- Handler 层统一返回 `{"code": X, "msg": "...", "data": ...}`
|
||||
- Service 层返回原始 error,由 Handler 转换
|
||||
|
||||
### 日志
|
||||
|
||||
- 使用结构化 log:`log.WithField("user_id", uid).Info("...")`
|
||||
- 禁用 `fmt.Println` / `print`
|
||||
|
||||
### 测试
|
||||
|
||||
- 单元测试文件名:`xxx_test.go`
|
||||
- 使用 `testify/assert`
|
||||
- Mock 用 `testify/mock` 或 `gomock`
|
||||
|
||||
### 依赖检查
|
||||
|
||||
- **新 handler 禁止直接 `import database/`**:需走 Service 层
|
||||
- `./scripts/check-architecture.sh check` 作为 CI 门禁
|
||||
@@ -0,0 +1,75 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 目录结构(MCP Bridge - TypeScript)
|
||||
|
||||
```
|
||||
mcp-task-bridge/
|
||||
├── src/
|
||||
│ ├── tools/ # MCP tool 定义(每个工具一个文件)
|
||||
│ ├── resources/ # MCP resources(若有)
|
||||
│ ├── prompts/ # MCP prompts(若有)
|
||||
│ ├── client/ # 后端 REST API 客户端
|
||||
│ ├── utils/ # 工具函数
|
||||
│ └── index.ts # 入口
|
||||
├── tests/
|
||||
└── dist/ # 编译产物(不提交)
|
||||
```
|
||||
|
||||
### 工具定义规范
|
||||
|
||||
每个 MCP tool 一个文件:
|
||||
|
||||
```typescript
|
||||
// tools/create-task.ts
|
||||
export const createTaskTool: Tool = {
|
||||
name: 'create_task',
|
||||
description: '...',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { ... },
|
||||
required: [...]
|
||||
}
|
||||
};
|
||||
|
||||
export async function handleCreateTask(args) { ... }
|
||||
```
|
||||
|
||||
### 后端 API 调用
|
||||
|
||||
- 所有 REST 请求通过 `src/client/api.ts` 统一封装
|
||||
- 认证头由 client 自动附加(不在 tool 里处理)
|
||||
- 错误统一转成 MCP error response
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| MCP tool name | snake_case | `create_task`, `list_requirements` |
|
||||
| 文件名 | kebab-case | `create-task.ts` |
|
||||
| 函数名 | camelCase | `handleCreateTask` |
|
||||
| Tool 变量 | camelCase + `Tool` | `createTaskTool` |
|
||||
|
||||
### 构建与部署
|
||||
|
||||
- `npm run build` → `dist/`
|
||||
- **修改代码后必须重新 build**:`pkill -f mcp-task-bridge/dist/index.js` 重启 MCP server
|
||||
- 不能直接运行 TypeScript 源码
|
||||
|
||||
### 环境配置
|
||||
|
||||
- `dev` 环境:`ai-proj-dev` MCP server
|
||||
- `prod` 环境:`ai-proj-prod` MCP server
|
||||
- 禁止跨环境传数据(dev 需求不能关联 prod 任务)
|
||||
|
||||
### 测试
|
||||
|
||||
- Jest + ts-jest
|
||||
- 集成测试模拟真实 MCP 协议
|
||||
|
||||
### 常见错误
|
||||
|
||||
- **Rule 1**: MCP 端点必须 `/api/v1/mcp/` 前缀
|
||||
- **Rule 2**: 修改后必须 rebuild + 重启
|
||||
- **Rule 3**: 环境隔离(dev / prod)
|
||||
@@ -0,0 +1,78 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 目录结构(React + TypeScript + Ant Design)
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── pages/ # 页面级组件(路由对应)
|
||||
├── components/ # 可复用 UI 组件
|
||||
├── services/ # API 客户端(Axios 封装)
|
||||
├── hooks/ # 自定义 React Hooks
|
||||
├── contexts/ # Context Providers(auth, timer 等)
|
||||
├── utils/ # 工具函数(auth, validation, date 等)
|
||||
├── types/ # TypeScript 类型定义
|
||||
└── config/ # Feature flags, 性能配置
|
||||
```
|
||||
|
||||
### 状态管理
|
||||
|
||||
| 状态类型 | 方案 |
|
||||
|---------|------|
|
||||
| 服务器状态 | React Query (TanStack Query) |
|
||||
| 全局状态 | Context API |
|
||||
| 本地状态 | useState / useReducer |
|
||||
| 表单状态 | Ant Design Form |
|
||||
|
||||
**禁止**:Redux / MobX(本项目不使用)
|
||||
|
||||
### 路由
|
||||
|
||||
- React Router v6
|
||||
- 路由定义集中在 `src/routes/`
|
||||
- 懒加载:`const Page = lazy(() => import(...))`
|
||||
|
||||
### API 调用
|
||||
|
||||
- 使用 `services/` 下的封装函数,不要在组件里直接 `axios.get`
|
||||
- 响应类型必须有 TypeScript interface
|
||||
- 错误统一由 axios 拦截器处理
|
||||
|
||||
### 样式
|
||||
|
||||
- Ant Design 组件 + CSS Module
|
||||
- 禁止内联 `style={{ ... }}` 用于复杂样式
|
||||
- 全局变量走 CSS Variables
|
||||
|
||||
### Modal 安全规则(重要)
|
||||
|
||||
`Modal.success/info/warning/error` 是非阻塞调用,后续 UI 操作必须放在 `onOk` 回调中:
|
||||
|
||||
```tsx
|
||||
// WRONG
|
||||
Modal.success({ title: '成功' });
|
||||
setNextModalOpen(true); // 立即执行,两个 modal 冲突
|
||||
|
||||
// CORRECT
|
||||
Modal.success({
|
||||
title: '成功',
|
||||
onOk: () => setNextModalOpen(true),
|
||||
});
|
||||
```
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| 组件文件 | PascalCase | `UserProfile.tsx` |
|
||||
| Hook 文件 | camelCase | `useAuth.ts` |
|
||||
| 工具文件 | kebab-case | `date-utils.ts` |
|
||||
| 组件名 | PascalCase | `UserProfile` |
|
||||
| Hook 名 | `use` 前缀 | `useAuth` |
|
||||
|
||||
### 测试
|
||||
|
||||
- 单测:Jest + React Testing Library
|
||||
- E2E:Playwright
|
||||
- 测试文件:`xxx.test.tsx` 与源文件同目录
|
||||
@@ -0,0 +1,67 @@
|
||||
<!-- 复制此片段到项目根 CLAUDE.md 的 "## Architecture" 章节 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
### 目录结构(Vue 3 + TypeScript + Element Plus)
|
||||
|
||||
```
|
||||
src/
|
||||
├── views/ # 页面级组件(路由对应)
|
||||
├── components/ # 可复用组件
|
||||
├── api/ # API 封装
|
||||
├── stores/ # Pinia stores
|
||||
├── composables/ # 组合式函数(use* hooks)
|
||||
├── utils/ # 工具函数
|
||||
├── types/ # TypeScript 类型定义
|
||||
└── router/ # Vue Router 配置
|
||||
```
|
||||
|
||||
### 状态管理
|
||||
|
||||
- **Pinia**(官方推荐)
|
||||
- 每个业务模块一个 store:`stores/user.ts`、`stores/order.ts`
|
||||
- 禁止直接在组件里写持久状态
|
||||
|
||||
### 路由
|
||||
|
||||
- Vue Router 4
|
||||
- 路由守卫统一在 `router/guards.ts`
|
||||
- 懒加载:`component: () => import('@/views/...')`
|
||||
|
||||
### Composition API
|
||||
|
||||
- **强制使用 `<script setup>`**,禁止 Options API
|
||||
- Props 用 `defineProps<T>()`,Emits 用 `defineEmits<T>()`
|
||||
|
||||
### API 调用
|
||||
|
||||
- `api/` 下按模块划分:`api/user.ts`、`api/order.ts`
|
||||
- 每个函数返回类型明确
|
||||
- 错误由 axios 拦截器统一处理
|
||||
|
||||
### 命名规范
|
||||
|
||||
| 类型 | 约定 | 示例 |
|
||||
|------|------|------|
|
||||
| 组件文件 | PascalCase | `UserProfile.vue` |
|
||||
| Composable | camelCase + use 前缀 | `useAuth.ts` |
|
||||
| Store | camelCase | `useUserStore` |
|
||||
| API 文件 | kebab-case | `user-api.ts` |
|
||||
| 工具函数 | camelCase | `formatDate` |
|
||||
|
||||
### 样式
|
||||
|
||||
- SCSS + Element Plus 主题
|
||||
- scoped style(避免全局污染)
|
||||
- 全局变量走 SCSS 变量或 CSS Variables
|
||||
|
||||
### 国际化
|
||||
|
||||
- 使用 vue-i18n
|
||||
- 消息文件:`src/locales/zh.json` / `en.json`
|
||||
- 禁止硬编码文本:用 `t('path.to.key')`
|
||||
|
||||
### 测试
|
||||
|
||||
- 单测:Vitest + Vue Test Utils
|
||||
- E2E:Playwright / Cypress
|
||||
8
skills-dev/dev-commit-plugin/.claude-plugin/plugin.json
Normal file
8
skills-dev/dev-commit-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "dev-commit-plugin",
|
||||
"description": "智能 /commit 命令:分支保护 + 自动建功能分支 + Conventional Commits 生成",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
144
skills-dev/dev-commit-plugin/skills/SKILL.md
Normal file
144
skills-dev/dev-commit-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
name: dev-commit
|
||||
description: 智能 git commit — 分支保护 + 自动建功能分支 + Conventional Commits 生成。当用户说"提交代码"、"commit"、"/commit"、"保存修改"时自动激活。
|
||||
---
|
||||
|
||||
# dev-commit Skill — 智能提交
|
||||
|
||||
借鉴自 devflow-claude `/req:commit`。源自 REQ-20260416-0017 P0-6。
|
||||
|
||||
## 核心功能
|
||||
|
||||
**用户说"/commit" 或 "提交代码" 时执行:**
|
||||
|
||||
1. **分支合规检查**
|
||||
2. **自动建功能分支**(若在保护分支)
|
||||
3. **Conventional Commits 生成**
|
||||
4. **自动关联 REQ-XXX**
|
||||
|
||||
## 流程详解
|
||||
|
||||
### 1. 分支合规检查(强制)
|
||||
|
||||
```bash
|
||||
CURRENT_BRANCH=$(git symbolic-ref --short HEAD)
|
||||
|
||||
# 保护分支名单
|
||||
PROTECTED_BRANCHES="main master develop production"
|
||||
|
||||
for b in $PROTECTED_BRANCHES; do
|
||||
if [ "$CURRENT_BRANCH" = "$b" ]; then
|
||||
IS_PROTECTED=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
**铁律**:**绝对禁止**在 main / develop / master / production 分支上直接 commit。
|
||||
|
||||
### 2. 保护分支上有改动 → 自动建功能分支
|
||||
|
||||
```
|
||||
检测到 main/develop 有未提交改动:
|
||||
|
||||
推荐方案:
|
||||
1. 自动建分支 feat/xxx 并切换过去(推荐)
|
||||
2. 取消本次 commit,手动切分支
|
||||
3. 临时 stash 后切分支
|
||||
|
||||
【默认选 1】
|
||||
```
|
||||
|
||||
**分支命名自动推断:**
|
||||
- 从对话上下文:如果刚在讨论 "REQ-20260416-0017" → `feat/REQ-20260416-0017-summary`
|
||||
- 从文件变更:扫描 staged files 的路径 → 推断模块(如 `backend/services/user/` → `feat/user-xxx`)
|
||||
- 从 commit message 意图:如果意图是 fix → `fix/xxx`
|
||||
|
||||
**前缀规则:**
|
||||
- `feat/` — 新功能
|
||||
- `fix/` — bug 修复
|
||||
- `chore/` — 杂项(依赖升级、CI 调整等)
|
||||
- `refactor/` — 重构
|
||||
- `docs/` — 文档
|
||||
|
||||
### 3. Conventional Commits 生成
|
||||
|
||||
**格式:**
|
||||
```
|
||||
<type>(<scope>): <description> [(REQ-XXX)] [closes #N]
|
||||
```
|
||||
|
||||
**type:**
|
||||
- feat - 新功能
|
||||
- fix - 修复
|
||||
- chore - 杂项
|
||||
- refactor - 重构
|
||||
- docs - 文档
|
||||
- test - 测试
|
||||
- perf - 性能
|
||||
- style - 格式
|
||||
|
||||
**scope:**
|
||||
- 从修改的文件路径推断:`backend/services/user/` → `user`
|
||||
- `frontend/src/pages/login/` → `login`
|
||||
|
||||
**示例:**
|
||||
```
|
||||
feat(mcp): 新增 set_requirement_reviewers 工具 (REQ-20260415-0023)
|
||||
fix(frontend): 403 权限重载死循环
|
||||
chore(cicd): 精简 CI/CD 流程,移除 Staging 环境 (REQ-20260415-0004) closes #242
|
||||
```
|
||||
|
||||
### 4. REQ-XXX 自动关联
|
||||
|
||||
**查找顺序:**
|
||||
1. 当前分支名:`feat/REQ-20260416-0017-xxx` → 提取 `REQ-20260416-0017`
|
||||
2. 最近对话中提到的 REQ ID
|
||||
3. MCP 查询当前用户的"进行中"需求(如只有一个,直接用)
|
||||
|
||||
**分支名含 `-iN` 时追加 `closes #N`:**
|
||||
```
|
||||
feat/REQ-xxx-i42 + commit → commit message 自动加 "closes #42"
|
||||
```
|
||||
|
||||
### 5. 提交确认
|
||||
|
||||
提交前展示预览:
|
||||
```
|
||||
即将执行:
|
||||
分支: feat/mcp-set-reviewers
|
||||
Files: backend/mcp/tools/set_reviewers.go, mcp-task-bridge/src/tools/set-reviewers.ts
|
||||
Message:
|
||||
feat(mcp): 新增 set_requirement_reviewers 工具 (REQ-20260415-0023)
|
||||
|
||||
确认提交?(y/n)
|
||||
```
|
||||
|
||||
## 与 ai-proj 集成
|
||||
|
||||
- **查询当前需求**:通过 MCP `mcp__ai-proj__find_requirement` 或 `list_requirements` 找 user 进行中的
|
||||
- **commit 后可选**:调用 `mcp__ai-proj__update_task` 更新关联任务进度
|
||||
|
||||
## 排除项
|
||||
|
||||
本 skill **不做**:
|
||||
- 推送远程(留给 `/pr` 或 `git push`)
|
||||
- 创建 PR(留给 pull-request skill)
|
||||
- 代码评审(留给 dev-review skill)
|
||||
|
||||
职责边界清晰,防止单命令膨胀。
|
||||
|
||||
## 风险控制
|
||||
|
||||
1. **保护分支改动不得 commit** — 强制拦截
|
||||
2. **message 必须用 Conventional Commits** — 后续 changelog 依赖
|
||||
3. **REQ-XXX 关联尽量自动推断** — 但推断不出不阻断
|
||||
|
||||
## 安装方式
|
||||
|
||||
本 skill 自动随 ai-proj-helper marketplace 加载。用户说"/commit" 即激活。
|
||||
|
||||
## 参考
|
||||
|
||||
- devflow-claude: `plugins/req/commands/commit.md`
|
||||
- REQ-20260416-0017 P0-6
|
||||
8
skills-dev/dev-deploy-plugin/.claude-plugin/plugin.json
Normal file
8
skills-dev/dev-deploy-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "dev-deploy-plugin",
|
||||
"description": "Plugin for dev-deploy",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
776
skills-dev/dev-deploy-plugin/skills/SKILL.md
Normal file
776
skills-dev/dev-deploy-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,776 @@
|
||||
---
|
||||
name: dev-deploy
|
||||
description: 应用部署技能。支持 iOS TestFlight、Docker 容器等多平台部署。当用户提到部署、发布、TestFlight、上架、build、archive 相关任务时自动激活。
|
||||
---
|
||||
|
||||
# 应用部署 Skill (dev-deploy)
|
||||
|
||||
## 概述
|
||||
|
||||
管理应用从构建到发布的完整部署流程,支持多平台:
|
||||
- **iOS**: TestFlight 内测 / App Store 发布
|
||||
- **Docker**: Staging / Production 容器部署
|
||||
|
||||
集成 ai-proj 任务系统进行部署记录和需求阶段推进。
|
||||
|
||||
---
|
||||
|
||||
## 命令参考
|
||||
|
||||
| 命令 | 说明 |
|
||||
|------|------|
|
||||
| `/deploy ios` | iOS TestFlight 部署 |
|
||||
| `/deploy docker [staging\|prod]` | Docker 容器部署 |
|
||||
| `/deploy status` | 查看部署状态 |
|
||||
|
||||
---
|
||||
|
||||
## iOS TestFlight 部署
|
||||
|
||||
### 前置条件
|
||||
|
||||
| 项目 | 要求 |
|
||||
|------|------|
|
||||
| 构建机器 | macOS + Xcode(通过 SSH 访问) |
|
||||
| 签名证书 | Apple Distribution 证书已安装在 Keychain |
|
||||
| Provisioning Profile | App Store Distribution profile 已安装 |
|
||||
| API Key | App Store Connect API Key (.p8) |
|
||||
| sshpass | 本机安装用于非交互 SSH(`brew install hudochenkov/sshpass/sshpass`) |
|
||||
| xcodegen | 构建机器安装用于从 project.yml 生成 xcodeproj |
|
||||
|
||||
### 完整部署流程
|
||||
|
||||
```
|
||||
1. git push → 代码推送到远程仓库
|
||||
2. SSH 连接构建机 → git pull 拉取最新代码
|
||||
3. xcodebuild archive → 无签名构建 Archive
|
||||
4. xcodebuild -exportArchive → Distribution 签名 + 上传 TestFlight
|
||||
5. ASC API 补全 → 合规信息 + 测试说明 + build 关联版本
|
||||
6. 验证 → 确认 TestFlight 状态为 IN_BETA_TESTING
|
||||
```
|
||||
|
||||
### Step 1: SSH 连接构建机
|
||||
|
||||
```bash
|
||||
# 使用 sshpass 进行非交互 SSH
|
||||
sshpass -p '<password>' ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no <user>@<host> '<command>'
|
||||
```
|
||||
|
||||
**关键经验**:
|
||||
- SSH 远程 codesign 需要先**解锁 Keychain**,否则报 `errSecInternalComponent`
|
||||
- 还需要 `set-key-partition-list` 授权 codesign 访问密钥
|
||||
|
||||
```bash
|
||||
# 必须在每次 SSH 会话开头执行
|
||||
security unlock-keychain -p "<password>" ~/Library/Keychains/login.keychain-db
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "<password>" ~/Library/Keychains/login.keychain-db
|
||||
```
|
||||
|
||||
### Step 2: 拉取代码
|
||||
|
||||
```bash
|
||||
cd <repo-path> && git pull origin develop
|
||||
```
|
||||
|
||||
### Step 3: Archive(无签名)
|
||||
|
||||
**关键经验**:Archive 阶段**不要签名**。原因:
|
||||
- xcodebuild CLI 签名参数会泄漏到 SPM 依赖的 targets,导致 "does not support provisioning profiles" 错误
|
||||
- 正确做法是 archive 时禁用签名,在 export 阶段单独签名
|
||||
|
||||
```bash
|
||||
xcodebuild archive \
|
||||
-project XiaoquCRM.xcodeproj \
|
||||
-scheme XiaoquCRM \
|
||||
-destination 'generic/platform=iOS' \
|
||||
-configuration Release \
|
||||
-archivePath ~/Desktop/XiaoquCRM.xcarchive \
|
||||
-skipMacroValidation \
|
||||
CODE_SIGNING_ALLOWED=NO \
|
||||
CODE_SIGNING_REQUIRED=NO
|
||||
```
|
||||
|
||||
**常见错误及解决**:
|
||||
|
||||
| 错误 | 原因 | 解决 |
|
||||
|------|------|------|
|
||||
| `Macro "X" must be enabled` | Swift Macros 安全限制 | 加 `-skipMacroValidation` |
|
||||
| `cannot find type 'AdminFeature'` | xcodeproj 未包含新文件 | 运行 `xcodegen generate` 重新生成 |
|
||||
| SPM 依赖报签名错误 | 签名参数泄漏到依赖 | Archive 用 `CODE_SIGNING_ALLOWED=NO` |
|
||||
|
||||
### Step 4: Export + 上传 TestFlight
|
||||
|
||||
```bash
|
||||
# ExportOptions.plist(提前创建在构建机上)
|
||||
cat > /tmp/ExportOptions.plist << EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>app-store-connect</string>
|
||||
<key>destination</key>
|
||||
<string>upload</string>
|
||||
<key>teamID</key>
|
||||
<string>{TEAM_ID}</string>
|
||||
<key>signingStyle</key>
|
||||
<string>manual</string>
|
||||
<key>signingCertificate</key>
|
||||
<string>Apple Distribution</string>
|
||||
<key>provisioningProfiles</key>
|
||||
<dict>
|
||||
<key>{BUNDLE_ID}</key>
|
||||
<string>{PROFILE_NAME}</string>
|
||||
</dict>
|
||||
<key>manageAppVersionAndBuildNumber</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
# Export + Upload
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath ~/Desktop/XiaoquCRM.xcarchive \
|
||||
-exportOptionsPlist /tmp/ExportOptions.plist \
|
||||
-exportPath ~/Desktop/XiaoquCRM-export \
|
||||
-authenticationKeyPath {API_KEY_PATH} \
|
||||
-authenticationKeyID {KEY_ID} \
|
||||
-authenticationKeyIssuerID {ISSUER_ID}
|
||||
```
|
||||
|
||||
**关键经验**:
|
||||
|
||||
| 问题 | 教训 |
|
||||
|------|------|
|
||||
| `errSecInternalComponent` | SSH 远程签名前必须 `unlock-keychain` + `set-key-partition-list` |
|
||||
| `No signing certificate "iOS Distribution"` | 机器上没装 Distribution 证书,需在 Xcode > Accounts 登录 Apple ID 下载 |
|
||||
| `Redundant Binary Upload` | build number 重复,需要在 project.yml 递增 `CURRENT_PROJECT_VERSION` |
|
||||
| `Missing required icon file` | 需要 Assets.xcassets/AppIcon.appiconset 含 1024x1024 PNG |
|
||||
| `UIInterfaceOrientation` iPad 错误 | 必须声明 iPad 四方向支持,或设置 `UIRequiresFullScreen=true` |
|
||||
| `Cloud signing permission error` | API Key 权限不够或 Issuer ID 错误;改用手动签名 + 本地 profile |
|
||||
|
||||
### Step 5: ASC API 补全 TestFlight 信息
|
||||
|
||||
上传成功后,需要通过 App Store Connect API 补全三项信息,否则测试者收不到通知或无法安装:
|
||||
|
||||
#### 5.1 生成 JWT Token
|
||||
|
||||
```python
|
||||
import jwt, time
|
||||
key = open("AuthKey_XXXXXX.p8").read()
|
||||
token = jwt.encode(
|
||||
{"iss": "{ISSUER_ID}", "iat": int(time.time()),
|
||||
"exp": int(time.time()) + 1200, "aud": "appstoreconnect-v1"},
|
||||
key, algorithm="ES256",
|
||||
headers={"kid": "{KEY_ID}"} # ← 必须包含 kid!
|
||||
)
|
||||
```
|
||||
|
||||
**关键经验**:JWT 必须包含 `headers={"kid": KEY_ID}`,否则 401 认证失败。还需要安装 `cryptography` 库支持 ES256。
|
||||
|
||||
#### 5.2 设置出口合规
|
||||
|
||||
```
|
||||
PATCH /v1/builds/{build_id}
|
||||
{"data": {"type": "builds", "id": "{build_id}",
|
||||
"attributes": {"usesNonExemptEncryption": false}}}
|
||||
```
|
||||
|
||||
不设置此项,build 会卡在 "Missing Compliance" 状态,内部测试者无法安装。
|
||||
|
||||
#### 5.3 填写测试说明 (whatsNew)
|
||||
|
||||
```
|
||||
# 先获取 localization ID
|
||||
GET /v1/builds/{build_id}/betaBuildLocalizations
|
||||
|
||||
# 更新 whatsNew
|
||||
PATCH /v1/betaBuildLocalizations/{loc_id}
|
||||
{"data": {"type": "betaBuildLocalizations", "id": "{loc_id}",
|
||||
"attributes": {"whatsNew": "更新内容..."}}}
|
||||
```
|
||||
|
||||
#### 5.4 关联 Build 到 App Store 版本
|
||||
|
||||
**关键经验**:App Store Connect 页面的 App Icon 来自关联的 build。如果没有把 build 关联到 App Store 版本,图标显示为空。
|
||||
|
||||
```
|
||||
# 关联 build 到版本
|
||||
PATCH /v1/appStoreVersions/{version_id}/relationships/build
|
||||
{"data": {"type": "builds", "id": "{build_id}"}}
|
||||
```
|
||||
|
||||
### Step 6: 验证部署状态
|
||||
|
||||
```python
|
||||
# 检查 build 状态
|
||||
GET /v1/builds/{build_id}?include=buildBetaDetail
|
||||
|
||||
# 期望结果:
|
||||
# processingState: VALID
|
||||
# internalBuildState: IN_BETA_TESTING
|
||||
# usesNonExemptEncryption: false
|
||||
```
|
||||
|
||||
### 一键部署脚本模板
|
||||
|
||||
将以上步骤整合为单次 SSH 调用:
|
||||
|
||||
```bash
|
||||
sshpass -p '<password>' ssh -o PreferredAuthentications=password \
|
||||
-o PubkeyAuthentication=no <user>@<host> '
|
||||
# 0. Keychain
|
||||
security unlock-keychain -p "<password>" ~/Library/Keychains/login.keychain-db
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "<password>" ~/Library/Keychains/login.keychain-db 2>/dev/null
|
||||
|
||||
# 1. Pull
|
||||
cd <repo> && git pull origin develop
|
||||
|
||||
# 2. Archive
|
||||
cd ios && rm -rf ~/Desktop/App.xcarchive ~/Desktop/App-export
|
||||
xcodebuild archive -project App.xcodeproj -scheme App \
|
||||
-destination "generic/platform=iOS" -configuration Release \
|
||||
-archivePath ~/Desktop/App.xcarchive \
|
||||
-skipMacroValidation CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO \
|
||||
2>&1 | tail -1
|
||||
|
||||
# 3. Export + Upload
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath ~/Desktop/App.xcarchive \
|
||||
-exportOptionsPlist /tmp/ExportOptions.plist \
|
||||
-exportPath ~/Desktop/App-export \
|
||||
-authenticationKeyPath <key_path> \
|
||||
-authenticationKeyID <key_id> \
|
||||
-authenticationKeyIssuerID <issuer_id> \
|
||||
2>&1 | grep -E "Upload|EXPORT|error:" | tail -5
|
||||
'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## iOS 部署检查清单
|
||||
|
||||
部署前逐项确认:
|
||||
|
||||
- [ ] build number 已递增(`CURRENT_PROJECT_VERSION` in project.yml)
|
||||
- [ ] `xcodegen generate` 已运行(新文件已包含在 xcodeproj 中)
|
||||
- [ ] 代码已 push 到远程仓库
|
||||
- [ ] 构建机可 SSH 访问
|
||||
- [ ] Assets.xcassets 包含 1024x1024 App Icon
|
||||
- [ ] Info.plist 包含 iPad 四方向支持
|
||||
- [ ] Distribution 证书已安装在构建机 Keychain
|
||||
|
||||
部署后逐项确认:
|
||||
|
||||
- [ ] Archive 成功
|
||||
- [ ] Export + Upload 成功
|
||||
- [ ] 合规信息已设置(usesNonExemptEncryption)
|
||||
- [ ] 测试说明已填写(whatsNew)
|
||||
- [ ] Build 已关联到 App Store 版本
|
||||
- [ ] TestFlight 状态为 IN_BETA_TESTING
|
||||
- [ ] 测试者收到更新通知
|
||||
|
||||
---
|
||||
|
||||
## Docker Staging/Production 部署
|
||||
|
||||
### 架构概览
|
||||
|
||||
```
|
||||
develop push → Build Image → Push ACR → SSH Deploy (staging) → Health Check
|
||||
main push → Build Image → Push ACR → 人工审批 → SSH Deploy (prod) → Health Check
|
||||
```
|
||||
|
||||
| 组件 | 说明 |
|
||||
|------|------|
|
||||
| 服务器 | 39.104.87.246(阿里云 ECS) |
|
||||
| Registry | Aliyun ACR: `crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com` |
|
||||
| 镜像 | `xiaoqu-gateway`, `xiaoqu-web` |
|
||||
| SSH Key | `~/.ssh/xiaoqu.pem` |
|
||||
| 部署方式 | Docker Compose |
|
||||
|
||||
### 完整部署流程
|
||||
|
||||
```
|
||||
1. 本地构建镜像 → docker build -t <image>:<tag>
|
||||
2. 推送到 ACR → docker push <registry>/<image>:<tag>
|
||||
3. SSH 到服务器 → docker compose pull + up -d
|
||||
4. 健康检查 → curl /health
|
||||
5. 通知 → 飞书 Webhook 发送部署结果
|
||||
```
|
||||
|
||||
### Staging 部署(develop 分支自动触发)
|
||||
|
||||
Push 到 `develop` 分支自动触发 staging 部署。流程:
|
||||
|
||||
```bash
|
||||
# 1. 构建镜像(tag 用 commit SHA 前 8 位)
|
||||
TAG=$(git rev-parse --short=8 HEAD)
|
||||
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
|
||||
|
||||
docker build -t $REGISTRY/xiaoqu-gateway:$TAG -f gateway/Dockerfile .
|
||||
docker build -t $REGISTRY/xiaoqu-web:$TAG -f web/Dockerfile .
|
||||
|
||||
# 2. 推送到 ACR
|
||||
docker push $REGISTRY/xiaoqu-gateway:$TAG
|
||||
docker push $REGISTRY/xiaoqu-web:$TAG
|
||||
|
||||
# 3. SSH 部署
|
||||
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
||||
cd /opt/xiaoqu/staging
|
||||
export IMAGE_TAG=$TAG
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
"
|
||||
|
||||
# 4. 健康检查
|
||||
sleep 10
|
||||
curl -sf http://39.104.87.246:8080/health || echo 'Health check failed!'
|
||||
```
|
||||
|
||||
### Production 部署(手动审批)
|
||||
|
||||
Production 部署需要人工确认,不会自动触发:
|
||||
|
||||
```bash
|
||||
# 使用 build-and-push 脚本
|
||||
./scripts/build-and-push.sh prod --detect --deploy --wait --verify
|
||||
|
||||
# 或手动执行:
|
||||
TAG=v1.2.3 # 使用语义化版本号
|
||||
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
|
||||
|
||||
# 构建 + 推送
|
||||
docker build -t $REGISTRY/xiaoqu-gateway:$TAG -f gateway/Dockerfile .
|
||||
docker build -t $REGISTRY/xiaoqu-web:$TAG -f web/Dockerfile .
|
||||
docker push $REGISTRY/xiaoqu-gateway:$TAG
|
||||
docker push $REGISTRY/xiaoqu-web:$TAG
|
||||
|
||||
# 部署(生产环境目录)
|
||||
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
||||
cd /opt/xiaoqu/production
|
||||
export IMAGE_TAG=$TAG
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
"
|
||||
|
||||
# 验证
|
||||
curl -sf http://39.104.87.246/health && echo 'Production deploy OK'
|
||||
```
|
||||
|
||||
### build-and-push 脚本模板
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/build-and-push.sh
|
||||
set -euo pipefail
|
||||
|
||||
ENV=${1:-staging}
|
||||
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
|
||||
SERVER=39.104.87.246
|
||||
SSH_KEY=~/.ssh/xiaoqu.pem
|
||||
IMAGES=(xiaoqu-gateway xiaoqu-web)
|
||||
|
||||
# 确定 tag
|
||||
if [ "$ENV" = "prod" ]; then
|
||||
TAG=${2:-$(git describe --tags --abbrev=0)}
|
||||
else
|
||||
TAG=$(git rev-parse --short=8 HEAD)
|
||||
fi
|
||||
|
||||
echo "=== Deploying to $ENV with tag $TAG ==="
|
||||
|
||||
# 构建
|
||||
for img in "${IMAGES[@]}"; do
|
||||
echo "Building $img..."
|
||||
docker build -t $REGISTRY/$img:$TAG -f ${img#xiaoqu-}/Dockerfile .
|
||||
done
|
||||
|
||||
# 推送
|
||||
for img in "${IMAGES[@]}"; do
|
||||
echo "Pushing $img..."
|
||||
docker push $REGISTRY/$img:$TAG
|
||||
done
|
||||
|
||||
# 部署
|
||||
DEPLOY_DIR=/opt/xiaoqu/$ENV
|
||||
ssh -i $SSH_KEY root@$SERVER "
|
||||
cd $DEPLOY_DIR
|
||||
export IMAGE_TAG=$TAG
|
||||
docker compose pull
|
||||
docker compose up -d --remove-orphans
|
||||
"
|
||||
|
||||
# 健康检查(重试 3 次)
|
||||
echo "Waiting for health check..."
|
||||
for i in 1 2 3; do
|
||||
sleep 5
|
||||
if curl -sf http://$SERVER/health > /dev/null 2>&1; then
|
||||
echo "✓ Health check passed"
|
||||
exit 0
|
||||
fi
|
||||
echo "Attempt $i failed, retrying..."
|
||||
done
|
||||
|
||||
echo "✗ Health check failed after 3 attempts"
|
||||
exit 1
|
||||
```
|
||||
|
||||
### Docker Compose 示例
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
version: "3.8"
|
||||
services:
|
||||
gateway:
|
||||
image: crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com/xiaoqu-gateway:${IMAGE_TAG:-latest}
|
||||
ports:
|
||||
- "8080:8080"
|
||||
environment:
|
||||
- DATABASE_URL=postgres://...
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
restart: unless-stopped
|
||||
|
||||
web:
|
||||
image: crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com/xiaoqu-web:${IMAGE_TAG:-latest}
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on:
|
||||
gateway:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 部署前健康检查
|
||||
|
||||
部署前进行预检,避免部署失败浪费时间。
|
||||
|
||||
### iOS 预检
|
||||
|
||||
```bash
|
||||
preflight_ios() {
|
||||
local errors=0
|
||||
|
||||
# 检查 Distribution 证书
|
||||
if ! security find-identity -v -p codesigning | grep -q "Apple Distribution"; then
|
||||
echo "ERROR: Apple Distribution 证书未安装"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
# 检查 Provisioning Profile 有效期
|
||||
local profile_dir="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||
if [ -d "$profile_dir" ]; then
|
||||
for profile in "$profile_dir"/*.mobileprovision; do
|
||||
local expiry
|
||||
expiry=$(security cms -D -i "$profile" 2>/dev/null | plutil -extract ExpirationDate raw - 2>/dev/null)
|
||||
if [ -n "$expiry" ]; then
|
||||
local expiry_epoch
|
||||
expiry_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$expiry" "+%s" 2>/dev/null)
|
||||
local now_epoch
|
||||
now_epoch=$(date "+%s")
|
||||
if [ "$expiry_epoch" -lt "$now_epoch" ]; then
|
||||
echo "WARNING: Profile 已过期: $(basename "$profile")"
|
||||
((errors++))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "ERROR: Provisioning Profiles 目录不存在"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
# 检查 API Key
|
||||
if [ ! -f "${API_KEY_PATH:-/dev/null}" ]; then
|
||||
echo "ERROR: ASC API Key (.p8) 文件不存在: $API_KEY_PATH"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
# 检查 Xcode
|
||||
if ! xcode-select -p > /dev/null 2>&1; then
|
||||
echo "ERROR: Xcode Command Line Tools 未安装"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
if [ $errors -gt 0 ]; then
|
||||
echo "iOS 预检失败: $errors 个问题"
|
||||
return 1
|
||||
fi
|
||||
echo "iOS 预检通过"
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
### Docker 预检
|
||||
|
||||
```bash
|
||||
preflight_docker() {
|
||||
local errors=0
|
||||
|
||||
# 检查 Docker daemon
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
echo "ERROR: Docker daemon 未运行"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
# 检查 ACR registry 可达
|
||||
local registry=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
|
||||
if ! docker login $registry --username dummy --password dummy 2>&1 | grep -qv "connection refused"; then
|
||||
# login 会失败但不应该是 connection refused
|
||||
echo "WARNING: ACR registry 可能不可达(将在 push 时验证)"
|
||||
fi
|
||||
|
||||
# 检查 SSH 连通性
|
||||
if ! ssh -i ~/.ssh/xiaoqu.pem -o ConnectTimeout=5 -o BatchMode=yes root@39.104.87.246 "echo ok" > /dev/null 2>&1; then
|
||||
echo "ERROR: 无法 SSH 连接到部署服务器 39.104.87.246"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
# 检查服务器磁盘空间
|
||||
local disk_usage
|
||||
disk_usage=$(ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "df -h / | tail -1 | awk '{print \$5}' | tr -d '%'" 2>/dev/null)
|
||||
if [ -n "$disk_usage" ] && [ "$disk_usage" -gt 85 ]; then
|
||||
echo "WARNING: 服务器磁盘使用率 ${disk_usage}%(建议清理 docker system prune)"
|
||||
fi
|
||||
|
||||
# 检查本地磁盘空间
|
||||
local local_disk
|
||||
local_disk=$(df -h . | tail -1 | awk '{print $5}' | tr -d '%')
|
||||
if [ "$local_disk" -gt 90 ]; then
|
||||
echo "ERROR: 本地磁盘使用率 ${local_disk}%,空间不足"
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
if [ $errors -gt 0 ]; then
|
||||
echo "Docker 预检失败: $errors 个问题"
|
||||
return 1
|
||||
fi
|
||||
echo "Docker 预检通过"
|
||||
return 0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 回滚策略
|
||||
|
||||
### iOS TestFlight 回滚
|
||||
|
||||
TestFlight **无法真正回滚**已安装的版本,但有以下应急手段:
|
||||
|
||||
| 手段 | 说明 | API |
|
||||
|------|------|-----|
|
||||
| 停止分发 | 将 build 从测试中移除,用户不再收到更新 | `PATCH /v1/builds/{id}` 设置 `expired: true` |
|
||||
| 过期 build | 强制过期有问题的 build | 同上 |
|
||||
| 紧急热修 | 构建新版本覆盖上线 | 常规部署流程 |
|
||||
|
||||
```bash
|
||||
# 通过 ASC API 停止分发某个 build
|
||||
curl -X PATCH "https://api.appstoreconnect.apple.com/v1/builds/$BUILD_ID" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"data":{"type":"builds","id":"'$BUILD_ID'","attributes":{"expired":true}}}'
|
||||
```
|
||||
|
||||
### Docker 回滚
|
||||
|
||||
Docker 回滚相对简单,拉取上一个正常版本的镜像重新部署即可:
|
||||
|
||||
```bash
|
||||
# 1. 确定上一个正常的 tag
|
||||
PREVIOUS_TAG=<previous-good-tag>
|
||||
REGISTRY=crpi-q4nnuivosic0zc98.cn-beijing.personal.cr.aliyuncs.com
|
||||
|
||||
# 2. 在服务器上回滚
|
||||
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
||||
cd /opt/xiaoqu/production # 或 /opt/xiaoqu/staging
|
||||
export IMAGE_TAG=$PREVIOUS_TAG
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
"
|
||||
|
||||
# 3. 验证回滚成功
|
||||
curl -sf http://39.104.87.246/health && echo 'Rollback OK'
|
||||
```
|
||||
|
||||
### 数据库回滚注意事项
|
||||
|
||||
| 场景 | 策略 |
|
||||
|------|------|
|
||||
| 可逆 migration(加列、加表) | 部署回滚后数据库无需回滚,旧代码忽略新列 |
|
||||
| 不可逆 migration(删列、改类型) | **必须先回滚 migration 再回滚代码**,否则旧代码报错 |
|
||||
| 数据 migration | 评估是否需要补偿脚本,建议 migration 前做备份快照 |
|
||||
|
||||
```bash
|
||||
# 数据库 migration 回滚示例(如果使用 golang-migrate)
|
||||
ssh -i ~/.ssh/xiaoqu.pem root@39.104.87.246 "
|
||||
docker compose exec gateway migrate -path /migrations -database \$DATABASE_URL down 1
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 部署监控
|
||||
|
||||
### Post-deploy 健康检查模式
|
||||
|
||||
```bash
|
||||
# 通用部署后验证函数
|
||||
post_deploy_verify() {
|
||||
local url=$1
|
||||
local max_retries=${2:-5}
|
||||
local interval=${3:-10}
|
||||
|
||||
echo "Verifying deployment at $url ..."
|
||||
for i in $(seq 1 $max_retries); do
|
||||
local status
|
||||
status=$(curl -sf -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
|
||||
if [ "$status" = "200" ]; then
|
||||
echo "Health check passed (attempt $i/$max_retries)"
|
||||
return 0
|
||||
fi
|
||||
echo "Attempt $i/$max_retries: status=$status, retrying in ${interval}s..."
|
||||
sleep $interval
|
||||
done
|
||||
echo "Health check FAILED after $max_retries attempts"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 使用示例
|
||||
post_deploy_verify "http://39.104.87.246/health" 5 10
|
||||
```
|
||||
|
||||
### 飞书通知模板
|
||||
|
||||
部署完成后通过飞书 Webhook 发送通知:
|
||||
|
||||
```bash
|
||||
# 部署成功通知
|
||||
send_feishu_deploy_notification() {
|
||||
local env=$1 # staging / production
|
||||
local version=$2 # 版本号或 tag
|
||||
local status=$3 # success / failure
|
||||
local detail=$4 # 额外说明
|
||||
|
||||
local WEBHOOK_URL="<飞书群 Webhook 地址>"
|
||||
|
||||
if [ "$status" = "success" ]; then
|
||||
local color="green"
|
||||
local emoji="✅"
|
||||
else
|
||||
local color="red"
|
||||
local emoji="❌"
|
||||
fi
|
||||
|
||||
curl -s -X POST "$WEBHOOK_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"msg_type": "interactive",
|
||||
"card": {
|
||||
"header": {
|
||||
"title": {"tag": "plain_text", "content": "'"$emoji"' 部署通知 - '"$env"'"},
|
||||
"template": "'"$color"'"
|
||||
},
|
||||
"elements": [
|
||||
{"tag": "div", "text": {"tag": "lark_md", "content": "**环境**: '"$env"'\n**版本**: '"$version"'\n**状态**: '"$status"'\n**时间**: '"$(date '+%Y-%m-%d %H:%M:%S')"'\n**详情**: '"$detail"'"}}
|
||||
]
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
# 使用示例
|
||||
send_feishu_deploy_notification "production" "v1.2.3" "success" "Gateway + Web 部署完成"
|
||||
send_feishu_deploy_notification "staging" "abc12345" "failure" "Health check 超时"
|
||||
```
|
||||
|
||||
### iOS TestFlight 构建状态监控
|
||||
|
||||
通过 ASC API 持续监控 build 处理状态:
|
||||
|
||||
```bash
|
||||
# 监控 TestFlight build 处理状态
|
||||
monitor_testflight_build() {
|
||||
local build_id=$1
|
||||
local jwt_token=$2
|
||||
local max_wait=600 # 最长等待 10 分钟
|
||||
local elapsed=0
|
||||
|
||||
while [ $elapsed -lt $max_wait ]; do
|
||||
local response
|
||||
response=$(curl -s "https://api.appstoreconnect.apple.com/v1/builds/$build_id" \
|
||||
-H "Authorization: Bearer $jwt_token")
|
||||
|
||||
local state
|
||||
state=$(echo "$response" | python3 -c "import sys,json; print(json.load(sys.stdin)['data']['attributes']['processingState'])" 2>/dev/null)
|
||||
|
||||
echo "[$(date '+%H:%M:%S')] Build $build_id: $state"
|
||||
|
||||
case "$state" in
|
||||
VALID)
|
||||
echo "Build 处理完成,可用于测试"
|
||||
return 0
|
||||
;;
|
||||
FAILED|INVALID)
|
||||
echo "Build 处理失败: $state"
|
||||
return 1
|
||||
;;
|
||||
PROCESSING)
|
||||
sleep 30
|
||||
((elapsed+=30))
|
||||
;;
|
||||
*)
|
||||
sleep 15
|
||||
((elapsed+=15))
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "Build 处理超时(${max_wait}s)"
|
||||
return 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 与需求工作流集成
|
||||
|
||||
部署完成后更新需求状态:
|
||||
|
||||
```bash
|
||||
# 推进到 released
|
||||
ai-proj req advance --id <req_id> --to released
|
||||
|
||||
# 创建部署任务并关联
|
||||
ai-proj task create --title "【部署】TestFlight 发布: {需求标题}"
|
||||
ai-proj req link --id <req_id> --task-ids <task_id>
|
||||
|
||||
# 附加部署文档
|
||||
ai-proj task append-doc --id <task_id> --content "部署记录..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 经验教训汇总
|
||||
|
||||
### iOS TestFlight 部署的 10 个坑
|
||||
|
||||
| # | 坑 | 解决方案 |
|
||||
|---|-----|---------|
|
||||
| 1 | SSH 远程 codesign 失败 | `unlock-keychain` + `set-key-partition-list` |
|
||||
| 2 | SPM 依赖报签名错误 | Archive 阶段 `CODE_SIGNING_ALLOWED=NO`,Export 阶段签名 |
|
||||
| 3 | Swift Macros 被拒 | `-skipMacroValidation` |
|
||||
| 4 | xcodeproj 缺文件 | 新增源文件后必须 `xcodegen generate` |
|
||||
| 5 | 无 Distribution 证书 | Xcode > Accounts 登录 Apple ID 自动下载 |
|
||||
| 6 | build number 冲突 | 每次部署前递增 `CURRENT_PROJECT_VERSION` |
|
||||
| 7 | 缺 App Icon | Assets.xcassets + AppIcon.appiconset + 1024x1024 PNG |
|
||||
| 8 | iPad 方向验证失败 | 声明四方向或 `UIRequiresFullScreen=true` |
|
||||
| 9 | ASC API 401 | JWT 必须包含 `kid` header + 正确的 Issuer ID |
|
||||
| 10 | App Store 图标为空 | 需将 build 关联到 App Store 版本(PATCH relationships/build) |
|
||||
| 11 | SSH 长连接断开 | xcodebuild 3-4 分钟无输出,Tailscale 断连。用 nohup 后台执行 |
|
||||
| 12 | xcodegen 后 cwd 错乱 | `cd ios && xcodegen && cd ..` 失败时不回退。用 subshell `(cd ios && xcodegen)` |
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "dev-integration-plugin",
|
||||
"description": "前后端联调技能。API 契约验证、联调报告、纯后端需求自动跳过。对应 req 流程 integration 阶段。",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
154
skills-dev/dev-integration-plugin/skills/SKILL.md
Normal file
154
skills-dev/dev-integration-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,154 @@
|
||||
---
|
||||
name: dev-integration
|
||||
description: 前后端联调技能。API 契约验证、接口对接、联调报告生成。对应 req 流程 integration 阶段。纯后端需求自动跳过。
|
||||
---
|
||||
|
||||
# 前后端联调 Skill (dev-integration)
|
||||
|
||||
## 概述
|
||||
|
||||
本技能用于前后端联调阶段,确保实际实现与 API 契约一致。
|
||||
|
||||
**核心价值**:发现契约偏差(字段名不一致、类型不匹配、缺少错误码处理)比测试阶段更早、修复成本更低。
|
||||
|
||||
---
|
||||
|
||||
## 技能间契约
|
||||
|
||||
| 上游 | 本技能输入 | 本技能输出 | 下游 |
|
||||
|------|-----------|-----------|------|
|
||||
| dev-coding | 已实现的前后端代码 + API 契约(来自 req-design) | 联调报告(通过/不通过 + 问题列表) | dev-test |
|
||||
|
||||
---
|
||||
|
||||
## 自动跳过条件
|
||||
|
||||
以下情况 integration 阶段**自动通过**,无需执行联调:
|
||||
|
||||
| 条件 | 原因 |
|
||||
|------|------|
|
||||
| 需求只有后端 implementation 任务(无前端任务) | 没有前端对接,无需联调 |
|
||||
| 需求只有前端 implementation 任务(无后端任务) | 使用已有 API,无需联调 |
|
||||
| 需求无 implementation 任务(纯 skill/ops/doc) | 非代码需求 |
|
||||
|
||||
**检测方法**:
|
||||
```
|
||||
get_requirement_tasks → 检查 linkRole=implementation 的任务标题
|
||||
含【开发-后端】和【开发-前端】→ 需要联调
|
||||
仅含一端 → 自动跳过
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 工作流程
|
||||
|
||||
```
|
||||
1. 检查是否需要联调
|
||||
├── 获取 implementation 任务列表
|
||||
├── 判断是否有前后端双端任务
|
||||
└── 仅单端 → 自动跳过,生成跳过说明
|
||||
|
||||
2. 获取 API 契约
|
||||
├── 从 req-design 文档中提取 API 契约
|
||||
└── 无契约 → 从代码反推接口定义
|
||||
|
||||
3. 契约验证
|
||||
├── 后端实际接口 vs 契约定义
|
||||
│ ├── URL 路径是否一致
|
||||
│ ├── 请求/响应字段名是否一致
|
||||
│ ├── 字段类型是否匹配
|
||||
│ └── 错误码是否完整
|
||||
├── 前端调用 vs 契约定义
|
||||
│ ├── API service 调用路径是否正确
|
||||
│ ├── 请求参数是否完整
|
||||
│ └── 响应处理是否覆盖所有错误码
|
||||
└── 前端 ↔ 后端一致性
|
||||
├── 字段命名一致(camelCase vs snake_case 转换)
|
||||
└── 分页参数格式一致
|
||||
|
||||
4. 功能对接验证
|
||||
├── 前端表单字段 vs 后端 binding 规则
|
||||
├── 前端列表列 vs 后端响应字段
|
||||
└── 前端状态流转 vs 后端状态机
|
||||
|
||||
5. 生成联调报告
|
||||
├── 契约一致性结果
|
||||
├── 发现的偏差列表
|
||||
└── 结论:通过/不通过
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 联调报告模板
|
||||
|
||||
```markdown
|
||||
## 联调报告 — {需求标题}
|
||||
|
||||
**日期**: YYYY-MM-DD
|
||||
**API 契约来源**: {req-design 文档 / 代码反推}
|
||||
|
||||
### 契约验证结果
|
||||
|
||||
| # | 接口 | 契约 | 后端实际 | 前端调用 | 结果 |
|
||||
|---|------|------|---------|---------|------|
|
||||
| 1 | POST /api/v1/xxx | ✅ 已定义 | ✅ 一致 | ✅ 一致 | PASS |
|
||||
| 2 | GET /api/v1/xxx | ✅ 已定义 | ⚠️ 字段名不一致 | ✅ 一致 | FAIL |
|
||||
|
||||
### 发现的偏差
|
||||
|
||||
| # | 类型 | 接口 | 描述 | 影响 | 建议 |
|
||||
|---|------|------|------|------|------|
|
||||
| 1 | 字段名不一致 | GET /api/v1/xxx | 契约定义 `created_at`,后端返回 `createTime` | 前端解析失败 | 统一为 `created_at` |
|
||||
|
||||
### 结论
|
||||
|
||||
**{通过 / 不通过}**
|
||||
|
||||
{如不通过,列出必须修复的偏差编号}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 插件支持
|
||||
|
||||
| 插件 | 触发条件 | 说明 |
|
||||
|------|---------|------|
|
||||
| `api-contract-verify` | 有 API 变更 | 自动化契约验证(未来) |
|
||||
|
||||
---
|
||||
|
||||
## 与 ai-proj 集成
|
||||
|
||||
### req 流程内
|
||||
|
||||
```typescript
|
||||
// 创建联调任务(如需要)
|
||||
mcp__ai-proj__create_task({ title: "【联调】前后端对接: {需求标题}" })
|
||||
mcp__ai-proj__link_tasks_to_requirement({
|
||||
requirementId, taskIds: [taskId], linkRole: "implementation"
|
||||
})
|
||||
|
||||
// 附加联调报告
|
||||
mcp__ai-proj__create-and-attach({
|
||||
taskId, title: "联调报告", content: "<报告内容>"
|
||||
})
|
||||
```
|
||||
|
||||
### 自动跳过时
|
||||
|
||||
```typescript
|
||||
// 记录跳过原因
|
||||
mcp__ai-proj__create-and-attach({
|
||||
taskId: <设计任务ID>,
|
||||
content: "## 联调阶段\n\n自动跳过:仅后端变更,无前端对接。"
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **契约先行** — API 契约是联调的基准,没有契约就先补
|
||||
2. **字段级验证** — 不只检查接口是否通,要检查每个字段名、类型、格式
|
||||
3. **错误码覆盖** — 前端必须处理契约中定义的所有错误码
|
||||
4. **snake_case 转换** — Go 后端用 snake_case,前端用 camelCase,确认自动转换正确
|
||||
1
skills-dev/dev-ios-plugin/.claude-plugin/plugin.json
Normal file
1
skills-dev/dev-ios-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"dev-ios-plugin","description":"iOS 开发插件。Swift/SwiftUI + MVVM 架构、TestFlight 部署、Xcode 构建。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
|
||||
90
skills-dev/dev-ios-plugin/skills/SKILL.md
Normal file
90
skills-dev/dev-ios-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: dev-ios
|
||||
description: iOS 开发插件。Swift/SwiftUI + MVVM 架构,TestFlight 部署。当涉及 iOS 开发任务时按需加载。
|
||||
---
|
||||
|
||||
# iOS 开发插件 (dev-ios)
|
||||
|
||||
## 架构:SwiftUI + MVVM
|
||||
|
||||
```
|
||||
AI-Proj-iOS/
|
||||
├── Core/ # 核心层
|
||||
│ ├── Architecture/ # AppCoordinator, AppState
|
||||
│ ├── Components/ # 通用 UI 组件
|
||||
│ ├── Config.swift # 配置
|
||||
│ ├── Services/ # APIEndpoints, AuthService, NetworkService, DIContainer
|
||||
│ ├── Theme/ # 主题配置
|
||||
│ └── Utilities/ # 设备适配
|
||||
├── Features/ # 功能模块(MVVM)
|
||||
│ └── Requirements/ # 示例:List/Detail View + ViewModel
|
||||
├── Models/ # 数据模型 + DTOs
|
||||
└── Resources/ # Assets
|
||||
```
|
||||
|
||||
**开发顺序**:Model → DTO → APIEndpoints → ServiceProtocols → ViewModel → View
|
||||
|
||||
## 代码规范
|
||||
|
||||
```swift
|
||||
@MainActor
|
||||
class TaskViewModel: ObservableObject {
|
||||
@Published private(set) var tasks: [Task] = []
|
||||
@Published private(set) var isLoading = false
|
||||
@Published var error: String?
|
||||
|
||||
private let taskService: TaskServiceProtocol
|
||||
|
||||
init(taskService: TaskServiceProtocol) {
|
||||
self.taskService = taskService
|
||||
}
|
||||
|
||||
func loadTasks() async {
|
||||
guard !isLoading else { return }
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
tasks = try await taskService.fetchTasks()
|
||||
} catch {
|
||||
self.error = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**规则**:
|
||||
- ViewModel 使用 `@MainActor`
|
||||
- Published 属性用 `private(set)`
|
||||
- 使用协议依赖注入
|
||||
- `async/await` 而非 completion handler
|
||||
- `guard` 提前返回,`defer` 确保状态重置
|
||||
|
||||
## 命名规范
|
||||
|
||||
| 类型 | 规范 | 示例 |
|
||||
|------|------|------|
|
||||
| 文件/类 | 大驼峰 | `ManualListView.swift` |
|
||||
| 协议 | 大驼峰 + Protocol | `ManualServiceProtocol` |
|
||||
| 函数/变量 | 小驼峰 | `loadManuals()`, `isLoading` |
|
||||
| 枚举 case | 小驼峰 | `case draft` |
|
||||
|
||||
## 构建与部署
|
||||
|
||||
```bash
|
||||
# 构建
|
||||
xcodebuild -scheme AI-Proj-iOS -configuration Debug
|
||||
|
||||
# 测试
|
||||
xcodebuild test -scheme AI-Proj-iOS
|
||||
|
||||
# TestFlight 部署详见 memory: testflight-deploy.md
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### SwiftLint 沙盒错误
|
||||
Xcode 15+ 默认启用 User Script Sandboxing → Build Settings → `ENABLE_USER_SCRIPT_SANDBOXING = NO`
|
||||
|
||||
### Personal Team 功能限制
|
||||
免费账户不支持 Push Notifications / Associated Domains / App Groups → 从 Entitlements 中移除
|
||||
1
skills-dev/dev-mcp-plugin/.claude-plugin/plugin.json
Normal file
1
skills-dev/dev-mcp-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"dev-mcp-plugin","description":"MCP Bridge 开发插件。TypeScript MCP 服务开发、Token 管理、HTTP 客户端模式。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
|
||||
59
skills-dev/dev-mcp-plugin/skills/SKILL.md
Normal file
59
skills-dev/dev-mcp-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: dev-mcp
|
||||
description: MCP Bridge 开发插件。TypeScript MCP 服务开发,Token 管理,HTTP 客户端模式。当涉及 mcp-task-bridge 开发时按需加载。
|
||||
---
|
||||
|
||||
# MCP Bridge 开发插件 (dev-mcp)
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
mcp-task-bridge/
|
||||
├── index.ts # 入口,MCP server 注册
|
||||
├── task-service.ts # 任务服务
|
||||
├── document-service.ts # 文档服务
|
||||
├── requirement-service.ts # 需求服务
|
||||
├── base-client.ts # HTTP 基类(认证、重试)
|
||||
├── types.ts # 类型定义
|
||||
└── token-storage.ts # Token 持久化
|
||||
```
|
||||
|
||||
## 代码规范
|
||||
|
||||
```typescript
|
||||
export class TaskService extends BaseClient {
|
||||
async createTask(
|
||||
title: string,
|
||||
projectId: number = 1,
|
||||
options: CreateTaskOptions = {}
|
||||
): Promise<ApiResponse<Task>> {
|
||||
try {
|
||||
const response = await this.makeRequest<Task>(
|
||||
'POST',
|
||||
`/projects/${projectId}/tasks`,
|
||||
{ title, project_id: projectId, ...options }
|
||||
);
|
||||
return response.success
|
||||
? { success: true, data: response.data, message: `✅ 任务创建成功` }
|
||||
: response;
|
||||
} catch (error: any) {
|
||||
return { success: false, error: `创建任务失败: ${error.message}` };
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 关键规则
|
||||
|
||||
1. **MCP endpoint 前缀**:所有 MCP 专用后端接口必须包含 `/mcp/` 前缀
|
||||
2. **修改后重新构建**:`npm run build` → `pkill -f "mcp-task-bridge/dist/index.js"`
|
||||
3. **环境一致性**:不要跨环境混用数据(dev/staging/prod)
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
npm run dev # 开发(hot reload)
|
||||
npm run build # 编译 TypeScript
|
||||
npm test # 快速测试
|
||||
npm run test:integration # 集成测试
|
||||
```
|
||||
1
skills-dev/dev-pda-plugin/.claude-plugin/plugin.json
Normal file
1
skills-dev/dev-pda-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"dev-pda-plugin","description":"PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先模式。按需加载。","version":"1.0.0","author":{"name":"qiudl"}}
|
||||
49
skills-dev/dev-pda-plugin/skills/SKILL.md
Normal file
49
skills-dev/dev-pda-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
name: dev-pda
|
||||
description: PDA 应用开发插件。Android 原生 + 扫码枪集成 + 离线优先。当涉及 PDA/手持终端开发时按需加载。
|
||||
---
|
||||
|
||||
# PDA 应用开发插件 (dev-pda)
|
||||
|
||||
## 特点
|
||||
|
||||
- Android 原生开发(Kotlin)
|
||||
- 扫码枪硬件集成
|
||||
- 离线优先(本地 Room DB + 同步队列)
|
||||
- 简洁 UI(大按钮、大字体、适配小屏幕)
|
||||
|
||||
## 扫码集成
|
||||
|
||||
```kotlin
|
||||
class ScanReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val barcode = intent.getStringExtra("SCAN_BARCODE")
|
||||
// 处理扫码结果
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 离线存储
|
||||
|
||||
```kotlin
|
||||
@Entity(tableName = "inventory")
|
||||
data class Inventory(
|
||||
@PrimaryKey val id: Long,
|
||||
val barcode: String,
|
||||
val quantity: Int,
|
||||
@ColumnInfo(name = "sync_status")
|
||||
val syncStatus: SyncStatus = SyncStatus.PENDING
|
||||
)
|
||||
|
||||
enum class SyncStatus { PENDING, SYNCED, FAILED }
|
||||
```
|
||||
|
||||
## 离线同步策略
|
||||
|
||||
```
|
||||
操作 → 写入本地 DB (PENDING)
|
||||
↓
|
||||
网络可用 → 批量上传 → 成功 → 标记 SYNCED
|
||||
↓ 失败
|
||||
标记 FAILED → 下次重试
|
||||
```
|
||||
8
skills-dev/dev-review-plugin/.claude-plugin/plugin.json
Normal file
8
skills-dev/dev-review-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "dev-review-plugin",
|
||||
"description": "代码评审技能。五视角对抗性扫描法(攻击者/泄露者/并发者/边界者/依赖者),CR 报告生成,独立于 req 工作流可单独使用。",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
280
skills-dev/dev-review-plugin/skills/SKILL.md
Normal file
280
skills-dev/dev-review-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,280 @@
|
||||
---
|
||||
name: dev-review
|
||||
description: 代码评审技能。五视角对抗性扫描法,用于 PR 代码审查、安全评审、质量检查。当执行 /req cr 或独立 PR review 时自动激活。
|
||||
---
|
||||
|
||||
# 代码评审 Skill (dev-review)
|
||||
|
||||
## 概述
|
||||
|
||||
独立的代码评审技能,核心方法论是**五视角对抗性扫描法**。
|
||||
|
||||
**适用场景**:
|
||||
- `/req cr [REQ-ID]` — 需求流程中的代码评审阶段
|
||||
- 独立 PR review — 不绑定需求的代码审查
|
||||
- 安全评审 — 专项安全扫描
|
||||
|
||||
**核心原则**:实现阶段关注"怎么让它跑通",评审阶段关注**"怎么让它出错"**。AI 必须切换到对抗性思维。
|
||||
|
||||
---
|
||||
|
||||
## 技能间契约
|
||||
|
||||
| 上游 | 本技能输入 | 本技能输出 | 下游 |
|
||||
|------|-----------|-----------|------|
|
||||
| dev-coding | PR diff + 开发设计文档 | CR 报告(五视角扫描 + 发现汇总 + 结论) | dev-test |
|
||||
|
||||
---
|
||||
|
||||
## 工作流程
|
||||
|
||||
```
|
||||
1. 确定评审范围
|
||||
├── git diff 获取变更文件列表
|
||||
├── 统计文件数、行数
|
||||
└── 排除 test 文件(单独审查)
|
||||
|
||||
2. 读取变更代码
|
||||
├── 逐个读取变更<E58F98><E69BB4><EFBFBD>件源码
|
||||
├── 理解业务上下文
|
||||
└── 参考开发设计文档(如有)
|
||||
|
||||
3. 五视角扫描(核心)
|
||||
├── 攻击<E694BB><E587BB><EFBFBD>视角
|
||||
├── 泄露者视角
|
||||
├── 并发者视角
|
||||
├── 边界者视角
|
||||
└── 依赖者视角
|
||||
|
||||
4. 加载项目检查清单(如有)
|
||||
└── review-checklist 插件
|
||||
|
||||
5. 生成 CR 报告
|
||||
├── 变更概要
|
||||
├── 五视角扫描结果
|
||||
├── 发现汇总表
|
||||
└── 结论(通过/有条件通过/需修改)
|
||||
|
||||
6. 创建评审任务(req 流程内)
|
||||
├── ai-proj task create【代码评审】
|
||||
├── 关联需求(linkRole=code_review)
|
||||
└── 附加 CR 报告文档
|
||||
|
||||
7. 处理发现
|
||||
├── Critical/High → 创建修复任务
|
||||
└── Medium/Low → 记录建议
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五视角对抗性扫描法
|
||||
|
||||
### 总览
|
||||
|
||||
| 视角 | 思维模式 | 核心问题 |
|
||||
|------|---------|---------|
|
||||
| **1. 攻击者** | "我怎么绕过/<2F><><EFBFBD>用它?" | 跨租户泄露、越权访问、参数注入、重放攻击 |
|
||||
| **2. 泄露者** | "它暴露了什么不该暴露的?" | 错误信息泄露、日志敏感数据、响应内部细节 |
|
||||
| **3. 并发者** | "两个请求同时来会怎样?" | 竞态条件、双重扣款、幂等性缺失、锁粒度 |
|
||||
| **4. 边界者** | "极端输入会怎样?" | 空值/零值/负值/超长字符串、类型溢出、分页越界 |
|
||||
| **5. 依赖者** | "外部服务挂了会怎样?" | 超时处理、重试策略、降级方案、连接泄露 |
|
||||
|
||||
### 视角1:攻击者(多租户安全)
|
||||
|
||||
**思维模式**:我是恶意用户,如何绕过权限获取他人数据。
|
||||
|
||||
扫描清单:
|
||||
- [ ] 所有 Store/Repository 层查询是否带 `tenant_id` 过滤?
|
||||
- [ ] 通过 ID 直接查询的方法是否校验归属?
|
||||
- [ ] 用户只能操作自己的数据?(consumer_id 校验)
|
||||
- [ ] URL/请求参数是否有注入风险?(SQL、URL、命令注入)
|
||||
- [ ] 外部输入是否直接拼接到查询/URL?(应使用参数化查询或编码)
|
||||
- [ ] 批量操作是否逐条校验权限?(不能只校验第一条)
|
||||
- [ ] 文件上传是否有类型/大小限制?
|
||||
|
||||
**典型发现示例**:
|
||||
```
|
||||
file:line — Store.GetByID(id) 缺少 tenant_id 过滤,
|
||||
攻击者可通过遍<EFBFBD><EFBFBD> ID 获取其他租户数据。
|
||||
建议:添加 WHERE tenant_id = ? 条件。
|
||||
```
|
||||
|
||||
### 视角2:泄露者(信息安全)
|
||||
|
||||
**思维模式**:我是安全审计员,检查每个出口是否泄露了不该泄露的信息。
|
||||
|
||||
扫描清单:
|
||||
- [ ] 错误消息是否泄露业务状态?(如"手机号未注册"暴露用户存在性)
|
||||
- [ ] 日志是否打印了密码、token、密钥、身份证号?
|
||||
- [ ] 响应是否包含不必要的内部字段?(如内部 ID、数据库字段名、堆栈跟踪)
|
||||
- [ ] panic recover 后是否返回了内部错误详情?
|
||||
- [ ] 导出/下载功能是否过滤了敏感字段?
|
||||
|
||||
### 视角3:并发者(数据一致性)
|
||||
|
||||
**思维模式**:两个用户同时操作同一条数据,会发生什么。
|
||||
|
||||
扫描清单:
|
||||
- [ ] 涉及金额/库存变更是否使用事务 + 悲观锁/乐观锁?
|
||||
- [ ] 关键操作是否有幂等保护?(bizNo 唯一索引、幂等键)
|
||||
- [ ] 全局状态(如进程内计数器、缓存)重启后是否安全?
|
||||
- [ ] 是否有 TOCTOU(检查-使用)竞态?(先查状态再操作,中间被修改)
|
||||
- [ ] 并发创建是否会产生重复数据?(唯一约束)
|
||||
|
||||
### 视角4:边界者(健壮性)
|
||||
|
||||
**思维模<E7BBB4><E6A8A1>**:用最极端的输入来测试系统的承受能力。
|
||||
|
||||
扫描清单:
|
||||
- [ ] 必填参数是否有 `binding:"required"` 校验?
|
||||
- [ ] 数值参数是否有范围校验?(min/max,防止负数、溢出)
|
||||
- [ ] 字符串是否有长度限制?(防止超长输入消耗内存)
|
||||
- [ ] 分页参数是否有默认值和上限?(page_size 不能为 0 或 999999)
|
||||
- [ ] 数组参数是否有长度限制?(批量操作不能传 10 万条)
|
||||
- [ ] 空数组/空字符串是否正确处理?(不应触发不必要的数据库操作)
|
||||
- [ ] 除零错误?百分比计算分母为 0?
|
||||
|
||||
### 视角5:依赖者(可靠性)
|
||||
|
||||
**思维模式**:外部服务全部挂掉,系统还能正常运行吗。
|
||||
|
||||
扫描清单:
|
||||
- [ ] HTTP 客户端是否设置超时?(connect/read/write timeout)
|
||||
- [ ] 外部 API 调用失败是否有合理的错误处理?(不能直接 panic)
|
||||
- [ ] 是否有重试策略?重试是否有退避?是否幂等安全?
|
||||
- [ ] 数据库连接池配置是否合理?(max idle/max open/lifetime)
|
||||
- [ ] Redis 不可用时是否有降级方案?(缓存穿透到数据库)
|
||||
- [ ] token 过期/刷新逻辑是否正确?(access vs refresh 不同策略)
|
||||
|
||||
---
|
||||
|
||||
## CR 报告模板
|
||||
|
||||
```markdown
|
||||
## 代<><E4BBA3>评审报告 — {需求标题/PR 标题}
|
||||
|
||||
**日期**: YYYY-MM-DD
|
||||
**评审范围**: {N} 个文件, {M} 行变更
|
||||
**评审人**: AI (dev-review)
|
||||
|
||||
### 变更概要
|
||||
{1-3 句描述本次变更的目的和范围}
|
||||
|
||||
### 五视角扫描结果
|
||||
|
||||
#### 1. 攻击者视角
|
||||
{扫描发现,或 "未发现问题"}
|
||||
|
||||
#### 2. 泄露者视角
|
||||
{扫描发现,或 "未发现问题"}
|
||||
|
||||
#### 3. 并发者视角
|
||||
{扫描发现,或 "未发现问题"}
|
||||
|
||||
#### 4. 边界者视角
|
||||
{扫描发现,或 "未发现问题"}
|
||||
|
||||
#### 5. 依赖者视角
|
||||
{扫描发现,或 "未发现问题"}
|
||||
|
||||
### 审查发现汇总
|
||||
|
||||
| # | 严重度 | 文件:行号 | <20><><EFBFBD>角 | 描述 | 建议 |
|
||||
|---|--------|----------|------|------|------|
|
||||
| 1 | {Critical/High/Medium/Low} | {file:line} | {视角} | {问题} | {建议} |
|
||||
|
||||
### 统计
|
||||
|
||||
| 严重度 | 数量 |
|
||||
|--------|------|
|
||||
| Critical | 0 |
|
||||
| High | 0 |
|
||||
| Medium | 0 |
|
||||
| Low | 0 |
|
||||
|
||||
### 结论
|
||||
|
||||
**{通过 / 有条件通过 / 需修改}**
|
||||
|
||||
{结论说明:如果有 Critical/High 必须修复后重新评审}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 严重度定义
|
||||
|
||||
| 严重度 | 含义 | 处理方式 |
|
||||
|--------|------|---------|
|
||||
| **Critical** | 安全漏洞、数据泄露、资金风险 | 必须修复,阻断合并 |
|
||||
| **High** | 数据一致性风险、业务逻辑错误 | 必须修复,阻断合并 |
|
||||
| **Medium** | 边界未处理、缺少校验、性能隐患 | 建议修复,不阻断 |
|
||||
| **Low** | 代码风格、命名优化、文档补充 | 可选修复 |
|
||||
|
||||
---
|
||||
|
||||
## CR 报告质量门禁
|
||||
|
||||
`/req next` 从 review 阶段推进时,检查 CR 报告质量:
|
||||
|
||||
| 检查项 | 标准 |
|
||||
|--------|------|
|
||||
| 文档存在 | CR 任务有附加文档 |
|
||||
| 字数 | ≥ 500 字 |
|
||||
| 代码引用 | 含 `file:line` 格式的引用 |
|
||||
| 五视角扫描 | 含全部 5 个视角章节 |
|
||||
| 结论章节 | 含明确的通过/不通过结论 |
|
||||
|
||||
---
|
||||
|
||||
## 插件支持
|
||||
|
||||
| 插件 | 触发条件 | 说明 |
|
||||
|------|---------|------|
|
||||
| `review-checklist` | 每次 CR | 加载项目特定检查清单 |
|
||||
| `figma-design-qa` | 有设计稿 | 设计还原度对比 |
|
||||
|
||||
---
|
||||
|
||||
## 与 ai-proj 集成
|
||||
|
||||
### req 流程内(/req cr)
|
||||
|
||||
```typescript
|
||||
// 1. 确认 implementation 任务已完成
|
||||
mcp__ai-proj__get_requirement_tasks({ requirementId })
|
||||
// 检查所有 linkRole=implementation 的任务状态
|
||||
|
||||
// 2. 创建 CR 任务
|
||||
mcp__ai-proj__create_task({ title: "【代码评审】CR: {需求标题}" })
|
||||
mcp__ai-proj__link_tasks_to_requirement({
|
||||
requirementId, taskIds: [crTaskId], linkRole: "code_review"
|
||||
})
|
||||
|
||||
// 3. 附加 CR 报告
|
||||
mcp__ai-proj__create-and-attach({
|
||||
taskId: crTaskId,
|
||||
title: "代码评审报告",
|
||||
content: "<CR <20><><EFBFBD>告 Markdown>"
|
||||
})
|
||||
|
||||
// 4. High/Critical 发现 → 创建修复任务
|
||||
mcp__ai-proj__create_task({ title: "【修复】{问题描述}" })
|
||||
mcp__ai-proj__link_tasks_to_requirement({
|
||||
requirementId, taskIds: [fixTaskId], linkRole: "implementation"
|
||||
})
|
||||
```
|
||||
|
||||
### 独立 PR review
|
||||
|
||||
不需要 ai-proj 集成,直接输出 CR 报告到对话。
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **先理解再审查** — 读完所有变更<E58F98><E69BB4><EFBFBD>件后再开始扫描,避免断章取义
|
||||
2. **对抗性思维** — 切换到"怎么让它出错"的心态,不是"怎么让它跑通"
|
||||
3. **证据驱动** — 每个发现必须引用具体的 `file:line`
|
||||
4. **严重度准确** — 不要所有问题都标 High,按实际影响分级
|
||||
5. **建议可操作** — 每个发现必须附带具体修复建议
|
||||
6. **关注变更** — 评审范围是 diff,不要对未变更的代码提意见(除非变更引入了对旧代码的新风险)
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "dev-scaffold-plugin",
|
||||
"description": "模块脚手架插件。新建模块时自动生成分层代码骨架(Model/Repository/Service/Handler/Route)。挂载在 dev 阶段。",
|
||||
"version": "1.0.0",
|
||||
"author": { "name": "qiudl" }
|
||||
}
|
||||
78
skills-dev/dev-scaffold-plugin/skills/SKILL.md
Normal file
78
skills-dev/dev-scaffold-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: dev-scaffold
|
||||
description: 模块脚手架插件。新建模块时自动生成分层代码骨架。挂载在 dev 阶段,新建模块时激活。
|
||||
---
|
||||
|
||||
# 模块脚手架插件 (dev-scaffold)
|
||||
|
||||
## 概述
|
||||
|
||||
当需要新建一个完整模块时,自动生成分层代码骨架,避免手动创建大量样板文件。
|
||||
|
||||
**触发条件**:
|
||||
- 需求需要新建数据库表 + 完整 CRUD
|
||||
- 开发设计文档中有"新增"类型的文件
|
||||
|
||||
## Go 后端脚手架
|
||||
|
||||
输入模块名(如 `manual`),生成以下文件:
|
||||
|
||||
```
|
||||
backend/
|
||||
├── models/manual.go # GORM 模型
|
||||
├── database/manual_repository.go # Repository
|
||||
├── services/manual_service.go # Service
|
||||
├── handlers/manual_handler.go # Handler
|
||||
├── routes/manual_routes.go # Route
|
||||
└── migrations/YYYYMMDDHHMMSS_create_manual.up.sql # Migration
|
||||
```
|
||||
|
||||
### 生成规则
|
||||
|
||||
**Model** (`models/{name}.go`):
|
||||
- struct 定义 + GORM tags
|
||||
- TableName() 方法
|
||||
- 标准字段:ID, TenantID, CreatedBy, CreatedAt, UpdatedAt, DeletedAt
|
||||
|
||||
**Repository** (`database/{name}_repository.go`):
|
||||
- interface 定义
|
||||
- Create, GetByID, List(分页), Update, Delete 方法
|
||||
- 所有查询带 tenant_id 过滤
|
||||
|
||||
**Service** (`services/{name}_service.go`):
|
||||
- interface 定义
|
||||
- 注入 Repository
|
||||
- 基础 CRUD + 业务校验
|
||||
|
||||
**Handler** (`handlers/{name}_handler.go`):
|
||||
- Create, Get, List, Update, Delete 方法
|
||||
- 请求参数绑定 + 验证
|
||||
- 统一错误处理
|
||||
|
||||
**Route** (`routes/{name}_routes.go`):
|
||||
- RESTful 路由注册
|
||||
- Auth 中间件
|
||||
|
||||
**Migration** (`migrations/YYYYMMDDHHMMSS_create_{name}.up.sql`):
|
||||
- CREATE TABLE + 标准字段 + 索引
|
||||
|
||||
## React 前端脚手架
|
||||
|
||||
输入模块名(如 `Manual`),生成:
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── types/manual.ts # TypeScript 类型
|
||||
├── services/manualService.ts # API Service
|
||||
├── pages/ManualListPage.tsx # 列表页
|
||||
└── pages/ManualDetailPage.tsx # 详情页(可选)
|
||||
```
|
||||
|
||||
## 使用方式
|
||||
|
||||
```
|
||||
用户: "新建 manual 模块的脚手架"
|
||||
AI: 根据 req-design 的变更文件清单,生成所有骨架文件
|
||||
```
|
||||
|
||||
**注意**:脚手架只生成骨架,具体业务逻辑需在骨架基础上补充。
|
||||
@@ -15,6 +15,7 @@ description: 软件测试技能。用于单元测试、集成测试、E2E测试
|
||||
| `android-testing.md` | Android 测试 (JUnit + Espresso + Compose) |
|
||||
| `e2e-testing.md` | E2E Playwright:API Mock 冒烟测试(无后端)+ 全链路集成测试 |
|
||||
| `templates/go-integration-test.md` | Go 集成测试模板(多步骤 API 流程、中间件验证、租户隔离) |
|
||||
| `templates/pdv-smoke-spec.md` | PDV 部署后验收 Playwright 模板(页面可达、菜单可见、API 连通) |
|
||||
|
||||
---
|
||||
|
||||
@@ -50,6 +51,7 @@ description: 软件测试技能。用于单元测试、集成测试、E2E测试
|
||||
| E2E (Mock 冒烟) | `npm run test:e2e:smoke-mock` | `e2e-testing.md` §API Mock |
|
||||
| E2E (全链路) | `npm run test:e2e` | `e2e-testing.md` §全链路 |
|
||||
| E2E (Coolbuy PaaS) | `make e2e` | `e2e-testing.md` §Coolbuy |
|
||||
| E2E (部署后验收 PDV) | `E2E_BASE_URL=<url> npx playwright test e2e/pdv/` | §PDV |
|
||||
|
||||
---
|
||||
|
||||
@@ -145,6 +147,60 @@ ai-proj task append-doc --id <taskId> --content "# 测试报告
|
||||
|
||||
---
|
||||
|
||||
## 部署后 E2E 验收 (PDV — Post-Deploy Verification)
|
||||
|
||||
部署后验收是独立于 TG4 的 E2E 冒烟模式,在 `/req deploy` 健康检查通过后执行。
|
||||
|
||||
### PDV vs TG4 区别
|
||||
|
||||
| 维度 | TG4 (开发阶段 E2E 冒烟) | PDV (部署后验收) |
|
||||
|------|------------------------|-----------------|
|
||||
| **触发时机** | `/req test` Gate 4 | `/req deploy` 步骤 6 |
|
||||
| **环境** | 本地开发环境,API Mock | 真实部署环境 (staging/prod) |
|
||||
| **目的** | 验证前端逻辑、UI 渲染 | 验证功能入口可达、基本可用 |
|
||||
| **API** | `page.route()` 拦截 | 真实后端 API |
|
||||
| **范围** | 回归冒烟 | 仅新功能可达性 |
|
||||
|
||||
### PDV 检查项
|
||||
|
||||
| 检查项 | 说明 | 示例 |
|
||||
|--------|------|------|
|
||||
| **页面可达** | 需求涉及的前端页面返回 200 | `/okr/my`, `/okr/team` |
|
||||
| **菜单可见** | 新增菜单项在侧栏中出现 | OKR 菜单对目标用户角色可见 |
|
||||
| **基础渲染** | 页面无白屏/JS 报错 | 页面有预期的标题/组件 |
|
||||
| **API 连通** | 关键 API 带 token 调用返回正常 | `GET /api/v1/okr/objectives` 返回 200 |
|
||||
|
||||
### PDV 不做什么
|
||||
|
||||
- 不做完整回归测试(那是 TG4 的事)
|
||||
- 不测试复杂业务流程(如多步骤表单提交)
|
||||
- 不替代手动验收
|
||||
- 只做「功能入口可达 + 基本可用」的冒烟验证
|
||||
|
||||
### PDV 执行方式
|
||||
|
||||
```bash
|
||||
E2E_BASE_URL=<部署环境URL> npx playwright test e2e/pdv/ --project=chromium
|
||||
```
|
||||
|
||||
### PDV Spec 生成规则
|
||||
|
||||
AI 根据需求变更范围动态生成 Playwright spec,模板见 `templates/pdv-smoke-spec.md`。生成流程:
|
||||
|
||||
1. 从需求关联任务提取前端变更范围(页面路由、菜单项、API 端点)
|
||||
2. 登录测试账号(使用 storageState 或手动登录)
|
||||
3. 验证新增菜单项可见(检查 `.ant-menu` 包含目标文本)
|
||||
4. 导航到新页面,验证非白屏(title 不含 error/500/404)
|
||||
5. 调用关键 API,验证返回状态码 < 500
|
||||
6. 每步截图保存为证据
|
||||
|
||||
### PDV 结果判定
|
||||
|
||||
- **全部 PASS** → 继续推进到 released
|
||||
- **任一 FAIL** → 阻断推进,在部署文档记录失败项,通知修复
|
||||
|
||||
---
|
||||
|
||||
## TG2 集成测试检测
|
||||
|
||||
### 模板映射
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# E2E 测试 (Playwright)
|
||||
|
||||
## 两种 E2E 测试模式
|
||||
## 三种 E2E 测试模式
|
||||
|
||||
| 模式 | 后端依赖 | 速度 | 适用场景 | 门禁阶段 |
|
||||
|------|---------|------|---------|---------|
|
||||
| **API Mock 冒烟测试** | ❌ 无需后端 | 快(<30s) | UI 布局、路由、菜单、权限隔离 | E2E 冒烟门禁 |
|
||||
| **API Mock 冒烟测试** | ❌ 无需后端 | 快(<30s) | UI 布局、路由、菜单、权限隔离 | TG4 E2E 冒烟门禁 |
|
||||
| **全链路集成测试** | ✅ 需完整后端+DB | 慢(分钟级) | CRUD 业务流程、数据持久化 | 手动/CI |
|
||||
| **部署后验收 (PDV)** | ✅ 真实部署环境 | 中(<2min) | 功能入口可达、菜单可见、API 连通 | `/req deploy` 步骤 6 |
|
||||
|
||||
**⚠️ 关键原则:E2E 冒烟门禁必须使用 API Mock 模式,不依赖后端。** 依赖后端的 E2E 在开发机上经常跑不通(后端没启动、DB 未初始化),导致门禁形同虚设。
|
||||
|
||||
> **PDV 与 TG4 的区别**:TG4 在开发阶段用 API Mock 验证前端逻辑;PDV 在部署后用真实环境验证功能可达性。详见 `SKILL.md` §PDV 章节。
|
||||
|
||||
> **与 req-test-gate 的关系**:本文档定义 E2E 测试的**执行技术**(怎么写 mock、怎么跑)。质量门禁流程(Gates 0-5、scope 分级、文档持久化)定义在 `req-test-gate` 技能中。
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
# PDV Smoke Spec 模板
|
||||
|
||||
部署后验收 (Post-Deploy Verification) Playwright 测试模板。AI 根据需求变更范围,基于此模板动态生成验收 spec。
|
||||
|
||||
## 使用方式
|
||||
|
||||
```bash
|
||||
# 指定部署环境 URL 执行
|
||||
E2E_BASE_URL=https://staging.example.com npx playwright test e2e/pdv/ --project=chromium
|
||||
```
|
||||
|
||||
## Spec 模板结构
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
const BASE_URL = process.env.E2E_BASE_URL || 'http://localhost:3000';
|
||||
|
||||
test.describe('PDV: {需求标题}', () => {
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// 方式 1: 使用 storageState(推荐,需预先保存登录状态)
|
||||
// test.use({ storageState: 'e2e/.auth/user.json' });
|
||||
|
||||
// 方式 2: 手动登录
|
||||
await page.goto(`${BASE_URL}/login`);
|
||||
await page.fill('input[name="username"]', '{测试账号}');
|
||||
await page.fill('input[name="password"]', '{测试密码}');
|
||||
await page.click('button[type="submit"]');
|
||||
await page.waitForURL('**/dashboard/**');
|
||||
});
|
||||
|
||||
test('菜单可见性: {菜单名}', async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/`);
|
||||
await page.waitForSelector('.ant-menu');
|
||||
|
||||
// 检查侧栏包含新菜单项
|
||||
const menu = page.locator('.ant-menu');
|
||||
await expect(menu).toContainText('{菜单名}');
|
||||
|
||||
// 截图证据
|
||||
await page.screenshot({ path: 'e2e-results/pdv-menu-{菜单名}.png', fullPage: false });
|
||||
});
|
||||
|
||||
test('页面可达: {页面路由}', async ({ page }) => {
|
||||
const response = await page.goto(`${BASE_URL}{页面路由}`);
|
||||
|
||||
// 验证 HTTP 状态
|
||||
expect(response?.status()).toBeLessThan(400);
|
||||
|
||||
// 验证非白屏 — title 不含错误关键词
|
||||
await expect(page).not.toHaveTitle(/error|500|404|not found/i);
|
||||
|
||||
// 验证页面有核心内容(非空白)
|
||||
await expect(page.locator('{核心选择器}')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// 检查无 JS 报错(通过 console error 监听)
|
||||
const errors: string[] = [];
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') errors.push(msg.text());
|
||||
});
|
||||
await page.waitForTimeout(2000);
|
||||
expect(errors.filter(e => !e.includes('favicon'))).toHaveLength(0);
|
||||
|
||||
// 截图证据
|
||||
await page.screenshot({ path: 'e2e-results/pdv-page-{页面名}.png', fullPage: true });
|
||||
});
|
||||
|
||||
test('API 连通: {接口描述}', async ({ request }) => {
|
||||
// 需要带认证 token 调用
|
||||
const resp = await request.get(`${BASE_URL}/api/v1/{路径}`, {
|
||||
headers: {
|
||||
'Authorization': 'Bearer {token}',
|
||||
},
|
||||
});
|
||||
|
||||
// 验证非 5xx 错误
|
||||
expect(resp.status()).toBeLessThan(500);
|
||||
|
||||
// 可选:验证响应结构
|
||||
// const body = await resp.json();
|
||||
// expect(body).toHaveProperty('data');
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
## 占位符说明
|
||||
|
||||
| 占位符 | 含义 | 来源 |
|
||||
|--------|------|------|
|
||||
| `{需求标题}` | 需求名称 | `ai-proj req get --id <id>` |
|
||||
| `{菜单名}` | 新增的菜单文本 | 从需求关联的前端任务中提取 |
|
||||
| `{页面路由}` | 新增/变更的前端路由 | 从前端路由配置或 PRD 提取 |
|
||||
| `{核心选择器}` | 页面核心内容的 CSS 选择器 | 如 `h1`, `.page-title`, `[data-testid="xxx"]` |
|
||||
| `{测试账号}` / `{测试密码}` | 测试环境登录凭据 | 环境配置 |
|
||||
| `{token}` | API 认证 token | 登录后获取 |
|
||||
| `{接口描述}` / `{路径}` | 关键 API 端点 | 从后端路由或 PRD 提取 |
|
||||
| `{页面名}` | 截图文件名标识 | 自定义 |
|
||||
|
||||
## 生成示例(OKR 功能)
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
const BASE_URL = process.env.E2E_BASE_URL || 'http://localhost:3000';
|
||||
|
||||
test.describe('PDV: OKR 团队/对齐/设置/评分功能', () => {
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/login`);
|
||||
await page.fill('input[name="username"]', 'testuser');
|
||||
await page.fill('input[name="password"]', 'TestPass123');
|
||||
await page.click('button[type="submit"]');
|
||||
await page.waitForURL('**/dashboard/**');
|
||||
});
|
||||
|
||||
test('菜单可见性: OKR', async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/`);
|
||||
await page.waitForSelector('.ant-menu');
|
||||
await expect(page.locator('.ant-menu')).toContainText('OKR');
|
||||
await page.screenshot({ path: 'e2e-results/pdv-menu-okr.png' });
|
||||
});
|
||||
|
||||
test('页面可达: /okr/my', async ({ page }) => {
|
||||
const response = await page.goto(`${BASE_URL}/okr/my`);
|
||||
expect(response?.status()).toBeLessThan(400);
|
||||
await expect(page).not.toHaveTitle(/error|500|404/i);
|
||||
await expect(page.locator('h1, .page-title')).toBeVisible({ timeout: 10000 });
|
||||
await page.screenshot({ path: 'e2e-results/pdv-page-okr-my.png', fullPage: true });
|
||||
});
|
||||
|
||||
test('页面可达: /okr/team', async ({ page }) => {
|
||||
const response = await page.goto(`${BASE_URL}/okr/team`);
|
||||
expect(response?.status()).toBeLessThan(400);
|
||||
await expect(page).not.toHaveTitle(/error|500|404/i);
|
||||
await expect(page.locator('h1, .page-title')).toBeVisible({ timeout: 10000 });
|
||||
await page.screenshot({ path: 'e2e-results/pdv-page-okr-team.png', fullPage: true });
|
||||
});
|
||||
|
||||
test('API 连通: OKR objectives', async ({ request }) => {
|
||||
const resp = await request.get(`${BASE_URL}/api/v1/okr/objectives`);
|
||||
expect(resp.status()).toBeLessThan(500);
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "review-checklist-plugin",
|
||||
"description": "项目级代码评审检查清单。按项目积累的特定检查项,挂载在 dev-review 下自动加载。",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
42
skills-dev/review-checklist-plugin/checklists/ai-proj.md
Normal file
42
skills-dev/review-checklist-plugin/checklists/ai-proj.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# AI-Proj 代码评审检查清单
|
||||
|
||||
## 后端(Go + Gin + GORM)
|
||||
|
||||
### 分层架构
|
||||
- [ ] Handler 是否直接 import `database/` 包?— 禁止,必须通过 Service 层。教训:架构退化导致循环依赖
|
||||
- [ ] 新 Handler 是否在 routes/ 中注册?— 遗漏会导致 404
|
||||
- [ ] MCP 专用 endpoint 是否包含 `/mcp/` 前缀?— 教训:缺少前缀导致 MCP bridge 404
|
||||
|
||||
### 数据库
|
||||
- [ ] 新增 Migration 文件名是否符合 `YYYYMMDDHHMMSS_xxx.up.sql` 格式?
|
||||
- [ ] Migration 是否有对应的 `.down.sql`?
|
||||
- [ ] GORM 查询是否带 `tenant_id` 过滤?(多租户安全)
|
||||
- [ ] 软删除查询是否正确使用 `Unscoped()`?— 误用导致查不到已删除数据或查出已删除数据
|
||||
|
||||
### 认证与权限
|
||||
- [ ] 新 API 是否配置了 Auth 中间件?— 遗漏导致未授权访问
|
||||
- [ ] JWT token 类型是否区分 access/refresh?— 教训:token 混用导致安全漏洞
|
||||
- [ ] bcrypt cost 是否使用 12?— 教训:默认 cost 10 导致登录失败
|
||||
|
||||
### Redis
|
||||
- [ ] Redis key 是否有 TTL?— 缺少 TTL 导致内存泄露
|
||||
- [ ] Redis 不可用时是否降级到数据库?
|
||||
|
||||
## 前端(React 18 + Ant Design)
|
||||
|
||||
### Modal 安全
|
||||
- [ ] `Modal.success/info/warning/error` 之后是否有立即执行的 UI 操作?— 必须放在 `onOk` 回调中。教训:两个 Modal 同时弹出互相遮挡
|
||||
|
||||
### 状态管理
|
||||
- [ ] React Query 的 queryKey 是否正确包含所有依赖参数?— 缺少导致缓存错误
|
||||
- [ ] 列表页分页是否正确重置 page?— 教训:筛选条件变更后 page 未重置导致空页
|
||||
|
||||
### 类型安全
|
||||
- [ ] 是否有 `any` 类型?— 应使用具体类型
|
||||
- [ ] API 响应是否有 TypeScript 接口定义?
|
||||
|
||||
## 通用
|
||||
|
||||
- [ ] `.env` <20><>凭据文件是否被意外加入 git?
|
||||
- [ ] 是否有硬编码的 URL/IP/端口?— 应使用配置
|
||||
- [ ] 错误日志是否包含足够的上下文信息?(user_id, tenant_id, request_id)
|
||||
@@ -0,0 +1,20 @@
|
||||
# Coolbuy PaaS(酷采3.0)代码评审检查清单
|
||||
|
||||
## 后端(Go + Gin + MySQL)
|
||||
|
||||
### 多租户
|
||||
- [ ] 所有查询是否带 `tenant_id` 和 `enterprise_id` 过滤?
|
||||
- [ ] 跨租户数据操作是否被阻止?
|
||||
|
||||
### 数据迁移
|
||||
- [ ] 从酷采2.0迁移的字段映射是否正确?(varchar ID → bigint ID)
|
||||
- [ ] 迁移脚本是否处理了酷采2.0<EFBFBD><EFBFBD><EFBFBD>软删除标记(is_delete → deleted_at)?
|
||||
|
||||
## 前端(Vue 3 + Ant Design Vue)
|
||||
|
||||
### i18n
|
||||
- [ ] 新增文案是否使用 `$t()` 国际化?— 不允许硬编码中文
|
||||
- [ ] i18n key 是否在 zh-CN 和 en-US 都有定义?
|
||||
|
||||
### 权限
|
||||
- [ ] 按钮/菜单是否有权限控制(v-permission 指令)?
|
||||
23
skills-dev/review-checklist-plugin/checklists/general.md
Normal file
23
skills-dev/review-checklist-plugin/checklists/general.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# 通用代码评审检查清单
|
||||
|
||||
适用于所有项目,补充五视角扫描法。
|
||||
|
||||
## API 设计
|
||||
- [ ] RESTful 命名是否规范?(复数名词、无动词)
|
||||
- [ ] 分页参数是否有默认值和上限?
|
||||
- [ ] 响应格式是否统一?(code/message/data)
|
||||
|
||||
## 错误处理
|
||||
- [ ] 错误是否被正确传播?(不要吞掉错误)
|
||||
- [ ] 用户可见的错误消息是否友好?(不暴露技术细节)
|
||||
- [ ] 是否有 panic recover 兜底?
|
||||
|
||||
## 性能
|
||||
- [ ] 列表查询是否有分页?(不允许无限制查询)
|
||||
- [ ] N+1 查询问题?(循环内查数据库)
|
||||
- [ ] 是否有不必要的全表扫描?(缺少索引)
|
||||
|
||||
## 可维护性
|
||||
- [ ] 魔法数字是否提取为常量?
|
||||
- [ ] 复杂业务逻辑是否有注释说明?
|
||||
- [ ] 函数是否过长?(超过 100 行考虑拆分)
|
||||
49
skills-dev/review-checklist-plugin/skills/SKILL.md
Normal file
49
skills-dev/review-checklist-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
name: review-checklist
|
||||
description: 项目级代码评审检查清单。按项目积累特定检查项,挂载在 dev-review 下自动加载。每次 CR 时触发。
|
||||
---
|
||||
|
||||
# 代码评审检查清单插件 (review-checklist)
|
||||
|
||||
## 概述
|
||||
|
||||
本插件为 `dev-review` 提供**项目特定的检查清单**,补充五视角扫描法之外的项目级经验。
|
||||
|
||||
检查清单按项目独立维护,每个项目文件记录该项目踩过的坑和必查项。
|
||||
|
||||
## 使用方式
|
||||
|
||||
1. `dev-review` 执行五视角扫描时,自动加载当前项目的检查清单
|
||||
2. 扫描完成后,逐条检查清单项
|
||||
3. 检查结果附加到 CR 报告的「项目检查清单」章节
|
||||
|
||||
## 检查清单文件
|
||||
|
||||
```
|
||||
review-checklist-plugin/
|
||||
├── skills/SKILL.md # 本文件
|
||||
└── checklists/
|
||||
├── ai-proj.md # AI-Proj 项目清单
|
||||
├── coolbuy-paas.md # 酷采3.0 项目清单
|
||||
└── general.md # 通用清单(所有项目适用)
|
||||
```
|
||||
|
||||
## 如何添加检查项
|
||||
|
||||
当 CR 中发现了一个**项目特有**的问题模式,且未来可能复发时:
|
||||
|
||||
1. 打开对应项目的检查清单文件
|
||||
2. 添加条目,格式:`- [ ] {检查项} — 教训:{来源}`
|
||||
3. 标注严重度和适用范围
|
||||
|
||||
**不要添加**五视角扫描已覆盖的通用安全/并发/边界问题。
|
||||
|
||||
## CR 报告附加章节
|
||||
|
||||
```markdown
|
||||
### 项目检查清单({项目名})
|
||||
|
||||
| # | 检查项 | 结果 | 说明 |
|
||||
|---|--------|------|------|
|
||||
| 1 | {检查项} | ✅/❌/N/A | {说明} |
|
||||
```
|
||||
8
skills-req/req-compare-plugin/.claude-plugin/plugin.json
Normal file
8
skills-req/req-compare-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "req-compare-plugin",
|
||||
"description": "对比式需求分析插件。系统平移、竞品借鉴、版本升级时的参考对象对比分析。挂载在 analysis 阶段。",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
210
skills-req/req-compare-plugin/skills/SKILL.md
Normal file
210
skills-req/req-compare-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,210 @@
|
||||
---
|
||||
name: req-compare
|
||||
description: 对比式需求分析插件。系统平移、竞品借鉴、版本升级时使用参考对象对比法编写 PRD。挂载在 analysis 阶段,需要对比分析时由 req-prd 推荐激活。
|
||||
---
|
||||
|
||||
# 对比式需求分析插件 (req-compare)
|
||||
|
||||
## 概述
|
||||
|
||||
当进行**系统平移、功能迁移、竞品借鉴**时,使用对比分析法编写 PRD,确保新系统功能完整且有所改进。
|
||||
|
||||
**触发条件**:
|
||||
- 用户提到"从 XX 系统迁移"、"参考 XX 功能"、"平移"、"借鉴"
|
||||
- req-prd 检测到需求涉及参考系统
|
||||
|
||||
## 适用场景
|
||||
|
||||
| 场景 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| 系统平移 | 旧系统迁移到新技术栈 | 酷采2.0 → 酷采3.0 |
|
||||
| 功能借鉴 | 参考竞品功能设计 | 参考飞书设计协作功能 |
|
||||
| 版本升级 | 基于当前版本优化 | V1.0 → V2.0 重构 |
|
||||
|
||||
---
|
||||
|
||||
## 对比分析工作流
|
||||
|
||||
```
|
||||
1. 确定参考对象
|
||||
├── 识别参考系统(可以是多个)
|
||||
├── 获取访问权限(测试环境、源代码)
|
||||
└── 明确对比目标
|
||||
|
||||
2. 参考对象分析
|
||||
├── 功能调研(前端页面操作)
|
||||
├── 业务数据分析(核心实体、字段含义)
|
||||
├── 业务逻辑分析(规则、流转、校验)
|
||||
└── 用户体验分析
|
||||
|
||||
3. 对比分析
|
||||
├── 功能对比表(保留/优化/新增/废弃)
|
||||
├── 业务数据对比(实体映射、新增数据项)
|
||||
├── 用户体验对比
|
||||
└── 非功能需求对比(性能、安全)
|
||||
|
||||
4. PRD 编写
|
||||
├── 背景说明(明确参考来源)
|
||||
├── 功能需求(标注来源与变更)
|
||||
├── 业务数据需求(实体、字段、规则)
|
||||
└── 非功能需求(性能、安全、兼容性)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 对比式 PRD 模板
|
||||
|
||||
```markdown
|
||||
# [功能模块名称] PRD
|
||||
|
||||
## 1. 文档概述
|
||||
|
||||
### 1.1 文档信息
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 文档名称 | [模块名称] 需求文档 |
|
||||
| 版本 | V1.0 |
|
||||
| 创建日期 | [日期] |
|
||||
| **需求来源** | **[参考系统名称] 平移/借鉴** |
|
||||
| **参考系统** | **[参考系统访问地址]** |
|
||||
|
||||
### 1.2 背景说明
|
||||
本需求文档基于 **[参考系统]** 的 [模块名称] 功能分析,将其平移至 [目标系统]。
|
||||
|
||||
**参考系统信息**:
|
||||
- 系统地址:[URL]
|
||||
- 技术栈:[技术栈描述]
|
||||
- 源码位置:[源码路径](如有)
|
||||
|
||||
---
|
||||
|
||||
## 2. 参考系统分析
|
||||
|
||||
### 2.1 功能截图
|
||||
[插入参考系统功能截图]
|
||||
|
||||
### 2.2 业务数据(参考系统)
|
||||
| 数据实体 | 核心字段 | 业务含义 |
|
||||
|----------|----------|----------|
|
||||
| [实体名] | [字段列表] | [业务说明] |
|
||||
|
||||
### 2.3 核心功能(参考系统)
|
||||
| 功能 | 用户操作 | 业务规则 |
|
||||
|------|----------|----------|
|
||||
| [功能名] | [操作描述] | [规则说明] |
|
||||
|
||||
### 2.4 业务逻辑(参考系统)
|
||||
- 核心业务规则摘要
|
||||
- 数据校验规则
|
||||
- 状态流转逻辑
|
||||
|
||||
---
|
||||
|
||||
## 3. 功能对比分析
|
||||
|
||||
### 3.1 功能对比表
|
||||
| 序号 | 功能 | 参考系统 | 目标系统 | 变更类型 | 说明 |
|
||||
|------|------|----------|----------|----------|------|
|
||||
| 1 | [功能1] | ✅ | ✅ | 保留 | 直接平移 |
|
||||
| 2 | [功能2] | ✅ | ✅+ | 优化 | [优化内容] |
|
||||
| 3 | [功能3] | ❌ | ✅ | 新增 | [新增原因] |
|
||||
| 4 | [功能4] | ✅ | ❌ | 废弃 | [废弃原因] |
|
||||
|
||||
### 3.2 业务数据对比
|
||||
| 数据项 | 参考系统 | 目标系统 | 变更 | 说明 |
|
||||
|--------|----------|----------|------|------|
|
||||
| [数据项] | [描述] | [描述] | 保留/优化/新增/废弃 | [说明] |
|
||||
|
||||
### 3.3 非功能需求对比
|
||||
| 维度 | 参考系统 | 目标系统要求 |
|
||||
|------|----------|-------------|
|
||||
| 性能 | [现状] | [目标] |
|
||||
| 安全 | [现状] | [目标] |
|
||||
| 用户体验 | [现状] | [目标] |
|
||||
|
||||
---
|
||||
|
||||
## 4. 目标系统设计
|
||||
|
||||
### 4.1 功能清单
|
||||
| 序号 | 功能 | 优先级 | 来源 | 说明 |
|
||||
|------|------|--------|------|------|
|
||||
| 1 | [功能] | P0 | 平移 | 从参考系统平移 |
|
||||
|
||||
### 4.2 业务数据需求(目标系统)
|
||||
| 数据实体 | 核心字段 | 业务规则 | 来源 |
|
||||
|----------|----------|----------|------|
|
||||
| [实体名] | [字段列表] | [校验/约束] | 平移/新增 |
|
||||
|
||||
### 4.3 业务规则
|
||||
- [ ] 规则1(沿用参考系统)
|
||||
- [ ] 规则2(优化调整)
|
||||
|
||||
---
|
||||
|
||||
## 5. 上线优先级
|
||||
1. [P0 功能] — 核心路径
|
||||
2. [P1 功能] — 重要但可后续迭代
|
||||
3. [P2 功能] — 优化项
|
||||
|
||||
## 6. 注意事项
|
||||
- 参考系统中 [xxx] 逻辑需要特别注意
|
||||
- 新系统中需改进 [xxx] 问题
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 参考对象分析方法
|
||||
|
||||
### 1. 前端功能调研
|
||||
- 访问参考系统,截图记录页面布局和交互流程
|
||||
- 记录用户操作路径(CRUD、搜索、筛选等)
|
||||
- 标注交互细节(表单校验、提示信息、异常处理)
|
||||
|
||||
### 2. 业务逻辑调研
|
||||
- 梳理核心业务规则和状态流转
|
||||
- 记录数据校验规则
|
||||
- 标注业务异常处理方式
|
||||
|
||||
> **技术层分析**(代码结构、数据库表结构、API 接口)请在 design 阶段使用 `req-design` 技能完成。
|
||||
|
||||
---
|
||||
|
||||
## 竞品分析模板
|
||||
|
||||
```markdown
|
||||
# [竞品名称] 分析
|
||||
|
||||
## 1. 产品概述
|
||||
- 定位:
|
||||
- 核心功能:
|
||||
- 目标用户:
|
||||
|
||||
## 2. 功能对比
|
||||
| 功能 | 我们 | 竞品A | 竞品B |
|
||||
|------|------|-------|-------|
|
||||
| 功能1 | ✅/❌/部分 | ... | ... |
|
||||
|
||||
## 3. 优劣势分析
|
||||
### 优势
|
||||
1. ...
|
||||
|
||||
### 劣势
|
||||
1. ...
|
||||
|
||||
## 4. 可借鉴点
|
||||
- ...
|
||||
|
||||
## 5. 差异化策略
|
||||
- ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **先调研再写 PRD** — 充分理解参考系统后再动笔
|
||||
2. **功能对比表必填** — 明确每个功能的保留/优化/新增/废弃决策
|
||||
3. **标注来源** — 每个功能需求标注是"平移"还是"新增"
|
||||
4. **记录废弃原因** — 参考系统有但不做的功能,必须记录原因
|
||||
5. **不抄技术实现** — 对比的是业务功能,不是代码结构
|
||||
8
skills-req/req-design-plugin/.claude-plugin/plugin.json
Normal file
8
skills-req/req-design-plugin/.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "req-design-plugin",
|
||||
"description": "需求开发设计技能。PRD 到开发设计的转换:API 契约、数据模型变更、任务拆分、风险评估。",
|
||||
"version": "2.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
}
|
||||
476
skills-req/req-design-plugin/skills/SKILL.md
Normal file
476
skills-req/req-design-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,476 @@
|
||||
---
|
||||
name: req-design
|
||||
description: 需求开发设计技能。用于 PRD 到开发设计的转换:API 契约定义、数据模型变更方案、变更文件清单、开发任务拆分、技术风险评估。当用户执行 /req doc 或需要编写开发设计文档时自动激活。
|
||||
arguments: <REQ-ID>
|
||||
---
|
||||
|
||||
# 需求开发设计 Skill (req-design)
|
||||
|
||||
## 概述
|
||||
|
||||
本技能用于将 PRD 文档转换为**开发设计文档**,是产品需求(req-prd)和编码实现(dev-coding)之间的桥梁。
|
||||
|
||||
**核心输出**:
|
||||
- API 契约定义(前后端对齐)
|
||||
- 数据模型变更方案(新增/修改表和字段)
|
||||
- 变更文件清单(标注层级和改动类型)
|
||||
- 开发任务拆分(SMART 原则,2-4h 粒度)
|
||||
- 技术风险评估
|
||||
|
||||
**不包含**(由其他技能负责):
|
||||
- 具体代码实现 → `dev-coding`
|
||||
- 代码规范和编码模式 → `dev-coding`
|
||||
- 深度架构设计 → `dev-arch`(插件)
|
||||
- 数据库迁移细节 → `db-migration`(插件)
|
||||
|
||||
---
|
||||
|
||||
## 技能间契约
|
||||
|
||||
| 上游 | 本技能输入 | 本技能输出 | 下游 |
|
||||
|------|-----------|-----------|------|
|
||||
| req-prd | PRD 文档(用户故事、业务规则、验收标准) | 开发设计文档 | dev-coding |
|
||||
|
||||
**输出格式要求**:
|
||||
- API 契约:B 级格式(见下方标准)
|
||||
- 变更清单:按文件列出,标注层级和改动类型
|
||||
- 任务拆分:每任务 2-4h,SMART 原则
|
||||
|
||||
---
|
||||
|
||||
## 工作流程
|
||||
|
||||
```
|
||||
0. ⚠️ 需求验证(防浪费,5-10 分钟)
|
||||
├── 现有 UI 检查:功能是否已存在?
|
||||
├── 数据检查:字段/API 是否已有?
|
||||
└── 价值验证:痛点明确?有更简方案?
|
||||
|
||||
1. 获取需求信息
|
||||
├── mcp__ai-proj__get_requirement
|
||||
└── mcp__ai-proj__get_requirement_tasks(找 PRD 任务)
|
||||
|
||||
2. 分析 PRD 文档
|
||||
├── 提取功能点和业务规则
|
||||
├── 识别数据实体和关系
|
||||
└── 确定非功能需求(性能、安全)
|
||||
|
||||
3. 探索代码库
|
||||
├── 搜索相关现有代码(Grep/Glob/Explore)
|
||||
├── 识别修改文件和可复用代码
|
||||
└── 分析依赖关系
|
||||
|
||||
4. 设计 API 契约
|
||||
├── 定义新增/修改接口(B 级格式)
|
||||
├── 定义请求/响应结构
|
||||
└── 定义错误码
|
||||
|
||||
5. 设计数据模型变更
|
||||
├── 新增/修改表和字段
|
||||
├── 索引设计
|
||||
└── 数据迁移方案(如需要)
|
||||
|
||||
6. 生成变更文件清单
|
||||
├── 后端:Model → Repository → Service → Handler → Route → Migration
|
||||
├── 前端:Types → Services → Components → Pages
|
||||
└── 标注每个文件的改动类型(新增/修改/删除)
|
||||
|
||||
7. 拆分开发任务
|
||||
├── 按模块/文件拆分子任务
|
||||
└── mcp__ai-proj__create_subtask
|
||||
|
||||
8. 技术风险评估
|
||||
├── 影响分析(兼容性、性能)
|
||||
└── 回退方案
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 需求验证(第 0 步)
|
||||
|
||||
> **教训**:不先验证需求,可能花几小时实现冗余功能。
|
||||
|
||||
**验证清单**(5-10 分钟):
|
||||
|
||||
1. **现有功能检查**(最重要)
|
||||
- 打开相关页面,目视检查功能是否已存在
|
||||
- 检查数据库是否已有相关字段
|
||||
- 检查 API 是否已返回所需数据
|
||||
|
||||
2. **价值验证**
|
||||
- 解决什么具体痛点?
|
||||
- 有无更简单的替代方案?
|
||||
- 投入产出比是否合理?
|
||||
|
||||
3. **设计审查**
|
||||
- UI 设计是否合理?不重复、不混乱?
|
||||
- 是否符合现有设计规范?
|
||||
|
||||
**通过验证后再开始设计!**
|
||||
|
||||
---
|
||||
|
||||
## API 契约格式标准(B 级)
|
||||
|
||||
所有新增/修改 API 必须使用以下格式定义契约,作为前后端对齐的桥梁:
|
||||
|
||||
```
|
||||
### POST /api/v1/enterprises
|
||||
描述: 创建企业
|
||||
|
||||
请求体:
|
||||
- name: string (required) — 企业名称,最长 100 字符
|
||||
- code: string (required) — 企业编码,唯一,正则 ^[A-Z0-9-]+$
|
||||
- contact_email: string (optional) — 联系邮箱
|
||||
|
||||
响应 200:
|
||||
- id: int
|
||||
- name: string
|
||||
- code: string
|
||||
- created_at: datetime
|
||||
|
||||
错误:
|
||||
- 400: 参数校验失败(name/code 为空或格式错误)
|
||||
- 409: 编码已存在
|
||||
- 403: 无权限创建企业
|
||||
```
|
||||
|
||||
**B 级规则**:
|
||||
- 列出所有字段、类型、必填/可选
|
||||
- 标注校验规则(长度、格式、唯一性)
|
||||
- 列出所有可能的错误码和含义
|
||||
- 不需要 JSON 示例(A 级才需要)
|
||||
|
||||
---
|
||||
|
||||
## 开发设计文档模板
|
||||
|
||||
```markdown
|
||||
# [需求标题] 开发设计文档
|
||||
|
||||
## 1. 需求概述
|
||||
|
||||
### 1.1 需求信息
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 需求编号 | REQ-YYYYMMDD-XXXX |
|
||||
| 需求标题 | [标题] |
|
||||
| 优先级 | high/medium/low |
|
||||
| 预估工时 | Xh |
|
||||
|
||||
### 1.2 功能点清单
|
||||
- [ ] 功能点1:[描述]
|
||||
- [ ] 功能点2:[描述]
|
||||
|
||||
---
|
||||
|
||||
## 2. 代码库分析
|
||||
|
||||
### 2.1 相关现有代码
|
||||
| 文件路径 | 层级 | 当前功能 | 改动类型 |
|
||||
|---------|------|----------|---------|
|
||||
| `backend/models/xxx.go` | Model | 数据模型 | 新增字段 |
|
||||
| `backend/services/xxx_service.go` | Service | 业务逻辑 | 新增方法 |
|
||||
|
||||
### 2.2 可复用代码
|
||||
- [描述已有的可参考实现]
|
||||
|
||||
### 2.3 依赖关系
|
||||
- [需要注意的调用链和依赖]
|
||||
|
||||
---
|
||||
|
||||
## 3. API 契约
|
||||
|
||||
### 3.1 新增接口
|
||||
|
||||
### POST /api/v1/xxx
|
||||
描述: [功能描述]
|
||||
|
||||
请求体:
|
||||
- field1: type (required) — 说明
|
||||
- field2: type (optional) — 说明
|
||||
|
||||
响应 200:
|
||||
- field1: type
|
||||
- field2: type
|
||||
|
||||
错误:
|
||||
- 400: [说明]
|
||||
- 404: [说明]
|
||||
|
||||
### 3.2 修改接口
|
||||
[列出需要修改的现有接口及变更内容]
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据模型变更
|
||||
|
||||
### 4.1 新增表
|
||||
| 表名 | 说明 |
|
||||
|------|------|
|
||||
| xxx | [用途] |
|
||||
|
||||
**字段设计**:
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
|------|------|------|------|
|
||||
| id | BIGSERIAL | PK | 主键 |
|
||||
| tenant_id | BIGINT | NOT NULL, INDEX | 租户ID |
|
||||
| name | VARCHAR(255) | NOT NULL | 名称 |
|
||||
| created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 创建时间 |
|
||||
|
||||
**索引**:
|
||||
| 索引名 | 字段 | 类型 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| idx_xxx_tenant | tenant_id | B-tree | 租户查询 |
|
||||
|
||||
### 4.2 修改表
|
||||
| 表名 | 变更 | 说明 |
|
||||
|------|------|------|
|
||||
| xxx | 新增字段 yyy | [用途] |
|
||||
|
||||
### 4.3 数据迁移(如需要)
|
||||
[迁移方案描述]
|
||||
|
||||
---
|
||||
|
||||
## 5. 变更文件清单
|
||||
|
||||
### 后端
|
||||
| 文件 | 层级 | 改动类型 | 改动说明 |
|
||||
|------|------|---------|---------|
|
||||
| `models/xxx.go` | Model | 修改 | 新增字段 |
|
||||
| `database/xxx_repository.go` | Repository | 修改 | 新增查询方法 |
|
||||
| `services/xxx_service.go` | Service | 修改 | 新增业务方法 |
|
||||
| `handlers/xxx_handler.go` | Handler | 修改 | 新增接口 |
|
||||
| `routes/xxx_routes.go` | Route | 修改 | 注册新路由 |
|
||||
| `migrations/YYYYMMDD_xxx.up.sql` | Migration | 新增 | 表结构变更 |
|
||||
|
||||
### 前端
|
||||
| 文件 | 层级 | 改动类型 | 改动说明 |
|
||||
|------|------|---------|---------|
|
||||
| `types/xxx.ts` | Types | 修改 | 新增接口定义 |
|
||||
| `services/xxxService.ts` | Service | 修改 | 新增 API 调用 |
|
||||
| `pages/XxxPage.tsx` | Page | 修改 | 新增功能 UI |
|
||||
|
||||
---
|
||||
|
||||
## 6. 开发任务拆分
|
||||
|
||||
### 6.1 任务列表
|
||||
| # | 任务标题 | 预估工时 | 依赖 |
|
||||
|---|---------|---------|------|
|
||||
| 1 | [后端] Model + Migration | 1h | - |
|
||||
| 2 | [后端] Repository + Service | 2h | #1 |
|
||||
| 3 | [后端] Handler + Route | 2h | #2 |
|
||||
| 4 | [前端] Types + Service | 1h | #3 |
|
||||
| 5 | [前端] 页面开发 | 3h | #4 |
|
||||
| 6 | [测试] 单元测试 | 2h | #3, #5 |
|
||||
|
||||
### 6.2 实施顺序
|
||||
[按依赖关系排列的开发步骤]
|
||||
|
||||
---
|
||||
|
||||
## 7. 技术风险评估
|
||||
|
||||
### 7.1 影响分析
|
||||
| 改动类型 | 影响范围 | 风险等级 | 应对措施 |
|
||||
|---------|---------|---------|---------|
|
||||
| [描述] | [范围] | 高/中/低 | [措施] |
|
||||
|
||||
### 7.2 风险清单
|
||||
- [ ] 数据库:是否需要数据迁移?是否影响数据完整性?
|
||||
- [ ] API:是否破坏兼容性?是否需要版本管理?
|
||||
- [ ] 前端:是否影响现有页面?是否有性能瓶颈?
|
||||
- [ ] 业务:是否影响核心流程?是否需要灰度?
|
||||
|
||||
### 7.3 回退方案
|
||||
[如果上线出问题,如何回退]
|
||||
|
||||
---
|
||||
|
||||
## 8. 注意事项
|
||||
[特殊说明、边界情况、兼容性要求]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 任务拆分规则
|
||||
|
||||
### SMART 原则
|
||||
|
||||
- **Specific**(具体):明确要修改哪个文件/模块
|
||||
- **Measurable**(可衡量):清晰的完成标准
|
||||
- **Achievable**(可实现):单个任务 2-4 小时完成
|
||||
- **Relevant**(相关):与需求直接相关
|
||||
- **Time-bound**(有时限):预估工时
|
||||
|
||||
### 拆分粒度
|
||||
|
||||
- ✅ 好的粒度:`[Handler] 修改 manual_handler.go 添加发布接口(2h)`
|
||||
- ❌ 太粗:`[后端] 实现所有后端功能`
|
||||
- ❌ 太细:`[Handler] 添加 import 语句`
|
||||
|
||||
### 按文件拆分(推荐)
|
||||
- 每个文件对应一个子任务
|
||||
- 格式:`[层级] 修改 文件名 简要说明`
|
||||
|
||||
### 按功能模块拆分(大改动)
|
||||
- 将相关文件归为一个模块
|
||||
- 格式:`[模块] 功能描述`
|
||||
|
||||
### 标准开发顺序
|
||||
|
||||
```
|
||||
第一阶段:数据层
|
||||
├── Model 层:定义数据结构
|
||||
├── Migration:创建/修改表
|
||||
└── Repository 层:实现数据访问
|
||||
|
||||
第二阶段:业务层
|
||||
├── Service 层:实现业务逻辑
|
||||
└── 编写单元测试
|
||||
|
||||
第三阶段:接口层
|
||||
├── Handler 层:实现 HTTP 接口
|
||||
├── Route 层:注册路由
|
||||
└── API 文档(Swagger)
|
||||
|
||||
第四阶段:前端层
|
||||
├── Types:定义 TypeScript 接口
|
||||
├── Services:封装 API 调用
|
||||
├── Components:开发可复用组件
|
||||
└── Pages:实现业务页面
|
||||
|
||||
第五阶段:测试与优化
|
||||
├── 后端单元测试
|
||||
├── 前端组件测试
|
||||
└── E2E 测试
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 技术风险评估框架
|
||||
|
||||
### 影响分析矩阵
|
||||
|
||||
| 改动类型 | 影响范围 | 风险等级 | 应对措施 |
|
||||
|---------|---------|---------|---------|
|
||||
| 新增字段(非必填) | 低 | 低 | 添加 Migration,向后兼容 |
|
||||
| 修改字段类型 | 中 | 中 | 数据迁移脚本,充分测试 |
|
||||
| 删除字段 | 高 | 高 | 确认无依赖,先标记废弃 |
|
||||
| 新增 API 接口 | 低 | 低 | 接口设计评审 |
|
||||
| 修改 API 接口 | 中 | 中 | 保留旧接口,添加版本号 |
|
||||
| 删除 API 接口 | 高 | 高 | 确认无调用,发布公告 |
|
||||
| 新增前端页面 | 低 | 低 | 路由冲突检查 |
|
||||
| 修改核心组件 | 高 | 高 | 充分测试所有使用场景 |
|
||||
|
||||
### 性能影响评估
|
||||
|
||||
| 维度 | 评估项 |
|
||||
|------|--------|
|
||||
| 数据库 | 查询复杂度、索引使用、数据量、并发影响 |
|
||||
| API | 响应时间预期、QPS 预估、缓存策略、限流需求 |
|
||||
| 前端 | 首屏加载影响、渲染性能、内存占用 |
|
||||
|
||||
---
|
||||
|
||||
## 与 ai-proj 集成
|
||||
|
||||
### 文档创建
|
||||
|
||||
```typescript
|
||||
// 创建开发设计文档并关联任务
|
||||
mcp__ai-proj__create-and-attach({
|
||||
taskId: <设计任务ID>,
|
||||
title: "开发设计文档 - [需求标题]",
|
||||
content: "<使用上方模板生成的内容>"
|
||||
})
|
||||
```
|
||||
|
||||
### 子任务拆分
|
||||
|
||||
```typescript
|
||||
mcp__ai-proj__create_subtask({
|
||||
parentId: <开发任务ID>,
|
||||
title: "[Handler] 修改 manual_handler.go 添加发布接口"
|
||||
})
|
||||
```
|
||||
|
||||
### 进度更新
|
||||
|
||||
```typescript
|
||||
mcp__ai-proj__update_task_document({
|
||||
taskId: <设计任务ID>,
|
||||
content: "<更新后的设计文档>"
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 插件触发
|
||||
|
||||
| 插件 | 触发条件 | 说明 |
|
||||
|------|---------|------|
|
||||
| `dev-arch` | 新模块/新表/复杂架构 | 深度架构设计 |
|
||||
| `db-migration` | 涉及数据库变更 | 数据库迁移方案 |
|
||||
|
||||
当检测到以上条件时,建议用户启用对应插件。
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **先验证再设计** — 5 分钟验证省 3 小时返工
|
||||
2. **API 契约先行** — 前后端对齐的桥梁,先定义再实现
|
||||
3. **复用优于新建** — 充分探索代码库,识别可复用代码
|
||||
4. **风险前置** — 在设计阶段识别风险,而非编码时才发现
|
||||
5. **粒度适中** — 任务拆分 2-4h,不要太粗也不要太细
|
||||
6. **文档可操作** — 开发者可直接按设计文档实施
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 如何确定是否需要新增数据库表?
|
||||
检查:新数据是否与现有实体有独立生命周期?是否需要独立查询?是否 1:N 或 N:N 关系?任一满足则新增表。
|
||||
|
||||
### Q2: 什么时候需要创建子任务?
|
||||
修改 3 个以上文件 / 预估工时 > 4h / 涉及多个层级 → 创建子任务。
|
||||
|
||||
### Q3: API 契约需要多详细?
|
||||
B 级:字段 + 类型 + 必填/可选 + 校验规则 + 错误码。不需要 JSON 示例。
|
||||
|
||||
### Q4: 设计文档和 PRD 的边界?
|
||||
PRD 说「做什么」(用户故事、业务规则、验收标准),设计文档说「怎么做」(API、数据模型、文件清单)。
|
||||
|
||||
---
|
||||
|
||||
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||
|
||||
**规则:本 skill 产出的设计文档禁止受 auto-memory 影响结构和字段定义。**
|
||||
|
||||
### 禁止行为
|
||||
1. 不得用 memory 里的历史 API 契约填充当前设计(避免张冠李戴)
|
||||
2. 不得根据 memory 偏好省略"变更文件清单"/"数据模型变更"等章节
|
||||
3. 不得读取 `~/.claude/projects/*/memory/` 生成 API 字段或 SQL
|
||||
|
||||
### 允许行为
|
||||
- memory 可影响交互风格、命令推荐
|
||||
- memory 可记住"用户偏好 RESTful 而非 RPC" 这类**偏好**,但字段定义必须基于当前需求
|
||||
|
||||
### Why
|
||||
设计文档是开发契约,字段/接口/表结构错一个字就是 bug。memory 污染会让 AI 脑补出"看起来像但不对"的字段。必须严格基于当前 PRD + 代码现状生成。
|
||||
|
||||
**参考**:devflow-claude `plugins/req/commands/_common.md` 同名规则。
|
||||
|
||||
---
|
||||
|
||||
## 变更记录
|
||||
|
||||
| 版本 | 日期 | 变更内容 |
|
||||
|------|------|----------|
|
||||
| V1.0 | 2026-01-26 | 初始版本(原名 req-dev) |
|
||||
| V2.0 | 2026-04-06 | 重构为 req-design:移除架构指南和编码规范,聚焦 API 契约 + 任务拆分 |
|
||||
| V2.1 | 2026-04-16 | 新增 Memory 隔离规则(REQ-20260416-0017) |
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"name": "req-dev-plugin",
|
||||
"description": "Plugin for req-dev",
|
||||
"description": "[已废弃] 请使用 req-design-plugin。需求开发设计功能已迁移。",
|
||||
"version": "1.0.0",
|
||||
"deprecated": true,
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ analysis → design → dev → review → testing → [待部署池] → releas
|
||||
- **操作前先确认实际 ID** — 从 URL 提取 ID(如 `/requirements/897` → ID=897)
|
||||
- **务实路线关闭也必须补全关联任务** — 每个进度条阶段需创建关联任务
|
||||
- **阶段内容门禁(防空转)** — `/req next` 时检查关键阶段任务是否有实质内容
|
||||
- **Harness 环境检测** — `/req dev` 启动开发前,快速检测项目基础设施(`.husky/` 存在?`scripts/check-*.sh` 存在?)。若两者都不存在(Level 0),输出一行提示:「项目无质量护栏,建议先运行 `/harness init`」。检测结果不阻塞开发,仅提示
|
||||
|
||||
## 禁止直接调用(必须走命令流程)
|
||||
|
||||
@@ -91,23 +92,40 @@ Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD
|
||||
5. /req review pass → 评审通过 (approved, delivery_stage=analysis)
|
||||
6. /req next → 推进到 design/dev 阶段
|
||||
7. /req dev → 启动开发(创建【开发】任务, delivery_stage=dev)
|
||||
8. /req next → 推进到 review 阶段
|
||||
9. /req cr → 代码评审(创建【代码评审】任务)
|
||||
7.5 /req ci → CI 质量门禁检查与自动修复(PR 提交后执行)
|
||||
8. /req next → 推进到 review(需 quality_gate 证据)
|
||||
9. /req cr → 代码评审 + 约定检查 + bug约定建议(⭐ harness 自动)
|
||||
10. /req next → 推进到 testing 阶段
|
||||
11. /req test → 测试验收(5-Gate 流程)
|
||||
11. /req test → 测试验收(Gate 0B 约定检查 ⭐ + Gate 1-5)
|
||||
12. /req deploy → 批量部署(build-and-push.sh → Jenkins ai-proj)
|
||||
13. /req done → 归档 (archived) + git commit + push
|
||||
```
|
||||
|
||||
> ⭐ 标记的步骤是 Harness Engineering 自动嵌入的,无需手动调用。
|
||||
|
||||
**5 阶段文档**:
|
||||
|
||||
| 阶段 | 文档名 | 技能 |
|
||||
|------|--------|------|
|
||||
| PRD | 01-PRD.md | `req-prd` |
|
||||
| 测试 | 02-测试报告.md | 自动生成 |
|
||||
| 发布 | 03-发布记录.md | 自动生成 |
|
||||
| 发布 | 03-发布记录.md | 自动生成(含 PDV 验收章节) |
|
||||
| 归档 | 04-生命周期总结.md | 自动生成 |
|
||||
|
||||
**03-发布记录.md 中 PDV 章节模板**:
|
||||
|
||||
```markdown
|
||||
### 部署后验收 (PDV)
|
||||
| 检查项 | 结果 | 截图 |
|
||||
|--------|------|------|
|
||||
| 页面可达: /path/to/page | ✅ PASS | [截图] |
|
||||
| 菜单可见: {菜单名} | ✅ PASS | [截图] |
|
||||
| 基础渲染: 无白屏/JS 报错 | ✅ PASS | [截图] |
|
||||
| API: /api/v1/{路径} | ✅ PASS | — |
|
||||
|
||||
PDV 结论: ✅ 全部通过 / ❌ {N}项失败,阻断推进
|
||||
```
|
||||
|
||||
## 任务命名规范
|
||||
|
||||
| linkRole | 前缀 | 示例 |
|
||||
@@ -118,6 +136,7 @@ Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD
|
||||
| code_review | 【代码评审】 | 【代码评审】CR: 代码审查 |
|
||||
| test | 【测试】 | 【测试】集成测试验证 |
|
||||
| deploy | 【部署】 | 【部署】部署到 staging |
|
||||
| verification | 【验收】 | 【验收】PDV: OKR 功能部署验收 |
|
||||
| documentation | 【文档】 | 【文档】API 文档更新 |
|
||||
|
||||
## 阶段内容门禁(防空转)
|
||||
@@ -132,6 +151,24 @@ Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD
|
||||
|
||||
未通过 → AskUserQuestion(补充文档 or 强制跳过+记录原因)
|
||||
|
||||
### 部署门禁(推进到 released 前)
|
||||
|
||||
`/req deploy` 推进 `released` 前,必须通过 3 道门禁:
|
||||
|
||||
```
|
||||
Deploy Gate 1: 健康检查 ── /health 返回 200,服务存活
|
||||
Deploy Gate 2: PDV 验收 ── linkRole=verification 的【验收】任务存在且 completed
|
||||
Deploy Gate 3: 证据完整 ── 验收任务有文档,含检查项表格 + E2E 截图 + 通过/失败结论
|
||||
```
|
||||
|
||||
**PDV 验收任务生命周期**:
|
||||
1. `/req deploy` 步骤 6 自动创建:`ai-proj task create --title "【验收】PDV: {需求标题}"`
|
||||
2. 关联需求:`ai-proj req link --id <req_id> --task-ids <task_id> --link-role verification`
|
||||
3. 执行 Playwright PDV 冒烟测试
|
||||
4. 附加验收文档(检查项表格 + 截图证据 + 结论):`ai-proj task append-doc --id <task_id>`
|
||||
5. **全部通过** → `ai-proj task complete --id <task_id>` → 门禁放行
|
||||
6. **任一失败** → 任务保持 in_progress,阻断推进,报告失败项
|
||||
|
||||
### CR 五视角扫描法
|
||||
|
||||
**核心原则**:实现阶段关注"怎么让它跑通",评审阶段关注"怎么让它出错"。AI 必须**切换到对抗性思维**,逐一用以下 5 个视角扫描代码。
|
||||
@@ -271,6 +308,10 @@ Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD
|
||||
5. 自动创建目标阶段建议任务
|
||||
6. 展示:「✅ 已创建: #XXXX {任务名}。如需撤销请说明」
|
||||
|
||||
**dev → review 门禁**:
|
||||
- `pr_created`: PR 已创建
|
||||
- `quality_gate`: CI 质量门禁通过记录(质量门禁未通过时提示:「CI 质量门禁未通过,请先运行 `/req ci`」)
|
||||
|
||||
**阶段顺序**:`analysis → design → dev → review → testing → [待部署池] → released`
|
||||
|
||||
**`/req resume [REQ-ID]`** — 会话断点恢复:`ai-proj req get --id <id>` 获取 delivery_stage + 任务状态 + 建议操作
|
||||
@@ -287,26 +328,73 @@ Gate 4: 完整性检查 ── 三段式完整 + 验收标准 ≥ 2 条 + PRD
|
||||
2. `git diff` / `find` 确定变更范围(文件数、行数)
|
||||
3. 读取所有变更文件源码(非 test 文件)
|
||||
4. **五视角扫描**:逐一用攻击者/泄露者/并发者/边界者/依赖者视角审查
|
||||
5. `ai-proj task create` 创建【代码评审】任务并关联需求(linkRole=code_review)
|
||||
6. `ai-proj create-and-attach` 附加 CR 报告文档(必须含五视角扫描结果)
|
||||
7. 展示发现摘要,AskUserQuestion 确认是否创建 bug 修复任务
|
||||
8. 若有 High/Critical 发现 → `ai-proj task create` 创建关联修复任务
|
||||
5. **约定检查**:运行所有 `scripts/check-*.sh --ci`,将结果写入 CR 报告
|
||||
6. **⭐ Bug 约定建议**(bug 类需求自动触发:category=bug,或标题含 fix/修复/bug,或描述含根因/复现步骤):
|
||||
- 分析本次 bug 的根因模式
|
||||
- 判断可检测性:能写 grep 找到同类问题?(规则见 convention-flow.md「可检测性判断」)
|
||||
- 不可检测 → 仅在 CR 报告记录根因,跳过
|
||||
- 可检测 → 检查已有 `scripts/check-*.sh` 是否已覆盖 → 已覆盖则跳过
|
||||
- 扫描代码库统计同类模式数量 N
|
||||
- AskUserQuestion(N=0: 防未来复发;N>0: 防恶化+逐步清理)
|
||||
- 是 → 执行 convention flow → 产出物**单独 commit**(`chore: 建立 {name} 约定`)
|
||||
7. `ai-proj task create` 创建【代码评审】任务并关联需求(linkRole=code_review)
|
||||
8. `ai-proj create-and-attach` 附加 CR 报告文档(必须含五视角扫描结果 + 约定检查结果)
|
||||
9. 展示发现摘要,AskUserQuestion 确认是否创建 bug 修复任务
|
||||
10. 若有 High/Critical 发现 → `ai-proj task create` 创建关联修复任务
|
||||
|
||||
**`/req ci [REQ-ID]`** — CI 质量门禁检查与自动修复:
|
||||
1. `check-ci-status.sh` 检查当前分支 CI 状态
|
||||
- 通过 → 写入 `quality_gate` 门禁证据(`gate-callback.sh`)→ 完成
|
||||
- 运行中 → `--wait` 等待最多 5 分钟
|
||||
- 失败 → 进入自动修复循环
|
||||
2. 自动修复循环(最多 3 轮):
|
||||
a. `fetch-ci-logs.sh --failed-only` 获取失败 job 日志
|
||||
b. AI 诊断根因(见错误模式目录)
|
||||
c. 自动修复代码
|
||||
d. 本地验证(`go vet ./...` / `npx tsc --noEmit`)
|
||||
e. `git commit && git push`
|
||||
f. `check-ci-status.sh --trigger --wait` 等待新 CI
|
||||
g. 通过 → 写入门禁证据 → 完成
|
||||
h. 仍失败 → 下一轮(超过 3 轮 → AskUserQuestion 是否继续)
|
||||
- 参数:`--trigger` 手动触发 CI,`--wait` 等待完成,`--no-fix` 仅诊断不修复
|
||||
|
||||
**错误模式目录**:
|
||||
|
||||
| 失败 Job | 错误模式 | 修复策略 |
|
||||
|----------|---------|---------|
|
||||
| architecture-gate | 架构 ratchet 违规 | 读取违规文件,用 service 层替代直接 database import |
|
||||
| backend-lint (go vet) | `file.go:42: unreachable code` | 读取文件,修复具体问题 |
|
||||
| backend-lint (gofmt) | `Files need gofmt:` | 运行 `gofmt -s -w` |
|
||||
| backend-lint (go test) | `--- FAIL: TestXxx` | 读取测试和实现,修复根因 |
|
||||
| frontend-lint (ESLint) | `warnings N > 4600` | 对比 main,只修复新增警告 |
|
||||
| frontend-lint (Prettier) | `unformatted N > 1000` | `npx prettier --write` 变更文件 |
|
||||
| frontend-lint (TS) | `errors N > 60` | 对比 main,只修复新增类型错误 |
|
||||
|
||||
**`/req test [REQ-ID]`** — 测试(前置:代码评审通过),遵循 dev-test 技能的 5-Gate 流程
|
||||
|
||||
**`/req deploy [--project <name>] [--env production]`** — 项目级批量部署(Staging 已自动化,push develop 即触发):
|
||||
1. 收集待部署需求(delivery_stage=testing + 全 test 任务 completed)
|
||||
2. AskUserQuestion 确认范围
|
||||
3. `ai-proj task create` 创建部署批次任务
|
||||
3. `ai-proj task create` 创建【部署】批次任务(linkRole=deploy)
|
||||
4. 部署前检查(变更检测、数据库迁移)
|
||||
5. 执行 `./scripts/build-and-push.sh prod --detect --deploy --wait --verify`
|
||||
6. 部署后验证(截图 + 定向验证)
|
||||
7. `ai-proj task append-doc` 记录部署文档 + 写入需求历史
|
||||
8. `ai-proj req advance --id <id> --to released` 批量推进
|
||||
6. **Deploy Gate 1: 健康检查** — `/health` 返回 200
|
||||
7. **Deploy Gate 2+3: PDV 验收**(为每个需求创建独立验收任务):
|
||||
a. `ai-proj task create --title "【验收】PDV: {需求标题}"` 创建验收任务
|
||||
b. `ai-proj req link --id <req_id> --task-ids <task_id> --link-role verification` 关联需求
|
||||
c. 从需求关联任务中提取前端变更范围(页面路由、菜单项、关键 API)
|
||||
d. 生成验收检查清单(页面可达 + 菜单可见 + 基础渲染 + API 连通)
|
||||
e. 用 Playwright 对部署环境执行冒烟测试:`E2E_BASE_URL=<部署环境URL> npx playwright test e2e/pdv/ --project=chromium`
|
||||
f. 收集截图证据
|
||||
g. `ai-proj task append-doc --id <task_id>` 附加验收文档(检查项表格 + 截图 + 结论)
|
||||
h. **全部通过** → `ai-proj task complete --id <task_id>` → Gate 2+3 放行
|
||||
i. **任一失败** → 任务保持 in_progress,**阻断推进**,报告失败项
|
||||
8. `ai-proj task append-doc` 记录【部署】任务文档(构建日志 + 健康检查 + PDV 结果摘要)
|
||||
9. `ai-proj req advance --id <id> --to released` 批量推进(仅 Gate 1-3 全部通过的需求)
|
||||
|
||||
**`/req done [REQ-ID]`** — 类型化归档门禁 + git commit + push + `ai-proj req archive --id <id>`:
|
||||
- **推断类型**:有 implementation → code;无 implementation 有 prd/test → skill;仅 deploy → ops
|
||||
- **code 检查**:delivery_stage=released + deploy 任务完成 + 部署文档 + 所有任务完成
|
||||
- **code 检查**:delivery_stage=released + deploy 任务完成 + verification 任务完成(PDV 通过) + 部署文档 + 所有任务完成
|
||||
- **skill 检查**:delivery_stage≥testing + 所有任务完成
|
||||
- **ops 检查**:deploy 任务完成 + 所有任务完成
|
||||
|
||||
@@ -332,8 +420,10 @@ ai-proj task append-doc --id 5214 --content "# PRD: 用户认证功能\n..."
|
||||
| design | 【评审】技术设计: {需求标题} | design |
|
||||
| dev | 【开发-后端】{需求标题}、【开发-前端】{需求标题} | implementation |
|
||||
| review | 【代码评审】{需求标题} | code_review |
|
||||
| review | 【约定】{约定名称}(bug 类需求,convention flow 自动创建) | documentation |
|
||||
| testing | 【测试】集成测试: {需求标题} | test |
|
||||
| deploy | 由 `/req deploy` 批量创建 | deploy |
|
||||
| released | 【验收】PDV: {需求标题}(`/req deploy` 自动创建) | verification |
|
||||
|
||||
## 测试环境流程
|
||||
|
||||
@@ -352,7 +442,8 @@ ai-proj task append-doc --id 5214 --content "# PRD: 用户认证功能\n..."
|
||||
| implementation | 【开发】 | dev | 50% |
|
||||
| code_review | 【代码评审】 | review | 5% |
|
||||
| test | 【测试】 | testing | 15% |
|
||||
| deploy | 【部署】 | staging/released | 10% |
|
||||
| deploy | 【部署】 | staging/released | 5% |
|
||||
| verification | 【验收】 | released | 5% |
|
||||
| documentation | 【文档】 | any | 5% |
|
||||
|
||||
## 标准任务结构
|
||||
@@ -365,7 +456,9 @@ ai-proj task append-doc --id 5214 --content "# PRD: 用户认证功能\n..."
|
||||
├── 🔍 review → 【代码评审】CR: {需求标题} (linkRole: code_review)
|
||||
├── 🧪 testing → 【测试】集成测试: {需求标题} (linkRole: test)
|
||||
├── 🚀 staging → 【部署】部署到 staging (linkRole: deploy)
|
||||
└── 🏁 released → 【部署】部署到 prod (linkRole: deploy)
|
||||
└── 🏁 released
|
||||
├── 【部署】部署到 prod (linkRole: deploy)
|
||||
└── 【验收】PDV: {需求标题} (linkRole: verification)
|
||||
```
|
||||
|
||||
## ai-proj CLI 速查
|
||||
@@ -389,7 +482,7 @@ ai-proj req tasks --id <id> # 查看关联任务
|
||||
|
||||
**开发阶段**: `backlog`, `analysis`, `design`, `dev`, `review`, `integration`, `testing`, `staging`, `released`
|
||||
|
||||
**linkRole**: `prd`, `design`, `implementation`, `code_review`, `test`, `deploy`, `regression`, `documentation`
|
||||
**linkRole**: `prd`, `design`, `implementation`, `code_review`, `test`, `deploy`, `verification`, `regression`, `documentation`
|
||||
|
||||
## 相关技能
|
||||
|
||||
@@ -398,3 +491,4 @@ ai-proj req tasks --id <id> # 查看关联任务
|
||||
| `req-prd` | PRD 文档编写 + 评审方法论 |
|
||||
| `req-prototype` | Stitch 原型生成 + 迭代 |
|
||||
| `dev-test` | 测试 + 质量门禁 |
|
||||
| `req-test-gate` (harness) | 工程约束方法论。Gate 0-2 的约定检查由项目本地脚本定义,方法论见 `/harness` 命令 |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "req-prd-plugin",
|
||||
"description": "Plugin for req-prd",
|
||||
"version": "1.0.0",
|
||||
"description": "产品需求设计技能。PRD 文档编写、需求分析、用户故事、对比式分析。纯产品视角,不含技术实现。",
|
||||
"version": "2.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
}
|
||||
|
||||
@@ -9,248 +9,23 @@ description: 产品设计与需求管理。用于 PRD 文档编写、需求分
|
||||
|
||||
本技能用于辅助产品设计和需求管理工作,包括:
|
||||
- PRD 文档编写与管理
|
||||
- **参考对象对比式 PRD 编写**(核心能力)
|
||||
- 需求分析与优先级排序
|
||||
- 用户故事创建
|
||||
- 功能设计与规划
|
||||
- 与 ai-proj 任务系统集成
|
||||
|
||||
---
|
||||
|
||||
## 参考对象对比式 PRD 编写
|
||||
|
||||
当进行**系统平移、功能迁移、竞品借鉴**时,应采用对比分析法编写 PRD,确保新系统功能完整且有所改进。
|
||||
|
||||
### 适用场景
|
||||
|
||||
| 场景 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| 系统平移 | 旧系统迁移到新技术栈 | 酷采2.0 → 酷采3.0 |
|
||||
| 功能借鉴 | 参考竞品功能设计 | 参考飞书设计协作功能 |
|
||||
| 版本升级 | 基于当前版本优化 | V1.0 → V2.0 重构 |
|
||||
|
||||
### 对比分析工作流
|
||||
|
||||
```
|
||||
1. 确定参考对象
|
||||
├── 识别参考系统(可以是多个)
|
||||
├── 获取访问权限(测试环境、源代码)
|
||||
└── 明确对比目标
|
||||
|
||||
2. 参考对象分析
|
||||
├── 功能调研(前端页面操作)
|
||||
├── 数据模型分析(数据库表结构)
|
||||
├── 业务逻辑分析(后端代码)
|
||||
└── API 接口分析
|
||||
|
||||
3. 对比分析
|
||||
├── 功能对比表(保留/优化/新增/废弃)
|
||||
├── 数据模型对比(字段映射、新增字段)
|
||||
├── 技术架构对比
|
||||
└── 用户体验对比
|
||||
|
||||
4. PRD 编写
|
||||
├── 背景说明(明确参考来源)
|
||||
├── 功能需求(标注来源与变更)
|
||||
├── 数据设计(标注字段来源)
|
||||
└── 实现建议
|
||||
```
|
||||
|
||||
### 对比式 PRD 模板
|
||||
|
||||
```markdown
|
||||
# [功能模块名称] PRD
|
||||
|
||||
## 1. 文档概述
|
||||
|
||||
### 1.1 文档信息
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 文档名称 | [模块名称] 需求文档 |
|
||||
| 版本 | V1.0 |
|
||||
| 创建日期 | [日期] |
|
||||
| **需求来源** | **[参考系统名称] 平移/借鉴** |
|
||||
| **参考系统** | **[参考系统访问地址]** |
|
||||
|
||||
### 1.2 背景说明
|
||||
本需求文档基于 **[参考系统]** 的 [模块名称] 功能分析,将其平移至 [目标系统]。
|
||||
|
||||
**参考系统信息**:
|
||||
- 系统地址:[URL]
|
||||
- 技术栈:[技术栈描述]
|
||||
- 源码位置:[源码路径](如有)
|
||||
**插件扩展**:
|
||||
- `req-compare` — 对比式 PRD 编写(系统平移/竞品借鉴时激活)
|
||||
- `req-prototype` — UI 原型生成
|
||||
|
||||
---
|
||||
|
||||
## 2. 参考系统分析
|
||||
## 对比式 PRD 编写
|
||||
|
||||
### 2.1 功能截图
|
||||
[插入参考系统功能截图]
|
||||
> 系统平移、竞品借鉴、版本升级时,使用 `req-compare` 插件进行对比分析。
|
||||
> 该插件包含完整的对比工作流、对比式 PRD 模板和竞品分析模板。
|
||||
|
||||
### 2.2 数据模型(参考系统)
|
||||
```sql
|
||||
-- 参考系统表结构
|
||||
CREATE TABLE [表名] (
|
||||
-- 从源代码/数据库提取
|
||||
);
|
||||
```
|
||||
|
||||
### 2.3 API 接口(参考系统)
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| /api/xxx | GET/POST | [描述] |
|
||||
|
||||
### 2.4 业务逻辑(参考系统)
|
||||
- 核心业务规则摘要
|
||||
- 数据校验规则
|
||||
- 状态流转逻辑
|
||||
|
||||
---
|
||||
|
||||
## 3. 功能对比分析
|
||||
|
||||
### 3.1 功能对比表
|
||||
| 序号 | 功能 | 参考系统 | 目标系统 | 变更类型 | 说明 |
|
||||
|------|------|----------|----------|----------|------|
|
||||
| 1 | [功能1] | ✅ | ✅ | 保留 | 直接平移 |
|
||||
| 2 | [功能2] | ✅ | ✅+ | 优化 | [优化内容] |
|
||||
| 3 | [功能3] | ❌ | ✅ | 新增 | [新增原因] |
|
||||
| 4 | [功能4] | ✅ | ❌ | 废弃 | [废弃原因] |
|
||||
|
||||
### 3.2 数据模型对比
|
||||
| 参考系统字段 | 目标系统字段 | 类型 | 变更 | 说明 |
|
||||
|--------------|--------------|------|------|------|
|
||||
| id (varchar) | id (bigint) | PK | 优化 | 改用自增ID |
|
||||
| company_id | tenant_id | FK | 重命名 | 统一租户字段 |
|
||||
| -- | created_by | bigint | 新增 | 审计字段 |
|
||||
|
||||
### 3.3 技术架构对比
|
||||
| 层次 | 参考系统 | 目标系统 |
|
||||
|------|----------|----------|
|
||||
| 后端框架 | [如: Spring Boot] | [如: Go Gin] |
|
||||
| 前端框架 | [如: Vue 2] | [如: React 18] |
|
||||
| 数据库 | [如: MySQL] | [如: PostgreSQL] |
|
||||
|
||||
---
|
||||
|
||||
## 4. 目标系统设计
|
||||
|
||||
### 4.1 功能清单
|
||||
| 序号 | 功能 | 优先级 | 来源 | 说明 |
|
||||
|------|------|--------|------|------|
|
||||
| 1 | [功能] | P0 | 平移 | 从参考系统平移 |
|
||||
|
||||
### 4.2 数据模型(目标系统)
|
||||
```sql
|
||||
-- 目标系统表结构(基于对比分析设计)
|
||||
CREATE TABLE [表名] (
|
||||
id BIGINT PRIMARY KEY,
|
||||
-- 字段设计...
|
||||
);
|
||||
```
|
||||
|
||||
### 4.3 API 设计(目标系统)
|
||||
| 接口 | 方法 | 说明 | 参考接口 |
|
||||
|------|------|------|----------|
|
||||
| /api/v1/xxx | GET | [描述] | 参考 /api/xxx |
|
||||
|
||||
### 4.4 业务规则
|
||||
- [ ] 规则1(沿用参考系统)
|
||||
- [ ] 规则2(优化调整)
|
||||
|
||||
---
|
||||
|
||||
## 5. 实现建议
|
||||
|
||||
### 5.1 开发顺序
|
||||
1. 数据模型迁移
|
||||
2. 后端 API 实现
|
||||
3. 前端页面开发
|
||||
4. 数据迁移脚本
|
||||
|
||||
### 5.2 注意事项
|
||||
- 参考系统中 [xxx] 逻辑需要特别注意
|
||||
- 新系统中需改进 [xxx] 问题
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 参考对象分析工具
|
||||
|
||||
#### 1. 前端分析
|
||||
```bash
|
||||
# 启动浏览器调试模式(macOS)
|
||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir=/tmp/chrome-debug
|
||||
|
||||
# 访问参考系统,截图、分析交互
|
||||
```
|
||||
|
||||
#### 2. 后端代码分析
|
||||
```bash
|
||||
# 搜索相关模型
|
||||
grep -r "class.*Model" --include="*.java" /path/to/legacy/
|
||||
|
||||
# 搜索相关控制器
|
||||
grep -r "@Controller\|@RestController" --include="*.java" /path/to/legacy/
|
||||
```
|
||||
|
||||
#### 3. 数据库分析
|
||||
```sql
|
||||
-- 查看表结构
|
||||
SHOW CREATE TABLE table_name;
|
||||
|
||||
-- 查看字段注释
|
||||
SELECT COLUMN_NAME, COLUMN_COMMENT
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_NAME = 'table_name';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 示例:酷采3.0标签管理模块
|
||||
|
||||
以下是基于酷采2.0平移的标签管理模块对比分析示例:
|
||||
|
||||
#### 参考系统(酷采2.0)
|
||||
|
||||
**数据模型**:
|
||||
```sql
|
||||
-- 酷采2.0 prd_product_label 表
|
||||
CREATE TABLE `prd_product_label` (
|
||||
`id` varchar(64) NOT NULL,
|
||||
`label_name` varchar(256) DEFAULT NULL,
|
||||
`company_id` varchar(64) DEFAULT NULL,
|
||||
`input_user_id` varchar(64) DEFAULT NULL,
|
||||
`input_user_name` varchar(64) DEFAULT NULL,
|
||||
`input_time` datetime DEFAULT NULL,
|
||||
`update_user_id` varchar(64) DEFAULT NULL,
|
||||
`update_user_name` varchar(64) DEFAULT NULL,
|
||||
`update_time` datetime DEFAULT NULL,
|
||||
`version` bigint(20) DEFAULT 0,
|
||||
`is_delete` tinyint(1) DEFAULT 0,
|
||||
`status` tinyint(1) DEFAULT 1,
|
||||
`remark` varchar(512) DEFAULT NULL,
|
||||
`sort_no` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
```
|
||||
|
||||
**代码位置**:
|
||||
- 后端: `cool_lining/module-provider/.../dao/model/prd/PrdProductLabel.java`
|
||||
- 前端: `ln_admin/src/views/module/prd/product_label/`
|
||||
|
||||
#### 目标系统(酷采3.0)设计
|
||||
|
||||
**数据模型对比**:
|
||||
| 酷采2.0 | 酷采3.0 | 变更 |
|
||||
|---------|---------|------|
|
||||
| id (varchar) | id (bigint) | 改用自增ID |
|
||||
| company_id | tenant_id | 统一租户标识 |
|
||||
| input_user_id | created_by | 简化字段命名 |
|
||||
| input_time | created_at | 统一时间字段 |
|
||||
| is_delete | deleted_at | 改用软删除时间戳 |
|
||||
**触发方式**:当需求涉及参考系统时,req-prd 自动推荐激活 req-compare 插件。
|
||||
|
||||
---
|
||||
|
||||
@@ -523,36 +298,9 @@ mcp__ai-proj__export_task_document_to_file
|
||||
|
||||
---
|
||||
|
||||
## 竞品分析模板
|
||||
## 竞品分析
|
||||
|
||||
### 分析框架
|
||||
|
||||
```markdown
|
||||
# [竞品名称] 分析
|
||||
|
||||
## 1. 产品概述
|
||||
- 定位:
|
||||
- 核心功能:
|
||||
- 目标用户:
|
||||
|
||||
## 2. 功能对比
|
||||
| 功能 | 我们 | 竞品A | 竞品B |
|
||||
|------|------|-------|-------|
|
||||
| 功能1 | ✅/❌/部分 | ... | ... |
|
||||
|
||||
## 3. 优劣势分析
|
||||
### 优势
|
||||
1. ...
|
||||
|
||||
### 劣势
|
||||
1. ...
|
||||
|
||||
## 4. 可借鉴点
|
||||
- ...
|
||||
|
||||
## 5. 差异化策略
|
||||
- ...
|
||||
```
|
||||
> 竞品分析模板已移至 `req-compare` 插件。涉及竞品对比时自动激活。
|
||||
|
||||
---
|
||||
|
||||
@@ -610,13 +358,14 @@ mcp__ai-proj__export_task_document_to_file
|
||||
- [ ] 操作可撤销
|
||||
- [ ] 符合用户习惯
|
||||
|
||||
### 技术方案检查
|
||||
### 非功能需求检查
|
||||
|
||||
- [ ] 技术可行性验证
|
||||
- [ ] 性能影响评估
|
||||
- [ ] 扩展性考虑
|
||||
- [ ] 安全性审查
|
||||
- [ ] 兼容性测试
|
||||
- [ ] 性能要求已量化(响应时间、并发量)
|
||||
- [ ] 安全需求已明确(权限、数据保护)
|
||||
- [ ] 兼容性要求已定义(浏览器、设备)
|
||||
- [ ] 可用性目标已设定
|
||||
|
||||
> **技术方案可行性检查**在 design 阶段由 `req-design` 技能完成。
|
||||
|
||||
---
|
||||
|
||||
@@ -662,3 +411,34 @@ mcp__ai-proj__export_task_document_to_file
|
||||
- 数据安全分级
|
||||
- 敏感操作审计
|
||||
- 权限最小化原则
|
||||
|
||||
---
|
||||
|
||||
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||
|
||||
**规则:本 skill 涉及模板/文档产出的命令禁止受 auto-memory 影响产出物。**
|
||||
|
||||
### 禁止行为
|
||||
1. 不得根据 memory 中的偏好跳过或合并 PRD 模板章节
|
||||
2. 不得用 memory 里的历史需求/项目内容填充当前 PRD
|
||||
3. 不得根据 memory 反馈调整 PRD 章节顺序、表格列数、标题层级
|
||||
4. 不得读取 `~/.claude/projects/*/memory/` 辅助生成 PRD 正文
|
||||
|
||||
### 允许行为
|
||||
- memory 可影响**交互风格**(提问详略、确认节奏、语气)
|
||||
- memory 可指导**命令选择**(如根据用户习惯推荐先走 req-compare 还是 req-prd)
|
||||
- memory 可影响**非产出文本**(如对话中的说明)
|
||||
|
||||
### Why
|
||||
auto-memory 设计用于跨会话建立用户画像。但 PRD/需求文档是正式产出物,必须由**模板结构 + 当前输入**决定,不能因 memory 中的偏好自作主张调整结构,否则会导致:
|
||||
- 模板章节漂移(用户不知道为什么这次少了一章)
|
||||
- 历史项目内容污染(张冠李戴)
|
||||
- 产出不可复现
|
||||
|
||||
### How to apply
|
||||
执行 `/req prd` / PRD 编写 / 需求描述生成等命令时:
|
||||
- 仅读取:模板文件、用户当前输入、引用的已有需求文档
|
||||
- 不读取:memory 目录下的任何文件
|
||||
- 产出前自检:章节数量和顺序与模板完全一致
|
||||
|
||||
**参考**:devflow-claude 的 `plugins/req/commands/_common.md` 同名规则。
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "req-research-plugin",
|
||||
"description": "需求调研插件。代码审计、数据库分析、现有功能调研。挂载在 analysis 阶段,需要深度调研时激活。",
|
||||
"version": "1.0.0",
|
||||
"author": { "name": "qiudl" }
|
||||
}
|
||||
124
skills-req/req-research-plugin/skills/SKILL.md
Normal file
124
skills-req/req-research-plugin/skills/SKILL.md
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
name: req-research
|
||||
description: 需求调研插件。代码审计、数据库分析、现有功能调研、技术可行性评估。挂载在 analysis 阶段,需要深度调研时由 req-prd 推荐激活。
|
||||
---
|
||||
|
||||
# 需求调研插件 (req-research)
|
||||
|
||||
## 概述
|
||||
|
||||
当 PRD 编写需要**深入了解现有系统**时使用。提供结构化的调研方法。
|
||||
|
||||
**触发条件**:
|
||||
- 需求涉及修改现有功能(需了解当前实现)
|
||||
- 需求涉及数据库表结构变更
|
||||
- 需要技术可行性评估
|
||||
|
||||
## 调研方法
|
||||
|
||||
### 1. 前端功能调研
|
||||
|
||||
```bash
|
||||
# 启动本地开发环境
|
||||
cd frontend && npm start
|
||||
|
||||
# 访问相关页面,记录:
|
||||
# - 页面布局和交互流程
|
||||
# - 表单字段和校验规则
|
||||
# - 列表列和筛选条件
|
||||
# - 异常情况处理(空状态、错误提示)
|
||||
```
|
||||
|
||||
**调研模板**:
|
||||
```markdown
|
||||
### 页面: [页面名称] ([URL])
|
||||
- **功能**: [功能描述]
|
||||
- **字段**: [列出所有字段]
|
||||
- **操作**: [CRUD/筛选/排序/导出]
|
||||
- **校验**: [表单校验规则]
|
||||
- **截图**: [如有]
|
||||
```
|
||||
|
||||
### 2. 后端代码审计
|
||||
|
||||
```bash
|
||||
# 搜索相关模型
|
||||
Grep(pattern="type.*struct", path="backend/models", glob="*xxx*.go")
|
||||
|
||||
# 搜索相关 Handler
|
||||
Grep(pattern="func.*Handler.*xxx", path="backend/handlers")
|
||||
|
||||
# 搜索相关路由
|
||||
Grep(pattern="xxx", path="backend/routes")
|
||||
|
||||
# 搜索相关 Service
|
||||
Grep(pattern="func.*Service.*xxx", path="backend/services")
|
||||
```
|
||||
|
||||
**审计模板**:
|
||||
```markdown
|
||||
### 模块: [模块名]
|
||||
- **Model**: `backend/models/xxx.go` — [字段数]个字段
|
||||
- **Repository**: `backend/database/xxx_repository.go` — [方法数]个方法
|
||||
- **Service**: `backend/services/xxx_service.go` — [方法数]个方法
|
||||
- **Handler**: `backend/handlers/xxx_handler.go` — [接口数]个接口
|
||||
- **Route**: `backend/routes/xxx_routes.go`
|
||||
- **关键业务逻辑**: [描述]
|
||||
```
|
||||
|
||||
### 3. 数据库分析
|
||||
|
||||
```bash
|
||||
# 查看表结构(本地)
|
||||
psql -U ai_user -d ai_project -c "\d table_name"
|
||||
|
||||
# 查看数据量
|
||||
psql -U ai_user -d ai_project -c "SELECT COUNT(*) FROM table_name"
|
||||
|
||||
# 查看索引
|
||||
psql -U ai_user -d ai_project -c "\di table_name*"
|
||||
```
|
||||
|
||||
**分析模板**:
|
||||
```markdown
|
||||
### 表: [表名]
|
||||
- **字段数**: N
|
||||
- **数据量**: ~N 行
|
||||
- **索引**: [列出索引]
|
||||
- **关联**: [外键关系]
|
||||
- **特殊字段**: [JSON/Array/Enum 等]
|
||||
```
|
||||
|
||||
### 4. API 分析
|
||||
|
||||
```bash
|
||||
# 查看 Swagger 文档
|
||||
open http://localhost:8080/swagger/index.html
|
||||
|
||||
# 搜索 API 路由
|
||||
Grep(pattern="GET|POST|PUT|DELETE.*xxx", path="backend/routes")
|
||||
```
|
||||
|
||||
## 调研报告模板
|
||||
|
||||
```markdown
|
||||
## 调研报告 — {需求标题}
|
||||
|
||||
### 1. 现有功能
|
||||
[页面/功能调研结果]
|
||||
|
||||
### 2. 代码结构
|
||||
[代码审计结果,按分层列出]
|
||||
|
||||
### 3. 数据库现状
|
||||
[表结构、数据量、索引]
|
||||
|
||||
### 4. 技术可行性
|
||||
| 方案 | 优点 | 缺点 | 工时预估 |
|
||||
|------|------|------|---------|
|
||||
| 方案A | ... | ... | Xh |
|
||||
| 方案B | ... | ... | Xh |
|
||||
|
||||
### 5. 建议
|
||||
[推荐方案及原因]
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "req-test-gate-plugin",
|
||||
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5,含前后端联调+视觉验证)、部署级验证(Deploy Gates)、持续回归(Regression)。",
|
||||
"description": "测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "qiudl"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
name: req-test-gate
|
||||
description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5,含前后端联调+视觉验证)、部署级验证(Deploy Gates)、持续回归(Regression)。
|
||||
description: 测试与质量门禁制度。覆盖需求级测试(Gates 1-5)、部署级验证(Deploy Gates)、持续回归(Regression)、Harness Engineering 工程约束方法论(Ratchet、约定建立、门禁层级)。当用户提到质量门禁、架构约束、约定检查、ratchet、harness 相关任务时自动激活。
|
||||
arguments: <subcommand> [args]
|
||||
---
|
||||
|
||||
# 测试与质量门禁制度 (req-test-gate) v2.1
|
||||
@@ -36,12 +37,13 @@ Gate 5: 回归贡献检查(按 scope 区分贡献形式)
|
||||
通过 → 允许 /req next 推进到待部署池
|
||||
```
|
||||
|
||||
## Gate 0: Scope 分级(自动)
|
||||
## Gate 0: Scope 分级 + 约定检查(自动)
|
||||
|
||||
**从 `git diff` 自动推断变更范围,决定后续 Gate 的裁剪策略。**
|
||||
**两件事**:① 从 `git diff` 推断变更范围;② 运行项目内所有约定检查脚本。
|
||||
|
||||
### 0A: Scope 分级
|
||||
|
||||
```bash
|
||||
# 自动推断逻辑
|
||||
git diff main...HEAD --name-only | classify_scope
|
||||
```
|
||||
|
||||
@@ -56,6 +58,43 @@ git diff main...HEAD --name-only | classify_scope
|
||||
|
||||
**scope 影响全部后续 Gate**,在测试报告中必须标注。
|
||||
|
||||
### 0B: 约定检查(Harness 自动执行)
|
||||
|
||||
**自动发现并运行项目内所有 `scripts/check-*.sh` 脚本。**
|
||||
|
||||
```bash
|
||||
# 自动执行逻辑
|
||||
for script in scripts/check-*.sh; do
|
||||
[ -x "$script" ] && bash "$script" --ci
|
||||
done
|
||||
```
|
||||
|
||||
| 结果 | 处理 |
|
||||
|------|------|
|
||||
| 全部 PASS | Gate 0 通过,继续 Gate 1 |
|
||||
| 有 FAIL | **阻塞**,按下方「失败处理」修复后重新 `/req test` |
|
||||
| 无 scripts/check-*.sh | 跳过(项目未建立约定检查) |
|
||||
|
||||
**失败处理**(区分脚本类型):
|
||||
|
||||
| 脚本类型 | 判断方法 | 修复方式 |
|
||||
|---------|---------|---------|
|
||||
| Hard wall(纯检测) | 脚本无 baseline 文件 | 修改代码消除违规 |
|
||||
| Ratchet(基线对比) | 脚本有 `.*-baseline.json` | 修改代码降低违规数,**或**确认是故意增加 → `./scripts/check-{name}.sh baseline` 更新基线 + AskUserQuestion 确认 |
|
||||
|
||||
> 更新基线是有意识的决策(如重构导致临时增加),不能静默执行。
|
||||
|
||||
**报告格式**:
|
||||
```
|
||||
### 约定检查 (Gate 0B)
|
||||
| 脚本 | 结果 | 详情 |
|
||||
|------|------|------|
|
||||
| check-architecture.sh | ✅ PASS | 5 rules, all within baseline |
|
||||
| check-modal-safety.sh | ✅ PASS | 0 violations |
|
||||
```
|
||||
|
||||
> 这样 Harness 建立的约定脚本会在每次 `/req test` 时自动运行,无需手动执行 `/harness report`。
|
||||
|
||||
---
|
||||
|
||||
## Gate 1: 前置条件检查
|
||||
@@ -439,7 +478,8 @@ DG6: 生产部署(不可跳过) → 健康检查+文档
|
||||
```
|
||||
v1: Gate 1 → Gate 2 → Gate 3 → Gate 4
|
||||
v2: Gate 1 → Gate 2(2A→2B→2C→2D→2E) → Gate 3 → Gate 4 → Gate 5
|
||||
v2.1: Gate 0(scope) → Gate 1 → Gate 2(2A→2B→2C→2D→2E→2F,按scope裁剪) → Gate 3 → Gate 4 → Gate 5
|
||||
v2.1: Gate 0(scope) → Gate 1 → Gate 2(2A→2B→2C→2D→2E→2F) → Gate 3 → Gate 4 → Gate 5
|
||||
v3: Gate 0A(scope) + 0B(约定检查) → Gate 1 → Gate 2(按scope裁剪) → Gate 3 → Gate 4 → Gate 5
|
||||
```
|
||||
|
||||
### `/req next` testing→staging 门禁
|
||||
@@ -449,3 +489,52 @@ v2.1: Gate 0(scope) → Gate 1 → Gate 2(2A→2B→2C→2D→2E→2F,按scope
|
||||
### `/req deploy` 部署门禁
|
||||
|
||||
在 req-deploy 流程前增加 Deploy Gates (DG1-DG6) 检查。
|
||||
|
||||
---
|
||||
|
||||
## Harness Engineering — 工程约束方法论
|
||||
|
||||
**项目级基础设施**的建立和维护方法论,与上述需求级门禁互补。
|
||||
|
||||
> 详见 [harness-engineering.md](harness-engineering.md) 完整文档。
|
||||
|
||||
### 核心理念
|
||||
|
||||
**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题。**
|
||||
|
||||
三大支柱:
|
||||
1. **Ratchet** — 遗留违规数只能减不能增 → [ratchet-pattern.md](ratchet-pattern.md)
|
||||
2. **约定建立** — 每个 bug 修复后立即建立约定和检测 → [convention-flow.md](convention-flow.md)
|
||||
3. **门禁层级** — 按项目成熟度逐级引入 → [project-bootstrap.md](project-bootstrap.md)
|
||||
|
||||
### `/harness` 命令集(手动可用 + 自动嵌入)
|
||||
|
||||
| 命令 | 手动 | 自动触发 |
|
||||
|------|------|---------|
|
||||
| `/harness assess` | 随时 | 首次 `/req dev` |
|
||||
| `/harness init [level]` | 随时 | — |
|
||||
| `/harness convention [name]` | 随时 | `/req cr` + bug 类型需求 |
|
||||
| `/harness report` | 随时 | `/req test` Gate 0B |
|
||||
|
||||
### 模板
|
||||
|
||||
`templates/` 目录提供可复用的脚本模板:
|
||||
- [ratchet-script.md](templates/ratchet-script.md) — Ratchet 脚本模板(check/baseline/report 三命令)
|
||||
- [convention-script.md](templates/convention-script.md) — 约定检测脚本模板(grep 模式匹配)
|
||||
- [gates-doc.md](templates/gates-doc.md) — GATES.md 文档模板
|
||||
- [pre-commit-config.md](templates/pre-commit-config.md) — husky + lint-staged 配置(Node.js/Go/Monorepo)
|
||||
|
||||
### 与需求级门禁的关系
|
||||
|
||||
```
|
||||
Harness(项目级护栏) req-test-gate(需求级门禁)
|
||||
├── .husky/ pre-commit ├── Gate 0A: scope 分级
|
||||
├── CI lint workflow ├── Gate 0B: 运行 harness 的 check-*.sh ←─ 连接点
|
||||
├── scripts/check-*.sh ├── Gate 1: 前置条件
|
||||
├── CLAUDE.md 约定 ├── Gate 2: 测试执行
|
||||
│ ├── Gate 3: 质量验证
|
||||
│ ├── Gate 4: 文档持久化
|
||||
│ └── Gate 5: 回归贡献
|
||||
```
|
||||
|
||||
> Harness 不使用 Gate 编号。它的产出物(check-*.sh 脚本)通过 Gate 0B 接入 req-test-gate 流程。
|
||||
|
||||
222
skills-req/req-test-gate-plugin/skills/convention-flow.md
Normal file
222
skills-req/req-test-gate-plugin/skills/convention-flow.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# Convention Flow — 约定建立流程
|
||||
|
||||
## 核心理念
|
||||
|
||||
**每个 bug 都是一个建立约定的机会。** 修复 bug 后,立即将教训固化为自动化检测,防止同类问题再次出现。
|
||||
|
||||
## 流程总览
|
||||
|
||||
```
|
||||
Step 1: 修 Bug → 标准修复,代码层面解决问题
|
||||
↓
|
||||
Step 2: 文档化约定 → 写入 CLAUDE.md(让 AI 和人都知道这条规则)
|
||||
↓
|
||||
Step 3: 自动化检测 → 创建 scripts/check-*.sh(让 CI 自动阻止违规)
|
||||
↓
|
||||
Step 4: 记录追踪 → 创建【约定】任务关联需求(仅 /req cr 自动触发时)
|
||||
```
|
||||
|
||||
> Step 1-3 是核心流程(手动和自动都执行)。Step 4 仅在 `/req cr` 自动触发时执行,用于在 ai-proj 中留痕。
|
||||
|
||||
### Step 1: 修 Bug
|
||||
|
||||
标准的 bug 修复流程。此步骤无特殊要求。
|
||||
|
||||
### Step 2: 文档化约定
|
||||
|
||||
在项目的 CLAUDE.md 中添加约定段落,格式参见 [harness-engineering.md](harness-engineering.md) 的「CLAUDE.md 约定文档格式」。
|
||||
|
||||
**必须包含**:
|
||||
- 原因描述(为什么这是个问题)
|
||||
- 一句话规则
|
||||
- 正例和反例代码
|
||||
- 检查命令
|
||||
|
||||
### Step 3: 自动化检测
|
||||
|
||||
创建 `scripts/check-{name}.sh` 脚本:
|
||||
|
||||
1. 扫描代码库查找违反约定的模式
|
||||
2. 输出违规文件和行号
|
||||
3. 支持 `--ci` 模式(退出码 0/1)
|
||||
4. 零违规时输出 PASSED 消息
|
||||
|
||||
### Step 4: 记录追踪(自动触发时)
|
||||
|
||||
当 convention flow 由 `/req cr` 自动触发时,产出物必须关联到当前需求:
|
||||
|
||||
```
|
||||
ai-proj task create --title "【约定】{约定名称}"
|
||||
ai-proj req link --id <req_id> --task-ids <task_id> # linkRole=documentation
|
||||
ai-proj create-and-attach --id <task_id> --content "..." # 约定详情文档
|
||||
```
|
||||
|
||||
**约定详情文档内容**:
|
||||
```markdown
|
||||
## 约定: {名称}
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 来源需求 | REQ-XXXX |
|
||||
| 触发 bug | {bug 描述} |
|
||||
| 根因模式 | {代码模式} |
|
||||
| 策略 | {hard wall / ratchet(N)} |
|
||||
| 现有违规 | {N} 处 |
|
||||
|
||||
### 产出物
|
||||
- CLAUDE.md 约定段落: ✅ 已添加
|
||||
- 检测脚本: `scripts/check-{name}.sh` ✅ 已创建
|
||||
- 首次运行结果: PASS
|
||||
|
||||
### 约定规则
|
||||
{规则内容,与 CLAUDE.md 中一致}
|
||||
```
|
||||
|
||||
> 这样在需求归档时能看到:这个 bug 不仅被修了,还产出了一条防止复发的约定。
|
||||
|
||||
## Commit 策略
|
||||
|
||||
Convention flow 的产出物(CLAUDE.md 修改 + `scripts/check-*.sh` 新脚本)**必须单独提交**,不与 bug fix 代码混在一起:
|
||||
|
||||
```bash
|
||||
# 1. Bug fix 已经提交
|
||||
git add src/...
|
||||
git commit -m "fix: 修复 Modal 重叠导致弹窗无法关闭"
|
||||
|
||||
# 2. Convention 产出物单独提交
|
||||
git add CLAUDE.md scripts/check-modal-safety.sh
|
||||
git commit -m "chore: 建立 Modal 安全约定和自动检测"
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- bug fix 可能需要 cherry-pick 到其他分支,约定脚本不应跟着走
|
||||
- 约定提交的 review 焦点不同(规则是否合理 vs 代码是否正确)
|
||||
- `git log --oneline scripts/` 可以快速看到所有约定建立历史
|
||||
|
||||
## 可检测性判断
|
||||
|
||||
`/req cr` 自动触发时,AI 必须先判断 bug 根因是否能自动检测。判断标准:
|
||||
|
||||
**可检测(建议约定)** — 根因是一个**代码模式**,能用 grep/regex 在源码中匹配:
|
||||
|
||||
| 模式类别 | 示例 | grep 可行性 |
|
||||
|---------|------|------------|
|
||||
| API 误用 | `Modal.success()` 后直接 `setState` | grep `Modal.success` + 上下文检查 |
|
||||
| 禁止导入 | handler 直接 import database 层 | grep import 路径 |
|
||||
| 缺失守卫 | SQL 拼接而非参数化查询 | grep 字符串拼接模式 |
|
||||
| 遗漏回调 | Promise 无 `.catch()` | grep `.then(` 无配对 `.catch` |
|
||||
| 硬编码 | 密钥/密码直接写在代码中 | grep 常见密钥模式 |
|
||||
|
||||
**不可检测(仅记录根因)** — 根因是**逻辑/设计问题**,代码形态上看不出来:
|
||||
|
||||
| 模式类别 | 示例 | 为什么不行 |
|
||||
|---------|------|-----------|
|
||||
| 算法错误 | 排序方向反了、边界条件漏了 | 代码语法正确,逻辑错误 |
|
||||
| 业务规则遗漏 | 退款应该校验订单状态但没校验 | 缺失的代码无法被 grep |
|
||||
| 竞态条件 | 两个请求并发修改同一资源 | 需要运行时才能暴露 |
|
||||
| 配置错误 | 环境变量填错、超时值设太小 | 值的正确性取决于上下文 |
|
||||
|
||||
**快速判断法**:问自己「能不能写一条 grep 命令找到所有同类问题?」能 → 可检测,不能 → 不可检测。
|
||||
|
||||
## 选择策略
|
||||
|
||||
修完 bug 后,根据当前违规数量选择不同策略:
|
||||
|
||||
| 现有违规数 | 策略 | 脚本类型 | 说明 |
|
||||
|-----------|------|---------|------|
|
||||
| **0** | Hard wall | 纯检测脚本(有 → FAIL) | 刚修完最后一个违规,或首次建立 |
|
||||
| **1-10** | Ratchet + 排期清理 | Ratchet 脚本 + 基线 | 创建任务逐一清理遗留 |
|
||||
| **>10** | Ratchet only | Ratchet 脚本 + 基线 | 不立即清理,仅防止恶化 |
|
||||
| **不可计数** | 仅检测/告警 | 告警脚本(exit 0) | 如"代码风格"类软约定 |
|
||||
|
||||
## 实例 1: Modal 安全规则
|
||||
|
||||
**Bug**:Ant Design `Modal.success()` 非阻塞,后续 `setState` 立即执行导致两个 modal 重叠,第一个无法关闭。
|
||||
|
||||
**Step 1 — 修 Bug**:
|
||||
将 `setNextModalOpen(true)` 移入 `onOk` 回调。
|
||||
|
||||
**Step 2 — 文档化**:
|
||||
在 CLAUDE.md 添加:
|
||||
```markdown
|
||||
### Frontend: Modal 安全规则
|
||||
|
||||
Ant Design 的 `Modal.success()` / `Modal.info()` 是**非阻塞**调用。
|
||||
|
||||
**规则:`Modal.success/info/warning/error` 之后如果还有 UI 操作,必须放在 `onOk` 回调中。**
|
||||
|
||||
// WRONG — 两个 modal 同时弹出
|
||||
Modal.success({ title: '成功' });
|
||||
setNextModalOpen(true);
|
||||
|
||||
// CORRECT — 等用户确认后再打开
|
||||
Modal.success({ title: '成功', onOk: () => setNextModalOpen(true) });
|
||||
|
||||
**检查命令**:`./scripts/check-modal-safety.sh`
|
||||
```
|
||||
|
||||
**Step 3 — 自动化**:
|
||||
创建 `scripts/check-modal-safety.sh`:
|
||||
- 扫描 `.tsx` 文件中的 `Modal.success/info/warning/error` 调用
|
||||
- 检查 25 行内是否有 `onOk` 回调
|
||||
- 如果没有且后续 20 行内有 `set*Open(true)`,标记为违规
|
||||
- 策略:现有违规 = 0 → Hard wall
|
||||
|
||||
## 实例 2: 架构层级约束
|
||||
|
||||
**问题**:Handler 直接调用 database 层,绕过 service 层导致业务逻辑散落。
|
||||
|
||||
**Step 1 — 识别问题**:
|
||||
发现 108 个 handler 直接 import database 的文件。
|
||||
|
||||
**Step 2 — 文档化**:
|
||||
在 CLAUDE.md 和 `docs/architecture/LAYER_RULES.md` 中描述依赖矩阵和正反示例。
|
||||
|
||||
**Step 3 — 自动化**:
|
||||
创建 `scripts/check-architecture.sh`(ratchet 脚本):
|
||||
- 5 条规则,3 个 ratchet + 2 个 hard wall
|
||||
- 基线记录各规则的当前违规数
|
||||
- 策略:遗留 108 个 → Ratchet(只能减不能增)
|
||||
|
||||
## 执行流程
|
||||
|
||||
### 自动触发(`/req cr` + bug 类需求)
|
||||
|
||||
**触发条件**(满足任一):
|
||||
- `category == bug`
|
||||
- category 为空但标题含 `fix`、`修复`、`bug`、`问题`、`缺陷`
|
||||
- category 为空但 description 含 `根因`、`复现步骤`
|
||||
|
||||
```
|
||||
1. /req cr 五视角扫描完成后
|
||||
2. 检测是否为 bug 类需求(按上述条件判断)
|
||||
3. 分析本次 bug 的根因模式(从 git diff 提取)
|
||||
4. 判断:根因是否可自动检测?(判断规则见上方「可检测性判断」)
|
||||
- 不可检测 → 仅在 CR 报告记录根因,不建议约定 → 结束
|
||||
- 可检测 → 继续下一步
|
||||
5. 检查是否已有覆盖此模式的脚本:
|
||||
- ls scripts/check-*.sh,读取每个脚本的注释头(# ... detects ...)
|
||||
- 已有覆盖 → CR 报告记录「已有约定: check-{name}.sh」→ 结束
|
||||
- 无覆盖 → 继续下一步
|
||||
6. 扫描代码库统计同类模式的现有数量(N)
|
||||
7. AskUserQuestion(按 N 值调整措辞):
|
||||
- N=0: 「此 bug 模式已修复,当前代码无同类问题。建立约定可防止未来复发,是否建立?」
|
||||
- N>0: 「代码库中还有 {N} 处同类模式。建立约定可防止恶化 + 逐步清理,是否建立?」
|
||||
- 否 → 记录到 CR 报告「约定建议:用户跳过」
|
||||
- 是 → 执行 convention flow(Step 2 + Step 3 + Step 4)
|
||||
8. 约定产出物关联到当前需求(Step 4)
|
||||
9. 约定检查结果写入 CR 报告
|
||||
```
|
||||
|
||||
### 手动触发(`/harness convention [name]`)
|
||||
|
||||
```
|
||||
1. 询问约定名称和类别(如果未提供)
|
||||
2. 分析刚修复的 bug,提取约定规则
|
||||
3. 扫描代码库统计现有违规数量
|
||||
4. 根据违规数量选择策略
|
||||
5. 生成 CLAUDE.md 约定段落
|
||||
6. 生成 scripts/check-{name}.sh 脚本
|
||||
7. 运行脚本验证(应该 PASS)
|
||||
8. 输出摘要:约定名称、策略、违规数、文件清单
|
||||
```
|
||||
113
skills-req/req-test-gate-plugin/skills/harness-engineering.md
Normal file
113
skills-req/req-test-gate-plugin/skills/harness-engineering.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Harness Engineering — 工程约束方法论
|
||||
|
||||
## 概述
|
||||
|
||||
Harness Engineering 是一套**建立自动化护栏防止新违规,同时允许渐进式清理遗留问题**的工程方法论。
|
||||
|
||||
**三大支柱**:
|
||||
|
||||
| 支柱 | 核心机制 | 说明 |
|
||||
|------|---------|------|
|
||||
| **Ratchet** | 基线 + 单调递减 | 遗留违规数只能减不能增,新代码零容忍 |
|
||||
| **约定建立** | Bug → 文档 → 自动检测 | 每个 bug 修复后立即建立约定和检测脚本 |
|
||||
| **门禁层级** | Level 1-3 递进 | 按项目成熟度逐级引入基础设施 |
|
||||
|
||||
## 子文件索引
|
||||
|
||||
| 文件 | 内容 |
|
||||
|------|------|
|
||||
| [ratchet-pattern.md](ratchet-pattern.md) | Ratchet 模式详解:Ratchet vs Hard Wall、基线格式、三命令接口 |
|
||||
| [convention-flow.md](convention-flow.md) | 三步约定建立流程:修 bug → 文档化 → 自动化检测 |
|
||||
| [project-bootstrap.md](project-bootstrap.md) | `/harness assess` 检测逻辑 + `/harness init` 分级实施指南 |
|
||||
| [templates/](templates/) | 脚本模板:ratchet、约定检测、GATES.md、pre-commit 配置 |
|
||||
|
||||
## 命令集与自动触发
|
||||
|
||||
| 命令 | 手动触发 | 自动触发时机 |
|
||||
|------|---------|-------------|
|
||||
| `/harness assess` | 用户显式调用 | **`/req dev`** 启动时快速检测(`.husky/` + `scripts/check-*.sh` 都不存在 → 提示 Level 0) |
|
||||
| `/harness init [level]` | 用户显式调用 | 不自动触发(创建基础设施需用户确认) |
|
||||
| `/harness convention [name]` | 用户显式调用 | **`/req cr` + category=bug** 时自动建议(分析根因 → 建议约定 → 用户确认后执行) |
|
||||
| `/harness report` | 用户显式调用 | **`/req test` Gate 0B** 自动运行所有 `scripts/check-*.sh` |
|
||||
|
||||
### 自动触发流程图
|
||||
|
||||
```
|
||||
/req dev (每次)
|
||||
└→ 快速检测: .husky/ 存在? scripts/check-*.sh 存在?
|
||||
└→ 都不存在 → 提示「Level 0,建议 /harness init」(不阻塞)
|
||||
└→ 有任一 → 静默通过
|
||||
|
||||
/req cr (bug 类型需求)
|
||||
└→ 五视角扫描
|
||||
└→ 运行 scripts/check-*.sh → 写入 CR 报告
|
||||
└→ ⭐ 分析 bug 根因 → 可自动检测? → 建议建立约定
|
||||
└→ 用户确认 → convention flow(文档化 → 脚本 → CLAUDE.md)
|
||||
|
||||
/req test
|
||||
└→ Gate 0A: scope 分级
|
||||
└→ Gate 0B: 运行所有 scripts/check-*.sh
|
||||
└→ 有 FAIL → 阻塞,必须修复
|
||||
└→ 全 PASS → 继续 Gate 1-5
|
||||
```
|
||||
|
||||
> 手动命令仍然可用。自动触发只是让你"不用记得去跑"。
|
||||
|
||||
## 成熟度分级
|
||||
|
||||
| Level | 基础设施 | 适用场景 |
|
||||
|-------|---------|---------|
|
||||
| **0** | 无自动化检查 | 新项目/原型 |
|
||||
| **1** | husky + lint-staged + formatter | 所有活跃项目 |
|
||||
| **2** | + ratchet 脚本 + GATES.md + CLAUDE.md 质量段落 | >10k LOC 或 >1 开发者 |
|
||||
| **3** | + CI workflow + 架构强制 + 约定检测 | 有 PR 流程和受保护分支的项目 |
|
||||
|
||||
## 技术栈检测矩阵
|
||||
|
||||
| 技术栈 | 检测标志 | Level 1 工具 | Level 2+ 工具 |
|
||||
|--------|---------|-------------|--------------|
|
||||
| **Go** | `go.mod` | gofmt + go vet | golangci-lint, architecture ratchet |
|
||||
| **React/TS** | `package.json` + react 依赖 | ESLint + Prettier | tsc --noEmit, convention scripts |
|
||||
| **Vue/TS** | `package.json` + vue 依赖 | ESLint + Prettier | vite build check |
|
||||
| **Monorepo** | 多个 `package.json` / `go.mod` | 各子项目独立 lint | 统一 CI + 分模块 ratchet |
|
||||
|
||||
## CLAUDE.md 约定文档格式
|
||||
|
||||
在项目的 CLAUDE.md 中添加约定时,必须遵循以下标准格式:
|
||||
|
||||
```markdown
|
||||
### {类别}: {约定名称}
|
||||
|
||||
{原因描述 — 为什么需要这条约定}
|
||||
|
||||
**规则:{一句话规则描述}**
|
||||
|
||||
\`\`\`{语言}
|
||||
// WRONG — {错误原因}
|
||||
{错误代码示例}
|
||||
|
||||
// CORRECT — {正确做法}
|
||||
{正确代码示例}
|
||||
\`\`\`
|
||||
|
||||
**检查命令**:`./scripts/check-{name}.sh`(CI 自动运行)
|
||||
```
|
||||
|
||||
## 与其他技能的关系
|
||||
|
||||
Harness 和 req-test-gate 是**互补关系**,不要混淆两者的 Gate 编号:
|
||||
|
||||
| 维度 | Harness | req-test-gate |
|
||||
|------|---------|--------------|
|
||||
| **作用域** | 项目级(所有 PR 共享) | 需求级(每个需求独立) |
|
||||
| **触发方式** | git commit / PR / `/req test` Gate 0B | `/req test` Gate 1-5 |
|
||||
| **管什么** | pre-commit、lint、ratchet 脚本、约定检测 | 测试执行、质量验证、文档持久化、回归 |
|
||||
| **产出物** | `.husky/`、`scripts/check-*.sh`、CLAUDE.md 约定 | 测试报告、回归用例 |
|
||||
|
||||
**⚠️ 注意**:项目的 `docs/quality/GATES.md` 中有自己的 Gate 编号(Gate 0=pre-commit, Gate 1=CI lint, Gate 2=ratchet...),这是**项目文档的编号**,与 req-test-gate 的 Gate 0-5 是两套独立体系,不要混用。
|
||||
|
||||
| 技能 | 职责 |
|
||||
|------|------|
|
||||
| **harness**(本文档) | 建立和维护项目级护栏(脚本、约定、CLAUDE.md) |
|
||||
| **req-test-gate** | 需求级测试门禁(Gate 0-5),其中 Gate 0B 自动调用 harness 建立的脚本 |
|
||||
| **dev-coding** | 遵守 harness 建立的 CLAUDE.md 约定,编码时不违反 ratchet |
|
||||
194
skills-req/req-test-gate-plugin/skills/project-bootstrap.md
Normal file
194
skills-req/req-test-gate-plugin/skills/project-bootstrap.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Project Bootstrap — 工程约束基础设施引导
|
||||
|
||||
## `/harness assess` 检测逻辑
|
||||
|
||||
扫描项目结构,自动判断成熟度等级(0-3),列出缺口和建议。
|
||||
|
||||
### 检测项清单(每项附实际执行的命令)
|
||||
|
||||
| 检测项 | 执行命令 | 判定 | Level |
|
||||
|--------|---------|------|-------|
|
||||
| 技术栈 | `Glob("package.json", "go.mod", "Podfile", "*.csproj")` | 存在即识别 | 基础 |
|
||||
| Pre-commit hooks | `Glob(".husky/pre-commit")` | 文件存在 | 1 |
|
||||
| Lint 配置 | `Glob(".eslintrc*", ".golangci.yml") + Grep("lint", "Makefile")` | 任一存在 | 1 |
|
||||
| Formatter | `Glob(".prettierrc*") + Grep("gofmt\\|prettier", ".husky/pre-commit")` | 任一存在 | 1 |
|
||||
| lint-staged | `Grep("lint-staged", "package.json") + Glob(".lintstagedrc*")` | 任一存在 | 1 |
|
||||
| 约定脚本 | `Glob("scripts/check-*.sh")` | 至少 1 个 | 2 |
|
||||
| 基线文件 | `Glob("scripts/.*-baseline.json")` | 至少 1 个 | 2 |
|
||||
| 门禁文档 | `Glob("docs/quality/GATES.md")` | 文件存在 | 2 |
|
||||
| CLAUDE.md 质量段落 | `Grep("检查命令\\|check-.*\\.sh", "CLAUDE.md")` | 有匹配 | 2 |
|
||||
| CI workflow | `Grep("check-.*\\.sh", ".gitea/workflows/*.yml", ".github/workflows/*.yml")` | 有匹配 | 3 |
|
||||
|
||||
### 等级判定
|
||||
|
||||
```
|
||||
Level 0: 无上述任何基础设施
|
||||
Level 1: 有 pre-commit hooks + lint + formatter
|
||||
Level 2: Level 1 + 有 ratchet/约定脚本 + GATES.md
|
||||
Level 3: Level 2 + CI 自动运行 + 受保护分支
|
||||
```
|
||||
|
||||
### 输出格式
|
||||
|
||||
```markdown
|
||||
## Harness Assessment: {项目名}
|
||||
|
||||
**技术栈**: Go + React (monorepo)
|
||||
**当前等级**: Level 2
|
||||
|
||||
### 已有基础设施
|
||||
- [x] husky pre-commit hooks
|
||||
- [x] ESLint + Prettier (frontend)
|
||||
- [x] gofmt + go vet (backend)
|
||||
- [x] check-architecture.sh (ratchet, 5 rules)
|
||||
- [x] check-modal-safety.sh (hard wall)
|
||||
- [x] GATES.md
|
||||
|
||||
### 缺口(升至 Level 3)
|
||||
- [ ] CI workflow 未调用 check-architecture.sh
|
||||
- [ ] CI workflow 未调用 check-modal-safety.sh
|
||||
|
||||
### 建议
|
||||
1. 在 .gitea/workflows/quality-gates.yaml 中添加 check-architecture.sh check
|
||||
2. 在 .gitea/workflows/quality-gates.yaml 中添加 check-modal-safety.sh --ci
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/harness init [level]` 分级实施
|
||||
|
||||
### Level 1: 基础代码格式化
|
||||
|
||||
**适用**:所有活跃项目。
|
||||
|
||||
#### Node.js 项目
|
||||
|
||||
```bash
|
||||
# 1. 安装 husky + lint-staged
|
||||
npm install -D husky lint-staged
|
||||
npx husky init
|
||||
|
||||
# 2. 配置 lint-staged(package.json)
|
||||
# 参见 templates/pre-commit-config.md — Node.js 变体
|
||||
|
||||
# 3. 配置 pre-commit hook
|
||||
echo "npx lint-staged" > .husky/pre-commit
|
||||
```
|
||||
|
||||
#### Go 项目
|
||||
|
||||
```bash
|
||||
# 1. 安装 husky(需要 package.json)
|
||||
npm init -y
|
||||
npm install -D husky lint-staged
|
||||
npx husky init
|
||||
|
||||
# 2. 配置 lint-staged(package.json)
|
||||
# 参见 templates/pre-commit-config.md — Go 变体
|
||||
|
||||
# 3. 配置 pre-commit hook
|
||||
echo "npx lint-staged" > .husky/pre-commit
|
||||
```
|
||||
|
||||
#### Monorepo
|
||||
|
||||
```bash
|
||||
# 根目录安装 husky,各子项目配置 lint-staged
|
||||
# 参见 templates/pre-commit-config.md — Monorepo 变体
|
||||
```
|
||||
|
||||
### Level 2: 约定与 Ratchet
|
||||
|
||||
**前置**:Level 1 已完成。
|
||||
|
||||
#### Step 1: 创建目录结构
|
||||
|
||||
```bash
|
||||
mkdir -p scripts docs/quality
|
||||
```
|
||||
|
||||
#### Step 2: 创建 GATES.md
|
||||
|
||||
参见 [templates/gates-doc.md](templates/gates-doc.md),填入项目实际的 lint 工具和 CI 系统。
|
||||
|
||||
#### Step 3: 约束发现(关键步骤)
|
||||
|
||||
AI 必须探索项目代码库,按以下清单逐项检查,找出适合建 ratchet 的约束:
|
||||
|
||||
| 检查项 | 检测方法 | 发现约束时 |
|
||||
|--------|---------|-----------|
|
||||
| **分层架构** | 读 `go.mod` / 目录结构,判断是否有 handler/service/repository 分层 | 检查跨层 import → ratchet |
|
||||
| **禁止 import** | `grep -r "import" --include="*.go"` 找不应出现的包引用 | 统计违规数 → ratchet 或 wall |
|
||||
| **前端 API 调用规范** | `grep -r "fetch\|axios" --include="*.tsx"` 看是否有直接调用(应走 service 层) | 统计违规数 → ratchet |
|
||||
| **console.log 残留** | `grep -r "console.log" --include="*.tsx" --include="*.ts"` | 统计数量 → ratchet |
|
||||
| **硬编码配置** | `grep -rn "localhost\|127.0.0.1\|:3000\|:8080" --include="*.ts" --include="*.go"` 排除测试和配置文件 | 统计数量 → ratchet |
|
||||
| **TODO/FIXME** | `grep -rn "TODO\|FIXME" --include="*.go" --include="*.ts"` | 统计数量 → ratchet(仅追踪,不阻塞) |
|
||||
|
||||
**执行流程**:
|
||||
```
|
||||
1. 读取项目目录结构(ls -R 前两层)
|
||||
2. 识别技术栈和架构风格
|
||||
3. 按上表逐项 grep 扫描
|
||||
4. 筛选:违规数 > 0 的项列为候选规则
|
||||
5. AskUserQuestion:展示候选规则表,让用户勾选要建哪些
|
||||
6. 对每条选中的规则:
|
||||
a. 用 ratchet-script 模板生成 scripts/check-{name}.sh
|
||||
b. 运行 baseline 记录初始值
|
||||
c. 在 CLAUDE.md 添加约定段落
|
||||
```
|
||||
|
||||
> 如果扫描后没有发现任何候选规则(代码量很小或结构简单),Level 2 只创建 GATES.md + CLAUDE.md 质量段落,不创建 ratchet 脚本。后续通过 convention flow(修 bug 时)逐步积累。
|
||||
|
||||
#### Step 4: CLAUDE.md 质量段落
|
||||
|
||||
参见 [harness-engineering.md](harness-engineering.md) 的「CLAUDE.md 约定文档格式」。
|
||||
|
||||
#### Step 5: 提交
|
||||
|
||||
```bash
|
||||
./scripts/check-{name}.sh baseline # 每个新脚本都要 baseline
|
||||
git add scripts/ docs/quality/ CLAUDE.md
|
||||
git commit -m "chore: establish harness engineering Level 2"
|
||||
```
|
||||
|
||||
### Level 3: CI 强制
|
||||
|
||||
**前置**:Level 2 已完成 + 项目有 CI 和受保护分支。
|
||||
|
||||
```yaml
|
||||
# .gitea/workflows/quality-gates.yaml 示例
|
||||
name: Quality Gates
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Architecture Check
|
||||
run: ./scripts/check-architecture.sh check
|
||||
- name: Convention Checks
|
||||
run: |
|
||||
for script in scripts/check-*.sh; do
|
||||
[ "$script" = "scripts/check-architecture.sh" ] && continue
|
||||
bash "$script" --ci
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `/harness init` 执行流程
|
||||
|
||||
```
|
||||
1. 运行 /harness assess 确定当前等级
|
||||
2. 确定目标等级(用户指定或默认 Level 1)
|
||||
3. 如果当前等级 ≥ 目标等级,提示"已满足"
|
||||
4. 按目标等级模板生成文件:
|
||||
- Level 1: .husky/ + package.json lint-staged
|
||||
- Level 2: scripts/ + docs/quality/ + CLAUDE.md 段落
|
||||
- Level 3: CI workflow
|
||||
5. 运行验证(husky hook 是否生效、脚本是否可执行)
|
||||
6. 输出文件清单和下一步建议
|
||||
```
|
||||
96
skills-req/req-test-gate-plugin/skills/ratchet-pattern.md
Normal file
96
skills-req/req-test-gate-plugin/skills/ratchet-pattern.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Ratchet Pattern — 渐进式违规清理
|
||||
|
||||
## Ratchet vs Hard Wall
|
||||
|
||||
| 策略 | 适用场景 | 基线值 | CI 行为 |
|
||||
|------|---------|--------|---------|
|
||||
| **Ratchet** | 有遗留违规(>0) | 当前违规数 | 新值 > 基线 → FAIL |
|
||||
| **Hard Wall** | 必须为零 | 固定 = 0 | 值 ≠ 0 → FAIL |
|
||||
|
||||
**选择依据**:
|
||||
- 遗留违规 = 0 → Hard Wall(绝对禁止)
|
||||
- 遗留违规 > 0 → Ratchet(允许存在但不能增加)
|
||||
|
||||
## 基线文件格式
|
||||
|
||||
基线以 JSON 格式存储,提交到 git:
|
||||
|
||||
```json
|
||||
{
|
||||
"rule_name_1": 108,
|
||||
"rule_name_2": 7,
|
||||
"rule_name_3": 0
|
||||
}
|
||||
```
|
||||
|
||||
**位置约定**:`scripts/.{检测名}-baseline.json`(如 `scripts/.architecture-baseline.json`)
|
||||
|
||||
## 三命令接口
|
||||
|
||||
每个 ratchet 脚本必须实现三个子命令:
|
||||
|
||||
| 命令 | 用途 | CI 使用 |
|
||||
|------|------|---------|
|
||||
| `check` | 比较当前值与基线,增加则失败 | PR 门禁 |
|
||||
| `baseline` | 记录当前值为新基线 | 修复违规后手动执行 |
|
||||
| `report` | 打印当前值 vs 基线的对比表 | 人工审查 |
|
||||
|
||||
```bash
|
||||
./scripts/check-{name}.sh check # CI: fail if violations increased
|
||||
./scripts/check-{name}.sh baseline # After fix: record new lower baseline
|
||||
./scripts/check-{name}.sh report # Human: see current vs baseline
|
||||
```
|
||||
|
||||
## 何时降低基线
|
||||
|
||||
```
|
||||
1. 修复一个或多个违规
|
||||
2. 运行 check → PASS(因为当前值 ≤ 旧基线)
|
||||
3. 运行 baseline → 记录更低的基线
|
||||
4. git commit 新的基线文件
|
||||
5. 后续 PR 使用更低的基线
|
||||
```
|
||||
|
||||
**关键**:修复后一定要 `baseline` + commit,否则下次有人新增违规会被允许(因为基线还是旧的高值)。
|
||||
|
||||
## 多规则 Ratchet
|
||||
|
||||
一个脚本可以管理多条规则,每条规则有独立的基线值和类型(ratchet/wall)。
|
||||
|
||||
```bash
|
||||
# rules 数组定义规则名
|
||||
rules=("handlers_import_database" "routes_import_database" "models_import_upper")
|
||||
# types 数组定义策略
|
||||
types=("ratchet" "ratchet" "wall")
|
||||
|
||||
# 循环检查每条规则
|
||||
for i in "${!rules[@]}"; do
|
||||
rule="${rules[$i]}"
|
||||
type="${types[$i]}"
|
||||
# ...
|
||||
done
|
||||
```
|
||||
|
||||
## 参考实现
|
||||
|
||||
**new-ai-proj** 项目的架构 ratchet:
|
||||
- 脚本:`scripts/check-architecture.sh`
|
||||
- 基线:`scripts/.architecture-baseline.json`
|
||||
- 规则文档:`docs/architecture/LAYER_RULES.md`
|
||||
- 门禁文档:`docs/quality/GATES.md`
|
||||
|
||||
五条规则:
|
||||
| 规则 | 类型 | 含义 |
|
||||
|------|------|------|
|
||||
| `handlers_import_database` | ratchet | Handler 不应直接访问数据库层 |
|
||||
| `routes_import_database` | ratchet | 路由不应直接访问数据库层 |
|
||||
| `routes_import_services` | ratchet | 路由应通过 handler 委托 |
|
||||
| `models_import_upper` | hard wall | 模型层禁止反向依赖 |
|
||||
| `services_import_handlers` | hard wall | 服务层禁止反向依赖 |
|
||||
|
||||
## 设计 Ratchet 规则的原则
|
||||
|
||||
1. **可计数** — 规则违规必须能通过 grep/AST 精确计数
|
||||
2. **无歧义** — 一个文件/行要么违规要么不违规,不能有灰色地带
|
||||
3. **可修复** — 开发者必须知道怎么把违规改正确
|
||||
4. **有文档** — 每条规则配 LAYER_RULES.md 或 CLAUDE.md 中的正反示例
|
||||
@@ -0,0 +1,120 @@
|
||||
# Convention Detection Script Template
|
||||
|
||||
泛化的约定检测脚本模板,基于 new-ai-proj `check-modal-safety.sh` 提取。用于检测代码中违反编码约定的模式。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 复制下方脚本到 `scripts/check-{NAME}.sh`
|
||||
2. 替换所有 `{PLACEHOLDER}` 为项目特定值
|
||||
3. 运行 `chmod +x scripts/check-{NAME}.sh`
|
||||
4. 验证:`./scripts/check-{NAME}.sh` 输出 PASSED
|
||||
|
||||
## 脚本模板
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# {CONVENTION_NAME} Check — detects {WHAT_IT_DETECTS} in {TARGET_SCOPE}.
|
||||
#
|
||||
# {PROBLEM_DESCRIPTION}
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/check-{NAME}.sh # Check and report
|
||||
# ./scripts/check-{NAME}.sh --ci # Exit code 1 on violations
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_DIR="$(cd "$(dirname "$0")/../{TARGET_DIRECTORY}" && pwd)"
|
||||
CI_MODE=false
|
||||
[[ "${1:-}" == "--ci" ]] && CI_MODE=true
|
||||
|
||||
violations=0
|
||||
|
||||
# ── detection logic ─────────────────────────────────────────────────
|
||||
|
||||
# Strategy A: Simple grep pattern (file-level)
|
||||
# Find files matching a bad pattern, optionally excluding false positives.
|
||||
#
|
||||
# while IFS= read -r file; do
|
||||
# rel_path="${file#"$TARGET_DIR"/}"
|
||||
# echo "WARNING: $rel_path — {VIOLATION_MESSAGE}"
|
||||
# violations=$((violations + 1))
|
||||
# done < <(grep -rl '{BAD_PATTERN}' "$TARGET_DIR" --include='{FILE_GLOB}' 2>/dev/null \
|
||||
# | while read f; do grep -L '{FALSE_POSITIVE_PATTERN}' "$f" 2>/dev/null; done)
|
||||
|
||||
# Strategy B: Line-level with context (multi-condition)
|
||||
# Find a primary pattern, check nearby lines for secondary pattern.
|
||||
#
|
||||
# while IFS= read -r file; do
|
||||
# while IFS= read -r line_num; do
|
||||
# # Check context around the match
|
||||
# has_mitigation=$(sed -n "${line_num},$((line_num + {CONTEXT_LINES}))p" "$file" \
|
||||
# | grep -c '{MITIGATION_PATTERN}' || true)
|
||||
# if [[ "$has_mitigation" -eq 0 ]]; then
|
||||
# # Additional condition: check if problematic follow-up exists
|
||||
# after_end=$((line_num + {LOOKAHEAD_LINES}))
|
||||
# has_problem=$(sed -n "$((line_num + 1)),${after_end}p" "$file" \
|
||||
# | grep -cE '{FOLLOW_UP_BAD_PATTERN}' || true)
|
||||
# if [[ "$has_problem" -gt 0 ]]; then
|
||||
# rel_path="${file#"$TARGET_DIR"/}"
|
||||
# echo "WARNING: $rel_path:$line_num — {VIOLATION_MESSAGE}"
|
||||
# violations=$((violations + 1))
|
||||
# fi
|
||||
# fi
|
||||
# done < <(grep -n '{PRIMARY_PATTERN}' "$file" | cut -d: -f1)
|
||||
# done < <(grep -rl '{PRIMARY_PATTERN}' "$TARGET_DIR" --include='{FILE_GLOB}' 2>/dev/null)
|
||||
|
||||
# ── results ─────────────────────────────────────────────────────────
|
||||
|
||||
if [[ "$violations" -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "Found $violations {CONVENTION_NAME} violation(s)."
|
||||
echo "Fix: {FIX_GUIDANCE}"
|
||||
echo "Docs: see CLAUDE.md '{CLAUDE_MD_SECTION}'"
|
||||
$CI_MODE && exit 1
|
||||
else
|
||||
echo "{CONVENTION_NAME} check PASSED."
|
||||
fi
|
||||
```
|
||||
|
||||
## Placeholder 说明
|
||||
|
||||
| Placeholder | 含义 | 示例 |
|
||||
|-------------|------|------|
|
||||
| `{NAME}` | 脚本短名 | `modal-safety`, `sql-injection` |
|
||||
| `{CONVENTION_NAME}` | 约定显示名 | `Modal safety`, `SQL injection prevention` |
|
||||
| `{WHAT_IT_DETECTS}` | 检测内容 | `potential modal overlap patterns` |
|
||||
| `{TARGET_SCOPE}` | 目标范围描述 | `frontend code`, `Go handlers` |
|
||||
| `{TARGET_DIRECTORY}` | 扫描目录 | `frontend/src`, `backend` |
|
||||
| `{PROBLEM_DESCRIPTION}` | 问题描述 | `Ant Design Modal.success is non-blocking...` |
|
||||
| `{FILE_GLOB}` | 文件匹配 | `*.tsx`, `*.go` |
|
||||
| `{PRIMARY_PATTERN}` | 主匹配模式 | `Modal\.\(success\|info\)` |
|
||||
| `{MITIGATION_PATTERN}` | 缓解模式(有则排除) | `onOk` |
|
||||
| `{FOLLOW_UP_BAD_PATTERN}` | 后续违规模式 | `set\w*Open\(true\)` |
|
||||
| `{CONTEXT_LINES}` | 上下文行数 | `25` |
|
||||
| `{LOOKAHEAD_LINES}` | 前瞻行数 | `20` |
|
||||
| `{BAD_PATTERN}` | Strategy A 主匹配模式 | `console\.log` |
|
||||
| `{FALSE_POSITIVE_PATTERN}` | Strategy A 排除模式(匹配到则非违规) | `// eslint-disable` |
|
||||
| `{VIOLATION_MESSAGE}` | 违规消息 | `Modal without onOk followed by setState` |
|
||||
| `{FIX_GUIDANCE}` | 修复指导 | `add onOk callback to Modal` |
|
||||
| `{CLAUDE_MD_SECTION}` | CLAUDE.md 章节名 | `Frontend: Modal 安全规则` |
|
||||
|
||||
## 两种检测策略
|
||||
|
||||
### Strategy A: 文件级检测
|
||||
|
||||
适用于:整个文件不应出现某模式(或出现即违规)。
|
||||
|
||||
例子:
|
||||
- `console.log` 在生产代码中
|
||||
- `TODO` / `FIXME` 计数
|
||||
- 缺少 license header
|
||||
|
||||
### Strategy B: 行级 + 上下文检测
|
||||
|
||||
适用于:模式本身不违规,需要检查周围上下文。
|
||||
|
||||
例子:
|
||||
- `Modal.success()` 不违规,但后面紧跟 `setState` 才违规
|
||||
- `db.Exec()` 不违规,但参数用字符串拼接才违规
|
||||
- `fmt.Println()` 不违规,但在 handler 中输出敏感数据才违规
|
||||
102
skills-req/req-test-gate-plugin/skills/templates/gates-doc.md
Normal file
102
skills-req/req-test-gate-plugin/skills/templates/gates-doc.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# GATES.md Template
|
||||
|
||||
项目质量门禁文档模板。放置于 `docs/quality/GATES.md`。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 复制下方模板到项目的 `docs/quality/GATES.md`
|
||||
2. 替换 `{PLACEHOLDER}` 为项目特定值
|
||||
3. 根据项目实际情况增删 Gate
|
||||
|
||||
## 模板
|
||||
|
||||
```markdown
|
||||
# Quality Gates
|
||||
|
||||
Quality gates are automated checks that code must pass before it can be merged or deployed.
|
||||
|
||||
## Gate Overview
|
||||
|
||||
| Gate | Name | Where | What it catches |
|
||||
|------|------|-------|-----------------|
|
||||
| 0 | Pre-commit | Local (husky) | Formatting, lint errors |
|
||||
| 1 | CI Lint | {CI_SYSTEM} | {LINT_TOOLS} |
|
||||
| 2 | Convention & Architecture | {CI_SYSTEM} | New violations of established rules |
|
||||
| 3 | Unit Tests | CI / local | Logic bugs, regressions |
|
||||
| 4 | Security Scan | CI | Dependency vulnerabilities |
|
||||
| 5 | Build | CI | Compilation errors |
|
||||
|
||||
## Gate 0: Pre-commit (Local)
|
||||
|
||||
Runs automatically on every `git commit` via husky + lint-staged.
|
||||
|
||||
{FRONTEND_SECTION — include if applicable}
|
||||
**Frontend** (`{FRONTEND_GLOBS}`):
|
||||
- {LINT_TOOL} with {LINT_FLAGS}
|
||||
- {FORMATTER}
|
||||
|
||||
{BACKEND_SECTION — include if applicable}
|
||||
**Backend** (`{BACKEND_GLOBS}`):
|
||||
- {FORMAT_TOOL} (auto-format)
|
||||
- {ANALYSIS_TOOL} (static analysis)
|
||||
|
||||
**Bypass** (emergency only): `git commit --no-verify`
|
||||
|
||||
## Gate 1: CI Lint
|
||||
|
||||
Runs on pull requests via {CI_SYSTEM}.
|
||||
|
||||
- **Backend**: {BACKEND_LINT_COMMANDS}
|
||||
- **Frontend**: {FRONTEND_LINT_COMMANDS}
|
||||
|
||||
## Gate 2: Convention & Architecture Checks
|
||||
|
||||
Runs on pull requests via {CI_SYSTEM}.
|
||||
|
||||
{LIST_EACH_CHECK_SCRIPT}
|
||||
- `./scripts/check-{name}.sh check` — {description}
|
||||
|
||||
See project CLAUDE.md and docs/architecture/ for rule details.
|
||||
|
||||
## Gates 3-5: Testing, Security, Build
|
||||
|
||||
These gates are enforced via the `/req test` workflow:
|
||||
|
||||
1. **Gate 3**: Unit test generation and execution
|
||||
2. **Gate 4**: Security regression scan
|
||||
3. **Gate 5**: E2E smoke test + final report
|
||||
|
||||
See the dev-test skill documentation for details.
|
||||
|
||||
## Local Commands
|
||||
|
||||
\`\`\`bash
|
||||
# Gate 0: Format & Lint
|
||||
{LOCAL_LINT_COMMANDS}
|
||||
|
||||
# Gate 2: Convention checks
|
||||
{LOCAL_CHECK_COMMANDS}
|
||||
|
||||
# Gate 3: Unit tests
|
||||
{LOCAL_TEST_COMMANDS}
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
## Placeholder 说明
|
||||
|
||||
| Placeholder | 含义 | 示例 |
|
||||
|-------------|------|------|
|
||||
| `{CI_SYSTEM}` | CI 系统名称 | `Gitea Actions`, `GitHub Actions` |
|
||||
| `{LINT_TOOLS}` | lint 工具列表 | `golangci-lint, ESLint, TypeScript errors` |
|
||||
| `{FRONTEND_GLOBS}` | 前端文件 glob | `*.ts, *.tsx`, `*.vue` |
|
||||
| `{BACKEND_GLOBS}` | 后端文件 glob | `*.go` |
|
||||
| `{LINT_TOOL}` | lint 工具 | `ESLint` |
|
||||
| `{LINT_FLAGS}` | lint 参数 | `--fix --max-warnings 0` |
|
||||
| `{FORMATTER}` | 格式化工具 | `Prettier`, `gofmt` |
|
||||
|
||||
## 注意事项
|
||||
|
||||
- **此文档的 Gate 编号是项目级的**(描述项目基础设施层级),与 req-test-gate 技能的 Gate 0-5(需求级测试门禁)是两套独立体系
|
||||
- Gate 0-2 由 harness engineering 方法论管理(pre-commit → CI lint → ratchet/约定检测)
|
||||
- Gate 3-5 由 `/req test` 流程管理(单元测试 → 安全扫描 → 构建验证)
|
||||
- 两者互补,不要混淆编号
|
||||
@@ -0,0 +1,135 @@
|
||||
# Pre-commit Configuration Template
|
||||
|
||||
husky + lint-staged 配置模板,三种变体:Node.js 项目、Go 项目、Monorepo。
|
||||
|
||||
---
|
||||
|
||||
## 变体 1: Node.js 项目(React / Vue / 纯 TS)
|
||||
|
||||
### 安装
|
||||
|
||||
```bash
|
||||
npm install -D husky lint-staged prettier eslint
|
||||
npx husky init
|
||||
echo "npx lint-staged" > .husky/pre-commit
|
||||
```
|
||||
|
||||
### package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": [
|
||||
"eslint --fix --max-warnings 0",
|
||||
"prettier --write"
|
||||
],
|
||||
"*.{css,less,scss}": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.{json,md}": [
|
||||
"prettier --write"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 变体 2: Go 项目
|
||||
|
||||
### 安装
|
||||
|
||||
```bash
|
||||
# Go 项目需要 package.json 来驱动 husky
|
||||
npm init -y
|
||||
npm install -D husky lint-staged
|
||||
npx husky init
|
||||
echo "npx lint-staged" > .husky/pre-commit
|
||||
```
|
||||
|
||||
### package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"lint-staged": {
|
||||
"*.go": [
|
||||
"gofmt -s -w",
|
||||
"bash -c 'go vet ./...'"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 可选:golangci-lint(更严格)
|
||||
|
||||
```json
|
||||
{
|
||||
"lint-staged": {
|
||||
"*.go": [
|
||||
"gofmt -s -w",
|
||||
"bash -c 'go vet ./...'",
|
||||
"bash -c 'golangci-lint run --fix'"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 变体 3: Monorepo(Go + Node.js)
|
||||
|
||||
### 安装(根目录)
|
||||
|
||||
```bash
|
||||
npm install -D husky lint-staged
|
||||
npx husky init
|
||||
echo "npx lint-staged" > .husky/pre-commit
|
||||
```
|
||||
|
||||
### package.json(根目录)
|
||||
|
||||
```json
|
||||
{
|
||||
"lint-staged": {
|
||||
"frontend/**/*.{ts,tsx}": [
|
||||
"bash -c 'cd frontend && npx eslint --fix --max-warnings 0'",
|
||||
"bash -c 'cd frontend && npx prettier --write'"
|
||||
],
|
||||
"frontend/**/*.{css,less,json}": [
|
||||
"bash -c 'cd frontend && npx prettier --write'"
|
||||
],
|
||||
"backend/**/*.go": [
|
||||
"gofmt -s -w",
|
||||
"bash -c 'cd backend && go vet ./...'"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
- `bash -c 'cd xxx && ...'` 确保命令在正确的子目录中执行
|
||||
- lint-staged 传递的文件路径是相对于根目录的,部分工具(如 `go vet`)需要在子目录执行
|
||||
- 如果前端和后端各自有 `package.json`,lint-staged 仍然在根目录配置
|
||||
|
||||
---
|
||||
|
||||
## 验证配置
|
||||
|
||||
```bash
|
||||
# 修改一个文件后测试 pre-commit hook
|
||||
echo "// test" >> some-file.ts
|
||||
git add some-file.ts
|
||||
git commit -m "test: verify pre-commit hook"
|
||||
# 应该看到 lint-staged 执行 ESLint + Prettier
|
||||
# 如果有 lint 错误,commit 会被阻止
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
| 问题 | 原因 | 解决 |
|
||||
|------|------|------|
|
||||
| lint-staged 不执行 | `.husky/pre-commit` 内容不对 | 确认内容为 `npx lint-staged` |
|
||||
| ESLint 报错太多 | 旧项目首次启用 | 先 `npx eslint --fix` 全量修复,再启用 hook |
|
||||
| gofmt 修改后 commit 内容不一致 | lint-staged 自动 format 后需要 re-stage | lint-staged 自动处理,无需手动 |
|
||||
| Monorepo 路径错误 | lint-staged 传递根目录相对路径 | 使用 `bash -c 'cd sub && ...'` 包装 |
|
||||
@@ -0,0 +1,189 @@
|
||||
# Ratchet Script Template
|
||||
|
||||
泛化的 ratchet 脚本模板,基于 new-ai-proj `check-architecture.sh` 提取。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 复制下方脚本到 `scripts/check-{NAME}.sh`
|
||||
2. 替换所有 `{PLACEHOLDER}` 为项目特定值
|
||||
3. 运行 `chmod +x scripts/check-{NAME}.sh`
|
||||
4. 运行 `./scripts/check-{NAME}.sh baseline` 记录首次基线
|
||||
5. 提交脚本和基线文件
|
||||
|
||||
## 脚本模板
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# {DESCRIPTION} Ratchet — ensures new code doesn't increase {WHAT} violations.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/check-{NAME}.sh check # CI / pre-push: fail if violations increased
|
||||
# ./scripts/check-{NAME}.sh baseline # Record current counts (commit the result)
|
||||
# ./scripts/check-{NAME}.sh report # Pretty-print current vs baseline
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
BASELINE_FILE="$SCRIPT_DIR/.{NAME}-baseline.json"
|
||||
TARGET_DIR="$SCRIPT_DIR/../{TARGET_DIRECTORY}"
|
||||
|
||||
# ── rule definitions ────────────────────────────────────────────────
|
||||
# Add/remove rules as needed. Each rule has:
|
||||
# - A name (used as JSON key)
|
||||
# - A type: "ratchet" (baseline can decrease) or "wall" (must be 0)
|
||||
# - A count function
|
||||
|
||||
RULES=({RULE_NAMES}) # e.g. ("rule_one" "rule_two" "rule_three")
|
||||
TYPES=({RULE_TYPES}) # e.g. ("ratchet" "ratchet" "wall")
|
||||
|
||||
# ── counting functions ──────────────────────────────────────────────
|
||||
|
||||
count_rule() {
|
||||
local rule="$1"
|
||||
case "$rule" in
|
||||
{RULE_NAME_1})
|
||||
# Example: count files in {DIR} that import {PACKAGE}
|
||||
grep -rl '{PATTERN_1}' "$TARGET_DIR/{SUBDIR_1}/" 2>/dev/null | wc -l | tr -d ' '
|
||||
;;
|
||||
{RULE_NAME_2})
|
||||
grep -rl '{PATTERN_2}' "$TARGET_DIR/{SUBDIR_2}/" 2>/dev/null | wc -l | tr -d ' '
|
||||
;;
|
||||
# Add more rules...
|
||||
*)
|
||||
echo "0"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ── helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
current_counts() {
|
||||
echo "{"
|
||||
local first=true
|
||||
for rule in "${RULES[@]}"; do
|
||||
local count
|
||||
count=$(count_rule "$rule")
|
||||
$first || echo ","
|
||||
printf ' "%s": %s' "$rule" "${count:-0}"
|
||||
first=false
|
||||
done
|
||||
echo ""
|
||||
echo "}"
|
||||
}
|
||||
|
||||
json_val() {
|
||||
local key="$1" file="$2"
|
||||
grep "\"${key}\"" "$file" | head -1 | sed 's/[^0-9]//g'
|
||||
}
|
||||
|
||||
json_val_str() {
|
||||
local key="$1" json="$2"
|
||||
echo "$json" | grep "\"${key}\"" | head -1 | sed 's/[^0-9]//g'
|
||||
}
|
||||
|
||||
# ── commands ────────────────────────────────────────────────────────
|
||||
|
||||
cmd_baseline() {
|
||||
echo "Recording {NAME} baseline..."
|
||||
current_counts > "$BASELINE_FILE"
|
||||
echo "Baseline written to $BASELINE_FILE"
|
||||
cat "$BASELINE_FILE"
|
||||
}
|
||||
|
||||
cmd_report() {
|
||||
if [[ ! -f "$BASELINE_FILE" ]]; then
|
||||
echo "No baseline found. Run: $0 baseline"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local current
|
||||
current=$(current_counts)
|
||||
|
||||
printf "\n%-35s %10s %10s %8s\n" "Rule" "Baseline" "Current" "Status"
|
||||
printf "%-35s %10s %10s %8s\n" "---" "---" "---" "---"
|
||||
|
||||
for i in "${!RULES[@]}"; do
|
||||
local rule="${RULES[$i]}"
|
||||
local type="${TYPES[$i]}"
|
||||
local base cur status
|
||||
|
||||
base=$(json_val "$rule" "$BASELINE_FILE")
|
||||
cur=$(json_val_str "$rule" "$current")
|
||||
|
||||
if [[ "$type" == "wall" ]]; then
|
||||
[[ "$cur" -eq 0 ]] && status="PASS" || status="FAIL"
|
||||
else
|
||||
[[ "$cur" -le "$base" ]] && status="PASS" || status="FAIL"
|
||||
fi
|
||||
|
||||
printf "%-35s %10d %10d %8s\n" "$rule" "$base" "$cur" "$status"
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
cmd_check() {
|
||||
if [[ ! -f "$BASELINE_FILE" ]]; then
|
||||
echo "ERROR: No baseline found. Run: $0 baseline"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local current
|
||||
current=$(current_counts)
|
||||
local failed=0
|
||||
|
||||
for i in "${!RULES[@]}"; do
|
||||
local rule="${RULES[$i]}"
|
||||
local type="${TYPES[$i]}"
|
||||
local base cur
|
||||
|
||||
base=$(json_val "$rule" "$BASELINE_FILE")
|
||||
cur=$(json_val_str "$rule" "$current")
|
||||
|
||||
if [[ "$type" == "wall" ]]; then
|
||||
if [[ "$cur" -ne 0 ]]; then
|
||||
echo "FAIL [hard wall] $rule: expected 0, got $cur"
|
||||
failed=1
|
||||
fi
|
||||
else
|
||||
if [[ "$cur" -gt "$base" ]]; then
|
||||
echo "FAIL [ratchet] $rule: baseline=$base, current=$cur (+$((cur - base)))"
|
||||
failed=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$failed" -eq 1 ]]; then
|
||||
echo ""
|
||||
echo "{NAME} check FAILED. New violations detected."
|
||||
echo "If this is intentional, update the baseline: $0 baseline"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "{NAME} check PASSED."
|
||||
}
|
||||
|
||||
# ── main ────────────────────────────────────────────────────────────
|
||||
|
||||
case "${1:-check}" in
|
||||
baseline) cmd_baseline ;;
|
||||
report) cmd_report ;;
|
||||
check) cmd_check ;;
|
||||
*)
|
||||
echo "Usage: $0 {check|baseline|report}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
## Placeholder 说明
|
||||
|
||||
| Placeholder | 含义 | 示例 |
|
||||
|-------------|------|------|
|
||||
| `{NAME}` | 脚本短名 | `architecture`, `import-rules` |
|
||||
| `{DESCRIPTION}` | 一句话描述 | `Architecture`, `Import Boundary` |
|
||||
| `{TARGET_DIRECTORY}` | 扫描目标目录(相对于 scripts/) | `backend`, `frontend/src` |
|
||||
| `{RULE_NAMES}` | Bash 数组,规则名列表 | `"handler_db" "route_db"` |
|
||||
| `{RULE_TYPES}` | Bash 数组,规则类型列表 | `"ratchet" "wall"` |
|
||||
| `{PATTERN_N}` | grep 匹配模式 | `"my-pkg/database"` |
|
||||
| `{SUBDIR_N}` | 目标子目录 | `handlers/`, `routes/` |
|
||||
@@ -17,15 +17,16 @@ description: 需求完整工作流。用于从创建到归档的完整流程、H
|
||||
4. /req review pass → 评审通过 (approved, delivery_stage=analysis)
|
||||
5. /req next → 推进到 design/dev 阶段
|
||||
6. /req dev → 启动开发(创建【开发】任务, delivery_stage=dev)
|
||||
7. req-dev 技能 → 编写开发文档
|
||||
8. /req next → 推进到 review 阶段
|
||||
7. req-design 技能 → 编写开发设计文档
|
||||
7.5 /req ci → CI 质量门禁检查与自动修复(PR 提交后执行)
|
||||
8. /req next → 推进到 review(需 quality_gate 门禁证据)
|
||||
9. /req cr → 代码评审(创建【代码评审】任务, delivery_stage=review)
|
||||
10. /req next → 推进到 testing 阶段
|
||||
11. /req test → 测试验收(创建【测试】任务, delivery_stage=testing)
|
||||
12. /req next → 推进到 staging 阶段
|
||||
13. /req deploy staging → 部署 staging(创建【部署】任务, delivery_stage=staging)
|
||||
14. /req next → 推进到 released 阶段
|
||||
15. /req deploy prod → 部署生产(创建【部署】任务, delivery_stage=released)
|
||||
15. /req deploy prod → 部署生产 + PDV 验收(健康检查 → E2E 验收 → released)
|
||||
16. /req done → 归档 (archived)
|
||||
```
|
||||
|
||||
@@ -36,9 +37,9 @@ description: 需求完整工作流。用于从创建到归档的完整流程、H
|
||||
| 阶段 | 文档名 | 技能 |
|
||||
|------|--------|------|
|
||||
| PRD | 01-PRD.md | `req-prd` |
|
||||
| 开发 | 02-开发设计.md | `req-dev` |
|
||||
| 开发 | 02-开发设计.md | `req-design` |
|
||||
| 测试 | 03-测试报告.md | 自动生成 |
|
||||
| 发布 | 04-发布记录.md | 自动生成 |
|
||||
| 发布 | 04-发布记录.md | 自动生成(含 PDV 验收章节) |
|
||||
| 归档 | 05-生命周期总结.md | 自动生成 |
|
||||
|
||||
> 注:Stitch 原型不单独编号,嵌入 01-PRD.md 的「4.2 界面原型」章节。
|
||||
@@ -74,7 +75,7 @@ mcp__ai-proj__batch_sync_tasks_to_remote(taskIds)
|
||||
|------|------|----------|
|
||||
| 本机 | 开发调试 | `go test ./...` |
|
||||
| 预发布 | 集成测试 | API 验证 |
|
||||
| 生产 | 最终验收 | API + 功能验证 |
|
||||
| 生产 | 最终验收 | 健康检查 + PDV E2E 验收(页面可达、菜单可见、API 连通) |
|
||||
|
||||
## 任务关联规范
|
||||
|
||||
@@ -87,7 +88,8 @@ mcp__ai-proj__batch_sync_tasks_to_remote(taskIds)
|
||||
| implementation | 【开发】 | dev | 50% |
|
||||
| code_review | 【代码评审】 | review | 5% |
|
||||
| test | 【测试】 | testing | 15% |
|
||||
| deploy | 【部署】 | staging/released | 10% |
|
||||
| deploy | 【部署】 | staging/released | 5% |
|
||||
| verification | 【验收】 | released | 5% |
|
||||
| documentation | 【文档】 | any | 5% |
|
||||
|
||||
**进度计算**:按 link_role 权重统计已完成任务。
|
||||
@@ -111,7 +113,7 @@ mcp__ai-proj__batch_sync_tasks_to_remote(taskIds)
|
||||
│ └── 【部署】部署到 staging (singapore) (linkRole: deploy)
|
||||
└── 🏁 released 阶段
|
||||
├── 【部署】部署到 prod (tools_ai_proj) (linkRole: deploy)
|
||||
└── 【验收】功能验收确认 (linkRole: documentation)
|
||||
└── 【验收】PDV: {需求标题} (linkRole: verification)
|
||||
```
|
||||
|
||||
**命名规则**:所有任务标题必须以阶段前缀开头(如【评审】、【开发】、【部署】)。
|
||||
@@ -182,3 +184,25 @@ mcp__ai-proj__batch_sync_tasks_to_remote(taskIds)
|
||||
发送邮件:`/req notify [REQ-ID] --type <prd|dev|test|deploy|archive>`
|
||||
|
||||
默认收件人:项目邮件组(见项目配置)
|
||||
|
||||
---
|
||||
|
||||
## Memory 隔离规则(强制,源自 devflow-claude 借鉴)
|
||||
|
||||
**规则:涉及生命周期文档产出(PRD / 设计 / 测试报告 / 部署报告 / 生命周期总结)时,禁止受 auto-memory 影响产出物结构。**
|
||||
|
||||
### 禁止行为
|
||||
1. 不得跳过或合并 5 阶段文档的任一阶段(01-PRD ~ 05-生命周期总结)
|
||||
2. 不得因 memory 里的历史项目跳过章节
|
||||
3. 不得因 memory 中的"精简偏好"缩减文档结构
|
||||
4. 不得读取 `~/.claude/projects/*/memory/` 生成这些文档的正文
|
||||
|
||||
### 允许行为
|
||||
- memory 可影响**交互风格**(如提问详略)
|
||||
- memory 可影响**通知习惯**(如默认 CC 某人)
|
||||
- memory 可影响**触发时机**(如归档时是否自动同步思源)
|
||||
|
||||
### Why
|
||||
需求生命周期文档对应审计和交付物。结构变化会破坏检索、对标、复盘。memory 应调节"怎么聊",不应调节"最终文档长什么样"。
|
||||
|
||||
**参考**:devflow-claude `plugins/req/commands/_common.md` 同名规则,以及本项目 REQ-20260416-0017。
|
||||
|
||||
Reference in New Issue
Block a user