Files
jiapuapp/docs/项目当前总览.md
T
2026-07-23 08:24:07 +08:00

263 lines
60 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目当前总览
> 当前阶段:导航任务 1—10、TAC 认证客户端、领域上下文基础与 M07 反馈客户端已经完成;T01、认证、家谱工作区、G03 原子创建、M06 帮助、个人资料读写、通知读写、M10 服务端退出、M04 登录态改密和 M05 手机号换绑后端接口门禁均为红灯,继续逐域关闭真实接口
> 当前状态:A01/A04/A05 已接入统一 TAC、真实短信和认证请求代码,M07 已接真实反馈提交 ownerG01/G05、G03、M06、M01/M02/M03 个人资料读写、N01/N02/M01/G01 通知域、M10 退出域、M04 密码凭证域与 M05 换绑域已完成三人接口审查和失败门禁,均未猜测接线;`runtimeConfig.mode` 仍为 `mock`MuMu 原生矩阵和真实环境联调待执行
> 当前基准 HEAD`9b0ad62df467f4e2c58b7689087683c77755e07b``main`,工作区有本轮未提交变更)
> 更新日期:2026-07-23
## 2026-07-23 换机续作断点
### 总目标与完成定义
本项目的总目标不可缩减为“只写规划”或“只让静态测试通过”:必须在 `main` 工作区内持续推进整个 UniApp 家谱项目,按测试先行和三人交叉评审完成导航、T01 长世代世系树、TAC/认证、家谱领域数据、G/T/F/R/N/M 全量页面与接口、异常恢复、无障碍、构建及上线验证,最终交付能够正常上线使用的项目。若本地可完成的工作全部完成后只剩后端合同重导、有效测试账号、应用签名、发布凭证或 MuMu 人工操作等外部状态,才允许停在明确硬阻塞;每项阻塞必须有失败门禁、复现证据、唯一所有者、解除条件和继续步骤,不能用 mock、fixture、定时器或宽松兼容伪装完成。
协作固定为当前主代理加两位评审者,共三人。三人都必须独立检查接口/字段、页面/业务闭环、交互/异常/视觉,再交叉质询并统一结论;主代理是唯一写入者,不再创建旧专家身份或增加并发评审者。普通技术取舍由三人自行收敛,不反复交给用户。系统保存的目标记录仍存在,但在 2026-07-23 读取时状态为 `paused`;换机后的执行者不得据此把任务视为取消,应以上述总目标和本节断点继续。只有项目真实达到完成定义时才能标记完成。
### 仓库与保护基线
- 当前分支为 `main`HEAD 为 `9b0ad62df467f4e2c58b7689087683c77755e07b`,上游显示 `main...origin/main`。工作区含大量本轮未提交的修改、删除和新增文件,均属于当前连续治理成果;换机后先运行 `git status -sb``git rev-parse HEAD` 核对,禁止用 `restore/checkout/reset` 清理,也禁止自行改分支或 worktree。
- 本节仅记录断点,没有执行 `git add/commit/push`。由用户自行上传;换机后必须保留当前完整工作区,而不能只依赖旧远端基线。
- 后端唯一目标地址是 `https://backend-api.ddxcjp.cn/`。受保护 `APP.openapi.yaml` 的 SHA-256 为 `8964CD583CE172425B63BBFD802F7EB587EB3641EADFD6F9D3B264FAA8090C6C``APP.openapi.json``87DB1DC148C5E6E877815AFF7B3A7FC7C7ECA95A2CEC50A88B42F3908961F31A`;两文件相对 HEAD 无差异。不得修改、格式化、覆盖或删除,只接受后端同一版本重新导出的双文件。
- 不得启动、关闭或调整 MuMu。浏览器截图和源码检查不能冒充 Android 原生视觉、TalkBack、系统字号、软键盘或返回键验收。
- 当前物理测试库存仍是 PowerShell `140`、Node `47`。最近一次完整记录为 PowerShell `126/140`,14 项均是预期发布红灯;Node 语法 `47/47`、纯 Node `19/19`、活动 Vue 脚本 `64/64` 通过。G03 最后一次布尔类型加固后又单独通过聚焦合同;换机后应先重跑完整套件,不能把这些历史数字冒充新机器证据。
### 已完成到哪里
导航任务 1—10、统一 TAC 客户端、领域上下文基础和 M07 真实反馈客户端已经完成本地可完成部分。T01、认证/TAC、家谱工作区、M06、个人资料读写、通知读写、退出、密码、手机号换绑均已经三人审查并建立明确 OpenAPI 红灯。任务 35 已完成 G03 原子创建合同:否决空谱加通用人物的两写方案,固定一次 atomic bootstrap、无 PII operation-status、统一 accessPreset、可信地区、词法 ID、始祖不变量及客户端激活门禁;`openapi-yaml-json-parity-runtime-smoke.js` 已升级为严格 YAML mapping 和任意精度数字的完整双源深比较。尚未批量把这些红灯域接到宽松线上接口,`runtimeConfig.mode` 仍诚实保持 `mock`
### 当前精确断点:任务 36 普通加入申请闭环
三人已经完成 G06/G08/G09/G10 与 2026-07-22 线上 OpenAPI 的只读核对,范围只包括“鉴权搜索公开可申请家谱 → 普通申请 → 我的申请/撤回 → 待审列表/单条审核”。邀请码校验和直接加入仍是后续独立任务,产品结论保持“邀请码成功后直接加入且不生成审核记录”,不得混入普通审核合同。
已统一的最小正确方案如下:
- 搜索、我的申请和待审列表使用各自专用最小投影与稳定 cursor 分页,不返回 `total`,所有 `genealogyId/applyId` 都是有界词法字符串。搜索项只暴露识别家谱所需字段和当前查看者状态;我的申请完整表达 `PENDING/APPROVED/REJECTED/WITHDRAWN`;待审项只含 `applyId/applicantName/relationDesc/applyReason?/submittedAt`,不得泄漏手机号、用户 ID、邀请人或审核人内部字段。
- 申请 body 闭合为 `applicantName/relationDesc/applyReason?`,前两项必填;删除 `phone/inviterUserId`。同账号、租户、家谱最多一个活动 `PENDING`,由数据库唯一约束而非先查后插保证。
- 申请 POST 必须带无 PII 的 `Idempotency-Key`。同 key、同 canonical body 重放同一结果;同 key、不同 body 返回 409。客户端只持久 `{sessionEpoch,requestKey,startedAt}`,不持久姓名、关系和理由。
- 冷启动恢复不复用 `mine` 查询,也不持久表单 PII;新增唯一只读 owner `GET /genealogy/app/genealogies/join-apply-requests/{requestKey}`,以显式 discriminator 返回 `PENDING {resolveBy,retryAfterSeconds}``SUCCEEDED {applyId/genealogyId/...无 PII 回执}``FAILED_NO_COMMIT`。只允许 `ABSENT→PENDING→SUCCEEDED/FAILED_NO_COMMIT`,两个终态不可变;GET 纯读,跨账号/租户/client 统一非泄漏 404,PENDING 必须有收敛期限,从未到达的 key 也必须在可计算时间边界后成为零写 `FAILED_NO_COMMIT`
- 不引入 `applicationVersion``If-Match` 或审核详情端点。申请在 PENDING 时不可编辑,撤回、通过和拒绝都以 `WHERE status=PENDING` 的数据库 CAS 决定唯一赢家。重复相同撤回返回同一 200;重复相同审核决定返回原 200,拒绝时只有规范化后理由相同才算相同动作;相反决定、不同拒绝理由或撤回/审核竞态败方返回 typed 409 和当前最小状态。终态不可改,重新申请创建新 `applyId`
- 审核 body 只允许 `APPROVE`,或 `REJECT+必填申请人可见 rejectionReason`。批准必须在同一事务完成唯一成员关系和申请终态;G05 的 `canReviewJoinApplications` 只是入口 capability,服务端在事务/CAS 时仍重新验证权限、家谱状态和 `PUBLIC_APPLY`
- 所有操作 required SaToken 和非空 `clientid`,仅使用 `application/json``Cache-Control: private, no-store`429 带 `Retry-After`。线上三个匿名 GET 当前实测为 HTTP 200 加业务 `code=401`,却与文档 HTTP 401 string 冲突;发布合同必须统一为真实 HTTP 状态与 typed JSON,禁止 `*/*`、200 包装认证错误、`default` 响应和 int64 JSON 身份。
- 客户端结果未知时禁止乐观改列表:申请查专用 operation;撤回刷新 mine 并可安全重放同一 DELETE;审核刷新 pending,行消失只能说“状态已变化/已被处理”,不能冒充本次审核成功。首版不承诺消息中心通知;G09 的 `onShow` 与手动刷新是当前业务真相。
**尚未实施的边界必须原样保留:** `tests/join-application-openapi-contract.ps1` 还没有创建,任务 36 尚未写入实施计划和接口映射,测试计数仍为 140/47G06/G08/G09/G10 业务代码也没有在本任务中修改。换机后的第一项写操作应是用 `apply_patch` 新增该 OpenAPI 失败门禁,先运行并取得精确 `JOIN-APPLICATION-OPENAPI-CONTRACT BLOCKED`,再把 `API-JOIN-001``006`、七个 operation、状态机、隐私投影、竞态和部署反例同步写入现有四份中文权威文档及文档合同。后端门禁通过前页面继续保持诚实本地预览,不写只适配当前宽松线上模型的临时代码。
完成任务 36 的本地门禁、全量回归和三人终审后,继续按独立批次推进邀请码直入、G11 设置写入、G12 字辈真实保存,以及其余 G/T/F/R/N/M 业务域;不得一次混合导航、验证码、领域持久化和无障碍多个阶段。最终仍须完成真实后端联调、构建、签名/隐私配置、MuMu 全流程矩阵和发布终审。
## 当前目标
阶段 0 的清理和验证基线已经完成。当前主代理与两位固定评审者已完成导航任务 1—10,建立路由注册表、导航网关和零债务门禁,并迁移共享组件与认证、G、T、F、R、N/M 全部活动页面。T01、认证/TAC、G01/G05 家谱工作区、G03 原子创建、M06 帮助、M01/M02/M03 个人资料读取、M02 个人资料写入、N01/N02/M01/G01 通知读写、M10 当前设备退出、M04/全认证密码凭证及 M05 手机号换绑的规范 OpenAPI 合同都已测试先行落地;TAC 客户端批次已经完成,A01 短信登录、A04 注册和 A05 忘记密码当前共用严格验证组件、服务端 `validToken`、4 位短信码与可取消请求,但生产 OTP 目标已收紧为统一 6 位,须等待后端同版原子迁移后再替换客户端,禁止 4/6 双接受。领域基础已把会话与当前家谱 ID 收紧为词法字符串:账号切换和损坏存储清理上下文;给定新列表发现历史 ID 消失或显式目标无权时写入持久失效标记,跨重载也禁止静默切谱;T01 也会在写入路由家谱前拒绝已有失效标记和无访问权限的夹具。M07 已按 `POST /genealogy/app/feedback` 接入严格真实提交,mock 模式固定返回 `WRITE_UNAVAILABLE`,不再伪造成功。后端认证门禁未关闭前保持 `mock`;其余尚未接真实写接口的页面仍是明确本地预览或硬关闭。
## 当前权威资料
- 治理设计:`docs/家谱项目全量治理设计.md`
- 当前实施计划:`docs/家谱项目全量治理实施计划.md`
- 视觉资产与构建:`docs/视觉资产与构建基线.md`
- 页面、流程与接口关系:`docs/接口与页面映射总表.md`
- 接口导出:`APP.openapi.json``APP.openapi.yaml`
- schema v3 视觉资产注册表:`design-pipeline/manifests/runtime-assets.json`
- 九宫格所有者:`styles/adaptive-frame-profiles.scss`
- 当前预览密码策略所有者:`utils/validation.js`;生产目标 wire/policy owner 等待 `API-PASSWORD-001``005` 关闭后原子替换,禁止单页双轨。
- 响应式覆盖:`tests/responsive-layout-coverage.json`
- 固定尺寸例外:`tests/responsive-layout-allowlist.json`
- 活动路由:`pages.json`;项目固定使用 Vue 3`uni.scss` 是 Sass 设计令牌唯一入口。
阶段 0 的临时清理清单已在长期结论迁入当前唯一所有者后删除。旧设计记录、规格、计划、交接和 H5/静态设计图中的有效事实已经迁移,旧文件已删除,不再存在并行入口。
## 接口文档所有权
后端维护的 Apifox 项目是接口合同的唯一编辑源。`APP.openapi.json` 用于离线自动扫描和测试,`APP.openapi.yaml` 用于人工阅读与跨工具导入;两份文件都只接受来自同一后端版本的重新导出,不得分别手工维护。部署地址的 `/v3/api-docs` 只作为当前线上实现证据,发现差异时必须推动同版本双导出更新,不能反向覆盖受保护文件。
当前 JSON 文档为 OpenAPI `3.0.1`,包含 `112` 条路径、`153` 个操作和 `72` 个模型。`tests/openapi-yaml-json-parity-runtime-smoke.js` 已使用无第三方依赖、无损任意精度数字且严格校验 mapping 分隔符的结构化解析器深比较完整 JSON/YAML;相邻不安全大整数差一反例已先红后绿,当前两份受保护快照语义一致。后端今后必须同版本双导出,任何单边漂移都会在领域合同前失败。
后端在 2026-07-22 新提供 `https://backend-api.ddxcjp.cn/`。同日 21:52Asia/Shanghai)只读获取其 `/v3/api-docs`,线上为 OpenAPI `3.1.0``722` 条路径、`858` 个操作和 `507` 个模型;本地 112 条路径中有 109 条仍在线,`/genealogy/app/files/reference``/genealogy/app/files/upload``/genealogy/pc/files/upload` 三条不在当前线上文档,线上另有 613 条路径。线上模型已把部分 `Genealogy*Body` 重命名为 `AppGenealogy*Body``GenerationPoemBatchBody` 也新增了由路径写入的 `genealogyId` 字段,因此本地双导出是明确的旧快照,不能再代表当前部署的完整合同。`utils/config.js` 的唯一 `baseUrl` 已更新为无尾斜杠的 HTTPS 地址;`mode` 仍保持 `mock`,在页面接口和安全合同逐批闭合前不得提前切换远端。
线上文档已经提供 `/captcha/require``/captcha/challenge``/captcha/verify`:挑战与校验绑定 `tenantId/clientId/sceneCode/subject`,校验成功响应可返回 `validToken`,而发送短信的 `AppSmsCodeBody` 已把 `validToken` 列为必填。客户端已按 `APP_SMS_LOGIN/APP_REGISTER/APP_FORGOT_PASSWORD` 三个精确场景实现“查询要求→取得挑战→供应商完成→服务端校验→携票发送短信”,注册和找回提交不重复执行 TAC;认证请求只接受 HTTP 200 的严格 JSON envelope,统一 15 秒超时,离页或返回会中止当前 RequestTask。密码登录体尚无 `validToken`,因此 A01 以短信登录为默认且密码登录入口保持不可用,绝不以客户端先滑动冒充服务端强制校验。
认证后端门禁仍有四组问题:`API-AUTH-TAC-001` 要求密码登录加入服务端可消费的同语义票据;`API-AUTH-TAC-002` 记录线上 `/captcha/challenge``APP_REGISTER` 实测返回 HTTP 500 且空响应;`API-AUTH-TAC-003` 要求验证请求以 provider discriminator/`oneOf` 严格关闭根对象和各 payload 的额外字段,并补齐必填 `providerCode/captchaType/payload``API-AUTH-TAC-004` 要求同一验证中心返回服务端绑定的 verification session、可验证方法和同一类短时单次 `validToken``required=false` 也必须直接签发可供短信接口消费的票据,不得形成无障碍绕过。`tests/auth-tac-openapi-contract.ps1` 当前输出 `AUTH-TAC-OPENAPI-CONTRACT BLOCKED`。密码登录、短信登录和注册的线上成功响应已统一为 `RAppLoginVo → AppLoginVo.access_token`,客户端旧令牌字段读取已删除。线上文档自身仍发布 `http://backend-api.ddxcjp.cn` server URL,与已验证可用的 HTTPS 地址不一致;客户端只能使用显式 HTTPS,后端还需修正文档 server 声明。对 `http://localhost:5173` 的预检已返回允许 `content-type/clientid`,这不能替代正式 H5 域名的 CORS 验证。
家谱工作区只读审查已固定 `/genealogy/app/genealogies/mine` 为可访问集合 owner、`/genealogy/app/genealogies/{genealogyId}/overview` 为 G05 唯一详情 owner;首批不同时请求语义重复的 `/{genealogyId}`。线上 `AppGenealogyVo.genealogyId` 仍是 JSON `integer/int64`,最大合法值进入 JavaScript 后会不可逆失真;三个相关模型均无 `required`,也没有必填 `canView``roleType` 无枚举。无令牌实测三条读取均返回 HTTP 200、`application/json;charset=UTF-8``{code:401,msg,data:null}`,但文档只列 200/401 且 401 为字符串,尚不能稳定区分登录失效、对象无权、已删除和服务故障。问题单 `API-GENEALOGY-WORKSPACE-001``003``tests/genealogy-workspace-openapi-contract.ps1` 已建立;同版本双导出通过前不写 G01/G05 专属 adapter,不用本地字段猜测替代服务端 owner。
M06 三人审查选择 `GET /genealogy/app/help-articles` 的完整列表作为唯一远端 owner;线上列表模型已经包含 `helpContent`,因此页面不调用详情端点、不消费 `helpId`,也不让 JSON `int64` 进入页面模型。每次响应只允许显式投影 `helpCategory/helpTitle/helpContent`,分类从当前列表动态派生,展开键只在当前响应生命周期内使用并在搜索、分类、刷新前清空。当前受保护双导出仍返回通用 `ListResult/RList`,线上 `RListHelpArticleVo/HelpArticleVo` 又没有 `required`,正文格式、仅发布内容、展示顺序和认证失败承载也未形成一致合同;匿名实测列表与详情均为 HTTP 200+业务 `code=401`,而线上文档声明 HTTP 401 string。问题单 `API-M06-001``003``tests/help-center-openapi-contract.ps1` 已建立;门禁通过前保留明确本地 FAQ,不写 live-only adapter。
个人资料读取三人审查固定 `GET /genealogy/app/auth/profile` 为 M01/M02/M03 共用的唯一接口 owner。首批只要求 canonical 11 位 `phone` 必填;`nickName/realName/email` 未设置时唯一省略,出现时必须是非空规范字符串,姓名最多 30、邮箱格式有效且最多 100。adapter 立即把明文手机号变为掩码和读屏标签,只输出固定页面模型;`userId/avatar/status` 等字段全部丢弃,因此它们的 int64/枚举不阻塞本批。受保护双导出仍是通用 `ObjectResult/RObject`,线上 `RAppProfileVo/AppProfileVo` 则无 required 和字段边界;匿名实测仍是 HTTP 200+业务 401,与文档 401 string 冲突。问题单 `API-PROFILE-READ-001``003``tests/profile-openapi-contract.ps1` 已建立;门禁通过前不把 live-only 字段接进页面。
通知三人审查把读取与写入拆成两个原子批次。读取唯一使用 `GET /genealogy/app/notifications``GET /genealogy/app/notifications/unread-count`:列表必须完整返回当前账号最多 200 条活动通知并按最新优先,未读数精确统计同一集合;页面只消费完整纯文本标题/正文、带时区时间和 `READ/UNREAD`,所有服务端 ID、发送者与业务目标都不进入公开页面模型。N02 没有详情接口,首批只能以当前内存 `generationordinal``snapshotKey` 打开完整快照,重启、账号切换或成功刷新后的旧 key 均提示返回消息中心重新打开。已读写入另由两个 POST 持有,要求无损字符串 `notificationId`、当前账号幂等、read-all 截止点和并发新消息语义;通过前删除伪本地已读而不发请求。当前受保护双导出的列表仍是通用模型且没有未读数路径,线上模型无 required/枚举/容量,所有 ID 仍为 int64,匿名实测又是 HTTP 200+业务 401。问题单 `API-NOTIFICATION-READ-001``003``API-NOTIFICATION-STATE-001``003` 以及两项失败合同已经建立,均不以猜测代码绕过。
M02 资料写入三人审查保留现有 `PUT /genealogy/app/auth/profile` 作为唯一 App owner,但要求后端把它正式定义为原子 dirty-only merge,而非全资源替换或依赖 DTO 惯例猜测。请求只允许脏的 `nickName/realName/email`:省略保持,昵称出现时必须非空,真实姓名/邮箱的精确空串表示清空,null、纯空白和边界空白非法;成功返回完整 canonical `RAppProfileVo`,清空后的可选字段仍省略。并发沿用 T01 的单一版本模型:`AppProfileVo.profileVersion` 是 opaque stringPUT 必带同形状 `If-Match`,旧版本返回 409 `PROFILE_VERSION_CHANGED`H5 CORS 同步允许该 header。受保护双导出还是旧 `ProfileUpdateBody → RObject`,线上则是无 required/merge/version/security 的 `AppProfileUpdateBody → RAppProfileVo`;当前页面还把同一 fixture 名称同时填入昵称和真实姓名,并用 500ms 定时器伪造本地校验。问题单 `API-PROFILE-UPDATE-001``004``tests/profile-update-openapi-contract.ps1` 已建立;GET 门禁和本门禁通过前不接写入。
M10 退出三人审查固定 `DELETE /genealogy/app/auth/logout` 只撤销请求中 bearer 所属的当前设备凭证族;同账号其他设备保持登录,“全部设备退出”必须另立接口。活动、已撤销和已过期但仍可验证为本 client 签发的历史凭证重复 DELETE 都返回同一个 200 `RVoid`,且成功后旧 token 对任何受保护接口均不可用;伪造、格式非法或 client 不匹配才返回 typed 401 拒绝。客户端唯一 `logoutCoordinator` 在同一同步临界段捕获 A 的 token/clientid、清本地 token/家谱上下文并 bump epoch、用显式快照创建不绑定 M10 生命周期的请求,然后立即 `goRoot(A01)`;异步回调永不再次 clear,避免误删随后登录的 B。线上 endpoint 虽存在,却无 security/clientid、required RVoid、范围/幂等/复用反例和 JSON/no-store;问题单 `API-LOGOUT-001``003``tests/logout-openapi-contract.ps1` 已建立。任何远端失败都不恢复本地 token,只区分“已从本机退出;服务器撤销已确认/未确认/未能撤销”。
M04 改密三人审查否决当前登录/注册/找回/改密共用的静态 32 个十六进制字符 MD5 wire:它是可直接重放的密码等价物,也使服务端无法执行真实新密码策略。唯一生产目标是四条入口原子迁移到 HTTPS 中的 raw `writeOnly` 密码;当前密码与登录兼容 1—64 Unicode code point,新密码统一 15—64 code point、NFC、允许空格与 Unicode、无组成规则,并由服务端执行常见/泄露密码 blocklist、账号限速和带独立盐的自适应慢哈希。M04 当前密码就是重新认证,TAC 不替代身份;用户明确要求的 A01/A04/A05 TAC 仍由原认证门禁持有。严格 200 前服务端必须原子落密、提升 `credentialEpoch` 并撤销包括调用者在内的所有 access/refresh session,客户端清本机回 A01;网络、超时、畸形响应或 5xx 也是结果未知,同样清本机且不自动重试。问题单 `API-PASSWORD-001``005``tests/password-change-openapi-contract.ps1` 已建立;门禁通过前 M04 保持诚实本地预览。
M05 换绑三人审查否决“活动 bearer+新号验证码”直接改号:这只能证明控制新号码,不能证明当前账号本人。唯一生产流程是活动 session、最终 PUT 内 raw `currentPassword` 重新认证、`APP_PHONE_CHANGE` TAC 和新号严格 6 位 OTP;不强制旧号 OTP,避免用户丢失旧号时永久锁死,但成功事务必须持久写入旧号安全通知 outbox。标准 OpenAPI 无法按公共发码 body 的 `sceneCode` 条件化鉴权,因此采用独立且强制 SaToken 的 `POST /genealogy/app/auth/phone/sms/code`scene 由路径固定;公共 `/auth/sms/code` 删除 `APP_PHONE_CHANGE`,两个 operation 仍复用同一 OTP 生成、限速和存储 owner。最终 `PUT /auth/phone` body 只含 `currentPassword/phone/smsCode`,在同一事务内消费 OTP、执行号码唯一约束、更新号码、提升 `credentialEpoch`、撤销包括当前在内的全部 access/refresh session并写通知 outbox,严格 200 返回 `RVoid`。无密码账号返回 `STEP_UP_UNAVAILABLE`,不能降级;最终 PUT 的超时、5xx、畸形响应或进程终止均按结果未知清本机回 A01且不自动重试。问题单 `API-PHONE-001``005``tests/phone-change-openapi-contract.ps1` 已建立;M04 raw-password 门禁、认证/TAC 门禁和本门禁通过前,M05 保持诚实本地预览。
G03 创建链路三人反向质询后否决“先建空谱、再写始祖”的 `ROOT_REQUIRED` 两写方案:当前产品没有跨会话保存空谱的需求,而两写会凭空增加半成品配额、可见性、恢复、取消、过期和第二次未知结果。唯一生产目标是第一步只在本页收集资料,最终按钮以 `AppGenealogyBootstrapBody` 一次原子创建家谱、OWNER 成员关系、带数据库权威标记的唯一一世始祖、READY 状态和幂等回执;通用人物 collection、人物 DELETE 与 parents mutation 必须阻断第二根、删除根和重挂父母,人物 PUT 对根的可编辑白名单精确只有 `name/sex/birthDate/biography`,其他字段全部 typed 422。首次 POST 先以短控制事务认领 PENDING/digest/fencing,再由业务事务完成全部写入,任一步失败全回滚并 CAS 终态;结构化转换只允许 `ABSENT→PENDING→SUCCEEDED/FAILED_NO_COMMIT` 且终态不可变,FAILED 机器保证零领域写与零 quotaPENDING 的 `resolveBy<=claimedAt+2 分钟``GenealogyBootstrapOperationKey` 由 13 位服务端判定的 issuedAt 毫秒与至少 128 位 CSPRNG 组成,`acceptUntil=issuedAt+10 分钟`、最大未来偏差 5 分钟,并由 600/300 秒扩展锁定;operation-status GET 无 body、纯读且只返回显式 discriminator 的三个状态,截止前 404 保持同 key,截止后无记录按 key 计算 FAILED 而不写墓碑。请求使用受鉴权 `/genealogy/app/region/search`、词法 `GenealogyRegionCode`、服务端 `selectable` 和统一 `GenealogyAccessPreset=MEMBER_ONLY/PUBLIC_APPLY`,同版删除公共地区旧路由,并递归清除组合 schema 中的 `visibility/joinMode` 及旧 create/update DTOG11 在本批只取得共享字段形状,其 If-Match、版本/CAS、权限刷新与结果未知仍是独立门禁。客户端仅持久 `{sessionEpoch,operationKey,startedAt}``IDEMPOTENCY_KEY_REUSED` 必须进入 fatal/quarantined,禁止查询或安装 status、自动换 key,只有用户明确放弃才能清理;其他分支按可证明未提交与 unknown 分离。成功固定按 receipt→`/mine` cache→context→G05 收口,context/导航失败只重试本地闭环。问题单 `API-G03-001``005`、完整双源深比较、后端 `tests/g03-bootstrap-openapi-contract.ps1` 和实际执行状态机套件的客户端 `tests/g03-bootstrap-client-release-gate.ps1` 已建立;G03 两门禁转绿仍不能绕过家谱工作区读取门禁、聚焦/全量回归与 MuMu 原生验收,全部通过前保留诚实本地预览,不接 `appApi.createGenealogy`
当前 JSON 的 SHA-256 为 `87db1dc148c5e6e877815aff7b3a7fc7c7eca95a2cec50a88b42f3908961f31a`YAML 的 SHA-256 为 `8964cd583ce172425b63bbfd802f7eb587eb3641eadfd6f9d3b264faa8090c6c`。两份文件均已被 Git 跟踪,当前相对 HEAD 无差异;本轮没有改写、格式化或覆盖接口导出。
## 必须保护的用户文件
- `APP.openapi.yaml``APP.openapi.json`:后端接口离线源快照,只接受同一后端版本的双导出替换,不得手工修改。
- `static/tac/`:当前共 5 个文件,其中 4 个是后端提供且不可改写的供应商文件,第 5 个是项目适配器 `static/tac/js/jiapu-tac-adapter.js`A01/A04/A05 已形成真实运行时消费者,文件集合与供应商哈希由 `tests/auth-tac-integration-contract.ps1` 唯一保护。
- 当前五份中文治理文档:长期唯一入口,随已验证进度同步更新,不另建平行计划。
任何清理批次都不得覆盖、恢复或删除以上文件。
## 清理前基线
- 五个核心验证:全部通过。
- PowerShell 合同:`129` 个,`128` 个通过,`1` 个旧 A01 固定画布合同失败。
- Vue 覆盖:实际 `66`、清单 `66`、缺失 `0`、陈旧 `0`
- Vue 中 `100% 100% no-repeat``0`
- Vue 中直接声明 `border-image-slice``0`
- 文档:`261` 个;PowerShell 测试:`129` 个;Node 测试:`29` 个。
- `static/``128` 个正式文件,约 `95.45 MiB`
以上数字只记录清理起点,不是必须保持的目标。
## 阶段 0 最终结果
- 旧 A01 固定画布、PSD、Photoshop、v2 按钮和旧卷轴入口已经三人一致退役;没有通过重建旧预览制造假绿。
- schema v3 注册表已经完整覆盖当前 `static/assets`:认证直接资产、其余应用直接资产、共享卷轴、六张长页面背景和 G01 空态边框各有唯一 owner;旧 G01 候选与模块背景清单及注册例外已经删除。
- `design-pipeline/manifests/shared-scroll-skins-v3.json` 只拥有母版、处理、物理输出和质量规则,不再拥有页面槽位或 `uni-app mode`
- 阶段 0 验收时,`tests/runtime-assets-contract.ps1` 精确闭合 `79` 个正式输出、物理文件和源码消费者;导航任务 3 又删除了退役通用页面唯一虚构消费的两张 notification frame,当前正式输出为 `77` 个,并由 `tests/retired-module-page-contract.ps1` 防止旧入口与孤立资产回归。
- 设计管线当前为 Node `25/25 PASS`、Python `19/19 PASS`;四张共享卷轴、六张长背景和 G01 空态边框均可真实重建并通过质量审计。
- 质量报告只含工作区相对路径;确定性测试证明相同输入连续两次得到相同字节哈希与报告。
- 六张长背景与 G01 空态边框迁移后的可见像素哈希逐张等于迁移前正式图;连续两次构建的文件 SHA-256 也完全一致。旧 Sharp 单用途依赖和专用构建器已删除。
- 已删除 `28` 个无运行时消费者的 `static/assets``3` 个旧 `static/icons``4` 个未接入业务的 TAC 文件和 `8` 个候选或伪母版;两张用户批准的 A01 选型图作为 `reference-only / non-runtime` 视觉锚点保留。
- 阶段 0 验收时的 MuMu 证据:`emulator-5554` 当时在线,ADB 设备字段为 `model:SDY_AN00`,系统型号为 `SDY-AN00`,物理尺寸 `720×1280`density `320 dpi`。本轮原生截图逐张打开复核了 A01、G01、T01 和 F01,未见缺图、透明错误、异常裁切或样式回退;复核结束后已回到 A01 页面,未启动、关闭或调整模拟器。
- `docs/接口与页面映射总表.md` 已按 `pages.json` 建立 `52/52` 活动页面映射;A04、G 系列第一轮、T01 和新线上 OpenAPI 差异已有专项结论,其余具体接口仍标记为“待对应业务阶段 OpenAPI 审查”。
- 第一轮六项产品优化已经完成:G01/G06 统一读取受控查询参数,G03 在本轮进一步删除了无业务参数的 `onLoad`;AppDialog 统一安全区与高度预算;AppTabbar/GenealogyCard 补齐可访问点击语义;G10 拒绝原因建立错误关联与失败聚焦;A04/A05/M04 统一消费唯一密码策略;T01 可在视图漂移后精确回到当前成员。
- 后续产品阶段按顺序为导航栈语义统一、T01 大规模世系树、短信验证码完整状态机、跨页面领域数据持久化、全局文字层级和无障碍第二轮;不得混合实施。
- 文档权威迁移已删除 `198` 个旧 Markdown、`35` 张旧 H5/静态设计图、`4` 个只保护旧资料的测试和 `.superpowers/sdd``12` 个过程文件;阶段 0 临时清单随后也已删除,当前 `docs/` 只保留 `5` 个长期中文 Markdown 入口。
- 浏览器截图链已把 G03、G06、G08—G12、T06、F02、N01 的有效状态断言迁回各自现有运行时合同;G01、T03—T08、T07 三个有价值的浏览器测试只剥离截图写盘,继续验证状态、滚动、溢出与交互。
- 已删除固定 Chrome 截图助手、联系表、8 个只保护截图助手的合同,以及迁移后重复的 N01/A02 合同;`tests/mumu-visual-acceptance-boundary-contract.ps1` 现在唯一约束“仓库不维护自动截图证据链、最终视觉通过只来自 MuMu”。
当前物理库存为 `140` 个 PowerShell 合同、`47` 个 Node 文件和 `4` 个 JSON 合同数据文件。最新 fresh 结果为 PowerShell `126/140` 通过;其余 14 项不是可删除的普通回归,而是保留真实外部或发布阻塞的 `tests/lineage-openapi-contract.ps1``tests/auth-tac-openapi-contract.ps1``tests/genealogy-workspace-openapi-contract.ps1``tests/g03-bootstrap-openapi-contract.ps1``tests/g03-bootstrap-client-release-gate.ps1``tests/help-center-openapi-contract.ps1``tests/profile-openapi-contract.ps1``tests/profile-update-openapi-contract.ps1``tests/notification-read-openapi-contract.ps1``tests/notification-read-state-openapi-contract.ps1``tests/logout-openapi-contract.ps1``tests/password-change-openapi-contract.ps1``tests/phone-change-openapi-contract.ps1` 和缺少 MuMu 人工证据时必须失败的 `tests/auth-android-accessibility-release-gate.ps1`。十四个输出分别为 `LINEAGE-OPENAPI-CONTRACT BLOCKED``AUTH-TAC-OPENAPI-CONTRACT BLOCKED``GENEALOGY-WORKSPACE-OPENAPI-CONTRACT BLOCKED``G03-BOOTSTRAP-OPENAPI-CONTRACT BLOCKED``G03-BOOTSTRAP-CLIENT-RELEASE BLOCKED``HELP-CENTER-OPENAPI-CONTRACT BLOCKED``PROFILE-OPENAPI-CONTRACT BLOCKED``PROFILE-UPDATE-OPENAPI-CONTRACT BLOCKED``NOTIFICATION-READ-OPENAPI-CONTRACT BLOCKED``NOTIFICATION-READ-STATE-OPENAPI-CONTRACT BLOCKED``LOGOUT-OPENAPI-CONTRACT BLOCKED``PASSWORD-CHANGE-OPENAPI-CONTRACT BLOCKED``PHONE-CHANGE-OPENAPI-CONTRACT BLOCKED``ANDROID-AUTH-ACCESSIBILITY-RELEASE BLOCKED`。Node 语法 `47/47` 通过,纯 Node 冒烟 `19/19` 通过,活动 Vue 脚本模块语法 `64/64` 通过;导航源码扫描保持 `MIGRATION-DEBT=0`。依赖 `127.0.0.1:9222` 调试页的浏览器运行时文件本轮只做语法检查,没有执行;这不能冒充浏览器行为或 MuMu 原生视觉通过。
阶段 0 清理结束时,`static/` 的正式基线为 `79` 个文件、共 `70,414,397` 字节。任务 3 退役两张孤立通知 frame 后,正式 `static/assets``77` 个文件、共 `69,093,742` 字节;`static/tac/` 当前 5 个文件、共 `68,190` 字节,因而当前 `static/` 物理库存为 `82` 个文件、共 `69,161,932` 字节。4 个供应商文件保持原字节并由哈希合同保护,新增适配器是唯一项目映射层;TAC 已进入 A01/A04/A05 业务运行时,但不属于可重建视觉资产注册表。`tmp/``unpackage/``design-pipeline/generated/` 均不存在;正式生成资产仍由清单和构建器恢复,质量报告不作为长期资料保留。
## 当前规划结论
### 导航栈
- 迁移前 52 个活动页面只读统计为 `navigateTo 59``navigateBack 11``redirectTo 14``reLaunch 7`,活动页面合计 91 次;活动组件另有 4 次,封存 A06 另有 2 次。任务 3—9 已依次迁移共享组件、认证、G、T、F、R、N/M,任务 10 删除最后一个无消费者旧表单组件并关闭门禁。当前 pages/components 中 `navigateTo``navigateBack``redirectTo``reLaunch``getCurrentPages` 和业务页面路径字面量均为 `0``switchTab` 全项目为零;五种 Uni 导航调用和页面栈读取只允许由 `utils/navigation.js` 持有,业务路径只允许由 `utils/navigation-routes.js` 持有。
- MuMu 已复现 F01→F03 返回后残留两个 F01、A01→A04→登录后残留两个 A01、T03 同路由连续叠页,以及栈深为 1 时 F03 错回 G01。
- 三人终选并已实现两个唯一所有者:`utils/navigation-routes.js` 持有 52 条路由、父页参数映射和结果操作枚举,`utils/navigation.js` 持有五种 Uni 导航 API、五个公开导航语义方法、一次性结果与统一返回优先级;没有真实替换边,因此不公开替换方法。
- G01、F01、M01 保持自定义 Tab 根页;A01 是认证根页。T03 只保留一个原生页面实例,初始路由 `personId` 是不可变宿主页身份,亲属浏览使用页内成员轨迹和可变活动成员;T05 本地预览离开使用 `goBack()`,不会把活动成员误写成宿主页路由身份。
- 任务 1—10 已完成测试先行和静态实现;一次性结果绑定真实目标路由与业务参数上下文,只能由当前真实栈顶目标页消费。52 个活动路由、共享组件、四个根语义以及认证、G、T、F、R、N/M 系列均已迁移;通知目标只能由本地类型白名单映射,未知或越权目标失败关闭。未接真实写接口的页面只能形成明确的本地预览或硬关闭,不再展示伪保存、伪邀请码、伪订单或伪安全结论。任务 4—10 的 MuMu 原生流程复核仍因本轮禁止触碰模拟器而待执行,静态零债务不能替代该验收。
### T01 大规模世系树
- 此前默认 6 人数据中的 103→106 断线已通过唯一成员 owner 与字符串 `parentId` 修复;普通节点实际半高与全局常量仍相差约 4rpx,10×12 压力数据约 45/54 个父分组仍会断线,而旧测试只检查线段数量。
- 当前布局最坏为 `O(G×N)`,单人一代时接近 `O(N²)`;页面与 mock 已统一当前阶段的 `id/parentId` 夹具,但 OpenAPI 仍使用递归 `children/spouses`,无法直接对接未来规范图窗口。
- 产品终选“焦点成员渐进窗口+独立全谱概览”,初始上二代/下二代;主世系为骨架,配偶并排,每段家庭关系有独立联合点,子女从对应联合点向下。
- 技术终选视口大小的单 Canvas 同画节点和边;纯 JavaScript 按“规范化→严格校验→可见投影→确定性布局→Scene/空间索引→相机”处理,DOM 只保留页头、工具条、两档抽屉、搜索、概览和无障碍线性列表。
- 本地旧导出和 2026-07-22 线上 OpenAPI 都只有 v1 `/lineage/tree`,仍是递归模型,并缺少 overview、locator、稳定关系寻址和树版本并发合同。最新线上文档的 `722` 条路径中没有任何 `/genealogy/app/v2/``507` 个模型中也没有新图辨识字段,四个稳定业务码同样缺失。后端问题单 `API-T01-001` 已固定四条 v2 路径、FOCUS/BOUNDARY 查询、`schemaVersion/treeVersion`、EMPTY/POPULATED 空谱判别、窗口入口与全谱根分离、严格匿名节点、以 `relationshipKind` 判别的可寻址伴侣/父子关系、空 PATCH 错误码和 Scene 原子版本;`tests/lineage-openapi-contract.ps1` 已建立严格红灯,在同版本线上文档与双导出通过前,T01 客户端不得开始任务 12。
- 当前实施轮没有改写 OpenAPI 文件,也没有启动、关闭或调整 MuMu;已完成导航共享所有者、共享组件/根页头部语义、退役死入口、认证、G 系列、T 系列和 F 系列导航合同,以及树成员与家族内容夹具统一。认证请求代码已对准真实端点,但运行模式仍失败关闭;G/T/F 系列真实写接口与 T01 新图尚未接入。
### TAC 认证与无障碍安全边界
- `utils/auth-verification.js` 是当前认证场景、4 位短信码和服务端票据形状的唯一客户端 owner;生产目标由后端 `SmsCodeSecret``tests/phone-change-openapi-contract.ps1` 锁定为严格 6 位,门禁通过时必须一次替换所有活动消费者并删除旧 owner 的四位规则。`components/TacVerification.vue` 是验证浮层与 renderjs 生命周期 owner`static/tac/js/jiapu-tac-adapter.js` 是 TianAi challenge/proof/verify 映射 owner`utils/api.js` 是严格 HTTP 200 envelope、15 秒超时、离页中止、认证会话写入和反馈 wire payload 的唯一 owner。旧认证专用请求控制器入口、占位验证、伪验证码、宽松令牌兼容和 M07 假提交均已删除。
- A01 默认短信登录;密码登录因 `API-AUTH-TAC-001` 保持可见但不可用。A04/A05 都只在发送短信前验证一次,并在手机号改变后使旧验证码上下文失效;成功、失败、取消、重复回调、空响应、非 JSON、超时、离页和返回键均有静态或纯运行时合同。短信发送与重设密码的 `RVoid` 没有声明 `data` 必填,客户端因此只要求合法 HTTP 200 与整数成功 `code`,并把“省略 data”或 `data:null` 都精确归一为 `null`;登录、注册和 challenge 等有实体响应仍强制 `data`
- 当前客户端浮层已补齐对话框命名、说明关联、初始聚焦、Tab 圈定、Escape/Android 返回、焦点恢复、原生刷新与关闭按钮、48px 目标及小视口内部滚动;这只是客户端壳层预检,不代表第三方滑块本身可由 TalkBack 或键盘完成。`tests/auth-android-accessibility-release-gate.ps1` 要求 MuMu 原生证据与非拖动等价验证方式,证据缺失时固定输出 `ANDROID-AUTH-ACCESSIBILITY-RELEASE BLOCKED`
- 三人交叉质询后的唯一方向是“同一验证中心、同一短时单次 `validToken`、供应商无关的服务端决策”,永久禁止 `accessibility=true`、跳过 TAC 或无票据发短信。中国大陆非交互风控供应商只进入限时 POC,必须在真实 UniApp Android WebView 中证明 TalkBack、外接键盘和 Switch Access 无焦点陷阱且误杀、防刷、弱网与故障指标达标,不能预先写成无障碍完成。已安全绑定设备只能作为加权信号;供应商不确定或不可用时进入支持文字/中继的可审计人工兜底或稍后重试,绝不 fail-open;音频验证码仅为后续独立 POC 候选,不能作为 P0 或唯一替代。
### 领域上下文与 M07 反馈
- `utils/genealogy-context.js` 只接受无边界空白的非空词法字符串 ID,并持有独立失效标记;`utils/session.js` 在损坏令牌、退出和账号令牌变化时同步清理 ID 与标记。首次且没有历史选择或标记时可确定选择首个可用家谱;针对调用方提供的新列表,历史选择消失或显式目标不可用时写入标记并由 G01 要求用户明确重选,跨重载也禁止静默串谱。真实后台撤权能否被及时发现仍取决于后续 workspace 的 onShow/事件刷新。
- `appApi.submitFeedback` 是反馈提交唯一 owner:只接受 `feedbackContent` 必填、`feedbackType/contactInfo` 可选的三个字符串字段,trim 后删除空选填值;调用方不能关闭认证头。remote 精确 POST `/genealogy/app/feedback`,只认 HTTP 200 且 `code` 为整数成功码的 envelope;反馈响应没有声明 `data` 必填,页面也不消费返回实体。mock 固定失败关闭且不调用网络。
- M07 提交中禁用输入并使用提交前快照;成功后保留回执内容并禁止原样重复提交,请求期若仍有迟到输入则明确区分“上一份已提交”和“当前修改未提交”。超时、断网、意外 2xx/3xx、HTTP 408/5xx 或响应无效进入 `uncertain`,同一快照禁止重提;unknown 回流也会重新比较当前表单,迟到输入保持可提交而不冒充旧快照。只有确定拒绝才允许重试。`tests/m07-feedback-state-runtime-smoke.js` 已直接执行这些页面状态;真实服务联调仍受认证远端门禁约束。
### 家谱工作区远端门禁
- 三人反向质询后没有要求后端把 `AppGenealogyVo` 全部字段设为必填,也不把缺少 `security` 注解或文档媒体类型 `*/*` 单独宣称为数据泄漏。首批只消费 `genealogyId/genealogyName/canView/canManage/canEditContent/roleType`;前五项需明确类型并必填,`roleType` 还需非空正式枚举。地点、堂号、人数和简介可选并由页面诚实降级,未知额外响应字段允许忽略。
- JSON 响应身份必须使用非空词法字符串。仅在客户端拒绝 unsafe number 虽可避免串谱,却会让 OpenAPI 合法的 int64 用户永久不可用,不能作为普遍上线方案;URL path 在 wire 上本就是文本,不机械要求为了同一问题改类型。
- `/mine` 必须以必填 `canView` 或等价、可自动验证的投影保证只把当前账号仍可查看的家谱送入 context;G01 只有成功取得新列表才 reconcile。网络、超时和 5xx 保留旧现场并显示错误,明确撤权/无权/不存在才写 tombstone。G05 使用 `/overview`,取消迟到请求并在新身份加载前清掉旧数据,远端失败绝不回退 fixture 角色。
- OpenAPI 注解修复后仍须用有效账号执行无凭证、跨账号、撤权、删除、5xx、畸形 JSON 和取消反例;当前无令牌 HTTP 200+业务 `code=401` 的承载方式可以保留,也可改规范 HTTP 状态,但文档、运行时 validator 与部署行为必须一致。
### M06 帮助内容远端门禁
- 首批只使用 `/genealogy/app/help-articles` 一次取得完整正文;详情接口、文章深链、封面、浏览量和业务 ID 均不属于 M06。adapter 只准输出 `{category,title,content}` 与当前响应生命周期的展示键,必须显式丢弃 `helpId/status/sortOrder/viewCount/coverOssId/remark`
- 响应 envelope 的 `code/data` 和每行 `helpCategory/helpTitle/helpContent` 必须 required;三项均为非空字符串,分类是可直接展示标签,正文首版固定为纯文本。用户侧列表只返回已发布内容,数组顺序就是展示顺序;客户端不猜分类码、不解释 HTML/Markdown。
- 搜索与筛选只作用于已投影数组;筛选后不得重新编号。搜索、分类和刷新先收起正文,新请求以 controllergeneration 拒绝迟到响应,离页取消请求;空列表、搜索无结果、加载失败和认证失效必须分别表达。
- M06 现有分类和问题标题使用 `<view role="button">` 且触控高度不足;真正接线时改为原生按钮语义,补 `aria-pressed/aria-expanded/aria-controls`、状态播报、重试和至少 44dp 目标。源码审查不能替代 MuMu 的系统字号、TalkBack、焦点与视觉验收。
### 个人资料读取远端门禁
- 唯一 profile adapter 在 mock/remote 两种模式都产出同一窄模型;运行模式必须经 `resolveRuntimeMode()` 校验,错误配置不得静默回 fixture。remote 使用严格 envelope、15 秒超时、请求取消和 generation 防迟到,不建立跨账号缓存或经路由传递个人资料。
- 原始手机号只在 adapter 局部验证 canonical 格式,输出仅含 `maskedPhone` 与“绑定手机号,尾号 xxxx”读屏标签;页面、日志、错误、缓存和路由都不得接触明文。可选昵称、实名、邮箱只有真正省略时规范为内部空串;出现 null、空白、超长或非法邮箱时整份失败关闭。
- M01 的加载/失败只替换身份卡,服务菜单与底栏保持可用,并删除查询参数制造的假错误和“重试即成功”;通知 fixture 不得与真实身份混装为线上数量。M02 完成异步填表后才建立 dirty baselineGET 不冒充 PUT 保存;M03 只让手机号行局部加载/失败,密码入口不受普通读取失败影响,并删除不属于账号资料的“创建者”角色。
- 真正实施时原子替换禁止页面调用 API 的旧静态断言,使用 `AppLoading` 和原生按钮,补 M02 的 `aria-invalid/aria-describedby`、失败聚焦、状态播报、44dp 目标、长昵称换行和装饰图隐藏;MuMu 仍须验证系统字号、TalkBack、焦点与真实纹理对比。
### 通知读取与已读状态远端门禁
- 读取批次以无筛选列表作为当前账号完整活动集合 owner,`RListNotificationVo.data` 允许空但最多 200 条并按 `publishTime` 最新优先;`RNotificationUnreadCount.data` 为 0—200,精确等于同一活动集合中 `readStatus=UNREAD` 的数量。两个请求之间发生并发新消息时允许瞬时差异,客户端不得据此判错。
- `NotificationVo` 的首批 required 字段为 `noticeTitle/noticeContent/publishTime/readStatus`:标题 1—50、完整正文 1—1000 且为纯文本,时间必须是带时区 RFC3339,状态只允许 `READ/UNREAD`。N01 摘要最多显示 160 个字素,N02 展示同一快照的完整正文;长文本必须换行,不解释 HTML、Markdown、URL 或服务端跳转字段。
- 读取 adapter 只公开 `{snapshotKey,title,content,publishedAt,unread}`key 由成功响应 generation 与映射前 ordinal 组成;不持久化、不使用裸序号,也不把 `notificationId/genealogyId/bizId/bizType/senderPhone` 暴露给页面。成功刷新原子替换 generation,退出和账号切换立即清空;N02 不能解析 key 时只提示“请返回消息中心重新打开”。
- 首批删除通用“前往入谱审核”、详情目标按钮和从消息字段猜路由;后端将来只有提供闭合的 `bizType → route key+必填词法参数+权限/失效语义` 字典后才可独立恢复 CTA。M01/G01 共同消费未读数 owner,文案统一为“未读消息”,可见角标封顶 `99+`,读屏仍播报真实数量。
- 写批次通过前,打开 N01/N02、点击“标记已读”或“全部已读”都不得只改本地 clone。写合同通过后,唯一通知 controller 才能私有保留 1—128 位 URL-safe opaque `notificationId`;页面仍只持有 `snapshotKey`。单条和全部已读必须对当前账号幂等;read-all 只覆盖服务端接收时已存在的活动通知,并发新消息保持未读;超时或结果未知时重新读取列表和计数收敛。
- 两个失败门禁分别是 `tests/notification-read-openapi-contract.ps1``tests/notification-read-state-openapi-contract.ps1`。当前分别输出 `NOTIFICATION-READ-OPENAPI-CONTRACT BLOCKED``NOTIFICATION-READ-STATE-OPENAPI-CONTRACT BLOCKED`;后端同版本双导出、有效账号反例和 MuMu 状态矩阵完成前不能宣称通知闭环上线。
### M02 个人资料写入远端门禁
- PUT 请求体唯一 owner 固定为关闭额外字段的 `AppProfileMergeUpdateBody`,只能包含 1—3 个真正脏的 `nickName/realName/email`。昵称出现时为去边界空白的 1—30 字符;真实姓名和邮箱的精确 `""` 是唯一清空命令,非空真实姓名 1—30、邮箱 1—100 且格式有效。省略表示保持,null、纯空白、边界空白、空 body、头像/性别/生日或任意额外字段全部早失败。
- `AppProfileVo` 新增 required `profileVersion`,形状为 1—128 位 URL-safe opaque string;写请求只在 required `If-Match` header 携带,不在 body 建第二版本字段。服务端按当前账号和租户原子 CAS,成功返回含新版本的完整 canonical profile,旧版本固定 409 `PROFILE_VERSION_CHANGED`;相同字段集重复执行不产生通知、审计之外的额外业务副作用。
- M02 首次 GET 完成后才建立 baseline;保存时冻结三个输入和提交快照,成功用响应回填并重置 baseline。确定失败保留草稿;超时、断网、408/5xx 或畸形成功响应属于结果未知,先 GET 逐项核对本次脏字段,匹配则确认成功、仍为旧值才允许重试、第三值进入冲突,不盲目重复 PUT。
- session generation 是账号隔离边界:账号切换、退出和页面卸载中止等待、清空未持久草稿并拒绝旧账号迟到响应;取消 RequestTask 不等于服务端未写。真实姓名、邮箱、提交 payload 和版本不得进入日志、路由、持久缓存或遥测。
- 当前假定昵称必填,所以旧账号昵称省略时可以只改其他字段,但不能把已有昵称清空。头像选择、相册权限、OSS ID、性别和生日全部留到独立批次;当前 `<view role="button">` 假头像动作在写入实施时删除。邮箱没有验证与送达合同,页面不得继续承诺“用于接收通知”。
- `tests/profile-update-openapi-contract.ps1` 当前输出 `PROFILE-UPDATE-OPENAPI-CONTRACT BLOCKED`。后端关闭 `API-PROFILE-UPDATE-001``004`、同版本双导出通过、读取 owner 可用、有效账号/CORS 反例和 MuMu 表单矩阵完成前,M02 保持诚实本地校验而不发写请求。
### M10 当前设备退出远端门禁
- 后端 DELETE 只撤销当前请求 bearer 及同一可续签凭证族;其他设备 token 保持有效。合法签发给同一 client 的 active/revoked/expired token 重复调用统一返回 200 RVoid,操作幂等且无额外副作用;成功后用旧 token 调 profile 等受保护接口必须失败。非法或 client 不匹配返回 `RLogoutRejected`,业务码只允许 `TOKEN_INVALID/TOKEN_CLIENT_MISMATCH`,不能冒充远端已撤销。
- operation 必须 required SaToken 与非空 clientid、禁止 request body、200/401 使用 application/json 并带 `Cache-Control: private, no-store`RVoid 的整数 code required,另声明 400/429/500。线上仅有文字“需要登录”和 `*/*`,本地虽有 security/clientid 但 RVoid 无 required、只列 200,均未达到发布合同。
- `logoutCoordinator` 是唯一跨页 owner:同步捕获 token/clientid 与 logoutEpoch,立即调用 session owner 清 token、家谱上下文和账号缓存并 bump epoch,再以显式 A 快照创建 RequestTask;整个同步段不 awaitM10 页面拿不到 token。创建失败也不恢复;请求不绑定页面 controllerreLaunch/A01/M10 卸载不 abort。
- coordinator 的公开内存态只含 `{attemptId,logoutEpoch,status}`,状态为 pending/confirmed/unconfirmed/not-revoked,不保存 token、请求头或错误 payload。A01 仅在 session 仍为空且 epoch 未变化时原子消费一次提示;B 登录后 A 的迟到结果直接丢弃。进程被杀允许丢提示,但旧 token 不落盘、不排队、不跨重启重试。
- 唯一用户承诺始终是“已从本机退出”。200 显示服务器撤销已确认;network/timeout/408/429/5xx/畸形响应与 generic 401 显示未确认;typed 401/400/403 显示未能撤销。所有分支都留在 A01,不恢复 token、不返回 M10、不重新开放登录后页面;异步 callback/finally 绝不能再次 `session.clear()`
- `tests/logout-openapi-contract.ps1` 当前输出 `LOGOUT-OPENAPI-CONTRACT BLOCKED`。同版本双导出、两设备 token 隔离/复用反例、真实网络异常和 MuMu 退出提示完成前,只能称现有行为为本机退出,不能宣称服务端注销完成。
### M04 登录态修改密码远端门禁
- 密码 wire 是跨 A01 登录、A04 注册、A05 找回和 M04 登录态改密的单一合同。`PasswordLoginBody.password``PasswordChangeBody.oldPassword` 只引用 `CurrentPasswordSecret`;注册、找回和改密的新密码只引用 `NewPasswordSecret`。生产入口不再接受 MD5、十六进制摘要或两套兼容分支;确认密码只留客户端。
- `CurrentPasswordSecret` 是原样、不 trim 的 1—64 Unicode code point`NewPasswordSecret` 是 NFC 后 15—64 code point,允许空格、Unicode 和密码管理器粘贴,不强制字母/数字组成。服务端才是策略权威,必须执行常见/泄露密码 blocklist、当前密码验证、新旧不同、账号级限速和带盐慢哈希;页面校验只是即时提示。
- PUT required SaToken、非空 clientid 与关闭额外字段的 JSON body200/400/401/409/422/429/500 均为 typed JSON、`private, no-store`429 带 `Retry-After`。409/422 用 `RPasswordChangeRejected` 区分 `CREDENTIAL_VERSION_CONFLICT/CURRENT_PASSWORD_INCORRECT/NEW_PASSWORD_SAME_AS_CURRENT/PASSWORD_POLICY_VIOLATION`,不解析 `msg`
- 唯一会话方案是 ALL:严格 200 前原子写入新 verifier、提升 credential epoch,并让所有设备/所有 client 的旧 access/refresh session 跨节点失效;不返回新 token,也不让当前 bearer 继续存活。200、401、409 和传输结果未知都清秘密与对应本机会话并回 A01;只有明确未写的 400/422/429 可清 `credentialChangeInFlight` 后留页。
- 为关闭 PUT 已发出后进程被杀的窗口,session owner 在发送前只持久化 `{sessionEpoch,startedAt}` marker,禁止保存 token、密码、摘要或 body。冷启动发现同 epoch marker 时必须在任何账号缓存渲染前清会话并进 A01;新登录 bump epoch,使旧 marker 和迟到响应失效。客户端不自动重试、不建 operation-status;重新登录就是最小对账路径。
- `tests/password-change-openapi-contract.ps1` 当前输出 `PASSWORD-CHANGE-OPENAPI-CONTRACT BLOCKED`。后端关闭 `API-PASSWORD-001``005`、原子重导双文件、密码登录 TAC 可用、两设备/并发/故障注入与 MuMu 无障碍矩阵完成前,M04 继续显示“不提交服务器”。
## 三人规则
- 总共三人:主代理与两位固定评审者。
- 三个人都完整检查文档、测试和资产,不割裂分工。
- 审查阶段只读,实际文件变更只由主代理执行。
- 删除候选必须三人一致确认;任意一人提出有效疑点时继续调查。
- 用户已授权三人一致后直接删除,无需逐文件再次确认。
## 当前禁止事项
- 不执行 Git add、commit、push、restore、checkout 或 reset。
- 不启动、关闭或调整 MuMu。
- 不删除活动业务源码、接口导出、当前权威设计或用户改动;只有三人以消费者、合同和资产清单共同证明已经退役的旧入口才可同轮删除。
- 不因测试失败而删除测试。
- 不因静态搜索无匹配而直接删除资产。
- 不归档确认失效的旧内容,不保留兼容入口。
## 下一步
1. T01 后端接口门禁已经建立并运行出预期红灯;等待后端按 `API-T01-001` 发布四条 v2 操作并提供同一版本重新导出的 JSON/YAML,不手改当前快照,也不提前实施任务 12。
2. TAC 客户端批次已经完成。认证后端仍须关闭 `API-AUTH-TAC-001``004`、提供同版本双导出并修复 challenge 500;随后才允许把 `runtimeConfig.mode``mock` 切为 `remote`,执行正式 H5 CORS、真实票据重放/限流和 MuMu Android 流程。
3. 并行推进非交互风控的限时 POC 与文字/中继人工兜底合同;只有 POC、服务端原子消费和 `ANDROID-AUTH-ACCESSIBILITY-RELEASE` 全部通过,才可宣称认证达到上线门槛。
4. 新图合同通过后,按规范化与校验、布局、Scene 与空间索引、单 Canvas、页面交互、接口回流的顺序实施 T01。
5. 家谱工作区三人审查和失败合同已经完成;等待后端关闭 `API-GENEALOGY-WORKSPACE-001``003` 并提供同版本双导出。门禁通过后再测试先行实现唯一 adapter、G01 `onShow` 列表现场和 G05 `/overview` 读取,不提前固化字段或枚举。
6. G03 原子创建三人审查和失败合同已经完成;等待后端关闭 `API-G03-001``005`、以同版本双导出交付 bootstrap POST、状态查询、地区选择与统一 accessPreset。通过前不接当前宽松 `createGenealogy`;通过后先实现严格 coordinator、无 PII 恢复 marker、地区选择器、context/G05/G01 闭环,再独立完成 MuMu 表单与无障碍矩阵。
7. M06 三人审查和失败合同已经完成;等待后端关闭 `API-M06-001``003` 并提供同版本双导出。通过后测试先行实现 list-only adapter、加载/空/失败/重试、动态分类、纯文本手风琴和无障碍语义,不接详情或文章 ID。
8. 个人资料读取三人审查和失败合同已经完成;等待后端关闭 `API-PROFILE-READ-001``003` 并提供同版本双导出。通过后测试先行实现唯一掩码 adapter 与 M01/M02/M03 局部状态,M05 当前手机号展示随同一只读 owner 原子迁移但不接换绑写接口。
9. 通知读取与已读写入已分别完成三人审查和失败合同;等待后端关闭 `API-NOTIFICATION-READ-001``003``API-NOTIFICATION-STATE-001``003`。读取通过后先实现无 ID 的内存快照和 M01/G01 未读数;写入另批迁移私有 ID、删除伪本地写并验证并发收敛,不恢复不可信目标 CTA。
10. M02 个人资料 PUT 已完成三人审查和失败合同;等待后端关闭 `API-PROFILE-UPDATE-001``004`。读取与写入门禁都通过后,按 normalizer、严格 API、M02 状态机和 MuMu 表单矩阵独立实施,不混头像或账号安全。
11. M10 服务端退出已完成三人审查和失败合同;等待后端关闭 `API-LOGOUT-001``003` 后,再测试先行实现 session epoch、唯一 logoutCoordinator、A01 一次性提示和两设备撤销矩阵。
12. M04 登录态改密与四条密码 wire 已完成三人审查和失败合同;等待后端关闭 `API-PASSWORD-001``005` 后,才原子迁移共享密码策略、MD5 调用、session marker、M04 状态机和全设备撤销矩阵。
13. M05 手机号换绑三人审查和失败合同已经完成;等待后端关闭 `API-PHONE-001``005`,且 M04 raw-password 与认证/TAC 前置门禁同时通过后,再原子迁移全活动 OTP、实现专用受保护发码、共享 credential marker 与 M05 状态机。
14. 继续审查下一个不依赖现有红灯的业务域;按 G/F/R 逐域推进。随后逐批完成文字层级与无障碍,不混合验证码、账号写入、领域数据或支付。