dialectical-framework 1.5.5__tar.gz → 1.5.6__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 (187) hide show
  1. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/PKG-INFO +1 -1
  2. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/pyproject.toml +1 -1
  3. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +61 -39
  4. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/system_prompts.py +8 -1
  5. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/apps.py +5 -2
  6. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/create_nexus.py +10 -2
  7. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/perspective_combination.py +44 -21
  8. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/cycle.py +24 -9
  9. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/LICENSE +0 -0
  10. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/README.md +0 -0
  11. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/__init__.py +0 -0
  12. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/__init__.py +0 -0
  13. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
  14. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/advisor/advisor.py +0 -0
  15. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/advisor/system_prompts.py +0 -0
  16. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
  17. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
  18. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
  19. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
  20. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
  21. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/agent_context.py +0 -0
  22. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
  23. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/analyst.py +0 -0
  24. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
  25. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
  26. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
  27. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
  28. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
  29. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
  30. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
  31. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/tools/create_dx_input.py +0 -0
  32. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
  33. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/conversation_facilitator.py +0 -0
  34. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/execution_report.py +0 -0
  35. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
  36. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/explorer.py +0 -0
  37. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
  38. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
  39. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
  40. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
  41. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/system_prompts.py +0 -0
  42. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
  43. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
  44. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
  45. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
  46. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
  47. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
  48. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
  49. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
  50. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
  51. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
  52. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +0 -0
  53. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +0 -0
  54. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
  55. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
  56. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
  57. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
  58. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
  59. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/agents/stream_events.py +0 -0
  60. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/__init__.py +0 -0
  61. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
  62. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/action_extraction.py +0 -0
  63. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/add_input.py +0 -0
  64. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
  65. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
  66. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
  67. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
  68. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/antithesis_extraction.py +0 -0
  69. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
  70. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/aspect_generation.py +0 -0
  71. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
  72. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
  73. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
  74. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
  75. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
  76. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
  77. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
  78. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
  79. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
  80. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
  81. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
  82. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
  83. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
  84. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/dialectical_context.py +0 -0
  85. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/discard.py +0 -0
  86. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
  87. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
  88. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
  89. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
  90. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/source_digest.py +0 -0
  91. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/statement_classification.py +0 -0
  92. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
  93. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/statement_placement.py +0 -0
  94. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
  95. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
  96. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
  97. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
  98. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/dialectical_reasoning.py +0 -0
  99. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/enums/__init__.py +0 -0
  100. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/enums/causality_preset.py +0 -0
  101. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/enums/di.py +0 -0
  102. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/events/__init__.py +0 -0
  103. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/events/graph_event.py +0 -0
  104. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/events/graph_event_bus.py +0 -0
  105. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/exceptions/__init__.py +0 -0
  106. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/exceptions/node_errors.py +0 -0
  107. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
  108. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/__init__.py +0 -0
  109. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
  110. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
  111. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/estimation_manager.py +0 -0
  112. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
  113. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
  114. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
  115. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
  116. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
  117. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
  118. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
  119. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/case.py +0 -0
  120. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
  121. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
  122. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/input.py +0 -0
  123. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
  124. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
  125. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
  126. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
  127. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/statement.py +0 -0
  128. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
  129. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
  130. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/transition.py +0 -0
  131. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
  132. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationship_manager.py +0 -0
  133. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
  134. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
  135. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
  136. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
  137. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
  138. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
  139. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
  140. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
  141. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
  142. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
  143. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
  144. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
  145. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
  146. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
  147. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
  148. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
  149. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
  150. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
  151. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
  152. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
  153. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
  154. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
  155. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
  156. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/rendering.py +0 -0
  157. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
  158. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
  159. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
  160. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
  161. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
  162. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
  163. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
  164. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
  165. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
  166. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
  167. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
  168. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
  169. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/scope_context.py +0 -0
  170. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
  171. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/wheel_segment.py +0 -0
  172. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
  173. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/protocols/__init__.py +0 -0
  174. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/protocols/has_config.py +0 -0
  175. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/protocols/input_resolver.py +0 -0
  176. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/settings.py +0 -0
  177. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/__init__.py +0 -0
  178. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/bedrock_provider.py +0 -0
  179. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/concurrency.py +0 -0
  180. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/dc_replace.py +0 -0
  181. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
  182. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/edge_context.py +0 -0
  183. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/effect_logger.py +0 -0
  184. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/input_context.py +0 -0
  185. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/order_transitions.py +0 -0
  186. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/src/dialectical_framework/utils/sequence_generation.py +0 -0
  187. {dialectical_framework-1.5.5 → dialectical_framework-1.5.6}/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.5.5
3
+ Version: 1.5.6
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.5.5"
3
+ version = "1.5.6"
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"
@@ -18,27 +18,36 @@ from pydantic import Field
18
18
 
19
19
  from dialectical_framework.agents.reasonable_concern import ReasonableConcern
20
20
  from dialectical_framework.enums.di import DI
21
- from dialectical_framework.concerns.aspect_generation import (AspectGeneration,
22
- AspectResult)
23
- from dialectical_framework.concerns.statement_deduplication import \
24
- StatementDeduplication
21
+ from dialectical_framework.concerns.aspect_generation import (
22
+ AspectGeneration,
23
+ AspectResult,
24
+ )
25
+ from dialectical_framework.concerns.statement_deduplication import (
26
+ StatementDeduplication,
27
+ )
25
28
  from dialectical_framework.graph.nodes.polarity import Polarity
26
- from dialectical_framework.graph.nodes.perspective import (POSITION_A_MINUS,
27
- POSITION_A_PLUS,
28
- POSITION_T_MINUS,
29
- POSITION_T_PLUS,
30
- Perspective)
29
+ from dialectical_framework.graph.nodes.perspective import (
30
+ POSITION_A_MINUS,
31
+ POSITION_A_PLUS,
32
+ POSITION_T_MINUS,
33
+ POSITION_T_PLUS,
34
+ Perspective,
35
+ )
31
36
  from dialectical_framework.graph.relationships.polarity_relationship import (
32
- AMinusRelationship, APlusRelationship, HasPolarityRelationship,
33
- TMinusRelationship, TPlusRelationship)
34
- from dialectical_framework.graph.repositories.statement_repository import \
35
- StatementRepository
36
- from dialectical_framework.graph.repositories.input_repository import \
37
- InputRepository
38
- from dialectical_framework.graph.repositories.node_repository import \
39
- NodeRepository
40
- from dialectical_framework.graph.repositories.perspective_repository import \
41
- PerspectiveRepository
37
+ AMinusRelationship,
38
+ APlusRelationship,
39
+ HasPolarityRelationship,
40
+ TMinusRelationship,
41
+ TPlusRelationship,
42
+ )
43
+ from dialectical_framework.graph.repositories.statement_repository import (
44
+ StatementRepository,
45
+ )
46
+ from dialectical_framework.graph.repositories.input_repository import InputRepository
47
+ from dialectical_framework.graph.repositories.node_repository import NodeRepository
48
+ from dialectical_framework.graph.repositories.perspective_repository import (
49
+ PerspectiveRepository,
50
+ )
42
51
 
43
52
  if TYPE_CHECKING:
44
53
  from dialectical_framework.protocols.input_resolver import InputResolver
@@ -51,20 +60,22 @@ class ExpandPolarity(ReasonableConcern[list[Perspective]]):
51
60
  Creates Perspectives from a Polarity by generating and connecting aspects
52
61
  (T+, T-, A+, A-).
53
62
 
54
- Each call produces one NEW perspective (alternative tetrad) for the Polarity,
55
- using `not_like_these` to ensure diversity from existing ones. Call multiple
56
- times on the same Polarity to build a richer space of tetrads.
63
+ Each call produces `count` NEW perspectives (alternative tetrads) for the
64
+ Polarity, generated sequentially so each uses `not_like_these` (existing +
65
+ already-generated-this-call) to ensure diversity. Any pre-existing partial
66
+ Perspective counts toward `count`.
57
67
 
58
68
  Flow:
59
69
  1. Resolve Polarity by hash
60
70
  2. Look up existing Perspectives for this Polarity
61
- 3. If none exist, create a new Perspective
62
- 4. Complete all partial PPs by generating aspects
71
+ 3. Create new partial Perspectives so `count` are generated this call
72
+ 4. Complete all partial PPs sequentially by generating aspects
63
73
  5. Return list of completed Perspectives (existing + new)
64
74
  """
65
75
 
66
- def __init__(self, polarity_hash: str) -> None:
76
+ def __init__(self, polarity_hash: str, count: int = 1) -> None:
67
77
  self.polarity_hash = polarity_hash
78
+ self.count = max(1, count)
68
79
 
69
80
  async def resolve(self) -> list[Perspective]:
70
81
  """
@@ -86,20 +97,17 @@ class ExpandPolarity(ReasonableConcern[list[Perspective]]):
86
97
  pp_repo = PerspectiveRepository()
87
98
  existing_pps = pp_repo.find_by_polarity(polarity)
88
99
 
89
- if not existing_pps:
90
- # No PP exists - create one referencing the Polarity
91
- pp = self._create_perspective_for_polarity(polarity)
92
- existing_pps = [pp]
93
-
94
100
  complete_pps = [pp for pp in existing_pps if pp.is_complete()]
95
101
  partial_pps = [pp for pp in existing_pps if not pp.is_complete()]
96
102
 
97
- if not partial_pps:
98
- # All existing perspectives are complete create a new one
99
- pp = self._create_perspective_for_polarity(polarity)
100
- partial_pps = [pp]
103
+ # Complete any existing partials, then create additional new ones so that
104
+ # `count` fresh Perspectives are produced this call (a pre-existing partial
105
+ # counts as one of them).
106
+ additional_needed = self.count - len(partial_pps)
107
+ for _ in range(max(0, additional_needed)):
108
+ partial_pps.append(self._create_perspective_for_polarity(polarity))
101
109
 
102
- # Complete all partial PPs
110
+ # Complete all partial PPs sequentially (each sees prior results)
103
111
  completed_pps: list[Perspective] = []
104
112
 
105
113
  for pp in partial_pps:
@@ -209,7 +217,12 @@ class ExpandPolarity(ReasonableConcern[list[Perspective]]):
209
217
 
210
218
  def _perspective_final_state(self, pp: Perspective) -> dict[str, str | None]:
211
219
  """Build a dict with the final post-dedup text at each position."""
212
- positions = [POSITION_T_PLUS, POSITION_T_MINUS, POSITION_A_PLUS, POSITION_A_MINUS]
220
+ positions = [
221
+ POSITION_T_PLUS,
222
+ POSITION_T_MINUS,
223
+ POSITION_A_PLUS,
224
+ POSITION_A_MINUS,
225
+ ]
213
226
  state: dict[str, str | None] = {"hash": pp.short_hash}
214
227
  for pos in positions:
215
228
  manager = pp.get_relationship_manager_by_position(pos)
@@ -299,13 +312,22 @@ class ExpandPolarity(ReasonableConcern[list[Perspective]]):
299
312
 
300
313
  @llm.tool
301
314
  async def expand_polarities(
302
- polarity_hashes: Annotated[list[str], Field(description="Hashes of Polarities to expand into full Perspectives")],
315
+ polarity_hashes: Annotated[
316
+ list[str],
317
+ Field(description="Hashes of Polarities to expand into full Perspectives"),
318
+ ],
319
+ count: Annotated[
320
+ int,
321
+ Field(
322
+ description="Number of new Perspectives to generate per Polarity (each is diverse from prior ones)"
323
+ ),
324
+ ] = 1,
303
325
  ) -> str:
304
- """Build complete Perspectives from Polarities by generating evaluative aspects (T+, T-, A+, A-) for each. Each call generates one new Perspective per Polarity — call multiple times for alternative perspectives on the same Polarity (duplicates in a single call are ignored). The Polarities must already exist in the graph."""
326
+ """Build complete Perspectives from Polarities by generating evaluative aspects (T+, T-, A+, A-) for each. Generates `count` new Perspectives per Polarity sequentially each sees prior tetrads to ensure diversity. The Polarities must already exist in the graph."""
305
327
  import asyncio
306
328
 
307
329
  async def _expand_one(h: str) -> str:
308
- concern = ExpandPolarity(polarity_hash=h)
330
+ concern = ExpandPolarity(polarity_hash=h, count=count)
309
331
  await concern.resolve()
310
332
  return str(concern.report)
311
333
 
@@ -35,6 +35,13 @@ Choose your approach based on the user's input:
35
35
  - User is working step-by-step and asks for the next step
36
36
  - User refers to specific existing theses and wants tensions found
37
37
  - User explicitly asks to expand a polarity into perspectives
38
+ - User asks to "find tetrades", "show aspects", "build T+/T-/A+/A-", or otherwise requests the four-aspect structure
39
+
40
+ **Disambiguating "perspectives", "angles", "points of view":**
41
+ These words map to DIFFERENT tools depending on what's in scope:
42
+ - Polarity in view/referenced → `expand_polarities` (build the tetrad for that polarity)
43
+ - Thesis in view/referenced → `find_polarities` (find opposing angles)
44
+ - No specific node → check what exists: unexpanded polarities → `expand_polarities`; theses without polarities → `find_polarities`
38
45
 
39
46
  **Use `add_input` when:**
40
47
  - User provides multi-sentence source material (a paragraph, article, or detailed description) to capture before analysis
@@ -93,7 +100,7 @@ When the user wants to explore interactions between perspectives:
93
100
  - `surface_theses` — Extract theses from inputs (requires inputs in scope).
94
101
  - `find_polarities` — Find antitheses for existing theses.
95
102
  - `introduce_polarity` — Directly introduce a known T-A tension (both sides clear).
96
- - `expand_polarities` — Expand polarities into full Perspectives (T+/T-/A+/A-).
103
+ - `expand_polarities` — Expand polarities into full Perspectives (T+/T-/A+/A-). Pass `count > 1` to generate several diverse tetrads per polarity in one call.
97
104
  - `place_statement` — Check if a statement exists in the graph already.
98
105
 
99
106
  **Steering:**
@@ -341,8 +341,11 @@ both sides contribute", "constructive co-existence", "where 1+1 > 2".
341
341
 
342
342
  ## Viewport Scope
343
343
 
344
- Messages may include context about what the user is currently viewing. This tells you their active focus. Respect it:
344
+ Messages may begin with `[User is currently viewing: ...]`. This tells you their active focus use it to resolve ambiguous requests:
345
345
 
346
+ - When the user asks for an action without specifying a target (e.g., "find tetrades", "expand", "show aspects", "find perspectives"), apply it to the node in their current view.
347
+ - If a polarity is in view and the user asks for tetrades/aspects/perspectives/angles, expand that polarity into its full structure.
348
+ - If a thesis is in view and the user asks for perspectives/angles/oppositions, find opposing viewpoints for that thesis.
346
349
  - Only operate on the node(s) in the user's viewport unless they explicitly reference something else or ask to switch.
347
350
  - Never silently pivot to other nodes outside the viewport. If you think working on a different node would help, ask first.
348
351
 
@@ -487,5 +490,5 @@ rules. Adjust interaction accordingly:
487
490
  - Present tetrads structurally with all six positions and scores.
488
491
  - Show transformation positions explicitly: Ac, Ac+, Ac-, Re, Re+, Re-.
489
492
  - Show control statements, modality alignment, diagonal contradictions.
490
- - Suggest next steps but don't auto-execute — the user drives.
493
+ - Suggest exploration-phase next steps (nexus, wheels) rather than auto-executing — the user drives structural expansion. Analysis tools (expand, find) still follow "act on clear intent."
491
494
  """
@@ -10,8 +10,7 @@ from typing import Optional
10
10
  from dialectical_framework.agents.reasonable_concern import ReasonableConcern
11
11
  from dialectical_framework.graph.nodes.nexus import Nexus
12
12
  from dialectical_framework.graph.nodes.perspective import Perspective
13
- from dialectical_framework.graph.repositories.node_repository import \
14
- NodeRepository
13
+ from dialectical_framework.graph.repositories.node_repository import NodeRepository
15
14
  from dialectical_framework.protocols.has_config import SettingsAware
16
15
 
17
16
 
@@ -48,7 +47,16 @@ class CreateNexus(SettingsAware, ReasonableConcern[CreateNexusResult]):
48
47
 
49
48
  repo = NodeRepository()
50
49
  perspectives: list[Perspective] = []
50
+ # Dedup input hashes, preserving order: BELONGS_TO_NEXUS is a directed
51
+ # relationship, so connect() does NOT deduplicate. A repeated hash here
52
+ # would create duplicate edges, causing nexus.perspectives.all() to
53
+ # yield the same PP twice and poisoning combination generation
54
+ # (degenerate cycles with repeated perspective_hashes).
55
+ seen_hashes: set[str] = set()
51
56
  for pp_hash in perspective_hashes:
57
+ if pp_hash in seen_hashes:
58
+ continue
59
+ seen_hashes.add(pp_hash)
52
60
  node = repo.find_by_hash(pp_hash, node_type=Perspective)
53
61
  if node is None:
54
62
  raise ValueError(f"Perspective not found: {pp_hash}")
@@ -122,26 +122,37 @@ class PerspectiveCombination(ReasonableConcern[CombinationResult], SettingsAware
122
122
  self._report.ok = False
123
123
  self._report.summary = "Nexus must be committed before combination"
124
124
  return CombinationResult(
125
- nexus=nexus, cycles=[], wheels=[],
126
- cycles_by_layer={}, wheels_by_layer={},
125
+ nexus=nexus,
126
+ cycles=[],
127
+ wheels=[],
128
+ cycles_by_layer={},
129
+ wheels_by_layer={},
127
130
  )
128
131
 
129
132
  if not perspectives:
130
133
  self._report.ok = False
131
134
  self._report.summary = "No Perspectives provided"
132
135
  return CombinationResult(
133
- nexus=nexus, cycles=[], wheels=[],
134
- cycles_by_layer={}, wheels_by_layer={},
136
+ nexus=nexus,
137
+ cycles=[],
138
+ wheels=[],
139
+ cycles_by_layer={},
140
+ wheels_by_layer={},
135
141
  )
136
142
 
137
143
  # Validate all PPs are committed
138
144
  for pp in perspectives:
139
145
  if not pp.is_committed:
140
146
  self._report.ok = False
141
- self._report.summary = "Perspective must be committed before combination"
147
+ self._report.summary = (
148
+ "Perspective must be committed before combination"
149
+ )
142
150
  return CombinationResult(
143
- nexus=nexus, cycles=[], wheels=[],
144
- cycles_by_layer={}, wheels_by_layer={},
151
+ nexus=nexus,
152
+ cycles=[],
153
+ wheels=[],
154
+ cycles_by_layer={},
155
+ wheels_by_layer={},
145
156
  )
146
157
 
147
158
  # Add PPs to Nexus (idempotent — skip already connected)
@@ -150,8 +161,19 @@ class PerspectiveCombination(ReasonableConcern[CombinationResult], SettingsAware
150
161
  self._report.artifacts["nexus_hash"] = nexus.short_hash
151
162
  self._report.artifacts["perspective_count"] = len(perspectives)
152
163
 
153
- # Build from all PPs in Nexus (not just the ones passed in)
154
- all_nexus_pps = [pp for pp, _ in nexus.perspectives.all()]
164
+ # Build from all PPs in Nexus (not just the ones passed in).
165
+ # Dedup by hash, preserving order: nexus.perspectives.all() can yield
166
+ # the same Perspective twice if a duplicate BELONGS_TO_NEXUS edge exists
167
+ # (BELONGS_TO_NEXUS is a directed relationship — connect() does NOT
168
+ # deduplicate it). combinations() over a list with repeats produces
169
+ # degenerate cycles whose perspective_hashes repeat a PP, corrupting
170
+ # all downstream wheel rendering.
171
+ seen: set[str] = set()
172
+ all_nexus_pps = [
173
+ pp
174
+ for pp, _ in nexus.perspectives.all()
175
+ if pp.hash not in seen and not seen.add(pp.hash)
176
+ ]
155
177
  total_pps = len(all_nexus_pps)
156
178
 
157
179
  # Build combinations layer by layer, collect only new structures
@@ -314,13 +336,17 @@ class PerspectiveCombination(ReasonableConcern[CombinationResult], SettingsAware
314
336
  Returns:
315
337
  Tuple of (Cycle, is_new)
316
338
  """
317
- from dialectical_framework.graph.repositories.cycle_repository import CycleRepository
339
+ from dialectical_framework.graph.repositories.cycle_repository import (
340
+ CycleRepository,
341
+ )
318
342
 
319
343
  # Layer-1 (single PP) Cycles have no causality intent (nothing to order)
320
344
  intent = self._preset if len(perspectives) >= 2 else None
321
345
 
322
346
  cycle_repo = CycleRepository()
323
- existing_cycles = cycle_repo.find_by_perspectives(perspectives, exact_order=True)
347
+ existing_cycles = cycle_repo.find_by_perspectives(
348
+ perspectives, exact_order=True
349
+ )
324
350
 
325
351
  for cycle in existing_cycles:
326
352
  if cycle.intent == intent:
@@ -392,9 +418,7 @@ class PerspectiveCombination(ReasonableConcern[CombinationResult], SettingsAware
392
418
  transition.commit()
393
419
  transition.cycle.connect(wheel)
394
420
  self._report.node_created(transition)
395
- self._report.relationship_created(
396
- transition.cycle, transition, wheel
397
- )
421
+ self._report.relationship_created(transition.cycle, transition, wheel)
398
422
 
399
423
  # Connect to cycle
400
424
  cycle.wheels.connect(wheel)
@@ -422,7 +446,9 @@ class PerspectiveCombination(ReasonableConcern[CombinationResult], SettingsAware
422
446
  Two sequences are opposite-direction if one is a circular reverse of the other.
423
447
  Connects pairs via the OPPOSITE_DIRECTION symmetric relationship.
424
448
  """
425
- from dialectical_framework.graph.repositories.cycle_repository import CycleRepository
449
+ from dialectical_framework.graph.repositories.cycle_repository import (
450
+ CycleRepository,
451
+ )
426
452
 
427
453
  cycle_repo = CycleRepository()
428
454
  wheel_repo = WheelRepository()
@@ -451,7 +477,7 @@ def _connect_opposite_direction_cycles(
451
477
 
452
478
  for i, cycle_a in enumerate(cycles):
453
479
  seq_a = cycle_a.perspective_hashes
454
- for cycle_b in cycles[i + 1:]:
480
+ for cycle_b in cycles[i + 1 :]:
455
481
  seq_b = cycle_b.perspective_hashes
456
482
  if _is_circular_reverse(seq_a, seq_b):
457
483
  pair_id = tuple(sorted([cycle_a.hash, cycle_b.hash]))
@@ -476,7 +502,7 @@ def _connect_opposite_direction_wheels(
476
502
  wheel_sequences.append((wheel, comp_hashes))
477
503
 
478
504
  for i, (wheel_a, seq_a) in enumerate(wheel_sequences):
479
- for wheel_b, seq_b in wheel_sequences[i + 1:]:
505
+ for wheel_b, seq_b in wheel_sequences[i + 1 :]:
480
506
  if _is_circular_reverse(seq_a, seq_b):
481
507
  pair_id = tuple(sorted([wheel_a.hash, wheel_b.hash]))
482
508
  if pair_id not in connected:
@@ -498,7 +524,4 @@ def _is_circular_reverse(seq_a: list[str], seq_b: list[str]) -> bool:
498
524
  if len(seq_a) <= 2:
499
525
  return False
500
526
  reversed_a = list(reversed(seq_a))
501
- return any(
502
- reversed_a[i:] + reversed_a[:i] == seq_b
503
- for i in range(len(reversed_a))
504
- )
527
+ return any(reversed_a[i:] + reversed_a[:i] == seq_b for i in range(len(reversed_a)))
@@ -14,7 +14,11 @@ from typing import ClassVar, Optional, TYPE_CHECKING
14
14
 
15
15
  from dialectical_framework.graph.nodes.assessable_entity import AssessableEntity
16
16
  from dialectical_framework.graph.mixins.intent_mixin import IntentMixin
17
- from dialectical_framework.graph.relationship_manager import RelationshipTo, RelationshipBoth, RelationshipManager
17
+ from dialectical_framework.graph.relationship_manager import (
18
+ RelationshipTo,
19
+ RelationshipBoth,
20
+ RelationshipManager,
21
+ )
18
22
  from dialectical_framework.graph.relationships.has_wheel_relationship import (
19
23
  HasWheelRelationship,
20
24
  )
@@ -82,7 +86,7 @@ class Cycle(IntentMixin, AssessableEntity, label="Cycle"):
82
86
  wheels: ClassVar[RelationshipManager[Wheel]] = RelationshipTo(
83
87
  "Wheel",
84
88
  model=HasWheelRelationship,
85
- cardinality=(0, None) # Zero or more wheels can implement this cycle
89
+ cardinality=(0, None), # Zero or more wheels can implement this cycle
86
90
  )
87
91
 
88
92
  # Opposite-direction counterpart (symmetric)
@@ -100,22 +104,31 @@ class Cycle(IntentMixin, AssessableEntity, label="Cycle"):
100
104
  Order determines the T-cycle: T1 → T2 → T3 → T1...
101
105
 
102
106
  Args:
103
- perspectives: Ordered list of committed Perspectives
107
+ perspectives: Ordered list of committed, distinct Perspectives
104
108
 
105
109
  Returns:
106
110
  Self for chaining
107
111
 
108
112
  Raises:
109
- ValueError: If any PP is not committed
113
+ ValueError: If any PP is not committed, or if any PP is repeated
110
114
  """
111
115
  hashes = []
112
116
  for pp in perspectives:
113
117
  if not pp.is_committed:
114
- raise ValueError(
115
- "Perspective must be committed before adding to Cycle"
116
- )
118
+ raise ValueError("Perspective must be committed before adding to Cycle")
117
119
  hashes.append(pp.hash)
118
120
 
121
+ # Reject duplicate perspectives. A cycle claiming to be "layer N"
122
+ # (N = len(perspective_hashes)) must reference N *distinct* PPs.
123
+ # Duplicates produce degenerate cycles (e.g. combinations() over a
124
+ # PP list poisoned by a transient duplicate BELONGS_TO_NEXUS edge)
125
+ # that render as malformed wheels. Fail loud so corruption is never
126
+ # persisted rather than surfacing later in the consuming app.
127
+ if len(set(hashes)) != len(hashes):
128
+ raise ValueError(
129
+ f"Cycle perspectives must be distinct; got duplicates in {hashes}"
130
+ )
131
+
119
132
  self.perspective_hashes = hashes
120
133
  self._pp_refs = perspectives # Keep refs for potential use
121
134
  return self
@@ -133,7 +146,10 @@ class Cycle(IntentMixin, AssessableEntity, label="Cycle"):
133
146
  if not self.perspective_hashes:
134
147
  return []
135
148
 
136
- from dialectical_framework.graph.repositories.node_repository import NodeRepository
149
+ from dialectical_framework.graph.repositories.node_repository import (
150
+ NodeRepository,
151
+ )
152
+
137
153
  repo = NodeRepository()
138
154
 
139
155
  result = []
@@ -268,4 +284,3 @@ class Cycle(IntentMixin, AssessableEntity, label="Cycle"):
268
284
  hash_str = self.short_hash if self.is_committed else "uncommitted"
269
285
  pp_count = len(self.perspective_hashes)
270
286
  return f"Cycle({hash_str}, pp_count={pp_count}, intent={self.intent})"
271
-