diff --git a/README.md b/README.md index 8ec7df7..454b021 100644 --- a/README.md +++ b/README.md @@ -17,9 +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 product line is published on PyPI through 0.2.21, while this repository - packages the 0.2.24 release candidate. The post-release target state is: `orro` - 0.2.24 is published on PyPI. It becomes true only after `v0.2.24` is tagged and +- The ORRO product line is published on PyPI through 0.2.24, while this repository + packages the 0.2.25 release candidate. The post-release target state is: `orro` + 0.2.25 is published on PyPI. It becomes true only after `v0.2.25` is tagged and the Trusted-Publishing workflow completes. Until then, a normal `pip install orro` installs the latest published product-line release. - The local wrapper package exposes both `orro` and `orro-wrapper`. @@ -33,7 +33,7 @@ Current focus: ## Can I use ORRO today? Yes. The published ORRO product line is installable from PyPI; see Current Status -for the latest published version. Development dogfood can run the 0.2.24 source +for the latest published version. Development dogfood can run the 0.2.25 source directly against pinned Depone and witnessd checkouts. Current split: @@ -74,7 +74,7 @@ migration. Current command source: the `orro` command is exposed by this ORRO package and delegates to `python -m orro`. -The 0.2.24 package metadata declares `witnessd>=2.40.0,<3.0.0` while keeping Depone and +The 0.2.25 package metadata declares `witnessd>=2.40.0,<3.0.0` while keeping Depone and witnessd as separate engine repositories. ## Pinned Engine E2E @@ -98,9 +98,9 @@ Engine-lock update discipline is documented in [`docs/engine-lock-update-process.md`](docs/engine-lock-update-process.md), and validated engine pairs are listed in [`docs/compatibility-matrix.md`](docs/compatibility-matrix.md). The published PyPI -version is documented in Current Status; this repository sources 0.2.24, +version is documented in Current Status; this repository sources 0.2.25, whose publication will be completed as a separate release step after the -`v0.2.24` tag triggers the +`v0.2.25` tag triggers the Trusted-Publishing workflow. Repository metadata changes do not publish or rewrite an artifact. @@ -137,7 +137,7 @@ not assurance. The current command source is the ORRO-owned `orro` console script. The published PyPI version is documented in Current Status, and this repository prepares source -version 0.2.24 with a `witnessd>=2.40.0,<3.0.0` dependency. The wrapper contains no +version 0.2.25 with a `witnessd>=2.40.0,<3.0.0` dependency. The wrapper contains no engine code and must not implement proofrun, proofcheck, scheduler, observer, fan-in, team-ledger, or verifier logic. @@ -241,6 +241,10 @@ orro boundary orro-wrapper self-test ``` +The installed `orro` console script is the only supported operator entrypoint. +Do not use `uv run python3 -m orro`: ambient uv project discovery can select an +unrelated environment and produce a misleading `No module named orro` error. + The wrapper is product/distribution metadata and a thin delegation surface. It is not proof, not verifier truth, not package publish, not approval, and not assurance. diff --git a/docs/bootstrap.md b/docs/bootstrap.md index 2738460..6f09924 100644 --- a/docs/bootstrap.md +++ b/docs/bootstrap.md @@ -94,10 +94,10 @@ that the installed metadata points to `orro_wrapper.cli:main`, `orro boundary` reports `contains_engine_logic: false`, `orro flowplan --help` delegates to witnessd, and both `orro-wrapper` and `python -m orro` remain usable. -The post-release target state is: `orro` 0.2.24 is published on PyPI. It becomes -true only after `v0.2.24` is tagged and the Trusted-Publishing workflow completes; -until then, PyPI contains the ORRO product line through 0.2.22. This repository is -the canonical 0.2.24 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. +The post-release target state is: `orro` 0.2.25 is published on PyPI. It becomes +true only after `v0.2.25` is tagged and the Trusted-Publishing workflow completes; +until then, PyPI contains the ORRO product line through 0.2.24. This repository is +the canonical 0.2.25 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. The wrapper distribution smoke is separate from bootstrap: diff --git a/docs/compatibility-matrix.md b/docs/compatibility-matrix.md index c1b0f91..061ad13 100644 --- a/docs/compatibility-matrix.md +++ b/docs/compatibility-matrix.md @@ -5,10 +5,10 @@ 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. -The post-release target state is: `orro` 0.2.24 is published on PyPI. It becomes -true only after `v0.2.24` is tagged and the Trusted-Publishing workflow completes; -until then, PyPI contains the ORRO product line through 0.2.22. This repository is -the canonical 0.2.24 source. +The post-release target state is: `orro` 0.2.25 is published on PyPI. It becomes +true only after `v0.2.25` is tagged and the Trusted-Publishing workflow completes; +until then, PyPI contains the ORRO product line through 0.2.24. This repository is +the canonical 0.2.25 source. ```text Depone verifies; witnessd executes; ORRO exposes the workflow. @@ -22,7 +22,7 @@ Depone verifies; witnessd executes; ORRO exposes the workflow. | orro-rc-locked-triplet | `f93f31fa09f5eb6133c508547f8e0f5e0636b930` | `c9559008427ab16a12a9002ffc27c434f8e5c6d6` | `b471b47ceb8cc2460e7221375526f63553c18353` | pass | Matches witnessd v2.40.0, `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.40.0` (source version 2.40.0). It -satisfies the ORRO 0.2.24 package requirement `witnessd>=2.40.0,<3.0.0`. +satisfies the ORRO 0.2.25 package requirement `witnessd>=2.40.0,<3.0.0`. Related files: diff --git a/docs/e2e-runner.md b/docs/e2e-runner.md index 4bbdfaa..6a0ff9f 100644 --- a/docs/e2e-runner.md +++ b/docs/e2e-runner.md @@ -78,4 +78,4 @@ separate pinned-engine e2e CI checks out the pinned witnessd and Depone commits from the e2e lock, then runs the full smoke. The ORRO product line is published on PyPI through 0.2.6. This pinned-engine -smoke validates the 0.2.24 source package without publishing a new release. +smoke validates the 0.2.25 source package without publishing a new release. diff --git a/docs/install.md b/docs/install.md index 2eedb97..c6549cb 100644 --- a/docs/install.md +++ b/docs/install.md @@ -1,17 +1,23 @@ # Install -The ORRO product line is published on PyPI through 0.2.22, while this repository -packages 0.2.24. The post-release target state is: `orro` 0.2.24 is published on -PyPI. It becomes true only after `v0.2.24` is tagged and the Trusted-Publishing -workflow completes. The 0.2.24 package metadata declares `witnessd>=2.40.0,<3.0.0`, and +The ORRO product line is published on PyPI through 0.2.24, while this repository +packages 0.2.25. The post-release target state is: `orro` 0.2.25 is published on +PyPI. It becomes true only after `v0.2.25` is tagged and the Trusted-Publishing +workflow completes. The 0.2.25 package metadata declares `witnessd>=2.40.0,<3.0.0`, and the package exposes `orro` plus the `orro-wrapper` compatibility alias. ```bash python3 -m pip install orro ``` -Until the `v0.2.24` workflow completes, that command installs `orro` 0.2.22. -Development dogfood may instead use the 0.2.24 source checkout and pinned engine +Use the installed `orro` console script for every operator-facing command. It is +the only supported entrypoint. Do not probe or run ORRO with +`uv run python3 -m orro`: uv may discover an unrelated ambient project +environment and report `No module named orro` even when the installed console +script and its pinned environment are healthy. + +Until the `v0.2.25` workflow completes, that command installs `orro` 0.2.24. +Development dogfood may instead use the 0.2.25 source checkout and pinned engine repositories directly. For the shared pinned-engine development layout, run bootstrap with the shared @@ -102,7 +108,7 @@ The v0 packaging decision is documented in `docs/packaging-decision.md` and verifier truth, and not package publish. Current installs use the ORRO-owned thin `orro` command. The ORRO product line is -published on PyPI through 0.2.22, this repository prepares source 0.2.24 for the +published on PyPI through 0.2.24, this repository prepares source 0.2.25 for the tag-triggered workflow, and the wrapper continues to contain no engine code. ## Wrapper Distribution Smoke diff --git a/docs/packaging-decision.md b/docs/packaging-decision.md index 73780f4..f1403dd 100644 --- a/docs/packaging-decision.md +++ b/docs/packaging-decision.md @@ -12,15 +12,15 @@ 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 post-release target state is: `orro` 0.2.24 is published on PyPI. -It becomes true only after `v0.2.24` is tagged and the Trusted-Publishing workflow -completes; until then, PyPI contains the ORRO product line through 0.2.22. This -repository is the canonical 0.2.24 source, whose metadata declares +to witnessd. The post-release target state is: `orro` 0.2.25 is published on PyPI. +It becomes true only after `v0.2.25` is tagged and the Trusted-Publishing workflow +completes; until then, PyPI contains the ORRO product line through 0.2.24. This +repository is the canonical 0.2.25 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. `published_package: true` means the ORRO product line has published PyPI releases; `published_package_scope: product-line` makes that scope explicit. It -does not assert that the 0.2.24 source candidate is already published. +does not assert that the 0.2.25 source candidate is already published. The package remains a thin wrapper that delegates to the existing engine command surface. It must contain no engine code. @@ -58,7 +58,7 @@ The ORRO repository must not contain: `orro` and `orro-wrapper`, and contains no engine packages or engine implementation files. 3. Published package: current phase. The ORRO product line is published through - 0.2.1, and source 0.2.24 is prepared for the tag-triggered Trusted-Publishing + 0.2.24, and source 0.2.25 is prepared for the tag-triggered Trusted-Publishing workflow. Any new release still requires pinned-engine e2e, boundary checks, bootstrap checks, and release metadata to remain green. diff --git a/docs/pinned-engine-fallback.md b/docs/pinned-engine-fallback.md index eab4b02..9d648f6 100644 --- a/docs/pinned-engine-fallback.md +++ b/docs/pinned-engine-fallback.md @@ -67,8 +67,8 @@ 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 post-release target state is: `orro` 0.2.24 is published on PyPI. It becomes -true only after `v0.2.24` is tagged and the Trusted-Publishing workflow completes; -until then, PyPI contains the ORRO product line through 0.2.22. This repository is -the canonical 0.2.24 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. The current +The post-release target state is: `orro` 0.2.25 is published on PyPI. It becomes +true only after `v0.2.25` is tagged and the Trusted-Publishing workflow completes; +until then, PyPI contains the ORRO product line through 0.2.24. This repository is +the canonical 0.2.25 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. The current engine lock pins witnessd 2.40.0 at tag `v2.40.0`, satisfying that dependency. diff --git a/docs/release-notes/0.2.25.md b/docs/release-notes/0.2.25.md new file mode 100644 index 0000000..66208cd --- /dev/null +++ b/docs/release-notes/0.2.25.md @@ -0,0 +1,14 @@ +# ORRO 0.2.25 + +ORRO 0.2.25 makes the thin wrapper's public surface match the workflow surface +that it delegates to witnessd. + +- `orro --help` shows the delegated public commands in `usage:` and explains + the delegation boundary on the first screen. +- `orro self-test` fails when the packaged ORRO skill documents name a command + absent from witnessd's `ORRO_COMMAND_MAP`. +- Installation guidance requires the `orro` console script and warns that + `uv run python3 -m orro` can select an unrelated ambient environment. + +The wrapper remains a wrapper: no engine command, runtime behavior, verifier +logic, or delegation semantics changed. diff --git a/docs/repository-strategy.md b/docs/repository-strategy.md index 0ac9004..8ca8188 100644 --- a/docs/repository-strategy.md +++ b/docs/repository-strategy.md @@ -32,9 +32,9 @@ 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 post-release target state is: -`orro` 0.2.24 is published on PyPI. It becomes true only after `v0.2.24` is tagged +`orro` 0.2.25 is published on PyPI. It becomes true only after `v0.2.25` is tagged and the Trusted-Publishing workflow completes; until then, PyPI contains the ORRO -product line through 0.2.22. This repository is the canonical 0.2.24 source. +product line through 0.2.24. This repository is the canonical 0.2.25 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 3d27d79..e4b1744 100644 --- a/docs/thin-wrapper-plan.md +++ b/docs/thin-wrapper-plan.md @@ -1,9 +1,9 @@ # Thin Wrapper Plan -The post-release target state is: `orro` 0.2.24 is published on PyPI. It becomes -true only after `v0.2.24` is tagged and the Trusted-Publishing workflow completes; -until then, PyPI contains the ORRO product line through 0.2.22. The ORRO repository -is the canonical 0.2.24 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. +The post-release target state is: `orro` 0.2.25 is published on PyPI. It becomes +true only after `v0.2.25` is tagged and the Trusted-Publishing workflow completes; +until then, PyPI contains the ORRO product line through 0.2.24. The ORRO repository +is the canonical 0.2.25 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. ## Current State diff --git a/docs/thin-wrapper.md b/docs/thin-wrapper.md index 5ce80e6..8642e7b 100644 --- a/docs/thin-wrapper.md +++ b/docs/thin-wrapper.md @@ -46,10 +46,10 @@ the engine command the operator explicitly invoked. The wrapper only delegates. ## Relationship To Packaging -The post-release target state is: `orro` 0.2.24 is published on PyPI. It becomes -true only after `v0.2.24` is tagged and the Trusted-Publishing workflow completes; -until then, PyPI contains the ORRO product line through 0.2.22. This repository is -the canonical 0.2.24 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. +The post-release target state is: `orro` 0.2.25 is published on PyPI. It becomes +true only after `v0.2.25` is tagged and the Trusted-Publishing workflow completes; +until then, PyPI contains the ORRO product line through 0.2.24. This repository is +the canonical 0.2.25 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. Package work must keep: @@ -60,7 +60,7 @@ Package work must keep: ## Self-Test -The self-test does not call engine repositories: +The self-test does not execute an engine command: ```bash python3 -m pip install -e . @@ -68,7 +68,12 @@ orro-wrapper self-test ``` It verifies the wrapper boundary, default delegation command parsing, and that -empty delegate invocations fail closed. +empty delegate invocations fail closed. It also reads the pinned witnessd +distribution's `ORRO_COMMAND_MAP` and packaged skill documents, then fails if a +public command named by either skill is absent from the engine map. An +intentional wrapper-only `--no-deps` packaging smoke reports that parity was not +checked because the engine metadata is absent; normal installs include witnessd +through the declared package dependency and run the parity check. ## Install Smoke @@ -82,8 +87,9 @@ python3 scripts/check_orro_wrapper_install.py --json The install smoke checks that both commands are installed, that boundary and self-test commands pass, and that explicit delegation works with a harmless -Python command. It does not call Depone or witnessd, does not run proofrun, does -not run proofcheck, and does not publish a package. +Python command. The self-test reads witnessd's packaged command/skill metadata, +but the smoke does not run proofrun, run proofcheck, call Depone, or publish a +package. The install smoke result is setup/test metadata, not proof, not verifier truth, not package publish, not approval, and not assurance. diff --git a/docs/wrapper-distribution.md b/docs/wrapper-distribution.md index cd8a172..f02c62c 100644 --- a/docs/wrapper-distribution.md +++ b/docs/wrapper-distribution.md @@ -57,7 +57,7 @@ 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 post-release target state is: `orro` 0.2.24 is published on PyPI. It becomes -true only after `v0.2.24` is tagged and the Trusted-Publishing workflow completes; -until then, PyPI contains the ORRO product line through 0.2.22. This repository is -the canonical 0.2.24 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. +The post-release target state is: `orro` 0.2.25 is published on PyPI. It becomes +true only after `v0.2.25` is tagged and the Trusted-Publishing workflow completes; +until then, PyPI contains the ORRO product line through 0.2.24. This repository is +the canonical 0.2.25 source, whose metadata declares `witnessd>=2.40.0,<3.0.0`. diff --git a/packaging/plugin-manifest.draft.json b/packaging/plugin-manifest.draft.json index b2ce83f..714b687 100644 --- a/packaging/plugin-manifest.draft.json +++ b/packaging/plugin-manifest.draft.json @@ -7,7 +7,7 @@ "entrypoint_plan": { "current": "ORRO-owned orro console script delegating to witnessd", "compatibility_alias": "orro-wrapper", - "distribution": "orro 0.2.24 source package" + "distribution": "orro 0.2.25 source package" }, "engine_dependencies": { "witnessd": { diff --git a/packaging/wrapper-package-plan.v0.json b/packaging/wrapper-package-plan.v0.json index 707915d..d0e5fcc 100644 --- a/packaging/wrapper-package-plan.v0.json +++ b/packaging/wrapper-package-plan.v0.json @@ -4,7 +4,7 @@ "status": "release-candidate", "package_goal": "single user-facing ORRO install that prepares and invokes pinned Depone and witnessd engines", "distribution_name": "orro", - "source_version": "0.2.24", + "source_version": "0.2.25", "published_pypi_versions": [ "0.0.1", "0.0.2", @@ -33,7 +33,8 @@ "0.2.19", "0.2.20", "0.2.21", - "0.2.22" + "0.2.22", + "0.2.24" ], "current_command_source": "ORRO-owned orro console script", "published_package": true, @@ -60,7 +61,7 @@ { "phase": "published-package", "status": "current", - "description": "Track the published ORRO product line through 0.2.22 and prepare source 0.2.24 for the tag-triggered Trusted-Publishing workflow.", + "description": "Track the published ORRO product line through 0.2.24 and prepare source 0.2.25 for the tag-triggered Trusted-Publishing workflow.", "contains_engine_logic": false } ], diff --git a/pyproject.toml b/pyproject.toml index bd7280c..b1a0781 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "orro" -version = "0.2.24" +version = "0.2.25" description = "Published ORRO product/distribution wrapper for pinned Depone and witnessd engines." readme = "README.md" requires-python = ">=3.10" diff --git a/scripts/check_orro_fallback_policy.py b/scripts/check_orro_fallback_policy.py index 4a0b8da..a326660 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, "`orro` 0.2.24 is published on PyPI") + require_contains("fallback docs", text, "`orro` 0.2.25 is published on PyPI") def main() -> int: diff --git a/scripts/check_orro_packaging_decision.py b/scripts/check_orro_packaging_decision.py index d03cd2f..5989b3c 100644 --- a/scripts/check_orro_packaging_decision.py +++ b/scripts/check_orro_packaging_decision.py @@ -67,8 +67,8 @@ def check_plan() -> None: fail("wrapper package plan status must be release-candidate") if plan.get("distribution_name") != "orro": fail("wrapper package plan distribution_name must be orro") - if plan.get("source_version") != "0.2.24": - fail("wrapper package plan source_version must be 0.2.24") + if plan.get("source_version") != "0.2.25": + fail("wrapper package plan source_version must be 0.2.25") if plan.get("current_command_source") != "ORRO-owned orro console script": fail("current command source must be ORRO-owned") @@ -123,7 +123,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, "`orro` 0.2.24 is published on PyPI") + require_contains("packaging decision doc", text, "`orro` 0.2.25 is published on PyPI") require_contains("packaging decision doc", text, WITNESSD_REQUIREMENT) require_contains("packaging decision doc", text, "proofrun") require_contains("packaging decision doc", text, "proofcheck") diff --git a/scripts/check_orro_release_state.py b/scripts/check_orro_release_state.py index 7268276..badcc4d 100644 --- a/scripts/check_orro_release_state.py +++ b/scripts/check_orro_release_state.py @@ -96,7 +96,7 @@ "Publishing 0.2.13 is a separate", "installs 0.2.11", ) -PUBLISHED_PYPI_VERSIONS = ("0.0.1", "0.0.2", "0.0.3", "0.1.0", "0.1.1", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.2.7", "0.2.8", "0.2.9", "0.2.10", "0.2.11", "0.2.12", "0.2.13", "0.2.14", "0.2.15", "0.2.16", "0.2.17", "0.2.18", "0.2.19", "0.2.20", "0.2.21", "0.2.22") +PUBLISHED_PYPI_VERSIONS = ("0.0.1", "0.0.2", "0.0.3", "0.1.0", "0.1.1", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.2.7", "0.2.8", "0.2.9", "0.2.10", "0.2.11", "0.2.12", "0.2.13", "0.2.14", "0.2.15", "0.2.16", "0.2.17", "0.2.18", "0.2.19", "0.2.20", "0.2.21", "0.2.22", "0.2.24") class ReleaseStateError(RuntimeError): @@ -339,8 +339,8 @@ def self_test() -> int: assert parsed_version == "0.1.0" assert parsed_requirement == "witnessd>=2.40.0,<3.0.0" base: dict[str, Any] = { - "pyproject_version": "0.2.24", - "setup_cfg_version": "0.2.24", + "pyproject_version": "0.2.25", + "setup_cfg_version": "0.2.25", "pyproject_requirement": "witnessd>=2.40.0,<3.0.0", "setup_cfg_requirement": "witnessd>=2.40.0,<3.0.0", "package_plan_requirement": "witnessd>=2.40.0,<3.0.0", @@ -350,14 +350,14 @@ def self_test() -> int: "engine_lock_witnessd_version": "2.40.0", "engine_lock_witnessd_ref": "v2.40.0", "manifest_witnessd_version": "2.40.0", - "package_plan_version": "0.2.24", + "package_plan_version": "0.2.25", "package_plan_status": "release-candidate", - "package_plan_published_versions": ["0.0.1", "0.0.2", "0.0.3", "0.1.0", "0.1.1", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.2.7", "0.2.8", "0.2.9", "0.2.10", "0.2.11", "0.2.12", "0.2.13", "0.2.14", "0.2.15", "0.2.16", "0.2.17", "0.2.18", "0.2.19", "0.2.20", "0.2.21", "0.2.22"], + "package_plan_published_versions": ["0.0.1", "0.0.2", "0.0.3", "0.1.0", "0.1.1", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.2.7", "0.2.8", "0.2.9", "0.2.10", "0.2.11", "0.2.12", "0.2.13", "0.2.14", "0.2.15", "0.2.16", "0.2.17", "0.2.18", "0.2.19", "0.2.20", "0.2.21", "0.2.22", "0.2.24"], "package_plan_published_package": True, "package_plan_published_package_scope": "product-line", "docs": { "README.md": ( - "The post-release target state is: `orro` 0.2.24 is published on PyPI. " + "The post-release target state is: `orro` 0.2.25 is published on PyPI. " "It becomes true only after Trusted Publishing completes." ) }, @@ -397,7 +397,7 @@ def self_test() -> int: ( "unreleased source listed as published", "package_plan_published_versions", - ["0.0.1", "0.0.2", "0.0.3", "0.1.0", "0.1.1", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.2.7", "0.2.8", "0.2.9", "0.2.10", "0.2.11", "0.2.12", "0.2.13", "0.2.14", "0.2.15", "0.2.16", "0.2.17", "0.2.18", "0.2.19", "0.2.20", "0.2.21", "0.2.22", "0.2.24"], + ["0.0.1", "0.0.2", "0.0.3", "0.1.0", "0.1.1", "0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.4", "0.2.5", "0.2.6", "0.2.7", "0.2.8", "0.2.9", "0.2.10", "0.2.11", "0.2.12", "0.2.13", "0.2.14", "0.2.15", "0.2.16", "0.2.17", "0.2.18", "0.2.19", "0.2.20", "0.2.21", "0.2.22", "0.2.24", "0.2.25"], ), ( "ambiguous package-plan publication scope", @@ -413,7 +413,7 @@ def self_test() -> int: continue fail(f"self-test accepted forgery: {label}") forged_docs = copy.deepcopy(base) - forged_docs["docs"]["README.md"] = "`orro` 0.2.24 is published on PyPI." + forged_docs["docs"]["README.md"] = "`orro` 0.2.25 is published on PyPI." try: validate_release_state(forged_docs) except ReleaseStateError: diff --git a/scripts/check_orro_repo_contract.py b/scripts/check_orro_repo_contract.py index 7325ea0..4a5dd99 100644 --- a/scripts/check_orro_repo_contract.py +++ b/scripts/check_orro_repo_contract.py @@ -568,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, "`orro` 0.2.24 is published on pypi") + require_contains("release docs", lower_text, "`orro` 0.2.25 is published on pypi") def check_os_support_matrix() -> None: @@ -643,7 +643,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, "`orro` 0.2.24 is published on PyPI") + require_contains("packaging decision docs", text, "`orro` 0.2.25 is published on PyPI") require_contains("packaging decision docs", text, "witnessd>=2.40.0,<3.0.0") require_contains("packaging decision docs", text, "no engine code") require_contains("packaging decision docs", text, "witnessd-hosted") diff --git a/scripts/check_orro_wrapper.py b/scripts/check_orro_wrapper.py index 0221f79..fab8a61 100644 --- a/scripts/check_orro_wrapper.py +++ b/scripts/check_orro_wrapper.py @@ -32,7 +32,7 @@ def require_contains(label: str, haystack: str, needle: str) -> None: def check_pyproject() -> None: text = PYPROJECT.read_text(encoding="utf-8") require_contains("pyproject.toml", text, 'name = "orro"') - require_contains("pyproject.toml", text, 'version = "0.2.24"') + require_contains("pyproject.toml", text, 'version = "0.2.25"') require_contains( "pyproject.toml", text, f'dependencies = ["{WITNESSD_REQUIREMENT}"]' ) @@ -43,7 +43,7 @@ def check_pyproject() -> None: def check_setup_cfg() -> None: text = SETUP_CFG.read_text(encoding="utf-8") require_contains("setup.cfg", text, "name = orro") - require_contains("setup.cfg", text, "version = 0.2.24") + require_contains("setup.cfg", text, "version = 0.2.25") require_contains("setup.cfg", text, WITNESSD_REQUIREMENT) require_contains("setup.cfg", text, "orro = orro_wrapper.cli:main") require_contains("setup.cfg", text, "orro-wrapper = orro_wrapper.cli:main") @@ -107,7 +107,15 @@ def check_help_discoverability() -> None: stdout = io.StringIO() with contextlib.redirect_stdout(stdout): try: - parse_args(["--help"]) + parse_args( + ["--help"], + command_map={ + "setup": "orro-setup", + "flow": "orro-flow", + "ship": "ship", + "proofrun": "proofrun", + }, + ) except SystemExit as exc: if exc.code != 0: fail("wrapper --help returned a non-zero status") @@ -115,41 +123,61 @@ def check_help_discoverability() -> None: fail("wrapper --help did not exit") help_text = stdout.getvalue() - # The help must surface every authoritative workflow command (drift => red), - # tiered so the product is discoverable rather than hidden behind `delegate`. - authoritative_commands = [ - "setup", - "skill", - "init", - "advise", - "scout", - "flow", - "flowplan", - "proofrun", - "proofcheck", - "advisory-provenance-check", - "handoff", - "ship", - "review", - "check", - "demo", - "status", - "workspace", - "auto", - "team", - "doctor", - "engine-lock", - ] - authoritative_line = " " + ", ".join(authoritative_commands) - require_contains("wrapper help", help_text, "Authoritative commands:") - require_contains("wrapper help", help_text, authoritative_line) - require_contains("wrapper help", help_text, "Start here:") - require_contains("wrapper help", help_text, "orro demo") - require_contains("wrapper help", help_text, "orro flow ") + first_screen = "\n".join(help_text.splitlines()[:15]) + require_contains( + "wrapper help usage", + first_screen, + "{boundary,self-test,delegate,setup,flow,ship,proofrun}", + ) + require_contains( + "wrapper help first screen", + first_screen, + "delegated to the pinned witnessd engine", + ) require_contains("wrapper help", help_text, "orro delegate -- ") require_contains("wrapper help", help_text, "orro delegate -- --help") +def check_surface_parity() -> None: + sys.path.insert(0, str(ROOT / "src")) + from orro_wrapper.cli import WrapperError, check_surface_parity + + command_map = { + "flow": "orro-flow", + "setup": "orro-setup", + } + skill_documents = { + "SKILL.md": """ +| Mode | Meaning | +| --- | --- | +| `orro` | root | +| `orro flow` | run | +| `orro setup` | prepare | +""", + "SKILL_INSPECT.md": """ +| Mode | Meaning | +| --- | --- | +| `orro flow --help` | inspect | +""", + } + result = check_surface_parity( + command_map=command_map, + skill_documents=skill_documents, + ) + if result["documented_commands"] != ["flow", "setup"]: + fail("surface parity did not derive commands from the skill documents") + + drifted = dict(skill_documents) + drifted["SKILL.md"] += "| `orro does-not-exist` | drift fixture |\n" + try: + check_surface_parity(command_map=command_map, skill_documents=drifted) + except WrapperError as exc: + if exc.code != "ERR_ORRO_WRAPPER_SURFACE_PARITY": + fail("surface parity returned the wrong drift error") + else: + fail("surface parity accepted a documented command missing from the engine map") + + def check_docs() -> None: text = DOC_PATH.read_text(encoding="utf-8") require_contains("thin wrapper doc", text, INVARIANT) @@ -175,6 +203,7 @@ def main() -> int: check_package_files() check_delegation_environment() check_help_discoverability() + check_surface_parity() check_docs() print("ORRO wrapper: pass") return 0 diff --git a/setup.cfg b/setup.cfg index dc293ad..e4867a5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = orro -version = 0.2.24 +version = 0.2.25 description = Published ORRO product/distribution wrapper for pinned Depone and witnessd engines. long_description = file: README.md long_description_content_type = text/markdown diff --git a/src/orro_wrapper/cli.py b/src/orro_wrapper/cli.py index 7635abc..d728ad5 100644 --- a/src/orro_wrapper/cli.py +++ b/src/orro_wrapper/cli.py @@ -7,18 +7,27 @@ from __future__ import annotations import argparse +import importlib +import importlib.metadata import json import os +import re import shlex import subprocess import sys -from typing import Any +from collections.abc import Mapping +from pathlib import Path +from typing import Any, cast from . import VersionMetadataError, get_version SCHEMA_VERSION = "0.1" DEFAULT_ENGINE_COMMAND = f"{sys.executable} -m orro" +PUBLIC_MODE_ROW = re.compile( + r"^\|\s*`orro(?:\s+([^`\s]+)[^`]*)?`\s*\|", + re.MULTILINE, +) class WrapperError(RuntimeError): @@ -108,6 +117,128 @@ def delegate(engine_command: str | None, delegate_args: list[str]) -> int: return completed.returncode +def engine_public_command_map() -> dict[str, str]: + try: + engine_module = importlib.import_module("orro.__main__") + except (ImportError, OSError) as exc: + raise WrapperError( + "ERR_ORRO_WRAPPER_ENGINE_SURFACE_UNAVAILABLE", + "the pinned witnessd engine command map is unavailable", + {"detail": str(exc)}, + ) from exc + command_map = getattr(engine_module, "ORRO_COMMAND_MAP", None) + if not isinstance(command_map, dict) or not command_map: + raise WrapperError( + "ERR_ORRO_WRAPPER_ENGINE_SURFACE_UNAVAILABLE", + "the pinned witnessd engine did not expose ORRO_COMMAND_MAP", + ) + if not all( + isinstance(public, str) + and public + and isinstance(target, str) + and target + for public, target in command_map.items() + ): + raise WrapperError( + "ERR_ORRO_WRAPPER_ENGINE_SURFACE_INVALID", + "the pinned witnessd ORRO_COMMAND_MAP must map non-empty strings", + ) + return cast(dict[str, str], command_map) + + +def packaged_skill_documents() -> dict[str, str]: + try: + distribution = importlib.metadata.distribution("witnessd") + except importlib.metadata.PackageNotFoundError as exc: + raise WrapperError( + "ERR_ORRO_WRAPPER_SKILL_SURFACE_UNAVAILABLE", + "the pinned witnessd packaged skill documents are unavailable", + {"distribution": "witnessd"}, + ) from exc + + candidate_paths: list[Path] = [] + for entry in distribution.files or (): + relative = Path(str(entry)) + if ( + relative.name.startswith("SKILL") + and relative.suffix == ".md" + and "share/witnessd" in relative.as_posix() + ): + candidate_paths.append(Path(distribution.locate_file(entry))) + for prefix in dict.fromkeys((sys.prefix, sys.base_prefix)): + candidate_paths.extend( + sorted((Path(prefix) / "share" / "witnessd").glob("SKILL*.md")) + ) + try: + engine_module = importlib.import_module("orro.__main__") + source_root = Path(engine_module.__file__).resolve().parents[1] + candidate_paths.extend(sorted(source_root.glob("SKILL*.md"))) + except (AttributeError, ImportError, OSError, TypeError): + pass + + documents: dict[str, str] = {} + for path in dict.fromkeys(candidate_paths): + if not path.is_file() or path.name in documents: + continue + try: + documents[path.name] = path.read_text(encoding="utf-8") + except (OSError, UnicodeDecodeError) as exc: + raise WrapperError( + "ERR_ORRO_WRAPPER_SKILL_SURFACE_UNAVAILABLE", + "a pinned witnessd packaged skill document could not be read", + {"path": str(path), "detail": str(exc)}, + ) from exc + if not documents: + raise WrapperError( + "ERR_ORRO_WRAPPER_SKILL_SURFACE_UNAVAILABLE", + "the pinned witnessd distribution contains no packaged skill documents", + {"distribution": "witnessd"}, + ) + return documents + + +def documented_public_commands(skill_documents: Mapping[str, str]) -> set[str]: + commands: set[str] = set() + for text in skill_documents.values(): + commands.update( + command + for command in PUBLIC_MODE_ROW.findall(text) + if command + ) + return commands + + +def check_surface_parity( + *, + command_map: Mapping[str, str] | None = None, + skill_documents: Mapping[str, str] | None = None, +) -> dict[str, Any]: + resolved_map = ( + dict(command_map) if command_map is not None else engine_public_command_map() + ) + resolved_documents = ( + dict(skill_documents) + if skill_documents is not None + else packaged_skill_documents() + ) + documented_commands = documented_public_commands(resolved_documents) + missing_commands = sorted(documented_commands - set(resolved_map)) + if missing_commands: + raise WrapperError( + "ERR_ORRO_WRAPPER_SURFACE_PARITY", + "packaged skill documents name commands missing from the pinned engine map", + { + "missing_commands": missing_commands, + "skill_documents": sorted(resolved_documents), + }, + ) + return { + "documented_commands": sorted(documented_commands), + "engine_commands": sorted(resolved_map), + "skill_documents": sorted(resolved_documents), + } + + def self_test() -> int: info = wrapper_info() assert info["kind"] == "orro-wrapper-info" @@ -125,12 +256,26 @@ def self_test() -> int: assert exc.code == "ERR_ORRO_WRAPPER_DELEGATE_ARGS_REQUIRED" else: raise AssertionError("empty delegate args did not fail") + try: + parity = check_surface_parity() + except WrapperError as exc: + if exc.code not in { + "ERR_ORRO_WRAPPER_ENGINE_SURFACE_UNAVAILABLE", + "ERR_ORRO_WRAPPER_SKILL_SURFACE_UNAVAILABLE", + }: + raise + parity = { + "decision": "not-checked", + "reason": exc.code, + "detail": exc.message, + } print( json.dumps( { "kind": "orro-wrapper-self-test-result", "schema_version": SCHEMA_VERSION, "decision": "pass", + "surface_parity": parity, "boundary": boundary(), }, indent=2, @@ -140,56 +285,26 @@ def self_test() -> int: return 0 -def parse_args(argv: list[str]) -> argparse.Namespace: - # This list mirrors the engine's ORRO_COMMAND_MAP public keys; keep it in sync. +def parse_args( + argv: list[str], + *, + command_map: Mapping[str, str] | None = None, +) -> argparse.Namespace: + delegated_commands = dict(command_map or {}) parser = argparse.ArgumentParser( - description="ORRO product command.", - epilog="""Workflow commands run directly (e.g. `orro demo`, `orro flow `); -they are delegated to the pinned witnessd engine, not implemented here. - -Start here: - demo AI-free 30s guardrail demo: Depone scope-conformance PASS/FAIL - flow guided init -> scout -> flowplan -> proofrun -> proofcheck - check verify already-driven work (Depone verdict) + read-only review - doctor engine/verifier/adapter readiness - -Workflow: - scout, flowplan, proofrun, proofcheck, handoff, review - advise routes the goal itself: bug-shaped -> trace advisory, new work -> sketch advisory (--mode overrides) - ship push a ship-ready branch and optionally open a PR (evidence-gated); merge approval stays human - -Automation: - auto, team - -Project legibility: - status roadmap items with evidence-bound state + off-plan runs + workspace summary - workspace begin - persistent per-item task worktree lifecycle (begin + optional open hook) - workspace tidy - inventory (default) and safe cleanup of run worktrees (--apply) - -Setup / diagnostics: - setup, skill, init, doctor, engine-lock, advisory-provenance-check - -Removed aliases: - sketch/trace -> advise --mode ..., next -> auto --dry-run, report -> status | --latest, lock -> engine-lock - -Authoritative commands: - setup, skill, init, advise, scout, flow, flowplan, proofrun, proofcheck, advisory-provenance-check, handoff, ship, review, check, demo, status, workspace, auto, team, doctor, engine-lock - -Try it in 30s (no AI adapter): - orro demo - -Run the guided workflow end-to-end (init -> scout -> flowplan -> proofrun -> proofcheck) with: - orro flow - -Any workflow command can also be run explicitly through the engine with: - orro delegate -- - -Show the authoritative engine command list with: - orro delegate -- --help - -The wrapper delegates execution; it does not implement these workflow commands.""", + description=( + "ORRO product command.\n\n" + "All workflow commands shown in usage are delegated to the pinned " + "witnessd engine; the wrapper does not implement engine logic." + ), + epilog="""Run a workflow command directly: + orro [args] + +Run it through the explicit delegation form: + orro delegate -- [args] + +Show the authoritative engine command list: + orro delegate -- --help""", formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument( @@ -208,12 +323,16 @@ def parse_args(argv: list[str]) -> argparse.Namespace: subparsers.add_parser("boundary", help="Print wrapper boundary metadata.") subparsers.add_parser( - "self-test", help="Run wrapper self-test without calling engines." + "self-test", + help="Check wrapper boundaries and packaged skill/engine command parity.", ) delegate_parser = subparsers.add_parser( "delegate", help="Delegate explicitly to the witnessd-hosted ORRO command." ) delegate_parser.add_argument("delegate_args", nargs=argparse.REMAINDER) + for command in delegated_commands: + if command not in {"boundary", "self-test", "delegate"}: + subparsers.add_parser(command, add_help=False) return parser.parse_args(argv) @@ -226,8 +345,12 @@ def main(argv: list[str] | None = None) -> int: and not raw_args[0].startswith("-") ): return delegate(None, raw_args) - args = parse_args(raw_args) try: + help_requested = raw_args == ["-h"] or raw_args == ["--help"] + args = parse_args( + raw_args, + command_map=engine_public_command_map() if help_requested else None, + ) if args.version: print(get_version()) return 0