dialectical-framework 1.6.2__tar.gz → 1.6.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/PKG-INFO +1 -1
  2. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/pyproject.toml +1 -1
  3. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/analyst.py +48 -0
  4. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +215 -45
  5. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/system_prompts.py +37 -0
  6. dialectical_framework-1.6.3/src/dialectical_framework/concerns/antithetical_thesis_detection.py +338 -0
  7. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/LICENSE +0 -0
  8. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/README.md +0 -0
  9. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/__init__.py +0 -0
  10. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/__init__.py +0 -0
  11. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
  12. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/advisor/advisor.py +0 -0
  13. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/advisor/system_prompts.py +0 -0
  14. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
  15. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
  16. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
  17. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
  18. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
  19. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/agent_context.py +0 -0
  20. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
  21. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
  22. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
  23. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
  24. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +0 -0
  25. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
  26. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
  27. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
  28. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/tools/create_dx_input.py +0 -0
  29. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
  30. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/apps.py +0 -0
  31. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/conversation_facilitator.py +0 -0
  32. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/execution_report.py +0 -0
  33. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
  34. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/explorer.py +0 -0
  35. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
  36. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
  37. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
  38. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
  39. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/system_prompts.py +0 -0
  40. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
  41. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
  42. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
  43. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
  44. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
  45. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
  46. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
  47. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
  48. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
  49. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
  50. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +0 -0
  51. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +0 -0
  52. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
  53. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
  54. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
  55. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
  56. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
  57. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/agents/stream_events.py +0 -0
  58. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/__init__.py +0 -0
  59. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
  60. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/action_extraction.py +0 -0
  61. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/add_input.py +0 -0
  62. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
  63. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
  64. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
  65. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
  66. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/antithesis_extraction.py +0 -0
  67. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
  68. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/aspect_generation.py +0 -0
  69. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
  70. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
  71. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
  72. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
  73. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
  74. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
  75. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
  76. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
  77. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
  78. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
  79. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
  80. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
  81. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/create_nexus.py +0 -0
  82. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
  83. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/dialectical_context.py +0 -0
  84. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/discard.py +0 -0
  85. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
  86. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
  87. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
  88. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
  89. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
  90. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/scoring_scales.py +0 -0
  91. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/source_digest.py +0 -0
  92. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/statement_classification.py +0 -0
  93. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
  94. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/statement_headline.py +0 -0
  95. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/statement_placement.py +0 -0
  96. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
  97. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
  98. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
  99. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
  100. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/dialectical_reasoning.py +0 -0
  101. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/enums/__init__.py +0 -0
  102. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/enums/causality_preset.py +0 -0
  103. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/enums/di.py +0 -0
  104. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/events/__init__.py +0 -0
  105. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/events/graph_event.py +0 -0
  106. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/events/graph_event_bus.py +0 -0
  107. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/exceptions/__init__.py +0 -0
  108. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/exceptions/node_errors.py +0 -0
  109. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
  110. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/__init__.py +0 -0
  111. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
  112. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
  113. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/estimation_manager.py +0 -0
  114. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
  115. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
  116. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
  117. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
  118. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
  119. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
  120. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
  121. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/case.py +0 -0
  122. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
  123. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
  124. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
  125. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/input.py +0 -0
  126. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
  127. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
  128. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
  129. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
  130. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/statement.py +0 -0
  131. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
  132. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
  133. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/transition.py +0 -0
  134. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
  135. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationship_manager.py +0 -0
  136. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
  137. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
  138. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
  139. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
  140. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
  141. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
  142. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
  143. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
  144. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
  145. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
  146. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
  147. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
  148. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
  149. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
  150. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
  151. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
  152. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
  153. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
  154. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
  155. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
  156. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
  157. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
  158. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
  159. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/rendering.py +0 -0
  160. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
  161. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
  162. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
  163. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
  164. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
  165. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
  166. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
  167. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
  168. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
  169. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
  170. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
  171. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
  172. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/scope_context.py +0 -0
  173. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
  174. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/wheel_segment.py +0 -0
  175. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
  176. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/protocols/__init__.py +0 -0
  177. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/protocols/has_config.py +0 -0
  178. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/protocols/input_resolver.py +0 -0
  179. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/settings.py +0 -0
  180. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/__init__.py +0 -0
  181. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/bedrock_provider.py +0 -0
  182. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/concurrency.py +0 -0
  183. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/dc_replace.py +0 -0
  184. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
  185. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/edge_context.py +0 -0
  186. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/effect_logger.py +0 -0
  187. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/input_context.py +0 -0
  188. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/order_transitions.py +0 -0
  189. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/sequence_generation.py +0 -0
  190. {dialectical_framework-1.6.2 → dialectical_framework-1.6.3}/src/dialectical_framework/utils/use_brain.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dialectical-framework
3
- Version: 1.6.2
3
+ Version: 1.6.3
4
4
  Summary: A dialectical framework for augmented intelligence. AI reasoning powered with dialectics supports humans in: system optimization (psychology, engineering, business, politics, etc.); dispute resolution (mediation, conflicts, negotiations, etc.); decision-making (dilemmas, challenging situations, win-win, etc.).
5
5
  License: MIT
6
6
  Keywords: dialectics,dialectical-reasoning,synthesis,thesis-antithesis,ai,artificial-intelligence,llm,reasoning-framework,philosophy,logic,argumentation,conflict-resolution,decision-making,critical-thinking,semantic-graph,mirascope,pydantic,perspectives,polarity-reasoning
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dialectical-framework"
3
- version = "1.6.2"
3
+ version = "1.6.3"
4
4
  description = "A dialectical framework for augmented intelligence. AI reasoning powered with dialectics supports humans in: system optimization (psychology, engineering, business, politics, etc.); dispute resolution (mediation, conflicts, negotiations, etc.); decision-making (dilemmas, challenging situations, win-win, etc.)."
5
5
  authors = ["Evaldas Taroza <evaldas@dialexity.com>"]
6
6
  readme = "README.md"
@@ -318,6 +318,14 @@ class AnalysisPipeline(ReasonableConcern[AnalysisResult]):
318
318
  seen.add(h)
319
319
  hashes_to_expand.append(h)
320
320
 
321
+ # Surface the HS-based gate to the agent. _rank_polarities silently
322
+ # drops tensions below HS_THRESHOLD (and beyond the top few); without
323
+ # this the agent only sees a bare perspective count and cannot explain
324
+ # WHY these framings are the strong ones or that weaker tensions were
325
+ # set aside.
326
+ polarity_quality = self._build_polarity_quality(polarity_data, hashes_to_expand)
327
+ set_aside_count = sum(1 for q in polarity_quality if not q["expanded"])
328
+
321
329
  expand_results = await asyncio.gather(
322
330
  *[self._expand_one(h) for h in hashes_to_expand],
323
331
  return_exceptions=True,
@@ -338,14 +346,21 @@ class AnalysisPipeline(ReasonableConcern[AnalysisResult]):
338
346
  reports.append(report)
339
347
 
340
348
  self._report.ok = True
349
+ set_aside_note = (
350
+ f", {set_aside_count} weaker tension(s) set aside"
351
+ if set_aside_count
352
+ else ""
353
+ )
341
354
  self._report.summary = (
342
355
  f"Analysis complete: {len(thesis_hashes)} theses, "
343
356
  f"{len(polarity_hashes)} polarities, "
344
357
  f"{len(perspective_hashes)} perspectives"
358
+ f"{set_aside_note}"
345
359
  )
346
360
  self._report.artifacts["thesis_hashes"] = thesis_hashes
347
361
  self._report.artifacts["polarity_hashes"] = polarity_hashes
348
362
  self._report.artifacts["perspective_hashes"] = perspective_hashes
363
+ self._report.artifacts["polarity_quality"] = polarity_quality
349
364
 
350
365
  return AnalysisResult(
351
366
  ideas_hash=ideas_hash,
@@ -375,6 +390,39 @@ class AnalysisPipeline(ReasonableConcern[AnalysisResult]):
375
390
 
376
391
  return ranked[:MAX_POLARITIES_TO_EXPAND]
377
392
 
393
+ @staticmethod
394
+ def _build_polarity_quality(
395
+ polarity_data: list[dict], hashes_to_expand: list[str]
396
+ ) -> list[dict]:
397
+ """Per-tension HS + gate outcome, HS-descending, for the agent to read.
398
+
399
+ Makes the otherwise-silent HS gate visible: each entry flags whether the
400
+ tension was `expanded` into a full perspective or set aside. HS on the
401
+ antithesis measures how genuine the opposition is (see "Reading Polarity
402
+ Quality" in the system prompt), not the quality of the idea itself.
403
+ """
404
+ expanded_set = set(hashes_to_expand)
405
+ quality: list[dict] = []
406
+ seen: set[str] = set()
407
+ for p in polarity_data:
408
+ h = p.get("polarity_hash")
409
+ if not h or h in seen or p.get("deduped", False):
410
+ continue
411
+ seen.add(h)
412
+ quality.append(
413
+ {
414
+ "polarity_hash": h,
415
+ "thesis": p.get("thesis_text"),
416
+ "antithesis": p.get("antithesis_text"),
417
+ "hs": p.get("heuristic_similarity"),
418
+ "expanded": h in expanded_set,
419
+ }
420
+ )
421
+ quality.sort(
422
+ key=lambda q: q["hs"] if q["hs"] is not None else 0.0, reverse=True
423
+ )
424
+ return quality
425
+
378
426
  async def _expand_one(self, polarity_hash: str) -> tuple[list[str], object]:
379
427
  from dialectical_framework.agents.analyst.skills.expand_polarities import \
380
428
  ExpandPolarity
@@ -39,8 +39,13 @@ from dialectical_framework.agents.execution_report import ExecutionReport
39
39
  from dialectical_framework.enums.di import DI
40
40
  from dialectical_framework.concerns.antithesis_extraction import \
41
41
  AntithesisExtraction
42
+ from dialectical_framework.concerns.antithetical_thesis_detection import \
43
+ AntitheticalThesisDetection
42
44
  from dialectical_framework.concerns.statement_deduplication import (
43
45
  DedupResult, StatementDeduplication)
46
+ from dialectical_framework.graph.estimation_manager import EstimationManager
47
+ from dialectical_framework.graph.nodes.estimation import (ArousalEstimation,
48
+ ModeEstimation)
44
49
  from dialectical_framework.graph.nodes.statement import \
45
50
  Statement
46
51
  from dialectical_framework.graph.nodes.ideas import Ideas
@@ -121,6 +126,15 @@ class FindPolarities(ReasonableConcern[Optional[Ideas]]):
121
126
  results: list[ThesisResult] = []
122
127
  newly_extracted: list[Statement] = []
123
128
 
129
+ # Phase 0: Consolidate theses that are antitheses of each other. Strongly
130
+ # opposed pairs (HS >= 0.7) are merged into a single Polarity here and
131
+ # removed from extraction; weaker pairs are surfaced as suggestions for
132
+ # the agent to confirm (leaving both theses to proceed independently).
133
+ unique_hashes = list(dict.fromkeys(self.thesis_hashes))
134
+ unique_hashes, consolidated_data = await self._consolidate_antithetical(
135
+ unique_hashes, input_text
136
+ )
137
+
124
138
  # Phase 1: For each thesis, collect existing oppositions + extract new ones (parallel)
125
139
  import asyncio
126
140
 
@@ -155,7 +169,6 @@ class FindPolarities(ReasonableConcern[Optional[Ideas]]):
155
169
  result.existing = existing_antitheses
156
170
  return result
157
171
 
158
- unique_hashes = list(dict.fromkeys(self.thesis_hashes))
159
172
  thesis_results = await asyncio.gather(
160
173
  *[_process_thesis(h) for h in unique_hashes]
161
174
  )
@@ -209,18 +222,20 @@ class FindPolarities(ReasonableConcern[Optional[Ideas]]):
209
222
  # Phase 3: Create Polarity nodes for each T-A pair
210
223
  total_antitheses = sum(len(r.antithesis_data) for r in results if not r.error)
211
224
 
212
- if total_antitheses == 0:
225
+ if total_antitheses == 0 and not consolidated_data:
213
226
  self._report.ok = True
214
227
  self._report.summary = "No polarities found"
215
228
  self._report.artifacts["polarity_data"] = []
216
229
  return None
217
230
 
218
- # Phase 4: Create Polarities and Ideas with all T-A pairs
219
- ideas = self._create_ideas(results)
231
+ # Phase 4: Create Polarities and Ideas with all T-A pairs (consolidated
232
+ # pairs already have their Polarities created in Phase 0).
233
+ ideas = self._create_ideas(results, consolidated_data)
220
234
  polarity_map = self._create_polarities(results)
221
235
 
222
- # Build polarity_data for report (includes HS for each T-A pair)
223
- polarity_data = []
236
+ # Build polarity_data for report (includes HS for each T-A pair). Start
237
+ # with the consolidated pairs, then the extracted ones.
238
+ polarity_data = list(consolidated_data)
224
239
  for result in results:
225
240
  if result.error:
226
241
  continue
@@ -258,11 +273,26 @@ class FindPolarities(ReasonableConcern[Optional[Ideas]]):
258
273
  self._report.artifacts["polarity_data"] = polarity_data
259
274
  pol_created = self._report.artifacts.get("created_polarity_count", 0)
260
275
  pol_existing = self._report.artifacts.get("existing_polarity_count", 0)
261
- self._report.summary = (
276
+ consolidated_count = len(consolidated_data)
277
+ suggestion_count = len(
278
+ self._report.artifacts.get("consolidation_suggestions", [])
279
+ )
280
+ summary = (
262
281
  f"Found {existing_count} existing + {new_count} new antithesis(es) "
263
282
  f"for {len(self.thesis_hashes)} thesis(es). "
264
283
  f"Polarities: {pol_created} created, {pol_existing} existing."
265
284
  )
285
+ if consolidated_count:
286
+ summary += (
287
+ f" Consolidated {consolidated_count} antithetical thesis pair(s) "
288
+ f"into single tension(s)."
289
+ )
290
+ if suggestion_count:
291
+ summary += (
292
+ f" {suggestion_count} weaker pair(s) suggested for consolidation "
293
+ f"(acting on one merges 2 theses into 1 tension)."
294
+ )
295
+ self._report.summary = summary
266
296
 
267
297
  return ideas
268
298
 
@@ -424,6 +454,115 @@ class FindPolarities(ReasonableConcern[Optional[Ideas]]):
424
454
  pass
425
455
  return None
426
456
 
457
+ async def _consolidate_antithetical(
458
+ self, unique_hashes: list[str], input_text: str
459
+ ) -> tuple[list[str], list[dict]]:
460
+ """Detect and merge theses that are antitheses of each other.
461
+
462
+ Strongly-opposed pairs (HS >= 0.7) are merged into a single Polarity and
463
+ their two hashes removed from the returned list (so they are NOT sent to
464
+ antithesis extraction). Weaker pairs (0.1 < HS < 0.7) are recorded as
465
+ suggestions but left in the list to proceed as independent theses.
466
+
467
+ Returns:
468
+ (reduced_hashes, consolidated_polarity_data) where each dict has the
469
+ same shape FindPolarities uses in ``polarity_data``.
470
+ """
471
+ if len(unique_hashes) < 2:
472
+ return unique_hashes, []
473
+
474
+ detector = AntitheticalThesisDetection()
475
+ detection = await detector.resolve(
476
+ thesis_hashes=unique_hashes, text=input_text
477
+ )
478
+ self._report = self._report.merge(detector.report)
479
+
480
+ # Surface weak pairs for the agent to confirm (non-destructive).
481
+ self._report.artifacts["consolidation_suggestions"] = [
482
+ p.as_dict() for p in detection.suggest_pairs
483
+ ]
484
+
485
+ if not detection.merge_pairs:
486
+ return unique_hashes, []
487
+
488
+ pol_repo = PolarityRepository()
489
+ est_manager = EstimationManager()
490
+ consolidated_data: list[dict] = []
491
+ merged_hashes: set[str] = set()
492
+ merged_count = 0
493
+
494
+ for pair in detection.merge_pairs:
495
+ thesis = self._resolve_component(pair.thesis_hash)
496
+ antithesis = self._resolve_component(pair.antithesis_hash)
497
+ if thesis is None or antithesis is None:
498
+ continue
499
+
500
+ # Record the dialectical opposition (symmetric, idempotent).
501
+ thesis.oppositions.connect(antithesis)
502
+ self._report.relationship_created(
503
+ thesis.oppositions, thesis, antithesis
504
+ )
505
+
506
+ existing_pols = pol_repo.find_by_tension(thesis, antithesis)
507
+ if existing_pols:
508
+ polarity = existing_pols[0]
509
+ self._report.artifacts.setdefault("existing_polarity_count", 0)
510
+ self._report.artifacts["existing_polarity_count"] += 1
511
+ else:
512
+ polarity = self._build_polarity(
513
+ thesis, antithesis, pair.heuristic_similarity
514
+ )
515
+ merged_count += 1
516
+
517
+ # Persist antithesis Mode/Arousal (Antithesis Persistence Checklist).
518
+ self._persist_mode_arousal(
519
+ est_manager, antithesis, pair.mode_value, pair.arousal_value
520
+ )
521
+
522
+ consolidated_data.append(
523
+ {
524
+ "polarity_hash": polarity.hash,
525
+ "thesis_hash": thesis.hash,
526
+ "thesis_text": thesis.text,
527
+ "antithesis_hash": antithesis.hash,
528
+ "antithesis_text": antithesis.text,
529
+ "heuristic_similarity": pair.heuristic_similarity,
530
+ "existing": False,
531
+ "deduped": False,
532
+ "consolidated": True,
533
+ }
534
+ )
535
+ merged_hashes.add(thesis.hash)
536
+ merged_hashes.add(antithesis.hash)
537
+
538
+ self._report.artifacts["consolidated_pairs"] = len(consolidated_data)
539
+ if merged_count:
540
+ self._report.artifacts["created_polarity_count"] = (
541
+ self._report.artifacts.get("created_polarity_count", 0) + merged_count
542
+ )
543
+
544
+ reduced = [h for h in unique_hashes if h not in merged_hashes]
545
+ return reduced, consolidated_data
546
+
547
+ def _persist_mode_arousal(
548
+ self,
549
+ est_manager: EstimationManager,
550
+ antithesis: Statement,
551
+ mode_value: float,
552
+ arousal_value: float,
553
+ ) -> None:
554
+ """Persist Mode/Arousal estimations on the antithesis (mirrors IntroducePolarity)."""
555
+ mode_est = est_manager.upsert_estimation(
556
+ antithesis, ModeEstimation, mode_value
557
+ )
558
+ arousal_est = est_manager.upsert_estimation(
559
+ antithesis, ArousalEstimation, arousal_value
560
+ )
561
+ if mode_est:
562
+ self._report.node_updated(mode_est, patch={"value": mode_value})
563
+ if arousal_est:
564
+ self._report.node_updated(arousal_est, patch={"value": arousal_value})
565
+
427
566
  def _create_polarities(self, results: list[ThesisResult]) -> dict[tuple[str, str], str]:
428
567
  """Create Polarity nodes (T-A pairs) for each T-A pair.
429
568
 
@@ -461,37 +600,63 @@ class FindPolarities(ReasonableConcern[Optional[Ideas]]):
461
600
  continue
462
601
 
463
602
  # Create new Polarity (atomic creation)
464
- polarity = Polarity()
465
- polarity.set_t(result.thesis, heuristic_similarity=1.0)
466
- polarity.set_a(
467
- antithesis, heuristic_similarity=data["heuristic_similarity"]
603
+ polarity = self._build_polarity(
604
+ result.thesis, antithesis, data["heuristic_similarity"]
468
605
  )
469
- polarity.commit()
470
-
471
606
  polarity_map[(result.thesis.hash, data["hash"])] = polarity.hash
472
607
  created_count += 1
473
- self._report.node_created(
474
- polarity, meta={"hs": data["heuristic_similarity"]}
475
- )
476
- self._report.relationship_created(
477
- polarity.t, result.thesis, polarity,
478
- patch={"heuristic_similarity": 1.0, "alias": "T"},
479
- )
480
- self._report.relationship_created(
481
- polarity.a, antithesis, polarity,
482
- patch={"heuristic_similarity": data["heuristic_similarity"], "alias": "A"},
483
- )
484
608
 
485
- self._report.artifacts["created_polarity_count"] = created_count
609
+ # Accumulate — Phase 0 consolidation may already have created polarities.
610
+ self._report.artifacts["created_polarity_count"] = (
611
+ self._report.artifacts.get("created_polarity_count", 0) + created_count
612
+ )
486
613
  return polarity_map
487
614
 
488
- def _create_ideas(self, results: list[ThesisResult]) -> Optional[Ideas]:
489
- """Create Ideas node with all theses and their antitheses."""
615
+ def _build_polarity(
616
+ self,
617
+ thesis: Statement,
618
+ antithesis: Statement,
619
+ heuristic_similarity: float,
620
+ ) -> Polarity:
621
+ """Create and commit a Polarity from two committed Statements, recording effects.
622
+
623
+ Shared by regular polarity creation and antithetical-thesis consolidation.
624
+ T defines the apex (HS=1.0); the meaningful HS lives on A.
625
+ """
626
+ polarity = Polarity()
627
+ polarity.set_t(thesis, heuristic_similarity=1.0)
628
+ polarity.set_a(antithesis, heuristic_similarity=heuristic_similarity)
629
+ polarity.commit()
630
+
631
+ self._report.node_created(polarity, meta={"hs": heuristic_similarity})
632
+ self._report.relationship_created(
633
+ polarity.t, thesis, polarity,
634
+ patch={"heuristic_similarity": 1.0, "alias": "T"},
635
+ )
636
+ self._report.relationship_created(
637
+ polarity.a, antithesis, polarity,
638
+ patch={"heuristic_similarity": heuristic_similarity, "alias": "A"},
639
+ )
640
+ return polarity
641
+
642
+ def _create_ideas(
643
+ self,
644
+ results: list[ThesisResult],
645
+ consolidated_data: Optional[list[dict]] = None,
646
+ ) -> Optional[Ideas]:
647
+ """Create Ideas node with all theses and their antitheses.
648
+
649
+ Includes both the extracted T-A pairs (from ``results``) and the
650
+ consolidated antithetical pairs (from ``consolidated_data``), whose
651
+ theses were removed from the extraction set in Phase 0.
652
+ """
653
+ consolidated_data = consolidated_data or []
490
654
  valid_results = [r for r in results if not r.error and r.antithesis_data]
491
- if not valid_results:
655
+ if not valid_results and not consolidated_data:
492
656
  return None
493
657
 
494
658
  thesis_statements = [r.thesis.text for r in valid_results]
659
+ thesis_statements += [d["thesis_text"] for d in consolidated_data]
495
660
  intent = f"Tensions for: {', '.join(thesis_statements[:3])}"
496
661
  if len(thesis_statements) > 3:
497
662
  intent += f" (+{len(thesis_statements) - 3} more)"
@@ -508,33 +673,38 @@ class FindPolarities(ReasonableConcern[Optional[Ideas]]):
508
673
 
509
674
  # Connect all theses and antitheses (deduplicated across theses)
510
675
  connected_hashes: set[str] = set()
676
+
677
+ def _connect(hash: str) -> None:
678
+ if hash in connected_hashes:
679
+ return
680
+ comp = self._resolve_component(hash)
681
+ if comp:
682
+ connected_hashes.add(hash)
683
+ ideas.statements.connect(comp)
684
+ self._report.relationship_created(ideas.statements, ideas, comp)
685
+
511
686
  for result in valid_results:
512
- if result.thesis.hash not in connected_hashes:
513
- connected_hashes.add(result.thesis.hash)
514
- ideas.statements.connect(result.thesis)
515
- self._report.relationship_created(
516
- ideas.statements, ideas, result.thesis
517
- )
687
+ _connect(result.thesis.hash)
518
688
  for data in result.antithesis_data:
519
- if data["hash"] in connected_hashes:
520
- continue
521
- comp = self._resolve_component(data["hash"])
522
- if comp:
523
- connected_hashes.add(data["hash"])
524
- ideas.statements.connect(comp)
525
- self._report.relationship_created(
526
- ideas.statements, ideas, comp
527
- )
689
+ _connect(data["hash"])
690
+
691
+ # Consolidated pairs: both sides are theses the user supplied.
692
+ for data in consolidated_data:
693
+ _connect(data["thesis_hash"])
694
+ _connect(data["antithesis_hash"])
528
695
 
529
696
  ideas.commit()
530
697
  self._report.node_committed(ideas)
531
698
 
532
699
  # Add rationale
533
- total_theses = len(valid_results)
534
- total_antitheses = sum(len(r.antithesis_data) for r in valid_results)
700
+ total_theses = len(valid_results) + len(consolidated_data)
701
+ total_antitheses = sum(len(r.antithesis_data) for r in valid_results) + len(
702
+ consolidated_data
703
+ )
535
704
  all_hs = [
536
705
  d["heuristic_similarity"] for r in valid_results for d in r.antithesis_data
537
706
  ]
707
+ all_hs += [d["heuristic_similarity"] for d in consolidated_data]
538
708
  max_hs = max(all_hs) if all_hs else 0.0
539
709
 
540
710
  rationale = Rationale(
@@ -67,6 +67,43 @@ Always check resonance AFTER presenting results — but never before acting.
67
67
  When new tensions emerge from conversation:
68
68
  - Call `analyze` with `thesis_hashes` to develop them without re-processing everything.
69
69
 
70
+ When `find_polarities` merges antithetical theses (a `polarity_data` entry with
71
+ `consolidated: true`): two theses the user supplied were strong direct opposites,
72
+ so they were combined into one tension automatically. Briefly state that you did
73
+ this — e.g. "I treated X and Y as one tension since they're direct opposites; say
74
+ so if you'd rather explore them separately." Don't ask first, but make the merge
75
+ visible so the user can reverse it (`discard` the polarity and re-anchor them
76
+ apart) if the framing was wrong.
77
+
78
+ When `find_polarities` returns `consolidation_suggestions` (theses that may be
79
+ antitheses of each other but the opposition is only moderate): present the
80
+ suggested pair(s) to the user and ask whether the two are really one tension. If
81
+ they confirm, call `introduce_polarity` with that thesis/antithesis pair and
82
+ `discard` the now-redundant separate polarities. If they want the theses kept
83
+ apart, leave them as-is. (Strong opposites are merged automatically, so
84
+ suggestions are only the ambiguous middle ground — always confirm before merging.)
85
+
86
+ ## Reading Polarity Quality (HS)
87
+
88
+ `analyze` and `find_polarities` score every tension with an HS (heuristic
89
+ similarity) on its antithesis — how *genuine* the opposition is, not how good the
90
+ idea is. Use it to decide what to lead with; this is the reasoning that separates
91
+ you from a generic list of pros and cons.
92
+
93
+ - **≥0.7** — a strong, real opposition. Lead with these.
94
+ - **0.5-0.7** — a moderate or partial tension. Worth presenting, but frame it as secondary.
95
+ - **<0.5** — weak or tangential; the two sides barely oppose. Don't present it as a core tension.
96
+
97
+ `analyze` expands only the strongest tensions into full perspectives and sets the
98
+ rest aside — the report's `polarity_quality` artifact lists every tension with its
99
+ `hs` and an `expanded` flag, and the summary notes how many were set aside. When
100
+ tensions were set aside, briefly say stronger framings were prioritized and that
101
+ you can develop a weaker one on request (via `find_polarities`/`expand_polarities`
102
+ on that specific tension) — don't silently drop them.
103
+
104
+ Communicate quality as meaning ("a strong tension", "a weaker, more tangential
105
+ pull"), not raw numbers, unless the app preamble or the user asks for the figures.
106
+
70
107
  ## Exploration Setup (Nexus)
71
108
 
72
109
  When the user wants to explore interactions between perspectives: