Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbBXoz1Qk3y9buDv1mkmTu
48 KiB
代码体检 2026-10-28(issue #118)
基线 origin/staging 0f0fea325。四路只读审计(前端加载与屎山、后端与推理速度),Claude 抽查了排在前面的结论。体检结论摘要与拆单清单见 Gitea issue #118 的评论;本文件是两份原始报告的合并稿,供拆出的小单引用数字和测法。
口径:「实测」= 审计机本地计时;「估」= 推算或取自 10-04~10-07 的进度记录。审计机没有模型 key、没有 PyJHora 与 VedAstro SDK,涉及这些的生产耗时都是估计。出生资料只用公开名人和一张虚构盘。
代码体检(后端 + 推理速度)· 2026-10-28
基线 origin/staging 0f0fea325(audit-20261028 工作树,只读,git status 干净)。
本机条件:
- Python 3.13,swisseph 走
~/.local。 - 没有 PyJHora、没有 VedAstro SDK。VedAstro 网络关闭。
- 没有模型 key。
标注口径:
- 「实测」= 本机计时。
- 「估」= 推算,或取自 10-04 ~ 10-07 的进度记录。
- 出生资料只用两组:公开名人(Einstein、Helen Keller)和一张虚构盘。
结论先行
- 咨询最慢的一段仍是答题步推理。 「推理 token 1 万已修」实际上没生效:开关
CONSULT_ANSWER_REASONING_EFFORT默认不设,compose 里也没有,A/B 一直卡在 BLOCKED。 - 后端可以一张单拿下的最大块是长报告冷生成,实测 15–23 s。
- 已有的提速开关
JYOTISH_REPORT_RENDER_FAST输出逐字节相同,本机实测快 27%,却默认关着。 - 另有约 9 s(profile 口径)花在整包 deepcopy 和 5 次重复哈希上。
- 已有的提速开关
- 10-21 PyJHora 进了生产(#93)以后,10-15「装不上就不起子进程」的省法在生产上不再生效。 报告和非 skip 排盘每次都会起 PyJHora 子进程(估)。
- 咨询每次调
consultation_workflow,只要 VedAstro 不命中,就起 3 个 Python 子进程。- 本机实测:0.55 s 里有 0.40 s 花在这上面。
- 生产上最多等 1.5 s。
- 失败结果只缓存 300 s。
- 两条链路都还有「第 0 步模型调用」的白耗:
- 校正:强制先调一次 read-case。
- 咨询:第 0 步开着思考,分类又串行挡在前面。
- 屎山方面:
- 226 个顶层脚本(约 3.4 万行)从运行入口不可达。
- 20 个模块按 2~3 个名字各加载一份。
- Vimshottari 仍有两套起算口径,差 1 天。
问题清单(按影响从大到小)
| # | 位置 | 问题 | 量化影响 | 建议改法(一张小单) | 验收标准 | 风险 |
|---|---|---|---|---|---|---|
| 1 | frontend/src/mastra/consultation-tools.ts:consultAnswerReasoningEffort / answerEffortOptions(183–207 行);BLOCKED.md 第 23 行 |
答题步推理强度开关不设就返回 undefined。compose 里没设,A/B(TASK-answer-effort-ab)因为没有 key 一直没跑。10-04 量到的「答题步推理 1 万 token」实际上还在 |
估:答题步推理 token 中位 9.5–11.9k,约 45–60 s(10-04 实测记录)。这是咨询最慢的一段 | 在有 key 的 staging 跑完 answer-effort-ab 的 T3。产品定档后写进 compose(或 .env),代码不改 |
/admin/usage 分段列里,第 1 步推理 token 中位数和写正文用时都下降;回答质量盲评不劣于基线 |
解读深度下降;部分供应商不认这个参数 |
| 2 | scripts/report_perf_flags.py:RENDER_FAST_DEFAULT = False |
渲染快捷路径输出逐字节相同,但因为没达到当时定的 35% 门槛就默认关着 | 实测(虚构盘,full_data zh+en,缓存关):关 15.75 / 15.27 s,开 11.20 / 11.64 s,快 27%。两边整份响应 sha256 相同(59c793e0b28c,5,262,387 B)。HTTP 冷跑 23.3 s / 6.1 MB,命中 0.08 s |
把默认值改成 True,或在 compose 里设 JYOTISH_REPORT_RENDER_FAST=1。保留旧路径作回退 |
用 scripts/research/report_reference_perf_ab.py(屏蔽 / 不屏蔽 PyJHora 各跑一遍):四张盘五字段 sha 相同;端到端 −25% 以上 |
低。快捷路径已有逐字节对照测试 |
| 3 | scripts/pl9_full_data_export.py:read_full_data_domain(2475 行);scripts/calculation_profile_contract.py:hash_bound_result(567 行)/ _drop_key_recursive(463 行) |
长报告里有两处重复劳动。① 一份报告调 10 次 read_full_data_domain,每次都把整包 worksheets deepcopy 一遍。② 溯源哈希算 5 次,每次都递归删键再哈希 |
实测(cProfile 口径,总 40.8 s):deepcopy 合计 8.9 s,其中 read_full_data_domain 5.3 s;hash_bound_result 5 次共 4.3 s,_drop_key_recursive 调了 407 万次。按无 profile 折算约 4–6 s / 份(估) |
① 同一个包只深拷一次,各领域共用只读视图;或者只拷该领域要的 worksheet 子树。② 同一个结果对象的绑定哈希按 id 记一次,或在 5 处调用的上游只算一次 |
冷生成再降 25% 以上;markdown、json 两版与改前逐字节相同;result_binding 哈希值不变 |
① 下游若原地改了拷贝,会串改共享数据。要先 grep 写入点,或改用 MappingProxy 防写 |
| 4 | scripts/report_render/annual_method_reference.py:_invoke_worker(169 行);scripts/annual_pyjhora_replay.py(202 行子进程);scripts/tajika.py(1517 / 1579 行);requirements.txt:7 PyJHora==4.8.7 |
10-15 的 BUG-1333/1334 靠「父进程导入不了 jhora 就不起子进程」省时间。10-21 #93 把 PyJHora 装进了生产,这个省法在生产上不再生效。现在每份报告、每次非 skip 的 /api/chart 都会起:年度回放子进程(超时 30 s)、Tajika 子进程、年度方法 worker(每次新开 python -m)。worker 起之前也不查 jhora 在不在 |
实测(本机没装 jhora,子进程秒退):一份报告 5 次 subprocess.run,等待 5.3 s,其中 worker 4.8 s。估:生产上每个子进程要重新 import jhora 和 swisseph,每个 1–3 s,单份报告多花 5–10 s |
起一个常驻 PyJHora 子进程,或进程内单例:一次 import,多次调用,按请求哈希缓存结果。另外 worker 也要先查 find_spec。放在新模块,主文件不增长 |
屏蔽 / 不屏蔽 PyJHora 两种口径下输出逐字节相同;一份报告的 subprocess.run 次数从 5 降到 0 或 1;冷生成时间下降 |
当初隔离进程是为了防 PyJHora 全局状态(岁差模式)污染主进程。常驻进程每次调用前都要显式重设岁差 |
| 5 | scripts/vedastro_foreground.py:ForegroundVedastroSession;scripts/vedastro_service_adapter.py:_run_official_full_snapshot_case(2549 行,依次起 capability runner、python bridge、capability catalog 三个子进程) |
咨询每次调 /api/consultation_workflow,只要快照不命中,就新开 3 个 Python 解释器。只有 official_verified 才写进前台快照缓存(is_cacheable_gateway);失败结果只在 adapter 层留 300 s。免费层限流时,几乎每轮每个领域都要重新等 |
实测(同一张盘换分钟,5 次中位):join 1.5 s 时 0.546 s,join 0 时 0.143 s,VedAstro 占 0.40 s / 次,3 个子进程。估:生产装了 SDK,又要走外网,每次未命中封顶 1.5 s;一问两个领域就是 3 s | 前台快照加负缓存:失败或限流按盘和日期记一整天。capability catalog(只用于覆盖率展示)挪到后台。可选:把三段子进程合成一个常驻 worker | 同一张盘当天第二次调用,vedastro_gateway 字段与改前一致,耗时 ≤0.15 s;成功路径输出逐字节不变 |
负缓存期内 VedAstro 恢复了也看不到。TTL 要和产品定 |
| 6 | frontend/src/lib/rectification-agentic/v9/agent-run-attempt.ts:259 的 prepareStep(stepNumber 0 只开放 rectification-read-case) |
校正每轮固定 5 次串行模型调用:分类、read-case、记证据、设焦点、写正文,都开思考 8192。其中第 0 步只是为了调 read-case,结果是确定的 | 估:每轮省 1 次带思考的模型调用,约 3–8 s(子代理按记录估)。09-26 记为 D6「另议」,至今没做 | 服务端先执行 read-case,把结果作为合成的工具调用和结果放进 bootstrap,模型从第 1 步开始 | stepCount 少 1;轮次中位耗时下降;case.loaded 回执和 Skill 收据各只写一次;read-case 投影逐字节不变 |
要改 Skill「第一步调用」的原文;涉及 BUG-282/937 的步骤约束 |
| 7 | 咨询:consultationNatalPrepareStep 与 consultationGenerationSettings(thinking: "enabled");route.ts 中 classifyConsultationTurn(约 721 行) |
第 0 步开着思考,但只用来发一个工具调用。分类(超时 ≤3 s)又排在扣点和写入问题之后串行等 | 估:第 0 步 ≤5 s(10-04 记录);分类 ≤3 s | (a) 第 0 步关思考,或扣点后立刻按已选主题预开算(复用时要比对领域参数)。(b) 分类和 prepare / append 同时开始 | 分段列里第 0 步耗时下降;prep.classify 不再在首字关键路径上 |
(a) 产品曾搁置过;(b) 判为寒暄时要撤销已经开始的工作 |
| 8 | scripts/consult_full_data_cache.py:get_or_build / _read_cached;pl9_full_data_export.read_full_data_domain |
咨询卡缓存命中时也不便宜:每个领域每轮都要读 5.5 MB JSON,再整包 deepcopy。缓存键含服务器当天日期,所以每天首问冷算。缓存文件权限是 0644,含出生资料,没跟随 0600 规则 | 实测:命中路径 attach_consult_full_data 0.425 s(profile)/ 约 0.2 s(正常),其中 JSON 解析 0.06 s、deepcopy 0.2 s;HTTP 冷首问 5.4 s。估:跨日首问 4.4–7.3 s(10-07 记录) |
① 进程内按 key 做小 LRU 缓存已解析的包(2–4 份),读视图时不再深拷。② 进入咨询页或 runTurn 开头不等待地发一次 consult_card_warm_only。③ 写缓存改用 write_private_json |
命中路径 ≤0.05 s / 领域;跨日首问 consult_card_facts.timing.hit=true;consult_card_facts 逐字节不变;新文件权限 0600 |
LRU 占内存,5 MB × N,要在 4 GB 主机上核算;预热占重计算名额 |
| 9 | scripts/jyotish_api_server.py:27/10885,ThreadingHTTPServer 单进程 |
重计算(v5 score、block_scan、报告)都是纯 Python CPU,受 GIL 限制。2 vCPU 的第二核在 Python 侧基本闲置,JYOTISH_HEAVY_COMPUTE_CONCURRENCY=2 等于两人抢一个核 |
实测(子代理):121 候选打分单跑 1.19 s;两路并发各 2.35–2.6 s(约 2.2 倍) | 新模块里给 v5 score / block_scan / 报告开进程池,max_workers = 重计算并发数,主文件只做薄注册 |
两路并发各 ≤1.3 倍单跑;规范化 JSON 与改前一致 | 内存(每个 worker 带一份引擎);worker 必须固定 PYTHONHASHSEED(ERR-111) |
| 10 | 校正交付期:jyotish_api_server.py:_attach_vedastro_minute_sensitive_to_score(约 428–515 行);前端 score-persist.ts:persistableReceipt(约 330 行) |
交付期 score 内部最多等 3 s 快照,随后前端又串行调 /v5/vedastro-validate(超时 12 s)。Python 侧区间扫描依次发,每次 20 s,前端放弃后它还在跑,并占着重计算名额。状态不是 passed 就每次重跑 |
估:交付期一轮最坏约 15 s;失败时每轮重复 | validate 结果按(证据指纹, 候选对)做负缓存;区间扫描按分钟 / 领域 / 日期缓存;Python 侧超时收到不超过前端的超时 | 同一状态第二次调用耗时接近 0;VedAstro 成功时回执逐字节不变 | 负缓存期内看不到恢复 |
| 11 | 咨询窗口问法:consultation-tools.ts:executeWindowConsultation |
/api/declared_window_chart 和 /api/rectification/v5/range_reading 串行,但后者不依赖前者的结果 |
估:0.3–1 s / 轮 | 改成 Promise.all |
工具耗时 ≈ 两者中较长的一个;输出不变 | 低 |
| 12 | 开关默认值:frontend/src/lib/consult/prep-flags.ts(CONSULT_PREP_PARALLEL 关,MODEL_VERSION_CACHE_TTL_MS 0,CONSULT_DOMAIN_CONCURRENCY 1);api_heavy_compute_gate.py(JYOTISH_GATE_CHART_OVERVIEW 关) |
compose 里都没设,.env 按要求没读。关着时:首帧前多等一次审核(接阿里云时最多 1.5 s);两领域问题串行(每领域 0.75–1.66 s) |
估:每轮 0.1–3 s,取决于审核供应商和领域数 | 先在服务器核对 .env 的 MODERATION_PROVIDER。是 aliyun 就开 CONSULT_PREP_PARALLEL=on。领域并发等第 9 条做完再开 |
consultation-prep-v1 日志里首帧前耗时下降 |
领域并发会让一个用户占满 2 个重计算名额 |
| 13 | 校正:dasha_transition_proximity.py:164-181、rectification/event_probes.py:1812 |
大运起点表每个候选重复算 3 遍(vim 两遍、那罗延一遍,引导窗口再一遍) | 实测(子代理):_vim_start_dates 479 次、_narayana_start_dates 542 次(121 候选);约占打分 20%,估 −0.15–0.25 s / 121 候选 |
静态上下文顶层每个候选建一张全量表,两处按年份过滤取用 | 两函数调用次数各 ≤121;11 个场景 sha256 不变 | 要按研究协议重新冻结(ERR-110) |
| 14 | scripts/jyotish_api_server.py:_load_local_module(2533 行,用 _jyotish_local_<name> 另起一份模块),再加上 scripts.X / X 两种 import 写法 |
同一个文件按 2~3 个名字各加载一份。各份的模块级缓存(lru_cache、memo 字典、_jhora_spec_cache 等)互不共享,isinstance 跨份也会失败 |
实测:一次咨询 + 一次排盘后,105 个脚本模块里有 20 个重复加载(共 2.8 万行),jyotish_engine 加载了 3 份。内存只多几 MB,主要是缓存命中被拆散 |
_load_local_module 先查 sys.modules 里的 scripts.<name> / <name>,有就复用;统一 sys.path 口径。只改加载器,不动调用点 |
重复加载数 20 → 0;增长护栏不涨;全量 pytest 与基线失败清单一致 | 有个别测试依赖「独立副本」去打补丁,要跑全量 |
| 15 | scripts/dasha_analyzer.py:build_dasha_timeline(85 行,strptime("%Y-%m-%d"))与 scripts/jyotish_engine.py:cmd_dasha(约 12290 行,用真实出生时刻) |
Vimshottari 还剩两套起算口径。/api/chart(经 domain_calculation_service.compute_vimshottari_timeline)和校正探针从出生当天 0 点起算;报告和完整解读从真实出生时刻起算。dasha_analyzer 里另有一份年数表字面量(白名单放行) |
估:两边 MD / AD 边界最多差 1 天。出生时刻越晚,日期串落到不同日的概率越高(约等于出生小时 / 24)。星盘页和报告可能对不上一天 | build_dasha_timeline 增加 datetime 入参,domain_calculation_service 传真实出生时刻;年数表改读 dasha_common |
用 golden 盘对比 /api/chart 与 cmd_dasha 的 MD / AD 边界,逐项相等;校正 11 个场景要重新冻结(会变) |
改变用户可见日期,属于数值变更,要按「引擎改值跑全量」执行 |
| 16 | scripts/ 根目录 |
从运行入口(api_server、engine、mcp_server、前端 / deploy 引用的脚本、quality_gate、pre_work_check)静态不可达的脚本有 226 个,约 33.9k 行;其中 208 个只被 tests 引用。dasha_calculator.py 已无任何运行时引用。python-hygiene 只加了「数量只降不升」的护栏 |
实测:459 个顶层脚本里 226 个不可达(字符串匹配宽松,这是下限) | 先把台账 / closure / matrix / inventory 类(约 84 个文件名命中)连同只测它们的测试,一起挪到 tools/ledger/ 或删除;一单只处理一个前缀 |
快速门与全量 pytest 失败清单不变;scripts/ 根目录 .py 数下降 |
有些被 workflow 或人工命令调用,要先核对 .gitea 和 docs |
后端超长文件前 10(行数,实测)
| 文件 | 行数 |
|---|---|
scripts/jyotish_engine.py |
18,679 |
scripts/classical_astrology.py |
13,065 |
scripts/jyotish_api_server.py |
10,949(冻结) |
mcp_server.py |
5,220 |
frontend/src/lib/personal-report-generation.ts |
3,740 |
frontend/src/lib/rectification-agentic/v9/method-followup.ts |
3,574 |
scripts/pl9_reader_export.py |
3,390 |
scripts/muhurta.py |
3,373 |
scripts/vedastro_service_adapter.py |
3,214 |
scripts/report_render/parity_chapters.py |
2,767 |
其后依次是 answer-choice.ts 2,641、rectification-v9-tools.ts 2,638、pl9_full_data_export.py 2,498。
超长函数前 10(实测)
| 函数 | 位置 | 行数 |
|---|---|---|
render_pl9_markdown |
jyotish_engine.py:3665 |
6,506 |
render_pl9_parity_markdown |
pl9_reader_export.py:637 |
2,754 |
cmd_full_reading |
jyotish_engine.py:15368 |
1,653 |
build_pl9_style_export_packet |
jyotish_engine.py:17023 |
934 |
_render_pl9_ai_density_raw_data_markdown_en |
pl9_full_data_export.py:1431 |
850 |
_auxiliary_dasha_section |
jyotish_engine.py:7608 |
761 |
_sanitize_pl9_ai_density_markdown |
pl9_full_data_export.py:97 |
451 |
main |
jyotish_engine.py:18239 |
437 |
_derive_event_judgement |
mcp_server.py:2317 |
423 |
_release_adjudication |
classical_astrology.py:801 |
419 |
另外:execute_consultation_workflow 400 行(api_server:2131);_sanitize_pl9_ai_density_markdown 一个函数在 profile 里占 8.6 s,主要是 7.1 s 的 re.sub,第 2 条的开关打开后会走快捷路径。
10-13 体检项核实结果(看代码与实测,不看状态板)
| 项 | 结论 | 证据 |
|---|---|---|
报告 worker defaultSleep 漏摘监听 |
已修 | personal-report-worker-core.ts:197,到点就 removeEventListener |
guided_collect_windows 按候选对重复算 |
已修 | event_probes.py:1871 起按(轨道, 序号)去重;_context_time 从 278 万次降到 2.4 万次(子代理实测) |
| 功能吉凶 / Narayana 每候选×事件×日重复算 | 已修(循环内) | derive_functional_benefic_malefic 从 6,519 次降到 121 次;_active_narayana 改走子运表;探针路径还剩 348 次 get_current_narayana_dasha,约 0.03 s |
| 打分响应 2 MB 含前端不读字段 | 已修 | 矩阵只在 include_internal 时返回,实测 0.84 MB。另外特征快照里 kp_cusps 约 298 KB 没人读,属卫生问题 |
| 长报告 18.5 s 不缓存 | 部分 | full_data markdown 按 GITHUB_SHA 缓存,命中实测 0.08 s;但冷生成仍 15–23 s,每次部署全部失效;提速开关默认关(第 2、3 条) |
/api/chart 非 skip 每次起 2 个子进程、不受闸门管 |
部分,生产上已回退 | 前端请求全部带 skip(有合同测试);闸门开关 JYOTISH_GATE_CHART_OVERVIEW 默认关;「装不上 PyJHora 就不起子进程」在 #93 装进生产后失效(第 4 条) |
| chart 缓存清理扫错目录 | 已修 | api_scratch_retention.py 登记了 api_chart_cache 等 9 个目录;但 consult_full_data_cache / report_reference_cache 文件仍是 0644(第 8 条) |
render_pl9_markdown 单函数 6917 行 |
部分 | 第一阶段搬出 34 个嵌套函数,现在 6,506 行、114 个嵌套函数,第二阶段没做 |
| api_server 伪造 handler(#70) | 已修(scripts 内) | scripts/ 和 mcp_server.py 里已没有 JyotishAPIHandler.__new__,改用 JyotishComputeService 和 offline_compute_mixins.py;tests 里还有 10 处 __new__ / object.__new__ |
| dasha 两套实现(#69) | 部分 | 年数表已收进 dasha_common(dasha_analyzer 和 tajika 在白名单里);但起算口径仍是两套(第 15 条);dasha_calculator.py 无运行时调用方,dasha_calculator_enhanced 仍被 api_server 用作「五级大运」 |
| 138 个只被测试用的台账脚本 | 未修 | 现在静态不可达 226 个(33.9k 行),208 个只被测试引用;只加了数量护栏(第 16 条) |
| 旧校正链约 2.6k 行 | 已修 | 旧 agentic 链(rectification-tools.ts、session.ts、/api/birth-rectification 等,合计 3,191 行)已删;birth-time-journey* / birth-time-scoring* 约 4.8k 行仍有首页引导在用,不是死代码 |
环境缺口
- 没有模型 key:第 1、6、7 条的秒数取自 10-04 ~ 10-07 的记录,要用 staging
/admin/usage分段核实。 - 没有 PyJHora 和 VedAstro SDK:第 4、5、10 条的生产耗时是估计,本机只量到「子进程秒退」时的下限。
.env没读:第 1、12 条的开关在生产上是否另行设置,需要产品在服务器核对。
前端体检(加载慢 + 屎山)· 2026-10-28
基线:.worktrees/audit-20261028 = origin/staging 0f0fea325。只读,没有改仓库。构建用 Node 22 跑 next build,再跑 next experimental-analyze -o,按模块归因。/ 仍是 ○ Static。
口径。 首屏 gzip 的量法和 DESIGN.md 一样:取 .next/server/app/index.html 引用的 39 个 js/css 块,每个 gzip -9 后求和。
- 实测 702,985 B。#111 接受的基线是 694,941,现在 +1.16%,还在 ±2% 内。
- 其中 39,392 B 是
polyfill-nomodule(<script noModule>),现代浏览器不下载。所以真实首屏约 663.6 KB。两次基线都含这一块,可比。 - 各路由首载 JS 的 gzip-9(实测,
route-bundle-stats.json的块求和):/chart579 KB,/583 KB,/people543 KB,/synastry486 KB,/reports429 KB,/ephemeris433 KB。
标记:「实测」= 本次构建或代码里数出来的;「估」= 按比例推算,或要浏览器才能测。
一、按影响从大到小
F1 | 星盘页、档案页首屏带上整张城市表和出生资料编辑器(BUG-1130 在次级页复发)
-
位置
src/components/chart-page/chart-page-view.tsx,第 24 行静态import { ChartBirthEditor }。它只在editingBirth为真时渲染(第 303 行)。src/components/people/people-page.tsx,第 8 行静态ChartProfileForm。src/lib/people-archive-view.ts:peopleCityLabel,经china-birth-place静态 importdata/china-locations.json。
-
问题:BUG-1130 只把城市表挪出了
/。/chart首载仍静态带着「改资料」弹窗的整条链:ChartBirthEditor → ChartProfileForm → profile-fields → birth-place-picker → china-birth-place → china-locations.json,外加 world-countries、react-day-picker、select、radio/toggle-group。这个弹窗要点铅笔才打开。/people列表只为算「城市」一列就整表 import,新增人物的表单也是静态的。 -
量化:
/chart首载里只由编辑器引入的块(实测,块 gzip-9)。四块合计 108.3 KB,占/chart首载 JS 的 19%。/people同样带 62.7 + 14.3 + 18.9 + 12.5 KB。块 gzip-9 2o97qgvjfxacu.js城市表62,653 B 3kvn2v-g_kh-c.js选地点 + 国家表14,310 B 0glp7drjfmf_j.jsreact-day-picker + date-fns18,865 B 2mlkn9k62l__6.jsselect12,516 B 可达性脚本核对过:从
/chart入口去掉chart-birth-editor.tsx后,这 27 个文件(源码 336 KB)全部不可达。 -
建议改法(一张小单)
ChartBirthEditor、people-page的ChartProfileForm改成next/dynamic。点铅笔或「新增」时再加载,并在 hover/focus 时预取。peopleCityLabel改走china-locations-client.ensureChinaLocations()和loadedChinaCountry(),与首页同法。老档案(只有省市代码、没有坐标)在揭幕前 await,保持 BUG-1130 的口径。
-
验收标准
/chart首载 JS gzip-9 从 579 KB 降到 ≤ 475 KB,/people降到 ≤ 445 KB。route-bundle-stats.json里这两个路由不再出现城市表块。- 点铅笔照常打开弹窗,第一次打开时不出现 spinner(DESIGN §9;预取 + 弹窗本身的进场即可)。
- 老档案城市名逐字不变。
-
风险:弹窗第一次打开慢一个块的下载(约 100 KB),需要预取。people 的老档案路径要有回归测试。
F2 | 首页首屏静态带着整个校正面(74 个文件)
- 位置:
src/app/(app)/page.tsx,第 25 行静态import { ConversationalBirthTimeRectification }。它只在rectificationSurfaceOpen && rectificationCaseId时渲染(第 1347 行附近)。 - 问题:只有进入校正才用到的 v9 题池、精炼包、分歧面板、校正棋盘、交付卡、报告用的
vedic-chart-svg等,全部进了/的页面块18d0n2myljhv9.js(实测 110 KB gzip)。绝大多数人打开首页是普通对话。 - 量化
- 可达性脚本(实测):只经这个组件可达的有 74 个文件,源码 620 KB,压缩后(minified)162 KB。
- 按页面块的 gzip/minified 比例 0.30 推算,约 48–52 KB gzip(估),占首屏 7%。
- 页面块内体积前几位:
collection-question-pool14.5 KB、refinement-packet13.8 KB、divergence-panel12.2 KB、rectification-agentic-chat10.1 KB、rectification-board6.6 KB(minified,实测)。
- 建议改法
- 换成
next/dynamic(() => import(...), { ssr: false }),并加一个preloadRectificationSurface()。 - 三处调用它:
openRectificationSession开始请求时;bootstrap 落地会话是校正型、或 URL 指向校正时,在揭幕前 await;校正 offer 卡出现时。 useRectificationSurface、consent、composer guard 等首页本身要用的 hook 留在原处。
- 换成
- 验收标准
- 首屏 gzip-9 降 ≥ 40 KB,
/仍○ Static。 - 进入校正与刷新校正 URL 后,揭幕后不出现 spinner 或空白帧。
docs/testing里的校正走查清单不变。- 若有源码文本测试在锁
page.tsx的 import 写法,按三栏改。
- 首屏 gzip-9 降 ≥ 40 KB,
- 风险:
page.tsx不得增长,dynamic加预取放进lib/新文件。须核对 Python 源码合同测试(tests/test_daily_and_rectification_entrypoints.py等)有没有锁这一行。
F3 | 回答流式期间,整个 Home 和右侧本命盘面板每帧重渲染
- 位置
src/app/(app)/page.tsx:Home,第 247 和 265 行:useSessionStore(useShallow(...))订阅了streamingReply。src/lib/stream-frame-buffer.ts:每个 rAF 提交一次setStreamingReply。src/components/chat-chart-frame.tsx:ChatChartFrame:children每次都是新的,Split没有 memo。src/components/chat-natal-chart.tsx:PanelBoard,以及ChartBoardCard、ChartInspector、ChartGrahaCard、ChartTags,全都没有 memo。
- 问题
- 流式文本每帧换一个新对象,Home(一个 1,235 行的函数)连同里面十来个自定义 hook 整体重跑。随之重渲染的有:顶栏、
ChatProfilePicker、CreditBalanceButton、ChatComposer+ModelSelector(toolbar 每次是新元素)、OnboardingHomeBridge、DailyStarlanguageBinder、AccountDialogOverlay,以及每次重建的accountOverlayModel。 - 桌面端本命盘面板默认打开(#114 Q8 A)。它挂在
ChatChartFrame下,于是useChartBoard、useTransitAt和整张星盘 SVG 跟着每帧重画。 - BUG-1329 只解决了侧栏重注册;
ChatTranscript和SettledMessageList已经 memo,历史消息不受影响。 - 同类问题:新手引导打字机
setPresetMessageLength每 26 ms 一次(第 929 行),也落在 Home 顶层,约 38 次/秒整页重渲染。
- 流式文本每帧换一个新对象,Home(一个 1,235 行的函数)连同里面十来个自定义 hook 整体重跑。随之重渲染的有:顶栏、
- 量化
- 每秒最多 60 次 Home 提交。按 DESIGN 放字节奏 240 字/秒推算,一条 1,500 字的回答约 375 帧,即 Home 375 次、星盘面板 375 次重渲染(估)。
- 测法:仿
tests/home-sidebar-rerender.test.tsx,用<Profiler>包住ChatChartSplit(或PanelBoard)和ChatComposer,推 60 帧setStreamingReply,数 commit 次数和actualDuration。在桌面 Chrome 上用 React DevTools Profiler 录一次长回答,可以直接看到 PanelBoard 的每帧耗时。
- 建议改法(一张小单)
ChatChartSplit用memo包住。它的 props 是 zustand 的稳定 setter 加上 open/width/subjectId,包一层即可隔离。- 新建
components/chat-streaming-transcript.tsx:它自己用 selector 订阅streamingReply(按 activeSessionId 过滤),再把流式字段喂给ChatTranscript。Home 只订阅派生的布尔值,例如hasStreamingText,供停止按钮文案用。 - 打字机长度下放到
HomeOnboardingShellSlot内部。
- 验收标准
- Profiler 测试:60 帧内 PanelBoard 提交 0 次、ChatComposer 提交 0 次、Home 提交 ≤ 2 次(开始和结束)。
- 正文逐字一致,停止按钮文案切换不变。
page.tsx行数不增。
- 风险:停止按钮文案依赖「已有正文」,派生布尔要准确。
useConversationScrollAnchor跟随底部依赖内容高度变化,不依赖 Home 重渲染,需要回归它的测试。
F4 | 首页首屏静态带着 Markdown 渲染全家(react-markdown + remark-gfm)
- 位置:
src/components/chat-markdown-view.tsx,经chat-message-content→ChatTranscript;校正面也经同一组件。 - 问题:空白首页(登录后、裸
/的默认落点,BUG-1052)没有任何消息,却要下载和解析 micromark、mdast、hast 这一整套。 - 量化:块
1stba0waqt7fg.js实测 42,332 B gzip-9,minified 141 KB,几乎全是 markdown 依赖。约占首屏 6%。 - 建议改法
ChatMarkdownView内部按需import("react-markdown")和remark-gfm,并提供preloadChatMarkdown()。- 三处调用它:bootstrap 落地会话有消息时,在揭幕前 await;用户按发送的那一帧(回答首字通常要几秒);校正面预取(F2)时。
- 未加载完成前用现有的纯文本段落渲染(
chat-message-paragraphs)。按上面的预取,正常路径碰不到这个分支。
- 验收标准
- 首屏 gzip-9 降 ≥ 40 KB。
?c=打开带消息的会话,揭幕时 Markdown 已渲染(不先出纯文本再跳格式)。- 流式首帧照常。
- 风险:揭幕时序。必须在 bootstrap 里 await,否则会违反「一次揭幕」。
F5 | 首屏几件「点了才用」的小依赖(合计约 27 KB gzip)
-
位置与问题
依赖 位置 体积(minified,实测) 实际用途 date-fns 的 parse/format 及整套 en-US locale 解析器 src/lib/birth-time-intake-model.ts:1,parseBirthDate、formatBirthDate45.4 KB,约 12 KB gzip(估) 只为 yyyy-MM-dd一个格式better-auth 客户端(better-fetch、nanostores) src/hooks/use-profile-onboarding.ts:63、src/components/account-deletion-gate.tsx:1831.7 KB,约 10 KB gzip(估) 只为点「退出」时的 signOut()consultation-report-export→report-public-projectionsrc/hooks/use-shell-session-actions.ts:8(外壳,所有页面首屏都有)15.3 KB,约 4.5 KB gzip(估) 只为点「分享」 另外
page.tsx:120那一行import { consultationReportMarkdown }在 Home 里是未使用的 import。 -
建议改法
- 用十来行手写
yyyy-MM-dd解析替换 date-fns:正则、new Date(y, m-1, d)、三段回写校验,保留年份上下限。日历组件里的 date-fns 随懒加载的 react-day-picker 走,不受影响。 signOut改成点击时await import("@/modules/identity/client")。shareTranscript改成点击时动态 import,并删掉 page.tsx 里未使用的 import。
- 用十来行手写
-
验收标准
- 首屏 gzip-9 降 ≥ 20 KB,所有次级页首载同降约 15 KB(外壳的两件)。
parseBirthDate现有测试全过,补上 2 月 30 日、0000 年、前导空格三条用例。- 退出和分享行为不变。
-
风险:低。手写解析必须与 date-fns 的严格回写校验等价,测试先在旧实现上跑一遍对齐。
F6 | 次级页冷开:bundle → /api/account → 业务接口,三段串行
- 位置
src/hooks/use-chart-page.ts,第 76 行的 effect 以subjectReady为门。src/lib/current-subject.ts:catalogReady要等/api/account绑定账户之后才为真。src/lib/boot-early-reads.ts:bootEarlyReadsScript只在pathname === "/"时发早读。/ephemeris、/reports同样形状。
- 问题:刷新或直接打开
/chart,要等 JS 执行完才发 account 和 chart-profiles,再等它们返回才发/api/chart-view(重计算)。HTML 阶段就能知道的读(account、chart-profiles、按本机记住的人物猜的 chart-view 或 ephemeris)全部推迟到 bundle 执行之后。 - 量化:串行段 =
/chart首载 579 KB JS 的下载与执行 + 1 个 RTT(account)+ chart-view 服务端耗时(估)。测法:在 staging 用 Chrome 冷开/chart,看 Network 瀑布里/api/chart-view的开始时间相对/api/account的结束时间。 - 建议改法:把早读脚本扩到
/chart、/ephemeris、/reports。account、chart-profiles 两条与首页共用;再按本机记住的人物猜一条页面主读。useChartPage首读用takeBootRead接过它;人物猜错就丢弃重读,与 BUG-1127 的会话猜测同一套红线。做完 F1 之后再做,这样 bundle 那段也变短。 - 验收标准
- 冷开
/chart:/api/chart-view的请求开始时间早于/api/account的响应结束时间(瀑布截图)。 - 换人或换账户后不显示别人的盘(沿用 BUG-1127 红线 3、8 的测试形状)。
- 冷开
- 风险:中。猜错人物会白算一次 chart-view,占重计算名额(
JYOTISH_HEAVY_COMPUTE_CONCURRENCY=2)。可以限定只在本机记住的人物 = self 时才猜。
F7 | 打开 ?c=<id> 时,会话详情要排在第二轮
- 位置:
src/lib/home-bootstrap-run.ts:resolveLanding,第 436 行的fetchSessionDetail;src/lib/home-cloud-sync.ts:resolveLookupBootstrap,第 576 行。 - 问题:分享链接、侧栏历史、在对话里刷新,都走
?c=。会话 id 在 HTML 阶段就已知,但GET /api/sessions/:id要等账户、会话列表、模型目录、active status 全部返回之后才发,于是揭幕多等一轮。id 不在第一页列表里时,走的是lookupSessionById,同一个 URL,同样排在第二轮。 - 量化:揭幕多 1 个 RTT + 会话详情的服务端耗时(估,手机网络约 150–400 ms)。测法:staging 冷开
/?c=<自己的会话>,看瀑布里/api/sessions/<id>的开始时间是否晚于/api/sessions?limit=的结束时间。 - 建议改法
- 早读脚本在
?c=存在时多发一条/api/sessions/<id>,键名为sessionDetail,与 id 绑定。 fetchSessionDetail和lookupSessionById先takeBootRead,没有再自己发。- 401 和 404 的处理照旧。
- 早读脚本在
- 验收标准
- 瀑布里会话详情与 account 同一轮发出。
- 打开别人的或已删除的会话,提示不变(SESSION_MISSING / LOOKUP_FAILED 两条文案)。
- 现有 home-bootstrap 测试全过。
- 风险:低。早读结果必须与 id 和账户绑定,账户变化时丢弃(同 BUG-1040)。
F8 | 档案页、合盘页各自重拉一遍外壳已有的数据,合盘页还是两轮串行
- 位置
src/components/people/people-page.tsx:load,第 126 行:fetchAccount()+fetch("/api/chart-profiles")。src/components/synastry/synastry-picker.tsx:load,第 68 行先await /api/product-access,第 90 行再fetchAccount()+/api/chart-profiles。- 外壳已经由
session-list-context.loadSessionList和current-subject.loadSubjectCatalog拿到同样的两份数据。
- 问题
- 同一份数据有两套来源:外壳 store 和页面自己的 state。每次进页都多 2 个请求,从侧栏点进来(外壳数据早已就绪)也一样。
- 合盘页 product-access → account 和 profiles 是 2 轮串行,其实三者互不依赖。
- 档案页在本页改人以后,还要自己
bindCurrentSubjectAccount去同步外壳。
- 量化:每次进入
/people或/synastry多 2 个请求(实测,代码路径)。合盘页揭幕多 1 个 RTT(估)。 - 建议改法
- 合盘页把三条请求改成
Promise.all。 - 两页的 account 改读
useSessionList().account,档案列表改读peekSubjectCatalog()/loadSubjectCatalog()。需要新鲜数据时调invalidateSubjectCatalog()再读,不再各自 fetch。
- 合盘页把三条请求改成
- 验收标准
- 从侧栏进
/people、/synastry:Network 里没有新的/api/account、/api/chart-profiles(外壳已就绪时)。 - 合盘页的三条请求同时发出。
- 改人、加人、删人后,侧栏人物切换器与档案页一致。
- 从侧栏进
- 风险:中。档案页的排序字段
archive_order要确认外壳 catalog 也带着;没带就先补进 catalog,不要另起一套。
F9 | Motion(约 85 KB gzip)在每个页面挂载后立刻下载,与首屏接口抢带宽
- 位置:
src/components/shell-motion.tsx:useMotionReact,第 18 行,在useEffect里直接loadMotionReact()。调用方是侧栏的SidebarCurrentHighlight,每个页面都有。 - 问题:#104~#111 规定 Motion 按需加载。首屏里确实没有它,但侧栏的当前项高亮在挂载那一刻就拉下 motion-dom 和 framer-motion。首页揭幕前的加载屏期间,它与 account、sessions、models 早读和会话详情同时下载。
- 量化:
33k991bwerupc.js59,466 B +2l8aqrmhvdn58.js22,010 B +076a3yxomi2av.js3,417 B,gzip-9 合计约 84.9 KB(实测块体积;挂载即下载是读代码得出)。 - 建议改法:
useMotionReact改为等document.documentElement.dataset.hydrated === "1"(首页揭幕标记)或requestIdleCallback之后再加载。次级页用 idle。滑动高亮在 Motion 到手前本来就有静态降级。 - 验收标准
- 首页冷开的瀑布里,motion 块在揭幕之后才开始下载。
- 侧栏高亮第一次切页仍会滑动(切页通常晚于 idle)。
- 减少动态效果设置照旧生效。
- 风险:低。
F10 | page.tsx 与大 hook 的状态纠缠(结构债,影响后续每一单的改动半径)
- 位置:
src/app/(app)/page.tsx:Home,第 207–1441 行,单函数 1,235 行。 - 问题(实测计数)
- Home 内有 27 个
useState、21 个useEffect、38 个useRef。 useConsultationRun({...})传入 54 个参数(第 754–809 行),useHomeShellRegistration25 个,useProfileOnboarding10 个。- 有 11 个 ref 在 Home 里只出现 2 次(声明一次、传给 hook 一次),本质上是那个 hook 的私有状态:
cancellationRequests、cancellationFeedbackRequest、stoppedRequestAwaitingSettlement、stoppedSessionPersistence、modelPersistence、modelSyncFailures、modelSelectionVersions、sessionDetailInFlight、accountRefreshGuard、birthTimeRevisionPendingRef、onboardingPaywallShown。 - 根源是 AGENTS §6 的「参数式 hook 内部保持 0 个 React hook」,状态只能放在 Home 再层层传进去。
- Home 内有 27 个
- 量化:见上面的计数。
useConsultationRun本身也有 1,147 行,其中send713 行。 - 建议改法(不碰 §6 红线的一张小单)
- 在
lib/新建createConsultationRunRefs(),返回一个普通对象,装上面 11 个可变容器。Home 只用一个useRef(createConsultationRunRefs()),传一次。参数从 54 降到 44 左右,行为零变化。 - 若要更进一步让 hook 自己持有 state,就要在任务书决策记录里明确推翻 §6 那一条,另立单。
- 在
- 验收标准
page.tsx行数减少;useConsultationRun参数 ≤ 45。- 全量前端测试名单与基线一致(frontend/AGENTS.md 的名单比对法)。
- tsc 和 lint 0 error。
- 风险:低到中。源码文本测试可能锁着 ref 名字,要用
git grep -- tests/ frontend/全量核对。
F11 | 同一件事好几套实现
| 编号 | 两套(或更多)实现 | 位置 | 影响 | 合并方向 |
|---|---|---|---|---|
| F11a | 会话「加载更多」 | use-session-management.ts:loadMoreSessions(369 行起,带 modelCatalog、cloudCreatedIds、代际号)与 use-shell-session-actions.ts:loadMoreSessions(140 行起,不带这三样)。外壳通过 hooks?.onLoadMore 二选一 |
首页内外行为不一致:外壳版用 readSessions(..., null),没有模型回落;修 bug 要改两处 |
只留首页版的逻辑,挪进 lib/session-page-load.ts,两边调用 |
| F11b | 读单个会话 | home-cloud-sync.ts:fetchSessionDetail(525 行)与 lookupSessionById(548 行),同一个 URL、同样的解析,只差 404 的处理 |
小;F7 的早读要两处都接 | 合成一个返回 found/missing/unavailable 的函数 |
| F11c | 北印度式星盘 SVG | personal-report/vedic-chart-svg.tsx(411 行;报告、校正棋盘、星盘页骨架、chart-vedic-tab 在用)与 chart-board/chart-board-svg.tsx(373 行;星盘页、聊天面板在用),共用 north-indian-chart-geometry |
视觉改动要做两遍,两套容易走样 | 大单,不建议现在做;先在 DESIGN 里写明哪一套是权威 |
| F11d | 账户与人物列表来源 | 外壳 store 与 people / synastry 页自取 | 见 F8 | 见 F8 |
| F11e | onboarding-run.ts |
既在首屏块(经 onboarding-progress-ring),又整份复制进懒块 21f3q(9.5 KB minified) |
约 3 KB gzip(估) | playRing 改动态 import,或让懒块直接复用 |
F12 | 只有测试引用、或没人引用的模块(死代码残余)
可达性脚本从全部 app 入口(page、layout、route、error 等 150 个)和 frontend/scripts/ 出发,计入动态 import。不可达的文件有 23 个、959 行(实测)。其中真正值得删的:
- 只被测试引用
lib/birth-time-choice-question-model.ts(50 行)lib/birth-time-guided-terminal.ts(29 行)lib/birth-time-guided-turn-identity.ts(3 行)stores/people-store.ts、stores/account-store.ts(各 12 行;只有ui-kit-stores.test.ts用)lib/motion/provider.tsx(16 行,静态 importmotion/react。将来谁一引用,Motion 就回到首屏)
- 无人引用
components/chart-page/chart-basics-tab.tsx(20 行,#114 改版后遗留)components/error-toast.tsx(17 行)- shadcn 生成后从未使用的
ui/field、avatar、empty、switch、kbd、separator、skeleton(共 543 行)。skeleton与 DESIGN §9 的「揭幕后不得出现骨架」冲突,留着容易被误用。
- 已知保留,dead-code 进度记录里写过理由,不再提:
reader-appendix-language.ts、personal-report-longform-download.ts。 - 建议一张小单删除,连同对应测试。删前用
git grep -n <名> -- tests/ frontend/核对。 - 验收:可达性脚本不可达文件数 ≤ 2;测试名单只少被删模块的用例,并在三栏表写明。
- 风险:低。
F13 | 首屏 CSS:整站样式一次性阻塞渲染(低优先)
- 位置:
src/app/globals.css(7,000 行,块3u1r1j1aer98v.css实测 57,259 B gzip-9);src/app/fonts/serif-sc/serif-sc.css(30 个@font-face,66 KB 原文,实测 22,547 B gzip-9),两者都由site-styles.ts在每个页面引入。 - 问题
- globals 里按类名前缀粗分,报告、合盘、档案、星历四类规则约 82 KB 原文(约 25%),首页用不到,但都是阻塞渲染的 CSS。
- 宋体切片的
unicode-range逐字列出码位,单这份声明就占 22.5 KB gzip。
- 量化:首页可省约 12–15 KB gzip CSS(估)。宋体声明若按码位连续段重新分片,可省 10–15 KB(估;会改变每片命中率,BUG-1128 要重新评估)。
- 建议:先做 globals 拆分:
personal-report-*、synastry-*、people-*、ephemeris-*四段挪到各自路由组件 import 的 css。宋体不动,记入 BLOCKED 或进度记录备查。 - 验收:
/首屏 CSS gzip 降 ≥ 10 KB;四个页面在docs/testing的截图对照无差。 - 风险:中。CSS 顺序与优先级可能变化,必须逐页截图对照。
二、超长文件 / 函数(前 10,实测)
文件(frontend/src,行数)
| # | 文件 | 行数 | 说明 |
|---|---|---|---|
| 1 | lib/personal-report-generation.ts |
3,740 | 服务端 |
| 2 | lib/rectification-agentic/v9/method-followup.ts |
3,574 | |
| 3 | lib/rectification-agentic/v9/answer-choice.ts |
2,641 | |
| 4 | mastra/rectification-v9-tools.ts |
2,638 | |
| 5 | lib/rectification-agentic/v9/tool-service.ts |
2,169 | |
| 6 | app/api/consult/route.ts |
1,855 | |
| 7 | mastra/consultation-tools.ts |
1,777 | |
| 8 | app/(app)/page.tsx |
1,441 | 客户端,冻结中 |
| 9 | lib/rectification-agentic/v9/collection-question-pool.ts |
1,404 | 客户端首屏,见 F2 |
| 10 | hooks/use-consultation-run.ts |
1,308 | 客户端 |
另:globals.css 7,000 行。
函数(TypeScript AST,按函数体行数)
| # | 函数 | 位置 | 行数 | 说明 |
|---|---|---|---|---|
| 1 | createRectificationV9Tools |
mastra/rectification-v9-tools.ts:1003 |
1,596 | |
| 2 | POST |
app/api/consult/route.ts:277 |
1,579 | 内含 runTurn 1,326 行、runAgenticConsultation 621 行;10-13 体检的 consult-route-split 单仍是待领取 |
| 3 | Home |
app/(app)/page.tsx:207 |
1,235 | |
| 4 | useConsultationRun |
hooks/use-consultation-run.ts:162 |
1,147 | 内含 send 713 行 |
| 5 | buildMethodFollowupPlan |
lib/rectification-agentic/v9/method-followup.ts:2286 |
1,025 | |
| 6 | streamAgentResponse |
lib/stream-agent-response.ts:505 |
704 | |
| 7 | EmailOtpLogin |
components/email-otp-login.tsx:87 |
624 | 客户端组件 |
| 8 | streamV9Attempt |
lib/rectification-agentic/v9/agent-run-attempt.ts:79 |
621 | |
| 9 | RectificationAgenticChat |
components/rectification-agentic-chat.tsx:113 |
617 | |
| 10 | useSessionManagement |
hooks/use-session-management.ts:97 |
512 |
本轮可执行的只有 F10(Home 和 useConsultationRun 的参数与 ref 收拢),其余服务端大函数属于另一半体检的范围。
三、已解决、不用再提(本次核对到的状态)
| 事项 | 状态与证据 |
|---|---|
| 侧栏每帧重注册(BUG-725/1329,#59) | 已合入 staging(635094315)。注册函数稳定,60 帧注册 0 次。F3 是另一件事:Home 本体和星盘面板的每帧重渲染。 |
| 前端死代码(BUG-1342,#63) | 已合入(1f9947a0d、3e20c125b)。删掉 17 个文件约 3,000 行和 3 个无调用方接口。F12 只是残余的 23 个小文件。 |
| 首页首屏串行与缓存(BUG-1127~1130) | 已合入。早读改 async data: 脚本(c2265dabb),宋体切片永久缓存(8112f62b5),加载屏改黑体(80ea7b0bc),城市表、holdout、引导壳移出首页首屏(b743b16b0)。本次核对 / 首屏确实没有城市表;F1 是同一问题出现在 /chart、/people。 |
| shadcn / Motion / dnd-kit / resizable 迁移(#104~#111) | 已合入。首屏里 motion-dom、framer-motion、@dnd-kit、react-day-picker、drawer、dropdown 都在懒块(实测,均不在 index.html 引用的块里);react-resizable-panels 不可达。首屏 694,941 → 702,985(+1.16%)在 ±2% 内。F9 只说 Motion 懒块的下载时机。 |
| 本地 PG 适配层每查询往返与现查表结构(BUG-1327/1328,#58) | 已合入(4c3485bc2)。local-postgres-client-core.ts:columnTypes 已带 metadataCache。 |
| 前端 SupabaseClient 假类型(#71,BUG-1351) | 第一阶段已合入(1de3cc307,lib/db/data-client.ts)。代码里仍有 212 处 SupabaseClient 字样,属于 #71 后续阶段,不另提。 |
| 接口定义合并(#72) | 第一阶段已合入(5d452a4ff)。 |
| 源码正则测试约 105 个文件 | 已在 10-13 体检里立项,本轮不重复。F2、F5、F10 动手前须按 frontend/AGENTS.md 全量 grep。 |
| 对话历史消息的 memo | ChatTranscript、SettledMessageList、HistoryMessageEntry、ChatMessageRow 已 memo,历史消息不随流式重渲染。 |
四、建议顺序(都是独立小单)
- F1:/chart、/people 降约 100 KB,改动最小。
- F3:流式卡顿。
- F2 + F4:首屏约 −90 KB。两者都改首页揭幕时序,串行做,先 F2。
- F5:约 −27 KB,零风险。
- F7 → F6:接口早读,F6 排在 F1 之后。
- F8、F9。
- F10、F11a、F12:清理。
- F13 最后,或只记账不做。
预计 F2 + F4 + F5 做完后首页首屏 gzip-9 约 595 KB(估,−15%)。F1 做完后 /chart 约 471 KB(估,−19%)。