文件
hyb-oa/.trellis/tasks/08-08-production-authenticated-readiness/implement.md
T

14 KiB
原始文件 Blame 文件历史

生产登录态只读验收:执行计划与证据

当前状态:IN PROGRESS / EMPLOYEE LOGIN READY。生产只读基线与扩展账号诊断已完成;管理员认证已由独立任务 08-09-production-admin-break-glass-reset 恢复,员工认证已由独立任务 08-10-production-employee-smoke-break-glass-reset 恢复并写入仓库外 secret。合同 UI 仍因生产零合同等待事件触发补验。

1. 规划确认

  • 前一部署任务证据确认:产品候选 0224ad8,65 条 migration applied,部署结束时 rollbackWindow=OPEN、terminatedRows=0,管理员/员工凭据均 401。
  • auth 路由确认:identifier 仅接受 email/phone;401、403、423 分别覆盖不存在/密码错误、停用、锁定;失败会更新计数和锁定状态。
  • 账号 dry-run helper 确认:可借鉴 BEGIN READ ONLY 与脱敏输出,但 employee_no 匹配宽于真实登录合同,--apply 明确排除。
  • 终止入口确认:仅 active/expired 显示;draft/archived 无法作为 UI 误点的服务端状态机兜底。
  • 安全验收选择:不打开确认层,只检查终止按钮初始禁用,并记录 terminate POST=0。
  • 回滚窗口选择:验收前后双快照,查询失败不得默认 OPEN。

2. 启动门禁(规划批准后)

  • 用户完成规划只读复核并明确批准实施。
  • 运行 task.py start,当前 task 状态为 in_progress。
  • 确认生产只读授权仍有效;本任务没有账号/权限/合同写入授权。
  • 确认已部署产品 SHA 基线为 0224ad8,不以当前 HEAD 替代。
  • command -v npx 可用;若凭据恢复,浏览器只能使用单 worker、零 retry。
  • 生产 preflight 使用两个独立时间戳报告引用;未复用历史 PASS,任务文档只记脱敏摘要。

3. 执行顺序

Phase A:生产只读基线

  • 复用受控身份引导和 read-only preflight,精确身份组与 13/13 聚合体检通过。
  • migration reconciliation 为 65 条 applied、pending=none,未见 failed/diverged 异常;只读 wrapper 未进入 deploy.sh。
  • 公网 build 短标识为 0224ad83d7f8,与已部署产品候选匹配;未把当前文档 HEAD 当作产品 SHA。
  • PRE 为 terminatedRows=0、rollbackWindow=OPEN、basis=terminated_status_count。

Phase B:登录前账号诊断

  • 使用同一 BEGIN READ ONLY 事务查询 admin/employee;查询合同覆盖 role key、matchedBy、active/archived/locked、failureCount、expectedRoleReady。
  • 两个角色均返回 matchedBy=none;由于没有匹配到账号,active/archived/locked、failureCount、expectedRoleReady 均记为 N/A,不把占位值当作生产账号事实;未输出 identifier、账号、锁定时间或角色明细。
  • 查询只按 email/phone 精确匹配,未把 employee_no 当作可登录身份。
  • 按 PRD R3 分流为仓库外 identifier 过期或不符合现行登录合同;标 PAUSED,没有进入密码、解锁、激活或授权写入。
  • 登录尝试数:admin=0、employee=0。

经用户确认继续只读定性后,补充完成:

  • 旧 identifier 对生产 employee_no 的精确匹配也均为 false,排除“只是把员工编号误当登录名”。
  • 在远端 BEGIN READ ONLY 事务内按角色筛选活跃候选,并用现有仓库外密码与候选 hash 离线比较;密码与 hash 均未离开远端内存。
  • admin:活跃候选 1、正确 identifier 可唯一解析为 email、locked=false、failureCount=0、passwordMatchCount=0。
  • employee:非管理员 employee 候选 5、passwordMatchCount=0,无法在不猜人的情况下选定验收账号。
  • 未把正确管理员 identifier 写入 task、对话或仓库,也未修改仓库外 secret;密码重置/员工账号选择转为独立批准事项。

员工负向目标资格补充诊断:

  • 重新通过生产 origin 与 systemd contract 身份门禁后,在运行容器内用 BEGIN READ ONLY 查询 5 个活跃非管理员候选;事务只读状态为 on,未调用登录端点。
  • 5 个候选的登录 identifier 均已就绪、当前锁定数为 0、失败计数均为 0;其中 1 个具备合同管理权限,3 个仍要求首次改密。
  • 以“可进入只读合同工作区、无合同管理权限、无需首次改密、未锁定”为资格合同,恰好 1 个候选通过,员工负向目标已从 5 个收敛为唯一 1 个;未输出 identifier、账号、角色明细或数据库 ID。
  • 使用运行容器内 repair-oa-only-smoke-accounts.js 的既有 smoke 安全判据对唯一目标分类:账号自身命名模式命中、私有 smoke allowlist 未命中,结论为专用 OA-only smoke 账号,不是真人员工;未输出命中的原始字段。
  • 本次未注入或比较任何密码,不把此前 passwordMatchCount=0 伪装为本轮现场结果;当前事实是目标已唯一,但仍缺少已确认有效的仓库外凭据,employee attempts 继续为 0。
  • 资格诊断 PRE/POST 与 smoke 分类后的最终 POST 均为 terminatedRows=0、rollbackWindow=OPEN;未产生账号、权限、合同、session、审计或失败计数写入。

自助密码重置可用性只读核对:

  • 运行容器 EMAIL_ENABLED=false;公网 /api/auth/capabilities 独立返回 emailDeliveryReady=false、原因 email_disabled。
  • 唯一活跃管理员有 email,passwordResetLocked=false、passwordResetRequestCount=0;未调用 /forgot-password,未生成验证码、增加请求计数或触发邮件。
  • ~/.hyb-oa/smtp-live.env 的本地存在不等同于生产邮件通道启用;启用生产邮件需要配置变更与重启,超出当前只读授权。
  • 选择更窄的后续路径:如获单独批准,优先使用仓库既有 reset-active-admin-password.js 的 exact-admin、bcrypt、事务与审计合同,不手写 hash SQL,不修改角色、员工启用状态或 email。
  • 已识别脚本完整副作用:设置 must_change_password、清理认证失败/锁定状态、递增 token version、撤销活跃会话、失效密码重置/邮件登录验证码并写不可删除的审计。批准请求必须逐项披露。
  • 如另行批准,旧 hash 仅能在受控生产现场以最小权限短期保存,作为有限凭据恢复材料;不进入仓库、task、普通日志或对话,不构成上述副作用的完整回滚,并须在验收/恢复窗口关闭后立即销毁。
  • 邮件能力与 break-glass 建议只读 reviewer 首轮 PASS_WITH_FINDINGS 的两项证据边界已修订,最终复查为 PASS;reviewer 未修改文件或生产状态。

执行备注:账号诊断前两次远端包装探针分别在脚本解码和 Compose 必填候选标识处未产生约定查询 marker,均不计为查询成功;修正为注入已验证产品候选完整/12 位标识后,唯一有效诊断事务返回 transaction=read_only。这些失败均先于数据库事务和登录。

Phase C:有限登录(仅凭据合法恢复后)

  • 管理员凭据来源已切换:来自已归档 08-09-production-admin-break-glass-reset 的受控 break-glass 恢复和首次强制改密,不是本任务最初诊断为失效的原始 secret;值不进入任务证据。
  • 员工凭据来源已切换:来自 08-10-production-employee-smoke-break-glass-reset 的受控 break-glass 恢复和首次强制改密,不是本任务最初诊断为失效的原始 secret;最终 secret 已就绪且值不进入任务证据。
  • 08-10 最终交接证明目标仍唯一、rolesExact=true,*、project.manage、project.view_all、system.settings 四项权限全 false;该结论只解除凭据阻塞,不替代本任务的生产登录态负向验收。
  • admin 只执行一次登录、零 retry,HTTP 200 并进入 /portal;写后只读复核为 failureCount=0、locked=false、近 15 分钟 login success=1、login failure=0。
  • READY:employee 最终凭据已由 08-10 合法恢复,负向前提仍成立;本任务下一步按单 worker、零 retry执行一次登录和合同权限负向,不扩大权限、不切换管理员身份。
  • admin attempts=1;未发生第二次登录。浏览器在合同台账阶段 fail-closed 后关闭,未到达正常 logout;active sessions 从恢复后的 1 变为 2,新增会话留待自然过期,本任务未获直接数据库撤销授权。
  • READY:employee 尚未在本任务登录,attempts=0;目标唯一性与凭据阻塞均已解除,等待执行本任务既有负向验收步骤。
  • 已记录 admin attempts=1、employee attempts=0;没有第二次尝试,管理员失败计数仍为 0 且未锁定。

Phase D:登录态只读验收

  • PARTIAL:admin 登录成功;浏览器进入合同台账阶段后因无可用合同样本停止,未进入详情。生产数据库只读计数确认合同总数为 0,active/expired/draft/archived/terminated 均为 0。
  • PAUSED:无 active/expired 样本,无法进入终止面板;未填写原因、未打开确认层、terminate POST=0、全部合同 mutation request=0。
  • PAUSED:员工会话未建立,未执行权限负向路径;未扩大权限或删除断言。
  • PAUSED:archived 样本数为 0,无法验证字段锁定;未创建数据。
  • PAUSED:terminated 样本数为 0,无法验证“已终止”标签与终止事实区;未制造样本。

Phase E:窗口复核与收口

  • 首次账号分流、扩展账号诊断、邮件能力诊断后均查询 POST;连同初始 PRE 共四次,均为 terminatedRows=0、rollbackWindow=OPEN、basis=terminated_status_count。
  • 四次窗口证据均为 OPEN/0,未发现同期真实用户关闭窗口。
  • 凭据恢复后的管理员验收 PRE/POST 再次均为 terminatedRows=0、rollbackWindow=OPEN、basis=terminated_status_count;本任务累计六次快照均 OPEN/0。
  • 本轮浏览器 admin login=1/200;terminate POST=0、合同 mutation=0。runner 的状态词基线曾写成“已到期”而产品映射为“已过期”,但生产合同总数为 0,状态循环未执行,该观察不是本轮停止原因;依零 retry 合同未重跑。
  • 汇总:admin attempts=1、employee attempts=0;terminate POST=0、合同 mutation=0;未执行账号修复、权限、合同或 schema 写入。成功登录按产品合同创建 1 个新 session 并写登录审计,浏览器未到达 logout。
  • 扩展诊断只读 reviewer 首轮 PASS_WITH_FINDINGS 要求收窄员工密码与 UI PAUSED 措辞,修订后最终复查为 PASS;浏览器验收收口复核另发现旧汇总把成功登录副作用误写成“写入 0”,修正为 admin attempts=1、session 1→2 与登录审计后,最终复核为 PASS。reviewer 未修改文件、代码或生产状态。
  • 已按实际证据更新 PRD AC;管理员认证记为通过,员工与无样本 UI 分支保持 PARTIAL/PAUSED,未把缺失样本勾成通过。
  • 已按 trellis-update-spec 将零 retry runner 的细粒度阶段、状态词源码基线、无样本独立分类和登录副作用对账写入现有 .trellis/spec/hyb-oa/deployment-trialrun.md,未新建规约文件。
  • 已为合同 UI PAUSED 写死事件触发条件:生产出现首条 contract_registry 记录后启动只读补验,不按日期等待、不制造样本,也不把记录出现视为终止授权。仓库事实源显示合同台账原已纳入首批真实使用且历史存在测试数据,因此当前零合同只记为待负责人确认的运营事实,不擅自归因为“模块尚未启用”。

4. 预期证据表

证据 允许记录 禁止记录
版本/迁移 候选 SHA、applied/pending/异常计数 数据库名、连接串、SSH identity
账号诊断 role key、matchedBy、布尔、失败计数 identifier、邮箱、手机号、员工编号、hash、锁定时间原值
登录 角色、attempts、HTTP 状态 密码、cookie、token、request payload
合同 UI 中文状态、按钮 disabled、POST 计数 合同正文、相对方、金额、终止原因
窗口 PRE/POST count、OPEN/CLOSED、顺序 原始数据库输出或业务行

5. 规划门禁结果

  • python3 ./.trellis/scripts/task.py validate 08-08-production-authenticated-readiness:PASS。
  • node scripts/qa/check-process-contract.mjs --baseline --json:PASS,0 errors / 0 warnings。
  • node scripts/qa/check-trellis-governance.mjs --json:PASS,0 errors / 0 warnings。
  • node scripts/qa/check-sensitive-content.mjs:PASS,0 findings。
  • git diff --check:PASS。

执行后复跑同一组门禁仍 PASS;docs registry audit 为 0 errors / 1 个既有“22 个未注册 docs 文件”warning,本任务未新增或修改 docs/ 文件。

6. 当前结论

生产只读基线与账号诊断已执行,独立任务 08-09-production-admin-break-glass-reset 已恢复管理员认证。管理员本轮唯一一次登录返回 200,失败计数仍为 0 且未锁定;但生产合同总数为 0,无法验证详情、终止入口、归档锁定或已终止标签,未制造样本。仓库事实源不能证明零合同属于预期未启用,后续由负责人确认其运营含义;一旦生产出现首条记录,即触发只读补验。员工负向目标已通过资格诊断从 5 个候选收敛为唯一 1 个,并确认是专用 OA-only smoke 账号而非真人员工,但缺少已确认有效凭据,负向权限仍未验证。smoke 分类后的最终 POST 仍为 OPEN/0,本任务累计九次窗口快照全部 OPEN/0,terminate POST 和合同 mutation 均为 0。当前结论为管理员认证通过、合同 UI 因无数据 PAUSED、员工负向因凭据缺失 PAUSED;仍不得告知用户可以测试合同终止功能。