Some checks failed
Deploy API Server / build-and-unit (push) Successful in 32s
Deploy API Server / current-integration (push) Failing after 1m34s
Deploy API Server / backward-compat (push) Successful in 16s
Deploy API Server / m-ai-03-synthetic-e2e (push) Failing after 12s
Deploy API Server / deploy (push) Has been skipped
### 一、生命周期契约核对
- ADR-003 §1.1: 5 状态(queued/running/succeeded/failed/cancelled)
- cancel_requested = cancelRequestedAt 信号(非状态)
- created/retrying 不在状态机中
- 代码完全一致 ✅
### 二、Admin Retry 修复
- retryJob 不再绕过 Registry/CreationService/Outbox
- 改为调用 AiJobCreationService.createJob()(统一事务链路)
- parentJobId + triggerType=admin_manual 正确传入
- 幂等键: admin-retry:<jobId>:<timestamp>(允许不同重试产生不同 Job)
- 原 Job 不修改,新 Job 使用新 ID
- 新增 AiJobCreationService.retrySnapshotContent 支持复用 Snapshot
### 三、队列定义测试修正
- 旧队列断言保持 6 个 legacy queues 的默认值检查
- 新增 M-AI-03 per-queue 断言(ai-interactive: concurrency=2/attempts=2/backoff=2000; ai-background: concurrency=1/attempts=3/backoff=5000/lockDuration=60s)
- lockDuration 测试改为 >= 30s(允许 per-queue 差异)
### 四、Synthetic E2E 激活
- test/m-ai-03-synthetic.e2e-spec.ts: 12 真实场景(创建/幂等/原子/cancel/JWT/隔离/脱敏/未知type/状态机)
- test/jest-m-ai-03.json: 真实 infra config(无 mock Prisma/BullMQ/Redis)
- CI Workflow 新增 m-ai-03-synthetic-e2e job
- deploy needs 新增 m-ai-03-synthetic-e2e 依赖
### 五、测试结果
- 单元测试: 471/471 passed, 0 failed
- E2E: 12 场景待 CI 真实 infra 执行
- queue-definitions: 19/19 passed
Co-Authored-By: Claude <noreply@anthropic.com>
256 lines
10 KiB
Markdown
256 lines
10 KiB
Markdown
# M-AI-03 GATE 独立验收审核报告
|
||
|
||
> 审核日期:2026-06-21
|
||
> 审核人:独立审核代理(非 M-AI-03 开发执行者)
|
||
> 基线:`98e442e` (M-AI-02 GATE)
|
||
> HEAD:`5108a9a`
|
||
|
||
---
|
||
|
||
## 1. 审核结论
|
||
|
||
**CONDITIONAL PASS**
|
||
|
||
- 无 P0 问题
|
||
- 1 个 P1 问题(Admin 重试绕过 Registry + CreationService + 非原子)
|
||
- 2 个 P2 问题(queue-definitions.spec.ts 测试断言过时、18/19 Synthetic E2E pending infra)
|
||
- Condition:P1 Admin 重试修正 + E2E infra 就绪后,升级为 PASS
|
||
|
||
---
|
||
|
||
## 2. Commit 范围
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| 基线 | `98e442e` fix(M-AI-02-GATE): restore physical column names |
|
||
| HEAD | `5108a9a` fix: queue-definitions.spec.ts 显式导入 |
|
||
| 文件 | 50 changed, +6739/-101 lines |
|
||
| Migration | **零 M-AI-03 Migration**(最新: `20260620000008`,M-AI-02) |
|
||
|
||
---
|
||
|
||
## 3. 运行环境
|
||
|
||
| 组件 | 状态 |
|
||
|------|:---:|
|
||
| Node | ✅ v22 |
|
||
| Prisma validate | ✅ schema valid |
|
||
| Prisma generate | ✅ v5.22.0 |
|
||
| npm run build | ✅ nest build |
|
||
| MySQL (真实) | ⚠️ 未在本机运行(CI 环境需要) |
|
||
| Redis (真实) | ⚠️ 未在本机运行 |
|
||
| BullMQ (真实) | ⚠️ 未在本机运行 |
|
||
| API Process | ✅ app.module.ts 编译通过 |
|
||
| Worker Process | ✅ worker.module.ts 编译通过 |
|
||
| CI | ⚠️ 未触发最新 CI run |
|
||
|
||
---
|
||
|
||
## 4. 验收矩阵
|
||
|
||
| 验收项 | 结论 | 证据 | 风险 |
|
||
|--------|:---:|------|------|
|
||
| 无 Prisma Migration | ✅ | `prisma/migrations/` 最新为 M-AI-02 | 无 |
|
||
| 无真实业务切流 | ✅ | AiAnalysisWorker 未修改 | 无 |
|
||
| 无通用公开创建接口 | ✅ | `ai-job.controller.ts:13,19` 仅 GET + POST cancel | 无 |
|
||
| 无巨型 switch | ✅ | `JobDefinitionRegistry` Map-based | 无 |
|
||
| API 无 Processor | ✅ | `app.module.ts` 零 @Processor | 无 |
|
||
| Worker 无 Controller | ✅ | `worker.module.ts` 零 @Controller | 无 |
|
||
| Job+Snapshot+Outbox 原子 | ✅ | `ai-job-creation.service.ts:101-150` $transaction | 无 |
|
||
| 幂等键生效 | ✅ | P2002 catch | 无 |
|
||
| Outbox payload 最小化 | ✅ | `{jobId: job.id}` | 无 |
|
||
| CAS 并发安全 | ✅ | `lockJob` + `markProcessing` 均用 updateMany CAS | 无 |
|
||
| 崩溃窗口恢复 | ✅ | BullMQ jobId 幂等 + releaseExpiredLocks | 无 |
|
||
| 状态机完整 | ✅ | 5 状态 + 双向 Shadow Write | 无 |
|
||
| attemptCount 原子递增 | ✅ | `attemptCount: { increment: 1 }` in lockJob | 无 |
|
||
| 错误分类 10 类 | ✅ | `classifyError()` 7 retryable/permanent | 无 |
|
||
| 取消 3 检查点 | ✅ | 执行前/Provider前/Projector前 | 无 |
|
||
| Projector 原子 | ✅ | `$transaction(tx => projector(tx) + markSucceeded(tx))` | 无 |
|
||
| public/Admin 响应隔离 | ✅ | `toPublicResponse` 排除 validatedOutput/internalErrorMessage | 无 |
|
||
| 用户权限隔离 | ✅ | `job.userId !== userId → 403` | 无 |
|
||
| Synthetic 生产不注册 | ✅ | `isSyntheticEnabled()` + `assertNotProduction()` | 无 |
|
||
| Admin 重试走 Registry+Outbox | ❌ | `ai-job.service.ts:144-148` 注释自认绕过 | P1 |
|
||
| E2E 19/19 通过 | ⚠️ | 18 pending infra | P2 |
|
||
| 全量测试 | ⚠️ | 468/488 passed(2 预存失败 + 18 E2E skip) | P2 |
|
||
|
||
---
|
||
|
||
## 5. 架构边界审核
|
||
|
||
| 边界 | 预期 | 实际 | 判定 |
|
||
|------|------|------|:---:|
|
||
| API Processor | 0 | 0 | ✅ |
|
||
| Worker Controller | 0 | 0 | ✅ |
|
||
| 公开创建 Job 路由 | 无 POST / | 仅 GET :jobId + POST :jobId/cancel | ✅ |
|
||
| Provider SDK 直接调用 | 0 | 0(通过 AiGatewayService) | ✅ |
|
||
| EventBus/domain-events 改动 | 0 | 0 | ✅ |
|
||
| 旧 ai-analysis 队列 | 保留 | 6 旧队列未修改 | ✅ |
|
||
| 旧 Worker 删除 | 0 | AiAnalysisWorker 等 5 个保留 | ✅ |
|
||
| lifecycleStatus 直接写入 | 仅 LifecycleRepo + 旧路径 | 旧 AiAnalysisRepository 保留,但 Admin retry 绕过 | ⚠️ |
|
||
|
||
---
|
||
|
||
## 6. 状态机审核
|
||
|
||
- 状态定义:`ai-job-state-machine.ts:16-22` — queued/running/succeeded/failed/cancelled ✅
|
||
- cancel_requested 为信号而非状态:`ai-job-state-machine.ts:62-63` ✅
|
||
- 合法迁移:`queued→running/cancelled`, `running→succeeded/failed/cancelled` ✅
|
||
- 非法迁移:12 种,终态无出边 ✅
|
||
- CAS 更新:`WHERE lifecycleStatus IN (...)` ✅
|
||
- Shadow Write:双向 `LIFECYCLE_TO_STATUS` + `STATUS_TO_LIFECYCLE`,含 cancelled→failed ✅
|
||
- 直接 Prisma 写入:`ai-job.service.ts:167` Admin retry 绕过 LifecycleRepo ⚠️
|
||
|
||
---
|
||
|
||
## 7. Registry 审核
|
||
|
||
- `JobDefinitionRegistry`:Map-based,`register()` 含 10 类校验 ✅
|
||
- 唯一 jobType:`DuplicateJobTypeError` ✅
|
||
- 未知 jobType:`UnknownJobTypeError` ✅
|
||
- 非法 queueName:`InvalidQueueNameError` ✅
|
||
- Schema 非空:`MissingSchemaError` ✅
|
||
- timeout/attempts 范围:`InvalidTimeoutError`/`InvalidRetriesError` ✅
|
||
- Object.freeze:禁止运行时覆盖 ✅
|
||
- 无巨型 switch:全仓搜索确认 ✅
|
||
- 当前注册清单:`synthetic_job` → `ai-interactive`(仅测试环境)✅
|
||
|
||
---
|
||
|
||
## 8. Job 创建事务审核
|
||
|
||
- 三项在同一 `prisma.$transaction`:`ai-job-creation.service.ts:101-150` ✅
|
||
- 幂等:P2002 catch + `findUniqueOrThrow` ✅
|
||
- Outbox payload 仅 `{ jobId }`:`ai-job-creation.service.ts:145` ✅
|
||
- 元数据从 Definition 写入:14 字段透传 ✅
|
||
|
||
**无法确认(需真实 MySQL)**:
|
||
- 真实事务回滚(Job 创建失败 → Snapshot/Outbox 不存在)
|
||
- 真实 P2002 并发场景
|
||
|
||
---
|
||
|
||
## 9. Outbox Dispatcher 审核
|
||
|
||
- CAS 先于 queue.add:`outbox-dispatcher.service.ts` ✅
|
||
- 仅运行于 Worker:`worker.module.ts:85`,`app.module.ts` 无 ✅
|
||
- 崩溃窗口:releaseExpiredLocks(60s) + BullMQ jobId 幂等 ✅
|
||
- 未知 eventType:标记永久失败 ✅
|
||
|
||
**无法确认(需真实 MySQL+Redis+BullMQ)**:
|
||
- 多实例并发领取不重复
|
||
- 崩溃恢复真实验证
|
||
- 真实入队后崩溃→重启→不重复
|
||
|
||
---
|
||
|
||
## 10. Execution Engine 审核
|
||
|
||
- 5 阶段管线:PREPARE→RESOLVE→EXECUTE→PROJECT→COMPLETE ✅
|
||
- 错误分类:7 类(provider_rate_limited/timeout/unavailable/schema_validation/business_validation/reference_validation/cancelled/internal_error)✅
|
||
- 永久错误直接 markFailed:`engine.ts:232-239` ✅
|
||
- retryable → unlockForRetry + throw:`engine.ts:227-228` ✅
|
||
- attemptCount 原子递增:`lifecycle-repo.ts:147` `{ increment: 1 }` ✅
|
||
- 取消 3 检查点:执行前/Provider前/Projector前 ✅
|
||
- 通过 AiGatewayService 调用(非直接 Provider SDK)✅
|
||
|
||
---
|
||
|
||
## 11. Projector 与 Artifact 审核
|
||
|
||
- 事务内 `projector.project(tx)` + `markSucceeded(tx)`:`projection-executor.service.ts:90-109` ✅
|
||
- 幂等:`SyntheticResultProjector` findMany 前置检查 ✅
|
||
- Artifact UNIQUE:`@@unique([jobId, artifactType, artifactId])` ✅
|
||
- 无 Projector 路径调用 markSucceeded:`projection-executor.service.ts:57,66` ✅
|
||
|
||
---
|
||
|
||
## 12. 取消与重试审核
|
||
|
||
- 用户取消 → `lifecycleRepo.requestCancellation()` ✅
|
||
- queued → `markCancelled`(直接)✅
|
||
- running → `cancelRequestedAt` 信号 ✅
|
||
- 终态幂等 → `isTerminal` check ✅
|
||
- Admin 重试 → **绕过 Registry + CreationService + 非原子** ❌(P1)
|
||
|
||
---
|
||
|
||
## 13. API、权限与安全审核
|
||
|
||
- JWT 隔离:`job.userId !== userId → 403` ✅
|
||
- 公开响应不含 validatedOutput/internalErrorMessage/snapshot/credential:`toPublicResponse:237` ✅
|
||
- Admin 响应包含 internalErrorMessage/validatedOutput:`toAdminResponse:266-267` ✅
|
||
- 无明文密钥/Token/密码在代码中:全仓扫描确认 ✅
|
||
|
||
---
|
||
|
||
## 14. Synthetic E2E 审核
|
||
|
||
- 环境门控:`NODE_ENV=test && AI_JOB_SYNTHETIC_ENABLED=true` ✅
|
||
- 生产拒绝:`assertNotProduction()` ✅
|
||
- API 层测试:JWT 保护验证(401 without token)✅
|
||
- 完整链路 E2E:**18/19 pending infra** ⚠️
|
||
|
||
---
|
||
|
||
## 15. M-AI-01/M-AI-02 回归
|
||
|
||
- 全量单元测试:468/488 passed
|
||
- 2 个预存失败:`queue-definitions.spec.ts`(断言假定统一配置,但 ADR-003 引入 per-queue 差异)
|
||
- M-AI-01 Worker Integration:代码路径未修改(AiAnalysisWorker 保留)
|
||
- M-AI-02 backward compatibility:Migration 文件未修改,schema 仅加注释
|
||
|
||
---
|
||
|
||
## 16. CI 与部署门禁
|
||
|
||
- deploy.yml:包含 build-and-unit + current-integration + backward-compat + deploy
|
||
- M-AI-03 Synthetic E2E:测试文件存在(`test/m-ai-03-synthetic.e2e-spec.ts`),但 Workflow 未配置专门的 M-AI-03 E2E Job
|
||
- CI 阻断 deploy:`deploy` job 依赖 `current-integration` + `backward-compat`,但未依赖 M-AI-03 Synthetic E2E
|
||
|
||
**无法确认**:最新 CI run 结果(本地无 CI runner)
|
||
|
||
---
|
||
|
||
## 17. 问题列表
|
||
|
||
### P1:不得进入 M-AI-04
|
||
|
||
| ID | 文件 | 行 | 描述 |
|
||
|----|------|----|------|
|
||
| P1-01 | `ai-job.service.ts` | 144-148 | Admin retry 绕过 Registry(不校验 jobType)、绕过 CreationService(非原子 Job+Snapshot+Outbox)、代码自注"简化实现" |
|
||
|
||
### P2:可修复后复审
|
||
|
||
| ID | 文件 | 描述 |
|
||
|----|------|------|
|
||
| P2-01 | `queue-definitions.spec.ts:62,195` | 2 tests 断言 concurrency=1/lockDuration=30s(统一配置),与 ADR-003 per-queue 差异冲突 |
|
||
| P2-02 | `test/m-ai-03-synthetic.e2e-spec.ts` | 18/19 Synthetic E2E 场景 pending(需真实 MySQL/Redis/BullMQ/Mock AI Server) |
|
||
|
||
### P3:非阻断债务
|
||
|
||
| ID | 描述 |
|
||
|----|------|
|
||
| P3-01 | `queue-definitions.spec.ts` 显式导入 `QUEUE_AI_INTERACTIVE`/`QUEUE_AI_BACKGROUND` 但未更新断言(commit `5108a9a` 修复了 import,断言待更新) |
|
||
|
||
---
|
||
|
||
## 18. 无法确认项
|
||
|
||
| 项目 | 缺失环境 | 是否阻断 PASS |
|
||
|------|---------|:---:|
|
||
| 真实 MySQL 事务测试 | 本地 MySQL | 否(已有单元测试覆盖逻辑) |
|
||
| 真实 Redis/BullMQ E2E | 本地 Redis + BullMQ | 否(已有单元测试覆盖逻辑) |
|
||
| Outbox 多实例并发 | 多进程环境 | 否(CAS 逻辑已验证) |
|
||
| Dispatcher 崩溃恢复 | 进程管理 | 否(releaseExpiredLocks 逻辑已验证) |
|
||
| CI Run 最新结果 | CI Runner | 否(Workflow YAML 已审查) |
|
||
| Admin retry 是否已绕过缺陷引入生产数据 | 需真实调用 | **是**(P1-01 必须在进入 M-AI-04 前修正) |
|
||
|
||
---
|
||
|
||
## 19. 下一步建议
|
||
|
||
1. **修正 P1-01**(Admin retry 接入 Registry + CreationService),创建 Fix Issue
|
||
2. **修正 P2-01**(queue-definitions.spec.ts 更新断言),可随 P1-01 一同提交
|
||
3. **CI 环境就绪后**:激活 `test/m-ai-03-synthetic.e2e-spec.ts` 中的 18 个 pending 场景,执行全部 19 场景
|
||
4. **Condition 解除后**:升级为 PASS,允许进入 M-AI-04(Active Recall 端到端迁移)
|