2026-09-15 的 UI 走查(web + 移动端)结果落盘。 并入 TASK-chat-reading-load:折叠层里的四列表在窄屏没有重排规则 (767 的卡片化只认三列,width:100% 又让 overflow-x 永不触发),它是 折叠能不能落地的前置;全局 input/select 是 14px,iOS 聚焦会把整页放大, 命中整条注册与资料录入漏斗。 新单 TASK-mobile-touch-and-breakpoints(BUG-695~697):消息操作按钮 命中区 27×34 且相邻只隔 1px;CSS 平板上限 900px 与 sidebar-state.ts 的 1024 不一致,901–1023 是混合态;报告域 720/760/860 三刀互不对齐, 761–860 目录已塌、正文还是桌面。含断点白名单契约测试作为防复发。 另加 docs/testing/ios-keyboard-composer-20260915.md:html/body overflow hidden + 100dvh 外壳 + sticky 输入框 + interactiveWidget 只有 Android 认, 是键盘遮挡的风险形状,但代码断不了真假,交真机确认后再决定是否立单。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
31 KiB
TASK · 首页主题卡下线 + 聊天回答两层化 + 两处移动端阻塞(降低阅读负担)
- 日期:2026-09-15
- 基线 commit:
origin/staging@ebd5ba68(docs: clear the timezone gap and record what actually keeps the gate red) - 执行分支:
codex/chat-reading-load-20260915 - 工作树:
.worktrees/chat-reading-load-20260915 - 类型:产品改造(非事故)。BUG 编号起点见文末。
1. 产品诉求(原话)
首页底下的那些问题都可以去掉;聊天过程中怎么让用户不会觉得「我现在看到字很多,我有点阅读障碍」。
两件事分别对应任务 1 和任务 2–3。
2026-09-15 的 UI 走查又带出两处移动端问题,同批并入(任务 2.6、任务 5):折叠层里的四列表在手机上读不了(它是折叠能不能落地的前置),以及全局输入框 14px 导致 iPhone 聚焦时整页放大。
全部任务可以在同一分支同一轮做完,互不依赖,共用一次测试基线测量(任务 4)。
2. 事故实证
行号会漂移,定位以符号名为准。以下全部核对于 origin/staging@ebd5ba68。
2.1 首页主题卡的真实成本
| 事实 | 定位 |
|---|---|
「从一个主题开始」六张卡 = <section className="starter-themes"> |
frontend/src/components/starter-home.tsx → StarterHome,starter-themes-heading 那一段 |
| 卡片文案每个用户现调大模型生成,一次写十个领域的问题 | frontend/src/app/api/onboarding/route.ts → generateText(export const maxDuration = 60) |
| 前端为它等:单次超时 50 s、最多 3 次尝试 | frontend/src/lib/onboarding-client.ts → defaultPolicy(requestTimeoutMs: 50_000, maxAttempts: 3) |
| 首页揭幕被它挡着 | frontend/src/app/page.tsx → bootstrapPrepareReady 的 onboardingSettled: onboarding !== null || onboardingError !== "" |
| 这份数据除了这六张卡没有第二个消费点 | page.tsx 全文 onboarding?. 只出现在 starterSuggestions 的赋值处 |
即:为了六行文案,首页每次都在等一次 LLM 生成,并把揭幕闸门押在它身上。
2.2 聊天「字很多」的根因不在排版
先排除排版嫌疑(frontend/src/app/globals.css):正文 17px / line-height 1.75(.conversation:not(.is-empty):not(.is-rectification) .message-markdown)、段距 var(--space-4)、栏宽 min(80%, 680px)。这些已经是舒服的值,继续调救不了。
真正的来源是生成合同要求每道本命题都输出一整篇报告:
frontend/src/mastra/product-voice.ts → natalSpokenReportContract:
- 「For career / wealth / marriage / family (and any natal domain reading): open with 3–6 heading-free sentences… Then write the skill Level 2 template. 骨架不可省略」
- 「Required body order after that opener, using these exact H2 headings:
## 统一参数与原始结构, then## {Chinese domain label}…, then## 技法审计表, then## 现代生活」 - 输出预算
AGENT_ANSWER_OUTPUT_TOKENS = 16_384(frontend/src/lib/agent-generation-settings.ts)
于是用户问一句「我事业怎么样」,读完三句人话,第二屏立刻撞上度数 / 宫位 / 分盘 / Dasha 边界表。
而渲染层对这一切是完全平铺的:frontend/src/components/chat-message-content.tsx → ChatMessageContent 把整篇 markdown 一次渲染到底,唯一的折叠是 TechniqueAuditDisclosure,而且 showFoldedAudit = rows.length > 0 && split.rows.length === 0 —— 正文里已经有那张表时反而不折。
2.3 折叠层里的宽表在手机上读不了
移动端的表格卡片化重排只认三列表(frontend/src/app/globals.css,@media (max-width: 767px) 内):
.markdown-table table:has(thead th:nth-child(3):last-child) thead { /* 视觉隐藏表头 */ }
.markdown-table table:has(thead th:nth-child(3):last-child) tbody tr { display: grid; … }
技法审计表(技法 / 状态 / 说明)正好三列,重排正常。但 natalSpokenReportContract 还要求一张 四列的 Yoga 表(name / formation / strength or hit-miss / domain effect),统一参数那节也会出宽表。它们落到基础规则:
.markdown-table { overflow-x: auto; }
.markdown-table table { width: 100%; } /* ← 与上一行互相抵消 */
.markdown-table th, .markdown-table td { word-break: keep-all; } /* ← 中文不断词 */
width: 100% 让表格永远不超出容器,overflow-x: auto 于是永不触发;四列被压成约 85px 一柱,keep-all 又不许中文断词。375px 屏上要么挤成竖排单字,要么撑破容器。
这正是任务 2 折叠层里的内容——手机用户点开「完整分析」第一眼就撞上,所以它是折叠能不能落地的前置条件,并进本轮。
2.4 所有输入框 14px,iPhone 一聚焦就整页放大
frontend/src/app/globals.css 的全局 input 规则:
input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"])…,
select:not([class^="ant-"])… { min-height: 44px; …; font-size: var(--type-body-sm); }
--type-body-sm = 14px,且移动端媒体查询里没有任何一处把它提上去(grep 过 @media (max-width: 767px) 之后的全部规则)。
iOS Safari 对字号小于 16px 的输入框会在聚焦时自动放大页面,并且不会自己缩回去。命中的是整条注册与资料录入漏斗:登录邮箱、姓名、出生日期、出生时间、出生地搜索。
已经躲过的只有两处,改动不得破坏它们:.otp-input 是 20px,.composer textarea 是 var(--type-body-md)(16px)。
2.5 根因一句话
- 首页:一个低价值入口押着一次 LLM 调用和整个揭幕闸门。
- 聊天:内容合同要求「全量输出」,渲染层没有「默认可见性」这一层,于是全量输出 = 全量可见。
- 移动端:宽表的响应式规则只覆盖了三列这一种形状;输入框字号沿用了正文小号,没有为触屏留出 16px 的下限。
3. 决策记录(产品已授权)
产品负责人 2026-09-15 确认,按本轮方案全量执行。逐条列明,执行方不必再问:
- 删除首页「从一个主题开始」整段,连同它背后的
/api/onboarding取数链路。 - 接受副作用:没有可用出生分钟的用户(
personalConsultationAvailable === false)将失去这组引导入口,面对的是问候语 + 两张产品卡 + 输入框。产品明确接受,不做替代入口(用户口径:多余入口宁可删除也不修)。 - 聊天回答改为两层可见性:第一个 H2 之前的口语层默认展开,从
## 统一参数与原始结构起的全部章节装进默认收起的折叠块。产品选了「全部收起」,不做「统一参数收起、领域章节展开」的折中。 - 口语层 ≤ 400 字,形状固定为「一句结论 → 2–3 条短要点 → 一句下一步」。
- 不做分段生成。
TASK-report-sectioned-generation-20260830.md那条路解决的是生成超时,不是阅读负担,本轮不碰。
3.1 本轮推翻 / 作废的既有条目
natalSpokenReportContract的「3–6 heading-free sentences」被细化为带形状与字数上限的口语层。骨架强制、不得跳过已执行层这两条不变。- 待验收项 BUG-630 的走查路径失效:
docs/tasks/README.md「聊天主链路与首页」板上codex/consultation-family-contract-incomplete-20260909(BUG-630,「主题卡无入口钉死」)的修复就是guided_topic入口 +pinsConsultationDomains。删掉主题卡之后,这个入口值不再有任何发出点,该项的浏览器走查清单docs/testing/consultation-family-contract-incomplete-20260909.md不再可执行。- 处理方式:保留
consultation-entrypoint.ts的guided_topic枚举值与pinsConsultationDomains行为(历史会话已把 entrypoint 持久化在会话态里,删枚举会让旧会话读不回来),只删发出点。 - 执行方需在 PROGRESS 里写明该项转为「入口已下线,机制保留供历史会话」,并在
docs/tasks/README.md对应行改状态。
- 处理方式:保留
4. 硬红线
- 内容一个字不许删。 骨架仍然完整生成、完整入库、完整可导出。本轮只改默认可见性与口语层形状。
AGENTS.mdPart B 与natalSpokenReportContract的「Skip an executed layer the question needs」禁令继续有效。 ./node_modules/.bin/tsc --noEmit0 错;npm run lint0 error。- 测试总数不得低于开工时
origin/staging的实测(frontend/AGENTS.md§7.3)。本轮会删入口、必然删断言,因此必须新增足量测试把总数补回来,见任务 4。 - 改任何既有断言必须写「原值 / 新值 / 原因」三栏。
next build后/仍是○ Static;首屏 gzip 变化 ±2% 以内,超出要在 PROGRESS 给原因。- 改 UI 的同一提交内更新
frontend/DESIGN.md;新文案先对照frontend/docs/VOICE.md。 - 不动数据库结构。
profiles.onboarding_payload/onboarding_version/onboarding_generated_at三列留着不迁移。 - 不得顺手升级依赖、不得顺手修不在本任务书里的 warning。
- 揭幕后不得出现 spinner / 骨架 / 「正在加载」——流式生成中除外(
AGENTS.md§6)。任务 2.4 的流式提示走的就是这个例外,必须是安静的一行字,不是转圈。
5. 任务分解
任务 1 · 下线首页「从一个主题开始」
1.1 组件层 — frontend/src/components/starter-home.tsx
- 删除
<section className="starter-themes">整段(含starter-section-heading、starter-theme-accordion、starter-theme-card、末尾onboardingError那行starter-note)。 - 从
StarterHomeProps删除starterThemes、starterSuggestions、startSuggestedConsultation、onboardingError四个 prop,以及不再使用的StarterHomeTheme/StarterHomeSuggestion类型导出与ConsultationEntrypoint/Themeimport。 - 保留
starter-hero与product-entrypoints(今日星语 / 生时校正)两段不动。
1.2 页面层 — frontend/src/app/page.tsx
- 删除
starterThemes、starterSuggestions两个派生值与generalGuidedJyotishTopics的 import。 - 删除
onboardingstate、setOnboarding、onboardingError、onboardingPending,以及调用requestOnboardingWithRecovery的那个useEffect(含activeOnboardingRequestIdentity、onboardingRequestIdentity、isCurrentOnboardingRequest、OnboardingAuthenticationError的相关引用)。 - 从
bootstrapPrepareReady的入参里删掉onboardingSettled,并同步改frontend/src/lib/(或 hook)里bootstrapPrepareSettled的签名与其单测。 - ⚠️
startSuggestedConsultation函数本身必须保留:今日星语卡仍在调用它(startSuggestedConsultation(dailyStarlanguageQuestion, "timing", "daily_starlanguage"))。只删卡片的调用点与 prop 传递。 previewMode === "onboarding"那条预览分支里对setOnboarding的赋值一并清理,预览态不得因此报错。
1.3 接口与 agent
onboarding 这个词在本仓有两条互不相干的链路,删错会拆掉出生资料录入。逐个文件列明:
| 文件 | 处置 | 理由 |
|---|---|---|
frontend/src/app/api/onboarding/route.ts |
删 | 建议问题的生成接口 |
frontend/src/lib/onboarding-client.ts |
删 | 建议问题的取数与重试策略 |
frontend/src/lib/onboarding-post.ts |
删 | 建议问题的服务端处理 |
frontend/src/lib/onboarding-payload.ts |
删 | 建议问题的 schema 与 fallback |
frontend/src/lib/onboarding-cache-policy.ts |
删 | 建议问题的版本与 TTL |
frontend/src/mastra/ 的 getOnboardingAgent / onboardingInstructions / onboardingAgents |
删 | 只写建议问题 |
frontend/src/lib/onboarding-copy.ts |
保留 | 这是出生资料录入那条链路的受保护文案,与建议问题无关 |
frontend/src/components/onboarding-chat-message.tsx |
保留 | 同上,是录入期的打字机消息 |
frontend/src/hooks/use-profile-onboarding.ts |
保留 | 同上,是资料表单的 hook |
createStartGreetingParts / createStartGreeting |
保留 | 首页问候语,不来自这个接口 |
isCurrentOnboardingRequest / onboardingRequestIdentity / OnboardingAuthenticationError |
删 | 只服务建议问题的请求去重 |
- 保留
frontend/src/lib/guided-jyotish-topics.ts与frontend/src/lib/consultation-domain-registry.ts:它们携带claimBoundary/strictWorkflowRoute/evidencePreview,是领域注册表合同的一部分,个人报告侧仍在用。若 lint 报出generalGuidedJyotishTopics完全无引用,把它删掉、defaultGuidedJyotishTopics与home-types.ts的themes导出保留。 - 保留
frontend/src/lib/consultation-entrypoint.ts的guided_topic枚举值与pinsConsultationDomains分支(见 §3.1)。在该文件加一行注释说明「入口已于 2026-09-15 下线,枚举值保留用于读回历史会话」。
1.4 样式 — frontend/src/app/globals.css
- 删除
.starter-themes(两处:.starter-hero, .product-entrypoints, .starter-themes的共享规则里摘掉选择器;以及独立的.starter-themes { display: grid; … })、.starter-section-heading系列、.starter-theme-accordion、.starter-theme-card系列、.starter-content,以及@media (min-width: 768px) and (max-width: 900px)、@media (max-width: 767px)两处响应式覆盖里的对应规则。 .starter-arrow仍被产品卡使用,不要删。
验收标准(任务 1)
- 首页登录后只剩:问候语、今日星语卡、生时校正卡、输入框;没有出生时间的账号同样如此。
- 浏览器 Network 面板不再出现
/api/onboarding请求。 - 从冷启动到首页揭幕不再等待建议问题;
bootstrapPrepareReady的入参里没有 onboarding 相关项。 grep -rn "starterSuggestions\|starter-theme-card\|requestOnboardingWithRecovery" frontend/src无结果。grep -rn "guided_topic" frontend/src只剩consultation-entrypoint.ts内部。
任务 2 · 聊天回答两层化(渲染层,本轮主改动)
2.1 新增纯函数 — frontend/src/lib/chat-answer-split.ts
export type SpokenAndReportSplit = Readonly<{
spoken: string; // 第一个 H2 之前的全部内容
report: string; // 第一个 H2 起的全部内容(含该 H2 本身)
headings: readonly string[]; // report 里所有 H2 的文字,供折叠摘要用
}>;
export function splitSpokenAndReport(text: string): SpokenAndReportSplit;
- 切点 = 第一行匹配
/^##\s+\S/的行(任意 H2,不写死统一参数与原始结构)。合同锁死第一个 H2 是它(frontend/src/lib/consultation-thinking-plan.ts→REPORT_HEADING.foundation),但按「任意 H2」切可以在模型漂移时自然降级,不会突然整篇不折。 - 代码围栏内的
##不算切点,复用frontend/src/lib/chat-markdown-split.ts里FENCE的同一判断方式。 - 没有 H2 时(闲聊、日常问题、onboarding 消息)返回
{ spoken: text, report: "", headings: [] }—— 自动全展开,不需要特判。
2.2 新增折叠组件 — frontend/src/components/chat-answer-detail.tsx
- 导出
AnswerDetailDisclosure,结构照抄既有的TechniqueAuditDisclosure(<details className="answer-detail">+<summary>),样式走globals.css新增的.answer-detail*,视觉上与.technique-audit同族但不复用同一个 class。 <summary>文案:主标「完整分析」,副标用<small>写章节名,不要只写「展开」。例:完整分析+统一参数、事业、技法审计 等 5 节。章节名取headings前三个,多于三个补「等 N 节」。文案定稿前对照frontend/docs/VOICE.md。- 默认收起(不给
open)。
2.3 接入 — frontend/src/components/chat-message-content.tsx
渲染管线改成:
- 先跑既有的
splitSpokenAnswerAndTechniqueAudit(text)(把正文里的技法审计表摘成rows,行为不变)。 - 对它的
spoken结果跑splitSpokenAndReport。 spoken部分照旧渲染进.message-markdown(流式时仍走StreamingMarkdown,memo 优化不得破坏)。report非空时,渲染<AnswerDetailDisclosure>,内部渲染report的 markdown。- 现有
showFoldedAudit的TechniqueAuditDisclosure移进折叠块内部(report为空时仍留在原位,闲聊场景不受影响)。
2.4 流式期间的行为
streaming === true时,折叠块保持收起且不渲染 children,<summary>的<small>位置显示一行安静的文字(例:正在写…)。不得出现 spinner / 骨架 / 转圈(AGENTS.md§6 的流式例外只允许文字)。streaming === false后按 2.2 渲染正式摘要,children 正常渲染(收起态由浏览器隐藏,markdown 只解析一次)。- 这条同时是性能要求:流式期间不得把还在增长的
report反复送进 markdown 解析器。
2.5 影响面确认(执行方开工前自查)
ChatMessageContent的消费方只有两个:chat-message-row.tsx与onboarding-chat-message.tsx。后者的文本没有 H2,行为必须零变化。- 个人报告页走
personal-report-document-view.tsx,不经过本组件,不得受影响。 - 会话落库、复制按钮、重新生成拿到的仍是完整原文,不是口语层。
2.6 折叠层里的宽表必须在手机上可读(本轮前置,不得延后)
处置分两档,按列数分流,不要动现有的三列规则(它有测试、工作正常):
- 三列表:保持现状的卡片化重排,一行也不改。第三列是说明性长文本,塌成第二行读起来像图注,这是它成立的原因。
- 四列及以上:改为真正可横向滚动,而不是继续卡片化。四字段 × N 行的表格塌成卡片会变成一条极长的竖列(Yoga 表 8 行 × 4 字段 = 32 行),比横滑更难读;而且用户是主动点开「完整分析」才看到它的,横滑是可接受的代价。
在 @media (max-width: 767px) 内加:
.markdown-table table:not(:has(thead th:nth-child(3):last-child)) {
width: auto;
min-width: max-content; /* 让 .markdown-table 的 overflow-x: auto 真正生效 */
}
并给 .markdown-table 加一个可横滑的视觉提示(右缘渐隐或 scroll-shadow,用既有 token,不引新库、不加 JS),否则用户不知道右边还有内容。word-break: keep-all 保留——横滑模式下它反而是对的。
同一处理必须同时覆盖:聊天里的宽表、.technique-audit-panel(它已有三列规则,不受影响,确认即可)。个人报告页的表格不在本轮范围(走 .personal-report-* 自己的规则)。
验收标准(任务 2)
- 一条本命类回答:口语层直接可读,下面一个收起的「完整分析」块;展开后章节顺序与原文完全一致,一个字不少。
- 375px 宽视口下展开「完整分析」:三列技法审计表仍是卡片式;四列 Yoga 表可横向滑动、不挤压、不撑破容器;页面本身不出现横向滚动条(
.conversation的overflow-x: hidden必须仍然成立)。 - 闲聊类回答(无 H2)渲染与改动前逐字一致。
- onboarding 打字机消息渲染与改动前逐字一致。
- 复制该条消息,得到的是完整原文(含骨架)。
- 刷新页面重新读历史会话,折叠态一致(默认收起)。
- 流式期间不出现 spinner / 骨架;折叠块在流式结束前不展开。
任务 3 · 口语层的形状(文案合同)
3.1 frontend/src/mastra/product-voice.ts → natalSpokenReportContract
把「open with 3–6 heading-free sentences that answer the user's actual question (directional reading + one clause of context)」细化为带形状与上限的版本:
- 仍然无标题、仍然在第一个 H2 之前、仍然 3–6 句。
- 形状:一句结论 → 2–3 条短要点 → 一句下一步。
- 要点必须是完整句子的短行,不超过 3 条,每条 ≤ 30 字;不得写成名词短语枚举(
VOICE.md禁的是公文枚举与审讯式清单,结论要点不在禁令内,但形似公文的写法在)。 - 口语层总量 ≤ 400 字。
- 「骨架不可省略,但必须以直接回应开场」原句保留。
3.2 同步三处口径,不得只改一处:
frontend/src/lib/consultation-thinking-plan.ts里After a 3-6 sentence spoken reply with no heading…那句。frontend/src/mastra/skill-binding.ts里present its Level 2 report template in the chat body after a 3-6 sentence spoken reply那句。frontend/src/mastra/index.ts里consultationSpokenHeadingRule("natal")相关文案。
3.3 frontend/docs/VOICE.md 新增一节「口语层的形状」,把 3.1 的规则和一组好/坏对照写进去。坏例用「一坨连续段落 + 立刻进度数」,好例用「结论 + 要点 + 下一步」。
3.4 frontend/tests/consultation-voice-contract.test.ts 与 frontend/tests/agent-voice-copy-contract.test.ts 里被改到的断言,逐条按「原值 / 新值 / 原因」三栏注释更新。已知会红的至少有 assert.match(voice, /3–6 heading-free sentences/)。
验收标准(任务 3)
npm test里 voice 相关套件 fail=0。- 三处口径互相一致,
grep -rn "3-6 sentence\|3–6 " frontend/src的结果全部指向同一版规则。 frontend/docs/VOICE.md有新节,且好/坏对照来自真实出现过的形状。
任务 4 · 测试与文档
4.1 先量基线,写进 PROGRESS
cd .worktrees/chat-reading-load-20260915/frontend
npm ci
npm test 2>&1 | tail -20 # 记下 pass / fail / tests 三个数字
4.2 删除的断言必须逐条列出
已知受影响:
frontend/tests/starter-theme-layout-contract.test.ts—— 整个文件随主题卡删除。frontend/tests/starter-questions.test.ts—— 该文件共 15 个 test,只有下面这些与主题卡相关,其余 11 个(会话历史可点、模型弹窗尺寸、额度居中、账户菜单、会员入口、admin 按钮、IME、错误播报等)必须原样保留:default starter questions derive every canonical domain with evidence and claim boundaries—— 其中对consultationDomainRegistry/defaultGuidedJyotishTopics的领域注册表断言必须搬到frontend/tests/consultation-domain-registry.test.ts(或新建同名文件)继续跑,不是删除;只删starterSuggestions.map/starterThemes.find/startSuggestedConsultation(item.text, …)三条针对渲染的断言。profiles without a usable birth minute receive user-centered starter prompts——generalGuidedJyotishTopics的文案与边界断言同样搬家保留;删掉starter-themes-heading/starter-theme-accordion的sourceBetween断言。homepage daily and topic cards start a consultation instead of filling the composer—— 改名为只覆盖今日星语卡,删掉item.theme, "guided_topic"那条。keeps starter questions visible while the user edits a draft—— 主题卡没了,该 test 删除。keeps the empty starter home at the top instead of auto-scrolling—— 保留,starterHomeVisible仍在。
frontend/tests/home-surface.ts不用改(starter-home.tsx 仍在)。- onboarding 相关,同样分两条链路,不要一把梭:
- 随接口删:
frontend/tests/onboarding-route.test.ts、frontend/tests/onboarding-route-fake.ts、frontend/tests/onboarding-client.test.ts、frontend/tests/onboarding-cache-policy.test.ts。 - 保留:
frontend/tests/onboarding-copy.test.ts(录入期受保护文案)。 - 部分删:
frontend/tests/onboarding-presentation.test.ts—— 其中createStartGreetingParts/createStartGreeting/createOnboardingFallbackGreeting的断言保留(首页问候语仍在),只删isCurrentOnboardingRequest相关的那部分。
- 随接口删:
4.3 必须新增的测试(补回总数,且是本轮的合同)
frontend/tests/chat-answer-split.test.ts:无 H2 原样返回;第一个 H2 切分正确;围栏内##不切;headings抽取正确;## 技法审计表与## 现代生活都落在report侧。frontend/tests/chat-answer-detail.test.ts(或并入既有渲染合同套件):折叠块默认收起;<summary>带章节摘要;流式态不渲染 children 且不出现 spinner / 骨架 / 「正在加载」字样;report为空时不渲染折叠块。- 首页合同:新增断言锁死「首页不再渲染主题卡、不再请求
/api/onboarding、bootstrapPrepareReady不含 onboarding 项」。 - 领域注册表搬家后的新套件(4.2 第二条)。
4.4 文档
frontend/DESIGN.md:新增「回答的两层可见性」一节(口语层 / 折叠层的判定规则、<summary>文案规则、流式期间的表现),并把首页去掉主题卡后的版式写进首页那一节。另加一条表格规则:三列表在窄屏卡片化、四列及以上横向滚动并带滑动提示,以及表单输入 16px 下限的理由(iOS 聚焦放大)。同一提交内完成。docs/testing/chat-reading-load-20260915.md:新建真机清单,至少含——iPhone Safari 上逐个聚焦表单输入框确认不放大;375px 下展开「完整分析」确认宽表可横滑且页面不横向滚动;折叠块在流式期间不展开、不出现转圈。CHANGELOG.md:一条日期 + 一句话标题 + 变更要点(首页主题卡下线;回答默认只展开口语层)。注明 Skill 版本不bump(本轮不改 Skill)。docs/tasks/PROGRESS-chat-reading-load-20260915.md:做了什么、测试三个数字(基线 / 改后)、删了哪些断言、新增哪些、偏离与原因。docs/tasks/README.md:把本任务书那行状态从「待领取」改成「执行中 / 待验收」;同时处理 §3.1 说的 BUG-630 那一行。
验收标准(任务 4)
npm test的 tests 总数 ≥ 基线,fail=0。- PROGRESS 里有「基线 / 改后」两组数字和删/增断言的对照清单。
./node_modules/.bin/tsc --noEmit0 错,npm run lint0 error。npm run build后/仍是○ Static,首屏 gzip 记录在 PROGRESS,变化 ±2% 内。
任务 5 · 输入框字号 14 → 16(iOS 聚焦放大)
5.1 frontend/src/app/globals.css 的全局 input / select 规则,font-size 从 var(--type-body-sm)(14px) 改为 var(--type-body-md)(16px)。
- 优先做无条件提升(所有视口一致 16px)。
min-height: 44px已经在了,行高和内边距不用动,桌面端视觉变化很小。 - 若产品在桌面端观感上不接受 16px,退而求其次只在触屏下提升:
两种都可以,选哪种写进 PROGRESS。不接受的是维持 14px。
@media (pointer: coarse) { input:not(…), select:not(…) { font-size: var(--type-body-md); } }
5.2 不得破坏既有的两处安全值:.otp-input(20px)、.composer textarea(--type-body-md)。
5.3 排除项不变:[type="radio"]、[type="checkbox"]、antd([class^="ant-"] / .ant-picker input)——admin 是 antd + Refine,不在本轮范围。
5.4 改完复查这几个表单的换行有没有被挤坏(字号变大会改变换行点):登录邮箱 + 验证码、姓名、出生日期、出生时间、出生地搜索下拉、设置弹窗里的各字段。桌面和 375px 各看一遍。
验收标准(任务 5)
grep -nE "font-size: var\(--type-body-sm\)" frontend/src/app/globals.css的结果里不再包含全局 input/select 规则。- 375px 视口下逐个聚焦上述输入框,页面不发生缩放(真机项:模拟器不一定复现 iOS 的放大行为,做不到就写进
docs/testing/清单,不得写成「通过」)。 - 表单在 375px 与桌面两种宽度下无换行崩坏、无溢出。
6. 让步顺序
被挡住时按这个顺序退,退到哪一步写进 PROGRESS:
- 任务 2.4「流式期间不渲染折叠 children」若造成闪烁或状态错乱,先放弃这条优化,改为始终渲染(收起态交给浏览器隐藏)。折叠本身不让。
- 任务 3 若导致 voice 合同套件大面积红且一轮内收不住,把任务 3 整体拆到下一轮,本轮只交任务 1 + 2 + 4。渲染层折叠独立生效,不依赖文案改动。
- 任务 1.3 删接口若牵出 Mastra agent 注册或模型目录的连带失败,保留
route.ts与 agent 文件但断开前端调用(前端不再请求即可拿到全部收益),把未删干净的部分写进根目录BLOCKED.md。 generalGuidedJyotishTopics若被别处引用,保留该模块,只删首页引用。- 任务 2.6 的横滑提示(渐隐 / scroll-shadow)若在深色主题下做不干净,先只交「能横滑」,提示留到下一轮,写进 PROGRESS。能横滑本身不让。
- 任务 5 若无条件 16px 在桌面端观感上被否,退到
(pointer: coarse)版本。维持 14px 不是选项。 - 绝不让步:骨架内容不得删减;完整原文必须仍然入库、可复制、可导出;
guided_topic枚举值不得从 schema 移除;不得动数据库结构。
7. 开工前置命令
cd /workspace/Jyotisha
git status -sb | head -1 # 确认当前分支,主检出常被别的会话切走
git fetch origin --prune
git worktree add -b codex/chat-reading-load-20260915 \
.worktrees/chat-reading-load-20260915 origin/staging
cd .worktrees/chat-reading-load-20260915/frontend
npm ci
npm test 2>&1 | tail -20 # 基线三个数字,抄进 PROGRESS
./node_modules/.bin/tsc --noEmit # 基线应为 0 错
npm run lint # 基线应为 0 error
本轮是纯前端改动,不要求跑 scripts/pre_work_check.py(AGENTS.md §9 末句),但 §2 分支模型与 §3 工作树规则照常。
交付:
git push origin HEAD:staging # 快进推送,触发 backend-quality-gate
推完核对远端 SHA;远端验证失败不得声称已交付。
8. 串行与依赖
- 本轮改
frontend/src/app/page.tsx与frontend/src/components/chat-message-content.tsx。开工前确认没有别的轮次同时在改这两个文件(docs/tasks/README.md状态板上「执行中」的行)。若有,本轮排在其后。 - 与
TASK-report-provenance-binding-20260915.md无文件重叠,可并行。 TASK-mobile-touch-and-breakpoints-20260915.md必须排在本轮之后:两轮都改frontend/src/app/globals.css,且那一轮的任务 2.2 依赖本轮是否已删掉主题卡。
9. BUG 编号起点
origin/staging@ebd5ba68的docs/BUG_HISTORY.md当前最大号 = BUG-694。- 本任务书是产品改造,不预分配 BUG 编号。执行中若发现或引入回归,从 BUG-698 起连续编号(BUG-695~697 已由
TASK-mobile-touch-and-breakpoints-20260915.md占用),并按AGENTS.md§5 在同一变更里补docs/BUG_HISTORY.md。 - 开工时请再核一次最大号——可能已有别的轮次占用。