diff --git a/control_plane/drivers/registry.py b/control_plane/drivers/registry.py index aefd88a..ab36386 100644 --- a/control_plane/drivers/registry.py +++ b/control_plane/drivers/registry.py @@ -587,17 +587,6 @@ def _route_alias( route_path="/v1/drivers/odoo/preview-apply", authz_action="odoo_preview_apply.execute", ), - _action( - "preview_verification", - "Record preview verification", - "Record Odoo product smoke verification for the latest preview generation.", - safety="safe_write", - scope="preview", - route_path="/v1/drivers/odoo/preview-verification", - authz_action="preview_generation.write", - operator_visible=False, - writes_records=("preview", "preview_generation"), - ), _action( "prod_backup_gate", "Capture prod backup gate", @@ -674,6 +663,11 @@ def _route_alias( "/v1/drivers/odoo/preview-destroy", "preview_destroy.execute", ), + _route_alias( + "preview_verification", + "/v1/drivers/odoo/preview-verification", + "preview_generation.write", + ), _route_alias( "stable_verification", "/v1/drivers/odoo/stable-verification", diff --git a/docs/driver-descriptors.md b/docs/driver-descriptors.md index 993bf0d..0ee4e28 100644 --- a/docs/driver-descriptors.md +++ b/docs/driver-descriptors.md @@ -162,12 +162,13 @@ workflows: `POST /v1/drivers/odoo/preview-desired-state`, `POST /v1/drivers/odoo/preview-refresh`, `POST /v1/drivers/odoo/preview-inventory`, `POST /v1/drivers/odoo/preview-readiness`, and -`POST /v1/drivers/odoo/preview-destroy`. New product workflows should use the -generic-web routes for generic lifecycle evidence unless the product driver -documents a narrower product-specific contract. The Odoo-specific -`POST /v1/drivers/odoo/preview-verification` route remains a driver action -because it returns the typed `odoo_preview_verification` response shape. The lower-level -`POST /v1/drivers/odoo/preview-apply` route applies a ready isolated-preview +`POST /v1/drivers/odoo/preview-destroy`. Odoo preview verification is also a +non-operator route alias: it keeps the typed `odoo_preview_verification` +response shape for existing workflows while the advertised action and durable +record mutation belong to generic-web preview verification. New product +workflows should use the generic-web routes for generic lifecycle evidence +unless the product driver documents a narrower product-specific contract. The +lower-level `POST /v1/drivers/odoo/preview-apply` route applies a ready isolated-preview Dokploy plan to provider state after service authorization and idempotency checks. The route resolves runtime env values from Launchplane-owned runtime-environment records and managed secret overlays, derives preview-specific diff --git a/docs/service-boundary.md b/docs/service-boundary.md index 9a4a24f..d3d72a6 100644 --- a/docs/service-boundary.md +++ b/docs/service-boundary.md @@ -1222,7 +1222,8 @@ preview targets while the isolated runtime migration is being exercised. For Odoo preview smoke follow-ups, `POST /v1/drivers/odoo/preview-verification` -accepts the product, context, anchor repo/PR, `verification_status`, +remains a compatibility alias for the generic-web preview verification action. +It accepts the product, context, anchor repo/PR, `verification_status`, `verified_at`, optional checked URLs as an explicit list plus `timeout_seconds`, and an optional failure summary, then marks the latest preview generation ready or failed. Scalar or object-shaped `checked_urls` payloads are diff --git a/tests/test_driver_descriptors.py b/tests/test_driver_descriptors.py index 74e1ee7..b0f7f87 100644 --- a/tests/test_driver_descriptors.py +++ b/tests/test_driver_descriptors.py @@ -151,11 +151,17 @@ def test_odoo_descriptor_marks_prod_rollback_as_destructive(self) -> None: self.assertEqual(actions["prod_rollback"].safety, "destructive") self.assertEqual(actions["prod_rollback"].route_path, "/v1/drivers/odoo/prod-rollback") self.assertNotIn("preview_refresh", actions) + self.assertNotIn("preview_verification", actions) self.assertEqual( route_aliases["preview_refresh"].route_path, "/v1/drivers/odoo/preview-refresh", ) self.assertFalse(route_aliases["preview_refresh"].operator_visible) + self.assertEqual( + route_aliases["preview_verification"].route_path, + "/v1/drivers/odoo/preview-verification", + ) + self.assertFalse(route_aliases["preview_verification"].operator_visible) self.assertEqual(actions["stable_bootstrap"].safety, "destructive") self.assertEqual( actions["stable_bootstrap"].route_path, @@ -175,7 +181,7 @@ def test_effective_odoo_actions_inherit_generic_web_preview_routes(self) -> None self.assertEqual(actions["preview_destroy"].safety, "destructive") self.assertEqual( actions["preview_verification"].route_path, - "/v1/drivers/odoo/preview-verification", + "/v1/drivers/generic-web/preview-verification", ) def test_verireel_descriptor_exposes_preview_and_stable_capabilities(self) -> None: @@ -463,6 +469,16 @@ def test_odoo_preview_execution_metadata_matches_descriptors(self) -> None: ].action_id, "preview_refresh", ) + self.assertEqual( + control_plane_service._driver_route_metadata_from_descriptors()[ + "/v1/drivers/odoo/preview-verification" + ].action_id, + "preview_verification", + ) + self.assertIs( + control_plane_service._ODOO_PREVIEW_VERIFICATION_ROUTE.envelope_model, + control_plane_service.OdooPreviewVerificationEnvelope, + ) self.assert_route_metadata_matches_descriptor( driver_id="odoo", route_metadata_by_action={ @@ -471,11 +487,6 @@ def test_odoo_preview_execution_metadata_matches_descriptors(self) -> None: control_plane_service.OdooPreviewApplyEnvelope, "apply Odoo preview", ), - "preview_verification": ( - control_plane_service._ODOO_PREVIEW_VERIFICATION_ROUTE, - control_plane_service.OdooPreviewVerificationEnvelope, - "Odoo preview verification", - ), }, )