diff --git a/.agents/skills/wavebench-docs/SKILL.md b/.agents/skills/wavebench-docs/SKILL.md new file mode 100644 index 00000000..d7bcdc7c --- /dev/null +++ b/.agents/skills/wavebench-docs/SKILL.md @@ -0,0 +1,106 @@ +--- +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 [文档宪法](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 + +| 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 constitutional rules. Do not turn every review into a repository-wide audit. + +## Invariants + +- 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 + WaveBench. +- Apply the [协作输出卫生](references/information-architecture.md#协作输出卫生) + rule to reader-facing content created or reviewed in this workflow. It is a + contextual write/review judgment, not a hook, text filter, or CI gate. +- 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 + +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. + +For trigger-boundary maintenance, read [eval-prompts.md](references/eval-prompts.md). + +## Handoff + +Treat the handoff as an execution and safety record under the +[协作输出卫生](references/information-architecture.md#协作输出卫生) rule. 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. Omit feedback back-and-forth and discarded alternatives unless they affect +a remaining decision or risk. 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..98ac8093 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/audit.md @@ -0,0 +1,48 @@ +# 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 + +每篇页面先按[文档宪法](information-architecture.md)确定主类别、读者目标、canonical +source 和一个主要动作: + +| File | Type | Audience | Reader Goal | Canonical? | Problems | Action | +| --- | --- | --- | --- | --- | --- | --- | + +`Canonical?` 说明页面是否为该事实的权威来源;若不是,写出实际来源。`Problems` +优先记录职责混合、重复事实、过时风险、旅程断点、Core/plugin 越界和 +Current/RFC 混杂,不以措辞偏好充数。生命周期动作的定义只以文档宪法为准。 + +## 系统级输出 + +除 inventory 外,列出并排序: + +- 最严重的 10 个系统问题; +- 八条用户旅程的断点; +- 重复维护的易变事实; +- 孤儿页面和失效导航; +- 超长 mixed-purpose 页面; +- Core/plugin 边界违规; +- Current、Experimental、RFC 和历史记录的混杂; +- 目标信息架构、事实源表和小步迁移顺序。 + +结论必须引用 `file:line`、符号名、命令输出或 tag。若只是推断,要明确标注,不把旧文档互相引用当作事实核验。 + +审计报告属于需要追溯的内容。为 Action 保留必要证据、影响、目标页或替代入口和验证结果;不记录讨论时间线或与结论无关的备选方案。 + +## 当前基线 + +首轮仓库审计见 [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..8aaba514 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/information-architecture.md @@ -0,0 +1,177 @@ +# WaveBench 文档宪法 + +> 加载时机:任何 `audit`、`migrate`、`write` 或 `review` 任务中,需要判断页面职责、事实来源、状态、生命周期、导航或 Core/plugin 边界时加载。 +> 本文件是 WaveBench 文档分类、来源与治理规则的唯一规范入口;其它 workflow reference 不重复定义这些规则。 + +## 适用范围与优先级 + +本规则适用于仓库的公开文档、示例说明、README、Reference、开发文档和 RFC。它不改变代码、CLI、schema、descriptor 或发布流程本身;发生冲突时,这些可执行或已发布事实优先于文档。 + +文档事实的核验顺序为: + +1. 实现、类型模型与 schema; +2. 离线可执行的 `--help`、`run schema`、`run template --list` 和 capability 查询; +3. 聚焦测试、descriptor 与 registry; +4. 已发布 Git tag、release 与 `CHANGELOG.md`; +5. 现有文档。 + +旧页面只能作为待核验材料,不能互相证明当前行为。 + +## 页面类别 + +每篇公开页面只选择一个主类别。页面可以链接到其它类别,但不能借「概览」之名承载所有内容。 + +| 类别 | 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`。不要求把这五项机械写成公开页面的元数据块。 + +### Tutorial + +- Learning goal; +- Prerequisites; +- 一条可完成的流程; +- 每个关键步骤的预期结果; +- 后续阅读入口。 + +深层原因链接 Concept,参数全集链接 Reference。 + +### How-to + +- 任务目标; +- 必要条件和真实硬件风险; +- 最短可靠步骤; +- Verification; +- 常见失败; +- 相关 Reference。 + +不把单个任务页扩展成产品总教程。 + +### Reference + +- Synopsis; +- Syntax / Schema; +- Inputs / Fields / Parameters; +- Outputs; +- Exact behavior 和 side effects; +- Errors; +- Compatibility / capability requirements。 + +可以从稳定实现生成的字段、参数或 capability 表,不长期手工复制。生成结果必须标明来源和生成/验证命令。 + +### Concept + +- Problem / motivation; +- Model; +- How it works; +- Design rationale; +- Trade-offs; +- 相关 Guide 与 Reference。 + +不把命令步骤、当前版本号、commit、里程碑或型号证据塞进概念页。 + +## 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`;派生页由 `scripts/generate_docs.py` 写入 `docs/reference/generated/run-schema.md` | 生成页必须以 `python scripts/generate_docs.py --check` 进入 CI;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,不是 specification。它应包含:项目身份和一句定位、主要入口、WaveBench 解决的问题、核心能力、无硬件 Quickstart 摘要、简短支持概览、安全提醒、Contributing、License 和 Acknowledgements。 + +README 不应承载:RFC 编号、milestone、A1/A2/A4 阶段、descriptor 内部合同、session/rollback 实现、型号级 capability/profile、易变化的型号参数或完整错误/配置/CLI Reference。需要这些信息时,用一两句摘要链接到唯一权威页面。 + +## 生命周期 + +每篇现有页面在 audit 或迁移计划中只选择一个主要动作: + +| 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 文档中作为实施状态出现,但必须明确不能据此宣称稳定能力、提高插件版本门或执行未经授权的硬件操作。 + +## 协作输出卫生 + +本条适用于本工作流撰写或审查的面向读者的当前文档、README、导航文案,以及只需说明最终结果的读者交接摘要。用户纠正、被否决的备选方案和内部协作过程应改变结果,但通常不是正文内容。直接说明当前对象、行为、限制或下一步,不用「根据反馈」「已移除」「不再包含」等对比式表述来纪念被放弃的内容。 + +兼容旧路径的短页应直接给出当前入口,可以说明该路径用于兼容旧链接;除非追溯用途需要,不展开迁移时间线。 + +任务执行和安全交接不属于上述省略范围。交接必须报告后续操作所需的范围、事实源、验证命令与结果、失败或跳过项、未验证内容、剩余风险和环境状态;只省略无关的反馈往返与已弃选项。 + +需要追溯事实时,不适用上述省略原则:RFC、archive、CHANGELOG、审计或迁移报告、事故记录,以及会影响读者决定的安全、合规或限制说明,可以记录变更原因、证据和影响。保留内容应至少说明来源、时间范围、影响、替代入口或可核验证据之一,并服务于页面职责或读者决策;页面位于追溯目录不等于其中所有过程叙述都应保留。不满足该判据的内容应压缩为当前事实。 + +这是一项写作和 review 判断,不通过 hook、关键词过滤或 CI 自动阻断实现。机械检查仍只处理确定性问题;是否应保留追溯信息由页面职责、读者目标和事实来源决定。 + +## 导航与迁移护栏 + +目标分类可以逐步采用 `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 → 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 new file mode 100644 index 00000000..d9b33204 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/migrate.md @@ -0,0 +1,50 @@ +# Migrate 模式 + +> 加载时机:已有被接受的 audit 或明确迁移决策,需要移动、拆分、合并、归档或生成文档时加载。 + +## 迁移门槛 + +迁移前确认: + +- 已加载[文档宪法](information-architecture.md),并按其页面类别、状态与生命周期规则完成判断; +- 页面主类型、受众和 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 没有真正重定向;移动高入链页面时,优先保留短的旧路径说明,直到外部入口已迁移。 +- 兼容页的 H1 使用「旧入口」标识,正文直接给出当前入口,可以说明该路径仅用于兼容旧链接,但不展开迁移时间线。机械 audit 不把这种仅为外部书签保留的页面报为孤儿。 +- RFC 的不可变决策与实现历史分开。不要为了目录整齐改写已经接受的历史裁决含义。 +- `CHANGELOG.md` 只记录正式 tag;迁移说明不能把开发分支写成发布版本。 +- 归档页面顶部标明历史范围、替代页面和不可作为当前事实源的边界。 +- 迁移报告保留 Action 的必要证据、影响、目标页或替代入口和验证结果,不记录讨论时间线或无关备选方案。 + +## 生成式 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..f6cc3a58 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/review.md @@ -0,0 +1,43 @@ +# Review 模式 + +> 加载时机:评审文档 PR、提交或工作区 diff 时加载。默认不是全仓 audit。 + +## Review scope + +优先检查: + +1. 本次修改的页面; +2. 它们直接依赖的 canonical Reference 或源码; +3. 导航入口与入链页面; +4. 被引用的命令、配置、plan、schema 和 artifact; +5. 与改动声明直接相关的测试或 descriptor。 + +只有发现系统性重复、迁移跨目录或用户明确要求时,才升级为全仓 audit。 + +## Review order + +- 先按[文档宪法](information-architecture.md)确认页面类别、状态、事实源、边界和 lifecycle; +- 页面 type、audience 和 reader outcome 是否一致; +- 当前行为、Experimental、RFC 与历史是否清楚分离; +- 易变事实是否回到唯一 canonical source; +- Core/plugin 边界是否正确; +- 链接、导航、文件路径和锚点是否有效; +- 命令、参数、schema、capability、artifact 和示例是否与实现一致; +- 按[协作输出卫生](information-architecture.md#协作输出卫生)区分当前正文、兼容入口、追溯内容和执行交接;不要因否定措辞而删除当前行为、安全边界或必要的交接事实; +- 中文页面最后使用 `tech-doc-style-chinese` 检查表达层。 + +先读 diff。代码 diff 可先用 `scripts/docs_impact.py --base --head HEAD` 找到 canonical 页面候选;它只缩小 review 范围,不能决定页面是否确实需要改动。确认本次修改的 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..cfb651d8 --- /dev/null +++ b/.agents/skills/wavebench-docs/references/write.md @@ -0,0 +1,46 @@ +# Write 模式 + +> 加载时机:新增页面,或按已确定职责重写一篇具体页面时加载。 + +## 写前合同 + +开始正文前回答: + +- 谁会来看? +- 当前要完成什么? +- 读完后能做到什么? +- 哪些陈述来自哪些 canonical source? +- 页面主类型是什么?相关细节应链接到哪里? + +无法回答时先做局部 audit。不要把一篇混合页面原样换个标题。 + +## 按类型写作 + +先加载[文档宪法](information-architecture.md)中的页面类别与页面合同。它是 +Tutorial、How-to、Reference 和 Concept 的唯一结构来源;本文件只处理写作时的 +WaveBench 核验,不重复页面类别规则。 + +## WaveBench 特有检查 + +- 示例命令先与当前 `--help`、`run schema` 或实现核对。 +- 涉及真实设备的步骤明确区分离线、连接读取和设备写入。 +- 不写入真实 IP、序列号、串口、凭据、本地实验目录或私有证据。 +- 型号级 SCPI、profile、quirk 和验证状态链接 instrument plugin 仓库。 +- 在文档宪法允许出现状态的位置,Current、Experimental、Proposed 和 Historical 使用明确标签。 +- README 只保留 landing page 内容,不展开内部合同。 +- 面向读者的当前页面遵守[协作输出卫生](information-architecture.md#协作输出卫生):写当前结果,不写纠正或删除过程;需要追溯的页面按宪法中的例外处理。 + +## 中文表达层 + +页面结构、事实源和技术边界确定后,再使用 `tech-doc-style-chinese` 完成中文措辞与排版:直角引号、克制语气、术语与中西文留白、机器字面量保护等由该 Skill 负责。 + +不要复制那套通用规则到本 Skill,也不要加载其与 WaveBench 无关的 `Project-Overrides.md`。若 WaveBench 以后需要术语覆盖,应在仓库内建立并评审自己的规则。 + +## 完成条件 + +- reader outcome 可验证; +- 事实均能追溯到 canonical source; +- 示例与路径可执行或明确标注未执行原因; +- 导航入口和 related pages 已更新; +- 对单页 write,`audit_docs.py` 显式传入改动页及其直接导航入口,且无新增错误;只有已批准迁移或已证实的系统性问题才运行全量 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..1cae57db --- /dev/null +++ b/.agents/skills/wavebench-docs/scripts/audit_docs.py @@ -0,0 +1,412 @@ +#!/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 and document.relative.parts[:2] != ("docs", "archive"): + 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 + ) + is_compatibility_entry = any( + heading.level == 1 and "旧入口" in heading.title for heading in document.headings + ) + if not is_entry and not is_compatibility_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..b192c94f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 2 - name: Set up Python uses: actions/setup-python@v5 @@ -32,6 +34,18 @@ 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: Check generated Reference + if: matrix.python-version == '3.11' + run: python scripts/generate_docs.py --check + + - name: Report documentation impact + if: matrix.python-version == '3.11' + run: python scripts/docs_impact.py + - name: Run unit tests run: python -m pytest -q diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..a3cffc00 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,66 @@ +name: Documentation + +on: + push: + branches: [master, main] + paths: + - ".agents/skills/wavebench-docs/**" + - ".github/workflows/docs.yml" + - "CHANGELOG.md" + - "README.md" + - "docs/**" + - "mkdocs.yml" + - "plans/**" + - "pyproject.toml" + - "scripts/docs_impact.py" + - "scripts/generate_docs.py" + - "src/wavebench/**" + - "wavebench.example.toml" + pull_request: + paths: + - ".agents/skills/wavebench-docs/**" + - ".github/workflows/docs.yml" + - "CHANGELOG.md" + - "README.md" + - "docs/**" + - "mkdocs.yml" + - "plans/**" + - "pyproject.toml" + - "scripts/docs_impact.py" + - "scripts/generate_docs.py" + - "src/wavebench/**" + - "wavebench.example.toml" + workflow_dispatch: + +jobs: + build: + name: Build documentation + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: pip + + - name: Install documentation dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e ".[dev,docs]" + + - name: Check generated Reference + run: python scripts/generate_docs.py --check + + - name: Audit documentation + run: python .agents/skills/wavebench-docs/scripts/audit_docs.py --quiet-warnings + + - name: Build site + run: mkdocs build --strict + + - name: Report documentation impact + run: python scripts/docs_impact.py diff --git a/.gitignore b/.gitignore index 19f86668..e2f782aa 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 @@ -22,4 +24,5 @@ __pycache__/ .ruff_cache/ dist/ build/ +site/ *.egg-info/ diff --git a/README.md b/README.md index 441853f0..ead3ec69 100644 --- a/README.md +++ b/README.md @@ -1,228 +1,114 @@ -# WaveBench - -[English documentation](docs/README_EN.md) · [中文文档总览](docs/README.md) · [更新日志](CHANGELOG.md) · [仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins) - -> [!WARNING] -> WaveBench 可以连接并控制真实实验设备。执行会改动仪器状态的命令前,应确认接线、输入阻抗、输出状态和电压 / 电流限制。 - -WaveBench 是一个用 Python 编写的实验室自动测量台,面向电子设计竞赛调试和日常实验。它把仪器控制、实验步骤和采集证据放在同一条命令链中,支持先离线检查 plan,再决定是否连接硬件。 - -当前仓库开发线为 `0.8.26`,最新稳定 tag 为 `v0.8.0`。不同版本的命令和能力可能不同,以对应 tag 中的文档为准。 +

+ WaveBench +

-## 🌟 特别鸣谢 +

+ Automate the bench. Measure what matters. +

-

- - LINUX DO +

+ + Stars + + + Release + + + License + Python

-

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

- -## 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) +

+ + Instrument Control · + Automated Measurement · + Data Analysis · + Open Source + +

-#### 多仪器 `run plan` +

+ + An open-source toolkit for automated laboratory measurement and instrument control. + +

-显式 `run plan` 可以把信号源、示波器、电源和万用表编排到同一条实验流程中。执行前先用 `run check` 做离线校验,再用 `run verify` 做连接和安全预检;执行过程中保留每个步骤的状态、测量结果、失败证据和恢复记录。 +--- -run 内的 Source / Power 基础写入会在实际 setter 前回读并比较状态;状态漂移会停止写入并写入差异。缺少完整 `scope.snapshot` 的驱动可通过 `scope status` 返回 `partial summary`,操作能力可用 `capability explain` 离线核对。需要固定 plan、配置和任意波形输入时,先生成 `run intent`,再用 `run plan --intent` 在打开仪器前核验摘要。 +

+ 中文文档 + · + English Overview + · + 更新日志 + · + 仪器插件仓库 +

-典型流程是「信号源 → DUT → 示波器 / 万用表」。 +--- -```mermaid -flowchart LR - source[信号源] --> dut[DUT] - dut --> scope[示波器] - dut --> dmm[万用表] - plan[run plan] --> source - plan --> scope - plan --> dmm - scope --> artifacts[采集包 / 离线报告] - dmm --> artifacts -``` +> [!WARNING] +> WaveBench 可以连接并控制真实实验设备。执行会改变仪器状态的命令前,应确认接线、输入阻抗、输出状态以及电压/电流限制。 -#### 示波器联合视图 +WaveBench 是面向电子设计调试和日常实验的 Python 测量台。它将仪器操作、显式 run plan 和可复查的实验产物放在同一条工作流中:先离线检查,再连接实验台,最后执行受控实验。 -`scope focus` 用一个事务配置一个或多个目标模拟通道,并可同时设置完整横向时间范围、各目标通道的 -V/div,以及是否隐藏插件 profile 声明的其他模拟通道: +![WaveBench 运行报告示例](docs/images/run_plan_result.png) -```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,也不修改耦合或输入终端。 +- 用信号源、示波器、电源和万用表组成可复现的实验流程。 +- 用 `run check` 在不连接仪器的情况下检查 plan。 +- 将采集包、运行记录和离线 HTML 报告保存在同一实验产物中。 +- 用显式命令控制输出、采集和恢复,不在后台隐式执行 reset 或输出切换。 +- 通过已安装的 instrument plugin 扩展具体仪器型号。 -## 先在没有仪器时跑通 +## 无硬件快速开始 -下面的命令只生成和检查 plan,不会连接仪器,也不会打开输出。 -原生 Windows 与 Linux / WSL 均可运行离线命令。Windows 原生硬件访问使用 -`portalocker[win32]` 提供的跨进程锁;Windows 与 WSL 不共享同一锁域,同一台仪器应固定由一种运行环境访问。 +以下命令只安装包、列出模板并打印一个示例 plan;不会连接仪器或打开输出。 ```bash +git clone https://github.com/Scaxlibur/WaveBench.git +cd WaveBench 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 -``` - -Windows 串口资源使用 `COM3`、`COM10` 等形式;`\\.\COM10` 也会规范化为同一串口身份。 -VISA、SocketIO 和 USB 设备仍需安装对应的厂商驱动或后端。纯 Python 插件支持原生 Windows 的安装、升级、删除和恢复;包含原生 DLL 的插件不在本轮支持范围内。 - -Windows 原生环境暂不保证与 WSL 进程对同一资源互相阻塞。需要使用 WSL 时,可继续使用 -[`scripts/wsl-run.ps1`](scripts/wsl-run.ps1) 作为兼容入口。 - -查看终端界面时,可另外安装 TUI 依赖。`--fake` 使用模拟设备,不连接实验台: - -```bash -.venv/bin/python -m pip install -e ".[tui]" -wavebench tui --fake +.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 ``` -执行 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 信号源 +最后一条命令以 `safety_limits=ok / 安全上限=通过` 结束时,表示示例 plan 已通过离线检查。完整的预期结果、Windows 命令和下一步见[无硬件快速开始](docs/getting-started/quickstart.md)。 -`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 或输出开关。 +| 示波器 | 读取、采集、截图和受 capability 约束的控制 | 由内建或已安装插件 descriptor 声明 | +| 信号源/RF 信号源 | 显式配置、输出控制、run plan 和安全预检 | 由对应插件的 profile 与 evidence 声明 | +| 电源/万用表 | 状态、设定、读数与 run plan 集成 | 由对应插件 descriptor 声明 | +| 插件 | 发现、安装和公开插件 API | 具体型号、SCPI、quirk 和限制见[仪器插件仓库](https://github.com/Scaxlibur/wavebench-instrument-plugins) | -外部 Python 插件按当前用户权限运行,不是安全沙箱。仅安装来源已确认的本地目录或 wheel;公开文档不得包含真实 IP、序列号、串口路径、凭据或实验产物。 +用[文档首页](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 自身的接口和设计文档 +- `run schema`、`run template`、`run check` 和 `run report` 不连接仪器。 +- `doctor` 与 `run verify` 会查询真实设备;先确认资源与接线。 +- `run plan`、输出控制、采集和部分 TUI 操作可能改变仪器状态。 +- WaveBench 不自动执行 `*RST`,也不会因设置电压、幅度或频率而自动开启输出。 -目前中文文档覆盖最完整,英文入口提供安装、离线体验和安全摘要。命令、字段名和 schema 以程序输出为准。 +开始真实实验前,请阅读[执行一次实验](docs/how-to/run-an-experiment.md)和[配置实验台](docs/getting-started/configure-bench.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/how-to/manage-plugins.md) +- [插件开发](docs/development/plugin-development.md) +- [更新日志](CHANGELOG.md) WaveBench 使用 MIT 许可证。感谢 Linux DO 社区提供交流和支持。 diff --git a/docs/README.md b/docs/README.md index 88a93a34..0494f5a9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,91 +1,8 @@ -# WaveBench 文档 +# WaveBench 文档旧入口 -[English](README_EN.md) · 中文 +当前中文文档入口为[文档首页](index.md)。此路径仅用于兼容旧链接。 -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/artifacts-reference-pre-migration.md b/docs/archive/artifacts-reference-pre-migration.md new file mode 100644 index 00000000..3bc6e87f --- /dev/null +++ b/docs/archive/artifacts-reference-pre-migration.md @@ -0,0 +1,874 @@ +# 归档:重构前的 WaveBench 数据输出格式 + +> [!NOTE] +> 本页保留文档重构前的采集、run、频响和报告材料。当前运行产物字段以 writer、typed result 和 loader 为准;请阅读[运行产物 Reference](../reference/artifacts.md)。 + +## 设计目标 + +数据输出围绕三个要求设计: + +1. 结果便于阅读; +2. Python 可以直接分析; +3. 采集过程和原始证据可以追溯。 + +当前阶段只支持: + +```text +CSV + NPY + JSON + commands.log +``` + +暂不考虑 MATLAB 兼容、Parquet、数据库和自动报告。 + +## 当前实现状态(2026-04-29) + +`9c9cc32 feat: fetch and capture RTM2032 waveforms` 已实现单通道采集包输出: + +```text +data/raw/20260429_162450_square_1khz/ +├─ ch1.csv +├─ ch1.npy +├─ metadata.json +└─ commands.log +``` + +实机样例来自 RTM2032 自带约 1 kHz 方波: + +```text +samples = 10000 +time = -1.000000e-03 .. 9.998000e-04 s +dt = 2.000000e-07 s +voltage = about -0.5 .. 0.62 V +``` + +当前 `capture` 已推进为 `SINGle + *OPC?` 单次采集后再读取波形并打包。 + + +## 采集包 + +每次采集生成一个独立目录,称为「采集包」。 + +单通道示例: + +```text +data/raw/20260429_011530_ch1/ +├─ ch1.csv +├─ ch1.npy +├─ metadata.json +└─ commands.log +``` + +以后如果支持多通道: + +```text +data/raw/20260429_011530_ch1_ch2/ +├─ ch1.csv +├─ ch1.npy +├─ ch2.csv +├─ ch2.npy +├─ metadata.json +└─ commands.log +``` + +如果以后支持截图,可增加: + +```text +screenshot.png +``` + +## 目录命名规则 + +默认规则: + +```text +YYYYMMDD_HHMMSS_