From 16fdcd075419b629cdff1a87db24ce394881c2b2 Mon Sep 17 00:00:00 2001 From: Scaxlibur <51772892+Scaxlibur@users.noreply.github.com> Date: Wed, 2 Sep 2026 21:16:45 +0800 Subject: [PATCH 01/14] feat(docs): add repository documentation workflow --- .agents/skills/wavebench-docs/SKILL.md | 104 +++++ .../skills/wavebench-docs/agents/openai.yaml | 6 + .../skills/wavebench-docs/references/audit.md | 54 +++ .../wavebench-docs/references/eval-prompts.md | 35 ++ .../references/information-architecture.md | 108 +++++ .../wavebench-docs/references/migrate.md | 47 ++ .../wavebench-docs/references/review.md | 41 ++ .../skills/wavebench-docs/references/write.md | 57 +++ .../wavebench-docs/scripts/audit_docs.py | 409 ++++++++++++++++++ .../references/development-validation.md | 4 + .github/workflows/ci.yml | 4 + .gitignore | 2 + docs/project/README.md | 1 + ...01\347\247\273\346\217\220\346\241\210.md" | 330 ++++++++++++++ tests/test_docs_audit.py | 74 ++++ 15 files changed, 1276 insertions(+) create mode 100644 .agents/skills/wavebench-docs/SKILL.md create mode 100644 .agents/skills/wavebench-docs/agents/openai.yaml create mode 100644 .agents/skills/wavebench-docs/references/audit.md create mode 100644 .agents/skills/wavebench-docs/references/eval-prompts.md create mode 100644 .agents/skills/wavebench-docs/references/information-architecture.md create mode 100644 .agents/skills/wavebench-docs/references/migrate.md create mode 100644 .agents/skills/wavebench-docs/references/review.md create mode 100644 .agents/skills/wavebench-docs/references/write.md create mode 100644 .agents/skills/wavebench-docs/scripts/audit_docs.py create mode 100644 "docs/project/design/WaveBench_\346\226\207\346\241\243\347\263\273\347\273\237\345\256\241\350\256\241\344\270\216\350\277\201\347\247\273\346\217\220\346\241\210.md" create mode 100644 tests/test_docs_audit.py diff --git a/.agents/skills/wavebench-docs/SKILL.md b/.agents/skills/wavebench-docs/SKILL.md new file mode 100644 index 00000000..0643ec6e --- /dev/null +++ b/.agents/skills/wavebench-docs/SKILL.md @@ -0,0 +1,104 @@ +--- +name: wavebench-docs +description: >- + Develop and maintain the WaveBench repository documentation system. Use only + for active documentation development: audits, information-architecture + migrations, writing or rewriting project docs, generated references, and + documentation-focused diff reviews. Documentation must be a primary requested + deliverable; do not use for incidental doc edits during ordinary code changes, + merely reading docs, instrument operation, or unrelated Chinese copyediting. +license: MIT +metadata: + author: "WaveBench maintainers" + version: "1.0.0" + project: "wavebench" +--- + +# WaveBench documentation development + +## Boundary + +Use this skill only while developing or reviewing documentation owned by the +WaveBench repository. It governs information architecture, page responsibility, +canonical sources, migrations, navigation, examples, and documentation CI. + +Do not use it for: + +- normal WaveBench operation, diagnosis, measurement, or hardware control; +- answering a question by reading existing docs without changing or auditing them; +- ordinary code changes with only an incidental one-line documentation update; +- generic Markdown editing or Chinese copyediting outside WaveBench. + +Hardware access is never part of a documentation audit. Runtime commands used to +verify help or schema must be offline. If a documentation example requires real +hardware, validate it statically unless the user separately authorizes the live +operation under the `wavebench` safety workflow. + +## Start from repository facts + +1. Work from the Git repository root and inspect `git status --short --branch`. +2. Read `README.md`, `pyproject.toml`, `CHANGELOG.md`, the relevant documentation + indexes, and the pages directly in scope. +3. Resolve changing claims from implementation, executable help/schema, tests, + descriptors, and release tags. Existing prose is evidence to audit, not proof + of current behavior. +4. Classify each page by audience, reader goal, type, canonical facts, and related + pages before editing it. +5. Preserve unrelated changes and do not move or rewrite broad document sets + without an accepted audit and migration slice. + +Read [information-architecture.md](references/information-architecture.md) when +deciding taxonomy, page contracts, sources of truth, Core/plugin ownership, +README scope, or user journeys. + +## Choose one mode + +| Mode | Use when | Load | +| --- | --- | --- | +| `audit` | Assess a documentation set without broad edits | [audit.md](references/audit.md) | +| `migrate` | Apply an accepted audit in small, traceable slices | [migrate.md](references/migrate.md) | +| `write` | Add or substantially rewrite a specific page | [write.md](references/write.md) | +| `review` | Review a documentation PR or diff | [review.md](references/review.md) | + +Load only the selected mode plus `information-architecture.md` when that mode +needs taxonomy or source ownership. Do not turn every review into a repository-wide +audit. + +## Invariants + +- One changing fact has one canonical source. Guides may explain or summarize it; + they must not become another complete copy. +- Separate current reliable behavior, explicitly marked Experimental behavior, + and future RFC/roadmap work. Milestones and RFCs do not prove availability. +- Core documentation owns generic models and contracts. Model-specific SCPI, + quirks, profiles, limits, and verification status belong to the instrument + plugin repository. +- A page has one primary action: `KEEP`, `REWRITE`, `SPLIT`, `MERGE`, `MOVE`, + `GENERATE`, `ARCHIVE`, or `DELETE`. +- Structure and facts come before prose polish. For Chinese writing or review, + apply `tech-doc-style-chinese` only after page responsibility and sources are + settled. Do not assume that skill's unrelated `Project-Overrides.md` applies to + WaveBench. +- Prefer generated Reference when stable code or schema can produce it. Generated + output must name its source and have a drift check before it becomes canonical. + +## Mechanical audit + +Run the dependency-free checker from the repository root: + +```bash +python .agents/skills/wavebench-docs/scripts/audit_docs.py +``` + +The script checks deterministic breakage and reports judgment-heavy concerns as +warnings. It does not decide page type, migration action, user-journey quality, or +whether content belongs in a Guide, Reference, or Concept. + +For trigger-boundary maintenance, read [eval-prompts.md](references/eval-prompts.md). + +## Handoff + +Report the documentation scope, canonical sources checked, files changed, audit +errors and warnings, commands or examples verified, content intentionally deferred, +and whether any live hardware, local configuration, generated data, or virtual +environment was touched. diff --git a/.agents/skills/wavebench-docs/agents/openai.yaml b/.agents/skills/wavebench-docs/agents/openai.yaml new file mode 100644 index 00000000..44a95db6 --- /dev/null +++ b/.agents/skills/wavebench-docs/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "WaveBench Docs" + short_description: "审计、迁移、编写与评审 WaveBench 仓库文档" + default_prompt: "使用 $wavebench-docs 审计本次 WaveBench 文档改动,并核对事实源、导航和验证结果。" +policy: + allow_implicit_invocation: true diff --git a/.agents/skills/wavebench-docs/references/audit.md b/.agents/skills/wavebench-docs/references/audit.md new file mode 100644 index 00000000..26945561 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/audit.md @@ -0,0 +1,54 @@ +# Audit 模式 + +> 加载时机:评估整个文档系统、一个目录或一组相关页面,且当前阶段不进行广泛迁移时加载。 + +## 范围 + +全仓 audit 至少盘点 `README.md`、`docs/`、`plans/README.md`、`CHANGELOG.md`、相关 CLI/schema/config/artifact/capability/safety 源码入口,以及 `.agents/skills/`。局部 audit 只扩展到修改页的导航入口、直接相关页面和 canonical source。 + +先运行: + +```bash +python .agents/skills/wavebench-docs/scripts/audit_docs.py +``` + +机械结果只是审计输入。不要把「链接没坏」写成「文档结构合理」。 + +## Inventory + +每篇页面只给一个主要动作: + +| File | Type | Audience | Canonical | Problems | Action | +| --- | --- | --- | --- | --- | --- | + +`Canonical` 说明页面自己是否为该事实的权威来源;若不是,写出实际来源。`Problems` 优先记录职责混合、重复事实、过时风险、旅程断点、Core/plugin 越界和 current/RFC 混杂,不以措辞偏好充数。 + +动作含义: + +- `KEEP`:职责清楚,事实来源稳定。 +- `REWRITE`:路径与主职责可保留,但内容需要按单一读者结果重建。 +- `SPLIT`:一个页面承担多个独立目标。 +- `MERGE`:与另一页面重复维护同一职责或事实。 +- `MOVE`:内容职责合理,目录语义错误。 +- `GENERATE`:机器事实可由稳定模型、schema 或 registry 产生。 +- `ARCHIVE`:历史仍有价值,但不能继续作为当前说明。 +- `DELETE`:无独立价值且由 canonical 页面完整替代。 + +## 系统级输出 + +除 inventory 外,列出并排序: + +- 最严重的 10 个系统问题; +- 八条用户旅程的断点; +- 重复维护的易变事实; +- 孤儿页面和失效导航; +- 超长 mixed-purpose 页面; +- Core/plugin 边界违规; +- Current、Experimental、RFC 和历史记录的混杂; +- 目标信息架构、事实源表和小步迁移顺序。 + +结论必须引用 `file:line`、符号名、命令输出或 tag。若只是推断,要明确标注,不把旧文档互相引用当作事实核验。 + +## 当前基线 + +首轮仓库审计见 [WaveBench 文档系统审计与迁移提案](../../../../docs/project/design/WaveBench_文档系统审计与迁移提案.md)。后续 audit 应重新扫描当前树,不得把这份日期快照当永久事实源。 diff --git a/.agents/skills/wavebench-docs/references/eval-prompts.md b/.agents/skills/wavebench-docs/references/eval-prompts.md new file mode 100644 index 00000000..56e23389 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/eval-prompts.md @@ -0,0 +1,35 @@ +# 触发与模式回归用例 + +> 加载时机:维护 `wavebench-docs` 的 description、隐式触发边界或 reference 路由时加载。 + +## 应触发 + +| 请求 | Mode | 预期加载 | +| --- | --- | --- | +| 「审计 WaveBench 的 README、docs 与事实源,给迁移表」 | `audit` | 入口、`audit.md`、`information-architecture.md` | +| 「按已批准的文档审计拆分 run plan 指南」 | `migrate` | 入口、`migrate.md`、`information-architecture.md` | +| 「为新的 run step 编写 WaveBench Reference」 | `write` | 入口、`write.md`、`information-architecture.md` | +| 「评审这个只改文档的 PR」 | `review` | 入口、`review.md`,按需加载信息架构 | + +## 不应触发 + +| 请求 | 正确处理 | +| --- | --- | +| 「执行这个 WaveBench run plan」 | 使用 `wavebench` 的 run/safety 工作流 | +| 「采集示波器 CH1 并生成报告」 | 使用 `wavebench`,先过硬件写入门禁 | +| 「README 里怎么安装?」 | 直接读取并回答,不进入文档开发 workflow | +| 「修复 config parser 的 bug,顺手更新一句报错说明」 | 普通 WaveBench 代码开发;附带的一行说明不触发完整文档工作流 | +| 「把这段中文写自然一点」 | 使用 `tech-doc-style-chinese`,除非明确是 WaveBench 文档开发 | +| 「解释 Diátaxis」 | 直接解释方法,不审计 WaveBench 仓库 | +| 「解释傅里叶变换」 | 普通知识任务 | + +## 行为检查 + +用全新上下文逐条测试,确认: + +1. 普通使用和只读问答不会加载本 Skill; +2. 文档开发能自动选择正确 mode; +3. `review` 不默认扩大为全仓 audit; +4. `write` 在结构和事实确定前不会先做措辞润色; +5. 文档任务不会自行连接仪器或执行实时 plan; +6. 中文写作层按需交给 `tech-doc-style-chinese`,且不套用无关项目覆盖规则。 diff --git a/.agents/skills/wavebench-docs/references/information-architecture.md b/.agents/skills/wavebench-docs/references/information-architecture.md new file mode 100644 index 00000000..a28f3203 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/information-architecture.md @@ -0,0 +1,108 @@ +# 信息架构与事实源 + +> 加载时机:判断文档类型、目录、页面合同、事实归属、Core/plugin 边界或用户旅程时加载。 + +## 页面类型 + +采用 Diátaxis 的四类用户文档,但按 WaveBench 的实际需求拆页,不为填满目录制造空页面。 + +| Type | Reader outcome | 不应承担 | +| --- | --- | --- | +| Tutorial | 在指导下学习并完成一条端到端流程 | 完整接口枚举、长篇设计论证 | +| How-to | 完成一个已经明确的任务 | 从零教学、完整 schema | +| Reference | 快速查到准确、完整、结构化的事实 | 教程叙事、路线图 | +| Concept | 理解模型、原因和 trade-off | 逐项参数表、发布状态流水账 | + +README 是 landing page;RFC、开发里程碑、发布历史和贡献指南是独立文档类型,不强塞进上述四类。 + +## 页面合同 + +每篇主要页面先确定以下字段,可放在工作笔记中,不要求把元数据块机械写进所有公开页面: + +- `type` +- `audience` +- `reader goal` +- `canonical facts` +- `related pages` + +页面骨架按类型选择: + +- Tutorial:Learning goal、Prerequisites、单条成功流程、关键步骤预期结果、下一步。 +- How-to:任务、必要条件与硬件风险、最短可靠步骤、Verification、常见失败、Reference。 +- Reference:Synopsis、Syntax/Schema、Inputs、Outputs、Exact behavior、Side effects、Errors、Compatibility。 +- Concept:Problem、Model、How it works、Rationale、Trade-offs、相关 Guide/Reference。 + +## Canonical sources + +| Fact | Canonical source | 文档策略 | +| --- | --- | --- | +| 当前包版本 | `pyproject.toml` 的 `[project].version` | 入口页不手写多份;需要展示时由检查器核对 | +| 正式发布版本 | Git tag / release | `CHANGELOG.md` 解释版本变化,不反向定义 tag | +| 版本变化 | `CHANGELOG.md`,内容受 tag 与发布提交约束 | 不把开发分支进度写成正式发布 | +| CLI 命令与参数 | `src/wavebench/cli_parser.py`、`wavebench --help` | Reference 优先生成或嵌入已验证输出 | +| run plan step 与字段 | `src/wavebench/services/run_plan.py`、`wavebench run schema` | 不在多个指南复制完整 step 表 | +| run template | 模板 registry、`wavebench run template --list` | 指南只选任务所需示例 | +| 配置字段 | `src/wavebench/config.py` 的模型与 `load_config()` | `wavebench.example.toml` 是示例,不是完整 schema | +| capability | `InstrumentDescriptor`、capability/operation registry | 用户页解释模型,精确支持由 descriptor 查询或生成 | +| 仪器支持状态 | Core 内建 descriptor 与已安装 plugin descriptor | 汇总页保持简短,并标明来源与生成时间 | +| artifact 格式 | artifact writer、typed result model、package loader | 按 artifact 家族拆 Reference,机器字段优先生成 | +| 安全约束 | Core safety/service contract 与测试 | 用户摘要链接到稳定 Concept/Reference;不得从旧文档抄写 | +| 厂商 SCPI、型号限制与 quirks | `wavebench-instrument-plugins` 的 descriptor、代码与证据 | Core 文档只说明通用合同和查找方式 | + +事实冲突时使用以下优先级:实现和模型 → 可执行 help/schema → 聚焦测试 → descriptor/registry → 对应 tag 的发布记录 → 当前文档。任何降级或无法验证的结论都要明说。 + +## Core 与 instrument plugin 边界 + +Core 文档负责通用仪器抽象、CLI、run plan、artifact、安全、capability、插件 API、通用配置、session 与 recovery。 + +instrument plugin 文档负责具体型号、厂商命令、私有参数、quirks、型号 profile/capability、限制和实机验证状态。 + +Core 可以展示短小的支持摘要,但不能长期维护 DSG830、RTM2032、DG4202 等型号的完整能力矩阵。需要精确答案时,引导读者查询 descriptor 或插件仓库。 + +## 生命周期标签 + +- Current:已发布且由当前实现、测试和文档共同支持。 +- Experimental:已经可用但稳定性或兼容性未承诺,页面显式标记。 +- Proposed:尚未实现或未发布,只能出现在 RFC、issue 或 roadmap。 +- Historical:旧实现、里程碑或证据记录,移入 archive 或对应 Git tag。 + +用户 Reference 对不可用能力只需说明 `unavailable` 或 `unsupported`,不复制开发过程。 + +## README policy + +根 README 只承担项目 landing page:项目名/tagline、主要入口、用途与价值、核心能力、无硬件 Quickstart、简短支持摘要、安全提醒、Contributing、License 和 Acknowledgements。 + +README 不维护 RFC 编号、milestone、A1/A2/A4 阶段、descriptor 内部合同、rollback 实现细节、型号级 capability/profile 或易变化的参数限制。需要提及时用一两句摘要链接到权威页面。 + +## 目标目录 + +目标形态如下,但只有明确读者任务存在时才创建页面: + +```text +docs/ + index.md + getting-started/ + tutorials/ + how-to/ + reference/ + plugins/ + concepts/ + development/ + rfcs/ + archive/ +``` + +`docs/project/` 是可以逐步消除的无语义中间层。先稳定 taxonomy、导航、页面职责和事实源,再决定 MkDocs Material、GitHub Pages、搜索、版本化文档和生成式 Reference。 + +## 必须能走通的用户旅程 + +1. README → Quickstart → 获得离线或 fake 的可见结果。 +2. Installation → Configure Bench → `doctor` → `run verify`。 +3. Tutorial/How-to → `run plan` → artifacts → report。 +4. error message → Troubleshooting → Error Reference。 +5. search/navigation → 精确 Reference。 +6. Concepts → 理解设计与取舍。 +7. Development → Driver/Plugin guide → API Reference。 +8. RFC / CHANGELOG → 查询未来设计或演进历史。 + +任一关键旅程明显断裂时,优先修入口和导航,不先润色深层页面。 diff --git a/.agents/skills/wavebench-docs/references/migrate.md b/.agents/skills/wavebench-docs/references/migrate.md new file mode 100644 index 00000000..3ff21bb5 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/migrate.md @@ -0,0 +1,47 @@ +# Migrate 模式 + +> 加载时机:已有被接受的 audit 或明确迁移决策,需要移动、拆分、合并、归档或生成文档时加载。 + +## 迁移门槛 + +迁移前确认: + +- 页面主类型、受众和 reader outcome 已确定; +- 每项易变事实已有 canonical source; +- 旧页面的 `KEEP/REWRITE/SPLIT/MERGE/MOVE/GENERATE/ARCHIVE/DELETE` 已决定; +- 入口、入链、出链和外部可见 URL 已盘点; +- 本轮切片有可观察的完成标准。 + +没有这些条件时退回 audit,不靠边移动文件边猜信息架构。 + +## 小步顺序 + +1. 先建立目标页或生成器,保留旧入口。 +2. 搬运一类职责,核对事实与技术内容没有丢失。 +3. 更新直接导航、交叉链接和代码示例引用。 +4. 运行文档 audit、相关 `--help`/schema 检查和聚焦测试。 +5. 再将旧页改为短跳转、归档或删除。 + +一轮优先处理 2~4 个能验证设计的页面。通常顺序为 README、docs index、Quickstart、最大 mixed-purpose guide;不要一次性重写整个 `docs/`。 + +## 链接与历史 + +- GitHub Markdown 没有真正重定向;移动高入链页面时,优先保留短的旧路径说明,直到外部入口已迁移。 +- RFC 的不可变决策与实现历史分开。不要为了目录整齐改写已经接受的历史裁决含义。 +- `CHANGELOG.md` 只记录正式 tag;迁移说明不能把开发分支写成发布版本。 +- 归档页面顶部标明历史范围、替代页面和不可作为当前事实源的边界。 + +## 生成式 Reference + +先选择稳定、离线、确定性的源。生成结果必须记录生成命令、源文件或 schema 标识,并由 CI 检查漂移。不要在首轮同时发明生成框架、文档站和版本系统;先从 `run schema` 或 CLI help 的一个窄 Reference 证明流程。 + +## 验证 + +至少执行: + +```bash +python .agents/skills/wavebench-docs/scripts/audit_docs.py +git diff --check +``` + +再按改动增加离线 CLI、schema、示例 plan 或聚焦测试。除非另有明确授权,迁移文档不连接真实仪器,不覆盖 `wavebench.toml`,不生成或提交真实实验数据。 diff --git a/.agents/skills/wavebench-docs/references/review.md b/.agents/skills/wavebench-docs/references/review.md new file mode 100644 index 00000000..8876b687 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/review.md @@ -0,0 +1,41 @@ +# Review 模式 + +> 加载时机:评审文档 PR、提交或工作区 diff 时加载。默认不是全仓 audit。 + +## Review scope + +优先检查: + +1. 本次修改的页面; +2. 它们直接依赖的 canonical Reference 或源码; +3. 导航入口与入链页面; +4. 被引用的命令、配置、plan、schema 和 artifact; +5. 与改动声明直接相关的测试或 descriptor。 + +只有发现系统性重复、迁移跨目录或用户明确要求时,才升级为全仓 audit。 + +## Review order + +- 页面 type、audience 和 reader outcome 是否一致; +- 当前行为、Experimental、RFC 与历史是否清楚分离; +- 易变事实是否回到唯一 canonical source; +- Core/plugin 边界是否正确; +- 链接、导航、文件路径和锚点是否有效; +- 命令、参数、schema、capability、artifact 和示例是否与实现一致; +- 中文页面最后使用 `tech-doc-style-chinese` 检查表达层。 + +先读 diff,确定本次修改的 Markdown,再把这些路径显式传给机械 audit。例如: + +```bash +python .agents/skills/wavebench-docs/scripts/audit_docs.py README.md docs/index.md +git diff -- README.md docs plans .agents/skills +git diff --check +``` + +脚本仍以完整文档图解析链接和入链,但只报告指定文件的问题。发现跨目录迁移或系统性重复时,再不带路径运行全量 audit。 + +## Findings + +按会导致错误操作、错误事实、断链或长期漂移的风险排序。每条 finding 给出紧凑的 `file:line`、影响、事实依据和最小修正方向。 + +不要把个人措辞偏好、未改动页面的旧问题或完整仓库愿望清单混进 PR review。没有实质问题时明确说明,并列出尚未验证的命令、外链或硬件行为。 diff --git a/.agents/skills/wavebench-docs/references/write.md b/.agents/skills/wavebench-docs/references/write.md new file mode 100644 index 00000000..8038c428 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/write.md @@ -0,0 +1,57 @@ +# Write 模式 + +> 加载时机:新增页面,或按已确定职责重写一篇具体页面时加载。 + +## 写前合同 + +开始正文前回答: + +- 谁会来看? +- 当前要完成什么? +- 读完后能做到什么? +- 哪些陈述来自哪些 canonical source? +- 页面主类型是什么?相关细节应链接到哪里? + +无法回答时先做局部 audit。不要把一篇混合页面原样换个标题。 + +## 按类型写作 + +### Tutorial + +固定一条可成功的学习路线。列出 prerequisites;每个关键步骤给可观察的预期结果;解释只保留完成路线所需内容,深层原因链接 Concept。 + +### How-to + +从明确任务开始。给必要条件、真实硬件风险、最短可靠步骤、Verification 和常见失败;参数全集链接 Reference。 + +### Reference + +覆盖 synopsis、syntax/schema、inputs、outputs、exact behavior、side effects、errors 和 compatibility/capability requirements。可从代码生成的表不手工复制。 + +### Concept + +解释 problem、model、how it works、rationale 和 trade-offs;链接相关 Guide 与 Reference,不把操作步骤或发布流水账塞进来。 + +## WaveBench 特有检查 + +- 示例命令先与当前 `--help`、`run schema` 或实现核对。 +- 涉及真实设备的步骤明确区分离线、连接读取和设备写入。 +- 不写入真实 IP、序列号、串口、凭据、本地实验目录或私有证据。 +- 型号级 SCPI、profile、quirk 和验证状态链接 instrument plugin 仓库。 +- Current、Experimental、Proposed 和 Historical 使用明确标签。 +- README 只保留 landing page 内容,不展开内部合同。 + +## 中文表达层 + +页面结构、事实源和技术边界确定后,再使用 `tech-doc-style-chinese` 完成中文措辞与排版:直角引号、克制语气、术语与中西文留白、机器字面量保护等由该 Skill 负责。 + +不要复制那套通用规则到本 Skill,也不要加载其与 WaveBench 无关的 `Project-Overrides.md`。若 WaveBench 以后需要术语覆盖,应在仓库内建立并评审自己的规则。 + +## 完成条件 + +- reader outcome 可验证; +- 事实均能追溯到 canonical source; +- 示例与路径可执行或明确标注未执行原因; +- 导航入口和 related pages 已更新; +- 文档 audit 无新增错误; +- 未验证、Experimental 或未来内容没有伪装成当前能力。 diff --git a/.agents/skills/wavebench-docs/scripts/audit_docs.py b/.agents/skills/wavebench-docs/scripts/audit_docs.py new file mode 100644 index 00000000..c19b53ed --- /dev/null +++ b/.agents/skills/wavebench-docs/scripts/audit_docs.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python3 +"""Audit deterministic documentation integrity without external dependencies. + +The checker deliberately leaves information architecture, page type, audience, +and migration decisions to a human or agent review. Errors fail the command; +warnings become failures only with ``--strict``. +""" + +from __future__ import annotations + +import argparse +import ipaddress +import re +import subprocess +import sys +import tomllib +import unicodedata +from collections import defaultdict +from dataclasses import dataclass +from pathlib import Path +from urllib.parse import unquote, urlsplit + + +DOCUMENT_ROOTS = ("docs", "plans", ".agents/skills") +ROOT_DOCUMENTS = ("README.md", "CHANGELOG.md", "SKILL.md") +EXCLUDED_PREFIXES = ("tool-of-rei/",) +ENTRY_NAMES = {"README.md", "CHANGELOG.md", "SKILL.md"} +LONG_PAGE_LINES = 600 + +FENCE_RE = re.compile(r"^\s*(`{3,}|~{3,})") +HEADING_RE = re.compile(r"^(#{1,6})\s+(.+?)\s*#*\s*$") +HTML_ID_RE = re.compile(r"<(?:a\s+name|[^>]+\sid)=[\"']([^\"']+)[\"']", re.IGNORECASE) +LINK_RE = re.compile( + r"!?\[[^\]]*\]\(\s*(<[^>]+>|[^)\s]+)" + r"(?:\s+(?:\"[^\"]*\"|'[^']*'|\([^)]*\)))?\s*\)" +) +SEMVER_RE = re.compile(r"(? Path: + result = subprocess.run( + ["git", "-C", str(start), "rev-parse", "--show-toplevel"], + check=True, + capture_output=True, + text=True, + ) + return Path(result.stdout.strip()).resolve() + + +def discover_markdown(root: Path) -> list[Path]: + paths: set[Path] = set() + for name in ROOT_DOCUMENTS: + candidate = root / name + if candidate.is_file(): + paths.add(candidate) + for directory in DOCUMENT_ROOTS: + base = root / directory + if base.is_dir(): + paths.update(path for path in base.rglob("*.md") if path.is_file()) + return sorted( + path + for path in paths + if not path.relative_to(root).as_posix().startswith(EXCLUDED_PREFIXES) + ) + + +def visible_markdown_lines(lines: tuple[str, ...]) -> tuple[tuple[int, str], ...]: + visible: list[tuple[int, str]] = [] + fence_char = "" + fence_length = 0 + for number, line in enumerate(lines, 1): + match = FENCE_RE.match(line) + if fence_char: + if match and match.group(1)[0] == fence_char and len(match.group(1)) >= fence_length: + fence_char = "" + fence_length = 0 + continue + if match: + fence_char = match.group(1)[0] + fence_length = len(match.group(1)) + continue + visible.append((number, line)) + return tuple(visible) + + +def heading_slug(title: str) -> str: + text = re.sub(r"`([^`]*)`", r"\1", title.casefold()) + text = re.sub(r"!?\[([^\]]+)\]\([^)]+\)", r"\1", text) + slug: list[str] = [] + for character in text: + if character.isspace(): + slug.append("-") + elif character in {"-", "_"} or not unicodedata.category(character).startswith(("P", "S")): + slug.append(character) + return re.sub(r"-+", "-", "".join(slug)).strip("-") + + +def markdown_structure( + visible_lines: tuple[tuple[int, str], ...], +) -> tuple[tuple[Heading, ...], frozenset[str]]: + headings: list[Heading] = [] + anchors: set[str] = set() + seen_slugs: defaultdict[str, int] = defaultdict(int) + for number, line in visible_lines: + match = HEADING_RE.match(line) + if match: + title = match.group(2).strip() + headings.append(Heading(len(match.group(1)), title, number)) + base = heading_slug(title) + suffix = seen_slugs[base] + anchors.add(base if suffix == 0 else f"{base}-{suffix}") + seen_slugs[base] += 1 + anchors.update(unquote(value) for value in HTML_ID_RE.findall(line)) + return tuple(headings), frozenset(anchors) + + +def load_document(path: Path, root: Path) -> Document: + lines = tuple(path.read_text(encoding="utf-8").splitlines()) + visible = visible_markdown_lines(lines) + headings, anchors = markdown_structure(visible) + return Document( + path=path, + relative=path.relative_to(root), + canonical=path.resolve(), + lines=lines, + visible_lines=visible, + headings=headings, + anchors=anchors, + ) + + +def markdown_links(document: Document) -> list[tuple[int, str]]: + links: list[tuple[int, str]] = [] + for number, line in document.visible_lines: + links.extend((number, match.group(1).strip("<>")) for match in LINK_RE.finditer(line)) + return links + + +def _inside(candidate: Path, root: Path) -> bool: + try: + candidate.relative_to(root) + except ValueError: + return False + return True + + +def _target_document(path: Path, documents: dict[Path, Document], root: Path) -> Document | None: + canonical = path.resolve() + if canonical in documents: + return documents[canonical] + if path.is_file() and path.suffix.lower() == ".md" and _inside(canonical, root): + return load_document(path, root) + return None + + +def check_links( + documents: list[Document], root: Path +) -> tuple[list[Finding], dict[Path, set[Path]]]: + findings: list[Finding] = [] + inbound: dict[Path, set[Path]] = defaultdict(set) + by_canonical = {document.canonical: document for document in documents} + + for document in documents: + base = document.canonical.parent if document.path.is_symlink() else document.path.parent + for line, destination in markdown_links(document): + parsed = urlsplit(destination) + if parsed.scheme or parsed.netloc: + continue + relative_target = unquote(parsed.path) + target = document.path if not relative_target else base / relative_target + target = target.resolve() + if not _inside(target, root): + findings.append( + Finding("error", document.relative.as_posix(), line, f"relative link escapes repository: {destination}") + ) + continue + if not target.exists(): + findings.append( + Finding("error", document.relative.as_posix(), line, f"relative link target does not exist: {destination}") + ) + continue + if target.is_file() and target.suffix.lower() == ".md": + inbound[target].add(document.canonical) + if parsed.fragment: + target_document = _target_document(target, by_canonical, root) + anchor = unquote(parsed.fragment).casefold() + if target_document and anchor not in target_document.anchors: + findings.append( + Finding("warning", document.relative.as_posix(), line, f"anchor was not found: {destination}") + ) + return findings, inbound + + +def check_structure( + documents: list[Document], inbound: dict[Path, set[Path]], max_lines: int +) -> list[Finding]: + findings: list[Finding] = [] + canonical_documents: dict[Path, Document] = {} + for document in documents: + canonical_documents.setdefault(document.canonical, document) + + titles: dict[str, list[Document]] = defaultdict(list) + for document in canonical_documents.values(): + h1 = [heading for heading in document.headings if heading.level == 1] + if not h1: + findings.append(Finding("warning", document.relative.as_posix(), 1, "page has no ATX H1")) + elif len(h1) > 1: + findings.append(Finding("warning", document.relative.as_posix(), h1[1].line, "page has more than one H1")) + else: + titles[h1[0].title.casefold()].append(document) + if len(document.lines) > max_lines: + findings.append( + Finding("warning", document.relative.as_posix(), 1, f"long page: {len(document.lines)} lines (threshold {max_lines})") + ) + + is_entry = document.relative.name in ENTRY_NAMES or ( + len(document.relative.parts) >= 2 + and document.relative.parts[-1] == "SKILL.md" + and "skills" in document.relative.parts + ) + if not is_entry and not inbound.get(document.canonical): + findings.append(Finding("warning", document.relative.as_posix(), 1, "orphan Markdown page: no inbound Markdown link")) + + for duplicate_title, matches in titles.items(): + if len(matches) < 2: + continue + paths = ", ".join(item.relative.as_posix() for item in matches) + for document in matches: + h1 = next(heading for heading in document.headings if heading.level == 1) + findings.append( + Finding("warning", document.relative.as_posix(), h1.line, f"duplicate H1 {duplicate_title!r}: {paths}") + ) + return findings + + +def project_version(root: Path) -> str: + data = tomllib.loads((root / "pyproject.toml").read_text(encoding="utf-8")) + return str(data["project"]["version"]) + + +def check_version_drift(documents: list[Document], current_version: str) -> list[Finding]: + findings: list[Finding] = [] + for document in documents: + relative = document.relative.as_posix() + if relative == "CHANGELOG.md" or relative.startswith("docs/project/rfcs/"): + continue + for number, line in document.visible_lines: + lowered = line.casefold() + current_claim = ( + "当前开发线" in line + or "current development line" in lowered + or bool(re.search(r"(?:Core|核心)\s*`?v?\d+\.\d+\.\d+`?\s*开发线", line)) + ) + if not current_claim or any(marker in line for marker in ("静态下限", "只表示", "核心基线")): + continue + versions = set(SEMVER_RE.findall(line)) + if versions and current_version not in versions: + findings.append( + Finding( + "warning", + relative, + number, + f"current-development version may have drifted: {sorted(versions)} != {current_version}", + ) + ) + return findings + + +def _allowed_documentation_ip(value: str) -> bool: + try: + address = ipaddress.ip_address(value) + except ValueError: + return True + return address.is_loopback or address.is_unspecified or any(address in network for network in TEST_NETWORKS) + + +def check_sensitive_resources(documents: list[Document]) -> list[Finding]: + findings: list[Finding] = [] + for document in documents: + relative = document.relative.as_posix() + for number, line in enumerate(document.lines, 1): + for match in IPV4_RE.finditer(line): + if not _allowed_documentation_ip(match.group(0)): + findings.append( + Finding("warning", relative, number, f"review possible real network address: {match.group(0)}") + ) + for match in VISA_RE.finditer(line): + resource = match.group(0) + if "<" in resource or "..." in resource: + continue + ips = IPV4_RE.findall(resource) + if not ips or any(not _allowed_documentation_ip(value) for value in ips): + findings.append(Finding("warning", relative, number, f"review possible real VISA resource: {resource}")) + if SERIAL_RE.search(line): + findings.append(Finding("warning", relative, number, "review possible device serial number")) + if HOME_PATH_RE.search(line): + findings.append(Finding("warning", relative, number, "review machine-specific absolute path")) + return findings + + +def audit(root: Path, max_lines: int) -> tuple[list[Document], list[Finding]]: + documents = [load_document(path, root) for path in discover_markdown(root)] + link_findings, inbound = check_links(documents, root) + findings = [ + *link_findings, + *check_structure(documents, inbound, max_lines), + *check_version_drift(documents, project_version(root)), + *check_sensitive_resources(documents), + ] + findings.sort(key=lambda item: (item.path.casefold(), item.line, item.level, item.message)) + return documents, findings + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "paths", + nargs="*", + type=Path, + help="report findings only for these repository-relative files or directories", + ) + parser.add_argument("--root", type=Path, help="repository root; defaults to the current Git worktree") + parser.add_argument("--max-lines", type=int, default=LONG_PAGE_LINES, help="long-page warning threshold") + parser.add_argument("--strict", action="store_true", help="treat warnings as failures") + parser.add_argument("--quiet-warnings", action="store_true", help="hide warning details but retain their count") + args = parser.parse_args() + + try: + root = args.root.resolve() if args.root else repository_root(Path.cwd()) + documents, findings = audit(root, args.max_lines) + except (OSError, subprocess.CalledProcessError, KeyError, tomllib.TOMLDecodeError) as error: + print(f"ERROR: unable to audit documentation: {error}", file=sys.stderr) + return 2 + + selected: set[str] = set() + for requested in args.paths: + candidate = requested.resolve() if requested.is_absolute() else (root / requested).resolve() + if not _inside(candidate, root): + print(f"ERROR: requested path escapes repository: {requested}", file=sys.stderr) + return 2 + relative = candidate.relative_to(root) + if candidate.is_dir(): + prefix = relative.as_posix().rstrip("/") + "/" + selected.update( + document.relative.as_posix() + for document in documents + if document.relative.as_posix().startswith(prefix) + ) + else: + selected.add(relative.as_posix()) + if selected: + findings = [finding for finding in findings if finding.path in selected] + + for finding in findings: + if args.quiet_warnings and finding.level == "warning": + continue + print(f"{finding.level.upper()}: {finding.path}:{finding.line}: {finding.message}") + errors = sum(item.level == "error" for item in findings) + warnings = sum(item.level == "warning" for item in findings) + canonical = len({document.canonical for document in documents}) + print( + f"SUMMARY: {len(documents)} Markdown paths ({canonical} canonical), " + f"{errors} errors, {warnings} warnings" + + (f", scoped to {len(selected)} requested paths" if selected else "") + ) + if errors or (args.strict and warnings): + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.agents/skills/wavebench/references/development-validation.md b/.agents/skills/wavebench/references/development-validation.md index aac8bed4..6936e312 100644 --- a/.agents/skills/wavebench/references/development-validation.md +++ b/.agents/skills/wavebench/references/development-validation.md @@ -40,6 +40,10 @@ git diff --check ## 文档规则 +涉及 WaveBench 文档体系审计、信息架构迁移、新增或重写页面以及文档 diff 评审时,使用仓库内 +`wavebench-docs` Skill;普通代码任务中只更新一两处直接相关说明时,不自动扩大为全仓文档审计。 +先确定页面职责和事实源,再处理中文表达。 + 中文 Markdown 使用 `tech-doc-style-chinese` 规则:正文使用直角引号「」,避免第二人称和宣传腔,中文与英文或数字之间留空格;代码、路径、URL、API 路径和配置键保持原样。修改后运行: ```bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 743dfcbd..5b3e58bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,10 @@ jobs: - name: Run Ruff run: python -m ruff check . + - name: Audit documentation + if: matrix.python-version == '3.11' + run: python .agents/skills/wavebench-docs/scripts/audit_docs.py --quiet-warnings + - name: Run unit tests run: python -m pytest -q diff --git a/.gitignore b/.gitignore index 19f86668..7981ad02 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ tool-of-rei/ /.agents/skills/* !/.agents/skills/wavebench/ !/.agents/skills/wavebench/** +!/.agents/skills/wavebench-docs/ +!/.agents/skills/wavebench-docs/** .codex/ # Local WaveBench config and generated data diff --git a/docs/project/README.md b/docs/project/README.md index c1063d64..5ec84b1b 100644 --- a/docs/project/README.md +++ b/docs/project/README.md @@ -32,6 +32,7 @@ DSG830 当前 production 范围包括只读状态、RF-OFF CW、RF-OFF 内部正 ## design:设计说明 +- [文档系统审计与迁移提案](design/WaveBench_文档系统审计与迁移提案.md) - [项目边界](design/WaveBench_项目边界.md) - [设备抽象层](design/WaveBench_设备抽象层.md) - [多仪器流程设计](design/WaveBench_多仪器协同流程设计.md) diff --git "a/docs/project/design/WaveBench_\346\226\207\346\241\243\347\263\273\347\273\237\345\256\241\350\256\241\344\270\216\350\277\201\347\247\273\346\217\220\346\241\210.md" "b/docs/project/design/WaveBench_\346\226\207\346\241\243\347\263\273\347\273\237\345\256\241\350\256\241\344\270\216\350\277\201\347\247\273\346\217\220\346\241\210.md" new file mode 100644 index 00000000..4fc60b85 --- /dev/null +++ "b/docs/project/design/WaveBench_\346\226\207\346\241\243\347\263\273\347\273\237\345\256\241\350\256\241\344\270\216\350\277\201\347\247\273\346\217\220\346\241\210.md" @@ -0,0 +1,330 @@ +# WaveBench 文档系统审计与迁移提案 + +> 审计日期:2026-09-02 +> 代码基线:`master@d602637`,包版本 `0.8.26` +> 状态:第一轮审计完成;尚未执行目录迁移或大规模重写 + +## 结论 + +WaveBench 已经积累了覆盖 CLI、run plan、artifact、插件、安全模型和 RFC 的大量文档,但当前主要问题不是覆盖不足,而是页面职责与事实归属失控:landing page、指南、Reference、Concept、RFC 和开发里程碑经常在同一页出现;版本、capability、schema 与型号状态又被多处手写复制。 + +第一轮不移动现有文件。先建立 `wavebench-docs` 仓库级 Skill、明确 canonical source、保存完整 inventory,并用轻量脚本把确定性的断链和漂移检查接入 CI。后续迁移从 README、docs index、Quickstart 和最大的 mixed-purpose guide 开始,每轮只处理 2~4 个页面。 + +本次审计覆盖: + +- 根 `README.md`、`CHANGELOG.md` 和 `SKILL.md`; +- `docs/` 下全部 41 份 Markdown; +- `plans/README.md` 和 19 份受跟踪 plan; +- `.agents/skills/wavebench/` 的入口与 8 份 reference; +- CLI、run schema、config、artifact、capability、registry 和 safety 的实现入口及相关测试; +- GitHub Actions 中现有的离线验证路径。 + +`tool-of-rei/` 是本机工作区,不属于公开文档 inventory。根 `SKILL.md` 是指向 `.agents/skills/wavebench/SKILL.md` 的受跟踪符号链接,因此 54 个 Markdown 路径对应 53 个规范文件。 + +## 当前事实源 + +| 易变事实 | Canonical source | 文档处理 | +| --- | --- | --- | +| 当前包版本 | `pyproject.toml` 的 `[project].version`;运行时由 `src/wavebench/__init__.py` 读取 distribution metadata | 入口页需要展示时由检查器核对,不在多页独立维护 | +| 正式发布版本 | Git tag / release | `CHANGELOG.md` 解释变化,但不反向定义 tag | +| 版本变化 | `CHANGELOG.md`,受对应 tag 的提交与测试约束 | 开发分支进度不得写成正式发布 | +| CLI 命令和参数 | `src/wavebench/cli_parser.py:29` 的 `build_parser()` 与实际 `wavebench --help` | CLI Reference 应生成或直接链接运行时输出 | +| run plan step 和字段 | `src/wavebench/services/run_plan.py:378` 的 `StepSchema`、`STEP_SCHEMAS`、`format_run_plan_schema()` 与 `wavebench run schema` | 指南不复制完整 step 表 | +| run template | 模板 registry 与 `wavebench run template --list` | 页面只选择与任务有关的模板 | +| 配置字段 | `src/wavebench/config.py` 的配置 dataclass 与 `load_config()` | `wavebench.example.toml` 只是示例,不是完整 schema | +| capability | `InstrumentDescriptor`、`CAPABILITY_METHODS`、`InstrumentRegistry`、`OPERATION_REGISTRY` | 精确能力由 descriptor/registry 查询或生成 | +| 仪器支持状态 | Core 内建 descriptor 与已安装的 instrument plugin descriptor | Core 页面只保留短摘要和查询入口 | +| artifact 格式 | `run_artifacts.py`、typed operation result、`data/packages.py` loader | 按 artifact 家族拆分,机器字段优先生成 | +| 安全约束 | Core safety/service contract、`OperationSpec` 与聚焦测试 | Guide 摘要链接稳定 Concept/Reference,不复制旧说明 | +| 厂商私有 SCPI、型号限制、quirk 和实机证据 | `wavebench-instrument-plugins` 的 descriptor、实现、测试和证据 | Core 仓库不维护完整型号矩阵 | + +事实发生冲突时,按「实现和模型 → 可执行 help/schema → 聚焦测试 → descriptor/registry → 对应 tag 的发布记录 → 当前文档」核验。RFC 是决策或提案来源,不是当前可用性的证明。 + +## 完整文档 inventory + +`Canonical` 表示该页面是否应直接维护所述事实。每篇页面只给一个主要动作;动作是迁移判断,不表示本轮已经执行。 + +### 根入口与导航 + +| File | Type | Audience | Canonical | Problems | Action | +| --- | --- | --- | --- | --- | --- | +| `README.md` | Landing / Mixed | 初次访问者、操作者、贡献者 | 否;版本、CLI、capability 来自代码、tag 与 descriptor | 228 行内混入 landing、Quickstart、型号矩阵、RF 里程碑、事务细节和安全 Reference | REWRITE | +| `CHANGELOG.md` | Release history | 用户、发布维护者 | 是;仅限已打 tag 的版本变化说明 | 当前边界清楚;应继续拒绝未发布开发进度 | KEEP | +| `docs/README.md` | Landing / Mixed | 中文文档读者 | 否;只应维护导航 | 与根 README 重复版本、scope/RF 状态和安全分类 | REWRITE | +| `docs/README_EN.md` | Landing / Mixed | 英文文档读者 | 否;只应维护英文入口和语言覆盖说明 | 复制版本与 capability;多数深页仍为中文,入口没有形成完整英文旅程 | REWRITE | +| `docs/project/README.md` | Navigation / Mixed | 用户、开发者 | 否;只应维护导航 | 自称只负责导航,却复制 RF production 状态、RFC 里程碑与精确预算 | MERGE | +| `plans/README.md` | Reference index | plan 使用者 | 是;plan 目录及风险说明 | 已覆盖全部 plan;列表仍需机械核对目录变化 | KEEP | + +### Guides 与 contributing + +| File | Type | Audience | Canonical | Problems | Action | +| --- | --- | --- | --- | --- | --- | +| `docs/project/guides/WaveBench_CLI形态.md` | Reference / Mixed | CLI 用户、脚本作者 | 否;参数来自 `--help` | CLI 总览混入 RF/Source V2 合同和多处能力表 | SPLIT | +| `docs/project/guides/WaveBench_HTTP_MCP_只读接口.md` | Reference | MCP 客户端、部署者 | 部分;HTTP 行为应由实现与测试约束 | 范围单一,边界清楚 | KEEP | +| `docs/project/guides/WaveBench_RF信号源使用指南.md` | How-to / Mixed | RF 操作者、plan 作者 | 否;型号状态来自插件 descriptor | 操作步骤、完整 production 矩阵、历史 evidence 和里程碑混写 | SPLIT | +| `docs/project/guides/WaveBench_TUI终端控制面板.md` | How-to | TUI 使用者 | 部分;命令和行为来自实现 | 页面短且任务明确;实验性标签需要持续保留 | KEEP | +| `docs/project/guides/WaveBench_run_plan_使用指南.md` | Tutorial / How-to / Reference | plan 作者、实验操作者、分析用户 | 否;字段来自 `run schema`,artifact 来自 model | 837 行混合起步、完整 step Reference、频响算法、artifact 与报告部署 | SPLIT | +| `docs/project/guides/WaveBench_可安装仪器插件.md` | How-to / Mixed | 插件用户、运维人员 | 否;lifecycle 和 registry 来自实现 | 安装流程混入 RTM transport 验收与具体覆盖矩阵 | SPLIT | +| `docs/project/contributing/WaveBench_插件开发指南.md` | Development / Reference | 插件开发者 | 部分;开发流程可维护,API 签名不可手写复制 | 350 行中大段 capability/API Reference 与开发流程混写 | SPLIT | +| `docs/project/contributing/WaveBench_新增仪器驱动指南.md` | Development | Core 驱动开发者 | 是;接入流程,具体 API 仍链接实现 | 职责清楚,保留 Core 与外置插件选择说明 | KEEP | + +### Design + +| File | Type | Audience | Canonical | Problems | Action | +| --- | --- | --- | --- | --- | --- | +| `docs/project/design/WaveBench_项目边界.md` | Concept / Product boundary | 用户、维护者 | 是;稳定范围与安全红线 | RF 行项目塞入 DSG830 证据、M/A 阶段和精确 profile | REWRITE | +| `docs/project/design/WaveBench_设备抽象层.md` | Concept | Core、插件开发者 | 是;稳定分层模型 | 当前架构、早期 MVP 目录、伪代码和型号历史混杂 | REWRITE | +| `docs/project/design/WaveBench_多仪器协同流程设计.md` | Concept / Reference / History | 用户、Core 开发者 | 部分;稳定 run 模型可保留 | 692 行同时维护建议第一版、当前 schema、实机记录和实施步骤 | SPLIT | +| `docs/project/design/WaveBench_sweep状态恢复设计.md` | Historical proposal | Core 开发者 | 否;当前 restore 以实现/schema 为准 | 第一版未完成清单仍像当前设计,且 restore 字段与现行说明冲突 | ARCHIVE | +| `docs/project/design/WaveBench_RF信号源设计.md` | Concept / Reference / History | Core、插件开发者 | 部分;稳定领域合同可保留 | 当前 capability、长期模型、CLI、M0~M4/A5 和提交证据混写 | SPLIT | +| `docs/project/design/WaveBench_RF信号源开发里程碑.md` | Development history | Core、插件维护者 | 否;当前 capability 来自 descriptor | 重复 production 清单,主体是 A1~A5 实机与提交历史 | ARCHIVE | + +### Reference + +| File | Type | Audience | Canonical | Problems | Action | +| --- | --- | --- | --- | --- | --- | +| `docs/project/reference/WaveBench_数据输出格式.md` | Reference / Mixed | 用户、分析脚本作者 | 否;字段来自 artifact models/loaders | 871 行混入五类 artifact;开头「只支持」与后文截图、报告等内容冲突 | SPLIT | +| `docs/project/reference/WaveBench_配置文件格式.md` | Reference | 实验台维护者、CLI 用户 | 否;字段来自 config model | `[safety_limits]`、`[source]` 重复,默认值和 driver 列表易漂移 | REWRITE | +| `docs/project/reference/WaveBench_错误处理和日志策略.md` | Reference / Concept | CLI 用户、自动化调用者、插件作者 | 部分;策略可解释,异常/退出码来自实现 | 顶层错误分类、异常类、scope 命令序列、日志和 session 合同混写 | REWRITE | +| `docs/project/reference/plugins/WaveBench_可执行仪器插件API.md` | API Reference / Mixed | 插件开发者、Core 维护者 | 否;签名和 capability 映射来自公开 Protocol/model | 783 行囊括全部领域 API、开发线版本门和测试矩阵 | SPLIT | +| `docs/project/reference/plugins/WaveBench_声明式SCPI插件.md` | Schema Reference | 本地实验室用户 | 否;字段来自 validator/schema | 字段、默认值与约束可从实现生成;人工页只需用途与示例 | GENERATE | +| `docs/project/reference/plugins/WaveBench_插件市场索引.md` | Schema Reference | 插件发现用户、索引生成者 | 否;JSON 结构来自 model/parser | 手写 schema、默认路径、搜索域和示例版本易漂移 | GENERATE | +| `docs/project/reference/plugins/WaveBench_插件注册表.md` | Concept / Reference map | 插件用户、开发者 | 是;三种插件路径与 Core/plugin 边界 | 少量 alias/CLI 细节后续可生成,当前地图价值明确 | KEEP | + +### RFC + +RFC 的 `Canonical` 只针对提案与裁决,不代表当前产品可用性。 + +| File | Type | Audience | Canonical | Problems | Action | +| --- | --- | --- | --- | --- | --- | +| `docs/project/rfcs/README.md` | RFC index | 维护者、插件作者 | 是;RFC 状态词与索引 | 索引复制大量开发线实现细节,并与项目总目录重复 | REWRITE | +| `docs/project/rfcs/WaveBench_scope可移植性RFC组合说明.md` | RFC series / History | Scope 维护者 | 是;系列关系 | 规范词典、实施计划、M0~M8 完成记录和验收日志混写 | SPLIT | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0001_消费型文本查询.md` | Superseded RFC | Transport、插件维护者 | 是;被替代裁决 | 已由 R1 合同取代,保留价值是历史决策 | ARCHIVE | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0002_通道输入状态.md` | RFC / Implementation record | Scope 插件作者 | 是;接口裁决 | V2 规范、实现状态、迁移与验收矩阵混写 | SPLIT | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0003_截图framing与菜单.md` | Superseded RFC | Scope 插件作者 | 是;被替代裁决 | 原入口被更严格的 binary/screenshot 合同替代 | ARCHIVE | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0004_数字通道状态.md` | RFC / Implementation record | Scope 插件作者 | 是;数据模型裁决 | 规范、核心实现和插件 opt-in 状态混写 | SPLIT | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0005_可组合状态快照.md` | RFC / Implementation record | Scope 插件作者 | 是;snapshot 裁决 | 候选模型、文档裁决和离线验收阶段混写 | SPLIT | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0006_采集状态与平均采集.md` | RFC bundle | Scope/Core 维护者 | 是;三个子合同 | 806 行内三个成熟度不同的子 RFC 共用顶层状态 | SPLIT | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0007_统计FFT与光标读取.md` | RFC bundle | Scope/Core 维护者 | 是;三个只读能力裁决 | statistics、FFT、cursor 无法独立判断生命周期 | SPLIT | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0008_有界波形传输裁决.md` | RFC summary | Waveform 维护者 | 部分;完整合同在标准波形 RFC | 与标准波形 RFC 高度重叠,形成双入口 | MERGE | +| `docs/project/rfcs/WaveBench_scope可移植性RFC-0009_SINGLE模式终态STOP证明.md` | RFC addendum | Acquisition 维护者 | 是;窄裁决 | 边界清楚;未发布状态由索引统一解释 | KEEP | +| `docs/project/rfcs/WaveBench_transport重放与session健康RFC.md` | RFC / Implementation history | Transport 维护者 | 是;replay/session 裁决 | Accepted 规范、实施状态和开发里程碑未分层 | SPLIT | +| `docs/project/rfcs/WaveBench_scope通用扩展接口RFC.md` | RFC / History | Scope/Core 维护者 | 是;R1.3 总合同 | 2306 行混入规范、候选实现、否决方案、里程碑和 addendum | SPLIT | +| `docs/project/rfcs/WaveBench_scope通用扩展接口RFC-R1.3-acceptance-addendum.md` | Acceptance record | Core、插件迁移者 | 否;总 RFC 已包含同内容 | 与总 RFC 第十二节重复,形成两个验收入口 | MERGE | +| `docs/project/rfcs/WaveBench_scope通用扩展接口RFC_核心实施说明.md` | Implementation note | Core、插件作者 | 否;当前行为来自实现 | 文件名和目录把随开发线变化的实施说明伪装成 RFC | MOVE | +| `docs/project/rfcs/WaveBench_source能力状态与复合输出安全RFC.md` | RFC bundle / History | Source/Core 维护者 | 是;多轮 Source V2 裁决 | 4122 行串联多轮修订、已实现能力、候选和里程碑,无法按 feature 判断状态 | SPLIT | +| `docs/project/rfcs/WaveBench_标准波形有界二进制传输RFC.md` | RFC / Implementation record | Waveform 维护者 | 是;标准传输裁决 | 稳定合同、开发线实现、commit 基线和实机验收混写 | SPLIT | + +### 现有 WaveBench Skill + +| File | Type | Audience | Canonical | Problems | Action | +| --- | --- | --- | --- | --- | --- | +| `SKILL.md` | Compatibility entry | Agent Skills host | 否;符号链接到规范入口 | 相对链接取决于宿主是否按 symlink target 解析;仓库校验器已把 target 设为规范入口 | KEEP | +| `.agents/skills/wavebench/SKILL.md` | Skill entry | WaveBench Agent | 是;运行、诊断、代码与硬件安全工作流 | 与新文档 Skill 有相邻职责,但不应承载完整文档信息架构 | KEEP | +| `.agents/skills/wavebench/references/development-validation.md` | Skill reference | 开发任务 | 是;开发验证与交接 | 文档段只保留通用开发验证;文档治理交给 `wavebench-docs` | KEEP | +| `.agents/skills/wavebench/references/eval-prompts.md` | Skill reference | Skill 维护者 | 是;触发与安全回归 | 范围清楚 | KEEP | +| `.agents/skills/wavebench/references/plugins.md` | Skill reference | 插件操作任务 | 是;插件操作工作流 | 范围清楚 | KEEP | +| `.agents/skills/wavebench/references/power-and-dmm.md` | Skill reference | 电源、DMM 任务 | 是;硬件工作流 | 范围清楚 | KEEP | +| `.agents/skills/wavebench/references/run-plans.md` | Skill reference | run plan 任务 | 是;安全执行工作流 | 与文档 How-to 不同,不应合并 | KEEP | +| `.agents/skills/wavebench/references/safety-and-recovery.md` | Skill reference | 实时写入任务 | 是;安全门与恢复 | 范围清楚 | KEEP | +| `.agents/skills/wavebench/references/scope-and-capture.md` | Skill reference | 示波器任务 | 是;采集工作流 | 范围清楚 | KEEP | +| `.agents/skills/wavebench/references/source-and-harmonics.md` | Skill reference | 信号源任务 | 是;信号源工作流 | 范围清楚 | KEEP | + +## Plan inventory + +19 份 plan 均由 `plans/README.md` 导航,未发现真实 IP、序列号、串口或 VISA 资源。下列动作只表示后续样例治理方向。 + +| Plan | 角色与问题 | Action | +| --- | --- | --- | +| `active_filter_raw_2d_10mv_2v_10hz_5mhz.toml` | 有源 DUT 高成本二维扫频,属于进阶实验 | MOVE | +| `closure_sine_1k.toml` | 当前公开正弦闭环示例 | KEEP | +| `closure_sine_1k_fft.toml` | 文件已声明为被替代的 v0.4 历史示例 | ARCHIVE | +| `closure_triangle_1k.toml` | 当前公开三角波闭环示例 | KEEP | +| `demo_dg4202_10k_screenshot_report.toml` | v0.2 型号绑定示例,与通用 scope quality 示例近重复 | MERGE | +| `dg4202_duty_10k_power_ch2_check.toml` | 台架验收性质强,不是通用用户路径 | ARCHIVE | +| `dp800_scope_probe_voltage_steps.toml` | 两次 power set,无 restore 或 safety gate,不能继续作为普通示例 | ARCHIVE | +| `example_dmm_acv_source_smoke.toml` | 有明确接线提醒的通用 source→DMM 示例 | KEEP | +| `example_scope_expect_quality.toml` | 通用 scope quality 示例 | KEEP | +| `example_source_scope_dmm_report.toml` | 多仪器 report 示例 | KEEP | +| `passive_filter_2d_calibrated.toml` | 引用时间戳 `data/runs` 路径,跨机器不可复用 | REWRITE | +| `passive_filter_adaptive_5mhz.toml` | 进阶频响流程 | MOVE | +| `passive_filter_dense_2d_calibrated.toml` | 引用时间戳 `data/runs` 路径,跨机器不可复用 | REWRITE | +| `passive_filter_raw_2d_10hz_1mhz.toml` | 进阶二维频响样例 | MOVE | +| `passive_filter_raw_2d_10hz_1mhz_500mv_1v_2v.toml` | 与 retry 版本共享同一测量网格 | MERGE | +| `passive_filter_raw_2d_10hz_1mhz_retry_test.toml` | 只为 warning/retry 验证增加开关,更像测试资产 | ARCHIVE | +| `through_baseline_2d_10k_500k.toml` | 与 stable baseline 目标重复 | MERGE | +| `through_baseline_2d_stable.toml` | 当前 calibrated plan 引用的基线流程 | KEEP | +| `through_diagnostic_100mvpp.toml` | 诊断型进阶样例 | MOVE | + +## 最严重的 10 个系统问题 + +1. **没有独立 Quickstart。** 根 README 的离线步骤位于 `README.md:94-126`,但没有明确的 reader outcome、逐步预期结果或单一可见成功产物;初次体验仍散落在 landing page。 +2. **根 README 同时承担至少五类职责。** 型号矩阵在 `README.md:129-141`,RF production 细节在 `README.md:143-151`,安全 Reference 在 `README.md:184-203`。入口因此会随实现细节频繁变化。 +3. **三个入口重复维护同一事实。** `README.md:10`、`docs/README.md:5` 和 `docs/README_EN.md:5` 都手写开发版本与稳定 tag;scope focus、RF 状态和 I/O 分类也在多处重复。 +4. **run plan 主指南已经失去单一读者目标。** `WaveBench_run_plan_使用指南.md` 有 837 行,`420-593` 行维护 Source V2 step,`701-783` 行维护 artifact,前段又承担 Quickstart 与频响教程。 +5. **Reference 主要靠人工复制机器事实。** `WaveBench_配置文件格式.md:378-431` 与 `525-587` 重复 `[safety_limits]` 和 `[source]`;`WaveBench_数据输出格式.md:11-17` 的范围陈述与后文新增 artifact 冲突。 +6. **当前版本与能力状态已经发生漂移。** 包版本是 `0.8.26`,但 `WaveBench_RF信号源设计.md:5,18` 和开发里程碑仍称 Core `0.8.25` 开发线,`docs/project/README.md:56` 仍维护 `0.8.24` 开发线状态。 +7. **RFC 既是规范,又是开发日志。** Scope 总 RFC 为 2306 行,Source V2 RFC 为 4122 行;Accepted、Implemented-unreleased、候选实现、commit 和实机 evidence 无法按 feature 快速区分。 +8. **Core/plugin 文档边界被型号状态污染。** DSG830、RTM2032、DG4202 等 production profile 在 README、项目边界、RF 设计、里程碑和指南中反复出现;精确状态应由插件 descriptor 与插件仓库负责。 +9. **旧设计仍与当前行为并列。** `WaveBench_sweep状态恢复设计.md:302-308` 只列四个恢复字段,`WaveBench_多仪器协同流程设计.md:604-616` 又列出 duty 字段;旧 proposal 未归档导致读者无法判断现行合同。 +10. **基线缺少文档 CI,样例治理也没有机械门。** 原 CI 只运行 Ruff、pytest 和 Windows CLI smoke;两个 calibrated plan 还引用受跟踪文件外的时间戳 `data/runs` 路径。本分支新增轻量 audit,但生成式 Reference 与 schema snapshot drift 尚未建立。 + +## 用户旅程评估 + +| Journey | 当前断点 | 目标入口 | +| --- | --- | --- | +| 第一次看到 WaveBench | README 信息过载;离线流程没有独立成功判据 | `README.md` → `docs/index.md` → `getting-started/quickstart.md` | +| 第一次连接实验台 | 安装、配置、`doctor`、`run verify` 分散在入口、配置 Reference 和 run 指南 | `installation.md` → `configure-bench.md` → `doctor` → `run verify` | +| 做一次真实实验 | 837 行 run 指南同时承担教程和 Reference | 窄 Tutorial/How-to → `run plan` → artifact → report | +| 出错 | 有错误策略 Reference,但没有按症状行动的 Troubleshooting | Error message → `how-to/troubleshooting.md` → `reference/errors.md` | +| 查精确参数 | `--help` 和 `run schema` 是事实源,但导航先落到手写混合页 | Search/nav → generated CLI/config/run Reference | +| 理解设计 | Concept 页面混入版本、型号和里程碑 | `concepts/` 下的稳定模型页 | +| 新增仪器 | 两份开发指南可用,但插件 API 总页过大 | `development/instrument-drivers.md` 或 `plugin-development.md` → generated API Reference | +| 查未来设计或历史 | RFC 状态定义存在,但正文与当前实现记录混杂 | `rfcs/index.md` 查决策,`CHANGELOG.md` 查发布,`archive/` 查实施历史 | + +## 目标信息架构 + +以下是迁移方向,不是一次性建空目录的任务清单: + +```text +docs/ + index.md + getting-started/ + quickstart.md + installation.md + configure-bench.md + tutorials/ + how-to/ + run-an-experiment.md + use-rf-source.md + use-tui.md + serve-mcp.md + manage-plugins.md + troubleshooting.md + reference/ + cli.md + configuration.md + run-schema.md + artifacts.md + capabilities.md + instrument-support.md + errors.md + plugins/ + concepts/ + architecture.md + safety-model.md + capability-model.md + sessions-and-recovery.md + device-abstraction.md + plugin-model.md + development/ + contributing.md + documentation.md + plugin-development.md + instrument-drivers.md + testing.md + rfcs/ + index.md + archive/ +``` + +第一批只创建具有明确用户任务的页面:`docs/index.md`、Quickstart、Installation/Configure Bench、run experiment、Troubleshooting、CLI/config/run Reference、Architecture/Safety、documentation workflow。其余页面在真实内容拆分到来时再创建。 + +`docs/project/` 没有额外语义,可以分批消除。迁移前先记录入链;对高入链旧路径保留短说明,避免外部链接立即失效。 + +## `wavebench-docs` Skill 设计 + +```text +.agents/skills/wavebench-docs/ + SKILL.md + agents/openai.yaml + references/ + information-architecture.md + audit.md + migrate.md + write.md + review.md + eval-prompts.md + scripts/ + audit_docs.py +``` + +入口只保留触发边界、事实优先级、四种模式路由和不变量;详细规则按模式加载。`audit` 负责 inventory、用户旅程和系统问题,`migrate` 负责已接受方案的小步实施,`write` 负责单页合同与事实核验,`review` 只检查 diff 及直接关联页面,不默认升级为全仓审计。 + +`allow_implicit_invocation` 保持开启,但 description 同时要求「文档开发是主要交付物」,并排除以下请求:只读查文档、仪器操作、普通代码修改中的附带一句说明、无关项目的 Markdown 和通用中文润色。这样可以在明确的 WaveBench 文档开发任务中自动选中 Skill,又不让日常使用和代码开发被文档全流程劫持。触发回归用例单独维护在 `references/eval-prompts.md`。 + +Skill 不授权硬件访问。help、schema、template 与生成检查必须离线;任何真实设备示例的执行都要另行进入 `wavebench` 的安全工作流并取得明确授权。 + +## 第一轮 pilot migration 建议 + +### Slice 1:README + +- 将根 README 收敛为 landing page。 +- 只保留项目定位、主要入口、4~6 个核心能力、无硬件 Quickstart 摘要、短支持表、安全提醒、Contributing、License 和 Acknowledgements。 +- 删除 RF 阶段号、descriptor 内部合同、精确 profile 和 recovery 实现细节,改为一两句摘要链接。 + +完成标准:README 不再维护完整 capability 状态;所有移出的技术内容都有目标页,不丢失信息。 + +### Slice 2:docs index + +- 将 `docs/README.md` 与 `docs/project/README.md` 的导航职责合并到 `docs/index.md`。 +- 入口按用户旅程组织,不按历史目录堆文件名。 +- 英文入口保留明确的语言覆盖边界,不伪装成完整英文文档站。 + +完成标准:八条旅程都有唯一首选入口;导航页不再复制版本和 RFC 开发状态。 + +### Slice 3:Quickstart + +- 从 README 和 run plan 指南提取一条无硬件、可见、可验证的完整流程。 +- 优先使用 plan template/check 或 `tui --fake`;每一步写明预期结果。 +- 安装背景、配置全集和设计原因分别链接 Installation、Reference 和 Concepts。 + +完成标准:新环境可以按单一路径得到明确成功结果,且全过程不连接仪器。 + +### Slice 4:run plan 拆分 + +- 保留最短可靠执行流程为 How-to。 +- 将 step/字段移到 generated `run-schema` Reference。 +- 将 artifact 段移到 artifact Reference,将频响与校准拆为独立 Tutorial/How-to。 + +完成标准:原 837 行页面的每一段都有去向;`wavebench run schema` 与 Reference 由同一检查保证一致。 + +每个 slice 独立提交并运行链接 audit、相关离线 CLI/schema、聚焦测试和 `git diff --check`。MkDocs Material、GitHub Pages、搜索和版本化文档放在 taxonomy 与导航稳定之后。 + +## `wavebench-docs` 与中文写作 Skill 的分工 + +| 职责 | `wavebench-docs` | `tech-doc-style-chinese` | +| --- | --- | --- | +| 页面 type、audience、reader outcome | 负责 | 不负责 | +| 信息架构、导航、用户旅程 | 负责 | 不负责 | +| canonical source 与重复事实 | 负责 | 不负责 | +| Core/plugin 边界 | 负责 | 不负责 | +| Current/Experimental/RFC/History | 负责 | 不负责 | +| `KEEP`~`DELETE` 生命周期判断 | 负责 | 不负责 | +| CLI/schema/example 技术核验 | 负责 | 不负责 | +| 中文语气、标点、术语、留白和扫读性 | 确定结构后调用 | 负责 | +| 机器字面量保护 | 提供项目上下文 | 负责最终表达检查 | + +`tech-doc-style-chinese` 保留为最后的中文表达层。它本机现有的 `Project-Overrides.md` 使用另一个项目的 `2.0`/`3.0` 版本规则,不适用于 WaveBench;本工作流不加载或复制该覆盖文件。 + +## 机械验证方案 + +本分支新增 `.agents/skills/wavebench-docs/scripts/audit_docs.py`,使用 Python 标准库,默认规则如下: + +- 失效相对链接和越出仓库的链接:error; +- 高置信度失效 anchor:warning; +- 无入链 Markdown、重复 H1、无 H1、超过 600 行:warning; +- 非 RFC 页面中的「当前开发线」版本漂移:warning; +- 非文档网段 IP、VISA resource、序列号和本机绝对路径:warning; +- root `SKILL.md` 按符号链接 target 解析,避免把兼容入口误判为第二套事实源。 + +warning 默认不阻断 CI;CI 使用 `--quiet-warnings`,只显示并阻断 error,避免每次评审重复刷出存量架构告警。局部 review 显式传入改动路径,完整 audit 才显示全仓 warning;`--strict` 可在清理存量告警后逐步启用。脚本不判断页面类型、是否应拆分、用户旅程或设计内容归属。 + +首轮暂不实现以下检查: + +- CLI/schema snapshot drift:仓库还没有规范 snapshot 或生成目标;先在 run-schema pilot 中建立一个窄生成流程。 +- generated docs drift:等首个 generated Reference 确定 source marker 和生成命令后再加,避免先发明无人使用的框架。 +- 外链在线可用性:不把网络波动引入基础 CI。 +- 全自动 anchor 阻断:不同 Markdown renderer 的 slug 规则仍可能产生误报。 + +原则保持不变:lint 检查文档有没有坏,Agent 检查文档有没有长歪。 diff --git a/tests/test_docs_audit.py b/tests/test_docs_audit.py new file mode 100644 index 00000000..33cdea86 --- /dev/null +++ b/tests/test_docs_audit.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path + + +SCRIPT = ( + Path(__file__).parents[1] + / ".agents" + / "skills" + / "wavebench-docs" + / "scripts" + / "audit_docs.py" +) +SPEC = importlib.util.spec_from_file_location("wavebench_docs_audit", SCRIPT) +assert SPEC and SPEC.loader +AUDIT = importlib.util.module_from_spec(SPEC) +sys.modules[SPEC.name] = AUDIT +SPEC.loader.exec_module(AUDIT) + + +def test_markdown_parser_ignores_fences_and_builds_duplicate_anchors(): + lines = ( + "# 文档 API", + "[有效](guide.md#运行-check)", + "```markdown", + "[忽略](missing.md)", + "# 也忽略", + "```", + "## 运行 `check`", + "## 运行 `check`", + ) + + visible = AUDIT.visible_markdown_lines(lines) + headings, anchors = AUDIT.markdown_structure(visible) + document = AUDIT.Document( + path=Path("README.md"), + relative=Path("README.md"), + canonical=Path("README.md"), + lines=lines, + visible_lines=visible, + headings=headings, + anchors=anchors, + ) + + assert [heading.title for heading in headings] == ["文档 API", "运行 `check`", "运行 `check`"] + assert {"文档-api", "运行-check", "运行-check-1"} <= anchors + assert AUDIT.markdown_links(document) == [(2, "guide.md#运行-check")] + + +def test_documentation_ip_allowlist_only_accepts_nonidentifying_examples(): + assert AUDIT._allowed_documentation_ip("192.0.2.10") + assert AUDIT._allowed_documentation_ip("127.0.0.1") + assert not AUDIT._allowed_documentation_ip("192.168.1.42") + + +def test_link_check_distinguishes_missing_targets_and_anchors(tmp_path): + readme = tmp_path / "README.md" + guide = tmp_path / "guide.md" + readme.write_text( + "# Index\n[missing](missing.md)\n[bad anchor](guide.md#missing)\n[good](guide.md#section)\n", + encoding="utf-8", + ) + guide.write_text("# Guide\n## Section\n", encoding="utf-8") + documents = [AUDIT.load_document(path, tmp_path) for path in (readme, guide)] + + findings, inbound = AUDIT.check_links(documents, tmp_path) + + assert [(finding.level, finding.line) for finding in findings] == [ + ("error", 2), + ("warning", 3), + ] + assert inbound[guide.resolve()] == {readme.resolve()} From cc1581966ad712ea4c22091ebd5170e3ef463ba4 Mon Sep 17 00:00:00 2001 From: Scaxlibur <51772892+Scaxlibur@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:11:53 +0800 Subject: [PATCH 02/14] docs(skill): add documentation constitution --- .agents/skills/wavebench-docs/SKILL.md | 26 +-- .../skills/wavebench-docs/references/audit.md | 22 +- .../references/information-architecture.md | 209 +++++++++++------- .../wavebench-docs/references/migrate.md | 1 + .../wavebench-docs/references/review.md | 1 + .../skills/wavebench-docs/references/write.md | 22 +- 6 files changed, 158 insertions(+), 123 deletions(-) diff --git a/.agents/skills/wavebench-docs/SKILL.md b/.agents/skills/wavebench-docs/SKILL.md index 0643ec6e..3fe81f41 100644 --- a/.agents/skills/wavebench-docs/SKILL.md +++ b/.agents/skills/wavebench-docs/SKILL.md @@ -47,9 +47,9 @@ operation under the `wavebench` safety workflow. 5. Preserve unrelated changes and do not move or rewrite broad document sets without an accepted audit and migration slice. -Read [information-architecture.md](references/information-architecture.md) when -deciding taxonomy, page contracts, sources of truth, Core/plugin ownership, -README scope, or user journeys. +Read [文档宪法](references/information-architecture.md) when deciding taxonomy, +page contracts, sources of truth, Core/plugin ownership, README scope, status, +lifecycle, or user journeys. This is the single normative source for those rules. ## Choose one mode @@ -61,20 +61,12 @@ README scope, or user journeys. | `review` | Review a documentation PR or diff | [review.md](references/review.md) | Load only the selected mode plus `information-architecture.md` when that mode -needs taxonomy or source ownership. Do not turn every review into a repository-wide -audit. +needs constitutional rules. Do not turn every review into a repository-wide audit. ## Invariants -- One changing fact has one canonical source. Guides may explain or summarize it; - they must not become another complete copy. -- Separate current reliable behavior, explicitly marked Experimental behavior, - and future RFC/roadmap work. Milestones and RFCs do not prove availability. -- Core documentation owns generic models and contracts. Model-specific SCPI, - quirks, profiles, limits, and verification status belong to the instrument - plugin repository. -- A page has one primary action: `KEEP`, `REWRITE`, `SPLIT`, `MERGE`, `MOVE`, - `GENERATE`, `ARCHIVE`, or `DELETE`. +- Follow the document constitution for one-fact/one-source, status labels, + Core/plugin ownership and lifecycle actions. - Structure and facts come before prose polish. For Chinese writing or review, apply `tech-doc-style-chinese` only after page responsibility and sources are settled. Do not assume that skill's unrelated `Project-Overrides.md` applies to @@ -84,12 +76,16 @@ audit. ## Mechanical audit -Run the dependency-free checker from the repository root: +In `audit` mode, run the dependency-free checker for the full requested scope: ```bash python .agents/skills/wavebench-docs/scripts/audit_docs.py ``` +For `write` or `review`, pass the changed page and its direct navigation entry as +paths; use a full audit only for an approved migration or a demonstrated systemic +problem. + The script checks deterministic breakage and reports judgment-heavy concerns as warnings. It does not decide page type, migration action, user-journey quality, or whether content belongs in a Guide, Reference, or Concept. diff --git a/.agents/skills/wavebench-docs/references/audit.md b/.agents/skills/wavebench-docs/references/audit.md index 26945561..3a373281 100644 --- a/.agents/skills/wavebench-docs/references/audit.md +++ b/.agents/skills/wavebench-docs/references/audit.md @@ -16,23 +16,15 @@ python .agents/skills/wavebench-docs/scripts/audit_docs.py ## Inventory -每篇页面只给一个主要动作: +每篇页面先按[文档宪法](information-architecture.md)确定主类别、读者目标、canonical +source 和一个主要动作: -| File | Type | Audience | Canonical | Problems | Action | -| --- | --- | --- | --- | --- | --- | +| File | Type | Audience | Reader Goal | Canonical? | Problems | Action | +| --- | --- | --- | --- | --- | --- | --- | -`Canonical` 说明页面自己是否为该事实的权威来源;若不是,写出实际来源。`Problems` 优先记录职责混合、重复事实、过时风险、旅程断点、Core/plugin 越界和 current/RFC 混杂,不以措辞偏好充数。 - -动作含义: - -- `KEEP`:职责清楚,事实来源稳定。 -- `REWRITE`:路径与主职责可保留,但内容需要按单一读者结果重建。 -- `SPLIT`:一个页面承担多个独立目标。 -- `MERGE`:与另一页面重复维护同一职责或事实。 -- `MOVE`:内容职责合理,目录语义错误。 -- `GENERATE`:机器事实可由稳定模型、schema 或 registry 产生。 -- `ARCHIVE`:历史仍有价值,但不能继续作为当前说明。 -- `DELETE`:无独立价值且由 canonical 页面完整替代。 +`Canonical?` 说明页面是否为该事实的权威来源;若不是,写出实际来源。`Problems` +优先记录职责混合、重复事实、过时风险、旅程断点、Core/plugin 越界和 +Current/RFC 混杂,不以措辞偏好充数。生命周期动作的定义只以文档宪法为准。 ## 系统级输出 diff --git a/.agents/skills/wavebench-docs/references/information-architecture.md b/.agents/skills/wavebench-docs/references/information-architecture.md index a28f3203..a50004c2 100644 --- a/.agents/skills/wavebench-docs/references/information-architecture.md +++ b/.agents/skills/wavebench-docs/references/information-architecture.md @@ -1,108 +1,165 @@ -# 信息架构与事实源 +# WaveBench 文档宪法 -> 加载时机:判断文档类型、目录、页面合同、事实归属、Core/plugin 边界或用户旅程时加载。 +> 加载时机:任何 `audit`、`migrate`、`write` 或 `review` 任务中,需要判断页面职责、事实来源、状态、生命周期、导航或 Core/plugin 边界时加载。 +> 本文件是 WaveBench 文档分类、来源与治理规则的唯一规范入口;其它 workflow reference 不重复定义这些规则。 -## 页面类型 +## 适用范围与优先级 -采用 Diátaxis 的四类用户文档,但按 WaveBench 的实际需求拆页,不为填满目录制造空页面。 +本规则适用于仓库的公开文档、示例说明、README、Reference、开发文档和 RFC。它不改变代码、CLI、schema、descriptor 或发布流程本身;发生冲突时,这些可执行或已发布事实优先于文档。 -| Type | Reader outcome | 不应承担 | -| --- | --- | --- | -| Tutorial | 在指导下学习并完成一条端到端流程 | 完整接口枚举、长篇设计论证 | -| How-to | 完成一个已经明确的任务 | 从零教学、完整 schema | -| Reference | 快速查到准确、完整、结构化的事实 | 教程叙事、路线图 | -| Concept | 理解模型、原因和 trade-off | 逐项参数表、发布状态流水账 | +文档事实的核验顺序为: + +1. 实现、类型模型与 schema; +2. 离线可执行的 `--help`、`run schema`、`run template --list` 和 capability 查询; +3. 聚焦测试、descriptor 与 registry; +4. 已发布 Git tag、release 与 `CHANGELOG.md`; +5. 现有文档。 + +旧页面只能作为待核验材料,不能互相证明当前行为。 + +## 页面类别 -README 是 landing page;RFC、开发里程碑、发布历史和贡献指南是独立文档类型,不强塞进上述四类。 +每篇公开页面只选择一个主类别。页面可以链接到其它类别,但不能借「概览」之名承载所有内容。 + +| 类别 | Audience | Reader goal | 页面职责与允许内容 | 必须链接到其它位置 | +| --- | --- | --- | --- | --- | +| README / Landing | 首次访问者、评估者、贡献者 | 判断项目是否适用,并进入正确起点 | 项目身份、用途、核心能力、无硬件起步摘要、简短支持摘要、安全提醒、主要入口、贡献与许可证 | 安装详情、完整参数、型号状态、RFC、实现细节、长故障排查 | +| Getting Started | 新环境使用者、首次接入实验台的人员 | 安装、配置或完成首个明确结果 | 最小前置条件、线性步骤、每个关键步骤的预期结果、成功判据、下一步 | 完整 schema、所有选项、架构原因、型号私有信息 | +| Tutorial | 正在学习 WaveBench 工作方式的人员 | 在指导下完成一次完整体验并建立直觉 | 单条端到端学习路径、必要解释、可观察结果、最小安全边界 | 参数全集、所有替代路径、深层设计论证 | +| How-to | 已知目标的操作者 | 可靠完成一个具体任务 | 任务目标、必要条件、硬件风险、最短步骤、Verification、常见失败、相关 Reference | 从零教学、完整 API/schema、开发历史 | +| Reference | 查询精确行为的用户、自动化调用方、插件作者 | 查到准确、完整、可定位的机器事实 | Synopsis、syntax/schema、inputs、outputs、exact behavior、side effects、errors、compatibility;优先生成或自动验证 | 教程叙事、设计取舍、路线图、实验过程记录 | +| Concept | 需要理解模型与取舍的用户、维护者 | 理解系统为何这样设计 | Problem、model、how it works、rationale、trade-offs、稳定边界 | 命令手册、字段全集、发布状态流水账、型号实机证据 | +| Development | Core 或插件开发者 | 修改、测试、发布或迁移实现 | 开发流程、贡献边界、测试门、兼容策略、链接到 API Reference 和 RFC | 当前用户操作指南、型号能力矩阵、私有实验细节 | +| RFC | 设计评审者、实现者 | 审阅提案、冻结合同或理解未发布设计 | 问题、约束、兼容性、决策、验收门和明确状态 | 面向用户的当前功能承诺;未发布实现不得伪装为稳定 Reference | +| Historical | 维护者、追溯者 | 查询旧提案、里程碑、证据或发布时状态 | 日期/版本范围、原始背景、替代页面、不能作为当前事实源的声明 | 当前 CLI、实时支持状态、新的用户路径 | + +`Navigation` 是页面的辅助职责,不是独立事实源。导航页只说明阅读目的和下一步,不复制参数、profile、RFC 进度或 schema。 ## 页面合同 -每篇主要页面先确定以下字段,可放在工作笔记中,不要求把元数据块机械写进所有公开页面: +所有主要页面先在工作笔记中确定:`type`、`audience`、`reader goal`、`canonical facts` 和 `related pages`。不要求把这五项机械写成公开页面的元数据块。 -- `type` -- `audience` -- `reader goal` -- `canonical facts` -- `related pages` +### Tutorial -页面骨架按类型选择: +- Learning goal; +- Prerequisites; +- 一条可完成的流程; +- 每个关键步骤的预期结果; +- 后续阅读入口。 -- Tutorial:Learning goal、Prerequisites、单条成功流程、关键步骤预期结果、下一步。 -- How-to:任务、必要条件与硬件风险、最短可靠步骤、Verification、常见失败、Reference。 -- Reference:Synopsis、Syntax/Schema、Inputs、Outputs、Exact behavior、Side effects、Errors、Compatibility。 -- Concept:Problem、Model、How it works、Rationale、Trade-offs、相关 Guide/Reference。 +深层原因链接 Concept,参数全集链接 Reference。 -## Canonical sources +### How-to -| Fact | Canonical source | 文档策略 | -| --- | --- | --- | -| 当前包版本 | `pyproject.toml` 的 `[project].version` | 入口页不手写多份;需要展示时由检查器核对 | -| 正式发布版本 | Git tag / release | `CHANGELOG.md` 解释版本变化,不反向定义 tag | -| 版本变化 | `CHANGELOG.md`,内容受 tag 与发布提交约束 | 不把开发分支进度写成正式发布 | -| CLI 命令与参数 | `src/wavebench/cli_parser.py`、`wavebench --help` | Reference 优先生成或嵌入已验证输出 | -| run plan step 与字段 | `src/wavebench/services/run_plan.py`、`wavebench run schema` | 不在多个指南复制完整 step 表 | -| run template | 模板 registry、`wavebench run template --list` | 指南只选任务所需示例 | -| 配置字段 | `src/wavebench/config.py` 的模型与 `load_config()` | `wavebench.example.toml` 是示例,不是完整 schema | -| capability | `InstrumentDescriptor`、capability/operation registry | 用户页解释模型,精确支持由 descriptor 查询或生成 | -| 仪器支持状态 | Core 内建 descriptor 与已安装 plugin descriptor | 汇总页保持简短,并标明来源与生成时间 | -| artifact 格式 | artifact writer、typed result model、package loader | 按 artifact 家族拆 Reference,机器字段优先生成 | -| 安全约束 | Core safety/service contract 与测试 | 用户摘要链接到稳定 Concept/Reference;不得从旧文档抄写 | -| 厂商 SCPI、型号限制与 quirks | `wavebench-instrument-plugins` 的 descriptor、代码与证据 | Core 文档只说明通用合同和查找方式 | +- 任务目标; +- 必要条件和真实硬件风险; +- 最短可靠步骤; +- Verification; +- 常见失败; +- 相关 Reference。 -事实冲突时使用以下优先级:实现和模型 → 可执行 help/schema → 聚焦测试 → descriptor/registry → 对应 tag 的发布记录 → 当前文档。任何降级或无法验证的结论都要明说。 +不把单个任务页扩展成产品总教程。 -## Core 与 instrument plugin 边界 +### Reference -Core 文档负责通用仪器抽象、CLI、run plan、artifact、安全、capability、插件 API、通用配置、session 与 recovery。 +- Synopsis; +- Syntax / Schema; +- Inputs / Fields / Parameters; +- Outputs; +- Exact behavior 和 side effects; +- Errors; +- Compatibility / capability requirements。 -instrument plugin 文档负责具体型号、厂商命令、私有参数、quirks、型号 profile/capability、限制和实机验证状态。 +可以从稳定实现生成的字段、参数或 capability 表,不长期手工复制。生成结果必须标明来源和生成/验证命令。 -Core 可以展示短小的支持摘要,但不能长期维护 DSG830、RTM2032、DG4202 等型号的完整能力矩阵。需要精确答案时,引导读者查询 descriptor 或插件仓库。 +### Concept -## 生命周期标签 +- Problem / motivation; +- Model; +- How it works; +- Design rationale; +- Trade-offs; +- 相关 Guide 与 Reference。 -- Current:已发布且由当前实现、测试和文档共同支持。 -- Experimental:已经可用但稳定性或兼容性未承诺,页面显式标记。 -- Proposed:尚未实现或未发布,只能出现在 RFC、issue 或 roadmap。 -- Historical:旧实现、里程碑或证据记录,移入 archive 或对应 Git tag。 +不把命令步骤、当前版本号、commit、里程碑或型号证据塞进概念页。 -用户 Reference 对不可用能力只需说明 `unavailable` 或 `unsupported`,不复制开发过程。 +## One fact, one canonical source + +| 事实 | Canonical source | 文档规则 | +| --- | --- | --- | +| 当前包版本 | `pyproject.toml` 的 `[project].version` | 展示时核对,不在多个页面长期手写 | +| 正式发布版本 | Git tag / release | 只有 tag/release 才能表示正式可用版本 | +| 版本变化 | `CHANGELOG.md`,以对应 tag 和发布提交约束 | 开发分支进度不得伪装成发布历史 | +| CLI 命令、参数和默认值 | CLI 实现和 `wavebench --help` | 参考页生成或验证;Guide 只保留任务所需命令 | +| run plan step、字段和 schema | `run_plan.py` 和 `wavebench run schema` | 不在多个 Guide 复制完整 step 表 | +| run template | 模板 registry 和 `wavebench run template --list` | 只在 Tutorial/How-to 选择适用示例 | +| capability | descriptor、capability/operation registry 与 `capability explain` | 解释模型可以人工维护;精确可用性应查询或生成 | +| 仪器支持状态 | Core 内建 descriptor 与 instrument plugin descriptor | Core 只给简短摘要;型号详情链接插件仓 | +| 配置字段和约束 | config model、parser 和 schema | `wavebench.example.toml` 是示例,不是完整 schema | +| artifact 格式 | artifact writer、typed result、package loader | 按 artifact 家族拆页;机器字段优先生成 | +| 安全语义 | Core safety/service contract 与测试 | 说明风险和流程;不得从旧文档复制限制 | +| 厂商 SCPI、型号参数、quirk、profile、实机验证 | `wavebench-instrument-plugins` 的 descriptor、实现、测试和证据 | Core 文档不维护完整型号矩阵 | +| 未来设计 | RFC、issue 或 roadmap | 不写成 Current;用户页只说明 `unavailable` 或 `unsupported` | + +Guide 可以解释或摘要 canonical fact,但不得变成第二份完整、易变的手工表。事实无法核验时,必须标为未验证,不以相邻文档补齐。 + +## Repository 边界 + +| 归属 | 文档责任 | +| --- | --- | +| WaveBench Core | 通用仪器抽象、CLI、run plan、artifact、安全模型、capability 模型、插件 API、通用配置、session 和 recovery | +| `wavebench-instrument-plugins` | 具体型号、厂商 SCPI、私有参数、quirk、型号 profile/capability、型号限制和实机验证状态 | +| 两仓交界 | Core 定义通用合同与查询入口;插件仓声明实际型号采用和证据。Core 可提供短支持摘要,但不能重复整张型号 capability 矩阵 | + +型号名本身不是越界理由。只有当页面维护型号私有命令、范围、profile 或 evidence 时,内容才应回到插件仓。 ## README policy -根 README 只承担项目 landing page:项目名/tagline、主要入口、用途与价值、核心能力、无硬件 Quickstart、简短支持摘要、安全提醒、Contributing、License 和 Acknowledgements。 +README 是 landing page,不是 specification。它应包含:项目身份和一句定位、主要入口、WaveBench 解决的问题、核心能力、无硬件 Quickstart 摘要、简短支持概览、安全提醒、Contributing、License 和 Acknowledgements。 + +README 不应承载:RFC 编号、milestone、A1/A2/A4 阶段、descriptor 内部合同、session/rollback 实现、型号级 capability/profile、易变化的型号参数或完整错误/配置/CLI Reference。需要这些信息时,用一两句摘要链接到唯一权威页面。 + +## 生命周期 + +每篇现有页面在 audit 或迁移计划中只选择一个主要动作: -README 不维护 RFC 编号、milestone、A1/A2/A4 阶段、descriptor 内部合同、rollback 实现细节、型号级 capability/profile 或易变化的参数限制。需要提及时用一两句摘要链接到权威页面。 +| Action | 适用条件 | +| --- | --- | +| `KEEP` | 职责清楚、事实来源稳定、导航可达 | +| `REWRITE` | 路径和主职责保留,但正文无法通过小修恢复单一读者结果 | +| `SPLIT` | 同页服务多个独立读者目标或混合多个页面类别 | +| `MERGE` | 两页重复维护同一职责或同一易变事实 | +| `MOVE` | 内容职责正确,但目录/类别表达错误 | +| `GENERATE` | 机器事实可由稳定模型、schema、registry 或 help 生成或自动验证 | +| `ARCHIVE` | 历史仍有追溯价值,但不能作为当前事实源 | +| `DELETE` | 没有独立价值,且已被 canonical 页面完整替代 | -## 目标目录 +不因页面很长就自动 `SPLIT`,不因目录不整齐就自动 `MOVE`,也不为对称性创建空页面。每次 `SPLIT` 或 `MOVE` 都要有目标读者、目标页面、链接处理和内容保全方案。 + +## 状态 policy + +| 状态 | 定义 | 可出现的位置 | +| --- | --- | --- | +| Current | 已发布,并由当前实现、测试和文档共同支持 | Guide、Reference、Concept、README 摘要 | +| Experimental | 已可用,但稳定性、兼容性或支持范围未承诺;必须显式标注 | Guide、Reference、Concept | +| Proposed / Future | 未实现、未发布或仅候选设计 | RFC、issue、roadmap;不得作为用户操作承诺 | +| Historical | 旧提案、里程碑、证据、旧发行物或替代前合同 | RFC/archive/对应 Git tag;页面顶部说明替代入口 | -目标形态如下,但只有明确读者任务存在时才创建页面: +「开发线已实现但未发布」对用户文档属于 `Proposed / Future`,不是 `Current`。它可以在 RFC 或 Development 文档中作为实施状态出现,但必须明确不能据此宣称稳定能力、提高插件版本门或执行未经授权的硬件操作。 -```text -docs/ - index.md - getting-started/ - tutorials/ - how-to/ - reference/ - plugins/ - concepts/ - development/ - rfcs/ - archive/ -``` +## 导航与迁移护栏 -`docs/project/` 是可以逐步消除的无语义中间层。先稳定 taxonomy、导航、页面职责和事实源,再决定 MkDocs Material、GitHub Pages、搜索、版本化文档和生成式 Reference。 +目标分类可以逐步采用 `getting-started/`、`tutorials/`、`how-to/`、`reference/`、`concepts/`、`development/`、`rfcs/` 和 `archive/`,但目录不是迁移的前置条件。只有明确读者目标和内容来源时才创建页面。 -## 必须能走通的用户旅程 +所有文档系统都应支持以下阅读路径: -1. README → Quickstart → 获得离线或 fake 的可见结果。 -2. Installation → Configure Bench → `doctor` → `run verify`。 -3. Tutorial/How-to → `run plan` → artifacts → report。 -4. error message → Troubleshooting → Error Reference。 -5. search/navigation → 精确 Reference。 -6. Concepts → 理解设计与取舍。 -7. Development → Driver/Plugin guide → API Reference。 -8. RFC / CHANGELOG → 查询未来设计或演进历史。 +1. README → Quickstart → 离线或 fake 的可见结果; +2. Installation → Configure Bench → `doctor` → `run verify`; +3. Tutorial / How-to → `run plan` → artifacts → report; +4. error → Troubleshooting → Error Reference; +5. navigation/search → 精确 Reference; +6. Concepts → 设计取舍; +7. Development → Driver/Plugin guide → API Reference; +8. RFC / CHANGELOG → 未来设计或发布历史。 -任一关键旅程明显断裂时,优先修入口和导航,不先润色深层页面。 +迁移先建立目标页面,再转移一类职责,更新入链/出链并验证,最后才归档或删除旧页面。日常 review 只检查本次 diff、直接相关页面和 canonical source;只有用户明确要求或发现系统性问题时才升级为全仓 audit。 diff --git a/.agents/skills/wavebench-docs/references/migrate.md b/.agents/skills/wavebench-docs/references/migrate.md index 3ff21bb5..aa74d8f9 100644 --- a/.agents/skills/wavebench-docs/references/migrate.md +++ b/.agents/skills/wavebench-docs/references/migrate.md @@ -6,6 +6,7 @@ 迁移前确认: +- 已加载[文档宪法](information-architecture.md),并按其页面类别、状态与生命周期规则完成判断; - 页面主类型、受众和 reader outcome 已确定; - 每项易变事实已有 canonical source; - 旧页面的 `KEEP/REWRITE/SPLIT/MERGE/MOVE/GENERATE/ARCHIVE/DELETE` 已决定; diff --git a/.agents/skills/wavebench-docs/references/review.md b/.agents/skills/wavebench-docs/references/review.md index 8876b687..32851d70 100644 --- a/.agents/skills/wavebench-docs/references/review.md +++ b/.agents/skills/wavebench-docs/references/review.md @@ -16,6 +16,7 @@ ## Review order +- 先按[文档宪法](information-architecture.md)确认页面类别、状态、事实源、边界和 lifecycle; - 页面 type、audience 和 reader outcome 是否一致; - 当前行为、Experimental、RFC 与历史是否清楚分离; - 易变事实是否回到唯一 canonical source; diff --git a/.agents/skills/wavebench-docs/references/write.md b/.agents/skills/wavebench-docs/references/write.md index 8038c428..29cf7c2b 100644 --- a/.agents/skills/wavebench-docs/references/write.md +++ b/.agents/skills/wavebench-docs/references/write.md @@ -16,21 +16,9 @@ ## 按类型写作 -### Tutorial - -固定一条可成功的学习路线。列出 prerequisites;每个关键步骤给可观察的预期结果;解释只保留完成路线所需内容,深层原因链接 Concept。 - -### How-to - -从明确任务开始。给必要条件、真实硬件风险、最短可靠步骤、Verification 和常见失败;参数全集链接 Reference。 - -### Reference - -覆盖 synopsis、syntax/schema、inputs、outputs、exact behavior、side effects、errors 和 compatibility/capability requirements。可从代码生成的表不手工复制。 - -### Concept - -解释 problem、model、how it works、rationale 和 trade-offs;链接相关 Guide 与 Reference,不把操作步骤或发布流水账塞进来。 +先加载[文档宪法](information-architecture.md)中的页面类别与页面合同。它是 +Tutorial、How-to、Reference 和 Concept 的唯一结构来源;本文件只处理写作时的 +WaveBench 核验,不重复页面类别规则。 ## WaveBench 特有检查 @@ -38,7 +26,7 @@ - 涉及真实设备的步骤明确区分离线、连接读取和设备写入。 - 不写入真实 IP、序列号、串口、凭据、本地实验目录或私有证据。 - 型号级 SCPI、profile、quirk 和验证状态链接 instrument plugin 仓库。 -- Current、Experimental、Proposed 和 Historical 使用明确标签。 +- 在文档宪法允许出现状态的位置,Current、Experimental、Proposed 和 Historical 使用明确标签。 - README 只保留 landing page 内容,不展开内部合同。 ## 中文表达层 @@ -53,5 +41,5 @@ - 事实均能追溯到 canonical source; - 示例与路径可执行或明确标注未执行原因; - 导航入口和 related pages 已更新; -- 文档 audit 无新增错误; +- 对单页 write,`audit_docs.py` 显式传入改动页及其直接导航入口,且无新增错误;只有已批准迁移或已证实的系统性问题才运行全量 audit; - 未验证、Experimental 或未来内容没有伪装成当前能力。 From 141b8d2fc384a36ab598681bf9efdf7ebd3ecc8f Mon Sep 17 00:00:00 2001 From: Scaxlibur <51772892+Scaxlibur@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:28:39 +0800 Subject: [PATCH 03/14] docs(plan): map target information architecture --- ...01\347\247\273\346\217\220\346\241\210.md" | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git "a/docs/project/design/WaveBench_\346\226\207\346\241\243\347\263\273\347\273\237\345\256\241\350\256\241\344\270\216\350\277\201\347\247\273\346\217\220\346\241\210.md" "b/docs/project/design/WaveBench_\346\226\207\346\241\243\347\263\273\347\273\237\345\256\241\350\256\241\344\270\216\350\277\201\347\247\273\346\217\220\346\241\210.md" index 4fc60b85..73f053e3 100644 --- "a/docs/project/design/WaveBench_\346\226\207\346\241\243\347\263\273\347\273\237\345\256\241\350\256\241\344\270\216\350\277\201\347\247\273\346\217\220\346\241\210.md" +++ "b/docs/project/design/WaveBench_\346\226\207\346\241\243\347\263\273\347\273\237\345\256\241\350\256\241\344\270\216\350\277\201\347\247\273\346\217\220\346\241\210.md" @@ -328,3 +328,132 @@ warning 默认不阻断 CI;CI 使用 `--quiet-warnings`,只显示并阻断 e - 全自动 anchor 阻断:不同 Markdown renderer 的 slug 规则仍可能产生误报。 原则保持不变:lint 检查文档有没有坏,Agent 检查文档有没有长歪。 + +## Step 3:目标架构与迁移图 + +本节将审计结论转化为迁移决策。它不是当前产品 Reference,也不改变任何代码、CLI、schema、descriptor 或发布状态;Current 行为仍以文档宪法所列的 machine source 为准。 + +### 最终导航树与页面职责 + +目录只在已有明确读者目标和可迁入内容时建立。`archive/` 不进入主导航,历史页面只从其替代页面或 RFC 索引按需链接。 + +```text +docs/ + index.md 文档首页:按用户目标导航 + getting-started/ + quickstart.md 无硬件完成首个离线结果 + installation.md 安装 WaveBench + configure-bench.md 配置实验台并准备 doctor/verify + tutorials/ + from-template-to-report.md 从模板学习一次完整实验流程 + how-to/ + run-an-experiment.md 预检、验证和执行一个既有 plan + frequency-response-and-calibration.md + troubleshooting.md + use-tui.md + serve-mcp.md + manage-plugins.md + use-rf-source.md + reference/ + cli.md CLI 命令入口和副作用分类 + configuration.md 配置模型与字段说明 + run-schema.md run plan Reference 的说明入口 + generated/run-schema.md 从 `wavebench run schema` 生成 + artifacts.md run artifact 和分析产物 + capabilities.md capability 查询模型 + instrument-support.md Core 支持摘要与插件入口 + errors.md + plugins/ + index.md + concepts/ + architecture.md + safety-model.md + capability-model.md + sessions-and-recovery.md + device-abstraction.md + plugin-model.md + development/ + contributing.md + documentation.md + plugin-development.md + instrument-drivers.md + testing.md + rfcs/ + index.md + archive/ +``` + +| 导航类别 | 一句话职责 | 主要事实来源 | +| --- | --- | --- | +| 首页与开始使用 | 让首次使用者获得明确的离线结果或完成实验台准备 | 安装配置、模板命令和离线 CLI 输出 | +| 教程与 How-to | 按学习目标或已知任务组织可验证步骤 | CLI、run schema、模板、artifact writer 与安全合同 | +| Reference | 让读者查询精确、可追溯的当前行为 | 实现、`--help`、schema、model、descriptor 与测试 | +| Concepts | 解释稳定模型、边界和取舍 | Core model/service contract 与测试 | +| Development | 指导贡献、插件开发、测试和文档维护 | 公开 API、打包/验证流程与 RFC | +| RFC 与 archive | 保留提案、裁决和历史,而不承诺当前可用性 | RFC、Git 历史、对应 release 和插件证据 | + +### Current → Target + +下表是逐步迁移图。`旧路径短说明` 表示保留原 URL 的简短说明和替代入口,直到内部入链与外部书签有明确迁移窗口;它不复制参数、版本或 capability 表。 + +| 当前页面或页面组 | 目标页面 | Action | 迁移理由与依赖 | +| --- | --- | --- | --- | +| `README.md` | 保留 `README.md` | REWRITE | 收敛为 landing page;依赖 `docs/index.md` 和 Quickstart 承接导航与完整步骤。 | +| `docs/README.md`、`docs/project/README.md` | `docs/index.md`,旧路径短说明 | MERGE / MOVE | 两页的导航职责重合,`docs/project/` 没有额外用户语义。 | +| `docs/README_EN.md` | 保留原路径 | REWRITE | 只说明英文覆盖范围和中文完整文档入口,不维护第二套事实表。 | +| `docs/project/guides/WaveBench_CLI形态.md` | `reference/cli.md`;相关 How-to/Concept | SPLIT / MOVE | 命令和参数转向 `--help`;任务步骤和模型解释各归其类。 | +| `docs/project/guides/WaveBench_run_plan_使用指南.md` | Tutorial、`how-to/run-an-experiment.md`、`how-to/frequency-response-and-calibration.md`、`how-to/troubleshooting.md`、`reference/run-schema.md`、`reference/artifacts.md`;旧路径短说明 | SPLIT | 该页承担教程、运行步骤、schema、artifact、频响和排错;先建立接收页,再收缩旧页。 | +| `docs/project/guides/WaveBench_RF信号源使用指南.md` | `how-to/use-rf-source.md` 和 capability Reference | SPLIT / MOVE | 操作步骤留 Core;型号 profile、SCPI、quirk 和证据回插件仓。 | +| `docs/project/guides/WaveBench_TUI终端控制面板.md` | `how-to/use-tui.md` | MOVE | 页面职责单一,保留 Experimental 边界。 | +| `docs/project/guides/WaveBench_HTTP_MCP_只读接口.md` | `how-to/serve-mcp.md` | MOVE | 页面职责单一;端点和选项由实现及 help 核验。 | +| `docs/project/guides/WaveBench_可安装仪器插件.md` | `how-to/manage-plugins.md`、`reference/plugins/index.md` | SPLIT / MOVE | 用户生命周期与 API/transport 细节分离。 | +| `docs/project/reference/WaveBench_配置文件格式.md` | `reference/configuration.md` | REWRITE / MOVE | 由 config model 核验,去除重复段和型号私有状态。 | +| `docs/project/reference/WaveBench_数据输出格式.md` | `reference/artifacts.md`;只在独立读者目标成立时再拆 family 页 | SPLIT / MOVE | 以 writer、typed result 和 loader 重新核验字段。 | +| `docs/project/reference/WaveBench_错误处理和日志策略.md` | `reference/errors.md`、`how-to/troubleshooting.md`、session Concept | SPLIT / MOVE | 错误查询、按症状排错和设计语义分离。 | +| `docs/project/reference/plugins/WaveBench_可执行仪器插件API.md` | `reference/plugins/` 的生成式 API Reference + `development/plugin-development.md` | SPLIT / GENERATE | API 签名和 capability 映射不再长期手写。 | +| `docs/project/reference/plugins/WaveBench_声明式SCPI插件.md`、`WaveBench_插件市场索引.md` | `reference/plugins/` 的人工说明 + generated schema | GENERATE | 字段和默认值由 parser/validator 生成或验证。 | +| `docs/project/reference/plugins/WaveBench_插件注册表.md` | `reference/plugins/index.md`、`concepts/plugin-model.md` | SPLIT / MOVE | 使用路径和信任边界可解释;命令枚举转到 CLI Reference。 | +| `docs/project/contributing/WaveBench_插件开发指南.md` | `development/plugin-development.md` 和 API Reference | SPLIT / MOVE | 开发流程与精确 API 合同分离。 | +| `docs/project/contributing/WaveBench_新增仪器驱动指南.md` | `development/instrument-drivers.md` | MOVE | 保留其已有的单一开发目标。 | +| `docs/project/design/WaveBench_项目边界.md`、`WaveBench_多仪器协同流程设计.md` | `concepts/architecture.md`、`safety-model.md`、`sessions-and-recovery.md` | SPLIT / REWRITE | 稳定模型迁入 Concept;schema、实机记录和阶段状态不进入 Current 页面。 | +| `docs/project/design/WaveBench_设备抽象层.md` | `concepts/device-abstraction.md` | REWRITE / MOVE | 保留 Core/plugin 分层;移除早期目录、伪代码和型号历史。 | +| `docs/project/design/WaveBench_sweep状态恢复设计.md`、`WaveBench_RF信号源开发里程碑.md` | `archive/`;旧路径短说明 | ARCHIVE | 分别是提案与里程碑,不能作为当前行为来源。 | +| `docs/project/design/WaveBench_RF信号源设计.md` | `concepts/capability-model.md`、`how-to/use-rf-source.md`、`archive/` | SPLIT | 通用模型留 Core;型号 private facts 回插件仓;实施历史归档。 | +| `docs/project/rfcs/README.md` | `rfcs/index.md`,旧路径短说明 | REWRITE / MOVE | RFC 索引只维护状态和决策入口,不复制开发线进度。 | +| Scope、Source、transport、waveform RFC | `rfcs/` 的裁决页与 `archive/` 的实施记录 | SPLIT / MOVE | 将提案、已接受裁决、未发布实现和验收历史明确分层。 | +| 已被替代的 Scope RFC-0001、RFC-0003 | `archive/rfcs/` | ARCHIVE | 保留决策历史和替代链接。 | +| Scope RFC-0008、R1.3 acceptance addendum | 各自的主 RFC;旧路径短说明或 archive | MERGE | 避免两份页面维护相同裁决。 | +| `plans/README.md` | 保留 `plans/README.md` | KEEP / 后续 GENERATE | 与 plan 目录同址;先保留风险说明,后续再决定派生清单是否生成。 | +| `CHANGELOG.md` | 保留根路径 | KEEP | 仅表达正式 tag 的发布历史。 | + +### Target → Sources + +| 目标页面 | Canonical source | 人工页面可解释的内容 | +| --- | --- | --- | +| `getting-started/quickstart.md` | `pyproject.toml`、`wavebench --help`、template registry、`run check` 测试 | 最短离线步骤、预期输出和下一步。 | +| `getting-started/installation.md`、`configure-bench.md` | `pyproject.toml`、config model、`doctor --help`、`run verify` | 安装、配置位置、连接读取边界。 | +| `reference/cli.md` | `cli_parser.py` 与 `wavebench --help` | 命令分类、读取/写入风险和深层入口。 | +| `reference/configuration.md` | `config.py`、parser、`wavebench.example.toml` | 配置目的和安全边界;示例不替代 schema。 | +| `reference/run-schema.md`、`generated/run-schema.md` | `StepSchema`、`STEP_SCHEMAS`、`format_run_plan_schema()`、`wavebench run schema` | 人工说明页面解释用法;机器字段在 generated 页面中维护。 | +| `reference/artifacts.md` | `run_artifacts.py`、typed results、package loader | 产物家族、解释方式和兼容策略。 | +| `reference/capabilities.md`、`instrument-support.md` | descriptor、registry、`capability explain` 与插件 descriptor | 查询模型、Core/plugin 责任;不复制型号 profile。 | +| `reference/errors.md` | error definitions、CLI error handling、logging/artifact writer、tests | 错误类别和稳定操作含义。 | +| `concepts/*.md` | Core model/service contract 与聚焦测试 | 设计动机、模型和 trade-off。 | +| `development/*.md` | public API、package checker、测试和贡献流程 | 如何扩展与验证;当前产品 Reference 另行链接。 | + +### 迁移依赖与推荐顺序 + +1. `docs/index.md`、README 和 Quickstart 建立首次使用入口。 +2. 建立 run plan 的短 How-to、Tutorial、排错入口和 schema Reference;原长页只在所有内容有去向后收缩。 +3. 迁移 CLI、configuration、artifacts 和错误 Reference,修复这些事实的高频重复。 +4. 依序处理 Scope、Source、RF Source、Power/DMM 与 plugins;每次只移动一个领域并回查对应 machine source。 +5. 迁移稳定 Concept 与 development 页面;不让 Concept 重新承担命令或字段 Reference。 +6. 最后整理 RFC 和 archive,因为它们是入链密集的历史材料。 + +每个切片必须先创建目标页、更新直接入链与导航、运行离线验证,再把旧页面改为短说明或 archive。不会为 Power/DMM、单独 Source、artifact family 或 RF Concept 创建没有实质内容的空页。 + +### 生成候选与仓库边界 + +首个生成切片是 `reference/generated/run-schema.md`:`format_run_plan_schema()` 的输出已排序、离线且不依赖真实仪器,适合由 CI 比较工作树。声明式 SCPI plugin schema 是第二候选;在 parser 尚未暴露稳定 schema emitter 前,先保持为评估项,不用手工表冒充生成结果。 + +Core 文档只保留通用仪器抽象、CLI、run plan、artifact、安全、capability、session/recovery 和 plugin API。具体型号、SCPI、私有参数、profile、quirk、限制与实机 evidence 的迁移目标是 `wavebench-instrument-plugins`;Core 页面只链接该仓库或 descriptor 查询入口。历史 milestone、提案与未发布实现进入 RFC 或 archive,并在页首明确它们不能作为 Current 产品承诺。 From b129876751786948b896f93ad37bdcb6edf0275d Mon Sep 17 00:00:00 2001 From: Scaxlibur <51772892+Scaxlibur@users.noreply.github.com> Date: Wed, 2 Sep 2026 22:38:14 +0800 Subject: [PATCH 04/14] docs: migrate landing and run-plan pilot --- README.md | 236 +---- docs/README.md | 95 +- docs/README_EN.md | 88 +- docs/archive/run-plan-guide-pre-migration.md | 840 ++++++++++++++++++ docs/getting-started/quickstart.md | 60 ++ .../frequency-response-and-calibration.md | 52 ++ docs/how-to/run-an-experiment.md | 69 ++ docs/how-to/troubleshooting.md | 43 + docs/index.md | 58 ++ docs/project/README.md | 76 +- ...77\347\224\250\346\214\207\345\215\227.md" | 840 +----------------- docs/reference/artifacts.md | 53 ++ docs/reference/run-schema.md | 25 + docs/tutorials/from-template-to-report.md | 67 ++ 14 files changed, 1336 insertions(+), 1266 deletions(-) create mode 100644 docs/archive/run-plan-guide-pre-migration.md create mode 100644 docs/getting-started/quickstart.md create mode 100644 docs/how-to/frequency-response-and-calibration.md create mode 100644 docs/how-to/run-an-experiment.md create mode 100644 docs/how-to/troubleshooting.md create mode 100644 docs/index.md create mode 100644 docs/reference/artifacts.md create mode 100644 docs/reference/run-schema.md create mode 100644 docs/tutorials/from-template-to-report.md diff --git a/README.md b/README.md index 441853f0..279c79b5 100644 --- a/README.md +++ b/README.md @@ -1,228 +1,62 @@ # WaveBench -[English documentation](docs/README_EN.md) · [中文文档总览](docs/README.md) · [更新日志](CHANGELOG.md) · [仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins) +[中文文档](docs/index.md) · [English overview](docs/README_EN.md) · [更新日志](CHANGELOG.md) · [仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins) > [!WARNING] -> WaveBench 可以连接并控制真实实验设备。执行会改动仪器状态的命令前,应确认接线、输入阻抗、输出状态和电压 / 电流限制。 +> WaveBench 可以连接并控制真实实验设备。执行会改变仪器状态的命令前,应确认接线、输入阻抗、输出状态以及电压/电流限制。 -WaveBench 是一个用 Python 编写的实验室自动测量台,面向电子设计竞赛调试和日常实验。它把仪器控制、实验步骤和采集证据放在同一条命令链中,支持先离线检查 plan,再决定是否连接硬件。 +WaveBench 是面向电子设计竞赛调试和日常实验的 Python 测量台。它将仪器操作、显式 run plan 和可复查的实验产物放在同一条工作流中:先离线检查,再连接实验台,最后执行受控实验。 -当前仓库开发线为 `0.8.26`,最新稳定 tag 为 `v0.8.0`。不同版本的命令和能力可能不同,以对应 tag 中的文档为准。 +![WaveBench 运行报告示例](docs/images/run_plan_result.png) -## 🌟 特别鸣谢 +## 适用范围 -

- - LINUX DO - -

-

学AI,上L站!祝小破站越来越好~

+- 用信号源、示波器、电源和万用表组成可复现的实验流程。 +- 用 `run check` 在不连接仪器的情况下检查 plan。 +- 将采集包、运行记录和离线 HTML 报告保存在同一实验产物中。 +- 用显式命令控制输出、采集和恢复,不在后台隐式执行 reset 或输出切换。 +- 通过已安装的 instrument plugin 扩展具体仪器型号。 -## WaveBench 适合做什么 +## 无硬件快速开始 -- 把信号源、示波器、电源和万用表组合成一条可复现的实验流程。 -- 保存 CSV、NPY、JSON metadata、命令记录和报告,方便复查结果。 -- 用明确的命令控制输出,不在后台隐式执行 reset、autoscale 或输出切换。 -- 在主包内使用常见仪器;需要其他型号时,再显式安装受信任的本地插件。 - -### WaveBench 特色功能 - -#### 测试报告 - -`run report` 会读取已有的 `run.json`、采集包和命令记录,生成可离线查看的 HTML 报告。报告汇总运行状态、验收结果、波形与频响分析、警告、恢复状态和原始证据链接,适合复查一次实验到底发生了什么。 - -![测试报告示例](docs/images/run_plan_result.png) - -#### 普通扫频 - -普通扫频在固定 Vpp 下沿频率轴采集 DUT 的幅频和相频响应。每条曲线对应一次固定幅值的扫频结果,便于观察通带、衰减和相位变化。 - -![幅频特性曲线](docs/images/magnitude_response.png) - -![相频特性曲线](docs/images/phase_response.png) - -#### 二维扫频 - -`sweep.frequency_response` 支持「请求 Vpp × 频率」二维扫频。每个网格点保留输入与输出波形、频率响应和质量状态,可进一步生成二维校准 LUT;安装 `.[report3d]` 后,还能在 HTML 报告中查看交互式三维增益曲面。 - -![二维频率响应](docs/images/2d_frequency_response.gif) - -#### 多仪器 `run plan` - -显式 `run plan` 可以把信号源、示波器、电源和万用表编排到同一条实验流程中。执行前先用 `run check` 做离线校验,再用 `run verify` 做连接和安全预检;执行过程中保留每个步骤的状态、测量结果、失败证据和恢复记录。 - -run 内的 Source / Power 基础写入会在实际 setter 前回读并比较状态;状态漂移会停止写入并写入差异。缺少完整 `scope.snapshot` 的驱动可通过 `scope status` 返回 `partial summary`,操作能力可用 `capability explain` 离线核对。需要固定 plan、配置和任意波形输入时,先生成 `run intent`,再用 `run plan --intent` 在打开仪器前核验摘要。 - -典型流程是「信号源 → DUT → 示波器 / 万用表」。 - -```mermaid -flowchart LR - source[信号源] --> dut[DUT] - dut --> scope[示波器] - dut --> dmm[万用表] - plan[run plan] --> source - plan --> scope - plan --> dmm - scope --> artifacts[采集包 / 离线报告] - dmm --> artifacts -``` - -#### 示波器联合视图 - -`scope focus` 用一个事务配置一个或多个目标模拟通道,并可同时设置完整横向时间范围、各目标通道的 -V/div,以及是否隐藏插件 profile 声明的其他模拟通道: - -```bash -wavebench scope focus \ - --channel 1 \ - --channel 2 \ - --time-range 0.01 \ - --vertical-scale 1=0.2 \ - --vertical-scale 2=0.5 \ - --hide-others -``` - -Core 不定义仪器型号、通道数量或数值范围;这些 guard 由当前插件的 descriptor profile 声明。 -操作会先读取 profile 全部模拟通道及受保护的时基、位置和偏置字段。成功后保留目标视图;任一写入 -或回读失败时恢复完整 baseline 并重新查询,恢复不完整则停止该 session 的后续写入。该命令不启动 -采集、不调用 autoscale,也不修改耦合或输入终端。 - -## 先在没有仪器时跑通 - -下面的命令只生成和检查 plan,不会连接仪器,也不会打开输出。 -原生 Windows 与 Linux / WSL 均可运行离线命令。Windows 原生硬件访问使用 -`portalocker[win32]` 提供的跨进程锁;Windows 与 WSL 不共享同一锁域,同一台仪器应固定由一种运行环境访问。 +以下命令只安装包、列出模板并打印一个示例 plan;不会连接仪器或打开输出。 ```bash python3 -m venv .venv .venv/bin/python -m pip install -e . -source .venv/bin/activate - -wavebench run template --list -wavebench run template source-scope-sine --output /tmp/wavebench-demo.toml --force -wavebench run check --plan /tmp/wavebench-demo.toml -``` - -PowerShell 示例: - -```powershell -py -3.11 -m venv .venv -.\.venv\Scripts\python.exe -m pip install -e ".[dev,analysis,tui]" -.\.venv\Scripts\python.exe -m wavebench run template --list +.venv/bin/python -m wavebench run template --list +.venv/bin/python -m wavebench run template source-scope-sine --print +.venv/bin/python -m wavebench run check --plan plans/closure_sine_1k.toml ``` -Windows 串口资源使用 `COM3`、`COM10` 等形式;`\\.\COM10` 也会规范化为同一串口身份。 -VISA、SocketIO 和 USB 设备仍需安装对应的厂商驱动或后端。纯 Python 插件支持原生 Windows 的安装、升级、删除和恢复;包含原生 DLL 的插件不在本轮支持范围内。 - -Windows 原生环境暂不保证与 WSL 进程对同一资源互相阻塞。需要使用 WSL 时,可继续使用 -[`scripts/wsl-run.ps1`](scripts/wsl-run.ps1) 作为兼容入口。 +最后一条命令以 `safety_limits=ok / 安全上限=通过` 结束时,表示示例 plan 已通过离线检查。完整的预期结果、Windows 命令和下一步见[无硬件快速开始](docs/getting-started/quickstart.md)。 -查看终端界面时,可另外安装 TUI 依赖。`--fake` 使用模拟设备,不连接实验台: +## 支持范围 -```bash -.venv/bin/python -m pip install -e ".[tui]" -wavebench tui --fake -``` - -执行 plan 前,应先核对 plan 中的 source、power、scope 步骤和恢复范围。`run verify` 用于连接与安全预检,`run plan` 才会进行真实实验。 - -## 内建支持 - -| 入口 | 内建设备 | 主要用途 | 状态 | -| --- | --- | --- | --- | -| 示波器 | R&S RTM2000/RTM2032、RIGOL DS1104Z/DS1000Z | 波形读取、单次采集、多通道和截图 | 主包能力 | -| 信号源 | RIGOL DG4000/DG4202 | 基本波形、频率控制、扫频和任意波上传 | 主包能力 | -| 电源 | RIGOL DP800 | 状态、保护、设定值和输出控制 | 主包能力 | -| 万用表 | RIGOL DM3000/DM3058 | 常用读数、功能和部分量程/触发状态 | 主包能力 | -| run plan | source、rf_source、power、scope、dmm、sleep、频响步骤 | 多仪器编排、质量检查和恢复 | 主入口 | -| TUI | 电源、万用表、信号源面板 | 人工查看和少量控制 | 实验性 | -| 插件 | `wavebench.instruments` 外部 driver | 添加或替换特定仪器实现 | 可选 | - -详细的能力边界和参数见 [文档总览](docs/README.md)、[项目文档分类](docs/project/README.md) 及 `docs/project/reference/` 下的参考页。 - -## RF 信号源 - -`rf_source` 是独立于普通 `source` 的仪器领域。DSG830 当前已开放只读状态、RF OFF 时的单字段 CW 配置、RF-OFF 内部正弦 AM/FM/PM 配置及按模式关闭、具有完整 safety 配置的 `rf_out` ON/OFF、RF-OFF internal/single Pulse 配置,以及 RF-OFF 的 frequency-only Step Sweep 配置。PM 的 production profile 限于 `1.25 rad`。A4-MO 已将受限 `rf_source.modulated_output_enable` 提升到 production:仅接受已激活且精确匹配的 AM `50 %`/`1 kHz`、FM `20 kHz`/`1 kHz`、PM `1.25 rad`/`1 kHz` profile,最大功率均为 `-50 dBm`;普通 `rf_source.output` 仍要求调制关闭。FM/PM 的 WaveBench CH2 分析只记录波形质量,不计量频偏或相偏。Step Sweep 固定为 `STEP`/`FWD`/`RAMP`/`LIN`,配置后保持 Sweep disabled。 - -已完成的 A5 只覆盖一条后面板物理路径:DSG830 的「PULSE IN/OUT」按 output 方向、固定 internal/single/normal/`1 ms`/`100 μs` profile,提供 `rf_source.pulse_output` 与 `wavebench rf-source pulse-output`。它不启用 RF 输出,也不定义 Pulse input、`TRIGGER IN`、Sweep fire、sync/reference、Level Sweep 或 list。A5-0 仍仅是逻辑 Pulse/Sweep trigger configuration 的零写读取合同,DSG830 production descriptor 不声明 `rf_source.trigger_snapshot`。 - -- 日常配置与操作:[RF 信号源使用指南](docs/project/guides/WaveBench_RF信号源使用指南.md) -- 模型、安全语义和 capability 边界:[RF 信号源领域设计](docs/project/design/WaveBench_RF信号源设计.md) -- Core/插件的同步计划和证据状态:[RF 信号源开发里程碑](docs/project/design/WaveBench_RF信号源开发里程碑.md) - -## 三条常用路径 - -### source → scope 完整流程 - -先用模板生成 plan,再检查它: - -```bash -wavebench run template source-scope-sine --output plans/my-sine.toml -wavebench run check --plan plans/my-sine.toml -``` - -确认接线、scope coupling 和安全限值后,才执行: - -```bash -cp -n wavebench.example.toml wavebench.toml -# 编辑 wavebench.toml,填写当前实验台的 resource;已有配置不要覆盖 -wavebench run verify --config wavebench.toml --plan plans/my-sine.toml -wavebench run plan --config wavebench.toml --plan plans/my-sine.toml -wavebench run report data/runs/ -``` - -### power → DMM / scope - -DP800 的设定值、保护和输出是三类独立操作。示例计划见 [plans/README.md](plans/README.md)。厂商命令和型号资料由[仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins)维护;本仓库只说明 WaveBench 的调用边界。 - -### 双通道频率响应 - -使用 `source-scope-frequency-response` 模板可以生成 reference / response 双通道扫频 plan。基础频响采集不要求额外依赖;PCHIP、平滑样条和二维校准需要 `analysis`,PDF 报告需要 `pdf`,交互式三维 HTML 需要 `report3d`。详细说明见 [run plan 使用指南](docs/project/guides/WaveBench_run_plan_使用指南.md);执行前仍需确认真实接线。 - -频响结果可用 `run compare` 离线比较多个 run,并用 `run resume` 生成缺失点补测清单;两条命令都不会连接仪器。每个已生成采集包的频响点会保存 `case_id`、`acquisition_id`、请求 Vpp 与参考通道实测 Vpp,便于复查测量来源。 - -## 命令的安全边界 - -| 类别 | 例子 | 说明 | +| 类别 | Core 提供的通用能力 | 精确型号状态 | | --- | --- | --- | -| 离线 | `run schema`、`run template`、`run check`、`run report`、`run compare`、`run resume`、`capture inspect`、`tui --fake` | 不连接仪器;TUI 可能写本地日志 | -| 连接读取 | `doctor`、`idn`、`status`、`run verify` | 会查询设备;仍应把它当作有状态的 I/O | -| 修改设备 | `scope fetch/capture/autoscale`、source/power setter、output、`run plan`、非 fake TUI | 可能改变设置、触发采集或切换输出 | - -WaveBench 的默认行为包括: - -- 不自动发送 `*RST`; -- 不因设定电压或幅度而自动打开输出; -- 不自动改变示波器输入阻抗;可能的 50 Ω 输入需要显式确认; -- `power set` 不改变输出开关,`power output` 不改变电压/限流设定; -- 各仪器配置支持 `access = "read_write"`、`"read_only"` 或 `"disabled"`;`read_only` 只允许状态和配置读取,`disabled` 只保留离线命令; -- 启用 source restore 后只覆盖文档注明的 basic 状态,不能当成完整通道快照; -- run step 默认在失败后停止后续步骤;只有显式 `on_failure = "continue"` 才会继续。需要保护输出时,可用 `[safety] safety_gate = true` 和授权的 OFF 通道列表;安全门会先关闭目标输出再停止 run; -- HTTP MCP 的工具入口需要认证,当前只提供只读工具;`/health` 是例外,不需要 token。它不提供 raw SCPI 或输出开关。 - -外部 Python 插件按当前用户权限运行,不是安全沙箱。仅安装来源已确认的本地目录或 wheel;公开文档不得包含真实 IP、序列号、串口路径、凭据或实验产物。 +| 示波器 | 读取、采集、截图和受 capability 约束的控制 | 由内建或已安装插件 descriptor 声明 | +| 信号源/RF 信号源 | 显式配置、输出控制、run plan 和安全预检 | 由对应插件的 profile 与 evidence 声明 | +| 电源/万用表 | 状态、设定、读数与 run plan 集成 | 由对应插件 descriptor 声明 | +| 插件 | 发现、安装和公开插件 API | 具体型号、SCPI、quirk 和限制见[仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins) | -## 按任务找文档 +用[文档首页](docs/index.md)按任务进入正确页面;精确命令、字段和 capability 以程序输出、schema 与 descriptor 为准,而不是以本页摘要为准。 -- 第一次安装和配置:[文档总览](docs/README.md)、[配置文件格式](docs/project/reference/WaveBench_配置文件格式.md) -- 编写和检查 run plan:[run plan 使用指南](docs/project/guides/WaveBench_run_plan_使用指南.md) -- 了解采集包和报告:[数据输出格式](docs/project/reference/WaveBench_数据输出格式.md) -- 安装或开发插件:[插件用户指南](docs/project/guides/WaveBench_可安装仪器插件.md)、[插件开发指南](docs/project/contributing/WaveBench_插件开发指南.md) -- 使用 TUI 或 HTTP MCP:[TUI 文档](docs/project/guides/WaveBench_TUI终端控制面板.md)、[HTTP MCP 文档](docs/project/guides/WaveBench_HTTP_MCP_只读接口.md) -- 查厂商命令和历史验证:见[仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins);本仓库只保留 WaveBench 自身的接口和设计文档 +## 安全摘要 -目前中文文档覆盖最完整,英文入口提供安装、离线体验和安全摘要。命令、字段名和 schema 以程序输出为准。 +- `run schema`、`run template`、`run check` 和 `run report` 不连接仪器。 +- `doctor` 与 `run verify` 会查询真实设备;先确认资源与接线。 +- `run plan`、输出控制、采集和部分 TUI 操作可能改变仪器状态。 +- WaveBench 不自动执行 `*RST`,也不会因设置电压、幅度或频率而自动开启输出。 -## 开发 +开始真实实验前,请阅读[执行一次实验](docs/how-to/run-an-experiment.md)和[配置实验台](docs/project/reference/WaveBench_配置文件格式.md)。 -要求 Python 3.11 或更高版本。开发依赖和 optional extras 见 [pyproject.toml](pyproject.toml)。常用检查: - -```bash -.venv/bin/python -m pip install -e ".[dev]" -.venv/bin/ruff check . -.venv/bin/python -m pytest -q -``` +## 文档与贡献 -## 许可证与致谢 +- [文档首页](docs/index.md) +- [示例计划及硬件边界](plans/README.md) +- [插件用户指南](docs/project/guides/WaveBench_可安装仪器插件.md) +- [插件开发指南](docs/project/contributing/WaveBench_插件开发指南.md) +- [更新日志](CHANGELOG.md) WaveBench 使用 MIT 许可证。感谢 Linux DO 社区提供交流和支持。 diff --git a/docs/README.md b/docs/README.md index 88a93a34..e5375ba1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,91 +1,8 @@ -# WaveBench 文档 +# WaveBench 文档旧入口 -[English](README_EN.md) · 中文 +中文文档首页已迁移到[文档首页](index.md)。该页面保留为旧链接入口,不再维护版本、命令、型号 capability 或 RFC 状态。 -WaveBench 是一个用 Python 编写的实验室自动测量台,提供 CLI、实验性 TUI、显式 run plan、采集包和离线报告。当前开发线为 `0.8.26`,最新稳定 tag 为 `v0.8.0`。版本变化见 [更新日志](../CHANGELOG.md);旧版本原始文档可切换到对应 Git tag 查看。 - -> [!WARNING] -> 部分命令会连接并控制真实仪器。示例会区分离线检查、连接读取和硬件写入;执行写入前,应确认接线和限制值。 - -## 从这里开始 - -### 尚未连接仪器 - -先阅读根目录的 [README](../README.md),完成安装和离线检查: - -```bash -wavebench run template --list -wavebench run template source-scope-sine --output /tmp/wavebench-demo.toml --force -wavebench run check --plan /tmp/wavebench-demo.toml -``` - -安装 `.[tui]` 后可运行 `wavebench tui --fake` 检查 TUI 界面;该模式使用模拟设备。 - -### 配置实验台 - -- [配置文件格式](project/reference/WaveBench_配置文件格式.md):TOML 查找顺序、字段和安全限制。 -- 仪器型号命令和编程手册由 [仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins) 维护;本仓库只记录 WaveBench 的接入边界。 - -### 配置示波器联合视图 - -`wavebench scope focus` 接受重复的 `--channel`、可选的 `--time-range`、重复的 -`--vertical-scale CHANNEL=V_PER_DIV` 和 `--hide-others`。Core 只定义可移植事务:插件 profile -声明模拟通道、数值范围、容差和 I/O 预算;Core 读取完整联合 baseline,成功后保留目标视图,失败时 -恢复并重新查询。未声明 `scope.focus_configure_v2` 的插件会在仪器 I/O 前拒绝操作。 - -该命令会修改仪器状态,但不会启动采集、调用 autoscale、修改耦合或切换输入终端。执行前应核对 -接线、输入状态和插件 capability。 - -### 使用 RF 信号源 - -`rf_source` 不复用普通 `source` 的 Vpp、offset 或数字 channel 模型。先从 [RF 信号源使用指南](project/guides/WaveBench_RF信号源使用指南.md) 确认当前 production capability 和端接声明;DSG830 已开放固定 profile 的调制输出,以及唯一受验证的后面板 `pulse_in_out` output 路径。后者不代表 Pulse input、`TRIGGER IN` 或同步能力。需要实现新型号或查看证据门时,再阅读 [领域设计](project/design/WaveBench_RF信号源设计.md) 与 [开发里程碑](project/design/WaveBench_RF信号源开发里程碑.md)。 - -### 执行实验 - -- [run plan 使用指南](project/guides/WaveBench_run_plan_使用指南.md):模板、`run check`、`run verify`、执行、恢复和报告。 -- [数据输出格式](project/reference/WaveBench_数据输出格式.md):采集包、run、频响和校准产物。 -- [示例计划目录](../plans/README.md):每个计划的硬件副作用和适用范围。 - -### 查询参数或接口 - -- [CLI 形态](project/guides/WaveBench_CLI形态.md):命令入口和交互/非交互边界。 -- [配置文件格式](project/reference/WaveBench_配置文件格式.md) -- [错误处理和日志策略](project/reference/WaveBench_错误处理和日志策略.md) -- [HTTP MCP 只读接口](project/guides/WaveBench_HTTP_MCP_只读接口.md) -- 插件接口以当前源码中的 Protocol、models 和对应插件文档为准。 - -### 安装或开发插件 - -- [可安装仪器插件用户指南](project/guides/WaveBench_可安装仪器插件.md):本地目录/wheel 的检查、安装、升级、卸载和恢复。 -- [插件开发指南](project/contributing/WaveBench_插件开发指南.md):V2 插件的接入流程和发布检查。 -- [可执行仪器插件 API 约定](project/reference/plugins/WaveBench_可执行仪器插件API.md):descriptor、factory、context、capability 和兼容边界。 -- [新增仪器驱动指南](project/contributing/WaveBench_新增仪器驱动指南.md):从 driver 到 CLI、run plan 和文档的接入路径。 -- [声明式 SCPI 插件](project/reference/plugins/WaveBench_声明式SCPI插件.md) · [插件注册表](project/reference/plugins/WaveBench_插件注册表.md) · [插件市场索引](project/reference/plugins/WaveBench_插件市场索引.md) - -### 理解设计取舍 - -- [项目边界](project/design/WaveBench_项目边界.md) -- [设备抽象层](project/design/WaveBench_设备抽象层.md) -- [多仪器流程设计](project/design/WaveBench_多仪器协同流程设计.md) -- [sweep 状态保存与恢复](project/design/WaveBench_sweep状态恢复设计.md) -- [TUI 终端控制面板](project/guides/WaveBench_TUI终端控制面板.md) - -目录分类见 [project/README](project/README.md)。本页只负责入口,不把阶段记录当作当前使用说明。 - -## 命令的副作用 - -| 类别 | 例子 | 是否连接仪器 | -| --- | --- | --- | -| 离线 | `run schema`、`run template`、`run check`、`run report`、`capture inspect`、`tui --fake` | 不连接仪器;TUI 可能写本地日志 | -| 连接读取 | `doctor`、`idn`、`status`、`run verify` | 是,仅读取或做预检 | -| 修改状态 | `scope focus/fetch/capture/autoscale`、source/power setter、output、`run plan` | 是,可能写入、触发或改变输出 | - -`run check` 不代表 plan 可以安全执行。它只检查 TOML 和字段;真正执行前,还要核对接线、scope coupling、输出状态、保护限值和 restore 条款。 - -## 历史资料与厂商手册 - -版本历史统一放在根目录的 [CHANGELOG.md](../CHANGELOG.md)。厂商手册和 SCPI 摘录由 [仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins) 维护;本仓库只保留 WaveBench 自身的接入边界、接口、配置和安全语义。 - -## 语言和事实源 - -中文页面覆盖最完整,英文入口提供对应的安装、离线体验和安全摘要。版本以 `pyproject.toml` 和 Git tag 为准;CLI 语法以 `wavebench --help`,run plan 语法以 `wavebench run schema` 和 `run template --list` 为准。公开文档中的示例资源必须使用保留地址或占位符,不得写入实验室真实 IP、序列号或串口路径。 +- 首次离线体验:[无硬件快速开始](getting-started/quickstart.md) +- 按任务查阅:[文档首页](index.md) +- 版本变更:[更新日志](../CHANGELOG.md) +- 具体型号、SCPI 和厂商限制:[仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins) diff --git a/docs/README_EN.md b/docs/README_EN.md index 40ceb08a..75aafd49 100644 --- a/docs/README_EN.md +++ b/docs/README_EN.md @@ -1,91 +1,27 @@ # WaveBench Documentation -[中文文档](README.md) · English +[中文完整文档](index.md) · [Project README](../README.md) · [Changelog](../CHANGELOG.md) -WaveBench is a Python measurement bench for laboratory debugging. It combines explicit instrument commands, run plans, capture packages, and offline reports. It requires Python 3.11 or newer. The current development line is `0.8.26`; the latest stable tag is `v0.8.0`. - -> [!WARNING] -> Some commands connect to and change real instruments. Check wiring, input impedance, output state, and voltage/current limits before running a hardware action. +The current complete documentation is maintained in Chinese. This page is an English orientation page; command syntax, schemas, and model-specific support are defined by the installed version of WaveBench and its instrument plugins. ## Start without instruments -The following commands generate and check a plan locally. They do not connect to instruments or enable an output: -Native Windows and Linux/WSL are supported for offline commands. Native Windows uses the -`portalocker[win32]` lock backend; native Windows and WSL do not share a lock domain. - ```bash python3 -m venv .venv .venv/bin/python -m pip install -e . -source .venv/bin/activate - -wavebench run template --list -wavebench run template source-scope-sine --output /tmp/wavebench-demo.toml --force -wavebench run check --plan /tmp/wavebench-demo.toml -``` - -PowerShell equivalent: - -```powershell -py -3.11 -m venv .venv -.\.venv\Scripts\python.exe -m pip install -e ".[dev,analysis,tui]" -.\.venv\Scripts\python.exe -m wavebench run template --list +.venv/bin/python -m wavebench run template --list +.venv/bin/python -m wavebench run template source-scope-sine --print +.venv/bin/python -m wavebench run check --plan plans/closure_sine_1k.toml ``` -Use `COM3` or `COM10` for native Windows serial resources. `\\.\COM10` is normalized to the -same serial identity. VISA, SocketIO, and USB access still depend on the corresponding vendor -driver or backend. Pure-Python plugins support the native Windows lifecycle; native-DLL plugins -require separate validation. The existing [`scripts/wsl-run.ps1`](../scripts/wsl-run.ps1) bridge -remains available for WSL workflows. - -For the terminal UI, install `.[tui]` and run `wavebench tui --fake`. The fake mode uses simulated devices. - -## Built-in support - -| Entry point | Built-in families | Scope | -| --- | --- | --- | -| Oscilloscope | R&S RTM2000/RTM2032, RIGOL DS1104Z/DS1000Z | Waveform reads, captures, multiple channels, screenshots | -| Signal generator | RIGOL DG4000/DG4202 | Basic waveforms, frequency control, sweeps, arbitrary-wave uploads | -| Power supply | RIGOL DP800 | Status, protection, setpoints, and explicit output control | -| DMM | RIGOL DM3000/DM3058 | Common readings and selected function/range/trigger state | -| Run plans | source, power, scope, dmm, sleep, frequency-response steps | Multi-instrument execution and offline checks | -| TUI | Power, DMM, and source panels | Experimental manual control | -| Plugins | `wavebench.instruments` drivers | Optional, explicitly selected extensions | - -## Configure a joint oscilloscope view - -`wavebench scope focus` accepts repeated `--channel` arguments, optional `--time-range`, repeated -`--vertical-scale CHANNEL=V_PER_DIV`, and `--hide-others`. Core defines only the portable -transaction. Each plugin descriptor profile owns its analog-channel set, numeric request guards, -tolerances, and I/O budgets. Core reads the complete joint baseline, preserves the requested view -after success, and restores then freshly verifies that baseline after failure. A plugin that does -not declare `scope.focus_configure_v2` is rejected before instrument I/O. - -This command changes front-panel state. It does not start acquisition, invoke autoscale, change -coupling, or switch input termination. Wiring, input state, and the selected plugin capability must -be checked before execution. - -## Find a guide - -- Setup and configuration: [configuration format](project/reference/WaveBench_配置文件格式.md) -- Run plans and reports: [run plan guide](project/guides/WaveBench_run_plan_使用指南.md) -- Capture and run artifacts: [data output format](project/reference/WaveBench_数据输出格式.md) -- Install or develop plugins: [plugin user guide](project/guides/WaveBench_可安装仪器插件.md) and [plugin development guide](project/contributing/WaveBench_插件开发指南.md) -- TUI and read-only HTTP MCP: [TUI](project/guides/WaveBench_TUI终端控制面板.md) and [HTTP MCP](project/guides/WaveBench_HTTP_MCP_只读接口.md) -- Example plans and their hardware boundaries: [plans README](../plans/README.md) -- RF-source domain and milestones: [guide](project/guides/WaveBench_RF信号源使用指南.md), [design](project/design/WaveBench_RF信号源设计.md), and [milestones](project/design/WaveBench_RF信号源开发里程碑.md). Core provides M0–M4 and one bounded A5 Pulse Output contract; DSG830 A1/A2/A3/A4/A4-MO/A5 evidence permits production identity, snapshot, OFF-only `rf_source.cw_configure`, RF-OFF internal-sine `rf_source.modulation_configure`, `rf_source.modulation_disable`, safety-gated `rf_source.output` ON/OFF, fixed-profile modulated output, internal/single Pulse configuration, `rf_source.pulse_output`, and frequency-only Step Sweep configuration that remains disabled. A5 covers only the declared `pulse_in_out` output route, not Pulse input, `TRIGGER IN`, trigger, sync, or Sweep execution. PM is limited to the verified `1.25 rad` production profile. - -Most detailed pages are currently maintained in Chinese. Commands, identifiers, and schemas should match across languages. - -## Check before running hardware +The last command succeeds with `safety_limits=ok / 安全上限=通过`. It performs no instrument I/O. See the [Chinese Quickstart](getting-started/quickstart.md) for expected output, Windows commands, and the next steps. -| Class | Examples | Instrument I/O | -| --- | --- | --- | -| Offline | `run schema`, `run template`, `run check`, `run report`, `capture inspect`, `tui --fake` | No instrument I/O; TUI may write a local log | -| Connected read/preflight | `doctor`, `idn`, `status`, `run verify` | Yes, for queries and checks | -| State-changing | `scope focus/fetch/capture/autoscale`, source/power setters, output commands, `run plan` | Yes; may change setup, trigger acquisition, or switch output | +## Safety boundary -WaveBench does not implicitly reset instruments, enable outputs, or change oscilloscope input impedance. `power set` and `power output` are separate operations. When enabled, source restoration covers only the documented basic fields; it is not a full channel snapshot. +- `run schema`, `run template`, `run check`, and `run report` are offline. +- `doctor` and `run verify` query configured instruments. +- `run plan`, output commands, capture commands, and non-fake TUI controls may change instrument state. -Executable Python plugins run with the current user's permissions. Install only a trusted local source directory or wheel, and keep real resources, serial numbers, credentials, and generated artifacts out of public documentation. +Before a hardware action, check wiring, input impedance, output state, and voltage/current limits. Model-specific SCPI, limits, and evidence belong to the [instrument plugin repository](https://github.com/Scaxlibur/wavebench-instrument-plugins). -The root [README](../README.md) is the short project entry point. The [Chinese documentation index](README.md) and [project document map](project/README.md) group the longer pages by task. Version history lives in the root [CHANGELOG](../CHANGELOG.md); vendor manuals are maintained in the [instrument plugin repository](https://github.com/Scaxlibur/wavebench-instrument-plugins). +Use the [Chinese documentation index](index.md) to find guides, Reference pages, Concepts, development material, RFCs, and release history. diff --git a/docs/archive/run-plan-guide-pre-migration.md b/docs/archive/run-plan-guide-pre-migration.md new file mode 100644 index 00000000..abd3eebb --- /dev/null +++ b/docs/archive/run-plan-guide-pre-migration.md @@ -0,0 +1,840 @@ +# 归档:重构前的 WaveBench run plan 使用指南 + +> [!NOTE] +> 本页保留了文档重构前的完整 run plan 指南,供追溯和迁移核对使用。它混合了教程、How-to、schema、artifact、频响、Source V2 和历史说明,不能作为当前字段、capability 或型号状态的唯一来源。请从[执行一次实验](../how-to/run-an-experiment.md)、[从模板到报告](../tutorials/from-template-to-report.md)、[run plan Reference](../reference/run-schema.md)和[运行产物 Reference](../reference/artifacts.md)开始。 + +本页面向需要编写 plan 的开发者。重点是现场快速编写、检查 plan,并定位失败原因;内部实现不在本页展开。 + +除非单独标注,带 `^` 的多行命令示例使用 PowerShell 续行;bash / zsh 可把 `^` 换成 `\`,或直接写成一行。 + +## 先看 schema,再写 plan + +先运行: + +```powershell +python -m wavebench run schema +``` + +它会列出当前代码真正支持的 step kind、必填字段和可选字段。文档可能会落后,`run schema` 以当前代码为准。 + +接线或 IP 可能变动时,先跑只读 doctor: + +```powershell +python -m wavebench doctor --config wavebench.toml + +python -m wavebench doctor --config wavebench.toml ^ + --discover-subnet 192.0.2.0/24 ^ + --discover-timeout-ms 500 +``` + +`doctor` 会对配置里的 scope/source/power/dmm 做只读 `*IDN?` 检查,确认资源可达、型号匹配,并给出排错建议。`--discover-subnet` 只在配置资源不可达或型号不匹配时扫描候选资源;它只建议替代 `resource`,不会修改配置。 + +最小流程: + +```powershell +python -m wavebench run schema +python -m wavebench run check --config wavebench.toml --plan plans/example_scope_expect_quality.toml +python -m wavebench run verify --config wavebench.toml --plan plans/example_scope_expect_quality.toml +python -m wavebench run plan --config wavebench.toml --plan plans/example_scope_expect_quality.toml +``` + +`run check` 只解析 plan 并打印摘要,不连接仪器;`run verify` 执行只读资源和安全预检;`run plan` 才会真实执行。 + +## 执行意图与摘要核验 + +需要把计划交给另一个进程或在执行前固定输入时,可以先生成执行意图: + +```powershell +python -m wavebench run intent --config wavebench.toml --plan plans/example_scope_expect_quality.toml ^ + --output data/intents/example.json + +python -m wavebench run plan --config wavebench.toml --plan plans/example_scope_expect_quality.toml ^ + --intent data/intents/example.json +``` + +执行意图使用 `wavebench.execution_intent.v1`,包含 plan/config 摘要、任意波形 payload 摘要和每个 +step 的 `OperationSpec`。`run plan --intent` 会在取得资源租约、打开仪器 session 前重新计算摘要; +计划、配置或 payload 发生变化时返回 `execution_intent_mismatch`。未提供外部意图文件时,run 仍会 +把生成的意图写入 `run.json.provenance.execution_intent`。 + +## run plan 的 session 生命周期 + +`run plan` 会在一次 run 开始时为 plan 需要的仪器统一打开 session,并在 safety guard、snapshot/restore 和所有 step 之间复用这些 session。run 成功、step 失败、safety guard 失败或 restore 失败时,都会统一关闭已经打开的 session。 + +这和普通 CLI one-shot 命令不同: + +- `power status`、`source set-freq` 这类单次 CLI 命令仍然是一次操作打开一次、结束关闭。 +- `run plan` 是整次实验持有 session,减少同一 plan 内反复连接/断开的开销,也让 safety、采集和 restore 使用同一批仪器连接。 +- 当前第一版不做长 session 断线自动重建;如果 run 中途断线,应该让 run 失败并留下 `run.json` 证据,而不是偷偷重连后继续。 + +## RF 信号源步骤 + +RF 使用独立的 `rf_source.*` step,不使用普通 `source` 的 channel、Vpp、restore 或 safety 语义。当前 DSG830 production descriptor 已开放只读状态、OFF-only CW、RF-OFF 内部正弦调制、受 safety 限制的 RF 输出、internal/single Pulse 和保持 Sweep disabled 的 frequency-only Step Sweep 配置: + +```toml +[[steps]] +kind = "rf_source.set_frequency" +port_id = "rf_out" +frequency_hz = 1000000 + +[[steps]] +kind = "rf_source.set_power_dbm" +port_id = "rf_out" +power_dbm = -40 + +[[steps]] +kind = "rf_source.output_enable" +port_id = "rf_out" + +[[steps]] +kind = "rf_source.output_disable" +port_id = "rf_out" + +[[steps]] +kind = "rf_source.pulse_configure" +port_id = "rf_out" +period_s = 0.001 +width_s = 0.0001 +polarity = "normal" + +[[steps]] +kind = "rf_source.sweep_configure" +port_id = "rf_out" +start_frequency_hz = 1000000 +stop_frequency_hz = 2000000 +points = 11 +dwell_s = 0.02 +``` + +CW、Pulse 和 Step Sweep 配置均要求 RF 输出明确 OFF,且调制、Pulse、Sweep 与 protection 没有冲突。Pulse 只支持 internal/single,配置后保持 disabled;Step Sweep 固定为 `STEP`/`FWD`/`RAMP`/`LIN`,不 arm、fire、trigger、execute、配置 Level Sweep 或切换 RF 输出,配置后保持 disabled。`rf_source.output_enable` 还会检查每端口安全配置、实际端接、频率、功率和 fresh snapshot;不满足时会在 ON 前拒绝。RF operation 的类型化 artifact 写入 `run.json.rf_source_operations`。 + +Core 已在 schema 中提供 M3 的 `rf_source.modulation_configure`: + +```toml +[[steps]] +kind = "rf_source.modulation_configure" +port_id = "rf_out" +modulation_kind = "fm" +frequency_deviation_hz = 10000 +internal_frequency_hz = 1000 +``` + +`modulation_kind` 为 `am`、`fm` 或 `pm`,分别只能使用 `depth_percent`、`frequency_deviation_hz` 或 `phase_deviation_rad`。它只覆盖内部 Sine,要求 RF OFF、所有调制模式 disabled、Pulse/Sweep disabled 和无活动 protection condition。DSG830 production descriptor 已声明调制 capability,profile 为 AM `0–100 %`、FM `0.1 Hz–1 MHz`、PM 精确 `1.25 rad`,且内部频率均为 `10 Hz–100 kHz`。不要把它与 `rf_source.output_enable` 拼成「调制输出」流程:当前 ON 合同要求调制 disabled。 + +RF 的配置、端接判断、CLI 与 A4 边界见 [RF 信号源使用指南](../project/guides/WaveBench_RF信号源使用指南.md)。 + +## 不想手写时先用模板 + +`run template` 只负责生成标准 TOML plan,不连接仪器、不改配置、不覆盖已有文件(除非传 `--force`)。生成后仍然走普通流程:`run check`、`run verify`、`run plan`、`run report`。 + +常用模板: + +```powershell +python -m wavebench run template --list + +python -m wavebench run template source-scope-sine ^ + --frequency 1000 ^ + --vpp 1.0 ^ + --source-channel 1 ^ + --scope-channel 1 ^ + --output plans/source_scope_sine_1k.toml + +python -m wavebench run template source-scope-sweep ^ + --frequencies 100,1000,10000 ^ + --vpp 1.0 ^ + --source-channel 1 ^ + --scope-channel 1 ^ + --output plans/source_scope_sweep.toml + +python -m wavebench run template source-scope-frequency-response ^ + --frequencies 100,1000,10000 ^ + --source-channel 1 ^ + --reference-channel 1 ^ + --response-channel 2 ^ + --fit ^ + --output plans/source_scope_frequency_response.toml + +python -m wavebench run template dmm-acv-source ^ + --frequency 1000 ^ + --vpp 1.0 ^ + --source-channel 2 ^ + --output plans/dmm_acv_source_smoke.toml + +python -m wavebench run template power-dmm-dcv ^ + --voltage 3.3 ^ + --current-limit 0.1 ^ + --power-channel 1 ^ + --output plans/power_dmm_dcv_smoke.toml +``` + +模板边界: + +- `source-scope-sine`:生成单频点 DG4202 -> RTM2032 完整流程 plan,带 source restore、scope safety、质量检查、`[steps.expect]` 和 `[steps.expect_fft]`。 +- `source-scope-sweep`:把 `--frequencies` 里的频点展开成多组 `source.set_freq` + `scope.capture`,每个频点都有独立 label、expect 和 FFT expect。它不是新的执行器,只是 run plan 展开器。 +- `source-scope-frequency-response`:生成一次同步双通道采集的频响 plan。reference 通道接 DUT 输入,response 通道接 DUT 输出;`--fit` 额外写入线性增益拟合配置。 +- `dmm-acv-source`:生成 DG4202 -> DMM ACV smoke plan,ACV 期望值按 `Vpp / (2 * sqrt(2))` 自动缩放。 +- `power-dmm-dcv`:生成 DP800 电压设置 + DMM DCV 读回 plan,只设置电压/限流,不自动打开或关闭电源输出。 + +模板生成后建议立刻跑: + +```powershell +python -m wavebench run check --config wavebench.toml --plan plans/source_scope_sweep.toml +python -m wavebench run verify --config wavebench.toml --plan plans/source_scope_sweep.toml +``` + +## 双通道频率响应 / Frequency response + +`sweep.frequency_response` 让信号源按离散频点设定频率,并让示波器在每个频点只触发一次、同步读取两路波形: + +- `reference_channel` 是 DUT 输入参考;`response_channel` 是 DUT 输出,二者必须不同。 +- source 输出必须已经由前面的显式 `source.output state = "on"` 打开。频响 step 不会偷偷打开输出;若输出关闭或设频写入失败,会立即停止后续频点并走已有 restore 路径。 +- 两路都会经过高阻保护。执行前仍需人工确认探头、线缆、量程和接地;WaveBench 不自动 deskew,也不会把测得相位冒充为已校准 DUT 相位。 +- 每个成功采集强制保存双路 NPY 与 `metadata.json` 作为原始证据,即使全局输出配置关闭了 NPY/JSON。采集或分析失败会写入该频点 CSV 行后继续;信号源状态/写入异常会停止。 +- 一个 plan 可以包含多个该 step,但每个 `label` 必须唯一;多 response 会写根目录 `frequency_responses.json`,每个响应使用独立子目录,因此不会覆盖产物。 + +显式频点示例: + +```toml +[[steps]] +kind = "source.output" +channel = 1 +state = "on" + +[[steps]] +kind = "sweep.frequency_response" +label = "lowpass_bode" +source_channel = 1 +reference_channel = 1 +response_channel = 2 +frequencies_hz = [100, 316.228, 1000, 3162.28, 10000] +target_cycles = 10 +settle_s = 0.3 +points = "def" +save_csv = false +screenshot = true + +[steps.fit] +methods = ["linear_log", "polynomial", "pchip", "smoothing_spline_db", "piecewise_chebyshev_db"] +polynomial_degree = 2 +``` + +也可让 parser 生成等比或等差频点: + +```toml +[[steps]] +kind = "sweep.frequency_response" +reference_channel = 1 +response_channel = 2 +start_frequency_hz = 100 +stop_frequency_hz = 100000 +frequency_count = 31 +spacing = "log" # "log" 或 "linear" +``` + +### 二维 Vpp × 频率扫频与自动校准 + +默认频响 plan 是一个固定源幅值切片。需要观察增益随输入幅值变化、或为数字后端生成二维 LUT 时,在同一个 step 中额外给出请求 Vpp 轴。可以显式列举,或用首末值和步进生成;两种写法不能混用: + +```toml +[[steps]] +kind = "sweep.frequency_response" +source_channel = 1 +reference_channel = 1 +response_channel = 2 +start_frequency_hz = 10000 +stop_frequency_hz = 500000 +frequency_count = 246 +spacing = "linear" +start_vpp = 0.005 +stop_vpp = 0.250 +vpp_step = 0.005 +target_cycles = 10 +settle_s = 1.0 + +[steps.calibration] +target_mode = "passband_median" +correction_min_db = -12 +correction_max_db = 12 +max_slope_db_per_octave = 6 +``` + +等价的显式幅值写法为 `amplitudes_vpp = [0.005, 0.010, 0.020]`。每个值都是信号源**请求/设定** Vpp;`reference_vpp_v` 始终保留为示波器 CH1 的实测审计量,二者不可互换。 + +| 字段 | 含义与限制 | +|---|---| +| `amplitudes_vpp` | 严格递增、正数的显式 Vpp 数组;不能与生成式字段共用。 | +| `start_vpp` / `stop_vpp` / `vpp_step` | 三者必须同时出现;`stop_vpp > start_vpp`,步进必须整除区间。 | +| `autoscale_each_amplitude` | 多幅值时默认 `true`;仅在已人工确认量程时才建议设为 `false`。 | +| `retry_warning_with_autoscale` | 默认 `true`;单点出现质量 warning 时 autoscale、稳定后重测一次。重测仍 warning 则该点标为 failed,并保留两次采集证据。 | +| `min_signal_vpp` | 仅本频响 step 的低信号 warning 门限,默认 `0.020` Vpp。若已用实测波形确认低幅值切片仍有足够 SNR,可降低(例如 `0.005`);它会写入每点原始 metadata 的 operation,不能用来掩盖削顶、频率失配等其他 warning。 | +| `[steps.calibration]` | 仅多幅值数据有实际意义;`enabled = false` 可关闭自动派生。 | + +执行顺序固定为「设定 Vpp → 为每个频点设频并等待 `settle_s` → 为每个幅值切片的首个频点执行 autoscale → 再等待 `settle_s` → 同步采集 CH1 / CH2」。`autoscale_each_amplitude = false` 可显式关闭切片首点 autoscale,但仅适用于已确认两路量程足够的情况。每个 Vpp 值都会在连接仪器前接受 `[safety_limits].max_source_vpp` 检查。 + +频响点的首次采集若出现质量 warning,默认会执行一次「autoscale → 等待 `settle_s` → 同频同幅值重测」。重测成功时,CSV 的最终值来自第二次采集;首次证据保存在 `quality_retry_count`、`initial_warnings`、`initial_capture_package` 和 `initial_metadata_path` 中。重测仍出现 warning 时,该点标为 `failed`,原因设为 `quality_retry_exhausted`,不参与拟合、软件校正或二维 LUT。可在 step 中设置 `retry_warning_with_autoscale = false` 关闭此策略。 + +每个频响点还会写入稳定的 `case_id`、本次物理采集的 `acquisition_id`、`plan_hash` 和 `capture_sync_grade`。`case_id` 由计划语义、频率、请求 Vpp、通道角色生成,适合跨 run 比较;重测会保留不同的 `acquisition_id`。`reference_vpp_v` 是参考示波器通道的实测值,`requested_source_vpp` 是信号源请求值;系统不会根据输入阻抗自动换算电压。`signal_level_evidence` 会记录参考平面和未执行换算的说明。 + +频响点级采集或分析失败仍会保留行并继续扫描,这是为了保留完整网格证据。需要在异常累计时停止整组扫描时,可显式设置 `stop_conditions`: + +```toml +[steps.stop_conditions] +max_failed_points = 2 +max_consecutive_failed_points = 1 +max_gain_jump_db = 6 +``` + +达到任一条件后,当前 response 步骤停止,并在 `artifact.frequency_response.stop_conditions` 中保存触发条件;信号源设频、输出状态或通信失败仍会立即停止该步骤。 + +默认 20 mVpp 的门限是保守质量保护,不是仪器限制。对已确认稳定的低幅值扫频,可只在该 `sweep.frequency_response` 中设置 `min_signal_vpp = 0.005`;这会在采集分析阶段生效,因此 `metadata.json`、CSV 状态和报告审计使用同一个判断依据。全局 `[waveform]` 也可设同名默认值,但不建议为了一个低幅值实验而永久降低所有普通采集的门限。 + +完整矩阵的点数为 `幅值数 × 频点数`;每个点至少消耗 `settle_s`,每个幅值切片还会增加一次 autoscale 与稳定等待。先用少量幅值和稀疏频点执行 `run check`、只读 `run verify`、再做实机小矩阵确认量程和耗时,别拿 5 mV 步进和 500 Hz 步进直接开 13 小时以上的盲扫,没必要给仪器和人都上强度。 + +`[steps.calibration]` 需要至少两个请求 Vpp 切片且每个切片至少四个共同有效频点。它会在测量结束后使用每切片的 dB 平滑样条、请求 Vpp 方向线性插值生成校准;频率节点沿用实测点,不在频率或 Vpp 定义域外外推。自动校准失败不会删除或重写原始频响 CSV:该 step 变为 `warning`,`artifact.frequency_response.calibration_error` 记录原因,之后可修正配置或数据再离线重算。 + +离线重算使用只含 `[calibration]` 的独立 TOML,既不读取 `wavebench.toml`,也不连接仪器: + +```powershell +python -m wavebench run calibrate data/runs/ --config plans/calibration.toml +``` + +`run calibrate` 使用的 `[calibration]` 与 plan 内 `[steps.calibration]` 使用同一 schema。除 `enabled`(仅 plan 使用)外,字段如下: + +| 字段 | 默认值 | 说明 | +|---|---:|---| +| `model` | `"smoothing_spline_db"` | 当前唯一受支持的二维频率模型。 | +| `target_mode` | `"passband_median"` | `passband_median`、`explicit_gain_db` 或 `unity_gain`。 | +| `target_gain_db` | 无 | `explicit_gain_db` 时必填。 | +| `target_frequency_min_hz` / `target_frequency_max_hz` | 全有效频段 | 仅确定通带中位数目标的频段。 | +| `correction_min_db` / `correction_max_db` | `-12` / `12` | 导出的补偿 dB 边界。 | +| `max_slope_db_per_octave` | `6` | 相邻频率校正的最大斜率,必须大于零。 | +| `chebyshev_degree` / `chebyshev_segment_count` | `3` / `8` | 为需要公式求值的后端导出的近似参数。 | + +该命令只覆盖选中 response 目录的 `frequency_response_calibration*` 派生产物,绝不改写 `frequency_response.csv` 或原始采集包。若 manifest 有多个 response,必须显式选择: + +```powershell +python -m wavebench run calibrate data/runs/ --config plans/calibration.toml --response dut_path +``` + +### 跨 run 比较与缺失点补测 + +`run compare` 是离线命令,按 `case_id` 优先、按频率和请求 Vpp 回退,对比增益、相位、参考 Vpp、状态和缺失点。不同通道角色、参考平面或计划哈希会被标记为不兼容,不会静默合并: + +```powershell +python -m wavebench run compare data/runs/baseline data/runs/current --gain-tolerance-db 0.5 --phase-tolerance-deg 5 --output reports/frequency_compare.json +``` + +使用 `--format json` 可将结果直接写到标准输出。比较命令只读取已有 run artifact,不打开仪器。 + +需要补测缺失点时,先生成离线清单: + +```powershell +python -m wavebench run resume data/runs/current --plan plans/lowpass_frequency_response.toml --response dut_path --output reports/frequency_resume.json +``` + +清单会列出可复用、待补测和被拒绝的点。新 plan 可在对应 response step 中设置 `resume_from = "../data/runs/current/frequency_response.csv"`;WaveBench 只复用计划哈希和案例标识均匹配且状态为 `ok` 的点,原 run 目录不会被改写。 + +补测清单先覆盖计划声明的初始频率网格;启用自适应加点时,新的中点仍按当前 plan 的自适应规则重新判断。 + +### 直通基线、软件 deskew 与自适应频率 + +直通基线必须是**独立 run**:操作者先把 CH1/CH2 手动直通、确认高阻与安全幅度,再按普通 `sweep.frequency_response` 采集。DUT step 只读引用该证据;它不改示波器 deskew 或前面板设置,且 `run check` 会在连接仪器前离线检查基线 response、Vpp 切片和初始频率有效域。 + +```toml +[[steps]] +kind = "sweep.frequency_response" +label = "dut_path" +source_channel = 1 +reference_channel = 1 +response_channel = 2 +start_frequency_hz = 10000 +stop_frequency_hz = 500000 +frequency_count = 41 +spacing = "log" + +[steps.baseline] +run_dir = "../runs/through_baseline" +# response = "through" # 基线 run 有多个 response 时必填 +mode = "complex_transfer" # 默认;或 phase_only / delay_only + +[steps.adaptive] +enabled = true # 默认关闭,保持旧 plan 行为 +gain_threshold_db = 0.5 +phase_threshold_deg = 10 +max_levels = 2 +max_frequency_points = 1000 +``` + +默认 `complex_transfer` 同时扣除基线 dB 增益和展开相位;`phase_only` 只校正相位;`delay_only` 从基线相位—频率线性拟合估算延迟后校正相位。原始列永远保留,报告并列原始和校正曲线,二维校准默认用校正增益。 + +自适应先采集初始网格;任一 Vpp 切片相邻点的增益或展开相位达到阈值时加入中点(log 为几何、linear 为算术),每个新频点都采集**所有** Vpp 切片以保持二维矩形网格。它不能发现端点相同而中间存在未采样窄带异常的特征,初始网格仍须覆盖已知关注区域。 + +### 定点 LUT / COE / MEM + +校准默认同时导出 signed-two's-complement `Q4.12`:审计 CSV、Xilinx `.coe` 与每行一个十六进制字的 `.mem`。默认地址为幅值主序 `amplitude_index * frequency_count + frequency_index`,默认越界报错,不静默截断。 + +```toml +[steps.calibration.fixed_point] +word_width = 16 +fractional_bits = 12 +formats = ["csv", "coe", "mem"] +layout = "amplitude_major" # 或 frequency_major +rounding = "nearest" +overflow = "error" # 需要硬件饱和时才显式使用 saturate +``` + +### 拟合方法 + +除专供二维校准的模型外,传统线性增益拟合仍使用因变量 `gain_linear` 与 `x = log10(f / Hz)`: + +- `linear_log`:分段线性插值,导出每段 `m`、`b`,即 `G = m*x + b`。 +- `polynomial`:1–5 阶多项式,导出降幂系数。阶数必须小于有效频点数。 +- `pchip`:保形三次插值,导出每段 `x_start`、`x_stop` 和 `[c3, c2, c1, c0]`,即 `G = c3*dx^3 + c2*dx^2 + c1*dx + c0`。它需要先安装 `python -m pip install -e ".[analysis]"`。 +- `smoothing_spline_db`:在 dB 增益域对 `log10(f / Hz)` 做平滑样条,输出曲线用于可读性更好的报告和调试;少于四个有效点时标为 unavailable。 +- `piecewise_chebyshev_db`:在 dB 域给出低阶分段 Chebyshev 近似,适合需要直接计算公式的调试后端。 + +二维校准固定使用 `smoothing_spline_db`;它会用规律留点交叉验证在若干平滑度候选中选择一个全切片共享的惩罚,再导出每幅值的三阶、默认 8 个对数频段的 Chebyshev 系数。校正定义为 `correction_db = target_gain_db - fitted_gain_db`,线性乘数为 `10^(correction_db / 20)`;补偿幅度和相邻频点的 dB/oct 斜率都会被限制并逐点标记。 + +相位使用输出相对输入的相量差,CSV 同时提供 `phase_wrapped_deg` 和不跨失败点连接的 `phase_unwrapped_deg`。探头、电缆和通道延迟均会包含在相位里;先做直通基线或 deskew,才能把相位解释为 DUT 本体特性。 + +## 一个 step 只做一件事 + +例如设置电源电压不会顺手打开输出: + +```toml +[[steps]] +kind = "power.set" +channel = 1 +voltage_v = 3.3 +current_limit_a = 0.1 +``` + +如果要开关输出,必须另写: + +```toml +[[steps]] +kind = "power.output" +channel = 1 +state = "on" +``` + +这看起来啰嗦,但它能避免现场调试时被隐藏动作吓到。 + +### Source V2 基础、高级配置与 ARB 写 step + +声明 `source.snapshot_v2` 与对应写 capability 的插件可以使用 Source V2 step。基础配置只在目标输出已关闭时执行;输出 ON 与 OFF 分别使用不同 step: + +```toml +[[steps]] +kind = "source.basic_configure_v2" +channel = 1 +waveform_kind = "sine" +frequency_hz = 1000 +amplitude_vpp = 1.0 +offset_v = 0.0 + +[[steps]] +kind = "source.output_enable_v2" +channel = 1 + +[[steps]] +kind = "source.output_disable_v2" +channel = 1 + +[[steps]] +kind = "source.counter_configure_v2" +input_id = "counter" +coupling = "ac" + +[[steps]] +kind = "source.counter_enable_v2" +input_id = "counter" + +[[steps]] +kind = "source.counter_measure_v2" +input_id = "counter" + +[[steps]] +kind = "source.counter_disable_v2" +input_id = "counter" + +[[steps]] +kind = "source.harmonics_configure_v2" +channel = 1 +order = 8 +preset = "odd" + +[[steps]] +kind = "source.harmonics_disable_v2" +channel = 1 + +[[steps]] +kind = "source.modulation_configure_v2" +channel = 1 +depth_percent = 80 +internal_frequency_hz = 25 + +[[steps]] +kind = "source.modulation_pm_configure_v2" +channel = 1 +phase_deviation_deg = 90 +internal_frequency_hz = 25 + +[[steps]] +kind = "source.modulation_fm_configure_v2" +channel = 1 +frequency_deviation_hz = 12500 +internal_frequency_hz = 25 + +[[steps]] +kind = "source.modulation_pwm_configure_v2" +channel = 1 +internal_frequency_hz = 25 +duty_deviation_percent = 30 + +[[steps]] +kind = "source.sweep_configure_v2" +channel = 1 +start_hz = 100 +stop_hz = 1000 +spacing = "linear" +steps = 101 +sweep_time_s = 1 + +[[steps]] +kind = "source.burst_configure_v2" +channel = 1 +cycles = 12 +phase_deg = 30 +internal_period_s = 0.25 +delay_s = 0.5 + +[[steps]] +kind = "source.pulse_configure_v2" +channel = 1 +width_s = 1e-6 +delay_s = 0 +leading_transition_s = 1e-8 +trailing_transition_s = 1e-8 + +[[steps]] +kind = "source.arbitrary_storage_v2" +channel = 1 +slot_id = "slot_a" +file = "payload.bin" +write_mode = "create_only" + +[[steps]] +kind = "source.arbitrary_select_v2" +channel = 1 +slot_id = "slot_a" +playback_mode = "dds" +playback_frequency_hz = 1000 + +[[steps]] +kind = "source.combine_configure_v2" +channels = [1, 2] +enabled = true +``` + +`source.basic_configure_v2` 的 `channel` 必填,五个 basic 字段中至少写一个;缺失字段保持当前值。 +`source.output_enable_v2`、`source.output_disable_v2` 和 `source.harmonics_disable_v2` 都只接受 `channel`。`source.harmonics_configure_v2` +要求 `channel`、整数 `order >= 2` 与 `all`、`even`、`odd` 之一的 `preset`;核心还会在执行前检查运行时 +profile 是否支持该 order 和预设。`source.modulation_configure_v2` 要求 `channel`、位于 `[0, 100]` 的 +`depth_percent` 与有限正值 `internal_frequency_hz`;它只配置内部正弦 AM。 +`source.counter_configure_v2` 要求安全 token 形式的 `input_id`,并在 coupling、 +`impedance_ohm`、`attenuation`、`trigger_level_v` 与 `statistics_enabled` 中恰好指定一个字段。 +它不启用 Counter。`source.counter_enable_v2` 与 `source.counter_disable_v2` 只接受 `input_id`, +不隐式改写输入配置;`source.counter_measure_v2` 也只接受 `input_id`,要求 Counter 已启用。 +Counter 测量 artifact 位于该 step 的 `counter_measurement`,不进入 `source_operations`。实际信号、 +输入阻抗与最大 Vpp 仍必须由计划 safety 和接线确认;Counter step 不替代输出安全门。 +`source.modulation_pm_configure_v2` 要求 `channel`、位于 `[0, 360]` 的 `phase_deviation_deg` 与有限正值 +`internal_frequency_hz`;它只配置内部正弦 PM。 +`source.modulation_fm_configure_v2` 要求 `channel`、有限正值 `frequency_deviation_hz` 与有限正值 +`internal_frequency_hz`;它只配置内部正弦 FM。 +`source.modulation_pwm_configure_v2` 要求 `channel`、有限正值 `internal_frequency_hz`,以及恰好一个显式单位分支: +位于 `[0, 50]` 的 `duty_deviation_percent` 或位于 `[0, 500000]` 秒的 `width_deviation_s`;它只配置内部正弦 PWM。 +`source.sweep_configure_v2` 要求 `channel`、有限正值且 start 不大于 stop 的 `start_hz`/`stop_hz`、`linear`、 +`logarithmic`、`step` 之一的 `spacing`、位于 `[2, 2048]` 的整数 `steps` 与位于 `[0.001, 300]` 秒的 `sweep_time_s`; +它只配置内建 Sweep,不会 fire 或开启输出。 +`source.burst_configure_v2` 要求 `channel`、位于 `[1, 500000]` 的整数 `cycles`、位于 `[0, 360]` 的 +`phase_deg`、有限正值 `internal_period_s` 与位于 `[0, 85]` 的 `delay_s`;它只配置内部 Triggered Burst, +不会 trigger 或开启输出。 +`source.pulse_configure_v2` 要求 `channel`、不小于 `4 ns` 的 `width_s`、有限非负 `delay_s`,以及有限正值 +`leading_transition_s`/`trailing_transition_s`;两个 transition 都不能超过 width 的 `0.625` 倍,且它只配置 WIDTH 脉冲形状。 + +`source.arbitrary_storage_v2` 要求 `channel`、安全 token 形式的 `slot_id`、相对或绝对的 `file`,以及 +`create_only` 或 `replace_if_digest_matches` 的 `write_mode`。replace mode 还必须给出 +`expected_previous_sha256`。执行时会从 plan 所在目录解析相对文件,计算 payload 的 SHA-256 和大小;这些字节不会 +写入执行意图或 Source operation artifact。storage 不选择波形、不改变输出,目标或其它独立端口不需要因上传被关闭。 + +`source.arbitrary_select_v2` 要求 `channel`、`slot_id` 与 `dds`/`true_arb` 之一的 `playback_mode`。DDS 只接受 +正的 `playback_frequency_hz`;true-ARB 只接受正的 `sample_rate_hz`。该 step 必须在目标输出已关闭时执行,完成后仍关闭; +它不包含 output ON。 + +`source.combine_configure_v2`、`source.coupling_configure_v2`、`source.tracking_configure_v2` 和 +`source.phase_relation_configure_v2` 都要求递增且唯一的 `channels` 数组(至少两个通道)与布尔 `enabled`。核心按 +descriptor relation graph 展开实际受影响端口;只有展开后的端口必须 OFF,未连通端口可以继续 ON。它们不会开启输出、 +不会设置厂商私有关系参数,也不会为之后的 output ON 提供额外授权。 + +Harmonic、内部 AM、内部 PM、内部 FM、内部 PWM、内部 Sweep、内部 Triggered Burst 与 WIDTH Pulse step 都必须在目标输出已关闭时执行,完成后仍保持关闭;它们不会隐式开启输出。现有 +`restore.source_state` 只恢复 basic 状态,不恢复 Harmonic、调制、Burst 或 Pulse 配置。双合同插件声明 +`source.harmonics_configure_v2` 或 `source.harmonics_disable_v2` 后,V1 `configure_harmonics` 会在仪器 I/O 前被拒绝;声明 +`source.modulation_configure_v2` 后,V1 `configure_am_modulation` 会在仪器 I/O 前被拒绝。未声明对应能力的 +旧插件继续使用 V1 路径。声明 `source.modulation_pm_configure_v2` 后,V1 `configure_pm_modulation` 也会在 +仪器 I/O 前被拒绝。声明 `source.modulation_fm_configure_v2` 后,V1 `configure_fm_modulation` 和 restore 也会在 +仪器 I/O 前被拒绝。声明 `source.modulation_pwm_configure_v2` 后,V1 `configure_pwm_modulation` 和 restore 也会在 +仪器 I/O 前被拒绝。声明 `source.sweep_configure_v2` 后,V1 `configure_sweep`、`trigger_sweep` 和 restore 也会在 +仪器 I/O 前被拒绝。声明 `source.burst_configure_v2` 后,V1 `configure_burst`、`trigger_burst` 和 restore 也会在 +仪器 I/O 前被拒绝。声明 `source.pulse_configure_v2` 后,V1 `configure_pulse` 也会在仪器 I/O 前被拒绝, +因为 V1 route 还允许 DUTY hold。该 step 不会让 `source.output_enable_v2` 获得 Pulse 输出 ON 授权。 + +声明 `source.coupling_configure_v2` 后,V1 `configure_coupling` 在仪器 I/O 前拒绝;声明任一跨通道 V2 capability +后,V1 restore 也会在仪器 I/O 前拒绝,避免它在未知 relation state 下重开输出。 + +执行意图会记录十八个 Source V2 operation,实际执行时的完整 Source V2 artifact 会写入 +`run.json.source_operations`;storage payload 只以文件名、SHA-256 与大小出现。没有声明 V2 capability 的旧插件继续使用 V1 step。 + +## 常见 `run check` 报错 + +### step kind 拼错 + +错误 plan: + +```toml +[[steps]] +kind = "scope.captur" +``` + +输出类似: + +```text +wavebench: steps[0].kind 'scope.captur' is not supported. Did you mean 'scope.capture'? Supported kinds: ... Run `python -m wavebench run schema` for field details. +``` + +修法:改成 `scope.capture`,或先运行 `run schema` 查看支持列表。 + +### 字段名拼错 + +错误 plan: + +```toml +[[steps]] +kind = "sleep" +duraton_s = 0.5 +``` + +输出类似: + +```text +wavebench: steps[0] has unknown key(s): duraton_s. Did you mean 'duraton_s' -> 'duration_s'? Allowed keys: duration_s, kind. Run `python -m wavebench run schema` for field details. +``` + +修法:改成 `duration_s`。 + +### 缺少必填字段 + +错误 plan: + +```toml +[[steps]] +kind = "power.set" +voltage_v = 3.3 +``` + +输出类似: + +```text +wavebench: steps[0] power.set missing required field 'current_limit_a'. Required fields: voltage_v, current_limit_a. Optional fields: channel. Run `python -m wavebench run schema` for examples. +``` + +修法:补上 `current_limit_a`。 + +## `scope.capture` 的质量检查和断言 + +`scope.capture` 默认只采集。需要截图、质量检查或断言时都要显式写: + +```toml +[[steps]] +kind = "scope.capture" +channel = 1 +label = "duty_50" +window_frequency_hz = 10000 +target_cycles = 10 +screenshot = true +quality_gate = true +auto_recover = true + +[steps.expect] +frequency_estimate_hz = { min = 9500, max = 10500 } +duty_cycle = { min = 0.45, max = 0.55 } +voltage_vpp_v = { min = 2.8, max = 3.8 } +``` + +含义: + +- `screenshot = true`:本次采集额外保存 `screenshot.png`,并在 `metadata.json.files.screenshot` 记录路径;截图失败不会吞掉波形包。 +- `quality_gate = true`:把采集质量状态写入 `run.json` / `summary.csv`。 +- `auto_recover = true`:如果质量有 warning,显式执行 `scope.auto` 并重采,最多次数由 `[quality].auto_recover_attempts` 控制。 +- `[steps.expect]`:对采集摘要指标做 min/max 断言。断言失败时当前 step 标记为 `failed`,默认停止后续步骤,但采集包仍保留。 + +### 失败策略和安全门 + +所有 step 都支持 `on_failure`,默认值是 `"stop"`。需要在失败后继续执行后续步骤时,必须显式写出: + +```toml +[[steps]] +kind = "scope.capture" +on_failure = "continue" +``` + +`quality_gate = true` 且采集仍有 warning 时,step 会标记为 `failed`;`quality_gate` 未启用时,普通质量 warning 不会单独停止 run。频响 step 的点级采集失败仍会保留 CSV 行,并按 `stop_conditions` 决定是否结束当前频响 step。 + +安全门独立于 `on_failure`。计划级安全门需要明确授权 OFF 通道: + +```toml +[safety] +safety_gate = true +off_source_channels = [1] +off_power_channels = [1] +``` + +安全门遇到失败或 gate warning 时,先对授权通道执行 OFF,再停止 run;`on_failure = "continue"` 不会覆盖这个停止动作。若启用 source restore,恢复配置后会再次确认授权通道为 OFF。关断结果会写入失败 step 的 `artifact.safety_gate` 和顶层 `run.json.error`。未列出任何 OFF 目标时,执行器会把安全门视为配置失败,不会猜测其他通道。 + +## run 输出字段契约 + +`run plan` 会生成: + +```text +data/runs/YYYYMMDD_HHMMSS_