规划(08-09-production-admin-break-glass-reset): 启动管理员应急重置
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-09 08:53:16 +08:00
父节点 00fecf2a03
当前提交 8806521c22
共修改 6 个文件,包含 362 行新增和 0 行删除
@@ -0,0 +1,114 @@
# 生产管理员 break-glass 密码重置:设计说明
## 1. 设计边界
本任务恢复唯一活跃管理员的认证能力,不修复产品、不启用邮件、不做合同验收。写入面由两个产品原生事务构成:break-glass 脚本写入临时密码,以及登录后的 `/api/auth/change-password` 写入最终密码。两者共同构成重置生命周期;除此之外没有生产账号或业务写入。
合同只读验收属于仍在 `LOGIN PAUSED` 的 `08-08-production-authenticated-readiness`,只有本任务完成、最终 secret 可用后才恢复。
## 2. 状态机与门禁
```text
PLANNING
-> source/target/runtime-script SHA(只读)
-> --preflight PASS/1/true(只读)
-> --diagnose-wecom-binding(只读)
-> 旧 hash 有限恢复材料 + 临时/最终 secret 就绪
-> --apply ACTIVE_ADMIN_BREAK_GLASS(唯一一次脚本写入)
-> --verify PASS(只读)
-> 正式 secret 写入临时凭据
-> /login 一次 -> must-change redirect
-> /change-password 一次(最终密码写入)
-> --verify-password-changed PASS(只读)
-> 正式 secret 写入最终凭据
-> 写后诊断 / 清理旧 hash、临时 secret、浏览器状态
-> RESET_COMPLETE / handoff readiness task
```
任何读门禁失败都停在写入前。apply 通道中断或非 PASS 时不得再 apply;先以 `--verify` 判断是否已经提交。一次登录/改密失败同样停止,不能消耗第二次尝试。
## 3. 被执行脚本的身份
本地当前脚本自已部署候选以来未变,但生产执行仍以三方事实确认:
1. 公网/运行 build 为产品候选;
2. `git show <产品候选>:server/src/scripts/reset-active-admin-password.js` 生成审查基线;
3. 运行容器同路径 SHA-256 与基线精确一致。
只记录 `scriptShaMatch=true`。不把当前 HEAD 的任务文档提交当产品 build,也不向生产同步脚本。
## 4. 唯一目标与并发保护
`--preflight` 使用 `findActiveAdmins(... take:2)`,只有 1 个 active、未 archived、具 `admin/system_admin` 的账号才 PASS。apply 前已取得目标 id/token version;事务内再读 status、archived、角色、token version。任何变化都抛 `active_admin_target_changed`,事务不写。
会话和 challenge 的 `updateMany` 均带 `employee_id=target.id`。审核时必须将返回计数理解为“唯一目标的副作用”,不能外推为全表操作。
## 5. 凭据生成、保管与流转
临时密码和最终密码由本机受控 Node/CSPRNG 进程生成,强制满足密码策略且不相同。值不进入环境变量或 argv:
- 临时密码写到新建 `0600` 临时 secret,通过 stdin 喂给 apply;
- 正确 email 只在受控进程中从唯一管理员记录取得;
- apply/verify PASS 后,原子更新正式 live-smoke secret 为 email + 临时密码;
- 强制改密成功后,再原子替换为最终密码;
- 更新过程保留其他角色键,不生成明文备份副本。
若主 secret 更新失败,临时 secret 保留到恢复决策,避免 apply 已成功但密码丢失。所有清理只输出布尔。
## 6. 旧 hash 有限恢复材料
写前使用 read-only 查询读取唯一目标当前 hash,并由远端进程直接写入 `0700` 受控目录下随机 `0600` 文件。hash 不经过普通 stdout,不离开生产现场。
它不是完整或部分状态回滚。即使另行批准写回,也不能恢复 token version、会话、challenge、锁定计数或审计。默认禁止写回;仅当新凭据异常且获得新的明确写入批准时,才可把它当作一个格式已知的 hash 候选。
保留期限取以下较早者:
- `--verify-password-changed` PASS;
- apply 后 24 小时。
到期删除并验证 `recoveryMaterialDestroyed=true`。若 hash 未销毁,任务不得宣称完全收口。
## 7. 写入事务与验证
### 7.1 Break-glass apply
输入仅走 stdin,命令必须包含精确确认串。预期输出:唯一目标、passwordUpdated、mustChangePassword、tokenVersionIncremented、locksCleared、仅目标会话/challenge 计数和 auditCreated 均满足合同。
非零退出、缺失 JSON、`status!=PASS` 或 SSH 中断都进入 `APPLY_UNCERTAIN`。此时只允许 `--verify`;若 verify 显示写入已落地,禁止重试。
### 7.2 首次登录与强制改密
一次性 Playwright Node runner 使用新会话、单 browser、零 retry,并只从 stdin/受控文件描述符读取临时与最终密码;runner 源码不落仓库或 test spec,凭据不进入 argv/env:
1. `/login` 填正确 email 和临时密码,提交一次;
2. 必须跳到 `/change-password`;任何 `/portal` 或业务路由出现都视为 contract mismatch;
3. 填临时密码、最终密码、确认密码,提交一次;
4. 允许应用成功跳到 `/portal`,但立即停止,不点击任何业务入口;
5. 清除浏览器 storage/cookie 并关闭 runner。
随后运行 `--verify-password-changed`。该验证要求唯一目标、must-change=false、locks cleared、activeSessions=1,以及改密和 break-glass 两类审计都存在。
## 8. 企微绑定影响
`--diagnose-wecom-binding` 写前和最终验证后均只读运行。它记录绑定存在、目录同步、冲突、近期密码/企微登录计数,不读取或输出 external userid。脚本不修改绑定;如出现冲突或目标不唯一,停止并另行处理,不在本任务绑定/解绑。
## 9. 失败矩阵
| 阶段 | 失败 | 动作 |
| --- | --- | --- |
| provenance/target/SHA | 不一致 | 停止,零写入 |
| preflight | 非 PASS/1/true | 停止,不选择目标 |
| recovery material | 无法安全保存 | 停止,不 apply |
| apply 明确失败 | `status=BLOCKED` | 停止,不重试 |
| apply 通道不确定 | 无完整结果 | 只跑 `--verify` 定性 |
| verify | BLOCKED | 保留现场,禁止登录 |
| secret 更新 | 失败 | 保留受控临时 secret,停止 |
| login/change-password | 首次失败 | 停止,零 retry,不 apply 第二次 |
| verify-password-changed | BLOCKED | 保留现场,另行决策 |
| hash 清理 | 失败 | 标记敏感清理未完成,不归档 |
## 10. Reviewer 与交接
本任务涉及唯一生产管理员、认证状态机和真实账号写入,执行后必须由只读 reviewer 核对:脚本身份、目标唯一、apply 次数、事务副作用、两次密码写入、secret/hash 清理和零业务写入。reviewer 不修改代码、账号或现场材料。
最终只交接“管理员最终凭据已在仓库外 secret 就绪”和稳定计数;不交接凭据值。合同功能是否可用仍由原 readiness task 独立判定。
@@ -0,0 +1,78 @@
# 生产管理员 break-glass 密码重置:执行计划
> 当前状态:IN PROGRESS。用户已于 2026-08-09 完成规划只读复核并明确批准启动;尚未执行生产脚本、账号写入、登录或 secret 修改。
## 1. 规划事实
- [x] 任务边界:只恢复唯一管理员认证生命周期,不做合同/员工负向验收。
- [x] 现有脚本具 `--preflight`、`--verify`、`--verify-password-changed`、`--diagnose-wecom-binding` 四个只读入口,以及精确 `ACTIVE_ADMIN_BREAK_GLASS` 写入确认串。
- [x] apply 事务内二次校验目标;session/challenge updateMany 均限定 `target.id`。
- [x] 脚本使用应用同源 bcrypt、强制改密、token/session/challenge/audit 合同;当前脚本自产品候选以来未变化。
- [x] npx/Playwright 运行条件可用;密码不得进入 CLI argv/env,执行时使用从 stdin 读取凭据的一次性 Playwright runner,新会话、零 retry,不新增 test spec。
- [x] 旧 hash 仅是短期高敏有限恢复材料,不是完整/部分状态回滚。
## 2. 启动门禁(规划复核后)
- [x] 用户完成规划只读复核并明确批准 task start。
- [x] 用户批准按本任务合同执行生产管理员认证恢复;批准范围包含脚本完整副作用与强制改密的两次密码写入,不包含任何业务页面或合同验收。
- [ ] 规划提交已 published,工作区 clean;原 readiness task 保持 `LOGIN PAUSED`。
- [ ] 本地 focused unit test 与 node syntax PASS。
- [ ] 生产 identity/read-only preflight、65 migrations、产品 build、合同窗口通过。
- [ ] 运行容器脚本 SHA 与产品候选精确一致。
## 3. 生产执行清单
### Phase A:只读前置
- [ ] 运行脚本 `--preflight`,人工核对 PASS/1/true。
- [ ] 运行写前 `--diagnose-wecom-binding`,记录脱敏布尔/计数。
- [ ] 记录唯一目标的 must-change、token/session/challenge 计数快照;不输出 identifier/hash。
- [ ] 创建远端 `0700/0600` recovery material,写入旧 hash;验证存在性,stdout 不含内容/路径。
- [ ] 生成互不相同的临时/最终强密码,写入本机 `0600` 临时 secret;不进入 env/argv/history。
### Phase B:唯一 break-glass 写入
- [ ] 从 stdin 执行一次且仅一次 `--apply ACTIVE_ADMIN_BREAK_GLASS`。
- [ ] 人工读取 JSON:status、target count、password/must-change/token/locks/session/challenge/audit 字段完整且 PASS。
- [ ] 任一通道/结果异常立即停;只允许 `--verify` 定性,禁止第二次 apply。
- [ ] 运行 `--verify`,要求 must-change、locks、sessions、codes、audit 全部符合脚本合同。
### Phase C:secret 与强制改密
- [ ] `--verify` PASS 后,原子更新仓库外 live-smoke admin identifier/temp password,权限 `0600`。
- [ ] 通过 stdin 凭据的一次性 Playwright runner 新会话登录一次,确认只跳 `/change-password`。
- [ ] 提交一次强制改密;成功到 `/portal` 后立即停止,不进入业务页面。
- [ ] 运行 `--verify-password-changed`,要求最终 PASS。
- [ ] 原子更新仓库外 live-smoke admin final password,权限仍为 `0600`。
- [ ] 运行写后 `--diagnose-wecom-binding`,只记录脱敏布尔/计数。
### Phase D:清理与交接
- [ ] 删除本机临时 secret、Playwright storage/cookie 和远端旧 hash;验证三个 destroyed/cleared 布尔。
- [ ] 复核其他账号/会话未被脚本触及,合同/schema/权限/业务写入为 0。
- [ ] 最终生产只读 status/window 快照。
- [ ] 启动只读 reviewer;按真实结果更新 PRD AC。
- [ ] 向 `08-08-production-authenticated-readiness` 只交接“最终管理员 secret 已就绪”,不交接值;恢复该任务继续合同只读验收。
## 4. 允许与禁止的输出
| 阶段 | 允许 | 禁止 |
| --- | --- | --- |
| preflight/SHA | PASS、count、match 布尔 | 主机、路径、账号、email |
| apply/verify | status、副作用布尔/计数、apply 次数 | 临时密码、新旧 hash、token |
| browser | 路由阶段、submit 次数、HTTP 状态 | 表单值、cookie、storage、截图中的账号 |
| cleanup | destroyed/cleared 布尔 | recovery material 路径/内容 |
## 5. 规划门禁
- [x] `cd server && npm test -- tests/unit/scripts/reset-active-admin-password.test.js`:PASS,6/6。
- [x] `node --check server/src/scripts/reset-active-admin-password.js`:PASS。
- [x] `python3 ./.trellis/scripts/task.py validate 08-09-production-admin-break-glass-reset`:PASS。
- [x] `node scripts/qa/check-process-contract.mjs --baseline --json`:PASS,0 errors / 0 warnings。
- [x] `node scripts/qa/check-trellis-governance.mjs --json`:PASS,0 errors / 0 warnings。
- [x] `node scripts/qa/check-sensitive-content.mjs`:PASS,0 findings。
- [x] `git diff --check`:PASS。
## 6. 当前结论
规划已获用户只读复核与启动批准。下一步先提交并发布规划,使执行来源满足 clean/published;随后进入生产只读门禁。所有前置门禁通过前,不生成密码、不保存旧 hash、不执行账号写入。
@@ -0,0 +1,131 @@
# 生产管理员 break-glass 密码重置
## Goal
在生产邮件自助重置不可用、唯一活跃管理员现有仓库外密码已确认不匹配的前提下,仅使用仓库既有 `reset-active-admin-password.js` 恢复管理员认证能力。任务必须先证明执行脚本与已部署候选一致、目标管理员唯一,再执行一次带字面确认串的事务写入;随后完成脚本要求的首次强制改密并销毁旧 hash 有限恢复材料。
本任务只完成“临时密码 → 强制改密 → 最终密码可用”的认证恢复生命周期,不进入合同或其他业务页面,不执行 `08-08-production-authenticated-readiness` 所属的合同登录态验收。恢复完成后回到该任务继续只读验收。
## Background and Confirmed Facts
- 前序任务已证明生产运行产品候选为 `0224ad8`,65 条 migration 全部 applied,连续四次 `rollbackWindow=OPEN / terminatedRows=0`,生产基线健康。
- 生产只有 1 个活跃管理员;其 email 可唯一解析,未锁定、登录失败计数为 0,但现有仓库外管理员密码与生产 hash 不匹配。登录 attempts 仍为 0。
- 生产 `EMAIL_ENABLED=false`,公网 capabilities 为 `emailDeliveryReady=false / email_disabled`;自助忘记密码不可用,且管理员 password reset 未锁定、请求计数为 0。
- `server/src/scripts/reset-active-admin-password.js` 自提交 `9b12451` 起存在,在已部署候选 `0224ad8` 与当前 HEAD 之间没有变化。执行前仍必须比较运行容器脚本与已审查候选脚本的 SHA-256,不能只依赖 Git 历史结论。
- 脚本 `--preflight`、`--verify`、`--verify-password-changed`、`--diagnose-wecom-binding` 均为只读;写入只在 `--apply ACTIVE_ADMIN_BREAK_GLASS` 分支发生,缺少字面确认串会返回 `explicit_confirmation_required`。
- `applyReset` 只选择唯一 active、未 archived、具有 `admin/system_admin` 角色的账号,并在事务内再次校验状态、角色和 token version,目标变化会 fail-closed。
- 所有会话、密码重置码、邮件登录码更新均限定 `employee_id=target.id`;不会撤销其他员工会话。
- 脚本使用应用同源 `hashPassword`,写入 `must_change_password=true`。首次成功登录必定进入 `/change-password`;只有完成该页面的密码写入后,`--verify-password-changed` 才能 PASS。
## Scope
1. 对已发布来源、唯一生产目标、运行脚本 SHA、唯一管理员目标和当前认证/企微绑定状态做只读门禁。
2. 在受控现场以最小权限短期保存旧 password hash,生成符合现行密码策略的一次性临时密码;二者均不回显、不进入仓库、task、普通日志或对话。
3. 仅一次执行 `reset-active-admin-password.js --apply ACTIVE_ADMIN_BREAK_GLASS`,密码只经 stdin 输入;人工读取结构化结果并立即运行 `--verify`。
4. 将唯一管理员 email 与临时密码原子写入仓库外 `live-smoke` secret,使用真实浏览器仅登录并完成强制改密;随后将最终密码原子更新到同一 secret。
5. 运行 `--verify-password-changed`,确认强制改密完成;销毁临时凭据和旧 hash 有限恢复材料,形成脱敏交接记录。
## Requirements
### R1 来源、目标与脚本门禁
- 执行来源必须 clean、committed、published,并通过既有生产精确身份/read-only preflight;不得使用 unpublished override。
- 生产运行/公网 build 仍须匹配产品候选 `0224ad8`;当前任务文档 HEAD 不作为产品 build 预期。
- 运行容器内脚本 SHA-256 必须与 `git show 0224ad8:server/src/scripts/reset-active-admin-password.js` 一致;不一致立即 `PAUSED`,不得从当前工作区向生产复制脚本。
- 本地先运行脚本单元测试;测试失败、脚本漂移、目标身份不确定、migration/status 异常均停止。
### R2 脚本只读前置
- 先运行 `--preflight`,要求 `status=PASS`、`activeAdminMatches=1`、`exactTarget=true`;不能用此前聊天或旧查询替代现场结果。
- 在写入前运行 `--diagnose-wecom-binding`,保存其布尔/计数摘要。`exactTarget=false`、目标冲突或查询失败立即停止;普通绑定存在本身不是密码重置阻塞,不修改/解绑企微。
- 另取生产认证状态只读快照:must-change、token version、活跃会话/验证码计数和窗口状态只记录布尔/计数;不输出账号、email、hash 或生产路径。
- `--preflight`、企微诊断、脚本 SHA 与旧 hash 保存完成前,禁止生成写入请求或调用 `--apply`。
### R3 临时凭据与有限恢复材料
- 临时密码与最终密码分别由 CSPRNG 生成,满足至少 12 位、含大小写和数字且不含公司名/常见序列;两者不得相同,也不得复用旧 secret。
- 临时密码先写入本机仓库外、`0600` 的受控临时 secret;apply 从 stdin 读取,不放 argv、环境变量、shell history 或日志。apply PASS 且 `--verify` PASS 后,才把正确 email/临时密码原子更新到正式 live-smoke secret。
- 旧 hash 仅保存于生产受控现场的随机 `0600` 文件,目录 `0700`;本地只记录 `recoveryMaterialPresent=true`,不得记录路径或 hash。
- 旧 hash 不是完整或部分状态回滚:写回也不能恢复被撤销会话、清理的锁定/计数、token version、失效验证码或审计。它唯一用途是新凭据异常时提供一个已知格式正确的 hash。
- 旧 hash 最晚在 `--verify-password-changed` PASS 后立即销毁;若流程 24 小时内未完成,也必须销毁并记录 `recoveryMaterialDestroyed=true`,不得无限保留。
### R4 唯一写入与失败即停
- 唯一 break-glass 写入命令为 `node src/scripts/reset-active-admin-password.js --apply ACTIVE_ADMIN_BREAK_GLASS`,执行次数严格等于 1。
- 必须接受并记录脚本的认证副作用:password hash 更新、`must_change_password=true`、三个认证失败/锁定域清零、password-reset request 状态清零、token version +1、仅目标账号活跃会话撤销、重置码/邮件码失效、写入 `SYSTEM_ADMIN_BREAK_GLASS_PASSWORD_RESET` 审计。
- `--apply` 非 PASS、SSH/容器通道中断、输出缺失/不可解析或目标变化时立即停止;不得盲重试第二次。先只读运行 `--verify` 定性实际状态,再回来取得新决策。
- 不手写 password hash UPDATE,不修改角色、启用/归档状态、email、手机号或其他账号。
### R5 写后验证与强制改密
- apply 后立即运行 `--verify`,要求:唯一目标、`mustChangePassword=true`、locksCleared、activeSessions=0、activePasswordResetCodes=0、activeEmailLoginCodes=0、auditPresent=true。
- `--verify` PASS 后,使用一次性 Playwright 浏览器 runner、单浏览器、零 retry 完成一次密码登录。runner 从 stdin/受控文件描述符读取凭据,不把密码放入 CLI argv、环境变量或持久 test spec;登录只允许 `/login` → `/change-password`,必须观察 `mustChangePassword` 跳转,不得进入 `/portal` 后的业务页面。
- 强制改密使用临时密码作为旧密码、独立生成的最终密码作为新密码;只提交一次。失败立即停止,不重复猜测。
- 改密成功后运行 `--verify-password-changed`,要求 `mustChangePassword=false`、locksCleared、activeSessions=1、passwordChangeAuditPresent=true、breakGlassAuditPresent=true。
- `--diagnose-wecom-binding` 可在写前和最终验证后各运行一次,只记录绑定/同步/近期登录计数;不得尝试企微绑定、解绑或真机登录。
### R6 Secret、清理与任务交接
- 最终密码经 `--verify-password-changed` PASS 后原子更新仓库外 live-smoke secret;文件必须为 `0600`,不得留下临时副本或 shell history。
- 清理临时密码文件、浏览器 storage/cookie、旧 hash 恢复材料;只记录布尔结论,不记录实际位置或内容。
- 本任务不执行合同详情、终止按钮、员工权限负向或其他业务验收。重置任务完成后,恢复 `08-08-production-authenticated-readiness`,使用已确认最终凭据继续其只读 AC。
- 任一敏感值出现在 stdout、artifact、task 或对话即 `FAIL`;先移除/轮换暴露材料,再继续。
## Acceptance Criteria
- [ ] AC1:执行来源 clean/published,生产身份、65 migration、产品 SHA 和运行脚本 SHA 门禁全部 PASS;未复制或替换生产脚本。
- [ ] AC2:`--preflight` 为 PASS/1/true,写前 `--diagnose-wecom-binding` 与认证状态快照完成;目标不唯一或冲突时没有执行写入。
- [ ] AC3:旧 hash 以 `0700/0600` 受控短期材料保存,临时/最终密码由 CSPRNG 独立生成并满足策略;任何敏感值均未回显或进入仓库/task/chat。
- [ ] AC4:`--apply ACTIVE_ADMIN_BREAK_GLASS` 恰好执行一次且返回结构化 PASS;未执行手写 SQL 写入,未修改角色、启用状态、email 或其他账号。
- [ ] AC5:紧随 apply 的 `--verify` PASS,脚本声明的 must-change、锁、会话、验证码、token/audit 副作用与实际计数自洽。
- [ ] AC6:浏览器仅完成一次管理员登录和一次强制改密提交,按预期进入 `/change-password`;未进入合同或其他业务页面,失败时没有自动 retry。
- [ ] AC7:`--verify-password-changed` PASS,最终仓库外 secret 原子更新且 `0600`;临时凭据、浏览器状态和旧 hash 均已销毁。
- [ ] AC8:生产账号写入仅限唯一管理员认证生命周期;其他员工会话/账号、合同、权限、schema 与业务数据写入计数为 0。
- [ ] AC9:只读 reviewer 复核目标唯一性、apply 次数、副作用、secret 清理和证据脱敏;task/process/governance/sensitive/diff 门禁全部 PASS。
- [ ] AC10:任务交接明确说明只恢复管理员认证,不代表合同登录态验收通过;`08-08-production-authenticated-readiness` 继续保持 PAUSED,直至其自身 AC 完成。
## Out of Scope
- 合同台账/详情/终止按钮、员工权限负向、archived/terminated 样本或其他产品验收。
- 员工账号选择或密码恢复;邮件通道启用、SMTP 配置、生产重启、忘记密码验证码流程。
- 手写 SQL/Prisma 写入、角色/状态/email/手机号修改、创建新管理员、企微绑定/解绑或真机验证。
- 部署应用、修改产品代码、脚本、schema、migration、生产 `.env` 或 feature flag。
- 把旧 hash 当作完整回滚,删除审计,或为恢复历史会话/计数执行额外写入。
## Accepted Risk and Stop Conditions
- 生产只有一个活跃管理员。脚本写入后到强制改密完成前,管理员访问依赖临时密码;任何异常都必须停止,不能重复 apply。
- apply 与 change-password 都会递增 token version、撤销会话和失效认证挑战;这是设计内副作用,不得用旧 hash 假装恢复这些状态。
- 企微身份绑定记录不由脚本修改,但旧会话失效可能改变现有访问体验;只读诊断用于交接,不授权绑定写入。
- 若 `--verify` BLOCKED、临时密码丢失或一次登录/改密失败,保留现场和有限恢复材料,在 24 小时上限内回来取得明确决策;禁止自行写回旧 hash。
- 合同终止回滚窗口可能被真实用户并发关闭;本任务只读记录,不通过停用功能或配置变更阻止使用。
## Open Questions
- 无。强制改密被归类为重置生命周期,不是合同验收;合同与员工负向验收归还原 readiness task。
## Test Plan
### 规划/本地
- `cd server && npm test -- tests/unit/scripts/reset-active-admin-password.test.js`
- `node --check server/src/scripts/reset-active-admin-password.js`
- `python3 ./.trellis/scripts/task.py validate 08-09-production-admin-break-glass-reset`
- `node scripts/qa/check-process-contract.mjs --baseline --json`
- `node scripts/qa/check-trellis-governance.mjs --json`
- `node scripts/qa/check-sensitive-content.mjs`
- `git diff --check`
### 获批生产窗口
- clean/published provenance、生产 read-only preflight、产品/脚本 SHA。
- `--preflight`、写前 `--diagnose-wecom-binding`、有限恢复材料存在性。
- 单次 `--apply ACTIVE_ADMIN_BREAK_GLASS`,随后 `--verify`。
- 仓库外 secret 更新;一次性 Playwright runner 通过 stdin 单次登录/强制改密,不把凭据放入 argv/env。
- `--verify-password-changed`、写后企微诊断、窗口复查、secret/hash/browser 清理。
## Evidence Rules
- 允许记录:commit/脚本 SHA 是否匹配、PASS/BLOCKED、目标计数、布尔、副作用计数、apply/login/change-password 次数和清理布尔。
- 禁止记录:管理员 identifier/email、临时/最终密码、旧/新 hash、cookie/token、生产路径、连接串、数据库/SSH 身份、原始 payload。
- 原始受控输出若包含敏感值不得进入 task;报告只保存稳定字段和相对 evidence 引用。
@@ -0,0 +1,39 @@
{
"id": "production-admin-break-glass-reset",
"name": "production-admin-break-glass-reset",
"title": "生产管理员 break-glass 密码重置",
"description": "仅对唯一活跃管理员执行一次既有 break-glass 密码重置脚本;前后使用脚本自带只读门禁,受控保存并销毁旧 hash 有限恢复材料,不包含登录态与合同验收。",
"status": "in_progress",
"dev_type": null,
"scope": "production-unique-admin-break-glass-reset-lifecycle",
"package": null,
"priority": "P0",
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-08-09",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [
"server/src/scripts/reset-active-admin-password.js",
"server/tests/unit/scripts/reset-active-admin-password.test.js",
"server/src/utils/crypto.js",
"server/src/utils/passwordPolicy.js",
"server/src/routes/auth.js",
"client/src/pages/LoginPage.tsx",
"client/src/pages/ChangePasswordPage.tsx",
"client/src/router/guards.tsx",
".trellis/tasks/08-08-production-authenticated-readiness",
".trellis/spec/hyb-oa/backend-fastify-prisma.md",
".trellis/spec/hyb-oa/deployment-trialrun.md",
".trellis/spec/hyb-oa/security-privacy.md"
],
"notes": "仅规划,尚未启动实施。任务只负责唯一活跃管理员 break-glass 重置生命周期:只读 preflight/企微诊断、单次脚本 apply、只读 verify、一次登录完成强制改密、verify-password-changed、受控 secret 更新与旧 hash 销毁;不进入合同或其他业务页面。完成后回到 08-08-production-authenticated-readiness 继续只读验收。",
"meta": {}
}