[project] name = "judgevet" version = "0.10.1" description = "Typed client, CLI and MCP server for TypeSafe's Jev (System One) judgment model" readme = "README.md" license = { file = "LICENSE" } authors = [{ name = "Alberto-Codes", email = "alberto.codes.dev@gmail.com" }] requires-python = ">=3.12" classifiers = [ "Development Status :: 2 - Pre-Alpha", "Environment :: Console", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Typing :: Typed", ] dependencies = [ "httpx>=0.28", "pydantic-settings>=2.4", "structlog>=24.1", "typer>=0.12", ] [project.optional-dependencies] # The MCP server is an inbound adapter, not core. Scripts and CI import the # library without pulling an MCP runtime. # # Floor is 2.2 because 2.2.0 is the tested SDK baseline. Its source supports # both eras (legacy initialize and modern per-request-envelope); judgevet has # exercised only the legacy initialization path (`runner.py` selects the era # from the first request). See the tested SDK source: # https://github.com/modelcontextprotocol/python-sdk/blob/v2.2.0/src/mcp/server/runner.py mcp = ["mcp>=2.2"] [project.scripts] judgevet = "judgevet.adapters.inbound.cli:app" judgevet-mcp = "judgevet.adapters.inbound.mcp_entrypoint:main" [project.urls] Documentation = "https://alberto-codes.github.io/judgevet/" Source = "https://github.com/Alberto-Codes/judgevet" Issues = "https://github.com/Alberto-Codes/judgevet/issues" [build-system] requires = ["uv_build>=0.11.20,<0.12.0"] build-backend = "uv_build" # --------------------------------------------------------------------------- # # PyPI indexes. # --------------------------------------------------------------------------- # [[tool.uv.index]] name = "testpypi" url = "https://test.pypi.org/simple/" publish-url = "https://test.pypi.org/legacy/" explicit = true # --------------------------------------------------------------------------- # # Include the py.typed marker so type checkers see inline annotations. # Run `docs/reference/py-typed-marker.md` for verification steps. # --------------------------------------------------------------------------- # [tool.uv_build] include = ["src/judgevet/py.typed"] [dependency-groups] dev = [ "docvet>=1.16.0", "griffe>=2.3.0", "import-linter>=2.15", "jsonschema>=4.26.0", "markdown>=3.10.3", "markdown-it-py>=4.2.0", "mkdocs>=1.6.1", "mkdocs-gen-files>=0.6.1", "mkdocstrings[python]>=1.0.6", "packaging>=26.3", "pre-commit>=4.6.2", "pytest>=9.1.1", "pytest-cov>=7.1.0", "ruff>=0.16.8", "ty>=0.0.82", "yamllint>=1.38.0", ] # --------------------------------------------------------------------------- # # Ruff — automarket's set (its ADR-0004, inherited from quantfit). # --------------------------------------------------------------------------- # [tool.ruff] line-length = 88 target-version = "py312" exclude = [".venv", "__pycache__", "*.egg-info"] [tool.ruff.lint] extend-select = [ "E402", "E501", "I", "D", "B", "SIM", "UP", "RUF", "PL", "C901", "TRY", "PERF", "S", "G", ] ignore = [ "TRY003", # long messages in raises — validation errors carry context ] [tool.ruff.lint.mccabe] max-complexity = 12 [tool.ruff.lint.pylint] max-args = 7 [tool.ruff.lint.pycodestyle] max-line-length = 100 [tool.ruff.lint.pydocstyle] convention = "google" [tool.ruff.lint.per-file-ignores] "tests/**/*.py" = [ "S101", "D100", "D101", "D102", "D103", "D104", "PLR2004", # magic values are the point of test assertions ] "tests/conftest.py" = [ "PLC0415", # import inside function for lazy loading; moving to module level trips E402 ] "tests/unit/test_secret_guard.py" = [ "S603", # subprocess call for test proof (the guard must be tested end-to-end) ] "scripts/smoke_release_child.py" = [ "S102", # exec(): running extracted docstring examples is the point of this script "BLE001", # except Exception: catching arbitrary failures from example code "S603", # subprocess.run: controlled invocation of judgevet --help "PLC0415", # judgevet imported lazily: --selftest must run where it is NOT installed ] "scripts/smoke_release.py" = [ "S603", # uv and python are invoked by absolute path with a list argv, never a shell ] "scripts/check_commit_msg.py" = [ "S603", # git is invoked by absolute path with a list argv, never a shell ] [tool.ruff.lint.isort] known-first-party = ["judgevet"] [tool.ruff.format] quote-style = "double" docstring-code-format = true # --------------------------------------------------------------------------- # # Pytest # --------------------------------------------------------------------------- # [tool.pytest.ini_options] testpaths = ["tests"] markers = [ "unit: fast, isolated", "contract: fixture suites pinning adapter behaviour — fake/real contract and sync/async equivalence", "live: touches the live Jev API; needs TYPESAFE_API_KEY; never in CI", ] addopts = [ "-ra", "--strict-markers", "--strict-config", "--showlocals", "-m", "not live", ] # --------------------------------------------------------------------------- # # Coverage # --------------------------------------------------------------------------- # [tool.coverage.run] source = ["judgevet"] [tool.coverage.report] fail_under = 90 show_missing = true # --------------------------------------------------------------------------- # # import-linter — the hex map. Inbound drives, outbound implements ports, # domain stays pure. # --------------------------------------------------------------------------- # [tool.importlinter] root_package = "judgevet" include_external_packages = true exclude_type_checking_imports = true [[tool.importlinter.contracts]] name = "Hexagonal layers" type = "layers" layers = [ "judgevet.adapters.inbound", "judgevet.adapters.outbound", "judgevet.ports", "judgevet.domain", ] [[tool.importlinter.contracts]] # The domain is pure: no IO, no serialization, no CLI or server framework. name = "Domain is IO-free" type = "forbidden" source_modules = ["judgevet.domain"] forbidden_modules = [ "json", "pathlib", "os", "io", "logging", "httpx", "typer", "mcp", "structlog", ] [[tool.importlinter.contracts]] # The MCP runtime is an optional extra. Nothing outside its own adapter may # import it, so the library and CLI install without it. name = "MCP stays in its adapter" type = "forbidden" source_modules = [ "judgevet.domain", "judgevet.ports", "judgevet.adapters.outbound", ] forbidden_modules = ["mcp"] # --------------------------------------------------------------------------- # # docvet # --------------------------------------------------------------------------- # [tool.docvet] fail-on = ["enrichment", "freshness", "coverage", "griffe", "presence"] # `scripts/` holds the gate scripts and the live probe. They form no package, # so the mkdocs cross-reference rules cannot apply to them — automarket # excludes its own scripts for the same reason. exclude = ["tests", "scripts"] [tool.docvet.presence] min-coverage = 100.0 # --------------------------------------------------------------------------- # # commit-msg gate # --------------------------------------------------------------------------- # [tool.judgevet.commit-msg] # The email in GIT_AUTHOR_IDENT must match one of these for the commit to be # allowed. A commit by an allowed author succeeds; a commit by any other # author is refused with instructions to use a scratch repository instead. allowed-authors = [ # Both addresses are Alberto's and both appear in his repositories: # judgevet's history uses the first, docvet's uses the second. A list # rather than a single value so a second contributor adds themselves # here rather than editing a script. "alberto.nieto.80@gmail.com", "alberto.codes.dev@gmail.com", # release-please authors the two commits on its own release pull request: # the version bump and the uv.lock refresh. Those are gate output, not # authorship, and without this line the release PR cannot pass the gate # that guards the branch it is asking to merge into. "github-actions[bot]@users.noreply.github.com", ] [[tool.importlinter.contracts]] name = "Policy facade stays pure" type = "forbidden" source_modules = ["judgevet.policy"] forbidden_modules = [ "judgevet.adapters", "judgevet.ports", "json", "pathlib", "os", "io", "logging", "httpx", "typer", "mcp", "structlog", "pydantic_settings", ] [[tool.importlinter.contracts]] name = "Policy JSON decoding does not depend on adapters" type = "forbidden" source_modules = ["judgevet.policy_json", "judgevet._policy_json"] forbidden_modules = ["judgevet.adapters", "judgevet.ports", "mcp"]