AGENTS.md#
Instructions for AI coding agents working on Osprey. README.md is for humans; this file is for machines. The nearest AGENTS.md to the edited file wins; explicit user prompts override everything.
Architecture#
Top-level modules:
osprey_worker/— main Python engine. Consumes events from Kafka, evaluates SML rules, emits verdicts and effects to output sinks. New worker/engine code belongs here (osprey_worker/src/osprey/worker/).osprey_rpc/— generated protobuf/gRPC bindings underosprey_rpc/src/osprey/rpc/. Do not edit generated files (*_pb2*.py,*_pb2*.pyi) by hand; regenerate via./gen-protos.shafter editing the.protofiles.osprey_ui/— React + TypeScript frontend (Ant Design, Highcharts; versions inosprey_ui/package.json). UI code belongs here.osprey_coordinator/— Rust gRPC coordinator (tokio, tonic, etcd, rdkafka). Rust code belongs here.proto/osprey/rpc/— protobuf source of truth forosprey_rpcandosprey_coordinatortypes.example_plugins/— reference plugins (UDFs, output sinks, labels service) using the pluggy-based plugin system. Do not add production code here.example_rules/— sample SML rules and YAML config.
Reference files: docs/DEVELOPMENT.md (setup), example_plugins/src/register_plugins.py (plugin patterns), example_plugins/src/services/labels_service.py (labels service example).
Design#
- API: gRPC between
osprey_coordinatorand workers; HTTP/Flask forosprey-ui-api(port 5004); protobuf definitions underproto/osprey/rpc/are authoritative. - Rules: SML (Osprey's rule language) with user-defined functions registered via pluggy hooks (
@hookimpl_osprey):register_udfs,register_output_sinks,register_labels_service_or_provider. - Data model conventions: Pydantic for models, SQLAlchemy for persistence (versions pinned in
pyproject.toml).
Build and run#
Prerequisites: Python (version in .python-version), uv, Docker + Docker Compose v2, Node.js (version in .github/workflows/code-quality.yml, UI only), Rust stable + protoc (coordinator only).
# Install Python deps (creates .venv, uses uv.lock)
uv sync --dev
# Install git hooks
uv run pre-commit install --install-hooks
# Start full stack (Kafka, Postgres, Druid, MinIO, Bigtable emulator, worker, UI, UI API)
docker compose up -d
# or
./start.sh
# with coordinator:
./start.sh --with-coordinator
# UI dev server
cd osprey_ui && npm ci && npm start
# Regenerate protobuf bindings after editing proto/osprey/rpc/**/*.proto
./gen-protos.sh
UI: http://localhost:5002 · UI API: http://localhost:5004 · Worker (port 5001)
Testing#
Run the full integration suite (spins up all services via docker compose; ~8 GB RAM):
./run-tests.sh
Pass pytest args through:
./run-tests.sh path/to/test_file.py::test_name
./run-tests.sh -k some_keyword
./run-tests.sh --junitxml=/tmp/test-results/junit-pytest.xml
Python lint / format / type-check (no Docker needed):
uv run ruff check
uv run ruff format --diff
uv run mypy .
uv run pre-commit run --all-files
UI checks (in osprey_ui/):
npm run format:check
Rust checks (in osprey_coordinator/; requires protoc). CI only gates on fmt and build; clippy and test are advisory (continue-on-error: true):
cargo fmt --check
cargo build --verbose
cargo clippy -- -D warnings # advisory
cargo test --verbose # advisory
Browser MCP (UI verification)#
A project-scoped MCP server (.mcp.json at repo root) registers @playwright/mcp@0.0.73 via npx. When Claude Code launches in this repo it gets browser_navigate, browser_snapshot, browser_evaluate, browser_take_screenshot, and the rest of the browser_* tool surface — useful for verifying visual UI changes against the running dev server without a full automated test suite. There is intentionally no playwright.config.ts / @playwright/test integration and no devDep in osprey_ui/package.json; the MCP is for ad-hoc verification, not CI.
All commands in this section — including any --dry-run previews — are operator-run only. Agents must never execute them. If a prereq is missing, the agent surfaces what the operator should run and waits.
The MCP needs Playwright's bundled Chromium binary plus its system shared libs. Setup is platform-specific — Playwright's docs cover it across macOS / Windows / WSL / Linux: https://playwright.dev/docs/browsers#install-browsers.
Operator runs the binary install (no sudo, user-cache only). Pinned via @playwright/mcp@0.0.73's bundled playwright-core, so the downloaded Chromium build matches what the MCP server will launch:
npx -y @playwright/mcp@0.0.73 install-browser chromium
System libs:
-
macOS, recent Windows / WSL: nothing extra to install.
-
Linux: distro-specific. The operator runs the dry-run to preview the package list
install-depswould apt-install:npx -y playwright install-deps chromium --dry-run…and then the operator runs the printed
apt-get installline themselves. The agent does neither step.install-depsonly auto-supports recent Ubuntu / Debian. Fedora, Arch, Alpine, and NixOS need manual lib installation — Playwright's troubleshooting docs cover their package names.
Before calling browser_navigate("http://localhost:5002"), the operator starts the dev server (cd osprey_ui && npm start, listens on :5002).
If the MCP approval prompt doesn't fire on a fresh claude launch, the operator runs claude mcp reset-project-choices and re-launches.
CI#
CI runs entirely via GitHub Actions on pull_request and push to main. Each line below is one literal CI run: step, in workflow order. Run them in your shell (paste-as-is — no && chaining, no error suppression — so each step's exit code matches the corresponding CI step's exit code):
# code-quality.yml → python-quality
uv sync --dev
uv run pre-commit install --install-hooks
SKIP=prettier-osprey-ui uv run pre-commit run --show-diff-on-failure --color=always --all-files
uv tool run fawltydeps --check-unused --pyenv .venv
# code-quality.yml → ui-quality (CI `working-directory: osprey_ui`)
( cd osprey_ui
npm ci
npm run format:check )
# code-quality.yml → rust-quality (CI `working-directory: osprey_coordinator`)
# Note: in CI the `cargo clippy` and `cargo test` steps are marked `continue-on-error: true`,
# so they currently print failures but do not fail the job. Locally, expect the same output.
( cd osprey_coordinator
cargo fmt --check
cargo clippy -- -D warnings
cargo build --verbose
cargo test --verbose )
# integration-tests.yml
./run-tests.sh
mdbook.yml, publish-coordinator-image.yml, and release-osprey-rpc.yml are release/deploy workflows — do not modify without human approval (see "Human-approval-required actions" below).
Security#
- No secrets in code or committed files. Use environment variables via
docker-compose.yaml. - Do not disable lint or type rules to silence errors. Fix the underlying issue, or use a narrowly-scoped
# noqa: <code>/# type: ignore[<code>]with a comment explaining why. - Before adding a new dependency, check it for known CVEs and confirm the license is compatible with
LICENSE.md. - Do not commit generated protobuf files from an untrusted toolchain; always regenerate via
./gen-protos.sh. - Default Docker bindings are
127.0.0.1; do not change bind addresses without explicit instruction (seedocs/DEVELOPMENT.md§6).
Code review#
- Keep diffs small and focused; split unrelated changes into separate PRs.
- PR titles are descriptive and imperative ("Add X", "Fix Y").
- New behavior requires a test. Bug fixes require a regression test.
- All CI checks (above) must pass before requesting review.
Code style#
- Python: version in
.python-version. Lint + format withruff, type-check withmypy(versions and config inpyproject.tomlunder[tool.ruff]and[tool.mypy]). - TypeScript / React in
osprey_ui/(versions inosprey_ui/package.json). Formatter is Prettier (npm run format:check); config inosprey_ui/.prettierrc. Node version in.github/workflows/code-quality.yml. - Rust stable in
osprey_coordinator/(edition and toolchain inosprey_coordinator/Cargo.toml). Formattercargo fmt; lintercargo clippy -- -D warnings. - Protobuf generated files (
*_pb2*.py,*_pb2*.pyi) are excluded from ruff and mypy — do not edit.
CD#
- Releases are cut by publishing a GitHub Release; the tag triggers
.github/workflows/release-osprey-rpc.ymlto build and attach theosprey_rpcsdist. Tags follow semver (vMAJOR.MINOR.PATCH). - Coordinator image publishes to
ghcr.iovia.github/workflows/publish-coordinator-image.ymlon push tomainand on release. - mdBook docs deploy via
.github/workflows/mdbook.ymlto GitHub Pages on push tomain. - Release/deploy workflows, production Dockerfiles, and signing/tagging are restricted — see "Human-approval-required actions" below.
Dependencies#
- Python deps are pinned in
pyproject.tomland locked inuv.lock. Add withuv add <pkg>(runtime) oruv add --dev <pkg>(dev); commit the updateduv.lock. - Node deps live in
osprey_ui/package.json; add withnpm install --save <pkg>and commit the updatedosprey_ui/package-lock.json. - Rust deps live in
osprey_coordinator/Cargo.toml. Note:Cargo.lockis currently in.gitignore— do not commit it without first un-ignoring it. - Every new or upgraded package including transitive dependencies requires human approval. Confirm the license is compatible with
LICENSE.mdand that there are no known CVEs. fawltydepsenforces that every declared Python dep is used; add intentional exceptions to[tool.fawltydeps].ignore_unusedinpyproject.tomlwith a comment.
ROOST guiding principles#
- Commands over prose. Prefer
./run-tests.sh path/to/test_file.py::test_nameover descriptive paragraphs. - Same review bar. PRs authored with agent assistance are held to the same standards as any other PR.
- Boundaries with alternatives. When stating a restriction, provide the alternative path (e.g. don't edit
*_pb2*.py— regenerate via./gen-protos.sh). - Iterate over time. Start minimal. When you give an agent the same instruction twice, add it to this file.
- Contributors update
AGENTS.md. When you find a gap, update this file as part of your PR.
Human-approval-required actions#
Stop and get explicit human approval before:
- Changing license headers, copyright notices, or any legal text (including
LICENSE.md). - Modifying release, signing, or deploy workflows:
.github/workflows/publish-coordinator-image.yml,.github/workflows/release-osprey-rpc.yml,.github/workflows/mdbook.yml, production Dockerfiles (osprey_coordinator/Dockerfile,osprey_worker/Dockerfile,osprey_ui/Dockerfile),docker-compose.yaml,start.sh, orentrypoint.sh. - Adding, removing, or upgrading any library or package (including transitive dependencies in
uv.lockorosprey_ui/package-lock.json) — confirm licenses are compatible. - Editing generated code under
osprey_rpc/src/osprey/rpc/by hand instead of regenerating via./gen-protos.sh.