Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -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.
- **`<<null>>` 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.<group>` 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.
Loading