121 lines
4.4 KiB
Markdown
121 lines
4.4 KiB
Markdown
# 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.
|