From b5f8db1aa592bbc640f7ef2b611ef53ccc13c633 Mon Sep 17 00:00:00 2001 From: Richard Abrich Date: Fri, 28 Aug 2026 11:55:58 -0400 Subject: [PATCH] chore(lifecycle): repin the Production lifecycle source to openadapt-ops main The pinned openadapt-ops bytes moved, so --require-current-source refuses the pin and the validate job fails on main and on every open PR. Repin to openadapt-ops 9246994b5d2645aed3fe9bfb25163fdce507a27f and rerender. The new projection also widens the canonical file inventory it binds. It now carries evidence-registry.json, its schema, and its validator, and the canonical validator at OpenAdaptAI/.github 4d2a8265 imports the registry validator by bare module name and reads evidence-registry.json beside the policy. The consumer holds that inventory as an exact closed set, so it refused with "canonical lifecycle file inventory is not exact" until the three files were named here too. They are fetched and digest-checked like every other canonical input, and the set stays exact rather than permissive. The materialized script directory is put on sys.path only for the duration of the validator call, then removed along with any module imported from it. The rendered block does not change: the live record still carries all seven targets with no active admission, so the README keeps the positive qualification contract. The bytes now pinned are identical to the live record served at https://docs.openadapt.ai/production-lifecycle.json. Co-Authored-By: Claude Opus 5 --- production-lifecycle-source.json | 10 +++++----- scripts/render_readme_maturity.py | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/production-lifecycle-source.json b/production-lifecycle-source.json index e8fb4b71a..190cb13c3 100644 --- a/production-lifecycle-source.json +++ b/production-lifecycle-source.json @@ -1,17 +1,17 @@ { "schema_version": "openadapt.production-readme-source/v1", "repository": "OpenAdaptAI/openadapt-ops", - "source_commit": "ef29c08ad27b05637abc75fc3ceb3e026821fdda", + "source_commit": "9246994b5d2645aed3fe9bfb25163fdce507a27f", "files": { "projection": { "path": "docs/production-lifecycle.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/openadapt-ops/ef29c08ad27b05637abc75fc3ceb3e026821fdda/docs/production-lifecycle.json", - "sha256": "sha256:6c3819c7c68acb1bea8045fa1f059721e535c960707998b8705438f8541fb3d7" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/openadapt-ops/9246994b5d2645aed3fe9bfb25163fdce507a27f/docs/production-lifecycle.json", + "sha256": "sha256:7acc2884b5f70bd94521288d07e0567269501d679cf52f1f216cbaf7b81e6a16" }, "schema": { "path": "docs/schemas/production-lifecycle-public.schema.json", - "url": "https://raw.githubusercontent.com/OpenAdaptAI/openadapt-ops/ef29c08ad27b05637abc75fc3ceb3e026821fdda/docs/schemas/production-lifecycle-public.schema.json", - "sha256": "sha256:f681a9bb47ff727b409df994e69e62e7c45b38e912911af4c99eca7dd3be40eb" + "url": "https://raw.githubusercontent.com/OpenAdaptAI/openadapt-ops/9246994b5d2645aed3fe9bfb25163fdce507a27f/docs/schemas/production-lifecycle-public.schema.json", + "sha256": "sha256:c6db48d6089314d745c2cf1af7bced511f359c7fee9cacc2e35b758fc22d7073" } } } diff --git a/scripts/render_readme_maturity.py b/scripts/render_readme_maturity.py index bb5137ebc..4494c9e25 100644 --- a/scripts/render_readme_maturity.py +++ b/scripts/render_readme_maturity.py @@ -47,6 +47,9 @@ "evidence_manifest_schema": ( "schemas/production-lifecycle-evidence-manifest.schema.json" ), + "evidence_registry": "evidence-registry.json", + "evidence_registry_schema": "schemas/evidence-registry.schema.json", + "evidence_registry_validator": "scripts/validate_evidence_registry.py", "evidence_summary_schema": ( "schemas/production-lifecycle-evidence-summary.schema.json" ), @@ -307,6 +310,14 @@ def _run_canonical_validator(inputs: Mapping[str, bytes]) -> dict[str, str]: if spec is None or spec.loader is None: raise MaturityError("canonical lifecycle validator cannot be loaded") module = importlib.util.module_from_spec(spec) + # The validator imports its sibling scripts by bare module name, so the + # materialized script directory has to be importable. It is prepended + # for the duration of the call and removed again, together with every + # module the validator imported from it, so nothing leaks into the + # importing process. + script_directory = str(validator_path.parent) + sys.path.insert(0, script_directory) + before = set(sys.modules) try: spec.loader.exec_module(module) active = module.validate_files(root) @@ -314,6 +325,15 @@ def _run_canonical_validator(inputs: Mapping[str, bytes]) -> dict[str, str]: raise MaturityError( f"canonical lifecycle validator refused: {exc}" ) from exc + finally: + for name in set(sys.modules) - before: + origin = getattr(sys.modules[name], "__file__", None) + if origin is not None and origin.startswith(script_directory): + del sys.modules[name] + try: + sys.path.remove(script_directory) + except ValueError: + pass if not isinstance(active, dict) or not all( isinstance(key, str) and isinstance(value, str) for key, value in active.items() ):