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).

6workspace packages
1shared version across all of them
v20current SQLite schema version
PackageRole
somm-coreDependency-free foundation: typed telemetry records, SQLite persistence, config, parsing, pricing, quota accounting, and evaluation primitives (packages/somm-core/README.md:3)
sommThe main library and CLI: provider routing, local telemetry, cost and budget controls, prompt management, evaluation, and diagnostics (packages/somm/README.md:3)
somm-serviceThe local service tier: web dashboard, telemetry APIs, an Anthropic-compatible proxy, and background intelligence workers (packages/somm-service/README.md:3)
somm-mcpStdio MCP server exposing telemetry, recommendations, model intelligence, and decision history to coding agents (packages/somm-mcp/src/somm_mcp/cli.py:27)
somm-langchainA thin adapter that makes somm's routed runtime look like a LangChain chat model (packages/somm-langchain/README.md:3)
somm-skillDependency-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"]
    
Internal dependency shape. 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).

GateCommand
Full test suiteuv run pytest packages/ tests/ (.github/PULL_REQUEST_TEMPLATE.md:7)
Blocklist guarduv run pytest tests/test_blocklist.py (.github/PULL_REQUEST_TEMPLATE.md:8)
Version consistencyuv run python scripts/check_release_gate.py (.github/workflows/ci.yml:23)
Performance budgetuv run python scripts/check_perf_budget.py (.github/workflows/ci.yml:32)
500warmed generate() calls per perf-budget run

The 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).

Only the median gates The script reports both p50 and p95, but a job fails only when p50 exceeds the configurable import or hot-path budget. P95 is published for visibility without letting noisy outliers flake CI (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).

Release metadata guardrail The 0.13.0 release path corrected the earlier stale 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:

CommandWhat it doesReference
somm generate …Run a routed, recorded generation from the shellpackages/somm/src/somm/cli.py:2051
somm status / somm tailInspect recorded telemetry from the terminalexamples/README.md:70, examples/README.md:78
somm doctorCheck processes, cooldowns, migrations, and permission drift — its output is requested with every bug reportdocs/errors/SOMM_PORT_BUSY.md:11, .github/ISSUE_TEMPLATE/bug_report.md:22
somm calls --status errorSurface 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 sidedocs/index.html:181
somm serve --project …Start the local dashboard, scheduler, and background workersdocs/index.html:174
somm plugin list / infoInspect installed hook pluginsdocs/plugins.md:167
somm-mcp --project …Launch the stdio MCP server for coding agentspackages/somm-mcp/src/somm_mcp/cli.py:12
somm-serveStart the service directly, or run intel, shadow, and agent admin commandspackages/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 pageWhat it covers
SOMM_WORKLOAD_UNREGISTEREDStrict 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_EXCEEDEDBudget 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_BUSYThe 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.