booking-agent-cli 0.11.0__tar.gz → 0.11.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/PKG-INFO +2 -2
  2. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/README.md +1 -1
  3. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/pyproject.toml +1 -1
  4. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/__init__.py +1 -1
  5. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/__init__.py +1 -1
  6. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/bundled_skill.md +9 -9
  7. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/commands/flow.py +29 -0
  8. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/updates.py +10 -9
  9. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_ucp_cancel_result.py +32 -0
  10. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_updates.py +74 -12
  11. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/.gitignore +0 -0
  12. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/authorization.py +0 -0
  13. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/backend/__init__.py +0 -0
  14. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/backend/live.py +0 -0
  15. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/calendar/__init__.py +0 -0
  16. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/calendar/filter.py +0 -0
  17. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/calendar/google.py +0 -0
  18. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/calendar/oauth_listener.py +0 -0
  19. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/calendar/scope.py +0 -0
  20. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/circuit_breaker.py +0 -0
  21. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/config.py +0 -0
  22. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/defaults.env +0 -0
  23. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/discovery.py +0 -0
  24. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/faults.py +0 -0
  25. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/headless.py +0 -0
  26. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/http_deadlines.py +0 -0
  27. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/http_retry.py +0 -0
  28. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/http_trace.py +0 -0
  29. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/merchant_pacing.py +0 -0
  30. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/models/__init__.py +0 -0
  31. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/models/platform.py +0 -0
  32. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/models/wire.py +0 -0
  33. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/namespace.py +0 -0
  34. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/observability.py +0 -0
  35. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/platform_profile.py +0 -0
  36. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/pop.py +0 -0
  37. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/profile_cache.py +0 -0
  38. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/registry_url.py +0 -0
  39. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/rest_client.py +0 -0
  40. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/secure_file.py +0 -0
  41. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/settings.py +0 -0
  42. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/signals.py +0 -0
  43. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/step_timing.py +0 -0
  44. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/ucp_capabilities.py +0 -0
  45. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/ucp_checkout.py +0 -0
  46. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/ucp_negotiation.py +0 -0
  47. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/ucp_profile.py +0 -0
  48. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent/usp_methods.py +0 -0
  49. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/activity_log.py +0 -0
  50. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/booking_poll.py +0 -0
  51. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/booking_summary.py +0 -0
  52. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/calendar_connection.py +0 -0
  53. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/calendar_delegated.py +0 -0
  54. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/calendar_event.py +0 -0
  55. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/calendar_preference.py +0 -0
  56. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/catalog.py +0 -0
  57. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/checkout_prereq.py +0 -0
  58. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/commands/__init__.py +0 -0
  59. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/commands/config.py +0 -0
  60. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/commands/lowlevel.py +0 -0
  61. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/commands/runtime.py +0 -0
  62. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/commands/update.py +0 -0
  63. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/config_loader.py +0 -0
  64. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/engine.py +0 -0
  65. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/fault_report_schema.py +0 -0
  66. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/fault_reports.py +0 -0
  67. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/flow_timings.py +0 -0
  68. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/gates.py +0 -0
  69. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/geo_eligibility.py +0 -0
  70. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/geocode.py +0 -0
  71. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/identity.py +0 -0
  72. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/install_integrity.py +0 -0
  73. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/link_cli.py +0 -0
  74. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/main.py +0 -0
  75. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/output.py +0 -0
  76. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/payment_helpers.py +0 -0
  77. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/payment_token.py +0 -0
  78. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/provenance.py +0 -0
  79. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/reschedule.py +0 -0
  80. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/runtime.py +0 -0
  81. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/runtime_prepare.py +0 -0
  82. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/search_area.py +0 -0
  83. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/session.py +0 -0
  84. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/skill_bundle.py +0 -0
  85. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/skill_integrity.py +0 -0
  86. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/support_bundle_schema.py +0 -0
  87. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/support_report.py +0 -0
  88. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/src/booking_agent_cli/timezone_context.py +0 -0
  89. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/.env.tests +0 -0
  90. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/conftest.py +0 -0
  91. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/e2e/__init__.py +0 -0
  92. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/e2e/test_cancel_reschedule_e2e.py +0 -0
  93. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/e2e/test_latency_soak.py +0 -0
  94. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/e2e/test_staging_registry_integration.py +0 -0
  95. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/e2e/test_timezone_booking_e2e.py +0 -0
  96. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/e2e/test_ucp_native_e2e.py +0 -0
  97. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/e2e/test_ucp_skill_flow_e2e.py +0 -0
  98. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/e2e/ucp_native.py +0 -0
  99. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/__init__.py +0 -0
  100. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_activity_log.py +0 -0
  101. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_at_rest_hygiene.py +0 -0
  102. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_booking_rejection.py +0 -0
  103. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_bulk_cancel.py +0 -0
  104. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_calendar_connection.py +0 -0
  105. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_calendar_connector.py +0 -0
  106. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_calendar_delegated.py +0 -0
  107. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_calendar_flow.py +0 -0
  108. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_calendar_preference.py +0 -0
  109. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_checkout_signals.py +0 -0
  110. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_circuit_breaker.py +0 -0
  111. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_cli_enforced_gates.py +0 -0
  112. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_config.py +0 -0
  113. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_correlation_flow.py +0 -0
  114. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_declined_spt.py +0 -0
  115. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_discover_registry_rejection.py +0 -0
  116. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_flow_evidence.py +0 -0
  117. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_flow_gates.py +0 -0
  118. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_flow_reschedule.py +0 -0
  119. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_flow_step_timings.py +0 -0
  120. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_google_calendar_oauth.py +0 -0
  121. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_home_visit_address.py +0 -0
  122. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_http_trace.py +0 -0
  123. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_identity_logout.py +0 -0
  124. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_install_integrity.py +0 -0
  125. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_lazy_oauth.py +0 -0
  126. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_link_auth.py +0 -0
  127. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_link_cli_spt.py +0 -0
  128. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_namespace.py +0 -0
  129. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_oauth_listener.py +0 -0
  130. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_oauth_metadata.py +0 -0
  131. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_platform_key_pop.py +0 -0
  132. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_platform_profile.py +0 -0
  133. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_pop_keystore.py +0 -0
  134. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_profile_cache.py +0 -0
  135. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_regression_index.py +0 -0
  136. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_release_hold_uses_delete.py +0 -0
  137. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_request_retry_semantics.py +0 -0
  138. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_resume_after_restart.py +0 -0
  139. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_runtime_prepare.py +0 -0
  140. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_search_area.py +0 -0
  141. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_session_compat.py +0 -0
  142. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_session_retention.py +0 -0
  143. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_skill_integrity.py +0 -0
  144. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_skill_portability.py +0 -0
  145. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_support_report.py +0 -0
  146. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_timezone_context.py +0 -0
  147. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_ucp_checkout_pop.py +0 -0
  148. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_ucp_checkout_proof_wireup.py +0 -0
  149. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_ucp_checkout_validate.py +0 -0
  150. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_ucp_profile.py +0 -0
  151. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_update_command.py +0 -0
  152. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/integration/test_wheel_contents.py +0 -0
  153. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/SOURCE +0 -0
  154. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/acp_booking_extension.json +0 -0
  155. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/availability.json +0 -0
  156. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/booking.json +0 -0
  157. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/calendar_freebusy.json +0 -0
  158. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/catalog.json +0 -0
  159. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/paid_bookings.json +0 -0
  160. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/profile.json +0 -0
  161. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/registry.json +0 -0
  162. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/rest_common.json +0 -0
  163. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/usp.json +0 -0
  164. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/waitlist.json +0 -0
  165. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/spec_schemas/webhook_event.json +0 -0
  166. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/__init__.py +0 -0
  167. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/fixtures/__init__.py +0 -0
  168. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/fixtures/profiles.py +0 -0
  169. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/fixtures/provenance/pypi_attestations-0.0.30-py3-none-any.whl.provenance.json +0 -0
  170. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/fixtures/services.py +0 -0
  171. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/mock/__init__.py +0 -0
  172. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/mock/backend.py +0 -0
  173. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/mock/link_cli.py +0 -0
  174. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/mock/mock_calendar.py +0 -0
  175. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/mock/pop_business.py +0 -0
  176. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/mock/pop_transport.py +0 -0
  177. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/mock/ucp_checkout_store.py +0 -0
  178. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/mock/ucp_server.py +0 -0
  179. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/offline_stack/__init__.py +0 -0
  180. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/support/offline_stack/stack.py +0 -0
  181. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/__init__.py +0 -0
  182. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_authorization_policy.py +0 -0
  183. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_booking_poll_credentials.py +0 -0
  184. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_booking_request_error.py +0 -0
  185. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_calendar_filter.py +0 -0
  186. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_catalog.py +0 -0
  187. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_check_version.py +0 -0
  188. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_conditional_writes.py +0 -0
  189. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_delivery_address.py +0 -0
  190. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_discovery.py +0 -0
  191. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_fault_reports.py +0 -0
  192. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_geo_eligibility.py +0 -0
  193. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_geocode.py +0 -0
  194. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_harness_docs.py +0 -0
  195. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_headless.py +0 -0
  196. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_http_deadlines.py +0 -0
  197. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_http_retry.py +0 -0
  198. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_live_search_registry.py +0 -0
  199. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_merchant_pacing.py +0 -0
  200. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_mock_support.py +0 -0
  201. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_money_path_events.py +0 -0
  202. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_nonce_from_pop_error_body.py +0 -0
  203. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_observability.py +0 -0
  204. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_outbound_member_names.py +0 -0
  205. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_package_layering.py +0 -0
  206. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_pay_at_service.py +0 -0
  207. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_provenance.py +0 -0
  208. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_proxy_env.py +0 -0
  209. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_registry_search_filters.py +0 -0
  210. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_registry_search_pricing.py +0 -0
  211. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_registry_search_responses.py +0 -0
  212. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_registry_url.py +0 -0
  213. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_release_notes.py +0 -0
  214. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_rest_client.py +0 -0
  215. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_scoped_method_authorization.py +0 -0
  216. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_session_credentials.py +0 -0
  217. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_spec_conformance.py +0 -0
  218. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_step_timing.py +0 -0
  219. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_ucp_capabilities.py +0 -0
  220. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_ucp_complete_outcome.py +0 -0
  221. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_ucp_create_price_guard.py +0 -0
  222. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_ucp_negotiation.py +0 -0
  223. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/unit/test_wire_model_conformance.py +0 -0
  224. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/001-jcs-nested-buyer.json +0 -0
  225. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/002-jcs-meta-only.json +0 -0
  226. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/003-jcs-unicode-empty-meta.json +0 -0
  227. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/004-proof-issuance-no-ath.json +0 -0
  228. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/005-proof-presentation-ath-nonce.json +0 -0
  229. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/006-cross-key-replay.json +0 -0
  230. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/007-downgrade-cnf-without-proof.json +0 -0
  231. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/008-replayed-jti.json +0 -0
  232. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/009-aud-mismatch.json +0 -0
  233. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/010-alg-none.json +0 -0
  234. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/README.md +0 -0
  235. {booking_agent_cli-0.11.0 → booking_agent_cli-0.11.2}/tests/vectors/pop/SOURCE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: booking-agent-cli
3
- Version: 0.11.0
3
+ Version: 0.11.2
4
4
  Summary: The Booking Agent CLI: a stateful USP booking flow with hard gates, plus its USP protocol library.
5
5
  Project-URL: Homepage, https://github.com/wix/booking-agent
6
6
  Project-URL: Documentation, https://github.com/wix/booking-agent/blob/master/booking-agent/README.md
@@ -82,7 +82,7 @@ Each release also publishes that skill at an immutable `skill-v<version>` tag an
82
82
  the release workflow, so a harness can pin and verify the exact text it follows:
83
83
 
84
84
  ```bash
85
- curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.58.0/booking-agent/skills/booking-agent/SKILL.md
85
+ curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.58.2/booking-agent/skills/booking-agent/SKILL.md
86
86
  gh attestation verify SKILL.md --repo wix/booking-agent \
87
87
  --signer-workflow wix/booking-agent/.github/workflows/release.yml
88
88
  ```
@@ -40,7 +40,7 @@ Each release also publishes that skill at an immutable `skill-v<version>` tag an
40
40
  the release workflow, so a harness can pin and verify the exact text it follows:
41
41
 
42
42
  ```bash
43
- curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.58.0/booking-agent/skills/booking-agent/SKILL.md
43
+ curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.58.2/booking-agent/skills/booking-agent/SKILL.md
44
44
  gh attestation verify SKILL.md --repo wix/booking-agent \
45
45
  --signer-workflow wix/booking-agent/.github/workflows/release.yml
46
46
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "booking-agent-cli"
3
- version = "0.11.0"
3
+ version = "0.11.2"
4
4
  description = "The Booking Agent CLI: a stateful USP booking flow with hard gates, plus its USP protocol library."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -3,4 +3,4 @@
3
3
  Ships in the booking-agent-cli wheel and never imports ``booking_agent_cli``.
4
4
  """
5
5
 
6
- __version__ = "0.11.0"
6
+ __version__ = "0.11.2"
@@ -1,3 +1,3 @@
1
1
  """booking_agent_cli: typer-based stateful CLI for USP booking flows."""
2
2
 
3
- __version__ = "0.11.0"
3
+ __version__ = "0.11.2"
@@ -1,5 +1,5 @@
1
1
  ---
2
- version: 0.58.0
2
+ version: 0.58.2
3
3
  name: booking-agent
4
4
  description: |
5
5
  Books time-based services via USP from a Link wallet. Use when the user wants to book, schedule, reserve, or pay for a service from a business (haircut, lesson, repair, class, appointment, consult). The `booking-agent` CLI owns the booking state machine and refuses to proceed when human gates are skipped. Sign the user in to Link on your own, right after install and at the start of every booking (`booking-agent flow link-auth connect`, then start `booking-agent flow link-auth poll` in the background and post the sign-in URL in the same turn, exactly like `pay poll`); never wait for the user to ask to sign in or to say they approved. After `booking-agent flow business use`, follow `next_command` through `use-ucp-checkout-mode`; businesses without native checkout fail closed. Payment uses `booking-agent flow pay request` (default returns immediately with the approval URL), then start `booking-agent flow pay poll` in the background and post the approval URL to the user in the same turn; the poll finishes checkout once they approve. Confirm through `booking-agent flow book poll --wait`; never start an inbound webhook. Surface the approval URL exactly once. Personal identification (name, phone, email, address) must always come from the user's Link account via `link-cli`, never from chat or placeholders. Before any location-based search, the CLI proposes the Link shipping address and the user confirms it or gives another; online services skip this. Talk to the user in plain everyday language only; never expose CLI, UCP, auth, session, or other implementation details unless they explicitly ask.
@@ -19,7 +19,7 @@ metadata:
19
19
  - link-cli
20
20
  install:
21
21
  - kind: uv
22
- package: "booking-agent-cli==0.11.0"
22
+ package: "booking-agent-cli==0.11.2"
23
23
  bins: [booking-agent]
24
24
  user-invocable: true
25
25
  ---
@@ -220,7 +220,7 @@ To take a fixed, published version instead of the latest, fetch it at the releas
220
220
  workflow signs it under, and check the signature before you follow it:
221
221
 
222
222
  ```bash
223
- curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.58.0/booking-agent/skills/booking-agent/SKILL.md
223
+ curl -fsSLO https://raw.githubusercontent.com/wix/booking-agent/skill-v0.58.2/booking-agent/skills/booking-agent/SKILL.md
224
224
  gh attestation verify SKILL.md \
225
225
  --repo wix/booking-agent \
226
226
  --signer-workflow wix/booking-agent/.github/workflows/release.yml
@@ -245,17 +245,17 @@ before installing:
245
245
 
246
246
  ```bash
247
247
  dir="$HOME/.booking-agent/verified-wheels"
248
- uvx pip download "booking-agent-cli==0.11.0" --no-deps --only-binary :all: -d "$dir"
248
+ uvx pip download "booking-agent-cli==0.11.2" --no-deps --only-binary :all: -d "$dir"
249
249
  uvx pypi-attestations==0.0.30 verify pypi --repository https://github.com/wix/booking-agent \
250
- "$dir/booking_agent_cli-0.11.0-py3-none-any.whl"
251
- uv tool install --force "$dir/booking_agent_cli-0.11.0-py3-none-any.whl"
250
+ "$dir/booking_agent_cli-0.11.2-py3-none-any.whl"
251
+ uv tool install --force "$dir/booking_agent_cli-0.11.2-py3-none-any.whl"
252
252
 
253
253
  booking-agent --help
254
254
  ```
255
255
 
256
256
  Install only a wheel whose verify step passed. If `uvx` is not
257
- available, install the same pinned release with `uv tool install booking-agent-cli==0.11.0` or
258
- `pipx install booking-agent-cli==0.11.0`; the CLI checks its own files against the signed release
257
+ available, install the same pinned release with `uv tool install booking-agent-cli==0.11.2` or
258
+ `pipx install booking-agent-cli==0.11.2`; the CLI checks its own files against the signed release
259
259
  on the first `booking-agent flow init` and refuses to book or pay if they do not match.
260
260
 
261
261
  Harnesses that read this skill's `install` block do this for you. Do not go on
@@ -468,7 +468,7 @@ confirmation email or on the business's page (`booking_url`). Do not invent a lo
468
468
  **Step 1b — Create a new session:**
469
469
 
470
470
  ```bash
471
- booking-agent flow init --user-request "<user's exact request>" --skill-version 0.58.0 \
471
+ booking-agent flow init --user-request "<user's exact request>" --skill-version 0.58.2 \
472
472
  --skill-path "<absolute path of the SKILL.md you loaded>"
473
473
  ```
474
474
 
@@ -793,6 +793,23 @@ def _unfinished_similar_sessions(store: Any, user_request: str) -> List[Dict[str
793
793
  return matches
794
794
 
795
795
 
796
+ def _inactive_booking_status_message(session: Session) -> str:
797
+ """What status tells the user once the appointment itself is over."""
798
+ from ..booking_summary import selected_slot, service_and_business, when_text
799
+
800
+ label = {
801
+ "canceled": "cancelled",
802
+ "completed": "completed",
803
+ "no_show": "a no-show",
804
+ }.get(session.booking.status, (session.booking.status or "inactive").replace("_", " "))
805
+ booking = f"Your booking, {service_and_business(session)},"
806
+ slot = selected_slot(session)
807
+ when = when_text(session, slot, dual=False) if slot is not None else None
808
+ if when:
809
+ booking = f"{booking} {when},"
810
+ return f"{booking} is {label}."
811
+
812
+
796
813
  @app.command("status")
797
814
  def status() -> None:
798
815
  session = load_session()
@@ -873,6 +890,18 @@ def status() -> None:
873
890
  )
874
891
  extra_data["services"] = session.discover_services
875
892
  extra_data["discover_query"] = session.discover_query
893
+ elif (
894
+ session.flow_phase == "CONFIRMED"
895
+ and session.booking.status in INACTIVE_BOOKING_STATUSES
896
+ ):
897
+ # Phase stays CONFIRMED: that is where a finished booking lives. The appointment
898
+ # itself is booking.status, and status must not describe a canceled one as booked.
899
+ status_message = _inactive_booking_status_message(session)
900
+ prompt = (
901
+ "This booking is no longer active. If the user asked how it is going, post "
902
+ "user_message as written. Do not say they are booked, and do not offer to "
903
+ "add or change a calendar event."
904
+ )
876
905
  elif session.flow_phase == "CONFIRMED" and not session.reschedule_slot_id \
877
906
  and not session.reschedule_original_slot_id:
878
907
  from ..booking_summary import booked_message
@@ -220,27 +220,28 @@ def local_pair_reason(reported: Optional[str]) -> Optional[str]:
220
220
 
221
221
 
222
222
  def select_target(installed: str, releases: Sequence[Release]) -> Tuple[Optional[Release], List[Release]]:
223
- """The newest release that is safe to move to, and every release that move brings in.
223
+ """The newest release that is safe to move to, and the ready releases that move brings in.
224
224
 
225
- Installing a version brings every release between the installed one and it, so a
226
- deferred release in that range holds back everything after it.
225
+ A deferred release is skipped, not a stopping point: it holds back only itself, so
226
+ a later recommended or urgent release is still offered. ``releases`` carries either
227
+ the general rollout or one replay's effective status; both are read the same way.
228
+ CLIs before 0.11.2 stop at the first deferred release, which is why the release
229
+ notes recommend every release up to 0.11.2 for each verified replay.
227
230
  """
228
231
  current = parse_version(installed)
229
232
  if current is None:
230
233
  return None, []
231
234
  target: Optional[Release] = None
232
- passed: List[Release] = []
235
+ brought: List[Release] = []
233
236
  for release in releases:
234
- if release.key <= current:
237
+ if release.key <= current or release.update_status == "deferred":
235
238
  continue
236
- if release.update_status == "deferred":
237
- break
238
- passed.append(release)
239
+ brought.append(release)
239
240
  if release.update_status != "withdrawn":
240
241
  target = release
241
242
  if target is None:
242
243
  return None, []
243
- return target, [r for r in passed if r.key <= target.key]
244
+ return target, [r for r in brought if r.key <= target.key]
244
245
 
245
246
 
246
247
  def decide(
@@ -398,6 +398,38 @@ def test_successful_cancel_persists_status_and_stops_being_actionable(
398
398
  assert not is_actionable(row)
399
399
 
400
400
 
401
+ def test_status_after_cancel_does_not_say_the_booking_is_still_booked(
402
+ runner: CliRunner, isolated_session, monkeypatch,
403
+ ):
404
+ """flow status keeps phase CONFIRMED, but must not tell the user they are booked."""
405
+ store = load_store()
406
+ session = _completed_checkout_409_session()
407
+ session.business.name = "HVAC-Koby"
408
+ session.live_service = {**session.live_service, "name": "AC Repair"}
409
+ session.calendar_event_html_link = "https://www.google.com/calendar/event?eid=abc"
410
+ add_session(store, session)
411
+ save_store(store)
412
+ _install_completed_checkout_409(
413
+ monkeypatch,
414
+ checkout={},
415
+ booking_result=_canceled_booking(),
416
+ )
417
+ _invoke_cancel(runner, exit_code=0)
418
+
419
+ result = runner.invoke(app, ["flow", "status"])
420
+ text = result.stdout or result.output or ""
421
+ assert result.exit_code == 0, text
422
+ body, _ = json.JSONDecoder().raw_decode(text[text.index("{"):])
423
+ message = body["user_message"]
424
+
425
+ assert body["phase"] == "CONFIRMED"
426
+ assert "cancelled" in message
427
+ assert "AC Repair with HVAC-Koby" in message
428
+ assert not message.startswith("You're booked")
429
+ assert "calendar/event" not in message
430
+ assert "confirmed" not in body["prompt"].lower()
431
+
432
+
401
433
  def test_completed_checkout_409_still_cancels_when_the_checkout_cannot_be_reread(
402
434
  runner: CliRunner, isolated_session, monkeypatch,
403
435
  ):
@@ -53,12 +53,64 @@ def test_replay_identity_is_generic_runtime_configuration(monkeypatch):
53
53
  assert updates.configured_replay() is None
54
54
 
55
55
 
56
- def test_the_committed_index_parses():
56
+ def _committed_releases():
57
57
  index = Path(__file__).resolve().parents[4] / "release-notes" / "index.json"
58
- releases = updates.parse_index(json.loads(index.read_text()))
58
+ return updates.parse_index(json.loads(index.read_text()))
59
+
60
+
61
+ def test_the_committed_index_parses():
62
+ releases = _committed_releases()
59
63
  assert releases and all(updates.parse_version(r.cli_version) for r in releases)
60
64
 
61
65
 
66
+ def _verified_harnesses():
67
+ return [h for h in release_notes.load_registry()["harnesses"].values() if h["status"] == "verified"]
68
+
69
+
70
+ def test_every_verified_harness_is_offered_its_approved_release():
71
+ """From each release since the replayed one, the decision targets the registry's pin."""
72
+ releases = _committed_releases()
73
+ for harness in _verified_harnesses():
74
+ replayed = updates.parse_version(harness["replayed"]["cli"])
75
+ approved = harness["recommended"]["cli"]
76
+ for start in [r.cli_version for r in releases if replayed <= r.key < updates.parse_version(approved)]:
77
+ d = updates.decide(
78
+ reported_skill=SKILL_VERSION, releases=releases, installed=start, replay_id=harness["replay"],
79
+ )
80
+ assert d.target_version == approved, (harness["name"], start, d.target_version)
81
+ at_pin = updates.decide(
82
+ reported_skill=SKILL_VERSION, releases=releases, installed=approved, replay_id=harness["replay"],
83
+ )
84
+ assert at_pin.target_version is None, (harness["name"], at_pin.target_version)
85
+
86
+
87
+ def _pre_skip_target(installed, releases):
88
+ """The walk inside CLIs before 0.11.2: it stops at the first deferred release."""
89
+ current, target = updates.parse_version(installed), None
90
+ for release in releases:
91
+ if release.key <= current:
92
+ continue
93
+ if release.update_status == "deferred":
94
+ break
95
+ if release.update_status != "withdrawn":
96
+ target = release
97
+ return target.cli_version if target else None
98
+
99
+
100
+ @pytest.mark.parametrize("installed,replay", [
101
+ ("0.7.0", None),
102
+ ("0.10.0", "grok-bot"),
103
+ ("0.10.2", "base44-superagent"),
104
+ ("0.11.1", "muse"),
105
+ ])
106
+ def test_installed_harness_clis_can_walk_to_the_first_cli_that_skips_gaps(installed, replay):
107
+ releases = _committed_releases()
108
+ if replay:
109
+ releases = updates.releases_for_replay(releases, replay)
110
+ target = _pre_skip_target(installed, releases)
111
+ assert target is not None and updates.parse_version(target) >= updates.parse_version("0.11.2"), target
112
+
113
+
62
114
  @pytest.mark.parametrize("text,ok", [
63
115
  ("0.2.0", True), ("1.10.3", True), ("0.2.0.dev1", True),
64
116
  ("v0.2.0", False), ("0.2", False), ("latest", False), (None, False), (3, False),
@@ -132,7 +184,7 @@ def test_harnesses_that_share_a_replay_promote_together():
132
184
  assert first.replay == second.replay == "shared"
133
185
 
134
186
 
135
- def test_every_deferred_release_in_a_verified_upgrade_needs_that_replay():
187
+ def test_a_gap_does_not_hold_back_the_newest_release_a_replay_recommends():
136
188
  old = _entry(
137
189
  "0.2.0", status="deferred", ready=False,
138
190
  replay_rollouts={"grok-bot": _rollout()},
@@ -142,15 +194,15 @@ def test_every_deferred_release_in_a_verified_upgrade_needs_that_replay():
142
194
  "0.4.0", status="deferred", ready=False,
143
195
  replay_rollouts={"grok-bot": _rollout()},
144
196
  )
145
- blocked = _decide(old, missing, newest, replay_id="grok-bot")
146
- assert blocked.target_version == "0.2.0"
197
+ assert _decide(old, missing, newest, replay_id="grok-bot").target_version == "0.4.0"
147
198
 
148
- middle = _entry(
199
+ held = _entry(
149
200
  "0.3.0", status="deferred", ready=False,
150
- replay_rollouts={"grok-bot": _rollout()},
201
+ replay_rollouts={"grok-bot": _rollout("deferred", ready=False)},
151
202
  )
152
- promoted = _decide(old, middle, newest, replay_id="grok-bot")
153
- assert promoted.target_version == "0.4.0"
203
+ d = _decide(old, held, newest, replay_id="grok-bot")
204
+ assert d.target_version == "0.4.0"
205
+ assert "0.3.0" not in d.user_summary
154
206
 
155
207
 
156
208
  def test_a_withdrawn_release_stays_withdrawn_for_a_verified_harness():
@@ -161,12 +213,22 @@ def test_a_withdrawn_release_stays_withdrawn_for_a_verified_harness():
161
213
  ).reason == "installed_version_withdrawn"
162
214
 
163
215
 
164
- def test_a_deferred_release_holds_back_every_later_one():
216
+ def test_a_deferred_release_holds_back_only_itself():
217
+ d = _decide(
218
+ _entry("0.2.0", "breaking", status="deferred", ready=False),
219
+ _entry("0.3.0", "fix", "urgent"),
220
+ )
221
+ assert (d.action, d.target_version, d.change_type) == ("urgent", "0.3.0", "fix")
222
+
223
+
224
+ def test_a_withdrawn_release_in_a_gap_is_never_the_target():
165
225
  d = _decide(
166
226
  _entry("0.2.0", status="deferred", ready=False),
167
- _entry("0.3.0", status="urgent"),
227
+ _entry("0.3.0", status="withdrawn", replacement_version="0.4.0"),
228
+ _entry("0.4.0", status="recommended"),
229
+ _entry("0.5.0", status="withdrawn", replacement_version="0.5.1"),
168
230
  )
169
- assert d.action == "none" and d.target_version is None
231
+ assert d.target_version == "0.4.0"
170
232
 
171
233
 
172
234
  def test_the_target_is_the_newest_safe_release_and_carries_the_strongest_status():