feat: implement institutional governance and recovery architecture
This commit is contained in:
@@ -28,10 +28,14 @@ cd "$ROOT"
|
||||
|
||||
GOVOPLAN_CORE_ROOT="$ROOT" PYTHON="$PYTHON" CHECK_TESTCLIENT_DEPRECATIONS=1 bash "$META_ROOT/tools/checks/check-dependency-hygiene.sh"
|
||||
"$PYTHON" "$META_ROOT/tools/checks/check-contracts.py" --no-impact
|
||||
PYTHONDONTWRITEBYTECODE=1 "$PYTHON" "$META_ROOT/tools/checks/check-manifest-shapes.py"
|
||||
PYTHONDONTWRITEBYTECODE=1 "$PYTHON" "$META_ROOT/tools/checks/check-manifest-shapes.py" --require-architecture
|
||||
|
||||
cd "$META_ROOT"
|
||||
"$PYTHON" -m unittest tests.test_deployment_installer
|
||||
"$PYTHON" -m unittest tests.test_capability_fit_evidence
|
||||
"$PYTHON" -m unittest tests.test_configuration_package_artifacts
|
||||
"$PYTHON" -m unittest tests.test_institutional_governance_journey
|
||||
"$PYTHON" -m unittest tests.test_institutional_service_journey
|
||||
cd "$ROOT"
|
||||
|
||||
"$PYTHON" - <<'PY'
|
||||
@@ -81,6 +85,11 @@ PY
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-views/tests
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-dashboard/tests
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-postbox/tests
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-portal/tests
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-forms/tests
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-forms-runtime/tests
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-cases/tests
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-committee/tests
|
||||
"$PYTHON" "$META_ROOT/tools/checks/check-datasource-composition.py"
|
||||
"$PYTHON" -m unittest discover -s /mnt/DATA/git/govoplan-mail/tests
|
||||
"$PYTHON" -m unittest tests.test_api_smoke.ApiSmokeTests.test_mailbox_message_listing_reports_total_count
|
||||
|
||||
@@ -16,6 +16,7 @@ META_ROOT = Path(__file__).resolve().parents[2]
|
||||
MODULE_NAME_PATTERN = re.compile(
|
||||
r"[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*"
|
||||
)
|
||||
REQUIRED_DOCUMENTATION_TYPES = frozenset({"admin", "user"})
|
||||
|
||||
|
||||
def main() -> int:
|
||||
@@ -26,6 +27,14 @@ def main() -> int:
|
||||
default=None,
|
||||
help="Directory containing the GovOPlaN repositories. Defaults to repositories.json default_parent.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--require-architecture",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Reject every module that has not adopted the versioned architecture "
|
||||
"declaration. GovOPlaN release and focused checks enable this gate."
|
||||
),
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
catalog = json.loads((META_ROOT / "repositories.json").read_text(encoding="utf-8"))
|
||||
@@ -90,6 +99,36 @@ def main() -> int:
|
||||
)
|
||||
continue
|
||||
|
||||
documented_types = {
|
||||
documentation_type
|
||||
for topic in manifest.documentation
|
||||
for documentation_type in topic.documentation_types
|
||||
}
|
||||
missing_documentation_types = sorted(REQUIRED_DOCUMENTATION_TYPES - documented_types)
|
||||
if missing_documentation_types:
|
||||
errors.append(
|
||||
f"{repository_name}: module {manifest.id!r} is missing static documentation for "
|
||||
f"{', '.join(missing_documentation_types)}; add manifest DocumentationTopic entries "
|
||||
"even when runtime documentation providers are registered"
|
||||
)
|
||||
continue
|
||||
|
||||
repository_root = manifest_path.parents[3]
|
||||
if manifest.architecture is None:
|
||||
if args.require_architecture:
|
||||
errors.append(
|
||||
f"{repository_name}: module {manifest.id!r} has no architecture declaration"
|
||||
)
|
||||
continue
|
||||
else:
|
||||
errors.extend(
|
||||
_architecture_evidence_errors(
|
||||
repository_name=repository_name,
|
||||
repository_root=repository_root,
|
||||
manifest=manifest,
|
||||
)
|
||||
)
|
||||
|
||||
manifests.append(manifest)
|
||||
|
||||
if errors:
|
||||
@@ -109,6 +148,11 @@ def main() -> int:
|
||||
f"Manifest registry check passed: {len(snapshot.manifests)} manifests "
|
||||
f"from {len(manifest_sources)} source files."
|
||||
)
|
||||
declared = sum(manifest.architecture is not None for manifest in manifests)
|
||||
print(
|
||||
f"Architecture declaration coverage: {declared}/{len(manifests)} modules "
|
||||
f"({(declared / len(manifests) * 100):.1f}%)."
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -120,5 +164,53 @@ def _module_entry_points(pyproject_path: Path) -> dict[str, str]:
|
||||
return {str(name): str(target) for name, target in values.items()}
|
||||
|
||||
|
||||
def _architecture_evidence_errors(
|
||||
*,
|
||||
repository_name: str,
|
||||
repository_root: Path,
|
||||
manifest: object,
|
||||
) -> list[str]:
|
||||
architecture = getattr(manifest, "architecture", None)
|
||||
if architecture is None:
|
||||
return []
|
||||
errors: list[str] = []
|
||||
references = [
|
||||
(f"{item.kind} evidence", item.reference)
|
||||
for item in architecture.evidence
|
||||
]
|
||||
for category in ("migration", "upgrade", "recovery", "security", "operations"):
|
||||
references.extend(
|
||||
(f"{category} documentation", reference)
|
||||
for reference in getattr(architecture.documentation, category)
|
||||
)
|
||||
for label, reference in references:
|
||||
if not _looks_like_repository_reference(reference):
|
||||
continue
|
||||
candidate = (repository_root / reference).resolve()
|
||||
try:
|
||||
candidate.relative_to(repository_root.resolve())
|
||||
except ValueError:
|
||||
errors.append(
|
||||
f"{repository_name}: {label} escapes the repository: {reference!r}"
|
||||
)
|
||||
continue
|
||||
if not candidate.exists():
|
||||
errors.append(
|
||||
f"{repository_name}: {label} does not exist: {reference!r}"
|
||||
)
|
||||
return errors
|
||||
|
||||
|
||||
def _looks_like_repository_reference(reference: str) -> bool:
|
||||
normalized = reference.strip()
|
||||
if not normalized or "://" in normalized:
|
||||
return False
|
||||
return (
|
||||
"/" in normalized
|
||||
or normalized.startswith("README")
|
||||
or normalized.endswith((".md", ".py", ".json", ".yaml", ".yml"))
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
@@ -25,6 +25,13 @@ cd "$ROOT"
|
||||
--core-root "$ROOT"
|
||||
"$PYTHON" "$META_ROOT/tools/checks/check_dependency_boundaries.py"
|
||||
|
||||
cd "$META_ROOT"
|
||||
"$PYTHON" -m unittest tests.test_configuration_package_artifacts
|
||||
PYTHONPATH="$META_ROOT/../govoplan-portal/src:$META_ROOT/../govoplan-forms/src:$META_ROOT/../govoplan-forms-runtime/src:$META_ROOT/../govoplan-cases/src:$ROOT/src${PYTHONPATH:+:$PYTHONPATH}" \
|
||||
"$PYTHON" -m unittest tests.test_institutional_service_journey
|
||||
PYTHONPATH="$META_ROOT/../govoplan-portal/src:$META_ROOT/../govoplan-cases/src:$META_ROOT/../govoplan-committee/src:$META_ROOT/../govoplan-services/src:$META_ROOT/../govoplan-parties/src:$META_ROOT/../govoplan-mandates/src:$META_ROOT/../govoplan-decisions/src:$ROOT/src${PYTHONPATH:+:$PYTHONPATH}" \
|
||||
"$PYTHON" -m unittest tests.test_institutional_governance_journey
|
||||
|
||||
cd "$ROOT/webui"
|
||||
PATH="$ROOT/webui/node_modules/.bin:$NODE_BIN:$PATH" "$NPM" run test:module-capabilities
|
||||
PATH="$ROOT/webui/node_modules/.bin:$NODE_BIN:$PATH" "$NPM" run test:module-permutations
|
||||
|
||||
Reference in New Issue
Block a user