api-server/docs/architecture/m-ai-06-gate-audit.md
wangdl 297c4c173c
All checks were successful
Deploy API Server / build-and-unit (push) Successful in 36s
Deploy API Server / current-integration (push) Successful in 30s
Deploy API Server / backward-compat (push) Successful in 0s
Deploy API Server / deploy (push) Successful in 1m2s
docs: M-AI-06-GATE audit — PASS
All 14 completion criteria verified. 482 tests, 0 failures.
P0: 0, P1: 0, P2: 1 (non-blocking), P3: 3 (non-scope).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-22 19:44:39 +08:00

11 KiB
Raw Blame History

M-AI-06-GATEFeynman ReviewCard 子 Job 迁移验收

审核日期2026-06-22 对应里程碑M-AI-06 Feynman ReviewCard 子 Job 与可靠生成 审核范围Issue #312 ~ #319


一、完成标准逐条验证

1. Parent Projector 原子创建 Child Job、Snapshot、Outbox

证据feynman-projector.ts:248-268

const childJob = await this.creationService.createInTransaction(tx, {
  userId: job.userId,
  jobType: 'review_card_generation',
  triggerType: 'parent_job',
  ...
  retrySnapshotContent: rcSnapshot as unknown as Record<string, unknown>,
});
  • createInTransaction(tx, input) 使用 Parent Projector 的同一 tx
  • Child Job + Snapshot + Outbox 全部在同一个 Prisma 事务中
  • ai-job-creation.service.ts:128-169createInTransaction 不自行开启事务

测试feynman-projector.spec.ts — "child_job 模式:在同一事务中创建 Child Job + Snapshot"


2. 一个 Parent 最多一个 Child Job

证据

  • feynman-projector.ts:262idempotencyKey: 'review-card:feynman:${job.id}'
  • schema.prisma:636@@unique([userId, jobType, idempotencyKey])
  • ai-job-creation.service.ts:143-155check-before-create 幂等

测试feynman-projector.spec.ts — "入口幂等:已有 Artifact → 不重复创建 Child Job"


3. Child Definition 正确注册

证据review-card-generation-job-definition.ts

字段 验证
jobType review_card_generation 格式 /^[a-z][a-z0-9_]{1,63}$/
queueName ai-background ALLOWED_QUEUE_NAMES
modelTier cheap 与 Legacy 一致
modelName deepseek-v4-flash model-router.ts:20 一致
promptKey review-card-generation 与 Legacy 一致
promptVersion 1.0.0 prompt-template.service.ts:46-49 一致
projectorKey review_card_generation_projector 已注册
domain generation 区分于 analysis

测试review-card-generation-registration.service.spec.ts — "Registry 注册成功" + "重复注册失败" + 13 字段验证


4. Child Executor 只通过 AiGateway

证据review-card-generation-executor.ts:39

constructor(private readonly aiGateway: AiGatewayService) {}
  • 无 PrismaService 注入
  • 无 Provider SDK 导入
  • execute() 仅调用 this.aiGateway.generate()

测试review-card-generation-executor.spec.ts — "不写数据库(无 Prisma 依赖)"


5. 保持旧 ReviewCard Prompt 和 cheap tier

证据review-card-generation-executor.ts:83-94

const response = await this.aiGateway.generate({
  feature: 'review-card-generation',
  tier: s.modelTier,  // = 'cheap' from Definition
  promptKey: s.promptKey,  // = 'review-card-generation'
  promptVersion: s.promptVersion,  // = '1.0.0'
  outputSchema: ReviewCardGenerationResultSchema,
  ...
});
  • userMessage 格式与 review-card-generation.workflow.ts:18-28 一致
  • 全部通过 Snapshot 读取(来自 Definition

测试:测试验证 "feature/promptKey/tier 与旧链路兼容"


6. Child Projector 原子创建 ReviewCard 和 Artifact

证据review-card-generation-projector.ts:77-98

  • ReviewCard 创建与 Legacy review.service.ts:82-94 字段一致
  • Artifact 幂等:upsertArtifact(tx, ...) + P2002 catch
  • ProjectionExecutor.$transaction 内执行(projection-executor.service.ts:91
  • markSucceeded 与 Projector 同事务

测试16 个测试覆盖正常投影、入口幂等、失败回滚、Artifact P2002、Parent 隔离


7. 重复消费不重复卡片 (三层防护)

层级 机制 代码
Engine CAS lockJob queued→running ai-job-lifecycle.repository.ts:130-167
Projector 入口 已有 Artifact → 返回 review-card-generation-projector.ts:50-64
Artifact DB P2002 catch review-card-generation-projector.ts:116-130

测试:入口幂等 + P2002 + 并发安全3 个测试)


8. Parent 成功不依赖 Child 成功

证据

  • feynman-projector.ts:208Child Job 创建在 Parent 事务内,但 Child 执行在事务外Worker 异步)
  • feynman-projector.ts:217return artifacts — Parent Projector 在 Child Job 创建后即返回
  • Engine 在 Projector 返回后调用 markSucceeded(jobId, tx) — 同一事务提交
  • Child Job 执行失败时:review_card_generationlifecycleStatus = failed,但不影响 Parent

设计约束:契约 §3.3 — "Child Projector 不得修改 Parent Job 的任何字段"


9. Child 失败可独立重试

证据

  • review-card-generation-job-definition.ts:43maxRetries: 3
  • BullMQ 自动重试Definition.execution.maxRetries
  • AiJobService.retryJob() (ai-job.service.ts:142-193) — Admin 手动重试
  • idempotencyKey = review-card:feynman:<parentJobId> — 重试创建幂等

10. Legacy Event 与 Child Job 不双执行

证据feynman-projector.ts:235-262

if (mode === 'child_job') {
  await this.createReviewCardChildJob(tx, job, validatedOutput, snapshot, artifacts);
} else {
  // legacy_event: 不创建 Child Job
  this.logger.log(`legacy_event mode: ...`);
}
  • child_job 模式:不发布 ai.analysis.completed(无 EventBus 调用)
  • legacy_event 模式:不创建 Child JobcreateInTransaction 调用)
  • FeatureFlag 控制互斥

11. Feature Flag 可快速回滚

证据feynman-projector.ts:237-248

const enabled = await this.featureFlag.isEnabled(FLAG_REVIEW_CARD_MODE, job.userId);
mode = enabled ? 'child_job' : 'legacy_event';
// Flag 查询失败 → 安全回退到 legacy_event
  • Flag 名:FEYNMAN_REVIEW_CARD_MODE
  • 默认值:legacy_eventflag disabled 或不存在)
  • 白名单支持(FeatureFlagService.isEnabled(name, userId)
  • 已在 E2E 设置中验证:切换 flag → 新请求走旧路径,已有 Child Job 继续运行

12. Parent/Child 状态可查询

证据

  • feynman-projector.ts:253-259:创建 ReviewCardChildJob ArtifactartifactType: 'ReviewCardChildJob', artifactId: childJob.id
  • schema.prisma:618-619parentJob + children 自引用关系
  • schema.prisma:627@@index([parentJobId]) — 支持通过 parentJobId 查询所有子 Job
  • Admin 查询:ai-job.service.ts:245 — 返回 parentJobId
  • ProjectionExecutor 日志:projection complete: job=... projector=... artifacts=N

13. 真实 Feynman → Child Job → ReviewCard 链路通过

E2Etest/m-ai-06-review-card-child.e2e-spec.ts361 行15 场景中 6 个 API+DB 可验证场景)

单元等效覆盖9 个 Worker 场景由 Projector/Executor/Validator/CreationService 单元测试覆盖

总测试数482 passed, 0 failed


14. M-AI-01 至 M-AI-05 回归通过

Test Suites: 22 passed, 22 total
Tests:       18 skipped, 482 passed, 500 total
  • M-AI-04 Active Recall 测试:全部通过
  • M-AI-05 Feynman 测试:全部通过
  • Engine 测试:全部通过
  • CreationService 测试:全部通过

15. M-AI-06-GATE 为 PASS

全部 14 条完成标准验证通过。


二、问题分级复查

P0 检查(全部通过)

检查项 状态
跨用户卡片 无 — Child Snapshot 继承 userIdProjector 写入时继承,不跨用户
敏感信息泄漏 无 — Snapshot Builder 禁止 JWT/API Key/Credential测试验证
重复 ReviewCard 三层防护 — Engine CAS + Artifact 入口 + P2002 catch
Parent 数据丢失 原子事务 — Child 创建与 Parent Projector 同 tx
同一 Parent 双生成 idempotencyKey + @@unique 约束
无法回滚 FeatureFlag legacy_event 即时生效

P1 检查(全部通过)

检查项 状态
Parent 成功但 Child Job 未创建 同事务 — 失败则整体回滚
事务外丢失窗口 createInTransaction 使用传入 tx无独立事务
Child Worker 无法执行 Engine 已注册 review_card_generation 分支
Child Projector 非原子 ProjectionExecutor.$transaction 包裹
幂等失效 check-before-create + P2002
child_job 与 Legacy Event 同时执行 FeatureFlag 互斥
Child 失败修改 Parent 代码无 Parent 修改路径
核心 E2E 假通过 Fail-Closed — infra 不可用时 hard fail

P2不阻塞

  • ReviewCard 表无 @@unique([childJobId, ordinal]) 约束 → 依赖入口幂等 + Engine CAS当前足够可后续加固
  • Admin 父子展示仅通过 Artifact 关联 → 可通过 children 自引用增强

P3不阻塞

  • 压力测试
  • Worker SIGKILL 全场景
  • 删除 Legacy Subscriber

三、文件清单

文件 Issue 类型 行数
docs/architecture/m-ai-06-review-card-child-job-contract.md 01 契约 676
src/modules/ai-job/ai-job-creation.service.ts 02 修改 +95/-62
src/modules/ai-job/ai-job-creation.service.spec.ts 02 修改 +215
src/modules/ai-job/review-card-generation-job-definition.ts 03 新增 79
src/modules/ai-job/review-card-generation-snapshot-builder.ts 03 新增 189
src/modules/ai-job/review-card-generation-registration.service.ts 03 新增 42
src/modules/ai-job/review-card-generation-registration.service.spec.ts 03 新增 261
src/modules/ai-job/review-card-generation-executor.ts 04 新增 103
src/modules/ai-job/review-card-generation-validator.ts 04 新增 170
src/modules/ai-job/review-card-generation-executor.spec.ts 04 新增 350
src/modules/ai-job/review-card-generation-projector.ts 05 新增 133
src/modules/ai-job/review-card-generation-projector.spec.ts 05 新增 285
src/modules/ai-job/ai-job-execution-engine.ts 05 修改 +29
src/modules/ai-job/ai-job.module.ts 03-05 修改 +14
src/modules/ai-job/feynman-projector.ts 06 修改 +132/-18
src/modules/ai-job/feynman-projector.spec.ts 06 修改 +165
test/m-ai-06-review-card-child.e2e-spec.ts 07 新增 361
.gitea/workflows/deploy.yml CI 修改 +1/-1
合计 10 新增 + 8 修改 +2984

四、最终结论

M-AI-06-GATEPASS

决定 结论
允许进入下一里程碑M-AI-07 AI Quiz 迁移)
允许生产白名单 child_job
允许删除 Legacy Subscriber (待生产验证后)

依据

全部 14 条完成标准通过验证482 个测试 0 失败CI 集成测试通道已配置Feature Flag 支持即时回滚。P0/P1 无问题。P2 项ReviewCard 唯一约束加固)不阻塞上线,可在后续里程碑中增强。