imbi-api 2.9.2__tar.gz → 2.9.3__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 (295) hide show
  1. {imbi_api-2.9.2 → imbi_api-2.9.3}/PKG-INFO +1 -1
  2. {imbi_api-2.9.2 → imbi_api-2.9.3}/pyproject.toml +1 -1
  3. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/commit_sync/service.py +58 -24
  4. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/project_commit_sync.py +8 -1
  5. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_commit_sync_service.py +24 -2
  6. {imbi_api-2.9.2 → imbi_api-2.9.3}/uv.lock +1 -1
  7. {imbi_api-2.9.2 → imbi_api-2.9.3}/.github/workflows/deploy.yaml +0 -0
  8. {imbi_api-2.9.2 → imbi_api-2.9.3}/.github/workflows/docs.yaml +0 -0
  9. {imbi_api-2.9.2 → imbi_api-2.9.3}/.github/workflows/testing.yaml +0 -0
  10. {imbi_api-2.9.2 → imbi_api-2.9.3}/.gitignore +0 -0
  11. {imbi_api-2.9.2 → imbi_api-2.9.3}/.pre-commit-config.yaml +0 -0
  12. {imbi_api-2.9.2 → imbi_api-2.9.3}/.python-version +0 -0
  13. {imbi_api-2.9.2 → imbi_api-2.9.3}/CLAUDE.md +0 -0
  14. {imbi_api-2.9.2 → imbi_api-2.9.3}/CODE_REVIEW_PUNCHLIST.md +0 -0
  15. {imbi_api-2.9.2 → imbi_api-2.9.3}/LICENSE +0 -0
  16. {imbi_api-2.9.2 → imbi_api-2.9.3}/README.md +0 -0
  17. {imbi_api-2.9.2 → imbi_api-2.9.3}/coderabbit.yaml +0 -0
  18. {imbi_api-2.9.2 → imbi_api-2.9.3}/compose.yaml +0 -0
  19. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0001-record-architecture-decisions.md +0 -0
  20. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0002-authentication-and-authorization-architecture.md +0 -0
  21. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0003-email-sending-architecture.md +0 -0
  22. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0004-phase-5-authentication-enhancements.md +0 -0
  23. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0005-file-upload-storage-architecture.md +0 -0
  24. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0006-project-identity-and-multi-type.md +0 -0
  25. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0007-relationship-blueprints.md +0 -0
  26. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0008-plugin-system-architecture.md +0 -0
  27. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0009-database-driven-oauth-providers.md +0 -0
  28. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0010-identity-plugin-architecture.md +0 -0
  29. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0011-graph-based-project-scoring.md +0 -0
  30. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0012-plugin-manifest-service-template.md +0 -0
  31. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0013-deployment-plugin-type.md +0 -0
  32. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0014-generic-plugin-entity-abstraction.md +0 -0
  33. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr/0015-cyclonedx-1.7-sbom-standard.md +0 -0
  34. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/adr.md +0 -0
  35. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/configuration.md +0 -0
  36. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/index.md +0 -0
  37. {imbi_api-2.9.2 → imbi_api-2.9.3}/docs/restore-backup.md +0 -0
  38. {imbi_api-2.9.2 → imbi_api-2.9.3}/justfile +0 -0
  39. {imbi_api-2.9.2 → imbi_api-2.9.3}/mkdocs.yml +0 -0
  40. {imbi_api-2.9.2 → imbi_api-2.9.3}/mypy.ini +0 -0
  41. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/__init__.py +0 -0
  42. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/app.py +0 -0
  43. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/__init__.py +0 -0
  44. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/authorization_codes.py +0 -0
  45. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/local_auth.py +0 -0
  46. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/login_providers.py +0 -0
  47. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/membership.py +0 -0
  48. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/models.py +0 -0
  49. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/oauth.py +0 -0
  50. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/oauth_clients.py +0 -0
  51. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/password.py +0 -0
  52. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/permissions.py +0 -0
  53. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/seed.py +0 -0
  54. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/sessions.py +0 -0
  55. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/tokens.py +0 -0
  56. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/auth/totp.py +0 -0
  57. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/backfill_embeddings.py +0 -0
  58. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/blueprint_attributes.py +0 -0
  59. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/commit_sync/__init__.py +0 -0
  60. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/commit_sync/queue.py +0 -0
  61. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/domain/__init__.py +0 -0
  62. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/domain/models.py +0 -0
  63. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/domain/scoring.py +0 -0
  64. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/__init__.py +0 -0
  65. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/client.py +0 -0
  66. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/dependencies.py +0 -0
  67. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/models.py +0 -0
  68. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/templates/base.html +0 -0
  69. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/templates/base.txt +0 -0
  70. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/templates/password_reset.html +0 -0
  71. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/templates/password_reset.txt +0 -0
  72. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/templates/welcome.html +0 -0
  73. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/templates/welcome.txt +0 -0
  74. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/email/templates.py +0 -0
  75. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/__init__.py +0 -0
  76. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/_credentials.py +0 -0
  77. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/_helpers.py +0 -0
  78. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/_json_fields.py +0 -0
  79. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/_pagination.py +0 -0
  80. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/_request_urls.py +0 -0
  81. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/admin.py +0 -0
  82. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/admin_plugins.py +0 -0
  83. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/api_keys.py +0 -0
  84. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/auth.py +0 -0
  85. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/auth_providers.py +0 -0
  86. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/blueprints.py +0 -0
  87. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/client_credentials.py +0 -0
  88. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/comments.py +0 -0
  89. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/dashboard.py +0 -0
  90. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/document_templates.py +0 -0
  91. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/documents.py +0 -0
  92. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/environments.py +0 -0
  93. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/events.py +0 -0
  94. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/graph_query.py +0 -0
  95. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/identity_plugins.py +0 -0
  96. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/link_definitions.py +0 -0
  97. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/local_auth.py +0 -0
  98. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/mcp_servers.py +0 -0
  99. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/mfa.py +0 -0
  100. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/oauth_metadata.py +0 -0
  101. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/operations_log.py +0 -0
  102. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/organizations.py +0 -0
  103. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/plugin_edges.py +0 -0
  104. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/plugin_entities.py +0 -0
  105. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/plugins.py +0 -0
  106. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/project_analysis.py +0 -0
  107. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/project_configuration.py +0 -0
  108. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/project_deployments.py +0 -0
  109. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/project_logs.py +0 -0
  110. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/project_plugins.py +0 -0
  111. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/project_type_plugins.py +0 -0
  112. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/project_types.py +0 -0
  113. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/projects.py +0 -0
  114. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/pull_requests.py +0 -0
  115. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/releases.py +0 -0
  116. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/roles.py +0 -0
  117. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/sa_api_keys.py +0 -0
  118. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/scoring.py +0 -0
  119. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/scoring_policies.py +0 -0
  120. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/search.py +0 -0
  121. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/service_accounts.py +0 -0
  122. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/service_plugins.py +0 -0
  123. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/status.py +0 -0
  124. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/tags.py +0 -0
  125. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/teams.py +0 -0
  126. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/third_party_services.py +0 -0
  127. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/uploads.py +0 -0
  128. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/user_activity.py +0 -0
  129. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/users.py +0 -0
  130. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/endpoints/webhooks.py +0 -0
  131. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/entrypoint.py +0 -0
  132. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/graph_sql.py +0 -0
  133. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/__init__.py +0 -0
  134. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/endpoints.py +0 -0
  135. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/errors.py +0 -0
  136. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/flows.py +0 -0
  137. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/host_integration.py +0 -0
  138. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/models.py +0 -0
  139. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/repository.py +0 -0
  140. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/resolution.py +0 -0
  141. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/state.py +0 -0
  142. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/identity/sweeper.py +0 -0
  143. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/lifespans.py +0 -0
  144. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/llm/__init__.py +0 -0
  145. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/llm/dependencies.py +0 -0
  146. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/mcp_test.py +0 -0
  147. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/middleware/__init__.py +0 -0
  148. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/middleware/rate_limit.py +0 -0
  149. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/models.py +0 -0
  150. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/openapi.py +0 -0
  151. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/patch.py +0 -0
  152. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/__init__.py +0 -0
  153. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/assignment_writer.py +0 -0
  154. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/assignments.py +0 -0
  155. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/credentials.py +0 -0
  156. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/installer.py +0 -0
  157. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/lifecycle.py +0 -0
  158. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/lifecycle_dispatch.py +0 -0
  159. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/reload.py +0 -0
  160. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/resolution.py +0 -0
  161. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/plugins/schemas.py +0 -0
  162. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/prompts/__init__.py +0 -0
  163. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/prompts/release_notes_system.md +0 -0
  164. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/py.typed +0 -0
  165. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/relationships.py +0 -0
  166. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/sbom.py +0 -0
  167. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/scoring/__init__.py +0 -0
  168. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/scoring/queue.py +0 -0
  169. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/settings.py +0 -0
  170. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/storage/__init__.py +0 -0
  171. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/storage/client.py +0 -0
  172. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/storage/dependencies.py +0 -0
  173. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/storage/thumbnails.py +0 -0
  174. {imbi_api-2.9.2 → imbi_api-2.9.3}/src/imbi_api/storage/validation.py +0 -0
  175. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/__init__.py +0 -0
  176. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/__init__.py +0 -0
  177. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_api_key_auth.py +0 -0
  178. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_authentication.py +0 -0
  179. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_authorization.py +0 -0
  180. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_encryption.py +0 -0
  181. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_login_providers.py +0 -0
  182. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_membership.py +0 -0
  183. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_oauth.py +0 -0
  184. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_oauth_server.py +0 -0
  185. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_permissions.py +0 -0
  186. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_seed.py +0 -0
  187. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_sessions.py +0 -0
  188. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/auth/test_totp.py +0 -0
  189. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/clickhouse/__init__.py +0 -0
  190. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/clickhouse/test_client.py +0 -0
  191. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/clickhouse/test_init.py +0 -0
  192. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/clickhouse/test_privacy.py +0 -0
  193. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/email/__init__.py +0 -0
  194. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/email/test_client.py +0 -0
  195. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/email/test_init.py +0 -0
  196. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/email/test_integration.py +0 -0
  197. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/email/test_models.py +0 -0
  198. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/email/test_templates.py +0 -0
  199. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/__init__.py +0 -0
  200. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_admin.py +0 -0
  201. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_admin_plugins.py +0 -0
  202. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_api_keys.py +0 -0
  203. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_auth.py +0 -0
  204. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_auth_providers.py +0 -0
  205. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_blueprints.py +0 -0
  206. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_client_credentials.py +0 -0
  207. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_comments.py +0 -0
  208. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_credentials.py +0 -0
  209. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_dashboard.py +0 -0
  210. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_document_templates.py +0 -0
  211. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_documents.py +0 -0
  212. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_environments.py +0 -0
  213. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_events.py +0 -0
  214. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_graph_query.py +0 -0
  215. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_init.py +0 -0
  216. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_json_fields.py +0 -0
  217. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_link_definitions.py +0 -0
  218. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_local_auth.py +0 -0
  219. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_mcp_servers.py +0 -0
  220. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_mfa.py +0 -0
  221. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_operations_log.py +0 -0
  222. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_organizations.py +0 -0
  223. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_pagination.py +0 -0
  224. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_plugin_label_validation.py +0 -0
  225. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_plugins.py +0 -0
  226. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_analysis.py +0 -0
  227. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_commit_sync.py +0 -0
  228. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_configuration.py +0 -0
  229. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_deployments.py +0 -0
  230. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_logs.py +0 -0
  231. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_plugins.py +0 -0
  232. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_response_surfacing.py +0 -0
  233. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_type_plugins.py +0 -0
  234. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_project_types.py +0 -0
  235. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_projects.py +0 -0
  236. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_projects_helpers.py +0 -0
  237. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_pull_requests.py +0 -0
  238. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_releases.py +0 -0
  239. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_request_urls.py +0 -0
  240. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_roles.py +0 -0
  241. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_sa_api_keys.py +0 -0
  242. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_scoring.py +0 -0
  243. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_scoring_policies.py +0 -0
  244. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_search.py +0 -0
  245. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_service_accounts.py +0 -0
  246. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_service_plugins.py +0 -0
  247. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_service_writeback.py +0 -0
  248. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_status.py +0 -0
  249. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_tags.py +0 -0
  250. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_teams.py +0 -0
  251. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_third_party_services.py +0 -0
  252. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_uploads.py +0 -0
  253. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_user_activity.py +0 -0
  254. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_users.py +0 -0
  255. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/endpoints/test_webhooks.py +0 -0
  256. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/fixtures/sbom/npm-realistic.json +0 -0
  257. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/fixtures/sbom/pypi-realistic.json +0 -0
  258. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/fixtures/sbom/tiny.json +0 -0
  259. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/__init__.py +0 -0
  260. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/test_endpoints.py +0 -0
  261. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/test_errors.py +0 -0
  262. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/test_flows.py +0 -0
  263. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/test_host_integration.py +0 -0
  264. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/test_repository.py +0 -0
  265. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/test_resolution.py +0 -0
  266. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/test_state.py +0 -0
  267. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/identity/test_sweeper.py +0 -0
  268. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/middleware/test_rate_limit.py +0 -0
  269. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/storage/__init__.py +0 -0
  270. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/storage/test_client.py +0 -0
  271. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/storage/test_init.py +0 -0
  272. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/storage/test_thumbnails.py +0 -0
  273. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/storage/test_validation.py +0 -0
  274. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/support.py +0 -0
  275. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_app.py +0 -0
  276. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_assignment_writer.py +0 -0
  277. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_backfill_embeddings.py +0 -0
  278. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_blueprint_attributes.py +0 -0
  279. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_blueprints.py +0 -0
  280. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_commit_sync_queue.py +0 -0
  281. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_entrypoint.py +0 -0
  282. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_graph_sql.py +0 -0
  283. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_init.py +0 -0
  284. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_installer.py +0 -0
  285. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_lifecycle_dispatch.py +0 -0
  286. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_lifespans.py +0 -0
  287. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_models.py +0 -0
  288. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_openapi.py +0 -0
  289. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_patch.py +0 -0
  290. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_plugin_schemas.py +0 -0
  291. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_plugins.py +0 -0
  292. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_sbom.py +0 -0
  293. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_scoring_queue.py +0 -0
  294. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_scoring_triggers.py +0 -0
  295. {imbi_api-2.9.2 → imbi_api-2.9.3}/tests/test_settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imbi-api
3
- Version: 2.9.2
3
+ Version: 2.9.3
4
4
  Summary: Imbi is a DevOps Service Management Platform designed to provide an efficient way to manage a large environment that contains many services and applications.
5
5
  Author-email: Alex Campbell <alexc@aweber.com>, Dave Shawley <daves@aweber.com>, "Gavin M. Roy" <gavinr@aweber.com>
6
6
  License: BSD-3-Clause
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "imbi-api"
3
- version = "2.9.2"
3
+ version = "2.9.3"
4
4
  description = "Imbi is a DevOps Service Management Platform designed to provide an efficient way to manage a large environment that contains many services and applications."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -13,6 +13,7 @@ node so the UI can poll it without a dedicated status store.
13
13
 
14
14
  from __future__ import annotations
15
15
 
16
+ import asyncio
16
17
  import datetime
17
18
  import logging
18
19
  import typing
@@ -33,6 +34,12 @@ _COMMIT_SYNC_SLUG = 'github-commit-sync'
33
34
  # Persisted error strings are truncated so a noisy upstream message can't
34
35
  # bloat the Project node.
35
36
  _MAX_ERROR_LEN = 500
37
+ # Apache AGE aborts a SET with "Entity failed to be updated: <n>"
38
+ # (heap_update -> TM_Updated) when another transaction updates the same
39
+ # Project vertex concurrently. The worker's authoritative status writes
40
+ # retry the transient conflict so they still land under contention.
41
+ _STATUS_WRITE_RETRIES = 3
42
+ _STATUS_RETRY_BACKOFF = 0.05
36
43
 
37
44
  SyncState = typing.Literal['idle', 'queued', 'running', 'success', 'failed']
38
45
 
@@ -210,6 +217,11 @@ def _now_iso() -> str:
210
217
  return datetime.datetime.now(datetime.UTC).isoformat()
211
218
 
212
219
 
220
+ def _is_write_conflict(exc: Exception) -> bool:
221
+ """True if *exc* is AGE's concurrent-update conflict (TM_Updated)."""
222
+ return 'failed to be updated' in str(exc)
223
+
224
+
213
225
  async def set_status(
214
226
  db: graph.Graph,
215
227
  project_id: str,
@@ -219,8 +231,17 @@ async def set_status(
219
231
  commits: int = 0,
220
232
  tags: int = 0,
221
233
  error: str = '',
234
+ retry: bool = True,
222
235
  ) -> None:
223
- """Persist last-sync state on the ``Project`` node (best-effort)."""
236
+ """Persist last-sync state on the ``Project`` node (best-effort).
237
+
238
+ *retry* re-attempts the write when AGE reports a transient concurrent
239
+ update, so the worker's authoritative transitions (running -> success/
240
+ failed) still land if a webhook touches the project mid-write. The
241
+ enqueue endpoint passes ``retry=False`` for its optimistic ``queued``
242
+ write: dropping that on conflict is correct, since the worker's newer
243
+ ``running`` write must win rather than be clobbered back to ``queued``.
244
+ """
224
245
  query: typing.LiteralString = """
225
246
  MATCH (p:Project {{id: {project_id}}})
226
247
  SET p.commit_sync_status = {status},
@@ -231,26 +252,39 @@ async def set_status(
231
252
  p.commit_sync_error = {error}
232
253
  RETURN p.id AS id
233
254
  """
234
- try:
235
- await db.execute(
236
- query,
237
- {
238
- 'project_id': project_id,
239
- 'status': status,
240
- 'at': _now_iso(),
241
- 'by': requested_by,
242
- 'commits': commits,
243
- 'tags': tags,
244
- 'error': error[:_MAX_ERROR_LEN],
245
- },
246
- ['id'],
247
- )
248
- except Exception: # noqa: BLE001
249
- LOGGER.warning(
250
- 'Failed to persist commit-sync status for project %s',
251
- project_id,
252
- exc_info=True,
253
- )
255
+ params = {
256
+ 'project_id': project_id,
257
+ 'status': status,
258
+ 'at': _now_iso(),
259
+ 'by': requested_by,
260
+ 'commits': commits,
261
+ 'tags': tags,
262
+ 'error': error[:_MAX_ERROR_LEN],
263
+ }
264
+ attempts = _STATUS_WRITE_RETRIES if retry else 1
265
+ for attempt in range(attempts):
266
+ try:
267
+ await db.execute(query, params, ['id'])
268
+ return
269
+ except Exception as exc: # noqa: BLE001
270
+ conflict = _is_write_conflict(exc)
271
+ if retry and conflict and attempt + 1 < attempts:
272
+ await asyncio.sleep(_STATUS_RETRY_BACKOFF * (attempt + 1))
273
+ continue
274
+ if conflict:
275
+ LOGGER.debug(
276
+ 'commit-sync status write for %s lost a concurrent '
277
+ 'update (status=%s); leaving the newer state in place',
278
+ project_id,
279
+ status,
280
+ )
281
+ else:
282
+ LOGGER.warning(
283
+ 'Failed to persist commit-sync status for project %s',
284
+ project_id,
285
+ exc_info=True,
286
+ )
287
+ return
254
288
 
255
289
 
256
290
  def _opt_str(value: object) -> str | None:
@@ -277,7 +311,7 @@ async def read_status(db: graph.Graph, project_id: str) -> CommitSyncStatus:
277
311
  MATCH (p:Project {{id: {project_id}}})
278
312
  RETURN p.commit_sync_status AS status,
279
313
  p.commit_sync_at AS at,
280
- p.commit_sync_by AS by,
314
+ p.commit_sync_by AS requested_by,
281
315
  p.commit_sync_commits AS commits,
282
316
  p.commit_sync_tags AS tags,
283
317
  p.commit_sync_error AS error
@@ -285,7 +319,7 @@ async def read_status(db: graph.Graph, project_id: str) -> CommitSyncStatus:
285
319
  records = await db.execute(
286
320
  query,
287
321
  {'project_id': project_id},
288
- ['status', 'at', 'by', 'commits', 'tags', 'error'],
322
+ ['status', 'at', 'requested_by', 'commits', 'tags', 'error'],
289
323
  )
290
324
  if not records:
291
325
  return CommitSyncStatus()
@@ -307,5 +341,5 @@ async def read_status(db: graph.Graph, project_id: str) -> CommitSyncStatus:
307
341
  commits_synced=_opt_int(graph.parse_agtype(row.get('commits'))),
308
342
  tags_synced=_opt_int(graph.parse_agtype(row.get('tags'))),
309
343
  error=_opt_str(graph.parse_agtype(row.get('error'))),
310
- requested_by=_opt_str(graph.parse_agtype(row.get('by'))),
344
+ requested_by=_opt_str(graph.parse_agtype(row.get('requested_by'))),
311
345
  )
@@ -60,8 +60,15 @@ async def sync_commits_and_tags(
60
60
  valkey_client, org_slug, project_id, requested_by
61
61
  )
62
62
  if enqueued:
63
+ # Optimistic, best-effort: if the worker has already flipped the
64
+ # project to ``running``, that newer write must win -- so this one
65
+ # does not retry the concurrent-update conflict.
63
66
  await service.set_status(
64
- db, project_id, status='queued', requested_by=requested_by
67
+ db,
68
+ project_id,
69
+ status='queued',
70
+ requested_by=requested_by,
71
+ retry=False,
65
72
  )
66
73
  return CommitSyncEnqueueResponse(enqueued=enqueued)
67
74
 
@@ -130,13 +130,35 @@ class StatusTests(unittest.IsolatedAsyncioTestCase):
130
130
  self.assertEqual(7, params['commits'])
131
131
  self.assertEqual('alice', params['by'])
132
132
 
133
+ async def test_set_status_retries_on_write_conflict(self) -> None:
134
+ db = mock.AsyncMock()
135
+ db.execute.side_effect = [
136
+ Exception('Entity failed to be updated: 3'),
137
+ [{'id': '"p1"'}],
138
+ ]
139
+ with mock.patch.object(service.asyncio, 'sleep'):
140
+ await service.set_status(db, 'p1', status='running')
141
+ self.assertEqual(2, db.execute.await_count)
142
+
143
+ async def test_set_status_no_retry_drops_conflict(self) -> None:
144
+ db = mock.AsyncMock()
145
+ db.execute.side_effect = Exception('Entity failed to be updated: 3')
146
+ await service.set_status(db, 'p1', status='queued', retry=False)
147
+ db.execute.assert_awaited_once()
148
+
149
+ async def test_set_status_swallows_unrelated_error(self) -> None:
150
+ db = mock.AsyncMock()
151
+ db.execute.side_effect = Exception('boom')
152
+ await service.set_status(db, 'p1', status='running')
153
+ db.execute.assert_awaited_once()
154
+
133
155
  async def test_read_status_idle_when_unset(self) -> None:
134
156
  db = mock.AsyncMock()
135
157
  db.execute.return_value = [
136
158
  {
137
159
  'status': None,
138
160
  'at': None,
139
- 'by': None,
161
+ 'requested_by': None,
140
162
  'commits': None,
141
163
  'tags': None,
142
164
  'error': None,
@@ -153,7 +175,7 @@ class StatusTests(unittest.IsolatedAsyncioTestCase):
153
175
  {
154
176
  'status': '"success"',
155
177
  'at': '"2026-06-04T12:00:00+00:00"',
156
- 'by': '"alice"',
178
+ 'requested_by': '"alice"',
157
179
  'commits': 12,
158
180
  'tags': 4,
159
181
  'error': '""',
@@ -1016,7 +1016,7 @@ wheels = [
1016
1016
 
1017
1017
  [[package]]
1018
1018
  name = "imbi-api"
1019
- version = "2.9.2"
1019
+ version = "2.9.3"
1020
1020
  source = { editable = "." }
1021
1021
  dependencies = [
1022
1022
  { name = "aioboto3" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes