From 259051c078b783fde6cfe5e0a88a0455d80c280e Mon Sep 17 00:00:00 2001 From: Sergey Marin Date: Fri, 30 Oct 2026 23:53:15 +0200 Subject: [PATCH] docs: add CLAUDE.md for coding agents --- CLAUDE.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..84dfe06 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,31 @@ +# CLAUDE.md — bddkit-exec + +Agent orientation for this plugin repository. + +`bddkit-exec` is a plugin for `bddkit`, a Gherkin-based acceptance-testing tool in Rust: this plugin runs local commands, keeps long-running ones streaming in the background, and validates what they print (text, JSON, CSV, TSV). It is a `cdylib` loaded by the host at run time; resource group: `exec`; plugin ABI 3 (`bddkit-plugin.json`). `README.md` is the tester-facing description of the steps. + +## Where the contract lives + +The authoring contract is `docs/plugin-authoring.md` in the `bddkit/bddkit` repository (https://github.com/bddkit/bddkit/blob/main/docs/plugin-authoring.md); the host's side of it is `src/plugin/CLAUDE.md` there. If your memory has no entry saying where a local checkout of the host is, do not guess a path: ask the operator for a folder or a URL and save the answer as a `reference` memory. A change to the ABI is made in the host first and then followed here. + +## Rules only the plugin can keep (host cannot check them) + +- **Every `extern "C"` export runs inside `catch_unwind`, argument decoding included.** Unwinding across the FFI boundary is undefined behaviour. A panic becomes `{"status":"fatal","error":"the plugin panicked"}` for dispatch and `{"ok":false,"error":"…"}` for the rest. +- **Only JSON strings cross the boundary** — never a Rust type, never a borrowed slice. `rustc` has no stable ABI. +- **Never print to stdout.** The host composes a file's output atomically; a plugin line lands inside another worker's failure dump. Evidence goes back as `diagnostics` in the reply. +- **`<>` arrives as two real NUL bytes inside a `String`** after the plugin's own JSON parse, so `CString::new(arg)` fails on it. Handle it before building a C string. +- **Instance config is plain scalars, sequences and mappings.** A YAML tag in a `resources.` body does not parse. +- **The plugin is written against `docs/plugin-authoring.md` and must never need the host's source.** `Manifest`, `StepSpec` and `DispatchResult` are a hand-copied mirror of the host's types; the document is the only thing keeping them in step. + +## Layout + +`src/lib.rs` the exports and `catch_unwind` guard · `config.rs` instance config · `instance.rs` per-instance state · `steps.rs` step declarations and dispatch · `exec/` spawning, capturing and stopping commands, one adapter per OS (`unix.rs`, `windows.rs`) · `matchers.rs` output assertions; the JSON semantics are the host's own (`contains` loose, `equals` strict), re-implemented because a plugin shares no code with the host · `reply.rs` reply shapes · `tests/e2e.rs` end-to-end, `tests/exports.rs` FFI-level · `examples/` runnable suites (`windows/` for Windows). + +## Dev workflow + +- Edition 2024, `rust-version` 1.97 (CI pins the toolchain). Dependency versions are not bumped without being asked. +- `cargo fmt --check` and `cargo clippy --all-targets -- -D warnings` must be clean. +- `cargo test --locked --lib --test exports` is the fast unit suite; `cargo test --locked --test e2e -- --nocapture` runs the end-to-end suite against the built plugin and a `bddkit` binary. +- Everything in the repository is English: code, comments, error messages, documentation. Conversational replies follow the operator's language. +- Markdown: never soft-wrap prose; one paragraph or list item is one physical line. +- `CHANGELOG.md` (where present) is generated by release-plz from commit messages — never hand-edit it. Use Conventional Commits.