Skip to content
OpenSmartRoute

osr-contributing

.claude/skills/osr-contributing/SKILL.md

Work inside the OpenSmartRoute repository itself. Add a new strategy, signal, adapter or module to the codebase under src/opensmartroute; update the frozen public API snapshot in tests/public_api.json; keep the version literal and pyproject in sync; pass the ruff, mypy and pytest quality gates and the docs gate (docs/REFERENCE.md regenerated, every docs/*.md linked from README); write or update docs/*.md and .claude/skills SKILL.md packages so they render on the documentation site; follow the zero-dependency, settings and branding single-source-of-truth rules and the design patterns; write tests and CHANGELOG entries; prepare a release. Use when contributing code or documentation to the repo, fixing a failing gate, or reviewing a pull request against project conventions.

Package
.claude/skills/osr-contributing
Compatibility
OpenSmartRoute repository, Python >= 3.10
License
Apache-2.0
Domains
coding
Quality prior
0.9
Tags
opensmartroute contributing repo conventions

Install by copying .claude/skills/osr-contributing/ into the skills folder of your coding assistant. To load every package as a routing target: osr --skills .claude/skills route "..." --plan.

Gates (run all four before finishing any change)#

python -m ruff format src tests
python -m ruff check src tests          # rules E,F,I,UP,B,S; line length 120
python -m mypy src                      # mypy_path=src, packages=opensmartroute; must be clean
python -m pytest tests -q               # pythonpath=src
python scripts/api_reference.py         # regenerate docs/REFERENCE.md after any public-name change

tests/test_docs.py fails when docs/REFERENCE.md is stale or an exported name has no summary; python scripts/api_reference.py report lists the undocumented names. It also requires every docs/*.md to be linked from README.md and every .claude/skills/<name>/ to contain a SKILL.md. tests/test_docs_claims.py (SDK) and platform/api/tests/test_platform_docs_claims.py (platform) fail when a doc names an osr command / flag, an OSR_* variable, an HTTP endpoint or a /platform/dashboard/<page> that does not exist, when a user-facing doc is missing from the site catalogue, or when a CHANGELOG section lacks a date, body or compare link - fix the doc or the code, never the test. Ad-hoc scripts: $env:PYTHONPATH="src"; python -X utf8 ... (the package is not installed in the venv).

Hard rules#

  • Zero runtime dependencies in the core. Anything optional (yaml, fastapi, httpx, sentence_transformers, opentelemetry, cryptography) is imported lazily inside the function that needs it and mapped to an extra in pyproject.toml (yaml, server, embeddings, otel, crypto, all).
  • Public API is frozen. tests/public_api.json snapshots opensmartroute.__all__. Adding a name = add it to the JSON (sorted; uppercase first, then __version__, then lowercase). Removing or renaming = major version bump.
  • Version literal. __version__ = "x.y.z" stays a literal in src/opensmartroute/__init__.py and must equal version = "x.y.z" in pyproject.toml (scripts/release.py rewrites both). Read it elsewhere through branding.version().
  • No hardcoded tunables. Thresholds, weights, scales go in settings.py dataclasses and are read through resolve(settings).<group>.<field>; document new fields (they become OSR_<GROUP>_<FIELD> automatically).
  • No brand literals. Use branding.PACKAGE / CLI / env_key / error_code / metadata_key / logger / user_agent. osr-* metadata keys come from metadata_key(field).
  • Typed errors only. Raise subclasses of OpenSmartRouteError (ConfigurationError, ValidationError, NoRouteError, TargetUnavailableError, ExecutionError, SecurityError, StateStoreError) with details={...}; never bare Exception.
  • Every exported name is documented. Classes and functions carry a one-line docstring; module-level constants and type aliases carry a same-line # comment (or a comment on the line above). The first line becomes its row in docs/REFERENCE.md.
  • Strategies must degrade to neutral scores on internal failure, not raise during route().

Where things go#

core/        types + TargetRegistry            strategies/  Strategy subclasses (name = snake_case noun)
signals/     SignalExtractor subclasses        policy/      PolicyRule callables + Policy chain
math/        pure numerics, no routing types   learning/    learners that persist state
realtime/    breakers, budgets, health         enterprise/  builder, middleware, telemetry, stores, ops
security/    guards, limits, provenance        adapters/    external systems; lazy optional imports
eval/        datasets, metrics, frontiers      sdk.py       ComponentRegistry + decorators
observability.py  Tracer, Span, Event, sinks   (emit with current_tracer().event(...); names in EVENT_NAMES)
settings.py  tunables                          branding.py  names (leaf module, imports nothing internal)

Adding a strategy#

  1. Subclass Strategy in strategies/<name>.py; set name; implement score(request, signals, candidates) -> dict[str, StrategyScore]; optional update(outcome), state()/load_state() for persistence.
  2. Take settings: Settings | None = None; add a WeightSettings.<name> default.
  3. Export from strategies/__init__.py (__all__) and, if public, opensmartroute/__init__.py + tests/public_api.json. Add to default_strategies() only if it needs no external service.
  4. Tests: deterministic (seed=0), one behavioural test + one persistence round-trip if stateful.
  5. Document in docs/ARCHITECTURE.md (strategies table) and README.md "Strategies"; CHANGELOG under [Unreleased]; run python scripts/api_reference.py so docs/REFERENCE.md picks it up.

Adding an adapter#

Return RouteTargets or handlers; never import the optional client at module import time; validate external input (ConfigurationError with details["path"]), pass descriptions through security.description_risk when they come from a third party.

Documentation#

  • Layout: README.md short overview; docs/GUIDE.md walkthroughs (numbered H2s); docs/SDK.md decorator SDK and settings; docs/ARCHITECTURE.md pipeline and strategies table; docs/MATH.md numbered sections with module references; docs/RESEARCH.md idea-to-module map; docs/ROADMAP.md rows name the module; docs/REFERENCE.md is generated - never edit by hand and never > docs/REFERENCE.md (the script writes it).
  • The Next.js documentation site (platform/web, /docs/<slug>) is end-user documentation for the hosted platform, the Python SDK and the pip package. It publishes docs/*.md (docs/PLATFORM.md is the platform user guide), the root README/CHANGELOG/CONTRIBUTING/SECURITY, deploy/README.md, spec/ocm/README.md, every .claude/skills/*/SKILL.md and a REST API reference generated from platform/api/openapi.json. Internal documents (docs/PLATFORM_PLAN.md, docs/GO_TO_MARKET.md, docs/BRAND.md, docs/sales/*.md, platform/README.md) stay in the repository and are linked from README.md only. A new user-facing docs/*.md needs an entry in platform/web/src/lib/docs/catalogue.ts and a link in README.md; skills are discovered automatically. Details: the osr-platform skill and platform/README.md.
  • Markdown that renders well on the site: one H1, H2/H3 headings (they become the table of contents and the search index), GitHub-flavoured tables, fenced code with a language, ```mermaid for diagrams, relative links to other .md files (rewritten to site routes) and to source files (rewritten to GitHub). Do not rename headings that other files link to without updating the anchors.
  • Skill packages: .claude/skills/<name>/SKILL.md with frontmatter name (= directory), description (<= 1024 chars, several request-like sentences - they are the similarity examples), license, compatibility, metadata.osr-domains (ontology names only, e.g. coding general), osr-tags, osr-quality-prior (0.8-0.95), osr-primary: "false". Omit osr-actions. Add a representative query to tests/test_skills.py that must select the new skill for the plan slot; osr skills .claude/skills validates the folder.

Tests#

  • tests/test_router.py, test_math.py, test_security.py, test_enterprise.py, test_adapters.py, test_execution.py, test_sdk.py, test_integrations.py, test_public_api.py.
  • Use tmp_path for state, monkeypatch for env, ComponentRegistry() instances rather than the global components; restore configure(before) in a finally when touching settings.

Release#

Edit CHANGELOG.md [Unreleased] as you go (Added / Changed / Fixed). scripts/release.py (run by the Prepare release workflow) bumps the version literal in both files and dates the section; the release.yml workflow publishes. Do not hand-edit released sections. scripts/release.py readiness computes the v1.0 readiness table (ROADMAP) from the repo - release rows block check for 1.x, adoption rows (docs/SECURITY_REVIEW.md review log, examples/leaderboard/results/README.md Listings rows with accepted, ADOPTERS.md Production users rows) are tracked only. Never fake a row - the table is the honest state of 1.0. Version mirrors outside release.py prepare: platform/api/{pyproject.toml (version and opensmartroute[...]>=), osr_platform/init.py}, platform/web/package{,-lock}.json (top two entries), deploy/helm/opensmartroute/Chart.yaml (version + appVersion), azure.yaml template@, deploy/Dockerfile and deploy/README.md image tags, README OSR_VERSION=, docs/sales/DEMO_SCRIPT.md, docs/SECURITY_REVIEW.md subject; then regenerate platform/api/openapi.json and docs/REFERENCE.md.