diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 8918cc19..cad18850 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -19543,5 +19543,22 @@ - 相关记录:BUG-1439、BUG-1043、BUG-1044、BUG-1045、BUG-1046。issue #96。 - 修复版本:分支 `codex/gate-birth-journey-contract-20261025`。 - 断言三栏:原值是注销确认的字在挂载点里,人物按钮的可见文字里要有名字;新值是注销确认到页面主体里找,打开人物只认「某某 打开档案」;原因是确认框画在页面主体上,人物名字在卡片里,打开档案的按钮没有可见文字。 +- 后续:下一道门禁(run 5414)里,上面 29 条已经通过。还剩 1 条侧栏流式检查,另记 BUG-1441。测试网站仍未更新。 + +## BUG-1441 | 侧栏流式检查把第一次补上的名单算进了流式帧 + +- 状态:resolved(这条检查已改;测试网站要等这次门禁通过才更新) +- 首次发现:2026-10-25 +- 最近更新:2026-10-25 +- 影响面:测试环境发布。页面行为没有新的改动。 +- 用户现象:BUG-1440 合进测试分支之后,测试网站仍停在完整数据版(二)。 +- 触发条件:run 5414 的快速门。前端 5188 条里 5187 条通过,1 条失败:`streaming frames do not republish the sidebar (30 sessions)`,原文是 `1 !== 0`。100 条会话那条没有出现在失败名单里。镜像没有发布。 +- 根因:侧栏先画出普通名单,拖拽名单和账户菜单是打开之后再补上的。30 条会话画得快,这一次补画落进了后面用来模拟流式回答的 60 帧里,被数成 1 次侧栏提交。流式帧本身没有让侧栏重登。门禁日志没有打出是哪一个计数,按「只有 30 条失败、100 条没失败」和本地两次都是帧内注册 0、提交 0 来判断。 +- 修复:检查先载入这两块,并等到侧栏安静,再开始数 60 帧。60 帧内注册和提交仍必须是 0。页面代码没有改。 +- 验证:`frontend/tests/home-sidebar-rerender.test.tsx` 的 30 条和 100 条都通过,帧内注册 0、提交 0。没有登录浏览器,不把页面走查写成通过。 +- 防复发:流式帧的计数仍要在首次补画结束之后。失败信息写明是注册、提交还是修订,期望值仍是 0。 +- 相关记录:BUG-1440、BUG-1329。issue #96。 +- 修复版本:分支 `codex/gate-birth-journey-contract-20261025`。 +- 断言三栏:原值是渲染后等 10 毫秒就开始数帧,失败只印 `1 !== 0`;新值是先载入拖拽名单和账户菜单,侧栏安静后再数,失败时写明哪一个计数;原因是第一次补画不是流式帧。期望值没有放宽,60 帧内注册和提交仍是 0。 diff --git a/docs/tasks/PROGRESS-birth-journey-contract-20261025.md b/docs/tasks/PROGRESS-birth-journey-contract-20261025.md index cf97d1ac..0325db43 100644 --- a/docs/tasks/PROGRESS-birth-journey-contract-20261025.md +++ b/docs/tasks/PROGRESS-birth-journey-contract-20261025.md @@ -4,7 +4,7 @@ ## 结论 -建档里的旧校正卡已经不在。校正从对话进入。Python 那条已经跟上。下一道门禁卡在测试画布画不出新零件,画布已补上。测试网站要等这道门禁通过才更新。 +建档里的旧校正卡已经不在。校正从对话进入。Python 那条已经跟上。画布也补上了。上一道门禁还剩一条:侧栏流式检查把第一次补上的名单算进了流式帧。检查改为等这次补画结束再数,60 帧里侧栏仍不得重绘。测试网站要等这道门禁通过才更新。 ## 断言改动(原值 / 新值 / 原因) @@ -33,6 +33,7 @@ - #104 的检查通过,发布镜像时下载前端依赖超时,没有部署。 - #96 到 #111 的快速门都停在这条 Python 检查。#111 是 1 条失败,其余通过。 - BUG-1439 合入后的门禁是 run 5404。Python 已过。前端 5188 条里 29 条失败,镜像没有发布。失败都在测试画布:写不了子节点、读不了样式、注销确认不在挂载点里、人物卡点到了「更多操作」。画布补上之后,这 6 份检查 50 条通过。页面没有改。记 BUG-1440。 +- BUG-1440 合入后的门禁是 run 5414。前端 5188 条里 5187 条通过,1 条失败:30 条会话的侧栏流式检查印出 `1 !== 0`。100 条那条不在失败名单里。镜像没有发布。记 BUG-1441。页面没有改。健康检查这一轮没有再问。 ## 断言改动(BUG-1440,原值 / 新值 / 原因) @@ -40,3 +41,9 @@ | --- | --- | --- | --- | | `account-deletion.test.tsx` 待注销账号 | 注销确认的字和两个按钮在挂载点里 | 到页面主体里找 | 确认框画在页面主体上,挂载点是空的 | | `new-chat-from-people-lifecycle.test.tsx` 打开人物 | 按钮可见文字里要有名字 | 只认「某某 打开档案」 | 名字在卡片里。同名的「更多操作」不能当成点开这个人 | + +## 断言改动(BUG-1441,原值 / 新值 / 原因) + +| 测试 | 原值 | 新值 | 原因 | +| --- | --- | --- | --- | +| `home-sidebar-rerender.test.tsx` 流式帧 | 渲染后等 10 毫秒就开始数;失败只印 `1 !== 0` | 先载入拖拽名单和账户菜单,侧栏安静后再数;失败时写明是注册、提交还是修订 | 第一次补画不是流式帧。60 帧内注册和提交仍必须是 0,期望值没有放宽 | diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 4cd543f3..f4925055 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -40,7 +40,7 @@ | `TASK-rectification-explicit-open-20261023.md` | `PROGRESS-rectification-explicit-open-20261023.md` | **明说要校正只回一句话(issue #103)**:找会话直接读一张对服务角色撤权的表、必然失败被吞(真正进行中的也续不上);`candidate_accepted` 被算作进行中挡住入口和提议卡;兜底只给一句话。产品选 C;只改入口口径、经 RPC 找会话、删兜底,不改共享状态集合、不动表。BUG-1423 | 已合入 staging | 分支 `codex/rectification-explicit-open-20261023` | | `TASK-chart-table-span-20261023.md` | `PROGRESS-chart-table-span-20261023.md` | **星盘页本命 / 月亮行星表挤进左栏(issue #102)**:#87 给表包了 `ChartSymbolHelp` 后跨栏规则失配;根因修法是 `VedicLayout` 用固定整行容器承载表格,跨栏只认容器;加结构断言防复发。BUG-1422 | 已合入 staging | 分支 `codex/chart-table-span-20261023` | | `TASK-ui-kit-migration-20261023.md` | `PROGRESS-ui-kit-0-base-20261023.md`;`PROGRESS-ui-kit-1-shell-20261023.md`;`PROGRESS-ui-kit-2-chat-20261023.md`;`PROGRESS-ui-kit-3-chart-20261023.md`;`PROGRESS-ui-kit-4-people-20261023.md`;`PROGRESS-ui-kit-5-pages-20261023.md`;`PROGRESS-ui-kit-6-rectification-20261023.md`;`PROGRESS-ui-kit-7-cleanup-20261023.md` | **界面统一换成组件库**:shadcn base-nova(补 `shadcn/tailwind.css`)· Lucide · Motion · react-resizable-panels · dnd-kit · Zustand;lint 拦手写控件(白名单只减不增);对话旁星盘面板;会话、档案拖动排序(动表);长相不变。产品 2026-10-23 拍板 D1~D8;8 个 issue(K0~K7)串行,取代全站动效 #98 #100 #101 | K0–K7 已在测试环境,待验收 | 原型 https://claude.ai/artifact/PGnWsjHhu38KJseEfEMNWG | -| —(门禁修复,无新任务书) | `PROGRESS-birth-journey-contract-20261025.md` | **测试分支过不了门禁,网站停在完整数据版(二)**:BUG-1439 让出生时间合同改锁对话里的校正。下一道门禁 Python 已过,前端 29 条画不出新对话框和滚动(BUG-1440),只补测试画布,页面不改 | 已合入 staging,等门禁 | `codex/gate-birth-journey-contract-20261025` | +| —(门禁修复,无新任务书) | `PROGRESS-birth-journey-contract-20261025.md` | **测试分支过不了门禁,网站停在完整数据版(二)**:BUG-1439 出生时间合同;BUG-1440 测试画布;BUG-1441 侧栏流式检查等第一次补画结束再数帧。页面不改 | 已合入 staging,等门禁 | `codex/gate-birth-journey-contract-20261025` | | `TASK-onboarding-journey-20261022.md` | `PROGRESS-onboarding-1-data-20261022.md`;`PROGRESS-onboarding-2-intake-20261022.md`;`PROGRESS-onboarding-4-motion-20261022.md` | **新用户启程**:建档加「先看谁的盘」(自己 / 帮别人看,本人资料可后补)、人物关系字段、首问免费(服务端原子)、起步三问、启程清单(七项从数据推导,收费项标价,老用户可见)、八条一次性提示(含星盘四页签)、启程流程专用动效。产品 2026-10-22 拍板 D1~D6;动表经产品同意进循环,拆四个 issue 串行。BUG-1420 已被文档去重占用,本轮首问免费记 BUG-1421。(一)T1+T2 在 `codex/onboarding-1-data-20261022`。(二)T3+T4 记 BUG-1431,在 `codex/onboarding-2-intake-20261022`。(四)T7 在 `codex/onboarding-4-motion-20261022` | (一)(二)(三)已在 staging;(四)本分支待验收 | 原型 https://claude.ai/artifact/PcTwMVb9RD611R3qStxv3U | | `TASK-site-motion-20261022.md` | — | **全站动效**:换页按侧栏方向滑入、选中块跟随、页签、盘型切换星体移宫、换人、翻日期、列表补位、弹窗从触发处长出、抽屉、顶部提示、点数滚动、中英交叉淡入;重写 DESIGN §6(推翻 180 ms 上限与 Spatial Instant),不引动画库,先验证 View Transitions。产品 2026-10-22 拍板;三个 issue,排在启程(三)之后、启程(四)之前;**2026-10-23 被 ui-kit-migration 取代** | 已取代 | 原型 https://claude.ai/artifact/7u9goivnmTNSGTNWNeVMQd | | `TASK-rectification-narrower-delivery-20261016.md` | `PROGRESS-rectification-narrower-delivery-fix-20261017.md` | **交付区间收窄**:显示改按活着的簇分段。第 1 次打回只补两处显示漏段(取整差 8 分、缺区间整段退回)。对半出题和门槛 8 改 6 仍不做。基线与对照各 924 条,12 格包含率都过红线。BUG-1367 起,本轮原记 BUG-1370,因与婚恋合盘保留号冲突改为 BUG-1392 | 待验收 | `codex/rectification-narrower-delivery-20261016` | diff --git a/frontend/tests/home-sidebar-rerender.test.tsx b/frontend/tests/home-sidebar-rerender.test.tsx index 0e81d881..7a392b96 100644 --- a/frontend/tests/home-sidebar-rerender.test.tsx +++ b/frontend/tests/home-sidebar-rerender.test.tsx @@ -70,6 +70,10 @@ function payloadSha(data: ReturnType) { return createHash("sha256").update(JSON.stringify(data)).digest("hex"); } +function uncaught(errors: readonly unknown[]) { + return errors.map((error) => error instanceof Error ? error.stack ?? error.message : String(error)).join("\n"); +} + async function runProbe(sessionCount: number) { const harness = createClientLifecycleHarness(); resetSubjectCatalogForTests(); @@ -225,18 +229,38 @@ async function runProbe(sessionCount: number) { }; try { + // The list and the account menu arrive as chunks after the first paint. + // A 30-row paint can finish before those chunks, so the swap landed inside + // the 60 frames and counted as one sidebar commit. Load them first. + await import("../src/components/ui/dropdown-menu.tsx"); + await import("../src/components/sidebar-session-sortable.tsx"); await harness.render( , ); await harness.idle(); - assert.equal(harness.errors.length, 0); + assert.equal(harness.errors.length, 0, uncaught(harness.errors)); assert.ok(latestShell, "hydrated home registers once before the frame run"); metrics.recording = true; metrics.registers = 0; metrics.commits = 0; metrics.actualDuration = 0; + let firstLoadQuiet = false; + for (let attempt = 0; attempt < 8; attempt += 1) { + const registersBefore = metrics.registers; + const commitsBefore = metrics.commits; + await harness.idle(); + if (metrics.registers === registersBefore && metrics.commits === commitsBefore && harness.errors.length === 0) { + firstLoadQuiet = true; + break; + } + } + assert.equal(firstLoadQuiet, true, "sidebar was still painting its first load"); + assert.equal(harness.errors.length, 0, uncaught(harness.errors)); + metrics.registers = 0; + metrics.commits = 0; + metrics.actualDuration = 0; for (let frame = 1; frame <= 60; frame += 1) { await harness.update(() => driver.setFrame(frame)); } @@ -280,13 +304,13 @@ async function runProbe(sessionCount: number) { otherTitle, }; console.log(`SIDEBAR_AB ${JSON.stringify(report)}`); - assert.equal(duringFrames.registers, 0); - assert.equal(duringFrames.commits, 0); + assert.equal(duringFrames.registers, 0, "streaming frames republished the sidebar"); + assert.equal(duringFrames.commits, 0, "streaming frames recommitted the sidebar"); assert.equal(afterActiveRegisters, 1); assert.equal(afterActive?.activeSessionId, "session-active"); assert.equal(afterTitleRegisters, 1); assert.equal(renamedTitle, "Renamed synthetic"); - assert.equal(afterRevisionRegisters, 0); + assert.equal(afterRevisionRegisters, 0, "a revision republished the sidebar"); assert.deepEqual(selectRevisions, [4]); assert.deepEqual(renameRevisions, [4]); assert.equal(afterSwitchRegisters, 1);