From c179af64a69cd96bfbc086b018388c56b7516e61 Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:30:51 +0800 Subject: [PATCH] =?UTF-8?q?docs(docs):=20coord=20=E5=AE=8C=E6=88=90=2015?= =?UTF-8?q?=20=E6=A8=A1=E5=9D=97=20issue=20=E4=BB=B2=E8=A3=81=E4=B8=8E?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E8=AE=BE=E6=96=BD=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit coord.md 新增 ARB-019/020/021 三章仲裁章节,修正 ARB-001。 - coord.md: 新增 ARB-019/020/021(student/parent/admin-portal 24 项) - coord.md: 修正 ARB-001(admin P2 预留/schema 文件名/classes 数据源) - 004 §4: 依赖图加 PBFF→DataAna+Msg - 004 §7.2: push-gateway→Redis 软失败标注 - 004 §11.4: 错误码前缀矩阵(11 服务+i18n key) - 004 §11.5: ActionState 信封规范(降级模式方案 B) - matrix §1: 依赖矩阵加 PBFF 边 - matrix §2: 移除 api-gateway 为 iam gRPC 消费方 - matrix §4: admin-portal→teacher-bff - matrix §5: 移除 /sse+鉴权头统一 - matrix §6: 错误码表补 i18n key 列 - 15 个 issue.md: 仲裁结论回写 - push-gateway_contract: 移除 /sse+鉴权头改 X-Internal-Token - packages/contracts: 新建包 ADMIN_* 权限点常量 AI: coord --- .../004_architecture_impact_map.md | 129 ++ .../issues/contracts/push-gateway_contract.md | 101 +- docs/architecture/issues/coord.md | 1396 ++++++++++++++++- docs/architecture/issues/matrix.md | 71 +- .../issues/objections/admin-portal_issue.md | 22 +- .../issues/objections/ai_issue.md | 46 +- .../issues/objections/content_issue.md | 69 +- .../issues/objections/core-edu_issue.md | 96 +- .../issues/objections/data-ana_issue.md | 69 +- .../issues/objections/iam_issue.md | 56 +- .../issues/objections/msg_issue.md | 20 + .../issues/objections/parent-bff_issue.md | 39 +- .../issues/objections/parent-portal_issue.md | 37 +- .../issues/objections/push-gateway_issue.md | 48 +- .../issues/objections/student-bff_issue.md | 171 +- .../issues/objections/student-portal_issue.md | 80 +- .../issues/objections/teacher-bff_issue.md | 15 +- packages/contracts/package.json | 15 + packages/contracts/src/index.ts | 1 + packages/contracts/src/permissions.ts | 108 ++ packages/contracts/tsconfig.json | 14 + pnpm-lock.yaml | 99 ++ 22 files changed, 2320 insertions(+), 382 deletions(-) create mode 100644 packages/contracts/package.json create mode 100644 packages/contracts/src/index.ts create mode 100644 packages/contracts/src/permissions.ts create mode 100644 packages/contracts/tsconfig.json diff --git a/docs/architecture/004_architecture_impact_map.md b/docs/architecture/004_architecture_impact_map.md index 5f50d81..f498148 100644 --- a/docs/architecture/004_architecture_impact_map.md +++ b/docs/architecture/004_architecture_impact_map.md @@ -375,6 +375,8 @@ graph TB PBFF --> IAM PBFF --> CoreEdu + PBFF --> DataAna + PBFF --> Msg CoreEdu -.事件.-> Content CoreEdu -.事件.-> DataAna @@ -626,6 +628,12 @@ graph LR | `edu.content.question.published` | 题目发布 | Content | AI、ES | | `edu.insight.mastery.updated` | 掌握度更新 | DataAna | CoreEdu、Msg | +> **必需依赖软失败标注**(ARB-015 §17.6,ISSUE-058 覆盖 ISSUE-055): +> +> - push-gateway → Redis:**软失败**(Redis 故障仅告警 + `degraded: true` + /readyz 返 200,不返 503、不触发 Pod 重启) +> - 理由:Redis 故障时单实例仍能服务本地连接(仅跨实例广播失效);重启会丢失本地连接表,加剧雪崩风险 +> - ISSUE-055 必需依赖列表应排除 push-gateway → Redis + ### 7.3 核心领域事件 | 事件 | 触发场景 | 消费者动作 | @@ -955,6 +963,127 @@ graph LR DataLoader --> S3 ``` +### 11.4 错误码前缀矩阵 + +> 来源:coord 仲裁 ARB-017 §19.6 / ARB-014 / ARB-015 / ARB-018 / G14 / F4 +> 规则:服务名大写 + 下划线分隔,禁止子前缀(如 EXAMS_/HOMEWORK_/GRADES_ 已移除) + +| 层级 | 服务 | 前缀 | 示例 | 仲裁依据 | +| ---- | ------------ | -------------- | ------------------------------------------------------------- | -------- | +| 网关 | api-gateway | `GW_` | GW_UNAUTHORIZED / GW_RATE_LIMITED | ARB-014 | +| 网关 | push-gateway | `PUSH_` | PUSH_DEVICE_NOT_FOUND / PUSH_CHANNEL_FAILED | ARB-015 | +| BFF | teacher-bff | `BFF_TEACHER_` | BFF_TEACHER_UNAUTHORIZED / BFF_TEACHER_BAD_GATEWAY | ARB-016 | +| BFF | student-bff | `BFF_STUDENT_` | BFF_STUDENT_UNAUTHORIZED / BFF_STUDENT_VALIDATION_ERROR | ARB-017 | +| BFF | parent-bff | `BFF_PARENT_` | BFF_PARENT_CHILD_NOT_BOUND / BFF_PARENT_BAD_GATEWAY | ARB-018 | +| 业务 | iam | `IAM_` | IAM_USER_NOT_FOUND / IAM_INVALID_CREDENTIALS | ARB-003 | +| 业务 | core-edu | `CORE_EDU_` | CORE_EDU_EXAM_NOT_FOUND / CORE_EDU_GRADE_NOT_FOUND | ARB-004 | +| 业务 | content | `CONTENT_` | CONTENT_QUESTION_NOT_FOUND / CONTENT_TEXTBOOK_NOT_FOUND | ARB-005 | +| 业务 | msg | `MSG_` | MSG_NOTIFICATION_NOT_FOUND / MSG_TEMPLATE_NOT_FOUND | ARB-008 | +| 业务 | data-ana | `DATA_ANA_` | DATA_ANA_DASHBOARD_UNAVAILABLE / DATA_ANA_ANALYTICS_NOT_READY | ARB-009 | +| 业务 | ai | `AI_` | AI_GENERATION_FAILED / AI_QUOTA_EXCEEDED | ARB-010 | + +**前端 i18n key 映射规则**(F4 裁决): + +| 服务 | i18n key 模式 | 示例 | +| ------------ | ------------------------------- | -------------------------------------- | +| api-gateway | `error.gateway.` | `error.gateway.unauthorized` | +| push-gateway | `error.push.` | `error.push.device_not_found` | +| teacher-bff | `error.bffTeacher.` | `error.bffTeacher.unauthorized` | +| student-bff | `error.bffStudent.` | `error.bffStudent.validation_error` | +| parent-bff | `error.bffParent.` | `error.bffParent.child_not_bound` | +| iam | `error.iam.` | `error.iam.user_not_found` | +| core-edu | `error.core_edu.` | `error.core_edu.exam_not_found` | +| content | `error.content.` | `error.content.question_not_found` | +| msg | `error.msg.` | `error.msg.notification_not_found` | +| data-ana | `error.data_ana.` | `error.data_ana.dashboard_unavailable` | +| ai | `error.ai.` | `error.ai.generation_failed` | + +### 11.5 ActionState 信封规范 + +> 来源:coord 仲裁 ARB-017 §19.6 +> 适用范围:所有 BFF / Gateway HTTP 响应、GraphQL response 的 errors 扩展字段 +> 设计原则:统一错误信封 + 降级模式方案 B(degraded 放 error.details 子字段) + +**信封结构**: + +```typescript +interface ActionState { + success: boolean; // 整体成功/失败 + data: T | null; // 成功时返回数据,失败时为 null + error: { + code: string; // 错误码(见 §11.4 前缀矩阵) + message: string; // 人类可读错误消息(i18n key 解析后) + details?: Record; // 附加详情(含 degraded 字段) + traceId: string; // 链路追踪 ID(X-Request-Id) + } | null; // 成功时为 null +} +``` + +**降级模式(方案 B)**: + +- `success = true`(整体成功) +- `data` 内含 `extensions.degraded: true` 子字段 +- `error` 仍为 null(非错误) +- 用于部分聚合失败场景(如 teacher-bff 聚合 iam 成功但 core-edu 失败) + +**示例 1:完全成功** + +```json +{ + "success": true, + "data": { "userId": "u001", "name": "张老师" }, + "error": null +} +``` + +**示例 2:完全失败** + +```json +{ + "success": false, + "data": null, + "error": { + "code": "BFF_TEACHER_BAD_GATEWAY", + "message": "下游服务不可用", + "details": { "downstream": "core-edu", "reason": "timeout" }, + "traceId": "req-abc123" + } +} +``` + +**示例 3:降级模式(部分聚合成功)** + +```json +{ + "success": true, + "data": { + "user": { "userId": "u001", "name": "张老师" }, + "classes": null, + "extensions": { "degraded": true, "failedServices": ["core-edu"] } + }, + "error": null +} +``` + +**GraphQL errors 数组扩展**: + +GraphQL 响应中,错误通过 `errors[].extensions` 携带 ActionState 字段: + +```json +{ + "errors": [ + { + "message": "下游服务不可用", + "extensions": { + "code": "BFF_TEACHER_BAD_GATEWAY", + "details": { "downstream": "core-edu" }, + "traceId": "req-abc123" + } + } + ] +} +``` + --- ## 12. 架构约束 diff --git a/docs/architecture/issues/contracts/push-gateway_contract.md b/docs/architecture/issues/contracts/push-gateway_contract.md index 7ac163d..d302264 100644 --- a/docs/architecture/issues/contracts/push-gateway_contract.md +++ b/docs/architecture/issues/contracts/push-gateway_contract.md @@ -17,29 +17,35 @@ ### 1.2 HTTP 端点 -| Method | Path | 用途 | 认证 | 请求体 | 响应 | -| ------ | --------------------------- | -------------------------------------- | --------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | -| GET | /ws | WebSocket 升级端点(实时推送通知) | JWT RS256(query `?token=` 或 `Authorization`) | — | 升级为 WebSocket 长连接 | -| POST | /internal/push | 内部单推接口(msg 服务触发) | `X-Internal-Token` 头 | `{user_id, event, data, ttl?}` | `{success, delivered, online}` | -| POST | /internal/broadcast | 内部广播接口(msg 服务触发) | `X-Internal-Token` 头 | `{event, data, filter?}` | `{success, reached}` | -| GET | /internal/online/\ | 查询用户在线状态 | `X-Internal-Token` 头 | — | `{online: bool, instances: []}` | -| GET | /healthz | 健康检查(liveness) | 公开 | — | `{status:"ok", service, version, connections}` | -| GET | /readyz | 就绪检查(readiness) | 公开 | — | `{status, degraded?}`(Redis/Kafka 软失败时 degraded:true) | -| GET | /metrics | Prometheus 指标端点 | 公开(内网) | — | Prometheus 文本格式 | +| Method | Path | 用途 | 认证 | 请求体 | 响应 | +| ------ | --------------------------- | ---------------------------------- | ----------------------------------------------- | ------------------------------ | ----------------------------------------------------------- | +| GET | /ws | WebSocket 升级端点(实时推送通知) | JWT RS256(query `?token=` 或 `Authorization`) | — | 升级为 WebSocket 长连接 | +| POST | /internal/push | 内部单推接口(msg 服务触发) | `X-Internal-Token` 头 | `{user_id, event, data, ttl?}` | `{success, delivered, online}` | +| POST | /internal/broadcast | 内部广播接口(msg 服务触发) | `X-Internal-Token` 头 | `{event, data, filter?}` | `{success, reached}` | +| GET | /internal/online/\ | 查询用户在线状态 | `X-Internal-Token` 头 | — | `{online: bool, instances: []}` | +| GET | /healthz | 健康检查(liveness) | 公开 | — | `{status:"ok", service, version, connections}` | +| GET | /readyz | 就绪检查(readiness) | 公开 | — | `{status, degraded?}`(Redis/Kafka 软失败时 degraded:true) | +| GET | /metrics | Prometheus 指标端点 | 公开(内网) | — | Prometheus 文本格式 | -**待 ISSUE-001 仲裁项**: -- ~~`GET /sse`~~(02 文档 §10 建议不支持;contract v1 列出但与 02 冲突,待 coord 仲裁移除) +**SSE 端点裁决**(ARB-015 §17.3,ISSUE-001 已裁决): -**待 ISSUE-002 仲裁项**: -- 内部 API 鉴权头命名:本契约采用 `X-Internal-Token`(对齐总裁裁决 §7.2);现码为 `X-Internal-Key`,待仲裁确认后统一 +- ❌ 不支持 `GET /sse`(仅 WebSocket /ws;删除 contract v1 的 /sse 行,02 文档 §10 正确) +- 理由:简化实现 + 统一推送通道 + WebSocket 双向通信能力更强 + +**鉴权头裁决**(ARB-015 §17.3,ISSUE-002 已裁决): + +- 统一采用 `X-Internal-Token` 头(对齐总裁裁决 §7.2) +- 现码 `X-Internal-Key` 需 ai02 统一为 `X-Internal-Token` **`X-Internal-Token` 校验机制**: + - 启动时从 `INTERNAL_API_TOKEN` 环境变量加载 - 与 msg 服务共享同一密钥(K8s Secret 注入) - DevMode(`DEV_MODE=true`)下跳过校验,便于本地联调 - 缺失/不匹配 → 401 + `PUSH_UNAUTHORIZED` **响应语义**(/internal/push): + - `delivered: true`:本实例或跨实例成功投递到至少一个连接 - `online: false`:用户离线,msg 应走离线推送(SMS/邮件) - `delivered: false, online: true`:投递失败(连接满/异常),msg 应重试或落库 @@ -81,10 +87,12 @@ ``` **客户端 → 服务端**: + - WebSocket Ping 控制帧(心跳,30s 间隔,非文本消息) - 重连时:`GET /ws?token=&session_id=&last_seq=`(P6 实现) **心跳规则**(RFC 6455 控制帧): + - 客户端每 30s 发送 Ping - 服务端自动回 Pong(gorilla/websocket 默认) - 服务端 `SetReadDeadline(60s)`,60s 无消息则关闭连接 @@ -101,43 +109,44 @@ ### 2.2 Kafka 事件订阅(异步) -| Topic | Event | 发布方 | mock 策略 | -| ------------------------------ | ------------------------ | ---------- | --------------------------------------------------------------------------- | -| `edu.notification.requested` | NotificationRequested | msg (ai10) | msg 就绪前不订阅,使用本地定时器每 30 秒推送 1 条 mock 通知到所有在线客户端 | +| Topic | Event | 发布方 | mock 策略 | +| ---------------------------- | --------------------- | ---------- | --------------------------------------------------------------------------- | +| `edu.notification.requested` | NotificationRequested | msg (ai10) | msg 就绪前不订阅,使用本地定时器每 30 秒推送 1 条 mock 通知到所有在线客户端 | > **topic 命名对齐 ISSUE-053 裁决**([president-final-rulings.md](../president-final-rulings.md) §1.5):禁止抽象名 `edu.*.events`,统一 `edu...` 格式。 > 原 contract v1 写的 `edu.msg.notification.events` 已废弃。 **消费语义**: + - Consumer Group:`push-gateway` - 至少一次(at-least-once),消费失败重试 3 次后入死信队列 - 幂等性:基于 `event_id` Redis SETNX 去重(TTL 24h) ### 2.3 HTTP 调用(如有) -| 调用方 | Method | Path | 用途 | 时机 | -| -------------- | ------ | --------------------------------- | --------------------------------- | ---------- | -| push-gateway | GET | `iam/.well-known/jwks.json` | 拉取 RS256 公钥校验 WebSocket JWT | iam 就绪后 | +| 调用方 | Method | Path | 用途 | 时机 | +| ------------ | ------ | --------------------------- | --------------------------------- | ---------- | +| push-gateway | GET | `iam/.well-known/jwks.json` | 拉取 RS256 公钥校验 WebSocket JWT | iam 就绪后 | ### 2.4 Redis 协议 -| 用途 | 数据结构 | Key 模式 | TTL | -| -------------------- | --------------- | ------------------------------------ | --------------- | -| 在线用户所在实例集合 | SET | `edu:push:online:` | 60s(心跳续期) | -| 单连接元数据 | HASH | `edu:push:session::` | 60s | +| 用途 | 数据结构 | Key 模式 | TTL | +| ---------------------- | --------------- | ------------------------------------ | --------------- | +| 在线用户所在实例集合 | SET | `edu:push:online:` | 60s(心跳续期) | +| 单连接元数据 | HASH | `edu:push:session::` | 60s | | 跨实例定向推送 channel | Pub/Sub channel | `edu:push:channel:user:` | — | -| 跨实例广播 channel | Pub/Sub channel | `edu:push:channel:broadcast` | — | -| 幂等去重 | SETNX | `edu:push:idempotent:` | 24h | +| 跨实例广播 channel | Pub/Sub channel | `edu:push:channel:broadcast` | — | +| 幂等去重 | SETNX | `edu:push:idempotent:` | 24h | > **Hub 启动重建机制**(对齐 ISSUE-058):实例启动时遍历内存连接 SADD + EXPIRE 60s;先清空 Redis 中本 instanceID 旧成员避免幽灵成员;实例崩溃 SET 自然过期(60s)。 ### 2.5 内部接口(msg 调用 push-gateway) -| 被调用方 | Method.Path | 用途 | 说明 | -| ------------ | ------------------------ | ------------------ | ---------------------------------------- | -| push-gateway | POST /internal/push | msg 服务单用户推送 | msg 渲染模板后定向推送给目标用户 | -| push-gateway | POST /internal/broadcast | msg 服务批量推送 | msg 收到业务事件后渲染模板,调此接口广播 | -| push-gateway | GET /internal/online/ | msg 查在线状态 | msg 决定走在线推送还是离线推送(SMS/邮件) | +| 被调用方 | Method.Path | 用途 | 说明 | +| ------------ | ----------------------------- | ------------------ | ------------------------------------------ | +| push-gateway | POST /internal/push | msg 服务单用户推送 | msg 渲染模板后定向推送给目标用户 | +| push-gateway | POST /internal/broadcast | msg 服务批量推送 | msg 收到业务事件后渲染模板,调此接口广播 | +| push-gateway | GET /internal/online/ | msg 查在线状态 | msg 决定走在线推送还是离线推送(SMS/邮件) | --- @@ -187,25 +196,25 @@ ## §5 与 02 文档、现码、总裁裁决的对齐说明 -| 维度 | 现 code | 02 文档 | 总裁裁决 | 本 contract 采用 | 对齐任务 | -| ---- | ------- | ------- | -------- | ---------------- | -------- | -| 内部端点路径 | `/internal/push` | `/internal/push` | §4.2 as-is 采纳 02 §4.2 | `/internal/push` | ✅ 已对齐(v1 写 `/internal/send` 已修正) | -| 鉴权头名 | `X-Internal-Key` | `X-Internal-Token` | §7.2 "X-Internal-Token 重命名" | `X-Internal-Token` | 待 ISSUE-002 仲裁后改代码 | -| 鉴权环境变量 | `INTERNAL_API_KEY` | `INTERNAL_API_TOKEN` | §7.2 | `INTERNAL_API_TOKEN` | 待 ISSUE-002 仲裁后改代码 | -| 鉴权机制 | 共享密钥 | 共享密钥 | — | 共享密钥(v1 写 mTLS 已废弃) | ✅ 已对齐 | -| Kafka topic | —(未实现) | `edu.notification.events` | §1.5 ISSUE-053 → `edu.notification.requested` | `edu.notification.requested` | ✅ 已对齐 ISSUE-053 | -| /readyz Redis 失败策略 | —(仅返连接数) | 返 503 硬失败 | §4.3 ISSUE-058 "仅告警不阻塞" | 软失败 200 + `degraded:true` | 待 ISSUE-006 仲裁最终策略 | -| /readyz Kafka 失败策略 | — | 未描述 | §3.3 ISSUE-055 软失败 | 软失败 200 + `degraded:true` | ✅ 已对齐 ISSUE-055 | -| /sse 端点 | 无 | §10 建议不支持 | — | 不提供(待 ISSUE-001 仲裁) | 待 ISSUE-001 仲裁 | -| 容量目标 | — | 10w+ | — | 10w+ | 待 ISSUE-003 仲裁更新 modules/README | -| 错误码前缀 | 无前缀 | `PUSH_*` | — | `PUSH_*` | 待 P5 实现时统一 | -| 心跳协议 | 文本 ping/pong | RFC 6455 控制帧 | — | RFC 6455 控制帧 | 待 P5 任务 4.3 重构 | +| 维度 | 现 code | 02 文档 | 总裁裁决 | 本 contract 采用 | 对齐任务 | +| ---------------------- | ------------------ | ------------------------- | --------------------------------------------- | ----------------------------- | ------------------------------------------ | +| 内部端点路径 | `/internal/push` | `/internal/push` | §4.2 as-is 采纳 02 §4.2 | `/internal/push` | ✅ 已对齐(v1 写 `/internal/send` 已修正) | +| 鉴权头名 | `X-Internal-Key` | `X-Internal-Token` | §7.2 "X-Internal-Token 重命名" | `X-Internal-Token` | 待 ISSUE-002 仲裁后改代码 | +| 鉴权环境变量 | `INTERNAL_API_KEY` | `INTERNAL_API_TOKEN` | §7.2 | `INTERNAL_API_TOKEN` | 待 ISSUE-002 仲裁后改代码 | +| 鉴权机制 | 共享密钥 | 共享密钥 | — | 共享密钥(v1 写 mTLS 已废弃) | ✅ 已对齐 | +| Kafka topic | —(未实现) | `edu.notification.events` | §1.5 ISSUE-053 → `edu.notification.requested` | `edu.notification.requested` | ✅ 已对齐 ISSUE-053 | +| /readyz Redis 失败策略 | —(仅返连接数) | 返 503 硬失败 | §4.3 ISSUE-058 "仅告警不阻塞" | 软失败 200 + `degraded:true` | 待 ISSUE-006 仲裁最终策略 | +| /readyz Kafka 失败策略 | — | 未描述 | §3.3 ISSUE-055 软失败 | 软失败 200 + `degraded:true` | ✅ 已对齐 ISSUE-055 | +| /sse 端点 | 无 | §10 建议不支持 | — | 不提供(待 ISSUE-001 仲裁) | 待 ISSUE-001 仲裁 | +| 容量目标 | — | 10w+ | — | 10w+ | 待 ISSUE-003 仲裁更新 modules/README | +| 错误码前缀 | 无前缀 | `PUSH_*` | — | `PUSH_*` | 待 P5 实现时统一 | +| 心跳协议 | 文本 ping/pong | RFC 6455 控制帧 | — | RFC 6455 控制帧 | 待 P5 任务 4.3 重构 | --- ## §6 变更历史 -| 版本 | 日期 | 变更内容 | 变更依据 | -| ---- | ---------- | ------------------------------------------------------------------------ | ------------------------------------- | -| v1 | 2026-07-09 | 初始版本(coord 生成) | — | +| 版本 | 日期 | 变更内容 | 变更依据 | +| ---- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | +| v1 | 2026-07-09 | 初始版本(coord 生成) | — | | v2 | 2026-07-10 | topic 改 `edu.notification.requested`;/internal/send → /internal/push;鉴权 mTLS → X-Internal-Token;移除 /sse(待仲裁);新增 /internal/online;新增 §5 对齐表 | ISSUE-053/055/056/058 + 总裁 §4.2/§7.2 | diff --git a/docs/architecture/issues/coord.md b/docs/architecture/issues/coord.md index 3e2081f..021a139 100644 --- a/docs/architecture/issues/coord.md +++ b/docs/architecture/issues/coord.md @@ -8,10 +8,29 @@ ## 仲裁记录索引 -| 编号 | 日期 | 提请方 | 主题 | 状态 | 仲裁章节 | -| ------- | ---------- | --------------------- | --------------------------------- | --------- | -------- | -| ARB-001 | 2026-07-09 | coord(全并行启动前) | teacher-bff GraphQL schema 第一版 | ✅ 已裁决 | §1 | -| ARB-002 | 2026-07-09 | coord(全并行启动前) | MF Shell 暴露清单 | ✅ 已裁决 | §2 | +| 编号 | 日期 | 提请方 | 主题 | 状态 | 仲裁章节 | +| ------- | ---------- | --------------------- | ------------------------------------------------------------------------ | --------- | -------- | +| ARB-001 | 2026-07-09 | coord(全并行启动前) | teacher-bff GraphQL schema 第一版 | ✅ 已裁决 | §1 | +| ARB-002 | 2026-07-09 | coord(全并行启动前) | MF Shell 暴露清单 | ✅ 已裁决 | §2 | +| ARB-003 | 2026-07-10 | ai06 | iam 模块 7 项 ISSUE 仲裁 | ✅ 已裁决 | §3 | +| ARB-004 | 2026-07-10 | ai08 | core-edu 模块 7 项 ISSUE 仲裁 | ✅ 已裁决 | §4 | +| ARB-005 | 2026-07-10 | ai09 | content 模块 10 项 ISSUE 仲裁 | ✅ 已裁决 | §5 | +| ARB-006 | 2026-07-10 | coord(跨模块) | events.proto 补全(跨 iam/core-edu/content) | ✅ 已裁决 | §6 | +| ARB-007 | 2026-07-10 | coord(跨模块) | proto 契约补全汇总(iam.proto + core_edu.proto) | ✅ 已裁决 | §7 | +| ARB-008 | 2026-07-10 | ai10 | msg 模块 15 项 ISSUE 仲裁 | ✅ 已裁决 | §10 | +| ARB-009 | 2026-07-10 | ai11 | data-ana 模块 6 项 ISSUE 仲裁 | ✅ 已裁决 | §11 | +| ARB-010 | 2026-07-10 | ai12 | ai 模块 9 项 ISSUE 仲裁 | ✅ 已裁决 | §12 | +| ARB-011 | 2026-07-10 | coord(跨模块) | events.proto 扩展补全(msg/data-ana/ai message)+ proto package 命名统一 | ✅ 已裁决 | §13 | +| ARB-012 | 2026-07-10 | coord(跨模块) | analytics.proto + ai.proto 补全 | ✅ 已裁决 | §14 | +| ARB-013 | 2026-07-10 | coord(跨模块) | Kafka topic 命名统一裁决(跨全模块) | ✅ 已裁决 | §15 | +| ARB-014 | 2026-07-10 | ai01 | api-gateway 模块 4 项内联 ISSUE 仲裁 | ✅ 已裁决 | §16 | +| ARB-015 | 2026-07-10 | ai02 | push-gateway 模块 7 项 ISSUE 仲裁 | ✅ 已裁决 | §17 | +| ARB-016 | 2026-07-10 | ai03 | teacher-bff 模块 3 项 ISSUE 仲裁 | ✅ 已裁决 | §18 | +| ARB-017 | 2026-07-10 | ai04 | student-bff 模块 4 项新 ISSUE 仲裁 | ✅ 已裁决 | §19 | +| ARB-018 | 2026-07-10 | ai05 | parent-bff 模块 10 项 ISSUE 仲裁 | ✅ 已裁决 | §20 | +| ARB-019 | 2026-07-10 | ai14 | student-portal 模块 7 项 ISSUE 仲裁 | ✅ 已裁决 | §21 | +| ARB-020 | 2026-07-10 | ai15 | parent-portal 模块 10 项 ISSUE 仲裁 | ✅ 已裁决 | §22 | +| ARB-021 | 2026-07-10 | ai16 | admin-portal 模块 7 项 ISSUE 仲裁 | ✅ 已裁决 | §23 | --- @@ -29,8 +48,10 @@ ```graphql # ============ P2 第一版 schema ============ -# 存放位置:packages/shared-ts/contracts/graphql/teacher-bff.graphql -# 仲裁原则:仅包含 P2 可实现的 Query(iam + classes 已就绪),Mutation 延后到 P3 +# 存放位置:packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql +# 仲裁原则:仅包含 P2 可实现的 Query(iam 已就绪),Mutation 延后到 P3 +# 修订(ARB-016 §18):admin 命名空间 P2 预留 schema 占位(总裁 §5.1 + §7.3 裁决优先级 president > coord) +# 修订(ARB-016 §18):schema 文件名统一为 .schema.graphql(总裁 §2.17) scalar DateTime @@ -39,7 +60,7 @@ enum DataScope { CLASS GRADE SCHOOL - DISTRICT + SUBJECT ALL } @@ -98,7 +119,7 @@ type DashboardData { # ============ Query(P2 仅读,无 Mutation) ============ type Query { - # 仪表盘聚合(并行:iam.me + iam.viewports + iam.permissions + classes.byTeacher) + # 仪表盘聚合(并行:iam.me + iam.viewports + iam.permissions + iam.classesByTeacher) dashboard: DashboardData! # 视口配置(导航) @@ -107,7 +128,7 @@ type Query { # 当前用户信息 me: User! - # 班级列表(按教师 dataScope 过滤) + # 班级列表(按教师 dataScope 过滤,数据来自 iam gRPC,总裁 §3.5) classes: [Class!]! class(id: ID!): Class } @@ -118,28 +139,54 @@ type Query { # - notifications → P5(msg 就绪后) # - 所有 Mutation → P3+ # - Subscription → P6+ 评估 + +# ============ admin 命名空间(P2 预留 schema 占位,P6 实现) ============ +# 总裁 §5.1:admin-portal 复用 teacher-bff,admin 命名空间 P2 预留 schema 占位 +# P2 仅声明类型骨架,Resolver 返 null;P6 实现(ARB-021 ISSUE-005) +# 详见 ARB-021 §23.4 admin 命名空间 schema 清单 +extend type Query { + # adminUsers / adminRoles / adminPermissions / adminViewports + # adminOrganization / adminClasses / adminTeachers / adminStudents + # auditLogs / adminDashboard + # (P6 实现,P2 仅占位) +} + +extend type Mutation { + # createUser / updateUser / deleteUser / toggleUserStatus + # createRole / updateRolePermissions / updateViewport + # (P6 实现,P2 仅占位) +} ``` ### 1.3 关键裁决 -| 裁决点 | 结论 | 依据 | -| ---------------- | ---------------------------------------------------------- | ------------------------------------------------------------ | -| Schema 存放位置 | `packages/shared-ts/contracts/graphql/teacher-bff.graphql` | 总裁裁决 §2.17 SDL-first + 集中管理 | -| P2 Query 范围 | dashboard / viewports / me / classes / class(5 个) | 仅依赖 iam + classes(P2 就绪) | -| P2 Mutation 范围 | **无**(P2 纯读) | Mutation 依赖 core-edu(P3),P2 无下游可写 | -| DataLoader | dashboard 内 classes 列表用 DataLoader | N+1 防御(ai03 §5.1 第 4 条) | -| 复杂度限制 | depth ≤ 7,cost ≤ 1000 | ai03 §5.1 第 5 条 | -| admin 命名空间 | **P2 不包含**,P6 admin-portal 阶段新增 `admin` 命名空间 | 总裁裁决 §5.1 admin 复用 teacher-bff + admin schema 命名空间 | -| 错误响应 | ActionState 信封(success/errors/data) | coord-final-decisions F9 | -| 降级模式 | success=true + error=null + data 内 degraded 字段 | 总裁裁决 §3.4 方案 B | +| 裁决点 | 结论 | 依据 | +| ----------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| Schema 存放位置 | `packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql` | 总裁裁决 §2.17 SDL-first + 集中管理 | +| schema 文件名格式 | `.schema.graphql`(统一格式,对齐 student-bff/parent-bff) | 总裁裁决 §2.17 + ARB-016 §18.3 | +| P2 Query 范围 | dashboard / viewports / me / classes / class(5 个) | 仅依赖 iam(P2 就绪),classes 数据从 iam gRPC 获取(总裁 §3.5) | +| P2 Mutation 范围 | **无**(P2 纯读) | Mutation 依赖 core-edu(P3),P2 无下游可写 | +| DataLoader | dashboard 内 classes 列表用 DataLoader | N+1 防御(ai03 §5.1 第 4 条) | +| 复杂度限制 | depth ≤ 7,cost ≤ 1000 | ai03 §5.1 第 5 条 | +| admin 命名空间 | **P2 预留** schema 占位(声明 admin Query/Mutation 类型骨架,Resolver 返 null),P6 实现 | 总裁裁决 §5.1 + §7.3(裁决优先级 president > coord,修正原 ARB-001 "P2 不包含") | +| DataScope 枚举 | SELF / CLASS / GRADE / SCHOOL / SUBJECT / ALL(SUBJECT 替代 DISTRICT) | coord DataScope 6 级统一 | +| 错误响应 | ActionState 信封(success/errors/data) | coord-final-decisions F9 | +| 降级模式 | success=true + error=null + data 内 degraded 字段 | 总裁裁决 §3.4 方案 B | + +> **修订记录**(ARB-016 §18,2026-07-10): +> +> 1. admin 命名空间由 "P2 不包含" 修正为 "P2 预留 schema 占位"(总裁 §5.1 + §7.3 优先级 president > coord) +> 2. schema 文件名由 `teacher-bff.graphql` 修正为 `teacher-bff.schema.graphql`(统一 `.schema.graphql` 格式,总裁 §2.17) +> 3. classes 数据来源明确为 iam gRPC(总裁 §3.5,非 core-edu) ### 1.4 ai03 执行项 -1. 在 `packages/shared-ts/contracts/graphql/teacher-bff.graphql` 创建 P2 第一版 schema 文件 +1. 在 `packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql` 创建 P2 第一版 schema 文件 2. teacher-bff 实现 Yoga GraphQL endpoint(`POST /graphql`) 3. 实现 5 个 Query Resolver(dashboard / viewports / me / classes / class) -4. dashboard Resolver 并行调用 iam(3 RPC)+ classes(1 RPC),用 DataLoader 防御 N+1 +4. dashboard Resolver 并行调用 iam(3 RPC)+ iam.classesByTeacher(1 RPC),用 DataLoader 防御 N+1(总裁 §3.5:classes 数据从 iam gRPC 获取) 5. 实现 ActionState 信封 + 降级模式(方案 B) +6. P2 预留 admin 命名空间 schema 占位(声明类型骨架,Resolver 返 null;P6 实现见 ARB-021 §23.4) ### 1.5 ai13 执行项 @@ -223,9 +270,1304 @@ shared: { --- -## §3 仲裁流程说明 +## §3 ARB-003:iam 模块 7 项 ISSUE 仲裁 -### 3.1 提请异议 +### 3.1 背景 + +ai06 在 [iam_issue.md](./objections/iam_issue.md) §2 提请 7 项异议,涉及文档回写、events.proto 缺失、Topic 命名不一致、DataScope 枚举冲突、iam.proto 未补全、AI 身份署名错误、双入口策略冲突。coord 对照 [iam_contract.md](./contracts/iam_contract.md)(v1.0,已含双入口策略 + 12 RPC + REST 端点清单)逐条仲裁。 + +### 3.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | +| 001 | 01/02 文档未回写 I1-I8 裁决 | ✅ 支持 ai06 方案:3 个工作日内回写 01/02,删除全部过渡方案,对齐 contract.md v1.0 | ai06 | +| 002 | events.proto 缺 UserEvent/RoleEvent/AuditEvent | ✅ coord 批次 0 补全(见 §6 ARB-006 统一处理) | coord | +| 003 | Topic 命名三方不一致(edu.identity.* vs edu.iam.*) | ✅ 统一 `edu.iam.*` 域名 + 聚合 topic 风格 | coord 修 004 + matrix.md | +| 004 | DataScope 枚举三方不一致 | ✅ 采用 president §3.2 P2.2 方案:ALL/SCHOOL/GRADE/CLASS/SUBJECT/SELF(SUBJECT 替代 DISTRICT) | coord 修 004 + ai06 修源码 + coord 修 ARB-001 GraphQL schema | +| 005 | iam.proto 仅 4 RPC,未补全至 12 RPC | ✅ coord 批次 0 补全(见 §7 ARB-007 统一处理) | coord | +| 006 | 01/02 文档 AI 身份署名错误 | ✅ 与 ISSUE-001 一并回写,署名改为 ai06 | ai06 | +| 007 | contract.md §1.2 与双入口策略冲突 | ✅ contract.md 已修正为双入口策略(REST + gRPC),ai06 回写 02 文档时对齐 | ai06 | + +### 3.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ----------------- | --------------------------------------------------------- | -------------------------------------------------------------- | +| Topic 域名 | `edu.iam.*`(服务名 iam,非 identity) | G16 规则 `edu...`,domain=服务名 | +| Topic 粒度 | 聚合 topic(`edu.iam.user.events` 含多 action) | 与 events.proto ClassEvent 模式一致,减少 topic 数量 | +| DataScope 枚举 | ALL/SCHOOL/GRADE/CLASS/SUBJECT/SELF(6 级) | president §3.2 P2.2;SUBJECT 替代 DISTRICT(K12 学科级更实用) | +| 双入口策略 | REST `/iam/v1/*` 供 gateway 透传 + gRPC 50052 供 BFF 聚合 | president §2.16 | +| iam.proto 补全 | 12 RPC(contract.md §1.1 已定义) | coord-final-decisions §5.1 + president §6 批次 0 | +| events.proto 补全 | UserEvent/RoleEvent/AuditEvent + schema_version 字段 | coord-final-decisions §5.1 + president §5.5 | +| 004 §7.2 修正 | `edu.identity.*` → `edu.iam.*`,改为聚合 topic 风格 | 与 matrix.md §4 / iam_contract.md §1.4 统一 | + +### 3.4 ai06 执行项 + +1. 回写 iam 01/02 文档(3 个工作日内): + - 删除全部过渡方案(P2 仅 REST / 自建 Outbox / PermissionGuard 待改造 / AuthMiddleware P2 不注册 / iam_parent_student_relations / 无 /v1 前缀 / HTTP 调用 iam) + - 对齐 contract.md v1.0(12 RPC + 双入口 + REST `/iam/v1/*` + gRPC 50052) + - 署名从 ai02 改为 ai06 + - 02 文档 §3.1.2 表名改为 `iam_student_guardians`(I6 裁决) + - 02 文档 §8.1 决策点 5 改为"P2 即启用 gRPC 50052"(I1 裁决) + - 02 文档 §8.1 决策点 4 改为"使用 shared-ts Outbox 工具包"(I2 裁决) + - 02 文档 §8.1 决策点 9 改为"P2 即 DB 驱动 + Redis 缓存"(I3 裁决) + - 02 文档 §1.1/§1.2/§8.1 决策点 10 改为"P2 即注册 AuthMiddleware"(I4 裁决) + - 02 文档 §4.1 REST API 加 `/v1` 前缀(I7 裁决) +2. 修改源码 iam.schema.ts DataScope 枚举:DISTRICT → SUBJECT(ISSUE-004) +3. 实现源码 PermissionGuard DB 驱动 + Redis 缓存(I3 裁决) +4. 实现源码 AuthMiddleware 注册(I4 裁决) +5. 实现源码 RS256 密钥(I5 裁决,本地文件 IAM_PRIVATE_KEY_PATH / IAM_PUBLIC_KEY_PATH) +6. 实现源码 iam_student_guardians 表 + GetChildrenByParent RPC + GET /iam/v1/children(I6 裁决) + +### 3.5 coord 执行项 + +1. 补全 iam.proto 至 12 RPC(ARB-007,批次 0) +2. 补全 events.proto UserEvent/RoleEvent/AuditEvent + schema_version(ARB-006,批次 0) +3. 修正 004 §7.2:`edu.identity.*` → `edu.iam.*`,改为聚合 topic 风格 +4. 修正 004 §5.3 表:DataScope 枚举 DISTRICT → SUBJECT;行 505 语义命名与枚举表对齐 +5. 修正 ARB-001 GraphQL schema DataScope 枚举:去掉 DISTRICT,加 SUBJECT +6. 修正 matrix.md §4:iam topic 已为 `edu.iam.*`(核查无需改);补 DataScope 枚举说明 + +--- + +## §4 ARB-004:core-edu 模块 7 项 ISSUE 仲裁 + +### 4.1 背景 + +ai08 在 [core-edu_issue.md](./objections/core-edu_issue.md) 提请 7 项异议,涉及 proto 实际状态与仲裁声称不符、events.proto 未同步、状态命名跨模块不一致、class.transferred topic 不一致、RPC 数量统计口径冲突、7 项设计决策未仲裁、01 文档审计待核对项。coord 对照 [core-edu_contract.md](./contracts/core-edu_contract.md)(已更新为 P3 目标态 27 RPC)逐条仲裁。 + +### 4.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| 001 | core_edu.proto 实际状态与仲裁声称不符 | ✅ coord 批次 0 补全至 5 Service 27 RPC(见 §7 ARB-007) | coord | +| 002 | events.proto 未同步 topic 命名仲裁 | ✅ coord 批次 0 补全(见 §6 ARB-006:AttendanceEvent + schema_version + edu.teaching.* 注释) | coord | +| 003 | 考试/作业状态命名跨模块不一致 | ✅ 采用 ai08 方案 A:统一用 `graded`(去掉 `SCORED`);submissions 统一为 `not_submitted/submitted/graded` | ai08 修源码 + coord 修 ARB-001 GraphQL schema | +| 004 | class.transferred 事件 topic 三处不一致 | ✅ 统一为 `edu.teaching.class.transferred` | coord 修 matrix.md + ai08 修文档 | +| 005 | RPC 数量统计口径不一致 | ✅ 统一为 P3 全量 27 RPC(ClassService 4 + ExamService 8 + HomeworkService 5 + GradeService 6 + AttendanceService 4) | coord 修 matrix.md + ai08 修文档 | +| 006 | 7 项设计决策待仲裁 | ✅ 全部采用 ai08 倾向方案(见 §4.4) | ai08 | +| 007 | 01 文档审计表"待核对"项 | ✅ 3 项纳入 P3 验收硬性标准 | ai08 | + +### 4.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| proto 补全范围 | 5 Service 27 RPC(含 ClassService + AttendanceService + P3 新增 5 RPC + Exam/Homework/Grade message 补字段 + SubmitHomeworkRequest 补 answers) | ISSUE-001 建议 | +| Topic 命名 | 统一 `edu.teaching..`(含 class.transferred) | coord-cross-review §3.1 | +| 聚合 topic | core-edu 采用具体动作 topic(`edu.teaching.exam.created` 等独立 topic,非聚合) | 与 iam 聚合 topic 不同:core-edu 事件量大,独立 topic 利于分区与消费隔离 | +| ExamStatus | draft/published/in_progress/grading/graded/archived/cancelled(小写) | 统一为 graded,去掉 SCORED | +| SubmissionStatus | not_submitted/submitted/graded(exam + homework 一致) | 统一初始状态为 not_submitted | +| GraphQL 枚举映射 | DRAFT/PUBLISHED/IN_PROGRESS/GRADING/GRADED/ARCHIVED/CANCELLED;NOT_SUBMITTED/SUBMITTED/GRADED | 大写映射,去掉 SCORED | +| RPC 统计口径 | 27 RPC(P3 全量) | ClassService 4 + ExamService 8 + HomeworkService 5 + GradeService 6 + AttendanceService 4 | + +> **注**:core-edu topic 策略采用"具体动作 topic"(`edu.teaching.exam.created` 等),与 iam/content 的"聚合 topic"(`edu.iam.user.events` / `edu.content.knowledge_point.events`)不同。原因:core-edu 事件量大(12 个事件类型),独立 topic 利于 Kafka 分区与消费者隔离;iam/content 事件量小,聚合 topic 减少元数据压力。此为 coord 裁决的策略差异,非矛盾。 + +### 4.4 ISSUE-006 七项设计决策仲裁 + +| # | 决策点 | 仲裁结论 | ai08 倾向 | +| --- | ------------------- | -------------------------------------------------- | --------- | +| 1 | classes 合并时机 | ✅ (a) P3 初期合并 | (a) | +| 2 | room_id | ✅ (b) 仅预留字段 | (b) | +| 3 | scope 优先级 | ✅ (a) class > subject > school | (a) | +| 4 | grace_period | ✅ (b) 300 秒(schema 默认值改为 `.default(300)`) | (b) | +| 5 | schema 校验 | ✅ (b) 仅文档约束(P3 不强制) | (b) | +| 6 | exam.submitted 事件 | ✅ (b) 仅含 submission_id | (b) | +| 7 | archived 考试 | ✅ (b) P3 仅软删除 | (b) | + +### 4.5 ai08 执行项 + +1. 修改 core-edu 01/02 文档: + - 01 §L48 补"ClassService 也缺失" + - 02 §4.2 对齐 27 RPC(补 ClassService 4 + AttendanceService 4 + P3 新增 5 RPC + message 字段) + - 02 §5.1 TOPIC_MAP 对齐 `edu.teaching.*` 命名(具体动作 topic) + - 02 §13.3 七项决策按 §4.4 仲裁结论更新(决策 #4 schema `.default(300)`) + - class.transferred topic 统一为 `edu.teaching.class.transferred` +2. 修改源码 status 字段值:exam `graded`(非 `scored`);submissions 统一 `not_submitted` +3. P3 实施时补齐 Dockerfile 多阶段 / ActionState traceId / 权限装饰器覆盖(ISSUE-007 验收标准) + +### 4.6 coord 执行项 + +1. 补全 core_edu.proto 至 5 Service 27 RPC(ARB-007,批次 0) +2. 补全 events.proto AttendanceEvent + schema_version + edu.teaching.* 注释(ARB-006,批次 0) +3. 修正 coord-cross-review.md L312:"✅ 已补全" → "⏳ 待 coord 补全(ARB-007)" +4. 修正 matrix.md §2:core-edu 22 RPC → 27 RPC +5. 修正 matrix.md §4:class.transferred topic 统一为 `edu.teaching.class.transferred` +6. 修正 ARB-001 GraphQL schema:ExamStatus 去掉 SCORED 加 CANCELLED;SubmissionStatus 去掉 IN_PROGRESS(如有)确认 NOT_SUBMITTED/SUBMITTED/GRADED +7. 修正 core-edu_contract.md §2.2:UserEvent topic 从 `edu.identity.user.*` 改为 `edu.iam.user.events`(与 iam ISSUE-003 仲裁一致,聚合 topic 风格) + +--- + +## §5 ARB-005:content 模块 10 项 ISSUE 仲裁 + +### 5.1 背景 + +ai09 在 [content_issue.md](./objections/content_issue.md) 提请 10 项异议,涉及 REST 端点设计不一致、事件 topic 命名策略冲突、Textbook/Chapter 事件遗漏、RPC 数量三方不一致、core-edu 失效事件无定义、文档 push main 冲突、created_by 迁移风险、缓存/API 版本化缺失、schema 字段缺失、Sync Worker 链路。coord 对照 [content_contract.md](./contracts/content_contract.md)(v1.1,已更新为 21 RPC + 4 聚合 topic)逐条仲裁。 + +### 5.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | +| 001 | REST 端点设计与实现不一致 | ✅ P4 重构时统一为 query 参数风格 + 补 DELETE prerequisite 端点;允许 API 路径变更(BFF 走 GraphQL 解耦) | ai09 | +| 002 | 事件 topic 命名策略冲突 | ✅ 采用聚合 topic + action 字段策略(4 聚合 topic) | ai09 修文档 + coord 补 events.proto | +| 003 | Textbook/Chapter 事件遗漏 | ✅ 补登记 edu.content.textbook.events + chapter.events | ai09 已在 contract.md 补 + coord 同步 matrix.md | +| 004 | gRPC RPC 数量三方不一致 | ✅ 统一为 21 RPC(Textbook 5 + Chapter 5 + KG 4 + Question 7) | ai09 修文档 + coord 修 matrix.md | +| 005 | core-edu → content 失效事件 topic 无定义 | ✅ 删除 02 文档 §5.2 该条目;联动改用 gRPC 调用 | ai09 | +| 006 | "直接 push main"与项目规则冲突 | ✅ 遵循总裁裁决(trunk-based,直接 push main,post-commit review);措辞优化为"trunk-based development" | ai09 | +| 007 | questions.created_by 迁移风险 | ✅ schema 用 `created_by varchar(32) NOT NULL DEFAULT 'system'`(历史数据回填 'system') | ai09 | +| 008 | 缓存策略与 API 版本化缺失 | ✅ P4 补缓存策略小节 + API 版本化说明 | ai09 | +| 009 | knowledge-points schema 缺字段 | ✅ P4 迁移补齐 difficulty + metadata + created_at + updated_at | ai09 | +| 010 | Sync Worker 链路 P4 不必要 | ✅ ES Sync Worker 标 P5;Neo4j Sync Worker 输入仅 Outbox 事件;P4 移除 safeCreateNode 同步写 | ai09 | + +### 5.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ------------------------ | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | +| REST 端点风格 | query 参数(`GET /knowledge-points?chapterId=`) | RESTful 规范 + 与 design doc 一致 | +| 事件 topic 策略 | 聚合 topic + action 字段(4 topic:textbook/chapter/knowledge_point/question) | 与 matrix.md / events.proto ClassEvent 模式一致;减少 topic 数量 | +| RPC 总数 | 21(TextbookService 5 + ChapterService 5 + KnowledgeGraphService 4 + QuestionService 7) | contract.md 为契约唯一源 | +| content 与 core-edu 关系 | content 是上游(core-edu 调 content),不消费 core-edu 事件 | ISSUE-005 建议 | +| Git 工作流 | trunk-based development(直接 push main + post-commit review) | 总裁裁决覆盖 project_rules §8 分支开发规则 | +| created_by 迁移 | `NOT NULL DEFAULT 'system'`(历史数据回填 + 新数据应用层校验) | ISSUE-007 方案二 | +| 缓存策略 | Redis 缓存教材树/章节树 + Outbox 事件触发失效 | 读多写少场景 | +| API 版本化 | REST `/v1/` 前缀 + gRPC proto package version | 破坏性变更隔离 | +| Neo4j 同步 | P4 改为 Outbox 事件驱动异步同步(移除 safeCreateNode 同步写) | 禁止业务事务内同步双写 | + +### 5.4 ai09 执行项 + +1. 修改 content 02 文档: + - §4.1 REST 端点改 query 参数风格 + 补 DELETE prerequisite 端点 + - §5.1 TOPIC_MAP 改为 4 聚合 topic(textbook/chapter/knowledge_point/question events) + - §5.2 删除 core-edu 失效事件条目 + - §4.2 gRPC RPC 对齐 21 RPC(补 Textbook Update/Delete + Chapter Update/Delete + Question Publish/Search) + - §6 补"缓存策略"小节(Redis + Outbox 失效) + - §4 补"API 版本化"说明(REST `/v1/` 前缀) + - §1.1 图 ES Sync Worker 标 P5(虚线/灰显);Neo4j Sync Worker 输入仅 Outbox 事件 + - 文档末尾措辞优化为"trunk-based development(直接 push main,post-commit review)" +2. 修改源码 schema:questions.created_by `NOT NULL DEFAULT 'system'`;knowledge_points 补 difficulty/metadata/created_at/updated_at +3. P4 任务 T6:重构 knowledge-points.service.ts 移除 safeCreateNode 同步写,改为发 Outbox 事件 + +### 5.5 coord 执行项 + +1. 补全 events.proto TextbookEvent/ChapterEvent/KnowledgePointEvent/QuestionEvent + schema_version(ARB-006,批次 0) +2. 修正 matrix.md §2:content 18 RPC → 21 RPC +3. 修正 matrix.md §4:补登记 edu.content.textbook.events + chapter.events(共 4 聚合 topic) + +--- + +## §6 ARB-006:events.proto 补全(跨 iam/core-edu/content) + +### 6.1 背景 + +iam ISSUE-002、core-edu ISSUE-002、content ISSUE-002/003 共同指向 events.proto 不完整。当前 events.proto 仅定义 4 个 message(ClassEvent/ExamEvent/HomeworkEvent/GradeEvent),缺 iam 的 UserEvent/RoleEvent/AuditEvent、core-edu 的 AttendanceEvent、content 的 TextbookEvent/ChapterEvent/KnowledgePointEvent/QuestionEvent,且所有 message 缺 schema_version 字段,文件头注释仍用旧 topic 命名。 + +### 6.2 仲裁结论 + +coord 在批次 0(P2 启动前)补全 events.proto: + +| 新增 message | 字段 | 来源 | +| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | +| UserEvent | event_id/aggregate_id/event_type/occurred_at/user_id/email/name/roles/action(created/updated/deleted/role_changed)/schema_version/metadata | iam 02 §5.2 + president §5.5 | +| RoleEvent | event_id/aggregate_id/event_type/occurred_at/role_id/role_name/permissions/action(created/updated)/schema_version/metadata | iam 02 §5.2 | +| AuditEvent | event_id/aggregate_id/event_type/occurred_at/user_id/action(create/update/delete/login/logout/permission_change)/resource_type/resource_id/schema_version/metadata | president §5.5 | +| AttendanceEvent | event_id/aggregate_id/event_type/occurred_at/attendance_id/student_id/class_id/schedule_id/status/action(recorded)/schema_version/metadata | core-edu 02 §5.1 | +| TextbookEvent | event_id/aggregate_id/event_type/occurred_at/textbook_id/title/subject_id/grade_id/version/action(created/updated/published/archived)/schema_version/metadata | content contract.md §1.4 | +| ChapterEvent | event_id/aggregate_id/event_type/occurred_at/chapter_id/textbook_id/title/order/action(created/updated/deleted)/schema_version/metadata | content contract.md §1.4 | +| KnowledgePointEvent | event_id/aggregate_id/event_type/occurred_at/kp_id/chapter_id/title/difficulty/action(created/updated/prerequisite_added/prerequisite_removed)/prerequisite_id/schema_version/metadata | content contract.md §1.4 | +| QuestionEvent | event_id/aggregate_id/event_type/occurred_at/question_id/kp_id/type/difficulty/status/source/created_by/action(created/updated/published/deleted)/schema_version/metadata | content contract.md §1.4 | + +**同时**: + +- 所有现有 message(ClassEvent/ExamEvent/HomeworkEvent/GradeEvent)补 `string schema_version = N;` 字段 +- 文件头注释改为 `edu.teaching.*` 命名(core-edu 事件)+ `edu.iam.*` 命名(iam 事件)+ `edu.content.*` 命名(content 事件) + +### 6.3 Topic 命名策略(跨模块统一) + +| 服务 | Topic 策略 | 示例 | 原因 | +| -------- | ------------------------ | ------------------------------------------------------------------------- | --------------------------------------- | +| iam | 聚合 topic + action 字段 | `edu.iam.user.events`(含 created/updated/deleted/role_changed) | 事件量小,聚合减少 topic 数 | +| core-edu | 具体动作 topic | `edu.teaching.exam.created`(每事件独立 topic) | 事件量大,独立 topic 利于分区与消费隔离 | +| content | 聚合 topic + action 字段 | `edu.content.knowledge_point.events`(含 created/updated/prerequisite_*) | 事件量中等,聚合减少 topic 数 | +| msg | 具体动作 topic | `edu.notify.notification.created` | 通知事件量大,独立 topic | + +> coord 裁决:允许跨服务采用不同 topic 粒度策略(聚合 vs 具体动作),以服务特征为准。所有 topic 命名统一遵循 `edu...` 或 `edu...events` 格式。 + +--- + +## §7 ARB-007:proto 契约补全汇总(iam.proto + core_edu.proto) + +### 7.1 背景 + +iam ISSUE-005 和 core-edu ISSUE-001 共同指向 proto 契约未补全。coord 作为 shared-proto 维护者,在批次 0 统一补全。 + +### 7.2 iam.proto 补全(12 RPC) + +| Service | RPC | 请求 | 响应 | +| ---------- | ----------------------- | ------------------------------ | ---------------------------- | +| IamService | Register | RegisterRequest | AuthResponse | +| IamService | Login | LoginRequest | AuthResponse | +| IamService | RefreshToken | RefreshTokenRequest | TokenPair | +| IamService | Logout | LogoutRequest | LogoutResponse | +| IamService | GetUserInfo | GetUserInfoRequest | UserInfo | +| IamService | BatchGetUsers | BatchGetUsersRequest | BatchGetUsersResponse | +| IamService | GetEffectivePermissions | GetEffectivePermissionsRequest | EffectivePermissionsResponse | +| IamService | GetEffectiveAccess | GetEffectiveAccessRequest | EffectiveAccessResponse | +| IamService | GetEffectiveDataScope | GetEffectiveDataScopeRequest | DataScopeResponse | +| IamService | GetViewports | GetViewportsRequest | ViewportsResponse | +| IamService | GetPublicKey | GetPublicKeyRequest | PublicKeyResponse | +| IamService | GetChildrenByParent | GetChildrenByParentRequest | ChildrenResponse | + +> 对齐 iam_contract.md §1.1 的 12 RPC 清单。新增 8 RPC(Logout/BatchGetUsers/GetEffectivePermissions/GetEffectiveAccess/GetEffectiveDataScope/GetViewports/GetPublicKey/GetChildrenByParent)。 + +### 7.3 core_edu.proto 补全(5 Service 27 RPC) + +| Service | RPC | 状态 | +| ----------------- | -------------------------------------------------------------------------------------------------------------------- | --------------- | +| ClassService | GetClass / GetClassesByTeacher / BatchGetClasses / ListStudentsByClass | 新增 4 RPC | +| ExamService | CreateExam / GetExam / ListExamsByClass / UpdateExam / DeleteExam / **PublishExam** / **SubmitExam** / **GradeExam** | 已有 5 + 新增 3 | +| HomeworkService | AssignHomework / GetHomework / ListHomeworkByClass / SubmitHomework / **GradeHomework** | 已有 4 + 新增 1 | +| GradeService | RecordGrade / GetGrade / ListGradesByStudent / ListGradesByExam / ListGradesByHomework / **UpdateGrade** | 已有 5 + 新增 1 | +| AttendanceService | **RecordAttendance** / **GetAttendance** / **ListAttendanceByStudent** / **ListAttendanceByClass** | 新增 4 RPC | + +**同时补全 message 字段**: + +- Exam:补 subject_id / school_id / status_changed_at / status_changed_by / archived_at +- Homework:补 subject_id / grace_period / school_id +- Grade:补 total_score / school_id / idempotency_key +- SubmitHomeworkRequest:补 answers 字段 + +> 对齐 core-edu_contract.md §1.1 的 27 RPC 清单。 + +### 7.4 coord 执行项 + +1. 补全 iam.proto 至 12 RPC(含全部 message 定义) +2. 补全 core_edu.proto 至 5 Service 27 RPC(含 ClassService + AttendanceService + P3 新增 5 RPC + message 字段补全) +3. 补全 events.proto(ARB-006,7 个新 message + schema_version + 注释更新) +4. 运行 `buf lint` + `buf breaking` 确保零错误 +5. 运行 `buf generate` 产出 TS gRPC 代码 +6. 修正 coord-cross-review.md L312 状态 +7. 更新 matrix.md §2 RPC 统计 + +--- + +## §10 ARB-008:msg 模块 15 项 ISSUE 仲裁 + +### 10.1 背景 + +ai10 在 [msg_issue.md](./objections/msg_issue.md) 提请 15 项异议(ISSUE-001~~005 已有仲裁核查 + ISSUE-006~~015 新发现),涉及 events.proto 字段缺失、proto 包名规则冲突、Kafka topic 三套约定、RPC 数量超预算、权限点不一致、降级方向矛盾、DLQ/幂等防线缺失、events.proto 缺 message、RPC 清单不一致、Kafka 发布事件不一致。 + +### 10.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------ | +| 001~005 | 已有仲裁核查 | ✅ 核查通过(001/002/003);004 待 coord 同步 events.proto 注释(ARB-006 已覆盖);005 待 coord 修正 004 §4.1 PushGW 方向 | coord | +| 006 | events.proto GradeEvent 缺 class_id + 全部事件缺 student_ids[] | ✅ coord 补全:GradeEvent 补 class_id;Exam/Homework/GradeEvent 补 student_ids[](fan-out 广播用) | coord | +| 007 | proto 包名规则冲突(project_rules §5 vs 实际) | ✅ 统一为 `next_edu_cloud..v1`(与实际 proto 一致);coord 更新 project_rules §5(见 §13 ARB-011) | coord | +| 008 | Kafka topic 命名三套约定并存 | ✅ 统一裁决(见 §15 ARB-013):msg 采用具体动作 topic(`edu.notify.notification.sent` 等,与 core-edu 策略一致) | coord | +| 009 | RPC 数量超预算(17 vs 13) | ✅ 采用方案 A:维持 13 RPC 预算,裁剪 4 RPC(BatchSendNotification/GetUnreadCount/BatchMarkAsRead/MarkAllAsRead 降级为 REST only) | ai10 | +| 010 | markAsRead 权限点不一致 | ✅ 以 02 文档 §6.1 为准:`MSG_NOTIFICATION_READ`(非 MANAGE) | ai10 | +| 011 | DB→ES 降级方向与 ai-allocation 相反 | ✅ 采用双向降级:ES 故障→DB LIKE;DB 故障→ES 只读(仅读操作可降级,写操作必须等 DB 恢复) | ai10 | +| 012 | 缺 DLQ 与三层幂等防线 | ✅ 非阻塞,ai10 自行补充设计文档(§3.1 补 msg_idempotency 表 + §5.5 三层幂等 + §5 DLQ 设计) | ai10 | +| 013 | events.proto 缺 4 类 message | ✅ coord 补全(见 §13 ARB-011:MasteryEvent/NotificationEvent + UserEvent/RoleEvent 已在 ARB-006 覆盖) | coord | +| 014 | msg_contract.md 与 02 文档 RPC 清单不一致 | ✅ 待 ISSUE-009 仲裁后统一为 13 RPC,两文档对齐 | ai10 | +| 015 | Kafka 发布事件不一致 | ✅ 待 ISSUE-008 仲裁后统一(见 §15 ARB-013) | ai10 + coord | + +### 10.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ---------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------- | +| msg RPC 总数 | 13(NotificationService 5 + PreferenceService 4 + TemplateService 4) | 维持 ai-allocation 预算;裁剪 4 RPC 降级 REST | +| Kafka topic 策略 | 具体动作 topic(`edu.notify.notification.sent` / `.read` / `.recalled` / `.failed`) | 与 core-edu 策略一致;事件量大,独立 topic 利于消费隔离 | +| markAsRead 权限 | `MSG_NOTIFICATION_READ` | 02 文档为准 | +| 降级策略 | 双向(ES 故障→DB LIKE;DB 故障→ES 只读) | ai-allocation §5 + 02 文档双向覆盖 | +| 三层幂等 | L1 Redis SETNX + L2 msg_idempotency 表 + L3 notifications.source_event_id 唯一索引 | known-issues 已记录 | +| DLQ | 重试 3 次后投递 `edu.notify.dlq` + 告警 | known-issues 已记录 | +| proto package | `next_edu_cloud.msg.v1`(保留现状) | 见 §13 ARB-011 | + +### 10.4 ai10 执行项 + +1. 修改 msg 02 文档: + - §4.2 RPC 裁剪至 13(移除 BatchSendNotification/GetUnreadCount/BatchMarkAsRead/MarkAllAsRead,降级 REST only) + - §5.2 Kafka topic 改为具体动作 topic(`edu.notify.notification.sent` 等 4 个 per-event topic) + - §3.1 补 msg_idempotency 表 schema + - §5.5 改为三层幂等防线 + - §5 补 DLQ 设计(`edu.notify.dlq` + 重试 3 次 + 告警) + - §3.2.2/§3.4 补双向降级策略(DB 故障→ES 只读) + - §6.1 markAsRead 权限改为 `MSG_NOTIFICATION_READ` +2. 修改源码 notifications.controller.ts markAsRead 权限点 +3. msg_contract.md §1.1 RPC 清单对齐 13 RPC + §1.4 topic 对齐具体动作 topic + +### 10.5 coord 执行项 + +1. 补全 events.proto(ARB-011:NotificationEvent + MasteryEvent + GradeEvent 补 class_id + 全部事件补 student_ids[]) +2. 修正 004 §4.1 PushGW 方向表述为"Msg → push-gateway (gRPC)" +3. 修正 matrix.md §4:msg topic 改为 `edu.notify.notification.*` 具体动作 topic + +--- + +## §11 ARB-009:data-ana 模块 6 项 ISSUE 仲裁 + +### 11.1 背景 + +ai11 在 [data-ana_issue.md](./objections/data-ana_issue.md) 提请 6 项异议,涉及 iam.proto 缺 GetEffectiveDataScope、events.proto 缺 AIUsageEvent、analytics.proto 未补全至 12 RPC、004 章节引用断裂、MasteryEvent topic 不一致、coord-cross-review §8.2 声明与实际不符。 + +### 11.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------- | +| 001 | iam.proto 缺 GetEffectiveDataScope RPC | ✅ coord 批次 0 补全 iam.proto 12 RPC(ARB-007 已覆盖,含 GetEffectiveDataScope) | coord | +| 002 | events.proto 缺 AIUsageEvent message | ✅ coord 补全(见 §13 ARB-011) | coord | +| 003 | analytics.proto 仅 3 RPC,声称 12 RPC 未落实 | ✅ coord 批次 0 补全 analytics.proto 至 12 RPC(见 §14 ARB-012) | coord | +| 004 | 004 章节引用断裂(coord 整改 #7/#8/#9/#10 未落实) | ✅ coord 补全 004 §7.2/§1.2/§4.1/§12.2 对应章节 | coord | +| 005 | MasteryEvent topic 命名三处不一致 | ✅ 统一为 `edu.insight.mastery.updated` + `edu.insight.warning.triggered`(domain=insight 智能洞察领域) | coord 修 matrix.md + ai11 已对齐 | +| 006 | coord-cross-review §8.2 声明与实际不符 | ✅ coord 更正 §8.2 状态为"待执行",明确补全时间点 | coord | + +### 11.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ------------------ | --------------------------------------------------------------- | --------------------------------------------------------------------- | +| analytics.proto | 12 RPC(4 端 Dashboard + Warning + Mastery + Server Streaming) | 02 文档 §4.2 已设计,coord 补全 proto | +| MasteryEvent topic | `edu.insight.mastery.updated` | 004 §7.2 命名规范 `edu...`,domain=insight | +| WarningEvent topic | `edu.insight.warning.triggered` | 同上 | +| 004 章节补全 | coord 补全 §7.2/§1.2/§4.1/§12.2 对应段落 | coord-cross-review §6 整改 #7/#8/#9/#10 | +| §8.2 声明更正 | "✅ 已完成" → "⏳ 待 coord 补全(ARB-007/012)" | 实际 proto 未补全 | +| Python Outbox 豁免 | ✅ 确认(MasteryUpdated/WarningTriggered 派生数据豁免) | coord-cross-review §3.3 | + +### 11.4 ai11 执行项 + +1. data-ana 01/02 文档:将 004 §4.2/§11.4/§11.5/§15.3 引用从 coord-cross-review.md 回切到 004 对应章节(待 coord 补全后) +2. contract.md §1.4 topic 已对齐 `edu.insight.*`(核查通过) + +### 11.5 coord 执行项 + +1. 补全 analytics.proto 至 12 RPC(ARB-012,批次 0) +2. 补全 events.proto AIUsageEvent + MasteryEvent(ARB-011,批次 0) +3. 补全 iam.proto GetEffectiveDataScope(ARB-007 已覆盖) +4. 补全 004 §7.2(补登 6 个 topic + CDC 命名规范)/§1.2(端口列)/§4.1(gRPC 启用阶段矩阵)/§12.2(派生数据事件 Outbox 豁免条款) +5. 更正 coord-cross-review.md §8.2 状态 +6. 修正 matrix.md §4:data-ana topic 改为 `edu.insight.*` + +--- + +## §12 ARB-010:ai 模块 9 项 ISSUE 仲裁 + +### 12.1 背景 + +ai12 在 [ai_issue.md](./objections/ai_issue.md) 提请 9 项异议,涉及 matrix.md 端口不同步、ai 用量事件 topic 三义、ai.proto 待补全、events.proto 缺 AIUsageEvent、contract 与设计文档矛盾、Temporal 决策点、iam GetEffectiveDataScope、proto package 命名、ActionState 整改。 + +### 12.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | +| 01 | matrix.md 端口 50057 vs 50058 不同步 | ✅ 统一为 50058(port-allocation.md §7 为准);coord 修 matrix.md | coord | +| 02 | ai 用量事件 topic 三义未决 | ✅ 统一为 `edu.ai.usage`(见 §15 ARB-013) | coord | +| 03 | ai.proto 待补全 | ✅ P5 目标 6 RPC(Chat/StreamChat/GenerateQuestion/StreamGenerateQuestion/OptimizeExpression/GenerateLessonPlan);备课状态查询/确认用 HTTP 端点(见 §14 ARB-012) | coord 补 proto | +| 04 | events.proto 缺 AIUsageEvent | ✅ coord 补全(见 §13 ARB-011) | coord | +| 05 | contract 与设计文档矛盾 | ✅ ai12 自纠重写 contract(端口 50058 / 补 HTTP 端点 / topic 待 §15 裁决后填 / 错误码对齐 02 §6.2 / 删除消费事件) | ai12 | +| 06 | 备课工作流 Temporal(P6 决策点) | ✅ P5 用 BackgroundTasks + Redis;P6 由 coord 评估 Temporal 引入(roadmap 标注 P6 决策点) | ai12 P5 + coord P6 | +| 07 | iam GetEffectiveDataScope 待 P4 补全 | ✅ coord 批次 0 补全 iam.proto 12 RPC(ARB-007 已覆盖) | coord | +| 08 | proto package 命名不符合 project_rules §5 | ✅ 统一为 `next_edu_cloud..v1`(见 §13 ARB-011) | coord | +| 09 | 响应信封偏离 ActionState | ✅ P5 实施时整改:所有返回值改为 ActionState;degraded 移至 `error.details.degraded` | ai12 | + +### 12.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ------------- | --------------------------------------------- | ----------------------------------------- | +| ai gRPC 端口 | 50058 | port-allocation.md §7 为准 | +| ai RPC 总数 | 6(P5 目标) | 避免 RPC 膨胀;备课状态查询/确认用 HTTP | +| ai 用量 topic | `edu.ai.usage` | 最简短,符合 `edu..` 约定 | +| proto package | `next_edu_cloud.ai.v1`(保留现状) | 见 §13 ARB-011 | +| Temporal | P5 不引入(BackgroundTasks + Redis);P6 评估 | 避免过早引入复杂度 | +| ActionState | P5 强制整改(degraded 移至 error.details) | 004 §11.5 | + +### 12.4 ai12 执行项 + +1. 重写 ai_contract.md 对齐设计文档(端口 50058 / 补 HTTP 端点 / topic `edu.ai.usage` / 错误码对齐 02 §6.2 / 删除消费事件或标注 P6+) +2. P5 实施时整改 ActionState(main.py 返回值改为 `{success, data, error:{code,message,details,traceId}}`;degraded 移至 `error.details.degraded`) +3. P5 备课工作流用 BackgroundTasks + Redis(02 文档 §2.4 状态机已设计) +4. 修正 01/02 文档引用:004 §7.2/§1.2 引用失实,改引 port-allocation.md + coord 仲裁 + +### 12.5 coord 执行项 + +1. 补全 ai.proto 至 6 RPC(ARB-012,批次 0) +2. 补全 events.proto AIUsageEvent(ARB-011,批次 0) +3. 修正 matrix.md §2/§8:ai gRPC 端口 50057 → 50058 +4. 在 roadmap 标注 P6 决策点(Temporal 引入评估) +5. 在 known-issues §2.9 ai 分区记录 ActionState 整改约束 + +--- + +## §13 ARB-011:events.proto 扩展补全 + proto package 命名统一 + +### 13.1 背景 + +msg ISSUE-013、data-ana ISSUE-002、ai ISSUE-04 共同指向 events.proto 还缺 msg/data-ana/ai 相关 message(ARB-006 已覆盖 iam/core-edu/content 的 7 个 message)。msg ISSUE-007 + ai ISSUE-08 共同指向 proto package 命名规则冲突。 + +### 13.2 events.proto 扩展补全(在 ARB-006 基础上) + +| 新增 message | 字段 | 来源 | +| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | +| MasteryEvent | event_id/aggregate_id/event_type/occurred_at/student_id/subject_id/mastery_score/action(updated)/schema_version/metadata | data-ana 02 文档 | +| WarningEvent | event_id/aggregate_id/event_type/occurred_at/student_id/warning_type/severity/action(triggered)/schema_version/metadata | data-ana 02 文档 | +| NotificationEvent | event_id/aggregate_id/event_type/occurred_at/notification_id/user_id/channel/action(sent/read/recalled/failed)/schema_version/metadata | msg 02 文档 | +| AIUsageEvent | event_id/request_id/user_id/school_id/provider/model/operation/prompt_tokens/completion_tokens/total_tokens/latency_ms/success/degraded/action(usage)/schema_version/metadata | ai 02 文档 §3.3 | + +**同时**: + +- GradeEvent 补 `class_id` 字段 +- ExamEvent/HomeworkEvent/GradeEvent 补 `student_ids[]` 字段(复数,fan-out 广播用) + +> events.proto 最终包含 15 个 message:ClassEvent/ExamEvent/HomeworkEvent/GradeEvent(原有 4 + 补字段)+ UserEvent/RoleEvent/AuditEvent/AttendanceEvent/TextbookEvent/ChapterEvent/KnowledgePointEvent/QuestionEvent(ARB-006 新增 8)+ MasteryEvent/WarningEvent/NotificationEvent/AIUsageEvent(本节新增 4)= 4+8+4 = 16 个 message(含原有 4)。 + +### 13.3 proto package 命名统一 + +| 裁决点 | 结论 | 依据 | +| --------------------- | ------------------------------------------------ | --------------------------------------- | +| proto package 命名 | `next_edu_cloud..v1`(保留现状) | 所有 proto 文件已采用此格式,改动成本高 | +| project_rules §5 更新 | `edu..v1` → `next_edu_cloud..v1` | 与实际 proto 一致 | + +### 13.4 coord 执行项 + +1. 补全 events.proto 4 个新 message(MasteryEvent/WarningEvent/NotificationEvent/AIUsageEvent) +2. 补全 GradeEvent class_id + 全部事件 student_ids[] 字段 +3. 更新 project_rules §5 包名规范为 `next_edu_cloud..v1`(需人类审批,project_rules §15.7 敏感文件) +4. 运行 `buf lint` + `buf breaking` 确保零错误 + +--- + +## §14 ARB-012:analytics.proto + ai.proto 补全 + +### 14.1 背景 + +data-ana ISSUE-003 和 ai ISSUE-03 共同指向 proto 契约未补全。coord 作为 shared-proto 维护者,在批次 0 统一补全。 + +### 14.2 analytics.proto 补全(12 RPC) + +| Service | RPC | 说明 | +| ---------------- | ---------------------- | ----------------------------- | +| AnalyticsService | GetClassPerformance | 班级表现 | +| AnalyticsService | GetStudentWeakness | 学生薄弱点 | +| AnalyticsService | GetLearningTrend | 学习趋势 | +| AnalyticsService | StreamClassPerformance | Server Streaming 班级实时表现 | +| AnalyticsService | GetStudentDashboard | 学生仪表盘 | +| AnalyticsService | GetTeacherDashboard | 教师仪表盘 | +| AnalyticsService | GetAdminDashboard | 管理员仪表盘 | +| AnalyticsService | GetParentDashboard | 家长仪表盘 | +| AnalyticsService | TriggerWarning | 触发预警 | +| AnalyticsService | GetWarnings | 查询预警列表 | +| AnalyticsService | UpdateMastery | 更新掌握度 | +| AnalyticsService | GetMasteryByStudent | 查询学生掌握度 | + +> 对齐 data-ana 02 文档 §4.2 的 12 RPC 清单(含 1 Server Streaming)。 + +### 14.3 ai.proto 补全(6 RPC) + +| Service | RPC | 说明 | +| --------- | ---------------------- | ------------ | +| AiService | Chat | 单轮对话 | +| AiService | StreamChat | 流式对话 | +| AiService | GenerateQuestion | 生成题目 | +| AiService | StreamGenerateQuestion | 流式生成题目 | +| AiService | OptimizeExpression | 优化表达 | +| AiService | GenerateLessonPlan | 生成教案 | + +**同时扩展 message 字段**: + +- GenerateQuestionRequest:补 grade / knowledge_point_ids / question_type / count +- ChatRequest:补 user_id / session_id / data_scope(可选) + +**备课工作流状态查询/确认用 HTTP 端点**(非 gRPC): + +- `GET /ai/v1/lesson/preparation/{id}`:查询备课状态 +- `POST /ai/v1/lesson/preparation/{id}/confirm`:确认入库 + +### 14.4 coord 执行项 + +1. 补全 analytics.proto 至 12 RPC(含 Server Streaming) +2. 补全 ai.proto 至 6 RPC + message 字段扩展 +3. 运行 `buf lint` + `buf breaking` 确保零错误 +4. 运行 `buf generate` 产出 TS/Go/Python gRPC 代码 + +--- + +## §15 ARB-013:Kafka topic 命名统一裁决(跨全模块) + +### 15.1 背景 + +msg ISSUE-008/015、data-ana ISSUE-005、ai ISSUE-02 共同指向 Kafka topic 命名跨模块不一致。需 coord 统一裁定。 + +### 15.2 仲裁结论 + +**统一命名规则**:`edu...` 或 `edu...events` + +**Topic 粒度策略**(按服务特征区分,coord 裁决允许差异): + +| 服务 | Topic 策略 | Topic 清单 | 原因 | +| -------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | +| iam | 聚合 topic + action 字段 | `edu.iam.user.events` / `edu.iam.role.events` / `edu.iam.audit.created` | 事件量小,聚合减少 topic 数 | +| core-edu | 具体动作 topic | `edu.teaching.exam.created` / `.updated` / `.deleted` / `.published` / `.submitted` / `edu.teaching.homework.assigned` / `.submitted` / `.graded` / `edu.teaching.grade.recorded` / `.updated` / `edu.teaching.attendance.recorded` / `edu.teaching.class.transferred` | 事件量大,独立 topic 利于分区 | +| content | 聚合 topic + action 字段 | `edu.content.textbook.events` / `edu.content.chapter.events` / `edu.content.knowledge_point.events` / `edu.content.question.events` | 事件量中等,聚合减少 topic 数 | +| msg | 具体动作 topic | `edu.notify.notification.sent` / `.read` / `.recalled` / `.failed` | 通知事件量大,独立 topic | +| data-ana | 具体动作 topic | `edu.insight.mastery.updated` / `edu.insight.warning.triggered` | 派生数据事件(Outbox 豁免) | +| ai | 具体动作 topic | `edu.ai.usage` | 派生数据事件(Outbox 豁免) | + +### 15.3 域名映射 + +| 服务 | domain | 依据 | +| -------- | ---------- | --------------------------------- | +| iam | `iam` | 服务名 | +| core-edu | `teaching` | 教学领域(非服务名 core_edu) | +| content | `content` | 内容领域 | +| msg | `notify` | 通知领域(非服务名 msg) | +| data-ana | `insight` | 智能洞察领域(非服务名 data_ana) | +| ai | `ai` | AI 领域 | + +### 15.4 matrix.md §4 同步更新 + +coord 修正 matrix.md §4 Kafka 事件矩阵,全部 topic 对齐上述清单。 + +### 15.5 各 AI 执行项 + +各 AI 检查自身 02 文档 TOPIC_MAP + contract.md §1.4,对齐本仲裁的 topic 命名。具体差异见各模块 ARB 章节。 + +--- + +## §16 ARB-014:api-gateway 模块 4 项内联 ISSUE 仲裁 + +### 16.1 背景 + +ai01 的 [api-gateway_issue.md](./objections/api-gateway_issue.md) 文件仅含标题未填写实质内容(文件截断,仅 2 行),但其 [api-gateway_contract.md](./contracts/api-gateway_contract.md) v2 内联了 4 项待仲裁 ISSUE(§1.2.2 注 3 + §2.4 + §3.1)。coord 提取 contract.md 中的内联 ISSUE 逐条仲裁。 + +### 16.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -------------- | +| 001 | JWKS 拉取方式(gRPC vs HTTP) | ✅ 已确认 HTTP JWKS(contract v2 §2.3 已执行:`GET /.well-known/jwks.json`,TTL 5min 缓存) | ai01(已完成) | +| 002 | shared-go 包接入 | ✅ shared-go 包骨架已完成(contract §3.1 显示 ✅);ai01 直接 import `packages/shared-go` 的 jwks/logger/tracer/env 4 模块 | ai01(已就绪) | +| 003 | API 版本化路由方案(双 /v1 前缀) | ✅ 采纳方案 A:前端调用 `/api/v1//v1/*`(双 /v1 前缀,Gateway 纯透传无路径重写) | ai01 | +| 004 | zap vs slog 日志库选型 | ✅ 遵循 project_rules §12 使用 **zap**(project_rules 明确规定 Go 日志用 zap) | ai01 | + +### 16.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ----------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| JWKS 拉取 | HTTP `GET /.well-known/jwks.json`(非 gRPC) | president §2.16 + contract v2 §2.3 | +| Gateway gRPC 消费 | **无**(Gateway 不消费任何 gRPC,纯 HTTP 透传) | president §2.16 + B3 裁决(权限由下游 Controller 自校验) | +| API 版本化 | 方案 A:`/api/v1//v1/*` 双前缀,Gateway 不做路径重写 | 纯透传最简,避免路径重写 bug;`/api/v1` 是 Gateway API 前缀,`//v1` 是服务内部 API 前缀 | +| Go 日志库 | zap(对齐 project_rules §12) | project_rules 强制规定;Go 1.22 slog 为备选,需 project_rules 变更后评估 | +| matrix.md §2 修正 | 移除 api-gateway 为 iam gRPC 消费方(contract §2.1 勘误已指出) | Gateway 不消费 gRPC | + +### 16.4 ISSUE-003 方案 A 路径规范 + +| 层级 | 路径格式 | 示例 | +| ------------ | ----------------------------------------------------- | -------------------------------------- | +| 前端调用 | `/api/v1//v1/` | `/api/v1/iam/v1/register` | +| Gateway 透传 | 原样转发到 `://v1/` | iam:3002`/iam/v1/register` | +| 服务内部路由 | `//v1/` | iam Controller 注册 `/iam/v1/register` | + +> **双 /v1 语义**:外层 `/api/v1` 是 Gateway 层 API 版本(全局),内层 `/v1` 是服务内部 API 版本(独立演进)。此设计允许 Gateway 和服务各自独立版本化。 + +### 16.5 ai01 执行项 + +1. P2.7 任务统一迁移路由至 `/api/v1//v1/*` 双前缀格式(当前 P1 代码为 `/api/v1//*` 单前缀) +2. 使用 zap 日志库(import `packages/shared-go/logger`) +3. ISSUE-001/002 已落地,无需额外动作 +4. 补充 issue.md 文件内容(当前为空,后续如有新问题需按格式追加) + +### 16.6 coord 执行项 + +1. 修正 matrix.md §2 gRPC 接口提供方矩阵:移除 api-gateway 为 iam gRPC 消费方 +2. 确认 shared-go 4 模块骨架已就绪(contract §3.1 已标 ✅) + +--- + +## §17 ARB-015:push-gateway 模块 7 项 ISSUE 仲裁 + +### 17.1 背景 + +ai02 在 [push-gateway_issue.md](./objections/push-gateway_issue.md) 提请 7 项异议,涉及 SSE 端点冲突、内部 API 鉴权命名、容量目标、端点路径、审计表设计、Redis /readyz 冲突、02 文档缺 ADR/NFR。coord 对照 [push-gateway_contract.md](./contracts/push-gateway_contract.md) v2 逐条仲裁。 + +### 17.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | +| 001 | SSE 端点三方冲突 | ✅ 采纳 ai02 建议:**不支持 SSE,仅 WebSocket**。移除 contract.md §1.2 的 `/sse` 端点 + matrix.md §5 SSE 行 | coord 修 contract + matrix + ai02 确认 | +| 002 | 内部 API 鉴权命名不一致 | ✅ 统一为 `X-Internal-Token` 头 + `INTERNAL_API_TOKEN` 环境变量(对齐总裁 §7.2) | ai02 改代码 + coord 修 contract | +| 003 | 容量目标 50k vs 10w+ | ✅ 统一为 **10w+**(对齐 02 文档 + pending-features 权威) | coord 修 modules/README §7 | +| 004 | /internal/send vs /internal/push | ✅ 统一为 `/internal/push`(ai02 已自行修复,contract v2 已对齐) | ai02(已完成) | +| 005 | 审计表设计缺失 | ✅ 方案 2:审计由 msg 服务维护(msg 已落库),push-gateway 保持无 DB 定位 | ai10 确认审计字段 + ai02 无需引入 DB | +| 006 | ISSUE-058 与 055 Redis /readyz 冲突 | ✅ **Redis 软失败**(仅告警 + `degraded: true` + 返 200,不返 503);从 ISSUE-055 必需依赖列表中移除 push-gateway → Redis | coord 澄清两裁决优先级 | +| 007 | 02 文档缺 ADR/NFR/容量/失败模式 | ✅ P5(批次 4)补全 02 文档 §14-§17 四章节(ADR/非功能性需求/失败模式/容量估算) | ai02 | + +### 17.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ------------- | ----------------------------------------------------- | ---------------------------------------------------------------------- | +| SSE 端点 | **不支持**,仅 WebSocket | 单一协议降低维护成本;SSE 单向下行不适合双向协议 | +| 内部鉴权 | `X-Internal-Token` + `INTERNAL_API_TOKEN`(共享密钥) | 总裁 §7.2;mTLS P5 成本过高,K8s NetworkPolicy 已隔离 | +| 容量目标 | 10w+ 连接/单实例 | pending-features 权威;Go goroutine-per-connection 约 2-3GB/10w 连接 | +| 审计表归属 | msg 服务维护(push-gateway 无 DB) | 保持 push-gateway 无 DB 定位,避免引入持久化层 | +| Redis /readyz | **软失败**(仅告警不阻塞,返 200 + degraded) | push-gateway 重启不解决 Redis 故障;Redis 故障时单实例仍能服务本地连接 | +| Kafka /readyz | 软失败(仅告警 + degraded) | ISSUE-055 裁决 | +| 心跳协议 | RFC 6455 控制帧(Ping/Pong,30s 间隔) | 02 文档 §10 | + +### 17.4 ISSUE-006 Redis 软失败裁决详情 + +**冲突**: + +- ISSUE-055(president §3.3):Redis 列为"必需依赖",失败返 503 +- ISSUE-058(president §4.3):Redis /readyz "仅告警不阻塞,避免雪崩" + +**coord 裁决**:以 ISSUE-058 为准(后裁决优先),Redis 对 push-gateway 为**软失败**。 + +**理由**: + +1. push-gateway 重启不解决 Redis 故障(Redis 是跨实例广播依赖,非本地状态) +2. Redis 故障时单实例仍能服务本地连接(仅跨实例广播失效) +3. 重启会丢失本地连接表,加剧雪崩风险 +4. ISSUE-055 的"必需依赖"列表应排除 push-gateway → Redis(Redis 故障不触发 Pod 重启) + +**修正**:004 §7.2 / coord-cross-review 中 ISSUE-055 必需依赖列表,push-gateway → Redis 标注为"软失败(ISSUE-058 裁决覆盖)"。 + +### 17.5 ai02 执行项 + +1. 修改源码鉴权头:`X-Internal-Key` → `X-Internal-Token`;`INTERNAL_API_KEY` → `INTERNAL_API_TOKEN`(ISSUE-002) +2. 02 文档 §3.1 补 Hub 启动重建机制(SADD + EXPIRE 60s + 清空旧 instanceID 成员,ISSUE-058 回写) +3. 02 文档 §6.7 /readyz Redis 检查改为软失败(返 200 + degraded,不返 503,ISSUE-006) +4. 02 文档 §5.4 添加"设计决策记录:gRPC vs HTTP 协议选型"章节,标注"coord 已采纳 P1"(ISSUE-056 回写) +5. 02 文档 §5.1 topic 已对齐 `edu.notification.requested`(ISSUE-053 回写确认) +6. 02 文档补 §14-§17 四章节(ADR/NFR/失败模式/容量估算,P5 批次 4 补全,ISSUE-007) +7. 02 文档补 `push_gateway_redis_set_rebuild_total` 指标 + 60s 不一致窗口文档说明(ISSUE-058 回写) +8. ISSUE-001/003/004/005 由 coord 修文档,ai02 确认 + +### 17.6 coord 执行项 + +1. 修正 contract.md §1.2:移除 `/sse` 端点(ISSUE-001) +2. 修正 matrix.md §5:移除 push-gateway SSE 行(ISSUE-001) +3. 修正 modules/push-gateway/README.md §7:50k → 10w+(ISSUE-003) +4. 修正 contract.md §1.2:鉴权头统一为 `X-Internal-Token`(ISSUE-002,contract v2 已部分对齐,确认最终) +5. 修正 004 §7.2 / coord-cross-review:ISSUE-055 必需依赖列表中 push-gateway → Redis 标注"软失败" +6. ISSUE-005 协调 ai10 确认 msg 审计表覆盖 push-gateway 推送结果字段 + +--- + +## §18 ARB-016:teacher-bff 模块 3 项 ISSUE 仲裁 + +### 18.1 背景 + +ai03 在 [teacher-bff_issue.md](./objections/teacher-bff_issue.md) 提请 3 项异议,涉及 admin 命名空间 P2 预留与 ARB-001 冲突、dashboard classes 数据来源与调用链冲突、GraphQL schema 文件命名不一致。coord 对照 [teacher-bff_contract.md](./contracts/teacher-bff_contract.md) v2 逐条仲裁。 + +### 18.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| 001 | admin 命名空间 P2 预留与 ARB-001 "P2 不包含" 冲突 | ✅ 以总裁裁决为准(president > coord),修正 ARB-001 §1.3 为 "P2 预留 admin schema 命名空间占位" | coord 修 ARB-001 | +| 002 | P2 dashboard classes 数据来源与调用链冲突 | ✅ 采纳 president §3.5:P2 classes 数据从 iam gRPC 获取(GetViewports/GetEffectiveAccess),不调 classes 服务 | coord 修 ARB-001 + ai03 修文档 | +| 003 | GraphQL schema 文件命名不一致 | ✅ 以总裁 §2.17 为准:统一为 `teacher-bff.schema.graphql` | coord 修 ARB-001 + ai03 创建文件 | + +### 18.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| -------------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | +| admin 命名空间 P2 | **P2 预留** schema 占位(声明 `admin` Query/Mutation 类型骨架,Resolver 返 null),P6 实现 | president §5.1 + §7.3(裁决优先级 president > coord) | +| P2 classes 数据来源 | **iam gRPC**(GetViewports/GetEffectiveAccess 返回关联班级),不调 classes 服务 | president §3.5 + B2 裁决(classes P2 无 gRPC,禁止 REST 过渡) | +| P3+ classes 数据来源 | 切换为 `core-edu.ClassService.GetClassesByTeacher` RPC(core-edu gRPC 50053 就绪后) | ARB-007 已补全 ClassService 4 RPC | +| schema 文件名 | `teacher-bff.schema.graphql`(统一 `.schema.graphql` 格式) | president §2.17 | +| 裁决优先级 | president > coord(ARB-001 修正为对齐总裁裁决) | 总裁裁决覆盖 coord 仲裁 | + +### 18.4 ISSUE-002 P2 dashboard 调用链修正 + +**原 ARB-001 §1.4 第 4 条**: + +> dashboard Resolver 并行调用 iam(3 RPC)+ classes(1 RPC),用 DataLoader 防御 N+1 + +**修正为**: + +> dashboard Resolver 并行调用 iam gRPC(GetUserInfo + GetViewports + GetEffectiveAccess),classes 列表从 iam 返回数据推导,P3 core-edu gRPC 就绪后切换为 `GetClassesByTeacher` RPC + +**理由**: + +1. classes P2 无 gRPC server(classes.proto 注释 "P3 起转 gRPC") +2. B2 裁决禁止 REST 过渡 +3. president §3.5 裁决 P2 班级列表数据来自 iam + +### 18.5 ai03 执行项 + +1. 创建 `packages/shared-ts/contracts/graphql/teacher-bff.schema.graphql`(文件名用 `.schema.graphql`,ISSUE-003) +2. schema 中预留 `admin` 命名空间占位(`admin.*` Query/Mutation 类型骨架,Resolver 返 null,ISSUE-001) +3. dashboard Resolver P2 实现:调 iam gRPC 获取 classes 数据(不调 classes 服务,ISSUE-002) +4. P3+ 切换 dashboard classes 数据源为 `core-edu.ClassService.GetClassesByTeacher` + +### 18.6 coord 执行项 + +1. 修正 ARB-001 §1.3:admin 命名空间改为 "P2 预留 schema 占位" +2. 修正 ARB-001 §1.3/§1.4:文件名改为 `teacher-bff.schema.graphql` +3. 修正 ARB-001 §1.4 第 4 条:dashboard classes 数据来源改为 iam gRPC(不调 classes 服务) +4. 修正 ARB-001 §1.2 GraphQL schema 代码块:DataScope 枚举去掉 DISTRICT 改 SUBJECT(ARB-003 ISSUE-004 已裁决,需同步 ARB-001) + +--- + +## §19 ARB-017:student-bff 模块 4 项新 ISSUE 仲裁 + +### 19.1 背景 + +ai04 在 [student-bff_issue.md](./objections/student-bff_issue.md) §1 提请 4 项新异议(STU-001~004),涉及 01 文档与 B1/B2/B5 裁决冲突、02 引用不存在的 004 章节、02 §8.3 八项已裁决决策未更新、GraphQL schema 第一版未起草。coord 对照 [student-bff_contract.md](./contracts/student-bff_contract.md) v2 逐条仲裁。 + +### 19.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------ | +| STU-001 | 01 文档与 B1/B2/B5 裁决冲突 | ✅ 与 ISSUE-028 合并处理:01 文档随 02 文档一并回写(删除 REST 端点清单、改 GraphQL、错误码改 BFF_STUDENT_) | ai04 | +| STU-002 | 02 引用不存在的 004 §11.4/§11.5 | ✅ coord 补全 004 §11.4(错误码前缀矩阵)+ §11.5(ActionState 信封规范)章节 | coord | +| STU-003 | 02 §8.3 八项已裁决决策未更新 | ✅ 02 文档回写时删除已裁决项的"待仲裁"标注,改为"已裁决(见 coord-final-decisions §2 BX)" | ai04 | +| STU-004 | GraphQL schema 第一版未起草 | ✅ ai04 在批次 1 等待期产出 schema 草案,存放 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql` | ai04 | + +### 19.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| -------------------- | ---------------------------------------------------------- | ------------------------------------------ | +| 01 文档回写范围 | 01 + 02 一并回写(01 不作为历史快照保留,必须对齐裁决) | ISSUE-028 + STU-001 合并 | +| 004 §11.4/§11.5 补全 | coord 补全错误码前缀矩阵 + ActionState 信封规范章节 | 004 作为架构设计意图唯一源,不应有断裂引用 | +| schema 仲裁时机 | ai04 批次 1 等待期起草 → coord 批次 2 启动前仲裁第一版 | president §2.2 | +| schema 文件名 | `student-bff.schema.graphql`(统一格式,对齐 teacher-bff) | president §2.17 | +| 02 §8.3 已裁决项 | B1/B2/B3/B4/B5/B6/B7/B8 全部已裁决,删除"待仲裁"标注 | coord-final-decisions §2 | + +### 19.4 ISSUE-028 回写范围确认 + +ai04 须在批次 2 启动前完成 02 文档回写(president §3.4 裁决),回写范围: + +1. **§4 API 设计**:21 个 REST 端点 → GraphQL Query/Mutation 清单 +2. **§9.2 演进路线**:删除"REST→GraphQL 演进"(违反"不分阶段"原则) +3. **§9.3 通信协议演进**:删除"HTTP→gRPC 演进"(违反 B2) +4. **§3.1 下游通信**:"HTTP fetch(当前阶段)" → "gRPC 调用下游(@grpc/grpc-js + @bufbuild/protobuf)" +5. **§3.3/§6 错误码**:`STUDENT_BFF_` → `BFF_STUDENT_` +6. **§8.3 未决决策**:8 项已裁决项标注"已裁决",仅保留 #7/#10/#11/#12 合理设计决策 +7. **01 文档**:同步回写(删除 REST 假设、对齐错误码前缀、删除已裁决项重复提请) + +### 19.5 ai04 执行项 + +1. 批次 1 等待期产出 `student-bff.schema.graphql` 草案(ISSUE-STU-004) +2. 批次 2 启动前回写 01 + 02 文档(ISSUE-028 + STU-001/003,范围见 §19.4) +3. 02 文档引用源修正:004 §11.4/§11.5 → coord 补全后引用(或暂引 coord-final-decisions G14/G8) +4. 实现 DownstreamClient 回写 teacher-bff(B8 裁决) + +### 19.6 coord 执行项 + +1. 补全 004 §11.4(错误码前缀矩阵:GW_/IAM_/CORE_EDU_/BFF_TEACHER_/BFF_STUDENT_/BFF_PARENT_/MSG_/PUSH_/DATA_ANA_/AI_)+ §11.5(ActionState 信封规范:`{success, data, error:{code,message,details,traceId}}` + degraded 放 error.details) +2. 批次 2 启动前仲裁 student-bff schema 第一版(president §2.2) + +--- + +## §20 ARB-018:parent-bff 模块 10 项 ISSUE 仲裁 + +### 20.1 背景 + +ai05 在 [parent-bff_issue.md](./objections/parent-bff_issue.md) 提请 10 项异议,涉及 01 文档未同步、ChildGuard TTL 不一致、契约引用错误、004 依赖图未同步、parent-portal REST vs GraphQL 跨模块冲突、proto 包名缺失、Notification.childId 缺失、ClassService 缺失、责任方过时、02 缺 ADR/NFR。coord 对照 [parent-bff_contract.md](./contracts/parent-bff_contract.md) 逐条仲裁。 + +### 20.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| 001 | 01 文档未同步 ai05 接手 + U3/U4/C1 仲裁 | ✅ 01 作为阶段 1 历史快照保留原样,以 02 为准(02 已执行 U3/U4/C1) | ai05 标注 01 为历史快照 | +| 002 | 02 文档 ChildGuard TTL 三处不一致(60s/30s) | ✅ 统一为 **30s**(§3.1.1 表格改为 30s,对齐 §9 #2 + §13 #7) | ai05 修文档勘误 | +| 003 | contract.md 引用 I3 → 应为 I6 | ✅ 修正为 I6 裁决(coord-final-decisions §1 I6:家长-学生关联) | ai05 修 contract 引用 | +| 004 | 004 §4 + matrix.md §1 未同步 C6 仲裁(缺 DataAna + Msg) | ✅ coord 补全 004 §4 服务依赖图 + matrix.md §1 依赖矩阵(加 PBFF → DataAna + PBFF → Msg) | coord | +| 005 | parent-portal 01 文档按 REST 消费 parent-bff(跨模块冲突) | ✅ coord 协调 ai15 将 parent-portal 01/02 改为 GraphQL 消费(`POST /api/v1/parent/v1/graphql`) | coord 协调 ai15 | +| 006 | proto 包名引用缺 next_edu_cloud 前缀 | ✅ 统一补 `next_edu_cloud.` 前缀(ARB-011 已裁决,ai05 修 02 §3.3 DTO 映射表) | ai05 | +| 007 | GraphQL Notification.childId 在 msg.proto 缺失 | ✅ coord 协调 ai10 在 msg.proto Notification message 补 `string child_id = 9;`(可选,非家长通知为空) | coord 协调 ai10 | +| 008 | core_edu.proto 缺 ClassService | ✅ coord 已在 ARB-007 补全 ClassService 4 RPC(GetClass/GetClassesByTeacher/BatchGetClasses/ListStudentsByClass);02 §7.1 状态改 "✅ 已有";Grade.score string→Float 转换规则补 §3.3 | ai05 修 02 §7.1 + §3.3 | +| 009 | ai-allocation iam 责任方 ai02 → ai06 | ✅ 修正为 ai06(iam 现归属 ai06,ai-allocation §3.2 确认) | ai05 修 01/02 引用 | +| 010 | 02 缺 ADR/NFR/容量规划/威胁建模 | ✅ P6 硬化阶段补全(当前不阻塞 P4 实施) | ai05 P6 补全 | + +### 20.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------- | +| 01 文档定位 | 历史快照保留原样,02 为权威源 | 避免回写成本;01 标注"阶段 1 历史快照,以 02 为准" | +| ChildGuard TTL | 30s + 主动失效(统一三处) | §9 #2 + §13 #7 已调整,§3.1.1 表格勘误 | +| 契约引用 | I6(非 I3),ISSUE-047 编号虚构,删除 | coord-final-decisions §1 I6 | +| C6 依赖图同步 | coord 补 004 §4 + matrix.md §1:PBFF → IAM + CoreEdu + DataAna + Msg | C6 仲裁 | +| parent-portal 消费方式 | GraphQL(`POST /api/v1/parent/v1/graphql`),非 REST | U3 仲裁 + parent-bff 02 §4.1 不实现 REST | +| proto 包名 | `next_edu_cloud..v1`(全引用补前缀) | ARB-011 已裁决 | +| msg.proto Notification.child_id | 补 `string child_id = 9;`(可选字段) | ISSUE-007 家长场景需按孩子过滤通知 | +| ClassService 归属 | core-edu(ARB-007 已补全 4 RPC) | coord 批次 0 补全 | +| Grade.score 类型转换 | BFF response-mapper 做 `Number.parseFloat(score)`,失败抛 BFF_PARENT_BAD_GATEWAY | ai05 建议 | +| iam 责任方 | ai06(非 ai02) | ai-allocation §3.2 | + +### 20.4 ISSUE-005 跨模块契约冲突处理 + +**冲突**:parent-portal(ai15)01 文档 §3.1 按 REST 消费 parent-bff,但 parent-bff 02 §4.1 明确"不实现 REST 业务端点"。 + +**coord 仲裁**:按 project_rules §14.4 跨模块变更顺序处理: + +1. parent-bff 契约已定义(GraphQL `POST /api/v1/parent/v1/graphql`) +2. coord 协调 ai15 将 parent-portal 01/02 文档的 parent-bff 消费契约从 REST 改为 GraphQL +3. 同步 C4 仲裁:iam 路径 `/iam/permissions/effective`(非 `/iam/effective-permissions`) +4. parent-portal §3.1 标注 "BFF 对接:parent-bff(ai05 设计)" + +### 20.5 ISSUE-007 msg.proto Notification 字段补全 + +coord 协调 ai10 在 msg.proto Notification message 补字段: + +```proto +message Notification { + string id = 1; + string user_id = 2; + string type = 3; + string title = 4; + string content = 5; + string channel = 6; + bool is_read = 7; + int64 created_at = 8; + string child_id = 9; // 新增:关联孩子 ID(可选,非家长通知为空) +} +``` + +> parent-bff GraphQL `Notification.childId` 映射自此字段。ai10 在 msg 02 文档同步补字段说明。 + +### 20.6 ISSUE-008 ClassService + Grade.score 处理 + +**ClassService**:coord 已在 ARB-007 补全 core_edu.proto ClassService 4 RPC(GetClass/GetClassesByTeacher/BatchGetClasses/ListStudentsByClass)。parent-bff 02 §7.1 状态从 "❌ 待补" 改为 "✅ 已有"。 + +**Grade.score 类型转换**:parent-bff 02 §3.3 DTO 映射表补转换规则说明: + +- `core_edu.v1.Grade.score`(string)→ GraphQL `Grade.score`(Float!) +- BFF response-mapper 执行 `Number.parseFloat(score)` +- 转换失败(NaN)抛 `BFF_PARENT_BAD_GATEWAY` + i18n key `error.bffParent.bad_gateway` + +### 20.7 ai05 执行项 + +1. 02 §3.1.1 ChildGuard TTL 改为 30s(ISSUE-002 勘误) +2. 02 §3.3 DTO 映射表补 `next_edu_cloud.` 前缀(ISSUE-006) +3. 02 §3.3 补 Grade.score string→Float 转换规则(ISSUE-008) +4. 02 §7.1 ClassService.GetClass 状态改 "✅ 已有"(ISSUE-008,ARB-007 已补全) +5. contract.md §2.1 引用 I3 → I6 修正(ISSUE-003) +6. 01 §7.1/§7.3 + 02 §7.3 #1 责任方 ai02 → ai06(ISSUE-009) +7. 01 文档头部标注"阶段 1 历史快照,以 02 为准"(ISSUE-001) +8. P6 硬化阶段补 02 ADR/NFR/容量规划/威胁建模(ISSUE-010,非阻塞) + +### 20.8 coord 执行项 + +1. 补全 004 §4 服务依赖图:加 `PBFF --> DataAna` + `PBFF --> Msg`(ISSUE-004,C6 仲裁同步) +2. 补全 matrix.md §1 服务依赖矩阵:加 `PBFF --> DataAna` + `PBFF --> Msg`(ISSUE-004) +3. 协调 ai10 在 msg.proto Notification 补 `child_id = 9` 字段(ISSUE-007) +4. 协调 ai15 将 parent-portal 01/02 改为 GraphQL 消费 parent-bff(ISSUE-005,跨模块变更) +5. 确认 ClassService 已在 ARB-007 补全(ISSUE-008 已覆盖) + +--- + +## §21 ARB-019:student-portal 模块 7 项 ISSUE 仲裁 + +### 21.1 背景 + +ai14 在 [student-portal_issue.md](./objections/student-portal_issue.md) 提请 7 项异议,涉及 GraphQL endpoint 路径、schema 文件存放位置、考试防作弊策略、主观题粘贴策略、附件上传协议、实时事件命名、DataScope L0 强制执行层。coord 对照 [student-portal_contract.md](./contracts/student-portal_contract.md) v2 + [student-bff_contract.md](./contracts/student-bff_contract.md) v2 + ARB-001/ARB-002/ARB-014 逐条仲裁。 + +### 21.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ------ | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| 014-01 | GraphQL endpoint 路径不一致 | ✅ 统一为 `POST /api/v1/student/graphql`(对齐 matrix.md §5 + ARB-014 方案 A 双 /v1 前缀) | ai14 修 contract.md §2.3 路径 + ai01 确认路由 | +| 014-02 | student-bff GraphQL schema 存放位置不一致 | ✅ 统一为 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql`(对齐 president §2.17 + ARB-016/017) | ai04 创建并维护 | +| 014-03 | 考试作答页全屏策略与防作弊检测边界 | ✅ P3 不强制全屏 + visibilitychange 仅记录 + 多标签警告不阻止 + 走 student-bff GraphQL mutation `recordExamViolation` | ai14 + ai04 | +| 014-04 | 主观题粘贴策略(防作弊 vs 学生体验) | ✅ 客观题禁止 + 主观题允许记录 + 作文题允许不记录;复用 recordExamViolation(type=PASTE) | ai14 + ai04 | +| 014-05 | 作业附件上传协议 | ✅ 方案 A:api-gateway REST `POST /api/v1/student/upload` → 对象存储 → GraphQL mutation 提交 URL | ai01 路由 + ai04 mutation 接受 attachmentUrls | +| 014-06 | 考试延长/题目重排等实时事件命名 | ✅ ai08 发布 ExamExtended + ExamForceSubmitted;题目重排 P3 不实现 P4 评估 | ai08 发布 + ai10 推送 | +| 014-07 | 学生端 DataScope L0 边界强制执行层 | ✅ 方案 A:student-bff Resolver 从 JWT x-user-id 提取 studentId,前端 query 不传 studentId | ai14 修 query 设计 + ai04 实现 | + +### 21.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ---------------- | ------------------------------------------------------------------------- | --------------------------------------------- | +| GraphQL endpoint | `POST /api/v1/student/graphql`(双 /v1 前缀,对齐 ARB-014 方案 A) | matrix.md §5 + ARB-014 §16.4 | +| schema 文件路径 | `packages/shared-ts/contracts/graphql/student-bff.schema.graphql` | president §2.17 + ARB-017 §19.3 | +| 全屏策略 | P3 推荐不强制(提示"建议全屏作答");P4 基于教师反馈评估升级 | 防止误伤合法场景 | +| 离开页面策略 | visibilitychange hidden 仅记录,累计 3 次警告教师端可见;不自动提交 | 误触发风险高 | +| 多标签检测 | BroadcastChannel 警告 + 记录,不阻止作答 | 合法场景如查词典 | +| 防作弊事件上报 | student-bff GraphQL mutation `recordExamViolation(examId, type, payload)` | GraphQL 统一入口;不走 push-gateway | +| 粘贴策略分层 | 客观题禁止 / 主观题允许记录 / 作文题允许不记录 | 防作弊与学生体验平衡 | +| 附件上传协议 | api-gateway REST + 对象存储 + GraphQL mutation 提交 URL | GraphQL 不适合大文件;REST + 对象存储业界通用 | +| 实时事件命名 | ExamExtended + ExamForceSubmitted(ai08 发布到 `edu.exam.events` topic) | 事件命名规范 `.` | +| 题目重排 | P3 不实现(题目顺序固定),P4 评估实时重排需求 | 降低 P3 复杂度 | +| DataScope L0 | student-bff Resolver 层从 JWT 强制过滤(方案 A),前端不传 studentId | 前端无法绕过;gRPC 层重新校验 | + +### 21.4 ISSUE-014-03 防作弊策略详情 + +**全屏 API**:P3 不强制(提示"建议全屏作答"),P4 评估是否升级为强制(基于教师反馈) + +**visibilitychange 策略**: + +- hidden 触发时仅记录(不自动提交) +- 累计 3 次警告后教师端可见(走 `recordExamViolation` mutation 上报) +- 避免误伤合法场景(如切到词典查询) + +**多标签检测**: + +- BroadcastChannel 检测到多标签时警告 + 记录 +- 不阻止作答(避免误伤) + +**事件上报链路**: + +``` +student-portal → POST /api/v1/student/graphql (recordExamViolation mutation) + → student-bff Resolver + → core-edu gRPC ExamService.RecordViolation(ai08 在 P3 补全 RPC) + → 落库 + 教师端可见 +``` + +### 21.5 ISSUE-014-05 附件上传协议详情 + +**方案 A(采纳)**: + +1. 前端:`POST /api/v1/student/upload`(multipart/form-data)→ api-gateway 鉴权 → 对象存储(MinIO/OSS)→ 返回 signed URL +2. 前端:GraphQL mutation `submitHomework(input: { homeworkId, answers, attachmentUrls: [String!] })` 提交作业 + 附件 URL +3. student-bff Resolver 接收 attachmentUrls 字段,透传给 core-edu + +**路由配置**: + +- api-gateway:`/api/v1/student/upload` → `student-bff:3009/upload`(或直接到对象存储代理) +- 由 ai01(api-gateway)确认路由配置 + 大小限制(建议 20MB)+ 类型白名单(图片/PDF/文档) + +### 21.6 ISSUE-014-06 实时事件命名详情 + +| 事件 | 发布方 | topic | 消费链路 | 阶段 | +| --------------------- | ---------------- | ----------------- | --------------------------------------------------- | ---- | +| ExamExtended | ai08(core-edu) | `edu.exam.events` | msg 消费 → push-gateway → student-portal 更新倒计时 | P3 | +| ExamForceSubmitted | ai08(core-edu) | `edu.exam.events` | msg 消费 → push-gateway → student-portal 触发提交 | P3 | +| ExamQuestionReordered | ai08(core-edu) | `edu.exam.events` | P3 不实现,P4 评估 | P4 | + +### 21.7 ISSUE-014-07 DataScope L0 强制执行详情 + +**方案 A(采纳)**:student-bff Resolver 层从 JWT 提取 studentId,强制过滤 + +**ai14 修改 02-architecture-design.md §4.2**: + +- 移除所有 query 的 `studentId` 参数(如 `myClasses(studentId: ID!)` → `myClasses`) +- 所有 query 改为无参或仅含分页/筛选参数 +- 前端不传 studentId(无法绕过 L0 边界) + +**ai04 实现**: + +- 所有 student-bff Query Resolver 从 JWT `x-user-id` 提取 studentId +- 强制过滤(`WHERE student_id = :jwtStudentId`) +- 不接受前端传入的 studentId 参数(GraphQL schema 层不暴露) + +### 21.8 ai14 执行项 + +1. 修 contract.md §2.3:统一路径为 `POST /api/v1/student/graphql`(ISSUE-014-01) +2. 02 §4.2 GraphQL query 定义:移除所有 `studentId` 参数(ISSUE-014-07) +3. 02 §14 防作弊策略:补全屏策略 + visibilitychange + 多标签 + 事件上报链路(ISSUE-014-03) +4. 02 §14 粘贴策略:补客观题/主观题/作文题分层(ISSUE-014-04) +5. 02 §16 实时事件:补 ExamExtended + ExamForceSubmitted 命名(ISSUE-014-06) +6. 02 §4.2 mutation 域:确认 recordExamViolation + recordPasteEvent 参数(依赖 ai04 补全) + +### 21.9 ai04 执行项 + +1. 创建 `packages/shared-ts/contracts/graphql/student-bff.schema.graphql`(ISSUE-014-02) +2. 所有 Query Resolver 从 JWT 提取 studentId,不接受前端参数(ISSUE-014-07) +3. P3 提供 `recordExamViolation(input: RecordExamViolationInput!): RecordExamViolationPayload!` mutation(ISSUE-014-03) +4. P3 提供 `recordPasteEvent` mutation 或复用 recordExamViolation(type=PASTE)(ISSUE-014-04) +5. submitHomework mutation 接受 `attachmentUrls: [String!]` 字段(ISSUE-014-05) + +### 21.10 ai01 执行项 + +1. 确认路由 `/api/v1/student/*` → `student-bff:3009/*`(ISSUE-014-01) +2. 确认 `/api/v1/student/upload` 路由配置(大小限制 + 类型白名单)(ISSUE-014-05) + +### 21.11 ai08 执行项 + +1. P3 发布 ExamExtended 事件到 `edu.exam.events` topic(ISSUE-014-06) +2. P3 发布 ExamForceSubmitted 事件到 `edu.exam.events` topic(ISSUE-014-06) +3. P3 补全 ExamService.RecordViolation RPC(防作弊违规落库)(ISSUE-014-03) + +### 21.12 coord 执行项 + +1. 确认 matrix.md §5 路径前缀对齐 `/api/v1/student/*`(ISSUE-014-01) +2. 协调 ai08 补全 ExamService.RecordViolation RPC(ISSUE-014-03) + +--- + +## §22 ARB-020:parent-portal 模块 10 项 ISSUE 仲裁 + +### 22.1 背景 + +ai15 在 [parent-portal_issue.md](./objections/parent-portal_issue.md) 提请 10 项异议,涉及 01/02 文档 REST 消费与 ARB-001 冲突、MF shared 配置缺 urql、switch-child 端点不一致、登录端点三方不一致、未决决策过时、contract §1.2 误标 HTTP 端点、MF 配置文件位置不一致、GraphQL 路径缺 v1、switchChild Mutation 未明确、iam GetChildrenByParent 缺失。coord 对照 [parent-portal_contract.md](./contracts/parent-portal_contract.md) + [parent-bff_contract.md](./contracts/parent-bff_contract.md) + ARB-001/ARB-002/ARB-018 逐条仲裁。 + +### 22.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------ | +| 001 | 01/02 文档 REST 消费 parent-bff 与 ARB-001 冲突 | ✅ 统一 GraphQL 消费(对齐 ARB-018 ISSUE-005 跨模块裁决) | ai15 修 01/02 | +| 002 | MF shared 配置缺 urql/graphql/@edu/* | ✅ 补全 7 项 singleton(react/react-dom/urql/graphql/@edu/ui-tokens/@edu/ui-components/@edu/hooks) | ai15 修 02 §1.2 | +| 003 | switch-child 端点在 01/02 不一致 | ✅ GraphQL Mutation `switchChild(childId: ID!): SwitchChildPayload!`(走 GraphQL,对齐 ISSUE-001) | ai15 修 01/02 + ai05 补 schema | +| 004 | 登录端点三方不一致 | ✅ 统一为 `POST /api/v1/iam/login`(对齐 matrix.md §5);登录走 REST,其余走 GraphQL | ai15 修 contract §2.3 | +| 005 | 02 §11.3 未决决策 #2 已由 ARB-001 裁决 | ✅ 移除未决项,改为"已裁决:见 ARB-001" | ai15 修 02 §11.3 | +| 006 | contract §1.2 误标前端路由为 HTTP 端点 | ✅ §1.2 改为"无(parent-portal 是前端 Remote,不对外提供 HTTP API)" | ai15 修 contract §1.2 | +| 007 | contract §1.6 MF 配置文件位置不一致 | ✅ 统一为 `apps/parent-portal/next.config.js`(对齐 02 + teacher-portal Shell) | ai15 修 contract §1.6 | +| 008 | contract §2.3 GraphQL 路径缺 v1 | ✅ 统一为 `POST /api/v1/parent/graphql`(对齐 matrix.md §5) | ai15 修 contract §2.3 + §2.4 | +| 009 | parent-bff GraphQL schema 是否含 switchChild | ✅ parent-bff 提供 `switchChild` Mutation(后端记录选中子女用于审计),同时前端 Zustand + localStorage 缓存 | ai05 补 schema + ai15 前端实现 | +| 010 | iam GetChildrenByParent 缺失(P0 阻塞) | ✅ ai06 P3 收尾前补全(I6 裁决);补全前用 mock(固定 2 个孩子 student-001 + student-002) | ai06 补全 + ai15 mock | + +### 22.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| ----------------------- | ----------------------------------------------------------------------- | ----------------------------- | +| parent-portal 消费方式 | GraphQL(`POST /api/v1/parent/graphql`),非 REST | ARB-001 + ARB-018 ISSUE-005 | +| MF shared | 7 项 singleton 全补齐(含 urql/graphql/@edu/*) | ARB-002 §2.2 | +| switchChild 归属 | parent-bff GraphQL Mutation(后端审计)+ 前端 Zustand 缓存 | 双层记录,避免状态不一致 | +| 登录端点 | `POST /api/v1/iam/login`(REST),登录前无 JWT 不能走 GraphQL | matrix.md §5 + ARB-014 方案 A | +| 前端路由分类 | 前端页面路由不进 contract §1.2 HTTP 端点表 | parent-portal 是前端 Remote | +| MF 配置文件 | `apps/parent-portal/next.config.js`(NextFederationPlugin) | 对齐 teacher-portal Shell | +| GraphQL 路径 | `POST /api/v1/parent/graphql`(双 /v1 前缀,对齐 ARB-014) | ARB-014 + matrix.md §5 | +| iam GetChildrenByParent | ai06 P3 收尾前补全(I6 裁决,P0 阻塞) | coord-final-decisions §1 I6 | +| mock 数据一致性 | parent-portal mock 与 parent-bff mock 一致(student-001 + student-002) | ChildGuard 越权校验依赖 | + +### 22.4 ISSUE-003 switchChild 端点裁决详情 + +**裁决**:switchChild 走 GraphQL Mutation(对齐 ISSUE-001 GraphQL 消费方式) + +**GraphQL schema**(ai05 补 parent-bff schema): + +```graphql +type SwitchChildPayload { + childId: ID! + childName: String! + selectedAt: DateTime! +} + +type Mutation { + switchChild(childId: ID!): SwitchChildPayload! +} +``` + +**后端职责**(parent-bff): + +- 记录选中子女用于审计(写 Redis `parent:selected:{parentId}` TTL 30s) +- ChildGuard 校验基于 iam.GetChildrenByParent 返回列表 + +**前端职责**(parent-portal): + +- Zustand store 缓存当前选中子女(`selectedChildId`) +- localStorage 持久化(避免刷新丢失) +- 切换子女时同时调 mutation(后端审计)+ 更新 Zustand + +### 22.5 ISSUE-009 switchChild Mutation 归属裁决 + +**裁决**:parent-bff 提供 `switchChild` Mutation(后端记录审计),同时前端 Zustand + localStorage 缓存 + +**理由**: + +1. 纯前端方案无法记录审计日志(谁在何时切换了哪个子女) +2. 后端记录用于异常行为检测(频繁切换可能暗示越权尝试) +3. 前端缓存提升用户体验(刷新不丢失选中状态) +4. 双层记录:后端审计 + 前端缓存,Redis TTL 30s 与 ChildGuard 对齐 + +### 22.6 ISSUE-010 iam GetChildrenByParent P0 阻塞处理 + +**裁决**:ai06 在 P3 收尾前补全(I6 裁决),补全前用 mock + +**mock 数据要求**: + +- 返回固定 2 个孩子:`student-001`(李同学)+ `student-002`(李妹妹) +- 与 parent-bff mock 数据一致(ChildGuard 校验依赖) +- parent-portal 启动时校验 myChildren 返回的 user_id 与下游查询的 student_id 一致性 + +**补全时间点**: + +- ai06 P3 收尾前(parent-portal P4 启动前) +- coord 在 workline.md §4 跟踪 ai06 进度 + +### 22.7 ai15 执行项 + +1. 01 §3.1 + 02 §4.1:REST → GraphQL 消费(ISSUE-001) +2. 02 §1.2 MF shared:补齐 7 项 singleton(ISSUE-002) +3. 01 §3.1 + 02 §2.2/§4.1:switch-child → GraphQL Mutation `switchChild`(ISSUE-003) +4. contract.md §2.3:登录端点改为 `POST /api/v1/iam/login`(ISSUE-004) +5. 02 §11.3 #2:移除未决项,标注"已裁决:见 ARB-001"(ISSUE-005) +6. contract.md §1.2:改为"无(前端 Remote,不对外提供 HTTP API)"(ISSUE-006) +7. contract.md §1.6:MF 配置文件改为 `apps/parent-portal/next.config.js`(ISSUE-007) +8. contract.md §2.3 + §2.4:GraphQL 路径改为 `POST /api/v1/parent/graphql`(ISSUE-008) +9. 前端实现 `useChildSwitcher` hook:调 mutation + Zustand + localStorage(ISSUE-009) +10. mock 数据与 parent-bff 一致(student-001 + student-002)(ISSUE-010) + +### 22.8 ai05 执行项 + +1. parent-bff GraphQL schema 补 `switchChild(childId: ID!): SwitchChildPayload!` Mutation(ISSUE-003/009) +2. switchChild Resolver:写 Redis `parent:selected:{parentId}` TTL 30s + 审计记录 + +### 22.9 ai06 执行项 + +1. P3 收尾前补全 iam GetChildrenByParent RPC + iam_student_guardians 表(ISSUE-010,I6 裁决) + +### 22.10 coord 执行项 + +1. 协调 ai05 补 switchChild Mutation schema(ISSUE-003/009) +2. 跟踪 ai06 GetChildrenByParent 补全进度(ISSUE-010,P0 阻塞) +3. 确认 matrix.md §5 路径前缀对齐 `/api/v1/parent/*`(ISSUE-008) + +--- + +## §23 ARB-021:admin-portal 模块 7 项 ISSUE 仲裁 + +### 23.1 背景 + +ai16 在 [admin-portal_issue.md](./objections/admin-portal_issue.md) 提请 7 项异议,涉及 01/02 文档归属错误(ai07→ai16)、端口错误(3003→4003)、通信协议与 ARB-001/002 不一致、遗漏审计日志与学校设置、teacher-bff 缺 admin 命名空间 schema、推送策略与同类 portal 不一致、matrix.md §4 将 admin-portal 列为 Kafka 直消费方。coord 对照 [admin-portal_contract.md](./contracts/admin-portal_contract.md) + ARB-001/ARB-002 逐条仲裁。 + +### 23.2 仲裁结论 + +| ISSUE | 标题 | 仲裁结论 | 执行方 | +| ----- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| 001 | 01/02 模块文档归属错误(ai07 → ai16) | ✅ 修正为 ai16;ai07 产出视为历史草稿 | ai16 修 01/02 头部 | +| 002 | 01/02 端口错误(3003 → 4003) | ✅ 确认 admin-portal 端口 4003;同步更新 full-stack-runbook §2.1 补齐 4 个 portal 4000 段 | ai16 修 01/02 + coord 修 runbook | +| 003 | 01/02 通信协议与 ARB-001/002 不一致(REST → GraphQL) | ✅ 统一 GraphQL(`POST /api/admin/graphql` → teacher-bff admin 命名空间);删除 useApi/ApiClient | ai16 修 01/02 全文 | +| 004 | 01/02 遗漏审计日志与学校设置 | ✅ 视口补齐为 11 个:dashboard/users/roles/permissions/viewports/organization/classes/teachers/students/audit-logs/system | ai16 修 01/02 | +| 005 | teacher-bff 缺 admin 命名空间 GraphQL schema | ✅ ai03 在 P6 启动前补齐 teacher-bff admin 命名空间 schema(参照 contract §2.4) | ai03 补 schema | +| 006 | 01/02 推送策略与同类 portal 不一致(轮询 → WebSocket) | ✅ admin-portal 接入 push-gateway WebSocket(审计告警/异常登录/系统异常);保留轮询仅用于监控指标 | ai16 修 01/02 | +| 007 | matrix.md §4 将 admin-portal 列为 Kafka 直消费方 | ✅ 修正为 teacher-bff(admin-portal 经 teacher-bff 间接消费,链路:iam → Kafka → teacher-bff → GraphQL → admin-portal) | coord 修 matrix.md §4 | + +### 23.3 关键裁决 + +| 裁决点 | 结论 | 依据 | +| --------------------- | ------------------------------------------------------------------------- | ----------------------------------- | +| 模块归属 | ai16(非 ai07);ai07 产出视为历史草稿 | ai-allocation §5 | +| 端口 | 4003(非 3003,3003 与 teacher-bff 冲突) | matrix.md §1 + ai-allocation §5 | +| 通信协议 | GraphQL(`POST /api/admin/graphql`),非 REST | ARB-001 + ARB-002 | +| GraphQL 端点路径 | `POST /api/admin/graphql`(对齐 contract §2.3,非双 /v1 前缀) | admin 走 teacher-bff,非独立 BFF | +| 视口数量 | 11 个(补齐审计日志 + 学校设置) | ai-allocation §5 + contract §1.2 | +| admin 命名空间 schema | ai03 在 P6 启动前补齐 teacher-bff admin schema | ARB-001 §1.3 P6 新增 admin 命名空间 | +| 推送策略 | WebSocket(审计告警/异常登录/系统异常)+ 轮询(监控指标 60s / 统计 5min) | 同类 portal 一致 + 低频数据轮询合理 | +| Kafka 消费链路 | iam → Kafka → teacher-bff 消费 → GraphQL auditLogs Query → admin-portal | 前端不直连 Kafka,经 BFF 聚合 | + +### 23.4 ISSUE-005 teacher-bff admin 命名空间 schema 补全 + +**裁决**:ai03 在 P6 启动前补齐 teacher-bff admin 命名空间 GraphQL schema + +**补全范围**(参照 admin-portal contract §2.4): + +```graphql +# teacher-bff.graphql admin 命名空间部分(ai03 在 P6 补齐) + +type Query { + # 用户管理 + adminUsers(filter: AdminUserFilter, page: PageInput): AdminUserConnection! + adminUser(id: ID!): AdminUser + + # 角色权限 + adminRoles: [AdminRole!]! + adminPermissions: [AdminPermissionGroup!]! + + # 视口配置 + adminViewports(scope: ViewportScope): [ViewportItem!]! + + # 组织管理 + adminOrganization(parentId: ID): [OrganizationNode!]! + + # 全局管理 + adminClasses(filter: ClassFilter): [Class!]! + adminTeachers(filter: TeacherFilter): [Teacher!]! + adminStudents(filter: StudentFilter): [Student!]! + + # 审计日志 + auditLogs(filter: AuditLogFilter, page: PageInput): AuditLogConnection! + + # 仪表盘 + adminDashboard: AdminDashboard! +} + +type Mutation { + createUser(input: CreateUserInput!): User! + updateUser(id: ID!, input: UpdateUserInput!): User! + deleteUser(id: ID!): Boolean! + toggleUserStatus(id: ID!): User! + + createRole(input: CreateRoleInput!): AdminRole! + updateRolePermissions(id: ID!, permissions: [String!]!): AdminRole! + + updateViewport(key: String!, input: UpdateViewportInput!): ViewportItem! +} +``` + +**存放位置**:`packages/shared-ts/contracts/graphql/teacher-bff.graphql`(admin 命名空间部分,对齐 ARB-001 §1.3 集中管理) + +**就绪信号**: + +- ai03 P6 启动前完成 +- 作为 admin-portal P6 的前置就绪信号 +- 更新 matrix.md §3 teacher-bff 行的 schema 文件状态 + +### 23.5 ISSUE-006 推送策略裁决详情 + +**裁决**:admin-portal 接入 push-gateway WebSocket + 保留轮询低频数据 + +**WebSocket 消费场景**: + +- 审计告警(异常权限变更、敏感操作) +- 异常登录(非工作时间登录、异地登录) +- 系统异常(服务健康检查异常、资源告警) + +**轮询保留场景**: + +- 监控指标(60s 间隔,Prometheus 数据) +- 统计数据(5min 间隔,全校统计) + +**实现**: + +- `ws://push-gateway:8081/ws`(与同类 portal 一致) +- JWT 鉴权 +- 事件类型:审计告警 / 异常登录 / 系统异常 + +### 23.6 ISSUE-007 Kafka 消费链路修正 + +**裁决**:matrix.md §4 该行消费方更正为 teacher-bff(admin-portal 经 teacher-bff 间接消费) + +**实际链路**: + +``` +iam → Kafka edu.iam.audit.created → teacher-bff 消费 + ↓ + GraphQL auditLogs Query + ↓ + admin-portal 消费 +``` + +**修正范围**: + +- matrix.md §4 第 111 行:`admin-portal` → `teacher-bff` +- 避免误导架构分层(前端不直连 Kafka) + +### 23.7 GraphQL 端点路径说明 + +**admin-portal 路径**:`POST /api/admin/graphql`(非双 /v1 前缀) + +**与 ARB-014 方案 A 的关系**: + +- ARB-014 方案 A 适用于独立服务(iam/core-edu 等):`/api/v1//v1/*` +- admin-portal 走 teacher-bff admin 命名空间:`POST /api/admin/graphql`(teacher-bff 已有 `/api/v1/teacher/*` 前缀,admin 命名空间作为 teacher-bff 子路径) +- 由 ai01 确认 api-gateway 路由:`/api/admin/*` → `teacher-bff:3003/admin/*` + +### 23.8 ai16 执行项 + +1. 01/02 头部:ai07 → ai16;分支名更正(ISSUE-001) +2. 01 §1 + 02 §12.1:端口 3003 → 4003(ISSUE-002) +3. 01 §3.1 + 02 §1/§4:REST → GraphQL(useApi → useGraphQLClient)(ISSUE-003) +4. 01 §2.1/§L1 导航/§L2 路由表:补齐 11 个视口(含 audit-logs + system)(ISSUE-004) +5. 01 §3.3 + 02 §5:推送策略改为 WebSocket + 轮询低频数据(ISSUE-006) +6. 02 §11.3:移除已裁决的"GraphQL vs REST"未决项(ISSUE-003) + +### 23.9 ai03 执行项 + +1. P6 启动前补齐 teacher-bff admin 命名空间 schema(参照 §23.4 清单)(ISSUE-005) +2. schema 存放 `packages/shared-ts/contracts/graphql/teacher-bff.graphql` admin 部分 + +### 23.10 coord 执行项 + +1. 修正 matrix.md §4 第 111 行:`admin-portal` → `teacher-bff`(ISSUE-007) +2. 同步 full-stack-runbook §2.1:补齐 4 个 portal 4000 段端口(ISSUE-002) +3. 确认 ai03 admin 命名空间 schema 作为 admin-portal P6 前置就绪信号(ISSUE-005) +4. 协调 ai01 确认 `/api/admin/*` → `teacher-bff:3003/admin/*` 路由配置 + +--- + +## §8 仲裁流程说明 + +### 8.1 提请异议 各 AI 在自己模块的 `objections/[模块名]_issue.md` 中追加异议条目,格式: @@ -240,7 +1582,7 @@ shared: { - **状态**:待 coord 仲裁 / 已裁决(见 coord.md §X) ``` -### 3.2 coord 仲裁 +### 8.2 coord 仲裁 coord 审阅各 `objections/[模块名]_issue.md` 后,在本文件(coord.md)追加仲裁章节: @@ -256,12 +1598,12 @@ coord 审阅各 `objections/[模块名]_issue.md` 后,在本文件(coord.md ### N.4 相关 AI 执行项 ``` -### 3.3 状态同步 +### 8.3 状态同步 - 仲裁完成后,coord 在对应 `objections/[模块名]_issue.md` 中更新状态为"已裁决(见 coord.md §N)" - coord 在 [workline.md](./workline.md) §8 就绪信号跟踪表中更新进度 -### 3.4 目录结构 +### 8.4 目录结构 ``` issues/ @@ -279,7 +1621,7 @@ issues/ --- -## §4 历史仲裁(已迁移) +## §9 历史仲裁(已迁移) 以下仲裁已在此前完成,原始记录见 [coord-final-decisions.md](../coord-final-decisions.md) 和 [president-final-rulings.md](../president-final-rulings.md): diff --git a/docs/architecture/issues/matrix.md b/docs/architecture/issues/matrix.md index ae40c5c..a33a5eb 100644 --- a/docs/architecture/issues/matrix.md +++ b/docs/architecture/issues/matrix.md @@ -63,6 +63,8 @@ graph TB PBFF --> IAM PBFF --> CORE + PBFF --> DATA + PBFF --> MSG PGW --> MSG @@ -81,14 +83,14 @@ graph TB ## §2 gRPC 接口提供方矩阵 -| 提供方 | gRPC 端口 | Service | RPC 数 | 消费方 | 就绪信号 | 状态 | -| --------------- | --------- | --------------------------------------------------------------------------------- | ------ | ---------------------------------------------------- | ----------------------------- | ---- | -| iam (ai06) | 50052 | IamService | 12 | teacher-bff / student-bff / parent-bff / api-gateway | HealthService.Check = SERVING | ⏳ | -| core-edu (ai08) | 50053 | ClassService + ExamService + HomeworkService + GradeService + AttendanceService | 22 | teacher-bff / student-bff / parent-bff | HealthService.Check = SERVING | ⏳ | -| content (ai09) | 50054 | TextbookService + ChapterService + KnowledgeGraphService + QuestionService | 18 | teacher-bff / student-bff / ai | HealthService.Check = SERVING | ⏳ | -| data-ana (ai11) | 50055 | AnalyticsService | 12 | teacher-bff / student-bff / parent-bff / ai | HealthService.Check = SERVING | ⏳ | -| msg (ai10) | 50056 | NotificationService + NotificationPreferenceService + NotificationTemplateService | 13 | teacher-bff / push-gateway | HealthService.Check = SERVING | ⏳ | -| ai (ai12) | 50057 | AiService | 6 | teacher-bff | HealthService.Check = SERVING | ⏳ | +| 提供方 | gRPC 端口 | Service | RPC 数 | 消费方 | 就绪信号 | 状态 | +| --------------- | --------- | --------------------------------------------------------------------------------- | ------ | ------------------------------------------- | ----------------------------- | ---- | +| iam (ai06) | 50052 | IamService | 12 | teacher-bff / student-bff / parent-bff | HealthService.Check = SERVING | ⏳ | +| core-edu (ai08) | 50053 | ClassService + ExamService + HomeworkService + GradeService + AttendanceService | 22 | teacher-bff / student-bff / parent-bff | HealthService.Check = SERVING | ⏳ | +| content (ai09) | 50054 | TextbookService + ChapterService + KnowledgeGraphService + QuestionService | 18 | teacher-bff / student-bff / ai | HealthService.Check = SERVING | ⏳ | +| data-ana (ai11) | 50055 | AnalyticsService | 12 | teacher-bff / student-bff / parent-bff / ai | HealthService.Check = SERVING | ⏳ | +| msg (ai10) | 50056 | NotificationService + NotificationPreferenceService + NotificationTemplateService | 13 | teacher-bff / push-gateway | HealthService.Check = SERVING | ⏳ | +| ai (ai12) | 50057 | AiService | 6 | teacher-bff | HealthService.Check = SERVING | ⏳ | --- @@ -108,7 +110,7 @@ graph TB | --------------- | --------------------------- | ----------------------------------------------------- | --------------------------------------------------------- | ------- | ---- | | iam (ai06) | edu.iam.user.events | UserEvent(created/updated/deleted/role_changed) | core-edu / msg / push-gateway / teacher-bff / student-bff | ✅ | ⏳ | | iam (ai06) | edu.iam.role.events | RoleEvent(created/updated) | core-edu / msg | ✅ | ⏳ | -| iam (ai06) | edu.iam.audit.created | AuditEvent(create/update/delete/login/logout) | admin-portal | ✅ | ⏳ | +| iam (ai06) | edu.iam.audit.created | AuditEvent(create/update/delete/login/logout) | teacher-bff | ✅ | ⏳ | | core-edu (ai08) | edu.exam.events | ExamEvent(created/updated/deleted) | msg / data-ana / push-gateway | ✅ | ⏳ | | core-edu (ai08) | edu.homework.events | HomeworkEvent(assigned/submitted/graded) | msg / data-ana / push-gateway | ✅ | ⏳ | | core-edu (ai08) | edu.grade.events | GradeEvent(recorded/updated) | msg / push-gateway / parent-bff | ✅ | ⏳ | @@ -123,36 +125,37 @@ graph TB ## §5 HTTP 接口矩阵(非 gRPC / 非 GraphQL) -| 提供方 | Method | Path | 用途 | 消费方 | 认证 | 状态 | -| ------------------- | ------ | ----------------- | -------------------- | ----------------------------------------------- | -------------- | ---- | -| api-gateway (ai01) | * | /api/v1/teacher/* | 反向代理 teacher-bff | teacher-portal | JWT | ⏳ | -| api-gateway (ai01) | * | /api/v1/student/* | 反向代理 student-bff | student-portal | JWT | ⏳ | -| api-gateway (ai01) | * | /api/v1/parent/* | 反向代理 parent-bff | parent-portal | JWT | ⏳ | -| api-gateway (ai01) | * | /api/v1/iam/* | 反向代理 iam | (内部) | JWT | ⏳ | -| push-gateway (ai02) | WS | /ws | WebSocket 推送通道 | teacher-portal / student-portal / parent-portal | JWT | ⏳ | -| push-gateway (ai02) | SSE | /sse | SSE 推送通道 | teacher-portal | JWT | ⏳ | -| push-gateway (ai02) | POST | /internal/push | 内部推送入口 | msg | X-Internal-Key | ⏳ | -| iam (ai06) | GET | /healthz | 存活检查 | k8s / 监控 | 无 | ⏳ | -| iam (ai06) | GET | /readyz | 就绪检查(5 依赖) | k8s / 监控 | 无 | ⏳ | -| 所有服务 | GET | /metrics | Prometheus 指标 | Prometheus | 无 | ⏳ | +| 提供方 | Method | Path | 用途 | 消费方 | 认证 | 状态 | +| ------------------- | ------ | ----------------- | -------------------- | -------------------------------------------------------------- | ---------------- | ---- | +| api-gateway (ai01) | * | /api/v1/teacher/* | 反向代理 teacher-bff | teacher-portal | JWT | ⏳ | +| api-gateway (ai01) | * | /api/v1/student/* | 反向代理 student-bff | student-portal | JWT | ⏳ | +| api-gateway (ai01) | * | /api/v1/parent/* | 反向代理 parent-bff | parent-portal | JWT | ⏳ | +| api-gateway (ai01) | * | /api/v1/iam/* | 反向代理 iam | (内部) | JWT | ⏳ | +| push-gateway (ai02) | WS | /ws | WebSocket 推送通道 | teacher-portal / student-portal / parent-portal / admin-portal | JWT | ⏳ | +| push-gateway (ai02) | POST | /internal/push | 内部推送入口 | msg | X-Internal-Token | ⏳ | +| iam (ai06) | GET | /healthz | 存活检查 | k8s / 监控 | 无 | ⏳ | +| iam (ai06) | GET | /readyz | 就绪检查(5 依赖) | k8s / 监控 | 无 | ⏳ | +| 所有服务 | GET | /metrics | Prometheus 指标 | Prometheus | 无 | ⏳ | --- ## §6 错误码前缀矩阵 -| 模块 | 前缀 | 示例 | -| ------------ | --------- | -------------------------------------------------- | -| api-gateway | GW_ | GW_UNAUTHORIZED / GW_RATE_LIMITED | -| push-gateway | PUSH_ | PUSH_DEVICE_NOT_FOUND / PUSH_CHANNEL_FAILED | -| teacher-bff | BFF_XXX_ | BFF_TEACHER_UNAUTHORIZED / BFF_TEACHER_BAD_GATEWAY | -| student-bff | BFF_XXX_ | BFF_STUDENT_UNAUTHORIZED | -| parent-bff | BFF_XXX_ | BFF_PARENT_CHILD_NOT_BOUND | -| iam | IAM_ | IAM_USER_NOT_FOUND / IAM_INVALID_CREDENTIALS | -| core-edu | CORE_EDU_ | CORE_EDU_EXAM_NOT_FOUND | -| content | CONTENT_ | CONTENT_QUESTION_NOT_FOUND | -| msg | MSG_ | MSG_NOTIFICATION_NOT_FOUND | -| data-ana | DATA_ANA_ | DATA_ANA_DASHBOARD_UNAVAILABLE | -| ai | AI_ | AI_GENERATION_FAILED / AI_QUOTA_EXCEEDED | +> 详见 [004 §11.4](../004_architecture_impact_map.md) 错误码前缀矩阵 + i18n key 映射规则 + +| 模块 | 前缀 | i18n key 模式 | 示例 | +| ------------ | ------------ | ------------------------------- | -------------------------------------------------- | +| api-gateway | GW_ | error.gateway.\ | GW_UNAUTHORIZED / GW_RATE_LIMITED | +| push-gateway | PUSH_ | error.push.\ | PUSH_DEVICE_NOT_FOUND / PUSH_CHANNEL_FAILED | +| teacher-bff | BFF_TEACHER_ | error.bffTeacher.\ | BFF_TEACHER_UNAUTHORIZED / BFF_TEACHER_BAD_GATEWAY | +| student-bff | BFF_STUDENT_ | error.bffStudent.\ | BFF_STUDENT_UNAUTHORIZED | +| parent-bff | BFF_PARENT_ | error.bffParent.\ | BFF_PARENT_CHILD_NOT_BOUND | +| iam | IAM_ | error.iam.\ | IAM_USER_NOT_FOUND / IAM_INVALID_CREDENTIALS | +| core-edu | CORE_EDU_ | error.core_edu.\ | CORE_EDU_EXAM_NOT_FOUND | +| content | CONTENT_ | error.content.\ | CONTENT_QUESTION_NOT_FOUND | +| msg | MSG_ | error.msg.\ | MSG_NOTIFICATION_NOT_FOUND | +| data-ana | DATA_ANA_ | error.data_ana.\ | DATA_ANA_DASHBOARD_UNAVAILABLE | +| ai | AI_ | error.ai.\ | AI_GENERATION_FAILED / AI_QUOTA_EXCEEDED | --- diff --git a/docs/architecture/issues/objections/admin-portal_issue.md b/docs/architecture/issues/objections/admin-portal_issue.md index 4178242..1389141 100644 --- a/docs/architecture/issues/objections/admin-portal_issue.md +++ b/docs/architecture/issues/objections/admin-portal_issue.md @@ -29,10 +29,10 @@ ARB-001/ARB-002 均未明确仲裁以下三项,而它们直接影响 admin-portal 实现,建议 coord 补充裁决: -| 待裁决项 | 当前依据 | 影响 | -| -------- | -------- | ---- | -| admin-portal 端口 | matrix.md / ai-allocation.md = 4003 | 01/02 文档误用 3003(与 teacher-bff 冲突) | -| admin-portal 是否消费 push-gateway WebSocket | 同类 portal(parent-portal)契约 = 消费 | 01/02 文档误声明"不消费推送" | +| 待裁决项 | 当前依据 | 影响 | +| --------------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------- | +| admin-portal 端口 | matrix.md / ai-allocation.md = 4003 | 01/02 文档误用 3003(与 teacher-bff 冲突) | +| admin-portal 是否消费 push-gateway WebSocket | 同类 portal(parent-portal)契约 = 消费 | 01/02 文档误声明"不消费推送" | | 审计日志消费机制(iam Kafka → teacher-bff → GraphQL auditLogs) | matrix.md §4 列 admin-portal 为 edu.iam.audit.created 消费方 | 前端不直连 Kafka,需经 teacher-bff 聚合,matrix.md 表述不精确 | --- @@ -106,4 +106,16 @@ ARB-001/ARB-002 均未明确仲裁以下三项,而它们直接影响 admin-por ## §1 历史问题 -(暂无已裁决问题) +### 1.1 仲裁结论汇总(coord 2026-07-10) + +> 详见 [coord.md §23 ARB-021](../coord.md) 完整仲裁章节 + +| ISSUE | 标题 | 仲裁结论摘要 | 执行方 | +| ----- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| 001 | 01/02 模块文档归属错误(ai07 → ai16) | ✅ 修正为 ai16;ai07 产出视为历史草稿 | ai16 修 01/02 头部 | +| 002 | 01/02 端口错误(3003 → 4003) | ✅ 确认 admin-portal 端口 4003;同步更新 full-stack-runbook §2.1 补齐 4 个 portal 4000 段 | ai16 修 01/02 + coord 修 runbook | +| 003 | 01/02 通信协议与 ARB-001/002 不一致(REST → GraphQL) | ✅ 统一 GraphQL(`POST /api/admin/graphql` → teacher-bff admin 命名空间);删除 useApi/ApiClient | ai16 修 01/02 全文 | +| 004 | 01/02 遗漏审计日志与学校设置 | ✅ 视口补齐为 11 个:dashboard/users/roles/permissions/viewports/organization/classes/teachers/students/audit-logs/system | ai16 修 01/02 | +| 005 | teacher-bff 缺 admin 命名空间 GraphQL schema | ✅ ai03 在 P6 启动前补齐 teacher-bff admin 命名空间 schema(参照 contract §2.4) | ai03 补 schema | +| 006 | 01/02 推送策略与同类 portal 不一致(轮询 → WebSocket) | ✅ admin-portal 接入 push-gateway WebSocket(审计告警/异常登录/系统异常);保留轮询仅用于监控指标 | ai16 修 01/02 | +| 007 | matrix.md §4 将 admin-portal 列为 Kafka 直消费方 | ✅ 修正为 teacher-bff(admin-portal 经 teacher-bff 间接消费,链路:iam → Kafka → teacher-bff → GraphQL → admin-portal) | coord 修 matrix.md §4 | diff --git a/docs/architecture/issues/objections/ai_issue.md b/docs/architecture/issues/objections/ai_issue.md index fd39304..3fced8a 100644 --- a/docs/architecture/issues/objections/ai_issue.md +++ b/docs/architecture/issues/objections/ai_issue.md @@ -10,15 +10,15 @@ > 任务要求:对已有的仲裁进行核查。coord.md 当前仅含 ARB-001 / ARB-002;另在 [port-allocation.md](../../../../infra/port-allocation.md) §7、coord-final-decisions.md、president-final-rulings.md 中存在涉及 ai 的历史仲裁。逐项核查如下。 -| 仲裁编号 / 来源 | 主题 | 涉及 ai? | 核查结论 | 状态 | -| -------------------------- | ----------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | -| ARB-001(coord.md §1) | teacher-bff GraphQL schema 第一版 | ❌ 否 | 与 ai 无关,不适用。 | — | -| ARB-002(coord.md §2) | MF Shell 暴露清单 | ❌ 否 | 与 ai 无关,不适用。 | — | -| port-allocation.md §7 | 50058 让给 ai(push-gateway 豁免 gRPC) | ✅ 是 | **仲裁有效**:port-allocation.md §3/§5 已登记 ai = HTTP 3008 / gRPC 50058。ai 的 01/02 文档已按 50058 设计,一致。**但 [matrix.md](../matrix.md) §2 gRPC 接口提供方矩阵、§8 就绪信号跟踪表仍写 50057,未同步** → 提请 coord 同步(见 ISSUE-01) | ⚠️ 仲裁有效但未同步 | -| coord-final-decisions §1-§4 | iam P2 契约 / BFF 设计 / Gateway / content | ❌ 否 | 历史仲裁,与 ai 无直接约束。ai 仅消费 content/data-ana gRPC,需待其就绪。 | — | -| president-final-rulings §2.3 | Temporal 用于 AI 编排 | ✅ 是 | **部分仲裁**:004 §2.3 列出 Temporal。ai12 已在 02-architecture-design.md §8.3 建议简单 4 步工作流用 BackgroundTasks + Redis,长运行评估 Temporal。需 coord 在 P6 决策点确认(见 ISSUE-06)。 | ⚠️ 部分仲裁 | -| 01/02 文档引用"004 §7.2 已确认 topic edu.insight.ai.usage" | ai 用量事件 topic | ✅ 是 | **核查不通过**:grep 004 全文未发现 `ai.usage` / `insight.ai` / `AIUsage` 任何字样,004 §7.2 并未确认 ai 的 topic。该引用无法证实,topic 命名实际处于三义未决状态(见 ISSUE-02)。 | ❌ 引用失实 | -| 01/02 文档引用"004 §1.2 端口矩阵 HTTP+10050 规则" | ai gRPC 端口推导规则 | ✅ 是 | **核查不通过**:004 全文无端口矩阵;`HTTP+10050` 公式不成立(3008+10050=13058≠50058)。端口唯一源是 port-allocation.md(顺序分配),ai=50058。ai 文档应改引 port-allocation.md。 | ❌ 引用失实 | +| 仲裁编号 / 来源 | 主题 | 涉及 ai? | 核查结论 | 状态 | +| ---------------------------------------------------------- | ------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| ARB-001(coord.md §1) | teacher-bff GraphQL schema 第一版 | ❌ 否 | 与 ai 无关,不适用。 | — | +| ARB-002(coord.md §2) | MF Shell 暴露清单 | ❌ 否 | 与 ai 无关,不适用。 | — | +| port-allocation.md §7 | 50058 让给 ai(push-gateway 豁免 gRPC) | ✅ 是 | **仲裁有效**:port-allocation.md §3/§5 已登记 ai = HTTP 3008 / gRPC 50058。ai 的 01/02 文档已按 50058 设计,一致。**但 [matrix.md](../matrix.md) §2 gRPC 接口提供方矩阵、§8 就绪信号跟踪表仍写 50057,未同步** → 提请 coord 同步(见 ISSUE-01) | ⚠️ 仲裁有效但未同步 | +| coord-final-decisions §1-§4 | iam P2 契约 / BFF 设计 / Gateway / content | ❌ 否 | 历史仲裁,与 ai 无直接约束。ai 仅消费 content/data-ana gRPC,需待其就绪。 | — | +| president-final-rulings §2.3 | Temporal 用于 AI 编排 | ✅ 是 | **部分仲裁**:004 §2.3 列出 Temporal。ai12 已在 02-architecture-design.md §8.3 建议简单 4 步工作流用 BackgroundTasks + Redis,长运行评估 Temporal。需 coord 在 P6 决策点确认(见 ISSUE-06)。 | ⚠️ 部分仲裁 | +| 01/02 文档引用"004 §7.2 已确认 topic edu.insight.ai.usage" | ai 用量事件 topic | ✅ 是 | **核查不通过**:grep 004 全文未发现 `ai.usage` / `insight.ai` / `AIUsage` 任何字样,004 §7.2 并未确认 ai 的 topic。该引用无法证实,topic 命名实际处于三义未决状态(见 ISSUE-02)。 | ❌ 引用失实 | +| 01/02 文档引用"004 §1.2 端口矩阵 HTTP+10050 规则" | ai gRPC 端口推导规则 | ✅ 是 | **核查不通过**:004 全文无端口矩阵;`HTTP+10050` 公式不成立(3008+10050=13058≠50058)。端口唯一源是 port-allocation.md(顺序分配),ai=50058。ai 文档应改引 port-allocation.md。 | ❌ 引用失实 | **核查总结**:coord.md 现有 2 项仲裁(ARB-001/002)均与 ai 无关;涉及 ai 的真实仲裁在 port-allocation.md(50058 已定)与 004 §2.3(Temporal 部分仲裁)。ai 文档对"004 §7.2/§1.2"的两处引用失实,需修正引用源。 @@ -44,8 +44,9 @@ - 01-understanding.md §3.2 / 02-architecture-design.md §5.2:`edu.insight.ai.usage`(自称"004 §7.2 已确认",但 004 中不存在) - matrix.md §4 Kafka 事件发布方矩阵:`edu.ai.usage`,事件 `AIUsageEvent` - contracts/ai_contract.md §1.4:`edu.ai.usage.events` - + events.proto 现状无 `AIUsageEvent` message,004 无 topic 记录。三义导致 data-ana 消费方无法对齐。 + - **建议方案**:ai12 建议采用 `edu.ai.usage`(与 matrix.md 一致,最简短,符合 `edu..` 简洁约定)。请 coord 裁定并在 004 §7.2 补登 + events.proto 补 `AIUsageEvent` message(schema 见 02-architecture-design.md §3.3)。ai12 据裁决同步 01/02 文档与 contract。 - **状态**:待 coord 仲裁 @@ -58,8 +59,9 @@ - 新增 RPC:`GenerateLessonPlan`、`StreamGenerateQuestion`(ai-allocation §5 "题目逐字生成") - `GenerateQuestionRequest` 扩展字段:grade / knowledge_point_ids / question_type / count - `ChatRequest` 扩展可选字段:user_id / session_id / data_scope - + 关于 RPC 总数存在分歧:matrix.md §2 与 contract 写 6 RPC;02-architecture-design.md §4.2 单方面扩到 8 RPC(追加 GetLessonPlanStatus / ConfirmLessonPlan)。需 coord 裁定 P5 目标 RPC 清单。 + - **建议方案**:ai12 建议 P5 目标 6 RPC(Chat / StreamChat / GenerateQuestion / StreamGenerateQuestion / OptimizeExpression / GenerateLessonPlan)。备课工作流的"查询状态/确认入库"用 HTTP 端点(`GET /ai/v1/lesson/preparation/{id}` + `POST .../confirm`)实现,避免 RPC 膨胀;如 coord 认为查询/确认也需 gRPC,则定为 8 RPC。请 coord 在 P5 启动前升级 ai.proto 到 v1 完整版。 - **状态**:待 coord 仲裁 @@ -83,7 +85,7 @@ 3. §1.4 topic `edu.ai.usage.events`(三义,见 ISSUE-02) 4. §1.5 错误码示例 `AI_PROVIDER_UNAVAILABLE` / `AI_TOKEN_LIMIT_EXCEEDED` / `AI_CONTENT_FILTERED` 与 02-architecture-design.md §6.2 清单(`AI_LLM_UNAVAILABLE` / `AI_QUOTA_EXCEEDED` / `AI_CONTENT_MODERATION_REJECTED`)命名不一致 5. §2.2 列出 ai 消费 2 个 content Kafka 事件,与 01/02 §5.1 "ai 不消费任何事件(无状态)"矛盾 - + - **建议方案**:ai12 在本次工作中直接重写 contracts/ai_contract.md 对齐设计文档(端口 50058 / 补 HTTP 端点 / topic 待 ISSUE-02 裁决后填 / 错误码对齐 02 §6.2 / 删除消费事件或标注 P6+ 评估)。仅 RPC 数与 topic 命名待 coord 裁决后最终定稿。 - **状态**:ai12 自纠中(依赖 ISSUE-01/02/03 裁决的字段待补) @@ -121,7 +123,25 @@ - **类型**:架构约束 - **描述**:004 §11.5 强制响应信封为 ActionState(`{success, data, error:{code,message,details,traceId}}`)。ai 当前 main.py 违反:返回 `{success:true, data:..., degraded:false}` 顶层 degraded 字段(见 main.py:89/201-210/233-244)。01/02 文档已提请(A5),contracts 未记录。P5 实施必须整改。 - **建议方案**:P5 实施时所有 HTTP 端点 + gRPC RPC 返回值改为 ActionState;degraded 作为 `error.details.degraded` 子字段。请 coord 在 known-issues §2.9 ai 分区记录此约束(01 文档已提请,需 coord 落地 known-issues)。 -- **状态**:待 coord 在 known-issues 记录约束(整改由 ai12 P5 执行) +- **状态**:已裁决(见 [coord.md §12 ARB-010](../coord.md#12-arb-010ai-模块-9-项-issue-仲裁)) + +--- + +## §1 仲裁结论(2026-07-10 coord) + +> 详见 [coord.md §12 ARB-010](../coord.md#12-arb-010ai-模块-9-项-issue-仲裁) + +| ISSUE | 仲裁结论 | 执行方 | +| ----- | ---------------------------------------------------------------------- | ------------ | +| 01 | ✅ 统一端口 50058;coord 修 matrix.md | coord | +| 02 | ✅ 统一 `edu.ai.usage`(ARB-013) | coord | +| 03 | ✅ P5 目标 6 RPC;备课状态查询/确认用 HTTP(ARB-012) | coord | +| 04 | ✅ coord 补全 events.proto AIUsageEvent(ARB-011) | coord | +| 05 | ✅ ai12 自纠重写 contract(端口 50058 / 补 HTTP / topic / 错误码对齐) | ai12 | +| 06 | ✅ P5 用 BackgroundTasks + Redis;P6 评估 Temporal | ai12 + coord | +| 07 | ✅ coord 批次 0 补全 iam.proto 12 RPC(ARB-007) | coord | +| 08 | ✅ 统一 `next_edu_cloud..v1`(ARB-011) | coord | +| 09 | ✅ P5 强制整改 ActionState(degraded 移至 error.details) | ai12 | --- diff --git a/docs/architecture/issues/objections/content_issue.md b/docs/architecture/issues/objections/content_issue.md index 3a92597..66ad5e2 100644 --- a/docs/architecture/issues/objections/content_issue.md +++ b/docs/architecture/issues/objections/content_issue.md @@ -12,21 +12,21 @@ ### 0.1 coord-final-decisions.md N1-N5 核查 -| 编号 | 仲裁结论 | 02-architecture-design.md 落实位置 | 核查结果 | -| ---- | ----------------------------------------------------------------------- | ------------------------------------------------ | -------- | -| N1 | P4 首次实现即启用 gRPC server 50054 | §1.2 入口 HTTP 3005 / gRPC 50054;§4.2 gRPC API | ✅ 已落实 | -| N2 | 首次实现即检查 DB/Neo4j/Kafka | §6.6 /readyz 多依赖检查 | ✅ 已落实 | -| N3 | P4 即补全 QuestionService proto(不等到 P5) | §4.2.4 QuestionService 6 RPC | ✅ 已落实 | -| N4 | 首次实现即对齐 ActionState | §4.3 错误响应结构 success/error 信封 | ✅ 已落实 | -| N5 | P4 首次实现即补全 ChapterService | §4.2.2 ChapterService 3 RPC | ✅ 已落实 | +| 编号 | 仲裁结论 | 02-architecture-design.md 落实位置 | 核查结果 | +| ---- | -------------------------------------------- | ----------------------------------------------- | --------- | +| N1 | P4 首次实现即启用 gRPC server 50054 | §1.2 入口 HTTP 3005 / gRPC 50054;§4.2 gRPC API | ✅ 已落实 | +| N2 | 首次实现即检查 DB/Neo4j/Kafka | §6.6 /readyz 多依赖检查 | ✅ 已落实 | +| N3 | P4 即补全 QuestionService proto(不等到 P5) | §4.2.4 QuestionService 6 RPC | ✅ 已落实 | +| N4 | 首次实现即对齐 ActionState | §4.3 错误响应结构 success/error 信封 | ✅ 已落实 | +| N5 | P4 首次实现即补全 ChapterService | §4.2.2 ChapterService 3 RPC | ✅ 已落实 | ### 0.2 01-understanding.md §A 已裁决项核查 -| 原编号 | 仲裁结论 | 02-architecture-design.md 落实位置 | 核查结果 | -| ------ | --------------------------------------------------------------------- | ----------------------------------------- | -------- | -| C2 | P4 必须引入 Outbox(004 §12.2 强制条款) | §3.1.5 content_outbox_events 表 + §5.4 Outbox Publisher | ✅ 已落实 | -| C4 | P4 必须实现 gRPC controller | §4.2 gRPC API(4 个 Service) | ✅ 已落实 | -| C10 | proto 包名保持 `next_edu_cloud.content.v1` | —(保持现状) | ✅ 已落实 | +| 原编号 | 仲裁结论 | 02-architecture-design.md 落实位置 | 核查结果 | +| ------ | ------------------------------------------ | ------------------------------------------------------- | --------- | +| C2 | P4 必须引入 Outbox(004 §12.2 强制条款) | §3.1.5 content_outbox_events 表 + §5.4 Outbox Publisher | ✅ 已落实 | +| C4 | P4 必须实现 gRPC controller | §4.2 gRPC API(4 个 Service) | ✅ 已落实 | +| C10 | proto 包名保持 `next_edu_cloud.content.v1` | —(保持现状) | ✅ 已落实 | ### 0.3 核查结论 @@ -87,10 +87,10 @@ N1-N5 与 C2/C4/C10 共 8 项已有仲裁**全部在 02-architecture-design.md | KnowledgeGraphService | 4 | 4 | — | | QuestionService | 6(无 Publish/Search) | 7(含 Publish/Search) | — | | **合计** | **18** | **18** | **18** | - - design doc 缺 QuestionService.PublishQuestion / SearchQuestions(contract 有) - contract 缺 TextbookService.Update/Delete(design doc 有) - design doc ChapterService 缺 Update(contract 有);contract ChapterService 缺 Delete(design doc 也缺) + - **建议方案**:以 contract.md 为契约唯一源(已对齐 matrix.md 18 RPC 总数),反向修正 design doc: 1. TextbookService 补 Update/Delete(与 contract 对齐) 2. ChapterService 补 Update + Delete(design doc + contract 都缺 Delete,需补) @@ -173,15 +173,34 @@ N1-N5 与 C2/C4/C10 共 8 项已有仲裁**全部在 02-architecture-design.md ## §2 待 coord 仲裁项汇总 -| # | 标题 | 阻塞性 | 状态 | -| ---- | -------------------------------------------- | ----------------------- | ------------ | -| 001 | REST 端点设计与实现不一致 | 🟡 P4 重构时对齐 | 待 coord 仲裁 | -| 002 | 事件 topic 命名策略冲突(独立 vs 聚合) | 🔴 阻塞 Outbox 实现 | 待 coord 仲裁 | -| 003 | Textbook/Chapter 事件在契约文档遗漏 | 🟡 契约完整性 | 待 coord 仲裁 | -| 004 | gRPC RPC 数量三方文档不一致 | 🔴 阻塞 proto 修改 | 待 coord 仲裁 | -| 005 | core-edu → content 失效事件 topic 无定义 | 🟢 建议删除 | 待 coord 仲裁 | -| 006 | "直接 push main"与项目规则冲突 | 🟡 文档修正 | 待 coord 仲裁 | -| 007 | questions.created_by 迁移风险 | 🟡 schema 迁移 | 待 coord 仲裁 | -| 008 | 缓存策略与 API 版本化策略缺失 | 🟢 长远架构 | 待 coord 仲裁 | -| 009 | knowledge-points schema 实际缺字段 | 🟡 P4 schema 迁移 | 待 coord 仲裁 | -| 010 | Sync Worker 链路与 P4 阶段不必要 | 🟡 设计澄清 | 待 coord 仲裁 | +| # | 标题 | 阻塞性 | 状态 | +| --- | ---------------------------------------- | ------------------- | ------------- | +| 001 | REST 端点设计与实现不一致 | 🟡 P4 重构时对齐 | 待 coord 仲裁 | +| 002 | 事件 topic 命名策略冲突(独立 vs 聚合) | 🔴 阻塞 Outbox 实现 | 待 coord 仲裁 | +| 003 | Textbook/Chapter 事件在契约文档遗漏 | 🟡 契约完整性 | 待 coord 仲裁 | +| 004 | gRPC RPC 数量三方文档不一致 | 🔴 阻塞 proto 修改 | 待 coord 仲裁 | +| 005 | core-edu → content 失效事件 topic 无定义 | 🟢 建议删除 | 待 coord 仲裁 | +| 006 | "直接 push main"与项目规则冲突 | 🟡 文档修正 | 待 coord 仲裁 | +| 007 | questions.created_by 迁移风险 | 🟡 schema 迁移 | 待 coord 仲裁 | +| 008 | 缓存策略与 API 版本化策略缺失 | 🟢 长远架构 | 待 coord 仲裁 | +| 009 | knowledge-points schema 实际缺字段 | 🟡 P4 schema 迁移 | 待 coord 仲裁 | +| 010 | Sync Worker 链路与 P4 阶段不必要 | 🟡 设计澄清 | 待 coord 仲裁 | + +--- + +## §3 仲裁结论(2026-07-10 coord) + +> 详见 [coord.md §5 ARB-005](../coord.md#5-arb-005content-模块-10-项-issue-仲裁) + +| ISSUE | 仲裁结论 | 执行方 | +| ----- | ------------------------------------------------------------------------------ | -------------------------------- | +| 001 | ✅ P4 重构统一 query 参数风格 + 补 DELETE prerequisite 端点 | ai09 | +| 002 | ✅ 采用聚合 topic + action 字段策略(4 聚合 topic) | ai09 + coord | +| 003 | ✅ 补登记 edu.content.textbook.events + chapter.events | ai09 已补 + coord 同步 matrix.md | +| 004 | ✅ 统一为 21 RPC(Textbook 5 + Chapter 5 + KG 4 + Question 7) | ai09 + coord | +| 005 | ✅ 删除 core-edu 失效事件条目;联动改用 gRPC 调用 | ai09 | +| 006 | ✅ 遵循总裁裁决(trunk-based development);措辞优化 | ai09 | +| 007 | ✅ `created_by varchar(32) NOT NULL DEFAULT 'system'` | ai09 | +| 008 | ✅ P4 补缓存策略 + API 版本化说明 | ai09 | +| 009 | ✅ P4 迁移补齐 difficulty/metadata/created_at/updated_at | ai09 | +| 010 | ✅ ES Sync Worker 标 P5;Neo4j Sync Worker 仅 Outbox 输入;移除 safeCreateNode | ai09 | diff --git a/docs/architecture/issues/objections/core-edu_issue.md b/docs/architecture/issues/objections/core-edu_issue.md index da20005..923cc5f 100644 --- a/docs/architecture/issues/objections/core-edu_issue.md +++ b/docs/architecture/issues/objections/core-edu_issue.md @@ -14,14 +14,14 @@ ### 0.1 已核查通过的仲裁 -| 仲裁编号 | 位置 | 核查结论 | -| -------- | ---- | -------- | -| coord-cross-review §3.1 | topic 命名统一为 `edu.teaching..` | ✅ 仲裁真实存在,结论准确,core-edu 01/02 文档引用正确 | -| coord-cross-review §5.5 | 错误码前缀 `CORE_EDU_*` 统一(不再细分 `EXAMS_`/`HOMEWORK_`/`GRADES_`) | ✅ 仲裁真实存在,结论准确 | -| coord-cross-review §5.7 | ActionState 信封结构统一 | ✅ 仲裁真实存在,core-edu GlobalErrorFilter 已对齐 | -| coord-cross-review §2.1 | proto 包名 `next_edu_cloud.core_edu.v1` | ✅ 仲裁真实存在,core_edu.proto L3 符合 | -| coord-cross-review §6 整改 #14 | core_edu.proto 补 AttendanceService | ⚠️ 仲裁真实存在,但状态与实际不符(见 ISSUE-001) | -| coord-cross-review §6 整改 #16 | buf.gen.yaml 补 gRPC 插件 | ⚠️ 待 ai08 核实 buf.gen.yaml 实际状态 | +| 仲裁编号 | 位置 | 核查结论 | +| ------------------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------ | +| coord-cross-review §3.1 | topic 命名统一为 `edu.teaching..` | ✅ 仲裁真实存在,结论准确,core-edu 01/02 文档引用正确 | +| coord-cross-review §5.5 | 错误码前缀 `CORE_EDU_*` 统一(不再细分 `EXAMS_`/`HOMEWORK_`/`GRADES_`) | ✅ 仲裁真实存在,结论准确 | +| coord-cross-review §5.7 | ActionState 信封结构统一 | ✅ 仲裁真实存在,core-edu GlobalErrorFilter 已对齐 | +| coord-cross-review §2.1 | proto 包名 `next_edu_cloud.core_edu.v1` | ✅ 仲裁真实存在,core_edu.proto L3 符合 | +| coord-cross-review §6 整改 #14 | core_edu.proto 补 AttendanceService | ⚠️ 仲裁真实存在,但状态与实际不符(见 ISSUE-001) | +| coord-cross-review §6 整改 #16 | buf.gen.yaml 补 gRPC 插件 | ⚠️ 待 ai08 核实 buf.gen.yaml 实际状态 | ### 0.2 核查发现的仲裁状态不一致(升级为 ISSUE) @@ -73,6 +73,7 @@ - SubmitHomeworkRequest 缺 `answers` 字段(02 文档 §4.2 要求含完整 answers) 缺失 P3 新增 RPC:`PublishExam` / `SubmitExam` / `GradeExam` / `GradeHomework` / `UpdateGrade`。 + - **影响**: 1. 01-understanding.md L48 仅声称缺 AttendanceService,遗漏了 ClassService 也缺失 2. matrix.md §2 声称 core-edu 22 RPC,但实际 proto 仅 14 RPC(5+4+5) @@ -109,6 +110,7 @@ 3. **缺 `schema_version` 字段**:所有 Event message(ClassEvent/ExamEvent/HomeworkEvent/GradeEvent)均无 `schema_version` 字段。coord §3.1 仲裁 + known-issues §1.3 + 01-understanding.md L67 + 02-architecture-design.md §5.1 均要求事件 payload 含 `schema_version`,但 proto 未定义。 4. **matrix.md §4 与 coord §3.1 仲裁不一致**:matrix.md §4 Kafka 事件发布方矩阵仍列 `edu.exam.events` / `edu.homework.events` / `edu.grade.events` / `edu.class.events`,未同步 coord §3.1 仲裁后的 `edu.teaching.*` 命名。 + - **影响**: - core-edu 修改 TOPIC_MAP 后,发布到 `edu.teaching.*` topic,但 events.proto 注释和 matrix.md 仍记录旧 topic,下游 AI 文档被误导 - 缺 AttendanceEvent message 导致 AttendanceService 无事件契约可发布 @@ -132,13 +134,14 @@ - **描述**: 02-architecture-design.md 定义的状态命名与 [coord.md](../coord.md) §1.2 GraphQL schema 枚举不一致: - | 维度 | 02-architecture-design.md(core-edu) | coord.md §1.2 GraphQL schema(teacher-bff) | 不一致点 | - | ---- | ------------------------------------- | ------------------------------------------- | -------- | - | ExamStatus | `draft / published / in_progress / grading / graded / archived / cancelled` | `DRAFT / PUBLISHED / IN_PROGRESS / GRADING / SCORED / ARCHIVED` | core-edu 用 `graded`,coord GraphQL 用 `SCORED`;core-edu 多 `cancelled` | - | SubmissionStatus(exam) | `in_progress / submitted / graded` | `NOT_SUBMITTED / SUBMITTED / GRADED` | core-edu 用 `in_progress`,coord 用 `NOT_SUBMITTED` | - | SubmissionStatus(homework) | `draft / submitted / graded` | `NOT_SUBMITTED / SUBMITTED / GRADED` | core-edu exam 用 `in_progress`,homework 用 `draft`,自身也不一致 | + | 维度 | 02-architecture-design.md(core-edu) | coord.md §1.2 GraphQL schema(teacher-bff) | 不一致点 | + | ---------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------ | + | ExamStatus | `draft / published / in_progress / grading / graded / archived / cancelled` | `DRAFT / PUBLISHED / IN_PROGRESS / GRADING / SCORED / ARCHIVED` | core-edu 用 `graded`,coord GraphQL 用 `SCORED`;core-edu 多 `cancelled` | + | SubmissionStatus(exam) | `in_progress / submitted / graded` | `NOT_SUBMITTED / SUBMITTED / GRADED` | core-edu 用 `in_progress`,coord 用 `NOT_SUBMITTED` | + | SubmissionStatus(homework) | `draft / submitted / graded` | `NOT_SUBMITTED / SUBMITTED / GRADED` | core-edu exam 用 `in_progress`,homework 用 `draft`,自身也不一致 | 02 文档 §3.1.1 exam_submissions.status 注释 `in_progress / submitted / graded`,§3.1.2 homework_submissions.status 注释 `draft / submitted / graded`,**core-edu 内部 exam 与 homework 的初始状态命名也不一致**(`in_progress` vs `draft`)。 + - **影响**: - teacher-bff GraphQL schema 枚举值与 core-edu DB status 字段值无法直接映射,BFF 需要转换层 - 前端展示需处理两套命名 @@ -147,7 +150,7 @@ coord 仲裁统一状态命名(建议二选一): - **方案 A(推荐)**:core-edu DB status 统一为小写动词形式 `draft / published / in_progress / grading / graded / archived / cancelled`,GraphQL 枚举映射为大写 `DRAFT / PUBLISHED / IN_PROGRESS / GRADING / GRADED / ARCHIVED / CANCELLED`(去掉 `SCORED`,统一用 `GRADED`)。SubmissionStatus 统一为 `NOT_SUBMITTED / SUBMITTED / GRADED`,core-edu exam/homework submissions 初始状态统一为 `not_submitted`(不再用 `in_progress` 或 `draft`)。 - **方案 B**:保留 coord GraphQL 现状(`SCORED`),core-edu 改 `graded` 为 `scored`。 - ai08 倾向方案 A(`graded` 是教育领域通用术语,`scored` 歧义大)。 + ai08 倾向方案 A(`graded` 是教育领域通用术语,`scored` 歧义大)。 - **状态**:待 coord 仲裁 --- @@ -160,13 +163,14 @@ - **描述**: `class.transferred` 事件的 topic 命名在三处文档不一致: - | 文档 | topic 命名 | - | ---- | ---------- | - | 01-understanding.md L64 / 02-architecture-design.md §5.1 / §7 | `edu.org.class.created`(合并后归 org 域) | - | matrix.md §4 | `edu.class.events`(ClassEvent,action: transferred) | - | events.proto L13 注释 | `edu.class.events` | + | 文档 | topic 命名 | + | ------------------------------------------------------------- | ----------------------------------------------------- | + | 01-understanding.md L64 / 02-architecture-design.md §5.1 / §7 | `edu.org.class.created`(合并后归 org 域) | + | matrix.md §4 | `edu.class.events`(ClassEvent,action: transferred) | + | events.proto L13 注释 | `edu.class.events` | core-edu 文档声称"classes 合并后归 org 域"用 `edu.org.class.created`,但 coord 维护的 matrix.md 和 events.proto 仍用 `edu.class.events`。 + - **影响**: - core-edu 按 `edu.org.class.created` 实现 TOPIC_MAP 后,matrix.md 记录的 `edu.class.events` topic 下游消费者订阅不上 - 命名归类不一致(org 域 vs class 域) @@ -174,7 +178,7 @@ coord 仲裁统一: - 若 classes 合并到 core-edu 后仍归"教学组织域",则 topic 应为 `edu.teaching.class.transferred`(遵循 §3.1 仲裁的 `edu.teaching..` 风格) - 若归"组织域",则为 `edu.org.class.transferred`(注意 action 应为 `transferred` 而非 `created`) - ai08 倾向 `edu.teaching.class.transferred`(与 §3.1 仲裁风格一致,classes 合并到 core-edu 后属教学域)。 + ai08 倾向 `edu.teaching.class.transferred`(与 §3.1 仲裁风格一致,classes 合并到 core-edu 后属教学域)。 - **状态**:待 coord 仲裁 --- @@ -187,13 +191,14 @@ - **描述**: core-edu gRPC RPC 数量在三处文档统计不一致: - | 文档 | RPC 数 | 包含 ClassService | 包含 P3 新增 RPC | - | ---- | ------ | ----------------- | ---------------- | - | matrix.md §2 | 22 | ✅ 是(4 RPC) | ❌ 否(P2 基线) | - | 02-architecture-design.md §4.2 | 22 | ❌ 否 | ✅ 是(PublishExam/SubmitExam/GradeExam/GradeHomework/UpdateGrade) | - | core-edu_contract.md §1.1 | 22 | ✅ 是(4 RPC) | ❌ 否(P2 基线) | + | 文档 | RPC 数 | 包含 ClassService | 包含 P3 新增 RPC | + | ------------------------------ | ------ | ----------------- | ------------------------------------------------------------------- | + | matrix.md §2 | 22 | ✅ 是(4 RPC) | ❌ 否(P2 基线) | + | 02-architecture-design.md §4.2 | 22 | ❌ 否 | ✅ 是(PublishExam/SubmitExam/GradeExam/GradeHomework/UpdateGrade) | + | core-edu_contract.md §1.1 | 22 | ✅ 是(4 RPC) | ❌ 否(P2 基线) | P3 全量应为:ClassService 4 + ExamService 8(5+3 新增)+ HomeworkService 5(4+1 新增)+ GradeService 6(5+1 新增)+ AttendanceService 4 = **27 RPC**。 + - **影响**: - matrix.md §2 声明 22 RPC 但含 ClassService,02 文档声明 22 RPC 但不含 ClassService,下游 AI 无法判断应实现多少 RPC - 就绪信号"core-edu gRPC 50053 + 22 RPC"含义模糊 @@ -214,17 +219,18 @@ - **描述**: 02-architecture-design.md §13.3 列出 7 项未决设计决策,ai08 尚未正式提请 coord 仲裁,现集中提请: - | # | 决策点 | ai08 倾向 | - | - | ------ | --------- | - | 1 | classes 服务合并到 core-edu 的时机 | (a) P3 初期合并 | - | 2 | 排课 room_id 是否 P3 实现 | (b) 仅预留字段 | - | 3 | 成绩计算公式 scope 优先级 | (a) class > subject > school | - | 4 | 作业 grace_period 默认值 | (b) 300 秒(5 分钟宽限) | - | 5 | P3 是否启用 events.proto schema 强制校验 | (b) 仅文档约束 | - | 6 | exam.submitted 事件是否包含完整 answers | (b) 仅含 submission_id | - | 7 | archived 考试数据是否物理迁移 | (b) P3 仅软删除 | + | # | 决策点 | ai08 倾向 | + | --- | ---------------------------------------- | ---------------------------- | + | 1 | classes 服务合并到 core-edu 的时机 | (a) P3 初期合并 | + | 2 | 排课 room_id 是否 P3 实现 | (b) 仅预留字段 | + | 3 | 成绩计算公式 scope 优先级 | (a) class > subject > school | + | 4 | 作业 grace_period 默认值 | (b) 300 秒(5 分钟宽限) | + | 5 | P3 是否启用 events.proto schema 强制校验 | (b) 仅文档约束 | + | 6 | exam.submitted 事件是否包含完整 answers | (b) 仅含 submission_id | + | 7 | archived 考试数据是否物理迁移 | (b) P3 仅软删除 | 注:决策 #4 与 02 文档 §3.1.2 homework 表 `gracePeriod: int("grace_period").notNull().default(0)` 默认 0 不一致,仲裁后需统一 schema 默认值。 + - **影响**: - 决策未定,core-edu P3 实现无法启动 - 决策 #4 schema 默认值与倾向不一致,仲裁前可能实现错 @@ -246,6 +252,22 @@ 3. homework/grades controller 权限装饰器覆盖核对(L104) ai08 在 P3 实施前需 coord 确认这些项是否作为 P3 验收硬性标准。 -- **建议方案**: - coord 确认上述 3 项是否纳入 P3 验收标准;若纳入,ai08 在 P3 实施时补齐。 -- **状态**:待 coord 仲裁 + +- **建议方案**:coord 确认上述 3 项是否纳入 P3 验收标准;若纳入,ai08 在 P3 实施时补齐。 +- **状态**:已裁决(见 [coord.md §4 ARB-004](../coord.md#4-arb-004core-edu-模块-7-项-issue-仲裁)) + +--- + +## §3 仲裁结论(2026-07-10 coord) + +> 详见 [coord.md §4 ARB-004](../coord.md#4-arb-004core-edu-模块-7-项-issue-仲裁) + +| ISSUE | 仲裁结论 | 执行方 | +| ----- | ---------------------------------------------------------------------------------------------------- | ------------ | +| 001 | ✅ coord 批次 0 补全 core_edu.proto 至 5 Service 27 RPC(ARB-007) | coord | +| 002 | ✅ coord 批次 0 补全 events.proto(AttendanceEvent + schema_version + edu.teaching.* 注释,ARB-006) | coord | +| 003 | ✅ 采用方案 A:统一 `graded`(去掉 SCORED);submissions 统一 `not_submitted/submitted/graded` | ai08 + coord | +| 004 | ✅ 统一为 `edu.teaching.class.transferred` | coord + ai08 | +| 005 | ✅ 统一为 P3 全量 27 RPC | coord + ai08 | +| 006 | ✅ 七项设计决策全部采用 ai08 倾向方案(见 coord.md §4.4) | ai08 | +| 007 | ✅ 3 项(Dockerfile/ActionState traceId/权限装饰器)纳入 P3 验收硬性标准 | ai08 | diff --git a/docs/architecture/issues/objections/data-ana_issue.md b/docs/architecture/issues/objections/data-ana_issue.md index 27e4ca6..3e1540f 100644 --- a/docs/architecture/issues/objections/data-ana_issue.md +++ b/docs/architecture/issues/objections/data-ana_issue.md @@ -12,37 +12,37 @@ ### 0.1 coord.md 仲裁核查 -| 编号 | 主题 | 涉及 data-ana | 核查结论 | -| ------- | ---------------------------- | ------------- | ------------------------------ | -| ARB-001 | teacher-bff GraphQL schema | ❌ 否 | 不涉及,无需 action | -| ARB-002 | MF Shell 暴露清单 | ❌ 否 | 不涉及,无需 action | +| 编号 | 主题 | 涉及 data-ana | 核查结论 | +| ------- | -------------------------- | ------------- | ------------------- | +| ARB-001 | teacher-bff GraphQL schema | ❌ 否 | 不涉及,无需 action | +| ARB-002 | MF Shell 暴露清单 | ❌ 否 | 不涉及,无需 action | **结论**:coord.md 无 data-ana 直接仲裁。 ### 0.2 coord-cross-review.md 仲裁核查(8 项涉及 data-ana) -| # | 审查章节 | 裁决内容 | 责任方 | 核查结论 | -| -- | ---------------- | ------------------------------------------------------------------ | ------ | ---------------------------------------------------------------------------------------------------- | -| 1 | §2.2 #3 | iam 新增 `GetEffectiveDataScope` RPC,P4 补全,data-ana gRPC 调用 | iam | ⚠️ **未落实**:iam.proto 当前仅 4 RPC(Register/Login/RefreshToken/GetUserInfo),无 GetEffectiveDataScope | -| 2 | §2.3 P4 行 | content + data-ana P4 启用 gRPC server | ai11 | ⏳ 未到 P4 阶段,待执行 | -| 3 | §3.2 | 补登 `edu.insight.ai.usage` topic + events.proto 补 AIUsageEvent | coord | ⚠️ **未落实**:events.proto 当前仅 4 message(Class/Exam/Homework/GradeEvent),缺 AIUsageEvent | -| 4 | §3.3 | Python 服务 Outbox 豁免(MasteryUpdated / WarningTriggered) | coord | ✅ **已对齐**:01/02 文档已声明豁免,引用 coord-cross-review.md §3.3 | -| 5 | §4.3 | data-ana HTTP=3006 / gRPC=50055 | coord | ✅ **已对齐**:01/02 文档端口声明一致 | -| 6 | §5.3 | Python 服务信封改为 ActionState(degraded 放 details 子字段) | ai11 | ✅ **已对齐**:02 §4.3 ActionState 实现已修正,degraded 移至顶层 details | -| 7 | §6 #4 | 同 #6,ai06 修正 data-ana/ai 02 文档 + 代码 | ai11 | ✅ **已对齐(文档)**:02 已修正;代码待 P4 实现阶段重构 | -| 8 | §6 #7/#8/#9/#10 | coord 在 004 §7.2 补登 topic + §1.2 端口列 + §4.1 gRPC 矩阵 + §12.2 豁免 | coord | ⚠️ **未落实**:004 正文无 §4.2/§7.2 补登段/§11.4/§11.5,01/02 引用断裂已临时改为引 coord-cross-review.md | +| # | 审查章节 | 裁决内容 | 责任方 | 核查结论 | +| --- | --------------- | ------------------------------------------------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------- | +| 1 | §2.2 #3 | iam 新增 `GetEffectiveDataScope` RPC,P4 补全,data-ana gRPC 调用 | iam | ⚠️ **未落实**:iam.proto 当前仅 4 RPC(Register/Login/RefreshToken/GetUserInfo),无 GetEffectiveDataScope | +| 2 | §2.3 P4 行 | content + data-ana P4 启用 gRPC server | ai11 | ⏳ 未到 P4 阶段,待执行 | +| 3 | §3.2 | 补登 `edu.insight.ai.usage` topic + events.proto 补 AIUsageEvent | coord | ⚠️ **未落实**:events.proto 当前仅 4 message(Class/Exam/Homework/GradeEvent),缺 AIUsageEvent | +| 4 | §3.3 | Python 服务 Outbox 豁免(MasteryUpdated / WarningTriggered) | coord | ✅ **已对齐**:01/02 文档已声明豁免,引用 coord-cross-review.md §3.3 | +| 5 | §4.3 | data-ana HTTP=3006 / gRPC=50055 | coord | ✅ **已对齐**:01/02 文档端口声明一致 | +| 6 | §5.3 | Python 服务信封改为 ActionState(degraded 放 details 子字段) | ai11 | ✅ **已对齐**:02 §4.3 ActionState 实现已修正,degraded 移至顶层 details | +| 7 | §6 #4 | 同 #6,ai06 修正 data-ana/ai 02 文档 + 代码 | ai11 | ✅ **已对齐(文档)**:02 已修正;代码待 P4 实现阶段重构 | +| 8 | §6 #7/#8/#9/#10 | coord 在 004 §7.2 补登 topic + §1.2 端口列 + §4.1 gRPC 矩阵 + §12.2 豁免 | coord | ⚠️ **未落实**:004 正文无 §4.2/§7.2 补登段/§11.4/§11.5,01/02 引用断裂已临时改为引 coord-cross-review.md | ### 0.3 coord-cross-review §8.2 批次 0 产出声明核查 coord-cross-review.md §8.2 声称批次 0 已完成 proto 补全,ai11 逐文件核查实际状态: -| 声明产出项 | 声明状态 | 实际文件状态(ai11 核查) | 核查结论 | -| -------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------- | ------------ | -| iam.proto 12 RPC | ✅ 12 RPC | **4 RPC**(Register/Login/RefreshToken/GetUserInfo) | ⚠️ 严重不符 | -| analytics.proto 扩展 | ✅ 12 RPC(含 Stream) | **3 RPC**(GetClassPerformance/GetStudentWeakness/GetLearningTrend) | ⚠️ 严重不符 | -| events.proto 补全 | ✅ 9 message(+AuditEvent) | **4 message**(ClassEvent/ExamEvent/HomeworkEvent/GradeEvent) | ⚠️ 严重不符 | -| core_edu.proto 补全 | ✅ 5 service | 未由 ai11 核查(非本模块边界) | — | -| buf.gen.yaml 插件 | ✅ go + python | 未由 ai11 核查(coord 维护) | — | +| 声明产出项 | 声明状态 | 实际文件状态(ai11 核查) | 核查结论 | +| -------------------- | --------------------------- | -------------------------------------------------------------------- | ----------- | +| iam.proto 12 RPC | ✅ 12 RPC | **4 RPC**(Register/Login/RefreshToken/GetUserInfo) | ⚠️ 严重不符 | +| analytics.proto 扩展 | ✅ 12 RPC(含 Stream) | **3 RPC**(GetClassPerformance/GetStudentWeakness/GetLearningTrend) | ⚠️ 严重不符 | +| events.proto 补全 | ✅ 9 message(+AuditEvent) | **4 message**(ClassEvent/ExamEvent/HomeworkEvent/GradeEvent) | ⚠️ 严重不符 | +| core_edu.proto 补全 | ✅ 5 service | 未由 ai11 核查(非本模块边界) | — | +| buf.gen.yaml 插件 | ✅ go + python | 未由 ai11 核查(coord 维护) | — | > **核查说明**:ai11 仅核查与 data-ana 直接相关的 proto(iam/analytics/events)。§8.2 声明与实际文件严重不符,可能原因:(a) 声明为计划态但未执行;(b) 执行后未提交到本 worktree 分支;(c) 在其他分支已执行但未合并。无论哪种原因,data-ana 的 P4 实现依赖这些 proto 补全,当前实际状态构成 P4 阻塞。 @@ -95,6 +95,7 @@ coord-cross-review.md §8.2 声称批次 0 已完成 proto 补全,ai11 逐文 - #10:004 §12.2 补充派生数据事件 Outbox 豁免条款 → 004 §12.2 未补充 这导致 01-understanding.md 和 02-architecture-design.md 中引用 004 §4.2/§11.4/§11.5/§15.3 等章节均断裂(004 正文仅到 §14,§15 在 004-p6-addendum.md 但内容不同)。ai11 已在 v2.1 修订中临时改为引用 coord-cross-review.md 对应裁决章节,但这是过渡方案。 + - **建议方案**:coord 按整改清单 #7/#8/#9/#10 补全 004 对应章节,使 004 成为可信的架构设计意图唯一源。各 AI 文档随后将引用从 coord-cross-review.md 回切到 004 对应章节。 - **状态**:待 coord 仲裁 @@ -107,13 +108,14 @@ coord-cross-review.md §8.2 声称批次 0 已完成 proto 补全,ai11 逐文 - **类型**:契约不明确 - **描述**:data-ana 发布的掌握度/预警事件 topic 命名在三个文档中不一致: - | 文档 | topic 命名 | - | -------------------------------------- | ------------------------------------- | + | 文档 | topic 命名 | + | ----------------------------------------------- | --------------------------------------------------------------- | | 01-understanding.md / 02-architecture-design.md | `edu.insight.mastery.updated` + `edu.insight.warning.triggered` | - | matrix.md §4 | `edu.analytics.mastery` | - | contracts/data-ana_contract.md(修正前) | `edu.data_ana.mastery.events` | + | matrix.md §4 | `edu.analytics.mastery` | + | contracts/data-ana_contract.md(修正前) | `edu.data_ana.mastery.events` | 按 004 §7.2 命名规范 `edu...`,data-ana 属于 D6 智能洞察领域(domain=insight),故 `edu.insight.mastery.updated` 符合规范。matrix.md 的 `edu.analytics.mastery` 不符合命名规范(缺 action 层级,且 domain 用了服务名而非领域名)。 + - **建议方案**:coord 裁决统一为 `edu.insight.mastery.updated` + `edu.insight.warning.triggered`,coord 修正 matrix.md §4。ai11 已在 contract.md 中采用此命名。 - **状态**:待 coord 仲裁 @@ -126,4 +128,19 @@ coord-cross-review.md §8.2 声称批次 0 已完成 proto 补全,ai11 逐文 - **类型**:其他(声明与实际不符) - **描述**:coord-cross-review.md §8.2 声称批次 0 已完成 iam.proto(12 RPC)/ analytics.proto(12 RPC)/ events.proto(9 message)补全,但 ai11 逐文件核查发现实际均未补全(详见 §0.3 核查表)。这影响所有依赖这些 proto 的下游 AI 的排期评估——若 AI 信任 §8.2 声明,会在排期中忽略 proto 补全的等待时间,导致排期失真。 - **建议方案**:coord 核实 §8.2 声明真实性。若实际已补全但未合并到各 worktree 分支,请协调合并;若实际未补全,请更新 §8.2 状态为"计划中"或"待执行",并明确补全时间点,以便下游 AI 据此排期。 -- **状态**:待 coord 仲裁 +- **状态**:已裁决(见 [coord.md §11 ARB-009](../coord.md#11-arb-009data-ana-模块-6-项-issue-仲裁)) + +--- + +## §2 仲裁结论(2026-07-10 coord) + +> 详见 [coord.md §11 ARB-009](../coord.md#11-arb-009data-ana-模块-6-项-issue-仲裁) + +| ISSUE | 仲裁结论 | 执行方 | +| ----- | ---------------------------------------------------------------------------------- | ------ | +| 001 | ✅ coord 批次 0 补全 iam.proto 12 RPC(ARB-007,含 GetEffectiveDataScope) | coord | +| 002 | ✅ coord 补全 events.proto AIUsageEvent(ARB-011) | coord | +| 003 | ✅ coord 批次 0 补全 analytics.proto 至 12 RPC(ARB-012) | coord | +| 004 | ✅ coord 补全 004 §7.2/§1.2/§4.1/§12.2 对应章节 | coord | +| 005 | ✅ 统一 `edu.insight.mastery.updated` + `edu.insight.warning.triggered`(ARB-013) | coord | +| 006 | ✅ coord 更正 coord-cross-review.md §8.2 状态为"待执行" | coord | diff --git a/docs/architecture/issues/objections/iam_issue.md b/docs/architecture/issues/objections/iam_issue.md index 330906a..bc7d494 100644 --- a/docs/architecture/issues/objections/iam_issue.md +++ b/docs/architecture/issues/objections/iam_issue.md @@ -12,33 +12,33 @@ ### 1.1 coord-final-decisions I1-I8(iam 专项)核查 -| 裁决 | 内容摘要 | 核查结论 | 证据 | -| ---- | -------- | -------- | ---- | -| I1 | P2 即启用 gRPC server 50052,REST + gRPC 并存 | ⚠️ **02 文档未回写**:[02-architecture-design.md](../../../services/iam/docs/02-architecture-design.md) §1/§7.1/§8.1 决策点 5 仍写"P2 仅 REST,P3 随 core-edu 引入 gRPC";iam.proto 仅 4 RPC 未补全 | [iam.proto](../../../packages/shared-proto/proto/iam.proto) 仅 4 RPC;[02 文档 §8.1](../../../services/iam/docs/02-architecture-design.md) 决策点 5 | -| I2 | 直接建 shared-ts Outbox 工具包,iam 首次实现即用 | ✅ **工具包已建立**:`packages/shared-ts/src/outbox/` 存在 outbox.service.ts + outbox.module.ts;⚠️ 02 文档 §8.1 决策点 4 仍写"iam 自建轻量 Outbox",未回写 | [shared-ts/outbox](../../../packages/shared-ts/src/outbox/) | -| I3 | 首次实现即 DB 驱动 + Redis 缓存,废弃本地 map | ❌ **源码未改造**:[permission.guard.ts](../../../services/iam/src/middleware/permission.guard.ts) 第 30-39 行仍用硬编码 `ROLE_PERMISSIONS` map(admin/teacher);02 文档 §8.1 决策点 9 描述为"待改造" | [permission.guard.ts](../../../services/iam/src/middleware/permission.guard.ts) | -| I4 | 首次实现即注册 AuthMiddleware,Controller 用 @Req() 注入 | ❌ **源码未注册**:[app.module.ts](../../../services/iam/src/app.module.ts) 仅注册 PermissionGuard 为 APP_GUARD,未在 configure() 消费 AuthMiddleware;02 文档 §1.1/§1.2/§8.1 决策点 10 仍写"P2 仍不注册" | [app.module.ts](../../../services/iam/src/app.module.ts) | -| I5 | P2 本地文件 IAM_PRIVATE_KEY_PATH / IAM_PUBLIC_KEY_PATH | ⚠️ **源码未实现**:[iam.service.ts](../../../services/iam/src/iam/iam.service.ts) 第 170-186 行仍用 `env.JWT_SECRET`(HS256 单密钥);02 文档 §8.1 决策点 1 已对齐(本地文件→P6 Vault)但未落地 | [iam.service.ts](../../../services/iam/src/iam/iam.service.ts) | -| I6 | P2 即补全 iam_student_guardians 表 + GetChildrenByParent RPC + GET /iam/children | ❌ **02 文档表名错误**:[02 文档 §3.1.2](../../../services/iam/docs/02-architecture-design.md) 用 `iam_parent_student_relations`,裁决表名为 `iam_student_guardians`;源码未实现 | 02 文档 §3.1.2 | -| I7 | 采用 /iam/v1/* 前缀,Gateway 透传 | ❌ **02 文档未回写**:[02 文档 §4.1](../../../services/iam/docs/02-architecture-design.md) REST API 清单全部用 `/iam/*` 无 `/v1` 前缀;源码 Controller 用 `@Controller("iam")` 无版本前缀 | [iam.controller.ts](../../../services/iam/src/iam/iam.controller.ts)、[rbac.controller.ts](../../../services/iam/src/iam/rbac.controller.ts) | -| I8 | 统一 GET /iam/permissions/effective | ✅ **源码已对齐**:[rbac.controller.ts](../../../services/iam/src/iam/rbac.controller.ts) 第 33 行用 `@Get("permissions/effective")` | [rbac.controller.ts](../../../services/iam/src/iam/rbac.controller.ts) | +| 裁决 | 内容摘要 | 核查结论 | 证据 | +| ---- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| I1 | P2 即启用 gRPC server 50052,REST + gRPC 并存 | ⚠️ **02 文档未回写**:[02-architecture-design.md](../../../services/iam/docs/02-architecture-design.md) §1/§7.1/§8.1 决策点 5 仍写"P2 仅 REST,P3 随 core-edu 引入 gRPC";iam.proto 仅 4 RPC 未补全 | [iam.proto](../../../packages/shared-proto/proto/iam.proto) 仅 4 RPC;[02 文档 §8.1](../../../services/iam/docs/02-architecture-design.md) 决策点 5 | +| I2 | 直接建 shared-ts Outbox 工具包,iam 首次实现即用 | ✅ **工具包已建立**:`packages/shared-ts/src/outbox/` 存在 outbox.service.ts + outbox.module.ts;⚠️ 02 文档 §8.1 决策点 4 仍写"iam 自建轻量 Outbox",未回写 | [shared-ts/outbox](../../../packages/shared-ts/src/outbox/) | +| I3 | 首次实现即 DB 驱动 + Redis 缓存,废弃本地 map | ❌ **源码未改造**:[permission.guard.ts](../../../services/iam/src/middleware/permission.guard.ts) 第 30-39 行仍用硬编码 `ROLE_PERMISSIONS` map(admin/teacher);02 文档 §8.1 决策点 9 描述为"待改造" | [permission.guard.ts](../../../services/iam/src/middleware/permission.guard.ts) | +| I4 | 首次实现即注册 AuthMiddleware,Controller 用 @Req() 注入 | ❌ **源码未注册**:[app.module.ts](../../../services/iam/src/app.module.ts) 仅注册 PermissionGuard 为 APP_GUARD,未在 configure() 消费 AuthMiddleware;02 文档 §1.1/§1.2/§8.1 决策点 10 仍写"P2 仍不注册" | [app.module.ts](../../../services/iam/src/app.module.ts) | +| I5 | P2 本地文件 IAM_PRIVATE_KEY_PATH / IAM_PUBLIC_KEY_PATH | ⚠️ **源码未实现**:[iam.service.ts](../../../services/iam/src/iam/iam.service.ts) 第 170-186 行仍用 `env.JWT_SECRET`(HS256 单密钥);02 文档 §8.1 决策点 1 已对齐(本地文件→P6 Vault)但未落地 | [iam.service.ts](../../../services/iam/src/iam/iam.service.ts) | +| I6 | P2 即补全 iam_student_guardians 表 + GetChildrenByParent RPC + GET /iam/children | ❌ **02 文档表名错误**:[02 文档 §3.1.2](../../../services/iam/docs/02-architecture-design.md) 用 `iam_parent_student_relations`,裁决表名为 `iam_student_guardians`;源码未实现 | 02 文档 §3.1.2 | +| I7 | 采用 /iam/v1/* 前缀,Gateway 透传 | ❌ **02 文档未回写**:[02 文档 §4.1](../../../services/iam/docs/02-architecture-design.md) REST API 清单全部用 `/iam/*` 无 `/v1` 前缀;源码 Controller 用 `@Controller("iam")` 无版本前缀 | [iam.controller.ts](../../../services/iam/src/iam/iam.controller.ts)、[rbac.controller.ts](../../../services/iam/src/iam/rbac.controller.ts) | +| I8 | 统一 GET /iam/permissions/effective | ✅ **源码已对齐**:[rbac.controller.ts](../../../services/iam/src/iam/rbac.controller.ts) 第 33 行用 `@Get("permissions/effective")` | [rbac.controller.ts](../../../services/iam/src/iam/rbac.controller.ts) | ### 1.2 president-final-rulings 相关条款核查 -| 条款 | 内容摘要 | 核查结论 | -| ----- | -------- | -------- | -| §2.15 | /iam/v1/* 版本化规则(Controller 加 v1 前缀) | ❌ 同 I7,未落地 | -| §2.16 | gRPC 与 REST 双入口策略(gateway HTTP 透传 + BFF gRPC 调用) | ⚠️ 02 文档未体现双入口设计,仅描述 REST 单入口;contract.md §1.2 写"无对外 HTTP 端点,仅 gRPC"与此冲突 | -| §3.2 | iam P2 拆分 P2.1(8 RPC 核心)+ P2.2(扩展) | ⚠️ workline.md 仅粗略列出 P2.1,P2.2-P6 未细化(见 worklines/iam_workline.md) | -| §5.5 | 审计日志归 iam(AuditEvent + edu.iam.audit.created topic + user_audit_log 表) | ❌ 02 文档未包含审计日志设计;events.proto 未定义 AuditEvent message | -| §5.1 | events.proto 补全 UserEvent/RoleEvent | ❌ events.proto 实际仅含 ClassEvent/ExamEvent/HomeworkEvent/GradeEvent,缺 UserEvent/RoleEvent/AuditEvent(coord-final-decisions §5.1 标注"✅ 已补全"与实际不符) | +| 条款 | 内容摘要 | 核查结论 | +| ----- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| §2.15 | /iam/v1/* 版本化规则(Controller 加 v1 前缀) | ❌ 同 I7,未落地 | +| §2.16 | gRPC 与 REST 双入口策略(gateway HTTP 透传 + BFF gRPC 调用) | ⚠️ 02 文档未体现双入口设计,仅描述 REST 单入口;contract.md §1.2 写"无对外 HTTP 端点,仅 gRPC"与此冲突 | +| §3.2 | iam P2 拆分 P2.1(8 RPC 核心)+ P2.2(扩展) | ⚠️ workline.md 仅粗略列出 P2.1,P2.2-P6 未细化(见 worklines/iam_workline.md) | +| §5.5 | 审计日志归 iam(AuditEvent + edu.iam.audit.created topic + user_audit_log 表) | ❌ 02 文档未包含审计日志设计;events.proto 未定义 AuditEvent message | +| §5.1 | events.proto 补全 UserEvent/RoleEvent | ❌ events.proto 实际仅含 ClassEvent/ExamEvent/HomeworkEvent/GradeEvent,缺 UserEvent/RoleEvent/AuditEvent(coord-final-decisions §5.1 标注"✅ 已补全"与实际不符) | ### 1.3 coord.md ARB-001 / ARB-002 核查 -| 仲裁 | 内容摘要 | 核查结论 | -| ------ | -------- | -------- | +| 仲裁 | 内容摘要 | 核查结论 | +| ------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------ | | ARB-001 | teacher-bff GraphQL schema 第一版(5 Query) | ✅ schema 中 `me: User!`、`viewports` 依赖 iam,与 iam GetUserInfo/GetViewports RPC 对齐,无冲突 | -| ARB-002 | MF Shell 暴露清单 | ✅ 不涉及 iam 直接交付物,无冲突 | +| ARB-002 | MF Shell 暴露清单 | ✅ 不涉及 iam 直接交付物,无冲突 | --- @@ -140,3 +140,19 @@ --> 见上方 §2 新提请异议(ISSUE-001 ~ ISSUE-007)。 + +--- + +## §3 仲裁结论(2026-07-10 coord) + +> 详见 [coord.md §3 ARB-003](../coord.md#3-arb-003iam-模块-7-项-issue-仲裁) + +| ISSUE | 仲裁结论 | 执行方 | +| ----- | ------------------------------------------------------------------------------ | ------------ | +| 001 | ✅ 支持 ai06:3 工作日内回写 01/02,删除全部过渡方案,对齐 contract.md v1.0 | ai06 | +| 002 | ✅ coord 批次 0 补全 events.proto(ARB-006) | coord | +| 003 | ✅ 统一 `edu.iam.*` 域名 + 聚合 topic 风格 | coord | +| 004 | ✅ DataScope 采用 ALL/SCHOOL/GRADE/CLASS/SUBJECT/SELF(SUBJECT 替代 DISTRICT) | coord + ai06 | +| 005 | ✅ coord 批次 0 补全 iam.proto 至 12 RPC(ARB-007) | coord | +| 006 | ✅ 与 ISSUE-001 一并回写,署名改 ai06 | ai06 | +| 007 | ✅ contract.md 已修正双入口,ai06 回写 02 文档对齐 | ai06 | diff --git a/docs/architecture/issues/objections/msg_issue.md b/docs/architecture/issues/objections/msg_issue.md index 9aaaeb6..640af73 100644 --- a/docs/architecture/issues/objections/msg_issue.md +++ b/docs/architecture/issues/objections/msg_issue.md @@ -192,3 +192,23 @@ - matrix.md §4 L118:`edu.notification.requested`(第三种命名) - **建议方案**:待 ISSUE-008 仲裁 topic 命名约定后统一 - **状态**:待 coord 仲裁(依赖 ISSUE-008) + +--- + +## §2 仲裁结论(2026-07-10 coord) + +> 详见 [coord.md §10 ARB-008](../coord.md#10-arb-008msg-模块-15-项-issue-仲裁) + +| ISSUE | 仲裁结论 | 执行方 | +| ------- | ------------------------------------------------------------------------------------------------------------- | ------------ | +| 001~005 | ✅ 核查通过(001/002/003);004 待 coord 同步 events.proto(ARB-006);005 待 coord 修正 004 §4.1 PushGW 方向 | coord | +| 006 | ✅ coord 补全 GradeEvent class_id + 全部事件 student_ids[](ARB-011) | coord | +| 007 | ✅ 统一 `next_edu_cloud..v1`(ARB-011) | coord | +| 008 | ✅ msg 采用具体动作 topic(ARB-013) | coord | +| 009 | ✅ 维持 13 RPC,裁剪 4 RPC 降级 REST | ai10 | +| 010 | ✅ markAsRead 权限改为 `MSG_NOTIFICATION_READ` | ai10 | +| 011 | ✅ 双向降级(ES 故障→DB LIKE;DB 故障→ES 只读) | ai10 | +| 012 | ✅ 非阻塞,ai10 补 DLQ + 三层幂等设计文档 | ai10 | +| 013 | ✅ coord 补全 events.proto(ARB-011:NotificationEvent + MasteryEvent) | coord | +| 014 | ✅ 统一 13 RPC,两文档对齐 | ai10 | +| 015 | ✅ 统一 topic 命名(ARB-013) | ai10 + coord | diff --git a/docs/architecture/issues/objections/parent-bff_issue.md b/docs/architecture/issues/objections/parent-bff_issue.md index b394e55..197f3b9 100644 --- a/docs/architecture/issues/objections/parent-bff_issue.md +++ b/docs/architecture/issues/objections/parent-bff_issue.md @@ -12,12 +12,12 @@ ### 0.1 核查通过项(已正确执行) -| 仲裁 | 主题 | 核查结论 | -| --- | --- | --- | -| U2 | push-gateway 豁免 gRPC,HTTP /internal/push | ✅ 02 §5.3/§7.1 已执行 HTTP 调用 push-gateway | -| C2 | 端口 3010,不暴露 gRPC | ✅ 02 §7.2 + matrix.md §3 已执行 | -| C3 | core-edu 错误码 CORE_EDU_* | ✅ 02 §6.2 已执行 | -| C5 | Kafka topic edu.notification.sent/read/recalled/failed | ✅ 02 §5.2 已执行(004 §7.2 同步属 coord 待办 #7,不阻塞 parent-bff) | +| 仲裁 | 主题 | 核查结论 | +| ---- | ------------------------------------------------------ | --------------------------------------------------------------------- | +| U2 | push-gateway 豁免 gRPC,HTTP /internal/push | ✅ 02 §5.3/§7.1 已执行 HTTP 调用 push-gateway | +| C2 | 端口 3010,不暴露 gRPC | ✅ 02 §7.2 + matrix.md §3 已执行 | +| C3 | core-edu 错误码 CORE_EDU_* | ✅ 02 §6.2 已执行 | +| C5 | Kafka topic edu.notification.sent/read/recalled/failed | ✅ 02 §5.2 已执行(004 §7.2 同步属 coord 待办 #7,不阻塞 parent-bff) | ### 0.2 核查发现的问题(仲裁已裁决但文档未同步/执行有偏差) @@ -48,7 +48,7 @@ - §3.1.1 Redis 缓存 Schema 表:写 "60s"(coord 推断原值) - §9 #2 ai05 review 结论:调整为 "30s + 主动失效" - §13 #7 黄金模板对齐表:写 "30s" - §3.1.1 表格未同步 §9 的调整,导致同一文档内 60s 与 30s 并存。 + §3.1.1 表格未同步 §9 的调整,导致同一文档内 60s 与 30s 并存。 - **建议方案**:§3.1.1 表格 ChildGuard 行 TTL 改为 "30s",与 §9 #2 + §13 #7 一致 - **状态**:待 coord 仲裁(ai05 建议直接修正,属于文档勘误) @@ -73,7 +73,7 @@ - 004_architecture_impact_map.md §4 服务依赖图(line 376-377)仍只画 `PBFF --> IAM` + `PBFF --> CoreEdu`,未加 DataAna + Msg - matrix.md §1 服务依赖矩阵(line 64-65)同样只画 `PBFF --> IAM` + `PBFF --> CORE` - 02 §0.1 C6 行已标注 "004 §4 待 coord 同步更新",但至今未同步 - 此差异导致新接手的 AI 看 004/matrix 会误以为 parent-bff 不依赖 data-ana/msg,与 02 设计冲突。 + 此差异导致新接手的 AI 看 004/matrix 会误以为 parent-bff 不依赖 data-ana/msg,与 02 设计冲突。 - **建议方案**:coord 在 004 §4 服务依赖图补 `PBFF --> DataAna` + `PBFF --> Msg`,matrix.md §1 同步;更新 004 时按 project_rules §1 "改码必同步图" 执行 - **状态**:待 coord 仲裁 @@ -87,7 +87,7 @@ - parent-bff 02 §4.1 明确 "不实现 REST 业务端点(仅保留 /healthz /readyz /metrics)" - parent-portal §3.1 还引用 `GET /iam/effective-permissions`(C4 仲裁已改为 `/iam/permissions/effective`) - parent-portal §3.1 标 "BFF 对接:parent-bff(ai04 设计)",ai04 已过时(现 ai05) - 此冲突若不解决,parent-portal(ai15)会按 REST 实现 frontend client,与 parent-bff GraphQL 端点不兼容。 + 此冲突若不解决,parent-portal(ai15)会按 REST 实现 frontend client,与 parent-bff GraphQL 端点不兼容。 - **建议方案**:coord 协调 ai15 将 parent-portal 01/02 文档的 parent-bff 消费契约从 REST 改为 GraphQL(`POST /api/v1/parent/graphql`),同步 C4 iam 路径仲裁;此属跨模块契约,按 project_rules §14.4 跨模块变更顺序处理 - **状态**:待 coord 仲裁 @@ -101,7 +101,7 @@ - core_edu.proto: `package next_edu_cloud.core_edu.v1;` - analytics.proto: `package next_edu_cloud.analytics.v1;` - msg.proto: `package next_edu_cloud.msg.v1;` - 引用不一致会导致 gRPC client 代码生成时 package 路径错误。 + 引用不一致会导致 gRPC client 代码生成时 package 路径错误。 - **建议方案**:02 §3.3 DTO 映射表 proto message 列全部补 `next_edu_cloud.` 前缀;或确认是否统一去掉前缀(需 buf.yaml 配置一致) - **状态**:待 coord 仲裁 @@ -165,6 +165,25 @@ --- +## §3 仲裁结论(coord,2026-07-10) + +> 详见 [coord.md](../coord.md) §20 ARB-018 + +| ISSUE | 仲裁结论 | 状态 | +| ------------------------------------ | -------------------------------------------------------------------------------------------------- | --------------------- | +| 001(01 文档未同步) | ✅ 01 作为阶段 1 历史快照保留原样,以 02 为准(02 已执行 U3/U4/C1) | 已裁决(§20 ARB-018) | +| 002(ChildGuard TTL 不一致) | ✅ 统一为 30s(§3.1.1 表格改为 30s,对齐 §9 #2 + §13 #7) | 已裁决(§20 ARB-018) | +| 003(引用 I3 → I6 错误) | ✅ 修正为 I6 裁决(coord-final-decisions §1 I6:家长-学生关联) | 已裁决(§20 ARB-018) | +| 004(004 §4 依赖图未同步 C6) | ✅ coord 补全 004 §4 服务依赖图 + matrix.md §1 依赖矩阵(加 PBFF → DataAna + PBFF → Msg) | 已裁决(§20 ARB-018) | +| 005(parent-portal REST vs GraphQL) | ✅ coord 协调 ai15 将 parent-portal 01/02 改为 GraphQL 消费(POST /api/v1/parent/v1/graphql) | 已裁决(§20 ARB-018) | +| 006(proto 包名缺前缀) | ✅ 统一补 next_edu_cloud. 前缀(ARB-011 已裁决) | 已裁决(§20 ARB-018) | +| 007(Notification.childId 缺失) | ✅ coord 协调 ai10 在 msg.proto Notification 补 child_id = 9 字段(可选) | 已裁决(§20 ARB-018) | +| 008(ClassService 缺失) | ✅ coord 已在 ARB-007 补全 ClassService 4 RPC;02 §7.1 状态改 ✅ 已有;Grade.score 转换规则补 §3.3 | 已裁决(§20 ARB-018) | +| 009(ai02→ai06 责任方过时) | ✅ 修正为 ai06(iam 现归属 ai06,ai-allocation §3.2 确认) | 已裁决(§20 ARB-018) | +| 010(02 缺 ADR/NFR) | ✅ P6 硬化阶段补全(当前不阻塞 P4 实施) | 已裁决(§20 ARB-018) | + +--- +