# Module Contracts and Install Boundaries ## Why Some Changes Require `pip install` GovOPlaN discovers runtime modules through Python package entry points in the `govoplan.modules` group. Core reads those entry points with `importlib.metadata.entry_points()`, imports the configured manifest factory, and then builds the module registry from the returned `ModuleManifest`. That means the Python environment must know that a package exists before core can discover it. In development, `requirements-dev.txt` installs modules with `-e ../govoplan-module`. With editable installs: - normal Python source changes are picked up after the process reloads; - manifest code changes are picked up after the process reloads; - new imports inside an already installed package are picked up after reload. `pip install` is still needed when package metadata changes: - a repository was not installed in the environment before; - a `pyproject.toml` entry point is added, renamed, or removed; - package dependencies change; - optional extras change; - package names or import roots change; - console scripts or other installed metadata change; - release installs need a different tag, wheel, or source ref. The same principle applies to WebUI packages: source changes are local during development, but `package.json` dependency or export changes require an install step so the consuming app sees the correct package metadata. ## Current Contract Mechanism Modules already announce contracts through `ModuleManifest`: - `dependencies` - `optional_dependencies` - `required_capabilities` - `optional_capabilities` - `provides_interfaces` - `requires_interfaces` - `capability_factories` - permissions and role templates - route factories, migrations, docs, lifecycle hooks, and frontend metadata Core validates the active manifest graph when it builds the registry. Release tooling can inspect those manifests to calculate compatibility and migration impact. ## What Core Can and Cannot Pick Up Automatically Core can pick up contract changes automatically after reload when the changed module package is already installed and importable. Core cannot discover a new module or new entry point that has not been installed into the environment, because there is no distribution metadata to enumerate. Core also cannot notify every module about a contract change at edit time by itself. The runtime registry is built from installed/importable packages. The right place for cross-module announcement is the meta repo tooling and CI: - scan manifests across all repositories; - build an impact graph from provided/required interfaces and capabilities; - run affected module tests; - post Gitea issue/release notes for affected modules; - block releases when a required interface is missing or incompatible. ## Mitigation Strategy Use three layers: 1. Editable development environment. Keep `requirements-dev.txt` in the meta repo as the one workspace installer. Source edits then need process reloads, not repeated full installs. 2. Versioned runtime contracts. Keep adding and tightening `provides_interfaces`, `requires_interfaces`, and capability protocols. Treat interface names and versions as public module contracts. 3. Meta-level contract audit. The meta repo statically reads `src/**/backend/manifest.py` files across all repositories and validates provided/required interface ranges without importing the packages. CI blocks missing or incompatible required interfaces before release installs are attempted. Run the static graph check with: ```sh ./tools/checks/check-contracts.sh ``` Use `--json` when the release console or another automation needs structured provider/consumer impact data. ## Practical Rule Do not run `pip install` for every code edit. Run it when package metadata or the set of installed packages changes. For normal development: ```sh ./.venv/bin/python tools/repo/sync-python-environment.py --requirements requirements-dev.txt --python ./.venv/bin/python ``` Then restart the server when Python code or manifests change. The development launcher runs this helper automatically by default; set `GOVOPLAN_AUTO_SYNC_PYTHON=0` to disable that preflight. For release validation: ```sh ./.venv/bin/python tools/repo/sync-python-environment.py --requirements requirements-release.txt --python ./.venv/bin/python ``` That install is necessary because the release environment intentionally resolves tagged package refs, not local editable source trees.