diff --git a/.claude/agents/code-simplifier.md b/.claude/agents/code-simplifier.md index affaf744..9f58970f 100644 --- a/.claude/agents/code-simplifier.md +++ b/.claude/agents/code-simplifier.md @@ -18,7 +18,7 @@ Find overly complex code and suggest simpler alternatives. ## Rules - Read the code before suggesting changes. -- Respect the project's conventions (see `CLAUDE.md`). +- Respect the project's conventions (see `AGENTS.md`). - Ensure suggestions maintain correctness — simplification must not break behavior. - Consider WASM constraints when suggesting alternatives. diff --git a/.claude/agents/pr-reviewer.md b/.claude/agents/pr-reviewer.md index bf047aaa..cc01ac6b 100644 --- a/.claude/agents/pr-reviewer.md +++ b/.claude/agents/pr-reviewer.md @@ -73,7 +73,7 @@ For each changed file, evaluate: - No Tokio or runtime-specific deps in `crates/trusted-server-core` - Fastly-specific APIs only in `crates/trusted-server-adapter-fastly` -#### Convention compliance (from CLAUDE.md) +#### Convention compliance (from AGENTS.md) - `expect("should ...")` instead of `unwrap()` in production code - `error-stack` (`Report`) with `derive_more::Display` for errors (not thiserror/anyhow) diff --git a/.claude/commands/review-changes.md b/.claude/commands/review-changes.md index 8c954605..93725215 100644 --- a/.claude/commands/review-changes.md +++ b/.claude/commands/review-changes.md @@ -3,7 +3,7 @@ Review all staged and unstaged changes in the working tree. 1. Run `git diff` and `git diff --cached` to see all changes. 2. Review each changed file for: - Correctness and logic errors - - Style violations (see CLAUDE.md conventions) + - Style violations (see AGENTS.md conventions) - Missing error handling - Security concerns (hardcoded secrets, injection risks) - Missing or incorrect tests diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5f4cc6ca..a5ea5f14 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -35,7 +35,7 @@ Closes # ## Checklist -- [ ] Changes follow [CLAUDE.md](/CLAUDE.md) conventions +- [ ] Changes follow [AGENTS.md](/AGENTS.md) conventions - [ ] No `unwrap()` in production code — use `expect("should ...")` - [ ] Uses `tracing` macros (not `println!`) - [ ] New code has tests diff --git a/AGENTS.md b/AGENTS.md index 836df88d..8940d133 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,29 +1,458 @@ # AGENTS.md -**Before doing anything else, read `CLAUDE.md` in this repository root.** It -contains all project conventions, coding standards, build commands, workflow -rules, and CI requirements. Everything in `CLAUDE.md` applies to you. +> Single source of truth for all AI coding agents (Claude Code, Codex, Cursor, +> etc.). `CLAUDE.md` is a symlink to this file, kept for tools that look it up +> by name. -This file exists because Codex looks for `AGENTS.md` by convention. All shared -rules are maintained in `CLAUDE.md` to avoid duplication and drift. If you -cannot access `CLAUDE.md`, the critical rules are summarized below as a -fallback. +## Project Overview + +Rust-based edge computing application targeting **Fastly Compute**. Handles +privacy-preserving Edge Cookie (EC) ID generation, ad serving with GDPR compliance, +real-time bidding integration, and publisher-side JavaScript injection. + +## Workspace Layout + +``` +crates/ + trusted-server-core/ # Core library — shared logic, integrations, HTML processing + trusted-server-adapter-fastly/ # Fastly Compute entry point (wasm32-wasip1 binary) + trusted-server-adapter-axum/ # Axum dev server entry point (native binary) + trusted-server-adapter-cloudflare/ # Cloudflare Workers entry point (wasm32-unknown-unknown binary) + trusted-server-adapter-spin/ # Fermyon Spin entry point (wasm32-wasip1 component) + trusted-server-cli/ # Host-target `ts` operator CLI + trusted-server-js/ # TypeScript/JS build — per-integration IIFE bundles + lib/ # TS source, Vitest tests, esbuild pipeline +``` + +Supporting files: `edgezero.toml`, `fastly.toml`, +`trusted-server.example.toml`, `.env.dev`, `rust-toolchain.toml`, +`CONTRIBUTING.md`. Operator-owned `trusted-server.toml` files are gitignored. + +## Toolchain + +| Tool | Version / Target | +| ----------- | ---------------------------------------- | +| Rust | 1.95.0 (pinned in `rust-toolchain.toml`) | +| WASM target | `wasm32-wasip1` | +| Node | 24.12.0 (from `.tool-versions`) | +| Fastly CLI | 15.1.0 (from `.tool-versions`) | +| Viceroy | 0.17.0 (from `.tool-versions`) | +| Wasmtime | 44.0.1 (from `.tool-versions`) | + +--- + +## Build & Test Commands + +### Rust + +```bash +# Build (per-target aliases — bare `cargo build` fails at the workspace root) +cargo build-fastly && cargo build-axum && cargo build-cloudflare + +# Production build for Fastly +cargo build --package trusted-server-adapter-fastly --release --target wasm32-wasip1 + +# Run locally with Fastly simulator +fastly compute serve + +# Deploy to Fastly +fastly compute publish + +# Run Axum dev server (native — no Viceroy) +cargo run -p trusted-server-adapter-axum + +# Test Axum adapter only +cargo test-axum + +# Check Cloudflare adapter (native) +cargo check -p trusted-server-adapter-cloudflare + +# Check Cloudflare adapter (WASM target — alias for the full command) +cargo check-cloudflare + +# Test Cloudflare adapter (native host) +cargo test-cloudflare + +# Check Spin adapter (native) +cargo check -p trusted-server-adapter-spin + +# Check Spin adapter (WASM target) +cargo check-spin + +# Test Spin adapter (native host) +cargo test-spin + +# Production-style Spin WASM artifact used by crates/trusted-server-adapter-spin/spin.toml +cargo build --package trusted-server-adapter-spin --target wasm32-wasip1 --features spin --release + +# Optional local Spin runtime smoke, if the Spin CLI is installed +spin up --from crates/trusted-server-adapter-spin +``` + +### Testing & Quality + +```bash +# Run all Rust tests — use workspace aliases (see .cargo/config.toml) +# default-members = [fastly] so Viceroy can locate the binary via `cargo run --bin`. +cargo test-fastly # Fastly adapter + core (wasm32-wasip1 via Viceroy) +cargo test-axum # Axum dev server adapter (native) +cargo test-cloudflare # Cloudflare Workers adapter (native host) +cargo test-spin # Spin adapter route tests (native host) + +# Run host-target CLI tests (workspace default target is wasm32-wasip1) +# Use your host triple, for example x86_64-unknown-linux-gnu on CI/Linux +# or aarch64-apple-darwin on Apple Silicon macOS. +# Use the local helper (recommended): +# ./scripts/test-cli.sh +cargo test --package trusted-server-cli --target + +# Format +cargo fmt --all -- --check + +# Lint by adapter target — target-matched clippy is the blocking gate because +# the workspace has multiple wasm runtimes and runtime-specific SDKs. A plain +# `cargo clippy --workspace --all-features` would trip the cloudflare +# feature's non-wasm32 compile_error! guard. +cargo clippy-fastly +cargo clippy-axum +cargo clippy-cloudflare +cargo clippy-cloudflare-wasm +cargo clippy-spin-native +cargo clippy-spin-wasm + +# Check compilation (per-target aliases — bare `cargo check` fails at the workspace root) +cargo check-fastly && cargo check-axum && cargo check-cloudflare + +# JS tests +cd crates/trusted-server-js/lib && npx vitest run + +# JS format +cd crates/trusted-server-js/lib && npm run format + +# Docs format +cd docs && npm run format + +# JS build +cd crates/trusted-server-js/lib && node build-all.mjs +``` + +### Install prerequisites + +```bash +cargo install viceroy --version 0.17.0 --locked --force +``` --- -## Fallback Summary +## Coding Conventions + +### Rust Style + +- Use the **2024 edition** of Rust. +- Prefer `derive_more` over manual trait implementations. +- Use `#[allow(lint)]` to suppress lints when necessary. +- Use `rustfmt` to format code. +- Invoke clippy with `--all-targets --all-features -- -D warnings`. +- Use `cargo doc --no-deps --all-features` for checking documentation. + +### Type System + +- Create strong types with **newtype patterns** for domain entities. +- Consider visibility carefully — avoid unnecessary `pub`. + +```rust +#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, derive_more::Display)] +pub struct UserId(Uuid); +``` + +### Function Arguments + +- Functions should **never** take more than 7 arguments — use a struct instead. +- Take references for immutable access, mutable references for modification. Only take ownership when the function consumes the value. +- Prefer `&[T]` instead of `&Vec`. +- Never use `impl Into>` — it hides that `None` can be passed. + +### `From` / `Into` + +- Prefer `From` implementations over `Into` — the compiler derives `Into` automatically. +- Prefer `Type::from(value)` over `value.into()` for clarity. +- For wrapper types (`Cow`, `Arc`, `Rc`, `Box`), use explicit constructors. + +### Allocations + +- Minimize allocations — reuse buffers, prefer borrowed data. +- Balance performance and readability. + +### Naming Conventions + +- Avoid abbreviations unless widely recognized (e.g., `Http`, `Json` — not `Ctx`). +- Do not suffix names with their types (`users` not `usersList`). + +### Import Style + +- No local imports within functions or blocks. +- `use super::*` is acceptable in `#[cfg(test)]` modules only. +- Never use a prelude `use crate::prelude::*`. +- Use `use Trait as _` when you only need trait methods, not the trait name. + +### Comments and Assertions + +- Place comments on separate lines **above** the code, never inline. +- All `expect()` messages should follow the format `"should ..."`. +- Use descriptive assertion messages: `assert_eq!(result, expected, "should match expected output")`. + +### Crate Preferences + +- `log` (with `log-fastly`) for instrumentation. + +--- + +## Error Handling + +- Use `error-stack` (`Report`) — not anyhow or eyre. + - **Exception**: `crates/trusted-server-adapter-spin/src/lib.rs` entry point returns `anyhow::Result` because `edgezero_adapter_spin::run_app` forces this type at the WASM FFI boundary. Do not use `anyhow` anywhere else. +- Use `Box` only in tests or prototyping. +- Use concrete error types with `Report`. +- Use `ensure!()` / `bail!()` macros for early returns. +- Import `Error` from `core::error::` instead of `std::error::`. +- Use `change_context()` to map error types, `attach()` for debug info. +- Define errors with `derive_more::Display` (not thiserror): + +```rust +#[derive(Debug, derive_more::Display)] +pub enum MyError { + #[display("Resource `{id}` not found")] + NotFound { id: String }, + #[display("Operation timed out after {seconds}s")] + Timeout { seconds: u64 }, +} + +impl core::error::Error for MyError {} +``` + +--- + +## Testing Strategy + +- Unit tests live alongside source files under `#[cfg(test)]` modules. +- Uses **Viceroy** for local Fastly Compute simulation. +- GitHub Actions CI runs format and test workflows. +- Structure tests with **Arrange-Act-Assert** pattern. +- Test both happy paths and error conditions. +- Use `expect()` / `expect_err()` with `"should ..."` messages instead of `unwrap()`. +- Use `json!` macro instead of raw JSON strings. +- Follow the same code quality standards in test code as production code. +- For JS tests: use `vi.hoisted()` for mock definitions referenced in `vi.mock()` factories. + +--- + +## Documentation Standards + +- Each public item must have a doc comment. +- Begin with a single-line summary, blank line, then details. +- Always use intra-doc links (`[`Item`]`) for referenced types. +- Document errors with `# Errors` section for all fallible functions. +- Document panics with `# Panics` section. +- Add `# Examples` sections for public API functions. +- Add `# Performance` sections for performance-critical functions. +- Skip documentation for standard trait implementations unless behavior is unique. +- Use `cargo doc --no-deps --all-features` to verify. + +--- + +## Logging Practices + +- Use `log` crate level-specific macros: `log::info!`, `log::debug!`, `log::trace!`, `log::warn!`, `log::error!`. +- Provide context in log messages with format strings. +- Format messages with present-tense verbs. +- Use `log-fastly` as the backend for Fastly Compute. + +## Other guidelines + +- Use only example or fictional information in comments, tests, docs, examples, + and similar non-runtime materials. (eg. for urls use: example.com domains only) +- Do not write or commit real domains, customer names, credentials, + configuration values, or other potentially sensitive real-world information in + comments, tests, docs, or examples. + +--- + +## Git Commit Conventions + +- Be descriptive and concise. +- Use sentence case (capitalize first word). +- Imperative, present-tense style. +- No semantic prefixes (`fix:`, `feat:`, `chore:`). +- No bracketed tags (`[Docs]`, `[Fix]`). +- Follow the detailed guidelines in `CONTRIBUTING.md`. + +Good: `"Add feature flags to Real type tests that require serde"` +Bad: `"fix: added feature flags"` + +--- + +## Integration System + +Integrations register in Rust via: + +```rust +IntegrationRegistration::builder(ID) + .with_proxy() + .with_attribute_rewriter() + .with_head_injector() + .build() +``` + +- Integration IDs match JS directory names: `prebid` (deferred), `lockr`, `permutive`, `datadome`, `didomi`, `testlight`. +- `creative` is JS-only (no Rust registration); `nextjs`, `aps`, `adserver_mock` are Rust-only. +- Integrations opt into deferred loading via `.with_deferred_js()` on the registration builder. Deferred modules are served as separate `