docs(home): records for the home first-load round (BUG-1127..1130); measurement script lint fix
Progress with before/after, T4 evidence (supportsImmutableAssets has no effect under self-hosted standalone) in BLOCKED.md, device checklist, changelog, board row to 待验收. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e2c3791fad
commit
856f611fcf
@@ -1,5 +1,13 @@
|
||||
# BLOCKED
|
||||
|
||||
## BUG-1128:JS 在每次部署后仍要重新下载(`supportsImmutableAssets` 自托管不生效,2026-09-30)
|
||||
|
||||
- 状态:blocked(TASK-home-first-load-20260930 T4 验证不成立,配置未改)
|
||||
- 缺什么:Next 16.3 的 `supportsImmutableAssets` 要由部署 adapter 在构建时启用、并把内容寻址的资源放进 `/_next/static/immutable/`。本仓是 `output: "standalone"` + `next start` 自托管,没有 adapter。
|
||||
- 实测(分支 `codex/home-first-load-20260930` 同代码,一次性 worktree 里在 `next.config.ts` 加 `supportsImmutableAssets: true`,`NEXT_DEPLOYMENT_ID=t4aaaa01` 生产构建):`.next/static` 下只有 `build-*`、`chunks`、`media`,没有任何 `immutable` 目录;预渲染的 `/` HTML 里 133 处资源地址仍是 `?dpl=t4aaaa01`。任务书三条证据的第一条就不成立,第二条(两次构建路径相同)、第三条(旧标签页整页刷新)没有意义,未测。
|
||||
- 替代:宋体切片改走 T3-b(public/ + 内容哈希 + immutable 响应头),部署后不再重下。JS 首屏靠 T7 瘦身(brotli 516 → 431 KB)和 T8 早发接口减轻;`deploymentId` 保持不变(BUG-936)。
|
||||
- 解除条件:部署方式换成支持 immutable assets 的 adapter,或 Next 在 standalone 下原生支持;届时按 T4 三条证据重测。
|
||||
|
||||
## BUG-1105:离线研究验收的本机环境缺口(2026-09-30)
|
||||
|
||||
- 推送范围更新:用户要求阶段性归档到 staging;M0 全量 JSON 与原始日志保留本地不提交。明确暂存的研究代码、测试、M1 JSON、M0 smoke 及文档已随 tracked 隐私门复验(与研究定向合计 140 passed)。只解除提交子集的隐私覆盖缺口,不解除下述未提交产物、完整 quick 或研究验收阻塞。
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# 印度占星 Skill 更新日志
|
||||
|
||||
## 2026-09-30 — 进站更快:加载时的请求提前并行发出,首页代码更小,宋体只下载一次
|
||||
|
||||
- 打开首页时,账户、人物档案、对话列表、模型列表四个请求随网页一起发出,不再等全部代码下载完、也不再一个等一个;本地模拟(每个请求 0.6 秒)从 4 轮等待变成 2 轮,揭幕时间约减三分之二(不限速)或 30%(模拟慢网)。
|
||||
- 首页首屏代码压缩后从约 516 KB 减到 431 KB:全国城市表、生时校正的研究评测数据、首次填资料用的引导页(含出生地选择、日期选择)不再随首页下载,只在用到时加载;还没填完资料的新用户,揭幕前就已加载好,看不到空白。
|
||||
- 「正在载入账户 / 正在准备对话」这一屏的标题改用黑体,不再为它下载宋体。
|
||||
- 标题宋体改为固定地址并长期缓存:同一台设备下载过一次,之后每次发布都不用再下。其余代码文件仍会在每次发布后重新下载(自托管环境暂不支持,见 BLOCKED.md)。Skill 版本不 bump。
|
||||
|
||||
## 2026-09-30 — 报告阅读页的「中文 / English」切换恢复正常
|
||||
|
||||
- 之前点切换只有按钮和地址栏在变,正文和目录停在页面打开时的语言(旧语言的正文没被换掉)。现在每次切换,正文、目录、核对表都会跟着换,页面上只保留一份正文。报告内容和导出不受影响。Skill 版本不 bump。
|
||||
|
||||
+20
-20
@@ -15048,56 +15048,56 @@
|
||||
|
||||
## BUG-1127 | 首页冷启动接口串行四轮,「正在载入账户」等得久
|
||||
|
||||
- 状态:investigating(根因已测量确认;待执行,任务书 `docs/tasks/TASK-home-first-load-20260930.md` T1、T8)
|
||||
- 状态:resolved(代码 + 回归测试;待部署,真机待产品)
|
||||
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
|
||||
- 影响面:`frontend/src/lib/session-list-context.tsx`(`loadSessionList`)、`(app)/page.tsx` 取校正入口摘要的 effect。
|
||||
- 现象:真机进站长时间停在「正在载入账户」。
|
||||
- 触发条件:新开页面的冷启动(暖快照 BUG-1040 只在同一文档内生效)。
|
||||
- 根因:账户 → 人物目录 → 会话列表 → 校正入口摘要依次等待,其中只有「会话列表要知道当前人物」是真依赖。Claude 在 staging 线上页面用 CDP 给接口统一加 600 ms 延迟实测:从首个接口到揭幕共 4 轮串行,约 2.5 s。另外,这些请求要等全部首屏 JS 执行完才开始发,DOMContentLoaded 后约 1 秒没有任何可并行的网络请求(2026-09-30 追加,任务书 T8)。
|
||||
- 修复:待执行。
|
||||
- 验证:待执行。
|
||||
- 防复发:待执行后补。
|
||||
- 修复:① `loadSessionList` 与 `loadSubjectCatalog` 把账户、人物目录、按本地记住的人物取的会话列表放在同一轮发出;人物目录回来后当前人物变了,或本地记住的账户与这次返回的账户不同,就丢弃这份会话列表,按正确的人物重新取;首次登录(本地没记住账户)不再作废刚读到的人物目录。② 校正入口摘要改在账户阶段就发(`rectification-entry-summary-read.ts`),首页只取用一次。③ T8:根 layout 加一段内联 ES5 脚本(只在 `/` 生效),HTML 一到就发出账户、人物目录、会话列表、模型目录四个请求,挂在 `window.__jyotishaBootReads` 上;启动代码对每个请求只取用一次,超过 30 s 就不信任,失败时回退到正常请求;请求路径和参数与正常请求共用 `boot-early-reads.ts` 里的常量。
|
||||
- 验证:`frontend/tests/home-first-load-20260930.test.tsx`(T1 六条、T8 十条:同一轮发出、人物被剔除后丢弃预取、换账户丢弃、401 照样登出、早发失败回退、只取用一次、30 s 失效、脚本是 ES5 且只在 `/` 生效、早发请求与正常请求逐字节相同)。本地生产构建 + 测量脚本 `frontend/scripts/measure-home-first-load.mjs`(接口统一延迟 600 ms):串行轮数 4 → 2;不限速时揭幕约 2.7 s → 0.9 s;模拟 1.6 Mbps / 150 ms 慢网时 8.0 s → 5.6 s,首个接口从「首屏 JS 下完后 +30 ms」提前到「首屏 JS 下完前约 3.2 s」。数字见 `docs/tasks/PROGRESS-home-first-load-20260930.md`。
|
||||
- 防复发:冷启动新增的请求先问两件事:它依赖谁;能不能放进 HTML 早发。预取出来的列表必须带上「为谁取的」(人物、账户),对不上就丢弃。早发请求与正常请求共用 `boot-early-reads.ts` 里的常量。
|
||||
- 相关记录:BUG-936、BUG-1021、BUG-1040、BUG-1104
|
||||
- 修复版本:—
|
||||
- 修复版本:分支 `codex/home-first-load-20260930`(`ab656e0d` T1、`2fd1e2b0` T8)
|
||||
|
||||
## BUG-1128 | 每次部署后浏览器重新下载全部 JS 与宋体字体
|
||||
|
||||
- 状态:investigating(待执行,任务书 `docs/tasks/TASK-home-first-load-20260930.md` T3/T4)
|
||||
- 状态:字体部分 resolved(待部署,staging 两次部署地址不变待核);JS 部分 blocked(`supportsImmutableAssets` 在自托管构建下不生效,见 BLOCKED.md)
|
||||
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
|
||||
- 影响面:`frontend/next.config.ts`(`deploymentId`)、`src/app/fonts/serif-sc/`。
|
||||
- 现象:进站慢、标题宋体很久才出来;staging 一天部署多次,用户几乎每次都是冷缓存。
|
||||
- 触发条件:任何一次新部署之后首次打开。
|
||||
- 根因:`deploymentId` 让 Next 给所有 `/_next/static` 资源(含 CSS 引用的字体)加 `?dpl=<SHA>`。内容没变,地址也会变,缓存随之失效。首屏 JS 约 690 KB(压缩后),宋体切片按页面用字 130~170 KB 起。
|
||||
- 修复:待执行(保留 `deploymentId`;验证 `supportsImmutableAssets`,不成立时字体改为自管的不变地址)。
|
||||
- 验证:待执行。
|
||||
- 防复发:待执行后补。
|
||||
- 修复:T3-b:30 个宋体切片从 `src/app/fonts/serif-sc/` 挪到 `public/fonts/serif-sc/`,文件名加 sha256 前 10 位(字节不变,git 重命名 100%),`serif-sc.css` 改为引用 `/fonts/serif-sc/...` 绝对地址;`next.config.ts` 给 `/fonts/serif-sc/:path*` 加 `Cache-Control: public, max-age=31536000, immutable`;`build_serif_slices.py` 与 `SOURCE.txt` 跟着改命名规则;OFL.txt 复制到切片旁。按任务书决策 2 推翻 `serif-headings-contract.test.ts` 里「bundled by Next, not served from public/」(三栏已写)。T4:在 `next.config.ts` 试 `supportsImmutableAssets: true` 后,生产构建产物没有 `/_next/static/immutable/`,预渲染 HTML 里 133 处资源地址仍带 `?dpl=`(Next 文档说明该功能要托管 adapter 配合),第一条证据不成立,配置没有改。
|
||||
- 验证:本地生产构建:CSS 里的切片地址为 `url(/fonts/serif-sc/jyotisha-serif-sc-00.1812be0085.woff2)`,不带 `?dpl`;`next start` 返回 `Cache-Control: public, max-age=31536000, immutable`。`tests/test_serif_font_slices.py` 新增「文件名哈希与内容一致、public 旁有 OFL」;`serif-headings-contract.test.ts` 新增 next.config 长期缓存断言;`font-stack-loadable-contract.test.ts` 按 public 解析绝对地址。
|
||||
- 防复发:要长期缓存的静态资源不走 Next 打包(打包资源带每次部署都变的 `?dpl=`),放 public/、文件名带内容哈希、配 immutable 响应头,并用测试钉住「名字对得上内容」。JS 的每次部署失效要等部署方式支持 immutable assets(adapter)后再议,不得为此去掉 `deploymentId`(BUG-936)。
|
||||
- 相关记录:BUG-936(引入 `deploymentId` 的原因)、BUG-737
|
||||
- 修复版本:—
|
||||
- 修复版本:分支 `codex/home-first-load-20260930`(`8112f62b`)
|
||||
|
||||
## BUG-1129 | 加载屏标题用宋体,JS 就绪前先下载 3~4 个字体切片
|
||||
|
||||
- 状态:investigating(待执行,任务书 `docs/tasks/TASK-home-first-load-20260930.md` T2)
|
||||
- 状态:resolved(代码 + 回归测试;待部署,真机待产品)
|
||||
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
|
||||
- 影响面:`frontend/src/app/globals.css` `.app-loading-content strong`。
|
||||
- 现象:加载屏标题换体晚、会跳一下;字体下载和关键 JS 抢带宽。
|
||||
- 触发条件:任何冷启动。
|
||||
- 根因:加载屏标题用 `--font-display`。「正在载入账户」一行命中 3 个切片(约 130 KB),实测连同副标题共触发 4 个切片请求。
|
||||
- 修复:待执行(产品 2026-09-30 定:加载屏改用黑体)。
|
||||
- 验证:待执行。
|
||||
- 防复发:待执行后补。
|
||||
- 修复:`.app-loading-content strong` 改用 `--font-body`;DESIGN.md 把它列入「刻意用黑体的标题」,并写明加载屏在 JS 就绪前不触发网络字体下载。
|
||||
- 验证:`serif-headings-contract.test.ts` 新增一条钉住加载屏标题用黑体。测量脚本:`load` 之前的宋体切片请求从 4 个(00/01/03/04)降到 1 个;剩下的是侧栏品牌字「Jyotisha」(`.brand-row`,00 号切片,38 KB,只含拉丁字母),它不在决策 1 的范围内,没有改,交产品决定。切片现在长期缓存,每台设备只下载一次。
|
||||
- 防复发:JS 就绪前就可见的元素(加载屏、静态 HTML 里的侧栏)用网络字体前先想清楚代价;加载屏标题的黑体由合同测试钉住。
|
||||
- 相关记录:BUG-737、BUG-1128
|
||||
- 修复版本:—
|
||||
- 修复版本:分支 `codex/home-first-load-20260930`(`80ea7b0b`)
|
||||
|
||||
## BUG-1130 | 首页首屏 JS 带着首屏用不到的模块(全国城市表、校正代码、研究数据等)
|
||||
|
||||
- 状态:investigating(待执行,任务书 `docs/tasks/TASK-home-first-load-20260930.md` T7)
|
||||
- 状态:resolved(代码 + 回归测试;待部署,真机待产品)——T7 第 1、2 项及日期选择器完成;校正界面与 markdown 渲染未做(见修复)
|
||||
- 首次发现 / 最近更新:2026-09-30 / 2026-09-30
|
||||
- 影响面:`frontend/src/data/china-locations.*` 及以值方式导入它的 `src/lib/home-types.ts`、`global-birth-payloads.ts`、`birth-rectification-payload.ts` 等;首页首屏 chunk。
|
||||
- 现象:进站等待长的原因之一,首屏 JS 约 520 KB(brotli,不含 `noModule` 补丁)。
|
||||
- 触发条件:任何冷启动。
|
||||
- 根因:Claude 用 source map 构建按来源统计首屏 JS:全国城市表 `china-locations.json`(brotli 47 KB,实测)经 `home-types.ts` 等以值导入进入首屏;约 20 KB 的校正研究封存评测 JSON 进入首屏;首屏还带着校正 / 生时录入代码(未压缩约 226 KB)、`react-day-picker` + `date-fns`(约 94 KB)、markdown 渲染(约 80 KB),而空白首页用不到这些。估计合计可以挪走约 150 KB(brotli),约占首屏三成。
|
||||
- 修复:待执行。
|
||||
- 验证:待执行。
|
||||
- 防复发:待执行后补。
|
||||
- 修复:① 城市表:客户端改为按需加载(`china-locations-client.ts`),`home-types.ts` 不再以值的方式导入;`selectedBirthPlace` 仍是同步的,读已加载的表。人物目录(包括本人资料)里只要有「只有省市编码、没有坐标」的老资料,就在 `upsertSelfChart` 和揭幕之前先加载城市表;出生地选择器与人物档案页继续静态引入城市表(数据跟着 /people 与引导页的代码包走)。② 研究数据:`confirmation-gate.ts` 不再整个导入 `rectification_sealed_holdout.v1.json`,只保留用到的 6 个字段,合同测试逐字段钉住它与 JSON 一致。③ 引导页(出生地选择器、城市表、生时录入、日期选择器都挂在它下面)改为按需加载:资料未填完时,冷启动在揭幕前预加载,`React.lazy` 拿到同步兑现的 thenable,揭幕同一帧就画出来。未做:校正界面、markdown 渲染按需加载——需要在「打开校正、打开会话、落在 `?c=` 会话」几个入口都加揭幕前等待,改动面大,按让步顺序留给下一单。
|
||||
- 验证:首页首屏 JS(排除 noModule,本地构建实测):29 个文件 / 1994 KB / brotli 516 KB / gzip 611 KB → 28 个 / 1608 KB / brotli 431 KB / gzip 504 KB(brotli −16.5%,gzip −17.5%;超出 ±2% 属预期);城市表、封存评测 JSON、日期选择器已不在首屏 chunk,markdown 仍在。测试:老资料(河北石家庄长安区、北京东城区两组虚构编码)在加载表后逐字段等于改动前的算法;冷启动遇老资料先加载表;首屏模块不再以值导入城市表和封存 JSON;资料未填完时揭幕前等引导页预加载、填完的不加载;预加载后引导页首帧即渲染。
|
||||
- 防复发:首页首屏只放空白首页用得到的东西;只有少数人或少数时刻用得到的模块(引导、老资料兼容、研究数据)按需加载,并在「一定会用到」的那一刻之前(揭幕前)预加载。别把整个研究 JSON 导入客户端模块。
|
||||
- 相关记录:BUG-1127、BUG-1128
|
||||
- 修复版本:—
|
||||
- 修复版本:分支 `codex/home-first-load-20260930`(`b743b16b`、`e2c3791f`)
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# PROGRESS · 进站等待太久:首页首开与字体加载 · 2026-09-30
|
||||
|
||||
> 任务书:`docs/tasks/TASK-home-first-load-20260930.md`。产品 2026-09-30「请你调用 subagent 执行」,由 Claude 派 fork 子代理直接执行;验收由 Claude 另做。
|
||||
> 分支 `codex/home-first-load-20260930`(**未推送**)。基线 `origin/staging` = `6a74a230`(开工实测;任务书写单时是 `d87ec44a`,中间只多了文档提交)。BUG-1127~1130(开工核对最大号 BUG-1130;另有 BUG-1131 已被别的任务书预留,本单没有新开号)。
|
||||
|
||||
## 提交
|
||||
|
||||
| 提交 | 内容 |
|
||||
| --- | --- |
|
||||
| `80ea7b0b` | T2 加载屏标题用黑体(BUG-1129) |
|
||||
| `ab656e0d` | T1 冷启动账户 / 人物目录 / 会话列表 / 校正入口同一轮(BUG-1127) |
|
||||
| `2fd1e2b0` | T8 HTML 内联脚本早发四个请求(BUG-1127) |
|
||||
| `b743b16b` | T7 城市表按需、封存 JSON 只留 6 字段、引导页按需 + 揭幕前预加载(BUG-1130) |
|
||||
| `ed4f5133` | 测试存储桩类型修正 |
|
||||
| `8112f62b` | T3-b 宋体切片搬 public/ + 内容哈希 + immutable(BUG-1128) |
|
||||
| `31d07378` | 测量脚本 `frontend/scripts/measure-home-first-load.mjs` |
|
||||
| `e2c3791f` | 引导页改为模块级 `React.lazy`(lint 规则);暖快照测试按启动流程先加载城市表 |
|
||||
| 分支最后一个提交 | 记录:BUG 历史、CHANGELOG、DESIGN、BLOCKED、真机清单、本进度、任务板 |
|
||||
|
||||
## 每项结果
|
||||
|
||||
| 任务 | 结果 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| T2 加载屏黑体 | 做了 | `.app-loading-content strong` → `--font-body`;合同测试钉住;DESIGN.md 已写。`load` 前宋体请求 4 → 1,剩下 1 个是侧栏品牌字「Jyotisha」(见偏离 1) |
|
||||
| T1 接口并行 | 做了 | 账户、人物目录、会话列表(按本地记住的人物猜)同一轮;人物变了或换了账户就丢弃会话列表重取;首登不再作废刚读到的人物目录;校正入口摘要在账户阶段就发。**先确认了摘要不依赖会话列表**:它是按用户读的独立接口,原来的等待只来自 effect 的 `sessionListSettled` 条件,该条件保留(只改了请求发出时机) |
|
||||
| T8 早发 | 做了 | 内联 ES5 脚本放在根 layout 的 `<head>`,脚本里用 `location.pathname !== "/"` 直接返回(见偏离 2);四个请求挂在 `window.__jyotishaBootReads`;只取用一次、30 s 失效、失败回退正常请求、401 照样登出、另一人物或另一账户的会话列表丢弃;路径和参数由 `boot-early-reads.ts` 共用 |
|
||||
| T7 首屏瘦身 | 部分做了 | 城市表、封存评测 JSON、引导页(及其下的出生地选择器、生时录入、日期选择器)已离开首屏。**校正界面、markdown 渲染没做**:需要在「打开校正、打开会话、落在 `?c=` 会话」几个入口都加揭幕前等待,改动面大,按让步顺序(至少完成第 1、2 项)留给下一单 |
|
||||
| T3 宋体只下一次 | 做了(T3-b) | T4 不成立,走 T3-b;决策 2 授权推翻的那条断言已按三栏改 |
|
||||
| T4 JS 不随部署失效 | 没上(blocked) | 第一条证据不成立,见下;写入 BLOCKED.md |
|
||||
| T5 准备阶段 JS 预取 | 没做 | 可选项;T7 已把引导页挪走,剩下的按需 chunk 大多是揭幕后的次要面板,留给下一单 |
|
||||
| T6 记录 | 做了 | BUG 历史四条、CHANGELOG、DESIGN、BLOCKED、真机清单 `docs/testing/home-first-load-20260930.md`、任务书决策 6 补一句、任务板改「待验收」 |
|
||||
|
||||
## 前后对照
|
||||
|
||||
测量:本机 `next build` + `next start`(基线在一次性 detached worktree `6a74a230`,最终版在本分支),`frontend/scripts/measure-home-first-load.mjs`,`/api/*` 统一延迟 600 ms 返回虚构数据,窗口 390×844,每组 3 次。机器走代理且有波动,**只看轮数与先后,不代表真机秒数**。
|
||||
|
||||
模拟慢网(`--throttle slow`:1.6 Mbps / 150 ms,最稳定的一组):
|
||||
|
||||
| 指标 | 基线 | 最终 |
|
||||
| --- | --- | --- |
|
||||
| 揭幕时刻 | 7942 / 7954 / 7956 ms | 5617 / 5630 / 5630 ms(−29%) |
|
||||
| 最后一个首屏 JS 下完 | ≈5.43 s | ≈4.11 s |
|
||||
| 首个接口发出 | 首屏 JS 下完**之后** +25~29 ms | 首屏 JS 下完**之前** ≈3.17 s |
|
||||
| 从首个接口到揭幕的串行轮数 | 4 | 2 |
|
||||
| `load` 前的宋体切片请求 | 4(00/01/03/04) | 1(00,侧栏品牌字) |
|
||||
|
||||
不限速(本机波动较大):基线 3.06~4.36 s(更早一次空闲时测得 2.71~2.74 s),最终 0.92~1.31 s(更早一次空闲时测得 0.90~0.97 s);轮数 4 → 2(有一次记为 3,是某个请求晚发了半轮以上)。
|
||||
|
||||
首页首屏 JS(排除 noModule,按预渲染 `/` 引用的 chunk 计):
|
||||
|
||||
| | 文件数 | 未压缩 | brotli | gzip |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 基线 | 29 | 1994 KB | 516 KB | 611 KB |
|
||||
| 最终 | 28 | 1608 KB | 431 KB | 504 KB |
|
||||
|
||||
首屏 gzip −17.5%,超出 ±2%,属任务书 T7 验收 3 所述的预期下降。城市表(`梅河口市`)、封存 JSON(`previous_pilot_id`)、`react-day-picker`(`rdp-`)三个标记都不再出现在首屏 chunk 中,markdown(`micromark`)仍在。归因构建(`productionBrowserSourceMaps`)只在一次性 worktree 里做,没有提交。
|
||||
|
||||
宋体:本地生产构建里 CSS 的切片地址是 `url(/fonts/serif-sc/jyotisha-serif-sc-00.1812be0085.woff2)`,换部署编号(`new00001` → `new00002`)后地址不变、不带 `?dpl`;`next start` 返回 `Cache-Control: public, max-age=31536000, immutable`。「staging 连续两次部署地址不变」要部署后由 Claude 核对。
|
||||
|
||||
## T4 三条证据
|
||||
|
||||
1. 在一次性 worktree 里给 `next.config.ts` 加 `supportsImmutableAssets: true`,用 `NEXT_DEPLOYMENT_ID=t4aaaa01` 做生产构建:`.next/static` 下只有 `build-*` / `chunks` / `media`,**没有 `immutable` 目录**;预渲染 `/` 的 HTML 里 133 处资源地址仍是 `?dpl=t4aaaa01`。**不成立。**
|
||||
2. 未测:第 1 条已不成立,两次构建路径是否相同没有意义。
|
||||
3. 未测:同上。
|
||||
|
||||
结论:不改配置,写入 `BLOCKED.md`;Next 源码里 `supportsImmutableAssets` 的生效依赖 adapter(`build/adapter/*`),自托管 standalone 没有这一步。
|
||||
|
||||
## 测试数字
|
||||
|
||||
| 项 | 基线(`6a74a230` 干净 worktree) | 最终 |
|
||||
| --- | --- | --- |
|
||||
| `tsc --noEmit` | — | 0 错 |
|
||||
| `npm run lint` | 0 error,126 warning | 0 error,126 warning(与基线同数,本单新文件 0 告警) |
|
||||
| `npm test`(Node 22) | 4489 / fail 24 / cancelled 0 | 4513 / fail 24 / cancelled 0 |
|
||||
| 失败名单 | 24 条 | 与基线逐条一致(全是要 Docker 的数据库、部署、备份套件) |
|
||||
| 测试名 | — | 新增 24 条,没有消失的 |
|
||||
| `next build` | `/` Static | `/` Static |
|
||||
| 快速门 Python 子段 | 1001 passed / 1 skipped | 1002 passed / 1 skipped(+1 = `test_slice_names_carry_their_content_hash…`) |
|
||||
| 快速门 npm 子段(系统 Node 20) | 4489 / fail 66 | 4513 / fail 66,名单逐条一致(环境:Node 20) |
|
||||
| `tests/test_repo_privacy_markers.py` | — | 通过(快速门内) |
|
||||
|
||||
改动的既有断言与夹具(每处都写了原值 / 新值 / 原因):
|
||||
|
||||
- `serif-headings-contract.test.ts`:切片 url 从 `./jyotisha-serif-sc-NN.woff2` 改为 `/fonts/serif-sc/…<哈希>.woff2`(决策 2 授权推翻);新增加载屏黑体、长期缓存两条。
|
||||
- `font-stack-loadable-contract.test.ts`:绝对地址按 public/ 解析,「url() 文件必须存在」不变。
|
||||
- `tests/test_serif_font_slices.py`:切片目录改为 public;新增文件名哈希与内容一致、public 旁有 OFL。
|
||||
- `chart-page-view.test.tsx`、`session-list-provider.test.ts`:会话、账户请求的源码断言改为共用常量的写法,并断言常量值不变。
|
||||
- `home-warm-snapshot.test.ts`:换账户分支的源码断言改为 `boundAccount` 写法;用例前先加载城市表(夹具是只有编码的老资料)。
|
||||
- `home-bootstrap-run.test.ts`:夹具 io 补 `preloadOnboardingShell` / `startEntrySummaryRead` 空操作,断言不动。
|
||||
- `rectification-confirmation-gate.test.ts`:新增「6 个字段逐字段等于封存 JSON」。
|
||||
|
||||
## 偏离任务书之处
|
||||
|
||||
1. **T2 验收「`load` 前 0 个宋体请求」未完全达到**:剩 1 个,是侧栏品牌字「Jyotisha」(`.brand-row`,按 serif-headings 方案属于标题,静态 HTML 里可见),只用 00 号切片(38 KB,拉丁字母)。它不在决策 1「加载屏改黑体」的范围内,没有擅自改;切片现在长期缓存,每台设备只下一次。是否也改黑体,由产品决定。
|
||||
2. **T8 内联脚本放在根 layout,用路径判断只在 `/` 生效**,而不是「只在首页 HTML 里」。首页 `(app)/page.tsx` 是客户端组件,在里面渲染 `<script>` 会进 `<body>`、执行更晚,还有水合风险;根 layout 已有同类的 BUG-936 兜底脚本。其他路径上脚本第一行就返回,不发请求(有测试覆盖 `/chart` `/reports` `/login` `/people` `/admin`)。
|
||||
3. **T1 里账户切换时,早发 / 预取的会话列表一律丢弃**(按红线 8),即使人物同为本人;代价是换账户那次多一轮,很少见。
|
||||
4. **城市表的「揭幕前加载」挂在人物目录加载里**(目录总包含本人资料),而不是 `loadSessionList`:`upsertSelfChart` 本身就要判断资料是否完整,必须在它之前加载。加载失败时,老资料会像没有表时一样被当成「地点未完整」(原来不会发生);网络断开才会遇到,没有另做兜底。
|
||||
5. 引导页若在揭幕后才需要(例如揭幕后资料被重置),走普通按需加载,空白一瞬(fallback 为空,不是 spinner)。
|
||||
|
||||
## 环境缺口
|
||||
|
||||
- 没有登录态与真机:浏览器级验收见 `docs/testing/home-first-load-20260930.md`(七条)。本地测量用虚构账户接管接口,真机网络下的秒数以产品掐表为准。
|
||||
- 没有 Docker:DB / 部署套件 24 条失败,与基线逐条一致。
|
||||
- worktree 里没有 `.venv`:`pre_work_check.py` 退出码 0,但外部引擎诊断那一项报「找不到 `.venv/bin/python`」;快速门借用本机 `.worktrees/rectification-holdout-expansion-20260929/.venv`(临时软链接,已删)。
|
||||
- 「staging 连续两次部署宋体地址不变」「staging 上的揭幕时间」要部署后由 Claude 复核。
|
||||
@@ -384,7 +384,7 @@
|
||||
| `TASK-rectification-midnight-date-anchor-20260920.md` | `PROGRESS-rectification-midnight-date-anchor-20260920.md` | **跨午夜的日期锚点与簇跨度(BUG-982 + BUG-983 合并一单)**:两条同根——线上契约只传钟点、日期不在契约里,后端按钟点大小**猜**哪端跨日。**BUG-983 是静默算错盘**:`_candidate_datetimes()` 把起始钟点无条件绑在申报日期上,前端零补偿。Claude 实测(申报日 2000-06-15):申报 `00:10` → 候选 `06-15 23:55`…`06-16 00:25`,**申报分钟本身落在 `06-16 00:10`,+1 天**;`00:02` 同样 +1 天;`12:00` 对照正确。受影响:申报落在午夜后半径内者(±15≈1.0%、±30≈2.1%、±60≈4.2%、±120≈8.3%);`late_night`(23:00–03:59) 更重,其 `00:00`–`03:59` 共 240 分钟**全部落在用户没申报过的日期上**。**BUG-982 影响面比原记录大**:同缺陷在链路上出现三处(`_cluster_span` / `unionStillValidRange` / `indistinguishableWidthMinutes`),原记录只写了 Python 两处。实测跨午夜簇 `23:58/23:59/00:00` 报告宽度 **1440 分钟**(真实 3)、`23:50/23:55/00:05` 报 **1431**(真实 16);该链经 `reportWidth` 直达 **交付卡「范围 X 分钟」**,深夜用户会看到候选其实差几分钟却被告知范围一千多分钟。**与 BUG-981 的关系**:981 让每个候选按自己日期算 Dasha 是对的,但 983 给的日期本身就错,**983 不修则 981 在申报近午夜的场景等于没上线**。**产品 2026-09-20 三点全部拍板**:D1 `late_night` **先问用户「午夜前还是午夜后」,答不上退「同日两段」`[D 00:00–03:59] ∪ [D 23:00–23:59]`,一律不得跨到 `D+1`**(连锁范围:退路产生**不连续候选集**,枚举/聚类/簇跨度/交付区间/宽度/交付卡都要支持两段,**不得用「取两段最小到最大」糊成一段**——那正是 982 的同型错误);D2 **允许窗口跨到前一天**,但交付层必须说明「若落在 23:5x 则出生日期是前一天」,不得静默改用户的出生日期(依据 Part B B4 真值覆盖率优先);D3 **加窗口相对序号**,排序/取首尾/算跨度一律用序号、钟点只作展示 —— 序号入契约会改候选身份与缓存指纹,**必须核对 BUG-984 的 `scoringIdentityMatches` 并确认历史结果只读不被重标**。硬红线:不调打分常数/确认门阈值;不得以排除真值换窄宽度;不得只修 `_cluster_span` 就宣称 982 已修;非跨午夜必须逐位不变且用同机 A/B(不得写死跨机浮点哈希,见 BUG-985)。沿用 BUG-982/983,不新开号 | **已验收通过并合入 staging**(2026-09-21,`b85c4a68`);已部署 staging(run 2831);真人验收待完成 | Claude 独立复核(用当初复现两条 Bug 的同一探针重跑):**BUG-983** 新契约下申报 `00:10`/`00:02` 的申报分钟落在**申报日**(+0 天),`late_night` 300 个候选**全在同一日**、两段;**BUG-982** 跨午夜 3 分钟簇 → **3 分钟**(原 1440),同日两段 → **300 分钟**(未糊成 1440)。三处(`decision_policy.py`/`credible-range.ts`/`candidate-plateau.ts`)均已改。**非跨午夜分数同机 A/B 6/6 逐位相同**;新测试在基线 6 条红、修复后全绿;Python `test_rectification_*` 基线 216 → **272 passed / 0 failed**;`tsc --noEmit` 退出 0。红线全清:打分常数零改动、`status=not_ready`、coverage 0、`holdout_passed()` False、Skill 未动、`official_eval_trial_count` 仍 0。D1 追问 4 选项含「不知道」「跳过」,均退同日两段、从不去 `D+1`,明写不计分可跳过;D2 交付层显示「(前一天)」;D3 用 `window_index`/`segment_index`,`interval_union_width` 按段求和(注释 “never fill a declared segment gap”)。超出要求的设计:`candidate_intervals` 拒绝重复钟点;engine-client 对 dated 路径 fail-closed 抛 `engine_invalid_candidate_decisions`。环境缺口:3 条前端测试在 Claude 机器红,但**同样的 `@/` 别名错误在既有测试上一模一样复现**(`ephemeris-route`、`api-service-unavailable`),非回归;无 Docker 故 `test:db` 未跑。**遗留待确认**:无 `cluster_intervals` 的行仍走旧按钟点算法(宽度仍 1440),应确认除修复前旧缓存外没有别的生产路径会产生无日期行 |
|
||||
| (直接执行,无任务书) | `PROGRESS-notice-loading-polish-20260930.md` | 性别只在档案(1119)、星盘气泡频闪与表格圆角(1120)、全站顶部浮层提示(1121/1122)、我的报告提速与分页(1123/1124)、宫位点亮等待与今日一句不跳(1125) | **已验收,推 staging**;真机清单 `docs/testing/notice-loading-polish-20260930.md` 待产品 | 合并分支 `codex/polish-integrate-20260930` |
|
||||
| `TASK-report-language-switch-stale-20260930.md` | `PROGRESS-report-language-switch-stale-20260930.md` | 报告阅读页切换中文 / English 后正文不换:正文与核对表同级重复 key(`key={shown}`),旧语言节点残留(BUG-1126);改 key + 整页切换回归测试 | **已验收,推 staging**(Claude 直接执行);真机第 10、11 步待产品 | 分支 `codex/report-language-switch-stale-20260930` |
|
||||
| `TASK-home-first-load-20260930.md` | — | 进站等待太久:冷启动接口串行 4 轮且要等 JS 跑完才发(BUG-1127,T1+T8 早发)、每次部署全部 JS 与宋体缓存失效(BUG-1128)、加载屏宋体抢带宽(BUG-1129)、首屏 JS 带城市表 / 校正代码 / 研究数据(BUG-1130,T7 瘦身);产品定加载屏用黑体、宋体只下一次;`supportsImmutableAssets` 先验证后上线;「新开页面先显示上次内容」待产品拍板,不在本单 | 待领取 | — |
|
||||
| `TASK-home-first-load-20260930.md` | `PROGRESS-home-first-load-20260930.md` | 进站等待太久:冷启动接口串行 4 轮且要等 JS 跑完才发(BUG-1127,T1+T8 早发)、每次部署全部 JS 与宋体缓存失效(BUG-1128)、加载屏宋体抢带宽(BUG-1129)、首屏 JS 带城市表 / 校正代码 / 研究数据(BUG-1130,T7 瘦身);产品定加载屏用黑体、宋体只下一次;`supportsImmutableAssets` 先验证后上线;「新开页面先显示上次内容」待产品拍板,不在本单 | **待验收**(Claude 派 fork 子代理直接执行,未推送):T2/T1/T8/T3-b/T7(城市表·研究 JSON·引导页) 完成;T4 blocked(自托管不支持 immutable assets);T5、T7 校正界面与 markdown 未做 | 分支 `codex/home-first-load-20260930` |
|
||||
|
||||
## 命名与归档
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ Claude 的测量方法:无头 Chrome 打开 staging 线上 `/`,用 CDP 拦
|
||||
3. 没有采用的方案:苹果设备优先用系统自带宋体、标题全部改回黑体。
|
||||
4. 接口并行化、按需 JS 预取属于纯工程优化,不改变可见行为,按本单红线执行即可。
|
||||
5. **追加(产品 2026-09-30「追加」)**:首屏 JS 瘦身(T7)、接口在 HTML 到达时就发出(T8),两项都不改可见行为。
|
||||
6. **未决,不在本单范围**:「新开页面先显示上次的账户和会话列表,后台再刷新」需要把账户与会话标题存进设备本地存储,和 BUG-1040 暖快照「只放内存」的设计冲突,涉及隐私取舍,等产品拍板后另开单。本单**不得**把这些数据写进 localStorage / sessionStorage / IndexedDB。
|
||||
6. **未决,不在本单范围**:「新开页面先显示上次的账户和会话列表,后台再刷新」需要把账户与会话标题存进设备本地存储,和 BUG-1040 暖快照「只放内存」的设计冲突,涉及隐私取舍,等产品拍板后另开单。本单**不得**把这些数据写进 localStorage / sessionStorage / IndexedDB。产品 2026-09-30 同意暂不做,本单验收后按真机掐表再议。
|
||||
|
||||
## 4. 硬红线
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# 真机清单 · 进站等待与字体(2026-09-30)
|
||||
|
||||
对应 `docs/tasks/TASK-home-first-load-20260930.md`(BUG-1127~1130)。手机和电脑各走一遍。计时用手机秒表:从点开网址开始,到能在输入框里打字为止。第 1、3 步要在**部署之后第一次打开**时做(部署会让代码缓存失效,第一次打开才是最慢的情况)。
|
||||
|
||||
| # | 操作 | 应该看到 | 结果(秒 / 是否) |
|
||||
|---|---|---|---|
|
||||
| 1 | 部署后第一次打开 `https://staging.jyotisha.chat/`(已登录) | 加载屏标题「正在载入账户」是黑体,不会过一会儿再变成宋体;揭幕后是空白首页,能打字。记下秒数 | |
|
||||
| 2 | 立刻刷新一次,再计时 | 比第 1 步快;揭幕后同样是空白首页 | |
|
||||
| 3 | 等下一次部署(或请 Claude 告诉你已部署),再打开首页 | 首页标题、问候语等宋体字**一出现就是宋体**,不会先显示黑体再换成宋体(说明宋体没有重新下载)。记下秒数 | |
|
||||
| 4 | 在「星盘档案」里切到另一位人物,回首页,刷新 | 侧栏列表只出现这位人物的对话,**不会先闪一下自己的对话**再换 | |
|
||||
| 5 | 用一个**还没填完资料**的新账号登录(没有的话跳过) | 揭幕后直接出现「你好,我是 Jyotisha」和填资料的卡片,不会先空白一下再出来;填出生地时省市列表正常 | |
|
||||
| 6 | 打开「星盘档案」 | 每位人物的一行里城市名和以前一样(例如「石家庄」) | |
|
||||
| 7 | 退出登录,再打开 `https://staging.jyotisha.chat/` | 正常跳到登录页,不会卡在加载屏 | |
|
||||
|
||||
说明:第 1、2 步的秒数请直接发给 Claude,和改动前的体感对比;本地模拟里揭幕时间约减三分之二(不限速)/ 30%(模拟慢网),但真机网络不同,以你的秒表为准。
|
||||
@@ -96,10 +96,7 @@ async function measureOnce() {
|
||||
if (/\/_next\/static\/.*\.js/.test(url) && m.params.initiator?.type === "parser") parserJs.add(m.params.requestId);
|
||||
if (/\.woff2/.test(url)) events.push([now(), `font request ${url.split("/").pop().split("?")[0]}`]);
|
||||
}
|
||||
if (m.method === "Network.loadingFinished") {
|
||||
const url = urls.get(m.params.requestId) ?? "";
|
||||
if (parserJs.has(m.params.requestId)) jsDone.push(now());
|
||||
}
|
||||
if (m.method === "Network.loadingFinished" && parserJs.has(m.params.requestId)) jsDone.push(now());
|
||||
if (m.method === "Page.domContentEventFired") events.push([now(), "DOMContentLoaded"]);
|
||||
if (m.method === "Page.loadEventFired") events.push([now(), "load"]);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user