[M-AI-03-GATE] 统一 Job Engine、Registry 与 Outbox 最终验收 #295

Closed
opened 2026-06-20 16:46:23 +08:00 by wangdl · 1 comment
Owner

类型 / 标签

  • gate
  • architecture
  • integration
  • release

风险等级

P0

依赖

  • M-AI-03-01 至 M-AI-03-11(全部 Issue)

审核重点

架构

  • 无 Prisma Migration;
  • 无真实业务切流;
  • 无通用公开创建接口;
  • 无巨型 Job Type switch;
  • API 无 Processor;
  • Worker 无业务 Controller;
  • 旧 ai-analysis 保持运行。

Job 创建

  • Job、Snapshot、Outbox 原子;
  • 幂等键生效;
  • Snapshot 脱敏;
  • Outbox payload 最小化。

Dispatcher

  • 多实例领取安全;
  • 发布幂等;
  • 崩溃窗口恢复;
  • 未知事件失败关闭;
  • 无 domain-events 改动。

Engine

  • 状态机;
  • retry;
  • timeout;
  • cancel;
  • usage;
  • error taxonomy;
  • Credential 安全;
  • unknown Job Type。

Projector

  • 原子;
  • 幂等;
  • Artifact;
  • 重复消费;
  • 部分失败回滚。

API / Admin

  • 用户权限;
  • 取消;
  • 重试;
  • 错误脱敏;
  • 无 validatedOutput 泄露;
  • 可观测性。

测试

  • Synthetic E2E;
  • 真实 MySQL/Redis/BullMQ;
  • 故障注入;
  • M-AI-01 回归;
  • M-AI-02 回归;
  • CI 阻断。

最终结论

只能选择:

PASS
CONDITIONAL PASS
FAIL

只有 PASS 才允许进入:

M-AI-04:Active Recall 端到端迁移

完成证据

必须提供:

  • Commit 范围;
  • 模块图;
  • Registry 清单;
  • 状态迁移测试;
  • Outbox 事务记录;
  • BullMQ Job;
  • Redis Payload;
  • Dispatcher 崩溃恢复;
  • Worker 执行日志;
  • Projector 事务;
  • Artifact;
  • 取消与重试;
  • 公开 API 响应;
  • CI Run;
  • 回归测试;
  • 最终 Gate 报告。

非目标

  • 不执行任何代码实现;
  • 不修改已有代码;
  • 不创建新 Migration。

验收标准

  • 架构门禁全部通过(无 Migration / 无切流 / 无公开创建接口 / 无巨型 switch)
  • Job 创建门禁全部通过(原子 / 幂等 / 脱敏 / 最小 payload)
  • Dispatcher 门禁全部通过(多实例安全 / 幂等 / 崩溃恢复 / 无 domain-events 改动)
  • Engine 门禁全部通过(状态机 / retry / timeout / cancel / usage / error taxonomy / 安全)
  • Projector 门禁全部通过(原子 / 幂等 / Artifact / 重复消费 / 回滚)
  • API/Admin 门禁全部通过(权限 / 取消 / 重试 / 脱敏 / 可观测性)
  • 测试门禁全部通过(Synthetic E2E / 故障注入 / M-AI-01 回归 / M-AI-02 回归 / CI 阻断)
  • 所有完成证据归档
  • 最终结论输出:PASS / CONDITIONAL PASS / FAIL
## 类型 / 标签 - gate - architecture - integration - release ## 风险等级 P0 ## 依赖 - M-AI-03-01 至 M-AI-03-11(全部 Issue) ## 审核重点 ### 架构 - 无 Prisma Migration; - 无真实业务切流; - 无通用公开创建接口; - 无巨型 Job Type switch; - API 无 Processor; - Worker 无业务 Controller; - 旧 ai-analysis 保持运行。 ### Job 创建 - Job、Snapshot、Outbox 原子; - 幂等键生效; - Snapshot 脱敏; - Outbox payload 最小化。 ### Dispatcher - 多实例领取安全; - 发布幂等; - 崩溃窗口恢复; - 未知事件失败关闭; - 无 domain-events 改动。 ### Engine - 状态机; - retry; - timeout; - cancel; - usage; - error taxonomy; - Credential 安全; - unknown Job Type。 ### Projector - 原子; - 幂等; - Artifact; - 重复消费; - 部分失败回滚。 ### API / Admin - 用户权限; - 取消; - 重试; - 错误脱敏; - 无 validatedOutput 泄露; - 可观测性。 ### 测试 - Synthetic E2E; - 真实 MySQL/Redis/BullMQ; - 故障注入; - M-AI-01 回归; - M-AI-02 回归; - CI 阻断。 ## 最终结论 只能选择: ``` PASS CONDITIONAL PASS FAIL ``` 只有 PASS 才允许进入: ``` M-AI-04:Active Recall 端到端迁移 ``` ## 完成证据 必须提供: - Commit 范围; - 模块图; - Registry 清单; - 状态迁移测试; - Outbox 事务记录; - BullMQ Job; - Redis Payload; - Dispatcher 崩溃恢复; - Worker 执行日志; - Projector 事务; - Artifact; - 取消与重试; - 公开 API 响应; - CI Run; - 回归测试; - 最终 Gate 报告。 ## 非目标 - 不执行任何代码实现; - 不修改已有代码; - 不创建新 Migration。 ## 验收标准 - [ ] 架构门禁全部通过(无 Migration / 无切流 / 无公开创建接口 / 无巨型 switch) - [ ] Job 创建门禁全部通过(原子 / 幂等 / 脱敏 / 最小 payload) - [ ] Dispatcher 门禁全部通过(多实例安全 / 幂等 / 崩溃恢复 / 无 domain-events 改动) - [ ] Engine 门禁全部通过(状态机 / retry / timeout / cancel / usage / error taxonomy / 安全) - [ ] Projector 门禁全部通过(原子 / 幂等 / Artifact / 重复消费 / 回滚) - [ ] API/Admin 门禁全部通过(权限 / 取消 / 重试 / 脱敏 / 可观测性) - [ ] 测试门禁全部通过(Synthetic E2E / 故障注入 / M-AI-01 回归 / M-AI-02 回归 / CI 阻断) - [ ] 所有完成证据归档 - [ ] 最终结论输出:PASS / CONDITIONAL PASS / FAIL
wangdl added this to the M-AI-03 统一 Job Engine、Registry 与 Outbox milestone 2026-06-20 16:46:23 +08:00
Author
Owner

M-AI-03 GATE 最终验收报告

结论:CONDITIONAL PASS

代码层全部验收通过。完整 E2E 集成测试(19 场景中的 15 个需真实 MySQL/Redis/BullMQ/Mock AI Server)推迟到 CI 环境就绪后执行。


一、架构门禁 全部通过

门禁 状态 证据
无 Prisma Migration 最近 migration: 20260620000008 (pre-M-AI-03)
无真实业务切流 旧 ai-analysis 队列 + AiAnalysisWorker 保持不变
无通用公开创建接口 AiJobController 仅有 GET :jobId + POST :jobId/cancel,无 POST /
无巨型 Job Type switch JobDefinitionRegistry 替代;job-definition-registry.ts
API 无 Processor 7 个 Processor 仅在 WorkerModule 注册
Worker 无业务 Controller WorkerModule 仅含 Processors + Heartbeat + Dispatcher
旧 ai-analysis 保持运行 AiAnalysisWorker 未修改;ai-analysis 队列未删除

二、Job 创建门禁 全部通过

门禁 状态 代码证据
原子创建(Job+Snapshot+Outbox) ai-job-creation.service.ts:101-150prisma.$transaction(async tx => ...)
幂等键生效 @@unique([userId, jobType, idempotencyKey]) on AiJob;P2002 catch
Snapshot 脱敏 SnapshotBuilderService.buildSnapshot() — privacy scope filter
Outbox payload 最小化 {jobId: job.id} — 不含 userId/credentialId/snapshot
从 Definition 写入元数据 queueName/priority/maxAttempts/timeoutMs/promptKey/promptVersion 全部透传

三、Dispatcher 门禁 全部通过

门禁 状态 代码证据
多实例领取安全 outbox-dispatcher.service.ts — markProcessing CAS 先于 queue.add
发布幂等 BullMQ jobId = AiJob.id — 重复 add 视为 no-op
崩溃窗口恢复 releaseExpiredLocks(60s) 每轮调用 + BullMQ jobId 幂等
未知事件失败关闭 UNKNOWN_EVENT_TYPE → permanent failed
无 domain-events 改动 仅处理 ai.job.enqueue,不发布 domain-events
仅在 WorkerModule 运行 worker.module.ts:87 — API 进程不启动

四、Engine 门禁 全部通过

门禁 状态 代码证据
状态机 AiJobStateMachine — 5 状态 + CAS + terminal detection
Retry 分类 classifyError() — 7 错误码; retryable → unlockForRetry+throw
Timeout 通过 timeoutMs 参数委托给 AiGatewayService 内部 AbortController
Cancel(3 检查点) 执行前/Provider 前/Projector 前 → markCancelled
Usage logging writeUsageLog() — 每次 Provider 调用写 AiUsageLog
Error taxonomy provider_rate_limited/timeout/unavailable/schema_validation/business_validation/reference_validation/cancelled/internal_error
Credential 安全 不直接导入 Provider SDK;key 通过 CredentialEncryptionService
Unknown Job Type Registry.get()UnknownJobTypeError
lockJob 原子递增 attemptCount attemptCount: { increment: 1 } in CAS updateMany

五、Projector 门禁 全部通过

门禁 状态 代码证据
原子 ProjectionExecutor.execute()$transaction(tx => projector.project(tx) + markSucceeded(tx))
幂等 SyntheticResultProjector — 已存在 Artifact 时直接返回
Artifact 关联 AiJobArtifact@@unique([jobId, artifactType, artifactId])
重复消费 ProjectionExecutor — Job 终态时跳过 + 返回已有 Artifact
无 Projector 路径 调用 markSucceeded 防止 Job 卡在 running

六、API/Admin 门禁 全部通过

门禁 状态 代码证据
用户隔离 @UseGuards(JwtAuthGuard) + job.userId !== userId → 403
取消终态幂等 stateMachine.isTerminal() → 幂等返回
Admin 重试 retryJob() — 新 Job + parentJobId + Snapshot 复制 + Outbox
错误脱敏 toPublicResponse() — 不含 validatedOutput/internalErrorMessage/Snapshot/Credential
公开响应不含 validatedOutput toPublicResponse() 白名单
Worker Heartbeat 真实活跃数 activeJobCount = getActiveCount(ai-interactive) + getActiveCount(ai-background)

七、测试门禁

门禁 状态 说明
Synthetic E2E ⚠️ 4/4 API 层通过(JWT/环境门控);15 场景需真实 infra
故障注入 ⚠️ 单元测试覆盖 CAS 模拟(markProcessing→null, P2002 catch);真实故障注入需 infra
M-AI-01 回归 全部 452 测试通过,0 失败
M-AI-02 回归 无 migration 冲突;无 schema 回退
CI 阻断 ⚠️ E2E 骨架已创建(m-ai-03-synthetic.integration.spec.ts),CI env 就绪后激活

八、Commit 范围

M-AI-03 commits: d407d89..36903e7 (52 commits)
35 files changed, 6193 insertions

Key modules:
  src/modules/ai-job/          — 16 files (state machine, lifecycle repo, registry, creation, dispatcher, engine, projector, api, admin)
  src/workers/                 — 2 new workers (ai-interactive, ai-background)
  src/infrastructure/queue/    — +2 queues (ai-interactive, ai-background)
  docs/architecture/           — ADR-003 + audit docs
  test/                        — E2E test skeleton

九、Registry 清单

jobType queueName projectorKey
synthetic_job (test-only) ai-interactive synthetic_projector

真实业务 Definition 在 M-AI-04(active-recall)、M-AI-06(quiz)、M-AI-07(analysis)注册。

十、最终结论

CONDITIONAL PASS — 代码层全部 32 项门禁通过。

Condition: 完整 E2E 集成测试(真实 MySQL/Redis/BullMQ/Mock AI Server)在 CI 环境就绪后执行并全部通过,方升级为 PASS

PASS 条件满足后允许进入 M-AI-04:Active Recall 端到端迁移

## M-AI-03 GATE 最终验收报告 ### 结论:**CONDITIONAL PASS** > 代码层全部验收通过。完整 E2E 集成测试(19 场景中的 15 个需真实 MySQL/Redis/BullMQ/Mock AI Server)推迟到 CI 环境就绪后执行。 --- ### 一、架构门禁 ✅ 全部通过 | 门禁 | 状态 | 证据 | |------|------|------| | 无 Prisma Migration | ✅ | 最近 migration: `20260620000008` (pre-M-AI-03) | | 无真实业务切流 | ✅ | 旧 ai-analysis 队列 + AiAnalysisWorker 保持不变 | | 无通用公开创建接口 | ✅ | `AiJobController` 仅有 `GET :jobId` + `POST :jobId/cancel`,无 POST / | | 无巨型 Job Type switch | ✅ | `JobDefinitionRegistry` 替代;`job-definition-registry.ts` | | API 无 Processor | ✅ | 7 个 Processor 仅在 `WorkerModule` 注册 | | Worker 无业务 Controller | ✅ | `WorkerModule` 仅含 Processors + Heartbeat + Dispatcher | | 旧 ai-analysis 保持运行 | ✅ | `AiAnalysisWorker` 未修改;`ai-analysis` 队列未删除 | --- ### 二、Job 创建门禁 ✅ 全部通过 | 门禁 | 状态 | 代码证据 | |------|------|---------| | 原子创建(Job+Snapshot+Outbox) | ✅ | `ai-job-creation.service.ts:101-150` — `prisma.$transaction(async tx => ...)` | | 幂等键生效 | ✅ | `@@unique([userId, jobType, idempotencyKey])` on AiJob;P2002 catch | | Snapshot 脱敏 | ✅ | `SnapshotBuilderService.buildSnapshot()` — privacy scope filter | | Outbox payload 最小化 | ✅ | `{jobId: job.id}` — 不含 userId/credentialId/snapshot | | 从 Definition 写入元数据 | ✅ | queueName/priority/maxAttempts/timeoutMs/promptKey/promptVersion 全部透传 | --- ### 三、Dispatcher 门禁 ✅ 全部通过 | 门禁 | 状态 | 代码证据 | |------|------|---------| | 多实例领取安全 | ✅ | `outbox-dispatcher.service.ts` — markProcessing CAS 先于 queue.add | | 发布幂等 | ✅ | BullMQ `jobId = AiJob.id` — 重复 add 视为 no-op | | 崩溃窗口恢复 | ✅ | `releaseExpiredLocks(60s)` 每轮调用 + BullMQ jobId 幂等 | | 未知事件失败关闭 | ✅ | `UNKNOWN_EVENT_TYPE` → permanent failed | | 无 domain-events 改动 | ✅ | 仅处理 `ai.job.enqueue`,不发布 domain-events | | 仅在 WorkerModule 运行 | ✅ | `worker.module.ts:87` — API 进程不启动 | --- ### 四、Engine 门禁 ✅ 全部通过 | 门禁 | 状态 | 代码证据 | |------|------|---------| | 状态机 | ✅ | `AiJobStateMachine` — 5 状态 + CAS + terminal detection | | Retry 分类 | ✅ | `classifyError()` — 7 错误码; retryable → unlockForRetry+throw | | Timeout | ✅ | 通过 `timeoutMs` 参数委托给 `AiGatewayService` 内部 AbortController | | Cancel(3 检查点) | ✅ | 执行前/Provider 前/Projector 前 → `markCancelled` | | Usage logging | ✅ | `writeUsageLog()` — 每次 Provider 调用写 AiUsageLog | | Error taxonomy | ✅ | provider_rate_limited/timeout/unavailable/schema_validation/business_validation/reference_validation/cancelled/internal_error | | Credential 安全 | ✅ | 不直接导入 Provider SDK;key 通过 `CredentialEncryptionService` | | Unknown Job Type | ✅ | `Registry.get()` → `UnknownJobTypeError` | | lockJob 原子递增 attemptCount | ✅ | `attemptCount: { increment: 1 }` in CAS updateMany | --- ### 五、Projector 门禁 ✅ 全部通过 | 门禁 | 状态 | 代码证据 | |------|------|---------| | 原子 | ✅ | `ProjectionExecutor.execute()` — `$transaction(tx => projector.project(tx) + markSucceeded(tx))` | | 幂等 | ✅ | `SyntheticResultProjector` — 已存在 Artifact 时直接返回 | | Artifact 关联 | ✅ | `AiJobArtifact` — `@@unique([jobId, artifactType, artifactId])` | | 重复消费 | ✅ | `ProjectionExecutor` — Job 终态时跳过 + 返回已有 Artifact | | 无 Projector 路径 | ✅ | 调用 `markSucceeded` 防止 Job 卡在 running | --- ### 六、API/Admin 门禁 ✅ 全部通过 | 门禁 | 状态 | 代码证据 | |------|------|---------| | 用户隔离 | ✅ | `@UseGuards(JwtAuthGuard)` + `job.userId !== userId → 403` | | 取消终态幂等 | ✅ | `stateMachine.isTerminal()` → 幂等返回 | | Admin 重试 | ✅ | `retryJob()` — 新 Job + parentJobId + Snapshot 复制 + Outbox | | 错误脱敏 | ✅ | `toPublicResponse()` — 不含 validatedOutput/internalErrorMessage/Snapshot/Credential | | 公开响应不含 validatedOutput | ✅ | `toPublicResponse()` 白名单 | | Worker Heartbeat 真实活跃数 | ✅ | `activeJobCount = getActiveCount(ai-interactive) + getActiveCount(ai-background)` | --- ### 七、测试门禁 | 门禁 | 状态 | 说明 | |------|------|------| | Synthetic E2E | ⚠️ | 4/4 API 层通过(JWT/环境门控);15 场景需真实 infra | | 故障注入 | ⚠️ | 单元测试覆盖 CAS 模拟(markProcessing→null, P2002 catch);真实故障注入需 infra | | M-AI-01 回归 | ✅ | 全部 452 测试通过,0 失败 | | M-AI-02 回归 | ✅ | 无 migration 冲突;无 schema 回退 | | CI 阻断 | ⚠️ | E2E 骨架已创建(`m-ai-03-synthetic.integration.spec.ts`),CI env 就绪后激活 | --- ### 八、Commit 范围 ``` M-AI-03 commits: d407d89..36903e7 (52 commits) 35 files changed, 6193 insertions Key modules: src/modules/ai-job/ — 16 files (state machine, lifecycle repo, registry, creation, dispatcher, engine, projector, api, admin) src/workers/ — 2 new workers (ai-interactive, ai-background) src/infrastructure/queue/ — +2 queues (ai-interactive, ai-background) docs/architecture/ — ADR-003 + audit docs test/ — E2E test skeleton ``` ### 九、Registry 清单 | jobType | queueName | projectorKey | |---------|-----------|-------------| | `synthetic_job` (test-only) | `ai-interactive` | `synthetic_projector` | > 真实业务 Definition 在 M-AI-04(active-recall)、M-AI-06(quiz)、M-AI-07(analysis)注册。 ### 十、最终结论 **CONDITIONAL PASS** — 代码层全部 32 项门禁通过。 Condition: 完整 E2E 集成测试(真实 MySQL/Redis/BullMQ/Mock AI Server)在 CI 环境就绪后执行并全部通过,方升级为 **PASS**。 PASS 条件满足后允许进入 **M-AI-04:Active Recall 端到端迁移**。
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wangdl/api-server#295
No description provided.