# Packaging for Mitos — declarative, setuptools-backed, zero new runtime dependency. # # This file is the single packaging home (it replaces setup.py). Later visions # extend the dependency list and the argparse subparser tree behind the `mitos` # console script; they do not rebuild this surface. # # Two fragile invariants are kept alive here: # 1. `__version__` stays the single source of truth in mitos/__init__.py and is # read DYNAMICALLY by the build backend (see [tool.setuptools.dynamic]). A # static `[project] version` field would silently kill the once-a-day update # nudge (mitos/_update.py fetches that literal from GitHub `main`). # 2. mitos/format-spec.md ships as package data — `mitos init` and # `import mitos.parser` (FIELD_MAP at import time) read it from the installed # package dir, so it MUST be in the wheel. [build-system] # setuptools >=77 is the minimum that supports all four things we rely on: # PEP 621 `[project]` metadata + `[tool.setuptools.dynamic]` attr-version (61.0), # PEP 660 editable installs (64.0, the dev loop's `pip install -e .`), AND # PEP 639 SPDX `license` string + `license-files` (77.0, the form PyPI renders # as a proper license badge). Build-time only — never a runtime dependency. requires = ["setuptools>=77"] build-backend = "setuptools.build_meta" [project] name = "mitos-adr" # distribution name (import package is `mitos`); kept on purpose dynamic = ["version"] # read from mitos/__init__.py — NOT a static literal description = "Architectural decision memory for LLM-native workflows — markdown for humans, a graph for agents." readme = "README.md" requires-python = ">=3.13" # UCD 15.1.0 goldens (§11) + SQLite 3.37+ for STRICT tables ship with 3.13 license = "Apache-2.0" license-files = ["LICENSE"] # Maintainer contact is the skyforge.sh address while the repo and PyPI account # stay personal (mirrors letterbox ADR-070: durable public contact, recovery on # gmail). The K2 guard in test_packaging.py scopes "never skyforge" to # [project.urls] only. authors = [{ name = "Vinga Luksaite", email = "vinga@skyforge.sh" }] keywords = [ "adr", "architectural-decisions", "decision-records", "llm", "ai-agents", "mcp", "knowledge-graph", "qdrant", ] classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console", "Intended Audience :: Developers", "Topic :: Software Development :: Documentation", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.13", ] dependencies = [ "filelock>=3.0.0", "google-genai>=1.66.0", # Ceilinged below 1.0 for the same reason as `mcp`, and discovered the same # way: `anthropic` 1.0.0 removes `temperature` from `Messages.create()` # (sampling params moved into `output_config`), so a `pipx install --force` # against an unceilinged floor silently upgrades the dogfood build and every # `mitos check` dies at `Fatal Unexpected Error: Messages.create() got an # unexpected keyword argument 'temperature'` — the LLM-judged layer gone, # exit 2, on an install that reports READY. Measured 2026-08-25. The 1.x port # (conflict_judgment.py's call site + the 0.3 temperature floor's new home) # is a ROADMAP item, not a dependency bump. "anthropic>=0.84.0,<1", # Ceilinged below 2.0: `mcp` 2.x drops `mcp.server.fastmcp` (renamed to # `mcp.server.mcpserver`, exporting `MCPServer`), which `mcp_server.py` # imports at module scope — so an unceilinged floor makes `mitos serve` # unstartable from a fresh install. The 2.x port is a ROADMAP item, not a # dependency bump. Gated by `test_packaging.py`'s `import mitos.mcp_server`. "mcp>=1.26.0,<2", "requests>=2.0.0", ] [project.scripts] mitos = "mitos.cli:main" [project.urls] Homepage = "https://github.com/dovahkiin-v/mitos" Repository = "https://github.com/dovahkiin-v/mitos" Documentation = "https://github.com/dovahkiin-v/mitos#readme" Issues = "https://github.com/dovahkiin-v/mitos/issues" [project.optional-dependencies] # Test tooling — the single declarative home for it (local dev AND CI install the # same set via `pip install -e .[test]`). Do NOT duplicate the runtime [project] # dependencies here; the base install pulls those. Floors, not pins: grounded in # the current releases (pytest 9.x, pytest-asyncio 1.4.0) but left open so a future # bump happens once, here. pytest-asyncio is load-bearing for the fast suite — the # offline async tests (test_mcp.py, test_record_decision.py) run in `-m "not packaging"`. test = [ "pytest>=8.0", "pytest-asyncio>=1.0", # Parallel runner for the OFFLINE suite only — `-n auto` takes it from ~78s to # ~48s with identical results. NOT safe for the live tier: conftest's # sweep_leaked_qdrant_collections is session-scoped, and under xdist every # worker is its own session, so the first to finish deletes the `mitos-tmp*` # collections the others are still using. Measured 2026-07-25: the same live # module went 5 passed → 4 → 2 across runs, exiting 0 every time. Use it as # `MITOS_NO_LIVE_TESTS=1 pytest -m "not packaging" -n auto`; run the live tier # serially until that sweep is made worker-aware. "pytest-xdist>=3.8", ] [tool.setuptools] packages = ["mitos"] [tool.setuptools.dynamic] version = { attr = "mitos.__version__" } [tool.setuptools.package-data] mitos = ["format-spec.md"] [tool.pytest.ini_options] # Explicit strict mode: every coroutine test must carry @pytest.mark.asyncio (they # already do). Makes the contract explicit and silences pytest-asyncio's unset-mode # config warning. Do NOT switch to "auto" — it would change collection semantics # for the whole suite. asyncio_mode = "strict" markers = [ "packaging: slow real-install packaging test (fresh venv + non-editable pip install). CI runs it explicitly with `-m packaging`; the fast suite skips it with `-m 'not packaging'`. Not selected by testmon (no imported source) — the marker is its CI-selection contract.", "judge: fires the live Anthropic judge and SPENDS REAL MONEY. Deselect with `-m 'not judge'` to run the rest of the live tier — Layer B retrieval, Qdrant integration — for free, since the embedding cache makes everything else near-zero. Run it when a change touches conflict judgment, the judge prompt, or the check engine; skip it for CLI, rendering or docs work. Measured 2026-09-18: three of these tests cost 8m23s, which is why selecting FEWER tests inside the module saves nothing and excluding the module is the only lever that does.", ]