提交 文档驱动编程的 元文档
This commit is contained in:
commit
52d63b2ef3
|
|
@ -0,0 +1,122 @@
|
||||||
|
name: PR Check (SDD Full Gate)
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
paths:
|
||||||
|
- '.gitea/workflows/**'
|
||||||
|
- 'scripts/**'
|
||||||
|
- 'spec/**'
|
||||||
|
- 'src/**'
|
||||||
|
- 'test/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sdd-full-gate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code (no GitHub dependency)
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
SERVER_URL="${GITHUB_SERVER_URL:-${GITEA_SERVER_URL:-}}"
|
||||||
|
REPO_NAME="${GITHUB_REPOSITORY:-${GITEA_REPOSITORY:-}}"
|
||||||
|
COMMIT_SHA="${GITHUB_SHA:-${GITEA_SHA:-}}"
|
||||||
|
|
||||||
|
: "${SERVER_URL:?Could not determine SERVER_URL}"
|
||||||
|
: "${REPO_NAME:?Could not determine REPO_NAME}"
|
||||||
|
: "${COMMIT_SHA:?Could not determine COMMIT_SHA}"
|
||||||
|
|
||||||
|
echo "Using SERVER_URL=$SERVER_URL"
|
||||||
|
echo "Using REPO_NAME=$REPO_NAME"
|
||||||
|
echo "Using COMMIT_SHA=$COMMIT_SHA"
|
||||||
|
|
||||||
|
if [ -d ".git" ]; then
|
||||||
|
echo "Repo already initialized in workspace; using fetch"
|
||||||
|
git remote set-url origin "$SERVER_URL/$REPO_NAME.git"
|
||||||
|
else
|
||||||
|
git clone "$SERVER_URL/$REPO_NAME.git" .
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 关键:不要把 main fetch 到本地分支 main(会冲突)
|
||||||
|
git fetch origin main:refs/remotes/origin/main
|
||||||
|
git fetch --depth=1 origin "$COMMIT_SHA"
|
||||||
|
git checkout -f "$COMMIT_SHA"
|
||||||
|
|
||||||
|
- name: 1. Commit Message Check
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
echo "Checking commit messages for [AC-...] or [TASK-...] (range: refs/remotes/origin/main..HEAD)"
|
||||||
|
|
||||||
|
range="refs/remotes/origin/main..HEAD"
|
||||||
|
|
||||||
|
# Ignore tool-generated merge commits and filter out merge titles
|
||||||
|
msgs="$(git log --format=%B --no-merges "$range" || true)"
|
||||||
|
|
||||||
|
if [ -z "${msgs}" ]; then
|
||||||
|
echo "WARNING: No non-merge commits found in range ${range}. Skipping commit message gate."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$msgs" | cat
|
||||||
|
|
||||||
|
# Drop lines like "Merge branch ..." just in case
|
||||||
|
filtered="$(echo "$msgs" | grep -Ev '^(Merge( branch)? |Merge pull request )' || true)"
|
||||||
|
|
||||||
|
if echo "$filtered" | grep -Eq '\[(AC|TASK)-'; then
|
||||||
|
echo "OK: Found [AC-...] or [TASK-...] in PR commits"
|
||||||
|
else
|
||||||
|
echo "ERROR: At least one non-merge commit message in the PR must contain [AC-...] or [TASK-...]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: 2. OpenAPI Contract Level Check
|
||||||
|
env:
|
||||||
|
REQUIRE_PROVIDER_L2: "1"
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/*.sh
|
||||||
|
./scripts/check-openapi-level.sh
|
||||||
|
|
||||||
|
- name: 3. AC Traceability Check
|
||||||
|
shell: sh
|
||||||
|
run: ./scripts/check-traceability.sh
|
||||||
|
|
||||||
|
- name: 4. OpenAPI Breaking Change Check
|
||||||
|
shell: sh
|
||||||
|
run: ./scripts/check-openapi-diff.sh
|
||||||
|
|
||||||
|
- name: 5. Minimum Self-Test (mvn test)
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
# 针对 Java Spring 项目运行最小单测 (方案 B: 不存在则提示跳过)
|
||||||
|
if command -v mvn >/dev/null 2>&1; then
|
||||||
|
# 处理本地 jar 依赖:如果 lib 目录下存在 jar 包,先安装到本地仓库
|
||||||
|
if [ -f "lib/commons-codec-1.9.jar" ]; then
|
||||||
|
echo "Installing local jar: lib/commons-codec-1.9.jar"
|
||||||
|
mvn -q install:install-file \
|
||||||
|
-Dfile=lib/commons-codec-1.9.jar \
|
||||||
|
-DgroupId=commons-codec \
|
||||||
|
-DartifactId=commons-codec \
|
||||||
|
-Dversion=1.9 \
|
||||||
|
-Dpackaging=jar \
|
||||||
|
-DgeneratePom=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
mvn -q -DskipTests=false test
|
||||||
|
else
|
||||||
|
echo "Warning: mvn not found, skipping unit tests. Please ensure Runner has JDK/Maven for full enforcement."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: YAML Parse Check (Optional)
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
if ! python3 -c "import yaml" 2>/dev/null; then
|
||||||
|
python3 -m pip install pyyaml --user >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
if python3 -c "import yaml" 2>/dev/null; then
|
||||||
|
find spec -name "*.yaml" -o -name "*.yml" | xargs -I {} python3 -c "import yaml; yaml.safe_load(open('{}'))"
|
||||||
|
echo "YAML check passed."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
# agents.md(ai 编码硬规则,必须遵守)
|
||||||
|
|
||||||
|
## 0. 开始编码前(必须)
|
||||||
|
- 必须已读取(路径格式:`spec/<module>/...`):
|
||||||
|
- `spec/contracting.md`(契约硬规则)
|
||||||
|
- `spec/<module>/requirements.md`(当前模块需求)
|
||||||
|
- `spec/<module>/openapi.provider.yaml`(本模块提供)
|
||||||
|
- `spec/<module>/openapi.deps.yaml`(本模块依赖,如存在)
|
||||||
|
- **长会话/复杂任务接续**:若当前任务满足 `docs/session-handoff-protocol.md` 中的触发条件,**必须**先读取并持续更新 `docs/progress/{module}-{feature}-progress.md`。
|
||||||
|
- 若上述任一文档缺失、冲突或内容不明确:
|
||||||
|
- **禁止开始实现**
|
||||||
|
- 必须在 `spec/<module>/tasks.md` 记录“待澄清”并停止
|
||||||
|
|
||||||
|
## 1. 提交与同步(Git Cadence,必须)
|
||||||
|
- **提交粒度**:
|
||||||
|
- `spec/<module>/` 下的规范文件变更必须**单独 commit**(不得与实现代码混在同一 commit)。
|
||||||
|
- 实现代码按 `spec/<module>/tasks.md` 的**子任务完成**为粒度提交。
|
||||||
|
- **提交触发点**(满足任一且必须通过“最小自测”):
|
||||||
|
- 任何 `spec/<module>/` 规范文件发生变更。
|
||||||
|
- 任一 `spec/<module>/tasks.md` 子任务完成(从 ⏳/🔄 → ✅)。
|
||||||
|
- 触发 `docs/session-handoff-protocol.md` 阈值并准备会话接续前。
|
||||||
|
- **最小自测(必须)**:
|
||||||
|
- 能编译/构建通过。
|
||||||
|
- 单元测试通过。
|
||||||
|
- 至少一条契约校验或接口冒烟通过(与本次变更相关)。
|
||||||
|
- **提交质量(必须)**:
|
||||||
|
- **严禁**提交编译不通过或未通过最小自测的代码(不允许 checkpoint commit)。
|
||||||
|
- commit message 必须包含关联的验收标准 ID:`feat/fix: <desc> [AC-...]`。
|
||||||
|
|
||||||
|
## 2. 接口契约与代码对齐(必须)
|
||||||
|
- OpenAPI 文件必须声明全局成熟度:`info.x-contract-level: L0|L1|L2|L3`。
|
||||||
|
- **契约升级同步校验**:当 `openapi.provider.yaml` 提升到 L2(或补充 required/enum/format 等约束)后,必须同步补齐代码校验:
|
||||||
|
- 更新 DTO/Controller 的校验注解(如 `@Valid`, `@NotBlank`, `@NotNull`)。
|
||||||
|
- 补充至少 1 条针对该契约约束的测试用例。
|
||||||
|
- **Provider 合并门槛**:`openapi.provider.yaml` **< L2** 时,禁止将实现代码自动合并到 main。
|
||||||
|
- **Consumer 并行规则**:允许在 feature 分支基于 `openapi.deps.yaml` 的 **L0/L1** 级别进行并行开发(Mock/SDK/页面流),无需等待提供方实现。
|
||||||
|
|
||||||
|
## 3. 自动合并门槛(全通过才允许)
|
||||||
|
- 单元测试通过。
|
||||||
|
- 契约测试通过(Provider 响应符合 OpenAPI Schema,满足 L2 要求)。
|
||||||
|
- OpenAPI Diff 检查通过(无未声明 breaking change)。
|
||||||
|
- 需求追踪检查通过(AC 引用未断裂,且符合 `spec/contracting.md` 的自检清单)。
|
||||||
|
|
||||||
|
## 4. 分支与提交规范
|
||||||
|
- 分支:`feature/<AC-ID>-desc` 或 `fix/<AC-ID>-desc`
|
||||||
|
- commit:`feat/fix: <desc> [AC-ID]`
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
# contracting-guide.md(给人看的:契约治理与落地指南)
|
||||||
|
|
||||||
|
本文档面向人类(架构/平台/DevOps/测试/协作负责人),用于说明如何将契约成熟度与门禁策略落地到实际工程中。
|
||||||
|
|
||||||
|
> 编码智能体必须遵守的硬规则见项目根目录 `agents.md` 与 `spec/contracting.md`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
- 降低 AI 开发过程中的需求偏移(通过可执行门禁约束)
|
||||||
|
- 支持多窗口并行:调用方先行、提供方后补实现
|
||||||
|
- 降低跨模块联调成本:契约成为单一可信源(SSOT)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. OpenAPI 拆分与职责
|
||||||
|
|
||||||
|
- `openapi.provider.yaml`
|
||||||
|
- 描述“本模块对外提供什么能力”
|
||||||
|
- 由提供方维护并对其实现负责
|
||||||
|
|
||||||
|
- `openapi.deps.yaml`
|
||||||
|
- 描述“本模块依赖外部什么能力”(调用方需求侧契约)
|
||||||
|
- 由调用方先起草(draft),用于生成 mock / sdk
|
||||||
|
- 提供方后续可认领实现或提出映射/替代方案
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 契约成熟度(L0-L3)如何在团队中使用
|
||||||
|
|
||||||
|
建议把 `info.x-contract-level` 当作“里程碑标签”,在代码评审/门禁中用于决策,而不是装饰。
|
||||||
|
|
||||||
|
- L0:允许快速并行启动(mock 驱动)
|
||||||
|
- L1:允许调用方完成主流程开发(sdk/调用可稳定)
|
||||||
|
- L2:允许提供方实现进入可合并门槛(契约测试可跑)
|
||||||
|
- L3:允许进入兼容治理与长期演进阶段(deprecate / examples / changelog)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 门禁落地点(实现建议)
|
||||||
|
|
||||||
|
> 裸 git 场景下,推荐以服务端 hooks 作为最终防线。
|
||||||
|
|
||||||
|
### 4.1 git hooks
|
||||||
|
|
||||||
|
- 本地 hooks(可选):`pre-commit` / `pre-push`
|
||||||
|
- 目的:快速反馈,减少无效 push
|
||||||
|
- 注意:可被跳过,不能作为最终门禁
|
||||||
|
|
||||||
|
- 服务端 hooks(推荐):
|
||||||
|
- `pre-receive` / `update`
|
||||||
|
- 用于 main 分支硬门禁:不符合规则直接拒绝
|
||||||
|
- `post-receive`
|
||||||
|
- 触发构建、测试、契约校验
|
||||||
|
- 通过后执行自动合并(若你们实现了 auto-merge 流程)
|
||||||
|
|
||||||
|
### 4.2 构建阶段(mvn verify)
|
||||||
|
|
||||||
|
建议在构建阶段串联以下检查:
|
||||||
|
- openapi 解析与 lint
|
||||||
|
- openapi diff(breaking change 检测)
|
||||||
|
- provider 契约校验(响应符合 schema)
|
||||||
|
- 需求追踪校验(AC 引用未断裂)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 推荐的软/硬门禁策略
|
||||||
|
|
||||||
|
- feature 分支:软门禁
|
||||||
|
- 允许并行推进、允许契约处于 L0/L1
|
||||||
|
- 但不允许自动合并进入 main
|
||||||
|
|
||||||
|
- main 分支:硬门禁
|
||||||
|
- 至少要求 provider 达到 L2
|
||||||
|
- 契约测试与 diff 检查必须通过
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Git Cadence(提交节奏)与 Hooks 门禁(落地建议)
|
||||||
|
|
||||||
|
本节用于将 `agents.md` 中的“提交与同步(Git Cadence)”规则落到工程实现(裸 Git/中央仓库)。
|
||||||
|
|
||||||
|
### 6.1 提交节奏(建议与约束)
|
||||||
|
|
||||||
|
- **提交粒度(推荐与 `agents.md` 一致)**:
|
||||||
|
- `spec/<module>/` 下的规范文件变更必须**单独 commit**(Spec-only commit),不要与实现代码混在同一提交。
|
||||||
|
- 实现代码按 `spec/<module>/tasks.md` 的**子任务完成**为粒度提交。
|
||||||
|
|
||||||
|
- **必须提交的触发点**(满足任一,且必须通过最小自测):
|
||||||
|
- 任何 `spec/<module>/` 规范文件发生变更。
|
||||||
|
- 任一 `spec/<module>/tasks.md` 子任务完成(从 ⏳/🔄 → ✅)。
|
||||||
|
- 触发 `docs/session-handoff-protocol.md` 的阈值并准备会话接续前。
|
||||||
|
|
||||||
|
- **最小自测(与 `agents.md` 对齐)**:
|
||||||
|
- 能编译/构建通过。
|
||||||
|
- 单元测试通过。
|
||||||
|
- 至少一条契约校验或接口冒烟通过(与本次变更相关)。
|
||||||
|
|
||||||
|
- **提交信息要求**:
|
||||||
|
- commit message 必须包含 `[AC-...]`,便于需求追踪与门禁脚本校验。
|
||||||
|
|
||||||
|
### 6.2 hooks 分层:feature 软门禁 / main 硬门禁
|
||||||
|
|
||||||
|
> 原则:feature 分支尽量给快速反馈;main 分支必须可验收、可回滚、可自动合并。
|
||||||
|
|
||||||
|
#### 6.2.1 feature 分支(软门禁)
|
||||||
|
|
||||||
|
建议做(可在本地 `pre-commit/pre-push` 或服务端提示性检查):
|
||||||
|
- commit message 包含 `[AC-...]`
|
||||||
|
- OpenAPI 变更时:
|
||||||
|
- YAML 可解析
|
||||||
|
- `info.x-contract-level` 存在且为 L0-L3
|
||||||
|
- provider: `operationId` 唯一
|
||||||
|
|
||||||
|
允许:
|
||||||
|
- `openapi.deps.yaml` 处于 L0/L1,用于 Mock/SDK 并行开发。
|
||||||
|
- provider 实现处于 L0/L1(但仅限 feature,不得进入 main 的自动合并路径)。
|
||||||
|
|
||||||
|
#### 6.2.2 main 分支(硬门禁)
|
||||||
|
|
||||||
|
建议在服务端 `pre-receive/update` 中强制:
|
||||||
|
- Provider 契约成熟度门槛:`openapi.provider.yaml >= L2`
|
||||||
|
- 契约测试通过(Provider 响应符合 OpenAPI schema)
|
||||||
|
- OpenAPI diff 检查通过(无未声明 breaking change)
|
||||||
|
- 需求追踪检查通过(AC 引用未断裂)
|
||||||
|
|
||||||
|
### 6.3 推荐的实现位置(Gitea 避坑建议)
|
||||||
|
|
||||||
|
> **重要:Gitea 环境下的 Hooks 优先级与冲突处理**
|
||||||
|
|
||||||
|
- **优先使用 Gitea 分支保护(Branch Protection)**:
|
||||||
|
- 进入 `仓库设置 -> 分支 -> 保护分支(main)`。
|
||||||
|
- 勾选 `禁止直接推送`:这是实现“禁推 main”最稳定、最标准的方式。
|
||||||
|
- 勾选 `允许由 PR 合并`:确保协作流程通畅。
|
||||||
|
- **避坑提示**:不要在服务端 `pre-receive` 钩子中一刀切地通过 `refs/heads/main` 拒绝所有更新。因为 Gitea 服务端的 PR 合并操作也会触发此钩子,导致 PR 无法合入。
|
||||||
|
|
||||||
|
- **服务端 Hooks(pre-receive)职责收敛**:
|
||||||
|
- 仅用于做“极轻量”的全局硬约束(如文件大小、非 utf-8 检查)。
|
||||||
|
- 不建议在 hook 里跑 Maven/JDK 等重型构建,否则会严重拖慢 push 速度并导致超时。
|
||||||
|
|
||||||
|
- **Gitea Actions / CI(推荐的门禁位置)**:
|
||||||
|
- 这是落地“provider >= L2”和“契约测试”的最佳位置。
|
||||||
|
- 将校验脚本(如 `scripts/check-openapi-level.sh`)集成进 Action。
|
||||||
|
- 在保护分支设置中勾选 `要求通过状态检查后才允许合并`。
|
||||||
|
- **环境搭建详见**:`docs/setup-gitea-actions-gate.md`(包含新环境部署、Runner 配置与离线环境优化)。
|
||||||
|
- **离线 Checkout 实战经验**:在无法访问 GitHub 的环境下,不应使用 `actions/checkout`,应通过 `git clone` 结合环境变量(优先 `GITHUB_`,fallback `GITEA_`)实现离线拉取。若遇到 `refusing to fetch into branch` 错误,应确保不直接 fetch 到本地 `main` 分支,而是使用 `refs/remotes/origin/main` 作为引用基准。
|
||||||
|
- **本地 JAR 依赖自动化**:对于无法通过中央仓库获取的本地 JAR,应在 CI 的测试步骤前通过 `mvn install:install-file` 自动安装到 Runner 容器的本地仓库,以确保 `mvn test` 能够顺利通过且无路径警告。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 检查清单(适合做成脚本/CI step)
|
||||||
|
|
||||||
|
- OpenAPI 可解析
|
||||||
|
- `info.x-contract-level` 存在且合法
|
||||||
|
- provider operationId 唯一
|
||||||
|
- AC 追踪一致性(x-requirements 或 traceability 表)
|
||||||
|
- 合并门槛:provider >= L2
|
||||||
|
- breaking change 检测:无未声明破坏性变更
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
# 规范驱动 + 接口先行(SDD + API-First)实战流程 Runbook(从 0 到可合并)
|
||||||
|
|
||||||
|
本文档用于复盘并固化一次完整的“从初始化规范体系 → 落地 Gitea 门禁 → 实际业务跑通(以 ai-robot 多渠道改造为例)”的全流程。
|
||||||
|
|
||||||
|
目标:换环境/换人/换模型后,可以按此 SOP 复现同样的协作链路,并在后续迭代中持续优化。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 核心原则(务必先读)
|
||||||
|
|
||||||
|
1. **SSOT(单一可信源)优先**:以 `spec/<module>/requirements.md` + OpenAPI 契约为准,任何实现必须可追溯到 AC。
|
||||||
|
2. **Consumer-first 并行**:依赖契约由调用方先写 `openapi.deps.yaml`,提供方后补实现并以 provider-driven 校验对齐。
|
||||||
|
3. **分支治理**:`main` 禁止直接 push,仅允许 PR 合并;feature 分支允许并行与草案(L0/L1)。
|
||||||
|
4. **门禁物理化**:规则必须进入 Actions/脚本,否则只停留在“口头约束”。
|
||||||
|
5. **长任务可接续**:满足阈值即启用 `docs/session-handoff-protocol.md`,并维护 `docs/progress/...`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 仓库文档分层(最终形态)
|
||||||
|
|
||||||
|
- 方法论(详细、指导生成规范):`docs/spec-product-zh.md`
|
||||||
|
- AI 入口硬规则(极简、命令式):`agents.md`
|
||||||
|
- AI 契约硬规则(L0-L3、门禁、自检):`spec/contracting.md`
|
||||||
|
- 人类落地指南(hooks/CI/操作细节):`docs/contracting-guide.md`
|
||||||
|
- 会话接续协议(复杂任务强制):`docs/session-handoff-protocol.md`
|
||||||
|
- Gitea Actions 门禁落地手册:`docs/setup-gitea-actions-gate.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Gitea(Docker)落地门禁:一次性 checklist
|
||||||
|
|
||||||
|
> 若是新环境,从这里开始。
|
||||||
|
|
||||||
|
### 2.1 分支保护(必须)
|
||||||
|
|
||||||
|
在 Gitea 仓库:`设置 -> 分支 -> 保护分支(main)`
|
||||||
|
- [ ] 禁止直接推送 main
|
||||||
|
- [ ] 禁止强制推送(force push)
|
||||||
|
- [ ] 禁止删除 main
|
||||||
|
|
||||||
|
> 经验:不要用 `pre-receive` 一刀切拒绝 `refs/heads/main`,否则 PR 合并也会被拒绝。
|
||||||
|
|
||||||
|
### 2.2 启用 Actions + Runner(必须)
|
||||||
|
|
||||||
|
- 在 Gitea `app.ini` 中启用:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[ACTIONS]
|
||||||
|
ENABLED = true
|
||||||
|
```
|
||||||
|
|
||||||
|
- 启动 runner(内网环境用宿主机内网 IP,不要用 127.0.0.1 / host.docker.internal):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d --name gitea-act-runner --restart always \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v /data/act_runner:/data \
|
||||||
|
-e GITEA_INSTANCE_URL="http://<HOST_IP>:<GITEA_PORT>" \
|
||||||
|
-e GITEA_RUNNER_REGISTRATION_TOKEN="<REG_TOKEN>" \
|
||||||
|
-e GITEA_RUNNER_NAME="runner-1" \
|
||||||
|
gitea/act_runner:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 离线环境必做(避免访问 GitHub)
|
||||||
|
|
||||||
|
- workflow **不要**使用 `actions/checkout@v3`。
|
||||||
|
- 使用离线 checkout(仓库内 workflow 已实现):
|
||||||
|
- 优先 `GITHUB_*` 变量,fallback `GITEA_*` 变量
|
||||||
|
- `git clone "$SERVER_URL/$REPO_NAME.git" .` + `git checkout "$COMMIT_SHA"`
|
||||||
|
|
||||||
|
### 2.4 Required Checks(必须)
|
||||||
|
|
||||||
|
- 首次运行 workflow 后,在保护分支启用状态检查。
|
||||||
|
- 推荐 job 名称:
|
||||||
|
- 轻量门禁:`contract-level-check`
|
||||||
|
- 全量门禁:`sdd-full-gate`
|
||||||
|
|
||||||
|
> 注意:Gitea 通常需要 **至少跑过一次 Actions** 才能在 UI 中选择/识别状态检查项。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 规范生成(文档阶段)SOP(强模型执行)
|
||||||
|
|
||||||
|
> 目标:把需求与契约先做对,再进入编码。
|
||||||
|
|
||||||
|
### 3.1 分支策略(文档与代码共存)
|
||||||
|
|
||||||
|
- 若“旧代码在 master,新规范在 main”,且历史不相关:
|
||||||
|
- 推荐创建 feature 分支后合并:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout master
|
||||||
|
git checkout -b feat/<feature>
|
||||||
|
git merge main --allow-unrelated-histories
|
||||||
|
```
|
||||||
|
|
||||||
|
- 若出现 untracked 覆盖:先提交 checkpoint(保护性提交)再 merge。
|
||||||
|
|
||||||
|
### 3.2 生成顺序(每次只生成 1 个文件)
|
||||||
|
|
||||||
|
严格按顺序:
|
||||||
|
1) `spec/<module>/requirements.md`
|
||||||
|
2) `spec/<module>/openapi.deps.yaml`
|
||||||
|
3) `spec/<module>/openapi.provider.yaml`
|
||||||
|
4) `spec/<module>/design.md`
|
||||||
|
5) `spec/<module>/tasks.md`
|
||||||
|
|
||||||
|
每生成 1 个文件:
|
||||||
|
- 人类审阅通过后,做 **spec-only commit**(只提交该文件)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 编码执行(实现阶段)SOP(弱模型可执行)
|
||||||
|
|
||||||
|
> 目标:按 `tasks.md` 原子任务执行,降低长上下文导致的偏移。
|
||||||
|
|
||||||
|
### 4.1 进入编码前必须读取
|
||||||
|
- `agents.md`
|
||||||
|
- `spec/contracting.md`
|
||||||
|
- `spec/<module>/requirements.md`
|
||||||
|
- `spec/<module>/openapi.provider.yaml` / `openapi.deps.yaml`
|
||||||
|
- `spec/<module>/design.md`
|
||||||
|
- `spec/<module>/tasks.md`
|
||||||
|
|
||||||
|
### 4.2 提交节奏(Git Cadence)
|
||||||
|
|
||||||
|
- `spec/<module>/` 变更:必须单独 commit
|
||||||
|
- 实现代码:按 tasks 子任务完成提交
|
||||||
|
- commit message:必须包含 `[AC-...]`
|
||||||
|
|
||||||
|
### 4.3 多窗口并行(推荐分工)
|
||||||
|
|
||||||
|
- 窗口 A:基础设施 + DTO + 配置 + DB
|
||||||
|
- 窗口 B:渠道适配层(Adapter/Factory/Controller)
|
||||||
|
- 窗口 C:路由层 + 会话管理
|
||||||
|
- 窗口 D:AI client + Resilience + fallback
|
||||||
|
- 窗口 E:集成测试/回归
|
||||||
|
|
||||||
|
合并策略:各窗口在同一 feature 分支上串行提交(避免互相覆盖),或各自分支最后 rebase 合并。
|
||||||
|
|
||||||
|
### 4.4 契约与实现双向对齐(L2 升级两步走)
|
||||||
|
|
||||||
|
当实现接近完成,准备进入可合并状态时:
|
||||||
|
1. **第一步:描述型升级**。AI 仅修改 `openapi.provider.yaml` 将等级提升至 L2,补充代码已实现的约束(描述、示例、错误结构)。执行 spec-only commit。
|
||||||
|
2. **第二步:实现约束对齐**。AI 根据 L2 契约中的 `required`、`enum`、`format` 等硬约束,同步补齐代码中的 `@Valid` 校验注解、枚举处理及单测。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 最终集成与收网 SOP
|
||||||
|
|
||||||
|
### 5.1 契约与实现双向对齐(L2 升级两步走)
|
||||||
|
|
||||||
|
当实现接近完成,准备进入可合并状态时:
|
||||||
|
1. **第一步:描述型升级**。AI 仅修改 `openapi.provider.yaml` 将等级提升至 L2,补充代码已实现的约束(描述、示例、错误结构)。执行 spec-only commit。
|
||||||
|
2. **第二步:实现约束对齐**。AI 根据 L2 契约中的 `required`、`enum`、`format` 等硬约束,同步补齐代码中的 `@Valid` 校验注解、枚举处理及单测。
|
||||||
|
|
||||||
|
### 5.2 合拢动作
|
||||||
|
|
||||||
|
1. **本地汇总**:将所有并行窗口的分支/提交合并至主功能分支。
|
||||||
|
2. **一致性自检**:确认 `spec/` 文档与 `src/` 实现 100% 对齐(AC 追踪、契约等级、任务勾选)。
|
||||||
|
3. **推送 PR**:推送到远端并发起向 `main` 的 PR。
|
||||||
|
4. **门禁验收**:观察 `sdd-full-gate` 状态。
|
||||||
|
- 若 `Commit Message Check` 失败:补充 `[AC-...]` 或 `[TASK-...]` 标注。
|
||||||
|
- 若 `AC Traceability Check` 失败:修正需求引用或代码注释。
|
||||||
|
5. **合并合拢**:Checks 全绿后执行合并,并同步本地 `main` 分支。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 自动化门禁(Actions 脚本)说明
|
||||||
|
|
||||||
|
### 5.1 当前已落地的脚本
|
||||||
|
|
||||||
|
- OpenAPI 等级门禁:`scripts/check-openapi-level.sh`
|
||||||
|
- AC 追踪门禁:`scripts/check-traceability.sh`
|
||||||
|
- OpenAPI Breaking change(最小版):`scripts/check-openapi-diff.sh`
|
||||||
|
|
||||||
|
### 5.2 常见报错与处理
|
||||||
|
|
||||||
|
- `provider contract-level must be >= L2`:提升 `openapi.provider.yaml` 到 L2
|
||||||
|
- `No module named yaml`:workflow 会尝试安装 PyYAML,失败则跳过 YAML parse check
|
||||||
|
- `Cannot ping Gitea instance`:runner URL 不可用,换宿主机内网 IP
|
||||||
|
- `cloning https://github.com/actions/checkout`:workflow 仍在用 checkout action,需改离线 checkout
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 需求迭代与生命周期管理 SOP
|
||||||
|
|
||||||
|
> 目标:确保功能演进过程中,规范文档同步滚动且历史可追溯。
|
||||||
|
|
||||||
|
### 7.1 迭代模式选择
|
||||||
|
- **微调模式**:直接在现有 `requirements.md` 追加 AC,ID 保持连续(如 `AC-AISVC-21`)。
|
||||||
|
- **特性模式**:在 `spec/<module>/` 下新增 `requirements-<feature>.md`,适用于独立大特性。
|
||||||
|
- **重构模式**:建立新模块目录(如 `spec/ai-service-v2/`),适用于架构级变更。
|
||||||
|
|
||||||
|
### 7.2 迭代执行步骤
|
||||||
|
1. **增量 Scoping**:分析新需求对现有 AC 的影响,明确是否涉及破坏性变更。
|
||||||
|
2. **版本滚动**:修改任一规范文件时,必须同步更新 Frontmatter 中的 `version`(如 `0.1.0 -> 0.2.0`)。
|
||||||
|
3. **任务追加**:新任务作为 `tasks.md` 的新 Phase(如 `Phase 6`),严禁删除已完成任务的历史。
|
||||||
|
4. **契约先行**:若涉及接口变动,必须先更新 `openapi.provider.yaml` 到 L0/L1,再写实现代码。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 维护与迭代建议
|
||||||
|
|
||||||
|
- 每次重大变更后更新:
|
||||||
|
- `docs/setup-gitea-actions-gate.md`
|
||||||
|
- 本文档(Runbook)
|
||||||
|
- 逐步增强门禁:OpenAPI 专业 diff、契约测试(schema 校验)、最小自测强制化(runner 预装 Maven/JDK)
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
# {module}-{feature} - Progress (Example)
|
||||||
|
|
||||||
|
> 示例文件:用于演示 `docs/session-handoff-protocol.md` 的进度文档结构。
|
||||||
|
> 复制此文件并重命名为:`docs/progress/{module}-{feature}-progress.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Context
|
||||||
|
|
||||||
|
- module: `ruoyi-forum`
|
||||||
|
- feature: `REG` (example)
|
||||||
|
- status: 🔄 进行中
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 Spec References (SSOT)
|
||||||
|
|
||||||
|
- agents: `agents.md`
|
||||||
|
- contracting: `spec/contracting.md`
|
||||||
|
- requirements: `spec/ruoyi-forum/requirements.md`
|
||||||
|
- openapi_provider: `spec/ruoyi-forum/openapi.provider.yaml`
|
||||||
|
- openapi_deps: `spec/ruoyi-forum/openapi.deps.yaml`
|
||||||
|
- design: `spec/ruoyi-forum/design.md`
|
||||||
|
- tasks: `spec/ruoyi-forum/tasks.md`
|
||||||
|
|
||||||
|
> 注意:以上路径必须与实际模块目录一致;如果文件不存在,先创建/补齐 spec,再编码。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Overall Progress (Phases)
|
||||||
|
|
||||||
|
- [ ] Phase 1: Spec 初始化与对齐 (60%) 🔄 [tasks.md: T-INIT]
|
||||||
|
- [ ] Phase 2: Provider 实现与契约校验 (0%) ⏳ [tasks.md: T-PROVIDER]
|
||||||
|
- [ ] Phase 3: Consumer 并行开发(Mock/SDK) (0%) ⏳ [tasks.md: T-CONSUMER]
|
||||||
|
- [ ] Phase 4: 集成测试 / 回归 / 合并门禁 (0%) ⏳ [tasks.md: T-INTEGRATION]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Current Phase
|
||||||
|
|
||||||
|
### Goal
|
||||||
|
在 `spec/ruoyi-forum/` 下完成 requirements + openapi(provider/deps) 的最小可并行版本。
|
||||||
|
|
||||||
|
### Sub Tasks
|
||||||
|
- [x] 梳理模块边界与依赖清单 ✅ [tasks.md: T-INIT-01]
|
||||||
|
- [ ] 生成 `requirements.md` 草案 🔄 [tasks.md: T-INIT-02]
|
||||||
|
- [ ] 生成 `openapi.provider.yaml` 与 `openapi.deps.yaml` (L0) ⏳ [tasks.md: T-INIT-03]
|
||||||
|
|
||||||
|
### Next Action (Must be Specific)
|
||||||
|
|
||||||
|
**Immediate**: 生成 `spec/ruoyi-forum/requirements.md` 的验收标准(AC)与 Traceability 映射表。
|
||||||
|
|
||||||
|
**Details**:
|
||||||
|
1. file: `spec/ruoyi-forum/requirements.md`
|
||||||
|
2. action: 补齐至少 3 条 AC(EARS),并在 Traceability 表中映射到计划中的 endpoint。
|
||||||
|
3. reference:
|
||||||
|
- `docs/spec-product-zh.md`(requirements 模板与 traceability 要求)
|
||||||
|
- `agents.md`(必须先读 spec 再编码、AC 追踪硬规则)
|
||||||
|
4. constraints:
|
||||||
|
- 不得在未更新 requirements 的情况下更改业务口径
|
||||||
|
- AC ID 必须稳定可追溯(示例:`AC-REG-01`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ Technical Context
|
||||||
|
|
||||||
|
### Module Structure (Only What Matters)
|
||||||
|
|
||||||
|
- `spec/ruoyi-forum/`
|
||||||
|
- `requirements.md`
|
||||||
|
- `openapi.provider.yaml`
|
||||||
|
- `openapi.deps.yaml`
|
||||||
|
- `design.md`
|
||||||
|
- `tasks.md`
|
||||||
|
|
||||||
|
### Key Decisions (Why / Impact)
|
||||||
|
|
||||||
|
- decision: OpenAPI 拆分 provider/deps
|
||||||
|
reason: 支持 consumer-first 并行开发;调用方可基于 deps 生成 mock/SDK
|
||||||
|
impact: provider 实现需在合并前提升 provider 契约到 L2 并通过契约校验
|
||||||
|
|
||||||
|
### Code Snippets (Optional but Recommended)
|
||||||
|
|
||||||
|
```text
|
||||||
|
// 示例:在 controller / @Operation / 测试用例 / commit 中包含 [AC-...] 以便追踪
|
||||||
|
// [AC-REG-01]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧾 Session History
|
||||||
|
|
||||||
|
### Session #1 (YYYY-MM-DD)
|
||||||
|
- completed:
|
||||||
|
- 初始化 Phase 划分与任务清单
|
||||||
|
- changes:
|
||||||
|
- 新增 docs/progress/example-progress.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Startup Guide
|
||||||
|
|
||||||
|
1. 读取本进度文档,定位当前 Phase 与 Next Action。
|
||||||
|
2. 打开并阅读 Spec References 指向的模块规范(requirements/openapi/tasks)。
|
||||||
|
3. 直接执行 Next Action;遇到缺口先更新 spec 再编码。
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
# SDD + API-First 提示词指南 (Prompt Playbook)
|
||||||
|
|
||||||
|
本文档提供在“规范驱动 + 接口先行”开发生命周期中,人类与 AI 协作的标准化提示词模板。通过这些模板,可以确保即便使用不同的 AI 窗口或模型,也能产生一致的规范工件。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 文档生成阶段 (强模型/架构 AI)
|
||||||
|
|
||||||
|
此阶段目标是确立 SSOT。建议采用“单步确认”模式。
|
||||||
|
|
||||||
|
### 1.1 启动 Scoping (定界)
|
||||||
|
**适用场景**:新需求启动时。
|
||||||
|
```text
|
||||||
|
【模式】规范驱动 + 接口先行。禁止直接写代码。
|
||||||
|
【必须遵守】agents.md / spec/contracting.md / docs/spec-product-zh.md
|
||||||
|
|
||||||
|
【任务背景】
|
||||||
|
- 模块: <module_name>
|
||||||
|
- 目标: <一句话描述功能>
|
||||||
|
- 依赖: <外部系统或模块清单>
|
||||||
|
|
||||||
|
【输出要求】
|
||||||
|
请在 spec/<module_name>/ 下输出 Scoping 结果:
|
||||||
|
1) 模块边界:明确覆盖与不覆盖的范围。
|
||||||
|
2) 依赖接口清单 (Consumer-First):重点列出你需要的外部服务接口草案。
|
||||||
|
3) 产出物计划:确认将生成的 spec 文件列表 (requirements, openapi.provider/deps, design, tasks)。
|
||||||
|
|
||||||
|
输出完成后停止,等待确认。
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 生成 Requirements (需求)
|
||||||
|
**适用场景**:Scoping 确认后。
|
||||||
|
```text
|
||||||
|
请生成 spec/<module_name>/requirements.md。
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1) 包含用户故事 [US-...] 和验收标准 [AC-...] (使用 EARS 语法)。
|
||||||
|
2) 必须包含 Traceability 表,将 AC 映射到预期的 API Endpoint。
|
||||||
|
3) 考虑降级、超时、幂等及多租户隔离 (若适用)。
|
||||||
|
|
||||||
|
完成后停止,待确认后请执行 spec-only commit。
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 生成 OpenAPI (契约)
|
||||||
|
**适用场景**:需求确认后。建议分两步:deps (依赖) -> provider (提供)。
|
||||||
|
```text
|
||||||
|
请生成 spec/<module_name>/openapi.provider.yaml (或 openapi.deps.yaml)。
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1) 在 info 下标记 x-contract-level: L0。
|
||||||
|
2) 100% 对齐 requirements.md 中的 AC。
|
||||||
|
3) 包含 operationId、结构化错误模型及必要的状态码。
|
||||||
|
4) 若涉及流式,请定义 SSE 事件模型 (message/final/error)。
|
||||||
|
|
||||||
|
完成后停止,待确认后请执行 spec-only commit。
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 编码实现阶段 (弱模型/执行 AI)
|
||||||
|
|
||||||
|
此阶段目标是按图施工,严格遵守提交节奏。
|
||||||
|
|
||||||
|
### 2.1 开启并行窗口 (分工指令)
|
||||||
|
**适用场景**:将任务分发给不同的 AI 窗口。
|
||||||
|
```text
|
||||||
|
【执行任务】Phase <X> (TASK-001 ~ TASK-00N)
|
||||||
|
【硬规则】
|
||||||
|
1. 严格按 spec/<module_name>/tasks.md 顺序执行。
|
||||||
|
2. 禁止修改其它窗口负责的文件或目录。
|
||||||
|
3. 每完成一个原子任务必须执行:最小自测 (mvn test / pytest) -> git commit -> 更新 progress.md。
|
||||||
|
4. 代码/注释必须包含关联的 [AC-...] 标注。
|
||||||
|
5. 严禁执行 git push,所有提交保留在本地 main 分支。
|
||||||
|
|
||||||
|
现在开始执行第一个任务:<task_desc>。
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 契约与实现对齐 (L2 升级)
|
||||||
|
**适用场景**:实现接近完成,准备合并前。
|
||||||
|
```text
|
||||||
|
【契约对齐】将 openapi.provider.yaml 提升至 L2 并对齐代码。
|
||||||
|
|
||||||
|
任务:
|
||||||
|
1. 修改 info.x-contract-level 为 L2。
|
||||||
|
2. 检查代码实现,确保 DTO 校验 (Validation)、枚举、错误码与契约 100% 一致。
|
||||||
|
3. 若契约收紧 (如增加必填项),必须同步修改代码与测试。
|
||||||
|
4. 确保 Traceability 校验通过。
|
||||||
|
|
||||||
|
完成后执行 spec-only commit 和实现 commit。
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 接续与收网阶段 (全流程)
|
||||||
|
|
||||||
|
### 3.1 启用会话接续协议 (Handoff)
|
||||||
|
**适用场景**:任务复杂 (子任务 >= 5) 或需要跨窗口接力。
|
||||||
|
```text
|
||||||
|
当前任务满足复杂度阈值,必须启用接续协议。
|
||||||
|
请按 docs/session-handoff-protocol.md,在 docs/progress/ 下创建/更新 <module>-progress.md。
|
||||||
|
要求:详细记录 Next Action,必须精确到 文件:行号。
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 PR 门禁自检 (收网前)
|
||||||
|
**适用场景**:推送远端并发起 PR 前。
|
||||||
|
```text
|
||||||
|
请进行收网前自检:
|
||||||
|
1. 确认所有 spec 文件已完成 spec-only commit。
|
||||||
|
2. 确认所有任务在 tasks.md 中已标为 ✅。
|
||||||
|
3. 确认 provider 等级为 L2。
|
||||||
|
4. 运行本地脚本:./scripts/check-traceability.sh 和 ./scripts/check-openapi-level.sh 确保 100% 通过。
|
||||||
|
|
||||||
|
自检无误后,创建功能分支推送到远端并发起 PR。
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 故障排除 (Troubleshooting)
|
||||||
|
- **Actions 报错**: “检查日志,若是环境变量缺失或离线 checkout 问题,参考 docs/setup-gitea-actions-gate.md 修复。”
|
||||||
|
- **门禁失败**: “若是 AC ID 找不到,检查 requirements.md 是否同步更新。”
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
# Session Handoff Protocol - AI Reference v3.0 (Spec-Driven Aligned)
|
||||||
|
|
||||||
|
> 面向“规范驱动 + 接口先行”体系的会话接续协议。
|
||||||
|
> 核心目标:在多窗口、长任务场景下,通过单文档(Progress)实现 80% 上下文恢复。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 核心目标
|
||||||
|
跨会话任务延续 = 读取 Progress 文档 + 引用模块 Spec 目录 = 立即开始工作。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ 触发条件(硬门禁阈值)
|
||||||
|
|
||||||
|
当满足以下 **任一** 条件时,AI **必须** 启用本协议并维护进度文档:
|
||||||
|
|
||||||
|
| 维度 | 触发阈值(必须启用) | 目的 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| **子任务数** | `spec/<module>/tasks.md` 中子任务 ≥ 5 个 | 避免长列表任务丢失状态 |
|
||||||
|
| **文件变更** | 预计或已修改文件数 ≥ 10 个 | 追踪改动范围,防止遗漏 |
|
||||||
|
| **工具调用** | 单个会话内工具调用次数 ≥ 40 次 | 应对上下文堆积导致的智能下降 |
|
||||||
|
| **上下文质量** | 用户反馈或 AI 自检发现明显的指令偏移 | 强制重置上下文并对齐 SSOT |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 进度文档强制清单(YAML 格式)
|
||||||
|
|
||||||
|
进度文档路径:`docs/progress/{module}-{feature}-progress.md`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# 进度文档必须包含的字段(不可缺失)
|
||||||
|
|
||||||
|
context:
|
||||||
|
module: "{module_name}" # 对应 spec/<module>/ 目录名
|
||||||
|
feature: "{feature_id}" # 对应 requirements.md 中的 feature_id
|
||||||
|
status: [🔄进行中 | ⏳待开始 | ✅已完成]
|
||||||
|
|
||||||
|
spec_references:
|
||||||
|
# 必须引用模块 Spec 目录下的 SSOT 文档
|
||||||
|
requirements: "spec/{module}/requirements.md"
|
||||||
|
openapi_provider: "spec/{module}/openapi.provider.yaml"
|
||||||
|
openapi_deps: "spec/{module}/openapi.deps.yaml"
|
||||||
|
design: "spec/{module}/design.md"
|
||||||
|
tasks: "spec/{module}/tasks.md"
|
||||||
|
|
||||||
|
overall_progress:
|
||||||
|
format: "- [ ] Phase X: 名称 (进度%) [关联 Tasks.md ID]"
|
||||||
|
min_phases: 3
|
||||||
|
max_phases: 6
|
||||||
|
|
||||||
|
current_phase:
|
||||||
|
goal: "一句话描述本阶段目标"
|
||||||
|
sub_tasks:
|
||||||
|
- [x] 子任务 A (✅) [Task ID]
|
||||||
|
- [ ] 子任务 B (🔄) [Task ID]
|
||||||
|
|
||||||
|
next_action:
|
||||||
|
immediate: "下一步立即执行的具体原子任务"
|
||||||
|
details:
|
||||||
|
file: "path/to/file.ext:line_number"
|
||||||
|
action: "具体做什么(如:实现 XXX 接口的校验逻辑)"
|
||||||
|
reference: "参考文件及行号"
|
||||||
|
constraints: "执行此任务必须注意的硬约束(如:必须符合 AC-REG-01)"
|
||||||
|
|
||||||
|
technical_context:
|
||||||
|
module_structure: "本次任务涉及的核心目录/文件"
|
||||||
|
key_decisions:
|
||||||
|
- decision: "技术决策内容"
|
||||||
|
reason: "为什么这么做"
|
||||||
|
impact: "对后续任务的影响"
|
||||||
|
code_snippets: "关键实现代码或 Mock 示例(至少 1-2 个)"
|
||||||
|
|
||||||
|
session_history:
|
||||||
|
- session: "Session #X (YYYY-MM-DD)"
|
||||||
|
completed: [任务列表]
|
||||||
|
changes: [改动的文件清单]
|
||||||
|
|
||||||
|
startup_guide:
|
||||||
|
- "Step 1: 读取本进度文档(了解当前位置与下一步)"
|
||||||
|
- "Step 2: 读取 spec_references 中定义的模块规范(了解业务与接口约束)"
|
||||||
|
- "Step 3: 直接执行 next_action"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 工作规则(CRITICAL)
|
||||||
|
|
||||||
|
### 1. 启动模式
|
||||||
|
- **继续模式**:检查 `docs/progress/` 下是否存在对应模块的进度文档。若存在,Read 文档 → 引用 Spec 目录 → 简短汇报状态 → 直接开始。
|
||||||
|
- **新建模式**:若满足“触发条件(硬门禁阈值)”任一项(或涉及跨模块并行),必须先创建进度文档,再开始工作。
|
||||||
|
|
||||||
|
### 2. 下一步行动(The Gold Rule)
|
||||||
|
- 禁止输出模糊的下一步(如“继续开发”)。
|
||||||
|
- **必须**具体到 `文件路径:行号` 和 `原子动作`。
|
||||||
|
|
||||||
|
### 3. 停止与交接
|
||||||
|
- 触发条件:Phase 完成、达到稳定检查点、或工具调用次数过多需同步。
|
||||||
|
- **动作**:更新进度文档(标记 ✅、更新 🔄、详细记录 `next_action`)、告知用户如何接续。
|
||||||
|
|
||||||
|
### 4. 禁止事项
|
||||||
|
- 禁止编造或假设需求;信息不足必须询问用户,并在 Progress 中记录澄清结果。
|
||||||
|
- 禁止使用不存在的工具接口名;所有操作应基于当前环境可用工具。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 响应模板
|
||||||
|
|
||||||
|
### 会话结束(产出交接)
|
||||||
|
```text
|
||||||
|
已达到阶段性检查点,进度文档已更新:
|
||||||
|
- 路径: docs/progress/{module}-{feature}-progress.md
|
||||||
|
|
||||||
|
当前进度:
|
||||||
|
✅ {已完成任务}
|
||||||
|
🔄 当前停留: {具体文件:行号}
|
||||||
|
|
||||||
|
下次接续请指令:"继续 {module} 的 {feature} 任务"
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
# Gitea(Docker)落地:分支保护 + Actions/Runner + OpenAPI 全量门禁(操作手册)
|
||||||
|
|
||||||
|
本文档用于在**新环境/新机器**上从零落地本仓库的“规范驱动 + 接口先行”门禁体系,确保换环境后无需重新摸索。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 目标与产物
|
||||||
|
|
||||||
|
落地后应满足:
|
||||||
|
- [ ] `main` 禁止直接 push,仅允许 PR 合并
|
||||||
|
- [ ] PR 合并到 `main` 时自动运行 Actions(全量门禁)
|
||||||
|
- [ ] 环境无法访问 GitHub 时也能正常运行校验
|
||||||
|
|
||||||
|
仓库内相关文件(必须存在于 `main`):
|
||||||
|
- `agents.md`:AI 编码总纲
|
||||||
|
- `spec/contracting.md`:契约标准
|
||||||
|
- `scripts/*.sh`:核心校验脚本(Level/Traceability/Diff)
|
||||||
|
- `.gitea/workflows/pr-check.yaml`:全量门禁工作流
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Gitea Actions 基础建设
|
||||||
|
|
||||||
|
按照 `docs/setup-gitea-actions-gate.md` 中的步骤完成:
|
||||||
|
1. **Gitea 配置**:在 `app.ini` 中启用 `[ACTIONS] ENABLED = true`。
|
||||||
|
2. **Runner 部署**:启动 `gitea/act_runner:latest` 容器并成功注册到 Gitea。
|
||||||
|
3. **内网连接**:使用宿主机内网 IP 作为 `GITEA_INSTANCE_URL`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 启用全量自动化门禁
|
||||||
|
|
||||||
|
### 2.1 门禁项列表
|
||||||
|
|
||||||
|
当前的 `.gitea/workflows/pr-check.yaml` (Job ID: `sdd-full-gate`) 包含以下检查:
|
||||||
|
|
||||||
|
| 门禁项 | 脚本位置 | 检查逻辑 | 失败处理 |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **1. 契约成熟度** | `check-openapi-level.sh` | 校验 `info.x-contract-level`。合并 main 必须 ≥ L2。 | AI 需更新 OpenAPI 文件等级。 |
|
||||||
|
| **2. 需求追踪** | `check-traceability.sh` | 校验代码中的 `[AC-ID]` 是否在 `requirements.md` 中定义过。 | AI 需补充需求文档或修正代码注释。 |
|
||||||
|
| **3. Breaking Change** | `check-openapi-diff.sh` | 对比 `main` 分支,拦截被删除的 Endpoint 或 Method。 | AI 需还原破坏性变更或与人类确认。 |
|
||||||
|
| **4. 最小自测** | 内置命令 | 执行 `mvn test`(Java)。若 Runner 环境无 mvn 则跳过并提示。 | AI 需修复单测或编译错误。 |
|
||||||
|
|
||||||
|
### 2.2 在 Gitea 中开启硬约束
|
||||||
|
|
||||||
|
1. **触发第一次运行**:新建一个 PR 到 `main`(修改 `src/` 或 `spec/` 下的文件)。
|
||||||
|
2. **配置分支保护**:
|
||||||
|
* 进入 `仓库设置 -> 分支 -> 保护分支(main)`。
|
||||||
|
* 勾选 **“启用状态检查”**。
|
||||||
|
* 在输入框中填入:`sdd-full-gate`。
|
||||||
|
* 保存。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 常见排障与优化
|
||||||
|
|
||||||
|
### 3.1 离线环境报错
|
||||||
|
如果报错 `GITEA_SERVER_URL is required`,请确认已合并最新的 `.gitea/workflows/pr-check.yaml`。当前版本已支持自动探测 `GITHUB_` 与 `GITEA_` 系列变量。
|
||||||
|
|
||||||
|
### 3.2 Maven (mvn) 找不到
|
||||||
|
如果日志提示 `Warning: mvn not found`,说明你的 Runner 镜像(ubuntu-latest)没有预装 Maven。
|
||||||
|
* **短期**:脚本会自动跳过,不阻断合并。
|
||||||
|
* **长期建议**:定制 Docker 镜像或在宿主机安装 Maven 并卷入容器。
|
||||||
|
|
||||||
|
### 3.3 Python yaml 模块缺失
|
||||||
|
脚本会自动尝试 `pip install pyyaml`。如果依然失败,YAML 语法解析步骤将自动跳过,不影响核心契约校验。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 关联文档入口
|
||||||
|
|
||||||
|
- `agents.md`:AI 实时遵循的提交与编码节奏。
|
||||||
|
- `docs/contracting-guide.md`:契约治理与 Gitea 避坑指南。
|
||||||
|
- `docs/session-handoff-protocol.md`:复杂任务接续协议。
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
---
|
||||||
|
name: spec-product-zh
|
||||||
|
version: 1.3.0
|
||||||
|
description: "基于 Kiro Spec-Driven Development 与 API-First 思想的‘规范驱动+接口先行’协同开发方法论。通过结构化文档(Requirements/OpenAPI/Design/Tasks)实现 AI 开发的全链路可控与高效协作。"
|
||||||
|
license: MIT
|
||||||
|
compatibility: opencode
|
||||||
|
metadata:
|
||||||
|
audience: "开发者, 架构师, AI 智能体"
|
||||||
|
workflow: specification
|
||||||
|
language: zh-CN
|
||||||
|
methodology:
|
||||||
|
- "Kiro Spec-Driven Development"
|
||||||
|
- "API-First (接口先行)"
|
||||||
|
artifacts:
|
||||||
|
- requirements.md
|
||||||
|
- openapi.provider.yaml
|
||||||
|
- openapi.deps.yaml
|
||||||
|
- design.md
|
||||||
|
- tasks.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 规范驱动 + 接口先行(Spec-Driven + API-First)开发方法论
|
||||||
|
|
||||||
|
本方法论旨在通过结构化规范文档作为人类与 AI 的“共同语言”,并以接口契约作为协作锚点,实现多角色 AI 的并行、有序、高质量开发。
|
||||||
|
|
||||||
|
> 职责边界:
|
||||||
|
> - 本文档专注于**生成规范文档**(requirements/design/tasks/openapi)。
|
||||||
|
> - AI 在编码阶段的行为准则、门禁、引用规则等执行规范:遵循项目根目录 `agents.md`。
|
||||||
|
> - 契约成熟度与门禁的硬规则(给 AI):见 `spec/contracting.md`。
|
||||||
|
> - hooks/构建检查等落地细则(给人):见 `docs/contracting-guide.md`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 前置步骤:模块拆分与依赖接口盘点(Module & Dependency Scoping)
|
||||||
|
|
||||||
|
在进入“某个模块的 Spec 生成”之前,必须先做一次轻量的边界澄清,否则后续文档会不可控、也无法高效并行。
|
||||||
|
|
||||||
|
### 0.1 产出物
|
||||||
|
|
||||||
|
建议在本模块的 Spec 目录下新增一个轻量清单(可写在 `requirements.md` 中的 Scope/Dependencies 小节,或单独写在 `scope.md`):
|
||||||
|
|
||||||
|
- **模块边界说明(Module Scope)**:本次 Spec 只覆盖哪个模块/子域?不覆盖什么?
|
||||||
|
- **依赖清单(Dependencies)**:本模块会调用哪些外部模块/第三方?
|
||||||
|
- **依赖接口清单(Dependency Contracts)**:对每个依赖,列出“需要的接口能力”(先不管实现在哪)。
|
||||||
|
|
||||||
|
### 0.2 判定标准(进入第 1 阶段的准入条件)
|
||||||
|
|
||||||
|
- 能明确回答:
|
||||||
|
- “这是哪个模块的需求?”
|
||||||
|
- “本模块对外提供什么能力?”
|
||||||
|
- “本模块依赖谁?依赖哪些接口?”
|
||||||
|
- 若依赖不明确:先补齐依赖接口清单(哪怕先是草案),再进入需求与接口建模。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 核心流程:三阶段协同(每次只做一个模块/一个领域边界)
|
||||||
|
|
||||||
|
### 第一阶段:需求定义与接口建模(Requirements & API Design)
|
||||||
|
|
||||||
|
- **目标**:明确“做什么”以及“如何交互”,建立单一可信源(SSOT)。
|
||||||
|
- **模块化原则**:对于庞大系统,一个 Spec 上下文应**仅聚焦于一个功能模块或领域边界**(如 `ruoyi-forum`),避免跨模块需求混杂导致 AI 上下文过载。
|
||||||
|
- **依赖先行(Dependency-First)**:
|
||||||
|
- 如果本模块依赖其他模块(或第三方服务),必须在此阶段先完成**外部依赖接口契约**(哪怕只有最小集合)。
|
||||||
|
- **调用方**:基于依赖契约生成 Mock 并直接进入开发逻辑,无需等待被调用方实现。
|
||||||
|
- **实现方**:后续根据该依赖契约补全真实实现,并通过契约一致性验证。
|
||||||
|
- **产出**(默认建议放在 `spec/<module>/` 目录下,例如 `spec/ruoyi-forum/requirements.md`):
|
||||||
|
- `requirements.md`(本模块需求)
|
||||||
|
- `openapi.provider.yaml`(本模块对外提供的 API)
|
||||||
|
- `openapi.deps.yaml`(本模块依赖的外部 API 契约,供 Mock/SDK 生成)
|
||||||
|
|
||||||
|
### 第二阶段:技术设计与方案评审(Technical Design)
|
||||||
|
|
||||||
|
- **目标**:细化“怎么实现”,解决技术实现路径与数据模型问题。
|
||||||
|
- **产出**:`design.md`(技术蓝图)。
|
||||||
|
- **规范**:
|
||||||
|
- 设计方案必须引用需求 ID。
|
||||||
|
- 若涉及跨模块调用,应明确定义:依赖 API 的 Mock 策略、超时/重试/熔断/降级、错误映射。
|
||||||
|
|
||||||
|
### 第三阶段:任务分解与并行开发(Task Decomposition & Implementation)
|
||||||
|
|
||||||
|
- **目标**:将设计转化为可执行的原子任务。
|
||||||
|
- **并行编程协作(多窗口并行)**:
|
||||||
|
- **前端/调用方 AI**:基于 `openapi.provider.yaml` + `openapi.deps.yaml` 生成 SDK + Mock,优先完成页面与业务流程。
|
||||||
|
- **后端/提供方 AI**:实现 `openapi.provider.yaml` 对应接口,并持续导出 OpenAPI 工件供校验。
|
||||||
|
- **依赖实现方 AI**:对照 `openapi.deps.yaml`(或其子集)补齐真实实现。
|
||||||
|
- **产出**:`tasks.md`(执行清单)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 关键文档规范
|
||||||
|
|
||||||
|
### 2.1 requirements.md(需求规范)
|
||||||
|
|
||||||
|
requirements 的目标是把“自然语言需求”固化为**可追踪、可校验、可版本化**的规范。
|
||||||
|
|
||||||
|
必须包含以下结构:
|
||||||
|
- **Frontmatter**:包含 `feature_id`, `title`, `status`, `version`(可按团队需要扩展)。
|
||||||
|
- **用户故事**:使用统一句式“作为…我希望…以便…”。
|
||||||
|
- **验收标准(EARS 语法)**:每条验收标准必须带稳定 ID(推荐 `AC-<FEATURE>-NN`)。
|
||||||
|
- **追踪映射(Traceability)**:验收标准到接口端点(以及可选 operationId)的映射。
|
||||||
|
|
||||||
|
#### 2.1.1 requirements.md 标准模板(可直接复制)
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
feature_id: "REG" # 功能短 ID(用于拼接 US/AC/NFR 编号)
|
||||||
|
title: "用户注册"
|
||||||
|
status: "draft" # draft | review | approved | implemented
|
||||||
|
version: "0.1.0"
|
||||||
|
owners:
|
||||||
|
- "product"
|
||||||
|
- "backend"
|
||||||
|
- "frontend"
|
||||||
|
last_updated: "2026-02-23"
|
||||||
|
source:
|
||||||
|
type: "conversation" # conversation | issue | doc | meeting
|
||||||
|
ref: ""
|
||||||
|
---
|
||||||
|
|
||||||
|
# 用户注册(REG)
|
||||||
|
|
||||||
|
## 1. 背景与目标
|
||||||
|
- 背景:
|
||||||
|
- 目标:
|
||||||
|
- 非目标(Out of Scope):
|
||||||
|
|
||||||
|
## 2. 模块边界(Scope)
|
||||||
|
- 覆盖:
|
||||||
|
- 不覆盖:
|
||||||
|
|
||||||
|
## 3. 依赖盘点(Dependencies)
|
||||||
|
- 依赖模块/第三方:
|
||||||
|
- 依赖 A:用途说明
|
||||||
|
- 依赖 B:用途说明
|
||||||
|
|
||||||
|
## 4. 用户故事(User Stories)
|
||||||
|
- [US-REG-01] 作为访客,我希望使用邮箱注册账号,以便获得会员权益。
|
||||||
|
|
||||||
|
## 5. 验收标准(Acceptance Criteria, EARS)
|
||||||
|
- [AC-REG-01] WHEN 访客提交有效的邮箱与密码 THEN 系统 SHALL 创建用户并返回 201。
|
||||||
|
- [AC-REG-02] WHEN 访客提交的邮箱不合法 THEN 系统 SHALL 返回 400,并给出字段级错误信息。
|
||||||
|
- [AC-REG-03] WHEN 邮箱已被注册 THEN 系统 SHALL 返回 400,并提示“邮箱已注册”。
|
||||||
|
|
||||||
|
## 6. 追踪映射(Traceability)
|
||||||
|
|
||||||
|
| AC ID | Endpoint | 方法 | operationId(可选) | 备注 |
|
||||||
|
|------|----------|------|---------------------|------|
|
||||||
|
| AC-REG-01 | /users/register | POST | registerUser | 创建用户 |
|
||||||
|
| AC-REG-02 | /users/register | POST | registerUser | 参数校验 |
|
||||||
|
| AC-REG-03 | /users/register | POST | registerUser | 唯一性约束 |
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 OpenAPI 契约(openapi.provider.yaml / openapi.deps.yaml)
|
||||||
|
|
||||||
|
为支持“大系统多模块 + 多窗口并行”,推荐将 OpenAPI **拆成两份**:
|
||||||
|
|
||||||
|
- `openapi.provider.yaml`:**本模块对外提供**的 API(本模块是 Provider)。
|
||||||
|
- `openapi.deps.yaml`:**本模块依赖**的外部 API 契约(本模块是 Consumer,便于 Mock/SDK 生成)。
|
||||||
|
|
||||||
|
约定:
|
||||||
|
- 两份文件都必须可被 Mock/SDK/测试工具消费。
|
||||||
|
- Provider 文件建议带 `x-requirements: [AC-xxx]` 追踪需求。
|
||||||
|
- Deps 文件应定义你“需要对方提供什么能力”,不以对方当前实现为准(可以先草案,后续对齐)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2.2.1 契约成熟度(Contract Refinement Levels)
|
||||||
|
|
||||||
|
为减少“边实现边改契约”导致的需求偏移,本方法论引入契约成熟度等级,并要求在 OpenAPI 的 `info` 下进行**全局标记**:
|
||||||
|
|
||||||
|
- `info.x-contract-level: L0 | L1 | L2 | L3`
|
||||||
|
|
||||||
|
> 约定:
|
||||||
|
> - `openapi.deps.yaml` 与 `openapi.provider.yaml` 都必须标记该字段。
|
||||||
|
> - L0/L1 用于并行启动;Provider 在进入可合并的实现阶段前应提升到 L2。
|
||||||
|
|
||||||
|
**L0(占位/可 Mock)**
|
||||||
|
- 目标:调用方可快速生成 Mock,跑通主流程(happy path)。
|
||||||
|
- 必须:最小 path/method/2xx 响应骨架;统一错误响应骨架(可粗)。
|
||||||
|
- 允许:schema 粗粒度、校验缺失、错误码不细。
|
||||||
|
|
||||||
|
**L1(可调用/可生成 SDK)**
|
||||||
|
- 目标:调用方可基于契约生成 SDK,并开发主要业务流程。
|
||||||
|
- 必须:关键请求/响应字段(必填/可选)、基本状态码集合、operationId、描述。
|
||||||
|
|
||||||
|
**L2(可验收/可契约测试)**
|
||||||
|
- 目标:提供者可运行 Provider-driven 契约校验(schema/错误语义/边界)。
|
||||||
|
- 必须:关键校验规则(format/minLength/enum/range 等)、结构化错误模型、核心异常分支。
|
||||||
|
|
||||||
|
**L3(可演进/可兼容治理)**
|
||||||
|
- 目标:长期维护与兼容演进,减少 breaking change。
|
||||||
|
- 必须:deprecated/兼容策略、示例(examples)、变更记录(changelog 或版本策略)。
|
||||||
|
|
||||||
|
#### 2.2.2 从轻量切分到可验收契约:细化路径
|
||||||
|
|
||||||
|
- 在第 0 节(Scoping)阶段:输出依赖能力清单(自然语言即可),并将其转写为 `openapi.deps.yaml` 的 **L0** 草案。
|
||||||
|
- 在第 1 阶段(需求与接口建模)阶段:
|
||||||
|
- 新增/完善 AC(EARS)会驱动契约从 L0 → L1(补齐核心字段、状态码与映射)。
|
||||||
|
- 每个新增字段/错误语义/校验规则,应能追溯到某条 `AC-*`(否则属于“潜在需求漂移”)。
|
||||||
|
- 在进入“提供者实现可合并”之前:必须将 `openapi.provider.yaml` 提升到 **L2**(达到可契约测试的程度)。
|
||||||
|
- 在产品稳定期:逐步推动关键接口达到 **L3**(兼容治理与示例完善)。
|
||||||
|
|
||||||
|
> 门禁落点(实现细节):成熟度等级与合并策略、校验规则应由 `agents.md` 中的执行规则 + Git hooks/构建检查共同约束。
|
||||||
|
|
||||||
|
### 2.3 design.md(技术设计)
|
||||||
|
|
||||||
|
- **内容**:系统架构、数据模型(ER 图)、核心流程、异常与重试、鉴权与审计。
|
||||||
|
- **跨模块调用要求**:必须描述依赖 API 的失败策略(超时/重试/熔断/降级)以及错误映射。
|
||||||
|
|
||||||
|
### 2.4 tasks.md(任务清单)
|
||||||
|
|
||||||
|
- **结构**:带状态的 Markdown 任务列表。
|
||||||
|
- **要求**:任务按“本模块提供能力”与“本模块消费依赖”拆分;每个任务需标注关联 `AC-ID`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 自动化协同增强(Hooks/门禁)
|
||||||
|
|
||||||
|
本方法论要求在流程中引入自动化门禁,以确保文档与交付一致。
|
||||||
|
|
||||||
|
- 当 `requirements.md` / OpenAPI 文件变更时,应触发:OpenAPI Lint、Diff(兼容性检查)、契约一致性测试。
|
||||||
|
- 当 `tasks.md` 任务完成时,应同步更新进度与相关说明。
|
||||||
|
|
||||||
|
> 说明:关于“AI 编码行为准则、阶段闸门、引用规则、变更策略”等执行层规范,见项目根目录 `agents.md`;关于 hooks/构建检查等落地细则,见 `docs/contracting-guide.md`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 如何执行
|
||||||
|
|
||||||
|
1. **前置步骤**:完成模块拆分与依赖接口盘点(第 0 节)。
|
||||||
|
2. **发起需求**:仅针对“一个模块”,生成初始 `requirements.md`。
|
||||||
|
3. **定义契约**:输出 `openapi.provider.yaml` 与 `openapi.deps.yaml`,并进行接口走查。
|
||||||
|
4. **架构设计**:生成 `design.md`,明确模块内边界、数据流与依赖策略。
|
||||||
|
5. **任务执行**:生成并执行 `tasks.md`;调用方优先基于 deps 契约 Mock 并行推进。
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# OpenAPI Breaking Change Detector (Minimal Script).
|
||||||
|
# 1. Compare the PR's openapi.provider.yaml with the version from the 'main' branch.
|
||||||
|
# 2. Detect basic breaking changes (deleted endpoints, changed methods, etc.)
|
||||||
|
# 3. Fail if breaking changes are found without explicit developer acknowledgement.
|
||||||
|
|
||||||
|
# Base branch to compare against
|
||||||
|
BASE_BRANCH="refs/remotes/origin/main"
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "ERROR: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a temporary directory for main branch files
|
||||||
|
tmp_base=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$tmp_base"' EXIT
|
||||||
|
|
||||||
|
check_breaking_changes() {
|
||||||
|
module_path="$1"
|
||||||
|
provider_file="spec/$module_path/openapi.provider.yaml"
|
||||||
|
base_file="$tmp_base/$module_path/openapi.provider.yaml"
|
||||||
|
|
||||||
|
[ -f "$provider_file" ] || return 0
|
||||||
|
|
||||||
|
# Try to extract the file from the base branch
|
||||||
|
mkdir -p "$(dirname "$base_file")"
|
||||||
|
if ! git show "$BASE_BRANCH:$provider_file" > "$base_file" 2>/dev/null; then
|
||||||
|
echo "New module or provider file detected: $provider_file. Skipping diff check."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Checking breaking changes for $provider_file against $BASE_BRANCH..."
|
||||||
|
|
||||||
|
# 1. Simple Endpoint/Method deletion check using grep/diff
|
||||||
|
# Extract paths and methods (simple grep for ' /path:' and ' get|post|put|delete:')
|
||||||
|
extract_endpoints() {
|
||||||
|
grep -E "^[[:space:]]{2}/|^[[:space:]]{4}(get|post|put|delete|patch):" "$1" | sed 's/[[:space:]]*//g'
|
||||||
|
}
|
||||||
|
|
||||||
|
old_endpoints=$(mktemp)
|
||||||
|
new_endpoints=$(mktemp)
|
||||||
|
extract_endpoints "$base_file" > "$old_endpoints"
|
||||||
|
extract_endpoints "$provider_file" > "$new_endpoints"
|
||||||
|
|
||||||
|
deleted_count=$(comm -23 "$old_endpoints" "$new_endpoints" | wc -l)
|
||||||
|
|
||||||
|
if [ "$deleted_count" -gt 0 ]; then
|
||||||
|
echo "CRITICAL: Detected deleted endpoints or methods in $provider_file:"
|
||||||
|
comm -23 "$old_endpoints" "$new_endpoints"
|
||||||
|
rm -f "$old_endpoints" "$new_endpoints"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$old_endpoints" "$new_endpoints"
|
||||||
|
echo "OK: No obvious breaking changes in $provider_file endpoints."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find modules
|
||||||
|
errors=0
|
||||||
|
for spec_dir in spec/*; do
|
||||||
|
if [ -d "$spec_dir" ]; then
|
||||||
|
module_name=$(basename "$spec_dir")
|
||||||
|
if ! check_breaking_changes "$module_name"; then
|
||||||
|
errors=$((errors + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$errors" -gt 0 ]; then
|
||||||
|
die "Breaking change check failed. Please revert changes or mark them as compatible."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "All OpenAPI Breaking Change checks passed."
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# Check OpenAPI contract levels for multi-module layout.
|
||||||
|
# - For PRs targeting main: require provider contract level >= L2.
|
||||||
|
# - Always require info.x-contract-level exists and is L0-L3.
|
||||||
|
#
|
||||||
|
# Expected locations:
|
||||||
|
# - spec/<module>/openapi.provider.yaml
|
||||||
|
# - spec/<module>/openapi.deps.yaml (optional)
|
||||||
|
|
||||||
|
require_provider_l2="${REQUIRE_PROVIDER_L2:-0}"
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "ERROR: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
level_rank() {
|
||||||
|
case "$1" in
|
||||||
|
L0) echo 0;;
|
||||||
|
L1) echo 1;;
|
||||||
|
L2) echo 2;;
|
||||||
|
L3) echo 3;;
|
||||||
|
*) echo -1;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_level() {
|
||||||
|
# Extracts the first occurrence of info.x-contract-level: L?
|
||||||
|
# Accepts patterns like:
|
||||||
|
# x-contract-level: L2
|
||||||
|
# x-contract-level: "L2"
|
||||||
|
# under info:
|
||||||
|
file="$1"
|
||||||
|
|
||||||
|
awk '
|
||||||
|
BEGIN{in_info=0; level=""}
|
||||||
|
{
|
||||||
|
# detect "info:" at any indentation
|
||||||
|
if ($0 ~ /^[[:space:]]*info:[[:space:]]*$/) { in_info=1; info_indent=match($0,/[^ ]/)-1; next }
|
||||||
|
if (in_info==1) {
|
||||||
|
# if indentation decreases or new top-level key begins, leave info block
|
||||||
|
cur_indent=match($0,/[^ ]/)-1;
|
||||||
|
if (cur_indent <= info_indent && $0 ~ /^[^[:space:]]/ ) { in_info=0 }
|
||||||
|
}
|
||||||
|
if (in_info==1 && level=="" && $0 ~ /^[[:space:]]*x-contract-level:[[:space:]]*/) {
|
||||||
|
line=$0
|
||||||
|
sub(/^[[:space:]]*x-contract-level:[[:space:]]*/,"",line)
|
||||||
|
gsub(/"|\047/,"",line)
|
||||||
|
# strip comments
|
||||||
|
sub(/[[:space:]]*#.*/,"",line)
|
||||||
|
gsub(/[[:space:]]+/,"",line)
|
||||||
|
level=line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END{print level}
|
||||||
|
' "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_file_level() {
|
||||||
|
file="$1"
|
||||||
|
kind="$2" # provider|deps
|
||||||
|
|
||||||
|
[ -f "$file" ] || die "Missing OpenAPI file: $file"
|
||||||
|
|
||||||
|
level="$(extract_level "$file" | tr -d '\r')"
|
||||||
|
[ -n "$level" ] || die "$file: missing info.x-contract-level (expected under info: x-contract-level: L0|L1|L2|L3)"
|
||||||
|
|
||||||
|
rank="$(level_rank "$level")"
|
||||||
|
[ "$rank" -ge 0 ] || die "$file: invalid x-contract-level '$level' (expected L0|L1|L2|L3)"
|
||||||
|
|
||||||
|
if [ "$kind" = "provider" ] && [ "$require_provider_l2" = "1" ]; then
|
||||||
|
if [ "$rank" -lt 2 ]; then
|
||||||
|
die "$file: provider contract-level must be >= L2 for merge-to-main (current: $level)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "OK: $file level=$level"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Find all provider openapi files under spec/*
|
||||||
|
provider_files="$(find spec -mindepth 2 -maxdepth 2 -type f -name 'openapi.provider.yaml' 2>/dev/null || true)"
|
||||||
|
[ -n "$provider_files" ] || die "No provider OpenAPI found. Expected at least one spec/<module>/openapi.provider.yaml"
|
||||||
|
|
||||||
|
# Provider files always must have a valid level; for main merges, require >= L2
|
||||||
|
for f in $provider_files; do
|
||||||
|
check_file_level "$f" provider
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
# Deps files are optional, but if present must have valid level
|
||||||
|
for d in $(find spec -mindepth 2 -maxdepth 2 -type f -name 'openapi.deps.yaml' 2>/dev/null || true); do
|
||||||
|
check_file_level "$d" deps
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "All OpenAPI contract level checks passed."
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# Check AC (Acceptance Criteria) Traceability.
|
||||||
|
# 1. Collect all AC IDs defined in spec/**/requirements.md (format: [AC-FEATURE-NN])
|
||||||
|
# 2. Collect all AC IDs referenced in src/** and test/** (format: [AC-FEATURE-NN])
|
||||||
|
# 3. Verify every referenced AC ID exists in requirements.
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "ERROR: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Collect defined ACs from requirements
|
||||||
|
defined_acs_file=$(mktemp)
|
||||||
|
# Matches patterns like [AC-REG-01]
|
||||||
|
find spec -name "requirements.md" -exec grep -o "\[AC-[A-Z0-9]\+-[0-9]\{2\}\]" {} + | sed 's/.*\[\(AC-[A-Z0-9]\+-[0-9]\{2\}\)\].*/\1/' | sort -u > "$defined_acs_file"
|
||||||
|
|
||||||
|
if [ ! -s "$defined_acs_file" ]; then
|
||||||
|
echo "Warning: No AC IDs found in spec/**/requirements.md. Skipping traceability check."
|
||||||
|
rm -f "$defined_acs_file"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found $(wc -l < "$defined_acs_file") defined AC IDs in requirements."
|
||||||
|
|
||||||
|
# 2. Collect referenced ACs from src/ and test/
|
||||||
|
referenced_acs_file=$(mktemp)
|
||||||
|
# Search in src and test directories
|
||||||
|
find src test -type f \( -name "*.java" -o -name "*.vue" -o -name "*.ts" -o -name "*.tsx" -o -name "*.md" \) -exec grep -o "\[AC-[A-Z0-9]\+-[0-9]\{2\}\]" {} + | sed 's/.*\[\(AC-[A-Z0-9]\+-[0-9]\{2\}\)\].*/\1/' | sort -u > "$referenced_acs_file"
|
||||||
|
|
||||||
|
if [ ! -s "$referenced_acs_file" ]; then
|
||||||
|
echo "OK: No AC references found in code. Traceability check skipped."
|
||||||
|
rm -f "$defined_acs_file" "$referenced_acs_file"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found $(wc -l < "$referenced_acs_file") AC references in code."
|
||||||
|
|
||||||
|
# 3. Verify references
|
||||||
|
missing_acs=0
|
||||||
|
while read -r ac; do
|
||||||
|
if ! grep -q "^$ac$" "$defined_acs_file"; then
|
||||||
|
echo "ERROR: Referenced AC ID '$ac' not found in any requirements.md"
|
||||||
|
missing_acs=$((missing_acs + 1))
|
||||||
|
fi
|
||||||
|
done < "$referenced_acs_file"
|
||||||
|
|
||||||
|
rm -f "$defined_acs_file" "$referenced_acs_file"
|
||||||
|
|
||||||
|
if [ "$missing_acs" -gt 0 ]; then
|
||||||
|
die "Traceability check failed: $missing_acs unknown AC reference(s) found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "OK: All AC references in code are traceable to requirements."
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
# contracting.md(ai 必须遵守:契约成熟度与门禁规则)
|
||||||
|
|
||||||
|
本文件仅包含**编码智能体必须遵守**的契约约束与门禁规则(面向执行与校验)。
|
||||||
|
|
||||||
|
> 约定:若本文件与 `agents.md` 冲突,以 `agents.md` 为准;若本文件与 `spec/<module>/requirements.md` 或 OpenAPI 契约冲突,必须先修正文档再编码,禁止猜测。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 文件与字段硬约束
|
||||||
|
|
||||||
|
- 本模块的 OpenAPI 契约拆分为:
|
||||||
|
- `openapi.provider.yaml`:本模块对外提供的 API(provider)
|
||||||
|
- `openapi.deps.yaml`:本模块依赖的外部 API 契约(consumer 需求侧)
|
||||||
|
|
||||||
|
- 两份 OpenAPI 都必须在 `info` 下声明成熟度(全局标记,不按 operation 级):
|
||||||
|
- `info.x-contract-level: L0 | L1 | L2 | L3`
|
||||||
|
|
||||||
|
- provider 端接口(建议强制):
|
||||||
|
- `operationId` 必须存在且在同文件内唯一
|
||||||
|
- 应通过 `x-requirements: [AC-...]` 或 requirements 的 traceability 表建立 AC 追踪
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 契约成熟度(L0-L3)最低要求
|
||||||
|
|
||||||
|
- **L0(占位/可 mock)**:允许 schema 粗粒度;仅用于并行启动与 mock。
|
||||||
|
- **L1(可调用/可生成 sdk)**:关键请求/响应字段 required/optional 明确;基本状态码集合明确。
|
||||||
|
- **L2(可验收/可契约测试)**:关键字段校验规则与错误语义稳定;可进行 provider-driven schema 校验。
|
||||||
|
- **L3(可演进/可兼容治理)**:具备兼容策略与示例;可治理 breaking change。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 细化路径(粗 → 细)执行规则
|
||||||
|
|
||||||
|
- 新增/调整 **AC** 会驱动契约细化;任何字段/错误语义/校验规则的新增,必须能追溯到某条 `AC-*`。
|
||||||
|
- 当实现准备进入可合并状态(尤其是自动合并)时,必须先把 `openapi.provider.yaml` 提升到 **L2**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 门禁规则(软/硬)
|
||||||
|
|
||||||
|
### 4.1 feature 分支(软门禁)
|
||||||
|
|
||||||
|
允许:
|
||||||
|
- 使用 `openapi.deps.yaml (L0/L1+)` 生成 mock/sdks,推进调用方开发。
|
||||||
|
|
||||||
|
禁止:
|
||||||
|
- 未满足 `openapi.provider.yaml >= L2` 的情况下,将 provider 实现以“自动合并”方式进入 main。
|
||||||
|
|
||||||
|
### 4.2 main 分支(硬门禁)
|
||||||
|
|
||||||
|
满足以下条件前,禁止合并(或禁止自动合并):
|
||||||
|
- `openapi.provider.yaml >= L2`
|
||||||
|
- 契约测试通过(响应符合 openapi schema)
|
||||||
|
- openapi diff 检查通过(无未声明 breaking change)
|
||||||
|
- 需求追踪检查通过(AC 引用未断裂)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 最小检查清单(编码智能体自检)
|
||||||
|
|
||||||
|
- [ ] openapi 文件可解析
|
||||||
|
- [ ] `info.x-contract-level`存在且为 L0-L3
|
||||||
|
- [ ] provider: operationId 唯一
|
||||||
|
- [ ] provider: 能追踪到 AC(x-requirements 或 traceability)
|
||||||
Loading…
Reference in New Issue