修复(08-10-session-fallback-failure-classification): 定性并修复会话夹具回落

这个提交包含在:
2026-08-10 12:50:06 +08:00
父节点 5518e974a2
当前提交 5cfd3d9bd3
共修改 11 个文件,包含 430 行新增和 0 行删除
@@ -0,0 +1,93 @@
# 会话回落失败定性
## Goal
在与 2026-08-07 历史记录一致的本地 Playwright runner 下,稳定复现并定性 8 条“完整导航或二次返回后落到登录页”的失败,给出可审计结论:测试 fixture/session mock 问题、真实会话行为问题、二者混合,或证据仍不足而保持 PAUSED。任务价值是消除唯一尚不明确且可能影响真实用户登录态的 Residual,不以让 8 条测试变绿作为终点。
## Confirmed Facts
- 父任务 Residual Register(`.trellis/tasks/archive/2026-08/08-05-product-interaction-consistency/prd.md:76-81`)记录:`r103-cross-page-ui-consistency.spec.ts` 7 条、`global-search.spec.ts` 1 条失败,统一症状为多次完整导航或二次返回后落到登录页;现有证据不能排除真实会话续期行为。
- 历史同 runner 命令为 `cd client && npx playwright test tests/e2e/procurement-flow.spec.ts tests/e2e/assets-flow.spec.ts tests/e2e/r103-cross-page-ui-consistency.spec.ts tests/e2e/pc-detail-mobile-visual-wave3.spec.ts tests/e2e/global-search.spec.ts --workers=1 --reporter=list`;其中目标两份 spec 当时分别为 `4 passed / 7 failed` 与 `3 passed / 1 failed`。
- 两份目标 spec 都拦截 `/api/auth/login`、`/api/auth/refresh` 与 `/api/me`,因此它们自身不能直接证明真实后端 refresh rotation 正常或异常。
- `AuthContext` 在完整页面重建时先检查可读的 `has_session=1`;无 session hint 时不会请求 refresh,而是按未认证状态完成 bootstrap。真实后端登录和 refresh 会同时写 refresh cookie 与 `has_session` cookie。
- 现有前端规约还记录另一条真实会话风险:本地 Vite proxy 若把浏览器 Host 从 `127.0.0.1:5173` 改写成后端 Host,refresh 的 Origin/Referer 校验会返回 `AUTH_005`,完整刷新后丢失会话。因此诊断必须同时取证 cookie/hint、refresh 请求与 runner origin/host,不能只看最终 URL。
- 仓库已有独立证据面:`login-portal-ux.spec.ts` 覆盖带 session hint 的 bootstrap refresh 重试;`auth.login.test.js` 覆盖后端 refresh rotation;它们可作为对照,但不能单独替代目标 runner 的复现。
## Scope
- 两份目标 Playwright spec 的原样复现、逐条失败账本和安全诊断 ledger。
- mock session fixture、真实前端 bootstrap、真实本地后端 session 三个证据面的对照实验。
- 基于明确判据输出 FIXTURE、PRODUCT、MIXED 或 INCONCLUSIVE/PAUSED 分类。
- 分类为 FIXTURE 后可选的最小 test-only 修复;任何认证产品改动、生产连接或部署均不在本任务范围。
## Requirements
### R1 同 runner 稳定复现
- 先用历史命令的同一目标 spec、`--workers=1 --reporter=list` 复现;不得先改 fixture、断言、认证实现、Vite proxy 或 runner 配置。
- 至少执行一次两份目标 spec 的原样组合;若结果不稳定,再对失败标题逐一、重复执行以区分顺序依赖、完整导航依赖和偶发 runner 状态。
- 记录测试名、阶段、最终 pathname、cookie 名称/是否存在、是否发出 login/refresh/me 请求、响应状态与安全错误码。不得记录 cookie/token 值、密码、完整账号或原始认证 payload。
### R2 证据隔离与分类实验
- 建立三个彼此独立的证据面:
1. **mock fixture 面**:目标 spec 的 login 响应后浏览器是否真的持有 `has_session`,完整导航时是否触发 mock refresh/me;
2. **真实前端 bootstrap 面**:显式存在 session hint 时 AuthContext 是否按设计请求 refresh 并保持路由;
3. **真实后端 session 面**:本地真实登录产生的 cookies、同源 refresh rotation 和完整导航是否成立,且 Vite proxy host/origin 符合规约。
- 对照实验只允许使用 synthetic fixture 或经 target gate 确认为回环/本地回归库的账号与会话;禁止连接生产或复用生产凭据。
- 为证明因果,可做最小的 test-side 控制变量(例如显式安装 session hint、记录安全 request ledger);在分类完成前不得修改产品认证代码。临时探针如不形成稳定回归价值,应在结论前移除。
### R3 分类判据
- **FIXTURE**:原样失败可复现;证据显示 mock 登录未建立 AuthContext 完整导航所需的浏览器 session contract,最小 fixture-only 控制变量消除同一失败;独立真实 bootstrap/backend 对照通过。
- **PRODUCT**:使用真实本地 session contract、正确同源 runner 与无 mock 的 refresh/me 路径仍可复现相同会话回落,或真实认证对照出现同一根因。立即停止本任务内修复,登记独立认证缺陷任务;后续实现必须只读 reviewer。
- **MIXED**:fixture 合同确有缺口,同时真实 session 对照也失败。分别登记测试债与认证缺陷,不在本任务合并修复。
- **INCONCLUSIVE / PAUSED**:无法稳定复现、关键证据面不可用或 target gate 不通过。不得把未复现解释为 PASS,也不得猜测根因。
### R4 修复边界
- 任务终点是分类结论和证据链,不是测试全绿。
- 若结论为 FIXTURE,可在分类检查点之后选择同任务内提交最小 test-only 修复,但它不是完成 AC 的必要条件;不得改变产品认证实现、弱化会话语义或扩大 mock 的默认放行。
- 若结论为 PRODUCT 或 MIXED,必须在任何认证代码修改前停止并请求创建独立任务与实施批准。
- **不得以删除断言作为默认修法。** 不得删除目标用例、移除完整导航、跳过 refresh/session 检查、增加 retry 掩盖失败,或把登录页当作可接受结果。
## Acceptance Criteria
- [x] AC1:在历史同 runner 参数下记录两份目标 spec 的原样结果,并能把 8 条历史失败逐条映射为当前 PASS/FAIL/未复现,不用套件总数代替逐条证据。
- [x] AC2:至少一个可复现失败拥有脱敏 request/cookie/navigation ledger,能回答 session hint 是否存在、refresh/me 是否发出、在哪一步进入 `/login`。
- [x] AC3:完成 mock fixture、真实前端 bootstrap、真实本地后端 session 三个证据面的对照;任何未执行项均有明确阻塞原因,不以另一层 PASS 顶替。
- [x] AC4:按 R3 输出且仅输出一个最终分类:FIXTURE、PRODUCT、MIXED 或 INCONCLUSIVE/PAUSED,并列出支持证据和反证。— 唯一分类 `FIXTURE`。
- [x] AC5:若为 PRODUCT/MIXED,认证产品代码保持未修改并停在独立任务批准前;若为 FIXTURE 且同任务修复,diff 仅限 test fixture/harness,先有分类检查点,且不得删除或弱化断言。
- [x] AC6:原样保留并落实“不得以删除断言作为默认修法”;目标测试数量、关键完整导航与会话语义无静默减少。
- [x] AC7:所有自动化、typecheck(若 test code 有改动)、task/process/governance/sensitive 与 `git diff --check` 按范围通过;结果明确为本地诊断,未连接生产、未部署。
## Out of Scope
- 未完成分类前修复 `AuthContext`、refresh rotation、cookie、Vite proxy 或后端认证路由。
- 生产登录、生产 session 查询、生产配置、部署、公网 smoke 或真实用户账号操作。
- 合同 UI 验收、员工负向验收、管理员手册附件承诺,以及其他 Playwright 失败。
- 为追求套件全绿而重写 8 条业务断言或扩大通用 mock 成功面。
## Risks and Deferred Items
- 当前源码证据更偏向 fixture/session hint 缺口,但这只是待验证假设,不是结论。
- 真实本地 session 对照会创建/轮换本地测试会话;执行前必须 fail-closed 确认回环主机和本地回归库,结束后按既有测试清理合同处理。
- 若最终需要认证产品修复,风险等级上升为生产登录态缺陷,必须新建独立任务、补真实数据库/浏览器回归并启动只读 reviewer。
- 管理员手册的合同附件承诺应在首条真实合同出现前单独修订,不并入本任务。
## Test Plan
- 原样复现:`cd client && npx playwright test tests/e2e/r103-cross-page-ui-consistency.spec.ts tests/e2e/global-search.spec.ts --workers=1 --reporter=list`
- 必要时按历史失败标题 `--grep` 单跑并 `--repeat-each` 验证稳定性;不以 retry 作为通过证据。
- 前端对照:聚焦 `login-portal-ux.spec.ts` 中 bootstrap refresh 用例及新增/临时安全 ledger。
- 后端对照:`cd server && npm test -- tests/unit/routes/auth.login.test.js`;若需真实本地 session,先运行本地 target gate,再执行最小浏览器/API witness。
- 若修改测试:`cd client && npm run typecheck`,再运行两份目标 spec 与相关 auth 对照。
- `python3 ./.trellis/scripts/task.py validate 08-10-session-fallback-failure-classification`
- `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`
## Open Questions
- 无。分类目标、四种停止状态、生产边界和修复分叉均已明确;技术未知由本任务实验回答。