Merge remote-tracking branch 'origin/master' into worktree/python-pypi-release-prep
# Conflicts: # docs/user/guide/python-sdk.i18n.yaml # docs/user/guide/python-sdk.md # docs/user/guide/python-sdk.zh.md # python/sdk/README.i18n.yaml # python/sdk/README.md # python/sdk/README.zh.md
This commit is contained in:
@@ -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-04-doc-tiers-and-budgets.md
|
||||
2026-07-04-doc-tiers-and-budgets.md: e7b3421d09a1ae5ab9a9373e8040832c1b0d4b97
|
||||
2026-07-04-doc-tiers-and-budgets.zh.md: 3bc04ae73a4d8c9c005e154a236772fc1845389e
|
||||
2026-07-04-doc-tiers-and-budgets.md: 3f263864b9b6ee9479d1133b908617f10073dd66
|
||||
2026-07-04-doc-tiers-and-budgets.zh.md: 63b0b2945e1ff3e3fdf6af3cddb80cf44cf448ee
|
||||
|
||||
@@ -12,6 +12,7 @@ Standing docs accumulated repeated rules, retold incidents, duplicated package m
|
||||
|
||||
- **Structure follows the documentation tree.** [docs/AGENTS.md](../../../../docs/AGENTS.md) is the documentation standard: a document owns detail about its subject, summarizes only the purpose, responsibility, and high-level behavior of direct children, and links to deeper owners. [Agent Notes](../../README.md) remain outside this structural contract. Every human-facing document is a tutorial with an ordered outcome or a reference with an explicit lookup scope; a [postmortem](../../../../docs/postmortem/README.md) is an incident-scoped reference whose chronology records evidence. Tutorials introduce concepts in prerequisite order for the reader's starting knowledge.
|
||||
- **A tier taxonomy with one home per fact.** The standard assigns every Markdown tier one job, forbids restating a fact outside its home tier, and carries the slop checklist used when writing or reviewing any doc.
|
||||
- **One product onboarding path.** The root README owns the recommended package-run path, the source-run alternative, and compact `dsh plugin --profile` usage. The published user guide starts with tasks inside the running Web UI, then links to distinct tutorials or reference owners for other interfaces, plugin development, and advanced configuration instead of repeating Web startup.
|
||||
- **A narrow, hard budget gate.** [scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) joins `doc-sync`: every doc listed in [scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) must stay under its word ceiling (`wc -w` semantics, whole file), and a budgeted file that is missing fails the gate so a rename cannot silently orphan its budget. Scope is deliberately only the accretion-prone standing docs — the root and subtree `AGENTS.md` files, `architecture.md`, `packages/README.md`, and the standing policy docs they evict content into (`docs/testing.md`, `docs/defensive-patterns.md`). Reference docs, Agent Notes, and package READMEs are unbudgeted: length is legitimate there when every row is a fact, and review plus the slop checklist govern them.
|
||||
- **Ceilings are an enforcement frontier that ratchets.** A doc at or below its target keeps at least 5% headroom as its ceiling ratchets down; a doc above target keeps a frozen ceiling that prevents growth until it reaches the target (root `AGENTS.md` ≤ 1,600 words; `architecture.md` ≤ 1,800; subtree `AGENTS.md` ≤ 600 except `packages/AGENTS.md` ≤ 650 and `docs/AGENTS.md` ≤ 1,250; `packages/README.md` ≤ 600). When the gate goes red, relocate or condense; raise a ceiling only with explicit PR justification.
|
||||
- **A thin workflow skill, contracts in docs.** [.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) carries the placement/audit/red-gate workflow and defers to the standard as its source of truth, the same split as [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) over the i18n contract.
|
||||
@@ -20,11 +21,13 @@ Standing docs accumulated repeated rules, retold incidents, duplicated package m
|
||||
|
||||
- **Skill and review discipline without a gate** — rejected: the accretion above happened while the current-state rule and reviewer attention already existed; a prose rule with no mechanical backstop demonstrably does not hold here, and this repo's own [quality-gates stance](2026-06-11-quality-gates.md) says invariants worth keeping are worth encoding.
|
||||
- **A broad gate over every doc tier** — rejected: a blanket ceiling punishes exactly the right kind of long doc (a feature matrix or type catalog where every row is a fact) and generates per-file override churn that trains contributors to rubber-stamp raises.
|
||||
- **Independent onboarding tutorials for each documentation entry point** — rejected: duplicated setup steps drift in command order, first outcome, and product identity. A short README path followed by task-focused guides keeps the transition explicit without maintaining competing tutorials.
|
||||
- **Housing the standard inside the skill** — rejected: contracts live in docs and workflows in skills; a standard packed into SKILL.md is invisible to an agent that edits docs without invoking the skill, and `docs/AGENTS.md` already loads as subtree instructions for anyone working under `docs/`.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Adding to a budgeted doc requires displacement: relocate the addition to its taxonomy home with a pointer, or condense existing prose to pay for it. Growth without pruning fails CI.
|
||||
- Structural review starts with ownership and document form before sentence-level editing, so lower-level detail moves to its owner instead of being polished in the wrong place.
|
||||
- Readers reach a running Web UI before encountering headless execution, SDK embedding, custom profiles, or direct settings files; those interfaces remain available from their reference owners.
|
||||
- Budgeted docs that remain above target cannot grow; reaching the target restores the 5% working headroom.
|
||||
- Word count is a crude proxy accepted deliberately: it cannot judge quality, but it forces the relocation decision at exactly the moment content is being added, which is when the author has the context to place it correctly.
|
||||
|
||||
@@ -12,6 +12,7 @@ Status: implemented
|
||||
|
||||
- **结构遵循文档树。**[docs/AGENTS.md](../../../../docs/AGENTS.md) 是文档标准:文档负责承载其主题的详细内容,仅概述直接子项的目的、职责和高层行为,并链接到更深层内容的归属文档。[Agent Note](../../README.md) 仍不受这一结构约定约束。每份面向人的文档要么是按顺序引导读者达成结果的教程(tutorial),要么是查阅范围明确的参考文档(reference);[事故复盘(postmortem)](../../../../docs/postmortem/README.md) 是范围限定于单起事故的参考文档,其时间线记录证据。教程结合读者的起始知识,按前置依赖顺序介绍概念。
|
||||
- **每项事实只归属一处的层级分类。**文档标准为每种 Markdown 层级分配单一职责,禁止在事实归属层级之外重复陈述,并包含编写或评审任何文档时使用的赘余检查清单。
|
||||
- **单一产品入门路径。**根 README 负责推荐的包运行路径、从源码运行的备选路径和简要的 `dsh plugin --profile` 用法。已发布的用户指南从运行中的 Web UI 内部任务开始,再链接到其他界面的独立教程或插件开发与进阶配置的参考文档归属处,而不会重复介绍 Web 启动步骤。
|
||||
- **范围窄且严格的预算门禁。**[scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) 接入 `doc-sync`:[scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) 列出的每份文档都必须低于其词数上限(采用 `wc -w` 语义,统计整个文件);预算内文件缺失也会使门禁失败,使重命名无法悄然遗落其预算。范围刻意只涵盖容易膨胀的常设文档——根目录和子树中的 `AGENTS.md` 文件、`architecture.md`、`packages/README.md`,以及它们将内容移入的常设策略文档(`docs/testing.md`、`docs/defensive-patterns.md`)。参考文档、Agent Note 和包 README 不设预算:只要每一行都是事实,长度在这些位置就是合理的;评审和赘余检查清单负责约束它们。
|
||||
- **上限是只进不退的执行红线。** 达到或低于目标的文档在上限逐步下调时保留至少 5% 的余量;高于目标的文档则维持冻结的上限,在达到目标之前不得增长(根 `AGENTS.md` ≤ 1,600 词;`architecture.md` ≤ 1,800;子树 `AGENTS.md` ≤ 600,但 `packages/AGENTS.md` ≤ 650、`docs/AGENTS.md` ≤ 1,250;`packages/README.md` ≤ 600)。门禁变红时,迁移或压缩内容;只有在 PR(Pull Request)描述中给出明确理由时才提高上限。
|
||||
- **精简的工作流 skill(技能),约定归文档。**[.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) 承载文档放置、审计和门禁失败处理工作流,并以文档标准为真源,与 [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) 和 i18n 约定之间的分工相同。
|
||||
@@ -20,11 +21,13 @@ Status: implemented
|
||||
|
||||
- **仅靠 skill 和评审纪律,不设门禁**:否决。上述膨胀正是在现行规则和评审注意力已经存在的情况下发生的;一条没有自动化保障的行文规则在此处已被证明无法维持,而本仓库自身的[质量门禁立场](2026-06-11-quality-gates.md)认为值得保持的不变式就值得编码。
|
||||
- **对所有文档层级全面设限**:否决。一刀切的上限恰好惩罚了那些正当的长文档(如功能矩阵或类型目录,每一行都是事实),并产生逐文件的例外变更,训练贡献者机械地批准提限。
|
||||
- **为每个文档入口维护独立入门教程**:否决。重复的设置步骤会在命令顺序、首个结果和产品定位上产生分歧。简短的 README 路径接上面向任务的指南,可明确衔接两者,且不需要维护相互竞争的教程。
|
||||
- **将标准放在 skill 内部**:否决。约定归文档,工作流归 skill;如果标准被塞进 SKILL.md,那些不调用该 skill 而直接编辑文档的 agent(智能体)就看不到它,而 `docs/AGENTS.md` 已经作为子树指令被任何在 `docs/` 下工作的人加载。
|
||||
|
||||
## 后果
|
||||
|
||||
- 向受预算约束的文档添加内容需要腾挪空间:将新增内容迁移到其分类体系归属地并留下链接,或压缩现有行文来腾出空间。只增不减会导致 CI 失败。
|
||||
- 结构评审先检查归属关系和文档形式,再进行句子层面的编辑,使较低层级的细节迁移到其归属文档,而不是在错误的位置加以润色。
|
||||
- 读者会先进入可运行的 Web UI,再遇到 headless 执行、SDK 嵌入、自定义 profile 或直接 settings 文件;这些入口仍可从各自的参考文档归属处访问。
|
||||
- 仍高于目标的受预算约束文档不得增长;达到目标后,将恢复 5% 的工作余量。
|
||||
- 词数是一个粗糙的代理指标,这是有意接受的:它无法判断质量,但它在内容被添加的那一刻强制触发迁移决策,而那正是作者拥有足够上下文来正确放置内容的时刻。
|
||||
|
||||
@@ -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-12-face-named-client-test-files.md
|
||||
2026-08-12-face-named-client-test-files.md: e83ebe4fda25218d96bab546af025b59064ca18f
|
||||
2026-08-12-face-named-client-test-files.zh.md: 7062c67f9c98d87f480e119df807955c1a10820a
|
||||
@@ -0,0 +1,61 @@
|
||||
# Agent Note: Face-named client test files
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-08-12-face-named-client-test-files.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`packages/client/*/tests/` holds tests for both compile faces. Most cover a Client package's browser half and belong to `tsconfig.client.json`; a few cover the Host half of a split package — the carrier's node-half specs — and can only type-check in `tsconfig.host.json`, because a Host-face spec reaching Host source needs the Host projects those files live in.
|
||||
|
||||
Nothing in a filename said which face a test covered, so the two aggregates could not partition the directory by pattern. The host aggregate excluded `packages/client/**` wholesale and the Client aggregate took everything, which left the Host-face specs in the Client program. They then needed the Client aggregate to reference `packages/client/connection/tsconfig.host.json` — a Client config entering a split package's Host face, which the `constraints` project-reference rule rejects.
|
||||
|
||||
Two escapes exist without a naming rule, and both are worse. Carving the four files back with `files` entries in the host aggregate contradicts the wholesale exclusion in the same file and grows with every new Host-face spec. Allowing the cross-face reference weakens the rule that keeps the two `Context` merges apart.
|
||||
|
||||
## Decision
|
||||
|
||||
A test file under `packages/client` names the face it covers:
|
||||
|
||||
| Suffix | Face | Count |
|
||||
|---|---|---|
|
||||
| `*.client.spec.ts` / `*.client.spec.tsx` | Client | 232 |
|
||||
| `*.client.ts` / `*.client.tsx` (shared helpers, fixtures) | Client | 5 |
|
||||
| `*.host.spec.ts` | Host | 4 |
|
||||
|
||||
The suffixes are mutually exclusive — neither is a suffix of the other — so each aggregate keeps one broad test glob and excludes the other face:
|
||||
|
||||
- `tsconfig.client.json` includes `packages/client/*/tests/**/*.{ts,tsx}` and excludes `packages/client/*/tests/**/*.host.spec.ts`.
|
||||
- `tsconfig.host.json` reaches the same directory through its repository-wide `packages/*/*/tests/**/*.ts` and excludes `packages/client/*/src/**` plus the four `*.client.*` patterns.
|
||||
|
||||
This rests on `exclude` filtering the result of `include`: when both match, the file stays out. No file is named in both aggregates, and neither aggregate needs a `files` entry or a cross-face project reference. `verify-md-links` and the `constraints` project-reference rule pass unchanged, with no exception for the carrier.
|
||||
|
||||
A new test under `packages/client` must carry a face suffix. An unsuffixed file is matched by the host aggregate's package glob and silently pulls Client source into the Host program.
|
||||
|
||||
## Renamed in this change
|
||||
|
||||
- 232 Client-face specs, from `*.spec.{ts,tsx}` to `*.client.spec.{ts,tsx}`.
|
||||
- 5 Client-face helpers, from `*.{ts,tsx}` to `*.client.{ts,tsx}`: `connection/tests/fake-api`, `runtime/tests/fake-api`, `runtime/tests/event-script`, `ui-conversation/tests/chat-snapshot-fixture`, `ui-tool/tests/tool-details-render`.
|
||||
- 4 Host-face specs in `packages/client/connection/tests/`, from `*.spec.ts` to `*.host.spec.ts`: `api-request-trust`, `http-bridge`, `node-half`, `websocket-downlink`.
|
||||
- 2 snapshot files, following their spec's name with unchanged content.
|
||||
|
||||
`scripts/rescope-vendor.ts` names three of these specs in its exact-edit table, so those paths moved with them.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Suffix only the Host-face files as `*.host.spec.ts` and leave the Client side alone.** The first attempt, and it cannot work: `.host.spec.ts` also ends in `.spec.ts`, so the host aggregate's exclusion of `*.spec.ts` swallows it, and `include` cannot win it back. Naming both faces is what makes the two patterns disjoint.
|
||||
|
||||
**Name the Host-face files `*.host-spec.ts`, outside the `.spec.ts` convention.** Disjoint from `*.spec.ts` without touching the Client side, but it leaves the repository's test-naming convention and the vitest discovery pattern for a config detail.
|
||||
|
||||
**Move the Host-face specs to a `tests/host/` subdirectory and partition by path.** Also works with globs, but it splits one package's tests across two directories, and a reader browsing `tests/` no longer sees them together.
|
||||
|
||||
**Keep the exclusion of `packages/client/**` and carve the Host-face specs back with `files`.** `files` is not filtered by `exclude`, so it does reach them — one file asserting a directory belongs to the other aggregate while listing exceptions to that assertion, with a new entry required per Host-face spec.
|
||||
|
||||
## Consequences
|
||||
|
||||
The rule costs a suffix on every client test filename and buys a mechanical partition: an aggregate's membership follows from a filename, not from a list. The `constraints` rule against cross-face references keeps its full strength — no package is exempt.
|
||||
|
||||
The Host program now sees 11 files under `packages/client` (the four Host-face specs and the carrier's Host-face declarations, resolved through its project reference) instead of the 60 that leaked in while the exclusion was pattern-based but the filenames were not.
|
||||
|
||||
vitest discovers every renamed file through `**/*.spec.{ts,tsx}`, so no test configuration changed; the full client suite runs 235 files and 3181 tests. knip's per-workspace `tests/**/*.spec.{ts,tsx}` entry patterns match the new names for the same reason.
|
||||
|
||||
An unsuffixed new test is the failure mode this leaves open: it type-checks in the Host program against Client source instead of failing loudly.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Agent Note: 用文件名标明 client 测试的编译面
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-08-12-face-named-client-test-files.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
`packages/client/*/tests/` 同时存放两个编译面的测试。多数覆盖某个 Client 包的浏览器半边,属于 `tsconfig.client.json`;少数覆盖拆分包的 Host 半边——载体的 node 半边 spec——只能在 `tsconfig.host.json` 里类型检查,因为触及 Host 源码的 Host 面 spec 需要那些文件所在的 Host 工程。
|
||||
|
||||
文件名不说明一个测试覆盖哪一面,两个聚合就无法按模式划分这个目录。host 聚合整体排除 `packages/client/**`,Client 聚合收下全部,于是 Host 面 spec 留在了 Client 程序里。它们随之需要 Client 聚合引用 `packages/client/connection/tsconfig.host.json`——一个 Client 配置进入拆分包的 Host 面,而 `constraints` 的工程引用规则拒绝这条边。
|
||||
|
||||
没有命名规则时另有两条出路,且都更差。在 host 聚合里用 `files` 把那四个文件凿回来,与同一个文件里的整体排除自相矛盾,且每新增一个 Host 面 spec 就要加一条。放行这条跨面引用则削弱了那条把两套 `Context` 合并隔开的规则。
|
||||
|
||||
## 决策
|
||||
|
||||
`packages/client` 下的测试文件在文件名里说明自己覆盖哪一面:
|
||||
|
||||
| 后缀 | 面 | 数量 |
|
||||
|---|---|---|
|
||||
| `*.client.spec.ts` / `*.client.spec.tsx` | Client | 232 |
|
||||
| `*.client.ts` / `*.client.tsx`(共用辅助文件、fixture) | Client | 5 |
|
||||
| `*.host.spec.ts` | Host | 4 |
|
||||
|
||||
两组后缀互斥——谁都不是对方的后缀——因此每个聚合各保留一条宽的测试 glob,并排除对面:
|
||||
|
||||
- `tsconfig.client.json` include `packages/client/*/tests/**/*.{ts,tsx}`,exclude `packages/client/*/tests/**/*.host.spec.ts`。
|
||||
- `tsconfig.host.json` 经其仓库级 `packages/*/*/tests/**/*.ts` 到达同一目录,exclude `packages/client/*/src/**` 以及四条 `*.client.*` 模式。
|
||||
|
||||
这建立在 `exclude` 过滤 `include` 结果之上:两者同时命中时,文件留在程序外。没有文件被两个聚合同时点名,两个聚合都不需要 `files` 条目或跨面工程引用。`verify-md-links` 与 `constraints` 的工程引用规则原样通过,载体不需要任何例外。
|
||||
|
||||
`packages/client` 下新增的测试必须带面名后缀。不带后缀的文件会被 host 聚合的包级 glob 命中,并静默地把 Client 源码拖进 Host 程序。
|
||||
|
||||
## 本次改名清单
|
||||
|
||||
- 232 个 Client 面 spec,从 `*.spec.{ts,tsx}` 改为 `*.client.spec.{ts,tsx}`。
|
||||
- 5 个 Client 面辅助文件,从 `*.{ts,tsx}` 改为 `*.client.{ts,tsx}`:`connection/tests/fake-api`、`runtime/tests/fake-api`、`runtime/tests/event-script`、`ui-conversation/tests/chat-snapshot-fixture`、`ui-tool/tests/tool-details-render`。
|
||||
- `packages/client/connection/tests/` 下 4 个 Host 面 spec,从 `*.spec.ts` 改为 `*.host.spec.ts`:`api-request-trust`、`http-bridge`、`node-half`、`websocket-downlink`。
|
||||
- 2 个 snapshot 文件,跟随各自 spec 改名,内容未变。
|
||||
|
||||
`scripts/rescope-vendor.ts` 的精确编辑表点名了其中三个 spec,那些路径随之移动。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
**只给 Host 面文件加 `*.host.spec.ts` 后缀,Client 侧不动。** 第一次尝试就是这样,而它行不通:`.host.spec.ts` 同样以 `.spec.ts` 结尾,于是 host 聚合对 `*.spec.ts` 的排除把它一并吞掉,`include` 也赢不回来。让两条模式互不相交,靠的正是两面都命名。
|
||||
|
||||
**把 Host 面文件命名为 `*.host-spec.ts`,脱离 `.spec.ts` 惯例。** 不动 Client 侧即与 `*.spec.ts` 不相交,但为了一个配置细节离开了仓库的测试命名惯例和 vitest 的发现模式。
|
||||
|
||||
**把 Host 面 spec 移到 `tests/host/` 子目录,按路径划分。** 用 glob 同样可行,但它把一个包的测试拆到两个目录,浏览 `tests/` 的读者不再一眼看到它们在一起。
|
||||
|
||||
**保留对 `packages/client/**` 的排除,用 `files` 把 Host 面 spec 凿回来。** `files` 不受 `exclude` 过滤,所以确实能拿到它们——代价是同一个文件一边断言该目录属于另一个聚合、一边列出这条断言的例外,且每个 Host 面 spec 都要加一条。
|
||||
|
||||
## 后果
|
||||
|
||||
这条规则的成本是每个 client 测试文件名多一个后缀,买到的是一次机械划分:一个聚合的成员资格由文件名推出,而不是由一份清单决定。`constraints` 里那条禁止跨面引用的规则保持全部强度——没有包获得豁免。
|
||||
|
||||
Host 程序现在在 `packages/client` 下看到 11 个文件(4 个 Host 面 spec,以及经工程引用解析到的载体 Host 面声明),而在排除按模式、文件名却不按模式的状态下漏进来的是 60 个。
|
||||
|
||||
vitest 经 `**/*.spec.{ts,tsx}` 仍能发现每个改名后的文件,因此测试配置没有变化;完整 client 套件跑 235 个文件、3181 个用例。knip 各 workspace 的 `tests/**/*.spec.{ts,tsx}` entry 模式同理匹配新名字。
|
||||
|
||||
这条规则留下的失败模式是新增一个不带后缀的测试:它会在 Host 程序里针对 Client 源码通过类型检查,而不是显式报错。
|
||||
Reference in New Issue
Block a user