Files
Agent-System/.doc/ops/short-term/2026-09-27-frontend-demo-fallback-localstorage-residue.md
T
weichen 9da300ae60 docs(task): finalize active-session residue closeout state
任务文档终态(done/merged/passed/cleanup done)、ISSUES 行 resolved
(dev 36c5fb54 复验证据)、CURRENT 置 done。
2026-09-27 07:08:05 +00:00

9.2 KiB
Raw Blame History

title, task_id, status, created, updated, owner, task_type, task_tier, policy_revision, branch, worktree, base_dev_sha, write_scope, allowed_environments, head_sha, mutex, review_state, integration_state, deployment, cleanup, cleanup_owner, cleanup_trigger, remote_sync
title task_id status created updated owner task_type task_tier policy_revision branch worktree base_dev_sha write_scope allowed_environments head_sha mutex review_state integration_state deployment cleanup cleanup_owner cleanup_trigger remote_sync
localStorage 残留 legacy demo sessionId 导致首屏 chat 404(ISSUE-20260927 修复残留路径) frontend-demo-fallback-2026-09-27-r3c done 2026-09-27 2026-09-27 pi implementation small agent-governance-2026-09-21-r1 @ dev 87db3621c6 fix/frontend-demo-fallback-2026-09-27-r3 .worktrees/frontend-demo-fallback-2026-09-27-r3 87db3621c6
web/src
.doc
dev-readonly
dev-browser-verify
083c63bb web-chat-fallback not_required merged passed done 独立集成/维护执行者 owner 退出且 dev 合入+验收证据齐备 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 修复的残留缺口修正,沿用同一 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. 根因与同类排查 — 见上文。
  2. 实施修复 — web/src/data/storage.ts(常量提取 + 读取侧清理);新增 web/src/data/storageActiveSession.test.ts(3 用例)。
  3. 单测 — node:test 515 pass/0 fail(含新增 3);vitest 34 pass/0 fail。
  4. 构建 — demo off/on 双模式 npm run build 均成功。
  5. 真实浏览器验证(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 — integrate prepare/validate/integrate 规范流程;dev 87db3621→36c5fb54(--no-ff,first parent=D,validate 收据 ea2681ee)。
  7. dev 主入口(:3100,frp 用户同一入口)实际运行复验 — admin + 预置残留 sess-001 + purge 版本 key 已写:37 API 全 2xx、phantom=0、4xx5xx=0、残留 key 被清除。
  8. 清理 worktree、收口 — ISSUES.md 更新 resolved、任务终态、CURRENT 置 done。

验证与证据

验证日期 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,激活真实会话 同上
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 假请求。

收口与交接

  • 剩余工作:无(授权范围验收全部满足)。后续可选项维持上轮结论:activeDocId 的 doc-001/5201/3 本地选择残留(无 API 请求,非本 bug);独立 lesson 文件补登。
  • 状态分别记录:
    • 本地提交:739b8c56(fix)+ 083c63bb(docs,任务分支 fix/frontend-demo-fallback-2026-09-27-r3)。
    • dev 合入:36c5fb54(candidate --no-ff)。
    • 实际运行版本:dev vite :3100 热载 36c5fb54 工作区代码,复验 PASS。
    • 远端同步:not_requested(用户未要求 push)。
    • 清理:任务与候选 worktree 已按规范 remove(本节下方登记)。