somm explained · part 6
Engineering Reference
The contributor's map: how the uv workspace is laid out, what CI refuses to merge, how six packages ship to PyPI under one shared version, and the commands you will actually type.
Everything on this page is machinery that exists to keep one promise: the library stays zero-config, fast, offline-capable, and private, no matter who is committing. The workspace layout, the CI gates, the release gate, and the test isolation fixtures are all enforcement mechanisms for that promise.
Workspace layout: six packages, one shared version
somm is a single repository organized as a uv workspace of six Python packages, splitting schema and storage, the client and router, service workers and web UI, MCP, LangChain integration, and agent onboarding into separately publishable units (README.md:528, pyproject.toml:7).
| Package | Role |
|---|---|
somm-core | Dependency-free foundation: typed telemetry records, SQLite persistence, config, parsing, pricing, quota accounting, and evaluation primitives (packages/somm-core/README.md:3) |
somm | The main library and CLI: provider routing, local telemetry, cost and budget controls, prompt management, evaluation, and diagnostics (packages/somm/README.md:3) |
somm-service | The local service tier: web dashboard, telemetry APIs, an Anthropic-compatible proxy, and background intelligence workers (packages/somm-service/README.md:3) |
somm-mcp | Stdio MCP server exposing telemetry, recommendations, model intelligence, and decision history to coding agents (packages/somm-mcp/src/somm_mcp/cli.py:27) |
somm-langchain | A thin adapter that makes somm's routed runtime look like a LangChain chat model (packages/somm-langchain/README.md:3) |
somm-skill | Dependency-free Markdown onboarding resources for coding agents, shipped as package data (packages/somm-skill/README.md:3, packages/somm-skill/pyproject.toml:4) |
graph TD
somm["somm — client, router, CLI"] --> core["somm-core — records, SQLite, pricing, quotas"]
service["somm-service — dashboard, proxy, workers"] --> core
mcp["somm-mcp — agent tools over stdio"] --> somm
mcp --> core
lc["somm-langchain — chat-model adapter"] --> somm
skill["somm-skill — agent onboarding docs, no dependencies"]
somm-langchain wraps SommLLM.generate() (packages/somm-langchain/src/somm_langchain/chat_model.py:79); somm-mcp delegates persistence and domain logic to somm-core and somm; somm-skill ships no runtime code at all (packages/somm-skill/src/somm_skill/__init__.py:1).The whole workspace moves as one version. Releases bump every package together (RELEASING.md:8), and a standard-library-only script enforces it: scripts/check_release_gate.py loads every package's pyproject.toml, requires a single shared version, verifies that somm_core.VERSION matches, and checks that internal somm/somm-core dependency pins are exact (scripts/check_release_gate.py:23, scripts/check_release_gate.py:54, scripts/check_release_gate.py:71). Underneath it all sits the migration-backed database: schema v20 evolves the original workload/prompt/call ledger into decisions, workload revisions, datasets, evaluation receipts, campaigns, canonical model aliases, and serving SLOs (packages/somm-core/src/somm_core/version.py:7).
CI gates: lint, tests on 3.12/3.13, pip-audit, and the perf budget
Every pull request runs a matrix on Python 3.12 and 3.13 against the frozen workspace. Each matrix job checks release-version consistency, linting, tests, and the performance budget (.github/workflows/ci.yml:9). A separate security job — deliberately isolated from the version matrix — runs pip-audit against exported, locked production dependencies (without workspace packages or dev dependencies) and audits the GitHub Actions themselves with zizmor (.github/workflows/ci.yml:35). Third-party actions are pinned to commit SHAs rather than mutable tags (.github/workflows/ci.yml:15).
| Gate | Command |
|---|---|
| Full test suite | uv run pytest packages/ tests/ (.github/PULL_REQUEST_TEMPLATE.md:7) |
| Blocklist guard | uv run pytest tests/test_blocklist.py (.github/PULL_REQUEST_TEMPLATE.md:8) |
| Version consistency | uv run python scripts/check_release_gate.py (.github/workflows/ci.yml:23) |
| Performance budget | uv run python scripts/check_perf_budget.py (.github/workflows/ci.yml:32) |
generate() calls per perf-budget runThe performance gate protects the hot path the whole project is built around. It measures clean import somm latency in fresh subprocesses, then exercises a warmed SommLLM.generate() path 500 times against a fake provider and a temporary repository — isolating somm's own overhead from network and provider latency (scripts/check_perf_budget.py:52, scripts/check_perf_budget.py:69, scripts/check_perf_budget.py:22).
scripts/check_perf_budget.py:96, scripts/check_perf_budget.py:100, scripts/check_perf_budget.py:108).
Human process backs the automation: the PR template requires contributors to confirm tests, the blocklist guard, lint status, changelog handling, and coordinated versioning (.github/PULL_REQUEST_TEMPLATE.md:5), and CONTRIBUTING.md requires tests plus an Unreleased changelog entry with every change (CONTRIBUTING.md:28).
Release discipline: the version gate, trusted publishing, and rerunnable publishes
Publishing starts when a GitHub release is published, or manually via workflow_dispatch (.github/workflows/publish.yml:23). A matrix then builds and publishes each of the six packages independently, each using its own package-specific GitHub environment and PyPI trusted publishing via OIDC (.github/workflows/publish.yml:28). The per-package environments are not decoration: pending trusted publishers on PyPI must be unique per repository, workflow, and environment (.github/workflows/publish.yml:4).
Two small decisions make multi-package publishing survivable. Each package builds into its own artifact directory, because workspace-member builds would otherwise all write into the root dist/ (.github/workflows/publish.yml:54). And skip-existing means a release that failed halfway through six packages can simply be rerun — already-published packages are skipped rather than erroring (.github/workflows/publish.yml:63).
Before any of that, the release gate runs — the same check_release_gate.py used in CI, written against only the standard library so it works before dependencies are even installed (scripts/check_release_gate.py:1). Beyond version consistency, releases with major version 1 or higher additionally require a committed approval marker (scripts/check_release_gate.py:82); RELEASING.md frames this as an explicit readiness file required before any 1.x publication (RELEASING.md:94). The release process also refreshes bundled pricing and plan data and updates agent-skill documentation (RELEASING.md:28).
roster.toml 1.0.0 value and keeps the workspace, README, roster, and package pins on the 0.x track. The ≥1.x approval-marker gate exists to block exactly that kind of accidental major-version publish.
Test strategy: isolated machine state, offline pricing, and the blocklist scanner
The suite runs across all packages plus the top-level tests/ directory (pyproject.toml:32). Its most important safety feature is invisible: an autouse fixture redirects machine-wide registry, plan, and global-database state into temporary paths and disables mirroring for every test (conftest.py:13). The reason is concrete — cross-project mirroring can otherwise touch the real ~/.somm/global.sqlite on the developer's machine (conftest.py:18).
Configuration tests pin down zero-config behavior precisely: they clear the relevant environment variables, call somm_core.config.load(), and verify defaults plus precedence among explicit environment settings, pyproject.toml, an existing project-root .somm, the project registry, and a fresh local .somm directory (tests/test_config_load.py:44). Local project state wins over registry reuse, an explicit SOMM_DB_DIR wins over everything tested, and registry reuse is announced on stderr so telemetry is never silently shared (tests/test_config_load.py:78, tests/test_config_load.py:102, tests/test_config_load.py:135).
Pricing tests keep cost accounting honest offline. The packaged JSON snapshot — regenerated by scripts/update_pricing_bundle.py, which normalizes LiteLLM data to per-million-token prices and prunes it to routes somm supports (scripts/update_pricing_bundle.py:71) — must cover every paid routed provider and populate model intelligence with no network access (tests/test_pricing_bundle.py:38, tests/test_pricing_bundle.py:47). Bundle sync replaces stale seeded data but preserves manually entered prices, and a fingerprint skips repeat work (tests/test_pricing_bundle.py:66, tests/test_pricing_bundle.py:84). A missing price yields zero cost plus a once-per-provider/model warning for paid providers, while free providers stay silent (tests/test_pricing_safety.py:87, tests/test_pricing_safety.py:101, tests/test_pricing_safety.py:115).
Finally, a repository-wide blocklist scanner rejects internal names and personal filesystem paths across selected source, documentation, example, test, and CI files (tests/test_blocklist.py:57, tests/test_blocklist.py:117, tests/test_blocklist.py:162). It is the last line of the project's privacy posture applied to the repository itself: private project details must not leak into the public codebase.
CLI quick reference: generate, status, doctor, compare, serve
The somm CLI spans generation, status, workloads, prompts, evals, campaigns, plans, diagnostics, and service startup (README.md:330). These are the commands contributors and bug reporters reach for most:
| Command | What it does | Reference |
|---|---|---|
somm generate … | Run a routed, recorded generation from the shell | packages/somm/src/somm/cli.py:2051 |
somm status / somm tail | Inspect recorded telemetry from the terminal | examples/README.md:70, examples/README.md:78 |
somm doctor | Check processes, cooldowns, migrations, and permission drift — its output is requested with every bug report | docs/errors/SOMM_PORT_BUSY.md:11, .github/ISSUE_TEMPLATE/bug_report.md:22 |
somm calls --status error | Surface failed-call telemetry when reporting bugs | .github/ISSUE_TEMPLATE/bug_report.md:28 |
somm compare … --models … | Run one prompt across several providers/models side by side | docs/index.html:181 |
somm serve --project … | Start the local dashboard, scheduler, and background workers | docs/index.html:174 |
somm plugin list / info | Inspect installed hook plugins | docs/plugins.md:167 |
somm-mcp --project … | Launch the stdio MCP server for coding agents | packages/somm-mcp/src/somm_mcp/cli.py:12 |
somm-serve | Start the service directly, or run intel, shadow, and agent admin commands | packages/somm-service/src/somm_service/cli.py:161 |
Canonical errors: the SOMM_* catalog
Failures in somm are deliberately visible and bounded rather than silently swallowed (README.md:162), and each named failure gets a canonical page. Every SOMM_* page documents the same four things: the problem, its cause, the runtime behavior, and the fix (docs/errors/index.html:28).
| Error page | What it covers |
|---|---|
SOMM_WORKLOAD_UNREGISTERED | Strict mode rejects calls whose workload was never declared with register_workload(...); the default observe mode auto-registers instead (docs/errors/SOMM_WORKLOAD_UNREGISTERED.md:19, packages/somm/src/somm/client.py:658) |
SOMM_BUDGET_EXCEEDED | Budget refusal is fatal and happens before dispatch, so provider fallback can never spend around the daily cap (docs/errors/SOMM_BUDGET_EXCEEDED.md:23) |
SOMM_PORT_BUSY | The service port is already taken; the documented fix path leans on somm doctor's process, cooldown, migration, and permission-drift checks (docs/errors/SOMM_PORT_BUSY.md:11) |
The budget error is the catalog's clearest expression of the project's design philosophy: auxiliary intelligence — hooks, mirroring, learned overrides — fails open so it can never break live inference, but hard budgets deliberately fail closed before any provider is contacted (packages/somm/src/somm/hooks.py:246, packages/somm/src/somm/client.py:571).
Where to go next
The Life of a Call
The runtime these gates protect: routing, the pre-dispatch budget check, fallback, and the telemetry write.
The Data Substrate
The SQLite schema those v20 migrations build, and the permission model the test isolation defends.
The Intelligence Loop
The workers somm serve starts: model intel, shadow evaluation, and recommendations.
Agents and Integrations
Half the workspace in action: somm-mcp, somm-langchain, and the somm-skill playbook.