From 05a86f283a519107c8e84edc5cd2ca58a2b54793 Mon Sep 17 00:00:00 2001 From: Doug Roeper Date: Tue, 24 Feb 2026 08:03:42 -0500 Subject: [PATCH] Add CONTRIBUTING.md, examples crate, and expand CLAUDE.md Move contributing guidelines (test commands, formatting) from README.md into a dedicated CONTRIBUTING.md. Expand CLAUDE.md with architecture, commands, environment, and testing sections. Add examples workspace member with a getting-started example. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 38 ++++++++++++++++++++++++- CONTRIBUTING.md | 42 +++++++++++++++++++++++++++ Cargo.toml | 1 + README.md | 40 +------------------------- examples/Cargo.toml | 18 ++++++++++++ examples/getting-started.rs | 57 +++++++++++++++++++++++++++++++++++++ 6 files changed, 156 insertions(+), 40 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 examples/Cargo.toml create mode 100644 examples/getting-started.rs diff --git a/CLAUDE.md b/CLAUDE.md index 12c663f..bb12f01 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,28 @@ The system consists of three major components: - **Server**: HTTP API backend that collects, stores, indexes, and serves observations - **Web UI**: Interface for searching and visualizing observations (served from server) -For more details refer to [Design](docs/design.md) +## Architecture + +Cargo workspace with 4 crates: + +| Crate | Type | Purpose | +|-------|------|---------| +| `observation-tools-client` | lib (cdylib + rlib) | Rust client + Node.js native module (NAPI) | +| `observation-tools-server` | bin + lib | HTTP API, web UI, storage (sled + object_store) | +| `observation-tools-shared` | lib | Core types shared across crates (WASM-compatible) | +| `observation-tools-macros` | proc-macro | `observe!()` and `group!()` macros | + +Client has optional feature flags: `axum` (middleware), `tracing` (subscriber integration). + +## Commands + +```bash +cargo build --workspace --all-features # Build +cargo run --bin observation-tools -- serve # Run server (default port 3000) +cargo test --workspace --all-features # Rust tests +``` + +For UI tests, formatting, and contribution setup, see [CONTRIBUTING.md](CONTRIBUTING.md). ## Core Concepts @@ -27,3 +48,18 @@ For more details refer to [Design](docs/design.md) - Always use workspace imports for dependencies to ensure consistency across crates. - Never use `unwrap()`. Always handle or propagate errors. - Only comment code when necessary. Prefer self-documenting code. + +## Environment + +| Variable | Purpose | +|----------|---------| +| `PORT` | Server listen port (default: 3000) | +| `SERVER_URL` | Point integration tests at an external server instead of spawning one | +| `RUST_LOG` | Logging filter (default set in `.cargo/config.toml`) | + +## Testing + +- Rust integration tests are in `crates/observation-tools-client/tests/` with a shared `TestServer` helper + that auto-spawns a server on a random port (or uses `SERVER_URL` for an external server). +- Playwright E2E tests are in `tests/` and test the web UI in Chromium. +- Always use `--all-features` when running `cargo test` to cover axum/tracing integrations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dd6949b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,42 @@ +# Contribution guidelines + +## Running tests + +We have two test suites: rust-based client+server integration tests and a playwright based UI test suite. By default, +every test will start up its own server instance. + +- You can use `SERVER_URL` to point the tests to a running server instead of starting a new one, + though keep in mind many tests assume a clean server state. + +### Rust tests + +```bash +cargo test --workspace --all-features +``` + +### UI tests + +To run the tests, you must build the NodeJS client library so the test can import it. + +```bash +pnpm --dir crates/observation-tools-client install +pnpm --dir crates/observation-tools-client build:debug +pnpm --dir tests install +pnpm --dir tests run test +``` + +The test suite uses [Playwright](http://playwright.dev/). You can use all of its debugging tools, e.g. open the +inspector: + +```bash +cd tests +pnpm playwright test --ui +``` + +## Formatting + +```bash +cargo +nightly fmt +pnpm dlx prettier --write . +``` + diff --git a/Cargo.toml b/Cargo.toml index d8c680c..371efa5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "crates/observation-tools-server", "crates/observation-tools-client", "crates/observation-tools-macros", + "examples" ] [workspace.package] diff --git a/README.md b/README.md index de1b521..a99a7d3 100644 --- a/README.md +++ b/README.md @@ -16,45 +16,7 @@ cargo run --bin observation-tools -- serve ## Contributing -### Running tests - -We have two test suites: rust-based client+server integration tests and a playwright based UI test suite. By default, -every test will start up its own server instance. - -- You can use `SERVER_URL` to point the tests to a running server instead of starting a new one, - though keep in mind many tests assume a clean server state. - -#### Rust tests - -```bash -cargo test -``` - -#### UI tests - -To run the tests, you must build the NodeJS client library so the test can import it. - -```bash -pnpm --dir crates/observation-tools-client install -pnpm --dir crates/observation-tools-client build:debug -pnpm --dir tests install -pnpm --dir tests run test -``` - -The test suite uses [Playwright](http://playwright.dev/). You can use all of its debugging tools, e.g. open the -inspector: - -```bash -cd tests -pnpm playwright test --ui -``` - -### Formatting - -```bash -cargo +nightly fmt -pnpm dlx prettier --write . -``` +See the [contributing guidelines](CONTRIBUTING.md). ## License diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 0000000..c8f3a21 --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "examples" +version = "0.0.0" +publish = false +edition = "2021" + +# If you copy one of the examples into a new project, you should be using +# [dependencies] instead, and delete the **path**. +[dev-dependencies] +observation-tools = { path = "../crates/observation-tools-client" } +tokio = { version = "1", features = ["full"] } +anyhow = "1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +[[example]] +name = "getting-started" +path = "getting-started.rs" diff --git a/examples/getting-started.rs b/examples/getting-started.rs new file mode 100644 index 0000000..341f41c --- /dev/null +++ b/examples/getting-started.rs @@ -0,0 +1,57 @@ +use observation_tools::{group, observe, with_execution, ClientBuilder}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // 1. Create a client pointing at a running observation-tools server + let client = ClientBuilder::new() + .base_url("http://localhost:3000") + .build()?; + + // 2. Begin an execution — this is the root scope for all observations + let execution = client + .begin_execution("getting-started")? + .wait_for_upload() + .await?; + println!("Execution URL: {}", execution.url()); + + // 3. Use with_execution to set the context for observe! calls + with_execution(execution, async { + let user_message = "What is the topic of this document?"; + observe!("user_message").payload(user_message); + + let document_content = load_document_content().await; + observe!("document_content").serde(&document_content); + + let api_request = serde_json::json!({ + "message": user_message, + "document": document_content, + }); + observe!("api_call").serde(&api_request); + let api_response = call_api(api_request).await; + observe!("api_response").serde(&api_response); + + let group = group!("processing_steps").build().into_handle(); + observe!("hello").group(&group).payload("Hello, world!"); + }) + .await; + + // 4. Shut down the client to flush all pending uploads + client.shutdown().await?; + println!("Done! Open the execution URL above to view your observations."); + + Ok(()) +} + +async fn load_document_content() -> serde_json::Value { + serde_json::json!({ + "title": "Rust Programming Guide", + "content": "Rust offers memory safety and fast performance.", + }) +} + +async fn call_api(_request: serde_json::Value) -> serde_json::Value { + serde_json::json!({ + "message": "The main theme of the document is Rust programming.", + }) +} +