[project]
name = "mgf-standard"
version = "0.1.61"
description = "The Magogi Foundation engineering-standards corpus, federation contract, and library registry — the source of truth for the mgf-* federation"
readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
authors = [
    { name = "Bassam Alsanie" },
    { name = "mgf-standard contributors" },
]
keywords = [
    "standards",
    "engineering-standards",
    "federation",
    "governance",
    "documentation",
]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Topic :: Documentation",
    "Topic :: Software Development :: Documentation",
    "Typing :: Typed",
]

# The runtime package is a TINY data carrier: it ships the standards
# corpus as wheel data + a typed resolver (standards_dir / read_standard
# / list_standards). It has ZERO runtime dependencies on purpose — it is
# the federation's new leaf-of-leaves, sitting BELOW mgf-common in the
# graph (mgf-common depends on it; it depends on nothing mgf-*). Keeping
# it dependency-free guarantees no runtime import cycle and no transitive
# weight in any downstream consumer.
dependencies = []

[project.optional-dependencies]
dev = [
    "pytest>=8.0,<10",
    "pytest-cov>=5.0,<8",
    "ruff>=0.4,<0.16",
    "mypy>=1.10,<2",
    # Layering enforcement — keeps mgf.standard a leaf (rule DP-01).
    "import-linter>=2.0,<3",
    # NOTE on the federation test runner (TS-23): mgf-standard does
    # NOT dev-depend on `mgf-test-supervisor`. That edge would be
    # mgf-standard -> mgf-test-supervisor, and since
    # mgf-test-supervisor -> mgf-common -> mgf-standard it closes a
    # CYCLE that the REL-13 registry generator would record (it reads
    # mgf-* deps from optional groups too — see gen_mgf_libraries.py
    # _parse_python). This repo's test surface is trivial (the resolver +
    # the moved registry-generator test), so we consciously decline TS-23
    # here and run plain pytest, keeping mgf-standard a PURE LEAF with
    # zero mgf-* deps. Documented as a CFM-04 decline-with-rationale in
    # docs/inprogress/MGF_STANDARDS_CONFORMANCE.md (see also
    # docs/standards/principles/non-adoption-is-valid.md).
]

[project.urls]
Homepage = "https://codeberg.org/magogi-admin/mgf-standard"
Issues = "https://codeberg.org/magogi-admin/mgf-standard/issues"

[build-system]
requires = ["hatchling>=1.21"]
build-backend = "hatchling.build"

# PEP 420 implicit namespace package — ships ``src/mgf/`` WITHOUT a
# top-level ``__init__.py`` so it co-exists with every other
# ``mgf.<sibling>`` under the shared ``mgf`` namespace.
[tool.hatch.build.targets.wheel]
packages = ["src/mgf"]

# Ship the standards corpus as wheel data so
# ``mgf.standard.standards_dir()`` (and, through it,
# ``mgf-common standards <topic>``) works after ``pip install``. Source of
# truth stays in ``docs/standards/``; this copies it into the wheel at
# ``mgf/standard/_standards/`` for importlib.resources lookup. Editable
# installs fall back to the repo-relative ``docs/standards/``.
[tool.hatch.build.targets.wheel.force-include]
"docs/standards" = "mgf/standard/_standards"
# The federation-operations recipes + governance templates. Shipped as data
# so mgf.standard.recipes_dir() / read_recipe() resolve a governance
# recipe (e.g. the /sync federation sync prompt) from the version a consumer
# pins — letting the mgf-common CLI surface it instead of a hand-copied,
# drift-prone command body. Source of truth stays in docs/recipes/.
"docs/recipes" = "mgf/standard/_recipes"
# The machine-readable federation registry, generated by
# scripts/gen_mgf_libraries.py. Shipped as data so mgf.standard.libraries()
# resolves the roster + dependency graph from the version a consumer pins.
"MGF_LIBRARIES.json" = "mgf/standard/_libraries.json"
# The federation CONTRACT (FEDERATION.md, DOC-17) + the foundation DESIGN
# papers (docs/design/, the federation-pattern paper). Shipped as data so
# mgf.standard.federation() / design_dir() resolve the WHOLE federation
# picture — contract + design, not only the rules — from the version a
# consumer pins, so an agent can read it offline via the CLI without a
# checkout. Source of truth stays in FEDERATION.md / docs/design/.
"FEDERATION.md" = "mgf/standard/_FEDERATION.md"
"docs/design" = "mgf/standard/_design"
# The canonical standardized git/CI scaffold artifacts — the AG-03
# prepare-commit-msg hook + the check_commit_trailers.py CI checker. Shipped as
# data so mgf.standard.read_scaffold() resolves the canonical text, letting
# mgf-common catch-up write it into a repo instead of a drift-prone copy.
# Source of truth stays in .githooks/ + scripts/.
".githooks/prepare-commit-msg" = "mgf/standard/_scaffold/prepare-commit-msg"
"scripts/check_commit_trailers.py" = "mgf/standard/_scaffold/check_commit_trailers.py"

# ----------------------------------------------------------------------
# Ruff — lint + format
# ----------------------------------------------------------------------
[tool.ruff]
line-length = 100
target-version = "py311"
src = ["src", "tests", "scripts"]
extend-exclude = [
    ".venv", "build", "dist",
    # Byte-exact template instance (drift-checked == templates/verify_codeberg.py); its S310 noqa +
    # comments are correct for S-selecting consumer repos — not mgf-standard's (no-S) lint concern.
    "scripts/verify_codeberg.py",
]

[tool.ruff.lint]
select = [
    "E", "W", "F", "I", "N", "UP", "B", "A", "C4", "SIM", "RET", "RUF",
    "PTH", "TCH", "FA",
]
ignore = [
    "E501",   # line length — handled by formatter
    "SIM108", # ternary-vs-if (subjective)
]

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["N802", "PLR2004"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"

# ----------------------------------------------------------------------
# mypy — strict
# ----------------------------------------------------------------------
[tool.mypy]
python_version = "3.11"
mypy_path = "src"
packages = ["mgf.standard"]
explicit_package_bases = true
namespace_packages = true
strict = true
warn_unused_ignores = true
warn_redundant_casts = true
warn_return_any = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
no_implicit_optional = true
show_error_codes = true
pretty = true

# ----------------------------------------------------------------------
# pytest
# ----------------------------------------------------------------------
[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = ["-ra", "--strict-markers", "--strict-config", "--showlocals"]
filterwarnings = ["error"]
markers = [
    "smoke: minimal post-install sanity (fast; no fixtures); tier-0",
    "integration: cross-module / cross-repo integration test",
]

[tool.coverage.run]
source = ["src/mgf"]
branch = true

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "raise NotImplementedError",
    "if TYPE_CHECKING:",
]
show_missing = true
fail_under = 80

# mgf-fed (woodpecker generator) CI carve-outs. In [tool.mgf-fed], NOT [tool.mgf]
# — the latter is mgf-common's MgfSettings config source (extra="forbid").
[tool.mgf-fed]
vuln_ignore = ["CVE-2025-71176"]
