Files
Jyotisha/docs/tasks/PROGRESS-mobile-chart-confirmed-edit-20260929.md
T
Jesse_ChenandClaude Opus 5.5 f51c830b52
Independent Staging Quality Gate / validate (push) Successful in 14m35s
Independent Staging Quality Gate / publish (push) Successful in 3m45s
docs(tasks): Claude acceptance for mobile chart, confirmed edits, sky replay, report cover removal
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
2026-09-29 08:48:41 +08:00

19 KiB
Raw Blame History

PROGRESS · 手机星盘 / 确认时间可改 / 天空定格 / 去掉报告封面(2026-09-29)

任务书:docs/tasks/TASK-mobile-chart-and-confirmed-edit-20260929.md(M1–M4 在 53c0b9ea,M5 在 47eda7e1 追加)。产品授权直接执行(Claude 子代理)。

  • 基线:origin/staging = 53c0b9ea(开工时;其后 47eda7e1 只加了任务书 M5 段,纯文档,分支未 rebase)
  • 分支 / 工作树:codex/mobile-chart-confirmed-edit-20260929 / .worktrees/mobile-chart-confirmed-edit-20260929,未推送
  • 环境:Node v22.14.0(/exec-daemon/node);系统 python3;无 Docker;无头 Chrome 151(--disable-dev-shm-usage,CDP 设备模拟)
  • BUG 编号:开工时最大 BUG-1082;本单 M1 = BUG-1083;M2 是 BUG-264 下的产品修订段,不另开号

结论

项 结果 提交
M1 手机星盘完整显示(BUG-1083) 完成 1
M2 已确认时间,改声明字段后重置 完成(数据库侧未跑 test:db,见环境缺口) 2
M4 「那一刻的天空」重放 → 定格 → 右上角分享 完成 3
M5 去掉报告星图封面 完成 4
M3 文档 完成(DESIGN、VOICE、CHANGELOG、BUG_HISTORY、BLOCKED、真机清单) 1–4 各自带

M1 · 根因与修复(BUG-1083)

复现方法:真实组件(ChartPageView / ChartVedicTab / AppSidebar / SidebarInset)+ golden 夹具(chart-view-golden.json、chart-view-bhava-golden.json)SSR 成静态页,套上本次构建产物里 /chart 引用的全部 CSS,无头 Chrome 用 CDP Emulation.setDeviceMetricsOverride 模拟 390×844(新版无头窗口最小 500 宽,--window-size 不够用),从盘面 SVG 往上逐层量 getBoundingClientRect().width。

修复前(基线 CSS,本命与 Bhava 相同):

层 宽度
svg.personal-report-chart-svg 1043
.chart-page-vedic-stage / -vedic-layout / .chart-page-vedic / .chart-page-panel 1069
.chart-page-body(第一个超宽祖先) 1101
.secondary-page 390
.secondary-panel / main.chat-app 390
  • 根因:.secondary-page 是 display: grid 且没有 grid-template-columns,隐式列是 auto;子项 .chart-page-body 保持默认 min-width: auto,这一列就按正文最小内容宽度定宽(不折行的 23 个盘型 chip、34rem 行星表)。内层表格与盘型条自己的 min-width: 0 + 横滚都在,撑宽发生在它们外面。面板 overflow-x: hidden 把溢出直接裁掉,所以看起来是「盘很大、右边没了」。
  • 修复:.secondary-page { grid-template-columns: minmax(0, 1fr); }。修在四个次级页共用的这一层。
  • 修复后(本次构建 CSS):390×844 下本命 / D9 / Bhava / 月亮 / D10 都是 documentElement.scrollWidth = 390、没有超宽祖先、盘面 332px。大运 / 基础信息 / 西洋盘三个 Tab 修复前就不溢出(让步 1:未另改)。
  • 桌面:1280×900 下修复前其实也被裁(.chart-page-vedic-layout 1069px 塞在约 1024px 的内容区,参数卡右边被切、「≡」看不到);修复后两栏布局不变,盘面 549 → 480px,参数卡与「≡」完整可见。
  • 回归测试:frontend/tests/chart-page-mobile-width-20260929.test.ts(2 条)。

截图(/tmp/claude-1000/-workspace-Jyotisha/fb88e01e-1ced-4bcc-92fe-44c5bdcc878e/scratchpad/mobilechart/):

  • 修复前:before-D1-390.png、before-BHAVA-390.png、before-D1-1280.png
  • 修复后(本次构建 CSS):after-D1-390.png、after-D9-390.png、after-BHAVA-390.png、after-MOON-390.png、after-D10-390.png、after-D1-1280.png
  • 桌面前后对比:cmp-1280.png

M2 · 结论

  • 改法:resolveAccountBirthTimeApplicationPatch 去掉「confirmed 直接 {}」。confirmed(含状态为空、有遗留分钟的 legacy 行)只有声明字段真的变了才重置,之后与未确认完全同一套派生:零误差家人时间 → accepted + 新分钟;否则 reported、active_birth_time = null;rectification_case_id = null。只改称呼、岁差、性别、头像 → {}。
  • 跨午夜三列(硬红线 3):
    • 写入方:采用 / 确认候选的 RPC(20260920020000_adopted_birth_date.sql 起的 dated-v1 路径);读取方:server-owned-birth-profile.ts(active_birth_timezone_offset 覆盖声明时区)、chart-birth-truth.ts(adoption = "adopted"、指纹)。
    • 数据库触发器 zz_guard_adopted_birth_date(BEFORE INSERT/UPDATE)在任一声明字段(日期、时间、来源、时段、范围、误差、经纬度、时区)变化或状态不在 accepted/confirmed 时把三列置空;前端镜像是 applyAdoptedBirthDateGuard。
    • 本次仍在派生补丁里显式写三列为 null(仅当当前行有值),不只依赖触发器。
  • 遗留列 birth_time:chart-birth-truth.ts 与 server-owned-birth-profile.ts 在 active_birth_time 为空时会回落读 birth_time。confirmed / legacy 重置成「只有时段」时,若不清它,星盘仍会按旧分钟排。所以当前行 birth_time 非空时一并写 null。
  • 触发器会不会改回 confirmed:不会。
    • guard_birth_time_journey 最新版(20260804020000)只在「状态为空且有 active 分钟」时补 confirmed,本修订总写明确状态。
    • agentic_rectification_profiles_rebaseline_guard 与 invalidate_agentic_rectification_results_on_profile_change(最新版 20260827010000)在声明变化时把进行中的校正 Case 标成 needs_rebaseline、把结果标 invalidated_at,这是期望行为。
    • 没有改触发器、没有加迁移。
  • 并发守卫:派生补丁非空时,路由照旧用 applyAccountProfileConcurrencyGuards 按当前行做条件更新,confirmed 行现在也会走到这条,能挡住与一次并发采用的竞争。
  • 客户端:/people 保存本人走 saveSelfProfile → applyPersistedBirthTime,用路由返回的 status / activeTime(取自写回的行);新增单测证明「确认 07:52 → 改 08:05」后本地是 accepted / 08:05,改成时段后是 reported / 空分钟。缓存失效沿用 BUG-1081 那一套。
  • 偏离(请 Claude 定):声明比较由 !== 改成按值比较(sameDeclarationValue:库里 HH:MM:SS 与表单 HH:MM、数字与数字字符串、空串与 null 视为相同)。原因:/people 每次保存都整表重提全部出生字段,库里的 time 列回来是 HH:MM:SS,原来的 !== 会把「只改称呼」判成新声明,于是本单改完后会把已确认分钟清掉。副作用:未确认状态(例如 accepted)下「只改称呼」以前也会被误判并退回 reported,现在不会了——这是修正,不是新行为。

M4 · 完成情况

  • 纯函数:birthSkyRevealAt(elapsed, reduced, mode),新增 mode = "hold":汇聚与文字与首页过场完全相同,文字淡入完(3.6 s;减少动态效果时 0.6 s)就 held,此后永远是定格帧、layer = 1、done = false。birthSkyHoldAtMs(reduced) 给出定格时刻。首页过场(reveal)时间轴不变。
  • 组件:BirthSkyConverge 加 mode 与 controls 两个可选参数(默认 reveal、无控件,首页过场行为不变)。hold 模式:
    • 定格后停止 requestAnimationFrame;resize 会清空画布,所以定格后 resize 时重画一次;
    • 点画面(不在控件上)直接跳到定格帧,不关闭;
    • Esc 关闭;卸载时取消动画。
  • 新组件 components/birth-sky/birth-sky-replay.tsx:左上 ×(读屏名「关闭」),右上「分享」(saveBirthSkyImage(cover.blob):能分享文件就调系统分享,不能就下载;用户取消不提示;其他失败提示「没能分享,再试一次。」)。样式在 birth-sky-converge.css,44px,顶部让出安全区。入口 birth-sky-entry.tsx 懒加载它,每次打开都重放。
  • 定格帧 = 海报:定格帧画布调用序列与导出 PNG 逐条相同(单测,三个 phase);并且 BirthSkyCover 新增 serif(render.ts 新增 paintBirthSkyPng 同时返回 PNG 与宋体判定),重放用画 PNG 时同一个字体判定,避免「PNG 用黑体、屏幕用宋体」。
  • 删除:静态海报弹层 birth-sky-dialog.tsx 与 birth-sky.css(不再有引用)。
  • 无头 Chrome 实测(真实组件 esbuild 打包 + golden 夜盘 + 「中国 · 浙江省 · 杭州市 · 西湖区」标签),390×844 与 1280×900 都通过:
    • 1.5 s 在播放;
    • 5 s 已定格、不透明;
    • 8 s 仍打开;
    • 0.7 s 时点画面 → 跳到定格、仍打开;
    • Esc → 关闭。 截图:replay-390-playing.png、replay-390-held.png、replay-390-tapped.png、replay-1280-playing.png、replay-1280-held.png、replay-1280-tapped.png、replay-390-strip.png(同一目录)。
  • 「分享」按钮「PNG 未就绪时禁用」:没有实现禁用态。星盘页只在 PNG 画好后才渲染入口按钮,重放层打开时 PNG 一定已经在手,这一状态不存在(偏离,见下)。

M5 · 完成情况

  • 删除:components/birth-sky/report-sky-cover.tsx / .css、lib/birth-sky/report-cover.ts、personal-report-page.tsx 的动态导入与挂载点(连同只给它用的 next/dynamic import)、classifyReportEnvelope 的 coverSubject 解析、ReportLoadState / ReportEnvelopeView 上的字段。
  • 服务端:reportView 的 coverSubject 参数、readyReportCoverSubject、ReportReadCoreDeps.loadCurrentBirthFingerprint、详情路由里的 loadCurrentBirthFingerprint(以及随之无用的 subjectBirthFingerprint / loadSubjectBirth import)全部删除;读报告不再多查一次人物资料。
  • lib/report-birth-binding.ts 只保留 F1 写入用到的:REPORT_BIRTH_FINGERPRINT_KEY、subjectBirthFingerprint、stampReportBirthFingerprint、readReportBirthFingerprint、withReportBirthFingerprint;删掉 reportDocumentBirthFingerprint、reportSubjectId、reportCoverSubject。
  • 保留:三条生成路径写 subject.birthFingerprint、TS / JSON Schema / Python 三处 schema 可选字段及其测试。
  • grep -rn "ReportSkyCover\|report-sky-cover\|coverSubject" frontend/src = 0。
  • BLOCKED.md T5 保持划掉,追加「→ 2026-09-29 产品决定去掉报告封面」。

验证

项 基线 53c0b9ea 交付
tsc --noEmit 0 错 0 错
npm run lint 0 error / 126 warning 0 error / 126 warning
npm test tests 4335 · pass 4280 · fail 24 · cancelled 0 · skipped 31 tests 4335 · pass 4280 · fail 24 · cancelled 0 · skipped 31
失败清单 24 条(Docker / PostgreSQL / 部署) 与基线逐条同名
测试名对比(同机同命令跑基线) — 消失 15、新增 15(下表逐条)
run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime(门禁同参数) — 997 passed / 1 skipped,Quality gate passed.,退出 0
commercial_privacy_artifact_scan.py --json — status: pass,finding_count: 0
pytest test_repo_privacy_markers + test_birth_sky + test_api_server_growth_contract + test_personal_report_contract — 126 passed
rm -rf .next && next build / ○ Static / ○ Static
page.tsx / jyotish_api_server.py — 未改

首屏 gzip(index.html 引用的全部 /_next/static/*.js|css 逐个 gzip -9 求和;同一工作树先在基线代码上全量构建一次,再在交付代码上全量构建)

基线 交付 变化
JS 651,520 B 651,512 B −8 B(−0.001%)
CSS 66,031 B 66,035 B +4 B(+0.006%)

消失的测试名(15 条,§7.3)

消失的测试名 去向 / 原因
ordinary declaration edits clear stale candidate application but never overwrite confirmed active time 改名为 “…and a new declaration drops a confirmed minute too”;产品 2026-09-29 推翻 BUG-264 约定(M2)
confirmed ordinary edits keep the active minute and clear a complete adopted tuple together 改名为 “confirmed declaration edits drop the minute and clear a complete adopted tuple together”;同上(M2)
chart page dialog shows only the picture: no title bar, no panel; icon × and 「保存图片」 由 “chart page replay stays on the poster: × top left, 「分享」 top right…” 取代;静态弹层删除(M4)
the chart page dialog no longer has a reveal variant (「继续」, is-reveal) 由 “the chart page replay has no 「继续」 and the poster dialog is gone” 取代;读的两个文件已删除(M4)
GET /api/reports/:id compares on the server through the chart page's subject loader 由 “GET /api/reports/:id no longer looks up the subject for a cover (M5)” 取代;产品决定去掉报告封面(M5)
coverSubject: only a matching fingerprint yields the subject id, self when chart_profile_id is null 删除:比对函数 reportCoverSubject 已删(M5)
core read: coverSubject is the subject id when the stored fingerprint matches the current one 删除;由 “core read: the detail response has no coverSubject, and never the fingerprint or birth fields” 取代(M5)
core read: coverSubject is null when the birth data changed since the report 同上(M5)
core read: coverSubject is null for a legacy report without the field, and nothing is looked up 同上(M5)
core read: coverSubject is null for a subject that is not the reader's (foreign, deleted, unreadable) 同上(M5)
matching fingerprint → the ready state carries coverSubject; mismatch or legacy → no cover 删除:report-sky-cover.test.ts 整个文件随组件删除(M5)
the cover sits above the body, lazy, and only when coverSubject is set 同上(M5)
loader paints night for the screen and paper for print with the same sky 同上(M5)
fetch failure, an empty sky or an abort → no cover, and nothing throws into the report 同上(M5)
print: paper picture alone on the first A4 page; the screen picture is hidden 同上(M5)

新增 15 条:M1 2 条(chart-page-mobile-width-20260929.test.ts);M2 4 条(两条改名 + “confirmed minute follows a new declaration and survives everything else” + “saving a new time over a confirmed minute takes the server's new status and minute”);M4 7 条(birth-sky-replay-20260929.test.ts 5 条 + 两条替代);M5 2 条(替代)。总数 4335 不变。

改动的既有断言(原值 / 新值 / 原因)

测试(文件) 原值 新值 原因
account-api.test.ts 第一条 confirmed + 新声明(两处) {} { active_birth_time: null, birth_time_status: "reported", rectification_case_id: null, birth_time: null } M2:新声明丢掉已确认分钟;夹具 birth_time 有值,所以一并清
account-api.test.ts confirmed / legacy + 新零误差家人时间 {} accepted + 05:40(legacy 另加 birth_time: null) M2
account-api.test.ts “PATCH answers…” confirmed / legacy + 改 district_code confirmed / 05:18 accepted / 05:00(夹具声明是零误差家人时间 05:00);另加两条「只改称呼 / 岁差 → 仍 confirmed 05:18」 M2:改地点属于新声明
account-api.test.ts confirmed + 只改岁差 {} 不变 ——
chart-profile-update-consistency.test.ts 跨午夜 confirmed + 新声明 {},排盘仍 confirmed 23:55(legacy) 回到 reported 并显式清三列,排盘按新声明 00:20(declared) M2
profile-onboarding-birth-sky-reveal.test.ts overlay layer.addEventListener("pointerdown", finish) onPointerDown = mode === "hold" ? skip : finish,默认 mode = "reveal" M4:定格模式点一下只跳到定格帧
chart-page-view.test.tsx 入口懒加载 import("./birth-sky-dialog") import("./birth-sky-replay") M4
chart-page-view.test.tsx 封面夹具 无 serif serif: false BirthSkyCover 新增字段(夹具形状,断言未变)
report-birth-binding.test.ts longform cover 另用 reportDocumentBirthFingerprint 读回 只看 subject.birthFingerprint M5:读回函数已删;F1 写入断言保留

改动的断言都在测试文件里就地写了三栏注释。

偏离任务书(请 Claude 定)

  1. M2 声明比较改成按值比较(见 M2 节)。不改的话,本单会让「只改称呼」清掉已确认分钟,违反决策记录第 1 条。
  2. M2 同时清遗留 birth_time(任务书只写了三列)。原因:图表在 active_birth_time 为空时会回落读它。
  3. M4 「分享」没有做禁用态:PNG 在入口按钮出现前已就绪,这个状态不存在。
  4. M4 失败文案改为「没能分享,再试一次。」,原「没能保存。长按或右键上面的图,也能存下来。」停用,因为画面是画布,不能长按存图。已收进 VOICE。
  5. M1 修在 .secondary-page(四个次级页共用),而不是只给 .chart-page-body 补 min-width: 0。桌面宽度下被裁的问题也随之消失。
  6. 首页过场结束后的提示「这片天空在星盘页可以保存。」未改;星盘页现在的按钮字是「分享」,保存要经分享面板或下载,句意仍成立。请 Claude 判断是否改句。
  7. 分支基于 53c0b9ea,未 rebase 到只加了文档的 47eda7e1(按协调要求);docs/tasks/README.md 索引行的状态未改,留给 Claude 交付时处理,避免冲突。

环境缺口

  • 无 Docker:
    • 24 条 DB / 部署套件失败,与基线逐条同名;
    • 没跑 npm run test:db,所以 M2 在真实 PostgreSQL 上的效果(触发器链、条件更新)只做了静态核对。staging 实测见真机清单第 5–7 条。
  • 无登录态与真机:M1 只在无头 Chrome 设备模拟上验证;M4 的系统分享面板、M5 的打印 PDF 没有实测。见 docs/testing/mobile-chart-confirmed-edit-20260929.md(18 条)。
  • 无模型凭据:本单不涉及模型输出。
  • 未推送、未部署。

Claude 验收(2026-09-29)

分支已 rebase 到 origin/staging 56722063(另一会话同期写入 BUG-1084~1089,BUG_HISTORY.md 冲突按编号顺序合并,BUG-1083 在前)。独立重跑:tsc 0 错;lint 0 error / 126 warning;npm test 4335 · pass 4280 · fail 24 · cancelled 0,失败清单与上一单逐条同名;门禁同参数 quick 门禁 997 passed、Quality gate passed.;隐私制品扫描 pass;rm -rf .next && next build / ○ Static;首屏 gzip 651,512 / 66,035(基线 651,520 / 66,031,−8 B / +4 B);src/ 下 grep ReportSkyCover|report-sky-cover|coverSubject = 0。

看过截图:before-BHAVA-390 复现产品真机现象;after-{BHAVA,D1}-390 盘面完整、表格在框内;replay-390-held 定格帧有 × 与「分享」。

M2 复核:最新 guard_birth_time_journey(20260804020000)已不在 birth_time 与 active_birth_time 间互相同步,因此同时清 birth_time 不会连带清掉新写入的 active_birth_time。偏离 1–7 接受;偏离 6(首页提示句「这片天空在星盘页可以保存。」)保留,待产品定。