Merge branch 'worktree/ci-native-windows-coverage-20260808' into worktree/ci-native-windows-multicore-20260809

# Conflicts:
#	.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml
This commit is contained in:
Tianyi Cui
2026-08-09 21:56:52 +08:00
791 changed files with 1930 additions and 1829 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-17-ts-build-config.md
2026-06-17-ts-build-config.md: f25731921aaa6da7a4c9760244f73dc0670dc1d3
2026-06-17-ts-build-config.zh.md: 80b051cd5fb76672d4c056d1b6321029897225e8
2026-06-17-ts-build-config.md: a44e86844113250c8b99575102d7ea2e611e97ed
2026-06-17-ts-build-config.zh.md: 4e98717fd843c93696402b71e8a41163c70e7283

View File

@@ -13,13 +13,13 @@ The current TypeScript build and typecheck setup had these issues:
- `build` used `tsc` to transform `.ts` to `.d.ts` files for packages under `packages/<group>/<pkg>` and `vendor/*`, and then used `tsdown` to transform `.ts` to bundled `.js` files. This made two tools do TypeScript transform.
- `typecheck` tended to validate packages, vendor source, examples, tests, and scripts through one root typecheck config.
The goal is to make build and typecheck use matching tsconfig boundaries and TypeScript resolution/transform behavior. Build should generate `.js`, `.d.ts`, `.js.map`, and `.d.ts.map` through one compiler and config, so publish output and type validation stay consistent.
Build and typecheck use matching tsconfig boundaries and TypeScript resolution/transform behavior. Build generates `.js`, `.d.ts`, `.js.map`, and `.d.ts.map` through one compiler and config, so publish output and type validation stay consistent.
Validation found several concrete technical issues and possible routes:
Concrete constraints:
- `tsdown` uses `oxc` to transform TypeScript, which is not the same behavior as `tsc`.
- Bundled `.d.ts` emitted by `tsdown` conflicts with Cordis' internal relative module augmentation shape.
- The tsc output is affected by `allowImportingTsExtensions`, so we need to ensure that generated `.js` files do not import `.ts` files and generated `.d.ts` files keep explicit relative specifiers that NodeNext/Node16 accepts. Therefore, in-package relative imports use explicit `.ts` specifiers in TypeScript source and `rewriteRelativeImportExtensions` rewrites those specifiers to `.js` in emitted JS.
- The tsc output is affected by `allowImportingTsExtensions`: generated `.js` files must not import `.ts` files, and generated `.d.ts` files must keep explicit relative specifiers that NodeNext/Node16 accepts. Therefore, in-package relative imports use explicit `.ts` specifiers in TypeScript source and `rewriteRelativeImportExtensions` rewrites those specifiers to `.js` in emitted JS.
- Bundled `.js` emitted by `tsdown` is not the same behavior as per-file `.js` emitted by `tsc -b`, such as decorator transform behavior.
- `vendor/*/src`, examples, tests, and scripts cannot all be plain-included in one root strict program.
- Directly typechecking `vendor/*/src` under the root strict config triggers many type errors outside this project's ownership.

View File

@@ -13,13 +13,13 @@ Status: implemented
- `build` 使用 `tsc``packages/<group>/<pkg>``vendor/*` 下的 `.ts` 转换为 `.d.ts` 文件,然后使用 `tsdown``.ts` 转换为打包后的 `.js` 文件。这导致两个工具各自执行 TypeScript 转换。
- `typecheck` 倾向于通过一个根目录的类型检查配置来校验包package、vendor 源码、示例、测试和脚本。
目标是让构建与类型检查使用一致的 tsconfig 边界和 TypeScript 解析/转换行为。构建通过单一编译器和配置生成 `.js``.d.ts``.js.map``.d.ts.map`,使发布产物与类型校验保持一致。
构建与类型检查使用一致的 tsconfig 边界和 TypeScript 解析/转换行为。构建通过单一编译器和配置生成 `.js``.d.ts``.js.map``.d.ts.map`,使发布产物与类型校验保持一致。
验证过程中发现了若干具体的技术问题和可能的路径
具体约束
- `tsdown` 使用 `oxc` 进行 TypeScript 转换,其行为与 `tsc` 不同。
- `tsdown` 输出的打包 `.d.ts` 与 Cordis 内部的相对模块增强module augmentation结构冲突。
- tsc 的输出受 `allowImportingTsExtensions` 影响,因此需要确保生成的 `.js` 文件不导入 `.ts` 文件,且生成的 `.d.ts` 文件保留 NodeNext/Node16 接受的显式相对说明符。为此,包内相对导入在 TypeScript 源码中使用显式 `.ts` 说明符,由 `rewriteRelativeImportExtensions` 在输出的 JS 中将其重写为 `.js`
- tsc 的输出受 `allowImportingTsExtensions` 影响生成的 `.js` 文件不导入 `.ts` 文件,且生成的 `.d.ts` 文件必须保留 NodeNext/Node16 接受的显式相对说明符。为此,包内相对导入在 TypeScript 源码中使用显式 `.ts` 说明符,由 `rewriteRelativeImportExtensions` 在输出的 JS 中将其重写为 `.js`
- `tsdown` 输出的打包 `.js``tsc -b` 逐文件输出的 `.js` 行为不同,例如装饰器转换行为。
- `vendor/*/src`、示例、测试和脚本无法全部以 plain-include 方式纳入一个根目录的严格程序。
- 在根目录严格配置下直接对 `vendor/*/src` 做类型检查,会触发大量不属于本项目所有权范围的类型错误。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-18-markdown-cross-link-lint.md
2026-06-18-markdown-cross-link-lint.md: 21c6884d3fd891794a11125a9aa51ac2bcb29059
2026-06-18-markdown-cross-link-lint.zh.md: 444cf6eb97d95049d4e6b7bb5f44138051f6577a
2026-06-18-markdown-cross-link-lint.md: 37e0a41c08e068d6b3128300f078c8e417f1f337
2026-06-18-markdown-cross-link-lint.zh.md: 457cc8b71ac91e5b5b9ae7ad8fb3b99dfd8ce0c7

View File

@@ -28,6 +28,6 @@ The gate now also checks `#fragment` anchors on Markdown targets — same-file a
## Consequences
- Renames and moves that orphan a cross-link fail `doc-sync` and CI instead of waiting for a reader to click a dead link. This made the Agent Note reorganization that introduced the gate self-verifying: the same PR that rewrote forty links also added the check that proves none dangle.
- Renames and moves that orphan a cross-link fail `doc-sync` and CI instead of waiting for a reader to click a dead link. This made the Agent Note reorganization that introduced the gate self-verifying: the check proves none of its own rewritten links dangle.
- One more fast tsx script in the `doc-sync` chain; no new dependency (the mdast/GFM stack is already in devDependencies for `verify-md-wrap`).
- The convention this enforces — cross-reference docs by machine-checkable relative link, never by bare prose or a number — is documented in [docs/AGENTS.md](../../../../docs/AGENTS.md) so authors know the gate exists and why.

View File

@@ -28,6 +28,6 @@ Status: implemented
## 后果
- 造成交叉链接失效的重命名与移动会直接使 `doc-sync` 和 CI 失败,而不是等读者点击死链才暴露。由此,引入该门禁的 Agent Note 重组具备自校验能力:同一个 PRPull Request在改写 40 条链接的同时,也添加了证明这些链接均未悬空的检查
- 造成交叉链接失效的重命名与移动会直接使 `doc-sync` 和 CI 失败,而不是等读者点击死链才暴露。由此,引入该门禁的 Agent Note 重组具备自校验能力:该检查证明其自身改写的链接均未悬空。
- `doc-sync` 链中多了一个快速 tsx 脚本无新增依赖mdast/GFM 技术栈已作为 `verify-md-wrap` 的 devDependencies 存在)。
- 该门禁强制执行的约定是:文档交叉引用必须使用可机械检查的相对链接,绝不能只写纯文本或编号。[docs/AGENTS.md](../../../../docs/AGENTS.md)记录了这项约定,使作者了解该门禁及其理由。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.md
2026-06-20-core-data-structures-catalog.md: 2f2e1fe43e29c972577f6b31fcd2f8249229779f
2026-06-20-core-data-structures-catalog.zh.md: e2653a8248f6be24699dc55196e64231280cb7f9
2026-06-20-core-data-structures-catalog.md: 957d3a31cf690b3f0fb821b69bbc3d6d5005b1d7
2026-06-20-core-data-structures-catalog.zh.md: aec1489695f8400768c958426a9d6a8d4061db0c

View File

@@ -18,7 +18,7 @@ A new `docs/subsystems/` folder catalogs the vocabulary, with a new `verify-type
> **Superseded as the page-scoping rule** by [package-anchored subsystem pages](2026-08-03-package-anchored-subsystem-pages.md): each page now anchors to the package group that declares its vocabulary. The `ts type-equiv` mechanism below remains current.
The scoping line was not picked top-down; it was discovered by testing candidate definitions against concrete borderline types until one rule survived every case. The decisive test was `BashExecRequest`/`BashExecSpec`/`BashRunResult`: bash is a capability *seam*, not part of the agent-loop spine, so if those are "core" then "core" means *all cross-package vocabulary* and the catalog is a flat dump; if they are not, "core" means *the central spine* and bash vocabulary belongs on its own subsystem page. The latter won, which set the whole structure: a **tiered folder**, not a flat document.
The decisive test for the scoping line is `BashExecRequest`/`BashExecSpec`/`BashRunResult`: bash is a capability *seam*, not part of the agent-loop spine, so if those are "core" then "core" means *all cross-package vocabulary* and the catalog is a flat dump; if they are not, "core" means *the central spine* and bash vocabulary belongs on its own subsystem page. The latter won, which set the whole structure: a **tiered folder**, not a flat document.
The rule that settled the remaining cases: ***the type you write, hold, or receive is core; the machinery that types it, renders it, or persists it is a subsystem-page detail.*** Worked through:
@@ -50,8 +50,6 @@ The durability requirement was specific: the doc shows the **literal** current t
## Verification lesson
The spine-vs-subsystem rule was tested against `BashExecRequest`, tool schemas and definitions, the schema DSL, presentation types, and the session/persistence split before adoption.
`verify-type-equiv` must scan the complete Markdown scope, not only manifest-named documents. Otherwise an unmanifested `type-equiv` block escapes the claimed one-to-one check. The gate therefore reports such blocks as orphans. This Agent Note records that fail-closed scan rule together with the spine-vs-subsystem and verbatim-match decisions; the generated Cordis catalog has the symmetric design record in [its archived Agent Note](../../archived/process/2026-06-20-generated-cordis-catalog.md).
## Consequences

View File

@@ -18,7 +18,7 @@ Status: implemented
> **作为页面范围界定规则已被取代**,见[按包锚定的子系统页面](2026-08-03-package-anchored-subsystem-pages.md):每页现在锚定到声明其词汇的包分组。下文的 `ts type-equiv` 机制仍然有效。
范围界定并非自上而下拍定,而是将候选定义逐一对照具体的边界类型反复测试,直到一条规则在所有案例中都成立。决定性测试是 `BashExecRequest`/`BashExecSpec`/`BashRunResult`bash 是一个能力 *seam*,不属于 agent loop智能体循环主干如果这些算「核心」那么「核心」就意味着*所有跨包词汇*,目录沦为平铺罗列;如果不算,「核心」就意味着*中央主干*bash 词汇归入其自身的子系统页面。后者胜出,由此确定了整体结构:一个**分层文件夹**,而非一份平铺文档。
范围界定决定性测试是 `BashExecRequest`/`BashExecSpec`/`BashRunResult`bash 是一个能力 *seam*,不属于 agent loop智能体循环主干如果这些算「核心」那么「核心」就意味着*所有跨包词汇*,目录沦为平铺罗列;如果不算,「核心」就意味着*中央主干*bash 词汇归入其自身的子系统页面。后者胜出,由此确定了整体结构:一个**分层文件夹**,而非一份平铺文档。
确定其余案例的规则是:***你编写、持有或接收的类型是核心;为其提供类型推导、渲染或持久化的机制是子系统页面细节。*** 逐一验证如下:
@@ -50,8 +50,6 @@ Status: implemented
## 验证教训
主干与子系统规则在采纳前经过了 `BashExecRequest`、工具 schema 与定义、schema DSL、展示类型以及会话/持久化拆分的逐一测试。
`verify-type-equiv` 必须扫描完整的 Markdown 范围,而不仅是 manifest 点名的文档。否则,未列入清单的 `type-equiv` 块就会逃过所宣称的一一检查。因此,门禁会将此类块报告为未列入清单的块。本 Agent Note 将这条默认拒绝放行的扫描规则,连同主干与子系统的分界决策及逐字匹配决策一并记录;生成的 Cordis 目录在[其已归档的 Agent Note](../../archived/process/2026-06-20-generated-cordis-catalog.md) 中有对称的设计记录。
## 后果

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-02-bilingual-docs-and-pairing-gate.md
2026-07-02-bilingual-docs-and-pairing-gate.md: 930efd3a2df8ad99b3c209c6084436ab6b27dc0a
2026-07-02-bilingual-docs-and-pairing-gate.zh.md: 2e288a29e9484683b9961d1e420a6e92ca05e5fe
2026-07-02-bilingual-docs-and-pairing-gate.md: 9e6611aa8391e1478603bedb7d2b96fdd9a8bad2
2026-07-02-bilingual-docs-and-pairing-gate.zh.md: 654d265f3e8b396e23a49fd53e522826a7adb2ff

View File

@@ -23,7 +23,7 @@ The verification contract covers each boundary independently. `verify-translatio
## Alternatives considered
- **English as the canonical source with a fingerprint inside the translation** — the design first proposed for this Agent Note: `.zh.md` files carried an HTML comment recording the English source's blob hash, and translation flowed EN → ZH only. Revised in review: the team wants Chinese-first authoring (write and review a Chinese Agent Note, then translate to English) with the two languages holding equal authority, which a one-directional canonical model cannot express. The sidecar record covering BOTH sides replaced the in-file one-directional fingerprint; the blob-hash mechanics survived unchanged.
- **English as the canonical source with a fingerprint inside the translation** — `.zh.md` files would carry an HTML comment recording the English source's blob hash, and translation would flow EN → ZH only. Rejected: the team wants Chinese-first authoring (write and review a Chinese Agent Note, then translate to English) with the two languages holding equal authority, which a one-directional canonical model cannot express. The sidecar record covering BOTH sides replaced the in-file one-directional fingerprint; the blob-hash mechanics survived unchanged.
- **Locale directories (`docs/en/` + `docs/zh/`, the Kubernetes/ECharts model)** — rejected: this repo has no docs-site framework to map locales to routes, moving every English file would churn every existing cross-reference, and `verify-md-links`/`verify-doc-refs` would need path-mapping logic instead of working unchanged.
- **A separate translation repo (the PingCAP `docs`/`docs-cn` model)** — rejected: right for a docs product with independent release trains, overkill for a monorepo's own documentation; it also puts the translation outside the reach of this repo's gates.
- **Interleaved bilingual files (single file, both languages)** — rejected: doubles every diff, breaks the one-line-per-paragraph convention's diff ergonomics, and makes partial inconsistency invisible.

View File

@@ -23,7 +23,7 @@ Status: implemented
## 曾考虑的替代方案
- **英文为正典源、指纹放在译文内**本 Agent Note 最初提出的设计:`.zh.md` 文件携带一条 HTML 注释记录英文源的 blob hash翻译只沿 EN → ZH 单向流动。评审中修订:团队需要中文先行的撰写方式(先写、先审中文 Agent Note再译英文两种语言同权而单向正典模型无法表达这一点。覆盖**两侧**的伴随记录取代了文件内的单向指纹blob hash 的机制本身保持不变。
- **英文为正典源、指纹放在译文内**`.zh.md` 文件携带一条 HTML 注释记录英文源的 blob hash翻译只沿 EN → ZH 单向流动。否决:团队需要中文先行的撰写方式(先写、先审中文 Agent Note再译英文两种语言同权而单向正典模型无法表达这一点。覆盖**两侧**的伴随记录取代了文件内的单向指纹blob hash 的机制本身保持不变。
- **语言目录(`docs/en/` + `docs/zh/`Kubernetes/ECharts 模式)**:否决。本仓库没有将 locale 映射到路由的文档站框架;如果移动所有英文文件,所有既有交叉引用都要随之修改;且 `verify-md-links`/`verify-doc-refs` 将需要路径映射逻辑,而非原样工作。
- **独立翻译仓库PingCAP `docs`/`docs-cn` 模式)**:否决。适合有独立发布节奏的文档产品,对 monorepo 自身的文档而言过重;还会把译文置于本仓库门禁触及不到的地方。
- **中英混排单文件(一个文件、两种语言)**:否决。每个 diff 都翻倍,破坏一段一行约定的 diff 易读性,且局部不一致不可见。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md
2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md: 3d35d532216c1644f0d65039574453752e9be7ac
2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md: 9e74a0149c9dcd17a88e9ff74a4b29fdc8916f32
2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md: 499141ca6a3703a12d10c195b732441d49419599
2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md: c57b7d32cd7204a845ab1884e9ae0b9045749a1c

View File

@@ -21,7 +21,7 @@ Outside `landlock-run.yml`, each workflow that installed pnpm hand-provisioned i
- **Keep the hand-rolled steps.** They worked, but they were drifting copies of setup boilerplate, and the corepack dependency was a known future break.
- **Convert the enterprise jobs' caching to `cache: pnpm`.** Rejected: the restore-only asymmetry is a documented latency decision in `ci.yml`'s comments; erasing it to unify tooling inverts the priority.
- **Convert serial-linux's store cache.** Rejected during implementation: the original proposal counted serial-linux among the symmetric setups, but its cache step is the producer half of the enterprise jobs' restore-only pairing — moving it to `setup-node`'s key format is the enterprise conversion by another route.
- **Stop at the cache-bearing workflows and leave the other `corepack enable` sites.** Rejected on review follow-up: provisioning and caching are separable concerns, and leaving corepack in the cache-less jobs kept the future break and two provisioning idioms for no benefit.
- **Stop at the cache-bearing workflows and leave the other `corepack enable` sites.** Rejected: provisioning and caching are separable concerns, and leaving corepack in the cache-less jobs kept the future break and two provisioning idioms for no benefit.
- **Rely on the runner image's Yarn.** Rejected: the hosted image exposes Yarn 1.22 after Corepack is removed, while the generated-project e2e requires Yarn 2 or newer. A locked root dev dependency makes that coverage independent of runner image contents.
- **A composite action wrapping action-setup + setup-node.** Rejected for now: the remaining per-job variation (node-version matrices, per-platform conditional caching, the restore-only pairing) is deliberate policy, not boilerplate — a wrapper would grow mirroring inputs or flatten a real asymmetry, and the two-line pair is already near the floor.

View File

@@ -21,7 +21,7 @@ Status: implemented
- **保留手写步骤。** 它们能用,但那是会各自漂移的设置样板副本,而且对 corepack 的依赖是已知的未来失效点。
- **把企业作业的缓存也转换成 `cache: pnpm`。** 否决:只恢复不上传的不对称是 `ci.yml` 注释中有记录的延迟决策;为统一工具而抹掉它,属于颠倒优先级。
- **转换 serial-linux 的 store 缓存。** 实现期间否决:原提案曾把 serial-linux 计入对称设置,但其缓存步骤是企业作业只恢复不上传配对中的生产者一端——把它改成 `setup-node` 的键格式,等于换条路径做了企业作业的转换。
- **只转换带缓存的工作流,留下其余出现 `corepack enable` 的位置。** 评审跟进时否决:提供 pnpm 与缓存是可分离的关注点,在无缓存作业里留下 corepack 只会保留未来失效点和两套并存的提供方式,毫无收益。
- **只转换带缓存的工作流,留下其余出现 `corepack enable` 的位置。** 否决:提供 pnpm 与缓存是可分离的关注点,在无缓存作业里留下 corepack 只会保留未来失效点和两套并存的提供方式,毫无收益。
- **依赖 runner 镜像自带的 Yarn。** 否决Corepack 移除后,托管镜像提供的是 Yarn 1.22,而 generated-project e2e 要求 Yarn 2 或更高版本。锁定版本的根开发依赖让该项覆盖率不再受 runner 镜像内容影响。
- **用一个组合 action 包装 action-setup + setup-node。** 暂不采纳剩余的按作业差异node 版本矩阵、按平台的条件缓存、只恢复不上传配对)是刻意的政策而非样板——包装层要么不得不增加与这些差异一一对应的输入,要么抹平一处真实的不对称,而两行的组合已接近下限。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-30-generated-third-party-notices.md
2026-07-30-generated-third-party-notices.md: f2591c8d0c7d4b83ecc96bf237ded667e322d55c
2026-07-30-generated-third-party-notices.zh.md: 97b62223c0f1e5d179e58f27a01251d8af43357b
2026-07-30-generated-third-party-notices.md: 58e3fd5007a16d6391956f423d1ec91e851df295
2026-07-30-generated-third-party-notices.zh.md: 834ec4ebc349bdee37b0873109de566223bb7d48

View File

@@ -16,7 +16,7 @@ A hand-written inventory answers none of those durably. Roughly a hundred rows o
**Freshness is maintained, not merely enforced.** A pre-commit job regenerates the file and stages it whenever a generator input is staged — any manifest, a workspace declaration, the root lock file, `vendor/README.md`, a `pyproject.toml`, the generator itself, or the script holding the build-time pin — so an unrelated dependency edit never has to come back and rerun a generator. The committed bytes are then asserted inside [`scripts/gen-third-party-notices.spec.ts`](../../../../scripts/gen-third-party-notices.spec.ts), which the test lane already runs — the check adds no gate process, no scheduler slot, and no separate CI step. `pnpm run verify-third-party-notices` remains available for a standalone check.
One trigger gap is accepted rather than worked around: lefthook inspects only files present on disk, so **deleting** a manifest runs no job, and removing a package reaches the assertion in the test lane instead. Reconstructing the staged file list to include deletions was tried and does not work — lefthook filters the list against the working tree either way. The assertion is the backstop for exactly this case.
One trigger gap is accepted rather than worked around: lefthook inspects only files present on disk, so **deleting** a manifest runs no job, and removing a package reaches the assertion in the test lane instead. Reconstructing the staged file list to include deletions does not work — lefthook filters the list against the working tree either way. The assertion is the backstop for exactly this case.
The file discloses **direct** dependencies by default. The complete npm closure with pinned versions already lives in `pnpm-lock.yaml` (`pnpm licenses list` renders it) and the Python closure in `python/sdk/uv.lock`; re-materializing either as prose would be a second, worse copy. The one explicit transitive disclosure is the official Claude platform payload set declared by `@anthropic-ai/claude-agent-sdk` through `optionalDependencies`, because those packages carry the distributed Claude Code executable rather than ordinary library implementation detail.

View File

@@ -16,7 +16,7 @@ Status: implemented
**新鲜度靠维护而非拦截。** 只要暂存了生成器的任一输入——任何 manifest、工作区声明、根锁文件、`vendor/README.md`、某个 `pyproject.toml`、生成器自身,或持有构建期 pin 的脚本——pre-commit 任务就会重新生成该文件并将其暂存,改依赖的人不必事后再折返跑一次生成器。已提交的字节随后由 [`scripts/gen-third-party-notices.spec.ts`](../../../../scripts/gen-third-party-notices.spec.ts) 断言,而测试 lane 本就会跑这个文件——这项校验不增加门禁进程、不占调度位、也不新增 CI 步骤。需要单独校验时,`pnpm run verify-third-party-notices` 仍然可用。
有一处触发缺口是接受而非绕过的lefthook 只检视磁盘上存在的文件,因此**删除** manifest 不会触发任何任务,移除一个包会落到测试 lane 的断言上。重构暂存文件列表以纳入删除的做法试过,不成立——无论怎么给列表lefthook 都会拿工作树过滤一遍。这个场景正由断言兜底。
有一处触发缺口是接受而非绕过的lefthook 只检视磁盘上存在的文件,因此**删除** manifest 不会触发任何任务,移除一个包会落到测试 lane 的断言上。重构暂存文件列表以纳入删除的做法不成立——无论怎么给列表lefthook 都会拿工作树过滤一遍。这个场景正由断言兜底。
文件默认只披露**直接**依赖。完整的 npm 闭包连同锁定版本已记录在 `pnpm-lock.yaml``pnpm licenses list` 可渲染Python 闭包记录在 `python/sdk/uv.lock`;再用散文誊一遍只会得到一份更差的副本。唯一明确披露的传递依赖,是 `@anthropic-ai/claude-agent-sdk` 通过 `optionalDependencies` 声明的官方 Claude 平台载荷集合,因为这些包承载随产品分发的 Claude Code 可执行文件,而非普通的库实现细节。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-31-installer-adopts-existing-checkout.md
2026-07-31-installer-adopts-existing-checkout.md: 3a213a6232e57f305910240505983421dcd288ad
2026-07-31-installer-adopts-existing-checkout.zh.md: e19baa0b1ee8ce71c1b8f3ee520322fb6557a720
2026-07-31-installer-adopts-existing-checkout.md: 531b780974e1490b27d8b96734ccfadae66c2fc2
2026-07-31-installer-adopts-existing-checkout.zh.md: 405b58d8d53b3f0895cc7746c957582455667cda

View File

@@ -20,7 +20,7 @@ The installer records nothing about where that repository lives. A container who
Adoption branches from `HEAD`, so committed work is what runs and uncommitted changes stay in the checkout. This is not prompted or warned about: the installer builds the layout and gets out of the way. Setting `DSH_SOURCE` to a different directory remains the one documented way to opt back into cloning a separate tree.
Every path comparison runs on physical paths through a `resolve_dir` helper, and every compared value is resolved at assignment rather than at the comparison. Git always reports resolved paths, so comparing one against an unresolved path disagrees whenever a symlink sits anywhere above the checkout — a symlinked home directory is enough, and macOS reaches every `mktemp` path that way through `/var` -> `private/var`. The mismatch misclassified an existing managed install as a foreign clone and would have built a second container beside the real one. The same defect recurred twice more during review, both times as one side of a comparison left unresolved: a curl install's `REPO_ROOT`, and the container path it was compared against. `resolve_dir` therefore echoes a missing path back rather than failing, so a not-yet-created container needs no per-call fallback and no site can compare against an empty path by forgetting one; callers that need "does not exist" test the directory explicitly. `git rev-parse --path-format=absolute` would do the same job but requires git 2.31+.
Every path comparison runs on physical paths through a `resolve_dir` helper, and every compared value is resolved at assignment rather than at the comparison. Git always reports resolved paths, so comparing one against an unresolved path disagrees whenever a symlink sits anywhere above the checkout — a symlinked home directory is enough, and macOS reaches every `mktemp` path that way through `/var` -> `private/var`. The mismatch misclassified an existing managed install as a foreign clone and would have built a second container beside the real one. The same defect class recurs whenever one side of a comparison is left unresolved — as it did with a curl install's `REPO_ROOT` and with the container path it was compared against. `resolve_dir` therefore echoes a missing path back rather than failing, so a not-yet-created container needs no per-call fallback and no site can compare against an empty path by forgetting one; callers that need "does not exist" test the directory explicitly. `git rev-parse --path-format=absolute` would do the same job but requires git 2.31+.
Before `current` is repointed, the installer rejects a staging path that resolves to the repository itself, enforcing the upgrade contract that the launcher never resolves to the master clone.
@@ -48,4 +48,4 @@ A container adopting an outside clone is also no longer self-contained: deleting
`scripts/install.sh` now has a real-shell PTY regression suite in `apps/cli/tests/install-script.spec.ts`, covering adoption and curl-style paths with stubbed dependencies. Curl-style installs default to the public `deepseek-ai/deepseek-harness-sdk` source, while replacing the installer with pnpm/npx remains separate work.
Verification was manual, through a throwaway harness driving the real script with a stubbed `pnpm`: adopting a standalone clone; adopting from a linked worktree into its existing container; an explicit `DSH_SOURCE` still opting back into cloning; a dirty tree adopting silently with no prompt or warning while its uncommitted file stays behind; a non-git checkout failing with guidance; and a `curl`-style clone install asserting the built layout, which is the regression that caught the unresolved-`REPO_ROOT` defect. The interactive path was exercised under tmux from a dirty checkout, confirming the run reaches the launcher with no adoption prompt and ends with `dsh` running from the new staging worktree while the original checkout keeps its branch and its uncommitted file.
Verification was manual, through a throwaway harness driving the real script with a stubbed `pnpm`: adopting a standalone clone; adopting from a linked worktree into its existing container; an explicit `DSH_SOURCE` still opting back into cloning; a dirty tree adopting silently with no prompt or warning while its uncommitted file stays behind; a non-git checkout failing with guidance; and a `curl`-style clone install asserting the built layout, the check that catches an unresolved `REPO_ROOT`. The interactive path was exercised under tmux from a dirty checkout, confirming the run reaches the launcher with no adoption prompt and ends with `dsh` running from the new staging worktree while the original checkout keeps its branch and its uncommitted file.

View File

@@ -20,7 +20,7 @@ Status: implemented
接管以`HEAD`为分支起点,因此运行的是已提交的内容,未提交的更改仍留在检出中。这一点既不提示也不警告:安装器构建好布局后便不再打扰。把`DSH_SOURCE`设为其他目录,仍是唯一有文档记载的、回到克隆另一棵树的方式。
所有路径比较都通过`resolve_dir`辅助函数在物理路径上进行且每个参与比较的值都在赋值时解析而非在比较时解析。git 报告的始终是已解析的路径,因此只要检出之上任意一层存在符号链接,拿它与未解析的路径相比较就会不相等——家目录本身是符号链接即已足够,而 macOS 通过`/var` -> `private/var`使每个`mktemp`路径都如此。这种不匹配会把已有的受管安装误判为外来克隆,并在真正的容器旁再建一个容器。同一缺陷在评审过程中又出现了两次,两次都是比较的一侧未经解析:一次是 curl 安装的`REPO_ROOT`,一次是与之比较的容器路径。因此`resolve_dir`在路径不存在时原样回显该路径而非失败,这样尚未创建的容器无需在每个调用点单独兜底,也就没有调用点会因遗漏兜底而与空路径比较;需要判断"不存在"的调用方则显式检测该目录。`git rev-parse --path-format=absolute`能完成同样的工作,但要求 git 2.31 及以上版本。
所有路径比较都通过`resolve_dir`辅助函数在物理路径上进行且每个参与比较的值都在赋值时解析而非在比较时解析。git 报告的始终是已解析的路径,因此只要检出之上任意一层存在符号链接,拿它与未解析的路径相比较就会不相等——家目录本身是符号链接即已足够,而 macOS 通过`/var` -> `private/var`使每个`mktemp`路径都如此。这种不匹配会把已有的受管安装误判为外来克隆,并在真正的容器旁再建一个容器。只要比较的一侧未经解析,同类缺陷就会重现——curl 安装的`REPO_ROOT`以及与之比较的容器路径都曾如此。因此`resolve_dir`在路径不存在时原样回显该路径而非失败,这样尚未创建的容器无需在每个调用点单独兜底,也就没有调用点会因遗漏兜底而与空路径比较;需要判断"不存在"的调用方则显式检测该目录。`git rev-parse --path-format=absolute`能完成同样的工作,但要求 git 2.31 及以上版本。
在重指`current`之前,安装器会拒绝解析结果等于仓库自身的 staging 路径,以此落实"启动器绝不解析到 master 克隆"这一升级约定。
@@ -48,4 +48,4 @@ Status: implemented
`scripts/install.sh` 现有一套位于 `apps/cli/tests/install-script.spec.ts` 的真实 shell PTY 回归测试,使用 stub 依赖覆盖接管路径和 curl 风格路径。curl 风格安装默认使用公开的 `deepseek-ai/deepseek-harness-sdk` 源,而以 pnpm/npx 替换安装器仍是另一项工作。
验证是手工完成的,通过一个一次性测试装置以打桩的`pnpm`驱动真实脚本:接管独立克隆;从 linked worktree 接管进其已有容器;显式`DSH_SOURCE`仍回到克隆路径;工作树不干净时静默接管、既不提示也不警告,且其未提交文件留在原处;非 git 检出失败并给出指引;以及`curl`式克隆安装断言所构建的布局——正是这项回归测试捕获了`REPO_ROOT`未解析的缺陷。交互路径在 tmux 下从一个不干净的检出走通,确认整个过程不出现接管提示即可到达启动器,最终`dsh`从新的 staging worktree 运行,而原检出保持其分支不变、未提交文件仍在。
验证是手工完成的,通过一个一次性测试装置以打桩的`pnpm`驱动真实脚本:接管独立克隆;从 linked worktree 接管进其已有容器;显式`DSH_SOURCE`仍回到克隆路径;工作树不干净时静默接管、既不提示也不警告,且其未提交文件留在原处;非 git 检出失败并给出指引;以及`curl`式克隆安装断言所构建的布局——该检查能捕获未解析的`REPO_ROOT`。交互路径在 tmux 下从一个不干净的检出走通,确认整个过程不出现接管提示即可到达启动器,最终`dsh`从新的 staging worktree 运行,而原检出保持其分支不变、未提交文件仍在。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-06-coverage-uncovered-locations.md
2026-08-06-coverage-uncovered-locations.md: 27c466e7ae505a2d318f8b21d442c31619610f59
2026-08-06-coverage-uncovered-locations.zh.md: 482b17227c4198ee27411da16b047288b331af9c
2026-08-06-coverage-uncovered-locations.md: 7a4bd7317bf66d090d73a6a05f361ccc1582f1c4
2026-08-06-coverage-uncovered-locations.zh.md: 7d820b3dcd46c844dfccc61dda283c754ed17f13

View File

@@ -33,7 +33,7 @@ CJS is a forced shape, and a justified exception to the ESM-everywhere disciplin
## Verification
Local matrix: with a deliberately induced failure, all three record kinds appear and their locations match the planted gaps; a mixed run emits records only for the failing files (files at 100% within the same run stay silent); an all-green run produces zero output and exit code 0. CI evidence: after temporarily planting one unreachable statement/branch/function in `clampTimeout`, the coverage lane — under the isolated condition of all tests passing (632 files / 10326 cases) with only the threshold failing — printed the 4 records above the ERROR lines (evidence links in the PR #1716 description); the demonstration commit was removed from the branch before merge.
Local matrix: with a deliberately induced failure, all three record kinds appear and their locations match the planted gaps; a mixed run emits records only for the failing files (files at 100% within the same run stay silent); an all-green run produces zero output and exit code 0. CI evidence: after temporarily planting one unreachable statement/branch/function in `clampTimeout`, the coverage lane — under the isolated condition of all tests passing (632 files / 10326 cases) with only the threshold failing — printed the 4 records above the ERROR lines; the planted failure is not in the committed tree.
## Consequences

View File

@@ -33,7 +33,7 @@ CJS 是被迫的形态,也是 ESM-everywhere 纪律的一个有据例外ist
## 验证
本地矩阵:故意制造未达标时三类记录齐全、位置与埋点一致;混合运行只输出未达标文件(同跑内 100% 的文件静默);全绿跑零输出、退出码 0。CI 实证:临时在 `clampTimeout` 埋入一处不可达语句/分支/函数后coverage lane 在全部测试通过632 文件 / 10326 用例)、仅 threshold 失败的隔离条件下,把 4 条记录打印在 ERROR 行上方(证据链接见 PR #1716 描述);演示提交在合并前已从分支撤除
本地矩阵:故意制造未达标时三类记录齐全、位置与埋点一致;混合运行只输出未达标文件(同跑内 100% 的文件静默);全绿跑零输出、退出码 0。CI 实证:临时在 `clampTimeout` 埋入一处不可达语句/分支/函数后coverage lane 在全部测试通过632 文件 / 10326 用例)、仅 threshold 失败的隔离条件下,把 4 条记录打印在 ERROR 行上方;埋入的失败并不在已提交的代码树中
## 后果

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md
2026-08-08-native-windows-pull-request-ci.md: 248201ad91f259ffa3eff898f82b36f741a27ef7
2026-08-08-native-windows-pull-request-ci.zh.md: 7cc5a1daf931a5a2463580e79085006ca53e4958
2026-08-08-native-windows-pull-request-ci.md: 5a3cec04cc0a0869f0c7a9d7370a2ceb765ba4bd
2026-08-08-native-windows-pull-request-ci.zh.md: 18247369d6d48e402dcf6a1e2b9c5db1951a6a01

View File

@@ -24,13 +24,13 @@ The 16-core allocation is the measured capacity point for this inventory. Relati
The first native run exposed two failures hidden by the compatibility lane. Documentation projection tests derived an image basename by splitting only on `/`; they now use Node's platform basename. Chokidar consumers received `%TEMP%` through the `C:\\Users\\RUNNER~1` 8.3 alias while libuv returned the long directory name, tripping its Windows event-path assertion. Shared settings and credentials watchers, plus Cordis module and exact-config HMR, now canonicalize the existing native watch base or deepest existing ancestor before opening the watcher and preserve a missing suffix, while file access and diagnostics retain the configured path. Module HMR attaches listeners and awaits the main watcher's ready event before plugin startup settles, so an immediate post-boot edit cannot race the initial scan. HMR acceptance derives expected identities through the same asynchronous native realpath operation, avoiding a synchronous Windows spelling that can retain the 8.3 alias.
Portable filesystem fixtures derive paths with `node:path`, compare native realpath identities, preserve file URLs at Node launcher boundaries, normalize only API-owned separators or line endings, and use filenames legal on every host. POSIX-only signal, mode-bit, unreadability, and writer-lock cases are platform-gated; portable failure contracts instead assert structured error codes, rollback, last-good state, atomic replacement, and absence of temporary residue through conflicts available on every host. Stress and integration workloads keep their original assertions and receive explicit bounded time budgets where Windows instrumentation or process teardown can exceed Vitest's default ceiling.
Portable filesystem fixtures derive paths with `node:path`, compare native realpath identities, preserve file URLs at Node launcher boundaries, normalize only API-owned separators or line endings, and use filenames legal on every host. POSIX-only signal, mode-bit, unreadability, and writer-lock cases are platform-gated; portable failure contracts instead assert structured error codes, rollback, last-good state, atomic replacement, and absence of temporary residue through conflicts available on every host. Credentials permission validation uses an invalid-path fixture whose pre-lookup `ERR_INVALID_ARG_VALUE` is non-absence on every host, rather than depending on whether a file ancestor produces `ENOTDIR` or `ENOENT`. Stress and integration workloads keep their original assertions and receive explicit bounded time budgets where Windows instrumentation or process teardown can exceed Vitest's default ceiling.
Native watchers use `canonicalizeWatchPath()` to realpath the deepest existing ancestor, prove it is an enumerable directory when a suffix is missing, and restore that suffix. This prevents Windows 8.3 aliases from being mixed with long-form libuv events and preserves `ENOTDIR` for a regular-file ancestor on every host. Settings, credentials, skill roots, and Cordis HMR retain configured paths for discovery and diagnostics; module HMR uses the canonical spelling for Node's load-cache identity, attaches listeners, and awaits its main watcher before plugin startup settles, so an immediate post-boot edit cannot race the initial scan. A skill root that is itself a symbolic link remains unexpanded when `watchFollowSymlinks: false`, allowing Chokidar to enforce that boundary.
Windows durable JSONL paths keep drive roots in native spelling and apply the extended-length namespace only to descendants and staging paths. The ACP teardown ladder uses real Node children, proves graceful and forced tiers with host-appropriate outcomes, and avoids claiming POSIX signal delivery on Windows. Executable fixtures provide `.cmd` shims and `PATHEXT` where the product accepts a bare command. Repository-cache helpers live inside the selected Git subpath so their declared `file:` dependencies expose command shims identically on Windows. The bundled installer exports pnpm's own workspace-ignore configuration, retains `PNPM_HOME` for pnpm data while removing that directory from lifecycle `PATH`, and prioritizes `.CMD` in `PATHEXT`; nested Git-package installation therefore cannot rejoin the enclosing workspace or let an inherited Windows pnpm executable outrank the transaction-owned wrapper.
Post-boot profile watcher setup proceeds only while the root fiber and Loader are both live. A concurrent setup error is contained only when the same invocation's recorded signal already owns shutdown; unrelated HMR failures remain loud. The vendored Include serializes debounced writes, retries only transient access or busy failures with bounded backoff, and observes every timer rejection. A terminal persistence failure remains on the queue and is rethrown to the teardown owner, while successful teardown drains the latest write.
Post-boot profile watcher setup proceeds only while the root fiber and Loader are both live. A concurrent setup error is contained only when the same invocation's recorded signal already owns shutdown; unrelated HMR failures remain loud. The [process-shutdown controller](../bug-fix/2026-08-03-cli-signal-shutdown-escalation.md) lets a successful one-shot completion drain Node's remaining handles after root disposal, while teardown failure, deadline, and signal escalation retain forced exit. The vendored Include serializes debounced writes, retries only transient access or busy failures with bounded backoff, and observes every timer rejection. A terminal persistence failure remains on the queue and is rethrown to the teardown owner, while successful teardown drains the latest write.
Shiki disables lazy TextMate-regex compilation and warms each boot grammar before user content enters the unchanged per-line tokenization budget, so scheduler contention cannot publish a partial highlighted stream. The Codex real-product fixture is pinned to stable 0.147.0 schemas and selects an actually advertised command tool and argument shape, preserving the provider-owned protocol while proving unattended rejection and whole-tree exit on each host.

View File

@@ -24,13 +24,13 @@ Status: implemented
首次原生运行暴露出两项被兼容性通道掩盖的故障。文档投影测试此前只按 `/` 拆分来派生图片 basename现在改为使用 Node 根据平台计算的 basename。Chokidar 消费方收到的 `%TEMP%``C:\\Users\\RUNNER~1` 这个 8.3 别名表示,而 libuv 返回的是长目录名,导致其 Windows 事件路径断言失败。共享的设置 watcher 与凭据 watcher以及 Cordis 的模块 HMR热模块替换与精确配置 HMR现在都会在打开 watcher 前规范化现有的原生监听基准路径或层级最深的现有祖先路径,并保留尚不存在的后缀;文件访问和诊断仍使用配置路径。模块 HMR 会挂接监听器并等待主 watcher 的 ready 事件之后插件启动才会完成因此启动后立即发生的编辑无法与初始扫描形成竞态。HMR 验收通过相同的异步原生 realpath 操作派生预期身份,避免同步 Windows 路径写法仍保留 8.3 别名。
可移植文件系统 fixture测试前置数据通过 `node:path` 派生路径、比较原生 realpath 标识、在 Node 启动器边界保留文件 URL只规范化由 API 负责的分隔符或行尾,并使用每个宿主均允许的文件名。仅适用于 POSIX 的信号、模式位、不可读状态和 writer lock 场景按平台设门禁;可移植故障约定则通过每个宿主均可构造的冲突,断言结构化错误码、回滚、最后有效状态、原子替换及不存在临时残留。压力与集成工作负载保留原有断言;如果 Windows 插桩或进程拆卸可能超过 Vitest 默认上限,就为其设置显式的有界时间预算。
可移植文件系统 fixture测试前置数据通过 `node:path` 派生路径、比较原生 realpath 标识、在 Node 启动器边界保留文件 URL只规范化由 API 负责的分隔符或行尾,并使用每个宿主均允许的文件名。仅适用于 POSIX 的信号、模式位、不可读状态和 writer lock 场景按平台设门禁;可移植故障约定则通过每个宿主均可构造的冲突,断言结构化错误码、回滚、最后有效状态、原子替换及不存在临时残留。凭据权限验证采用无效路径 fixture该路径在每个宿主上都会于系统查找前产生表示“非缺失”的 `ERR_INVALID_ARG_VALUE`,而不依赖文件祖先究竟产生 `ENOTDIR` 还是 `ENOENT`压力与集成工作负载保留原有断言;如果 Windows 插桩或进程拆卸可能超过 Vitest 默认上限,就为其设置显式的有界时间预算。
原生 watcher 使用 `canonicalizeWatchPath()` 对层级最深的现有祖先执行 realpath 解析;后缀缺失时,先证明该祖先是可枚举目录,再拼回后缀。这可避免 Windows 8.3 别名与长格式 libuv 事件混用,并让所有宿主在祖先为普通文件时都保留 `ENOTDIR`。设置、凭据、skill技能根与 Cordis HMR热模块替换在发现和诊断时保留配置路径模块 HMR 则使用规范写法作为 Node 加载缓存标识、挂接监听器并在插件启动完成前等待主 watcher 就绪,因此启动后立即发生的编辑不会与初始扫描形成竞态。`watchFollowSymlinks: false` 时,若 skill 根本身是符号链接,系统不会展开最后这一级链接,从而让 Chokidar 强制执行该边界。
Windows 的持久 JSONL 路径会保留驱动器根目录的原生写法并仅对后代路径与暂存路径应用扩展长度命名空间。ACPAgent Client Protocol拆卸阶梯使用真实 Node 子进程,以符合宿主语义的结果证明优雅终止与强制终止两个层级,并避免声称 Windows 会交付 POSIX 信号。产品接受裸命令时,可执行 fixture 会提供 `.cmd` 包装脚本与 `PATHEXT`。repository-cache 辅助包位于所选 Git 子路径内,因此它们声明的 `file:` 依赖会在 Windows 上以相同方式暴露命令包装脚本。随附的安装器会导出 pnpm 自有的 workspace-ignore 配置,保留 `PNPM_HOME` 作为 pnpm 数据配置,同时从生命周期 `PATH` 中移除该目录,并在 `PATHEXT` 中优先选择 `.CMD`;因此,嵌套 Git 包安装既不会重新加入外层 workspace也不会让继承的 Windows pnpm 可执行文件抢在事务持有的 wrapper 之前。
启动后,只有根 fiber 与 Loader 均处于活跃状态时,系统才会继续设置 profile watcher。只有当同一次调用所记录的信号已取得关闭流程所有权时系统才会隔离并发设置错误无关 HMR 故障仍会响亮失败。vendored Include 会串行化防抖写入,只对瞬时访问或忙碌故障执行有界退避重试,并确保每个由计时器触发的拒绝都得到观察。持久化最终失败后,该故障会保留在队列中,并重新抛给拆卸责任方;成功拆卸则会排空最新写入。
启动后,只有根 fiber 与 Loader 均处于活跃状态时,系统才会继续设置 profile watcher。只有当同一次调用所记录的信号已取得关闭流程所有权时系统才会隔离并发设置错误无关 HMR 故障仍会响亮失败。[进程关闭控制器](../bug-fix/2026-08-03-cli-signal-shutdown-escalation.md)会在根级 dispose 成功后让单次任务的正常完成流程排空 Node 剩余句柄,同时让拆卸失败、截止时间到期和信号升级继续强制退出。vendored Include 会串行化防抖写入,只对瞬时访问或忙碌故障执行有界退避重试,并确保每个由计时器触发的拒绝都得到观察。持久化最终失败后,该故障会保留在队列中,并重新抛给拆卸责任方;成功拆卸则会排空最新写入。
Shiki 会禁用 TextMate 正则的延迟编译,并在用户内容进入保持不变的逐行 tokenization词元化预算前预热每种启动语法从而避免调度器争用发布不完整的高亮流。Codex 真实产品 fixture 固定使用稳定版 0.147.0 schema并选择实际提供的命令工具与对应参数形态这样既保留由提供方负责的协议也能在每种宿主上证明无人值守拒绝和整棵进程树退出。

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-09-committed-artifact-citations.md
2026-08-09-committed-artifact-citations.md: 31b2d52b423a080579b3ca95cf077859d0bf4c91
2026-08-09-committed-artifact-citations.zh.md: b69bb8743d10e2d1e194905d6d0de2d3d3667b1d

View File

@@ -0,0 +1,37 @@
# Agent Note: Cite committed artifacts, never design-session ordinals
Status: implemented
English | [中文](2026-08-09-committed-artifact-citations.zh.md)
## Problem
Large design and review sessions leave working shorthand — decision ordinals, audit item codes, plan section numbers, task and stack ordinals, reviewer rulings — that reads naturally while the session transcript is open and resolves to nothing after it closes. A repo-wide audit found the pattern concentrated in `packages/client`: bare `(decision 12/16/19/20/21)` citations of which only decision 21 had a committed owner, `(audit C2/S1/S3/S7)` codes with no audit document anywhere, `design §4.7` / `web2 §0` / `plan §1.4` references to uncommitted drafts, plan-phase labels (`T2/T5/T9`, `P-I`, `W5`), stack positions ("a later PR in this stack") in durable JSDoc, and "ruling" / "design ledger" vocabulary. The same families appeared in tests, CSS comments, generator templates, CI comments, and Agent Notes ("this PR/branch/review round" vantage, review-choreography attributions, stale "deferred to a later PR" claims whose targets had since shipped). The [documentation standard](../../../../docs/AGENTS.md) already banned the change-history half (previously/now, PR and commit references) but stated no counterpart rule for citations, so unresolvable ordinals kept landing.
## Decision
Durable prose — comments, JSDoc, docs, notes, test comments and titles — cites only committed artifacts, resolvable in-repo without grep archaeology:
- Name the owning Agent Note (its path at least once per file, a searchable name inline), the doc page path, or a GitHub issue number. PR, commit, branch, and stack positions stay banned in docs and code per the documentation standard; issues are durable and citable, and Agent Notes and postmortems may cite merged PRs and issues as evidence, per the [documentation standard](../../../../docs/AGENTS.md)'s change-story routing.
- A design-session ordinal whose decision has a committed owner is replaced by the decision's name — the ordinal once logged as "decision 21" is now "the plain-text-reference decision", owned by the [web input-machine note](../architecture/2026-07-25-web-input-machine-and-slash-pipeline.md); the ordinal itself resolves nowhere in-repo and was dropped everywhere. An ordinal without an owner is deleted and its factual clause restated to stand alone.
- Fixed regressions are pinned as present-tense counterfactuals ("without X, Y happens"; "a naive X would…"), never as repo history ("used to Y").
- Implemented notes state shipped reality: a "deferred to a later PR" claim whose target shipped names the shipped note instead.
- Recorded fixtures, snapshots, and archived notes are exempt: recorded model output and sealed history keep their original voice. Inside a note's change-story sections, a historical stage name ("the first cut shipped X") is current-state-safe; indexical stamps ("this cut") stay banned everywhere.
One repo-wide purge applied these rules across the prose surfaces, including the generator-owned templates (`scripts/gen-doc-graphs.ts`, `scripts/gen-tool-catalog.ts`, the typert generator's page notice) with regeneration, the type-equiv source JSDoc with page re-pastes, and the bilingual counterparts with pair re-records.
## Alternatives considered
- **Commit the design ledgers and audit documents so the ordinals resolve.** Rejected: session transcripts are working artifacts, not maintained references; committing them would create a parallel, ungated decision corpus beside Agent Notes, and their internal numbering would still drift.
- **A mechanical gate for the banned vocabulary.** Deferred: the vocabulary is unbounded natural language, and the audit's recall batteries need judgment to separate leakage from legitimate prose ("wait" the noun, contrastive "actually", runtime old/new states). A narrow high-precision gate (for example `\(decision \d`, `\(audit [A-Z]\d`, `\bcut \d`, `this cut`, a bare `\bT\d\b`, `P-I`, `used to `, a bare `\bv1\b`, and `§\d` — the last excluding citations whose section numbering has a committed owner, such as web-styling.md's own §N) is the candidate if the pattern recurs; review of the purge itself caught residuals in exactly these post-battery shapes, so they lead the candidate list.
- **Delete the rationale that cited dead artifacts.** Rejected: the factual clauses were preserved or restated; only citations, review choreography, and derivation transcripts were removed, per the prose standard's complete-proposition rule.
## Verification
The audit's grep batteries (English and Chinese, comments and prose, `--hidden` for `.agents/`) return no design-ordinal citations outside recorded fixtures and archived notes; `verify-type-equiv`, the `gen-*` freshness checks, and `verify-translation-pairing` pin the regenerated and re-recorded surfaces. Coverage gap: no gate rejects a new ordinal citation — review owns the rule.
## Consequences
- A comment's citations resolve by path or name; readers never reconstruct a closed session to follow one.
- Design sessions must land their decisions in Agent Notes before durable prose can cite them; ordinal shorthand stays inside the session.
- Citations get longer (a note path instead of "(decision 21)") in exchange for grep-free resolution.

View File

@@ -0,0 +1,37 @@
# Agent Note: 引用已提交的产物,绝不引用设计会话序号
Status: implemented
[English](2026-08-09-committed-artifact-citations.md) | 中文
## 问题
大型设计与评审会话会留下工作速记:决策序号、审计条目代号、计划章节编号、任务与栈序号、评审人裁定。这些速记在会话 transcript文本记录还开着时读起来顺理成章一旦关闭就什么也解析不到。一次全仓库审计发现该模式集中在 `packages/client`:裸写的 `(decision 12/16/19/20/21)` 引用中只有决策 21 有已提交的归属文档;`(audit C2/S1/S3/S7)` 代号在任何地方都没有对应的审计文档;`design §4.7``web2 §0``plan §1.4` 指向未提交的草稿;计划阶段标签(`T2/T5/T9``P-I``W5`);持久 JSDoc 里的栈内位置("a later PR in this stack"以及「ruling」裁定、「design ledger」设计台账一类词汇。同样的几类模式也出现在测试、CSS 注释、生成器模板、CI 注释与 Agent Note 中(「本 PR本分支本评审轮」视角、评审编排式的归因、目标其后已经交付却仍写着「推迟到后续 PR」的陈旧说法。[文档标准](../../../../docs/AGENTS.md)早已禁止了变更历史这一半previously/now、PRPull Request与 commit 引用),却没有为引用写下对应的规则,于是无法解析的序号不断落进仓库。
## 决策
持久行文注释、JSDoc、文档、Agent Note、测试注释与测试标题只引用已提交的产物无需 grep 考古即可在仓库内解析:
- 点名归属的 Agent Note其路径在每个文件里至少出现一次行内用可检索的名称、文档页面路径或 GitHub issue 编号。PR、commit、分支与栈位置依文档标准在文档与代码中仍在禁止之列issue 是持久且可引用的Agent Note 与事后分析可依[文档标准](../../../../docs/AGENTS.md)的变更故事归置规则引用已合并的 PR 与 issue 作为证据。
- 决策有已提交归属文档的设计会话序号替换为该决策的名称——曾以「决策 21」记录的序号如今是「纯文本引用决策」归属于 [web 输入状态机 note](../architecture/2026-07-25-web-input-machine-and-slash-pipeline.md);该序号本身在仓库内无从解析,已全部移除。没有归属文档的序号予以删除,其事实性语句改写为可独立成立的表述。
- 已修复的回归以现在时反事实句固定下来(「没有 X 就会发生 Y」、「朴素的 X 会……」),绝不写成仓库历史(「过去曾 Y」
- 已实现的 Agent Note 陈述已交付的现实:「推迟到后续 PR」的说法若其目标已经交付就改为点名那篇已交付的 note。
- 已录制的 fixture测试前置数据、快照与已归档的 Agent Note 不受此约束:已录制的模型输出与封存的历史保持原有行文。在 note 的变更故事段落内,历史阶段名称(「首版交付了 X」属于安全的现状表述指示性切次戳"this cut")在任何地方都仍被禁止。
一次全仓库清理把这些规则应用到了各个行文表面,包括生成器持有的模板(`scripts/gen-doc-graphs.ts``scripts/gen-tool-catalog.ts`、typert 生成器的页面提示语改后重新生成、type-equiv 源码 JSDoc改后把文档页重新粘贴以及双语对侧文件改后重新记录配对
## 曾考虑的替代方案
- **把设计台账与审计文档提交入库,让序号得以解析。**不予采纳:会话 transcript 是工作产物,不是持续维护的参考资料;提交它们会在 Agent Note 之外形成一套平行且不受门禁约束的决策语料,其内部编号也仍会漂移。
- **为被禁词汇建一道机械门禁。**暂缓这类词汇是无界的自然语言审计中以查全为目标的成批检索需要人工判断才能把泄漏与正当行文区分开作名词的「wait」、表转折的「actually」、运行时的新旧状态。若该模式再次出现候选方案是一道窄而高查准的门禁例如 `\(decision \d``\(audit [A-Z]\d``\bcut \d``this cut`、裸 `\bT\d\b``P-I``used to `、裸 `\bv1\b``§\d`——最后一种需排除章节编号有已提交归属的引用,如 web-styling.md 自身的 §N对本次清扫自身的评审恰好在这些电池之外的形态中发现残留因此它们位居候选清单之首。
- **删除引用了失效产物的设计理由。**不予采纳:事实性语句都得到保留或改写;依行文标准的完整命题规则,删掉的只有引用、评审编排与推导过程记录。
## 验证
审计的成批 grep 检索(英文与中文、注释与行文、对 `.agents/``--hidden`)在已录制 fixture 与已归档 Agent Note 之外没有命中任何设计序号引用;`verify-type-equiv`、各 `gen-*` 新鲜度检查与 `verify-translation-pairing` 把重新生成与重新记录的表面固定下来。覆盖缺口:没有门禁会拒绝新的序号引用,这条规则由评审把关。
## 后果
- 注释中的引用凭路径或名称即可解析;读者永远不必为追溯一条引用而重建已关闭的会话。
- 设计会话必须先把决策落进 Agent Note持久行文才能引用这些决策序号速记只留在会话内部。
- 引用变长了(用一条 note 路径取代「(decision 21)」),换来的是无需 grep 即可解析。