GeneralManager 0.86.1__tar.gz → 0.86.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. {generalmanager-0.86.1/src/GeneralManager.egg-info → generalmanager-0.86.2}/PKG-INFO +1 -1
  2. {generalmanager-0.86.1 → generalmanager-0.86.2}/pyproject.toml +1 -1
  3. {generalmanager-0.86.1 → generalmanager-0.86.2/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/data_change_context.py +41 -0
  5. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/signals.py +22 -11
  6. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/mutations.py +33 -2
  7. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/update_state.py +11 -3
  8. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/signal_bridge.py +4 -2
  9. {generalmanager-0.86.1 → generalmanager-0.86.2}/LICENSE +0 -0
  10. {generalmanager-0.86.1 → generalmanager-0.86.2}/README.md +0 -0
  11. {generalmanager-0.86.1 → generalmanager-0.86.2}/setup.cfg +0 -0
  12. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
  13. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  14. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/GeneralManager.egg-info/requires.txt +0 -0
  15. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/GeneralManager.egg-info/top_level.txt +0 -0
  16. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/__init__.py +0 -0
  17. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/__init__.py +0 -0
  18. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/api.py +0 -0
  19. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/bucket.py +0 -0
  20. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/cache.py +0 -0
  21. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/dataframes.py +0 -0
  22. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/factory.py +0 -0
  23. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/general_manager.py +0 -0
  24. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/interface.py +0 -0
  25. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/manager.py +0 -0
  26. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/measurement.py +0 -0
  27. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/permission.py +0 -0
  28. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/rule.py +0 -0
  29. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/search.py +0 -0
  30. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/_types/utils.py +0 -0
  31. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/__init__.py +0 -0
  32. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql.py +0 -0
  33. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_as_of.py +0 -0
  34. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_errors.py +0 -0
  35. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_groups.py +0 -0
  36. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_identifiers.py +0 -0
  37. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_mutations.py +0 -0
  38. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_ordering.py +0 -0
  39. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_output.py +0 -0
  40. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_prefetch.py +0 -0
  41. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_relations.py +0 -0
  42. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_resolvers.py +0 -0
  43. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_search.py +0 -0
  44. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  45. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_subscriptions.py +0 -0
  46. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_type.py +0 -0
  47. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_view.py +0 -0
  48. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_warmup.py +0 -0
  49. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_warmup_registry.py +0 -0
  50. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
  51. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/mutation.py +0 -0
  52. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/notification_batching.py +0 -0
  53. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/property.py +0 -0
  54. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/registry.py +0 -0
  55. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/remote_api.py +0 -0
  56. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/remote_invalidation.py +0 -0
  57. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/api/remote_invalidation_client.py +0 -0
  58. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/apps.py +0 -0
  59. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/as_of.py +0 -0
  60. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bootstrap.py +0 -0
  61. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/__init__.py +0 -0
  62. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/_materialized_bucket.py +0 -0
  63. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/_ordering.py +0 -0
  64. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/base_bucket.py +0 -0
  65. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/calculation_bucket.py +0 -0
  66. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/database_bucket.py +0 -0
  67. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/excel_bucket.py +0 -0
  68. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/group_bucket.py +0 -0
  69. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/indexing.py +0 -0
  70. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/projection.py +0 -0
  71. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/bucket/request_bucket.py +0 -0
  72. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/__init__.py +0 -0
  73. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/_dependency_graph.py +0 -0
  74. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/batch_refresh.py +0 -0
  75. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/cache_decorator.py +0 -0
  76. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/cache_tracker.py +0 -0
  77. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/dependency_cache.py +0 -0
  78. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/dependency_index.py +0 -0
  79. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/dependency_matching.py +0 -0
  80. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/dependency_publish.py +0 -0
  81. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/dependency_shards.py +0 -0
  82. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/model_dependency_collector.py +0 -0
  83. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/run_context.py +0 -0
  84. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/run_context_lru.py +0 -0
  85. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/cache/transaction_barrier.py +0 -0
  86. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/__init__.py +0 -0
  87. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/audit.py +0 -0
  88. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/bootstrap.py +0 -0
  89. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/checks.py +0 -0
  90. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/consumer.py +0 -0
  91. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/context.py +0 -0
  92. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/errors.py +0 -0
  93. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/__init__.py +0 -0
  94. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/__main__.py +0 -0
  95. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/baseline.py +0 -0
  96. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
  97. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
  98. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
  99. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
  100. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
  101. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
  102. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/planned_orchestration.yaml +0 -0
  103. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/diagnostics.py +0 -0
  104. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/fingerprints.py +0 -0
  105. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/fixtures.py +0 -0
  106. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/__init__.py +0 -0
  107. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
  108. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/contract.py +0 -0
  109. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
  110. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
  111. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/readiness.py +0 -0
  112. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/runner.py +0 -0
  113. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/evals/traces.py +0 -0
  114. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/grounding.py +0 -0
  115. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/models.py +0 -0
  116. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/__init__.py +0 -0
  117. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/budget.py +0 -0
  118. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/calculations.py +0 -0
  119. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/catalog.py +0 -0
  120. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/config.py +0 -0
  121. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/events.py +0 -0
  122. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/evidence.py +0 -0
  123. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/models.py +0 -0
  124. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/planner.py +0 -0
  125. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/provider_calls.py +0 -0
  126. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/resolver.py +0 -0
  127. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/routing.py +0 -0
  128. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/scheduler.py +0 -0
  129. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/synthesis.py +0 -0
  130. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/planned/validation.py +0 -0
  131. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/providers/__init__.py +0 -0
  132. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/providers/_shared.py +0 -0
  133. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/providers/anthropic.py +0 -0
  134. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/providers/base.py +0 -0
  135. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/providers/google.py +0 -0
  136. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/providers/ollama.py +0 -0
  137. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/providers/openai.py +0 -0
  138. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/rate_limits.py +0 -0
  139. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/routing.py +0 -0
  140. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/schema_index.py +0 -0
  141. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/settings.py +0 -0
  142. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/signals.py +0 -0
  143. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/system_prompt.py +0 -0
  144. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/tool_metadata.py +0 -0
  145. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/tools.py +0 -0
  146. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/turns.py +0 -0
  147. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/chat/views.py +0 -0
  148. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/conf.py +0 -0
  149. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/dataframes/__init__.py +0 -0
  150. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/dataframes/measurements.py +0 -0
  151. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/factory/__init__.py +0 -0
  152. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/factory/auto_factory.py +0 -0
  153. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/factory/factories.py +0 -0
  154. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/factory/factory_methods.py +0 -0
  155. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/__init__.py +0 -0
  156. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/base_interface.py +0 -0
  157. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/bundles/__init__.py +0 -0
  158. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/bundles/calculation.py +0 -0
  159. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/bundles/database.py +0 -0
  160. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/bundles/excel.py +0 -0
  161. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  162. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/bundles/request.py +0 -0
  163. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/__init__.py +0 -0
  164. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/base.py +0 -0
  165. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/builtin.py +0 -0
  166. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  167. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  168. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/calculation/input_resolution.py +0 -0
  169. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  170. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/configuration.py +0 -0
  171. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  172. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  173. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/excel/__init__.py +0 -0
  174. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  175. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  176. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  177. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  178. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/factory.py +0 -0
  179. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  180. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  181. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/bulk.py +0 -0
  182. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/create_many.py +0 -0
  183. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  184. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  185. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  186. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  187. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  188. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  189. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  190. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  191. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  192. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
  193. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  194. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  195. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/registry.py +0 -0
  196. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  197. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  198. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/excel.py +0 -0
  199. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/excel_store.py +0 -0
  200. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/excel_workbook.py +0 -0
  201. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  202. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  203. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  204. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/__init__.py +0 -0
  205. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/calculation.py +0 -0
  206. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/database.py +0 -0
  207. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/excel.py +0 -0
  208. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  209. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/read_only.py +0 -0
  210. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  211. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/request.py +0 -0
  212. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/manifests/__init__.py +0 -0
  213. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  214. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  215. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/manifests/capability_models.py +0 -0
  216. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/orm_interface.py +0 -0
  217. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/requests.py +0 -0
  218. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/utils/__init__.py +0 -0
  219. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  220. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/utils/errors.py +0 -0
  221. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/utils/history.py +0 -0
  222. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/interface/utils/models.py +0 -0
  223. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/logging.py +0 -0
  224. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/chat_cleanup.py +0 -0
  225. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
  226. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/graphql_warmup.py +0 -0
  227. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
  228. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/search_index.py +0 -0
  229. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/search_reconcile.py +0 -0
  230. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  231. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/shell.py +0 -0
  232. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/sync_read_only.py +0 -0
  233. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  234. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  235. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/manager/__init__.py +0 -0
  236. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/manager/bulk_create.py +0 -0
  237. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/manager/general_manager.py +0 -0
  238. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/manager/group_manager.py +0 -0
  239. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/manager/input.py +0 -0
  240. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/manager/meta.py +0 -0
  241. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/measurement/__init__.py +0 -0
  242. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/measurement/measurement.py +0 -0
  243. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/measurement/measurement_field.py +0 -0
  244. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/metrics/__init__.py +0 -0
  245. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/metrics/graphql.py +0 -0
  246. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0001_initial.py +0 -0
  247. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  248. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  249. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
  250. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0004_search_index_state.py +0 -0
  251. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
  252. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
  253. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0007_upload_intent.py +0 -0
  254. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
  255. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
  256. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
  257. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +0 -0
  258. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/0012_chat_context_watermarks.py +0 -0
  259. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/migrations/__init__.py +0 -0
  260. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/models.py +0 -0
  261. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/__init__.py +0 -0
  262. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/audit.py +0 -0
  263. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/base_permission.py +0 -0
  264. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/graphql_capabilities.py +0 -0
  265. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/manager_based_permission.py +0 -0
  266. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/mutation_permission.py +0 -0
  267. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/permission_checks.py +0 -0
  268. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/permission_data_manager.py +0 -0
  269. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/permission/utils.py +0 -0
  270. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/public_api_registry.py +0 -0
  271. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/py.typed +0 -0
  272. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/rule/__init__.py +0 -0
  273. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/rule/handler.py +0 -0
  274. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/rule/rule.py +0 -0
  275. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/__init__.py +0 -0
  276. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/async_tasks.py +0 -0
  277. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/backend.py +0 -0
  278. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/backend_registry.py +0 -0
  279. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/backends/__init__.py +0 -0
  280. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/backends/dev.py +0 -0
  281. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/backends/meilisearch.py +0 -0
  282. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/backends/opensearch.py +0 -0
  283. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/backends/typesense.py +0 -0
  284. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/checks.py +0 -0
  285. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/config.py +0 -0
  286. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/identity.py +0 -0
  287. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/indexer.py +0 -0
  288. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/invalidation.py +0 -0
  289. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/m2m_invalidation.py +0 -0
  290. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/models.py +0 -0
  291. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/reconciliation.py +0 -0
  292. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/registry.py +0 -0
  293. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/tasks.py +0 -0
  294. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/search/utils.py +0 -0
  295. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/seeding/__init__.py +0 -0
  296. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/seeding/manager_landscape.py +0 -0
  297. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/__init__.py +0 -0
  298. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/adapters.py +0 -0
  299. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/checks.py +0 -0
  300. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/config.py +0 -0
  301. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/errors.py +0 -0
  302. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/finalization.py +0 -0
  303. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/graphql.py +0 -0
  304. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/graphql_types.py +0 -0
  305. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/metrics.py +0 -0
  306. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/models.py +0 -0
  307. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/public.py +0 -0
  308. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/s3.py +0 -0
  309. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/services.py +0 -0
  310. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/tokens.py +0 -0
  311. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/types.py +0 -0
  312. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/urls.py +0 -0
  313. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/uploads/views.py +0 -0
  314. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/__init__.py +0 -0
  315. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/_args_to_kwargs.py +0 -0
  316. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/_cache_key_encoder.py +0 -0
  317. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/_make_cache_key.py +0 -0
  318. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/_none_to_zero.py +0 -0
  319. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/filter_parser.py +0 -0
  320. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/format_string.py +0 -0
  321. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/json_encoder.py +0 -0
  322. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/path_mapping.py +0 -0
  323. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/public_api.py +0 -0
  324. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/testing.py +0 -0
  325. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/utils/type_checks.py +0 -0
  326. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/__init__.py +0 -0
  327. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/actions.py +0 -0
  328. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/backend_registry.py +0 -0
  329. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/backends/__init__.py +0 -0
  330. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/backends/celery.py +0 -0
  331. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/backends/local.py +0 -0
  332. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/backends/n8n.py +0 -0
  333. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/config.py +0 -0
  334. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/engine.py +0 -0
  335. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/event_registry.py +0 -0
  336. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/events.py +0 -0
  337. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/models.py +0 -0
  338. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/tasks.py +0 -0
  339. {generalmanager-0.86.1 → generalmanager-0.86.2}/src/general_manager/workflow/telemetry.py +0 -0
  340. {generalmanager-0.86.1 → generalmanager-0.86.2}/tests/test_settings.py +0 -0
  341. {generalmanager-0.86.1 → generalmanager-0.86.2}/tests/test_urls.py +0 -0
  342. {generalmanager-0.86.1 → generalmanager-0.86.2}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.86.1
3
+ Version: 0.86.2
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.86.1"
7
+ version = "0.86.2"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.86.1
3
+ Version: 0.86.2
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -162,3 +162,44 @@ def may_reuse_data_change_transaction(database_alias: str) -> bool:
162
162
  and not owner.caller_in_atomic_block
163
163
  and is_data_change_operation_authorized(database_alias)
164
164
  )
165
+
166
+
167
+ @dataclass
168
+ class DataChangeOutcome:
169
+ """Private per-call outcome; unknown/custom mutations remain changes."""
170
+
171
+ interface: object
172
+ unchanged: bool = False
173
+
174
+
175
+ _operation_outcome: ContextVar[DataChangeOutcome | None] = ContextVar(
176
+ "data_change_operation_outcome", default=None
177
+ )
178
+
179
+
180
+ @contextmanager
181
+ def suspend_data_change_outcome() -> Iterator[None]:
182
+ """Keep nested signal/lifecycle receivers outside their parent's outcome."""
183
+ token = _operation_outcome.set(None)
184
+ try:
185
+ yield
186
+ finally:
187
+ _operation_outcome.reset(token)
188
+
189
+
190
+ @contextmanager
191
+ def capture_data_change_outcome(interface: object) -> Iterator[DataChangeOutcome]:
192
+ """Isolate the wrapped operation from pre-signals and nested mutations."""
193
+ outcome = DataChangeOutcome(interface)
194
+ token = _operation_outcome.set(outcome)
195
+ try:
196
+ yield outcome
197
+ finally:
198
+ _operation_outcome.reset(token)
199
+
200
+
201
+ def mark_data_change_unchanged(interface: object) -> None:
202
+ """Accept a proven no-op only for the interface owned by this call."""
203
+ outcome = _operation_outcome.get()
204
+ if outcome is not None and outcome.interface is interface:
205
+ outcome.unchanged = True
@@ -15,8 +15,10 @@ from django.utils.connection import ConnectionDoesNotExist
15
15
  from general_manager.cache.data_change_context import (
16
16
  DataChangeTransactionScope,
17
17
  authorize_data_change_operation,
18
+ capture_data_change_outcome,
18
19
  own_data_change_transaction,
19
20
  record_data_change_phase,
21
+ suspend_data_change_outcome,
20
22
  )
21
23
  from general_manager.as_of import reject_historical_mutation
22
24
  from general_manager.logging import get_logger
@@ -66,7 +68,9 @@ def data_change(
66
68
  clears run-scoped ORM bucket/index caches, emits `pre_data_change`, invokes
67
69
  the wrapped callable, then emits `post_data_change` with the changed
68
70
  instance, previous instance, action name, identification, and copied
69
- `_old_values` payload. Both signals receive a per-call `change_context` and
71
+ `_old_values` payload. Proven unchanged built-in ORM updates skip only the
72
+ post-change signal; pre-change preparation and transaction cleanup still run.
73
+ Both signals receive a per-call `change_context` and
70
74
  the manager interface's database alias. ORM-backed signal dispatch and the
71
75
  mutation share one transaction. GraphQL warm-up requeue keys collected
72
76
  during signal handling are drained only after the outermost active
@@ -101,6 +105,7 @@ def data_change(
101
105
  )
102
106
 
103
107
  @wraps(decorator_source)
108
+ @suspend_data_change_outcome()
104
109
  def perform_change(*args: P.args, **kwargs: P.kwargs) -> R:
105
110
  """
106
111
  Emit pre_data_change and post_data_change signals around the wrapped function call.
@@ -252,7 +257,12 @@ def data_change(
252
257
  if is_orm_backed
253
258
  else nullcontext()
254
259
  )
255
- with operation_context:
260
+ with (
261
+ operation_context,
262
+ capture_data_change_outcome(
263
+ getattr(instance_before, "_interface", None)
264
+ ) as outcome,
265
+ ):
256
266
  mutation_started = (
257
267
  perf_counter() if transaction_scope is not None else None
258
268
  )
@@ -282,15 +292,16 @@ def data_change(
282
292
  if identification is None:
283
293
  identification = pre_identification
284
294
 
285
- post_data_change.send(
286
- sender=sender,
287
- instance=instance,
288
- previous_instance=instance_before,
289
- identification=identification,
290
- action=action,
291
- old_relevant_values=old_relevant_values,
292
- **signal_kwargs,
293
- )
295
+ if not outcome.unchanged:
296
+ post_data_change.send(
297
+ sender=sender,
298
+ instance=instance,
299
+ previous_instance=instance_before,
300
+ identification=identification,
301
+ action=action,
302
+ old_relevant_values=old_relevant_values,
303
+ **signal_kwargs,
304
+ )
294
305
  if instance_before is not None:
295
306
  try:
296
307
  delattr(instance_before, "_old_values")
@@ -11,7 +11,10 @@ from django.contrib.auth import get_user_model
11
11
  from django.db import DEFAULT_DB_ALIAS, models, router, transaction
12
12
  from django.db.models import NOT_PROVIDED
13
13
 
14
- from general_manager.cache.data_change_context import owns_data_change_transaction
14
+ from general_manager.cache.data_change_context import (
15
+ mark_data_change_unchanged,
16
+ owns_data_change_transaction,
17
+ )
15
18
  from general_manager.interface.capabilities.base import CapabilityName
16
19
  from general_manager.interface.capabilities.builtin import BaseCapability
17
20
  from general_manager.interface.capabilities.orm_utils.payload_normalizer import (
@@ -20,6 +23,7 @@ from general_manager.interface.capabilities.orm_utils.payload_normalizer import
20
23
  from general_manager.interface.capabilities.orm_utils.update_state import (
21
24
  changed_many_to_many,
22
25
  is_unchanged_update,
26
+ record_skipped_update_save,
23
27
  track_update,
24
28
  )
25
29
  from general_manager.interface.utils.database_interface_protocols import (
@@ -225,6 +229,7 @@ class OrmMutationCapability(BaseCapability):
225
229
  object.__setattr__(instance, "changed_by_id", creator_id)
226
230
  instance.full_clean()
227
231
  if is_unchanged_update(instance):
232
+ record_skipped_update_save(instance)
228
233
  return instance.pk
229
234
  if batch is not None and database_alias is None:
230
235
  if (
@@ -606,7 +611,7 @@ class OrmUpdateCapability(BaseCapability):
606
611
  changed_many,
607
612
  history_comment,
608
613
  database_alias=database_alias,
609
- ):
614
+ ) as update_state:
610
615
  instance = mutation.assign_simple_attributes(
611
616
  interface_instance.__class__, instance, normalized_simple
612
617
  )
@@ -624,6 +629,32 @@ class OrmUpdateCapability(BaseCapability):
624
629
  history_comment=history_comment,
625
630
  )
626
631
  discard_orm_instance_cache(interface_instance.__class__, pk)
632
+ if update_state is not None and update_state.save_skipped:
633
+ # A custom wrapper may write after delegating to a built-in
634
+ # method. Only the complete standard path can prove a no-op.
635
+ from general_manager.interface.base_interface import InterfaceBase
636
+
637
+ standard_methods = (
638
+ (interface_instance.update, InterfaceBase.update),
639
+ (self.update, OrmUpdateCapability.update),
640
+ (
641
+ mutation.assign_simple_attributes,
642
+ OrmMutationCapability.assign_simple_attributes,
643
+ ),
644
+ (
645
+ mutation.save_with_history,
646
+ OrmMutationCapability.save_with_history,
647
+ ),
648
+ (
649
+ mutation.apply_many_to_many,
650
+ OrmMutationCapability.apply_many_to_many,
651
+ ),
652
+ )
653
+ if all(
654
+ getattr(method, "__func__", None) is standard
655
+ for method, standard in standard_methods
656
+ ):
657
+ mark_data_change_unchanged(interface_instance)
627
658
  return {"id": pk}
628
659
 
629
660
  return call_with_observability(
@@ -16,10 +16,11 @@ from django.db.models.fields.files import FieldFile
16
16
  from general_manager.measurement.measurement_field import MeasurementField
17
17
 
18
18
 
19
- @dataclass(frozen=True)
19
+ @dataclass
20
20
  class _UpdateState:
21
21
  instance: models.Model
22
22
  values: dict[str, object]
23
+ save_skipped: bool = False
23
24
 
24
25
 
25
26
  _update_state: ContextVar[_UpdateState | None] = ContextVar(
@@ -47,7 +48,7 @@ def track_update(
47
48
  history_comment: str | None,
48
49
  *,
49
50
  database_alias: str | None = None,
50
- ) -> Iterator[None]:
51
+ ) -> Iterator[_UpdateState | None]:
51
52
  """Scope the original row to its save, including nested mutation calls.
52
53
 
53
54
  The save compares after full_clean(), so validation and changes made by
@@ -85,7 +86,7 @@ def track_update(
85
86
  state = _UpdateState(instance, values)
86
87
  token = _update_state.set(state)
87
88
  try:
88
- yield
89
+ yield state
89
90
  finally:
90
91
  _update_state.reset(token)
91
92
 
@@ -107,6 +108,13 @@ def is_unchanged_update(instance: models.Model) -> bool:
107
108
  return True
108
109
 
109
110
 
111
+ def record_skipped_update_save(instance: models.Model) -> None:
112
+ """Record the built-in save decision without classifying custom wrappers."""
113
+ state = _update_state.get()
114
+ if state is not None and state.instance is instance:
115
+ state.save_skipped = True
116
+
117
+
110
118
  def changed_many_to_many(
111
119
  instance: models.Model,
112
120
  values: dict[str, list[object]],
@@ -29,6 +29,8 @@ _WORKFLOW_SIGNAL_BRIDGE_KEY = "WORKFLOW_SIGNAL_BRIDGE"
29
29
  _DISPATCH_UID = "general_manager_workflow_signal_bridge"
30
30
 
31
31
  _RESERVED_KEYS = {
32
+ "change_context",
33
+ "database_alias",
32
34
  "creator_id",
33
35
  "history_comment",
34
36
  "identification",
@@ -88,8 +90,8 @@ def _manager_change_to_event(
88
90
  for key, value in kwargs.items()
89
91
  if key not in _RESERVED_KEYS and not key.startswith("_")
90
92
  }
91
- if action in {"create", "update"} and not relevant_fields:
92
- return None
93
+ # The mutation outcome controls no-op suppression. Empty input can still
94
+ # create defaults, change values in clean(), or request an audit touch.
93
95
 
94
96
  identification = kwargs.get("identification")
95
97
  if identification is None:
File without changes