docs(issue): register active-session residue issue and r3 task record

登记 ISSUE-20260927-active-session-residue(ISSUE-20260927-demo-fallback
修复残留路径);新增 r3 任务主记录(根因/同类排查/修复方案/验证证据);
CURRENT 登记 r3c 任务行。
This commit is contained in:
weichen
2026-09-27 07:05:09 +00:00
parent 739b8c56f9
commit 083c63bb9c
3 changed files with 110 additions and 0 deletions
+1
View File
@@ -30,3 +30,4 @@
| ISSUE-20260926-governance-multi-entry | docgov / 多意图 / 多入口 / Coordinator / OwnerMux / GovernanceEngine | docgov 三入口 + Coordinator ~40 case 与 intentflow OwnerMux 平行,鉴权/确认/审计易漂移 | high | deferred | pi | 2026-09-26;flag 漂移面已修(dual-flags-drift);入口合并是多日级重构,需独立任务授权与回归矩阵 | [主记录](../remediation/2026-09-26-system-code-review-fixes.md#6-find-qual-01-双套-flags-静默漂移issue-20260926-dual-flags-drift含残差) | 沿用 failclosed-bypass-contract |
| ISSUE-20260926-server-pool-wiring | server / swboot_handlers / Runtime.Pool / 组合根越界 / FIND-ARCH-02 | server 持 Pool 直装 swboot repo/executor 并直查表,成为第二组合根(本轮未授权修复) | critical | open | pi | 2026-09-26;代码审证据齐(审计 FIND-ARCH-02),待修复授权 | [主记录](../audit/2026-09-26-system-code-review.md) | 待修复后补 |
| ISSUE-20260927-demo-fallback | web / App.tsx / sess-001 / projectId fallback / demo seeds 关闭 | demo seeds 关闭模式下前端硬编码 fallback(激活 sess-001、项目 1)对真实后端发假请求,新登录用户首屏 9 个 404/403(chat 窗口被迫回退本地缓存) | medium | resolved | pi | 2026-09-27;dev 真实浏览器修复前后对照(author+member 双账号:9 假请求→0) | [主记录](../ops/short-term/2026-09-27-frontend-demo-fallback.md) | [经验小结](../ops/short-term/2026-09-27-frontend-demo-fallback.md#经验小结)(独立 lesson 待后续任务补登) |
| ISSUE-20260927-active-session-residue | web / storage.ts / loadActiveSessionId / localStorage 残留 / sess-001 / demo-seed-purge 写回窗口 | 旧前端把 demo fallback 值写回 localStorage active_session;一次性 purge 已执行不再清,读取侧无 legacy 校验,老浏览器 + 无可见会话账号首屏 4×chat 404(ISSUE-20260927-demo-fallback 修复残留路径) | medium | in_progress | pi | 2026-09-27;dev PG 只读 + 代码链路已证实;修复验证见主记录 | [主记录](../ops/short-term/2026-09-27-frontend-demo-fallback-localstorage-residue.md) | [经验小结](../ops/short-term/2026-09-27-frontend-demo-fallback-localstorage-residue.md#经验小结补充-issue-20260927-经验) |
+1
View File
@@ -18,6 +18,7 @@
| owner | task | branch | worktree | mutex | status |
|---|---|---|---|---|---|
| pi | frontend-demo-fallback-2026-09-27-r3c | fix/frontend-demo-fallback-2026-09-27-r3 | .worktrees/frontend-demo-fallback-2026-09-27-r3 | web-chat-fallback | in_progress |
| pi | frontend-demo-fallback-2026-09-27-r2 | fix/frontend-demo-fallback-2026-09-27 | (已清理) | web-chat-fallback | done |
| Cursor | full-system-browser-e2e-2026-09-26 | feature/full-system-browser-e2e-2026-09-26 | .worktrees/full-system-browser-e2e-2026-09-26 | env:dev+docs-test-report | done |
| pi | sysadmin-maturity-refresh-2026-09-26 | feature/sysadmin-maturity-refresh-2026-09-26 | .worktrees/sysadmin-maturity-refresh-2026-09-26 | sysadmin-maturity | done |
@@ -0,0 +1,108 @@
---
title: "localStorage 残留 legacy demo sessionId 导致首屏 chat 404(ISSUE-20260927 修复残留路径)"
task_id: "frontend-demo-fallback-2026-09-27-r3c"
status: in_progress
created: 2026-09-27
updated: 2026-09-27
owner: pi
task_type: implementation
task_tier: small
policy_revision: "agent-governance-2026-09-21-r1 @ dev 87db3621c62f8d9473b7191cce3a782cfbaf3d90"
branch: fix/frontend-demo-fallback-2026-09-27-r3
worktree: .worktrees/frontend-demo-fallback-2026-09-27-r3
base_dev_sha: "87db3621c62f8d9473b7191cce3a782cfbaf3d90"
write_scope: ["web/src", ".doc"]
allowed_environments: ["dev-readonly", "dev-browser-verify"]
head_sha: ""
mutex: "web-chat-fallback"
review_state: not_required # 小任务;沿用 ISSUE-20260927 修复线,验证契约前端范围全过
integration_state: pending
deployment: pending
cleanup: pending
cleanup_owner: "独立集成/维护执行者"
cleanup_trigger: "owner 退出且 dev 合入+验收证据齐备"
remote_sync: not_requested
---
# localStorage 残留 legacy demo sessionId 导致首屏 chat 404
## 用户目标与授权
- 用户目标/授权来源:用户报告 dev(frp-mix.com:58880)控制台仍出现 4 个 `session_id=sess-001` 的 chat API 404(无 `projects/1` 假请求);本轮会话明确授权"按上轮任务同路径修复(worktree 修改 → 测试 → 合入 dev → dev 真实浏览器复验 → 清理)"。
- 交付终点:修复合入 dev、dev 实际运行验证通过、worktree 清理。
- 禁止项:不 push 远端;不动 test/main;不改后端。
- 例外:无。本任务是 [ISSUE-20260927-demo-fallback](./2026-09-27-frontend-demo-fallback.md) 修复的残留缺口修正,沿用同一 issue 与 mutex。
## 问题事实与根因(2026-09-27 排查)
- 现象:登录后首屏 `GET /api/v1/chat/messages/window|unread|categories|dates?session_id=sess-001` 4×404;**无** `projects/1` 假请求。
- 运行态核实:dev vite :3100(主目录,base 87db3621,修复后代码)按需编译;`projects/1` 假请求消失证明上轮 fallback 常量修复已生效,本例 `sess-001` 来源不是 fallback 常量。
- 代码核实:
- `web/src/data/storage.ts` `loadActiveSessionId()`:localStorage `smdf_agent_workbench_v6_active_session` **已存值优先于 fallback**(:110);
- 旧前端(上轮修复前)由 fallback 激活 `sess-001` 后,`useWorkbenchPersistence`(:44 `saveActiveSessionId`)把它**写回** localStorage;
- 一次性清理 `purgeDemoSeedsFromLocalStorage`(`demo-seed-purge-v1` 版本 key 幂等跳过)在旧代码写回**之前**已执行过,不再清;
- `loadMessages` 对 messages 有 `legacyIds` 防御(:121),`loadActiveSessionId` 对 active_session **没有**——同一机制缺口。
- 数据核实(dev PG :5433 只读):`chat_sessions` 80 行无 `sess-001`;user_id=4(admin,报障账号)拥有 0 个会话 → 前端 `visibleSessions` 为空 → 会话纠正 effect(App.tsx:268)不触发 → `sess-001` 持续激活。
- 根因:上轮修复只切断"生成"路径(fallback 常量),未覆盖"旧代码已把 demo 值持久化到用户浏览器"的读取路径;读取侧无 legacy 校验 + 一次性 purge 无法覆盖其后的写回窗口。
- 为何上轮验证未拦:真实浏览器复验用"空 storage"登录(phantom=0),未覆盖"localStorage 已残留 sess-001 + 当前账号无可见会话"组合。
- 功能影响:老浏览器(修复前访问过 dev 且当时激活 demo 会话 id)+ 无可见会话账号首屏 4×404 噪音、chat 回退本地缓存;无数据损坏。
## 同类排查
- grep 生产代码:`sess-001` 常量已无(仅 tests + storage.ts legacy 清理集合)。
- 同 key 家族:`ACTIVE_SESSION` 是唯一"旧代码写回 demo 值 + 新代码读出不校验"路径;messages 已有 legacyIds 防御;sessions/deliverables/users/projects 走一次性 purge 且写回源为当前内存值(新代码不产生 demo 值)。
- `activeDocId` 的 `doc-001`/5201/3 本地选择残留维持上轮结论(无 API 请求,另行清理,不属本 bug)。
- ISSUES.md 开工检索命中 ISSUE-20260927-demo-fallback(本条即其残留路径);lessons 无新命中。
## 修复方案(A:读取侧幂等校验,共用机制)
1. `storage.ts` 提取共享常量 `LEGACY_DEMO_SESSION_IDS = {'sess-cusa-001','sess-001','sess-002'}`(与 `loadMessages` 同一集合,单一来源)。
2. `loadActiveSessionId` 读出 localStorage 值后命中该集合 → `removeItem` 该 key 并返回 fallback。每次启动幂等生效,覆盖任何历史/未来写回窗口,不依赖一次性 purge 版本升级。
3. `loadMessages` 复用共享常量,行为不变。
## 步骤与验收
1. [x] 根因与同类排查 — 见上文。
2. [x] 实施修复 — `web/src/data/storage.ts`(常量提取 + 读取侧清理);新增 `web/src/data/storageActiveSession.test.ts`(3 用例)。
3. [x] 单测 — node:test 515 pass/0 fail(含新增 3);vitest 34 pass/0 fail。
4. [x] 构建 — demo off/on 双模式 `npm run build` 均成功。
5. [x] 真实浏览器验证(worktree vite :3101 + dev 后端 :8080,Playwright chromium headless):
- A 原例:admin(user 4)+ 预置 `active_session='sess-001'` + purge 版本 key 已写 → 37 API 全 2xx、phantom=0、4xx5xx=0、残留 key 被清除;
- B 变体:`sess-cusa-001` 同样清除、phantom=0;
- C 回归:author(有真实会话)+ 预置 `sess-002` → 56 API 全 2xx、正常激活真实会话 `sess-p6020-u60`(真实 id 不误清)。
6. [ ] 合入 dev(candidate 流程)。
7. [ ] dev 主入口(:3100)实际运行复验。
8. [ ] 清理 worktree、收口(ISSUES.md 更新、任务终态)。
## 验证与证据
验证日期 2026-09-27;被测代码:worktree `fix/frontend-demo-fallback-2026-09-27-r3`(基准 dev `87db3621` + 本修复);环境:dev 后端 `127.0.0.1:8080`(ENV=dev 裸进程)+ worktree 独立 vite `:3101`(proxy /api → 8080)。账号:admin(本机 `.env`)与 author `e2e26_author_mui2xpg2`(来源 `2026-09-26-full-system-browser-e2e/audit-login-credentials.md`,凭证不入库)。
| 范围 | 命令与环境 | 预期 | 实际 / 判定 | 证据 |
|---|---|---|---|---|
| 单测(新增) | `npx tsx --test src/data/storageActiveSession.test.ts` | 3 pass | **PASS**:legacy 清除+返回 fallback、真实 id 不误清、SSR 安全 | 控制台输出(本节) |
| 单测(全量) | `npm test`(worktree) | 全绿 | **PASS**:node:test 515 pass/0 fail;vitest 34 pass/0 fail | 控制台输出 |
| 构建 | `npm run build`(off/on 双模式) | 成功 | **PASS**:66 组产物预压缩 ×2 | 控制台输出 |
| 原例(修复后) | 场景 A | 0 假请求 | **PASS**:37 API 全 2xx,phantom(sess-001)=0,key 被清除 | SUMMARY 输出(下附) |
| 同类变体 | 场景 B(sess-cusa-001) | 0 假请求 | **PASS**:37 API 全 2xx,phantom=0 | 同上 |
| 正常行为回归 | 场景 C(author,真实会话) | 不受损 | **PASS**:56 API 全 2xx,激活真实会话 | 同上 |
```text
SUMMARY: [{"phantom":0,"bad":0,"total":37,"activeAfter":""}, // A: admin + sess-001
{"phantom":0,"bad":0,"total":37,"activeAfter":""}, // B: admin + sess-cusa-001
{"phantom":0,"bad":0,"total":56,"activeAfter":"sess-p6020-u60"}] // C: author 回归
```
注:临时验证脚本未入库(/tmp 已删);worktree 独立 vite :3101 已停。
## 经验小结(补充 ISSUE-20260927 经验)
- 触发条件:持久化读取路径存在"旧版本可写入、新版本不校验"的值域(本例 active_session)。
- 机制:一次性 purge(版本 key 幂等)无法覆盖"purge 之后旧代码写回"的窗口;localStorage 已存值优先于 fallback,修掉 fallback 常量不影响已持久化的值。
- 应做检查:修复"假 ID 产生请求"类问题时,区分生成路径与持久化路径;对读取侧加值域校验(与 messages 的 legacyIds 同源),验证必须覆盖"老浏览器残留值"状态而非仅空 storage。
- 验证方法:预置残留 localStorage(含 purge 版本 key)+ 无可见会话账号登录,统计 Network 假请求。
## 收口与交接
- 剩余工作:合入 dev、dev 主入口复验、清理。
- 状态分别记录:见 frontmatter。