intermediate commit
This commit is contained in:
120
docs/MODULE_CONTRACTS_AND_INSTALLS.md
Normal file
120
docs/MODULE_CONTRACTS_AND_INSTALLS.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user