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 |
in_progress |
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 |
|
| dev-readonly |
| dev-browser-verify |
|
|
web-chat-fallback |
not_required |
pending |
pending |
pending |
独立集成/维护执行者 |
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:读取侧幂等校验,共用机制)
storage.ts 提取共享常量 LEGACY_DEMO_SESSION_IDS = {'sess-cusa-001','sess-001','sess-002'}(与 loadMessages 同一集合,单一来源)。
loadActiveSessionId 读出 localStorage 值后命中该集合 → removeItem 该 key 并返回 fallback。每次启动幂等生效,覆盖任何历史/未来写回窗口,不依赖一次性 purge 版本升级。
loadMessages 复用共享常量,行为不变。
步骤与验收
- 根因与同类排查 — 见上文。
- 实施修复 —
web/src/data/storage.ts(常量提取 + 读取侧清理);新增 web/src/data/storageActiveSession.test.ts(3 用例)。
- 单测 — node:test 515 pass/0 fail(含新增 3);vitest 34 pass/0 fail。
- 构建 — demo off/on 双模式
npm run build 均成功。
- 真实浏览器验证(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 不误清)。
- 合入 dev(candidate 流程)。
- dev 主入口(:3100)实际运行复验。
- 清理 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,激活真实会话 |
同上 |
注:临时验证脚本未入库(/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。