diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75c0626..5001cea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,10 @@ jobs: run: python3 scripts/check_orro_release_manifest.py --self-test - name: Compatibility matrix self-test run: python3 scripts/check_compatibility_matrix.py --self-test + - name: Check ORRO release state + run: python3 scripts/check_orro_release_state.py + - name: Release state self-test + run: python3 scripts/check_orro_release_state.py --self-test - name: Check ORRO packaging decision run: python3 scripts/check_orro_packaging_decision.py - name: Check ORRO fallback policy @@ -45,7 +49,7 @@ jobs: - name: Check ORRO wrapper distribution run: python3 scripts/check_orro_wrapper_distribution.py --json --allow-network - name: Compile scripts - run: python3 -m py_compile scripts/check_no_bidi_controls.py scripts/check_orro_repo_contract.py scripts/check_orro_language_boundaries.py scripts/check_orro_assurance_contract_fixtures.py scripts/check_orro_release_manifest.py scripts/check_compatibility_matrix.py scripts/check_orro_packaging_decision.py scripts/check_orro_fallback_policy.py scripts/check_orro_command_migration.py scripts/check_orro_command_migration_dry_run.py scripts/check_orro_wrapper.py scripts/check_orro_wrapper_install.py scripts/check_orro_wrapper_distribution.py scripts/orro_e2e_smoke.py scripts/update_orro_engine_lock.py scripts/bootstrap_orro.py src/orro_wrapper/__init__.py src/orro_wrapper/__main__.py src/orro_wrapper/cli.py + run: python3 -m py_compile scripts/check_no_bidi_controls.py scripts/check_orro_repo_contract.py scripts/check_orro_language_boundaries.py scripts/check_orro_assurance_contract_fixtures.py scripts/check_orro_release_manifest.py scripts/check_compatibility_matrix.py scripts/check_orro_release_state.py scripts/check_orro_packaging_decision.py scripts/check_orro_fallback_policy.py scripts/check_orro_command_migration.py scripts/check_orro_command_migration_dry_run.py scripts/check_orro_wrapper.py scripts/check_orro_wrapper_install.py scripts/check_orro_wrapper_distribution.py scripts/orro_e2e_smoke.py scripts/update_orro_engine_lock.py scripts/bootstrap_orro.py src/orro_wrapper/__init__.py src/orro_wrapper/__main__.py src/orro_wrapper/cli.py - name: Bidi control scanner self-test run: python3 scripts/check_no_bidi_controls.py --self-test - name: Install ORRO wrapper package (editable) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a8dc18..88407a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,3 +64,5 @@ jobs: path: dist/ - name: Publish to PyPI (Trusted Publishing) uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 + with: + skip-existing: true diff --git a/README.md b/README.md index 2cbbed2..c99c065 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ Today: - ORRO is the product and workflow surface for observed run and review. - This repository keeps ORRO docs, product boundary, locks, thin wrapper package metadata, assurance contract checks, and integration-surface policy. - The runnable `orro` command is ORRO-owned and delegates to witnessd. -- The `orro` package is published on PyPI (0.0.x is live); this repository now - sources version 0.1.0. +- `orro` 0.1.0 is published on PyPI, and this repository is its canonical + source. A normal `pip install orro` installs the 0.1.0 package, whose metadata + declares `witnessd>=2.3.2`. - The local wrapper package exposes both `orro` and `orro-wrapper`. Current focus: @@ -29,8 +30,8 @@ Current focus: ## Can I use ORRO today? -Yes. The `orro` package is published on PyPI, and development dogfood can also -run directly against pinned Depone and witnessd checkouts. +Yes. `orro` 0.1.0 is published on PyPI, and development dogfood can also run +directly against pinned Depone and witnessd checkouts. Current split: @@ -95,7 +96,8 @@ Engine-lock update discipline is documented in validated engine pairs are listed in [`docs/compatibility-matrix.md`](docs/compatibility-matrix.md). The `orro` package is published on PyPI; this repository sources 0.1.0, whose publication -is a separate release step. +was completed as a separate release step. Repository metadata changes do not +republish or rewrite that artifact. Use `scripts/update_orro_engine_lock.py` for future pin updates so the e2e engine lock, release manifest, and compatibility matrix stay aligned. The diff --git a/docs/bootstrap.md b/docs/bootstrap.md index 0e646d4..513ed37 100644 --- a/docs/bootstrap.md +++ b/docs/bootstrap.md @@ -79,8 +79,8 @@ python3 scripts/bootstrap_orro.py \ --json ``` -The `orro` package is published on PyPI; 0.0.x is live and this repository now -sources 0.1.0 with `witnessd>=2.3.2`. +`orro` 0.1.0 is published on PyPI, this repository is its canonical source, and +the package metadata declares `witnessd>=2.3.2`. The wrapper distribution smoke is separate from bootstrap: diff --git a/docs/compatibility-matrix.md b/docs/compatibility-matrix.md index 7285f05..cd77644 100644 --- a/docs/compatibility-matrix.md +++ b/docs/compatibility-matrix.md @@ -5,16 +5,21 @@ proof, not verifier truth, not approval, and not assurance. It does not replace Depone or witnessd engine tests, and it does not imply that unlisted future commits are compatible. +`orro` 0.1.0 is published on PyPI, and this repository is its canonical source. + ```text Depone verifies; witnessd executes; ORRO exposes the workflow. ``` | Matrix entry | ORRO repo commit | witnessd commit | Depone commit | e2e status | notes | | --- | --- | --- | --- | --- | --- | -| depone-n-witnessd-n | n/a | `9715ea278242ac920a721f6079aad6cebf49cd40` | `44eb26ef84a239d4d04570d763c549cee05839ee` | pass | Current locally validated engine pair; witnessd commit is published on the configured remote. | +| depone-n-witnessd-n | n/a | `ae260fb9c020a0bf9e00b60d4053a8fcd7adc8a9` | `e1b020b8300c569e35f771c9f84f6998f99a86ce` | pass | Current locally validated engine pair; witnessd v2.3.3. | | depone-n-witnessd-n-1 | n/a | `d601fa86fd8b658e8f4a2cf7fa4b35bde26c554d` | `0e86a55ad10c464d35308a7b6315860a47bcf8a5` | warn | Declared downgrade row, not a release lock. | | depone-n-1-witnessd-n | n/a | `90d4bc77b71cecf464f1f8a820d9fe17b308211f` | `b203130b5720c6ef6fc9b7492f085ac5ee733786` | warn | Declared downgrade row, not a release lock. | -| orro-rc-locked-triplet | `136c07d714ca61e999097de69859515961f9638b` | `5db87ac975452d9fed233306645e179b78087d68` | `e1b020b8300c569e35f771c9f84f6998f99a86ce` | pass | Matches `engine-lock/orro-e2e-engine-lock.json`, `release/orro-release-manifest.v0.json`, and `release/compatibility-matrix.v0.json`. | +| orro-rc-locked-triplet | `136c07d714ca61e999097de69859515961f9638b` | `ae260fb9c020a0bf9e00b60d4053a8fcd7adc8a9` | `e1b020b8300c569e35f771c9f84f6998f99a86ce` | pass | Matches witnessd v2.3.3, `engine-lock/orro-e2e-engine-lock.json`, `release/orro-release-manifest.v0.json`, and `release/compatibility-matrix.v0.json`. | + +The locked witnessd revision is release `v2.3.3` (source version 2.3.3). It +satisfies the published ORRO package requirement `witnessd>=2.3.2`. Related files: diff --git a/docs/engine-lock-update-process.md b/docs/engine-lock-update-process.md index 852efb3..000a2c7 100644 --- a/docs/engine-lock-update-process.md +++ b/docs/engine-lock-update-process.md @@ -42,6 +42,7 @@ checkout or open an intentional engine-lock update PR. ```bash python3 scripts/update_orro_engine_lock.py \ --witnessd-commit <40-hex-witnessd-commit> \ + --witnessd-version \ --depone-commit <40-hex-depone-commit> ``` diff --git a/docs/install.md b/docs/install.md index 539629a..96fa5a8 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,17 +1,16 @@ # Install -The `orro` package is published on PyPI (0.0.x is live). This repository now -sources 0.1.0, whose metadata declares `witnessd>=2.3.2`; publishing that new -version is a separate release step. The package exposes `orro` and the -`orro-wrapper` compatibility alias. +`orro` 0.1.0 is published on PyPI, and this repository is its canonical source. +The package metadata declares `witnessd>=2.3.2`, and the package exposes `orro` +plus the `orro-wrapper` compatibility alias. ```bash python3 -m pip install orro ``` -Until 0.1.0 is published, that command installs the live 0.0.x release. -Development dogfood may instead use the source checkout and pinned engine -repositories directly. +That command installs `orro` 0.1.0 and resolves its `witnessd>=2.3.2` +dependency. Development dogfood may instead use the source checkout and pinned +engine repositories directly. Current development layout uses the engine repositories directly: diff --git a/docs/packaging-decision.md b/docs/packaging-decision.md index 0f0efbe..1737a69 100644 --- a/docs/packaging-decision.md +++ b/docs/packaging-decision.md @@ -12,9 +12,8 @@ ORRO will remain product/distribution/wrapper only while Depone and witnessd stay separate engine repositories. Current command source is the ORRO-owned `orro` console script, which delegates -to witnessd. The `orro` package is published on PyPI (0.0.x is live), and this -repository now sources 0.1.0 with a `witnessd>=2.3.2` dependency. Publishing -0.1.0 is a separate approved release step. +to witnessd. `orro` 0.1.0 is published on PyPI, this repository is its canonical +source, and its package metadata declares `witnessd>=2.3.2`. The package remains a thin wrapper that delegates to the existing engine command surface. It must contain no engine code. @@ -51,9 +50,9 @@ The ORRO repository must not contain: The local wheel distribution smoke verifies that the built wheel exposes `orro` and `orro-wrapper`, and contains no engine packages or engine implementation files. -3. Published package: current phase. `orro` 0.0.x is live on PyPI, this - repository sources 0.1.0, and any new release still requires pinned-engine - e2e, boundary checks, bootstrap checks, and release metadata to remain green. +3. Published package: current phase. `orro` 0.1.0 is published on PyPI from this + repository. Any new release still requires pinned-engine e2e, boundary + checks, bootstrap checks, and release metadata to remain green. 4. Command ownership: current phase. `orro = orro_wrapper.cli:main` and `orro-wrapper = orro_wrapper.cli:main` are committed package metadata. Both diff --git a/docs/pinned-engine-fallback.md b/docs/pinned-engine-fallback.md index 7a9abac..3e2a8c6 100644 --- a/docs/pinned-engine-fallback.md +++ b/docs/pinned-engine-fallback.md @@ -67,5 +67,6 @@ mismatched engines and point to bootstrap/setup commands, but it must not auto-select different engine commits or rewrite the engine lock outside an intentional update PR. -The `orro` package is published on PyPI; 0.0.x is live and this repository now -sources 0.1.0 with `witnessd>=2.3.2`. +`orro` 0.1.0 is published on PyPI, this repository is its canonical source, and +the package metadata declares `witnessd>=2.3.2`. The current engine lock pins +witnessd 2.3.3 at tag `v2.3.3`, satisfying that declared dependency. diff --git a/docs/repository-strategy.md b/docs/repository-strategy.md index 357c901..5f767ba 100644 --- a/docs/repository-strategy.md +++ b/docs/repository-strategy.md @@ -31,8 +31,8 @@ witnessd-hosted engine surface. The release manifest and compatibility matrix are product/distribution metadata. They record which pinned engine pair has passed ORRO e2e CI, but they are not proof, not verifier truth, not approval, and not assurance. Engine-lock -update process is intentional and PR-reviewed. The `orro` package is published -on PyPI; 0.0.x is live and this repository now sources 0.1.0. +update process is intentional and PR-reviewed. `orro` 0.1.0 is published on +PyPI, and this repository is its canonical source. The packaging decision in `docs/packaging-decision.md` and `packaging/wrapper-package-plan.v0.json` is also product metadata, not package diff --git a/docs/thin-wrapper-plan.md b/docs/thin-wrapper-plan.md index 2b117b4..ca17471 100644 --- a/docs/thin-wrapper-plan.md +++ b/docs/thin-wrapper-plan.md @@ -1,8 +1,7 @@ # Thin Wrapper Plan -The ORRO repository is the canonical source of the published `orro` -distribution. PyPI has live 0.0.x releases; this repository now sources 0.1.0 -and declares `witnessd>=2.3.2`. +`orro` 0.1.0 is published on PyPI, and the ORRO repository is its canonical +source. The package metadata declares `witnessd>=2.3.2`. ## Current State diff --git a/docs/thin-wrapper.md b/docs/thin-wrapper.md index c7574ff..49eaa09 100644 --- a/docs/thin-wrapper.md +++ b/docs/thin-wrapper.md @@ -46,9 +46,8 @@ the engine command the operator explicitly invoked. The wrapper only delegates. ## Relationship To Packaging -This repository is the canonical source of the published `orro` package. PyPI -has live 0.0.x releases; this repository sources 0.1.0 and declares -`witnessd>=2.3.2`. +`orro` 0.1.0 is published on PyPI, and this repository is its canonical source. +The package metadata declares `witnessd>=2.3.2`. Package work must keep: diff --git a/docs/wrapper-distribution.md b/docs/wrapper-distribution.md index 9999bb6..dfcb793 100644 --- a/docs/wrapper-distribution.md +++ b/docs/wrapper-distribution.md @@ -57,6 +57,5 @@ This is local distribution smoke only. It builds and installs a local wheel, but it does not publish a package, upload artifacts, create a marketplace release, or claim verifier truth. -The `orro` package is published on PyPI. PyPI has live 0.0.x releases, while -this repository sources 0.1.0 with `witnessd>=2.3.2`; publishing 0.1.0 is a -separate approved step. +`orro` 0.1.0 is published on PyPI, this repository is its canonical source, and +the package metadata declares `witnessd>=2.3.2`. diff --git a/engine-lock/orro-e2e-engine-lock.json b/engine-lock/orro-e2e-engine-lock.json index a345301..2772b5e 100644 --- a/engine-lock/orro-e2e-engine-lock.json +++ b/engine-lock/orro-e2e-engine-lock.json @@ -3,8 +3,9 @@ "schema_version": "1.0", "witnessd": { "repository": "Moonweave-Systems/witnessd", - "commit": "5db87ac975452d9fed233306645e179b78087d68", - "ref_name": "5db87ac975452d9fed233306645e179b78087d68" + "commit": "ae260fb9c020a0bf9e00b60d4053a8fcd7adc8a9", + "version": "2.3.3", + "ref_name": "v2.3.3" }, "depone": { "repository": "Moonweave-Systems/Depone", diff --git a/packaging/wrapper-package-plan.v0.json b/packaging/wrapper-package-plan.v0.json index 0b6ffd1..ca2e3be 100644 --- a/packaging/wrapper-package-plan.v0.json +++ b/packaging/wrapper-package-plan.v0.json @@ -5,7 +5,7 @@ "package_goal": "single user-facing ORRO install that prepares and invokes pinned Depone and witnessd engines", "distribution_name": "orro", "source_version": "0.1.0", - "published_pypi_versions": "0.0.x", + "published_pypi_versions": "0.1.0", "current_command_source": "ORRO-owned orro console script", "published_package": true, "phases": [ @@ -30,7 +30,7 @@ { "phase": "published-package", "status": "current", - "description": "Record that orro 0.0.x is live on PyPI and this repository now sources 0.1.0; publishing 0.1.0 remains a separate approved release step.", + "description": "Record that orro 0.1.0 is published on PyPI and this repository is its canonical source.", "contains_engine_logic": false } ], diff --git a/release/compatibility-matrix.v0.json b/release/compatibility-matrix.v0.json index 18df99a..0f57e6e 100644 --- a/release/compatibility-matrix.v0.json +++ b/release/compatibility-matrix.v0.json @@ -10,9 +10,10 @@ "id": "depone-n-witnessd-n", "type": "engine-pair", "depone_commit": "e1b020b8300c569e35f771c9f84f6998f99a86ce", - "witnessd_commit": "5db87ac975452d9fed233306645e179b78087d68", + "witnessd_commit": "ae260fb9c020a0bf9e00b60d4053a8fcd7adc8a9", "status": "pass", - "notes": "Current locally validated engine pair; witnessd commit is published on the configured remote." + "notes": "Current locally validated engine pair; witnessd commit is published on the configured remote.", + "witnessd_version": "2.3.3" }, { "id": "depone-n-witnessd-n-1", @@ -35,11 +36,12 @@ "type": "product-triplet", "orro_commit": "136c07d714ca61e999097de69859515961f9638b", "depone_commit": "e1b020b8300c569e35f771c9f84f6998f99a86ce", - "witnessd_commit": "5db87ac975452d9fed233306645e179b78087d68", + "witnessd_commit": "ae260fb9c020a0bf9e00b60d4053a8fcd7adc8a9", "status": "pass", "engine_lock": "engine-lock/orro-e2e-engine-lock.json", "release_manifest": "release/orro-release-manifest.v0.json", - "notes": "ORRO release-candidate base with the updated immutable engine triplet." + "notes": "ORRO release-candidate base with the updated immutable engine triplet.", + "witnessd_version": "2.3.3" } ], "boundary": { diff --git a/release/orro-release-manifest.v0.json b/release/orro-release-manifest.v0.json index d22d685..aa44a8f 100644 --- a/release/orro-release-manifest.v0.json +++ b/release/orro-release-manifest.v0.json @@ -10,7 +10,8 @@ "engines": { "witnessd": { "repository": "Moonweave-Systems/witnessd", - "commit": "5db87ac975452d9fed233306645e179b78087d68", + "commit": "ae260fb9c020a0bf9e00b60d4053a8fcd7adc8a9", + "version": "2.3.3", "role": "execution engine" }, "depone": { diff --git a/scripts/check_compatibility_matrix.py b/scripts/check_compatibility_matrix.py index a97a49c..bebeace 100644 --- a/scripts/check_compatibility_matrix.py +++ b/scripts/check_compatibility_matrix.py @@ -20,6 +20,7 @@ RELEASE_MANIFEST_PATH = ROOT / "release" / "orro-release-manifest.v0.json" DOC_MATRIX_PATH = ROOT / "docs" / "compatibility-matrix.md" COMMIT_RE = re.compile(r"^[0-9a-f]{40}$") +VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$") FORBIDDEN_REFS = {"main", "master", "HEAD"} @@ -133,6 +134,10 @@ def validate_matrix_shape(matrix: dict[str, Any]) -> dict[str, dict[str, Any]]: require_commit(f"{entry_id}.orro_commit", entry.get("orro_commit")) if entry.get("status") not in {"pass", "warn"}: fail(f"{entry_id}.status must be pass or warn") + for entry_id in ("depone-n-witnessd-n", "orro-rc-locked-triplet"): + version = by_id[entry_id].get("witnessd_version") + if not isinstance(version, str) or VERSION_RE.fullmatch(version) is None: + fail(f"{entry_id}.witnessd_version must be an X.Y.Z version") return by_id @@ -152,6 +157,10 @@ def validate_lock_and_manifest(by_id: dict[str, dict[str, Any]]) -> None: fail("engine-lock witnessd commit must match depone-n-witnessd-n") if lock["depone"].get("commit") != current["depone_commit"]: fail("engine-lock Depone commit must match depone-n-witnessd-n") + if lock["witnessd"].get("version") != current["witnessd_version"]: + fail("engine-lock witnessd version must match depone-n-witnessd-n") + if lock["witnessd"].get("ref_name") != f"v{current['witnessd_version']}": + fail("engine-lock witnessd ref_name must match depone-n-witnessd-n version") engines = manifest.get("engines") product = manifest.get("product") if not isinstance(engines, dict) or not isinstance(product, dict): @@ -160,6 +169,8 @@ def validate_lock_and_manifest(by_id: dict[str, dict[str, Any]]) -> None: fail("release manifest product.commit must match ORRO triplet") if engines.get("witnessd", {}).get("commit") != triplet["witnessd_commit"]: fail("release manifest witnessd commit must match ORRO triplet") + if engines.get("witnessd", {}).get("version") != triplet["witnessd_version"]: + fail("release manifest witnessd version must match ORRO triplet") if engines.get("depone", {}).get("commit") != triplet["depone_commit"]: fail("release manifest Depone commit must match ORRO triplet") @@ -173,11 +184,12 @@ def validate_doc_matrix(by_id: dict[str, dict[str, Any]]) -> None: "orro-rc-locked-triplet", ): entry = by_id[entry_id] - if entry_id not in text: + row = next((line for line in text.splitlines() if line.startswith(f"| {entry_id} |")), None) + if row is None: fail(f"docs compatibility matrix missing {entry_id}") - for key in ("orro_commit", "witnessd_commit", "depone_commit"): + for key in ("orro_commit", "witnessd_commit", "witnessd_version", "depone_commit"): value = entry.get(key) - if isinstance(value, str) and value not in text: + if isinstance(value, str) and value not in row: fail(f"docs compatibility matrix missing {entry_id}.{key}") @@ -220,10 +232,10 @@ def self_test() -> int: "not_verifier_truth": True, "not_package_publish": True, "entries": [ - {"id": "depone-n-witnessd-n", "depone_commit": "1" * 40, "witnessd_commit": "2" * 40, "status": "pass"}, + {"id": "depone-n-witnessd-n", "depone_commit": "1" * 40, "witnessd_commit": "2" * 40, "witnessd_version": "2.3.3", "status": "pass"}, {"id": "depone-n-witnessd-n-1", "depone_commit": "1" * 40, "witnessd_commit": "3" * 40, "status": "warn"}, {"id": "depone-n-1-witnessd-n", "depone_commit": "4" * 40, "witnessd_commit": "2" * 40, "status": "warn"}, - {"id": "orro-rc-locked-triplet", "orro_commit": "5" * 40, "depone_commit": "1" * 40, "witnessd_commit": "2" * 40, "status": "pass"}, + {"id": "orro-rc-locked-triplet", "orro_commit": "5" * 40, "depone_commit": "1" * 40, "witnessd_commit": "2" * 40, "witnessd_version": "2.3.3", "status": "pass"}, ], "boundary": { "approves_merge": False, @@ -234,13 +246,22 @@ def self_test() -> int: } validate_matrix_shape(matrix) matrix["entries"][0]["witnessd_commit"] = "main" + try: + with contextlib.redirect_stderr(io.StringIO()): + validate_matrix_shape(matrix) + except SystemExit: + matrix["entries"][0]["witnessd_commit"] = "2" * 40 + else: + print("self-test failed: mutable ref was accepted", file=sys.stderr) + return 1 + matrix["entries"][0]["witnessd_version"] = "latest" try: with contextlib.redirect_stderr(io.StringIO()): validate_matrix_shape(matrix) except SystemExit: print("ORRO compatibility matrix self-test: pass") return 0 - print("self-test failed: mutable ref was accepted", file=sys.stderr) + print("self-test failed: invalid witnessd version was accepted", file=sys.stderr) return 1 diff --git a/scripts/check_orro_fallback_policy.py b/scripts/check_orro_fallback_policy.py index 436b67a..1406adf 100644 --- a/scripts/check_orro_fallback_policy.py +++ b/scripts/check_orro_fallback_policy.py @@ -100,7 +100,7 @@ def check_docs() -> None: require_contains("fallback docs", text, "intentional engine-lock update PR") require_contains("fallback docs", text, "proofrun") require_contains("fallback docs", text, "proofcheck") - require_contains("fallback docs", text, "The `orro` package is published on PyPI") + require_contains("fallback docs", text, "`orro` 0.1.0 is published on PyPI") def main() -> int: diff --git a/scripts/check_orro_packaging_decision.py b/scripts/check_orro_packaging_decision.py index 53ee350..b07d8a7 100644 --- a/scripts/check_orro_packaging_decision.py +++ b/scripts/check_orro_packaging_decision.py @@ -118,7 +118,7 @@ def check_docs() -> None: require_contains("packaging decision doc", text, "not package publish") require_contains("packaging decision doc", text, "witnessd-hosted") require_contains("packaging decision doc", text, "no engine code") - require_contains("packaging decision doc", text, "The `orro` package is published on PyPI") + require_contains("packaging decision doc", text, "`orro` 0.1.0 is published on PyPI") require_contains("packaging decision doc", text, "witnessd>=2.3.2") require_contains("packaging decision doc", text, "proofrun") require_contains("packaging decision doc", text, "proofcheck") diff --git a/scripts/check_orro_release_manifest.py b/scripts/check_orro_release_manifest.py index 4b31d59..8946bfc 100644 --- a/scripts/check_orro_release_manifest.py +++ b/scripts/check_orro_release_manifest.py @@ -18,6 +18,7 @@ MANIFEST_PATH = ROOT / "release" / "orro-release-manifest.v0.json" ENGINE_LOCK_PATH = ROOT / "engine-lock" / "orro-e2e-engine-lock.json" COMMIT_RE = re.compile(r"^[0-9a-f]{40}$") +VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$") ZERO_COMMIT = "0" * 40 REQUIRED_SURFACES = { "advise", @@ -131,9 +132,17 @@ def validate(manifest: dict[str, Any], engine_lock: dict[str, Any], *, repo_root if engine.get("role") != role: fail(f"engines.{key}.role must be {role}") manifest_commit = require_commit(f"engines.{key}.commit", engine.get("commit")) - lock_commit = require_object(f"engine_lock.{key}", engine_lock.get(key)).get("commit") + locked_engine = require_object(f"engine_lock.{key}", engine_lock.get(key)) + lock_commit = locked_engine.get("commit") if manifest_commit != lock_commit: fail(f"engines.{key}.commit must match engine-lock/orro-e2e-engine-lock.json") + if key == "witnessd": + manifest_version = engine.get("version") + lock_version = locked_engine.get("version") + if not isinstance(manifest_version, str) or VERSION_RE.fullmatch(manifest_version) is None: + fail("engines.witnessd.version must be an X.Y.Z version") + if manifest_version != lock_version: + fail("engines.witnessd.version must match engine-lock/orro-e2e-engine-lock.json") release_lock = require_object("engine_lock", manifest.get("engine_lock")) if release_lock.get("path") != "engine-lock/orro-e2e-engine-lock.json": @@ -180,6 +189,7 @@ def _build_fixtures() -> tuple[dict[str, Any], dict[str, Any]]: "witnessd": { "repository": "Moonweave-Systems/witnessd", "commit": witnessd_commit, + "version": "9.8.7", "role": "execution engine", }, "depone": { @@ -215,6 +225,7 @@ def _build_fixtures() -> tuple[dict[str, Any], dict[str, Any]]: "witnessd": { "repository": "Moonweave-Systems/witnessd", "commit": witnessd_commit, + "version": "9.8.7", "ref_name": "main", }, "depone": { @@ -246,6 +257,7 @@ def self_test() -> int: forgeries: list[tuple[str, Any]] = [ ("engines.witnessd.commit mismatch", lambda m: m["engines"]["witnessd"].__setitem__("commit", "3" * 40)), + ("engines.witnessd.version mismatch", lambda m: m["engines"]["witnessd"].__setitem__("version", "9.8.6")), ("validated_surfaces missing proofrun", lambda m: m["validated_surfaces"].remove("proofrun")), ("boundary.approves_merge true", lambda m: m["boundary"].__setitem__("approves_merge", True)), ("not_proof false", lambda m: m.__setitem__("not_proof", False)), diff --git a/scripts/check_orro_release_state.py b/scripts/check_orro_release_state.py new file mode 100644 index 0000000..d411f66 --- /dev/null +++ b/scripts/check_orro_release_state.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python3 +"""Validate ORRO's repository-internal published release state.""" + +from __future__ import annotations + +import argparse +import ast +import configparser +import copy +import json +import re +import sys +from pathlib import Path +from typing import Any + + +ROOT = Path(__file__).resolve().parents[1] +PYPROJECT_PATH = ROOT / "pyproject.toml" +SETUP_CFG_PATH = ROOT / "setup.cfg" +WRAPPER_PATH = ROOT / "src" / "orro_wrapper" / "cli.py" +ENGINE_LOCK_PATH = ROOT / "engine-lock" / "orro-e2e-engine-lock.json" +RELEASE_MANIFEST_PATH = ROOT / "release" / "orro-release-manifest.v0.json" +PACKAGE_PLAN_PATH = ROOT / "packaging" / "wrapper-package-plan.v0.json" +DOC_PATHS = ( + Path("README.md"), + Path("docs/install.md"), + Path("docs/compatibility-matrix.md"), + Path("docs/packaging-decision.md"), + Path("docs/repository-strategy.md"), + Path("docs/bootstrap.md"), + Path("docs/pinned-engine-fallback.md"), + Path("docs/wrapper-distribution.md"), + Path("docs/thin-wrapper-plan.md"), + Path("docs/thin-wrapper.md"), +) +VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$") +WITNESSD_REQUIREMENT_RE = re.compile(r"^witnessd>=(\d+\.\d+\.\d+)$") +STALE_RELEASE_PHRASES = ( + "0.0.x is live", + "Until 0.1.0 is published", + "publishing 0.1.0 is a separate", + "Publishing 0.1.0 is a separate", +) + + +class ReleaseStateError(RuntimeError): + pass + + +def fail(message: str) -> None: + raise ReleaseStateError(message) + + +def read_text(path: Path) -> str: + try: + return path.read_text(encoding="utf-8") + except OSError as exc: + fail(f"could not read {path.relative_to(ROOT)}: {exc}") + + +def read_json(path: Path) -> dict[str, Any]: + try: + value = json.loads(read_text(path)) + except json.JSONDecodeError as exc: + fail(f"malformed JSON in {path.relative_to(ROOT)}: {exc}") + if not isinstance(value, dict): + fail(f"{path.relative_to(ROOT)} must contain a JSON object") + return value + + +def require_match(label: str, pattern: str, text: str) -> str: + match = re.search(pattern, text, flags=re.MULTILINE) + if match is None: + fail(f"{label} is missing") + return match.group(1) + + +def wrapper_published_claim(text: str) -> bool: + try: + module = ast.parse(text, filename=str(WRAPPER_PATH)) + except SyntaxError as exc: + fail(f"could not parse {WRAPPER_PATH.relative_to(ROOT)}: {exc}") + for node in module.body: + if not isinstance(node, ast.FunctionDef) or node.name != "wrapper_info": + continue + for child in ast.walk(node): + if not isinstance(child, ast.Return) or not isinstance(child.value, ast.Dict): + continue + for key, value in zip(child.value.keys, child.value.values): + if isinstance(key, ast.Constant) and key.value == "published_package": + if isinstance(value, ast.Constant) and isinstance(value.value, bool): + return value.value + fail("wrapper_info published_package must be a literal boolean") + fail("wrapper_info must declare published_package") + + +def parse_setup_cfg(text: str) -> tuple[str, str]: + parser = configparser.ConfigParser() + try: + parser.read_string(text) + version = parser["metadata"]["version"].strip() + requirements = [line.strip() for line in parser["options"]["install_requires"].splitlines() if line.strip()] + except (configparser.Error, KeyError) as exc: + fail(f"could not parse setup.cfg release metadata: {exc}") + witnessd_requirements = [item for item in requirements if item.startswith("witnessd")] + if len(witnessd_requirements) != 1: + fail("setup.cfg must declare exactly one witnessd requirement") + return version, witnessd_requirements[0] + + +def parse_pyproject(text: str) -> tuple[str, str]: + version = require_match("pyproject.toml project version", r'^version\s*=\s*"([^"]+)"$', text) + match = re.search(r"^dependencies\s*=\s*(\[[\s\S]*?\])", text, flags=re.MULTILINE) + if match is None: + fail("pyproject.toml project dependencies are missing") + try: + dependencies = ast.literal_eval(match.group(1)) + except (SyntaxError, ValueError) as exc: + fail(f"could not parse pyproject.toml project dependencies: {exc}") + if not isinstance(dependencies, list) or not all(isinstance(item, str) for item in dependencies): + fail("pyproject.toml project dependencies must be a list of strings") + witnessd_requirements = [item for item in dependencies if item.startswith("witnessd")] + if len(witnessd_requirements) != 1: + fail("pyproject.toml must declare exactly one witnessd requirement") + return version, witnessd_requirements[0] + + +def repository_state() -> dict[str, Any]: + pyproject = read_text(PYPROJECT_PATH) + pyproject_version, pyproject_requirement = parse_pyproject(pyproject) + setup_cfg_version, setup_cfg_requirement = parse_setup_cfg(read_text(SETUP_CFG_PATH)) + engine_lock = read_json(ENGINE_LOCK_PATH) + manifest = read_json(RELEASE_MANIFEST_PATH) + package_plan = read_json(PACKAGE_PLAN_PATH) + witnessd_lock = engine_lock.get("witnessd") + manifest_engines = manifest.get("engines") + if not isinstance(witnessd_lock, dict): + fail("engine-lock witnessd must be an object") + if not isinstance(manifest_engines, dict) or not isinstance(manifest_engines.get("witnessd"), dict): + fail("release manifest witnessd engine must be an object") + witnessd_manifest = manifest_engines["witnessd"] + return { + "pyproject_version": pyproject_version, + "setup_cfg_version": setup_cfg_version, + "pyproject_requirement": pyproject_requirement, + "setup_cfg_requirement": setup_cfg_requirement, + "published_package": wrapper_published_claim(read_text(WRAPPER_PATH)), + "engine_lock_witnessd_version": witnessd_lock.get("version"), + "engine_lock_witnessd_ref": witnessd_lock.get("ref_name"), + "manifest_witnessd_version": witnessd_manifest.get("version"), + "package_plan_version": package_plan.get("source_version"), + "package_plan_published_versions": package_plan.get("published_pypi_versions"), + "package_plan_published_package": package_plan.get("published_package"), + "docs": {str(path): read_text(ROOT / path) for path in DOC_PATHS}, + } + + +def version_tuple(label: str, value: Any) -> tuple[int, int, int]: + if not isinstance(value, str) or VERSION_RE.fullmatch(value) is None: + fail(f"{label} must be an X.Y.Z version") + major, minor, patch = value.split(".") + return int(major), int(minor), int(patch) + + +def minimum_witnessd_version(label: str, requirement: Any) -> tuple[int, int, int]: + if not isinstance(requirement, str): + fail(f"{label} must be a string") + match = WITNESSD_REQUIREMENT_RE.fullmatch(requirement) + if match is None: + fail(f"{label} must be an exact witnessd>=X.Y.Z requirement") + return version_tuple(label, match.group(1)) + + +def validate_release_state(state: dict[str, Any]) -> None: + package_version = state.get("pyproject_version") + version_tuple("pyproject.toml version", package_version) + if state.get("setup_cfg_version") != package_version: + fail("setup.cfg version must match pyproject.toml version") + if state.get("published_package") is not True: + fail("wrapper_info published_package must be true") + + pyproject_minimum = minimum_witnessd_version("pyproject.toml witnessd requirement", state.get("pyproject_requirement")) + setup_cfg_minimum = minimum_witnessd_version("setup.cfg witnessd requirement", state.get("setup_cfg_requirement")) + if setup_cfg_minimum != pyproject_minimum: + fail("setup.cfg witnessd requirement must match pyproject.toml") + + witnessd_version = state.get("engine_lock_witnessd_version") + locked_witnessd = version_tuple("engine-lock witnessd version", witnessd_version) + if locked_witnessd < pyproject_minimum: + fail("engine-lock witnessd version must satisfy the package requirement") + if state.get("engine_lock_witnessd_ref") != f"v{witnessd_version}": + fail("engine-lock witnessd ref_name must match its version tag") + if state.get("manifest_witnessd_version") != witnessd_version: + fail("release manifest witnessd version must match the engine lock") + + if state.get("package_plan_version") != package_version: + fail("wrapper package plan source_version must match the packaged version") + if state.get("package_plan_published_versions") != package_version: + fail("wrapper package plan published_pypi_versions must match the packaged version") + if state.get("package_plan_published_package") is not True: + fail("wrapper package plan must record published_package true") + + docs = state.get("docs") + if not isinstance(docs, dict) or not docs: + fail("release-state docs must be a non-empty mapping") + published_sentence = f"`orro` {package_version} is published on PyPI" + for path, text in docs.items(): + if not isinstance(path, str) or not isinstance(text, str): + fail("release-state docs must map path strings to text") + normalized_text = " ".join(text.split()) + if published_sentence not in normalized_text: + fail(f"{path} must state that {published_sentence}") + for phrase in STALE_RELEASE_PHRASES: + if phrase in text: + fail(f"{path} contains stale release claim {phrase!r}") + + +def self_test() -> int: + parsed_version, parsed_requirement = parse_pyproject( + '[project]\nversion = "0.1.0"\ndependencies = [\n "example>=1",\n "witnessd>=2.3.2",\n]\n' + ) + assert parsed_version == "0.1.0" + assert parsed_requirement == "witnessd>=2.3.2" + base: dict[str, Any] = { + "pyproject_version": "0.1.0", + "setup_cfg_version": "0.1.0", + "pyproject_requirement": "witnessd>=2.3.2", + "setup_cfg_requirement": "witnessd>=2.3.2", + "published_package": True, + "engine_lock_witnessd_version": "2.3.3", + "engine_lock_witnessd_ref": "v2.3.3", + "manifest_witnessd_version": "2.3.3", + "package_plan_version": "0.1.0", + "package_plan_published_versions": "0.1.0", + "package_plan_published_package": True, + "docs": {"README.md": "`orro` 0.1.0 is published on PyPI."}, + } + validate_release_state(base) + for label, key, value in ( + ("package version mismatch", "setup_cfg_version", "0.1.1"), + ("unpublished wrapper claim", "published_package", False), + ("unsatisfied witnessd lock", "engine_lock_witnessd_version", "2.3.0"), + ("mutable witnessd ref", "engine_lock_witnessd_ref", "main"), + ("manifest version mismatch", "manifest_witnessd_version", "2.3.2"), + ("package plan stale version", "package_plan_published_versions", "0.0.x"), + ): + forged = copy.deepcopy(base) + forged[key] = value + try: + validate_release_state(forged) + except ReleaseStateError: + continue + fail(f"self-test accepted forgery: {label}") + forged_docs = copy.deepcopy(base) + forged_docs["docs"]["README.md"] = "Only 0.0.x is live." + try: + validate_release_state(forged_docs) + except ReleaseStateError: + pass + else: + fail("self-test accepted stale release documentation") + print("ORRO release state self-test: pass") + return 0 + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--self-test", action="store_true", help="run built-in inconsistency rejection checks") + args = parser.parse_args(sys.argv[1:] if argv is None else argv) + try: + if args.self_test: + return self_test() + validate_release_state(repository_state()) + except ReleaseStateError as exc: + print(f"ORRO release state violation: {exc}", file=sys.stderr) + return 1 + print("ORRO release state: pass") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/check_orro_repo_contract.py b/scripts/check_orro_repo_contract.py index 13e1519..8c92f8a 100644 --- a/scripts/check_orro_repo_contract.py +++ b/scripts/check_orro_repo_contract.py @@ -537,6 +537,7 @@ def check_e2e_docs() -> None: def check_release_discipline() -> None: required_paths = [ "scripts/check_orro_release_manifest.py", + "scripts/check_orro_release_state.py", "scripts/check_compatibility_matrix.py", "scripts/update_orro_engine_lock.py", "release/orro-release-manifest.v0.json", @@ -567,7 +568,7 @@ def check_release_discipline() -> None: require_contains("release docs", lower_text, "engine-lock update") require_contains("release docs", text, "not proof") require_contains("release docs", text, "not verifier truth") - require_contains("release docs", lower_text, "the `orro` package is published on pypi") + require_contains("release docs", lower_text, "`orro` 0.1.0 is published on pypi") def check_os_support_matrix() -> None: @@ -641,7 +642,7 @@ def check_packaging_decision() -> None: ) require_contains("packaging decision docs", text, "packaging decision") require_contains("packaging decision docs", text, "not package publish") - require_contains("packaging decision docs", text, "The `orro` package is published on PyPI") + require_contains("packaging decision docs", text, "`orro` 0.1.0 is published on PyPI") require_contains("packaging decision docs", text, "witnessd>=2.3.2") require_contains("packaging decision docs", text, "no engine code") require_contains("packaging decision docs", text, "witnessd-hosted") @@ -814,6 +815,7 @@ def check_no_engine_code() -> None: "check_orro_packaging_decision.py", "check_orro_repo_contract.py", "check_orro_release_manifest.py", + "check_orro_release_state.py", "check_compatibility_matrix.py", "check_orro_version_coherence.py", "check_orro_wrapper.py", diff --git a/scripts/update_orro_engine_lock.py b/scripts/update_orro_engine_lock.py index 757a875..c061229 100644 --- a/scripts/update_orro_engine_lock.py +++ b/scripts/update_orro_engine_lock.py @@ -22,6 +22,7 @@ ROOT = Path(__file__).resolve().parents[1] COMMIT_RE = re.compile(r"^[0-9a-f]{40}$") +VERSION_RE = re.compile(r"^\d+\.\d+\.\d+$") ZERO_COMMIT = "0" * 40 ENGINE_LOCK_REL = Path("engine-lock/orro-e2e-engine-lock.json") RELEASE_MANIFEST_REL = Path("release/orro-release-manifest.v0.json") @@ -84,10 +85,20 @@ def resolve_ref_name(requested: str | None, engine_lock: dict[str, Any], engine: return current["ref_name"] +def resolve_witnessd_ref_name(requested: str | None, version: str) -> str: + version_ref = f"v{version}" + if requested is None: + return version_ref + if requested != version_ref: + raise UpdateError(f"witnessd ref must be {version_ref} for witnessd version {version}") + return requested + + def update_files( root: Path, *, witnessd_commit: str, + witnessd_version: str, depone_commit: str, witnessd_ref: str | None, depone_ref: str | None, @@ -95,6 +106,8 @@ def update_files( dry_run: bool, ) -> dict[str, Any]: validate_commit("witnessd commit", witnessd_commit) + if VERSION_RE.fullmatch(witnessd_version) is None: + raise UpdateError("witnessd version must be an X.Y.Z version") validate_commit("Depone commit", depone_commit) validate_commit("ORRO commit", orro_commit, allow_zero=True) @@ -104,7 +117,7 @@ def update_files( structured_compatibility_matrix_path = root / STRUCTURED_COMPAT_MATRIX_REL engine_lock = read_json(engine_lock_path) - witnessd_ref = resolve_ref_name(witnessd_ref, engine_lock, "witnessd") + witnessd_ref = resolve_witnessd_ref_name(witnessd_ref, witnessd_version) depone_ref = resolve_ref_name(depone_ref, engine_lock, "depone") engine_lock.update( { @@ -113,6 +126,7 @@ def update_files( "witnessd": { "repository": "Moonweave-Systems/witnessd", "commit": witnessd_commit, + "version": witnessd_version, "ref_name": witnessd_ref, }, "depone": { @@ -143,6 +157,7 @@ def update_files( "witnessd": { "repository": "Moonweave-Systems/witnessd", "commit": witnessd_commit, + "version": witnessd_version, "role": "execution engine", }, "depone": { @@ -168,7 +183,7 @@ def update_files( release_manifest["not_package_publish"] = True matrix_text = compatibility_matrix_path.read_text(encoding="utf-8") - matrix_text = replace_matrix_row(matrix_text, witnessd_commit, depone_commit, orro_commit) + matrix_text = replace_matrix_row(matrix_text, witnessd_commit, witnessd_version, depone_commit, orro_commit) structured_matrix = read_json(structured_compatibility_matrix_path) structured_entries = matrix_entries_by_id(structured_matrix) @@ -180,6 +195,7 @@ def update_files( current_entry.update( { "witnessd_commit": witnessd_commit, + "witnessd_version": witnessd_version, "depone_commit": depone_commit, } ) @@ -187,6 +203,7 @@ def update_files( { "orro_commit": orro_commit, "witnessd_commit": witnessd_commit, + "witnessd_version": witnessd_version, "depone_commit": depone_commit, } ) @@ -197,6 +214,7 @@ def update_files( "compatibility_matrix": str(COMPAT_MATRIX_REL), "structured_compatibility_matrix": str(STRUCTURED_COMPAT_MATRIX_REL), "witnessd_commit": witnessd_commit, + "witnessd_version": witnessd_version, "depone_commit": depone_commit, "orro_commit": orro_commit, "dry_run": dry_run, @@ -217,21 +235,38 @@ def update_files( return changed -def replace_matrix_row(text: str, witnessd_commit: str, depone_commit: str, orro_commit: str) -> str: +def replace_matrix_row( + text: str, + witnessd_commit: str, + witnessd_version: str, + depone_commit: str, + orro_commit: str, +) -> str: # The matrix table now leads with a stable "Matrix entry" label column, so the # ORRO repo commit (pending or pinned) lives in the second column, not the first. orro_repo_commit = "pending release manifest" if orro_commit == ZERO_COMMIT else f"`{orro_commit}`" - row = ( - f"| orro-rc-locked-triplet | {orro_repo_commit} | `{witnessd_commit}` | `{depone_commit}` | pass | " - f"Matches `engine-lock/orro-e2e-engine-lock.json`, `release/orro-release-manifest.v0.json`, " - f"and `release/compatibility-matrix.v0.json`. |" - ) + rows = { + "depone-n-witnessd-n": ( + f"| depone-n-witnessd-n | n/a | `{witnessd_commit}` | `{depone_commit}` | pass | " + f"Current locally validated engine pair; witnessd v{witnessd_version}. |" + ), + "orro-rc-locked-triplet": ( + f"| orro-rc-locked-triplet | {orro_repo_commit} | `{witnessd_commit}` | `{depone_commit}` | pass | " + f"Matches witnessd v{witnessd_version}, `engine-lock/orro-e2e-engine-lock.json`, " + f"`release/orro-release-manifest.v0.json`, and `release/compatibility-matrix.v0.json`. |" + ), + } lines = text.splitlines() + found: set[str] = set() for index, line in enumerate(lines): - if "pending release manifest" in line or "engine-lock/orro-e2e-engine-lock.json" in line: - lines[index] = row - return "\n".join(lines) + "\n" - raise UpdateError("could not find compatibility matrix release row") + for entry_id, row in rows.items(): + if line.startswith(f"| {entry_id} |"): + lines[index] = row + found.add(entry_id) + missing = sorted(rows.keys() - found) + if missing: + raise UpdateError(f"could not find compatibility matrix rows: {', '.join(missing)}") + return "\n".join(lines) + "\n" def self_test() -> int: @@ -249,6 +284,8 @@ def self_test() -> int: str(tmp), "--witnessd-commit", "1" * 40, + "--witnessd-version", + "9.8.7", "--depone-commit", "2" * 40, "--orro-commit", @@ -262,15 +299,19 @@ def self_test() -> int: structured_entries = {entry["id"]: entry for entry in structured_matrix["entries"]} matrix = (tmp / COMPAT_MATRIX_REL).read_text(encoding="utf-8") assert engine_lock["witnessd"]["commit"] == "1" * 40 + assert engine_lock["witnessd"]["version"] == "9.8.7" assert engine_lock["depone"]["commit"] == "2" * 40 - assert engine_lock["witnessd"]["ref_name"] == original_lock["witnessd"]["ref_name"] + assert engine_lock["witnessd"]["ref_name"] == "v9.8.7" assert engine_lock["depone"]["ref_name"] == original_lock["depone"]["ref_name"] assert release_manifest["engines"]["witnessd"]["commit"] == "1" * 40 + assert release_manifest["engines"]["witnessd"]["version"] == "9.8.7" assert release_manifest["engines"]["depone"]["commit"] == "2" * 40 assert structured_entries["depone-n-witnessd-n"]["witnessd_commit"] == "1" * 40 + assert structured_entries["depone-n-witnessd-n"]["witnessd_version"] == "9.8.7" assert structured_entries["depone-n-witnessd-n"]["depone_commit"] == "2" * 40 assert structured_entries["orro-rc-locked-triplet"]["orro_commit"] == "3" * 40 assert structured_entries["orro-rc-locked-triplet"]["witnessd_commit"] == "1" * 40 + assert structured_entries["orro-rc-locked-triplet"]["witnessd_version"] == "9.8.7" assert structured_entries["orro-rc-locked-triplet"]["depone_commit"] == "2" * 40 assert "`1111111111111111111111111111111111111111`" in matrix assert "`2222222222222222222222222222222222222222`" in matrix @@ -282,6 +323,12 @@ def self_test() -> int: check=False, ) assert checker.returncode == 0, checker.stderr + try: + resolve_witnessd_ref_name("main", "9.8.7") + except UpdateError: + pass + else: + raise AssertionError("mismatched witnessd version ref was accepted") print("ORRO engine-lock update helper: self-test pass") return 0 @@ -289,6 +336,7 @@ def self_test() -> int: def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser(description="Update ORRO e2e engine-lock and release metadata together.") parser.add_argument("--witnessd-commit") + parser.add_argument("--witnessd-version") parser.add_argument("--depone-commit") parser.add_argument("--witnessd-ref") parser.add_argument("--depone-ref") @@ -303,12 +351,13 @@ def main(argv: list[str] | None = None) -> int: args = parse_args(sys.argv[1:] if argv is None else argv) if args.self_test: return self_test() - if not args.witnessd_commit or not args.depone_commit: - fail("--witnessd-commit and --depone-commit are required") + if not args.witnessd_commit or not args.witnessd_version or not args.depone_commit: + fail("--witnessd-commit, --witnessd-version, and --depone-commit are required") try: result = update_files( Path(args.root).resolve(), witnessd_commit=args.witnessd_commit, + witnessd_version=args.witnessd_version, depone_commit=args.depone_commit, witnessd_ref=args.witnessd_ref, depone_ref=args.depone_ref, diff --git a/src/orro_wrapper/cli.py b/src/orro_wrapper/cli.py index 1b1ad10..dc91fef 100644 --- a/src/orro_wrapper/cli.py +++ b/src/orro_wrapper/cli.py @@ -52,7 +52,7 @@ def wrapper_info() -> dict[str, Any]: "schema_version": SCHEMA_VERSION, "version": get_version(), "current_command_source": "ORRO-owned orro console script", - "published_package": False, + "published_package": True, "not_proof": True, "not_verifier_truth": True, "boundary": boundary(),