# Edu 项目规则（微服务版）

> 版本：1.0
> 日期：2026-07-08
> 适用范围：Edu 微服务架构（DDD + EDA + CQRS，多语言 monorepo）
> 关联文档：
>
> - [004 架构影响地图](../../docs/architecture/004_architecture_impact_map.md)
> - [理想蓝图 0010](../../docs/architecture/0010_architecture.md)
> - [迁移指南](../../MIGRATION_GUIDE.md)
> - [known-issues 速查](../../docs/troubleshooting/known-issues.md)

---

## 1. 架构图优先规则

**任何任务开始前，必须先查阅架构影响地图，通过图定位代码和模块。**

1. **先图后码**：执行任何分析、修改、搜索任务时，首先运行 `pnpm run arch:scan` 更新 arch.db，再通过 `pnpm run arch:query` 查询目标模块、函数、依赖关系，结合阅读 `docs/architecture/004_architecture_impact_map.md` 定位架构设计意图，最后按图索骥读取源码
2. **图未覆盖则先补图**：如果发现项目中存在 arch.db 未记录的模块、函数、表、路由、proto 等结构，**必须先运行 `pnpm run arch:scan` 重新扫描**（多语言扫描器：TS + Go + Python + Proto），然后检查 004 是否需要补充
3. **改码必同步图**：对源码的任何修改完成后，必须运行 `pnpm run arch:scan` 更新 arch.db；若架构设计意图有变化，同步更新 004

### 架构文档清单

| 文档                                               | 用途                                                     |
| -------------------------------------------------- | -------------------------------------------------------- |
| `docs/architecture/004_architecture_impact_map.md` | 架构设计意图唯一源（人类可读）                           |
| `docs/architecture/0010_architecture.md`           | 理想蓝图（目标态）                                       |
| `docs/architecture/roadmap/`                       | 长远规划（tech-debt / pending-features）                 |
| `docs/architecture/runbooks/`                      | 运维手册（P6 硬化、post-p6-followup、incident-response） |
| `docs/troubleshooting/known-issues.md`             | 已知问题速查（场景→技术映射，索引式，不写工作日志）      |

### 需要同步图的场景

- 新增/删除/重命名导出函数、组件、Hook、类型
- 修改函数签名（参数、返回类型）
- 修改权限点（Permissions 常量）或角色-权限映射
- 新增/删除数据库表
- 新增/删除路由页面、API 路由、gRPC 服务、proto message
- 修改模块间依赖关系或服务间调用关系
- 新增服务、新增模块、新增 proto 定义
- 修改 Kafka topic、Outbox 事件命名

### 同步方式

- 修改源码后运行 `pnpm run arch:scan` 更新 arch.db（强制）
- 若架构设计意图变化，同步更新 `docs/architecture/004_architecture_impact_map.md`
- 若发现新的"场景→技术"映射或工作经验，更新 `docs/troubleshooting/known-issues.md`

---

## 2. 架构元数据库规则（arch.db）

**arch.db 是代码结构唯一源，AI 工作前必须运行 `pnpm run arch:scan` 更新。**

1. **多语言扫描**：arch.db 覆盖 TypeScript（ts-morph）、Go（tree-sitter-go）、Python（tree-sitter-python）、Proto（buf AST）四类语言
2. **查询命令**：
   - `pnpm run arch:query -- sql "<SQL>"` 自定义 SQL 查询
   - `pnpm run arch:query -- module-deps` 查模块依赖
   - `pnpm run arch:query -- module-reverse-deps <module>` 查反向依赖
   - `pnpm run arch:query -- symbol-refs <symbol>` 查符号引用链
   - `pnpm run arch:query -- tech-usage <tag>` 查技术使用
   - `pnpm run arch:query -- violations` 查架构违规
   - `pnpm run arch:query -- stats` 查总体统计
   - `pnpm run arch:query -- modules` 查模块列表
3. **arch.db 不替代 004**：arch.db 是"代码现状"，004 是"设计意图"，两者互补
4. **预期规模**：模块数 ≥ 10（api-gateway、push-gateway、teacher-bff、iam、classes、core-edu、content、msg、data-ana、ai），符号数 ≥ 100

---

## 3. 编码规范

详细规范见 `docs/standards/coding-standards.md`，以下为核心强制规则。

### 3.1 代码质量规则

- 每次修改后运行 `pnpm run lint` 和 `pnpm run typecheck` 确保零错误
- TypeScript 服务必须用 `@RequirePermission()` 装饰器进行权限校验
- 前端组件禁止使用 `role === "xxx"` 硬编码，统一使用 `usePermission().hasPermission()`
- 单文件行数遵循企业级规范：
  - 配置文件、常量文件、类型定义文件、proto：无限制
  - Controller / Service：建议 ≤ 500 行
  - Repository / data-access：建议 ≤ 800 行
  - 工具函数：建议 ≤ 40 行
  - 自定义 Hook：建议 ≤ 80 行
  - 硬性上限：任何文件不超过 1000 行，超过必须拆分

### 3.2 架构分层规则（微服务）

- **四层分层**：Gateway → BFF → Services → Data
- **依赖方向单向**：Gateway → BFF → Services → Data，禁止反向依赖
- **服务间通信**：通过 protobuf gRPC 或 Kafka 事件，**禁止直接访问对方 DB**
- **Gateway 职责**：JWT 校验、限流、熔断、CORS、请求 ID 注入
- **BFF 职责**：聚合多个 Service 的数据，为前端提供场景化 API
- **Service 职责**：业务逻辑，每服务独占 DB（DDD 限界上下文）

### 3.3 模块标准结构（DDD）

```
services/[service]/src/
├─ [domain]/                  # 限界上下文
│  ├─ *.controller.ts         # Controller（HTTP/gRPC 入口）
│  ├─ *.service.ts            # Application Service（编排层）
│  ├─ *.repository.ts         # Repository（数据访问）
│  ├─ *.schema.ts             # Zod 验证
│  └─ *.dto.ts                # DTO
├─ config/                    # 配置（database / env / kafka / neo4j / elasticsearch）
├─ middleware/                # 中间件（auth.middleware / permission.guard）
├─ shared/                    # 共享
│  ├─ errors/                 # 错误（application-error / global-error.filter）
│  ├─ health/                 # 健康检查
│  ├─ lifecycle/              # 生命周期
│  ├─ observability/          # 可观测性（logger / metrics / tracer）
│  └─ outbox/                 # Outbox（仅事件驱动服务）
├─ app.module.ts              # 根模块
└─ main.ts                    # 入口
```

### 3.4 TypeScript 规则

- **禁止 `any`**：未知类型用 `unknown` 并做类型守卫
- **禁止 `as` 断言**（除非从 `unknown` 转换或测试中，需注释原因）
- **函数返回值必须显式标注**，特别是 `Promise<T>`
- **仅用于类型的导入必须使用 `import type`**
- **可选链后禁止跟非空断言 `!`**
- **ESM 模式**：NestJS ESM 模式下，相对 import 的 `.js` 后缀（构建产物）

### 3.5 Go 规则

- 包名小写单词，不使用下划线或驼峰
- 导出函数必须有 doc comment（`// FuncName ...`）
- 错误处理：`if err != nil { return err }`，禁止 `_ = err`
- 不使用 `interface{}`，用 `any`（Go 1.18+）
- 包内文件命名 lowercase，不使用下划线（除 `_test.go`）
- 中间件位于 `internal/middleware/`，路由位于 `internal/routing/`

### 3.6 Python 规则

- 类型注解强制（所有函数签名、变量声明）
- FastAPI 路由用 `APIRouter`
- 异步优先：`async def`，IO 密集场景禁止同步阻塞调用
- 模块名 snake_case，类名 PascalCase
- 配置统一通过 `pydantic-settings` 管理（`config.py`）

### 3.7 命名规范（多语言）

| 对象        | 规范                                               | 示例                                             |
| ----------- | -------------------------------------------------- | ------------------------------------------------ |
| 目录        | kebab-case                                         | `api-gateway/`、`core-edu/`                      |
| TS 组件     | PascalCase                                         | `UserController.ts`                              |
| TS Hook     | camelCase                                          | `useAuth.ts`                                     |
| Go 文件     | lowercase                                          | `circuitbreaker.go`                              |
| Python 文件 | snake_case                                         | `clickhouse_client.py`                           |
| Proto 文件  | snake_case                                         | `core_edu.proto`                                 |
| 变量/函数   | camelCase（TS）/ lowercase（Go）/ snake_case（Py） | `getUserById` / `getUserById` / `get_user_by_id` |
| 常量        | UPPER_SNAKE_CASE                                   | `MAX_RETRY_COUNT`                                |
| 类/接口     | PascalCase，接口不加 `I` 前缀                      | `UserService`                                    |
| 布尔值      | `is/has/can/should` 前缀                           | `isActive`、`hasPermission`                      |

### 3.8 Controller / Server Action 规范

- 每个 Controller 方法必须用 `@RequirePermission()` 装饰器声明权限
- 输入用 Zod / class-validator 验证，验证失败返回结构化错误
- 返回 protobuf message 或 DTO（结构同 ActionState）
- 错误统一走 `GlobalErrorFilter`
- BFF Controller 聚合多个 Service 调用，禁止直接访问 DB

### 3.9 Tailwind 规范

- 使用 `cn()` 工具函数管理条件类名
- **禁止**字符串拼接动态类名（`bg-${color}-500`）
- **禁止**使用任意值（`w-[137px]`），除非有充分理由并注释
- 设计令牌在 `packages/shared-tokens/`（待建立）或 `apps/teacher-portal/src/app/styles/tokens/`

### 3.10 设计令牌规范（强制）

- **禁止硬编码颜色**：TSX/TS/CSS 中不得出现 `#hex` 颜色字面量，统一使用 `hsl(var(--*))` 或 Tailwind 类 `bg-*`
- **禁止硬编码字体**：不得出现 `'Inter'`/`'Fraunces'`/`'JetBrains Mono'` 字面量，使用 `var(--font-family-sans/serif/mono)`
- **禁止硬编码字号**：不得出现 `font-size: Npx`，使用 `var(--font-size-1~9)`
- **禁止 Tailwind 任意值**：不得使用 `w-[Npx]`/`h-[Npx]`/`p-[Npx]` 等，映射到 `--space-*` 或 Tailwind 默认阶梯
- **三层令牌**：
  - Layer 1 Primitive：原始色板/字号/间距/阴影，业务代码不直接引用
  - Layer 2 Semantic：语义令牌（light/dark），业务代码唯一引用入口
  - Layer 3 Tailwind Theme：`@theme inline` 将 Semantic 令牌暴露为 `bg-*`/`text-*`/`font-*` 类
- **改令牌必同步图**：修改令牌定义后，同步更新 004 与 arch.db
- **ESLint 强制约束**：
  - `no-restricted-syntax`：禁止 `#hex` 字面量
  - `design-tokens/no-hardcoded-fonts`：禁止 `'Inter'`/`'Fraunces'` 字面量
  - 白名单：`primitive.css`、`email-channel`、`manifest.ts`

---

## 4. 安全规范

- **JWT RS256**：IAM 签发，Gateway 公钥校验（不共享密钥）
- **Cookie**：`httpOnly + Secure + SameSite=Strict`
- **环境变量**：服务端变量不加 `NEXT_PUBLIC_` 前缀；前端变量必须加
- **环境校验**：用 `@t3-oss/env-nextjs` + Zod 或 `pydantic-settings` 校验
- **禁止 `dangerouslySetInnerHTML`**（如必须使用，先用 DOMPurify 清洗）
- **密码哈希**：bcrypt（cost ≥ 12），禁止 MD5/SHA1
- **限流**：Gateway 层 IP 级令牌桶，登录接口额外加用户级限流
- **CORS**：白名单域名，禁止 `*`
- **WAF**：见 `infra/security/waf-rules.conf`

---

## 5. 契约规范

- **契约先行**：服务间通信先定 `.proto`，再写实现
- **工具链**：protobuf + buf v2
- **buf lint**：`STANDARD` 规则集
- **buf breaking**：`FILE` 级别检查（CI 强制）
- **proto 文件位置**：`packages/shared-proto/proto/`
- **包名规范**：`edu.<domain>.v1`（如 `edu.iam.v1`、`edu.core_edu.v1`）
- **生成代码**：`buf generate`（TS 走 `buf.gen.yaml`，Go/Python 各自配置）
- **gRPC 服务端口**：每服务独立端口，见 004 服务清单

---

## 6. 事件驱动规范

- **Outbox 模式**：事务内写业务表 + outbox 表，独立 publisher 投递到 Kafka（保证 at-least-once）
- **事件命名**：`<Aggregate>.<Action>`（如 `ExamCreated`、`HomeworkSubmitted`、`GradeReleased`）
- **TOPIC_MAP 路由**：每个服务维护 `事件 → topic` 映射（见 `shared/outbox/`）
- **幂等性**：
  - Kafka producer：`idempotent=true` + `transactionalId`
  - Consumer：基于 `event_id` 去重（Redis SETNX 或 DB 唯一索引）
- **CDC**：MySQL → Debezium → Kafka（可选，用于读模型同步）
- **事件版本**：schema 演化用 `v1`/`v2` 后缀，禁止破坏性变更

---

## 7. 提交规范

- 使用 Conventional Commits 格式：`feat(scope): description`
- **类型**：`feat`、`fix`、`chore`、`docs`、`style`、`refactor`、`test`、`perf`、`ci`
- **scope**（服务名或包名）：
  - 服务：`iam`、`classes`、`core-edu`、`content`、`msg`、`ai`、`data-ana`、`api-gateway`、`push-gateway`、`teacher-bff`
  - 包：`shared-proto`、`shared-ts`、`shared-go`、`shared-py`、`shared-tokens`
  - 基础设施：`infra`、`k8s`、`helm`、`ci`
  - 文档：`docs`
- **提交前必须运行**：`pnpm run lint` + `pnpm run typecheck`（TS）；`go vet ./...`（Go）；`ruff check src/`（Python）
- **commitlint + husky**：强制校验（见 `.commitlintrc.js`、`.husky/commit-msg`）

---

## 8. Git 工作流

- **分支策略**：分支开发。**人类决策者手动维护分支**（创建分支、合并到 main），AI 不得自行切换/创建/合并分支
- **AI 分支约束（强制）**：
  - AI 只在人类分配的当前分支上工作
  - **禁止** `git checkout -b`、`git switch`、`git branch` 创建或切换分支
  - **禁止** `git merge`、`git push origin main`（合并与推送 main 由人类负责）
  - AI 完成工作后 `git add` + `git commit` 提交到当前分支，通知人类合并
- **提交前校验**：commit 前必须本地通过 `pnpm run lint` + `pnpm run typecheck`（TS）/ `go vet ./...`（Go）/ `ruff check src/`（Python）
- **pre-commit hook**：`lint-staged` 自动修复 + 校验（见 `lint-staged.config.js`）
- **commit-msg hook**：commitlint 校验格式
- **回滚**：由人类在 main 上 `git revert <commit>`，不使用分支回滚
- **默认多 AI 协作**：所有工作默认按多 AI 合作模式进行（见 §14），每个 AI 在各自分支独立工作

---

## 9. 问题记录规则

**所有工作完成后，必须将遇到的问题记录到 `docs/troubleshooting/known-issues.md`（索引式速查手册）。**

### 必须记录的场景

| 场景                                               | 记录要求                          |
| -------------------------------------------------- | --------------------------------- |
| 构建报错（dev/build/lint/tsc/go vet/ruff）         | 记录到"全局经验"对应主题分区      |
| 运行时异常（白屏/API 报错/数据加载失败/gRPC 错误） | 记录到"模块经验"对应模块分区      |
| 框架/库版本兼容问题                                | 记录到"全局经验: 框架与运行时"    |
| 依赖配置问题（go mod / uv / pnpm workspace）       | 记录到"全局经验: 依赖管理"        |
| 架构约束违规（跨服务访问 DB / 缺契约）             | 记录到"全局经验: 架构约束"        |
| 多语言 monorepo 问题（共享包链接失败等）           | 记录到"全局经验: 多语言 monorepo" |

### 记录格式

索引式表格，指明"场景→技术/规则"映射，不写多行代码示例：

```markdown
### X.X 主题分区

| 场景     | 技术/规则          |
| -------- | ------------------ |
| 简述场景 | 正确做法（一句话） |
```

### 记录要求

- **索引式**：场景→技术/规则映射，不写代码示例和错误示范列
- **去重**：同类问题在原条目补充，不重复创建
- **引用架构规则**：架构分层、模块结构等规则引用 004 和本规则文件，不重复
- **只读自己模块分区**：AI 查阅 known-issues 时只读自己负责模块的分区，禁止读其他 AI 模块的分区（避免被历史记录误导）
- **禁止工作日志**：known-issues 不设"工作经验日志"区，不写流水账、不写"做了什么/学到什么"
- **经验沉淀不标注 AI 身份**：所有经验记录只写"场景→技术/规则"映射，**不得出现 AI 标识（如 ai01/ai02/coord 等）**，避免新同名 AI 将前任记录误认为当前事实
- **模块内经验**：模块内的经验沉淀在各自模块的 README/文档中，同样不标注 AI 身份

---

## 10. AI 工作强制流程

**所有 AI 工作必须遵循此流程，违反即违规。**

### 阶段 1：上下文加载（按需阅读，严格模块边界）

> **架构文档按需阅读**：模块开发只读自己模块的架构与服务 README；项目整体相关工作（跨模块契约、基础设施、文档体系、shared-proto）才读全局架构文档。功能开发/文档书写等强依赖场景必须读对应架构。
>
> **严格模块边界**：AI 只读自己负责模块的文档与源码，不越界阅读其他模块的内部实现细节（避免被历史记录或他人方案误导）。仅在需要确认跨模块契约时，才读对方的对外接口（proto/API/事件）。

1. `pnpm run arch:scan` 更新 arch.db
2. `pnpm run arch:query -- module-deps` 查目标模块依赖
3. `pnpm run arch:query -- symbol-refs <目标函数>` 查调用链
4. 阅读 `services/[service]/README.md` 读**自己模块**的工作流程
5. 查 `docs/troubleshooting/known-issues.md` **仅读自己模块的分区**，禁止读其他 AI 模块分区
6. 模块开发：查 `docs/architecture/004_architecture_impact_map.md` **对应模块章节**；全局工作：查 004 全文

### 阶段 2：执行工作

1. 按规划执行（契约先行：先 proto，再实现）
2. 修改代码后立即运行 `pnpm run arch:scan` 更新 arch.db
3. 运行质量校验确保零错误：
   - TS：`pnpm run lint` + `pnpm run typecheck`
   - Go：`go vet ./...` + `go build ./...`
   - Python：`ruff check src/`

### 阶段 3：经验沉淀（强制，不可跳过）

1. 若发现新的"场景→技术"映射 → 提炼到 `docs/troubleshooting/known-issues.md` 对应**模块分区**（索引式一行，不写流水日志，**不标注 AI 身份**）
2. 若发现新的架构决策 → 更新 004
3. 若代码结构变化 → `pnpm run arch:scan` 确认 arch.db 已更新
4. 模块内的经验沉淀到**自己模块的 README/文档**，不污染全局 known-issues，同样不标注 AI 身份

> **禁止**在 known-issues.md 写"工作经验日志/流水账"。known-issues 是索引式速查手册，只保留场景→技术映射，**不出现任何 AI 标识**。

---

## 11. 多语言 monorepo 规则

- **包管理器分工**：
  - TypeScript：pnpm workspace（`pnpm-workspace.yaml`）
  - Go：go.work（`go.work`）
  - Python：uv workspace（根 `pyproject.toml` 的 `[tool.uv.workspace]`）
- **共享包**（`packages/`）：
  - `shared-proto`：protobuf 契约定义（跨语言共享）
  - `shared-ts`：TypeScript 共享类型与工具（待建立）
  - `shared-go`：Go 共享工具（待建立）
  - `shared-py`：Python 共享工具（待建立）
  - `shared-tokens`：设计令牌（待建立）
- **跨语言契约**：通过 protobuf，禁止 JSON Schema 或 OpenAPI 作为内部契约
- **依赖版本对齐**：共享依赖（如 uuid、jwt）在多语言版本号保持一致

---

## 12. 可观测性规范

- **三支柱必须同时启用**（所有服务）：
  - **日志**：pino（TS）/ zap（Go）/ structlog（Python）—— 结构化 JSON
  - **指标**：prom-client（TS）/ prometheus（Go）/ prometheus-client（Python）—— `/metrics` 端点
  - **链路**：OpenTelemetry SDK + OTLP exporter —— 统一 traceparent
- **健康检查**：`/health`（liveness）+ `/ready`（readiness），见各服务 `shared/health/`
- **请求 ID**：Gateway 注入 `X-Request-Id`，全链路传递（日志、metrics、trace）
- **指标命名**：`<service>_<module>_<operation>_<unit>`（如 `iam_login_duration_seconds`）
- **告警规则**：见 `infra/prometheus/rules.yml` + `infra/alertmanager/alertmanager.yml`
- **Grafana 面板**：见 `infra/grafana/dashboards/`

---

## 13. 部署与基础设施规范

- **容器化**：每服务独立 Dockerfile（见 `services/[service]/Dockerfile`）
- **K8s**：基础设施 manifest 位于 `infra/k8s/`，演化目标为 Helm chart（见 `infra/k8s/helm/`）
- **命名空间**：`edu-system`、`edu-monitoring`、`edu-chaos`、`edu-backup`
- **配置分离**：
  - ConfigMap：非敏感配置（环境变量、特性开关）
  - Secret：敏感配置（DB 密码、JWT 密钥、API key）—— 见 `infra/security/secrets.example.env`
- **备份**：见 `infra/backup/backup-mysql.sh`，每服务独立备份，保留 7 天
- **监控栈**：Prometheus + Grafana + Alertmanager（见 `infra/docker-compose.monitoring.yml`）
- **混沌工程**：见 `infra/chaos/experiments.yaml`

---

## 14. 多 AI 协作规范

> **默认多 AI 协作**：所有 AI 工作默认按多 AI 合作模式进行。详细流程见 [多 AI 协作指南](../../docs/standards/multi-ai-collaboration.md)，本节为强制约束摘要。

### 14.1 角色与权限

| 角色                       | 职责                                                     | 在分支提交  | 合并到 main               | force push main |
| -------------------------- | -------------------------------------------------------- | ----------- | ------------------------- | --------------- |
| **协调 AI（Coordinator）** | 契约管理、交叉审查、冲突仲裁、发布                       | ✅          | ❌（由人类合并）          | ⚠️（仅事故）    |
| **开发 AI（Dev）**         | 按模块分工写代码、在分支提交                             | ✅          | ❌                        | ❌              |
| **SRE AI**                 | `infra/` 维护、部署                                      | ✅（infra） | ❌（由人类合并）          | ⚠️（仅事故）    |
| **人类决策者**             | 架构决策、分支管理、合并、Breaking Change 审批、发布确认 | —           | ✅（手动合并分支到 main） | ⚠️（仅事故）    |

> AI 在分配的分支上提交，**不自行合并到 main**。合并由人类决策者负责。

### 14.2 模块单一负责制与模块边界（强制）

- **每个模块（限界上下文）只有一个 AI 负责，禁止并行修改同一模块**
- **严格模块边界**：AI 只修改自己负责的目录（`services/<my-service>/` 或 `apps/<my-app>/`），禁止越界修改他人模块的源码
- **只读自己模块文档**：AI 阅读文档时只读自己模块的 README/设计文档/known-issues 分区，**禁止阅读其他 AI 模块的内部文档和历史记录**（避免被前任同名 AI 的过时方案/审计结果误导）
- **跨模块契约只读接口**：需要确认跨模块协同时，只读对方的对外接口（proto message / API 端点 / Kafka 事件 schema），不读对方内部实现
- `shared-proto`、`shared-tokens`、`docs/` 由协调 AI 维护，开发 AI 只读引用
- `infra/` 由 SRE AI 专门负责，业务 AI 不直接修改

### 14.3 分支开发规则（强制）

1. **分支由人类创建**：人类决策者为每个任务/模块创建分支并分配给 AI，AI 不得自行创建分支
2. **AI 禁止切换分支**：AI 不得执行 `git checkout -b`、`git switch`、`git branch` 等分支创建/切换命令
3. **AI 禁止合并/推送 main**：`git merge`、`git push origin main` 由人类决策者执行，AI 只提交到当前分支
4. **AI 提交流程**：`git add <files>` → `git commit -m "<type>(<scope>): <subject>"` → 通知人类合并
5. **提交前校验**：commit 前必须本地通过 `pnpm run lint` + `pnpm run typecheck`（TS）/ `go vet ./...`（Go）/ `ruff check src/`（Python）
6. **提交信息规范**：遵循 Conventional Commits（见 §7）

### 14.4 跨模块变更顺序（强制）

修改涉及多模块时，按依赖顺序依次在各模块分支完成，由人类决策者按顺序合并到 main：

1. `shared-proto`（proto 契约）
2. 业务服务（classes / iam / core-edu 等）
3. `api-gateway`（路由）
4. BFF（teacher-bff 等）
5. 微前端（teacher-portal 等）

> 每合并一层到 main 后，再合并下一层。协调 AI 负责监督顺序，避免越级合并导致下游构建失败。

### 14.5 冲突处理规则

- **分支冲突**：由人类决策者在合并时解决，或通知 AI 在分支上 `git rebase` 最新 main 后重新提交
- **架构冲突**：由协调 AI 仲裁保留方案，通过新 commit 修正
- **禁止 `git push --force` 到 main**：仅协调 AI 在事故时可 `--force-with-lease`

### 14.6 经验沉淀规则（强制）

- **不记录工作日志**：AI 不在 known-issues.md 或任何文档中写"工作经验日志/流水账"
- **只做经验沉淀**：遇到新的"场景→技术"映射，以索引式一行记录到 known-issues 对应模块分区
- **不标注 AI 身份**：经验沉淀记录**不得出现 AI 标识**（如 ai01/ai02/coord 等），避免新同名 AI 误读
- **模块内经验**：模块内的经验沉淀到各自服务 README/文档，同样不标注 AI 身份

### 14.7 敏感文件保护

以下文件修改需人类决策者额外审批：

- `.env`、`.env.example`、`infra/security/secrets.example.env`
- `infra/k8s/`（生产部署）
- `.github/workflows/`（CI 配置）
- `.trae/rules/project_rules.md`（项目规则）

---

## 15. CI/CD 规范

> 详细配置见 `.github/workflows/ci.yml`，使用手册见 [cicd-runbook](../../docs/standards/cicd-runbook.md)，设计文档见 [no-push-local-build-design](../../docs/superpowers/specs/2026-07-08-cicd-no-push-local-build-design.md)。

### 15.1 核心模式：no-push 本地构建

- **不推送镜像到 registry**：构建即部署，镜像只存在于构建机本地
- **不依赖自建镜像**：全部使用官方镜像（node:22-alpine / golang:1.22-alpine / bufbuild/buf / docker:25-git）
- **DooD 模式**：deploy job 容器挂载 `/var/run/docker.sock`，容器内 docker 命令作用于宿主机
- **单文件管理**：一个 `.github/workflows/ci.yml` 管全部 CI/CD

### 15.2 流水线阶段

| 阶段              | 并行 | 内容                                 | 失败策略 |
| ----------------- | ---- | ------------------------------------ | -------- |
| **quality-ts**    | ✅   | pnpm lint + typecheck + test + build | 失败阻断 |
| **quality-go**    | ✅   | go vet + build + test                | 失败阻断 |
| **quality-proto** | ✅   | buf lint + buf breaking              | 失败阻断 |
| **deploy**        | 串行 | docker compose up --build + 健康检查 | 失败阻断 |

> 质量检查在分支推送时触发，部署仅在合并到 main 时触发。

### 15.3 触发条件

| 事件              | 触发阶段                                        | 触发条件                         |
| ----------------- | ----------------------------------------------- | -------------------------------- |
| 分支 push / PR    | quality-ts + quality-go + quality-proto（并行） | 分支推送自动                     |
| push 到 main      | 上述全部 + deploy                               | 合并后自动                       |
| workflow_dispatch | 上述全部 + deploy                               | 手动触发，支持 `commit_sha` 回滚 |

> **不再支持 tag 发布**：no-push 模式下不用 `git tag v*` 触发。版本管理通过 commit SHA 追溯。

### 15.4 镜像规范

- **不推送到 registry**，本地构建本地使用
- **不保留历史镜像**：layer cache 在宿主机本地，未变更的层秒过
- **回滚**：`git revert` 重跑 CI，或 `workflow_dispatch` 指定 `commit_sha`

### 15.5 部署策略

- **目标环境**：服务器 Docker Compose（P1-P2 阶段），K8s（P3+ 阶段）
- **部署方式**：`docker compose up -d --build --remove-orphans`（在 `/opt/edu/` 目录）
- **部署目录**：`/opt/edu/`（compose 文件）+ `/opt/edu/repo/`（CI 同步的源码，供 build.context 使用）
- **健康检查**：部署后轮询 `/healthz` 端点（10 次 × 6 秒），失败输出容器日志
- **回滚**：`git revert + push` 或 `workflow_dispatch` 指定 `commit_sha`

### 15.6 必需的 CI 文件

| 文件                              | 用途                                  |
| --------------------------------- | ------------------------------------- |
| `.github/workflows/ci.yml`        | 唯一 CI/CD 流水线（quality + deploy） |
| `infra/docker-compose.deploy.yml` | 部署用 compose（build: 替代 image:）  |
| `infra/docker-compose.tools.yml`  | 一次性预拉所有 CI 镜像                |
| `infra/deploy.env.example`        | 部署环境变量模板                      |

### 15.7 actrunner 配置

```toml
# /etc/gitea/act_runner/config.yaml
container:
  valid_volumes:
    - /var/run/docker.sock
```

### 15.8 镜像预拉（一次性）

部署前在服务器执行：

```bash
docker compose -f infra/docker-compose.tools.yml pull
```

拉取清单：node:22-alpine、golang:1.22-alpine、bufbuild/buf:latest、docker:25-git、node:20-alpine、alpine:3.20、mysql:8.0（开发测试）、redis:7-alpine（开发测试）。

---

**本规则文件是项目的强制约束，所有 contributor（含 AI）必须遵守。规则变更需同步更新 004 与 arch.db。**
