完成40%

This commit is contained in:
rain
2026-07-23 17:21:27 +08:00
parent f1edc6b533
commit bb6431b319
114 changed files with 10931 additions and 877 deletions
+100 -9
View File
@@ -849,7 +849,7 @@ PATCH /genealogy/app/v2/genealogies/{genealogyId}/lineage/relationships/{relatio
### 22.2 页面与状态机
A01 默认短信登录;密码登录因为服务端请求体无法消费 TAC 票据而保持可见但不可用,微信登录也不得以视觉入口冒充已接通。A01/A04/A05 的发码顺序固定为:本地字段和协议校验 → `/captcha/require` → 严格 TAC challenge/verify → 取得服务端 `validToken``/genealogy/app/auth/sms/code`。验证码精确 4 位,注册或重设提交只消费短信码,不再重复 TAC。手机号变化、刷新 challenge、切换验证方法、返回或卸载都使旧上下文失效;超时、空响应、非 JSON、重复回调和迟到回调失败关闭并保留表单。短信发送与找回密码返回 `RVoid`,其 schema 未把 `data` 列为必填;客户端仍强制 HTTP 200 和整数成功 `code`,但把省略 `data``data:null` 都归一为 `null`,不把这一合法空响应误判为失败。有实体的 challenge、登录和注册响应继续要求 `data`
A01 默认密码登录并已接入线上密码 wire、`access_token` 持久化和 G01 跳转;默认 mode 保持 mock。remote 联调先展示 static TAC,但登录端点尚未消费 `validToken`,所以这不是安全闭环,`API-AUTH-TAC-001` 仍阻止发布;微信登录也不得以视觉入口冒充已接通。A01/A04/A05 的短信发码顺序固定为:本地字段和协议校验 → `/captcha/require` → 严格 TAC challenge/verify → 取得服务端 `validToken``/genealogy/app/auth/sms/code`。验证码精确 4 位,注册或重设提交只消费短信码,不再重复 TAC。手机号或密码变化、刷新 challenge、切换验证方法、返回或卸载都使旧上下文失效;超时、空响应、非 JSON、重复回调和迟到回调失败关闭并保留表单。
短信状态覆盖可发送、验证中、发送中、60 秒倒计时、失败重试、到期、手机号变更和重复点击。客户端倒计时不是服务端限流证据;最终仍须用真实服务验证手机号/IP/设备/租户限流、前后台恢复、过期和并发重放。注册成功只读取 `RAppLoginVo → AppLoginVo.access_token`,保存会话后直接进入 G01;找回成功返回 A01,不自动登录。
@@ -879,17 +879,17 @@ M07 的唯一接口 owner 是 `appApi.submitFeedback`。请求只允许 `feedbac
### 23.1 家谱工作区只读合同
家谱工作区第一批唯一选择 `GET /genealogy/app/genealogies/mine` 持有当前账号可访问集合,选择 `GET /genealogy/app/genealogies/{genealogyId}/overview` 持有 G05 展示;语义重复的 `GET /{genealogyId}` 不同时接入。页面不把 fixture 与远端事实混用,也不在两个详情响应之间择优补字段。
家谱工作区第一批唯一选择 `GET /genealogy/app/genealogies/mine``appListMyGenealogies`持有当前账号可访问集合,选择 `GET /genealogy/app/genealogies/{genealogyId}/overview``appGetGenealogyOverview`)持有 G05 展示;两个 operationId 全局唯一,路径只开放 GET 且无 request body语义重复的 `GET /{genealogyId}` 不同时接入。页面不把 fixture 与远端事实混用,也不在两个详情响应之间择优补字段。
线上 `AppGenealogyVo.genealogyId` 当前是 JSON `integer/int64`。最大合法 int64 经 JavaScript JSON 解析后会不可逆失真,事后 `String()` 无法恢复,因此响应身份必须改为非空词法字符串;URL path 在 wire 上本是文本,手写客户端可保持词法字符串,不机械把 path 声明本身当成同一阻塞。仅拒绝 unsafe number 可以失败关闭,却会使 OpenAPI 合法用户永久不可用,只能用于关闭功能开关的预研,不能作为正式兼容方案。
线上 `AppGenealogyVo.genealogyId` 当前是 JSON `integer/int64`。最大合法 int64 经 JavaScript JSON 解析后会不可逆失真,事后 `String()` 无法恢复,因此响应身份与 overview path 都必须引用非空、1—128 位 URL-safe 词法 `GenealogyId`仅拒绝 unsafe number 可以失败关闭,却会使 OpenAPI 合法用户永久不可用,只能用于关闭功能开关的预研,不能作为正式兼容方案。
首批最小实体只消费 `genealogyId/genealogyName/canView/canManage/canEditContent/roleType`两层响应 envelope 的 `code/data` 与这些实体字段必须进入 `required`ID、名称为非空字符串,三项 capability 为布尔值,`roleType` 提供至少两个稳定非空枚举供 G01 分组和标签。其他字段保持可选:地点、堂号、人数、简介等有值才展示未知额外响应字段允许忽略。若产品坚持保留当前 fixture 的来源、管理者、认证、始祖、上级谱、支系、更新时间和激活人数,则后端必须另补合同;首批不为复刻 mock 强制 22 个字段全部必填。
首批最小实体只消费 `genealogyId/genealogyName/canView/canManage/canEditContent/roleType``RListAppGenealogyVo/RAppGenealogyVo` 都是关闭额外字段且精确只有 required `code/data` 的成功 envelope`code` 固定 200data 分别为 `AppGenealogyVo[]` 与单个 `AppGenealogyVo`;实体上述字段进入 requiredID 与名称分别引用 `GenealogyId/GenealogyName`,三项 capability 为非 null 布尔值,`roleType` 提供至少两个稳定非空枚举供 G01 分组和标签。`AppGenealogyVo` 其他字段保持可选:地点、堂号、人数、简介等有值才展示未知额外实体字段允许忽略。若产品坚持保留当前 fixture 的来源、管理者、认证、始祖、上级谱、支系、更新时间和激活人数,则后端必须另补合同;首批不为复刻 mock 强制 22 个字段全部必填。
`canView` 是进入 `availableIds` 的授权投影,不能从 `roleType/status/memberStatus` 猜值;后端也可以改为能够由自动化证明“`/mine` 只返回当前仍可查看 ACTIVE 成员”的等价合同。G01 仅在新列表成功校验后 reconcile;网络、超时、5xx 和畸形响应保留旧现场并显示错误,明确无权、撤权或不存在才写 tombstone。G05 每次加载先清空旧数据、取消迟到请求,只消费 `/overview`;能力缺失按最低权限处理,不回退本地角色。
`canView` 是进入 `availableIds` 的授权投影,不能从 `roleType/status/memberStatus` 猜值;`/mine` `x-current-account-viewable-only=true``x-revocation-policy=OMIT_ONLY_AFTER_CONFIRMED_ACCESS_LOSS` 机器声明只返回当前账号仍可查看的家谱。G01 仅在新列表成功校验后 reconcile;网络、超时、5xx 和畸形响应保留旧现场并显示错误,成功列表确认消失才写 tombstone。G05 每次加载先清空旧数据、取消迟到请求,只消费 `/overview`对象无权、撤权与不存在统一 `NON_DISCLOSING_GENEALOGY_NOT_AVAILABLE`能力缺失按最低权限处理,不回退本地角色。
当前线上 OpenAPI 没有有效 security 声明且媒体写作 `*/*`,但无令牌实测三条读取均被拒绝、实际 Content-Type 为 JSON,因此两项是必须修复的发布文档质量问题,不单独声称已经公开泄漏。真正发布门禁还包括有效令牌下的无凭证、跨账号、撤权、删除、401/403/404/5xx、畸形 JSON 与取消反例。当前部署使用 HTTP 200+业务 `code=401`;后端可以保留业务码承载或改为规范 HTTP 状态,但文档、运行时 validator 与部署行为必须一致
当前线上 OpenAPI 没有有效 security 声明且媒体写作 `*/*`,但无令牌实测三条读取均被拒绝、实际 Content-Type 为 JSON,因此两项是必须修复的发布文档质量问题,不单独声称已经公开泄漏。生产合同不再允许 HTTP 200 包业务错误:两条读取都 required SaToken 与 1—128 位非空 clientid,只允许 typed `application/json``/mine` 精确 200/401/429/500`/overview` 精确 200/400/401/404/429/500,分别固定 `GENEALOGY_ID_INVALID/AUTH_REQUIRED/GENEALOGY_NOT_AVAILABLE/RATE_LIMITED/GENEALOGY_WORKSPACE_UNAVAILABLE`。每个响应必须包含固定单值 enum `Cache-Control: private, no-store`429 另有 1—300 秒 Retry-After;只允许 `traceparent/tracestate/x-request-id/x-correlation-id` 四种非语义 tracing header,禁止 ETag 与其他语义 header。component parameter/response/header/schema 解析只接受 exact local `#/components/<section>/...` 引用,拒绝外部或错分区 `$ref` 冒充 owner;四种 tracing header 的 component 引用也必须 exact local,内联时只接受 allowed-key 关闭且带非 null 的 string schema 的 Header Object。clientid、成功/错误 code/data/businessCode、实体字段与 role 枚举都用 allowed-key 集拒绝 `nullable`、readOnly/writeOnly、冲突组合或其他未拥有的验证关键字。真正发布门禁还包括有效令牌下的无凭证、跨账号、撤权、删除、typed HTTP、畸形 JSON 与取消反例
`tests/genealogy-workspace-openapi-contract.ps1` 只读取受保护的同版本 JSON/YAML,固定上述响应身份、typed envelope、required 与 capability/role 边界;当前旧双导出与线上文档都不能通过。门禁关闭前不建立 G01/G05 专属字段 adapter,不让客户端成为第二份猜测合同。问题固定为 `API-GENEALOGY-WORKSPACE-001` 无损身份与最小 schema 闭包、`API-GENEALOGY-WORKSPACE-002` 可访问集合与能力投影、`API-GENEALOGY-WORKSPACE-003` 错误语义及对象级授权行为。
`tests/genealogy-workspace-openapi-contract.ps1` 以结构化 JSON 为判断对象并先要求受保护 JSON/YAML 语义深比较通过,唯一拥有上述 path/method/operationId、SaToken/clientid、词法参数、响应身份、typed envelope、required cache headers、required 与 capability/role 边界;它全局递归扫描 APP GET 的 200 schema 闭包,除 `/overview` 外任何返回 `RAppGenealogyVo/AppGenealogyVo` 或结构等价单谱实体的旁路详情都失败,`/mine` 的数组投影不误算成单谱读取。当前旧双导出与线上文档都不能通过。G11 门禁只消费该门禁的 PASS 结论,不复制 overview 合同。门禁关闭前不建立 G01/G05 专属字段 adapter,不让客户端成为第二份猜测合同。问题固定为 `API-GENEALOGY-WORKSPACE-001` 无损身份与最小 schema 闭包、`API-GENEALOGY-WORKSPACE-002` 可访问集合与能力投影、`API-GENEALOGY-WORKSPACE-003` typed HTTP、缓存与对象级授权行为。
### 23.2 M06 帮助内容只读合同
@@ -1017,6 +1017,96 @@ POST required SaToken、非空 clientid 和 `Idempotency-Key`。`GenealogyBootst
页面实施必须同步删除伪提交 owner 与 mock fixture mutation,覆盖地区加载、确定拒绝、fatal/quarantined、PENDING、结果未知、已提交待进入、上下文失败和导航失败。文本标签与 input 建立关系;访问预设使用真正 radio/`aria-checked`;按钮至少 44dp,提交中冻结字段和返回;错误具备 `aria-invalid/aria-describedby`、首错聚焦和持续状态播报;重复提醒与成功/放弃层复用具备焦点圈定和恢复的 `AppDialog`。当前源码仍有 clickable view、54rpx 目标、23rpx 选项、无关联错误和 UTC 日期上限,这些只登记为实施项;没有 MuMu 证据前不得宣称视觉或 TalkBack 上线。后端门禁先执行无第三方依赖的 `tests/openapi-yaml-json-parity-runtime-smoke.js`,以无损任意精度数字、严格 YAML mapping 分隔符和完整对象结构深比较 JSON/YAML,确保不安全整数差一也不能假绿,再由 `tests/g03-bootstrap-openapi-contract.ps1` 检查唯一 JSON 语义、组合 schema 内旧字段、同 scope 参数重复、根 PUT 精确白名单和结构化不可变终态;客户端门禁 `tests/g03-bootstrap-client-release-gate.ps1` 实际执行依赖注入的 marker/status 状态机测试,不再用 helper 名称顺序冒充行为证据。两项只是 G03 自身门禁;真实开放还必须同时通过家谱工作区读取门禁、聚焦测试全量回归与 MuMu 原生交互/无障碍验收,任一红灯都不得开放真实创建。
### 23.10 G06/G08/G09/G10 普通加入申请合同
普通申请与邀请码直入是两个独立业务合同。任务 36 只覆盖七个 APP operation`appSearchPublicGenealogies``appCreateGenealogyJoinApplication``appGetGenealogyJoinApplicationRequest``appListMyGenealogyJoinApplications``appWithdrawGenealogyJoinApplication``appListPendingGenealogyJoinApplications``appReviewGenealogyJoinApplication`。邀请码成功后直接建立成员关系且不生成审核记录,后续必须使用独立票据和直接加入接口;G08 的 `source=invite` 不得调用本批普通申请 POST。
`API-JOIN-001` 固定三个专用 cursor page 和词法身份。搜索、mine、pending 都使用 `limit=1..50`、opaque cursor、无 `total`cursor 绑定 tenant/account/client 与过滤条件。搜索按 `updatedAt DESC, genealogyId DESC`mine/pending 按 `submittedAt DESC, applyId DESC`,不得跨投影复用不存在的排序字段。`GenealogyId/JoinApplicationId` 均为 1—128 位 URL-safe 词法字符串,禁止 JSON int64。搜索项只含识别家谱所需的谱名、姓氏、地区、可选堂号/上级谱/支系/认证标签/成员数/更新时间与单一 `viewerState`;不同时返回可冲突的 `canApply`。mine 用 discriminator 精确区分 `PENDING/APPROVED/REJECTED/WITHDRAWN`,只有 REJECTED 必填申请人可见 `rejectionReason`pending 只含 `applyId/applicantName/relationDesc/applyReason?/submittedAt`,递归禁止 phone、用户、邀请人和审核人身份。
`API-JOIN-002` 固定闭合申请体 `applicantName/relationDesc/applyReason?`,前两项必填,长度依次为 1—50、1—100、1—500。`JOIN_APPLICATION_TEXT_V1` 是本域唯一文本规范:Unicode NFC、去首尾边界空白、CRLF/CR 归一为 LF、不折叠内部空白并按 Unicode code point 计长。旧 `GenealogyJoinApplyBody/GenealogyJoinAuditBody``phone/inviterUserId` 与数字 `status/auditRemark` 不再是 APP owner;通用 G-series 与 core-flow 测试必须把所有权移交给专项门禁。
`API-JOIN-003` 固定无 PII 幂等与崩溃恢复。`GenealogyJoinApplicationRequestKey``gja.{13 位 issuedAt 毫秒}.{22—43 位 base64url CSPRNG}`,随机量至少 128 位;服务端按时间固定 10 分钟接受窗、5 分钟未来偏差与认领后最多 2 分钟收敛。canonical identity 覆盖 method、规范 path、genealogyId、tenant、account、client 与规范 body。同 scope/key/request 重放同一 `GenealogyJoinApplicationReceipt`,不同 digest 返回 `IDEMPOTENCY_KEY_REUSED`;同 tenant/account/genealogy 最多一个活动 PENDING,必须由数据库唯一约束保证。幂等 scope、活动唯一 scope、事务 effects、重验字段、状态迁移与 cursor 机器扩展都必须是真实 JSON 数组,逗号字符串不能冒充集合。POST 在领域事务内重验 genealogyState=READY、accessPreset=PUBLIC_APPLY 与 applicationEligibility,并与 G11 设置 PUT 共同声明 `x-public-apply-coordination=ATOMIC_SINGLE_WINNER`;关闭公开申请与新准入竞态只能一方提交。申请领域行和 SUCCEEDED 回执在同一业务事务提交,resolver 判定 FAILED 前不得把已提交申请误报为零写。
状态唯一 owner 是只读 `GET /genealogy/app/genealogies/join-apply-requests/{requestKey}`。响应以显式 mapping 的 oneOf 精确为 `PENDING{resolveBy,retryAfterSeconds}``SUCCEEDED{result}``FAILED_NO_COMMIT`;只允许 `ABSENT→PENDING→SUCCEEDED/FAILED_NO_COMMIT`,终态不可变。GET 不创建墓碑、不推进状态;接受窗内未知 key 返回带 acceptUntil/Retry-After 的非泄漏 404,窗后无记录按 key 时间零写计算 FAILED,迟到 POST 永久拒绝。跨 account/tenant/client 统一 404。客户端只持久 `{sessionEpoch,requestKey,startedAt}`;当前进程可用冻结 snapshot 重放,重启只查 status,不把姓名、关系、理由或完整 body 落盘。
`API-JOIN-004` 固定撤回 CAS:DELETE 只允许当前申请人撤回 PENDING;`WHERE status=PENDING` 决定唯一赢家,相同撤回只返回 WITHDRAWN typed receipt,审核已赢或其他终态返回 typed 409 与无 PII current state。`API-JOIN-005` 固定审核 CASbody 只允许 `{decision:APPROVE}``{decision:REJECT,rejectionReason}` 两个闭合分支;相同决定只重放 APPROVED/REJECTED 原 200,规范化后不同拒绝理由或相反决定返回 409,审核成功绝不返回 WITHDRAWN。批准在一个事务内重验权限、READY 与 PUBLIC_APPLY,完成 PENDING CAS、唯一成员关系和 APPROVED;入口 capability 不是服务端授权证明。POST 的 409 只引用 `RJoinApplicationKeyConflict`DELETE/PUT 的 409 只引用带 current 的 `RJoinApplicationStateConflict`;禁止用联合两类业务码的宽响应 owner 产生跨操作假绿。key 复用、过期或活动 PENDING 冲突不强制携带不存在的申请 current,只有申请状态/决定冲突分支必带 current。不引入 applicationVersion、If-Match 或审核详情端点。
`API-JOIN-006` 固定部署与客户端发布门槛。七个 operation required SaToken/非空 clientid,所有声明响应只允许 `application/json``Cache-Control: private, no-store`429 带 Retry-After;禁止 `*/*`、default、int64 身份、通用 RList/RObject/RVoid、HTTP 200 包装认证错误。OpenAPI 静态扩展只能登记幂等、cursor、CAS 和事务意图,不能证明数据库实现;后端还须用数据库观测、并发与 fault injection 验证唯一约束、零写、事务、fencing、真实 HTTP/CORS。客户端门禁通过前 G06/G08/G09/G10 继续本地预览;接线时原子删除 G10 手机号、三处消息中心承诺、LOCAL_WITHDRAWN 和遗留 adapter,补齐原生按钮、44dp、提交冻结、状态播报、错误关联、首错聚焦、长文换行及 MuMu 的系统字号/TalkBack/键盘/返回/慢网/杀进程矩阵。
`tests/join-application-openapi-contract.ps1` 是上述七个 operation、schema 与响应闭包的唯一静态 owner,当前精确输出 `JOIN-APPLICATION-OPENAPI-CONTRACT BLOCKED`。只有后端同版本重导 JSON/YAML、专项门禁、后端集成反例、客户端状态机、全量回归和 MuMu 发布矩阵全部通过,普通加入申请才可称为上线闭环;静态门禁单独转绿不开放页面。
### 23.11 G06/M08 邀请码签发与直接加入合同
任务 37 与普通申请完全隔离。唯一产品流程为:M08 管理自己有权签发的活动邀请;G06 在 JSON body 中安全解析邀请码(不消费票据、不建立成员,仅允许轮换短时 grant 摘要),展示可信的最小家谱目标和“确认后立即成为成员、不进入审核”,用户明确确认后直接兑换;成功刷新 workspace、安装家谱 context,再进入 G01/G05。邀请码流程不进入 G08,不提交 `applicantName/relationDesc/applyReason`,不创建 join-application、PENDING 或审核通知。真实接线时必须原子删除 G06→G08 的 invite 导航、G08 `source=invite` 分支、`JP2026`、首个 fixture 目标和普通 `canApply` 资格复用;成员资料或人物关联另立合同。
`API-INVITE-001` 固定六个 operation`appListMyGenealogyInviteTickets``appIssueGenealogyInviteTicket``appRevokeGenealogyInviteTicket``appResolveGenealogyInviteTicket``appRedeemGenealogyInviteTicket``appGetGenealogyInviteRedemptionRequest`。所有 operation required SaToken、非空 clientid、typed JSON 和 private/no-store429 带 Retry-After。邀请码只能置于 resolve 的 JSON body,绝不进入 URL、query、导航、持久化、日志、APM、分析、自动测试、崩溃报告或发布证据截图;用户主动显示/复制/系统分享属于受提示的显式动作。列表只返回当前活动票据元数据,不返回原码、领取人或剩余目标身份。
`API-INVITE-002` 固定首版为单次、24 小时、服务端时间判定的 opaque bearer ticket。原码使用版本前缀和 26 位 Crockford Base32 随机量,至少 128 位 CSPRNG 熵;展示可分组,canonical 校验不接受易混淆字符。数据库以 HMAC/摘要查找,仅为同 key 幂等重放使用隔离 KMS envelope encryption 保存原码 600 秒;恢复窗内重放同一 ticket 和原码,窗后只返回同一 ticket 的无秘密 metadata 与 `ISSUED_SECRET_UNAVAILABLE`,不得新建票据。列表永不恢复原码。状态唯一为 `ACTIVE→CONSUMED/REVOKED/EXPIRED`,终态不可逆。resolve 不消费票据或建立成员,但允许写入/轮换每个 tenant/account/client/ticketVersion 唯一的短时 grant 摘要;旧 grant 立即失效、TTL 最多五分钟并清理。redemption token 绑定 tenant/account/client/inviteTicketId/ticketVersion/genealogyId/authorizationEpoch;这里的 authorizationEpoch 是服务端邀请授权版本,签发者移除/降权、邀请功能关闭或票据版本变化时提升并使旧 grant 失效。编辑输入、切账号或 session epoch 变化立即销毁客户端内存 token。
`API-INVITE-003` 固定签发与撤销权限。M08 从 workspace `/mine` 取得当前明确家谱,不新增可信 URL genealogyIdworkspace 投影字段 `canInviteMembers` 唯一映射服务端 capability `INVITE_MEMBER`,服务端仍重验 READY、邀请功能和当前成员关系。POST 无可配置 body,并用数据库约束控制每个 tenant/genealogy/issuer 的活动票据上限。签发 `Idempotency-Key``gii.{13 位毫秒}.{22—43 位 base64url CSPRNG}`;600 秒只限制不存在 key 的首次认领,已存在 key 永远定位同一 ticket,不同 digest 优先 409,恢复窗内重放原码、窗后只返 metadata,绝不签第二枚。客户端 marker 固定 `{sessionEpoch,genealogyId,requestKey,startedAt}`,不得从重启后的当前 workspace 猜 genealogyId。KMS 密文、ticket、HMAC lookup 和 request receipt 作为单一提交单元,任一步失败零票据。DELETE 只允许签发者本人管理并做 ACTIVE→REVOKED CAS,相同撤销重放原 200CONSUMED/EXPIRED 返回 typed 409 current state,跨签发者统一 404。权限降级或邀请功能关闭必须让活动票据和旧 redemption token 立即失效。
`API-INVITE-004` 固定兑换事务。redeem 无 request body,只通过 required、敏感的 `Genealogy-Redemption-Token` header 和 `gir` Idempotency-Key 消费当前账号 grant。服务端在同一事务内重验 token 绑定/有效期、票据 ACTIVE/版本/未过期、签发者权限、家谱 READY/邀请功能、账号可加入与容量,执行 ACTIVE→CONSUMED CAS、唯一 `(tenant,genealogyId,account)` MEMBER 关系和 SUCCEEDED receipt;失败全部回滚。不得建立人物、亲属关系或姓名副本。同码两账号、同账号两码、撤销/兑换、权限撤销/兑换竞态都只能有一个事务赢家;失败事务不得消费票据。已有普通 PENDING 固定 `ACTIVE_PENDING_APPLICATION`,引导先在 G09 撤回,邀请流程不得暗改任务 36 状态。
`API-INVITE-005` 固定结果未知恢复。兑换 request key 采用 `gir.{issuedAt}.{CSPRNG}`,600 秒接受窗、300 秒未来偏差、认领后 120 秒内收敛;状态纯读 GET 只允许 `ABSENT→PENDING→SUCCEEDED/FAILED_NO_COMMIT`,终态不可变,已存在终态优先于 absent 计算,FAILED 保证票据未消费且成员未创建。成员、票据消耗和成功回执同事务提交,resolver/watchdog 以 fencing CAS 收敛,旧 worker 不得迟交。终态回执至少保留 30 天,客户端 marker TTL 为 30 天并先于回执清理;任何清理后也不得凭控制记录缺失否定已提交领域写。客户端 dispatch 前只持久 `{sessionEpoch,requestKey,startedAt}`,不持久邀请码、redemption token、目标或 bodySUCCEEDED 后刷新 `/mine` 并只重试本地 context/导航收口,FAILED 回 G06 重新输入,unknown 不换 key。
`API-INVITE-006` 固定安全、交互与发布反例。invalid/expired/revoked/consumed/跨 tenant 统一非枚举 `INVITE_TICKET_NOT_AVAILABLE`resolve 需要账号、设备/IP、前缀和全局多维限流与时间侧信道控制。M08 覆盖加载、无权限、无票据、签发/unknown、原码显示/复制/分享、即将过期、撤销/竞态;G06 覆盖输入、格式错、解析、可信目标、明确确认、redeem PENDING/unknown、已加入、活动普通申请、限流、票据失效、账号切换和迟到响应。原生按钮、tablist/tab/aria-selected、label、aria-invalid/describedby、首错聚焦、44dp、状态播报、长码/长谱名换行和提交冻结均为发布门槛;MuMu 覆盖 320/360/412、1.3 倍字号、TalkBack、键盘、粘贴、双击、慢网/断网、杀进程、Android 返回与系统分享取消。后端还须用真实账号、数据库观测、并发、fault injection、日志审计和正式 HTTP/CORS 证明合同,OpenAPI 扩展不能替代实现证据。
### 23.12 G11 家谱设置版本化写入合同
任务 38 只治理 G11 的设置读取与写入,不混入 G12 字辈、成员管理、邀请码签发或普通申请审核。全局唯一写 owner 保留 `PUT /genealogy/app/genealogies/{genealogyId}`operationId 固定为 `appUpdateGenealogySettings` 且全局只出现一次;不新增 PATCH、`/settings` 旁路或任何递归引用设置 body/包含任意一个 `genealogyName/intro/accessPreset` 单字段的第二写入口,外部或错分区同名 `$ref` 也按违规 owner 失败,只有精确 G03 建谱 POST 豁免。为消除裸 `intro` 歧义,`/genealogy/app/genealogies...` 写 body 根层的 `intro` 由设置合同保留,子域必须使用自身有界名称。单谱唯一读取 owner 继续是 `GET /genealogy/app/genealogies/{genealogyId}/overview`,同版删除重复的通用 GET,并对全部 `/genealogy/app/` GET 以响应 schema 闭包和 allOf 属性合成拒绝任何旁路详情;G11 只消费 workspace 唯一门禁的 PASS 结论,每次进入必须 fresh 读取 overview 后才建立可编辑 baseline,不从路由、`/mine` 卡片、G05 旧内存对象、`roleType` 或 fixture 推导当前设置与权限。
`API-SETTINGS-001` 固定共享字段与唯一模型。家谱 ID 继续引用词法 `GenealogyId`;新增共享 `GenealogyName`,统一 G03 创建、G11 更新和 `AppGenealogyVo` 为非 null、NFC、无边界空白、无换行/控制字符的 1—24 Unicode code point,删除 G11 旧 20 字双上限。共享 `GenealogyIntro` 精确允许非 null 空串,或 NFC、无边界空白、1—80 code point 的内容;正文只允许内部 LF,拒绝 CR、tab 和其他控制字符,空串就是清空,省略才是保持,null 与纯空白非法。`GenealogyAccessPreset` 仍由任务 35 作为 APP 读/建/改唯一枚举 owner,只有 `MEMBER_ONLY/PUBLIC_APPLY`。overview 与 PUT 200 都复用关闭额外字段、精确 `{code,data}``RAppGenealogyVo → AppGenealogyVo`;后者 required `genealogyId/genealogyName/intro/accessPreset/settingsVersion/canManage`,不新建会重复字段规则的 settings snapshot。
`API-SETTINGS-002` 固定原子 dirty-only merge。请求 body 唯一为关闭额外字段、`minProperties=1/maxProperties=3``AppGenealogySettingsUpdateBody`,属性精确为 `genealogyName/intro/accessPreset` 且都非 required;出现字段更新、省略字段保持,body 不携带 version。服务端在同一事务锁定家谱并重新校验权限后一次应用全部出现字段,不能按整资源 PUT 把其他字段覆盖。canonical 后实际无变化可返回 200,但必须保留原版本且除审计外不产生领域副作用;客户端 clean draft 永不发请求。
`API-SETTINGS-003` 固定唯一并发模型。新增非 null、1—128 位 URL-safe opaque `GenealogySettingsVersion`,只允许比较,不得解析、递增或经过 JavaScript Number;它以 `x-version-scope-fields=[genealogyName,intro,accessPreset]``x-version-change-policy=CANONICAL_SETTINGS_CHANGE_ONLY` 锁定只在这三个 canonical 设置实际变化时更换,其他领域写与 canonical no-op 都不得误增版本。`AppGenealogyVo.settingsVersion` requiredPUT required `If-Match` 引用同一 owner,请求 body 不复制 version。项目统一采用 typed HTTP 409,而不是为 G11 单独引入强 ETag/412。409 `RGenealogySettingsConflict` 根层只允许 `oneOf/discriminator` 两个关键词,每个分支只能是 exact local schema ref`GENEALOGY_SETTINGS_VERSION_CHANGED` 分支 required `current: AppGenealogyVo``GENEALOGY_NOT_READY``ACTIVE_PENDING_APPLICATIONS` 分支不得带 current 或审核数据。只有仍具管理权限的调用者可得到版本/current;服务端事务内重验权限。冲突优先级固定为 version → READY → active PENDING,旧 writer 不得用过期 baseline 触发业务判断。
`API-SETTINGS-004` 固定访问预设迁移与普通申请并发。活动 PENDING 只阻断实际的 `PUBLIC_APPLY→MEMBER_ONLY`:只改名称/简介、已经是 MEMBER_ONLY 的 no-op 或 `MEMBER_ONLY→PUBLIC_APPLY` 不得被误阻断;同一请求还改名称时,只要访问预设迁移被阻断,整笔请求零写。活动 PENDING 返回 `ACTIVE_PENDING_APPLICATIONS`,G11 引导去 G10 处理,不自动拒绝、撤回或迁移申请;邀请码合同不受影响。设置 PUT 和任务 36 新申请 POST 都必须在事务内重验 genealogyState/accessPreset(申请另重验 applicationEligibility),并共同声明 `x-public-apply-coordination=ATOMIC_SINGLE_WINNER`PENDING 检查、preset 更新和新申请准入竞态只能一方提交,不把实现限制为特定数据库隔离级别。
`API-SETTINGS-005` 固定响应、结果未知与缓存收口。PUT 精确声明 200/400/401/403/404/409/422/429/500,全部只允许 typed `application/json`;每个 response 必须包含固定单值 enum `Cache-Control: private, no-store`429 另有 1—300 秒 `Retry-After`,只允许 `traceparent/tracestate/x-request-id/x-correlation-id` 非语义 tracing header,禁止 ETag 与其他语义 header。parameter/response/header/schema 解析只接受 exact local component ref;四种 tracing header 若内联,必须是 allowed-key 关闭且带非 null 的 string schema 的 Header Object,不能以 external/wrong-section ref 或任意 inline schema 绕过。所有标量、引用字段、成功/错误 envelope 和 fieldErrors 用 allowed-key 集拒绝 `nullable`、readOnly/writeOnly、冲突组合与其他未拥有的验证关键字。禁止 default、3xx、`*/*`、通用 RObject/Void、可加 debug 字段的宽 envelope 和 HTTP 200 包业务错误。200 返回完整 canonical `RAppGenealogyVo`400、401、403、404、422、429 为确定失败,500 `GENEALOGY_SETTINGS_OUTCOME_UNKNOWN`、网络、超时、408、取消和畸形 2xx 都是结果未知。客户端提交时冻结 `{baseline,dirtyPatch,settingsVersion,sessionEpoch}`,未知后不自动第二次 PUT,而 fresh 读取 overview:全部脏字段等于目标只可陈述“当前设置已与所选内容一致”,不能证明本次请求成功;仍为 baseline 才允许用户明确重试;部分匹配或第三值进入字段级三方比较。成功原子回填 canonical baseline,失效 `/mine`、overview、公开搜索和公开预览缓存并重新校验 context;权限/成员关系已变时清 context 回 G01,否则回 G05。
`API-SETTINGS-006` 固定客户端与发布激活条件。本批只建立 OpenAPI 门禁与权威文档,不创建因生产 coordinator 不存在而恒红、只扫描源码 token 或复制 OpenAPI 断言的占位 client gate。后端同版双导出、workspace 读取和共享 owner 门禁转绿后,客户端批次第一项写操作必须先新增实际执行生产 normalizer/coordinator 的纯 Node 失败测试,再实现 adapter/coordinator;覆盖 24/80 code point、dirty/clear、三个 409、三方比较、unknown 对账、权限撤销、双击/取消/迟到响应、sessionEpoch、canonical 回填、缓存/context 收口。随后才可原子删除 fixture 数字映射、本地成功 timer 和“禁止 appApi”旧断言。页面至少有 loading、ready/dirty、saving、saved、validation、uncertain/reconciling、conflict、no-permission、auth-expired、unavailable;用原生 button/radio、真实 label、aria-invalid/describedby、首错聚焦、busy/live 状态、44dp 与返回冻结。G11 OpenAPI、workspace/shared owner、可执行客户端行为门禁、聚焦/全量回归及 MuMu 320/360/412、1.3 倍字号、TalkBack、软键盘、双击、慢网/断网/杀进程、Android 返回与权限/版本竞态必须全部通过,OpenAPI 绿只表示可以开始客户端 TDD,不表示允许直接接线或上线。
`tests/g11-settings-openapi-contract.ps1` 是全局唯一设置写入口、body、settingsVersion/If-Match、409 oneOf、响应矩阵与设置字段 owner 的唯一静态门禁;任务 35 的 G03 门禁只继续拥有 bootstrap、共享 accessPreset/旧 DTO 删除及其读取传播,不再检查设置 operation、body 或响应,workspace 与 join 门禁分别唯一拥有读取基础和申请准入反向协调。当前专项门禁稳定输出 `G11-SETTINGS-OPENAPI-CONTRACT BLOCKED` 和 48 项缺口;受保护 OpenAPI 未修改。
### 23.13 G12 字辈集合版本化保存合同
任务 39 只治理 G12 字辈集合读取与原子保存,不混入逐条编辑、人物世代、T01 高亮、邀请码或 G11 设置。`API-POEM-001` 固定全局唯一 owner 为 `GET/PUT /genealogy/app/genealogies/{genealogyId}/generation-poems`operationId 分别且全局唯一为 `appGetGenerationPoemSet``appUpdateGenerationPoemSet`。同版删除旧 collection POST、逐行 PUT、`/batch/preview``/batch/save``/management` 及其旧 schema/response;不引入 preview token、服务端 preview/status 或第二写入口。GET/PUT 200 共用关闭额外字段的 `RGenerationPoemSetSnapshot → GenerationPoemSetSnapshot`,快照精确为 `{genealogyId,poemSetVersion,items}`,只投影 ACTIVE、严格按 generationNo 升序;普通读只要求 canView,维护写在事务内重验 tenant、canEditContent 与 READY。
`API-POEM-002` 固定唯一请求与完整候选语义。`AppGenerationPoemSetUpdateBody` 精确 required `{items,disableMissing}`items 为 0—500 个严格 generationNo 升序的 `AppGenerationPoemSetItem`。已有 ACTIVE 行必须提交当前词法 poemId,允许把稳定 ID 显式移动到新世代;新行省略 poemId,引用已停用/未知 ID 失败。generationNo 是唯一排序 owner,范围 1—2147483647generationText 允许重复。`disableMissing=false` 保留全部未声明 baseline ACTIVE`true` 软停用遗漏项;空 false 是 no-op,空 true 停用全部,历史绝不物理删除。服务端必须依次 `RESOLVE_BASELINE_ACTIVE_IDS``VALIDATE_DECLARED_STRICT_ORDER`、构造声明目标、应用遗漏策略、按 generationNo 排序 merged candidate,再验证 ID/世代唯一、`GENERATION_SLOT_CONFLICT`、连续性、`VALIDATE_FINAL_ACTIVE_CAPACITY`,随后 `ALLOCATE_UNIQUE_NEW_IDS` 并验证新 ID 非空、唯一、非 baseline,最后 `WRITE_ATOMICALLY`。swap 必须显式声明所有受影响行,全部成功或零领域写且外部不可见中间态。
`API-POEM-003` 固定唯一并发与文本模型。`GenerationPoemSetVersion` 是非空、词法、不可解析且永不复用的 ACTIVE 语义版本;PUT required `If-Match` 引用同一 owner,body 不重复版本,不发布 ETag/412。canonical no-op 返回当前 200 并保持版本,ACTIVE 语义变化必须生成新版本。字辈 ID 与版本均只能词法比较,不经 JavaScript Number。`GenerationPoemText` 必须为 well-formed UTF-16、NFC、1—50 Unicode code point、无边界空白,并拒绝 C0/C1、CR/LF/tab、bidi override/isolate、方向标记、zero-width、BOM、word joiner、行/段分隔符与孤立 surrogate;不以 UTF-16 code unit 截断。
`API-POEM-004` 固定 typed HTTP、授权和部署 owner。GET 精确声明 200/400/401/404/429/500PUT 另含 403/409/422;成功、固定错误、409 union 和 422 fieldErrors 都是关闭额外字段的 typed `application/json`,禁止 default、3xx、`*/*`、200 包业务错误或通用 RObject。所有响应 required `Cache-Control: private, no-store`429 required Retry-AfterSaToken 必须是精确 header apiKey `Authorization` 且 security requirement 是 JSON 数组,clientid 非空。浏览器预检唯一声明 owner 为顶层 `x-app-gateway-policies.APP_GATEWAY_PREFLIGHT`,精确允许 Authorization/Content-Type/If-Match/clientid、GET/PUT/OPTIONS、显式部署 origin allowlist 和 600 秒;operation 只引用该 owner。正式发布仍须真实 gateway OPTIONS/preflight smoke,导出扩展不能替代部署证据。
`API-POEM-005` 固定冲突与结果未知恢复。409 只允许 `POEM_SET_VERSION_CONFLICT + current``GENEALOGY_NOT_READY`;422 精确包含请求顺序、重复 ID/世代、slot collision、非连续、容量、文本与行不可用,校验失败全程零写。网络、超时、408、取消、5xx 和畸形 2xx 都按 unknown。客户端冻结 baselinePoemSetVersion、baselineOrderedItems、declaredItems、disableMissing 与 sessionEpoch,按相同规则构造 effective target 后 fresh GET,执行 `FRESH_GET_THREE_WAY_NO_AUTO_PUT`existing 按 poemIdgenerationNoNFC 文本匹配,NEW 按 generationNo+文本匹配且返回 ID 必须非空、唯一、非 baseline;比较要求同基数、无额外 ACTIVE 行,忽略版本判断语义等价,但变化 target 必须有新且未复用版本。先判 current==target 并执行 `CURRENT_EQUALS_TARGET_FIRST_NO_ATTRIBUTION`,只确认当前事实;再判 current==old 允许用户明确重试,否则进入 divergent,任何分支都不自动 PUT。
`API-POEM-006` 固定客户端、无障碍与激活条件。本批只建立受保护 OpenAPI 门禁、对抗合同、Unicode 运行时证据和中文权威文档;G12 在全部后端门禁转绿前保持明确本地预览。后端同版双导出通过后,客户端第一项写操作必须新增直接执行生产 normalizer/coordinator 的纯 Node 失败测试,再实现唯一 adapter/coordinator,并原子删除 fixture、timer、本地成功、旧 preview/save 假合同及“禁止 appApi”旧断言。页面覆盖 loading/empty/editing/validating/saving/saved/unknown/conflict/no-permission/auth-expired/unavailable、清空二次确认、草稿冻结、live region、首错聚焦、原生按钮、44dp 与返回保护;MuMu 覆盖 320/360/412、1.3 倍字号、TalkBack、软键盘、0/1/500×50、swap、双击、慢网/断网/杀进程、权限撤销、READY 变化和账号切换。未来 T01 高亮只消费正式 ACTIVE 快照,不能成为字辈写 owner。
唯一静态 owner 为 `tests/g12-generation-poem-openapi-contract.ps1`,当前输出 `G12-GENERATION-POEM-OPENAPI-CONTRACT BLOCKED`;合法 zero issues 种子、大小写严格 JSON Pointer、ref 图、HTTP 方法、callback、候选/unknown/CORS 和 annotation 非 owner 由 `tests/g12-generation-poem-openapi-adversarial-contract.ps1` 覆盖,字辈 Unicode 由 `tests/g12-generation-poem-unicode-contract-runtime-smoke.js` 覆盖。G-series 只保留 owner 转移说明,不再重复 G12 合同;受保护 OpenAPI 与页面保持未修改。
### 23.14 F01/F03 家族动态与一级评论读取合同
任务 40 只治理 F01 动态列表、F03 动态详情和正常一级评论读取,不混入 F02 发布、点赞、评论/回复写、回复读取或媒体文件读取。受保护双导出中的 APP 路径已有谱上下文,页面与 fixture 也以 `(genealogyId,feedId)` 为身份;因此三方否决全局 feed 路由,保留谱内解析和授权。2026-07-23 线上 OpenAPI 3.1.0 与受保护旧导出都不能直接接页:两者分别存在 `/feeds``/feeds/page``/comments``/comments/page` 双读取 owner,无稳定 operationId,身份仍为 int64feed 使用通用或无 required 的响应,线上 `FamilyFeedCommentVo` 还暴露手机号、业务用户 ID、内部审核人/动作/原因、状态和持久化字段。双导出内部 parity 只能证明两份旧文件相同,不能证明它们与线上同版本或适合发布。
`API-FEED-READ-001` 固定三个唯一 owner`GET /genealogy/app/genealogies/{genealogyId}/feeds``appListFamilyFeeds``GET /genealogy/app/genealogies/{genealogyId}/feeds/{feedId}``appGetFamilyFeed``GET /genealogy/app/genealogies/{genealogyId}/feeds/{feedId}/comments``appListFamilyFeedRootComments`。同版本删除两个 `/page` GET;非分页 collection GET 成为唯一 cursor owner。写方法、likes、replies 和媒体读取不由本批声明或删除,不能借本批旁路接入。三个 operationId 在全局各出现一次,GET 无 request body。
`API-FEED-READ-002` 固定词法身份与无 PII 最小投影。`GenealogyId/FamilyFeedId/FamilyFeedCommentId` 均为 1—128 位 URL-safe opaque string,客户端只比较、不解析,不得进入 JavaScript Number。`AppFamilyFeedReadItem` 是 closed required `{feedId,feedContent,authorDisplayName,publishedAt,hasMedia}`:正文为 `FAMILY_FEED_TEXT_V1`、1—300 Unicode code point;作者名是经过授权的展示投影,原账号缺失时由服务端提供非空退化文案;时间是服务端生成且不可修改的 RFC3339。`hasMedia` 只证明存在附件,不泄露 `mediaOssIds`;在独立媒体读取合同通过前客户端必须展示诚实占位,不能把媒体动态静默伪装为完整纯文字内容。标题、标签、点赞、状态、排序值和总评论数不是当前页面的可靠服务端字段,不从 fixture 或正文猜值。
`AppFamilyFeedRootCommentReadItem` 是 closed required `{commentId,commentContent,authorDisplayName,publishedAt}`;正文为 `FAMILY_FEED_COMMENT_TEXT_V1`、1—1000 Unicode code point。comments collection 只投影当前正常可见的一级评论,排除回复、删除占位、parent/level、手机号、账号/业务用户 ID、tenant、状态、remark、审核字段和 createBy/updateBy。详情不嵌 comments,评论独立加载和独立失败;没有 total 时客户端只能显示“已加载 N 条”,不得把已加载数组长度冒充服务端总数。
`API-FEED-READ-003` 固定对象级可见性。三个 GET 每次请求都重新验证 tenant、genealogy 与 membership;详情和评论另验证 feed 属于 path 家谱且当前可见。未知 ID、错谱、动态删除/隐藏、无权、撤权或 cursor 跨主体/跨谱/跨 feed 重放统一 typed `404 FAMILY_FEED_NOT_AVAILABLE`,不以 403 或不同消息泄露资源存在性。未认证固定 401。后端只能从已通过可见性过滤的根评论集合生成响应和 cursor;前一页授权不延续为后一页授权。
`API-FEED-READ-004` 固定 opaque keyset cursor,不采用 pageNum/pageSize。首请求不带 cursor`limit` 可省略且默认 20,范围 1—50refresh 必须丢弃旧 cursor`nextCursor` 缺席表示结束,响应不含 total、pageNum 或 hasMore 冗余真值。feed 按 `(publishedAt DESC, feedId DESC_ORDINAL)`,一级评论按 `(publishedAt ASC, commentId ASC_ORDINAL)`。cursor 绑定 tenant/account/authSession/client、genealogyId、评论的 feedId、projection/order/limit、windowUpperBound 与 lastTuple,并具有完整性和过期校验;篡改或过期返回 `400 FAMILY_FEED_CURSOR_INVALID`,不静默从首页重启。
读取窗口精确命名为 `UPPER_BOUND_KEYSET_LATEST_VISIBLE`,不能宣传成严格 MVCC snapshot:首次请求确定上界,新插入内容等 refresh 后进入;后续读取前删除、审核隐藏或撤权的内容省略;仍可见但已编辑的正文按该页读取时的最新可见版本返回。无 total,因此上述变化不会伪造恒定总量;keyset 与不可变 publishedAt/词法 ID tie-breaker 只保证在合同窗口内不因新增头部内容产生 offset 重复或漏项。
`API-FEED-READ-005` 固定 typed HTTP、安全与缓存。三个 GET 精确声明 200/400/401/404/429/500,只允许 closed typed `application/json`;禁止 default、3xx、`*/*`、通用 RList/RObject/PageResult、HTTP 200 包业务错和开放 DTO。所有 response 必须有单值 `Cache-Control: private, no-store`429 另有 1—300 秒 Retry-Afteroperation security 必须是精确 SaToken 数组且空 scopesrequired `clientid` 为非空 1—128 字符串。读取复用唯一 `APP_GATEWAY_PREFLIGHT`OpenAPI 扩展只登记 owner;正式 H5 origin 的真实 OPTIONS/preflight、凭证与拒绝 origin/header/method 反例仍由部署门禁证明。
`API-FEED-READ-006` 固定激活顺序。`tests/family-feed-read-openapi-contract.ps1` 是三条 GET、词法 ID、closed projection、cursor、可见性、typed response 与 no-PII 闭包的唯一静态 owner,当前输出 `FAMILY-FEED-READ-OPENAPI-CONTRACT BLOCKED``Issues: 85``tests/family-feed-read-openapi-adversarial-contract.ps1` 用完整 zero issues 合法种子和 58 个独立变异覆盖旧 owner、外部/多跳 ref、大小写、security owner/数组、HEAD/OPTIONS/callback、PII/审核字段、int64、offset/total、错误 403 分流、wildcard 与缓存头,输出 `FAMILY-FEED-READ-OPENAPI-ADVERSARIAL-CONTRACT PASS MUTANTS=58`;它只证明静态门禁会正确转绿和拒绝反例,不解除后端红灯。后端还须以同版本 JSON/YAML/live `/v3/api-docs`、真实账号、权限撤销、跨谱 cursor、并发插入/删除/隐藏、数据库观测与正式 HTTP/CORS 关闭 P0。
后端绿前 F01/F03 保持按复合身份深拷贝且失败关闭的 fixture,`runtimeConfig.mode` 保持 mock,不把 dormant `appApi.getFeeds` 宽分支接页,也不在远端失败后静默回退 fixture。后端三份证据通过后,客户端第一项写操作必须是直接执行生产 read normalizer/coordinator 的纯 Node 失败测试;随后原子迁移 F01 列表和 F03 详情/评论,删除 fixture 锁、嵌入评论、伪重试和旧“必须 fixture”断言。状态必须覆盖首屏 loading/empty/error、refresh、load-more/end/局部失败、详情 404、评论独立失败、`hasMedia` 占位、离页取消、sessionEpoch/切谱/切账号迟到响应、权限撤销、滚动与焦点恢复;原生 button、44dp、live region、长文换行和 MuMu 320/360/412、1.3 倍字号、TalkBack、键盘、慢网/断网/返回矩阵是发布条件。
## 二十四、当前精确执行顺序
后续不再按页面样式迁移重做,而按以下独立阶段执行:
@@ -1033,7 +1123,8 @@ POST required SaToken、非空 clientid 和 `Idempotency-Key`。`GenealogyBootst
10. M10 当前设备退出三人审查与 OpenAPI 红灯已完成;等待后端关闭 `API-LOGOUT-001``003` 后,再实现 session epoch、logoutCoordinator 和 A01 一次性状态。
11. M04 登录态改密与四条密码 wire 三人审查、OpenAPI 红灯已完成;等待后端关闭 `API-PASSWORD-001``005` 后,原子迁移共享策略、MD5 消费者、session marker、页面状态机与全设备撤销。
12. M05 手机号换绑与全活动 OTP 三人审查、OpenAPI 红灯已完成;等待后端关闭 `API-PHONE-001``005` 且 M04/认证前置门禁通过后,再原子迁移六位码、专用受保护发码、最终 PUT 与 credential marker。等待期间转向 G/F/R,不混入本批。
13. 全局文字层级与无障碍第二轮
14. 生产配置、隐私权限、可观测性、构建发布、升级回滚与 MuMu 全流程终审
13. 任务 36 普通加入、任务 37 邀请码直入、任务 38 G11 设置、任务 39 G12 字辈集合和任务 40 F01/F03 家族动态读取的三人审查、专项 OpenAPI 红灯与 owner 移交已完成;等待后端分别关闭 API-JOIN/API-INVITE/API-SETTINGS/API-POEM/API-FEED-READ 后按各自客户端 TDD 顺序实施,任何单项 OpenAPI 转绿不开放页面。当前继续下一个独立业务域
14. 全局文字层级与无障碍第二轮
15. 生产配置、隐私权限、可观测性、构建发布、升级回滚与 MuMu 全流程终审。
T01 必需的线性目录和 44dp 控件随 T01 一起完成;认证浮层的壳层无障碍不扩张为全项目已通过。外部门禁阻塞时继续无依赖批次,但任何真实成功、接口兼容或视觉通过都必须有对应证据。