From c4176a3b3411f0011924a20baed9ba99bb126619 Mon Sep 17 00:00:00 2001 From: ThreeFish Date: Tue, 1 Sep 2026 15:03:01 +0800 Subject: [PATCH 1/2] =?UTF-8?q?build(deps):=20pnpm=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E8=87=B3=2012.2.1=EF=BC=8C=E5=B9=B6=E8=A1=A5=E9=BD=90=E5=B5=8C?= =?UTF-8?q?=E5=A5=97=E5=B7=A5=E7=A8=8B=20workspace=20=E6=A0=B9=E5=A3=B0?= =?UTF-8?q?=E6=98=8E=E4=BB=A5=E9=98=BB=E6=96=AD=E6=A0=B9=20lockfile=20?= =?UTF-8?q?=E8=A2=AB=E8=A6=86=E5=86=99;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnpm 12.2.1 已发布但 npm `latest` dist-tag 仍指向 11.25.0,本次为知情抢升。 升级过程暴露一个会破坏数据的 v12 回归,故与修复一并原子提交。 升级 - 根 package.json#packageManager → pnpm@12.2.1。integrity 需 hex 而非 registry 的 base64,换算方法先用 11.25.0 反向复算命中仓库原值后才落笔。 - pnpm-lock.yaml +101 行:v12 新增 packageManagerDependencies(多文档 YAML, doc2 逐字节未变,无依赖重解析)。旧 lockfile 上 --frozen-lockfile 会硬失败, 故这段变更是升级的必要组成而非副产品。 - docker/{frontend,wiki}/Dockerfile 的 `corepack prepare pnpm@latest` 收敛为 `corepack install`,消除与 packageManager 并存的第二事实源。 - pre-commit check-yaml 只豁免 pnpm-lock.yaml(多文档机器产物),手写 YAML 的 单文档约束保持不变,已做反向正控。 回归修复 - 嵌套 Remotion 工程跑 `pnpm install --ignore-workspace` 时,pnpm 12 仍沿 packageManager 向上锚定到仓库根,用本集解析结果覆写根 pnpm-lock.yaml (overrides 全丢)且当场不报错,故障延后到 Docker 才以 ERR_PNPM_LOCKFILE_CONFIG_MISMATCH 现形。v11 对照组无此行为。 - 修复:8 集 + 骨架模板各加 video/pnpm-workspace.yaml(packages: []),钉为独立 workspace 根;已登记进 skeleton.toml frozen 清单受漂移门执法(17→18 文件)。 给嵌套工程补 packageManager pin 无效,此为唯一有效解。 验证 - pnpm -r typecheck 0 error;pnpm test 1152 passed;pnpm build 三个 Next 应用全绿 - 两个 Docker 镜像本地构建成功(frontend 493MB / wiki 24.6MB) - pipeline/tests 261 项全绿;verify_skeleton --strict 未登记漂移 0 处 - 嵌套 install 实测根 lockfile 零变更;globalShims 新默认零副作用 - 回滚实测:pnpm 11 可正常读并保留 v12 多文档 lockfile,回滚只需改回 package.json 取证与复现详见 docs/.agents/issue.md ISSUE-175。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang --- .pre-commit-config.yaml | 3 + .../video/pnpm-workspace.yaml | 4 + .../video/pnpm-workspace.yaml | 4 + .../video/pnpm-workspace.yaml | 4 + .../video/pnpm-workspace.yaml | 4 + .../video/pnpm-workspace.yaml | 4 + .../video/pnpm-workspace.yaml | 4 + .../video/pnpm-workspace.yaml | 4 + .../video/pnpm-workspace.yaml | 4 + apps/negentropy-influence/pipeline/README.md | 5 +- .../templates/video-skeleton/skeleton.toml | 2 + .../video-skeleton/video/pnpm-workspace.yaml | 4 + docker/frontend/Dockerfile | 6 +- docker/wiki/Dockerfile | 7 +- docs/.agents/issue.md | 93 ++++++++++++++++ package.json | 2 +- pnpm-lock.yaml | 101 ++++++++++++++++++ 17 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 apps/negentropy-influence/episodes/claude-code-concurrency-video/video/pnpm-workspace.yaml create mode 100644 apps/negentropy-influence/episodes/claude-code-explained-video/video/pnpm-workspace.yaml create mode 100644 apps/negentropy-influence/episodes/claude-code-memory-video/video/pnpm-workspace.yaml create mode 100644 apps/negentropy-influence/episodes/claude-code-multiagent-video/video/pnpm-workspace.yaml create mode 100644 apps/negentropy-influence/episodes/claude-code-planning-video/video/pnpm-workspace.yaml create mode 100644 apps/negentropy-influence/episodes/experience-era-agents-video/video/pnpm-workspace.yaml create mode 100644 apps/negentropy-influence/episodes/self-evolving-coding-agents-video/video/pnpm-workspace.yaml create mode 100644 apps/negentropy-influence/episodes/self-improving-agents-video/video/pnpm-workspace.yaml create mode 100644 apps/negentropy-influence/pipeline/templates/video-skeleton/video/pnpm-workspace.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ddd2cf8ba..f47806c7a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,6 +24,9 @@ repos: args: [--markdown-linebreak-ext=md] - id: end-of-file-fixer - id: check-yaml + # pnpm 12 起根 pnpm-lock.yaml 是多文档 YAML(packageManagerDependencies 独占首个 + # 文档,见 ISSUE-175);只豁免这份机器产物,手写 YAML 仍受单文档校验约束。 + exclude: ^pnpm-lock\.yaml$ - id: check-toml - id: check-merge-conflict - id: check-added-large-files diff --git a/apps/negentropy-influence/episodes/claude-code-concurrency-video/video/pnpm-workspace.yaml b/apps/negentropy-influence/episodes/claude-code-concurrency-video/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/episodes/claude-code-concurrency-video/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/video/pnpm-workspace.yaml b/apps/negentropy-influence/episodes/claude-code-explained-video/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/episodes/claude-code-explained-video/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/apps/negentropy-influence/episodes/claude-code-memory-video/video/pnpm-workspace.yaml b/apps/negentropy-influence/episodes/claude-code-memory-video/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/episodes/claude-code-memory-video/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/apps/negentropy-influence/episodes/claude-code-multiagent-video/video/pnpm-workspace.yaml b/apps/negentropy-influence/episodes/claude-code-multiagent-video/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/episodes/claude-code-multiagent-video/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/apps/negentropy-influence/episodes/claude-code-planning-video/video/pnpm-workspace.yaml b/apps/negentropy-influence/episodes/claude-code-planning-video/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/episodes/claude-code-planning-video/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/apps/negentropy-influence/episodes/experience-era-agents-video/video/pnpm-workspace.yaml b/apps/negentropy-influence/episodes/experience-era-agents-video/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/episodes/experience-era-agents-video/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/apps/negentropy-influence/episodes/self-evolving-coding-agents-video/video/pnpm-workspace.yaml b/apps/negentropy-influence/episodes/self-evolving-coding-agents-video/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/episodes/self-evolving-coding-agents-video/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/apps/negentropy-influence/episodes/self-improving-agents-video/video/pnpm-workspace.yaml b/apps/negentropy-influence/episodes/self-improving-agents-video/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/episodes/self-improving-agents-video/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/apps/negentropy-influence/pipeline/README.md b/apps/negentropy-influence/pipeline/README.md index aeebde41d..bede1bc36 100644 --- a/apps/negentropy-influence/pipeline/README.md +++ b/apps/negentropy-influence/pipeline/README.md @@ -60,7 +60,7 @@ $P/ │ ├── narration.json # 派生物(build_narration.py 生成) │ └── storyboard.md # 分镜表(镜号↔句 id 区间↔画面↔动效) ├── scripts/*.py # 薄包装 → ../../pipeline/scripts/(保 CLI 契约) -├── video/ # Remotion 独立 pnpm 工程(--ignore-workspace 隔离) +├── video/ # Remotion 独立 pnpm 工程(自带 pnpm-workspace.yaml,钉为独立 workspace 根) └── out/ # 渲染产物(gitignored) ``` @@ -158,6 +158,9 @@ schema、默认值与校验的单一事实源是 [scripts/config.py](./scripts/c 跑完立刻 `uv run --no-project $R/verify_skeleton.py` 确认新集与模板零漂移。 2. `theme.ts` 换本集概念色;`video/src/scenes/*` 与 `Main.tsx` 注册表全部新写。 3. `cd video && pnpm install --ignore-workspace`(必须显式忽略根 workspace;`onlyBuiltDependencies: [esbuild]` 已在 package.json);装完检查根 lockfile 零变更。 + > pnpm 12 起只声明 `--ignore-workspace` 不够:pnpm 仍会沿 `packageManager` 向上锚定到仓库根, + > **覆写根 `pnpm-lock.yaml`** 且当场不报错。隔离由 `video/pnpm-workspace.yaml` 真正兜住 + > ([ISSUE-175](../../../docs/.agents/issue.md))。 > pnpm ≥ 11 会提示 `package.json` 的 `pnpm` 字段不再被读取,并报 `ERR_PNPM_IGNORED_BUILDS: esbuild` > ([ISSUE-076](../../../docs/.agents/issue.md))。**已实测确认对本工程无害**:esbuild 的平台二进制 > 是 optionalDependency,落地不依赖 postinstall —— 旧写法下 `remotion bundle` 端到端通过。 diff --git a/apps/negentropy-influence/pipeline/templates/video-skeleton/skeleton.toml b/apps/negentropy-influence/pipeline/templates/video-skeleton/skeleton.toml index 187e03a82..67cf9655a 100644 --- a/apps/negentropy-influence/pipeline/templates/video-skeleton/skeleton.toml +++ b/apps/negentropy-influence/pipeline/templates/video-skeleton/skeleton.toml @@ -58,6 +58,8 @@ baselineOf = "self-evolution" [classes] frozen = [ "video/.npmrc", + # 与 .npmrc 同属隔离契约:缺此文件时嵌套 install 会覆写根 lockfile(ISSUE-175) + "video/pnpm-workspace.yaml", "video/tsconfig.json", "video/remotion.config.ts", "video/src/index.ts", diff --git a/apps/negentropy-influence/pipeline/templates/video-skeleton/video/pnpm-workspace.yaml b/apps/negentropy-influence/pipeline/templates/video-skeleton/video/pnpm-workspace.yaml new file mode 100644 index 000000000..f2686ac7d --- /dev/null +++ b/apps/negentropy-influence/pipeline/templates/video-skeleton/video/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +# 把本工程钉成它自己的 pnpm workspace 根——这是本文件存在的唯一理由。 +# pnpm 12 起仅靠 `--ignore-workspace` 不够:pnpm 仍会沿 packageManager 向上锚定到仓库根, +# 用本工程的解析结果覆写根 pnpm-lock.yaml(overrides 全丢)且当场不报错。见 ISSUE-175。 +packages: [] diff --git a/docker/frontend/Dockerfile b/docker/frontend/Dockerfile index 20bd78079..2af01bd59 100644 --- a/docker/frontend/Dockerfile +++ b/docker/frontend/Dockerfile @@ -11,13 +11,17 @@ ARG TARGET_SERVICE=negentropy-ui # ── Stage 1: Install dependencies ─────────────────────────────────────────── FROM node:22-slim AS deps -RUN corepack enable && corepack prepare pnpm@latest --activate +RUN corepack enable WORKDIR /app # 先复制 workspace 根文件(利用 Docker 层缓存) COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ +# pnpm 版本单一事实源=根 package.json#packageManager(`corepack install` 不带参数即读本地清单)。 +# 勿改回 `corepack prepare pnpm@latest`:npm 的 latest 与 packageManager 会各自漂移(ISSUE-175)。 +RUN corepack install + # 复制所有 package.json(workspace resolution 需要) COPY packages/agents-chat-core/package.json ./packages/agents-chat-core/package.json COPY apps/negentropy-ui/package.json ./apps/negentropy-ui/package.json diff --git a/docker/wiki/Dockerfile b/docker/wiki/Dockerfile index f22d71aab..d184bbf01 100644 --- a/docker/wiki/Dockerfile +++ b/docker/wiki/Dockerfile @@ -14,12 +14,17 @@ # ── Stage 1: Install dependencies ──────────────────────────────────────────── FROM node:22-slim AS deps -RUN corepack enable && corepack prepare pnpm@latest --activate +RUN corepack enable WORKDIR /app # 复制 workspace 根文件(层缓存) COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ + +# pnpm 版本单一事实源=根 package.json#packageManager(与 docker/frontend/Dockerfile 同构)。 +# 勿改回 `corepack prepare pnpm@latest`:npm 的 latest 与 packageManager 会各自漂移(ISSUE-175)。 +RUN corepack install + # 复制 workspace 内相关 package.json(与 frontend Dockerfile 同集合,确保 frozen-lockfile 解析稳定) COPY packages/agents-chat-core/package.json ./packages/agents-chat-core/package.json COPY apps/negentropy-ui/package.json ./apps/negentropy-ui/package.json diff --git a/docs/.agents/issue.md b/docs/.agents/issue.md index bf0030d53..a226a3b22 100644 --- a/docs/.agents/issue.md +++ b/docs/.agents/issue.md @@ -3745,3 +3745,96 @@ R7 后浏览器对照 Section 2.1 区域发现两类正交缺陷: - **同类问题影响**:所有会改 `sampling_suffix` 的参数(temperature/top-p/top-k/length-penalty/ repetition-penalty/max-mel-tokens)都有同一形态——对成片集做**实验性**参数调整时, 先 `--plan` 看失配面,再决定。 + +## ISSUE-175 pnpm 12 升级:嵌套工程 `install --ignore-workspace` 会**静默覆写根 lockfile**,故障延后到 Docker 才以「overrides 不符」现形(2026-09-01) + +- **问题描述**:把 pnpm 从 11.25.0 升到 12.2.1(`latest` dist-tag 当时仍指向 11.25.0)时踩到四个坑, + 其中第 ④ 个会**破坏数据**:① `packageManager` 的 integrity 编码抄错;② 旧 lockfile 上 + `--frozen-lockfile` 直接硬失败;③ 嵌套工程加 `--frozen-lockfile` 报 + `ERR_PNPM_PACKAGE_MANAGER_NO_IMPORTER`;④ **嵌套工程 `pnpm install --ignore-workspace` + 把仓库根 `pnpm-lock.yaml` 覆写成"只含该嵌套工程"的 lockfile,全部 overrides 丢失**。 +- **表因与根因**: + 1. **integrity 编码不同源**。npm registry 的 `dist.integrity` 是 SRI 标准的 **base64** + (`sha512-9Vymiqy…==`),`packageManager` 字段要的是 corepack 的 **hex** + (`sha512.f55ca68a…`)。二者只差一个分隔符(`-` vs `.`),肉眼极易误判为同一编码。 + 换算:`Buffer.from(b64,'base64').toString('hex')`;**换算方法必须先用在用版本反向复算验证** + (本次用 11.25.0 复算命中仓库原值后才敢写 12.2.1)。corepack 这次报错友好,别的工具未必。 + 2. **v12 新增 `packageManagerDependencies`**:把 pnpm 自身与 8 个平台的 `@pnpm/exe.*` 原生二进制 + (v12 起 CLI 是 Rust 实现)记进 lockfile,落盘为**多文档 YAML**——第 1–100 行是新增的 + packageManager 文档,第 101 行 `---` 之后才是原项目文档(原文档逐字节未变)。 + 关键后果:**旧 lockfile 上 `pnpm install --frozen-lockfile` 会硬失败** + (`Cannot update packageManagerDependencies with "frozen-lockfile"`),即 CI / Docker 的执行路径 + 会当场红。**这一段 lockfile 变更是升级的必要组成,不是可选副产品。** + 3. 同一机制在嵌套工程上翻车两次。嵌套 Remotion 工程不 pin `packageManager`,corepack 沿目录树 + 向上找到仓库根的 pin,于是 pnpm 把**仓库根**当作 lockfile 锚点——`--frozen-lockfile` 下 + 禁止写入且找不到对应 importer,报 ③;**不带 frozen 时它就直接写**,用嵌套工程的解析结果 + 覆盖根 lockfile,这就是 ④。全过程零报错,唯一线索是进度条上一个不起眼的 `../..` 前缀。 + 4. **④ 的症状与病灶相距极远**:根 lockfile 被毁后本地一切照常(node_modules 已就绪), + 直到 `docker build` 才以 `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH`("当前 overrides 配置与 lockfile + 中的值不符")现形——报错指向 overrides,而真正被动过的是 lockfile 的**整体身份**。 +- **实测取证(隔离夹具,非推断)**: + - v12 复现:根 lockfile 128 行 / `overrides=1` / importers=`.`+`pkgs/a` + → 在嵌套目录跑一次 `pnpm install --ignore-workspace` + → 123 行 / **`overrides=0`** / importers=`.`+`episodes/vid`,且嵌套工程自己的 lockfile **未生成**。 + - **v11 对照组:根 lockfile 逐字节不变,嵌套 lockfile 正常生成** ⇒ 确证为 v12 独有回归。 + - **失败的缓解**:给嵌套工程自身补 `packageManager` pin **无效**,根 lockfile 照样被覆写。 +- **处理方式**: + 1. 根 `package.json#packageManager` → `pnpm@12.2.1+sha512.f55ca68a…`(hex)。CI 的 + [`setup-pnpm-ui`](../../.github/actions/setup-pnpm-ui/action.yml) / + [`setup-pnpm-wiki`](../../.github/actions/setup-pnpm-wiki/action.yml) 用不带 version 的 + `pnpm/action-setup@v4` 自动继承,无需改动——**单一事实源在这里兑现了收益**。 + 2. **给 8 集 + 骨架模板的 `video/` 各加一份 `pnpm-workspace.yaml`(`packages: []`)**,把每个嵌套 + 工程钉成它自己的 workspace 根。实测:带不带 `--ignore-workspace`,根 lockfile 均零变更, + 嵌套工程正常生成 22 行单文档 lockfile(不含 packageManagerDependencies,故 8 份既有 + lockfile 无需改动)。该文件已登记进 + [`skeleton.toml`](../../apps/negentropy-influence/pipeline/templates/video-skeleton/skeleton.toml) + 的 `frozen` 清单受漂移门执法(受门 17→18 文件);`scaffold.py` 用 `rglob` 全量复制,新集自动继承。 + 3. 顺带消除两处**第二事实源**:[`docker/frontend/Dockerfile`](../../docker/frontend/Dockerfile) 与 + [`docker/wiki/Dockerfile`](../../docker/wiki/Dockerfile) 原为 `corepack prepare pnpm@latest + --activate`。npm 的 `latest` 与 `packageManager` 是两条独立漂移的轨道,升级后立即分叉 + (该层预热 11.25.0,`pnpm install` 却按 pin 另取 12.2.1)。改为 `corepack enable` + + COPY 清单后 `corepack install`(不带参数=读本地 `packageManager`),层缓存不变。 + 4. lockfile 用非 frozen install 重建(+101 行),并**逐字节校验 doc2 与升级前一致**, + 确保没有夹带依赖重解析。 + 5. 多文档格式撞上 pre-commit 的 `check-yaml`(默认只许单文档),加 + `exclude: ^pnpm-lock\.yaml$` **只豁免这份机器产物**——而非全局开 + `--allow-multiple-documents` 把手写 YAML 的约束一起放掉。已做反向正控: + 给 `pnpm-workspace.yaml` 临时插入第二个文档,门确实报红。 +- **验证结论**:workspace 键校验(v12 把未识别键从静默忽略改为硬报错 + `ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGS`)**通过**,`packages`/`allowBuilds`/`catalog`/`overrides` + 均在册;最担心的 peer 解析 canonical cycle breaking **未触发 re-key**(`Lockfile is up to date`); + `pnpm -r typecheck` 0 error、`pnpm test` 1152 passed、`pnpm build` 三个 Next 应用全绿; + **两个 Docker 镜像本地构建成功**(frontend 493MB / wiki 24.6MB,`corepack install` 正确解析到 + 12.2.1);`pipeline/tests` 261 项全绿、`verify_skeleton --strict` 未登记漂移 0 处; + `globalShims` 新默认(`{node,deno,bun:true}`)**零副作用**——未生成任何全局 shim,`node` 仍首位 + 命中 nvm。另注意 v12 每次 install 会跑一遍 supply-chain policy 校验(根 workspace 1264 entries ≈ 43s), + 是新增的固定开销。 +- **回滚可行性(已实测,勿凭直觉推断)**:先验假设是"多文档 lockfile 会让 v11 读不懂,回滚不对称", + **实测推翻**——隔离工程里用 v12 生成多文档 lockfile 后把 `packageManager` 回退到 11.25.0, + pnpm 11.25.0 `install --frozen-lockfile` 正常通过(`Lockfile is up to date`),且**原样保留** + v12 写入的 packageManager 文档(frozen 与非 frozen 两种写法皆不剥离)。即 v11 对该文档是前向兼容的 + 惰性忽略,回滚只需改回 `package.json` 一处。 +- **后续防范**: + 1. **升级包管理器后,第一个要验的不是"能不能装",而是"lockfile 还是不是原来那个 lockfile"**。 + 本次 `git status` 只显示"pnpm-lock.yaml modified"——与预期中的变更同名,于是被一眼放过; + 真正该做的是 `git diff --stat` 看量级、并核对 `overrides` / `importers` 等结构性锚点仍在。 + [`pipeline/README.md`](../../apps/negentropy-influence/pipeline/README.md) 早有"装完检查根 + lockfile 零变更"的纪律,说明这个危险区**此前已被识别**,只是 v12 把"偶尔"变成了"必然"。 + 2. **`latest` dist-tag 未切换=上游自己说"还不建议默认"**。本次是在 `latest` 仍指向 11.25.0 时 + 抢升(12.0.0 发布 6 天内已迭代到 12.2.1),属知情决策;后续同类升级须显式核对 + `npm view pnpm dist-tags`,把"`latest` 指向哪"作为独立于版本号的判据记录下来。 + 3. **`@latest` 与显式 pin 并存即分叉**——可跨文件复用的类模式。仓库内任何 + `install/prepare @latest` 都应先问"这个工具是否已有单一事实源 pin"。 + 4. **"隔离"不是一个布尔量**。嵌套工程此前有三重隔离声明(`.npmrc` 的 `ignore-workspace=true`、 + 命令行 `--ignore-workspace`、根 workspace glob 不匹配),v12 仍从第四个维度 + (`packageManager` 的目录树向上查找)穿透进来。判断隔离是否成立,要问的是 + "**还有哪些机制会沿目录树向上走**",而不是"我声明了几次隔离"。 + 5. `package.json#pnpm` 字段自 v11 起失效([ISSUE-076](#issue-076-pnpm-v11-升级后-pnpm-install-报-err_pnpm_ignored_builds--packagejsonpnpmoverrides-静默失效2026-05-08)) + 的状况在 v12 **未恶化**,仍是 WARN;[ISSUE-166](#issue-166-err_pnpm_ignored_builds-在-esbuild-上是无害噪声不要为消音改动跨集冻结文件2026-08-21) + "不为消音改动跨集冻结文件"的裁决继续有效——本次新增 `pnpm-workspace.yaml` 是出于 + **数据安全的独立必要性**,故**刻意不**顺手把失效的 `onlyBuiltDependencies` 迁进去: + 那会让已发布集的 esbuild postinstall 从"被拦"变成"执行",是与本次无关的行为变更。 +- **同类问题影响**:与 [ISSUE-076](#issue-076-pnpm-v11-升级后-pnpm-install-报-err_pnpm_ignored_builds--packagejsonpnpmoverrides-静默失效2026-05-08) + (v10→v11)构成升级序列。凡"子目录里跑包管理器"的场景都需重新体检: + [`travel-agent-ui`](../../apps/cognizes/src/cognizes/examples/e2e_travel_agent/frontend/travel-agent-ui) + (同为解耦独立安装,见 `pnpm-workspace.yaml` 内注)尚未验证,下次触碰时须先备份根 lockfile。 diff --git a/package.json b/package.json index d85d2ac22..345d1eb90 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "tsup": "^8.3.5", "typescript": "^5.7.0" }, - "packageManager": "pnpm@11.25.0+sha512.5cde925b4f075f725eb71fbae18a42ffe784524789f19b61c731cb8721ec28aaee160e01a8d5af4fedb2a42cdbf300efe23db356b0d4a17b4d63e11f8ab7c956", + "packageManager": "pnpm@12.2.1+sha512.f55ca68aacb9eb5ab69c66f828a98af89a32286703aef1f8da131ca7eab4d677f34bfa85e186467a919c0799df8b6f4aa240f7dc2919b33b3273190104162616", "engines": { "node": ">=22" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a3d4f992..68378a36c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,3 +1,104 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.2.1 + version: 12.2.1 + +packages: + + '@pnpm/exe.darwin-arm64@12.2.1': + resolution: {integrity: sha512-efC1yfz2xbyHiMLrQAYtnoo5XP20LeAiYYcyL1962qQagZrMXIB0BoQYeZoPTLggLh0Q4MD7jpZUInU5fWxCgQ==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.2.1': + resolution: {integrity: sha512-0bloFmxrvYY5LYex5V6SZySwg+egBk0pOW+YUeOm9fiG2U1wv+qRVLBOUvIzLAeJE7vBlAzxUwXkQAYtr12n+Q==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.linux-arm64-musl@12.2.1': + resolution: {integrity: sha512-AZl7apVZC4TV1/vTp/bS16XUsHeqx9AHsU4V55joiGR+iR6vl+WF0WlGvGCTjqjICS4q/kThpgNrtG8aqQ9seQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.2.1': + resolution: {integrity: sha512-S36+tMEGrPz3nwtfFU9kYxbHoi8sV7WJfMO9njEL4jRaShTNaLRERMAqyI2DUCiA5QCr6/a50lt4+pKCV2mCvw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.2.1': + resolution: {integrity: sha512-4MQClkAk1em55LFZJpeIqb7j+gEqCgX8yC42j9DFWL42q1uMX1e1tvTy2Dtd4NoWQqgdkLxOStZ+D9CYC+28ow==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.2.1': + resolution: {integrity: sha512-A/AlR71Leph7qgB3ThSU9yU/vLfre4HmP6nbt3/qt51fzAjQ0xuq7j+cw0f1yDskzOsGzmo5uD6ruefCwLK5uQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.2.1': + resolution: {integrity: sha512-TEGUmroT6NGfo2O2+tHK9Zr1lhP1zLzpwx+QAQV19l8V4ljUoMfz8Os5V9zzNCtIpzyKcK7SuG7fkVbxFgRMzg==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.2.1': + resolution: {integrity: sha512-V2Q+AF8fCsw8/CC3bWF8kopOmH0aAjgk9m7H7uAQNulOYIJs9YJBiAlgByswmt+igV9axc4dBI//ZlK2NKpDcw==} + cpu: [x64] + os: [win32] + + pnpm@12.2.1: + resolution: {integrity: sha512-9Vymiqy561q2nGb4KKmK+JoyKGcDrvH42hMcp+q01nfzS/qF4YZGepGcB5nfi29KokD33CkZszsycxkBBBYmFg==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.darwin-arm64@12.2.1': + optional: true + + '@pnpm/exe.darwin-x64@12.2.1': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.2.1': + optional: true + + '@pnpm/exe.linux-arm64@12.2.1': + optional: true + + '@pnpm/exe.linux-x64-musl@12.2.1': + optional: true + + '@pnpm/exe.linux-x64@12.2.1': + optional: true + + '@pnpm/exe.win32-arm64@12.2.1': + optional: true + + '@pnpm/exe.win32-x64@12.2.1': + optional: true + + pnpm@12.2.1: + optionalDependencies: + '@pnpm/exe.darwin-arm64': 12.2.1 + '@pnpm/exe.darwin-x64': 12.2.1 + '@pnpm/exe.linux-arm64': 12.2.1 + '@pnpm/exe.linux-arm64-musl': 12.2.1 + '@pnpm/exe.linux-x64': 12.2.1 + '@pnpm/exe.linux-x64-musl': 12.2.1 + '@pnpm/exe.win32-arm64': 12.2.1 + '@pnpm/exe.win32-x64': 12.2.1 + +--- lockfileVersion: '9.0' settings: From 494aef70543e5b81b7816399178fd9944c5ccf18 Mon Sep 17 00:00:00 2001 From: ThreeFish Date: Tue, 1 Sep 2026 16:45:48 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs(pnpm-upgrade):=20=E5=90=8C=E6=AD=A5=20?= =?UTF-8?q?frozen=20=E8=AE=A1=E6=95=B0=E8=87=B3=2015=20=E5=B9=B6=E8=90=BD?= =?UTF-8?q?=E5=AE=9A=20travel-agent-ui=20=E7=9A=84=E5=AE=9E=E6=B5=8B?= =?UTF-8?q?=E9=A3=8E=E9=99=A9=E7=BB=93=E8=AE=BA;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 评审收口,两处均为 ISSUE-175 变更的文档侧遗留: 1. frozen 计数与 SSOT 对齐(14 → 15)。新增 video/pnpm-workspace.yaml 使 skeleton.toml 的 frozen 清单增至 15 条,但三处散文未同步;scaffold.py 的 计数由清单动态派生(len(skel['classes']['frozen'])),运行时打印 15 而 文档写 14,且无测试或漂移门覆盖这些散文数字。改 pipeline/README.md 两处 与 skills/06 一处。skeleton.toml:50 的「14 个」是带日期的历史实测记录 (2026-08 两系列 4 集观测),非当前计数声明,保持原样。 2. travel-agent-ui 从「尚未验证」升级为实测结论。完整 5 member 夹具复现表明 风险形态与最初推断不同:根 pnpm-workspace.yaml 内注教的裸 pnpm install 不覆写根 lockfile(仅装到仓库根、子目录不生成 node_modules,示例跑不起来 但无数据损失);真正覆写的是 --ignore-workspace——实测根 lockfile 12991 → 12705 行、overrides 归零。故危险路径是「发现裸 install 没装到 子目录 → 照 8 集习惯补 --ignore-workspace」。该目录连 .npmrc 都没有, 三重隔离一条不具备,是本仓唯一还敞着的同款入口;修复方案照搬本次即可, 因不属 pnpm 升级的必要变更而未纳入。 另:评审中曾判定「新增 pnpm-workspace.yaml 使嵌套 install 退出码由 0 变 1、 影响 pipeline.py render 控制流」,经 pnpm 11 基线复测推翻——升级前该命令 即为 EXIT=1(ERR_PNPM_IGNORED_BUILDS),属 ISSUE-076/166 既存噪声,本次 未引入回归,故不作改动。 验证:verify_skeleton 未登记漂移 0 处;pipeline/tests 261 项全绿; pre-commit 对三份改动文件全通过。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang --- apps/negentropy-influence/pipeline/README.md | 4 ++-- .../pipeline/skills/06-remotion-implementation.md | 2 +- docs/.agents/issue.md | 14 ++++++++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/negentropy-influence/pipeline/README.md b/apps/negentropy-influence/pipeline/README.md index bede1bc36..a5cda4e52 100644 --- a/apps/negentropy-influence/pipeline/README.md +++ b/apps/negentropy-influence/pipeline/README.md @@ -137,7 +137,7 @@ schema、默认值与校验的单一事实源是 [scripts/config.py](./scripts/c ## 四、复用边界(显式权衡) - **Python 脚本:集中共享(SSOT)**——三个纯文本变换工具,跨集零差异,中心化防 split-brain。 -- **Remotion 工程原语:复制适配,不做共享包**——`timing.ts` / `Subtitle` / `cards.tsx` / `theme.ts` 等每集复制后按本集视觉契约修改。理由:每集工程须保持 pnpm `--ignore-workspace` 独立可渲染(嵌套 workspace 隔离 + Remotion 版本自由),共享 TS 包会把「一集的视觉改动」泄漏进其他集。**复制源头是 [templates/video-skeleton/](./templates/video-skeleton/)**(14 个 frozen 文件 + skeleton.toml 档位清单),新集用 `scaffold.py` 实例化、「改任何一处须同步」由 `verify_skeleton.py` 机器执法——此前「以任一既有集为模板」的说法等于给 391 行冻结基建设 4 个同权真理声明者,且纸面义务从未被执行过(详见 skeleton.toml 内注)。同类做法:`go mod vendor` + `go mod verify`(物理副本 + 校验门)、Copier(模板 + 应答记录)。 +- **Remotion 工程原语:复制适配,不做共享包**——`timing.ts` / `Subtitle` / `cards.tsx` / `theme.ts` 等每集复制后按本集视觉契约修改。理由:每集工程须保持 pnpm `--ignore-workspace` 独立可渲染(嵌套 workspace 隔离 + Remotion 版本自由),共享 TS 包会把「一集的视觉改动」泄漏进其他集。**复制源头是 [templates/video-skeleton/](./templates/video-skeleton/)**(15 个 frozen 文件 + skeleton.toml 档位清单),新集用 `scaffold.py` 实例化、「改任何一处须同步」由 `verify_skeleton.py` 机器执法——此前「以任一既有集为模板」的说法等于给 391 行冻结基建设 4 个同权真理声明者,且纸面义务从未被执行过(详见 skeleton.toml 内注)。同类做法:`go mod vendor` + `go mod verify`(物理副本 + 校验门)、Copier(模板 + 应答记录)。 - **每集视觉契约独立设计**(色彩语义映射到本集核心概念),但底层规范复用:深色底 `#0E1116` 系、警示红 `#FF5C5C`、确认绿 `#7ED321`、金句卡衬线体、公式只作角标彩蛋。 ## 五、音画同步机制(零手工对轨) @@ -153,7 +153,7 @@ schema、默认值与校验的单一事实源是 [scripts/config.py](./scripts/c uv run --no-project $R/scaffold.py -video --title "本集标题" \ --ref <样本名> --ref-sha1 <12位指纹> --style <档名> ``` - scaffold 复制 14 个 frozen 文件 + 渲染 4 个模板(package.json / theme.ts / pipeline.toml / README), + scaffold 复制 15 个 frozen 文件 + 渲染 4 个模板(package.json / theme.ts / pipeline.toml / README), **刻意不生成 scenes/**(样例留在模板里)、不改根 `.gitignore`(已通配到分集级)、不写 series.json。 跑完立刻 `uv run --no-project $R/verify_skeleton.py` 确认新集与模板零漂移。 2. `theme.ts` 换本集概念色;`video/src/scenes/*` 与 `Main.tsx` 注册表全部新写。 diff --git a/apps/negentropy-influence/pipeline/skills/06-remotion-implementation.md b/apps/negentropy-influence/pipeline/skills/06-remotion-implementation.md index 3c5ae49d9..302ac5c90 100644 --- a/apps/negentropy-influence/pipeline/skills/06-remotion-implementation.md +++ b/apps/negentropy-influence/pipeline/skills/06-remotion-implementation.md @@ -20,7 +20,7 @@ uv run --no-project $R/verify_skeleton.py --strict # 有未登记漂移即失 要点(详见 skeleton.toml 内注): -- **A 档 · frozen 逐字节保留**(14 个文件,含 `src/timing.ts` 的 computeTimeline + beatWindow + SCENE_FADE_FRAMES、`@remotion/media` 的 NarrationAudio、fitText 的 Subtitle、幕间呼吸淡入淡出的 SceneFade、cards、三份薄包装与全部工程配置)。改任何一处 = 改模板 + 改各集,`--strict` 会盯住。 +- **A 档 · frozen 逐字节保留**(15 个文件,含 `src/timing.ts` 的 computeTimeline + beatWindow + SCENE_FADE_FRAMES、`@remotion/media` 的 NarrationAudio、fitText 的 Subtitle、幕间呼吸淡入淡出的 SceneFade、cards、三份薄包装与全部工程配置)。改任何一处 = 改模板 + 改各集,`--strict` 会盯住。 - **B 档 · overridable**:`src/timing.json`(时序常数 SSOT——timing.ts 与 Python 侧共读同一文件,**改常量只改此处**)。覆写许可存在但四集从未行使过。 - **regioned**:`src/Main.tsx` 区外冻结(每集内容只有场景 import 与 SCENE_COMPONENTS 注册表);**structured**:`package.json` 门住依赖零漂移、忽略 name/description。 - **每集改写**:`src/design/theme.ts`(本集色板)、`src/scenes/*`(全部重写;骨架样例见模板里的 scenes-EXAMPLE.tsx.txt)。 diff --git a/docs/.agents/issue.md b/docs/.agents/issue.md index a226a3b22..59bd27794 100644 --- a/docs/.agents/issue.md +++ b/docs/.agents/issue.md @@ -3835,6 +3835,16 @@ R7 后浏览器对照 Section 2.1 区域发现两类正交缺陷: **数据安全的独立必要性**,故**刻意不**顺手把失效的 `onlyBuiltDependencies` 迁进去: 那会让已发布集的 esbuild postinstall 从"被拦"变成"执行",是与本次无关的行为变更。 - **同类问题影响**:与 [ISSUE-076](#issue-076-pnpm-v11-升级后-pnpm-install-报-err_pnpm_ignored_builds--packagejsonpnpmoverrides-静默失效2026-05-08) - (v10→v11)构成升级序列。凡"子目录里跑包管理器"的场景都需重新体检: + (v10→v11)构成升级序列。凡"子目录里跑包管理器"的场景都需重新体检。 [`travel-agent-ui`](../../apps/cognizes/src/cognizes/examples/e2e_travel_agent/frontend/travel-agent-ui) - (同为解耦独立安装,见 `pnpm-workspace.yaml` 内注)尚未验证,下次触碰时须先备份根 lockfile。 + (同为解耦独立安装,见 `pnpm-workspace.yaml` 内注)**已用完整 5 member 夹具实测**,是本仓 + 当前唯一还敞着的同款入口——它连 `.npmrc` 都没有,三重隔离声明一条不具备(8 集 `video/` 至少还有): + - 根 `pnpm-workspace.yaml` 内注教的**裸 `pnpm install` 不覆写**根 lockfile,只是 + `Scope: all 5 workspace projects` 装到仓库根、子目录不生成 `node_modules/` + ——**该示例按文档跑不起来,但无数据损失**。 + - 覆写根 lockfile 的是 **`pnpm install --ignore-workspace`**:实测 12991 行 → 12705 行、 + `overrides` 归零、子目录不生成 lockfile,与 8 集 `video/` 同一形态,线索同样只有 + 进度条上的 `../../../../../../../..`。 + - 故危险路径是"**发现裸 install 没装到子目录 → 照 8 集的习惯补 `--ignore-workspace`**"。 + 修复照搬本次方案(补一份 `packages: []` 的 `pnpm-workspace.yaml`)即可,因不属 pnpm 升级 + 的必要变更而未纳入本次改动;触碰该示例前先补文件,勿先跑安装。