diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 54e269c4..882ea215 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -215,6 +215,7 @@ | 任务书 | 进度 | 主题 | 状态 | 落点 | | --- | --- | --- | --- | --- | +| `TASK-staging-gate-red-20260923.md` | — | **staging 门禁自 09-22 起持续红、两天无新部署(BUG-1012~1014)**:Node 22 逐条复现并二分到提交:`45ec4617` 新增 375px 断点不在白名单;`2a3013f2`(BUG-1000 人物选择器)两处 `focus()` 未带 `preventScroll`、`inputDisabled` 多了 `subjectDeleted` 而源码合同未跟上(行为正确、合同滞后)。决策:375 并入既有空 480 块、不扩白名单;focus 带 preventScroll;合同加三栏并改守「不含 isLoading」。其余本机失败为缺 Docker / rsync,须读门禁日志确认。与 BUG-1011 单文件不重叠可并行 | **待领取** | — | | `TASK-repo-hygiene-20260903.md` | `PROGRESS-repo-hygiene-20260903.md` | 岁差默认 Raman + 用户设置可选、参考集锁口径、发布门本地可复现、注册表白名单、仓库地址、早期日志搬家、远端分支清理 | 已验收(带修复单) | `b6a70aa7`、`2415e751`、`01a45363`(BUG-511);验收结论见修复单附录 | | `TASK-repo-hygiene-fix-20260903.md` | `PROGRESS-repo-hygiene-fix-20260903.md` | Gulika 全局岁差泄漏(P1)、设置类名无样式、提示语对齐校正现状、剩余 lahiri 字面量、测试不写仓库 | 已验收 | `027e4b08`、`c1e3f32a`(BUG-512/513);Gulika 泄漏实验已复核为不泄漏,全量 pytest 后仓库干净 | diff --git a/docs/tasks/TASK-staging-gate-red-20260923.md b/docs/tasks/TASK-staging-gate-red-20260923.md new file mode 100644 index 00000000..bf5371f7 --- /dev/null +++ b/docs/tasks/TASK-staging-gate-red-20260923.md @@ -0,0 +1,104 @@ +# TASK-staging-gate-red-20260923 · staging 门禁自 09-22 起持续变红 + +## 基线 commit + +- 诊断基线:`origin/staging` = `88e89e10`(2026-09-23) +- 最后一次部署成功:`1bc6a954`(2026-09-22 01:14)。此后门禁 run 1445(`6d06fca0`)、1448(`3fed71ab`)、2853(`f77a1b5a`)、2854(原 `f968cb21`)、6294(`a12f2c0d`)全部红,**staging 两天没有新部署** +- 修复分支:`codex/staging-gate-red-20260923`,从 `origin/staging` 起 + +## 事故实证 + +在 `88e89e10` 上用 Node 22(与门禁同版本)逐文件重跑本机与基线共有的前端失败。分成两类: + +### A · 代码或合同真问题(3 条,本单修) + +每一条都在引入提交的父提交上通过、在引入提交上失败,逐条复现: + +| 测试 | 父提交 | 引入提交 | 失败内容 | +|---|---|---|---| +| `viewport-breakpoint-contract.test.ts` · CSS width breakpoints stay on the published allowlist | `92e79642` 通过 | `45ec4617`(首页入口边框变轻)失败 | `globals.css` 新增 `@media (max-width: 375px)`,375 不在白名单 `480 / 640 / 641 / 767 / 768 / 860 / 1023 / 1024` | +| `chat-panel-scroll-guard.test.ts` · the guard covers every component that renders inside the panel today | `6d06fca0` 通过 | `2a3013f2`(BUG-1000 聊天人物选择)失败 | `chat-profile-picker.tsx` 两处 `triggerRef.current?.focus()` 没带 `{ preventScroll: true }`,关闭选择器时会滚动聊天面板 | +| `chat-composer-queue.test.ts` · generating does not disable the textarea | `6d06fca0` 通过 | `2a3013f2` 失败 | `(app)/page.tsx` 的 `inputDisabled` 前面多了 `subjectDeleted \|\|`(资料已删除的旧会话阻断发送,BUG-1000 的设计),源码合同的正则没跟上 | + +第三条是**合同滞后,不是行为错误**:它要守的性质——生成中不得禁用输入框(`inputDisabled` 里不能有 `isLoading`)——仍然成立。 + +### B · 本机缺工具,门禁环境有(本单不修,但要在门禁上确认) + +| 测试 | 本机报错 | 判断 | +|---|---|---| +| `health-deployment` 1 条 | 子进程 status `null`(`spawnSync docker`) | 本机无 Docker | +| `staging-backend-workflows` 2 条 | `rsync: command not found` | 本机无 rsync | +| `model-configuration-security`、`identity-auth-integration`、`admin-database` 各 1 条,以及全部 `database-*.test.ts` | `spawnSync docker ENOENT` | 本机无 Docker | + +诊断方看不到门禁日志(Gitea job 日志需要 token,不借用)。**B 类在门禁上是否通过,本单执行方必须读门禁日志确认**,不得据本机推断。其中 `database-personal-report-sections.test.ts` 已知会在门禁上失败,见 BUG-1011 与 `TASK-report-density-fix2-20260923.md`,不在本单。 + +## 根因 + +1. 两次 09-22 的提交(`45ec4617`、`2a3013f2`)落 staging 时门禁已经是红的(BUG-1002 那一组),新增的失败被原有红色淹没,没人逐条比对。 +2. `45ec4617` 按"新切点必须先改白名单"本应先过 `viewport-breakpoint-contract`,但直接加了 375。 +3. `2a3013f2` 新组件渲染在聊天面板里,没有按 `chat-panel-scroll-guard` 的规则给 `focus()` 带 `preventScroll`;同一提交改了 `inputDisabled` 却没更新源码合同。 + +## 决策记录 + +- **375 断点并入既有 480 断点,不扩白名单。** 依据 BUG-697 的收敛口径(宁可合并,不新增切点)。那条规则只把入口按钮行与按钮限制为 `max-width: 100%`,放到 ≤480 时,376–480px 宽度下只会防止溢出、不会改变不溢出时的样子。`globals.css` 里 `.starter-entry` 附近已经有一个**空的** `@media (max-width: 480px) {}`,放进去即可。这是 Claude 的常规判断;若执行方实测发现 376–480 出现可见变化,停下来报告,不要改成扩白名单。 +- **选择器的 `focus()` 一律带 `{ preventScroll: true }`。** 触发按钮在顶栏,本来就在视口里,`preventScroll` 不改变焦点落点,只阻止面板被滚动。 +- **更新 `chat-composer-queue` 的源码合同**,把 `subjectDeleted ||` 写进期望,并按 AGENTS.md §7.3 写三栏说明。同时新增一条断言:`inputDisabled` 表达式里不含 `isLoading` 与 `cancellationPending`,让真正要守的性质不依赖整串正则。 + +## 硬红线 + +1. 不得删测试、跳过测试或把失败测试标 skip;不得放宽 `viewport-breakpoint-contract` 白名单;不得给 `chat-panel-scroll-guard` 加豁免。 +2. 不得改 `(app)/page.tsx` 的逻辑,本单只改测试合同;`Home()` 的 `useState` / `useRef` 数不得变。 +3. 不得改 `.gitea/workflows/**`,不得为了过门禁在门禁里跳步骤。 +4. 不得动 `tests/helpers/postgres-fixture.ts`(属于 `TASK-report-density-fix2-20260923.md`)。 +5. 测试总数不得低于开工时 `origin/staging` 的实测。 + +## 任务分解 + +### H1 · 375 断点并入 480(BUG-1012) + +把 `@media (max-width: 375px)` 里的两条规则移进 `.starter-entry` 附近那个空的 `@media (max-width: 480px)`,删掉 375 块。改 UI 的同一提交更新 `frontend/DESIGN.md`。 + +**验收标准**:`viewport-breakpoint-contract.test.ts` 全过;`globals.css` 中不再有 375 的宽度断点;在 360 / 375 / 390 / 414 / 480 五个宽度下首页入口按钮不溢出(本机 Chrome 无头截图或 `docs/testing/` 清单一条)。 + +### H2 · 人物选择器不滚动面板(BUG-1013) + +`chat-profile-picker.tsx` 的两处 `triggerRef.current?.focus()` 改为 `focus({ preventScroll: true })`。 + +**验收标准**:`chat-panel-scroll-guard.test.ts` 全过;选择器关闭后焦点仍回到触发按钮(新增或已有测试覆盖)。 + +### H3 · 输入框合同跟上 BUG-1000(BUG-1014) + +更新 `chat-composer-queue.test.ts` 对 `inputDisabled` 的期望,加三栏说明;新增"表达式不含 `isLoading` / `cancellationPending`"断言。 + +**验收标准**:该文件全过;把 `isLoading ||` 临时加进 `inputDisabled` 时新断言变红(变异验证写进进度记录)。 + +### H4 · 门禁确认(对应 B 类) + +修完后推 staging,读门禁 run 日志,把失败测试名单(如有)逐条列进进度记录,并与本单 B 类及 BUG-1011 对照。 + +**验收标准**:门禁 run 除 BUG-1011 那一条外无失败;若还有别的失败,列出 run 编号与测试名,写成新的 investigating 记录,不得声称门禁已绿。 + +### H5 · 记录 + +`docs/BUG_HISTORY.md` 写 BUG-1012~1014,关联 BUG-1000、BUG-1002、BUG-697;`docs/tasks/PROGRESS-staging-gate-red-20260923.md` 写三栏说明与门禁证据。 + +## 让步顺序 + +H1、H2、H3 都很小,必须一起做;H4 在推送后做;H5 同一提交。 + +## 与其他单的关系 + +与 `TASK-report-density-fix2-20260923.md`(BUG-1011)文件不重叠,可以并行。两单都合入后门禁才可能全绿。 + +## 开工前置命令 + +```bash +git fetch origin --prune +git status -sb +git worktree add -b codex/staging-gate-red-20260923 .worktrees/staging-gate-red-20260923 origin/staging +# 前端测试用 Node 22;本机 Node 20 下有一批子进程 @/ 别名解析失败的误红 +``` + +## BUG 编号 + +BUG-1012~1014。开工时核对 `docs/BUG_HISTORY.md` 最大号(当前 1011)。