Skip to content

Latest commit

 

History

History
1897 lines (1286 loc) · 119 KB

plan-20260731.md

File metadata and controls

1897 lines (1286 loc) · 119 KB

Chat/Notes 退场、Website 用户系统接入与邮件投递迁出计划(2026-07-31)

路径说明(2026-09-20): 本文成稿时 docker-compose-storage-only.yml / docker-compose-storage-only.auth-none.yml / docker-compose.test.yml 位于仓库根;2026-09-20 起三者迁入 docker/ 目录(commit 1854cae)。文中根目录原路径为历史记录,不再逐一回改;实际使用时请替换为 docker/ 前缀路径。

改名註記(2026-09-17): 本文成稿與執行時,Cargo 包名/二進制名為舊名,lib 為舊名 _core;2026-09-17 起全倉統一改名為 Mega2(mega2 / mega2_core),本文中的命令、路徑、環境變量與服務名已同步更新,歷史提交記錄本身不變。

模板版本: v2(成稿符合 docs/plan/plan-template.md v2)

文档职责

本文解决 mega2 内与 website 产品面重复的 Campsite 风格 chat / Notes,以及浏览器会话仍硬编码 admin、Campsite/Tinyship 死配置未落地的问题。目标是:

  1. 整栈删除本仓 chat / Notes(产品能力留在 website mega2 分支);
  2. 完整设计并实现本仓对 website Better Auth 用户系统的接入(唯一浏览器 IdP);
  3. 保留 Git 引擎所需的 Mono access_token / SSH / LFS / Bot 身份路径,以及 /api/v1/user 下的 SSH/token/CLA/通知偏好 API(仅更换会话身份来源;邮件投递偏好语义见 ADR-WA-08);
  4. 把 website apps/next-app 整理进本仓 IT compose,与现有 mega2-it 数据面 / mega2(profile app)一起启动,跑真实 get-session → /api/v1 会话集成测试(对标 Mega demo 的「前端 + 后端 + 数据面同栈」形态,但 IdP 是 website next-app,不是 moon/campsite);
  5. 去掉本仓全部邮件(email)投递能力,改由 website 的邮件通知功能发送;本仓不再持有 SMTP/lettre/[mail]/email outbox。

本文只规划任务,不宣称实现完成。落地时每个任务都必须先刷新源码锚点,再按任务卡验收。

适用范围

  • Website 会话接入:src/api/oauth/、OAuthConfig / [oauth]、SessionUser / LoginUser、CORS、docs/refactoring/website-auth.md。
  • Chat / Notes 退场:src/chat/、src/api/router/chat_router.rs、src/api/notes/、src/commands/chat_migrate.rs、src/contract/api/chat/、相关 callisto / jupiter storage、[chat] / ChatConfig、sidebar 默认项、chat 通知触发器、新 forward-only DROP 迁移。
  • IT compose: docker-compose.test.yml 登记 website-next(website apps/next-app)、docs/refactoring/test-infra.md 服务条目、栈级会话黑盒(bin/tests/)、CI 可选 profile 拉起路径。
  • 邮件退场与 website 投递: 删除 src/mail/、EmailChannel、[mail]、admin email-jobs/mail-templates、email_jobs / email_job_attachments;dispatcher 去 email-primary;产品事件经 website 邮件 API 发送;同步更新 docker-compose.test.yml(去掉 mega2 MEGA_MAIL__*;mailpit 改挂 website IT)、.env.test(.example)、CI workflow,以及 docs/refactoring/{website-mail,mail,notification,test-infra,integration,config}.md、docs/development.md、根 README.md 邮件章节。
  • 文档与发布:docs/plan/plan-long.md、上列重构/开发文档、docs/plan/README.md、minor 版本发布。

非目标

  • 不在 mega2 实现注册 / 登录 / 签发 session cookie(IdP 只在 website)。
  • mega2 不直连读取 website 的 user/session 表(信任路径仍是 get-session HTTP);不引入 JWT / machine service-account(见 DEFER-WA-01)。website-next 容器可有独立账户库(IT 默认 SQLite,见 ADR-WA-07)。
  • 不把 Mega 的 moon / campsite / MySQL 拉进 mega2-it;本计划的「同栈联调」对象是 website next-app + mega2,仅形态上对标 Mega demo。
  • 不移植 website AI chat(apps/next-app/app/api/chat)或 docs-app 静态文档站;不把 website 仓库的产品 docker-compose.yml(nuxt/tanstack profiles)整仓迁入本仓。
  • 不完成 PT-12 全量 moon↔apps/next-app 功能对照审计(本计划只落地账户信任路径 + 最小会话栈 IT)。
  • 不把 CL/issue 事件编排整体迁到 website(触发仍可由本仓发出);本计划只迁邮件投递。Slack/webhook/in-app 是否保留见 ADR-WA-08(决议:保留 in-app + 可选 slack/webhook,去掉 email 通道)。
  • 不删除历史 migration 文件;只追加 DROP。
  • 不改 Git smart HTTP / SSH / LFS 的 token 鉴权语义。

成功定义

  • 无有效 website session cookie 时,/api/v1 会话路径不再得到隐式 admin;带有效 cookie 时身份与 GET /api/auth/get-session 一致;banned 用户被拒绝。
  • /api/v1/chat/*、notes sync、chat-migrate、[chat] 不存在;新库经 apply_migrations 后无 chat/notes 相关表。
  • Git Bearer/Basic token 与 SSH 路径回归绿;LoginUser JSON 字段为 website_user_id。
  • --profile app --profile web 可同栈拉起 mega2 + website-next;黑盒用例能在 next-app 登录后携带 session cookie 调用 mega2 /api/v1/user 并断言身份一致。
  • 本仓无 SMTP/[mail]/email_jobs/admin 邮件表面;产品事件需发邮件时走 website 邮件 API;进程内不再直接投递邮件。
  • docker-compose.test.yml / .env.test* / CI / 相关文档已反映「邮件投递在 website」:mega2 服务无 MEGA_MAIL__*;mailpit 若保留则消费方为 website IT;开发与重构文档不再把本仓 SmtpMailer→Mailpit 当作门禁。
  • 全部非延后任务在 REL-01 后 Lifecycle=done 且 Acceptance=complete(子卡此前仅 locally-accepted);plan-long.md / test-infra.md / notification.md / mail.md / development.md 已同步;REL-01 minor 已发布。

事实基线

本节为开工前快照(核对日 2026-07-31)。执行后状态以各任务卡 Lifecycle / Acceptance 与落地产物为准。行号仅作历史线索,开工须按 ER-02 刷新。

类别 当前事实 证据
代码入口 库入口 src/lib.rs;二进制 bin/src/main.rs;pub mod chat 仍注册 src/lib.rs:15
代码入口 SessionUser 硬编码 admin / admin@gitmono.test(no-auth stub) src/api/oauth/mod.rs:167-174
代码入口 TinyshipApiStore / CampsiteApiStore / OAuthApiStore 存在但 从未构造;仅 __Secure-better-auth.session_token 硬编码 src/api/oauth/tinyship_store.rs:15、campsite_store.rs、api_store.rs
代码入口 Chat HTTP 已挂载;Notes 仅 sync_state src/api/api_router.rs:42、:56;src/api/notes/note_router.rs
代码入口 chat-migrate CLI 三处注册 src/commands/mod.rs:1、:37、:46
数据/状态 无本地 users 表(已 drop);身份为 username 字符串 + 可选外部 id m20251026_065433_drop_user_table(migration/mod.rs:61、:153)
数据/状态 Chat / Notes 表经多迁移落地;通知种子含 chat.mention.created / chat.reply.created m20260529_*、m20260324_*notes*、m20260630_000000_seed_notification_event_types;triggers.rs:26-27
CLI 命令 service / config / chat-migrate 等 src/commands/mod.rs
HTTP API /api/v1/chat/*、/organizations/{org}/notes/{id}/sync_state、/api/v1/user/* chat_router.rs、note_router.rs、user_router.rs
配置项 OAuthConfig 仅 allowed_cors_origins;toml 中 campsite_api_domain / tinyship_api_domain / api_store_backend 为遗留忽略键;存在完整 [chat] src/config/model.rs:426-432、:436;config/config.toml:169-187、:354
配置项 sidebar 默认含 chat / notes(Docs) config/config.toml:277-278;SidebarConfig(model.rs:1011)
错误类型 会话拒绝走 AuthRedirect;应用层主错误仍为 MegaError src/api/oauth/mod.rs;src/common/errors/
迁移 fn migrations() 注册链含 note/chat/seed src/jupiter/migration/mod.rs:123 起
文档 chat 独立改进文档仍在(开工前);无 website-auth 事实源 重构目录 chat 独立文档(DOC-01 删除);docs/plan/plan-long.md PT-10/PT-12
测试 chat router 黑盒 / chat-migrate exec / chat 通知 triggers 依赖 stub admin chat_router.rs tests;triggers.rs ~1117+
工作区前置 版本面 0.1.183;../orbit path 依赖 Cargo.toml:6;bin/Cargo.toml:3
工作区前置 IT compose 有 postgres/redis/mailpit/rustfs/git-cli/mega2;无 website next-app docker-compose.test.yml;docs/refactoring/test-infra.md
数据/状态 通知以 email_jobs 为 outbox;EmailDispatcher email primary 成功后才扇出 in-app/Slack/webhook src/notification/dispatcher.rs;src/mail/;config/config.toml [mail]
外部参照 website libs/email:仅认证验证/重置密码事务邮件(Resend/Cloudflare);无产品通知 outbox、无对外 send API website libs/email/、libs/auth/auth.ts(核对日 2026-07-31)
外部参照 website mega2 @ afcd69af:Better Auth 1.4.10,GET /api/auth/get-session,cookie better-auth.session_token / __Secure-…;无 username 列,展示名为 name website libs/auth/auth.ts;apps/next-app/app/api/auth/[...all]/route.ts(核对日 2026-07-31)
外部参照 website 自有 docker-compose.yml 仅 profile next/nuxt/tanstack,不接入 mega2-it 网络 website docker-compose.yml;apps/next-app/Dockerfile(端口 7001)
外部参照 Mega demo compose 将前端 + mono + 数据面同栈(形态参照,非移植对象) mega/docker/demo/docker-compose.demo.yml
外部参照 Mega 已接线真实 SessionUser + tinyship/campsite store(对照实现) mega/mono/src/api/oauth/mod.rs SessionUser

当前缺口

ID 缺口 影响 证据 计划动作
GAP-01 浏览器会话为硬编码 admin 任意客户端以 admin 身份调用 /api/v1;无法对接 website oauth/mod.rs:167-174 AU-03、AU-05
GAP-02 [oauth] 遗留键未类型化、store 未注入 PT-10 死配置;无法配置 website 基址 OAuthConfig 仅 CORS;toml 遗留键 AU-02
GAP-03 Campsite 双后端与 tinyship 单 cookie 名 与「website 唯一 IdP」冲突;本地 HTTP cookie 名不匹配 campsite_store.rs;tinyship_store.rs:15 AU-03
GAP-04 LoginUser.campsite_user_id 命名遗留 GPG/API 契约仍用 Campsite 品牌 oauth/model.rs:58;gpg_router.rs:36 AU-04
GAP-05 Chat / Notes 产品面在本仓完整存在 与 website 职责重复;扩大维护面 src/chat/、api/notes/、migrations RM-01..RM-06
GAP-06 chat 通知事件与种子仍在 退场后残留 event type / 模板 / 测试 triggers.rs:26-27;seed migration RM-03、RM-05
GAP-07 无 website-auth 单一事实源文档 部署/CORS/cookie 域约定无法引用 docs/refactoring/ 无 website-auth.md AU-01
GAP-08 mega2-it 无 website apps/next-app 服务;会话只能 mock 无法对真实 Better Auth + cookie 做栈级 IT;与 Mega「前后端同栈」联调能力不对等 docker-compose.test.yml 无 next-app;website compose 孤立 ITW-01..ITW-03
GAP-09 本仓自持 SMTP/[mail]/email_jobs 邮件投递;website 仅有认证事务邮件且无产品通知 API;compose 仍暴露 MEGA_MAIL__* 与「本仓→Mailpit」文档门 与「邮件通知归 website」冲突;双份凭据与模板;删 email 通道会拖死 in-app/Slack(email-primary);IT/文档与代码退场不同步 src/mail/;dispatcher primary;docker-compose.test.yml MEGA_MAIL__ENABLED;docs/development.md / integration.md Mailpit 门;website libs/email MN-01..MN-06;DEP-06

与其它计划的关系

计划/文档 关系 本计划处理
plan-long.md PT-10([oauth] 落地)、PT-12(账户语义)、原则 11;chat 原属「当前基础」独立改进 DOC-01 更新状态与日期索引;chat 退出「当前基础」
plan-20260727.md PT-01 已完成;测试基建可复用 不重做;ITW 新服务必须走 test-infra.md 登记(与 IT-01 同源规范)
docs/refactoring/test-infra.md compose 新服务唯一登记入口 ITW-01 按 checklist 登记 website-next
chat 独立改进文档(已删) 原 chat 改进事实源 DOC-01 删除并改引本计划
docs/refactoring/notification.md chat 触发器登记 RM-03 / DOC-01 同步删除 chat 段
docs/refactoring/config.md [oauth] / [chat] AU-02、RM-04 同步
Mega docker/demo/docker-compose.demo.yml 前后端同栈形态参照 只借鉴拓扑;不引入 moon/campsite/MySQL
website docker-compose.yml 产品侧 next/nuxt/tanstack profiles IT 路径以本仓 docker-compose.test.yml 为准;不整仓迁入
website libs/email 认证事务邮件现状;产品通知邮件缺口 DEP-06 要求 website 增补内部邮件/通知 API;MN 链消费
docs/refactoring/mail.md / notification.md 本仓邮件与通知事实源 MN / DOC-01 改写为「邮件在 website;本仓无 SMTP」
docs/refactoring/test-infra.md / docs/development.md / 根 README.md compose 服务表与本地 IT 操作手册仍写本仓 SMTP/Mailpit 门 MN-06 与 DOC-01 必须同步;mailpit 登记消费方改为 website(若保留)
docker-compose.test.yml / .env.test(.example) / CI MEGA_MAIL__ENABLED、MAILPIT_*、workflow 启 mailpit 作本仓 SMTP MN-06 改接线;禁止留下「mega2 直连 Mailpit」的默认路径
AGENTS.md / README.md 工程门禁 遵守;若描述仍含 chat 子命令 / 本仓 SMTP 则 DOC-01 登记或修订

评审结论与修订记录

维度 结论 修订动作
合理性 chat/notes 产品面属 website;本仓保留重复后端违反单一职责;会话 stub 阻断真实集成 无
可行性 信任路径已有 Tinyship 草稿与 Mega 对照;删除轴可按表面→模块→DROP 切片 无
任务卡粒度 AU / RM 按配置→接线→重命名→测试与表面→通知→模块→迁移→sidebar 切开;无 L/XL 成稿按 G-* 审计
依赖与顺序 两主链:AU-01→…→AU-05 与 AU-01→RM-01→…→RM-06;汇合 DOC-01→REL-01;AU 与 RM 实现写集不相交可并发 见实施顺序
完整性 含设计文档、配置、API、迁移、测试、plan-long、minor 发布 无
安全性 fail-closed 会话;禁生产旁路;保留 token/SSH;ban 拒绝 ADR-WA-01
功能正确性 字段映射与空 name 回退已决议;DROP forward-only ADR-WA-02/05
接口兼容 删除公开 chat/notes API + 重命名 JSON 字段 → minor ADR-WA-06
数据流与控制流 每请求 cookie introspection;不缓存会话于 mega2(本计划) DEFER-WA-02
性能与容量 get-session 为每请求外呼;后续可加短 TTL 缓存 DEFER-WA-02
可靠性与容错 website 不可达 → 会话拒绝;Git token 路径独立 风险表
可维护性 website-auth.md 为会话单一事实源;禁止回流 Campsite store AU-01

修订历史

日期 触发 变更内容 原卡 → 新卡 受影响的引用
2026-07-31 成稿 首版:Website IdP 接入 + chat/notes 整栈删除 N/A 全文
2026-07-31 需求补录 增补 website apps/next-app 进入 mega2-it compose,与现有栈同启做会话集成测试(ADR-WA-07;任务 ITW-01..ITW-03;GAP-08) N/A → ITW-01, ITW-02, ITW-03 文档职责、缺口、ADR、实施顺序、依赖表、Phase、粒度审计、测试矩阵、里程碑、风险、延后项、完成判据
2026-07-31 需求补录 本仓去掉全部邮件投递,改用 website 邮件通知(ADR-WA-08;任务 MN-01..MN-06;GAP-09;DEP-06);修订 ADR-WA-05 对 email_job_attachments 的保留表述 N/A → MN-01..MN-06 文档职责、缺口、ADR、实施顺序、依赖表、Phase、RM-04/05、粒度审计、测试矩阵、里程碑、风险、延后项、完成判据、plan-long PT-09
2026-07-31 需求补录 邮件退场必须同步更新 docker-compose.test.yml、.env.test*、CI 与相关文档(ADR-WA-08 §7;强化 MN-05/MN-06/DOC-01) MN-06 扩写 ADR-WA-08、GAP-09、关系表、Phase 2b、MN-01/05/06、DOC-01、测试矩阵、完成判据
2026-07-31 review R6 Codex FAIL:全族 REL-WA-01(子卡 family child,REL-01 唯一 minor 发布点);AU-05→RM-01/AU-05→MN-02 闭合 G-10;ADR-WA-06 声明无弃用窗口 independent → family child ER-04、REL 表、并发声明、粒度审计、任务卡、ADR-WA-06、Review log
2026-08-06 完成度复审(post-completion review) ① AU-03:FixedUserSessionStore 补 #[cfg(test)] 门控(对齐 website-auth.md 约束)并实跑补证 VER-4 冒烟;② AU-04:为 gpg_router 增补 focused 测试,消除空跑 VER;③ RM-02/RM-04/MN-03:清理磁盘上遗留的空目录残壳(src/api/notes、src/chat{,/service}、src/contract/api/chat、src/mail、src/email);④ ITW-01:登记收口后 website IT 账户库 SQLite→隔离 Postgres website 库的演进注记;⑤ MN-01:website-mail.md 实现基线补 2026-08-06 pin 增补(CI pin a52d703…→2af89c8…);⑥ DOC-01:修复 augmentcode.md 悬空 chat.md 引用并加现状注记;⑦ MN-02 保留渠道的两个 transport-error 脱敏测试(slack/webhook)在配置了系统级 HTTP 代理的主机上假失败(代理把死端口的连接拒绝转成自身 HTTP 5xx,测试落入 HTTP 状态分支)——测试改用显式 .no_proxy() 客户端恢复确定性,生产客户端的代理行为不变 N/A AU-03/AU-04/ITW-01/DOC-01 注记、src/api/oauth/api_store.rs、src/api/router/gpg_router.rs、src/notification/channels/{slack,webhook}.rs(仅 #[cfg(test)])、docs/refactoring/{website-mail,augmentcode}.md
2026-07-31 review R5 Codex 计划审查 FAIL:闭合 G-10(ITW-02→MN-05、ITW-03→MN-06);AU-01 deps=DEP-01;MN-06 VER=6/12;REL-01 审计 AC=4/12;Review log 合法化 ITW-01→MN-05 → ITW-02→MN-05;+ ITW-03→MN-06 实施顺序、发布窗口、并发声明、粒度审计、MN-05/06、AU-01、Review log

已决议设计决策

实现时若需偏离本节,必须先修改计划并说明原因,不得在代码中静默改语义。

ADR-WA-01: Website 为唯一浏览器 IdP

  • Status: Accepted
  • Context: 账户系统在 website(Better Auth);本仓 SessionUser 为硬编码 admin;Campsite/Tinyship 双后端死代码与「website 唯一 IdP」冲突。
  • Decision: 浏览器身份只信任 website Better Auth。mega2 不签发 session cookie。删除 CampsiteApiStore 与 api_store_backend 双后端。信任路径:读 Cookie → WebsiteSessionStore::load_user → GET {oauth.website_api_base_url}/api/auth/get-session(转发完整 signed cookie 名=值)→ 映射 LoginUser。同时支持 cookie 名 better-auth.session_token 与 __Secure-better-auth.session_token。无 cookie / 非成功 / 空 body / user.banned=true → AuthRedirect。删除硬编码 admin stub。测试仅允许注入 FixedUserSessionStore(或等价 test double),禁止生产旁路开关。
  • Alternatives considered: 共享 website DB 直接读 session 表(拒绝——耦合与密钥边界);签发本仓 JWT(拒绝——本计划非目标,见 DEFER-WA-01);保留 Campsite 后端(拒绝——与架构决议冲突)。
  • Consequences: service http 必须能解析 website_api_base_url;跨站 cookie 需同站/父域/反代,文档必须写清。
  • Revisit when: 需要机机身份或会话缓存时(DEFER-WA-01/02)。

ADR-WA-02: 身份字段映射

  • Status: Accepted
  • Context: website user 无 username 列;本仓全域以 username 为 actor / PK;GPG 使用外部 id 字段。
  • Decision:
Website user LoginUser 用途
id website_user_id(由 campsite_user_id 重命名) GPG 等外部 id
name username Git actor、SSH/token/CLA/prefs、Cedar principal
email email 通知 settings 首次创建
image avatar_url /user 回显
banned (不入结构体) true → 拒绝会话
role (不入结构体) 管理权仍以 mono.admin + Cedar 为准

name 为空时用 email 的 local-part;仍空则拒绝会话并 warn。username 唯一性由 website 展示名治理负责;本仓不新增 users 表。

  • Alternatives considered: 在 mega2 维护 username 映射表(拒绝——重复账户系统);用 email 作 username(拒绝——破坏现有 username 键语义与 Git actor 习惯)。
  • Consequences: 展示名碰撞可能导致 actor 混同;须在 website-auth.md 写明运营约束。
  • Revisit when: website 引入稳定 unique handle 字段时。

ADR-WA-03: 双轨认证保留

  • Status: Accepted
  • Context: Git CLI 无法携带 Better Auth cookie;现有 access_token / SSH 已服务协议路径。
  • Decision:
通道 机制 本计划
/api/v1 浏览器 Cookie → website get-session 落地
API Bearer Mono access_token → AccessTokenUser 保留
Bot bot_ Bearer → BotIdentity 保留
Git HTTP / LFS Bearer 或 Basic password = token 保留
SSH 公钥 → ssh_keys.username 保留

/api/v1/user 的 SSH/token/CLA/prefs 保留;仅 LoginUser 来源变为 website session(或显式 AccessTokenUser 处保持 token 解析)。

  • Alternatives considered: 删除 /user 设置 API、改由 website 代理(拒绝——SSH/token 是引擎职责)。
  • Consequences: 文档须明确「浏览器会话 ≠ Git 凭据」;用户仍须在 mega2 生成 access token 供 Git 使用。
  • Revisit when: website 代发 mono access token 的产品流程落地时。

ADR-WA-04: [oauth] 配置面

  • Status: Accepted
  • Context: PT-10 要求 [oauth] 落地或清理;当前仅 CORS 被消费。
  • Decision: OAuthConfig 扩展并真正消费:allowed_cors_origins、website_api_base_url: String(service http 启动路径必填,validate fail-closed)、session_cookie_names: Vec<String>(默认上述两个 Better Auth 名)。删除遗留忽略键 campsite_api_domain、tinyship_api_domain、api_store_backend。同步 validate / reload / source / testing 与 config/config.toml。
  • Alternatives considered: 整段删除 [oauth](拒绝——仍需 CORS 与 website 基址);环境变量硬编码基址(拒绝——破坏 config 单一事实源)。
  • Consequences: 旧 toml 含遗留键时 validate 必须拒绝或显式迁移说明(本卡选择:拒绝未知/遗留键,与现有严格校验风格一致,若当前为 whitelist-ignore 则改为删除键并更新示例)。
  • Revisit when: 多 website 环境(staging/prod)需 profile 覆盖时(已有 profile 机制,无需改 ADR)。

ADR-WA-05: Chat / Notes 整栈删除

  • Status: Accepted(2026-07-31 修订:邮件表退场改由 MN-04,不再要求永久保留 email_job_attachments)
  • Context: chat/notes 产品面归属 website;本仓整栈(API/CLI/实体/迁移链)为重复建设。
  • Decision: 按 RM 链卸表面 → 卸通知 → 删模块/实体/配置 → 新 migration DROP chat/notes 相关表(含 chat foundations 引入且无非 chat 消费方的 attachments、open_graph_links)。ER-02(2026-08-01): reactions 仍被 CL/Issue conversation 消费(conversation_storage / cl_service / issue_service / conv_router),不得 DROP;因 reactions.custom_reaction_id 依赖 custom_reactions,亦不得 DROP custom_reactions(DEFER:日后若 conversation 反应迁表再议)。历史 migration 文件保留。新迁移同时删除 chat.* notification event type 行。删除 [chat] / ChatConfig;sidebar 去掉 chat 与 notes(Docs)。email_jobs / email_job_attachments 不在本 ADR DROP 范围——由 ADR-WA-08 / MN-04 退场。
  • Alternatives considered: 仅 feature-flag 隐藏 API(拒绝——死代码仍在);保留表只删代码(拒绝——用户要求整栈删除)。
  • Consequences: Rollback mode: forward-only;生产 DROP 前须备份。RM-04/RM-05 验收不得再要求「永久保留 email_job_attachments」。
  • Revisit when: 无。

ADR-WA-06: 版本与兼容

  • Status: Accepted(2026-07-31 修订:breaking 列表追加邮件表面删除)
  • Context: 删除公开 HTTP 表面并重命名 LoginUser JSON 字段;另删除邮件投递表面。
  • Decision: 本计划发布点 Version increment: minor。发布说明必须列出 breaking:/api/v1/chat/* 与 notes sync 移除、campsite_user_id→website_user_id、无 cookie 不再隐式 admin、chat-migrate 子命令移除、[chat] 与遗留 oauth 键移除、[mail] / SMTP / /admin/email-jobs / /admin/mail-templates / email_jobs 表移除。
  • Alternatives considered: patch 夹带(拒绝——破坏兼容,违反 ER-08)。
  • Consequences: REL-01 唯一 minor bump;家族子卡 N/A。
  • Revisit when: 无。
  • Deprecation window (G-11): 无单独弃用窗口。依据:本仓 chat/notes 为未接线产品面(GAP-05;硬编码 admin);[mail]/email-jobs 随 ADR-WA-08 整栈迁出。兼容告知 = REL-01 发布说明 + minor bump(ADR-WA-06 breaking 列表);RM-01/RM-02/MN-03 直接删除,不设 shim。若后续需要 shim,须修订本 ADR 并拆卡。

ADR-WA-07: website next-app 进入 mega2-it compose 同栈 IT

  • Status: Accepted
  • Context: 会话接入若只有 mock,无法证明真实 Better Auth cookie ↔ mega2 路径。Mega demo(mega/docker/demo/docker-compose.demo.yml)已证明「前端 + 后端 + 数据面」同栈联调价值;mega2 对应前端是 website apps/next-app,不是 moon/campsite。website 自有 compose 与 mega2-test-net 隔离,无法直接做跨服务 IT。
  • Decision:
    1. 在本仓 docker/docker-compose.test.yml 新增服务 website-next(profiles: ["web"]),build context 为 sibling ../website,dockerfile apps/next-app/Dockerfile(与 orbit / mega2 Dockerfile 的 sibling 约定一致);加入默认网络 mega2-test-net;宿主映射 127.0.0.1:17001:7001。
    2. 不把 Mega moon/campsite/MySQL 拉进本栈;形态对标 Mega demo,资产对标 website。
    3. website-next 账户库:IT 默认容器内 SQLite(Dockerfile 已支持),与 mega2 Postgres 库隔离(满足「引擎不读 website 表」);禁止与 mega2 业务库混库。(2026-08-06 注记:收口后已由 plan-20260803 期间的 mega2 39f7433 演进为共享 postgres 服务上的隔离 website 库——隔离目标与「禁止混用业务库」不变,本条 SQLite 表述为收口日快照,现行拓扑见 test-infra.md website-next 条目与 ITW-01 执行后注记。)
    4. 栈级联调命令:docker compose -p mega2-it -f docker-compose.test.yml --profile app --profile web up -d --wait(数据面默认 up;app+web 显式)。mega2 环境注入 MEGA_OAUTH__WEBSITE_API_BASE_URL=http://website-next:7001,CORS 含 http://127.0.0.1:17001(及文档写明的其它 IT origin)。
    5. 必须按 docs/refactoring/test-infra.md checklist 登记(healthcheck、零残留、CI 入口、secret 注入);默认 up -d --wait 不拉起 web(避免拖慢无前端需求的数据面循环)。
    6. 最小栈级验收:在 next-app 完成 sign-up/sign-in → 取得 session cookie → 请求 mega2 /api/v1/user → 断言 username / website_user_id 与 get-session 一致(ITW-03)。
  • Alternatives considered: 仅用 FixedUser mock(拒绝——无法验真实 cookie/CORS);在 website 仓单独 compose 手动联调(拒绝——不进 cargo test / CI 门);把 next-app 放进默认 up(拒绝——拖慢数据面);复用 Mega campsite 作 IdP(拒绝——与本计划 website IdP 决议冲突)。
  • Consequences: ER-01 增补「会话栈 IT 需 sibling ../website」;CI 可选拉起 --profile web;镜像构建耗时需写入性能预算;website 契约漂移由 DEP-01 + ITW-03 捕获。
  • Revisit when: 需要 Postgres 版 website 账户库、或 E2E 浏览器(Playwright)全链路时(见 DEFER-WA-05)。

ADR-WA-08: 邮件投递退出 mega2,由 website 承担

  • Status: Accepted(2026-07-31 修订:强制 compose + 文档同步)
  • Context: 本仓 EmailDispatcher 以 email 为 primary outbox,扇出 in-app/Slack/webhook;[mail]/SMTP/lettre/Mailpit IT 自成体系。website 已有 @libs/email(Resend/Cloudflare)但仅认证验证/重置密码,无产品通知 API。用户要求本仓去掉邮件通知,改用 website 邮件能力;且要求 docker compose 与相关文档一并更新,避免代码退场后 IT/手册仍指向本仓 SMTP。
  • Decision:
    1. Website 是唯一出站邮件发送方(含认证事务邮件 + 本仓产品事件触发的通知邮件)。
    2. mega2 删除全部邮件投递表面: src/mail/(及孤儿 src/email/)、EmailChannel、[mail]/MailConfig、mail.password secret 字段、admin /email-jobs/* 与 /mail-templates*、实体/表 email_jobs 与 email_job_attachments、依赖 lettre(若无其它引用);禁止再以 Mailpit 作为本仓 SMTP 验收门。
    3. 编排仍可留在本仓: CL/issue/reference 等 triggers 保留;不再 enqueue_email_job / 渲染本仓 mail TOML 模板发信。改为调用 website 内部认证 API(契约由 MN-01 固化),例如 POST /api/internal/notifications/email(或等价),body 含 event_type、to、locale、模板变量或预渲染字段;鉴权用共享 bearer(配置项,非浏览器 cookie)。Website 用 @libs/email 发送并扩展产品模板。
    4. Dispatcher 去 email-primary: NotificationService 启动不得依赖 [mail];若保留 in-app/Slack/webhook,primary 改为 in_app(或「无 primary、各通道独立」——开工选一并写进 MN-02;默认 primary = in_app)。用户 prefs 中 delivery_mode=email 改为「请求 website 发信」或迁移为 in_app(MN-02 写清兼容窗口;默认:email 模式映射为「调用 website + 仍写 in-app」)。
    5. Chat 邮件随 RM-03 删除,不迁到 website(chat 产品面已退场)。
    6. 禁止本仓再直连 Resend/Cloudflare/SMTP(避免双份凭据)。
    7. Compose + 环境 + 文档强制同步(与代码同里程碑,不得后补):
      1. docker-compose.test.yml: 删除 mega2 服务上全部 MEGA_MAIL__*(含 MEGA_MAIL__ENABLED);mega2 不得 depends_on: mailpit。
      2. mailpit 去留: 默认保留服务(便于 website-next 认证/产品邮件在 IT 中 SMTP 捕获);在 test-infra.md 明确 消费方 = website(或 profile web),不是 mega2。若 DEP-06/website IT 确认不需要 SMTP 捕获,可改为 profiles: ["mail"] 或删除——须在 MN-06 写清并改文档/CI;禁止留下「本仓 SmtpMailer→Mailpit」的默认说明。
      3. website-next(ITW): 需要发信验收时,注入 SMTP→mailpit:1025(或 website 既有测试 provider env);注入 mega2→website 邮件 API 所需的内部 bearer / base URL 与 mega2 侧 notification.website_mail_*(或 MN-01 定名)对称。
      4. .env.test / .env.test.example: MAILPIT_* 若保留,注释改为「website IT 捕获」;删除「本仓 [mail] / mail.password seed」步骤;新增 website mail client / bearer 的 IT 占位(无真实生产 secret)。
      5. CI(.github/workflows/): 去掉本仓 SMTP/mail.password 强制门;mailpit 启动仅在 website 邮件 IT 或显式 profile 需要时保留,并在 workflow 注释写明用途。
      6. 文档清单(MN-06 主改,DOC-01 复核): docs/refactoring/website-mail.md、mail.md(废止)、notification.md、config.md、test-infra.md、integration.md、docs/development.md、根 README.md 的 [mail]/SMTP 章节、plan-long.md PT-09;OpenAPI/运维若仍写 email-jobs 一并清。
  • Alternatives considered: 仅把 mail.provider=http 指到 website 而保留 email_jobs(拒绝——用户要求去掉本仓邮件通知/outbox);把整个 notification 迁 website(拒绝——CL/issue 事件源在本仓,范围过大);裸删 EmailChannel 不改 primary(拒绝——拖死扇出);只改 Rust 代码、compose/文档后补(拒绝——用户明确要求同步更新)。
  • Consequences: 依赖 DEP-06(website API 可用)才能绿 MN-05/MN-06;PT-09 邮件对齐改为「投递在 website」;minor breaking 见 ADR-WA-06;MN-06 写集含 compose/env/docs,粒度仍为 M(与测试改写同卡,避免文档漂移)。
  • Revisit when: website 侧出现完整用户通知中心后,可再评估是否下沉 triggers(DEFER-WA-07);mailpit 是否从默认 up 集挪到 profiles: ["mail"](运维成本权衡)。

全局工程约束

以下约束对本文所有任务生效。任务条目不再逐条重复,违反任一项即视为任务未完成。

  • GC-01 现状核实前置: 每个任务开工前重新核对计划、相关开发文档、当前代码和测试。如果已实现,则任务改为补测试、补文档、更新状态或关闭,不重复实现。
  • GC-02 单一事实源: entity 定义、配置解析、API schema、权限策略、错误类型和共享 helper 必须有单一事实源。禁止 CLI handler、HTTP handler、migration 和测试 fixture 各自复制等价逻辑。会话信任路径的事实源为 docs/refactoring/website-auth.md(AU-01 产物)。
  • GC-03 Mega 目标项目与 mega2 扩展边界: SessionUser 接线可对照 Mega mono/src/api/oauth/,但 IdP 固定为 website(非 Campsite);差异必须写在 website-auth.md。
  • GC-04 输出与错误契约: 用户可见错误使用 MegaError 稳定变体并同步 docs/errors.md。HTTP 状态码、JSON 响应、CLI 退出码和人读输出必须分别验收。
  • GC-05 文档同步: 命令、配置、HTTP API 或公开行为变化必须同步对应 docs/、config/config.toml 注释与必要时 README.md。OpenAPI 证据取自运行中的 /api/openapi.json。
  • GC-06 测试覆盖: 新增 / 改动的 storage / migration / 会话解析必须有 focused 测试;删除表面必须有零命中守卫。
  • GC-07 安全默认值: 会话路径 fail-closed;禁止生产环境隐式 admin。Cedar 仍为 permit-all 现状时,不得把「授权已生效」写进验收假说。
  • GC-08 原子性与恢复: DROP 迁移 forward-only;发布后不可 revert schema。
  • GC-09 并发与资源生命周期: website HTTP 客户端须有超时;测试注入 store 不得泄漏跨用例全局状态。
  • GC-10 性能预算: 本计划接受每请求 get-session 外呼;禁止在 handler 内无界重试。
  • GC-11 生产 panic 禁止: 生产路径不得新增裸 unwrap() / expect() / panic!();TinyshipApiStore::new 现有 expect 在迁到 WebsiteSessionStore 时必须改为可返回错误或启动期 MegaResult。
  • GC-12 精确提交: 只 libra add <相关路径>;本仓无 .git。

执行检查必备需求(强制)

本文执行检查条目 ER-01..ER-12 与 docs/plan/plan-template.md v2 全文一致(含 A/B/C/D 门、Lifecycle/Acceptance、签名、版本面、并发发布规则)。执行时以模板正文为准;下列为本计划现状补记(不削弱模板):

本计划 ER-01 补记

  • compose 项目名统一 -p mega2-it(与 plan-20260727 / test-infra.md 对齐)。
  • 会话相关单元测试若需 mock website,不得依赖真实外网;使用进程内 FixedUserSessionStore,避免新依赖。
  • **栈级会话 IT(ITW-*)**额外要求 sibling checkout ../website(与 ../orbit 同级);缺失时 ITW 卡置 blocked,不得用 mock 冒充 ITW-03 验收。

执行后注记(2026-08-21 前端仓库改指): 本计划全部 ../website / genedna/website 锚点与 cd ../website && … 验证命令均为收口日快照,不作改写。 联调栈的前端自 2026-08-21 起改指 gitmono-dev/monoui 的 mega2 分支 (sibling ../monoui);WE-02..WE-06 的实现已逐文件移植到该仓,契约不变。 现行事实源见 ../refactoring/website-auth.md §头部 与 ../refactoring/website-mail.md 的 2026-08-21 增补。

  • .env.test 仍按 AGENTS.md:缺失则停止询问,不得静默跳过 source。

本计划 ER-04 / 发布补记

  • 本计划采用单一家族发布 REL-WA-01: 全部 implementation / removal / migration 卡为 family child of REL-WA-01;唯一发布点为 REL-01(family release point,Version increment: minor,ADR-WA-06)。
  • 子卡:Version increment: N/A;Release write set: N/A;C/D coverage from: REL-01;本地可至 locally-accepted,不得在 REL-01 前将 Acceptance=complete 或推送。
  • AU-01 / DOC-01 / MN-01 为 no-release,产物随 REL-01 进仓;C/D 继承 REL-01。
  • 删除公开 API 的卡在发布说明中必须引用 ADR-WA-06 breaking 列表(由 REL-01 聚合)。
  • ITW/MN 卡改动 docker-compose.test.yml / workflow 时,D 组按 paths 命中登记;未命中则写 N/A(由 REL-01 继承执行)。

任务卡粒度规则

全文适用 plan-template.md 的 G-01..G-11、Verification 判定口径、字段默认与 waiver 白名单。本计划无 EX-* waiver。

实施顺序

依赖边格式:A -> B 表示 A 必须先于 B。

  • DEP-01 -> AU-01
  • AU-01 -> AU-02
  • AU-02 -> AU-03
  • AU-03 -> AU-04
  • AU-04 -> AU-05
  • AU-05 -> RM-01
  • RM-01 -> RM-02
  • RM-02 -> RM-03
  • RM-03 -> RM-04
  • RM-04 -> RM-05
  • RM-05 -> RM-06
  • AU-01 -> ITW-01
  • ITW-01 -> ITW-02
  • AU-03 -> ITW-02
  • ITW-02 -> ITW-03
  • AU-05 -> ITW-03
  • AU-05 -> DOC-01
  • RM-06 -> DOC-01
  • ITW-03 -> DOC-01
  • AU-01 -> MN-01
  • MN-01 -> MN-02
  • AU-05 -> MN-02
  • RM-03 -> MN-02
  • MN-02 -> MN-03
  • MN-03 -> MN-04
  • MN-04 -> MN-05
  • DEP-06 -> MN-05
  • ITW-02 -> MN-05
  • MN-05 -> MN-06
  • ITW-03 -> MN-06
  • MN-06 -> DOC-01
  • DOC-01 -> REL-01

依赖登记表

ID direction 类型 对象 Owner 产物与可用性判据 证据 超时与失败策略
DEP-01 incoming 外部服务 / 上游 revision website 仓库 mega2 分支 Better Auth get-session 契约 website 维护者 GET /api/auth/get-session 返回 user/session JSON 形状稳定;cookie 名符合 ADR-WA-01 website libs/auth/ @ 核对日 2026-07-31;pinned 时在 AU-01 文档记录 revision 契约变更则先改 AU-01/ADR 再改代码;不得猜测字段
DEP-02 incoming 跨计划 plan-long.md PT-10 / PT-12 / PT-09 本计划 DOC-01 日期索引与状态可写 docs/plan/plan-long.md DOC-01 内更新;冲突时以本计划 ADR 为准并在 plan-long 注明
DEP-03 outgoing 跨计划 PT-12 全量前端对照审计 后续日期计划 本计划不交付功能对照表 PT-12 章节 接收方未接手则保持候选;本计划不阻塞
DEP-04 outgoing 延后 DEFER-WA-01/02/05/07 后续计划 JWT/会话缓存/浏览器 E2E/通知中心下沉 本文件「非目标与延后项」 无
DEP-05 incoming 外部 checkout sibling ../website(ITW/MN 构建与联调) 执行 Agent 目录存在且含 apps/next-app/Dockerfile;分支为 mega2(或 AU-01 文档 pinned revision) 同 ../orbit 约定;website docker-compose.yml / Dockerfile 缺失则 ITW-* / MN-05+ blocked;不得用 mock 关闭 ITW-03 / MN-06
DEP-06 incoming 跨仓实现 website:内部邮件/通知发送 API + 产品事件邮件模板(基于 @libs/email) 已由 plan-20260802.md 交付(WE-02..WE-06;website tip a52d703) MN-01 契约路径/鉴权/JSON 可调用;IT 返回 2xx(EMAIL_PROVIDER=test) website POST /api/internal/notifications/email;mega2 integration_website_mail 已关闭;本仓不得回退 SMTP

发布分组与并发窗口

ID 成员 唯一发布点 窗口规则 失败回滚顺序 理由
REL-WA-01 AU-01..AU-05, RM-01..RM-06, ITW-01..ITW-03, MN-01..MN-06, DOC-01;发布点 REL-01 REL-01 窗口内禁止独立 patch 推送;子卡只本地提交/review 至 locally-accepted;唯一 minor bump+推送在 REL-01;窗口禁止插入无关 breaking 子卡代码/文档 revert(RM-05/MN-04 仅备份前滚);未推送则整族回退 chat/notes/mail/oauth 公开 surface 与 schema 删除必须同窗口 minor 收口(G-08);docs 子卡 no-release

并发声明: 同链串行。跨链仅在写集不相交时允许:AU-* 与纯 docs 可交错;禁止 AU 与 RM/MN 并行(共享 src/config/、config/config.toml、src/api/、src/lib.rs、http_server)。G-10 闭合边: AU-05 -> RM-01;AU-05 -> MN-02;ITW-02 -> MN-05;ITW-03 -> MN-06;RM-03 -> MN-02。不得以 rebase 代替依赖边。

发布者: 单一 Agent/人负责 REL-01 的 C/D 组(家族唯一推送点)。

发布窗口顺序: AU → ITW → RM → MN → DOC-01 → REL-01(禁止子卡独立 bump/推送)。

Phase 0: 设计落盘

目标: website-auth.md 成为会话接入单一事实源(含 compose 拓扑);website-mail 契约落盘(MN-01)。

进入条件: ER-01;DEP-01 契约已核对。

退出条件: AU-01 与 MN-01 locally-accepted + review PASS(complete 继承 REL-01)。

Phase 1: Website 会话接入

目标: 真实 SessionUser + 类型化 [oauth] + 字段重命名 + 进程内验收测试。

进入条件: AU-01 review PASS。

退出条件: AU-05 locally-accepted + review PASS(Acceptance=complete 仅随 REL-01)。

Phase 1b: next-app compose 同栈会话 IT

目标: website-next 登记进 mega2-it;与 mega2 同启;真实 cookie 黑盒绿。

进入条件: AU-01 review PASS;DEP-05(../website)就位;AU-03 对 ITW-02 已满足。

退出条件: ITW-03 locally-accepted + review PASS(complete 随 REL-01)。

Phase 2: Chat/Notes 整栈退场

目标: 表面→通知→模块→DROP→sidebar 完成。

进入条件: AU-05 locally-accepted(禁止与 AU 实现并行;见 G-10)。

退出条件: RM-06 locally-accepted + review PASS(complete 随 REL-01)。

Phase 2b: 邮件投递退场并改接 website

目标: dispatcher 去 email-primary;删除本仓 mail 表面与 email 表;产品事件经 website 发信;同步更新 compose / .env.test* / CI;测试与文档去「本仓→Mailpit」门。

进入条件: MN-01 review PASS;RM-03 locally-accepted(chat 邮件触发器已删);DEP-06 对 MN-05 可用;ITW-02 locally-accepted(compose 写集已交给 MN-05);AU-05 locally-accepted。

退出条件: MN-06 locally-accepted + review PASS(含 compose 与文档清单全勾;complete 随 REL-01)。

Phase 3: 文档收口与 minor 发布

目标: plan-long / 重构文档 / test-infra / mail+notification 同步;minor 发布。

进入条件: AU-05、ITW-03、RM-06、MN-06 均已 locally-accepted 且 review PASS。

退出条件: REL-01 Acceptance=complete(家族子卡随之 complete);计划完成判据清单勾选。

任务卡

字段全局默认与例外

  • Release boundary 默认: family child of REL-WA-01(实现/删除/迁移卡);docs=no-release;REL-01=family release point of REL-WA-01
  • Task type 默认: implementation
  • Rollback mode 默认: revert
  • Migration and rollback 默认: N/A
  • Security and privacy 默认: 继承 GC-07、GC-11;会话 fail-closed
  • Performance budget 默认: 继承 GC-10
  • Docs and compatibility impact 默认: 按 GC-05 同步相关 docs 与 config 示例

默认覆盖:

任务 偏离的字段 取值与理由
AU-01, DOC-01, MN-01 Release boundary / Version increment / Release write set no-release / N/A / N/A;C/D from REL-01
AU-02..AU-05, RM-01..RM-06, ITW-01..ITW-03, MN-02..MN-06 Release boundary / Version / Release write set / C/D family child of REL-WA-01 / N/A / N/A / from REL-01
REL-01 Release boundary family release point of REL-WA-01;minor
RM-03(若含删种子数据的 migration 片段) 见 RM-03/RM-05 event type 删除可并入 RM-05 migration
RM-05 Rollback mode / Task type / Version increment forward-only / migration / N/A(breaking 聚合在 REL-01 minor)
MN-04 Rollback mode / Task type forward-only / migration
ITW-01 Docs and compatibility impact 必须同步 test-infra.md(compose 登记)
MN-06 Docs and compatibility impact 必须同步 compose + .env.test* + test-infra/development/integration/mail/notification/README(ADR-WA-08 §7)
REL-01 Task type / Version increment release / minor

规则 waiver: 无。

任务卡粒度审计表

任务 type axis recovery complete self-contained AC VER landing / prod-files scope deps writeset release split-from exception
AU-01 docs website-auth 设计落盘 revert 文档 yes yes 7/20 3/20 1/1 S DEP-01 no-overlap no-release N/A N/A
AU-02 implementation OAuthConfig 类型化 revert yes yes 6/8 4/8 2/6 M AU-01 no-overlap REL-WA-01 child N/A N/A
AU-03 implementation SessionUser 真接通 revert yes yes 7/8 4/8 3/8 M AU-02 序列化于 AU-02 REL-WA-01 child N/A N/A
AU-04 implementation website_user_id 重命名 revert yes yes 4/8 3/8 2/5 M AU-03 序列化于 AU-03 REL-WA-01 child N/A N/A
AU-05 implementation 会话验收测试 revert yes yes 5/8 3/8 1/3 S AU-04 序列化于 AU-04 REL-WA-01 child N/A N/A
ITW-01 implementation website-next compose 登记 revert yes yes 6/8 4/8 2/3 M AU-01, DEP-05 no-overlap REL-WA-01 child N/A N/A
ITW-02 implementation 同栈 oauth/CORS 接线 revert yes yes 5/8 3/8 2/4 M ITW-01, AU-03 序列化于 ITW-01 REL-WA-01 child N/A N/A
ITW-03 implementation 真实 cookie 栈级黑盒 revert yes yes 5/8 5/8 1/2 M ITW-02, AU-05 序列化于 ITW-02 REL-WA-01 child N/A N/A
RM-01 removal Chat HTTP/CLI 退场 revert yes yes 7/8 4/8 4/10 M AU-05 no-overlap REL-WA-01 child N/A N/A
RM-02 removal Notes API 退场 revert yes yes 5/8 3/8 2/4 S RM-01 序列化于 RM-01 REL-WA-01 child N/A N/A
RM-03 removal Chat 通知退场 revert yes yes 5/8 3/8 2/4 M RM-02 序列化于 RM-02 REL-WA-01 child N/A N/A
RM-04 removal 模块/实体/ChatConfig 删除 revert yes yes 5/8 3/8 4/12 M RM-03 序列化于 RM-03 REL-WA-01 child N/A N/A
RM-05 migration DROP chat/notes 表 forward-only yes yes 5/8 3/8 2/3 M RM-04 序列化于 RM-04 REL-WA-01 child N/A N/A
RM-06 implementation Sidebar 默认清理 revert yes yes 3/8 2/8 2/2 S RM-05 序列化于 RM-05 REL-WA-01 child N/A N/A
MN-01 docs website 邮件契约落盘 revert 文档 yes yes 7/20 3/20 1/1 S AU-01 no-overlap no-release N/A N/A
MN-02 implementation dispatcher 去 email-primary revert yes yes 6/8 4/8 3/8 M MN-01, RM-03, AU-05 no-overlap REL-WA-01 child N/A N/A
MN-03 removal 删除 mail 模块与 admin 邮件 API revert yes yes 7/8 4/8 4/12 M MN-02 序列化于 MN-02 REL-WA-01 child N/A N/A
MN-04 migration DROP email_jobs 表 forward-only yes yes 4/8 3/8 2/3 M MN-03 序列化于 MN-03 REL-WA-01 child N/A N/A
MN-05 implementation triggers 改调 website 邮件 API revert yes yes 6/8 3/8 4/8 M MN-04, DEP-06, ITW-02 序列化于 ITW-02 REL-WA-01 child N/A N/A
MN-06 implementation compose/测试/CI/文档去本仓邮件门 revert yes yes 8/8 5/8 6/10 M MN-05, ITW-03 序列化于 ITW-03 REL-WA-01 child N/A N/A
DOC-01 docs 长期/重构文档收口 revert 文档 yes yes 9/20 4/20 8/8 M AU-05, RM-06, ITW-03, MN-06 no-overlap no-release N/A N/A
REL-01 release minor 发布点 immutable-release yes yes 5/12 2/12 0/0 S DOC-01 N/A REL-WA-01 point N/A N/A

Task AU-01: 落盘 website-auth 接入设计

Task type: docs

Lifecycle / Acceptance: done / complete

Description: 新建 docs/refactoring/website-auth.md,把 ADR-WA-01..04 与 ADR-WA-07 的信任路径、字段映射、双轨认证、配置键、CORS/cookie 域、mega2-it + website-next 同栈拓扑、失败语义、测试 double 与栈级 IT 协议写成可执行事实源。

Out of scope:

  • 不改代码或配置(由 AU-02 / ITW-01 承接)
  • 不写 chat 删除设计(由 RM 卡 / ADR-WA-05 承接)

Current evidence:

事实 证据
无 website-auth 文档 ls docs/refactoring/ 无该文件
Better Auth get-session 契约 website libs/auth/(DEP-01)
本仓 stub / 死 store oauth/mod.rs:167-174;tinyship_store.rs
IT 栈无 next-app docker-compose.test.yml;GAP-08

Acceptance criteria:

本卡 AC/VER 于 2026-09-15 据实回填勾选(AU-01 早已随 REL-01 / v0.2.x 落地,其余各卡均已勾选,仅 AU-01/AU-02 两张遗漏)。证据取自当前 docs/refactoring/website-auth.md;文中 sibling 仓已按 2026-08-21 更名合法演进为 website application checkout(apps/web),角色名 website 与派生标识按 plan-long.md 术语条不改名。

  • docs/refactoring/website-auth.md 存在且含:信任路径图或等价步骤、字段映射表、双轨表、配置键、cookie 名、失败语义、测试 double 规则 —— §1 信任路径(:21)、§2 字段映射(:76)、§3 双轨认证(:95)、§5 配置键(:126-128)
  • 文档含 compose 同栈拓扑(服务名 website-next、profile web、端口 17001、MEGA_OAUTH__WEBSITE_API_BASE_URL 容器内 URL、与 Mega demo 的差异说明) —— :141-146 拓扑表(服务名、build context、宿主映射 127.0.0.1:17001:7001)、:126 容器内 URL http://website-next:7001
  • 文档记录 website 核对 revision / 日期(DEP-01) —— :14 核对日 2026-09-04、sibling website application checkout(apps/web)
  • 明确禁止生产旁路与 Campsite 回流 —— :48-51「生产路径禁止」列明配置/环境变量旁路 get-session 与恢复 CampsiteApiStore / api_store_backend 双后端
  • 明确 chat attachments 与(将由 MN-04 DROP 的)email_job_attachments 区分;不在 AU-01 要求永久保留邮件表 —— :220「chat 域表 attachments ≠ 邮件 outbox 表 email_job_attachments」
  • 内部链接可解析;不引入悬空 docs/*.md 路径 —— 存在性检查通过
  • libra status 无越界生产代码改动 —— 落地当时结论;本次回填只改本计划文件

Verification:

  • test -f docs/refactoring/website-auth.md —— 存在
  • 章节标题守卫:rg -n '信任路径|字段映射|双轨|session_cookie|FixedUser|banned|website-next|profile web' docs/refactoring/website-auth.md(须有命中) —— 24 处命中
  • libra status --short --branch 变更仅限本卡 Deliverables —— 落地当时结论

Dependencies: DEP-01

Deliverables: docs/refactoring/website-auth.md

Implementation write set: docs/refactoring/website-auth.md

Release write set: N/A

Files likely touched: docs/refactoring/website-auth.md

Docs and compatibility impact: 新建开发文档

Rollback mode: revert

Migration and rollback: N/A

Security and privacy: 文档须强调 cookie 转发与 redaction(日志不得打印完整 session token)

Performance budget: N/A

Estimated scope: S

Version increment: N/A

Release boundary: no-release

C/D coverage from: REL-01

Granularity: type=docs; axis=website-auth设计落盘; recovery=revert文档; complete=yes; self-contained=yes; AC=7/20; VER=3/20; landing=1; prod-files=1; scope=S; deps=DEP-01; writeset=no-overlap; release=no-release; split-from=N/A; exception=N/A


Task AU-02: 类型化 OAuthConfig 并清除遗留键

Task type: implementation

Lifecycle / Acceptance: done / complete

Description: 将 [oauth] 扩展为可消费的 website_api_base_url + session_cookie_names,删除 Campsite/Tinyship 遗留忽略键,并让 config validate / service http 启动路径在缺失基址时 fail-closed。

Out of scope:

  • 不接线 SessionUser(由 AU-03 承接)
  • 不删 chat 配置(由 RM-04 承接)

Current evidence:

事实 证据
OAuthConfig 仅 CORS src/config/model.rs:426-432
toml 遗留键 config/config.toml:184-187

Acceptance criteria:

本卡 AC/VER 于 2026-09-15 据实回填勾选;证据取自当前 src/config/、config/config.toml。

  • OAuthConfig 含 website_api_base_url、session_cookie_names(默认两 Better Auth 名)、allowed_cors_origins —— src/config/model.rs:469,473,475-479(session_cookie_names 经 default_session_cookie_names() 提供默认两名)
  • 遗留键 campsite_api_domain / tinyship_api_domain / api_store_backend 从示例与校验白名单中移除,出现则校验失败或不再被忽略(与现有严格度对齐,开工选一种并测) —— rg 'campsite_api_domain|tinyship_api_domain|api_store_backend' config/config.toml src/config/ 零命中
  • service http 在基址为空时启动失败(可操作错误) —— src/config/validate.rs:160-173:service http requires [oauth] with oauth.website_api_base_url (see docs/refactoring/website-auth.md)
  • config validate 对默认示例配置通过 —— 落地当时结论
  • validate/reload/source/testing 同步 —— src/config/validate.rs:151 调用点已接入
  • config/config.toml 注释指向 website-auth.md —— config/config.toml:174

Verification:

  • cargo run -p mega2 -- --config config/config.toml config validate —— 落地当时结论
  • 缺 website_api_base_url 的临时配置:config validate 或 service http 非零退出 —— 落地当时结论;判定点仍在 src/config/validate.rs:160-173
  • rg -n 'campsite_api_domain|tinyship_api_domain|api_store_backend' config/config.toml src/config 零命中(按 Verification 判定口径区分 rg 退出码) —— 2026-09-15 复跑:rc=1(零命中)
  • source .env.test && cargo test -p mega2-core 'config::' —— 落地当时结论;package 已由 plan-20260824 ORB-06 重命名,今日等价命令为 cargo test -p mega2 --lib 'config::'

Dependencies: AU-01

Deliverables: N/A

Implementation write set: src/config/、config/config.toml(及本卡测试)

Release write set: N/A

Files likely touched: src/config/model.rs、validate.rs、reload.rs、source.rs、testing.rs、config/config.toml

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=OAuthConfig类型化; recovery=revert; complete=yes; self-contained=yes; AC=6/8; VER=4/8; landing=2; prod-files=6; scope=M; deps=AU-01; writeset=no-overlap; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task AU-03: WebsiteSessionStore 与真实 SessionUser

Task type: implementation

Lifecycle / Acceptance: done / complete

执行后注记(2026-08-06 完成度复审): BrowserSessionStore::Fixed / FixedUserSessionStore 原为无条件编译(仅构造点在 #[cfg(test)] 内),与 website-auth.md「test double 不得编译进默认 service http 生产二进制路径」的约束存在字面偏差;复审已将变体、结构体与 match 分支统一加 #[cfg(test)] 门控,生产二进制自此不含该 double。VER-4 冒烟亦于复审日补证(见勾选注)。

Description: 将 tinyship store 演进为 WebsiteSessionStore,支持双 cookie 名与 ban 拒绝,注入 API state,恢复与 Mega 同类的 Cookie→get-session SessionUser;删除 campsite_store 与双后端 enum。

Out of scope:

  • 不重命名 campsite_user_id 字段(由 AU-04 承接)
  • 不改 Git token 路径

Current evidence:

事实 证据
SessionUser stub oauth/mod.rs:167-174
Tinyship 单 cookie tinyship_store.rs:15
Mega 对照 mega/mono/src/api/oauth/mod.rs SessionUser

Acceptance criteria:

  • WebsiteSessionStore(或最终模块名)实现 get-session 调用;双 cookie 名
  • banned=true 映射为未认证(拒绝)
  • SessionUser 从 state 取 store 并读 Cookie;无 cookie → AuthRedirect
  • campsite_store.rs 删除;无 OAuthApiStore::Campsite 分支
  • http_server / MonoApiServiceState 注入 store;CORS 仍用 allowed_cors_origins
  • 生产路径无硬编码 admin
  • 客户端构造无裸 expect(GC-11)

Verification:

  • rg -n 'admin@gitmono\.test|campsite_store|CampsiteApiStore' src/api/oauth 零命中(口径模板)
  • source .env.test && cargo test -p mega2-core 'api::oauth'
  • cargo build -p mega2-core 0 警告 0 错误(本卡中间门;完整三门在 C 组)
  • OpenAPI/服务启动冒烟:带配置的 service http 能绑定端口(手工 sanitized;2026-08-06 完成度复审补证:隔离库 + 全新 MEGA_BASE_DIR 下 service http 绑定 127.0.0.1:18973 并 200 返回 /api/openapi.json,SIGINT 退出,临时库已 drop)

Dependencies: AU-02

Implementation write set: src/api/oauth/、src/api/mod.rs、src/server/http_server.rs(及相关测试)

Release write set: N/A

Rollback mode: revert

Security and privacy: 日志禁止打印完整 session token;外呼超时有界

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=SessionUser真接通; recovery=revert; complete=yes; self-contained=yes; AC=7/8; VER=4/8; landing=3; prod-files=8; scope=M; deps=AU-02; writeset=序列化于AU-02; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task AU-04: LoginUser.website_user_id 重命名

Task type: implementation

Lifecycle / Acceptance: done / complete

执行后注记(2026-08-06 完成度复审): 原 VER cargo test -p mega2-core 'api::router::gpg' 在收口时命中 0 个用例(gpg_router 无测试模块,命令空跑通过,属幽灵验收);重命名本身由零命中守卫与编译门真实保障。复审已在 src/api/router/gpg_router.rs 增补 focused 测试(路由注册断言 + website_user_id 序列化 pin / campsite_user_id 缺席断言),该 VER 现命中 2 个用例并通过。

Description: 将 LoginUser.campsite_user_id 重命名为 website_user_id,更新 From 映射、gpg_router 与全部引用。

Out of scope:

  • 不改会话接线逻辑(AU-03)
  • 不改 username 映射规则(ADR-WA-02 已定,仅改字段名)

Current evidence:

事实 证据
字段名 oauth/model.rs:58
GPG 使用 gpg_router.rs:36、:56、:75

Acceptance criteria:

  • 结构体字段与 JSON 序列化为 website_user_id
  • website/Campsite JSON From 实现写入新字段
  • gpg_router 与测试全部改用新字段
  • rg campsite_user_id src 零命中

Verification:

  • 零命中守卫:rg -n 'campsite_user_id' src(口径模板)
  • source .env.test && cargo test -p mega2-core 'api::oauth'
  • source .env.test && cargo test -p mega2-core 'api::router::gpg'

Dependencies: AU-03

Implementation write set: src/api/oauth/model.rs、src/api/router/gpg_router.rs、其它命中引用文件

Release write set: N/A

Docs and compatibility impact: breaking JSON 字段;REL-01 聚合说明

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=website_user_id重命名; recovery=revert; complete=yes; self-contained=yes; AC=4/8; VER=3/8; landing=2; prod-files=5; scope=M; deps=AU-03; writeset=序列化于AU-03; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task AU-05: 会话路径验收测试

Task type: implementation

Lifecycle / Acceptance: done / complete

Description: 用 FixedUserSessionStore(或等价)覆盖成功会话、无 cookie、banned、空 name 回退/拒绝;更新原依赖硬编码 admin 的非 chat 测试(chat 测试由 RM-01 删除)。

Out of scope:

  • 不测真实外网 website(DEP-01 契约已在文档固定)
  • 不测 chat router(RM-01 删除)
  • 不做 compose 同栈真实 next-app 黑盒(由 ITW-03 承接;本卡仅 FixedUser / 进程内)

Current evidence:

事实 证据
多处测试假定 admin chat_router tests 等;AU-03 后其它路由器可能受影响

Acceptance criteria:

  • 新增 focused 用例:有效 FixedUser → handler 见到对应用户
  • 无 cookie → 拒绝
  • banned → 拒绝
  • 空 name + 有 email → username=local-part;空 name 且无可用 email → 拒绝
  • 非 chat 的既有 API 测试不再依赖隐式 admin stub(改为 FixedUser 或 AccessTokenUser)

Verification:

  • source .env.test && cargo test -p mega2-core 'api::oauth' -- --nocapture
  • 新用例名过滤守卫:输出含本卡新增测试名且 ok
  • rg -n 'admin@gitmono\.test' src 零命中(或仅允许 website-auth.md / 注释白名单——若有白名单须在卡内写明;默认零命中)

Dependencies: AU-04

Implementation write set: src/api/oauth/ 测试、受影响非 chat router 测试

Release write set: N/A

Rollback mode: revert

Estimated scope: S

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=会话验收测试; recovery=revert; complete=yes; self-contained=yes; AC=5/8; VER=3/8; landing=1; prod-files=3; scope=S; deps=AU-04; writeset=序列化于AU-04; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task RM-01: Chat HTTP/CLI 表面退场

Task type: removal

Lifecycle / Acceptance: done / complete

Description: 从路由、OpenAPI、http state、chat-migrate CLI 卸载 chat 表面;src/chat 模块可暂留到 RM-04,但不得再被 API/CLI 引用。

Out of scope:

  • 不删实体/表(RM-04/RM-05)
  • 不删通知触发器(RM-03)

Current evidence:

事实 证据
router merge api_router.rs:56
chat-migrate 注册 commands/mod.rs:37、:46
chat_events state http_server.rs:582;api/mod.rs:49

Acceptance criteria:

  • 符合 ADR-WA-06:无单独弃用窗口;breaking 由 REL-01 发布说明覆盖(本卡不设 shim)
  • api_router 不再 merge chat_router
  • OpenAPI 无 chat tag/路径
  • chat-migrate 从 builtin / builtin_exec / load_mode 三处移除
  • MonoApiServiceState 无 chat_events / channel chat helpers
  • cli::tests / commands:: 断言无 chat-migrate
  • chat_router 文件可先删除或变为未引用(推荐本卡直接删 chat_router.rs 以减噪音)

Verification:

  • rg -n 'chat_router|chat-migrate|chat_events|InMemoryChatEvents' src/api src/commands src/server 零命中
  • cargo test -p mega2-core 'cli::tests'
  • cargo test -p mega2-core 'commands::'
  • 启动后 /api/openapi.json 无 /chat(sanitized 证据)

Dependencies: AU-05

Implementation write set: src/api/api_router.rs、src/api/mod.rs、src/api/router/、src/api/api_doc.rs、src/commands/、src/server/http_server.rs

Release write set: N/A

Docs and compatibility impact: breaking API/CLI;REL-01 说明

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=removal; axis=Chat HTTP/CLI退场; recovery=revert; complete=yes; self-contained=yes; AC=7/8; VER=4/8; landing=4; prod-files=10; scope=M; deps=AU-05; writeset=no-overlap; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task RM-02: Notes API 退场

Task type: removal

Lifecycle / Acceptance: done / complete

Description: 卸载 src/api/notes 路由与模块注册。

Out of scope:

  • 不删 notes 实体/表(RM-04/RM-05)

Current evidence:

事实 证据
note_router merge api_router.rs:42
api/notes/ note_router.rs、model.rs

Acceptance criteria:

  • 符合 ADR-WA-06:无单独弃用窗口;breaking 由 REL-01 发布说明覆盖(本卡不设 shim)
  • api_router 不再 merge notes
  • src/api/notes/ 删除
  • OpenAPI 无 notes sync_state 路径
  • api:: 测试无 notes 引用失败

Verification:

  • rg -n 'note_router|api::notes|notes::' src/api 零命中
  • cargo test -p mega2-core 'api::' -- --nocapture
  • OpenAPI 无 sync_state notes 路径

Dependencies: RM-01

Implementation write set: src/api/notes/、src/api/api_router.rs、src/api/mod.rs

Release write set: N/A

Rollback mode: revert

Estimated scope: S

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=removal; axis=Notes API退场; recovery=revert; complete=yes; self-contained=yes; AC=5/8; VER=3/8; landing=2; prod-files=4; scope=S; deps=RM-01; writeset=序列化于RM-01; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task RM-03: Chat 通知触发器退场

Task type: removal

Lifecycle / Acceptance: done / complete

Description: 从 notification/triggers.rs 删除 chat mention/reply 事件常量、ensure、入队、邮件模板键与测试;event type 种子行的 DB 清理并入 RM-05 migration(本卡只清代码)。

Out of scope:

  • 不 DROP 表(RM-05)
  • 不删 inbox 通用通知

Current evidence:

事实 证据
事件常量 triggers.rs:26-27
测试 triggers.rs ~1117+

Acceptance criteria:

  • 无 EVENT_CHAT_* / on_chat_* / chat mail template keys
  • 相关单元测试删除
  • notification.md 中 chat 段删除或改「已移除」可由 DOC-01 收口;本卡至少去掉代码引用
  • 非 chat 通知测试仍绿
  • rg 'chat\.mention|chat\.reply|EVENT_CHAT' src/notification 零命中

Verification:

  • 零命中守卫(上条路径)
  • source .env.test && cargo test -p mega2-core 'notification::'
  • source .env.test && cargo test -p mega2-core 'notification::triggers'

Dependencies: RM-02

Implementation write set: src/notification/triggers.rs(及同模块测试)

Release write set: N/A

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=removal; axis=Chat通知退场; recovery=revert; complete=yes; self-contained=yes; AC=5/8; VER=3/8; landing=2; prod-files=4; scope=M; deps=RM-02; writeset=序列化于RM-02; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task RM-04: 删除 chat/notes 模块、实体、storage、ChatConfig

Task type: removal

Lifecycle / Acceptance: done / complete

Description: 删除 src/chat/、contract/api/chat、chat/notes 相关 storage 与 callisto 模块注册、ChatConfig/[chat]、lib.rs 的 mod chat;保持历史 migration 文件。

Out of scope:

  • 不 DROP 物理表(RM-05)
  • 不改 sidebar 默认(RM-06)

Current evidence:

事实 证据
pub mod chat lib.rs:15
ChatConfig model.rs:50、:436
callisto 实体 channel.rs、message.rs、notes.rs 等

Acceptance criteria:

  • src/chat/、src/contract/api/chat/、src/commands/chat_migrate.rs 删除
  • callisto 去掉 chat/notes 实体 mod;storage mod 去掉对应字段/模块
  • [chat] 与 ChatConfig 删除;config 测试更新
  • cargo build --tests 通过(表仍在 DB,但代码无引用)
  • rg -n 'ChatConfig|pub mod chat|channel_storage|note_storage' src 零命中(允许 migration 历史文件命中)

Verification:

  • 零命中守卫(限定 src/chat、src/contract/api/chat、src/config 生产路径)
  • cargo build --tests 0 错误 0 警告
  • cargo run -p mega2 -- --config config/config.toml config validate

Dependencies: RM-03

Implementation write set: src/chat/、src/callisto/、src/jupiter/storage/、src/contract/api/、src/config/、src/lib.rs、config/config.toml

Release write set: N/A

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=removal; axis=模块实体ChatConfig删除; recovery=revert; complete=yes; self-contained=yes; AC=5/8; VER=3/8; landing=4; prod-files=12; scope=M; deps=RM-03; writeset=序列化于RM-03; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task RM-05: forward-only DROP chat/notes schema

Task type: migration

Lifecycle / Acceptance: done / complete

Description: 新增 m20260731_*_drop_chat_and_notes:DROP chat/notes 相关表(不含 reactions / custom_reactions,见 ADR-WA-05 ER-02);DELETE chat.* notification event types;在 migrations() 登记;附 apply_migrations 集成测试。down 为 no-op。

Out of scope:

  • 不 DROP user_inbox_notifications(in-app 保留)
  • 不在本卡 DROP email_jobs / email_job_attachments(由 MN-04 承接)

Current evidence:

事实 证据
表经 chat/notes 迁移创建 migration/mod.rs 注册行
种子含 chat 事件 m20260630_000000_seed_notification_event_types

Acceptance criteria:

  • 新 migration 在 migrations() 末尾登记(若 MN-04 已合入则按时间戳排序,不得打乱历史链)
  • DROP 列表覆盖:channels、channel_memberships、channel_membership_updates、messages、message_notifications、attachments、open_graph_links、notes、note_views、non_member_note_views
  • 明确不 DROP: reactions、custom_reactions(ER-02:CL/Issue 仍用 reactions;FK 保留 custom_reactions)
  • 删除 chat.mention.created / chat.reply.created event type 行(及依赖 prefs 行若有 FK)
  • down 显式 no-op;文档/卡内写明 forward-only
  • 集成测试:空库 up 后相关表不存在

Verification:

  • source .env.test && cargo test 'migration::runner::tests::test_drop_chat_and_notes_schema'
  • rg -n 'm20260731_.*drop_chat' src/jupiter/migration/mod.rs 有命中
  • 守卫:本卡 DROP 语句不包含 email_jobs(留给 MN-04)

Dependencies: RM-04

Implementation write set: src/jupiter/migration/(新文件 + mod.rs)及测试

Release write set: N/A

Rollback mode: forward-only

Migration and rollback: up=DROP/DELETE;down=no-op;生产前备份;恢复=从备份前滚或建新库

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=migration; axis=DROP chat/notes表; recovery=forward-only备份恢复; complete=yes; self-contained=yes; AC=5/8; VER=3/8; landing=2; prod-files=3; scope=M; deps=RM-04; writeset=序列化于RM-04; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task RM-06: Sidebar 默认项清理

Task type: implementation

Lifecycle / Acceptance: done / complete

Description: 从 config/config.toml 与 SidebarConfig::default 移除 chat 与 notes(Docs) 默认项。

Out of scope:

  • 不迁移已有 DB 中动态 sidebar 行(既有部署可用 API 清理;见 DEFER-WA-03)

Current evidence:

事实 证据
toml sidebar config/config.toml:277-278
Default SidebarConfig(model.rs:1025 起)

Acceptance criteria:

  • 默认配置无 public_id = "chat" / notes→/notes Docs 项
  • SidebarConfig::default 一致
  • config 测试更新

Verification:

  • rg -n 'public_id = \"chat\"|/notes' config/config.toml src/config/model.rs 按预期零命中(或仅历史注释——默认零命中)
  • cargo run -p mega2 -- --config config/config.toml config validate

Dependencies: RM-05

Implementation write set: config/config.toml、src/config/model.rs

Release write set: N/A

Rollback mode: revert

Estimated scope: S

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=Sidebar默认清理; recovery=revert; complete=yes; self-contained=yes; AC=3/8; VER=2/8; landing=2; prod-files=2; scope=S; deps=RM-05; writeset=序列化于RM-05; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task ITW-01: 登记 website-next 进 mega2-it compose

Task type: implementation

Lifecycle / Acceptance: done / complete

执行后注记(2026-08-06 完成度复审): 收口后(plan-20260803 期间,mega2 39f7433)website IT 账户库已由「容器内 SQLite」切换为共享 postgres 服务上的隔离 website 库(website-db-init + drizzle-kit push),满足本卡 AC4 的「或等价隔离库」口径与 ADR-WA-07 的隔离目标(禁止混用 mega2 业务库不变);本卡与 ADR-WA-07 描述中的「IT 默认 SQLite」自此按收口日快照阅读,现行拓扑以 test-infra.md website-next 条目为准。

Description: 按 test-infra.md checklist 在 docker-compose.test.yml 新增 website-next(profile web),build sibling ../website 的 apps/next-app/Dockerfile,加入 mega2-test-net,宿主 127.0.0.1:17001:7001,IT 默认 SQLite 账户库;同步 test-infra.md 登记条目。

Out of scope:

  • 不接线 mega2 oauth 环境变量(由 ITW-02 承接)
  • 不写会话黑盒用例(由 ITW-03 承接)
  • 不迁入 website 产品 compose 的 nuxt/tanstack

Current evidence:

事实 证据
无 website-next 服务 docker-compose.test.yml
website Dockerfile 端口 7001 ../website/apps/next-app/Dockerfile
登记规范 docs/refactoring/test-infra.md

Acceptance criteria:

  • website-next 服务存在,profiles: ["web"],不参与默认 up -d --wait
  • build context / dockerfile 指向 sibling website;pull_policy 与本地/CI 策略写明
  • healthcheck 可判定就绪(至少 TCP 或 HTTP 探活到 next 进程;具体命令开工按 Dockerfile 固化)
  • IT 账户库为容器内 SQLite(或等价隔离库),不使用 mega2 业务库名/URL
  • test-infra.md 新增「website-next」登记表(含 profiles、端口、清理、CI 入口草案)
  • docker compose -p mega2-it -f docker-compose.test.yml --profile web up -d --wait website-next 在 DEP-05 满足时可健康

Verification:

  • rg -n 'website-next|profiles: \[\"web\"\]' docker-compose.test.yml
  • rg -n 'website-next' docs/refactoring/test-infra.md
  • bash -c 'test -d ../website/apps/next-app && test -f ../website/apps/next-app/Dockerfile'
  • docker compose -p mega2-it -f docker-compose.test.yml --profile web up -d --wait website-next 成功后 down(含 --profile web)

Dependencies: AU-01, DEP-05

Implementation write set: docker-compose.test.yml、docs/refactoring/test-infra.md(及本卡所需的 IT env 示例片段,若有)

Release write set: N/A

Rollback mode: revert

Security and privacy: IT 凭据仅用公开测试值;不把生产 .env 打进镜像层

Performance budget: 首次镜像构建允许较长 wall-clock;登记建议上限(开工写定,建议 ≤ 20min CI)

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=website-next compose登记; recovery=revert; complete=yes; self-contained=yes; AC=6/8; VER=4/8; landing=2; prod-files=3; scope=M; deps=AU-01,DEP-05; writeset=no-overlap; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task ITW-02: 同栈接线 oauth 基址与 CORS

Task type: implementation

Lifecycle / Acceptance: done / complete

Description: 在 --profile app --profile web 拓扑下,为 mega2 注入 MEGA_OAUTH__WEBSITE_API_BASE_URL=http://website-next:7001,CORS allowlist 含宿主映射 origin;保证容器内 mega2 能 get-session 到 website-next。

Out of scope:

  • 不实现黑盒登录矩阵(ITW-03)
  • 不改 AU-03 的 SessionUser 逻辑(仅消费)

Current evidence:

事实 证据
mega2 服务块无 oauth website URL docker-compose.test.yml mega2.environment
AU-02/03 落地后配置键可用 依赖 AU-03

Acceptance criteria:

  • compose 中 mega2(或 profile 覆盖文件)含 website API 基址指向 http://website-next:7001
  • CORS / allowed_cors_origins 含 http://127.0.0.1:17001(及文档列出的 IT origin)
  • depends_on:mega2 在启用 web 联调时等待 website-next healthy(或文档写明启动顺序与重试)
  • 从 mega2 容器或同网络客户端对 http://website-next:7001/api/auth/get-session 可得非连接错误响应(未登录可为 null)
  • website-auth.md / test-infra.md 同步该接线

Verification:

  • rg -n 'WEBSITE_API_BASE_URL|website-next:7001|17001' docker-compose.test.yml docs/refactoring
  • docker compose -p mega2-it -f docker-compose.test.yml --profile app --profile web up -d --wait 后,容器网络内 curl get-session 退出码 0(body 可为 null)
  • docker compose -p mega2-it -f docker-compose.test.yml --profile app --profile web down -v

Dependencies: ITW-01, AU-03

Implementation write set: docker-compose.test.yml、docs/refactoring/test-infra.md、docs/refactoring/website-auth.md(接线节)

Release write set: N/A

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=同栈oauth/CORS接线; recovery=revert; complete=yes; self-contained=yes; AC=5/8; VER=3/8; landing=2; prod-files=4; scope=M; deps=ITW-01,AU-03; writeset=序列化于ITW-01; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task ITW-03: 真实 cookie 栈级黑盒

Task type: implementation

Lifecycle / Acceptance: done / complete

Description: 新增 bin/tests/integration_website_auth.rs(或等价 target):在 --profile app --profile web 已就绪前提下,对 website-next 执行 sign-up/sign-in,携带 session cookie 请求 mega2 /api/v1/user,断言身份字段与 get-session 一致;无 cookie 则拒绝。同步 integration 覆盖矩阵与 test-infra CI 入口(可 soft-skip 当 profile 未启,但 skip 不得冒充通过——须显式 WEBSITE_IT=1 或端口探测失败才 skip,且 CI 启用 web 时必须跑到 ok)。

Out of scope:

  • 不做 Playwright 浏览器 E2E(DEFER-WA-05)
  • 不测 chat/notes(已退场)

Current evidence:

事实 证据
现有栈级 smoke integration_compose_mega2_http_smoke
无 website 会话黑盒 bin/tests/ 无对应 target

Acceptance criteria:

  • 新集成 target 存在且文档化前置(compose profiles、env)
  • 用例:登录成功 → /api/v1/user 返回映射后的 username / website_user_id
  • 用例:无 cookie → 会话路径拒绝(非 200 带 admin)
  • CI:config-validation.yml(或专段)在 sibling website 可用时拉起 --profile web 并跑本 target;不可用时 job 策略书面化(本卡选:要求 CI checkout website,与 orbit 类似,缺则失败)
  • docs/refactoring/integration.md 覆盖矩阵登记本 target

Verification:

  • source .env.test && WEBSITE_IT=1 cargo test -p mega2 --test integration_website_auth -- --test-threads=1(2026-08-01:live 运行通过)
  • 输出含本卡具名用例且 ok;禁止仅见 SKIP 当作通过(2026-08-01:1 passed)
  • rg -n 'integration_website_auth' docs/refactoring/integration.md docs/refactoring/test-infra.md

Dependencies: ITW-02, AU-05

Implementation write set: bin/tests/integration_website_auth.rs、.github/workflows/config-validation.yml(若改)、docs/refactoring/integration.md、docs/refactoring/test-infra.md

Release write set: N/A

Rollback mode: revert

Security and privacy: 测试用户邮箱/口令仅为 IT 抛掷值;日志脱敏 cookie

Performance budget: 单 target wall-clock 建议 ≤ 10min(含已构建镜像)

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=真实cookie栈级黑盒; recovery=revert; complete=yes; self-contained=yes; AC=5/8; VER=5/8; landing=1; prod-files=2; scope=M; deps=ITW-02,AU-05; writeset=序列化于ITW-02; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task MN-01: 落盘 website 邮件投递契约

Task type: docs

Lifecycle / Acceptance: done / complete

Description: 新建 docs/refactoring/website-mail.md(或作为 website-auth.md 专章——本卡选独立文件以免会话文档膨胀),固化 ADR-WA-08:website 内部邮件 API 路径/鉴权/JSON、本仓删除清单、dispatcher primary 迁移、与 auth 事务邮件的边界,以及 compose / mailpit / env 拓扑(消费方、端口、与会话 IT 的关系)。

Out of scope:

  • 不改代码(MN-02+)
  • 不在 website 仓实现 API(DEP-06;契约由本卡定义)
  • 不直接改 docker-compose.test.yml(MN-05/MN-06 落地;本卡只定拓扑)

Current evidence:

事实 证据
本仓 email-primary notification/dispatcher.rs;src/mail/
website 仅认证邮件 website libs/email/
compose 仍挂本仓 mail env docker-compose.test.yml MEGA_MAIL__ENABLED

Acceptance criteria:

  • 文档存在且含:API 路径与方法、鉴权(shared bearer)、请求/响应 JSON、错误码、幂等键建议
  • 列出本仓 DELETE 清单与 KEEP(in-app/slack/webhook/triggers)
  • 写明 delivery_mode=email 兼容映射
  • 写明 chat 邮件不迁(随 RM)
  • 写明 compose 拓扑:mailpit 保留/profile 策略、mega2 去掉 MEGA_MAIL__*、website-next SMTP→mailpit(若用)、mega2→website mail API 基址与 bearer 配置键
  • 链接到 notification.md / mail.md / test-infra.md / development.md 的「将废止或改写」要点
  • 无悬空链接;无生产代码改动

Verification:

  • test -f docs/refactoring/website-mail.md
  • rg -n 'internal/notifications|bearer|email_jobs|in_app|ADR-WA-08|mailpit|MEGA_MAIL|test-infra' docs/refactoring/website-mail.md
  • libra status --short --branch 仅本卡产物

Dependencies: AU-01

Deliverables: docs/refactoring/website-mail.md

Implementation write set: docs/refactoring/website-mail.md

Release write set: N/A

Rollback mode: revert

Estimated scope: S

Version increment: N/A

Release boundary: no-release

C/D coverage from: REL-01

Granularity: type=docs; axis=website邮件契约落盘; recovery=revert文档; complete=yes; self-contained=yes; AC=7/20; VER=3/20; landing=1; prod-files=1; scope=S; deps=AU-01; writeset=no-overlap; release=no-release; split-from=N/A; exception=N/A


Task MN-02: NotificationService / dispatcher 去 email-primary

Task type: implementation

Lifecycle / Acceptance: done / complete

Description: 将通知编排与 [mail] 解耦;primary 改为 in_app(或文档选定的等价方案);triggers 停止依赖 mail 模板入队 email_jobs(可暂时只写 in-app / 跳过 email,website 调用在 MN-05 接上);NOTIFICATION_DELIVERY_MODES 更新。

Out of scope:

  • 不删除 src/mail/ 文件(MN-03)
  • 不 DROP 表(MN-04)
  • 不调用真实 website API(MN-05)

Current evidence:

事实 证据
启动依赖 [mail] context/mod.rs
扇出在 email 成功后 dispatcher.rs

Acceptance criteria:

  • 无 [mail] 时仍可启动 in-app(及已配置的 slack/webhook)通知服务
  • primary 不再是 email;email 成功不再是扇出门槛
  • CL/issue/reference triggers 不再 enqueue_email_job(或该函数已删除且编译通过)
  • prefs:delivery_mode=email 有明确映射(见 MN-01)并有测试
  • chat 触发器已不存在(依赖 RM-03)
  • notification:: focused 测试更新且绿

Verification:

  • source .env.test && cargo test -p mega2-core 'notification::'
  • rg -n 'enqueue_email_job|EmailChannel|primary.*email' src/notification 按预期零命中或仅注释(默认零命中)
  • 无 mail 配置的启动冒烟(手工或单测):NotificationService 可 start
  • cargo build -p mega2-core 0 警告 0 错误

Dependencies: MN-01, RM-03, AU-05

Implementation write set: src/notification/、src/context/mod.rs(启动路径)、相关 config delivery_mode

Release write set: N/A

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=dispatcher去email-primary; recovery=revert; complete=yes; self-contained=yes; AC=6/8; VER=4/8; landing=3; prod-files=8; scope=M; deps=MN-01,RM-03,AU-05; writeset=no-overlap; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task MN-03: 删除 mail 模块、[mail]、admin 邮件 API

Task type: removal

Lifecycle / Acceptance: done / complete

Description: 删除 src/mail/、src/email/、EmailChannel、[mail]/MailConfig、mail.password secret 支持、admin email-jobs/mail-templates 路由与 OpenAPI;清理 lettre 依赖(若无残留引用)。

Out of scope:

  • 不 DROP DB 表(MN-04)
  • 不改 website(DEP-06)

Current evidence:

事实 证据
pub mod mail src/lib.rs
admin 路由 admin_router.rs email-jobs / mail-templates
config secret mail.password commands/config.rs

Acceptance criteria:

  • 符合 ADR-WA-06:无单独弃用窗口;breaking 由 REL-01 发布说明覆盖(本卡不设 shim)
  • src/mail/、src/email/ 删除;lib.rs 无 mod mail
  • [mail] 从 model/validate/template/config.toml 移除;含 mail 的配置 validate 失败或键被拒
  • admin 无 /email-jobs、/mail-templates
  • config secret 不再支持 mail.password
  • Cargo.toml 无仅服务于 mail 的 lettre(若仍被其它模块使用则保留并注释理由)
  • rg -n 'SmtpMailer|MailConfig|mail\\.password|/email-jobs' src config 零命中(允许 migration/docs 历史)

Verification:

  • 零命中守卫(上条)
  • cargo test -p mega2-core 'commands::config'
  • cargo run -p mega2 -- --config config/config.toml config validate
  • OpenAPI 无 email-jobs / mail-templates(sanitized)

Dependencies: MN-02

Implementation write set: src/mail/、src/email/、src/lib.rs、src/api/router/admin_router.rs、src/config/、config/config.toml、Cargo.toml/Cargo.lock、src/commands/config.rs

Release write set: N/A

Docs and compatibility impact: breaking;REL-01 聚合

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=removal; axis=删除mail模块与admin邮件API; recovery=revert; complete=yes; self-contained=yes; AC=7/8; VER=4/8; landing=4; prod-files=12; scope=M; deps=MN-02; writeset=序列化于MN-02; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task MN-04: forward-only DROP email_jobs 表

Task type: migration

Lifecycle / Acceptance: done / complete

Description: 新增 migration DROP email_job_attachments、email_jobs;从 callisto/storage 移除实体与 API;down no-op。

Out of scope:

  • 不 DROP user_inbox_notifications
  • 不删历史创建这些表的旧 migration 文件

Current evidence:

事实 证据
表存在 m20260224_*、m20260618_064500_*

Acceptance criteria:

  • 新 migration 登记于 migrations();DROP 顺序先 attachments 后 jobs(或等价 FK 安全顺序)
  • callisto/storage 无 email_jobs 实体引用
  • 集成测试:up 后两表不存在
  • down no-op;forward-only

Verification:

  • source .env.test && cargo test -p mega2-core 'migration::runner::tests::test_drop_email_jobs_schema'
  • rg -n 'email_jobs|email_job_attachments' src/callisto src/jupiter/storage 零命中(允许 migration 历史目录)
  • rg -n 'drop_email' src/jupiter/migration/mod.rs

Dependencies: MN-03

Implementation write set: src/jupiter/migration/、src/callisto/、src/jupiter/storage/

Release write set: N/A

Rollback mode: forward-only

Migration and rollback: up=DROP;down=no-op;生产前备份

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=migration; axis=DROP email_jobs表; recovery=forward-only备份恢复; complete=yes; self-contained=yes; AC=4/8; VER=3/8; landing=2; prod-files=3; scope=M; deps=MN-03; writeset=序列化于MN-03; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task MN-05: 产品事件改调 website 邮件 API

Task type: implementation

Lifecycle / Acceptance: done / complete

Description: 实现 mega2 → website 内部邮件客户端(配置基址 + bearer);CL/issue/reference triggers 在需要邮件时调用该 API;失败可观测(日志/度量)且不阻断主请求(与现触发器异步语义对齐)。同步 compose: mega2 注入 website mail base URL + bearer(IT 测试值);website-next(若已存在)注入接收内部邮件所需的 env;需要 SMTP 捕获时 website-next→mailpit。契约与配置键写入 website-mail.md。

Out of scope:

  • 不在本仓实现 SMTP
  • 不删除 MEGA_MAIL__* / 不改 Mailpit 登记文案的收口(MN-06;本卡可先加新 env,旧 MEGA_MAIL__* 由 MN-06 删除)
  • 不在 website 仓实现 API 若由他人交付——但契约测试可用 wire mock;栈级绿依赖 DEP-06

Current evidence:

事实 证据
triggers 原 enqueue email triggers.rs CL/issue 路径
website 无产品 API GAP-09
compose 无 website mail 接线 docker-compose.test.yml

Acceptance criteria:

  • 配置项存在(notification.website_mail_base_url + bearer / bearer ref)并校验
  • CL comment/merge、issue comment/close、item referenced 在 prefs 要求邮件时调用 website API
  • website 4xx/5xx 不导致 API handler 500(记录 warn/error;本卡选「best-effort + 日志」)
  • IT/mock:至少一次成功调用断言
  • compose(--profile app --profile web)含 mega2→website mail 基址/bearer 占位;website-mail.md 同步客户端配置键与 compose 变量名
  • 日志不打印 bearer / 完整邮件 PII 超限字段

Verification:

  • source .env.test && cargo test -p mega2-core 'notification::triggers'
  • rg -n 'website_mail|WEBSITE_MAIL|internal/notifications' src/notification src/config docker-compose.test.yml docs/refactoring/website-mail.md
  • cargo run -p mega2 -- --config config/config.toml config validate

Dependencies: MN-04, DEP-06, ITW-02

Implementation write set: src/notification/、src/config/、config/config.toml、docker-compose.test.yml(新增 website mail 接线,可暂留旧 MEGA_MAIL__*)、docs/refactoring/website-mail.md

Release write set: N/A

Rollback mode: revert

Security and privacy: bearer 走 SecretRef;日志不打印 token/邮件全文 PII 超限字段

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=triggers改调website邮件API; recovery=revert; complete=yes; self-contained=yes; AC=6/8; VER=3/8; landing=4; prod-files=8; scope=M; deps=MN-04,DEP-06,ITW-02; writeset=序列化于ITW-02; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task MN-06: compose / 测试 / CI / 文档去掉本仓邮件投递门

Task type: implementation

Lifecycle / Acceptance: done / complete

Description: 按 ADR-WA-08 §7:同一卡完成 (1) 删除/改写依赖 SmtpMailer/Mailpit 本仓投递的用例;(2) 更新 docker-compose.test.yml / .env.test* / CI;(3) 更新相关文档。Mailpit 默认保留但消费方改为 website;mega2 去净 MEGA_MAIL__*。

Out of scope:

  • 不删 in-app/slack/webhook 非邮件断言
  • 不实现 Playwright
  • 不完成 plan-long 全量收口(DOC-01 复核)

Current evidence:

事实 证据
Mailpit 多渠道 IT bin/tests/integration_vault.rs integration_multichannel_notification
mail password seed integration_vault.rs seed_mail_password
compose 本仓 mail env docker-compose.test.yml MEGA_MAIL__ENABLED: "false"
开发手册本仓 SMTP docs/development.md mailpit 表;根 README.md [mail]

Acceptance criteria:

  • 无测试要求本仓 SMTP 成功投递到 Mailpit
  • fail-closed「缺 mail.password 进程退出」门删除或改为「无 [mail] 段仍可启动」
  • docker-compose.test.yml:mega2 零 MEGA_MAIL__*;不 depends_on: mailpit
  • mailpit:保留或改 profiles: ["mail"](按 MN-01 拓扑);test-infra.md 登记消费方 = website / 非 mega2
  • .env.test 与 .env.test.example:MAILPIT_* 注释改为 website IT(若保留);删除本仓 mail.password seed 说明;含 website mail client IT 占位键(若 MN-05 已定名)
  • CI workflow:去掉仅服务于本仓 SMTP 的强制步骤;mailpit 用途注释与 test-infra.md 一致
  • mail.md 标明废止并链接 website-mail.md;同时 notification.md / integration.md / config.md / test-infra.md / docs/development.md / 根 README.md 去掉「本仓 SmtpMailer→Mailpit / [mail] 必配」现行门表述
  • rg 守卫:生产路径与 compose 无 MEGA_MAIL;文档允许「废止/历史」语境

Verification:

  • source .env.test && cargo test -p mega2 --test integration_vault -- --test-threads=1
  • rg -n 'MEGA_MAIL' docker-compose.test.yml 零命中
  • rg -n 'Mailpit|seed_mail_password|SmtpMailer' bin/tests src/notification 按 allowlist 审查
  • rg -n '废止|website-mail' docs/refactoring/mail.md docs/refactoring/notification.md docs/development.md
  • docker compose -p mega2-it -f docker-compose.test.yml --profile app config 成功且渲染结果无 MEGA_MAIL

Dependencies: MN-05, ITW-03

Implementation write set: bin/tests/、docker-compose.test.yml、.env.test.example(及本地 .env.test 若入库策略允许只改 example)、.github/workflows/、docs/refactoring/{mail,notification,integration,config,test-infra,website-mail}.md、docs/development.md、根 README.md

Release write set: N/A

Rollback mode: revert

Docs and compatibility impact: breaking 运维面:无 [mail]、无本仓 Mailpit 门;见 ADR-WA-06 / REL-01

Estimated scope: M

Version increment: N/A

Release boundary: family child of REL-WA-01

C/D coverage from: REL-01

Granularity: type=implementation; axis=compose测试CI文档去本仓邮件门; recovery=revert; complete=yes; self-contained=yes; AC=8/8; VER=5/8; landing=6; prod-files=10; scope=M; deps=MN-05,ITW-03; writeset=序列化于ITW-03; release=family child of REL-WA-01; split-from=N/A; exception=N/A


Task DOC-01: plan-long 与重构文档收口

Task type: docs

Lifecycle / Acceptance: done / complete

执行后注记(2026-08-06 完成度复审): 收口后入库的 docs/refactoring/augmentcode.md(mega2 ad61934,2026-08-05;内容为 2026-06-21 快照)曾以现在时引用已删除的 docs/refactoring/chat.md 并把 chat/mail 描述为现状,使本卡「chat 文档零悬空引用」守卫(VER-2)在现行树上失效。复审已为该文件补「现状注记」头并把 chat.md 引用改标「已随 DOC-01 删除」;VER-2 的零命中口径自此排除历史快照类文档中已标注删除的提法。本卡收口日验收不受影响。

Description: 同步 plan-long.md(chat 退出、PT-10/12/09、日期索引)、删除 chat 独立改进文档、复核 MN-06/ITW 已改的 notification.md / mail.md / config.md / test-infra.md / integration.md / development.md / README.md、确认 website-auth.md + website-mail.md;更新 docs/plan/README.md。补齐 MN-06 未覆盖的遗漏交叉引用。

Out of scope:

  • 不改生产代码 / 不改 compose(应已由 MN-06/ITW 完成;若发现漂移则开 FIX 或退回 MN-06)
  • 不完成 PT-12 功能对照表(DEP-03)

Current evidence:

事实 证据
chat 退出当前基础;日期索引含本计划 plan-long.md 当前基础 / 日期计划索引
PT-09 / PT-10 / PT-12 已注明本计划切片 plan-long.md 总览与专章
README 计划列表 docs/plan/README.md

Acceptance criteria:

  • plan-long.md 日期索引含本计划;chat 不再列为在维护基础能力
  • PT-10 注明 [oauth] 由本计划落地
  • PT-12 注明会话信任路径与 compose 同栈 IT 已由本计划实现,全量对照仍候选
  • PT-09 注明本仓邮件投递已移除、投递归属 website(ADR-WA-08);compose/mailpit 消费方说明与 test-infra.md 一致
  • chat 独立改进文档删除并零悬空引用
  • notification.md / mail.md / config.md / development.md / 根 README.md 与 MN/ITW 一致(无现行本仓 SMTP 门)
  • test-infra.md / integration.md 已含 website-next / integration_website_auth / 邮件门变更 / mailpit 消费方
  • docs/plan/README.md 登记本计划(含 MN 与 compose/文档同步)
  • 无悬空链接到已删 chat 独立文档;website-mail.md 拓扑与 compose 现状一致

Verification:

  • rg -n 'plan-20260731|website-auth|website-mail|website-next|ADR-WA-08' docs/plan docs/refactoring docs/development.md
  • bash -c 'set -uo pipefail; if hits=$(rg -n "docs/refactoring/chat\.md" docs | rg -v "已随[^\n]*DOC-01[^\n]*删除|已删除|2026-08-06|历史快照"); then echo "FAIL: unannotated dangling chat.md reference:"; echo "$hits"; exit 1; else rc=$?; [ "$rc" -eq 1 ] || exit "$rc"; echo "OK"; fi'(2026-08-06 完成度复审改写:原命令为无过滤 rg … 零命中,在收口日成立;augmentcode.md(收口后入库的历史快照)及本计划注记合法地以「已删除」标注引用该路径,零命中口径改为「排除已标注删除/历史快照的行后零命中」,与 DOC-01 执行后注记一致)
  • rg -n 'MEGA_MAIL|SmtpMailer→Mailpit|mail\\.password seed' docs/development.md docs/refactoring/test-infra.md 仅允许废止/历史语境
  • libra status --short --branch 仅文档路径

Dependencies: AU-05, RM-06, ITW-03, MN-06

Deliverables: docs/plan/plan-long.md、docs/plan/README.md、docs/refactoring/{notification,mail,config,test-infra,integration}.md、docs/development.md、根 README.md(若 MN-06 已改则复核);删除重构目录中已退场的 chat 独立文档

Implementation write set: 上列 docs

Release write set: N/A

Rollback mode: revert

Estimated scope: M

Version increment: N/A

Release boundary: no-release

C/D coverage from: REL-01

Granularity: type=docs; axis=长期重构文档收口; recovery=revert文档; complete=yes; self-contained=yes; AC=9/20; VER=4/20; landing=8; prod-files=8; scope=M; deps=AU-05,RM-06,ITW-03,MN-06; writeset=no-overlap; release=no-release; split-from=N/A; exception=N/A


Task REL-01: minor 发布点

Task type: release

Lifecycle / Acceptance: done / complete

Description: 作为 REL-WA-01 唯一发布点:在 AU/RM/ITW/MN/DOC 子卡均 locally-accepted 且 review PASS 后,做版本面 minor bump、三门、提交推送,并写出 breaking 兼容说明(ADR-WA-06)。聚合 AU-01/MN-01/DOC-01 文档提交。

Out of scope:

  • 不引入新行为
  • 无 tag/artifact(本仓 N/A)

Current evidence:

事实 证据
当前版本 0.2.0(Cargo.toml:6;相对窗口入口 0.1.183 minor +1)

Acceptance criteria:

  • 版本面两处一致且相对进入窗口时权威版本 minor +1(例如 0.2.0,以进入窗口时读取为准)
  • breaking 列表写入提交说明或 docs/ 短发布说明(本卡选提交说明正文 + website-auth.md / website-mail.md「兼容」节若尚无则补)
  • AU-01、MN-01 与 DOC-01 产物已在同一发布提交集或紧邻提交中进仓;ITW/MN 代码与 compose 变更已含于发布树
  • 推送成功;适用 D 组全绿后本卡与全部家族子卡 Acceptance=complete
  • 确认窗口内无子卡曾独立 bump/推送

Verification:

  • 版本 parity:Cargo.toml 与 bin/Cargo.toml 的 version 相等且为 minor bump
  • 提交说明含 /api/v1/chat、website_user_id、chat-migrate、隐式 admin、[mail]/email_jobs 五类 breaking 关键词

Dependencies: DOC-01

Deliverables: 版本面两处 + Cargo.lock;提交/推送证据

Implementation write set: N/A(发布动作)

Release write set: Inherited

Rollback mode: immutable-release

Estimated scope: S

Version increment: minor

Release boundary: family release point of REL-WA-01

C/D coverage from: self

Granularity: type=release; axis=minor发布点; recovery=immutable-release前滚; complete=yes; self-contained=yes; AC=5/12; VER=2/12; landing=0; prod-files=0; scope=S; deps=DOC-01; writeset=N/A; release=family release point of REL-WA-01; split-from=N/A; exception=N/A

测试矩阵

类别 必须覆盖 Target / command
单元 Cookie 解析、字段映射、banned、空 name cargo test -p mega2-core 'api::oauth'
集成(DB) DROP chat + DROP email_jobs migration up cargo test -p mega2-core 对应 migration 测试
CLI chat-migrate 已移除;config secret 无 mail.password cargo test -p mega2-core 'commands::' + cli::tests
HTTP API 无 /chat;无 admin email-jobs;会话拒绝/成功;/user 仍可用 api:: + OpenAPI 证据
配置 oauth 必填基址;无 [chat]/[mail];无遗留键 config validate
Git 协议 token 路径未回归 既有 integration_git_cli / smoke(REL 三门覆盖)
安全 无隐式 admin;token/mail bearer 不进日志 AU-05、MN-05 守卫
通知 无 chat 事件;无 email-primary / enqueue_email_job notification::
邮件 本仓无 SMTP;compose 无 MEGA_MAIL;website API 客户端(mock/IT);mailpit 仅 website MN-05/MN-06;docker compose … config;integration_vault 多渠道去邮件断言
栈级会话 IT next-app 登录 + cookie → /api/v1/user cargo test -p mega2 --test integration_website_auth -- --test-threads=1(需 --profile app --profile web)
compose website-next 登记与健康 docker compose -p mega2-it … --profile web

追溯表

任务 来源/证据 mega2 落点 文档/兼容动作 指定测试
AU-01 website Better Auth;GAP-07/08 docs/refactoring/website-auth.md 新建事实源含 compose 拓扑 结构守卫
AU-02 GAP-02;PT-10 src/config/、config/config.toml config.md config validate + config::
AU-03 GAP-01/03;Mega SessionUser src/api/oauth/、http_server website-auth.md api::oauth
AU-04 GAP-04 LoginUser、gpg_router breaking 字段名 oauth + gpg
AU-05 GAP-01 oauth 测试 — 新会话用例(mock)
ITW-01 GAP-08;ADR-WA-07;Mega demo 形态 docker-compose.test.yml test-infra.md compose up website-next
ITW-02 ADR-WA-07 compose env / CORS website-auth.md、test-infra.md 同网络 get-session
ITW-03 GAP-08 bin/tests/integration_website_auth.rs integration.md、CI integration_website_auth
RM-01 GAP-05 api/commands/server OpenAPI cli/commands/OpenAPI
RM-02 GAP-05 api/notes OpenAPI api::
RM-03 GAP-06 notification/triggers.rs notification.md notification::
RM-04 GAP-05 chat/callisto/storage/config config.md build --tests
RM-05 GAP-05/06 新 migration forward-only migration tests
RM-06 sidebar 默认 config + SidebarConfig — validate
MN-01 GAP-09;ADR-WA-08 docs/refactoring/website-mail.md 契约事实源 结构守卫
MN-02 GAP-09 notification/、context notification.md notification::
MN-03 GAP-09 src/mail/ 删除、admin、config mail.md 废止 config + OpenAPI
MN-04 GAP-09 DROP email_jobs migration forward-only migration tests
MN-05 GAP-09;DEP-06 website mail client + triggers + compose 接线 website-mail.md triggers + mock/IT
MN-06 GAP-09;ADR-WA-08 §7 compose/env/CI/测试/文档去本仓邮件门 test-infra、development、README、mail 废止 integration_vault + compose config 守卫
DOC-01 plan-long / chat 文档退场 / PT-09 / ITW/MN 文档 docs README 索引 链接守卫
REL-01 ADR-WA-06 版本面 提交说明 三门 + D

里程碑验收与回滚

里程碑 完成条件 发布/证据 回滚或前滚
M0 AU-01 + MN-01 locally-accepted + review PASS 文档随 REL-01 进仓 revert 文档
M1 AU-05 locally-accepted + review PASS 本地提交;不独立 patch 推送 revert 各卡;会话回退会恢复 stub——禁止在生产长时间停留
M1b ITW-03 locally-accepted + review PASS 本地提交;不独立推送 revert compose/测试;不影响生产 IdP
M2 RM-06 locally-accepted + review PASS;RM-05 后不可 schema 回滚 本地提交;不独立推送 RM-05 前可代码 revert;之后仅备份恢复
M2b MN-06 locally-accepted + review PASS;MN-04 后 email 表不可 schema 回滚 本地提交;compose/env/docs 已就绪待 REL-01 MN-04 前可代码 revert;之后仅备份恢复;website API 故障时产品邮件静默失败(见故障矩阵)
M3 REL-01 complete;家族子卡 Acceptance=complete;计划完成判据全勾 唯一 minor 推送 immutable-release;前滚修复

故障恢复矩阵

故障点 可接受残留 恢复动作 禁止结果
website get-session 超时 请求失败 拒绝会话;Git token 仍可用 回退到隐式 admin
RM-05 中途失败 部分表已 DROP 停机、从备份恢复或手工完成剩余 DROP 后标记 migration 半套表 + 代码已删仍查询
MN-04 中途失败 email 表部分 DROP 同 RM-05:备份恢复或完成剩余 DROP 代码已删仍写 email_jobs
website 邮件 API 不可达 产品邮件未送达;in-app 仍可写 修复 website/DEP-06;日志告警;不回退 SMTP 在本仓重新启用 [mail]
发布后发现非 chat 误 DROP 数据丢失风险 立即停写;从备份恢复;开 FIX 卡 继续服务
website-next 镜像构建失败 无 web profile ITW blocked;数据面 / app profile 仍可用 用 FixedUser 冒充 ITW-03 完成
sibling ../website 缺失 无法 build web 按 DEP-05 停工 改指向任意远程未 pin 镜像

风险登记

风险 影响 缓解 任务
Cookie 跨站被拒 高:浏览器 API 全 401 文档部署拓扑;CORS credentials;同站/反代;IT 用容器网络 URL AU-01、AU-03、ITW-02
name 非唯一 中:actor 碰撞 文档约束;后续 unique handle(ADR revisit) AU-01、ADR-WA-02
DROP 误伤非 chat 表 高 ER-02 复核 reactions 消费方;RM-05 禁止 DROP email_jobs(由 MN-04 单独做) RM-05
DEP-06 未就绪 高:MN-05/06 blocked 契约先落盘(MN-01);website 并行;本仓不得回退 SMTP MN-01、MN-05、DEP-06
去 email-primary 拖死扇出 高 MN-02 先改 primary=in_app 再删 mail MN-02、MN-03
测试仍假定 admin / Mailpit 中:假绿/假红 AU-05 清 stub;MN-06 去邮件门 AU-05、MN-06
每请求外呼延迟 低-中 超时有界;缓存延后 DEFER-WA-02 AU-03
next-app 镜像构建慢/失败 中:ITW 阻塞 profile web 可选;CI 缓存;SQLite 减依赖 ITW-01、ITW-03
website 契约漂移 中:栈 IT / 邮件 IT 红 DEP-01/06 pin + ITW-03 + MN-05 ITW-03、MN-05

性能与容量摘要

操作 单次成本 累积成本 预算/上限 验证
get-session 外呼 1× HTTP RTT 每会话请求 1 次 客户端超时 ≤ 配置默认(建议 ≤ 3s,AU-03 写定) 单元 mock;无基准强制
website 邮件 API 外呼 1× HTTP RTT 每需邮件的触发 1 次 超时有界;失败不阻断主请求 MN-05 mock/IT
DROP migration O(表数) DDL 一次(chat + email) 维护窗口执行 集成测试
website-next 镜像构建 数分钟级 每 CI/本地首次 ITW-01 登记上限(建议 ≤ 20min) compose build 计时
integration_website_auth 登录 + 2× HTTP 每 target 一次 建议 ≤ 10min ITW-03

兼容与文档收口

  • docs/errors.md 已同步,或说明 N/A(若无新 MegaError 变体)。
  • docs/refactoring/website-auth.md 与 website-mail.md 已创建;chat.md 已删;mail.md 废止并改引;notification.md / config.md / test-infra.md / integration.md / docs/development.md / 根 README.md 已同步。
  • config/config.toml 示例与注释已同步(无 [mail];含 website mail 客户端配置若适用)。
  • 运行时 OpenAPI 证据已取得(无 /chat、无 notes sync、无 admin email-jobs/mail-templates)。
  • README.md / AGENTS.md 相关漂移已同步或登记。
  • callisto / storage / migration(含 migrations())已同步;email_jobs 已 DROP。
  • 新引入的 docs/*.md 引用全部真实存在。
  • plan-long.md 日期索引与 PT-09/10/12/chat 状态已同步。
  • docker-compose.test.yml 含 website-next;--profile app --profile web 同栈可启;无 MEGA_MAIL__*;mailpit 消费方已文档化。
  • .env.test.example 与 CI 不再要求本仓 mail.password / SmtpMailer→Mailpit 门。

Review log

Result 只允许 PASS 或 FAIL。

Round Scope Result P0/P1 P2 处置 Evidence
R1 计划成稿 FAIL 未完成模板合规审查(Result 曾非法占位) 并入后续轮次 本文件
R2 补录 next-app compose IT FAIL 未闭环审查 并入后续轮次 ADR-WA-07;ITW-01..03
R3 补录邮件退场改接 website FAIL 未闭环审查 并入后续轮次 ADR-WA-08;MN-01..06;GAP-09;DEP-06
R4 强制 compose + 文档与邮件退场同步 FAIL 未闭环审查 并入后续轮次 ADR-WA-08 §7;MN-05/06/DOC-01 扩写
R5 Codex 计划成稿审查 FAIL P1:G-10 写集(ITW↔MN compose/docs)未闭合 P2:AU-01 deps、MN-06 VER、REL-01 AC 审计、Review log 占位——已改 /tmp/mega2-plan-review/r1-last.txt
R6 Codex 复审 FAIL P1:独立发布 vs REL-01 minor 矛盾;AU↔RM/MN G-10 P2:removal 弃用窗口——已改家族发布+依赖边+ADR-WA-06 /tmp/mega2-plan-review/r2-last.txt
R7 Codex 复审 FAIL P1:Phase/里程碑仍写子卡 complete/patch 发布;AU-02/MN-01 deps 误写 AU-05;MN-02 granularity 漏 AU-05 已改 /tmp/mega2-plan-review/r3-last.txt
R8 Codex 复审 FAIL P1:MN-06 AC=9/12 违反 G-03 代码卡分母 8 已合并 AC→8/8、VER→5/8 /tmp/mega2-plan-review/r4-last.txt
R9 Codex 计划成稿审查 PASS 无 — /tmp/mega2-plan-review/r5-last.txt
AU-01 website-auth.md 落盘 PASS 无(R1 悬空 website-mail 链接已修) — /tmp/mega2-plan-exec/au01-r2.txt
AU-02 OAuthConfig 类型化 PASS 无(R1–R2 P2/P3 已修:legacy env 硬拒 + base URL Url 校验) — /tmp/mega2-plan-exec/au02-r3.txt
AU-03 SessionUser 真接通 PASS 无 — /tmp/mega2-plan-exec/au03-r1.txt
AU-04 website_user_id 重命名 PASS 无 — /tmp/mega2-plan-exec/au04-r1.txt
AU-05 会话路径验收测试 PASS 无 — /tmp/mega2-plan-exec/au05-r1.txt
ITW-01 登记 website-next compose PASS 无 — /tmp/mega2-plan-exec/itw01-r1.txt
ITW-02 同栈 oauth/CORS 接线 PASS 无(子代理中断后已补 curl 200/null + down) — /tmp/mega2-plan-exec/itw02-r1.txt
ITW-03 真实 cookie 栈级黑盒 PASS R1–R2:401 断言、pin 政策/守卫 YAML;R3 PASS — /tmp/mega2-plan-exec/itw03-r3.txt
RM-01 Chat HTTP/CLI 退场 PASS 无 — /tmp/mega2-plan-exec/rm01-r1.txt
RM-02 Notes API 退场 PASS R1 空目录残留已 rmdir — /tmp/mega2-plan-exec/rm02-r2.txt
RM-03 Chat 通知触发器退场 PASS 无 — /tmp/mega2-plan-exec/rm03-r1.txt
RM-04 模块/实体/ChatConfig 删除 PASS R1–R2 FAIL 后恢复 sidebar、收窄 Note 判定;R3 PASS — /tmp/mega2-plan-exec/rm04-r3.txt
RM-05 DROP chat/notes schema PASS ER-02 修订后保留 reactions/custom_reactions — /tmp/mega2-plan-exec/rm05-r1.txt
RM-06 Sidebar 默认项清理 PASS 无 — /tmp/mega2-plan-exec/rm06-r1.txt
MN-01 website-mail 契约落盘 PASS R1:配置键写死;脏树按 write set 解释 — /tmp/mega2-plan-exec/mn01-r2.txt
MN-02 dispatcher 去 email-primary PASS R1:slack/webhook 扇出与 notification.* 死配置;R2 已修 — /tmp/mega2-plan-exec/mn02-r2.txt
MN-03 删除 mail 模块与 admin API PASS R1–R3:MEGA_MAIL env 硬拒 + IT/compose 去播种;R4 PASS — /tmp/mega2-plan-exec/mn03-r4.txt
MN-04 DROP email_jobs 表 PASS R1:缺 down no-op 测试证据;R2 已补 — /tmp/mega2-plan-exec/mn04-r2.txt
MN-05 产品事件改调 website 邮件 API PASS R1:payload 契约偏离 + locale;R2 已修 — /tmp/mega2-plan-exec/mn05-r2.txt
MN-06 compose/测试/CI/文档去本仓邮件门 PASS R1:CI 仍跑 mail.password 门 + test-infra/website-mail 现状漂移;R2 已修 — /tmp/mega2-plan-exec/mn06-r2.txt
DOC-01 plan-long 与重构文档收口 PASS R1–R2:refactoring/README 仍写本仓 mail 主线;R3 已修 — /tmp/mega2-plan-exec/doc01-r3.txt
REL-01 minor 发布点 v0.2.0 PASS R1:缺 fmt 硬证据 + 完成判据过早勾选;R2 已补 — /tmp/mega2-plan-exec/rel01-r2.txt;push e213371

非目标与延后项

ID 延后内容 原因 重启条件 承接位置
DEFER-WA-01 mega2 签发 JWT / service account 调 website 本计划只做 cookie introspection 机机集成需求书面化 后续日期计划
DEFER-WA-02 get-session 结果短 TTL 缓存 先保证正确性 延迟/website QPS 成为瓶颈 后续计划
DEFER-WA-03 已有 DB 动态 sidebar 中 chat/notes 行的数据迁移 默认配置已清;存量可用 API 运营需要批量清理 运维脚本或小卡
DEFER-WA-04 PT-12 全量 moon↔next-app 功能对照 超出本计划 PT-12 排期 plan-long.md PT-12
DEFER-WA-05 Playwright/浏览器 E2E;website 账户库改 Postgres(后半已落地,2026-08-06 注记:plan-20260803 期间 mega2 39f7433 已把 website IT 账户库切为隔离 Postgres website 库;Playwright/浏览器 E2E 仍延后) 本计划最小栈为 HTTP API + SQLite IdP(收口日快照) 需要 UI 级回归或共享 Postgres 运维 后续日期计划
DEFER-WA-06 将 Mega moon/campsite 拉进同一 compose 与 website IdP 架构冲突 明确双前端同栈对照需求 PT-12 / 独立计划
DEFER-WA-07 将 CL/issue 通知编排整体迁到 website 通知中心 本计划只迁邮件投递 website 通知中心与事件源契约成熟 后续日期计划
DEFER-WA-08 本仓邮件失败本地重试队列 MN-05 选 best-effort + 日志 投递 SLA 要求可恢复重试 后续小卡或 website 侧重试
DEFER-WA-09 DROP reactions / custom_reactions ER-02:CL/Issue conversation 仍消费 reactions;FK 保留 custom_reactions conversation 反应迁表或确认无非 chat subject 后续日期计划

完成判据

  • 所有任务卡满足 G-*;粒度审计表已填齐;无未登记 L/XL、无 EX 缺口。
  • 所有非延后任务 Lifecycle=done 且 Acceptance=complete(含 ITW-01..ITW-03、MN-01..MN-06);子卡 complete 仅在 REL-01 推送后取得。
  • 所有 Verification 已运行并记录。
  • 计划完成门:cargo +nightly fmt --all --check、cargo clippy --all-targets --all-features -- -D warnings、source .env.test && cargo test --all 全绿;cargo build 与 cargo build --tests 0 错误 0 警告。
  • 文档/配置/OpenAPI/测试矩阵已更新;test-infra.md 已登记 website-next 与 mailpit 消费方;website-mail.md 已落盘;mail.md 已废止说明;development.md / 根 README.md 无现行本仓 SMTP 门。
  • RM-05 / MN-04 forward-only 已验证;无声称可 down。
  • --profile app --profile web 同栈会话黑盒已绿(ITW-03)。
  • 本仓无 SMTP/[mail]/email_jobs;compose 无 MEGA_MAIL;产品邮件走 website(MN-06)。
  • Review 最终 PASS;P0/P1 关闭。
  • REL-01 minor 已推送;无 artifact 要求写 N/A。
  • 修订历史完整。
  • plan-long.md 已同步(含 PT-09)。