跟上已经换过的界面检查,测试环境才能接着发布(BUG-1439)。
Independent Staging Quality Gate / validate (push) Failing after 19m8s
Independent Staging Quality Gate / publish (push) Skipped

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-25 15:27:14 +08:00
co-authored by Grok (agent loop)
parent 689e01296e
commit 1c428d7b6e
11 changed files with 108 additions and 14 deletions
+17
View File
@@ -19510,4 +19510,21 @@
- 复发自:无
- 修复版本:分支 `codex/ui-kit-6-rectification-20261023`。
## BUG-1439 | 出生时间合同仍要求建档里的旧校正卡
- 状态:resolved(定向回归已改;测试网站要等这次门禁通过才更新)
- 首次发现:2026-10-25
- 最近更新:2026-10-25
- 影响面:测试环境发布。页面行为没有新的改动。
- 用户现象:启程和组件库已经合进测试分支,测试网站仍停在完整数据版(二)那一版。
- 触发条件:#96 去掉建档里到不了的校正卡之后推上 staging。从那一笔起,快速门的 Python 检查失败,后面的提交没有再发布。
- 根因:`tests/test_birth_time_journey_contract.py` 仍要求首页扫描面里出现 `<BirthTimeRectification`。这张卡已经不在建档里。对话里的校正是 `<ConversationalBirthTimeRectification`,前端合同已经改过,Python 这条没跟上。更早的 #104 检查是过的,镜像构建时下载依赖超时,那一次也没有发布。
- 修复:这条断言改成扫描面不得再出现旧标签,并且首页必须挂载对话里的校正。同一道门禁里,几条前端检查还锁着换界面之前的写法(会话 store、点数按钮、外观菜单、编辑表上的出生时间范围、没有样式的类名)。检查改到现在的写法。手机侧栏在不认识 `dvh` 时用 `100vh`。外观菜单的勾选用回主题色。其余断言不放宽。
- 验证:`tests/test_birth_time_journey_contract.py`。没有登录浏览器,不把页面走查写成通过。
- 防复发:建档再删校正入口时,Python 合同和 `frontend/tests/rectification-lazy-loading.test.ts` 要一起改。
- 相关记录:BUG-1029、BUG-1431。issue #96。
- 复发自:BUG-1029。上次是组件搬家后文件清单没跟上;这次是入口删了,断言还锁着旧标签。
- 修复版本:分支 `codex/gate-birth-journey-contract-20261025`。
- 断言三栏:原值是扫描面必须出现 `<BirthTimeRectification`;新值是扫描面不得出现该标签,且 `page.tsx` 必须挂载 `<ConversationalBirthTimeRectification`;原因是 BUG-1431 去掉了到不了的建档校正卡,校正仍从对话进入。
@@ -0,0 +1,34 @@
# PROGRESS · 出生时间合同跟上对话里的校正(2026-10-25)
没有新工单。测试分支已经含启程(二)到组件库收尾,测试网站仍停在完整数据版(二)。这一张只改一条检查,不改页面。
## 结论
建档里的旧校正卡已经不在。校正从对话进入。Python 门禁还在找旧标签,所以从启程(二)那一笔起没有发布。
## 断言改动(原值 / 新值 / 原因)
| 测试 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| `tests/test_birth_time_journey_contract.py` `test_web_onboarding_uses_the_deterministic_free_journey` | 首页扫描面必须出现 `<BirthTimeRectification` | 扫描面不得再出现这个标签;`page.tsx` 必须挂载 `<ConversationalBirthTimeRectification` | BUG-1431 去掉了到不了的建档校正卡。前端合同已改,Python 这条没跟上(BUG-1439,复发自 BUG-1029) |
其余断言没有放宽。没有新迁移,没有改页面。
## 同一道门禁还会卡住的前端合同
这些是界面已经换过、检查句子还停在旧写法。页面行为没有再改一轮,只让检查跟上,并给手机侧栏补了没有 `dvh` 时的 `vh` 高度,给外观菜单的勾选用回主题色。
| 测试 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| `class-name-definition-contract.test.ts` | `is-done` 算没有样式 | 删掉;大运面板、对话旁星盘拖条、侧栏面板、导航项和手机侧栏列入钩子 | 进度条已经给 `is-done` 上色。其余类名落在自己会画的零件上 |
| `ui-kit-stores.test.ts`、`motion-presets.test.ts` | 首页不得引用任何 store | 首页可以引用会话 store,不得引用另外三个,也不得静态引用动效 | K2 把对话状态放进会话 store |
| `starter-home-polish-20260926.test.tsx` | 趋势句只看有没有分钟;硬币图标写在首页 | 本人资料不完整时不给趋势句;硬币图标在点数按钮里 | #96 与 K1 |
| `theme-preference-contract.test.ts` | 旧菜单零件 `Menu.*` | 组件库下拉菜单;勾选仍用主题色 | K1 |
| `profile-gender-ui-20260927.test.tsx` | 编辑表的按下项只有性别 | 前面还有出生时间范围,性别仍是男 | 编辑表带了出生时间范围 |
| `viewport-unit-fallback-contract.test.ts` | 手机侧栏只有 `100dvh` | 先写 `100vh`,认识 `dvh` 的浏览器再用 `100dvh` | 不认识 `dvh` 时侧栏不能没有高度 |
## 门禁事实
- 测试网站 `/api/health` 的 `deployment.gitCommit` 仍是 `0b8fcdb842499a2b38f4e618c8c190ffe92e9cf0`。
- #104 的检查通过,发布镜像时下载前端依赖超时,没有部署。
- #96 到 #111 的快速门都停在这条 Python 检查。#111 是 1 条失败,其余通过。
+1
View File
@@ -40,6 +40,7 @@
| `TASK-rectification-explicit-open-20261023.md` | `PROGRESS-rectification-explicit-open-20261023.md` | **明说要校正只回一句话(issue #103)**:找会话直接读一张对服务角色撤权的表、必然失败被吞(真正进行中的也续不上);`candidate_accepted` 被算作进行中挡住入口和提议卡;兜底只给一句话。产品选 C;只改入口口径、经 RPC 找会话、删兜底,不改共享状态集合、不动表。BUG-1423 | 已合入 staging | 分支 `codex/rectification-explicit-open-20261023` |
| `TASK-chart-table-span-20261023.md` | `PROGRESS-chart-table-span-20261023.md` | **星盘页本命 / 月亮行星表挤进左栏(issue #102)**:#87 给表包了 `ChartSymbolHelp` 后跨栏规则失配;根因修法是 `VedicLayout` 用固定整行容器承载表格,跨栏只认容器;加结构断言防复发。BUG-1422 | 已合入 staging | 分支 `codex/chart-table-span-20261023` |
| `TASK-ui-kit-migration-20261023.md` | `PROGRESS-ui-kit-0-base-20261023.md`;`PROGRESS-ui-kit-1-shell-20261023.md`;`PROGRESS-ui-kit-2-chat-20261023.md`;`PROGRESS-ui-kit-3-chart-20261023.md`;`PROGRESS-ui-kit-4-people-20261023.md`;`PROGRESS-ui-kit-5-pages-20261023.md`;`PROGRESS-ui-kit-6-rectification-20261023.md`;`PROGRESS-ui-kit-7-cleanup-20261023.md` | **界面统一换成组件库**:shadcn base-nova(补 `shadcn/tailwind.css`)· Lucide · Motion · react-resizable-panels · dnd-kit · Zustand;lint 拦手写控件(白名单只减不增);对话旁星盘面板;会话、档案拖动排序(动表);长相不变。产品 2026-10-23 拍板 D1~D8;8 个 issue(K0~K7)串行,取代全站动效 #98 #100 #101 | K0–K7 已在测试环境,待验收 | 原型 https://claude.ai/artifact/PGnWsjHhu38KJseEfEMNWG |
| —(门禁修复,无新任务书) | `PROGRESS-birth-journey-contract-20261025.md` | **出生时间合同仍要求建档里的旧校正卡(BUG-1439)**:#96 去掉到不了的卡片后,Python 门禁从那一笔起失败,测试网站停在完整数据版(二)。合同改为锁首页对话里的校正。页面不改 | 已合入 staging,等门禁 | `codex/gate-birth-journey-contract-20261025` |
| `TASK-onboarding-journey-20261022.md` | `PROGRESS-onboarding-1-data-20261022.md`;`PROGRESS-onboarding-2-intake-20261022.md`;`PROGRESS-onboarding-4-motion-20261022.md` | **新用户启程**:建档加「先看谁的盘」(自己 / 帮别人看,本人资料可后补)、人物关系字段、首问免费(服务端原子)、起步三问、启程清单(七项从数据推导,收费项标价,老用户可见)、八条一次性提示(含星盘四页签)、启程流程专用动效。产品 2026-10-22 拍板 D1~D6;动表经产品同意进循环,拆四个 issue 串行。BUG-1420 已被文档去重占用,本轮首问免费记 BUG-1421。(一)T1+T2 在 `codex/onboarding-1-data-20261022`。(二)T3+T4 记 BUG-1431,在 `codex/onboarding-2-intake-20261022`。(四)T7 在 `codex/onboarding-4-motion-20261022` | (一)(二)(三)已在 staging;(四)本分支待验收 | 原型 https://claude.ai/artifact/PcTwMVb9RD611R3qStxv3U |
| `TASK-site-motion-20261022.md` | — | **全站动效**:换页按侧栏方向滑入、选中块跟随、页签、盘型切换星体移宫、换人、翻日期、列表补位、弹窗从触发处长出、抽屉、顶部提示、点数滚动、中英交叉淡入;重写 DESIGN §6(推翻 180 ms 上限与 Spatial Instant),不引动画库,先验证 View Transitions。产品 2026-10-22 拍板;三个 issue,排在启程(三)之后、启程(四)之前;**2026-10-23 被 ui-kit-migration 取代** | 已取代 | 原型 https://claude.ai/artifact/7u9goivnmTNSGTNWNeVMQd |
| `TASK-rectification-narrower-delivery-20261016.md` | `PROGRESS-rectification-narrower-delivery-fix-20261017.md` | **交付区间收窄**:显示改按活着的簇分段。第 1 次打回只补两处显示漏段(取整差 8 分、缺区间整段退回)。对半出题和门槛 8 改 6 仍不做。基线与对照各 924 条,12 格包含率都过红线。BUG-1367 起,本轮原记 BUG-1370,因与婚恋合盘保留号冲突改为 BUG-1392 | 待验收 | `codex/rectification-narrower-delivery-20261016` |
+2
View File
@@ -1972,6 +1972,7 @@ section[popover]:has(> [data-sonner-toaster]), section[popover][aria-label^="页
.theme-menu-label { padding: var(--space-1) var(--space-3); color: var(--color-ink-tertiary); font-size: var(--type-caption); font-weight: 500; }
.theme-menu-item { cursor: pointer; }
.theme-menu-check { display: inline-grid; margin-left: auto; place-items: center; color: var(--color-action); }
.theme-menu [data-slot="dropdown-menu-radio-item-indicator"] { color: var(--color-action); }
.theme-menu-check svg { width: 15px; height: 15px; }
.account-menu-separator { height: 1px; margin: var(--space-2) var(--space-3); background: var(--color-border); }
@@ -6953,6 +6954,7 @@ button.synastry-dd-add { border-radius: 0 0 var(--radius-md) var(--radius-md); }
}
@media (max-width: 767px) {
[data-sidebar="sidebar"] { height: 100vh; }
@supports (height: 1dvh) {
[data-sidebar="sidebar"] { height: 100dvh; }
.auth-page { height: 100dvh; }
@@ -55,7 +55,15 @@ const tailwindCollisions = new Set([
// on the same element.
const knownUnstyled = new Set([
"birth-time-evidence-receipt",
"is-done",
// 原值:is-done 在白名单里,因为当时没有样式。
// 新值:删掉。chart-page-dasha-panel、chat-chart-resize-handle 加入。
// 原因:启程进度条已经给 li.is-done 上色。下面这几个类名落在自己会画样式的
// 折叠内容、分栏、侧栏导航和手机侧栏上,类名只是钩子。
"chart-page-dasha-panel",
"chat-chart-resize-handle",
"shell-sidebar-panel",
"sidebar-nav-item",
"sidebar-sheet",
"is-live",
"is-working",
"onboarding-step-card",
+6 -1
View File
@@ -73,6 +73,11 @@ test("LazyMotion loads domAnimation asynchronously and honors reduced motion", (
assert.match(providerSource, /reducedMotion="user"/);
assert.match(providerSource, /import\("motion\/react"\)\.then\(\(module\) => module\.domAnimation\)/);
assert.doesNotMatch(providerSource, /domMax/);
assert.doesNotMatch(pageSource, /motion\/react|@\/lib\/motion\/provider|@\/stores\//);
// 原值:首页不得引用 motion/react、动效外壳和任何一个 store。
// 新值:首页仍不得静态引用 motion 或动效外壳;可以引用会话 store,不得引用另外三个。
// 原因:K2 把对话状态放进会话 store。动效仍按需加载,不进首页同步包。
assert.doesNotMatch(pageSource, /motion\/react|@\/lib\/motion\/provider/);
assert.match(pageSource, /useSessionStore/);
assert.doesNotMatch(pageSource, /usePeopleStore|useAccountStore|useUiStore/);
assert.doesNotMatch(layoutSource, /motion\/react|@\/lib\/motion\/provider|@\/stores\//);
});
@@ -104,7 +104,13 @@ test("people edit form: the gender field is part of the form and writes the draf
onChange: (next) => { draft = typeof next === "function" ? next(draft) : next; },
};
const markup = renderToStaticMarkup(<ChartProfileForm {...props} />);
assert.deepEqual(pressed(markup), ["女:false", "男:true", "不填:false"]);
// 原值:整张表的按下项只有性别三选。
// 新值:前面还有出生时间范围四选,「差不多准」按下;性别仍是男。
// 原因:编辑表带上了出生时间范围。这份资料是家人记得的钟点,15 分钟那一档对应「差不多准」。
assert.deepEqual(pressed(markup), [
"差不多准:true", "前后半小时:false", "前后一小时:false", "前后两小时:false",
"女:false", "男:true", "不填:false",
]);
const field = elements(ChartProfileForm(props)).find((element) => element.type === ProfileGenderField);
assert.ok(field);
(field.props.onChange as (value: ProfileGender | null) => void)(null);
@@ -120,6 +126,7 @@ test("people edit form: the gender field is part of the form and writes the draf
// 新值: 同一张表直接渲染;页面源码把表单放进 Dialog,并传 showRelation={!editingSelf}
// 原因: K4 编辑用 Dialog,传送门不进静态 HTML;关系和性别仍是这一张表上的 ToggleGroup
assert.deepEqual(pressed(editing), [
"差不多准:true", "前后半小时:false", "前后一小时:false", "前后两小时:false",
"伴侣:false", "孩子:false", "父母:false", "朋友:false", "其他:true",
"女:false", "男:true", "不填:false",
]);
@@ -140,19 +140,26 @@ test("D1: today's trend is a sub-line under the greeting, static until ready, hi
test("D1: page.tsx feeds the sub-line from the same daily state the warm snapshot seeds", () => {
const page = readFileSync(new URL("../src/app/(app)/page.tsx", import.meta.url), "utf8");
assert.match(page, /useState<DailyStarlanguageState>\(\(\) => warmStart\(\)\?\.daily \?\? \{ kind: "pending" \}\)/);
assert.match(page, /<StarterGreeting starterGreeting=\{starterGreeting\} dailyTrend=\{natalMinuteAvailable \? dailyStarlanguageTrend : null\} dailyTrendSettled=\{dailyStarlanguage\.kind === "ready"\} \/>/);
// 原值:dailyTrend 只看 natalMinuteAvailable。
// 新值:本人资料不完整时不给趋势句。
// 原因:#96 先帮别人看时,本人没有出生资料就不显示今日星语趋势。
assert.match(page, /<StarterGreeting starterGreeting=\{starterGreeting\} dailyTrend=\{selfChartComplete\(profile\) && natalMinuteAvailable \? dailyStarlanguageTrend : null\} dailyTrendSettled=\{dailyStarlanguage\.kind === "ready"\} \/>/);
assert.doesNotMatch(page, /dailyStarlanguageTrend=\{/);
});
test("D3: daily entry uses MoonStar, credits use Coins, accessible names unchanged", () => {
const starterHome = readFileSync(new URL("../src/components/starter-home.tsx", import.meta.url), "utf8");
const page = readFileSync(new URL("../src/app/(app)/page.tsx", import.meta.url), "utf8");
const credits = readFileSync(new URL("../src/components/credit-balance-button.tsx", import.meta.url), "utf8");
assert.match(starterHome, /<MoonStar aria-hidden="true" \/>/);
assert.doesNotMatch(starterHome, /Sparkles/);
assert.match(page, /import \{ Coins \} from "lucide-react";/);
assert.match(page, /<Coins className="credit-icon" aria-hidden="true" \/>/);
// 原值:硬币图标和余额名称写在 page.tsx。
// 新值:图标在点数按钮里,首页把原来的名称作为 label 传进去。
// 原因:K1 把点数按钮换成组件库按钮,可读名称没改。
assert.match(credits, /import \{ Coins \} from "lucide-react";/);
assert.match(credits, /<Coins className="credit-icon" aria-hidden="true" \/>/);
assert.doesNotMatch(page, /Sparkles/);
assert.match(page, /aria-label=\{account \? `余额 \$\{account\.credits\} 点,会员与充值` : accountError \|\| "读取余额中"\}/);
assert.match(page, /label=\{account \? `余额 \$\{account\.credits\} 点,会员与充值` : accountError \|\| "读取余额中"\}/);
const markup = renderToStaticMarkup(<StarterEntries {...entriesProps()} />);
assert.match(markup, /aria-label="深入看今日"/);
@@ -64,14 +64,18 @@ test("stored preference is read as external state, not effect-synced React state
});
test("the control lives in the account menu and keeps menu semantics", () => {
const radioItem = readFileSync(new URL("../src/components/ui/dropdown-menu.tsx", import.meta.url), "utf8");
// 原值:Menu.Separator、Menu.RadioGroup、Menu.RadioItem、Menu.RadioItemIndicator,勾选样式是 .theme-menu-check。
// 新值:DropdownMenuSeparator、DropdownMenuRadioGroup、DropdownMenuRadioItem;勾选标记在组件库里,颜色仍是主题色。
// 原因:K1 账户菜单改用组件库。三项外观、点了不立刻关上,都还在。
assert.match(sidebar, /<ThemePreferenceMenu \/>/);
assert.match(sidebar, /<Menu\.Separator className="account-menu-separator" \/>\s*<ThemePreferenceMenu \/>/);
assert.match(menu, /Menu\.RadioGroup/);
assert.match(menu, /Menu\.RadioItem/);
assert.match(menu, /Menu\.RadioItemIndicator/);
assert.match(sidebar, /<DropdownMenuSeparator className="account-menu-separator" \/>\s*<ThemePreferenceMenu \/>/);
assert.match(menu, /DropdownMenuRadioGroup/);
assert.match(menu, /DropdownMenuRadioItem/);
assert.match(radioItem, /RadioItemIndicator/);
// Picking a theme should not slam the menu shut before the change is visible.
assert.match(menu, /closeOnClick=\{false\}/);
assert.match(menu, /外观/);
for (const label of ["浅色", "深色", "跟随系统"]) assert.match(menu, new RegExp(label));
assert.match(globalStyles, /\.theme-menu-check \{[^}]*color: var\(--color-action\)/);
assert.match(globalStyles, /\.theme-menu \[data-slot="dropdown-menu-radio-item-indicator"\] \{[^}]*color: var\(--color-action\)/);
});
+6 -1
View File
@@ -10,9 +10,14 @@ import { useUiStore } from "../src/stores/ui-store.ts";
const home = readFileSync(new URL("../src/app/(app)/page.tsx", import.meta.url), "utf8");
test("K0 store skeletons exist and Home() does not move state into them", () => {
// 原值:Home() 不引用任何一个 store。
// 新值:Home() 只引用会话 store;人物、账户、界面三个 store 仍是空骨架。
// 原因:K2 把对话状态放进会话 store,Home() 的 useState 从 33 降到 25。另外三个还没接上。
assert.equal(useSessionStore.getState().placeholder, true);
assert.equal(typeof useSessionStore.getState().setSessions, "function");
assert.equal(usePeopleStore.getState().placeholder, true);
assert.equal(useAccountStore.getState().placeholder, true);
assert.equal(useUiStore.getState().placeholder, true);
assert.doesNotMatch(home, /useSessionStore|usePeopleStore|useAccountStore|useUiStore/);
assert.match(home, /useSessionStore/);
assert.doesNotMatch(home, /usePeopleStore|useAccountStore|useUiStore/);
});
+5 -1
View File
@@ -181,7 +181,11 @@ def test_web_onboarding_uses_the_deterministic_free_journey() -> None:
assert "<HomeOnboardingShell" in PAGE.read_text(encoding="utf-8")
assert "<BirthTimeIntakeFields" in page
assert "requestBirthTimeAssessment" in page
assert "<BirthTimeRectification" in page
# 原值:扫描面必须出现 `<BirthTimeRectification`。
# 新值:扫描面不得再出现这个标签;首页必须挂载 `<ConversationalBirthTimeRectification`。
# 原因:BUG-1431 去掉了建档里到不了的校正卡。校正仍从对话进入。BUG-1439。
assert "<BirthTimeRectification" not in page
assert "<ConversationalBirthTimeRectification" in PAGE.read_text(encoding="utf-8")
assert "reported_birth_time" in page
assert "active_birth_time" in page
assert "birthTimeStatus" in page