agentevo-cloud 0.2.7__tar.gz → 0.2.9__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 (278) hide show
  1. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/LICENSE +1 -1
  2. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/PKG-INFO +6 -6
  3. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/README.md +4 -4
  4. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/pyproject.toml +2 -2
  5. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/community.py +5 -1
  6. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/health_receipts.py +33 -7
  7. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_community.py +12 -0
  8. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_health_freshness.py +48 -2
  9. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/.gitignore +0 -0
  10. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/openapi/openapi.v1.json +0 -0
  11. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/__init__.py +0 -0
  12. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/analytics.py +0 -0
  13. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/app.py +0 -0
  14. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/application_alerts.py +0 -0
  15. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_activation.py +0 -0
  16. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_admission.py +0 -0
  17. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_authority.py +0 -0
  18. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_bootstrap.py +0 -0
  19. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_continuity.py +0 -0
  20. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_epoch.py +0 -0
  21. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_legacy.py +0 -0
  22. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_mode.py +0 -0
  23. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_retained.py +0 -0
  24. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_rows.py +0 -0
  25. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_schema.py +0 -0
  26. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/audit_writer.py +0 -0
  27. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/backup_archive.py +0 -0
  28. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/backup_workflows.py +0 -0
  29. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/billing.py +0 -0
  30. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/bounded_dns.py +0 -0
  31. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/clerk.py +0 -0
  32. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/clerk_session.py +0 -0
  33. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/cli.py +0 -0
  34. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/config.py +0 -0
  35. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/data_exports.py +0 -0
  36. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/db.py +0 -0
  37. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/demo.py +0 -0
  38. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/device_grants.py +0 -0
  39. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/devices.py +0 -0
  40. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/entitlement_recovery.py +0 -0
  41. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/erasure_journal.py +0 -0
  42. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/event_evidence.py +0 -0
  43. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/event_retirement.py +0 -0
  44. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/events.py +0 -0
  45. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/handoffs.py +0 -0
  46. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/health.py +0 -0
  47. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/housekeeping.py +0 -0
  48. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/http_deadline.py +0 -0
  49. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/job_authority.py +0 -0
  50. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/jobs.py +0 -0
  51. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/live.py +0 -0
  52. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/managed.py +0 -0
  53. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/mcp.py +0 -0
  54. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/mcp_delivery.py +0 -0
  55. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/mcp_delivery_asgi.py +0 -0
  56. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/metering.py +0 -0
  57. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/migration_lifecycle.py +0 -0
  58. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/observability.py +0 -0
  59. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/oidc.py +0 -0
  60. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/openapi.py +0 -0
  61. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/operator_privacy.py +0 -0
  62. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/privacy_boundary.py +0 -0
  63. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/promote.py +0 -0
  64. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/py.typed +0 -0
  65. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/recall.py +0 -0
  66. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/records.py +0 -0
  67. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/recovery_authority.py +0 -0
  68. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/relationships.py +0 -0
  69. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/retention.py +0 -0
  70. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/revoke.py +0 -0
  71. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/screen.py +0 -0
  72. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/session_closure.py +0 -0
  73. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/sqlite_engine.py +0 -0
  74. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/storage_conversion.py +0 -0
  75. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/summaries.py +0 -0
  76. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/sync.py +0 -0
  77. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/tenant_guard.py +0 -0
  78. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/tenants.py +0 -0
  79. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/tokens.py +0 -0
  80. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/webhooks.py +0 -0
  81. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/src/agentevo_cloud/workers.py +0 -0
  82. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/mcp_delivery_fixtures.py +0 -0
  83. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/mcp_delivery_support.py +0 -0
  84. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_acceptance_m12.py +0 -0
  85. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_acceptance_m8.py +0 -0
  86. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_account_export.py +0 -0
  87. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_analytics.py +0 -0
  88. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_app.py +0 -0
  89. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_app_auth.py +0 -0
  90. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_app_jobs.py +0 -0
  91. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_app_managed.py +0 -0
  92. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_app_objects.py +0 -0
  93. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_app_sync.py +0 -0
  94. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_app_tenant_export.py +0 -0
  95. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_admission_slice2.py +0 -0
  96. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_bootstrap_slice1.py +0 -0
  97. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_configured_commit.py +0 -0
  98. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_cutover_context.py +0 -0
  99. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_details.py +0 -0
  100. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_epoch.py +0 -0
  101. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_legacy_retained.py +0 -0
  102. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_negative_mode.py +0 -0
  103. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_order.py +0 -0
  104. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_preparation.py +0 -0
  105. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_producer_d1.py +0 -0
  106. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_rows.py +0 -0
  107. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_schema.py +0 -0
  108. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_sequence_boundary.py +0 -0
  109. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_spending.py +0 -0
  110. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_sql_lifecycle.py +0 -0
  111. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_v18_lineage_graph.py +0 -0
  112. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_audit_v18_preparation.py +0 -0
  113. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_authz_b5.py +0 -0
  114. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_backup_archive.py +0 -0
  115. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_backup_script.py +0 -0
  116. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_backup_workflows.py +0 -0
  117. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_billing.py +0 -0
  118. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_body_limit.py +0 -0
  119. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_caddy_edge.py +0 -0
  120. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_clerk.py +0 -0
  121. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_clerk_online.py +0 -0
  122. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_clerk_session_transport.py +0 -0
  123. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_clerk_transactions.py +0 -0
  124. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cli.py +0 -0
  125. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_compile.py +0 -0
  126. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_console_gate.py +0 -0
  127. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_console_titles.py +0 -0
  128. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_containment_at_quota.py +0 -0
  129. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_contributors.py +0 -0
  130. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_app.py +0 -0
  131. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_application_alerts.py +0 -0
  132. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_activation.py +0 -0
  133. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_admission.py +0 -0
  134. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_authority.py +0 -0
  135. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_bootstrap.py +0 -0
  136. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_continuity.py +0 -0
  137. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_epoch.py +0 -0
  138. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_legacy.py +0 -0
  139. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_mode.py +0 -0
  140. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_retained.py +0 -0
  141. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_rows.py +0 -0
  142. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_schema.py +0 -0
  143. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_audit_writer.py +0 -0
  144. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_backup_workflows.py +0 -0
  145. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_billing.py +0 -0
  146. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_clerk_session.py +0 -0
  147. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_cli.py +0 -0
  148. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_config.py +0 -0
  149. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_data_exports.py +0 -0
  150. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_db.py +0 -0
  151. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_device_grants.py +0 -0
  152. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_devices.py +0 -0
  153. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_entitlement_recovery.py +0 -0
  154. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_erasure_journal.py +0 -0
  155. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_event_evidence.py +0 -0
  156. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_event_retirement.py +0 -0
  157. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_events.py +0 -0
  158. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_health_receipts.py +0 -0
  159. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_http_deadline.py +0 -0
  160. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_job_authority.py +0 -0
  161. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_jobs.py +0 -0
  162. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_mcp.py +0 -0
  163. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_mcp_delivery.py +0 -0
  164. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_mcp_delivery_asgi.py +0 -0
  165. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_migration_lifecycle.py +0 -0
  166. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_observability.py +0 -0
  167. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_oidc.py +0 -0
  168. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_operator_privacy.py +0 -0
  169. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_privacy_boundary.py +0 -0
  170. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_recovery_authority.py +0 -0
  171. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_retention.py +0 -0
  172. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_revoke.py +0 -0
  173. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_session_closure.py +0 -0
  174. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_sqlite_engine.py +0 -0
  175. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_storage_conversion.py +0 -0
  176. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_sync.py +0 -0
  177. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_tenant_guard.py +0 -0
  178. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_tenants.py +0 -0
  179. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_tokens.py +0 -0
  180. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_webhooks.py +0 -0
  181. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_cov_workers.py +0 -0
  182. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_database_publication.py +0 -0
  183. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_deletion_backup_receipts.py +0 -0
  184. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_demo.py +0 -0
  185. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_deploy_build_context.py +0 -0
  186. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_deploy_image_lock.py +0 -0
  187. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_device_grant_lifecycle.py +0 -0
  188. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_device_grants.py +0 -0
  189. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_devices_ci.py +0 -0
  190. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_encrypted_backup_workflows.py +0 -0
  191. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_encrypted_service.py +0 -0
  192. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_entitlement_mixed_live.py +0 -0
  193. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_entitlement_recovery.py +0 -0
  194. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_erasure_http.py +0 -0
  195. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_erasure_journal.py +0 -0
  196. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_event_evidence.py +0 -0
  197. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_event_loop_blocking.py +0 -0
  198. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_event_retirement.py +0 -0
  199. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_events.py +0 -0
  200. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_feed.py +0 -0
  201. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_foundation.py +0 -0
  202. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_handoffs.py +0 -0
  203. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_housekeeping.py +0 -0
  204. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_idempotency_scope.py +0 -0
  205. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_identity_privacy_hops.py +0 -0
  206. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_int_bounds.py +0 -0
  207. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_isolation_matrix.py +0 -0
  208. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_job_authority_migration.py +0 -0
  209. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_job_submitter_authority.py +0 -0
  210. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_jobs.py +0 -0
  211. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_license.py +0 -0
  212. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_live.py +0 -0
  213. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_lone_surrogates.py +0 -0
  214. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_maintenance.py +0 -0
  215. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_managed.py +0 -0
  216. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_mcp_delivery_authority.py +0 -0
  217. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_mcp_delivery_leases.py +0 -0
  218. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_mcp_server_send_boundary.py +0 -0
  219. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_mcp_sse.py +0 -0
  220. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_metered_write_lock.py +0 -0
  221. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_metering_records.py +0 -0
  222. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_migration_atomicity.py +0 -0
  223. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_migration_lifecycle.py +0 -0
  224. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_migration_lock.py +0 -0
  225. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_migration_minimum_bootstrap.py +0 -0
  226. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_object_workspace_ownership.py +0 -0
  227. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_observability.py +0 -0
  228. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_oidc.py +0 -0
  229. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_openapi.py +0 -0
  230. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_operator_privacy.py +0 -0
  231. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_perf_b6.py +0 -0
  232. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_privacy_boundary.py +0 -0
  233. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_privileged_integrity.py +0 -0
  234. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_promote.py +0 -0
  235. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_protected_backup_scripts.py +0 -0
  236. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_proxy_ip.py +0 -0
  237. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_prune_foreign.py +0 -0
  238. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_readiness.py +0 -0
  239. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_recall.py +0 -0
  240. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_recovery_schema.py +0 -0
  241. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_relationship_integrity.py +0 -0
  242. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_relationship_parent_report.py +0 -0
  243. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_reserved_key_ids.py +0 -0
  244. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_restore_cutover.py +0 -0
  245. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_restore_script.py +0 -0
  246. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_retention.py +0 -0
  247. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_revoke.py +0 -0
  248. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_runbook_schema.py +0 -0
  249. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_scope_hardening.py +0 -0
  250. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_screen.py +0 -0
  251. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_screening_events.py +0 -0
  252. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_sdist_layout.py +0 -0
  253. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_session_closure.py +0 -0
  254. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_shared_proxy_guard.py +0 -0
  255. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_smoke.py +0 -0
  256. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_snapshot_current_authority.py +0 -0
  257. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_snapshot_evidence.py +0 -0
  258. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_sql_tenant_scoping.py +0 -0
  259. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_sqlite_engine.py +0 -0
  260. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_storage_conversion.py +0 -0
  261. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_storage_operator.py +0 -0
  262. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_summaries.py +0 -0
  263. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_sync_mode.py +0 -0
  264. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_sync_push_pull.py +0 -0
  265. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_tenant_binding_contract.py +0 -0
  266. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_tenant_delete.py +0 -0
  267. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_tenant_guard.py +0 -0
  268. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_tenant_guard_adversarial.py +0 -0
  269. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_token_ancestry.py +0 -0
  270. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_webhook_dns_deadline.py +0 -0
  271. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_webhook_ssrf.py +0 -0
  272. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_webhook_write_lock.py +0 -0
  273. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_webhooks.py +0 -0
  274. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_worker_privacy.py +0 -0
  275. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_workers.py +0 -0
  276. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_workspace_delete_jobs.py +0 -0
  277. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_workspace_export_handoffs.py +0 -0
  278. {agentevo_cloud-0.2.7 → agentevo_cloud-0.2.9}/tests/test_write_lock_convoy.py +0 -0
@@ -3,7 +3,7 @@ Business Source License 1.1
3
3
  Parameters
4
4
 
5
5
  Licensor: Agentevo
6
- Licensed Work: agentevo-cloud 0.2.7
6
+ Licensed Work: agentevo-cloud 0.2.9
7
7
  Copyright Notice: Copyright (c) 2026 Agentevo
8
8
 
9
9
  Additional Use Grant:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentevo-cloud
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Agentevo SaaS (TZ-3 control plane, TZ-4 managed intelligence): auth, metering, C2 APIs, workers.
5
5
  Project-URL: Homepage, https://agentevo.sh
6
6
  Project-URL: Documentation, https://agentevo.sh/#setup
@@ -20,7 +20,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
20
  Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.10
22
22
  Requires-Dist: agentevo-core==0.1.6
23
- Requires-Dist: agentevo-local==0.1.6
23
+ Requires-Dist: agentevo-local==0.1.7
24
24
  Requires-Dist: anyio>=4
25
25
  Requires-Dist: cryptography>=44
26
26
  Requires-Dist: fastapi>=0.115
@@ -51,16 +51,16 @@ Part of [Agentevo](https://agentevo.sh).
51
51
 
52
52
  ## Encrypted runtime release
53
53
 
54
- Cloud **0.2.7** is the pending source version for authenticated accepted event
54
+ Cloud **0.2.9** is the pending source version for authenticated accepted event
55
55
  context, reviewed legacy transitions and the existing encrypted storage,
56
- provider and backup controls. Verified0.2.6 artifacts and consumer seal remain
57
- preserved;0.2.7 is not built/sealed/cold-installed yet. A local passing test alone
56
+ provider and backup controls. Verified 0.2.8 artifacts and consumer seal remain
57
+ preserved; 0.2.9 is not built/sealed/cold-installed yet. A local passing test alone
58
58
  does not establish publication, deployment or complete operating acceptance.
59
59
 
60
60
  The optional `encrypted` extra pins `sqlcipher3==0.6.3`. For source development,
61
61
  install it with `uv sync --frozen --package agentevo-cloud --extra encrypted`.
62
62
  After final publication, the package selection is
63
- `agentevo-cloud[encrypted]==0.2.7`; use the approved hashed, wheel-only consumer
63
+ `agentevo-cloud[encrypted]==0.2.9`; use the approved hashed, wheel-only consumer
64
64
  lock for a hosted image. Installation alone does not activate encryption.
65
65
 
66
66
  Select `AGENTEVO_DB_STORAGE=sqlcipher-v1`, an explicit public
@@ -19,16 +19,16 @@ Part of [Agentevo](https://agentevo.sh).
19
19
 
20
20
  ## Encrypted runtime release
21
21
 
22
- Cloud **0.2.7** is the pending source version for authenticated accepted event
22
+ Cloud **0.2.9** is the pending source version for authenticated accepted event
23
23
  context, reviewed legacy transitions and the existing encrypted storage,
24
- provider and backup controls. Verified0.2.6 artifacts and consumer seal remain
25
- preserved;0.2.7 is not built/sealed/cold-installed yet. A local passing test alone
24
+ provider and backup controls. Verified 0.2.8 artifacts and consumer seal remain
25
+ preserved; 0.2.9 is not built/sealed/cold-installed yet. A local passing test alone
26
26
  does not establish publication, deployment or complete operating acceptance.
27
27
 
28
28
  The optional `encrypted` extra pins `sqlcipher3==0.6.3`. For source development,
29
29
  install it with `uv sync --frozen --package agentevo-cloud --extra encrypted`.
30
30
  After final publication, the package selection is
31
- `agentevo-cloud[encrypted]==0.2.7`; use the approved hashed, wheel-only consumer
31
+ `agentevo-cloud[encrypted]==0.2.9`; use the approved hashed, wheel-only consumer
32
32
  lock for a hosted image. Installation alone does not activate encryption.
33
33
 
34
34
  Select `AGENTEVO_DB_STORAGE=sqlcipher-v1`, an explicit public
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentevo-cloud"
3
- version = "0.2.7"
3
+ version = "0.2.9"
4
4
  description = "Agentevo SaaS (TZ-3 control plane, TZ-4 managed intelligence): auth, metering, C2 APIs, workers."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -22,7 +22,7 @@ classifiers = [
22
22
  ]
23
23
  dependencies = [
24
24
  "agentevo-core==0.1.6",
25
- "agentevo-local==0.1.6",
25
+ "agentevo-local==0.1.7",
26
26
  "cryptography>=44",
27
27
  "fastapi>=0.115",
28
28
  "anyio>=4",
@@ -46,6 +46,9 @@ SIGNING_KEY_ENV = "AGENTEVO_FEED_SIGNING_KEY"
46
46
  #: Key id of the manifest signature (rotation adds ids; clients pin the map).
47
47
  SIGNING_KEY_ID = "vendor-feed-v1"
48
48
 
49
+ #: Env overriding the manifest key id (the deploy sets it when it rotates).
50
+ SIGNING_KEY_ID_ENV = "AGENTEVO_FEED_SIGNING_KEY_ID"
51
+
49
52
 
50
53
  @dataclass(frozen=True)
51
54
  class CommunityGuard:
@@ -187,7 +190,8 @@ def feed(*, since: str | None = None) -> dict[str, Any]:
187
190
  }
188
191
  key = os.environ.get(SIGNING_KEY_ENV, "").strip()
189
192
  if key:
190
- return sign_feed(envelope, key)
193
+ key_id = os.environ.get(SIGNING_KEY_ID_ENV, "").strip() or SIGNING_KEY_ID
194
+ return sign_feed(envelope, key, key_id=key_id)
191
195
  return envelope
192
196
 
193
197
 
@@ -8,11 +8,13 @@ These files are application observations, not backup-restorability proofs.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import errno
11
12
  import json
12
13
  import os
13
14
  import stat
14
15
  import sys
15
16
  import tempfile
17
+ import time
16
18
  from collections.abc import Iterator
17
19
  from contextlib import contextmanager
18
20
  from dataclasses import dataclass
@@ -32,6 +34,13 @@ COMPONENTS = frozenset({"jobs", "webhooks", "backup"})
32
34
  _EVENTS = frozenset({"attempt", "completed", "success", "item_success", "failure"})
33
35
  _MAX_MARKER_BYTES = 1024
34
36
 
37
+ #: Contended receipt writes wait this long in total (sibling passes hold
38
+ #: the lock for milliseconds; a lock stuck past this is a real fault).
39
+ _WRITER_LOCK_DEADLINE_SECONDS = 5.0
40
+
41
+ #: Pause between writer-lock attempts (mirrors the migration lock poll).
42
+ _WRITER_LOCK_POLL_SECONDS = 0.05
43
+
35
44
 
36
45
  def receipt_directory(settings: Settings) -> Path:
37
46
  """A dedicated private directory, normally beside the shared database."""
@@ -165,18 +174,35 @@ def _writer_lock(directory: Path) -> Iterator[None]:
165
174
  try:
166
175
  if not stat.S_ISREG(os.fstat(fd).st_mode):
167
176
  raise ValueError("invalid receipt lock file")
168
- if sys.platform == "win32":
169
- if os.fstat(fd).st_size == 0:
170
- os.write(fd, b"0")
171
- os.lseek(fd, 0, os.SEEK_SET)
172
- msvcrt.locking(fd, msvcrt.LK_NBLCK, 1)
173
- else:
174
- fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
177
+ if sys.platform == "win32" and os.fstat(fd).st_size == 0:
178
+ os.write(fd, b"0")
179
+ deadline = time.monotonic() + _WRITER_LOCK_DEADLINE_SECONDS
180
+ while True:
181
+ try:
182
+ _lock_writer_file(fd)
183
+ break
184
+ except OSError as exc:
185
+ if exc.errno not in (errno.EACCES, errno.EAGAIN):
186
+ raise
187
+ remaining = deadline - time.monotonic()
188
+ if remaining <= 0:
189
+ raise TimeoutError("timed out waiting for the receipt writer lock") from None
190
+ time.sleep(min(_WRITER_LOCK_POLL_SECONDS, remaining))
175
191
  yield
176
192
  finally:
177
193
  os.close(fd) # Also releases the kernel lock after a crashed writer.
178
194
 
179
195
 
196
+ def _lock_writer_file(fd: int) -> None:
197
+ """Try kernel ownership once (the caller retries contention)."""
198
+ if sys.platform == "win32":
199
+ os.lseek(fd, 0, os.SEEK_SET)
200
+ # Windows permits the locked byte range to extend beyond EOF.
201
+ msvcrt.locking(fd, msvcrt.LK_NBLCK, 1)
202
+ else:
203
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
204
+
205
+
180
206
  def _write_marker(directory: Path, component: str, event: str, moment: datetime) -> None:
181
207
  """Publish only after a complete file has reached the filesystem."""
182
208
  payload = {"version": 1, "at": moment.astimezone(UTC).isoformat()}
@@ -18,6 +18,8 @@ from agentevo_cloud.community import (
18
18
  FEED_VERSION,
19
19
  FORMAT,
20
20
  SIGNING_KEY_ENV,
21
+ SIGNING_KEY_ID,
22
+ SIGNING_KEY_ID_ENV,
21
23
  CommunityGuard,
22
24
  feed,
23
25
  sign_feed,
@@ -348,6 +350,16 @@ def test_feed_signs_when_env_set(monkeypatch: pytest.MonkeyPatch) -> None:
348
350
  assert "manifest" not in feed()
349
351
 
350
352
 
353
+ def test_feed_honors_key_id_env(monkeypatch: pytest.MonkeyPatch) -> None:
354
+ private_pem, _ = _ed25519_pem()
355
+ monkeypatch.setenv(SIGNING_KEY_ENV, private_pem)
356
+ assert feed()["manifest"]["key_id"] == SIGNING_KEY_ID
357
+ monkeypatch.setenv(SIGNING_KEY_ID_ENV, "vendor-feed-v2")
358
+ assert feed()["manifest"]["key_id"] == "vendor-feed-v2"
359
+ monkeypatch.setenv(SIGNING_KEY_ID_ENV, " ")
360
+ assert feed()["manifest"]["key_id"] == SIGNING_KEY_ID
361
+
362
+
351
363
  def test_route_since_filter_and_no_billing(client: Any) -> None:
352
364
  secret = client.provisioned["secret"]
353
365
  before = client.get("/v1/usage", headers=auth(secret)).json()["used"]
@@ -3,6 +3,7 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import errno
6
7
  import gzip
7
8
  import json
8
9
  import os
@@ -94,7 +95,9 @@ def test_independent_freshness_boundaries_and_failure_recovery(
94
95
  assert component_status(settings, other, now=STAMP)["last_successful_pass_at"] is None
95
96
 
96
97
 
97
- def test_receipts_are_private_atomic_and_do_not_regress(tmp_path: Path) -> None:
98
+ def test_receipts_are_private_atomic_and_do_not_regress(
99
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
100
+ ) -> None:
98
101
  settings = _settings(tmp_path, "jobs")
99
102
  _success(settings, "jobs")
100
103
  directory = receipt_directory(settings)
@@ -106,13 +109,56 @@ def test_receipts_are_private_atomic_and_do_not_regress(tmp_path: Path) -> None:
106
109
  assert component_status(settings, "jobs", now=STAMP)["last_successful_pass_at"] == (
107
110
  STAMP.isoformat()
108
111
  )
109
- with health_receipts._writer_lock(directory), pytest.raises(OSError):
112
+ # A lock stuck past the deadline still surfaces (TimeoutError is an
113
+ # OSError) and the timestamp never regresses; production showed the
114
+ # old fail-on-first-contention dropping worker passes.
115
+ monkeypatch.setattr(health_receipts, "_WRITER_LOCK_DEADLINE_SECONDS", 0.01)
116
+ with health_receipts._writer_lock(directory), pytest.raises(TimeoutError):
110
117
  record_event(settings, "jobs", "success", now=STAMP + timedelta(seconds=1))
111
118
  assert component_status(settings, "jobs", now=STAMP)["last_successful_pass_at"] == (
112
119
  STAMP.isoformat()
113
120
  )
114
121
 
115
122
 
123
+ def test_writer_lock_retries_transient_contention(
124
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
125
+ ) -> None:
126
+ settings = _settings(tmp_path, "jobs")
127
+ calls = 0
128
+ real_flock = health_receipts.fcntl.flock
129
+
130
+ def _flaky(fd: int, operation: int) -> None:
131
+ nonlocal calls
132
+ calls += 1
133
+ if calls <= 2:
134
+ raise OSError(errno.EAGAIN, "contended")
135
+ real_flock(fd, operation)
136
+
137
+ monkeypatch.setattr(health_receipts.fcntl, "flock", _flaky)
138
+ record_event(settings, "jobs", "success", now=STAMP)
139
+ assert calls == 3
140
+ assert component_status(settings, "jobs", now=STAMP)["last_successful_pass_at"] == (
141
+ STAMP.isoformat()
142
+ )
143
+
144
+
145
+ def test_writer_lock_rejects_non_contention_errors_without_retry(
146
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
147
+ ) -> None:
148
+ settings = _settings(tmp_path, "jobs")
149
+ calls = 0
150
+
151
+ def _boom(fd: int, operation: int) -> None:
152
+ nonlocal calls
153
+ calls += 1
154
+ raise OSError(errno.EBADF, "synthetic")
155
+
156
+ monkeypatch.setattr(health_receipts.fcntl, "flock", _boom)
157
+ with pytest.raises(OSError, match="synthetic"):
158
+ record_event(settings, "jobs", "success", now=STAMP)
159
+ assert calls == 1
160
+
161
+
116
162
  @pytest.mark.parametrize(
117
163
  "payload",
118
164
  [