diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 732d6938..66eabedb 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -3846,3 +3846,35 @@ - 防复发:**「配置开启 + 构建绿」绝不等于「React Compiler 生效」**。编译器放弃某个组件时不报错、不警告、不留日志,这是它的默认行为(`panicThreshold: "none"`)而非缺陷。任何启用 React Compiler 的改动都必须在构建产物里定位目标组件、确认存在编译器注入的缓存槽,并用 `"use no memo"` 做一次反向验证证明取证方法会随编译状态变化;只贴构建退出码等于没验证。另外 `"use memo"` / `"use no memo"` 是函数体内的指令,写在文件顶部时 `"use no memo"` 可作整文件退出、但 `"use memo"` 不构成整文件加入。 - 相关记录:BUG-260 无前序同类记录。本记录三次改号:初次写作取 255;第一次 rebase 到 `origin/staging`(`c8d9ec64`)时远端已占 254–255,改 256;第二次 rebase 到 `e1db5762` 时远端又占到 259,改 260。每次都按 BUG-254 的防复发要求处理——标题与远端各记录均不同,故另分新号而非合并。BUG-253 所记的抢号失效模式在同一轮交付里连续复现两次,暴露出「追加前检索最大编号」这条措施的边界:它只在写记录那一刻成立,防不住推送前远端继续前进。可靠做法是把定号推迟到推送前最后一次 rebase 之后。 - 修复版本:本地未提交候选 + +## BUG-257 | staging web 镜像把 `skills/` 拷两遍,符号链接撞上被解引用的同名目录 + +- 状态:resolved +- 首次发现:2026-08-17 +- 最近更新:2026-08-17 +- 影响面:`deploy/railway-web.Dockerfile` 的镜像构建,即 staging 与生产的 web 镜像产出。构建期缺陷,不影响已在运行的实例。 +- 用户现象:无终端用户可见现象。对维护者而言:`docker build -f deploy/railway-web.Dockerfile` 在最后一步失败,`ERROR: failed to build: failed to solve: cannot replace to directory /var/lib/docker/buildkit/containerd-overlayfs/cachemounts//app/skills/jyotish-vedic-astrology/assets with file`,指向 `COPY --from=build /app/skills /app/skills`。危险之处在于它**依赖 BuildKit 实现才会暴露**:Gitea/GitHub runner 上一直构建成功,本地 Docker 29.1.3(containerd-overlayfs 快照器)必定失败,因此这是一颗按 runner 环境触发的定时炸弹,而不是一个稳定可见的错误。 +- 触发条件:用一个不允许「以文件覆盖已存在目录」的 BuildKit 快照器构建该 Dockerfile。与 Next 版本、CPU 架构均无关:在升级前的基线提交(`next` 16.2.10)与 amd64/arm64 两种平台上都能复现同一条错误。 +- 根因:同一份 `skills/` 以两种互不兼容的形态进了最终阶段。`skills/jyotish-vedic-astrology/assets` 是仓库里 git 跟踪的符号链接(`-> ../../assets`)。`frontend/src/lib/skill-package-registry.ts` 存在动态文件访问,Next 构建期打印 `Dynamic filesystem access ... causes tracing of the whole project`,于是文件追踪把 `skills/` 整棵树带进 `.next/standalone/`,并在拷贝时把符号链接**解引用成真目录**。最终阶段第一步 `COPY --from=build /app/frontend/.next/standalone /app` 先把这份解引用产物落到 `/app/skills`,随后第 36 行 `COPY --from=build /app/skills /app/skills` 又要把源码树里的符号链接放到同一路径上——用文件覆盖目录。两行 COPY 相隔 5 行、意图并不冲突,冲突完全由中间那层不可见的文件追踪行为造成。 +- 修复:在构建阶段 `npm run build` 之后加一行 `RUN rm -rf /app/frontend/.next/standalone/skills`,删掉追踪产物里的那份,让显式 `COPY skills` 确定性地独占该路径。没有改动两行 COPY 的顺序或语义,也没有改 `outputFileTracingIncludes`:`/app/assets` 仍由 standalone 提供,而它是 `outputFileTracingIncludes` 为 `/api/consult` 显式声明的(`../assets/**/*`),不是追踪的附带产物,因此符号链接照旧可解析。 +- 验证:修复后完整构建镜像成功(`naming to docker.io/library/jyotisha-web:fixed done`)。进镜像逐项核对:`/app/skills` 下 1208 个文件、`assets` 仍是 `lrwxrwxrwx ... -> ../../assets` 且 `ls` 能列出其中文件、`/app/frontend/server.js` 存在。运行时冒烟:容器启动打印 `▲ Next.js 16.3.1`,`GET /` 返回 200(日志中的 `SupabaseConfigurationError` 是未注入环境变量所致,属预期)。对照实验确认与本批 Next 升级无关——在升级前的 `3371baca`(`next` 16.2.10)上用同一条命令构建,在同一步报同一条错误。 +- 待跟进:**本条没有自动化回归测试**,与 BUG_HISTORY 工作流对 `resolved` 的要求存在缺口,此处如实标注而非掩盖。原因是复现该缺陷必须真的构建镜像(本机约 16 分钟),放不进 `npm test`。可行的折中是在 `tests/health-deployment.test.ts` 加一条结构断言:若 Dockerfile 同时存在「拷贝 standalone 到 `/app`」与「显式拷贝 `skills`」两行,则必须存在删除追踪副本的一行。这是结构守卫而非行为回归,能防住这一行被顺手删掉,未在本轮加入。更彻底的方向是消除 `skill-package-registry.ts` 的动态文件访问以恢复精确追踪,那属业务代码改动,另开一轮。 +- 防复发:Next 的输出文件追踪会把项目文件带进 `.next/standalone/` 并**把符号链接解引用成真目录**;任何 Dockerfile 若既拷 standalone 又显式拷同名目录,两者就在争同一路径,必须显式指定谁赢,不能靠 COPY 顺序碰运气。仓库内 git 跟踪的符号链接(如 `skills/*/assets`、`skills/*/references`、`skills/*/scripts`)是这类冲突的固定诱因。更普遍的教训:镜像构建在 CI 上绿不等于 Dockerfile 正确,凡涉及符号链接与目录同名的 COPY,判定必须落到「换一个 BuildKit 快照器是否仍然成立」。 +- 相关记录:BUG-257 无前序同类记录。与 BUG-256 同批交付但成因无关,是为验证 BUG-256 的 Next 16.3.1 升级而首次在本地构建 staging 镜像时暴露出来的。 +- 修复版本:本地未提交候选 + +## BUG-258 | staging 镜像内 `posthog-node` 的 Node 引擎要求高于基础镜像实际版本 + +- 状态:investigating +- 首次发现:2026-08-17 +- 最近更新:2026-08-17 +- 影响面:staging 与生产 web 镜像内的 `posthog-node@5.41.0`(产品分析上报)。 +- 用户现象:目前无任何可见现象。仅在镜像构建日志里表现为一条 npm 警告:`npm warn EBADENGINE package: 'posthog-node@5.41.0', required: { node: '^20.20.0 || >=22.22.0' }, current: { node: 'v22.15.0', npm: '10.9.2' }`。 +- 触发条件:构建 `deploy/railway-web.Dockerfile`。`node:22-alpine` 当前解析到 Node v22.15.0,而 `posthog-node@5.41.0` 声明需要 `^20.20.0 || >=22.22.0`,v22.15.0 落在两个区间之外。 +- 根因:尚未确认实际影响面。已确认的事实只有两点:一是版本区间确实不满足,二是与本批 Next 16.3.1 升级无关——`git diff 3371baca 37938187 -- frontend/package-lock.json` 中没有任何 `posthog` 相关改动,且该警告在升级前的基线构建日志里就已出现。尚未确认的是 `posthog-node` 是否真的用到了 Node 22.22 才有的 API:`EBADENGINE` 只是警告、不阻断安装,npm 不会因此拒绝装包,所以风险形态是「构建期无声、运行期在某个具体调用上抛错」,而不是构建失败。 +- 修复:未修复。两条候选路径的代价都超出本轮范围:升基础镜像到 `node:24-alpine` 会引入一整片新的验证面(原生模块需重编、`sharp` 的 musl 二进制需重新确认);降 `posthog-node` 需确认低版本是否仍满足现有调用。 +- 验证:不适用(未修复)。已核实的事实见根因。 +- 待跟进:先确定这是真风险还是纯噪声——读 `posthog-node@5.41.0` 的 changelog/引擎声明来源,确认它把下限提到 `>=22.22.0` 是因为用了新 API 还是仅仅是维护者的支持策略。若属后者,本条可降级为「已知噪声」并就地记录,不必改依赖。 +- 防复发:镜像构建日志里的 `EBADENGINE` 不得当作噪声跳过——它意味着依赖声明的运行环境与镜像实际提供的不一致,而 npm 不会阻止这种安装,因此它是少数「构建期唯一一次提示、之后只会在运行期爆发」的信号。基础镜像用浮动 tag(`node:22-alpine`)时,实际 Node 版本会随上游重建漂移,依赖的引擎下限也会随升级上移,两者相向移动,这类不匹配会反复出现。 +- 相关记录:BUG-258 无前序同类记录。与 BUG-257 同为首次本地构建 staging 镜像时暴露的构建期问题。 +- 修复版本:未修复