# PROGRESS · 星盘档案本人编辑、各页头像菜单、我的报告去说明(2026-09-28) 任务书:`docs/tasks/TASK-self-edit-avatar-menu-20260928.md`。产品授权直接执行(Claude 子代理)。 - 基线:`origin/staging` = `adbb2054`(birth-sky-polish 已推 staging 后开工,串行) - 分支 / 工作树:`codex/self-edit-avatar-menu-20260928` / `.worktrees/self-edit-avatar-menu-20260928`(三个本地提交,**未推送**) - 环境:Node v22.14.0(`/exec-daemon/node`);系统 `python3`;无 Docker;无头 Chrome(本机 `/dev/shm` 只有 64MB,需 `--disable-dev-shm-usage`) - BUG 编号:开工时最大 BUG-1080;本单 BUG-1081 ## 结论 | 项 | 结果 | 提交 | | --- | --- | --- | | S1 本人可在星盘档案编辑(BUG-1081) | 完成 | 1 | | S2 各页头像弹账户菜单 | 完成 | 2 | | S3 我的报告去掉顶部说明区 | 完成 | 3 | | S4 文档(DESIGN、VOICE、CHANGELOG、BUG_HISTORY、真机清单、索引、本文件) | 完成(DESIGN / VOICE 随 S1、S2 各自提交,见偏离 1) | 1–3 | | 让步 | 未砍:只读模式余额照常显示;弹窗在首页 `hydrated && account` 后打开 | — | ## S1 · 本人可编辑(BUG-1081) - `lib/self-profile-save.ts`:`selfProfilePatchBody`(原 `persistProfile` 的载荷,带 `birth_time_source`,**不带** `gender`)、`saveSelfProfile`(`PATCH /api/account`,返回应用服务端出生时间真值后的资料与 `chartBirth.fingerprint`)、`saveSelfGender`(单字段 `{ gender }`)。 - 首页 `use-profile-onboarding.ts` 的 `persistProfile` 改用 `saveSelfProfile`(行为不变)。无 UI 调用的 `saveProfile` 删除;它独用的 hook 参数 `setBirthTimeConsultationConsent`、`setRectificationError` 从 hook 类型与 `page.tsx` 调用处一并移除(`page.tsx` 里这两个 state 仍被别处使用)。 - `people-page.tsx`:本人详情底部「本人不能删除」下面加「编辑」;编辑视图与他人同一张 `ChartProfileForm`,标题「编辑我的资料」、说明「称呼和出生资料。」,无删除区。`saveSelf`:出生资料 `saveSelfProfile(draft)`,性别变了再 `saveSelfGender`;然后 `pinChartSnapshotIdentity`(星盘缓存按新指纹失效)、`clearHomeWarmSnapshot`(首页暖快照与今日卡)、`invalidateEphemerisPage`、布局 provider `setAccount`(资料与 `chartBirth.fingerprint`)、`invalidateSubjectCatalog` 后重读列表。失败时表单里显示服务端文案,没有就「保存失败,请重试」。 - 编辑他人后原来只 `invalidateSubjectCatalog` + 重读(已核实);本人多出的几项是因为本人资料还活在布局 provider、首页暖快照和星盘 / 星历缓存里。 - 删掉详情「性别」下的小字「在「设置 → 个人资料」里改」和它的 CSS 规则 `.people-archive-fact-note`。 - `.people-archive-foot` 加 `gap`、`justify-items: center`(灰字与按钮上下排)。 ## S2 · 各页头像弹账户菜单 - `lib/account-dialog-intent.ts`:`/?account=profile|general|billing|logout` 的生成与解析,其他值一律忽略。 - `app-sidebar.tsx`:页脚在两种模式都是同一个 `Menu.Root`(全文件只有一个)。首页由 `controls` 控制开合,四项 `Menu.Item` 就地开弹窗;其他页菜单自持开合状态(`useState`,在 `AppSidebar` 里,不在 `Home()`),四项是 `Menu.LinkItem` + `AppLink` 到 `/?account=`,点了收起手机抽屉。主题菜单不变、就地生效。余额「N 点」来自布局 provider 的账户,只读模式照常显示。账户还没读到时页脚是同尺寸的静态块(无链接、无转圈);未登录仍是「去登录」。 - `people-home-links.tsx` 新增 `AccountDialogDeepLink`:挂载时读一次 `?account=`、立刻 `replaceState` 去掉(非法值也去掉),`ready` 后调用 `openAccountDialog`(billing 带 `source: "account-menu"`,与首页菜单一致),之后不再触发。`page.tsx` 只多一行接线(import 合在同一行)。 - `page.tsx`:1364 → 1363 行;`Home()` 的 `useState` / `useRef` 数不变(未新增)。 ## S3 · 我的报告 - 删 `.report-center-hero` 整段(说明段 + 统计行)与只给它用的 `latestReady`(`useMemo` import 随之移除);`hasGenerating` 仍给轮询用,保留。`globals.css` 删 `.report-center-hero*`、`.report-center-meta` 与移动端那条规则。生成按钮的 `title` 不动。 ## 截图(本地宿主页,虚构资料,交付构建的编译 CSS) - `/tmp/claude-1000/-workspace-Jyotisha/fb88e01e-1ced-4bcc-92fe-44c5bdcc878e/scratchpad/selfedit/people-menu-1280x800.png`(/people 本人详情,底部「本人不能删除」+「编辑」;侧栏账户菜单展开) - 同目录 `people-menu-390x844.png`(手机抽屉里展开账户菜单) - 两张图 CDP 读回:菜单四个弹窗项的 href 依次为 `/?account=profile|general|billing|logout`,无横向滚动。宿主页用 esbuild 打包真实组件,`next/navigation`、`next/link` 用本地 shim(未提交)。 ## 验证 | 项 | 基线 `adbb2054` | 交付(第 3 个提交) | | --- | --- | --- | | `tsc --noEmit` | 0 错 | 0 错 | | `npm run lint` | 0 error / 127 warning(polish 验收记录) | 0 error / 126 warning | | `npm test` | tests 4322 · pass 4267 · fail 24 · cancelled 0 · skipped 31 | tests 4334 · pass 4279 · fail 24 · cancelled 0 · skipped 31 | | 失败清单 | 24 条(Docker / PostgreSQL / 部署套件) | 与基线逐条同名 | | 测试名对比(`frontend/AGENTS.md` 方法) | — | 消失 1(改名,见下表)、新增 13 | | `python3 -m pytest tests/test_repo_privacy_markers.py` | — | 63 passed | | `rm -rf .next && next build` | `/` ○ Static | `/` ○ Static;路由表与基线逐行相同 | | `page.tsx` 行数 / `Home()` state·ref 数 | 1364 / — | 1363 / 不变 | 未跑 Python 快速门:本单没有改 `scripts/**` 或 `tests/**`(Python)。 ### 首屏 gzip(`index.html` 引用的 `/_next/static/*.js|css` 逐个 gzip -9 求和;基线在临时 detached 工作树同机同脚本干净构建) | | 基线 `adbb2054` | 交付 | 变化 | | --- | ---: | ---: | ---: | | JS | 650,972 B | 651,520 B | +548 B(+0.08%) | | CSS | 66,126 B | 66,031 B | −95 B(−0.14%) | JS 增量是首页 bundle 里的 `AccountDialogDeepLink`、`account-dialog-intent` 与侧栏只读菜单分支;CSS 减少是删掉的报告中心说明区与 `.people-archive-fact-note`。 ## 改动的既有断言(原值 / 新值 / 原因,测试文件里同样写了注释) | 测试 | 原值 | 新值 | 原因 | | --- | --- | --- | --- | | `sidebar-contract` · read-only when `/` is not mounting it | 只读页脚是 `` | 不再有页脚链接;全文件一个 `Menu.Root`,两种模式都渲染;只读弹窗项是 `AppLink` 到 `accountDialogHref(dialog)` | S2 产品决定 | | `home-warm-return-lifecycle` · warm return to bare / … | `followLink("林遥,打开账户")` | 断言页脚不再是该链接;用等价站内跳转 `followHref("/")`(记下目标再路由,与 `AppLink` 同步骤) | S2;测试守的是「暖回裸 `/` 落空白首页」 | | `new-chat-from-people-lifecycle` · stashed rectification … not reopened | 等并点只读页脚链接回 `/` | 等页脚菜单按钮出现、断言无该链接,`followHref("/")` 回 `/` | 同上 | | `new-chat-from-people-lifecycle` 测试工具 `button(label)` | 取第一个文本含 label 的按钮 | 跳过 `.profile-trigger`(页脚菜单按钮也含本人名字) | S2 后页脚是按钮;不改断言,只避免点错元素 | | `profile-gender-ui` · detail card(**改名**:`…本人 points to 设置 → 个人资料` → `…本人 edits it in the same form as everyone`) | 本人 `
未填在「设置 → 个人资料」里改
`,本页无本人性别单选 | 本人 `
未填
`;详情卡无单选,本人编辑视图有 | BUG-1081 | | `profile-gender-ui` · CSS 类清单 | 含 `people-archive-fact-note` | 去掉(类与规则已删) | BUG-1081 | | `settings-mvp-contract` · chart library owns self-profile editing | `onSubmit={(event) => void saveOther(event)}` | `editingSelf ? saveSelf(event) : saveOther(event)`,且 `await saveSelfProfile(draft)` | BUG-1081 | | `people-archive-regressions` · people delete … | `title={selectedId ? "编辑人物" : "添加一个人"}` | 前面多一档 `editingSelf ? "编辑我的资料"` | BUG-1081 | | `consultation-entrypoint` · profile and place saves do not auto-start … | 切出 `saveProfile` 断言不评估 | 断言首页无 `saveProfile`;切出 `/people` 的 `saveSelf` 断言不评估 | `saveProfile` 删除 | | `profile-onboarding-birth-sky-reveal` · only own onboarding place save triggers | 切出 `saveProfile` 断言不含 `onOwnBirthPlaceSaved` | 断言首页无 `saveProfile`(people-page 不触发由下方原循环锁住) | 同上 | | `birth-time-consultation-consent` · source-aware consultation options | `match(page, /birthTimeConsultationOptionsCopy\(savedProfile\)/)` | `doesNotMatch`;录入表单那条断言不动 | 这句只在已删的 `saveProfile` 保存提示里 | | `birth-time-guided-review-fixes` · terminal CJK copy … | `match` 「出生资料已保存。${…(savedProfile)}」 | `doesNotMatch`;其余断言不动 | 同上 | | `personal-report-entry` · global report copy … | 报告中心源码四条 `match`(分钟即可生成 / 需要具体分钟 / 与某一次对话无关 / 后台继续处理) | 四条 `doesNotMatch`,另断言无 `report-center-hero` / `report-center-meta` / 报告概览;按钮 `title` 断言不动 | S3 | 测试工具改动(非断言):`tests/home-surface.ts` 的首页源码清单加 `src/lib/self-profile-save.ts`(`persistProfile` 的载荷与 `applyPersistedBirthTime` 行搬到这里,`profile-persistence` 的原断言继续命中);两份生命周期测试新增 `followHref` 帮手。 ## 偏离任务书 1. **DESIGN / VOICE 随各自的 S1、S2 提交**,不全压在 S3+S4:AGENTS §7.5 要求改 UI 的提交同时更新 DESIGN。S4 提交里是 S3 的 DESIGN、CHANGELOG、真机清单、索引与本文件。 2. **首页 `saveProfile` 连带删掉两个 hook 参数**(`setBirthTimeConsultationConsent`、`setRectificationError`),它们只被 `saveProfile` 用;否则会留下未使用的参数。 3. **本人保存成功后不再显示「出生资料已保存。…」提示**:那句只在已无 UI 的 `saveProfile` 里;星盘档案保存后直接回到已更新的详情。若产品想要一句确认,另开单(详情卡现有的 notice 是错误样式,不宜复用)。 4. **本人编辑视图说明写「称呼和出生资料。」**,没写他人那句「填报的出生时间保存后不能再改」——本人资料走账户接口,可以再改。 ## 另发现(不在本单范围,未改) - **封面城市取不到**:库里中国地点标签以「中国 · 」开头(`lib/geoapify-location-service.ts` 生成 `中国 · 省 · 市 · 区`),而 polish 单的 `lib/birth-sky/caption.ts` `birthSkyCity` 要求第一段是省级名,遇到「中国」直接返回 null。所以中国出生地的封面实际只会印到时刻、不带城市。该代码已在 staging(`adbb2054`)。 - 账户接口在 `birth_time_status = confirmed` 时,声明字段变化不会重置状态(`resolveAccountBirthTimeApplicationPatch` 直接返回 `{}`),这是既有语义,本单未动;本人在星盘档案改时间后若原来是 confirmed,排盘仍按 confirmed 的时间。建议产品确认是否符合预期。 ## 环境缺口 - 无登录态与真机:本人编辑的真实保存、跳首页开弹窗、手机抽屉行为只在单测、源码合同与本地宿主页截图上验证;真机清单 `docs/testing/self-edit-avatar-menu-20260928.md`(17 条)。 - 无 Docker:24 条 DB / 部署套件失败与基线逐条同名。 - 未推送、未部署。 ## Claude 验收(2026-09-29,含 Claude 补修 `3d0f1cf6` BUG-1082) 独立重跑:`tsc` 0 错;lint 0 error / 126 warning;`npm test` 4335 · pass 4280 · fail 24,失败清单与 polish 验收逐条同名(Docker / DB / 部署);`rm -rf .next && next build` `/` ○ Static;首屏 gzip(Claude 口径,基线 `adbb2054` = 650,972 / 66,126)→ 651,520 / 66,031,JS +548 B(+0.08%),CSS −95 B;pytest(隐私 + birth_sky)通过。`page.tsx` 1364 → 1363,`useState`/`useRef` 70 → 70。看过 `/people` 390 宽菜单截图。 执行方报告的上一单缺陷(中国出生地 caption 永远没有城市)核实属实:存储标签以「中国 · 」开头。Claude 在本分支补一个提交修复并记 BUG-1082。 偏离裁定:1–4 接受。遗留给产品:本人 `birth_time_status = confirmed` 时,改出生时间不会移动已确认的排盘时间(账户路由既有行为),需产品决定。 ### 门禁 run 1584 红(`4ed0e8e7`)→ 补修 - 本机按门禁同参数复现:`run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime` 唯一失败 `tests/test_supabase_user_data_contract.py::test_profile_coordinates_are_persisted_with_database_bounds`——坐标写入的源码文本从 `use-profile-onboarding` 挪进了 `lib/self-profile-save.ts`,而 Python 侧的首页表面白名单 `HOME_SURFACE_FILES` 没有同步(前端 `tests/home-surface.ts` 已同步)。 - 修:`HOME_SURFACE_FILES` 加入 `lib/self-profile-save.ts`。断言本身未改(原值 / 新值 / 原因:白名单 8 → 9 个文件 / 断言不变 / 写入逻辑换了文件)。复跑同参数 quick 门禁:997 passed,`Quality gate passed.`;隐私制品扫描 pass。 - 教训:执行方没跑 Python quick 门禁(理由是「没改 Python 文件」),但 Python 合同测试会按源码文本读前端文件。前端改动也必须跑 quick 门禁。