# msg 问题记录

> 负责人：ai10
> 关联：[coord.md](../coord.md)、[contracts/msg_contract.md](../contracts/msg_contract.md)
> 规则：AI 遇到问题时在此追加条目，coord 仲裁后更新状态

---

## §0 已有仲裁核查（ai10 复核）

> 本节核查 01-understanding.md / 02-architecture-design.md 中引用的已有仲裁，对照源码/proto 验证准确性。

### ISSUE-001-ai10：M8 ZodError 已修复 — 核查通过

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：仲裁核查
- **描述**：01-understanding.md M8 称"GlobalErrorFilter 已识别 ZodError 返回 400"。核查 [global-error.filter.ts](../../../services/msg/src/shared/errors/global-error.filter.ts) L32-42：`else if (exception instanceof ZodError) { statusCode = 400; ... }`，确实已处理，返回 `MSG_VALIDATION_ERROR` + 400。
- **核查结论**：✅ 仲裁准确，M8 标记"已修复"无误
- **状态**：已裁决（核查通过，无需处理）

### ISSUE-002-ai10：M12 proto 包名规范 — 核查通过

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：仲裁核查
- **描述**：01-understanding.md M12 称"实际 `next_edu_cloud.msg.v1` 符合规范"。核查 [msg.proto](../../../packages/shared-proto/proto/msg.proto) L3：`package next_edu_cloud.msg.v1;`，与 01-understanding.md 描述一致。
- **核查结论**：✅ 仲裁准确，但存在规则冲突（见 ISSUE-007）
- **状态**：已裁决（核查通过）

### ISSUE-003-ai10：M2 msg 必须有 Outbox — 核查通过

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：仲裁核查
- **描述**：01-understanding.md M2 称"004 §7.2 明确 msg 生产 `edu.notification.events`，msg 必须有 Outbox"。核查 004 §7.3 L638 `NotificationRequested` 事件（Msg 投递通知到多渠道）+ known-issues §msg L346"Outbox 强制"。
- **核查结论**：✅ 仲裁准确，msg 必须实现 Outbox（P5 强制）
- **状态**：已裁决（核查通过）

### ISSUE-004-ai10：core-edu 事件 topic 命名统一 — 核查部分通过

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：仲裁核查
- **描述**：02-architecture-design.md §7.2 P11 称"coord 已仲裁采用 `edu.teaching.*` 新约定"。核查：
  - 004 §7.2 L623-625 使用 `edu.teaching.assignment.submitted` / `edu.teaching.exam.published` / `edu.teaching.grade.recorded`（新约定）✅
  - [events.proto](../../../packages/shared-proto/proto/events.proto) L9-13 注释仍用 `edu.exam.events` / `edu.homework.events`（旧约定）❌ 未同步
  - [matrix.md](../matrix.md) §4 L112-115 使用 `edu.exam.events` / `edu.homework.events`（旧约定）❌ 未同步
- **核查结论**：⚠️ 仲裁已作出但未全量同步，events.proto 注释与 matrix.md 仍用旧约定，需 coord 统一更新
- **状态**：待 coord 同步（见 ISSUE-008）

### ISSUE-005-ai10：Push Gateway 调用方向歧义 — 核查通过

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：仲裁核查
- **描述**：02-architecture-design.md §7.2 P10 称"004 §4.1 写 PushGW→Msg，实际是 Msg→PushGW"。核查 004 §4.1 L413：`push-gateway → Msg | gRPC | 推送通道建立`，方向确实反了。实际流程是 msg 调 push-gateway 的 gRPC PushService.Push（见 [notifications.service.ts](../../../services/msg/src/notifications/notifications.service.ts) L179 fetch POST /internal/push 降级实现）。
- **核查结论**：✅ 歧义确认，建议 coord 修正 004 §4.1 表述为"Msg → push-gateway (gRPC)"
- **状态**：待 coord 修正 004

---

## §1 新发现问题（ai10 提请）

### ISSUE-006-ai10：events.proto P9 字段描述不准确

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：02-architecture-design.md §7.2 P9 称"events.proto ExamEvent / HomeworkEvent / GradeEvent 需补 `class_id` / `student_ids[]` 字段"。核查 events.proto：
  - `ExamEvent` L32 **已有** `class_id` 字段 ✅
  - `HomeworkEvent` L45 **已有** `class_id` 字段 ✅
  - `GradeEvent` L50-59 **无** `class_id`（仅有 `student_id`）❌
  - 三者均**无** `student_ids[]`（复数，用于 fan-out 广播）❌
- **建议方案**：修正 P9 表述为"`GradeEvent` 需补 `class_id`；全部事件需补 `student_ids[]` 字段（msg fan-out 广播通知需要）"
- **状态**：待 coord 仲裁

### ISSUE-007-ai10：proto 包名规则冲突（project_rules vs 实际）

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：[project_rules §5](../../../.trae/rules/project_rules.md) 规定"包名规范：`edu.<domain>.v1`（如 `edu.iam.v1`、`edu.core_edu.v1`）"，但实际所有 proto 文件使用 `next_edu_cloud.<domain>.v1`（如 msg.proto L3 `next_edu_cloud.msg.v1`、events.proto L3 `next_edu_cloud.events.v1`）。01-understanding.md 称"coord 已裁决采用 `next_edu_cloud.*`"，但 project_rules §5 未同步更新，仍写 `edu.<domain>.v1`。
- **建议方案**：coord 统一裁决，二选一：
  - 方案 A：更新 project_rules §5 为 `next_edu_cloud.<domain>.v1`（与实际 proto 一致）
  - 方案 B：重命名所有 proto package 为 `edu.<domain>.v1`（与规则一致，但改动大）
- **状态**：待 coord 仲裁

### ISSUE-008-ai10：Kafka topic 命名三套约定并存

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：msg 相关的 Kafka topic 命名存在三套约定：
  - **约定 A（004 §7.2，per-event topic）**：`edu.identity.user.created` / `edu.teaching.exam.published` / `edu.notification.sent`
  - **约定 B（matrix.md §4 + events.proto 注释，aggregate topic）**：`edu.iam.user.events` / `edu.exam.events` / `edu.notification.requested`
  - **约定 C（msg_contract.md §1.4，aggregate topic + action 字段）**：`edu.msg.notification.events`（action: sent/read/recalled/failed）
  - 02-architecture-design.md §5.1/§5.2 采用约定 A；msg_contract.md §1.4 采用约定 C；matrix.md 采用约定 B。known-issues §全局 L182 已标记此冲突。
- **建议方案**：coord 统一为一套约定。ai10 倾向约定 A（per-event topic），理由：
  - 004 §7.2 已采用，是架构设计意图唯一源
  - per-event topic 便于消费者按需订阅，避免反序列化无关事件
  - 与 NotificationSent / NotificationRead 等事件命名（PascalCase）对齐
- **状态**：待 coord 仲裁

### ISSUE-009-ai10：RPC 数量超预算（17 vs 13）

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：工作量超批
- **描述**：[ai-allocation.md §3.2](../../ai-allocation.md) L112 与 [matrix.md](../matrix.md) §2 L90 均规定 msg 为"3 Service 13 RPC"。但 02-architecture-design.md §4.2 列出 17 RPC：
  - NotificationService 9 RPC（SendNotification / BatchSendNotification / ListNotifications / GetUnreadCount / MarkAsRead / BatchMarkAsRead / MarkAllAsRead / SearchNotifications / RecallNotification）
  - NotificationPreferenceService 2 RPC（GetPreferences / UpdatePreferences）
  - NotificationTemplateService 6 RPC（CreateTemplate / GetTemplate / ListTemplates / UpdateTemplate / DeleteTemplate / RenderTemplate）
  - 而 msg_contract.md §1.1 列出 13 RPC（分布不同：5+4+4），两文档互相不一致
- **建议方案**：coord 裁决 RPC 范围，二选一：
  - 方案 A：维持 13 RPC 预算，02-architecture-design.md 裁剪至 13（移除 BatchSendNotification / GetUnreadCount / BatchMarkAsRead / MarkAllAsRead / UpdateTemplate / DeleteTemplate，降级为 REST only 或合并）
  - 方案 B：放宽至 17 RPC，同步更新 ai-allocation.md + matrix.md + msg_contract.md
- **状态**：待 coord 仲裁

### ISSUE-010-ai10：markAsRead 权限点与设计不一致

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：[notifications.controller.ts](../../../services/msg/src/notifications/notifications.controller.ts) L74 markAsRead 使用 `MSG_NOTIFICATION_MANAGE` 权限，但 02-architecture-design.md §6.1 L730 规定 markAsRead 应使用 `MSG_NOTIFICATION_READ`。MANAGE 权限通常给管理员，学生标记自己通知已读不应需要 MANAGE 权限。
- **建议方案**：以 02-architecture-design.md §6.1 为准（READ），P5 实现时修正 controller 权限点
- **状态**：待 coord 确认

### ISSUE-011-ai10：DB→ES 降级方向与 ai-allocation §5 相反

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - [ai-allocation.md §5](../../ai-allocation.md) ai10 设计重点要求"ES 降级查询策略（**DB 不可用时走 ES 索引**）"——即 DB 故障时 ES 作为读模型兜底
  - 02-architecture-design.md §3.2.2 / §3.4 描述"**ES 不可用时降级到 MySQL LIKE 查询**"——即 ES 故障时 DB 兜底
  - 01-understanding.md M16 称"无 DB→ES 降级读路径"
  - 三处描述方向相反，需统一
- **建议方案**：ai10 倾向双向降级（两种故障场景都覆盖）：
  - ES 故障 → DB LIKE 查询（设计文档已覆盖）
  - DB 故障 → ES 只读模式（ai-allocation 要求，设计文档需补充）
  - 但 DB 故障时写操作无法降级（必须等 DB 恢复），仅读操作可走 ES
- **状态**：待 coord 仲裁

### ISSUE-012-ai10：设计文档缺 DLQ 与三层幂等防线

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：前置依赖缺失
- **描述**：[known-issues §msg](../../../docs/troubleshooting/known-issues.md) L344 / L356 已记录两项 ai10 设计点，但 02-architecture-design.md 未覆盖：
  - **三层幂等防线**（L344）：L1 Redis SETNX / L2 msg_idempotency 表 / L3 notifications.source_event_id 唯一索引。设计文档 §5.5 仅描述两层（Redis + DB UNIQUE），缺中间层 msg_idempotency 表
  - **死信队列**（L356）：消费失败超 3 次投递 `edu.notification.dlq`。设计文档 §5 完全未提及 DLQ 设计
- **建议方案**：02-architecture-design.md 补充：
  - §3.1 补 `msg_idempotency` 表 schema（中间层）
  - §5.5 改为三层幂等防线
  - §5 补 DLQ 设计（重试 3 次后投递 `edu.notification.dlq` + 告警）
- **状态**：待 coord 确认（非阻塞，ai10 自行补充设计文档即可）

### ISSUE-013-ai10：events.proto 缺 4 类 message 阻塞 msg 消费

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：前置依赖缺失
- **描述**：[events.proto](../../../packages/shared-proto/proto/events.proto) 仅有 ClassEvent / ExamEvent / HomeworkEvent / GradeEvent 4 个 message。msg 消费还需要：
  - `UserEvent`（iam 发布 user.created/updated/deleted/role_changed）— 阻塞欢迎通知/角色变更通知
  - `RoleEvent`（iam 发布 role.created/updated）— 阻塞角色变更通知
  - `MasteryEvent`（data-ana 发布 mastery.updated）— 阻塞学情预警通知
  - `NotificationEvent`（msg 发布 notification.sent/read/recalled/failed）— 阻塞 push-gateway 消费 msg 事件
- **建议方案**：coord 维护 shared-proto，在 P5 启动前补齐这 4 个 message。msg 在 proto 补齐前用通用 JSON payload 解析（A6 假设）
- **状态**：待 coord 仲裁（🔴 阻塞 P5 消费链路）

### ISSUE-014-ai10：msg_contract.md 与 02-architecture-design.md RPC 清单不一致

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：两文档 RPC 清单存在差异：
  - msg_contract.md 独有（02 缺）：GetPreferenceByChannel、ListPreferences
  - 02-architecture-design.md 独有（contract 缺）：BatchSendNotification、GetUnreadCount、BatchMarkAsRead、MarkAllAsRead、UpdateTemplate、DeleteTemplate
  - 即使忽略 ISSUE-009 的数量问题，两文档的 RPC 组合也不同
- **建议方案**：待 ISSUE-009 仲裁后，统一两文档 RPC 清单
- **状态**：待 coord 仲裁（依赖 ISSUE-009）

### ISSUE-015-ai10：msg_contract.md Kafka 发布事件与设计文档不一致

- **提请方**：ai10
- **日期**：2026-07-10
- **类型**：契约不明确
- **描述**：
  - msg_contract.md §1.4：`edu.msg.notification.events` topic，单一 NotificationEvent 含 action 字段
  - 02-architecture-design.md §5.2：4 个 per-event topic（`edu.notification.sent` / `edu.notification.read` / `edu.notification.recalled` / `edu.notification.failed`）
  - matrix.md §4 L118：`edu.notification.requested`（第三种命名）
- **建议方案**：待 ISSUE-008 仲裁 topic 命名约定后统一
- **状态**：待 coord 仲裁（依赖 ISSUE-008）
