···11+# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
22+#
33+# Review rules live in `.github/copilot-instructions.md` and `AGENTS.md`
44+# CodeRabbit picks both up automatically. This file only carries settings that
55+# differ from CodeRabbit's defaults.
66+77+reviews:
88+ profile: chill
99+ request_changes_workflow: false
1010+ high_level_summary: true
1111+ poem: false
1212+ review_status: true
1313+ collapse_walkthrough: true
1414+1515+ auto_review:
1616+ enabled: true
1717+ drafts: false
1818+ base_branches:
1919+ - main
2020+ ignore_usernames:
2121+ - 'dependabot[bot]'
2222+ - 'dependabot-preview[bot]'
2323+ labels:
2424+ - '!dependabot'
2525+2626+ path_filters:
2727+ # Generated protobuf bindings — regenerate via ./gen-protos.sh, never
2828+ # hand-edit (AGENTS.md > Architecture).
2929+ - '!osprey_rpc/src/osprey/rpc/**/*_pb2*.py'
3030+ - '!osprey_rpc/src/osprey/rpc/**/*_pb2*.pyi'
3131+ - '!osprey_coordinator/src/proto/**'
3232+ # Build artifacts and caches.
3333+ - '!**/node_modules/**'
3434+ - '!**/dist/**'
3535+ - '!**/build/**'
3636+ - '!**/target/**'
3737+ - '!**/__pycache__/**'
3838+ - '!**/.venv/**'
3939+ - '!**/*.snap'
4040+4141+ # Cross-cutting rules live in .github/copilot-instructions.md.
4242+ path_instructions:
4343+ - path: 'osprey_worker/src/osprey/worker/ui_api/**/*.py'
4444+ instructions: |
4545+ Flask HTTP API on port 5004. Verify each view enforces authentication
4646+ and that any caller-supplied IDs are authorization-checked, not just
4747+ existence-checked (IDOR). Flag string-built SQL or shell commands —
4848+ SQLAlchemy bound parameters and `subprocess` arg arrays (no
4949+ `shell=True`) are required. Validate request bodies and query params
5050+ (Pydantic preferred). Error responses must not leak Python stack
5151+ traces or internal exception messages.
5252+5353+ - path: 'osprey_worker/src/osprey/worker/sinks/**/*.py'
5454+ instructions: |
5555+ Output sinks emit verdicts/effects to external systems (Kafka, HTTP,
5656+ etc.). Flag sink writes that don't bound retries or backoff, that log
5757+ full request/response bodies or raw Kafka payloads (PII risk), or
5858+ that swallow exceptions without a metric, log, or rethrow.
5959+6060+ - path: 'osprey_worker/src/osprey/worker/adaptor/**/*.py'
6161+ instructions: |
6262+ Pluggy plugin manager and hookspecs (`register_udfs`,
6363+ `register_output_sinks`, `register_labels_service_or_provider`). New
6464+ or renamed hookspecs change the plugin contract — surface signature
6565+ changes that would break `example_plugins/` or downstream consumers.
6666+ Generic-name UDFs collide silently; flag duplicates.
6767+6868+ - path: 'osprey_ui/src/**/*.{ts,tsx}'
6969+ instructions: |
7070+ - XSS: flag `dangerouslySetInnerHTML`, `innerHTML`, `document.write`,
7171+ `javascript:` URLs, and unsanitized `href`/`src` from user input.
7272+ Prefer `textContent`; sanitize with DOMPurify when raw HTML is
7373+ unavoidable.
7474+ - Token storage: auth tokens belong in HttpOnly, Secure, SameSite
7575+ cookies — flag `localStorage` or `sessionStorage` use for tokens.
7676+ - Open redirects: redirecting to a user-supplied URL without an
7777+ allowlist is risky.
7878+7979+ - path: 'osprey_coordinator/src/**/*.rs'
8080+ instructions: |
8181+ - `unwrap()` / `expect()` / `panic!` on user-driven or RPC paths
8282+ turn a bad input into a process crash; prefer `?` or explicit
8383+ error mapping.
8484+ - `unsafe` blocks deserve a justifying comment and the tightest
8585+ possible scope.
8686+ - Holding a `std::sync::Mutex` guard across `.await` deadlocks the
8787+ tokio runtime; long-lived `tokio::sync` guards can starve other
8888+ tasks. Flag them.
8989+ - Spawned tasks should be cancellation-safe — surface anything that
9090+ will leak on shutdown.
9191+9292+ - path: 'proto/osprey/rpc/**/*.proto'
9393+ instructions: |
9494+ gRPC contract consumed by both Python workers and the Rust
9595+ coordinator. Removing or renaming fields, changing field numbers, or
9696+ changing tag types is a breaking change — flag it and ask for a
9797+ migration plan. Bindings must be regenerated with `./gen-protos.sh`
9898+ in the same PR.
9999+100100+ - path: 'example_plugins/**/*.py'
101101+ instructions: |
102102+ Reference plugins. Per AGENTS.md > Architecture no production code
103103+ belongs here — flag if production-only logic is being added.
104104+105105+ - path: 'example_rules/**'
106106+ instructions: |
107107+ Sample SML rules and YAML config. Reference, not production — flag
108108+ if production rule logic is being added here instead of in the
109109+ consumer's own rules directory.
110110+111111+ - path: '**/{pyproject.toml,uv.lock,Cargo.toml,Cargo.lock,package.json,package-lock.json}'
112112+ instructions: |
113113+ Dependency additions, removals, or upgrades (including transitive
114114+ bumps) require human approval for license (Apache 2.0, per
115115+ `LICENSE.md`) and CVE review per AGENTS.md > "Human-approval-required
116116+ actions". Surface every change so reviewers don't miss it. New
117117+ Python deps must also be exercised by `fawltydeps` or added to
118118+ `[tool.fawltydeps].ignore_unused` with a comment.
119119+120120+ - path: '{docker-compose.yaml,start.sh,entrypoint.sh,**/Dockerfile,.github/workflows/publish-coordinator-image.yml,.github/workflows/release-osprey-rpc.yml,.github/workflows/mdbook.yml}'
121121+ instructions: |
122122+ AGENTS.md > "Human-approval-required actions" lists these as
123123+ restricted: release/deploy workflows, production Dockerfiles,
124124+ signing/tagging, and infra entrypoints. Flag any change here as
125125+ needing explicit human approval. Default Docker bindings are
126126+ `127.0.0.1` — flag any bind-address change.
127127+128128+ # These overlap with checks already enforced in CI, or add style noise.
129129+ tools:
130130+ ruff:
131131+ enabled: false
132132+ prettier:
133133+ enabled: false
134134+ markdownlint:
135135+ enabled: false
136136+ languagetool:
137137+ enabled: false
···11+# Code review instructions
22+33+These instructions apply to AI tools when they review pull requests in this repository, and when they answer questions about this codebase. They are guidance, not a checklist. Use judgment, prefer fewer high-signal comments over many low-signal ones, and skip points that don't apply to the diff in front of you. For human contributor guidance see [`README.md`](../README.md); for general agent rules see [`AGENTS.md`](../AGENTS.md).
44+55+## Repository at a glance
66+77+- Multi-language monorepo: Python (worker + UI API), TypeScript (UI), Rust (coordinator). Top-level modules are documented in [`AGENTS.md`](../AGENTS.md) § Architecture — `osprey_worker/`, `osprey_rpc/`, `osprey_ui/`, `osprey_coordinator/`, `proto/osprey/rpc/`, `example_plugins/`, `example_rules/`.
88+- Python version pinned in `.python-version`, managed with [`uv`](https://docs.astral.sh/uv/). Linted and type-checked in CI by `ruff` + `mypy` via `pre-commit`. Unused-dep enforcement via `fawltydeps`.
99+- TypeScript in `osprey_ui/` (React + Ant Design + Highcharts; Node version in `.github/workflows/code-quality.yml`). Formatted in CI by `prettier` (`npm run format:check`).
1010+- Rust in `osprey_coordinator/` (stable toolchain, `tokio` + `tonic` + `etcd` + `rdkafka`). `cargo fmt` and `cargo build` gate CI; `cargo clippy -- -D warnings` and `cargo test` run advisory (`continue-on-error: true`).
1111+- **Generated code**: `osprey_rpc/src/osprey/rpc/**/*_pb2*.py`, `*_pb2*.pyi`, and `osprey_coordinator/src/proto/` are produced by `./gen-protos.sh` from `proto/osprey/rpc/**/*.proto`. Hand-edits drift from the schema — regenerate instead. Generated files are excluded from `ruff` and `mypy` for a reason.
1212+- Plugin system: `pluggy` with `@hookimpl_osprey` hooks (`register_udfs`, `register_output_sinks`, `register_labels_service_or_provider`). Reference patterns live in `example_plugins/src/register_plugins.py`.
1313+- Data model conventions: Pydantic for models, SQLAlchemy for persistence (versions pinned in `pyproject.toml`).
1414+1515+## Scope of review — focus on quality and security
1616+1717+Lint and formatting are enforced in CI, so please skip:
1818+1919+- formatting, whitespace, indentation, quote style, or import ordering
2020+- `ruff` / `mypy` / `prettier` / `cargo fmt` rule violations
2121+- typos in comments or doc grammar nits
2222+- missing docstrings on internal helpers
2323+- subjective style preferences not codified in a project rule
2424+2525+If a finding would be caught by `uv run ruff check`, `uv run mypy .`, `npm run format:check` (in `osprey_ui/`), or `cargo fmt --check` (in `osprey_coordinator/`), it's redundant.
2626+2727+## Security (cross-cutting)
2828+2929+Security findings are the highest-value comments you can leave. When you spot one, name the risk concretely and suggest a fix. Areas to watch across the whole codebase:
3030+3131+- **Hard-coded secrets.** API keys, tokens, passwords, OAuth secrets, JWT signing keys, DB connection strings (Postgres, Druid, Kafka SASL, MinIO, Bigtable), or webhook secrets in source or committed config. `docker-compose.yaml` uses environment variables intentionally — flag any secret that leaks into committed files.
3232+- **Injection.** String-built SQL, shell commands, file paths, HTML, or LLM prompts are usually a smell. Look for SQLAlchemy bound parameters (or `text()` with `bindparams`), `subprocess` arg arrays (no `shell=True`), context-aware HTML encoding, and a clear separation between trusted system prompts and untrusted user content.
3333+- **Sensitive logging.** Secrets, JWTs, full `Authorization` headers, full request/response bodies, raw Kafka payloads, or PII in logs, traces, metric labels, or error responses are risky. Error responses from the UI API (port 5004) shouldn't leak Python stack traces.
3434+- **Weak crypto.** MD5 or SHA-1 used for security, ECB mode, reused IVs, `random.random()` / `random.choice()` for tokens or IDs (prefer `secrets` in Python, `OsRng` / `getrandom` in Rust), or hand-rolled crypto are worth questioning. JWTs should reject the `none` algorithm, use strong secrets, and have short access-token expirations.
3535+- **Unsafe deserialization or evaluation.** `eval`, `exec`, `pickle.loads`, `yaml.load` without `SafeLoader`, and `marshal.loads` on untrusted input are risky. Same for `eval` / the `Function` constructor / `setTimeout` with string arguments in TypeScript.
3636+- **Removing security controls.** If a diff disables authentication, authorization, CSRF, CORS, rate limiting, or the default `127.0.0.1` bindings in `docker-compose.yaml` (see `docs/DEVELOPMENT.md` §6), ask whether it's intentional and justified.
3737+- **Untrusted protobuf toolchain.** Per `AGENTS.md` § Security, only regenerate bindings via `./gen-protos.sh`. Flag generated-file diffs that don't match a corresponding `.proto` change.
3838+3939+Path-specific concerns (UI API views, output sinks, plugin adaptor, Rust coordinator, raw `.proto` changes, dependency manifests, restricted infra) are scoped in [`.coderabbit.yaml`](../.coderabbit.yaml) under `reviews.path_instructions`. When reviewing those areas, apply the same general principles — authorization checks on UI-API-supplied IDs, parameterized queries, XSS care on the client, panic-safety in Rust, license/CVE attention on dependency bumps.
4040+4141+## Code quality (cross-cutting)
4242+4343+Use judgment — these patterns tend to cause bugs or maintenance pain regardless of where they appear:
4444+4545+- **Generated files.** `*_pb2*.py`, `*_pb2*.pyi`, and `osprey_coordinator/src/proto/` are produced by `./gen-protos.sh`; hand-edits drift from the `.proto` schema.
4646+- **Error handling.** Silently swallowed exceptions (`except Exception: pass` with no log or rethrow), Rust `let _ = result;` discarding an error, unhandled promise rejections in TS, and missing `await` on a coroutine whose result matters tend to cause production surprises.
4747+- **Async correctness (Python).** Calling sync I/O inside an `async def` blocks the event loop. `asyncio.gather(...)` without `return_exceptions=True` or explicit handling can mask failures.
4848+- **Async correctness (Rust).** Holding a `std::sync::Mutex` guard across `.await` deadlocks the tokio runtime; long-lived `tokio::sync` guards can starve other tasks.
4949+- **Type safety.** New `# type: ignore` / `Any` / `cast(...)` in Python, `any` / `as unknown as` / non-null `!` / `@ts-ignore` in TypeScript, or `unwrap()` / `expect()` / `unsafe` on user-driven paths in Rust are worth questioning. Narrowly-scoped `# type: ignore[<code>]` and `@ts-expect-error` with a justifying comment are preferred when an escape hatch is genuinely needed (matches `AGENTS.md` § Security).
5050+- **Tests.** New behavior generally warrants a test; bug fixes generally warrant a regression test (`AGENTS.md` § "Code review"). Integration tests run via `./run-tests.sh`.
5151+- **Duplicated logic.** If a helper already exists in the same package, prefer it over a parallel implementation.
5252+- **Dead code.** Commented-out blocks and TODOs without an issue link are worth a nudge.
5353+5454+## What not to flag
5555+5656+These categories of comments tend to add noise without surfacing real risk — please skip them:
5757+5858+- "Consider adding a `None` check" on a value already typed as non-`None` by mypy or non-`null`/`undefined` by TypeScript.
5959+- "Consider adding error handling" on a wrapper that already propagates errors via `async`/`await` or `?` in Rust.
6060+- "This could be a constant" on a string literal used in a single place.
6161+- "Add a docstring" on an internal helper.
6262+- Rhetorical questions like "have you considered…" without a concrete risk attached.
6363+- Defensive-coding suggestions on values whose types already prevent the failure mode.
6464+- "Add a test" on a config-only, doc-only, or comment-only change.
6565+- Suggestions to rename a symbol "for clarity" without a concrete ambiguity.
6666+- Style nits in generated protobuf files (`*_pb2*.py`, `*_pb2*.pyi`, `osprey_coordinator/src/proto/`) — they're excluded from `ruff` and `mypy` for a reason.
6767+6868+## Tone
6969+7070+Be specific and concise. When you flag something, name the concrete risk ("possible SQL injection via string-built `text()`", "missing authorization check — possible IDOR", "secret logged at info level", "`unwrap()` on RPC input — panics will crash the worker") and, where helpful, show the fix in code. Skip nits, and stay quiet when nothing in this list applies.