dialectical-framework 2.1.0__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/PKG-INFO +2 -2
  2. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/pyproject.toml +8 -2
  3. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/consultant/consultant.py +39 -3
  4. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/antithesis_extraction.py +17 -4
  5. dialectical_framework-2.1.2/src/dialectical_framework/concerns/attribution_check.py +148 -0
  6. dialectical_framework-2.1.2/src/dialectical_framework/concerns/tetrad_candidates.py +359 -0
  7. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/tetrad_sketch.py +18 -3
  8. dialectical_framework-2.1.2/src/dialectical_framework/concerns/thesis_fit_check.py +165 -0
  9. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/view_sketch.py +135 -11
  10. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/views.py +9 -0
  11. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/bedrock_provider.py +211 -44
  12. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/format_compat.py +65 -1
  13. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/thinking_compat.py +21 -0
  14. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/use_brain.py +11 -1
  15. dialectical_framework-2.1.0/src/dialectical_framework/concerns/tetrad_candidates.py +0 -197
  16. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/LICENSE +0 -0
  17. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/PATENTS +0 -0
  18. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/README.md +0 -0
  19. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/__init__.py +0 -0
  20. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/__init__.py +0 -0
  21. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
  22. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/advisor.py +0 -0
  23. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/build_policy.py +0 -0
  24. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/migration.py +0 -0
  25. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/reply_hygiene.py +0 -0
  26. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/system_prompts.py +0 -0
  27. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
  28. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
  29. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/deepen.py +0 -0
  30. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
  31. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
  32. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/note.py +0 -0
  33. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/record_decision.py +0 -0
  34. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/scoped.py +0 -0
  35. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
  36. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/agent_context.py +0 -0
  37. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
  38. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/analyst.py +0 -0
  39. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
  40. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
  41. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
  42. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +0 -0
  43. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
  44. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
  45. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/skills/sketch_tetrad.py +0 -0
  46. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
  47. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/system_prompts.py +0 -0
  48. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
  49. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
  50. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/app_spec.py +0 -0
  51. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/apps.py +0 -0
  52. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/consultant/__init__.py +0 -0
  53. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/conversation_facilitator.py +0 -0
  54. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/execution_report.py +0 -0
  55. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
  56. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/explorer.py +0 -0
  57. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
  58. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
  59. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
  60. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
  61. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/system_prompts.py +0 -0
  62. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
  63. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
  64. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
  65. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
  66. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
  67. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
  68. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
  69. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
  70. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/audit_feasibility.py +0 -0
  71. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/create_dx_input.py +0 -0
  72. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
  73. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
  74. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +0 -0
  75. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +0 -0
  76. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
  77. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
  78. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
  79. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
  80. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
  81. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/stream_events.py +0 -0
  82. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/toolsets.py +0 -0
  83. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/agents/turn_timing.py +0 -0
  84. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/__init__.py +0 -0
  85. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
  86. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/action_extraction.py +0 -0
  87. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/add_input.py +0 -0
  88. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
  89. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
  90. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
  91. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
  92. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/antithetical_thesis_detection.py +0 -0
  93. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
  94. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/aspect_generation.py +0 -0
  95. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/build_status.py +0 -0
  96. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
  97. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
  98. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
  99. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
  100. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
  101. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
  102. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
  103. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
  104. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
  105. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
  106. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
  107. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
  108. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/create_nexus.py +0 -0
  109. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/decision_coherence_check.py +0 -0
  110. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/decision_confirmation_check.py +0 -0
  111. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
  112. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/dialectical_context.py +0 -0
  113. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/discard.py +0 -0
  114. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
  115. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
  116. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
  117. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
  118. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
  119. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/record_decision.py +0 -0
  120. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/scoring_scales.py +0 -0
  121. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/source_digest.py +0 -0
  122. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/statement_classification.py +0 -0
  123. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
  124. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/statement_headline.py +0 -0
  125. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/statement_placement.py +0 -0
  126. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
  127. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/tetrad_grounding.py +0 -0
  128. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
  129. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
  130. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
  131. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/concerns/transformation_sketch.py +0 -0
  132. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/dialectical_reasoning.py +0 -0
  133. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/enums/__init__.py +0 -0
  134. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/enums/causality_preset.py +0 -0
  135. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/enums/di.py +0 -0
  136. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/events/__init__.py +0 -0
  137. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/events/graph_event.py +0 -0
  138. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/events/graph_event_bus.py +0 -0
  139. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/events/progress_event.py +0 -0
  140. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/exceptions/__init__.py +0 -0
  141. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/exceptions/node_errors.py +0 -0
  142. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/exceptions/provider_errors.py +0 -0
  143. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
  144. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/__init__.py +0 -0
  145. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
  146. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
  147. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/estimation_manager.py +0 -0
  148. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
  149. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
  150. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
  151. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
  152. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
  153. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
  154. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
  155. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/case.py +0 -0
  156. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
  157. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/decision.py +0 -0
  158. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
  159. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
  160. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/input.py +0 -0
  161. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
  162. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/note.py +0 -0
  163. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
  164. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
  165. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
  166. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/statement.py +0 -0
  167. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
  168. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
  169. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/transition.py +0 -0
  170. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
  171. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationship_manager.py +0 -0
  172. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
  173. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
  174. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
  175. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
  176. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
  177. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
  178. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
  179. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
  180. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
  181. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
  182. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/grounded_in_relationship.py +0 -0
  183. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
  184. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
  185. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
  186. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
  187. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
  188. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
  189. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
  190. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
  191. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
  192. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
  193. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
  194. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
  195. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
  196. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/rendering.py +0 -0
  197. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
  198. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
  199. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
  200. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/decision_repository.py +0 -0
  201. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
  202. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
  203. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
  204. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/note_repository.py +0 -0
  205. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
  206. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
  207. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
  208. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
  209. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
  210. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
  211. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/scope_context.py +0 -0
  212. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
  213. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/wheel_segment.py +0 -0
  214. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
  215. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/protocols/__init__.py +0 -0
  216. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/protocols/has_config.py +0 -0
  217. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/protocols/input_resolver.py +0 -0
  218. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/py.typed +0 -0
  219. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/settings.py +0 -0
  220. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/settings_context.py +0 -0
  221. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/__init__.py +0 -0
  222. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/async_drain.py +0 -0
  223. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/call_census.py +0 -0
  224. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/chunking.py +0 -0
  225. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/concurrency.py +0 -0
  226. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/dc_replace.py +0 -0
  227. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
  228. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/edge_context.py +0 -0
  229. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/effect_logger.py +0 -0
  230. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/input_context.py +0 -0
  231. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/order_transitions.py +0 -0
  232. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/progress.py +0 -0
  233. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/retry_accounting.py +0 -0
  234. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/sequence_generation.py +0 -0
  235. {dialectical_framework-2.1.0 → dialectical_framework-2.1.2}/src/dialectical_framework/utils/utterance.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dialectical-framework
3
- Version: 2.1.0
3
+ Version: 2.1.2
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
@@ -32,7 +32,7 @@ Requires-Dist: broadcaster (>=0.3.1,<0.4.0)
32
32
  Requires-Dist: dependency-injector (>=4.48.2,<5.0.0)
33
33
  Requires-Dist: gqlalchemy (>=1.8.0,<2.0.0)
34
34
  Requires-Dist: langfuse (>=4.5.1,<5.0.0)
35
- Requires-Dist: mirascope[anthropic,openai,ops] (>=2.5.0,<3.0.0)
35
+ Requires-Dist: mirascope[anthropic,openai,ops] (>=2.8.2,<3.0.0)
36
36
  Requires-Dist: python-dotenv (>=1.2.1,<2.0.0)
37
37
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
38
38
  Project-URL: Homepage, https://github.com/dialexity/dialectical-framework
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dialectical-framework"
3
- version = "2.1.0"
3
+ version = "2.1.2"
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"
@@ -43,11 +43,17 @@ from = "src"
43
43
 
44
44
  [tool.poetry.dependencies]
45
45
  python = "^3.11"
46
+ # 2.8.2 is the FLOOR, not a preference: below it Mirascope's Anthropic encoder
47
+ # sends extended thinking as the legacy token budget, which every Claude 5 model
48
+ # rejects with a 400 — so a thinking structured call (the view turn, TetradSketch)
49
+ # cannot run on the native `anthropic/` provider at all. The framework's own
50
+ # `thinking_compat` covers this for `bedrock/` only. Measured 2026-10-26, story in
51
+ # docs/dev-notes/antithesis-selection.md.
46
52
  mirascope = {extras = [
47
53
  "openai",
48
54
  "anthropic",
49
55
  "ops"
50
- ], version = "^2.5.0"}
56
+ ], version = "^2.8.2"}
51
57
  python-dotenv = "^1.2.1"
52
58
  tabulate = "^0.9.0"
53
59
  dependency-injector = "^4.48.2"
@@ -425,11 +425,15 @@ class Consultant:
425
425
  """
426
426
  from dialectical_framework.concerns.view_sketch import (
427
427
  ViewSketchDto, exploration_view_from_sketch, history_ask,
428
- history_text, perspective_from_sketch, view_sketch_prompt)
428
+ history_text, judge_context, perspective_from_sketch,
429
+ view_sketch_prompt)
429
430
 
430
431
  import asyncio
431
432
  import dataclasses
432
433
 
434
+ from dialectical_framework.agents.advisor.migration import person_turns
435
+ from dialectical_framework.concerns.attribution_check import \
436
+ drop_unborne_holders
433
437
  from dialectical_framework.concerns.tetrad_candidates import (
434
438
  clamp_attempts, ranking, select_sketch)
435
439
 
@@ -459,7 +463,6 @@ class Consultant:
459
463
  sketch_turn._messages = list(history)
460
464
  return await sketch_turn.submit(ViewSketchDto, request)
461
465
 
462
- n = clamp_attempts(attempts)
463
466
  # Fully parallel, deliberately. On a COLD cache all N draws write the
464
467
  # ~12k system head instead of one writing and the rest reading (~$0.06
465
468
  # a card at list prices, 2026-10-06), and the fix would be to await the
@@ -470,6 +473,13 @@ class Consultant:
470
473
  # (`utils/bedrock_provider.py::_CACHE_TTL`) attacks the same cost
471
474
  # without the latency, which is the trade taken. Revisit only with a
472
475
  # measured cold-start rate.
476
+ n = clamp_attempts(attempts)
477
+ # The SAME request N times, by measurement: N identical requests draw
478
+ # the same opposition N times, reworded (the first app, 52/52 cards),
479
+ # and asking draw 2..N for a different one made the spares differ and
480
+ # the winner worse (CC 19/40 against 27/40, 2026-10-13,
481
+ # `tests/e2e/probe_view_turn_ask.py`) — so this is a coherence re-roll,
482
+ # and a host's "another" served from `runners_up` is a rewording.
473
483
  drawn = await asyncio.gather(*(draw() for _ in range(n)), return_exceptions=True)
474
484
  sketches = [d for d in drawn if not isinstance(d, BaseException)]
475
485
  failures = [d for d in drawn if isinstance(d, BaseException)]
@@ -497,19 +507,45 @@ class Consultant:
497
507
  for drawn_sketch in sketches:
498
508
  if drawn_sketch.tensions:
499
509
  drawn_sketch.tensions[0].thesis = pinned
510
+ # A pinned position is the host's own words for the
511
+ # person's own position: never someone else's.
512
+ drawn_sketch.tensions[0].thesis_held_by = ""
513
+ # The person's turns, for the judges below. Also the words a named
514
+ # holder is checked against.
515
+ words = judge_context(person_turns(history))
516
+ # A holder the words do not bear is dropped, in code, on every draw
517
+ # (`concerns/attribution_check.py`, 2026-10-27): the prompt rule
518
+ # halved the misquotation on pasted material and did not end it, and a
519
+ # runner-up served later must not carry one either. No call where no
520
+ # holder is named — the person's own words cost nothing here.
521
+ firsts_to_check = [s.tensions[0] for s in sketches if s.tensions]
522
+ for dropped in await drop_unborne_holders(firsts_to_check, words):
523
+ logger.info("A drawn holder was dropped: %s", dropped)
500
524
  # With several draws the framework's own coherence check picks the one
501
525
  # whose FIRST tension holds best (`concerns/tetrad_candidates.py`); a
502
526
  # draw with no complete first tension sorts last. The card a blindspot
503
527
  # screen shows is that first tension. The judged draws that lost come
504
528
  # back too, best first, as `runners_up` — a host's "again"/"another"
505
529
  # can serve one without a new call (the module's stated intent).
530
+ # The judge reads the PERSON's turns as its context (`judge_context`):
531
+ # it never sees T or A, and called blind — as it was here until
532
+ # 2026-10-13, while the build and the persisted path passed their
533
+ # material — it scored the four aspects against nothing.
506
534
  runners_up: list[PerspectiveView] = []
507
535
  if len(sketches) > 1:
508
536
  firsts = [s.tensions[0] if s.tensions else None for s in sketches]
509
537
  judgeable = [i for i, t in enumerate(firsts) if t is not None and all(
510
538
  (getattr(t, k) or "").strip() for k in ("thesis", "antithesis", "t_plus", "t_minus", "a_plus", "a_minus"))]
511
539
  if len(judgeable) > 1:
512
- _best, verdicts = await select_sketch([firsts[i] for i in judgeable])
540
+ # This writer CHOOSES the thesis, so a second judge reads each
541
+ # draw's T, A and A+ against the person's words — the claim
542
+ # they would sign, an opposition they have not granted
543
+ # (`tetrad_candidates.judge_fit`, 2026-10-20). With a pinned T
544
+ # the first read is the same on every draw and the second
545
+ # still tells them apart.
546
+ _best, verdicts = await select_sketch(
547
+ [firsts[i] for i in judgeable], context=words, fit_against=words
548
+ )
513
549
  order = ranking(verdicts)
514
550
  sketch = sketches[judgeable[order[0]]]
515
551
  runners_up = [perspective_from_sketch(firsts[judgeable[i]]) for i in order[1:]]
@@ -66,7 +66,17 @@ _AwaitedT = TypeVar("_AwaitedT")
66
66
  # because the previous ask ("an antithesis that represents Negation of the
67
67
  # thesis") was complied with exactly — and at the high rungs exact compliance
68
68
  # is a caricature (measured 19/19, `tests/probe_blindspot_paths.py`).
69
- _OPPOSING_POSITION_ASK = """The antithesis is a POSITION someone holds for its own value, that functionally opposes the role the thesis plays — at this rung's mechanism, in the thesis's own terms. It is not the thesis's absence or degradation restated as a stance. Example: for "Quit my job and start my own company", Negation is "Keep the security and craft of employed work", not "Never quit, stay employed forever"; the second is a caricature nobody holds, and a caricature cannot be developed constructively — rate such a candidate's tetrad potential low rather than dressing it up."""
69
+ #
70
+ # The second failure, the caricature's mirror (2026-10-20, the first app's
71
+ # handoff `docs/dev-notes/antithesis-selection.md`, "The hedge wins on coherence"): "in the thesis's own
72
+ # terms" was complied with exactly too, and when the thesis's own words NAME
73
+ # their opponent and concede it ("not only schools, but also …"), that
74
+ # opponent is the most literal answer — the writer drew it as A on Sonnet 5.5
75
+ # in every configuration, and the card handed the person their own clause as
76
+ # the blind spot they were not seeing. This ask was one-sided: tuned against
77
+ # the strawman end, it had nothing to say about the granted end. UNMEASURED
78
+ # as a rule; the shape is the caricature rule's, in the same place.
79
+ _OPPOSING_POSITION_ASK = """The antithesis is a POSITION someone holds for its own value, that functionally opposes the role the thesis plays — at this rung's mechanism, in the thesis's own terms. It is not the thesis's absence or degradation restated as a stance. Example: for "Quit my job and start my own company", Negation is "Keep the security and craft of employed work", not "Never quit, stay employed forever"; the second is a caricature nobody holds, and a caricature cannot be developed constructively — rate such a candidate's tetrad potential low rather than dressing it up. The mirror failure: an opposition the thesis's own words already name AND concede ("not only X but also Y", "X matters, but …") is seen, not missing — its constructive side is something the person has already granted, and a tetrad built on it shows them their own concession as a blind spot. Rate such a candidate's tetrad potential low as well, and oppose the claim they actually make."""
70
80
 
71
81
 
72
82
  # --- Extraction-specific DTOs ---
@@ -94,7 +104,9 @@ class ModePointResultDto(BaseModel):
94
104
  "A is a position someone holds for its own value, it can be developed so "
95
105
  "that it also strengthens what T is for (an A+ exists), and it has its own "
96
106
  "one-sided failure (an A-). Low: A is T's absence or degradation dressed as "
97
- "a position, or a caricature nobody would hold — nothing to develop."
107
+ "a position, a caricature nobody would hold — nothing to develop — or an "
108
+ "opposition the thesis's own words already name and concede, whose A+ the "
109
+ "person has granted."
98
110
  ),
99
111
  )
100
112
  arousal_label: str = Field(
@@ -132,7 +144,8 @@ class OptimumARankingDto(BaseModel):
132
144
  "functionally opposes the role the thesis plays; it can be developed so "
133
145
  "that it also strengthens what the thesis is for, and it has its own "
134
146
  "one-sided failure. Worst = the thesis's absence or degradation dressed "
135
- "as a stance, or a caricature nobody holds."
147
+ "as a stance, a caricature nobody holds, or an opposition the thesis's own "
148
+ "words already concede."
136
149
  )
137
150
  )
138
151
  reasoning: str = Field(
@@ -542,7 +555,7 @@ Thesis: "{thesis.prompt_text}"
542
555
  Candidates:
543
556
  {numbered}
544
557
 
545
- The best antithesis is the one most likely to yield a coherent tetrad with the thesis: a POSITION someone holds for its own value that functionally opposes the role the thesis plays — it can be developed constructively so that it also strengthens what the thesis is for, and it has its own one-sided failure. Rank lower any candidate that is the thesis's absence or degradation dressed as a stance, a caricature nobody would hold, or the thesis merely hedged. Return every number exactly once."""
558
+ The best antithesis is the one most likely to yield a coherent tetrad with the thesis: a POSITION someone holds for its own value that functionally opposes the role the thesis plays — it can be developed constructively so that it also strengthens what the thesis is for, and it has its own one-sided failure. Rank lower any candidate that is the thesis's absence or degradation dressed as a stance, a caricature nobody would hold, the thesis merely hedged, or an opposition the thesis's own words already name and concede. Return every number exactly once."""
546
559
  try:
547
560
  verdict = await self._conversation.isolate().submit(
548
561
  response_model=OptimumARankingDto, user_content=prompt
@@ -0,0 +1,148 @@
1
+ """
2
+ AttributionCheck: do the words bear the holder a drawn pole names?
3
+
4
+ WHY A CHECK IN CODE
5
+ ===================
6
+ The view turn names WHO holds each pole when it is not the person's own
7
+ (`ViewSketchPerspectiveDto.thesis_held_by` / `antithesis_held_by`, carried as
8
+ `PoleView.held_by`, since 2026-10-26), and a prompt rule says a party is
9
+ named only for a position its own words take (`view_sketch.HOLDER_FROM_THE_WORDS`,
10
+ 2026-10-27). Measured on 37 pieces of pasted material, two draws each, the
11
+ rule HALVED the misquotation and did not end it: about one draw in twenty
12
+ still named a party for words the text does not bear — a speaker given the
13
+ inverse of their own position, two sides swapped — and a screen shows that
14
+ as "the Minister's position" over words the Minister never said. Prompt
15
+ enforcement scales with compliance (CLAUDE.md, the machinery leak), so the
16
+ last step is the same shape as the thesis pin: enforced by code after the
17
+ draw, not asked for again.
18
+
19
+ WHAT IT DOES
20
+ ============
21
+ One structured call per NAMED holder, shown the person's words and one pole,
22
+ asked whether the text attributes that position to that party — stated in
23
+ their own words or reported as theirs (a role and the person in it are one
24
+ party). NOT attributed: the holder is dropped, so the pole reads as a built
25
+ opposition (A) or as unattributed (T) instead of a misquotation. Attributed:
26
+ kept. Never a replacement: the judge is not handed the right to put a
27
+ different name on the screen. Free where it matters most — on the person's
28
+ own words every holder is empty and no call is made.
29
+
30
+ Fail-soft the way the fit judge is: a failed judging KEEPS the holder. The
31
+ alternative, dropping on error, would turn "the Minister's position" into
32
+ "your position" on a provider hiccup, which is the 2026-10-26 defect back.
33
+ A missing holder is also what `held_by` means on the graph path, so a host
34
+ must already handle None.
35
+
36
+ THE VERDICT'S WORDING IS VERSION 2. Version 1 asked whether the text
37
+ "attributes this position" to the party and, applied to the 130 named
38
+ holders of the gate's WITH-arm draws, dropped 28 — 18 of the 27 the probe's
39
+ own judge had flagged, and 10 it had grounded: it read an elaboration as a
40
+ misquotation ("opponents of trophy hunting" refused "killing for pleasure is
41
+ wrong" because the text says only that the controversy is about the morality
42
+ of hunting for pleasure; a rhetorical question that takes a side was "no
43
+ stance"). A dropped true holder costs what this check exists to prevent in
44
+ reverse — "the Minister's position" shown as "your position" — so version 2
45
+ says a fair condensation of the stance the text gives them is theirs, and
46
+ NOT_ATTRIBUTED is no stance at all, the opposite side, or someone else's.
47
+
48
+ MEASURED (2026-10-27, `tests/e2e/rescore_attribution_check.py`): see
49
+ `docs/dev-notes/views.md`, "A name on words never said" — applied to the
50
+ saved WITH-arm draws of the holder-rule gate and then live on the 37-case
51
+ finding run.
52
+ """
53
+
54
+ from __future__ import annotations
55
+
56
+ import asyncio
57
+ import logging
58
+ from typing import Any, Literal, Sequence
59
+
60
+ from pydantic import BaseModel, Field
61
+
62
+ from dialectical_framework.agents.conversation_facilitator import \
63
+ ConversationFacilitator
64
+
65
+ logger = logging.getLogger(__name__)
66
+
67
+
68
+ class AttributionDto(BaseModel):
69
+ """One verdict; the safe value first (the mocked suite fills the first)."""
70
+
71
+ verdict: Literal["attributed", "not_attributed"] = Field(
72
+ description=(
73
+ "ATTRIBUTED: the text gives that party this position — stated in their "
74
+ "own words, reported as what they hold, argue, claim or did, or a fair "
75
+ "condensation of the stance the text gives them (an opponent of X holds "
76
+ "that X is wrong; a rhetorical question that takes a side takes it; a "
77
+ "role and the person in it, or a group and a member named as its voice, "
78
+ "are one party). Wording that goes a little beyond their words while "
79
+ "keeping their side is still theirs. NOT_ATTRIBUTED: the text gives that "
80
+ "party no stance on this at all, gives them the OPPOSITE side, or gives "
81
+ "this position to someone else; a party who is only described, who "
82
+ "disagrees with it, or who is merely asked about it does not hold it."
83
+ )
84
+ )
85
+ reasoning: str = Field(
86
+ description="One sentence: the words that attribute it, or that none do."
87
+ )
88
+
89
+
90
+ class AttributionCheck:
91
+ """Text-only, graph-free, no report: a check the view turn runs."""
92
+
93
+ async def score_texts(
94
+ self, *, position: str, held_by: str, text: str
95
+ ) -> AttributionDto:
96
+ prompt = f"""<text>
97
+ {text}
98
+ </text>
99
+
100
+ Does this text attribute the following position to {held_by.strip()} — stated in their own words, or reported as what they hold, argue, claim or did?
101
+
102
+ "{position.strip()}\""""
103
+ return await ConversationFacilitator().submit(
104
+ response_model=AttributionDto, user_content=prompt
105
+ )
106
+
107
+
108
+ #: The (pole, holder) attribute pairs a tension carries.
109
+ _POLES = (("thesis", "thesis_held_by"), ("antithesis", "antithesis_held_by"))
110
+
111
+
112
+ async def drop_unborne_holders(tensions: Sequence[Any], text: str) -> list[str]:
113
+ """Blank every named holder the text does not bear, IN PLACE, on every
114
+ tension given (the winner and the spares alike — a host serving a
115
+ runner-up must not get the misquotation back). Returns one line per
116
+ holder dropped, for the log. No call where no holder is named; a failed
117
+ judging keeps the holder (fail-soft, see the module)."""
118
+ words = (text or "").strip()
119
+ if not words:
120
+ return []
121
+ jobs: list[tuple[Any, str, str, str]] = []
122
+ for tension in tensions:
123
+ for pole, holder_attr in _POLES:
124
+ holder = (getattr(tension, holder_attr, "") or "").strip()
125
+ position = (getattr(tension, pole, "") or "").strip()
126
+ if holder and position:
127
+ jobs.append((tension, pole, holder_attr, holder))
128
+
129
+ async def one(job: tuple[Any, str, str, str]) -> bool:
130
+ tension, pole, _attr, holder = job
131
+ try:
132
+ dto = await AttributionCheck().score_texts(
133
+ position=getattr(tension, pole), held_by=holder, text=words
134
+ )
135
+ except Exception as exc: # noqa: BLE001 — not judged, the holder stays
136
+ logger.warning("Checking a pole's holder failed; it stays: %s", exc)
137
+ return False
138
+ if not isinstance(dto, AttributionDto):
139
+ return False
140
+ return dto.verdict == "not_attributed"
141
+
142
+ verdicts = await asyncio.gather(*(one(j) for j in jobs))
143
+ dropped: list[str] = []
144
+ for (tension, pole, holder_attr, holder), unborne in zip(jobs, verdicts):
145
+ if unborne:
146
+ setattr(tension, holder_attr, "")
147
+ dropped.append(f"{pole} held_by {holder!r}: the words do not bear it")
148
+ return dropped