From 514b5561655d1bd72fd62f228f22e2d5284d2b77 Mon Sep 17 00:00:00 2001 From: brainx <12695242+brainx@users.noreply.github.com> Date: Fri, 31 Jul 2026 22:27:53 +0200 Subject: [PATCH] Pin Olymp to Zeus v0.5.0 --- CHANGELOG.md | 6 +-- README.md | 14 +++---- docs/OPERATIONS.md | 14 +++---- docs/PROJECT_PLAN.md | 12 +++--- docs/STAGING_RUNBOOK.md | 12 +++--- docs/THREAT_MODEL.md | 2 +- ...5.0.dev0.json => zeus-openapi-v0.5.0.json} | 2 +- olymp/plans.py | 2 +- scripts/repo_check.sh | 2 +- scripts/verify_real_zeus.sh | 22 ++++++++-- tests/test_api_security.py | 8 ++-- tests/test_bot_creation.py | 22 +++++----- tests/test_cli_api.py | 34 +++++++-------- tests/test_events.py | 2 +- tests/test_plans_evidence.py | 26 ++++++------ tests/test_policy.py | 41 +++++++++---------- tests/test_repo_contracts.py | 33 ++++++++------- tests/test_rollouts.py | 2 +- 18 files changed, 136 insertions(+), 120 deletions(-) rename docs/{zeus-openapi-v0.5.0.dev0.json => zeus-openapi-v0.5.0.json} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46ed4c1..25055a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,11 @@ - Added a SQLite-backed operation event bus with CLI/API timeline readers and in-process plugin subscriber hooks for extensions. -- Updated the Zeus mutation compatibility target to exact `0.5.0.dev0` at - upstream contract commit `f4a7af8a0ce802b3f9daeed0ee44958ef92abf1a`, +- Updated the Zeus mutation compatibility target to exact `0.5.0` at + upstream contract commit `ce9dfdfd7c9ea7dc367fd6c1cb15ab8be0d1ea95`, while retaining v0.3.0 and v0.1.4 as legacy read-only contracts. - Added authenticated Zeus `/ready` support, fail-closed `not_ready` mutation - preflights, schema-version validation, and an exact `0.5.0.dev0` + preflights, schema-version validation, and an exact `0.5.0` `expected_version` gate. - Added contract coverage for creating `kimi-k3-coding-bot` using only bot and template metadata. Provider credentials and harness configuration remain in diff --git a/README.md b/README.md index ce088dd..84574f4 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ Zeus. Operators run Zeus from the upstream project on each node, keep the Zeus API loopback-only, and register those nodes in Olymp for inventory, planning, approved lifecycle execution, audit trails, and evidence export. -The current mutation compatibility target is Zeus `0.5.0.dev0`, captured -exactly from upstream commit `f4a7af8a0ce802b3f9daeed0ee44958ef92abf1a` -in `docs/zeus-openapi-v0.5.0.dev0.json`. The retained v0.3.0 and v0.1.4 +The current mutation compatibility target is Zeus `0.5.0`, captured +exactly from upstream commit `ce9dfdfd7c9ea7dc367fd6c1cb15ab8be0d1ea95` +in `docs/zeus-openapi-v0.5.0.json`. The retained v0.3.0 and v0.1.4 fixtures are legacy read-only contracts. ## Goals @@ -70,8 +70,8 @@ fixtures are legacy read-only contracts. - CLI: `olymp` - Local API: `127.0.0.1:4321` by default - State: SQLite for the local control plane, with append-only JSONL audit logs -- Integration: typed Zeus client checked against the Zeus `0.5.0.dev0` - mutation compatibility fixture in `docs/zeus-openapi-v0.5.0.dev0.json`, +- Integration: typed Zeus client checked against the Zeus `0.5.0` + mutation compatibility fixture in `docs/zeus-openapi-v0.5.0.json`, with v0.3.0 and v0.1.4 kept as legacy read-only fixtures - Deployment: systemd service, optional Caddy HTTPS front door for the Olymp UI/API, Zeus nodes kept loopback-only behind local agents or SSH/mTLS tunnels @@ -132,7 +132,7 @@ olymp node add local-zeus \ --name "Local Zeus" \ --base-url http://127.0.0.1:4311 \ --api-key-env ZEUS_API_KEY \ - --expected-version 0.5.0.dev0 + --expected-version 0.5.0 olymp node check local-zeus olymp zeus ready local-zeus @@ -144,7 +144,7 @@ Bot history is returned newest first. Pass the previous response's `next_before` value to `--before` to fetch the next page. Mutation plans require every target node to declare the exact reviewed -`0.5.0.dev0` `expected_version`. Health, inventory, and history remain +`0.5.0` `expected_version`. Health, inventory, and history remain available for versionless or legacy nodes, but execution rechecks the current registration and authenticated Zeus `/ready` response before any mutation. Zeus `not_ready` responses fail closed without recording a mutation attempt. diff --git a/docs/OPERATIONS.md b/docs/OPERATIONS.md index d298e44..68af323 100644 --- a/docs/OPERATIONS.md +++ b/docs/OPERATIONS.md @@ -43,8 +43,8 @@ requires the spawned instance's nonce. Olymp operates registered [brainx/zeus](https://github.com/brainx/zeus) nodes. Install and run Zeus from that upstream project on each host, keep its API bound to loopback, then register the local or tunneled endpoint in Olymp. -The current mutation compatibility target is Zeus `0.5.0.dev0` at contract -commit `f4a7af8a0ce802b3f9daeed0ee44958ef92abf1a`. Zeus v0.3.0 and +The current mutation compatibility target is Zeus `0.5.0` at contract +commit `ce9dfdfd7c9ea7dc367fd6c1cb15ab8be0d1ea95`. Zeus v0.3.0 and v0.1.4 remain available only as legacy read-only contracts. Register a loopback Zeus node: @@ -55,11 +55,11 @@ olymp node add local-zeus \ --name "Local Zeus" \ --base-url http://127.0.0.1:4311 \ --api-key-env ZEUS_API_KEY \ - --expected-version 0.5.0.dev0 + --expected-version 0.5.0 ``` Every mutation plan target must declare `expected_version` in the supported -exact `0.5.0.dev0` contract. Planning and execution fail closed for missing, +exact `0.5.0` contract. Planning and execution fail closed for missing, malformed, older, or unreviewed newer versions. Read-only health, inventory, and history remain available for legacy/versionless nodes. @@ -337,9 +337,9 @@ audit directories, audit files, and audit lock files under the state directory. ## Compatibility Olymp's mutation compatibility target is -[brainx/zeus](https://github.com/brainx/zeus) `0.5.0.dev0` at commit -`f4a7af8a0ce802b3f9daeed0ee44958ef92abf1a`, captured as an exact, immutable -upstream copy in `docs/zeus-openapi-v0.5.0.dev0.json`. Repository and CI +[brainx/zeus](https://github.com/brainx/zeus) `0.5.0` at commit +`ce9dfdfd7c9ea7dc367fd6c1cb15ab8be0d1ea95`, captured as an exact, immutable +upstream copy in `docs/zeus-openapi-v0.5.0.json`. Repository and CI contract checks use this vendored fixture and do not require network access. The retained v0.3.0 and v0.1.4 contracts are legacy read-only fixtures; they are not mutation compatibility targets. diff --git a/docs/PROJECT_PLAN.md b/docs/PROJECT_PLAN.md index 0c6708c..e05e25c 100644 --- a/docs/PROJECT_PLAN.md +++ b/docs/PROJECT_PLAN.md @@ -17,12 +17,12 @@ node connectors rather than asking operators to expose Zeus directly. Olymp should treat the upstream [brainx/zeus](https://github.com/brainx/zeus) OpenAPI file as the integration contract and test against it on every release. -Zeus `0.5.0.dev0` at commit -`f4a7af8a0ce802b3f9daeed0ee44958ef92abf1a` is the current mutation +Zeus `0.5.0` at commit +`ce9dfdfd7c9ea7dc367fd6c1cb15ab8be0d1ea95` is the current mutation compatibility target, vendored unchanged as -`docs/zeus-openapi-v0.5.0.dev0.json`. The v0.3.0 and v0.1.4 contracts remain +`docs/zeus-openapi-v0.5.0.json`. The v0.3.0 and v0.1.4 contracts remain available only as legacy read-only fixtures. Mutation planning and execution -require each registered target to declare the exact reviewed `0.5.0.dev0` +require each registered target to declare the exact reviewed `0.5.0` `expected_version`; read-only access remains available for legacy or versionless nodes. @@ -146,14 +146,14 @@ Status: initial foundation implemented. ### 1. Zeus Client MVP -- Implement a typed client for the Zeus OpenAPI `0.5.0.dev0` mutation surface +- Implement a typed client for the Zeus OpenAPI `0.5.0` mutation surface while retaining v0.3.0 and v0.1.4 as legacy read-only compatibility. - Cover auth success/failure, missing key behavior, timeouts, malformed JSON, response-size limits, and diagnostic endpoint redaction. - Add contract tests using a fake Zeus server. Status: the client and fake-Zeus tests are implemented. The exact Zeus -`0.5.0.dev0` contract is the mutation compatibility target; the v0.3.0 and +`0.5.0` contract is the mutation compatibility target; the v0.3.0 and v0.1.4 fixtures are retained as legacy read-only coverage. Timeout-specific tests are implemented. diff --git a/docs/STAGING_RUNBOOK.md b/docs/STAGING_RUNBOOK.md index 34a8786..7887ff6 100644 --- a/docs/STAGING_RUNBOOK.md +++ b/docs/STAGING_RUNBOOK.md @@ -20,12 +20,12 @@ API: ZEUS_API_KEY=change-me OLYMP_API_KEY=change-me sh scripts/verify_real_zeus.sh ``` -The verifier is pinned to the Zeus `0.5.0.dev0` contract commit -`f4a7af8a0ce802b3f9daeed0ee44958ef92abf1a`, registers the smoke node with -`expected_version` `0.5.0.dev0`, checks authenticated `/ready` plus inventory, -creates and executes an approved lifecycle plan only when a disposable -existing bot is explicitly configured, exports evidence, and removes its -temporary state by default. +The verifier is pinned to the Zeus `0.5.0` contract commit +`ce9dfdfd7c9ea7dc367fd6c1cb15ab8be0d1ea95`, registers the smoke node with +`expected_version` `0.5.0`, checks authenticated `/ready`, requires the +`kimi-k3-coding-bot` template, and reads inventory. It creates and executes an +approved lifecycle plan only when a disposable existing bot is explicitly +configured, exports evidence, and removes its temporary state by default. It does not create or replace bots. Set `OLYMP_REAL_ZEUS_KEEP_STATE=1` to preserve the verifier database, audit log, and evidence bundle for review. diff --git a/docs/THREAT_MODEL.md b/docs/THREAT_MODEL.md index 2b10e22..fcc20de 100644 --- a/docs/THREAT_MODEL.md +++ b/docs/THREAT_MODEL.md @@ -65,7 +65,7 @@ That makes Olymp security-sensitive even when the first release is local-only. - Require dry-run output, policy validation, and explicit approval before mutating fleet actions. - Require every mutation target to declare the exact reviewed Zeus - `0.5.0.dev0` `expected_version`, then recheck the current registration and + `0.5.0` `expected_version`, then recheck the current registration and authenticated `/ready` response before Zeus mutation I/O. Treat `not_ready` as a fail-closed preflight result. - Allow normal `bot.create`, keep `bot.replace` and `bot.replace_stop` diff --git a/docs/zeus-openapi-v0.5.0.dev0.json b/docs/zeus-openapi-v0.5.0.json similarity index 99% rename from docs/zeus-openapi-v0.5.0.dev0.json rename to docs/zeus-openapi-v0.5.0.json index b38dc0d..c9241cd 100644 --- a/docs/zeus-openapi-v0.5.0.dev0.json +++ b/docs/zeus-openapi-v0.5.0.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Zeus Local API", - "version": "0.5.0.dev0", + "version": "0.5.0", "description": "Local JSON API for managing Hermes bot profiles and gateway lifecycle." }, "servers": [ diff --git a/olymp/plans.py b/olymp/plans.py index 32439d5..a08d75c 100644 --- a/olymp/plans.py +++ b/olymp/plans.py @@ -13,7 +13,7 @@ from olymp.store import node_fingerprint LIFECYCLE_ACTIONS = {"start", "stop", "restart", "reconcile"} -SUPPORTED_ZEUS_MUTATION_VERSION = "0.5.0.dev0" +SUPPORTED_ZEUS_MUTATION_VERSION = "0.5.0" # Zeus readiness waits are deliberately bounded so approved lifecycle plans have # one predictable execution and transport budget. LIFECYCLE_WAIT_TIMEOUT_SECONDS = 30 diff --git a/scripts/repo_check.sh b/scripts/repo_check.sh index e1468b9..4642f2b 100755 --- a/scripts/repo_check.sh +++ b/scripts/repo_check.sh @@ -9,7 +9,7 @@ for json_file in \ docs/openapi.json \ docs/zeus-openapi-v0.1.4.json \ docs/zeus-openapi-v0.3.0.json \ - docs/zeus-openapi-v0.5.0.dev0.json; do + docs/zeus-openapi-v0.5.0.json; do "$PYTHON" -m json.tool "$json_file" >/dev/null done "$PYTHON" -m compileall -q olymp tests diff --git a/scripts/verify_real_zeus.sh b/scripts/verify_real_zeus.sh index 3effba9..4ea2a0a 100755 --- a/scripts/verify_real_zeus.sh +++ b/scripts/verify_real_zeus.sh @@ -42,8 +42,8 @@ esac state_created=0 started_zeus=0 -install_dir="${OLYMP_REAL_ZEUS_DIR:-.tmp/zeus-v0.5.0.dev0}" -zeus_contract_commit="f4a7af8a0ce802b3f9daeed0ee44958ef92abf1a" +install_dir="${OLYMP_REAL_ZEUS_DIR:-.tmp/zeus-v0.5.0}" +zeus_contract_commit="ce9dfdfd7c9ea7dc367fd6c1cb15ab8be0d1ea95" zeus_host="${ZEUS_HOST:-127.0.0.1}" zeus_port="${ZEUS_PORT:-4311}" zeus_url="${ZEUS_BASE_URL:-http://$zeus_host:$zeus_port}" @@ -112,7 +112,7 @@ if [ "$checkout" = "1" ]; then git -C "$install_dir" checkout --detach "$zeus_contract_commit" fi validate_zeus_checkout - echo "Zeus 0.5.0.dev0 contract checkout is available at $install_dir" + echo "Zeus 0.5.0 contract checkout is available at $install_dir" fi if [ "$start_zeus" = "1" ] && [ "$checkout" = "0" ]; then @@ -162,10 +162,24 @@ fi --name "Real Zeus Smoke" \ --base-url "$zeus_url" \ --api-key-env ZEUS_API_KEY \ - --expected-version 0.5.0.dev0 >/dev/null + --expected-version 0.5.0 >/dev/null "$PYTHON" -m olymp.cli node check "$node_id" >/dev/null "$PYTHON" -m olymp.cli zeus ready "$node_id" >/dev/null +"$PYTHON" -m olymp.cli zeus templates "$node_id" | + "$PYTHON" -c ' +import json +import sys + +templates = json.load(sys.stdin) +template_ids = { + item.get("id") + for item in templates + if isinstance(item, dict) and isinstance(item.get("id"), str) +} +if "kimi-k3-coding-bot" not in template_ids: + raise SystemExit("Zeus templates are missing kimi-k3-coding-bot") +' "$PYTHON" -m olymp.cli zeus inventory "$node_id" >/dev/null if [ -n "$bot_id" ]; then diff --git a/tests/test_api_security.py b/tests/test_api_security.py index 1cce8d2..94f7c87 100644 --- a/tests/test_api_security.py +++ b/tests/test_api_security.py @@ -78,7 +78,7 @@ def test_cli_redacts_expected_exception_message(self) -> None: node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) stdout = StringIO() @@ -114,7 +114,7 @@ def test_lifecycle_rejects_non_string_text_without_saving_plan(self) -> None: node_id="123", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) store = PlanRunStore(path) @@ -157,7 +157,7 @@ def test_reconcile_rejects_non_string_text_without_saving_plan(self) -> None: node_id="123", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) store = PlanRunStore(path) @@ -210,7 +210,7 @@ def bot_status(self, bot_id: str) -> dict[str, str]: node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) plan = lifecycle_plan( diff --git a/tests/test_bot_creation.py b/tests/test_bot_creation.py index a62cba0..beb6579 100644 --- a/tests/test_bot_creation.py +++ b/tests/test_bot_creation.py @@ -31,7 +31,7 @@ def test_single_plan_rechecks_current_version_before_client_construction(self) - with tempfile.TemporaryDirectory() as temp: db_path = Path(temp) / "olymp.db" registry = NodeRegistry(db_path) - node = registry.add(_node(expected_version="0.5.0.dev0")) + node = registry.add(_node(expected_version="0.5.0")) plan = lifecycle_plan( node, bot_id="coder", @@ -56,7 +56,7 @@ def test_single_plan_rechecks_current_version_before_client_construction(self) - self.assertIsNotNone(run) assert run is not None self.assertEqual(run["error"]["code"], "incompatible_zeus_version") - self.assertIn("0.5.0.dev0", run["error"]["message"]) + self.assertIn("0.5.0", run["error"]["message"]) self.assertEqual(client_requests, []) self.assertEqual(run["attempts"], []) @@ -64,7 +64,7 @@ def test_rollout_rechecks_each_current_version_before_zeus_io(self) -> None: with tempfile.TemporaryDirectory() as temp: db_path = Path(temp) / "olymp.db" registry = NodeRegistry(db_path) - node = registry.add(_node(expected_version="0.5.0.dev0", tags=("staging",))) + node = registry.add(_node(expected_version="0.5.0", tags=("staging",))) plan = rollout_lifecycle_plan( (node,), selector_tags=("staging",), @@ -106,7 +106,7 @@ def test_rollout_validates_all_current_versions_before_any_client_construction( node_id=f"staging-{suffix}", display_name=f"Staging {suffix.upper()}", base_url=f"http://127.0.0.1:{port}", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("staging",), ) ) @@ -208,7 +208,7 @@ def test_create_actions_execute_once_with_exact_body_flags_and_durable_metadata( with self.subTest(action=action), tempfile.TemporaryDirectory() as temp: db_path = Path(temp) / "olymp.db" registry = NodeRegistry(db_path) - node = registry.add(_node(expected_version="0.5.0.dev0")) + node = registry.add(_node(expected_version="0.5.0")) plan = create_bot_plan( node, bot_id="coder", @@ -273,7 +273,7 @@ def test_kimi_k3_template_creation_keeps_provider_credentials_in_zeus(self) -> N with tempfile.TemporaryDirectory() as temp: db_path = Path(temp) / "olymp.db" registry = NodeRegistry(db_path) - node = registry.add(_node(expected_version="0.5.0.dev0")) + node = registry.add(_node(expected_version="0.5.0")) plan = create_bot_plan( node, bot_id="kimi-coder", @@ -322,7 +322,7 @@ def test_not_ready_preflight_blocks_mutation_without_attempt(self) -> None: with tempfile.TemporaryDirectory() as temp: db_path = Path(temp) / "olymp.db" registry = NodeRegistry(db_path) - node = registry.add(_node(expected_version="0.5.0.dev0")) + node = registry.add(_node(expected_version="0.5.0")) plan = create_bot_plan( node, bot_id="coder", @@ -384,7 +384,7 @@ def set_body(plan: dict[str, Any], body: dict[str, Any]) -> None: with self.subTest(case=name), tempfile.TemporaryDirectory() as temp: db_path = Path(temp) / "olymp.db" registry = NodeRegistry(db_path) - node = registry.add(_node(expected_version="0.5.0.dev0")) + node = registry.add(_node(expected_version="0.5.0")) plan = create_bot_plan( node, bot_id="coder", @@ -445,7 +445,7 @@ def test_create_postcheck_fails_closed_and_preserves_mutation_evidence(self) -> with self.subTest(case=name), tempfile.TemporaryDirectory() as temp: db_path = Path(temp) / "olymp.db" registry = NodeRegistry(db_path) - node = registry.add(_node(expected_version="0.5.0.dev0")) + node = registry.add(_node(expected_version="0.5.0")) plan = create_bot_plan( node, bot_id="coder", @@ -483,7 +483,7 @@ def test_indeterminate_create_is_recorded_once_and_never_automatically_repeated( with tempfile.TemporaryDirectory() as temp: db_path = Path(temp) / "olymp.db" registry = NodeRegistry(db_path) - node = registry.add(_node(expected_version="0.5.0.dev0")) + node = registry.add(_node(expected_version="0.5.0")) plan = create_bot_plan( node, bot_id="coder", @@ -852,7 +852,7 @@ def _add_cli_node() -> int: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ) diff --git a/tests/test_cli_api.py b/tests/test_cli_api.py index 239c3eb..53254cd 100644 --- a/tests/test_cli_api.py +++ b/tests/test_cli_api.py @@ -118,7 +118,7 @@ def test_cli_node_lifecycle_uses_workspace_state(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -148,7 +148,7 @@ def test_cli_zeus_ready_uses_authenticated_contract(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -172,7 +172,7 @@ def test_cli_redacts_output(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -203,7 +203,7 @@ def test_cli_zeus_history_defaults_and_audits_only_safe_metadata(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -257,7 +257,7 @@ def test_cli_zeus_history_forwards_pagination(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -308,7 +308,7 @@ def test_cli_zeus_history_rejects_control_character_id_before_upstream_io(self) "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -545,7 +545,7 @@ def test_api_node_read_endpoints(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -595,7 +595,7 @@ def test_api_bot_history_is_authenticated_decodes_ids_and_paginates_once(self) - "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -664,7 +664,7 @@ def test_api_bot_history_defaults_without_extra_zeus_requests(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -717,7 +717,7 @@ def test_api_bot_history_rejects_invalid_query_before_zeus_request(self) -> None "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -759,7 +759,7 @@ def test_api_bot_history_rejects_invalid_encoded_ids_before_zeus_request(self) - "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -823,7 +823,7 @@ def test_api_bot_history_maps_unknown_node_and_zeus_errors(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -862,7 +862,7 @@ def test_api_bot_history_maps_unknown_node_and_zeus_errors(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -907,7 +907,7 @@ def test_api_bot_history_maps_invalid_upstream_envelope_without_success_audit(se "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -950,7 +950,7 @@ def test_cli_zeus_history_returns_error_when_upstream_request_id_is_missing(self "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -984,7 +984,7 @@ def test_api_plan_run_and_evidence_endpoints(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -1103,7 +1103,7 @@ def test_api_rollout_lifecycle_plan_endpoint(self) -> None: "--base-url", f"http://127.0.0.1:{port_number}", "--expected-version", - "0.5.0.dev0", + "0.5.0", "--tag", "staging", ] diff --git a/tests/test_events.py b/tests/test_events.py index ac7427c..944f4a9 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -169,7 +169,7 @@ def test_plan_run_and_mutation_methods_emit_timeline_events(self) -> None: node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) plan = lifecycle_plan( diff --git a/tests/test_plans_evidence.py b/tests/test_plans_evidence.py index 801f241..993fbcd 100644 --- a/tests/test_plans_evidence.py +++ b/tests/test_plans_evidence.py @@ -36,7 +36,7 @@ def test_legacy_rollout_step_records_actual_wait_request_path(self) -> None: node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("staging",), ) ) @@ -151,7 +151,7 @@ def test_execution_persists_running_run_and_started_attempt_before_mutation(self node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) plan = lifecycle_plan( @@ -234,7 +234,7 @@ def test_execution_marks_transport_without_http_status_indeterminate(self) -> No node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) plan = lifecycle_plan( @@ -344,7 +344,7 @@ def test_cli_lifecycle_plan_is_dry_run_and_audited(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -401,7 +401,7 @@ def test_cli_plan_rejects_invalid_bot_id(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -493,7 +493,7 @@ def test_concurrent_plan_execution_claim_allows_exactly_one_mutation(self) -> No node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) plan = lifecycle_plan( @@ -640,7 +640,7 @@ def test_plan_rollout_lifecycle_executes_each_selected_node(self) -> None: "--base-url", f"http://127.0.0.1:{port}", "--expected-version", - "0.5.0.dev0", + "0.5.0", "--tag", "staging", ] @@ -710,7 +710,7 @@ def test_plan_rollout_preflight_error_records_failed_run(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", "--tag", "staging", ] @@ -958,7 +958,7 @@ def test_plan_execute_rejects_stale_node_before_zeus_request(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -1027,7 +1027,7 @@ def test_evidence_bundle_redacts_audit_and_writes_private_file(self) -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ), 0, @@ -1145,7 +1145,7 @@ def test_lifecycle_postcheck_requires_exact_converged_bot_projection(self) -> No node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) plan = lifecycle_plan( @@ -1204,7 +1204,7 @@ def test_reconcile_summary_is_authoritative_and_never_falls_back(self) -> None: node_id="local", display_name="Local Zeus", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) ) from olymp.plans import reconcile_node_plan @@ -1353,7 +1353,7 @@ def _add_local_node() -> None: "--base-url", "http://127.0.0.1:4311", "--expected-version", - "0.5.0.dev0", + "0.5.0", ] ) if self_code != 0: diff --git a/tests/test_policy.py b/tests/test_policy.py index ecabc89..bb919e0 100644 --- a/tests/test_policy.py +++ b/tests/test_policy.py @@ -35,7 +35,7 @@ def test_create_bot_plan_uses_distinct_action_path_and_exact_body_variants(self) node_id="staging", display_name="Staging", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) replacement_policy = Policy( allowed_actions=(*DEFAULT_ALLOWED_ACTIONS, "bot.replace", "bot.replace_stop") @@ -78,7 +78,7 @@ def test_replacement_plans_are_default_denied_and_require_explicit_policy_opt_in node_id="staging", display_name="Staging", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) for stop, action in ((False, "bot.replace"), (True, "bot.replace_stop")): with self.subTest(action=action), self.assertRaisesRegex(PolicyViolation, action): @@ -96,7 +96,7 @@ def test_create_bot_plan_rejects_unsafe_flag_and_display_name_values(self) -> No node_id="staging", display_name="Staging", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) invalid_cases = ( ({"stop": True}, "stop requires replace"), @@ -123,7 +123,7 @@ def test_all_create_variants_require_reason_even_when_policy_disables_it(self) - node_id="staging", display_name="Staging", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) policy = Policy( require_reason_for_mutation=False, @@ -155,7 +155,7 @@ def test_create_bot_plan_rejects_control_characters_in_identifiers(self) -> None node_id="staging", display_name="Staging", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) for field in ("bot_id", "template_id"): values = {"bot_id": "coder", "template_id": "coding-bot"} @@ -163,7 +163,7 @@ def test_create_bot_plan_rejects_control_characters_in_identifiers(self) -> None with self.subTest(field=field), self.assertRaisesRegex(ValueError, "path segment"): create_bot_plan(node, reason="provision coder", **values) - def test_mutation_plans_require_declared_zeus_03_patch_version(self) -> None: + def test_mutation_plans_require_exact_zeus_050_version(self) -> None: builders = { "lifecycle": lambda node: lifecycle_plan( node, @@ -192,10 +192,9 @@ def test_mutation_plans_require_declared_zeus_03_patch_version(self) -> None: for version in ( None, "", - "v0.5.0.dev0", + "v0.5.0", "0.3.0", "0.3.42", - "0.5.0", "0.5.0.dev1", ): node = ZeusNode( @@ -208,16 +207,16 @@ def test_mutation_plans_require_declared_zeus_03_patch_version(self) -> None: for name, build in builders.items(): with ( self.subTest(version=version, builder=name), - self.assertRaisesRegex(ValueError, r"expected_version.*0\.5\.0\.dev0"), + self.assertRaisesRegex(ValueError, r"expected_version.*0\.5\.0"), ): build(node) - def test_mutation_plans_accept_exact_zeus_050_dev0_version(self) -> None: + def test_mutation_plans_accept_exact_zeus_050_version(self) -> None: node = ZeusNode( node_id="staging", display_name="Staging", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) plan = lifecycle_plan( @@ -227,14 +226,14 @@ def test_mutation_plans_accept_exact_zeus_050_dev0_version(self) -> None: reason="maintenance", ) - self.assertEqual(plan.node.expected_version, "0.5.0.dev0") + self.assertEqual(plan.node.expected_version, "0.5.0") def test_lifecycle_plan_requires_reason(self) -> None: node = ZeusNode( node_id="local", display_name="Local", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) with self.assertRaisesRegex(PolicyViolation, "reason"): @@ -245,7 +244,7 @@ def test_restart_plan_requires_rollback_note(self) -> None: node_id="local", display_name="Local", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", ) with self.assertRaisesRegex(PolicyViolation, "rollback"): @@ -261,7 +260,7 @@ def test_policy_blocks_denied_tags(self) -> None: node_id="prod", display_name="Production", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("production",), ) @@ -274,14 +273,14 @@ def test_rollout_policy_limits_parallelism(self) -> None: node_id="staging-a", display_name="Staging A", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("staging",), ), ZeusNode( node_id="staging-b", display_name="Staging B", base_url="http://127.0.0.1:4312", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("staging",), ), ) @@ -303,14 +302,14 @@ def test_rollout_rejects_parallel_execution_when_policy_limit_is_higher(self) -> node_id="staging-a", display_name="Staging A", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("staging",), ), ZeusNode( node_id="staging-b", display_name="Staging B", base_url="http://127.0.0.1:4312", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("staging",), ), ) @@ -344,7 +343,7 @@ def test_policy_file_overrides_deny_tags(self) -> None: node_id="prod", display_name="Production", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("production",), ) @@ -380,7 +379,7 @@ def test_break_glass_requires_incident_prefix(self) -> None: node_id="prod", display_name="Production", base_url="http://127.0.0.1:4311", - expected_version="0.5.0.dev0", + expected_version="0.5.0", tags=("production",), ) policy = load_policy(path) diff --git a/tests/test_repo_contracts.py b/tests/test_repo_contracts.py index bb98425..5582e04 100644 --- a/tests/test_repo_contracts.py +++ b/tests/test_repo_contracts.py @@ -34,7 +34,7 @@ def test_required_publication_files_exist(self) -> None: "docs/openapi.json", "docs/zeus-openapi-v0.1.4.json", "docs/zeus-openapi-v0.3.0.json", - "docs/zeus-openapi-v0.5.0.dev0.json", + "docs/zeus-openapi-v0.5.0.json", "docs/assets/olymp-social-preview-chain.png", "examples/benchmark/olymp.env.example", "examples/benchmark/zeus.env.example", @@ -105,22 +105,22 @@ def test_legacy_zeus_fixture_remains_read_only_compatibility_surface(self) -> No self.assertIn(path, paths) self.assertTrue(methods.issubset(paths[path]), path) - def test_current_zeus_fixture_is_exact_upstream_v050_dev0_contract(self) -> None: - fixture = Path("docs/zeus-openapi-v0.5.0.dev0.json") + def test_current_zeus_fixture_is_exact_upstream_v050_contract(self) -> None: + fixture = Path("docs/zeus-openapi-v0.5.0.json") self.assertEqual( hashlib.sha256(fixture.read_bytes()).hexdigest(), - "9ed13a758f3463308556f48fc133281db2953b069c4d8aaec5fae60c041359b1", + "dbf873a17a2a5d10af5193312b6789a7fb95782576d3f8dd2492845b18762a71", ) zeus_contract = json.loads(fixture.read_text(encoding="utf-8")) - self.assertEqual(zeus_contract["info"]["version"], "0.5.0.dev0") + self.assertEqual(zeus_contract["info"]["version"], "0.5.0") self.assertEqual( zeus_contract["components"]["securitySchemes"]["ZeusApiKey"]["name"], "x-zeus-api-key", ) - def test_current_zeus_fixture_covers_v050_dev0_mutation_contract(self) -> None: + def test_current_zeus_fixture_covers_v050_mutation_contract(self) -> None: zeus_contract = json.loads( - Path("docs/zeus-openapi-v0.5.0.dev0.json").read_text(encoding="utf-8") + Path("docs/zeus-openapi-v0.5.0.json").read_text(encoding="utf-8") ) paths = zeus_contract["paths"] schemas = zeus_contract["components"]["schemas"] @@ -210,7 +210,7 @@ def test_current_docs_declare_zeus_compatibility_targets(self) -> None: ]: with self.subTest(path=path): text = Path(path).read_text(encoding="utf-8") - self.assertIn("0.5.0.dev0", text) + self.assertIn("0.5.0", text) self.assertIn("mutation compatibility target", text) self.assertIn("v0.3.0", text) self.assertIn("v0.1.4", text) @@ -449,17 +449,17 @@ def test_mutation_docs_cover_version_gate_replacement_and_ambiguity_boundaries( ): with self.subTest(path=path): text = Path(path).read_text(encoding="utf-8").lower() - self.assertIn("0.5.0.dev0", text) + self.assertIn("0.5.0", text) self.assertIn("bot.replace", text) self.assertIn("exact", text) self.assertIn("indeterminate", text) self.assertIn("no archive/delete automation", text) - def test_real_zeus_verifier_and_staging_runbook_pin_safe_v050_dev0_smoke(self) -> None: + def test_real_zeus_verifier_and_staging_runbook_pin_safe_v050_smoke(self) -> None: script = Path("scripts/verify_real_zeus.sh").read_text(encoding="utf-8") - self.assertIn(".tmp/zeus-v0.5.0.dev0", script) + self.assertIn(".tmp/zeus-v0.5.0", script) self.assertIn( - 'zeus_contract_commit="f4a7af8a0ce802b3f9daeed0ee44958ef92abf1a"', + 'zeus_contract_commit="ce9dfdfd7c9ea7dc367fd6c1cb15ab8be0d1ea95"', script, ) self.assertIn('checkout --detach "$zeus_contract_commit"', script) @@ -471,14 +471,17 @@ def test_real_zeus_verifier_and_staging_runbook_pin_safe_v050_dev0_smoke(self) - self.assertIn("does not match required contract commit", script) self.assertIn("status --porcelain", script) self.assertIn("must be clean", script) - self.assertIn("Zeus 0.5.0.dev0 contract checkout", script) - self.assertIn("--expected-version 0.5.0.dev0", script) + self.assertIn("Zeus 0.5.0 contract checkout", script) + self.assertIn("--expected-version 0.5.0", script) self.assertIn('olymp.cli zeus ready "$node_id"', script) + self.assertIn('olymp.cli zeus templates "$node_id"', script) + self.assertIn("kimi-k3-coding-bot", script) self.assertNotIn("v0.1.4", script) runbook = Path("docs/STAGING_RUNBOOK.md").read_text(encoding="utf-8").lower() - self.assertIn("0.5.0.dev0", runbook) + self.assertIn("0.5.0", runbook) self.assertIn("expected_version", runbook) + self.assertIn("kimi-k3-coding-bot", runbook) self.assertIn("does not create or replace bots", runbook) diff --git a/tests/test_rollouts.py b/tests/test_rollouts.py index eb0f0d2..41ebd4e 100644 --- a/tests/test_rollouts.py +++ b/tests/test_rollouts.py @@ -213,7 +213,7 @@ def _add_staging_nodes() -> None: "--base-url", f"http://127.0.0.1:{port}", "--expected-version", - "0.5.0.dev0", + "0.5.0", "--tag", "staging", ]