loom-kernel 2.19.0__tar.gz → 2.20.0__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 (687) hide show
  1. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/CHANGELOG.md +53 -0
  2. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/PKG-INFO +1 -1
  3. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/backend/core_model.py +2 -1
  4. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/backend/sqlalchemy.py +43 -8
  5. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/repository.py +64 -36
  6. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/__init__.py +2 -0
  7. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/introspection.py +14 -5
  8. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/abc/cursor.py +32 -8
  9. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/dynamodb/repository.py +17 -2
  10. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mongo/repository.py +17 -4
  11. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/mixins.py +82 -36
  12. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/query_compiler/compiler.py +21 -9
  13. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/query_compiler/cursor.py +22 -11
  14. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/autocrud.py +30 -6
  15. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/testing/in_memory.py +22 -1
  16. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/.gitignore +0 -0
  17. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/LICENSE +0 -0
  18. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/README.md +0 -0
  19. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/pyproject.toml +0 -0
  20. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/__init__.py +0 -0
  21. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/__init__.py +0 -0
  22. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/_concurrency.py +0 -0
  23. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/_filters.py +0 -0
  24. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/_paths.py +0 -0
  25. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/_roles.py +0 -0
  26. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/_startup.py +0 -0
  27. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/_transport.py +0 -0
  28. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/_usecase.py +0 -0
  29. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/a2a/__init__.py +0 -0
  30. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/a2a/_binding.py +0 -0
  31. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/a2a/_handlers.py +0 -0
  32. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/a2a/_rpc.py +0 -0
  33. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/a2a/card.py +0 -0
  34. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/a2a/events.py +0 -0
  35. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/a2a/server.py +0 -0
  36. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/abc.py +0 -0
  37. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/bootstrap.py +0 -0
  38. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/__init__.py +0 -0
  39. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/_compiler.py +0 -0
  40. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/_fingerprint.py +0 -0
  41. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/_plan.py +0 -0
  42. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/__init__.py +0 -0
  43. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_capabilities.py +0 -0
  44. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_conversation.py +0 -0
  45. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_feedable.py +0 -0
  46. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_hook.py +0 -0
  47. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_instructions.py +0 -0
  48. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_limits.py +0 -0
  49. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_model_role.py +0 -0
  50. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_output.py +0 -0
  51. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/compiler/phases/_state.py +0 -0
  52. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/config.py +0 -0
  53. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/declarative/__init__.py +0 -0
  54. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/declarative/_envelope.py +0 -0
  55. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/declarative/_schema.py +0 -0
  56. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/declarative/_v1.py +0 -0
  57. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/declarative/schemas/agent-spec-v1.schema.json +0 -0
  58. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/describe.py +0 -0
  59. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/__init__.py +0 -0
  60. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/__init__.py +0 -0
  61. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_a2a.py +0 -0
  62. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_capabilities.py +0 -0
  63. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_engine.py +0 -0
  64. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_errors.py +0 -0
  65. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_escape.py +0 -0
  66. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_events.py +0 -0
  67. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_guards.py +0 -0
  68. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_history.py +0 -0
  69. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_instructions.py +0 -0
  70. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_limits.py +0 -0
  71. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_mcp.py +0 -0
  72. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_models.py +0 -0
  73. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_native.py +0 -0
  74. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_output.py +0 -0
  75. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_returns.py +0 -0
  76. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_schemas.py +0 -0
  77. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/_spec.py +0 -0
  78. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/extras/__init__.py +0 -0
  79. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/extras/a2a.py +0 -0
  80. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/extras/handlebars.py +0 -0
  81. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/extras/mcp.py +0 -0
  82. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/extras/skills.py +0 -0
  83. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/provider.py +0 -0
  84. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/providers/__init__.py +0 -0
  85. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/providers/_shared.py +0 -0
  86. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/providers/anthropic.py +0 -0
  87. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/providers/bedrock.py +0 -0
  88. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/providers/gateway.py +0 -0
  89. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/providers/openai.py +0 -0
  90. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/engines/pydantic_ai/providers/typesafe.py +0 -0
  91. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/errors.py +0 -0
  92. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/etl/__init__.py +0 -0
  93. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/etl/_batch.py +0 -0
  94. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/etl/_columns.py +0 -0
  95. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/etl/_frames.py +0 -0
  96. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/etl/_ledger.py +0 -0
  97. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/etl/_runner.py +0 -0
  98. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/etl/_validation.py +0 -0
  99. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/fastapi/__init__.py +0 -0
  100. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/fastapi/endpoints.py +0 -0
  101. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/fastapi/response.py +0 -0
  102. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/fastapi/streaming.py +0 -0
  103. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/inference.py +0 -0
  104. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/pricing.py +0 -0
  105. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/registry.py +0 -0
  106. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/remote_auth.py +0 -0
  107. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/__init__.py +0 -0
  108. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_bounded.py +0 -0
  109. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_chain.py +0 -0
  110. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_conversation.py +0 -0
  111. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_grants.py +0 -0
  112. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_handle.py +0 -0
  113. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_health.py +0 -0
  114. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_hooks.py +0 -0
  115. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_lifecycle.py +0 -0
  116. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_limits.py +0 -0
  117. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/runtime/_mcp.py +0 -0
  118. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/validate/__init__.py +0 -0
  119. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/ai/validate/__main__.py +0 -0
  120. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/celery/__init__.py +0 -0
  121. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/celery/auto.py +0 -0
  122. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/celery/bootstrap.py +0 -0
  123. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/celery/config.py +0 -0
  124. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/celery/constants.py +0 -0
  125. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/celery/runner.py +0 -0
  126. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/celery/service.py +0 -0
  127. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/cli/__init__.py +0 -0
  128. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/cli/app.py +0 -0
  129. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/cli/schema.py +0 -0
  130. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/async_bridge.py +0 -0
  131. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/authz/__init__.py +0 -0
  132. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/authz/_decide.py +0 -0
  133. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/authz/_grants.py +0 -0
  134. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/authz/_roles.py +0 -0
  135. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/authz/_scope.py +0 -0
  136. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/authz/elevation.py +0 -0
  137. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/authz/product.py +0 -0
  138. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/backend/__init__.py +0 -0
  139. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/backend/protocol.py +0 -0
  140. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/backend/scoped_ddl.py +0 -0
  141. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/bootstrap/__init__.py +0 -0
  142. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/bootstrap/bootstrap.py +0 -0
  143. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/bootstrap/kernel.py +0 -0
  144. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/__init__.py +0 -0
  145. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/_batching.py +0 -0
  146. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/_single_flight.py +0 -0
  147. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/abc/__init__.py +0 -0
  148. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/abc/backend.py +0 -0
  149. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/abc/config.py +0 -0
  150. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/abc/dependency.py +0 -0
  151. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/calls.py +0 -0
  152. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/codec.py +0 -0
  153. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/decorators.py +0 -0
  154. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/dependency.py +0 -0
  155. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/errors.py +0 -0
  156. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/gateway.py +0 -0
  157. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/keys.py +0 -0
  158. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/memory.py +0 -0
  159. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/result_codec.py +0 -0
  160. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/serializer.py +0 -0
  161. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/cache/wiring.py +0 -0
  162. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/command/__init__.py +0 -0
  163. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/command/adapter.py +0 -0
  164. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/command/base.py +0 -0
  165. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/command/field.py +0 -0
  166. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/command/introspection.py +0 -0
  167. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/compiler.py +0 -0
  168. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/__init__.py +0 -0
  169. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/_boto3.py +0 -0
  170. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/_includes.py +0 -0
  171. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/_resolver_utils.py +0 -0
  172. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/binder.py +0 -0
  173. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/configurable.py +0 -0
  174. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/context.py +0 -0
  175. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/errors.py +0 -0
  176. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/keys.py +0 -0
  177. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/loader.py +0 -0
  178. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/model.py +0 -0
  179. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/observability.py +0 -0
  180. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/resolver.py +0 -0
  181. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/secrets.py +0 -0
  182. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/config/ssm.py +0 -0
  183. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/contracts/__init__.py +0 -0
  184. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/contracts/manifest.py +0 -0
  185. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/di/__init__.py +0 -0
  186. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/di/container.py +0 -0
  187. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/di/scope.py +0 -0
  188. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/discovery/__init__.py +0 -0
  189. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/discovery/_utils.py +0 -0
  190. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/discovery/base.py +0 -0
  191. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/discovery/interfaces.py +0 -0
  192. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/discovery/manifest.py +0 -0
  193. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/discovery/modules.py +0 -0
  194. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/engine/__init__.py +0 -0
  195. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/engine/compilable.py +0 -0
  196. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/engine/compiler.py +0 -0
  197. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/engine/events.py +0 -0
  198. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/engine/executor.py +0 -0
  199. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/engine/metrics.py +0 -0
  200. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/engine/plan.py +0 -0
  201. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/engine/post_commit.py +0 -0
  202. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/errors/__init__.py +0 -0
  203. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/errors/codes.py +0 -0
  204. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/errors/errors.py +0 -0
  205. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/expr/__init__.py +0 -0
  206. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/expr/eval.py +0 -0
  207. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/expr/nodes.py +0 -0
  208. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/expr/refs.py +0 -0
  209. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/identity/__init__.py +0 -0
  210. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/identity/context.py +0 -0
  211. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/identity/identity.py +0 -0
  212. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/identity/issuer.py +0 -0
  213. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/identity/wire.py +0 -0
  214. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/introspection/__init__.py +0 -0
  215. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/introspection/base.py +0 -0
  216. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/introspection/describe.py +0 -0
  217. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/job/__init__.py +0 -0
  218. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/job/callback.py +0 -0
  219. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/job/context.py +0 -0
  220. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/job/handle.py +0 -0
  221. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/job/job.py +0 -0
  222. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/job/service.py +0 -0
  223. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/locator.py +0 -0
  224. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/logger/__init__.py +0 -0
  225. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/logger/abc.py +0 -0
  226. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/logger/config.py +0 -0
  227. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/logger/registry.py +0 -0
  228. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/logger/structlogger.py +0 -0
  229. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/_loom_type.py +0 -0
  230. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/base.py +0 -0
  231. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/convert.py +0 -0
  232. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/enums.py +0 -0
  233. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/field.py +0 -0
  234. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/partition.py +0 -0
  235. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/privilege.py +0 -0
  236. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/projection.py +0 -0
  237. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/relation.py +0 -0
  238. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/scoped.py +0 -0
  239. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/struct.py +0 -0
  240. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/timestamped.py +0 -0
  241. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/types.py +0 -0
  242. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/model/types_postgres.py +0 -0
  243. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/__init__.py +0 -0
  244. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/config.py +0 -0
  245. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/event.py +0 -0
  246. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/observer/__init__.py +0 -0
  247. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/observer/noop.py +0 -0
  248. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/observer/otel.py +0 -0
  249. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/observer/structlog.py +0 -0
  250. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/otel_ids.py +0 -0
  251. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/protocol.py +0 -0
  252. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/runtime.py +0 -0
  253. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/span.py +0 -0
  254. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/observability/topology.py +0 -0
  255. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/persistence/__init__.py +0 -0
  256. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/persistence/abc.py +0 -0
  257. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/persistence/none.py +0 -0
  258. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/persistence/registry.py +0 -0
  259. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/plugins/__init__.py +0 -0
  260. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/plugins/entrypoints.py +0 -0
  261. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/plugins/optional.py +0 -0
  262. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/projection/__init__.py +0 -0
  263. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/projection/loaders.py +0 -0
  264. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/projection/runtime.py +0 -0
  265. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/__init__.py +0 -0
  266. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/abc/__init__.py +0 -0
  267. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/abc/errors.py +0 -0
  268. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/abc/query.py +0 -0
  269. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/abc/query_compiler.py +0 -0
  270. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/abc/repo_for.py +0 -0
  271. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/abc/repository.py +0 -0
  272. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/abc/session_scope.py +0 -0
  273. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/dynamodb/__init__.py +0 -0
  274. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/dynamodb/backend.py +0 -0
  275. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/dynamodb/registry.py +0 -0
  276. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/dynamodb/uow.py +0 -0
  277. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mongo/__init__.py +0 -0
  278. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mongo/backend.py +0 -0
  279. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mongo/ids.py +0 -0
  280. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mongo/query_compiler.py +0 -0
  281. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mongo/registry.py +0 -0
  282. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mongo/uow.py +0 -0
  283. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mongo/values.py +0 -0
  284. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/mutation.py +0 -0
  285. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/registration.py +0 -0
  286. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/registry.py +0 -0
  287. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/__init__.py +0 -0
  288. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/backend.py +0 -0
  289. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/integrity.py +0 -0
  290. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/loaders.py +0 -0
  291. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/migrations/__init__.py +0 -0
  292. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/migrations/env_template.py +0 -0
  293. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/migrations/hook.py +0 -0
  294. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/migrations/operations.py +0 -0
  295. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/migrations/runners.py +0 -0
  296. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/model.py +0 -0
  297. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/profile_loader.py +0 -0
  298. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/projection.py +0 -0
  299. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/query_compiler/__init__.py +0 -0
  300. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/query_compiler/errors.py +0 -0
  301. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/query_compiler/filters.py +0 -0
  302. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/query_compiler/ordering.py +0 -0
  303. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/query_compiler/paths.py +0 -0
  304. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/query_compiler/subquery.py +0 -0
  305. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/registry.py +0 -0
  306. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/repository.py +0 -0
  307. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/__init__.py +0 -0
  308. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/bootstrap.py +0 -0
  309. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/config.py +0 -0
  310. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/elevate.py +0 -0
  311. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/guard/0001.sql +0 -0
  312. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/guard/0002.sql +0 -0
  313. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/guard/preflight.sql +0 -0
  314. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/guard_manifest.py +0 -0
  315. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/integrity.py +0 -0
  316. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/partitions.py +0 -0
  317. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/provider.py +0 -0
  318. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/schema.py +0 -0
  319. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/sources.py +0 -0
  320. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/rls/verify.py +0 -0
  321. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/session_manager.py +0 -0
  322. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/session_settings.py +0 -0
  323. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/transactional.py +0 -0
  324. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/repository/sqlalchemy/uow.py +0 -0
  325. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/response/__init__.py +0 -0
  326. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/response/base.py +0 -0
  327. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/routing/__init__.py +0 -0
  328. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/routing/ref.py +0 -0
  329. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/routing/resolver.py +0 -0
  330. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/runner.py +0 -0
  331. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/schema/__init__.py +0 -0
  332. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/schema/schema_mode.py +0 -0
  333. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/schema_names.py +0 -0
  334. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/__init__.py +0 -0
  335. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/abc/__init__.py +0 -0
  336. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/abc/contracts.py +0 -0
  337. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/abc/errors.py +0 -0
  338. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/caller_bound.py +0 -0
  339. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/clickhouse/__init__.py +0 -0
  340. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/clickhouse/_client.py +0 -0
  341. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/clickhouse/executor.py +0 -0
  342. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/clickhouse/registry.py +0 -0
  343. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/config.py +0 -0
  344. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/roles.py +0 -0
  345. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/sql/service.py +0 -0
  346. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/symbols.py +0 -0
  347. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/tracing/__init__.py +0 -0
  348. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/tracing/context.py +0 -0
  349. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/transaction.py +0 -0
  350. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/transport/__init__.py +0 -0
  351. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/transport/adapter.py +0 -0
  352. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/uow/__init__.py +0 -0
  353. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/uow/abc.py +0 -0
  354. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/uow/context.py +0 -0
  355. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/__init__.py +0 -0
  356. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/_predicates.py +0 -0
  357. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/agent_markers.py +0 -0
  358. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/compute.py +0 -0
  359. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/constants.py +0 -0
  360. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/factory.py +0 -0
  361. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/field_ref.py +0 -0
  362. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/invoker.py +0 -0
  363. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/keys.py +0 -0
  364. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/markers.py +0 -0
  365. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/mcp_markers.py +0 -0
  366. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/registry.py +0 -0
  367. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/rule.py +0 -0
  368. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/core/use_case/use_case.py +0 -0
  369. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/__init__.py +0 -0
  370. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/_uri.py +0 -0
  371. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/__init__.py +0 -0
  372. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_format_registry.py +0 -0
  373. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_historify/__init__.py +0 -0
  374. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_historify/_common.py +0 -0
  375. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_historify/_log.py +0 -0
  376. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_historify/_ops.py +0 -0
  377. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_historify/_snapshot.py +0 -0
  378. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_historify/_transform.py +0 -0
  379. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_merge.py +0 -0
  380. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_path_template.py +0 -0
  381. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_predicate.py +0 -0
  382. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_schema_aligner/__init__.py +0 -0
  383. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_schema_aligner/_aligner.py +0 -0
  384. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_schema_aligner/_policy.py +0 -0
  385. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/_write_policy.py +0 -0
  386. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/__init__.py +0 -0
  387. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_dtype.py +0 -0
  388. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_file_writer.py +0 -0
  389. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_frame.py +0 -0
  390. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_historify.py +0 -0
  391. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_predicate.py +0 -0
  392. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_reader.py +0 -0
  393. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_schema.py +0 -0
  394. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_schema_aligner.py +0 -0
  395. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_streaming.py +0 -0
  396. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/_writer.py +0 -0
  397. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/polars/provider.py +0 -0
  398. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/spark/__init__.py +0 -0
  399. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/spark/_dtype.py +0 -0
  400. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/spark/_historify.py +0 -0
  401. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/spark/_reader.py +0 -0
  402. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/spark/_schema.py +0 -0
  403. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/spark/_schema_aligner.py +0 -0
  404. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/spark/_writer.py +0 -0
  405. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/backends/spark/provider.py +0 -0
  406. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/checkpoint/__init__.py +0 -0
  407. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/checkpoint/_backends/_polars.py +0 -0
  408. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/checkpoint/_backends/_spark.py +0 -0
  409. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/checkpoint/_cleaners.py +0 -0
  410. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/checkpoint/_options.py +0 -0
  411. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/checkpoint/_paths.py +0 -0
  412. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/checkpoint/_scope.py +0 -0
  413. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/checkpoint/_store.py +0 -0
  414. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/__init__.py +0 -0
  415. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_binding.py +0 -0
  416. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_compiler.py +0 -0
  417. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_errors.py +0 -0
  418. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_filter.py +0 -0
  419. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_plan.py +0 -0
  420. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_validators.py +0 -0
  421. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_validators_agents.py +0 -0
  422. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_validators_config.py +0 -0
  423. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_validators_plan.py +0 -0
  424. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/compiler/_validators_step.py +0 -0
  425. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/__init__.py +0 -0
  426. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/_format.py +0 -0
  427. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/_read_options.py +0 -0
  428. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/_utils.py +0 -0
  429. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/_with_agent.py +0 -0
  430. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/_write_options.py +0 -0
  431. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/expr/__init__.py +0 -0
  432. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/expr/_params.py +0 -0
  433. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/expr/_predicate.py +0 -0
  434. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/expr/_predicate_dialect.py +0 -0
  435. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/expr/_refs.py +0 -0
  436. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/source/__init__.py +0 -0
  437. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/source/_from.py +0 -0
  438. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/source/_from_clickhouse.py +0 -0
  439. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/source/_from_config.py +0 -0
  440. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/source/_from_dynamodb.py +0 -0
  441. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/source/_from_mongo.py +0 -0
  442. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/source/_specs.py +0 -0
  443. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/__init__.py +0 -0
  444. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_client.py +0 -0
  445. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_errors.py +0 -0
  446. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_file.py +0 -0
  447. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_history/__init__.py +0 -0
  448. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_history/_builder.py +0 -0
  449. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_history/_enums.py +0 -0
  450. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_history/_errors.py +0 -0
  451. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_history/_report.py +0 -0
  452. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_history/_spec.py +0 -0
  453. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_into.py +0 -0
  454. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_schema_mode.py +0 -0
  455. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_table.py +0 -0
  456. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/declarative/target/_temp.py +0 -0
  457. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/executor/__init__.py +0 -0
  458. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/executor/_agents.py +0 -0
  459. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/executor/_dispatcher.py +0 -0
  460. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/executor/_executor.py +0 -0
  461. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/__init__.py +0 -0
  462. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/_registry.py +0 -0
  463. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/__init__.py +0 -0
  464. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_clickhouse.py +0 -0
  465. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_dynamodb.py +0 -0
  466. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_dynamodb_items.py +0 -0
  467. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_dynamodb_predicate.py +0 -0
  468. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_mongo.py +0 -0
  469. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_mongo_batch.py +0 -0
  470. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_mongo_bson.py +0 -0
  471. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_mongo_predicate.py +0 -0
  472. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/sources/_serialization.py +0 -0
  473. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/targets/__init__.py +0 -0
  474. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/io/targets/_clickhouse.py +0 -0
  475. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/lineage/__init__.py +0 -0
  476. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/lineage/_config.py +0 -0
  477. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/lineage/_observer.py +0 -0
  478. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/lineage/_records.py +0 -0
  479. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/lineage/sinks/__init__.py +0 -0
  480. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/lineage/sinks/_protocol.py +0 -0
  481. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/lineage/sinks/_table.py +0 -0
  482. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/lineage/sinks/_writer.py +0 -0
  483. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/maintenance/__init__.py +0 -0
  484. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/maintenance/_builder.py +0 -0
  485. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/maintenance/_ops.py +0 -0
  486. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/maintenance/_protocol.py +0 -0
  487. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/maintenance/_runner.py +0 -0
  488. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/maintenance/_step.py +0 -0
  489. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/maintenance/backends/__init__.py +0 -0
  490. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/maintenance/backends/_delta_rs.py +0 -0
  491. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/__init__.py +0 -0
  492. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/_generics.py +0 -0
  493. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/_params.py +0 -0
  494. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/_pipeline.py +0 -0
  495. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/_process.py +0 -0
  496. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/_sql.py +0 -0
  497. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/_step.py +0 -0
  498. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/_step_client.py +0 -0
  499. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/pipeline/_step_sql.py +0 -0
  500. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runner/__init__.py +0 -0
  501. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runner/_agents.py +0 -0
  502. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runner/_providers.py +0 -0
  503. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runner/_wiring.py +0 -0
  504. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runner/config_loader.py +0 -0
  505. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runner/core.py +0 -0
  506. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runner/errors.py +0 -0
  507. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runner/filtering.py +0 -0
  508. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runtime/__init__.py +0 -0
  509. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runtime/_config_values.py +0 -0
  510. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/runtime/contracts.py +0 -0
  511. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/schema/__init__.py +0 -0
  512. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/schema/_contract.py +0 -0
  513. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/schema/_schema.py +0 -0
  514. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/storage/__init__.py +0 -0
  515. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/storage/_config.py +0 -0
  516. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/storage/_file_locator.py +0 -0
  517. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/storage/_locator.py +0 -0
  518. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/storage/routing.py +0 -0
  519. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/testing/__init__.py +0 -0
  520. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/testing/_result.py +0 -0
  521. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/testing/_runners.py +0 -0
  522. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/testing/_scenario.py +0 -0
  523. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/testing/_stubs.py +0 -0
  524. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/etl/testing/spark.py +0 -0
  525. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/__init__.py +0 -0
  526. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/_async.py +0 -0
  527. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/_config.py +0 -0
  528. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/_ctx.py +0 -0
  529. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/_flow_yaml.py +0 -0
  530. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/_meta.py +0 -0
  531. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/_placeholders.py +0 -0
  532. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/_summary.py +0 -0
  533. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/deploy/__init__.py +0 -0
  534. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/deploy/_discovery.py +0 -0
  535. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/deploy/_schedule.py +0 -0
  536. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/deploy/_yaml_etls.py +0 -0
  537. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/deploy/entrypoint.py +0 -0
  538. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/__init__.py +0 -0
  539. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_assemble.py +0 -0
  540. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_backfill.py +0 -0
  541. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_backfill_body.py +0 -0
  542. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_body.py +0 -0
  543. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_common.py +0 -0
  544. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_factory.py +0 -0
  545. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_hooks.py +0 -0
  546. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_maintenance.py +0 -0
  547. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_params.py +0 -0
  548. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_run_name.py +0 -0
  549. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_runtime.py +0 -0
  550. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_signature.py +0 -0
  551. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/flow/_stages.py +0 -0
  552. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/manifest/__init__.py +0 -0
  553. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/manifest/_model.py +0 -0
  554. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/manifest/_s3.py +0 -0
  555. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/manifest/_store.py +0 -0
  556. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/notify/__init__.py +0 -0
  557. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/notify/_event.py +0 -0
  558. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/notify/_factory.py +0 -0
  559. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/notify/_format.py +0 -0
  560. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/notify/_port.py +0 -0
  561. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/notify/_slack.py +0 -0
  562. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/notify/_sns.py +0 -0
  563. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/observer/__init__.py +0 -0
  564. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/observer/_flow.py +0 -0
  565. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/observer/_logging_bridge.py +0 -0
  566. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/observer/_manifest.py +0 -0
  567. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prefect/observer/_task_run.py +0 -0
  568. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prometheus/__init__.py +0 -0
  569. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prometheus/_instruments.py +0 -0
  570. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prometheus/adapter.py +0 -0
  571. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prometheus/kafka.py +0 -0
  572. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prometheus/lifecycle.py +0 -0
  573. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/prometheus/middleware.py +0 -0
  574. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/py.typed +0 -0
  575. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/__init__.py +0 -0
  576. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/_body.py +0 -0
  577. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/adapter.py +0 -0
  578. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/auth/__init__.py +0 -0
  579. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/auth/abc.py +0 -0
  580. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/auth/config.py +0 -0
  581. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/auth/jwt.py +0 -0
  582. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/auth/middleware.py +0 -0
  583. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/compiler.py +0 -0
  584. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/config.py +0 -0
  585. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/constants.py +0 -0
  586. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/cors.py +0 -0
  587. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/errors.py +0 -0
  588. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/__init__.py +0 -0
  589. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/_errors.py +0 -0
  590. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/_exclusions.py +0 -0
  591. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/app.py +0 -0
  592. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/auto.py +0 -0
  593. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/health.py +0 -0
  594. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/openapi.py +0 -0
  595. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/response.py +0 -0
  596. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/router_runtime.py +0 -0
  597. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/fastapi/sql.py +0 -0
  598. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/middleware.py +0 -0
  599. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/model.py +0 -0
  600. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/rest/rest_adapter.py +0 -0
  601. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/__init__.py +0 -0
  602. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/__init__.py +0 -0
  603. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_adapter.py +0 -0
  604. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_batch_spans.py +0 -0
  605. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_commit_tracker.py +0 -0
  606. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_dlq.py +0 -0
  607. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_error_boundary.py +0 -0
  608. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_errors.py +0 -0
  609. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_operators.py +0 -0
  610. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_resource_manager.py +0 -0
  611. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_runtime_io.py +0 -0
  612. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/_sink_registry.py +0 -0
  613. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/__init__.py +0 -0
  614. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/_shared.py +0 -0
  615. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/boundary.py +0 -0
  616. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/dispatcher.py +0 -0
  617. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/routing.py +0 -0
  618. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/scopes.py +0 -0
  619. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/shapes.py +0 -0
  620. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/steps.py +0 -0
  621. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/handlers/storage.py +0 -0
  622. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/bytewax/runner.py +0 -0
  623. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/compiler/__init__.py +0 -0
  624. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/compiler/_bindings.py +0 -0
  625. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/compiler/_compiler.py +0 -0
  626. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/compiler/_errors.py +0 -0
  627. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/compiler/_plan.py +0 -0
  628. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/compiler/phases/build_plan.py +0 -0
  629. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/compiler/phases/validate.py +0 -0
  630. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/core/__init__.py +0 -0
  631. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/core/_errors.py +0 -0
  632. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/core/_exceptions.py +0 -0
  633. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/core/_message.py +0 -0
  634. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/core/_tracing.py +0 -0
  635. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/core/_typing.py +0 -0
  636. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/graph/__init__.py +0 -0
  637. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/graph/_flow.py +0 -0
  638. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/__init__.py +0 -0
  639. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/_codec.py +0 -0
  640. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/_config.py +0 -0
  641. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/_errors.py +0 -0
  642. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/_key_resolver.py +0 -0
  643. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/_message.py +0 -0
  644. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/_record.py +0 -0
  645. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/_wire.py +0 -0
  646. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/client/__init__.py +0 -0
  647. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/client/_consumer.py +0 -0
  648. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/client/_producer.py +0 -0
  649. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/client/_protocol.py +0 -0
  650. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/client/_retry.py +0 -0
  651. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/message/__init__.py +0 -0
  652. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/message/_consumer.py +0 -0
  653. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/message/_producer.py +0 -0
  654. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/kafka/message/_protocol.py +0 -0
  655. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/mongo/__init__.py +0 -0
  656. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/mongo/_bytewax_source.py +0 -0
  657. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/mongo/_config.py +0 -0
  658. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/mongo/_event.py +0 -0
  659. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/mongo/_normalize.py +0 -0
  660. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/__init__.py +0 -0
  661. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_boundary.py +0 -0
  662. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_branches.py +0 -0
  663. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_broadcast.py +0 -0
  664. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_capabilities.py +0 -0
  665. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_decompose.py +0 -0
  666. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_expand_routes.py +0 -0
  667. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_expr_eval.py +0 -0
  668. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_fork.py +0 -0
  669. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_helpers.py +0 -0
  670. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_mongo.py +0 -0
  671. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_protocols.py +0 -0
  672. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_router.py +0 -0
  673. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_shape.py +0 -0
  674. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_sink.py +0 -0
  675. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_step.py +0 -0
  676. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_table/__init__.py +0 -0
  677. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_table/common.py +0 -0
  678. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_table/config.py +0 -0
  679. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/_with.py +0 -0
  680. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/nodes/refs.py +0 -0
  681. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/streaming/testing.py +0 -0
  682. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/testing/__init__.py +0 -0
  683. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/testing/agents.py +0 -0
  684. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/testing/golden.py +0 -0
  685. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/testing/http_harness.py +0 -0
  686. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/testing/repository_harness.py +0 -0
  687. {loom_kernel-2.19.0 → loom_kernel-2.20.0}/src/loom/testing/runner.py +0 -0
@@ -375,6 +375,34 @@
375
375
 
376
376
  ### core
377
377
 
378
+ - **core:** on a model with a composite primary key, the SQLAlchemy
379
+ repository's `get_by_id`, `exists`, `update` and `delete` take the key as a
380
+ tuple of every key value in declaration order (`repo.get_by_id((account_id,
381
+ entry_id))`) and raise `ValueError` for anything else, a scalar included,
382
+ instead of matching the first key column. A `Load` step and a cached
383
+ repository's `get_by_id`, `update` and `delete` pass the id unchanged, so a
384
+ scalar id raises there too; a cached `@cache_query(scope="entity")` read
385
+ takes the tuple as its first argument and raises `TypeError` for anything
386
+ else. A cursor token issued for such a model before this release is refused with
387
+ `UnsupportedQuery`, `400 unsupported_query`; clients restart from the first
388
+ page. `decode_cursor` takes `id_count`, the number of key columns, and
389
+ `get_id_attributes` returns every key column name.
390
+
391
+ - **core:** `compile_all` raises `ValueError` for a model with a composite
392
+ primary key that declares a relation or a projection whose loader runs
393
+ `load_many`, naming the model, its key and those fields. The backend loads
394
+ them by the first key column only, so a many-to-one relation attached
395
+ another owner's row and a one-to-many relation grouped children by that
396
+ column; load them by hand until they are matched by the full key.
397
+
398
+ - **core:** the `RepositoryMongo`, `RepositoryDynamoDB` and
399
+ `loom.testing.InMemoryRepository` constructors raise `ConfigError` for a
400
+ model with a composite primary key, naming the model and its key. Each
401
+ keys an item by one field (Mongo's `_id` and the DynamoDB partition key by
402
+ the first key column, the in-memory store by its `id_field`), so a second
403
+ row sharing that field's value raised `Conflict` on Mongo and DynamoDB and
404
+ replaced the first row in memory.
405
+
378
406
  - **core:** the miss path of a cached `@cache_query`/`@cache_call` read,
379
407
  `McpHandle.call` and `CacheGateway.get_value`/`multi_get_values` now raise
380
408
  `BoundaryValidationError` — a `ValueError` — on a stale or malformed cached
@@ -457,6 +485,10 @@
457
485
 
458
486
  ### rest
459
487
 
488
+ - **rest:** auto-CRUD mounts no `/{id}` route (get, update, delete) for a
489
+ model with a composite primary key, since one path segment cannot carry the
490
+ key; naming one of them in `include` raises `ValueError` when the routes are
491
+ built. Those routes matched the first key column only.
460
492
  - **rest:** a JSON object body that fails `Command` validation now answers
461
493
  `422 boundary_validation` with a `violations` list (`{field, message}`
462
494
  pairs, `field` being the wire name the client sent, the same shape
@@ -570,6 +602,27 @@
570
602
 
571
603
  ### core
572
604
 
605
+ - **core:** cursor pagination and the by-id operations of the SQLAlchemy
606
+ repository use the full primary key of a model whose key is composite, such
607
+ as a `RowScoped` model keyed by its boundary column and its own id. They
608
+ used only the first key column: a cursor walk ordered and compared on it
609
+ alone and skipped the rows sharing that column's value with the last row of
610
+ each page, and `get_by_id`, `exists`, `update` and `delete` matched that
611
+ column only. The cursor now orders and compares on
612
+ every key column after the sort keys and carries every key value in the
613
+ token; offset pages and `list_paginated` order by every key column too.
614
+ Tokens of single-column keys are unchanged. A cached repository over such a
615
+ model keyed its entries by the first key column too, so a warm
616
+ `list_paginated` or first cursor page served the rows sharing a first key
617
+ value as copies of one of them; it now caches and reloads every row by its
618
+ full key, one `get_by_id` per missing row.
619
+
620
+ - **core:** a `MANY_TO_MANY` relation joins the target table on the column
621
+ the secondary table's foreign key references. It joined on the target
622
+ model's first key column, so a target with a composite key, or whose link
623
+ references a unique non-key column, loaded the rows whose first key column
624
+ held the linked value instead of the linked rows.
625
+
573
626
  - **core:** the RLS guard refuses, in the same statement, a drop outside the
574
627
  hatch that breaks its invariants. `DROP POLICY` or `DROP TRIGGER
575
628
  loom_deny_owner_dml` on a scoped table used to commit, because drops are not
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: loom-kernel
3
- Version: 2.19.0
3
+ Version: 2.20.0
4
4
  Summary: Loom Python project
5
5
  License-File: LICENSE
6
6
  Classifier: Programming Language :: Python :: 3
@@ -61,7 +61,8 @@ class CoreRelationStep:
61
61
 
62
62
  * ``MANY_TO_MANY``:
63
63
  ``fk_col`` = owner FK column in the secondary table.
64
- ``pk_col`` = PK column on the *target* table.
64
+ ``pk_col`` = target column the secondary table's FK references (a key
65
+ or unique column).
65
66
  ``secondary_table`` / ``secondary_target_fk`` required.
66
67
  """
67
68
 
@@ -13,6 +13,7 @@ from sqlalchemy import (
13
13
  BigInteger,
14
14
  Boolean,
15
15
  CheckConstraint,
16
+ Column,
16
17
  Constraint,
17
18
  DateTime,
18
19
  Float,
@@ -57,6 +58,7 @@ from loom.core.model.introspection import (
57
58
  extract_model_from_hint,
58
59
  get_column_fields,
59
60
  get_id_attribute,
61
+ get_id_attributes,
60
62
  get_projections,
61
63
  get_relations,
62
64
  get_table_name,
@@ -879,6 +881,7 @@ def _compile_core_model(struct_cls: type, comp: _Compilation) -> None:
879
881
  profile_plans = _build_profile_plans(
880
882
  profiles, all_columns, id_attr, relations, relation_steps, projections, struct_cls
881
883
  )
884
+ _refuse_composite_key_loads(struct_cls, profile_plans)
882
885
 
883
886
  core_model = CoreModel(struct_cls, table, id_attr, profile_plans)
884
887
  for name, col in zip(column_fields, all_columns, strict=False):
@@ -887,6 +890,37 @@ def _compile_core_model(struct_cls: type, comp: _Compilation) -> None:
887
890
  comp.core[struct_cls] = core_model
888
891
 
889
892
 
893
+ def _refuse_composite_key_loads(struct_cls: type, plans: dict[str, CoreProfilePlan]) -> None:
894
+ """Refuse relations and backend projections on a model with a composite primary key.
895
+
896
+ The core loaders match loaded rows to their owner by ``CoreProfilePlan.id_attr``,
897
+ the first primary-key column, so on a composite key they attach rows to the
898
+ wrong owner.
899
+ """
900
+ key = get_id_attributes(struct_cls)
901
+ if len(key) == 1:
902
+ return
903
+ loads = _fields_loaded_by_key(plans)
904
+ if loads:
905
+ raise ValueError(
906
+ f"{struct_cls.__name__} has the composite primary key ({', '.join(key)}); the "
907
+ f"backend loads {', '.join(loads)} by its first key column only, so drop "
908
+ "them from the model and load them by hand"
909
+ )
910
+
911
+
912
+ def _fields_loaded_by_key(plans: dict[str, CoreProfilePlan]) -> list[str]:
913
+ relations = {step.attr for plan in plans.values() for step in plan.relation_steps}
914
+ projections = {
915
+ step.name
916
+ for plan in plans.values()
917
+ for level in plan.projection_plan.levels
918
+ for step in level
919
+ if not step.prefer_memory
920
+ }
921
+ return sorted(relations | projections)
922
+
923
+
890
924
  def _collect_profiles(
891
925
  relations: dict[str, Any],
892
926
  projections: dict[str, Any],
@@ -1167,12 +1201,12 @@ def _compile_many_to_many_step(
1167
1201
  if secondary_table is None:
1168
1202
  return None
1169
1203
  owner_table_name = get_table_name(struct_cls)
1170
- secondary_owner_fk, secondary_target_fk, target_table_name = _inspect_secondary(
1204
+ secondary_owner_fk, secondary_target_fk, target_ref = _inspect_secondary(
1171
1205
  secondary_table, owner_table_name
1172
1206
  )
1173
- if secondary_owner_fk is None or secondary_target_fk is None or target_table_name is None:
1207
+ if secondary_owner_fk is None or secondary_target_fk is None or target_ref is None:
1174
1208
  return None
1175
- target_sa = _find_target_sa_class(target_table_name, comp)
1209
+ target_sa = _find_target_sa_class(target_ref.table.name, comp)
1176
1210
  if target_sa is None:
1177
1211
  return None
1178
1212
  related_struct = getattr(target_sa, "__struct_cls__", None)
@@ -1188,7 +1222,7 @@ def _compile_many_to_many_step(
1188
1222
  related_struct=related_struct,
1189
1223
  owner_pk_col=get_id_attribute(struct_cls),
1190
1224
  fk_col=secondary_owner_fk,
1191
- pk_col=get_id_attribute(related_struct),
1225
+ pk_col=target_ref.name,
1192
1226
  secondary_table=secondary_table,
1193
1227
  secondary_target_fk=secondary_target_fk,
1194
1228
  )
@@ -1197,18 +1231,19 @@ def _compile_many_to_many_step(
1197
1231
  def _inspect_secondary(
1198
1232
  secondary_table: Any,
1199
1233
  owner_table_name: str,
1200
- ) -> tuple[str | None, str | None, str | None]:
1234
+ ) -> tuple[str | None, str | None, Column[Any] | None]:
1235
+ """Return the owner FK column, the target FK column and the target column it references."""
1201
1236
  owner_fk: str | None = None
1202
1237
  target_fk: str | None = None
1203
- target_table_name: str | None = None
1238
+ target_ref: Column[Any] | None = None
1204
1239
  for col in secondary_table.columns:
1205
1240
  for fk in col.foreign_keys:
1206
1241
  if fk.column.table.name == owner_table_name:
1207
1242
  owner_fk = col.name
1208
1243
  else:
1209
1244
  target_fk = col.name
1210
- target_table_name = fk.column.table.name
1211
- return owner_fk, target_fk, target_table_name
1245
+ target_ref = fk.column
1246
+ return owner_fk, target_fk, target_ref
1212
1247
 
1213
1248
 
1214
1249
  def _target_cols(related_struct: type, target_table: Any) -> tuple[Any, ...]:
@@ -7,6 +7,7 @@ import warnings
7
7
  from collections.abc import Awaitable, Callable, Mapping, Sequence
8
8
  from dataclasses import dataclass
9
9
  from functools import partial, wraps
10
+ from types import GenericAlias
10
11
  from typing import Any, Generic, NamedTuple, TypeVar, cast
11
12
 
12
13
  import msgspec
@@ -30,7 +31,7 @@ from loom.core.model import BoundaryValidationError, LoomType, is_pydantic_model
30
31
  from loom.core.model.convert import to_struct
31
32
  from loom.core.model.enums import Cardinality
32
33
  from loom.core.model.introspection import (
33
- get_id_attribute,
34
+ get_id_attributes,
34
35
  get_projections,
35
36
  get_relations,
36
37
  list_element_type,
@@ -127,37 +128,42 @@ class _DependencySpec:
127
128
  class _PrimaryKey(NamedTuple):
128
129
  """Primary key of the cached model as the wrapper reads it.
129
130
 
131
+ A single-column key is its value; a composite key is the tuple of every
132
+ key value in declaration order, the form the SQLAlchemy repository's
133
+ ``get_by_id`` takes.
134
+
130
135
  Attributes:
131
- attribute: Name of the primary-key attribute; ``id`` when the wrapped
132
- repository declares no model or the model declares no primary key.
133
- python_type: Type of the key, or ``None`` when it is unresolvable or
134
- not a plain class (``int | None``, for instance).
136
+ attributes: Names of the primary-key attributes in declaration order;
137
+ ``("id",)`` when the wrapped repository declares no model or the
138
+ model declares no primary key.
139
+ python_types: Type of each key attribute, ``None`` where it is
140
+ unresolvable or not a plain class (``int | None``, for instance).
135
141
  """
136
142
 
137
- attribute: str
138
- python_type: type | None
143
+ attributes: tuple[str, ...]
144
+ python_types: tuple[type | None, ...]
139
145
 
140
146
 
141
- _FALLBACK_PRIMARY_KEY = _PrimaryKey(attribute="id", python_type=None)
147
+ _FALLBACK_PRIMARY_KEY = _PrimaryKey(attributes=("id",), python_types=(None,))
142
148
 
143
149
 
144
150
  def _resolve_primary_key(repository: Repository[Any, Any, Any, Any]) -> _PrimaryKey:
145
151
  """Resolve the primary key of the wrapped repository's model.
146
152
 
147
153
  Falls back to the attribute ``id`` with no type for a repository without a
148
- model or whose model declares no primary key; the type alone is ``None``
149
- when the key's annotation is not a plain class. The cached index is then
150
- used as it was decoded.
154
+ model or whose model declares no primary key; the type of one attribute
155
+ alone is ``None`` when its annotation is not a plain class.
151
156
  """
152
157
  model = getattr(repository, "model", None)
153
158
  if model is None:
154
159
  return _FALLBACK_PRIMARY_KEY
155
160
  try:
156
- attribute = get_id_attribute(model)
161
+ attributes = get_id_attributes(model)
157
162
  except (TypeError, ValueError):
158
163
  return _FALLBACK_PRIMARY_KEY
159
- hint = resolve_type_hints(model).get(attribute)
160
- return _PrimaryKey(attribute, hint if isinstance(hint, type) else None)
164
+ hints = resolve_type_hints(model)
165
+ types = tuple(hint if isinstance(hint, type) else None for hint in map(hints.get, attributes))
166
+ return _PrimaryKey(attributes, types)
161
167
 
162
168
 
163
169
  class CachedRepository(
@@ -174,9 +180,11 @@ class CachedRepository(
174
180
  single ``<primary key> IN (...)`` query, so the wrapped repository should
175
181
  support :attr:`~loom.core.repository.abc.query.FilterOp.IN` on the primary
176
182
  key; a repository whose ``allowed_filter_fields`` excludes the primary key
177
- is detected and served with per-id reads instead. The key is read by the
178
- attribute name the model declares (``id`` when there is no model or the
179
- model declares no primary key).
183
+ is detected and served with per-id reads instead, and so is a model with a
184
+ composite primary key, which a filter on one field cannot match. The key
185
+ is read by the attribute names the model declares (``id`` when there is no
186
+ model or the model declares no primary key); a composite key is the tuple
187
+ of its values in declaration order.
180
188
 
181
189
  Concurrent misses of the same key on the entity read and on a
182
190
  ``@cache_query`` read are coalesced inside the process: the first caller
@@ -699,12 +707,15 @@ class CachedRepository(
699
707
  return max(1, round(ttl + self._rng.uniform(-spread, spread)))
700
708
 
701
709
  def _extract_entity_id(self, item: Any) -> object | None:
702
- """Read the primary key of *item* by the attribute the model declares.
710
+ """Read the primary key of *item* by the attributes the model declares.
703
711
 
704
- The same attribute names the refill filter, which queries the missing
705
- entities with ``FilterSpec(field=<primary key>, ...)``.
712
+ Returns the value of a single-column key and the tuple of every value
713
+ of a composite one, or ``None`` when a key value is missing.
706
714
  """
707
- return getattr(item, self._primary_key.attribute, None)
715
+ values = tuple(getattr(item, name, None) for name in self._primary_key.attributes)
716
+ if None in values:
717
+ return None
718
+ return values[0] if len(values) == 1 else values
708
719
 
709
720
  async def _create_many(self, data: Sequence[msgspec.Struct]) -> tuple[OutputT, ...]:
710
721
  if not data:
@@ -842,11 +853,12 @@ class CachedRepository(
842
853
  """Return the primary key a ``scope="entity"`` read is keyed by, or raise.
843
854
 
844
855
  The key must be the first positional argument (a keyword does not
845
- count) and must not be ``None``. When the model's primary-key type
846
- resolved to a plain class, the argument must be of that exact type:
847
- a ``datetime`` for a ``date`` key or a ``bool`` for an ``int`` key
856
+ count) and must not be ``None``; a composite key must be a tuple of
857
+ one value per key attribute. Where a key attribute's type resolved
858
+ to a plain class, its value must be of that exact type: a
859
+ ``datetime`` for a ``date`` key or a ``bool`` for an ``int`` key
848
860
  would render a tag no mutation event ever bumps, so both are
849
- rejected even though ``isinstance`` would accept them. A key
861
+ rejected even though ``isinstance`` would accept them. An attribute
850
862
  declared ``int | None`` resolves no class and gets no type check.
851
863
  Only this entry point is guarded: ``get_by_id``, ``update`` and
852
864
  ``delete`` are typed ``IdT`` and stay unchecked.
@@ -859,11 +871,18 @@ class CachedRepository(
859
871
  f"{method_name}: @cache_query(scope='entity') needs the primary key"
860
872
  " as first positional argument"
861
873
  )
862
- expected = self._primary_key.python_type
863
- if expected is not None and type(args[0]) is not expected:
874
+ types = self._primary_key.python_types
875
+ composite = len(types) > 1
876
+ values = args[0] if composite and isinstance(args[0], tuple) else (args[0],)
877
+ if len(values) != len(types) or any(
878
+ expected is not None and type(value) is not expected
879
+ for value, expected in zip(values, types, strict=True)
880
+ ):
881
+ names = ", ".join("Any" if t is None else t.__name__ for t in types)
864
882
  raise TypeError(
865
883
  f"{method_name}: @cache_query(scope='entity') received a"
866
- f" {type(args[0]).__name__} as primary key, expected {expected.__name__}"
884
+ f" {type(args[0]).__name__} as primary key,"
885
+ f" expected {f'tuple[{names}]' if composite else names}"
867
886
  )
868
887
  return args[0]
869
888
 
@@ -930,10 +949,12 @@ class CachedRepository(
930
949
  A repository may restrict the filterable fields (``allowed_filter_fields``).
931
950
  When the primary-key attribute is not among them the batched refill would
932
951
  be rejected, so the wrapper degrades to per-id reads rather than failing
933
- the page; the repository's declared policy is left untouched.
952
+ the page; the repository's declared policy is left untouched. A
953
+ composite key is always read per id, since a filter names one field.
934
954
  """
955
+ attributes = self._primary_key.attributes
935
956
  allowed = getattr(self._repository, "allowed_filter_fields", None)
936
- return not allowed or self._primary_key.attribute in allowed
957
+ return len(attributes) == 1 and (not allowed or attributes[0] in allowed)
937
958
 
938
959
  async def _fetch_missing_one_by_one(
939
960
  self,
@@ -966,7 +987,9 @@ class CachedRepository(
966
987
  def _id_in_query(self, ids: Sequence[IdT]) -> QuerySpec:
967
988
  return QuerySpec(
968
989
  filters=FilterGroup(
969
- filters=(FilterSpec(field=self._primary_key.attribute, op=FilterOp.IN, value=ids),)
990
+ filters=(
991
+ FilterSpec(field=self._primary_key.attributes[0], op=FilterOp.IN, value=ids),
992
+ )
970
993
  ),
971
994
  limit=len(ids),
972
995
  )
@@ -1029,16 +1052,21 @@ class CachedRepository(
1029
1052
  """Restore the ids of a cached index to the model's primary-key type.
1030
1053
 
1031
1054
  The index round-trips through msgpack, which renders a ``datetime``, a
1032
- ``date`` or a ``UUID`` as a string, while the repository binds the
1033
- declared type: SQLAlchemy returns no rows for a ``datetime`` column
1034
- filtered with strings. Converting here keeps every later use of the id
1035
- — cache key, filter value, identity match — in the declared type.
1055
+ ``date`` or a ``UUID`` as a string and a tuple as a list, while the
1056
+ repository binds the declared type: SQLAlchemy returns no rows for a
1057
+ ``datetime`` column filtered with strings, and the SQLAlchemy
1058
+ repository takes a composite key only as a tuple. Converting here
1059
+ keeps every later use of the id — cache key, filter value, identity
1060
+ match — in the declared type.
1036
1061
 
1037
1062
  Returns:
1038
1063
  The converted ids, or ``None`` when they do not fit the current
1039
1064
  primary-key type, which makes the index unusable.
1040
1065
  """
1041
- id_type = self._primary_key.python_type
1066
+ types = self._primary_key.python_types
1067
+ id_type: Any = types[0]
1068
+ if len(types) > 1:
1069
+ id_type = GenericAlias(tuple, tuple(Any if t is None else t for t in types))
1042
1070
  if id_type is None:
1043
1071
  return cast(list[IdT], raw_ids)
1044
1072
  try:
@@ -22,6 +22,7 @@ from loom.core.model.introspection import (
22
22
  declared_unique,
23
23
  get_column_fields,
24
24
  get_id_attribute,
25
+ get_id_attributes,
25
26
  get_projections,
26
27
  get_relations,
27
28
  get_table_name,
@@ -107,6 +108,7 @@ __all__ = [
107
108
  "UnsupportedBoundaryType",
108
109
  "get_column_fields",
109
110
  "get_id_attribute",
111
+ "get_id_attributes",
110
112
  "get_projections",
111
113
  "get_relations",
112
114
  "get_table_name",
@@ -145,12 +145,21 @@ def get_projections(cls: type) -> dict[str, Projection]:
145
145
  return _collect_inherited_dict_metadata(cls, "__loom_projections__")
146
146
 
147
147
 
148
+ def get_id_attributes(cls: type) -> tuple[str, ...]:
149
+ """Return the names of the primary-key fields, in declaration order.
150
+
151
+ Raises:
152
+ ValueError: If the model declares no primary-key field.
153
+ """
154
+ names = tuple(name for name, info in get_column_fields(cls).items() if info.field.primary_key)
155
+ if not names:
156
+ raise ValueError(f"No primary key field found on {cls.__name__}")
157
+ return names
158
+
159
+
148
160
  def get_id_attribute(cls: type) -> str:
149
- """Return the name of the primary key field."""
150
- for name, info in get_column_fields(cls).items():
151
- if info.field.primary_key:
152
- return name
153
- raise ValueError(f"No primary key field found on {cls.__name__}")
161
+ """Return the name of the first primary-key field."""
162
+ return get_id_attributes(cls)[0]
154
163
 
155
164
 
156
165
  def get_table_name(cls: type) -> str:
@@ -1,7 +1,9 @@
1
1
  """Backend-neutral cursor tokens for keyset pagination.
2
2
 
3
3
  A token is the base64url form of a msgspec-encoded record
4
- ``{"b": backend, "k": [sort key values...], "id": tie-breaker}``. Every
4
+ ``{"b": backend, "k": [sort key values...], "id": tie-breaker}``, where the
5
+ tie-breaker is the primary-key value, or the list of its values when the key
6
+ is composite. Every
5
7
  backend issues and consumes the same shape, so a token from another backend
6
8
  or in a legacy format is rejected at decode time with
7
9
  :class:`~loom.core.repository.abc.errors.UnsupportedQuery`.
@@ -28,7 +30,8 @@ class Cursor(LoomFrozenStruct, frozen=True):
28
30
  Args:
29
31
  backend: Name of the backend that issued the token.
30
32
  keys: Sort key values of the last row on the previous page, in sort order.
31
- tie_breaker: Primary key of that row.
33
+ tie_breaker: Primary key of that row; a tuple in key order when the
34
+ key is composite.
32
35
  """
33
36
 
34
37
  backend: str
@@ -58,7 +61,7 @@ _Key = int | float | str | bool | None | _DateTimeKey | _DateKey | _UuidKey | _D
58
61
  class _Token(msgspec.Struct):
59
62
  b: str
60
63
  k: list[_Key]
61
- id: _Key
64
+ id: _Key | list[_Key]
62
65
 
63
66
 
64
67
  def _wrap(value: object) -> _Key:
@@ -89,7 +92,8 @@ def encode_cursor(backend: str, keys: Sequence[object], tie_breaker: object) ->
89
92
  Args:
90
93
  backend: Name of the issuing backend.
91
94
  keys: Sort key values of the last row on the page, in sort order.
92
- tie_breaker: Primary key of that row.
95
+ tie_breaker: Primary key of that row; a tuple in key order when the
96
+ key is composite.
93
97
 
94
98
  Returns:
95
99
  base64url token that :func:`decode_cursor` accepts for ``backend``.
@@ -97,11 +101,18 @@ def encode_cursor(backend: str, keys: Sequence[object], tie_breaker: object) ->
97
101
  Raises:
98
102
  TypeError: If a key has a type the token format cannot carry.
99
103
  """
100
- token = _Token(b=backend, k=[_wrap(key) for key in keys], id=_wrap(tie_breaker))
104
+ tie: _Key | list[_Key]
105
+ if isinstance(tie_breaker, tuple):
106
+ tie = [_wrap(value) for value in tie_breaker]
107
+ else:
108
+ tie = _wrap(tie_breaker)
109
+ token = _Token(b=backend, k=[_wrap(key) for key in keys], id=tie)
101
110
  return base64.urlsafe_b64encode(msgspec.json.encode(token)).decode()
102
111
 
103
112
 
104
- def decode_cursor(token: str, backend: str, model: str, *, key_count: int | None = None) -> Cursor:
113
+ def decode_cursor(
114
+ token: str, backend: str, model: str, *, key_count: int | None = None, id_count: int = 1
115
+ ) -> Cursor:
105
116
  """Decode a token issued by :func:`encode_cursor` for ``backend``.
106
117
 
107
118
  Args:
@@ -110,13 +121,17 @@ def decode_cursor(token: str, backend: str, model: str, *, key_count: int | None
110
121
  model: Qualified model name, used in the error.
111
122
  key_count: Number of sort keys the query orders by; when given, a
112
123
  token carrying a different number is rejected.
124
+ id_count: Number of primary-key columns. A single-column key is
125
+ carried as a scalar and a composite key as a list of that many
126
+ values; any other token is rejected.
113
127
 
114
128
  Returns:
115
129
  The decoded :class:`Cursor`.
116
130
 
117
131
  Raises:
118
132
  UnsupportedQuery: If the token is undecodable, in a legacy format,
119
- was issued by another backend or does not match the sort.
133
+ was issued by another backend or does not match the sort or the
134
+ primary key.
120
135
  """
121
136
  try:
122
137
  record = msgspec.json.decode(base64.urlsafe_b64decode(token.encode()), type=_Token)
@@ -126,8 +141,17 @@ def decode_cursor(token: str, backend: str, model: str, *, key_count: int | None
126
141
  raise UnsupportedQuery(backend, model, "cursor token was issued by another backend")
127
142
  if key_count is not None and len(record.k) != key_count:
128
143
  raise UnsupportedQuery(backend, model, "cursor token does not match the sort")
144
+ tie_breaker: object
145
+ if isinstance(record.id, list):
146
+ carried: int | None = len(record.id)
147
+ tie_breaker = tuple(_unwrap(key) for key in record.id)
148
+ else:
149
+ carried = None
150
+ tie_breaker = _unwrap(record.id)
151
+ if carried != (id_count if id_count > 1 else None):
152
+ raise UnsupportedQuery(backend, model, "cursor token does not match the primary key")
129
153
  return Cursor(
130
154
  backend=record.b,
131
155
  keys=tuple(_unwrap(key) for key in record.k),
132
- tie_breaker=_unwrap(record.id),
156
+ tie_breaker=tie_breaker,
133
157
  )
@@ -23,10 +23,11 @@ from typing import Any, ClassVar, Generic, cast
23
23
 
24
24
  import msgspec
25
25
 
26
+ from loom.core.config import ConfigError
26
27
  from loom.core.errors import Conflict
27
28
  from loom.core.logger import get_logger
28
29
  from loom.core.model.convert import to_struct
29
- from loom.core.model.introspection import get_column_fields, get_id_attribute, get_table_name
30
+ from loom.core.model.introspection import get_column_fields, get_id_attributes, get_table_name
30
31
  from loom.core.repository.abc import (
31
32
  Creatable,
32
33
  Deletable,
@@ -70,6 +71,9 @@ class RepositoryDynamoDB(
70
71
  table_name: Name of the DynamoDB table backing this repository.
71
72
  model: The Loom struct model this repository is bound to. Its
72
73
  ``primary_key`` field becomes the DynamoDB partition key.
74
+
75
+ Raises:
76
+ ConfigError: If the model's primary key is composite.
73
77
  """
74
78
 
75
79
  backend_name: ClassVar[str] = "dynamodb"
@@ -86,7 +90,7 @@ class RepositoryDynamoDB(
86
90
  self._client = client
87
91
  self._table_name = table_name
88
92
  self._model = model
89
- self._id_attr = get_id_attribute(model)
93
+ self._id_attr = _single_id_attribute(model)
90
94
  self._column_fields = frozenset(get_column_fields(model).keys())
91
95
  self._serializer = TypeSerializer()
92
96
  self._deserializer = TypeDeserializer()
@@ -260,6 +264,17 @@ class RepositoryDynamoDB(
260
264
  return {self._id_attr: self._encode_value(obj_id)}
261
265
 
262
266
 
267
+ def _single_id_attribute(model: type) -> str:
268
+ key = get_id_attributes(model)
269
+ if len(key) == 1:
270
+ return key[0]
271
+ raise ConfigError(
272
+ f"{model.__qualname__} has the composite primary key ({', '.join(key)}); "
273
+ "the repository keys each item by one partition key, so declare a single "
274
+ "primary key"
275
+ )
276
+
277
+
263
278
  def _is_conditional_check_failure(exc: Exception) -> bool:
264
279
  """Return whether *exc* is a DynamoDB conditional-check failure."""
265
280
  response = getattr(exc, "response", None)
@@ -33,11 +33,12 @@ from pymongo.asynchronous.client_session import AsyncClientSession
33
33
  from pymongo.errors import BulkWriteError, DuplicateKeyError, PyMongoError
34
34
  from pymongo.results import DeleteResult, InsertManyResult, InsertOneResult
35
35
 
36
+ from loom.core.config import ConfigError
36
37
  from loom.core.errors import Conflict
37
38
  from loom.core.logger import get_logger
38
39
  from loom.core.model.convert import to_struct
39
40
  from loom.core.model.enums import ServerDefault, ServerOnUpdate
40
- from loom.core.model.introspection import get_column_fields, get_id_attribute, get_table_name
41
+ from loom.core.model.introspection import get_column_fields, get_id_attributes, get_table_name
41
42
  from loom.core.repository.abc import (
42
43
  BulkCreatable,
43
44
  Countable,
@@ -164,8 +165,9 @@ class RepositoryMongo(
164
165
  the current UTC time. A duplicate key raises
165
166
  :class:`~loom.core.errors.Conflict`.
166
167
 
167
- The repository does not judge the model's key declaration: a model whose
168
- key Mongo cannot mint (``autoincrement=True``) is rejected at boot by
168
+ Beyond refusing a composite key, the repository does not judge the
169
+ model's key declaration: a model whose key Mongo cannot mint
170
+ (``autoincrement=True``) is rejected at boot by
169
171
  :func:`~loom.core.repository.mongo.ids.validate_id_field`, which the
170
172
  backend runs over every discovered model.
171
173
 
@@ -182,6 +184,7 @@ class RepositoryMongo(
182
184
 
183
185
  Raises:
184
186
  ValueError: If the model declares no primary key.
187
+ ConfigError: If the primary key is composite: ``_id`` holds one value.
185
188
 
186
189
  Example::
187
190
 
@@ -202,7 +205,7 @@ class RepositoryMongo(
202
205
  self._collection = collection
203
206
  self._ids: IdPolicy = id_policy if id_policy is not None else Uuid4IdPolicy()
204
207
  self._session = session_provider
205
- self._id_attr = get_id_attribute(model)
208
+ self._id_attr = _single_id_attribute(model)
206
209
  column_fields = get_column_fields(model)
207
210
  self._column_fields = frozenset(column_fields)
208
211
  self._naive_datetime_fields = frozenset(
@@ -528,6 +531,16 @@ class RepositoryMongo(
528
531
  return cast(OutputT, to_struct(self._model, kwargs))
529
532
 
530
533
 
534
+ def _single_id_attribute(model: type) -> str:
535
+ key = get_id_attributes(model)
536
+ if len(key) == 1:
537
+ return key[0]
538
+ raise ConfigError(
539
+ f"{model.__qualname__} has the composite primary key ({', '.join(key)}); "
540
+ "Mongo stores one key value in _id, so declare a single primary key"
541
+ )
542
+
543
+
531
544
  def _equality_group(filter_params: FilterParams | None) -> FilterGroup:
532
545
  """Translate page-mode equality filters into a flat AND group."""
533
546
  if filter_params is None: