krons 0.2.2__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. {krons-0.2.2 → krons-0.2.3}/.gitignore +2 -1
  2. krons-0.2.3/CLAUDE.md +183 -0
  3. {krons-0.2.2 → krons-0.2.3}/PKG-INFO +1 -1
  4. krons-0.2.3/examples/__init__.py +50 -0
  5. krons-0.2.3/examples/code_review_panel.py +452 -0
  6. krons-0.2.3/examples/codegen_pipeline.py +352 -0
  7. krons-0.2.3/examples/dynamic_response.py +171 -0
  8. krons-0.2.3/examples/event_sourcing.py +225 -0
  9. krons-0.2.3/examples/multi_agent_orchestration.py +429 -0
  10. krons-0.2.3/examples/pipeline_router.py +215 -0
  11. krons-0.2.3/examples/research_agent.py +425 -0
  12. krons-0.2.3/examples/tech_debate.py +496 -0
  13. krons-0.2.3/examples/validation_loop.py +304 -0
  14. {krons-0.2.2 → krons-0.2.3}/pyproject.toml +1 -1
  15. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/__init__.py +0 -4
  16. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/flow.py +7 -0
  17. {krons-0.2.2 → krons-0.2.3}/src/krons/session/session.py +54 -49
  18. krons-0.2.3/tests/session/test_session_logging.py +268 -0
  19. {krons-0.2.2 → krons-0.2.3}/uv.lock +1 -1
  20. krons-0.2.2/CLAUDE.md +0 -399
  21. krons-0.2.2/src/krons/core/base/log.py +0 -32
  22. krons-0.2.2/tests/core/test_log.py +0 -44
  23. krons-0.2.2/tests/session/test_session_logging.py +0 -337
  24. {krons-0.2.2 → krons-0.2.3}/.github/workflows/release.yml +0 -0
  25. {krons-0.2.2 → krons-0.2.3}/.python-version +0 -0
  26. {krons-0.2.2 → krons-0.2.3}/LICENSE +0 -0
  27. {krons-0.2.2 → krons-0.2.3}/README.md +0 -0
  28. {krons-0.2.2 → krons-0.2.3}/cookbooks/007_fan_out_in.py +0 -0
  29. {krons-0.2.2 → krons-0.2.3}/cookbooks/test_conduct.py +0 -0
  30. {krons-0.2.2 → krons-0.2.3}/src/krons/__init__.py +0 -0
  31. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/__init__.py +0 -0
  32. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/mcps/__init__.py +0 -0
  33. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/mcps/loader.py +0 -0
  34. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/mcps/wrapper.py +0 -0
  35. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/message/__init__.py +0 -0
  36. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/message/action.py +0 -0
  37. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/message/assistant.py +0 -0
  38. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/message/common.py +0 -0
  39. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/message/instruction.py +0 -0
  40. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/message/prepare_msg.py +0 -0
  41. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/message/role.py +0 -0
  42. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/message/system.py +0 -0
  43. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/__init__.py +0 -0
  44. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/act.py +0 -0
  45. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/generate.py +0 -0
  46. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/llm_reparse.py +0 -0
  47. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/operate.py +0 -0
  48. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/parse.py +0 -0
  49. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/react.py +0 -0
  50. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/specs.py +0 -0
  51. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/structure.py +0 -0
  52. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/operations/utils.py +0 -0
  53. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/providers/__init__.py +0 -0
  54. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/providers/anthropic_messages.py +0 -0
  55. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/providers/claude_code.py +0 -0
  56. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/providers/gemini.py +0 -0
  57. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/providers/match.py +0 -0
  58. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/providers/oai_chat.py +0 -0
  59. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/third_party/__init__.py +0 -0
  60. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/third_party/anthropic_models.py +0 -0
  61. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/third_party/claude_code.py +0 -0
  62. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/third_party/gemini_models.py +0 -0
  63. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/third_party/openai_models.py +0 -0
  64. {krons-0.2.2 → krons-0.2.3}/src/krons/agent/tool.py +0 -0
  65. {krons-0.2.2 → krons-0.2.3}/src/krons/core/__init__.py +0 -0
  66. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/broadcaster.py +0 -0
  67. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/element.py +0 -0
  68. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/event.py +0 -0
  69. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/eventbus.py +0 -0
  70. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/graph.py +0 -0
  71. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/node.py +0 -0
  72. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/pile.py +0 -0
  73. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/processor.py +0 -0
  74. {krons-0.2.2 → krons-0.2.3}/src/krons/core/base/progression.py +0 -0
  75. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/__init__.py +0 -0
  76. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/adapters/__init__.py +0 -0
  77. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/adapters/_utils.py +0 -0
  78. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/adapters/dataclass_field.py +0 -0
  79. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/adapters/factory.py +0 -0
  80. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/adapters/pydantic_adapter.py +0 -0
  81. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/adapters/sql_ddl.py +0 -0
  82. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/catalog/__init__.py +0 -0
  83. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/catalog/_audit.py +0 -0
  84. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/catalog/_common.py +0 -0
  85. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/catalog/_content.py +0 -0
  86. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/catalog/_enforcement.py +0 -0
  87. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/factory.py +0 -0
  88. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/operable.py +0 -0
  89. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/protocol.py +0 -0
  90. {krons-0.2.2 → krons-0.2.3}/src/krons/core/specs/spec.py +0 -0
  91. {krons-0.2.2 → krons-0.2.3}/src/krons/core/types/__init__.py +0 -0
  92. {krons-0.2.2 → krons-0.2.3}/src/krons/core/types/_sentinel.py +0 -0
  93. {krons-0.2.2 → krons-0.2.3}/src/krons/core/types/base.py +0 -0
  94. {krons-0.2.2 → krons-0.2.3}/src/krons/core/types/db_types.py +0 -0
  95. {krons-0.2.2 → krons-0.2.3}/src/krons/core/types/identity.py +0 -0
  96. {krons-0.2.2 → krons-0.2.3}/src/krons/errors.py +0 -0
  97. {krons-0.2.2 → krons-0.2.3}/src/krons/protocols.py +0 -0
  98. {krons-0.2.2 → krons-0.2.3}/src/krons/py.typed +0 -0
  99. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/__init__.py +0 -0
  100. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/backend.py +0 -0
  101. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/endpoint.py +0 -0
  102. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/hook.py +0 -0
  103. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/imodel.py +0 -0
  104. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/registry.py +0 -0
  105. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/utilities/__init__.py +0 -0
  106. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/utilities/header_factory.py +0 -0
  107. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/utilities/rate_limited_executor.py +0 -0
  108. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/utilities/rate_limiter.py +0 -0
  109. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/utilities/resilience.py +0 -0
  110. {krons-0.2.2 → krons-0.2.3}/src/krons/resource/utilities/token_calculator.py +0 -0
  111. {krons-0.2.2 → krons-0.2.3}/src/krons/session/__init__.py +0 -0
  112. {krons-0.2.2 → krons-0.2.3}/src/krons/session/constraints.py +0 -0
  113. {krons-0.2.2 → krons-0.2.3}/src/krons/session/exchange.py +0 -0
  114. {krons-0.2.2 → krons-0.2.3}/src/krons/session/message.py +0 -0
  115. {krons-0.2.2 → krons-0.2.3}/src/krons/session/registry.py +0 -0
  116. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/__init__.py +0 -0
  117. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/_function_arg_parser.py +0 -0
  118. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/_hash.py +0 -0
  119. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/_json_dump.py +0 -0
  120. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/_lazy_init.py +0 -0
  121. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/_pythonic_function_call.py +0 -0
  122. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/_to_list.py +0 -0
  123. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/_to_num.py +0 -0
  124. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/_utils.py +0 -0
  125. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/__init__.py +0 -0
  126. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_async_call.py +0 -0
  127. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_cancel.py +0 -0
  128. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_errors.py +0 -0
  129. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_patterns.py +0 -0
  130. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_primitives.py +0 -0
  131. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_priority_queue.py +0 -0
  132. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_resource_tracker.py +0 -0
  133. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_run_async.py +0 -0
  134. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_task.py +0 -0
  135. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/concurrency/_utils.py +0 -0
  136. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/display.py +0 -0
  137. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/fuzzy/__init__.py +0 -0
  138. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/fuzzy/_extract_json.py +0 -0
  139. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/fuzzy/_fuzzy_json.py +0 -0
  140. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/fuzzy/_fuzzy_match.py +0 -0
  141. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/fuzzy/_string_similarity.py +0 -0
  142. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/fuzzy/_to_dict.py +0 -0
  143. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/schemas/__init__.py +0 -0
  144. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/schemas/_breakdown_pydantic_annotation.py +0 -0
  145. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/schemas/_formatter.py +0 -0
  146. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/schemas/_minimal_yaml.py +0 -0
  147. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/schemas/_typescript.py +0 -0
  148. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/sql/__init__.py +0 -0
  149. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/sql/_sql_validation.py +0 -0
  150. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/validators/__init__.py +0 -0
  151. {krons-0.2.2 → krons-0.2.3}/src/krons/utils/validators/_validate_image_url.py +0 -0
  152. {krons-0.2.2 → krons-0.2.3}/src/krons/work/__init__.py +0 -0
  153. {krons-0.2.2 → krons-0.2.3}/src/krons/work/engine.py +0 -0
  154. {krons-0.2.2 → krons-0.2.3}/src/krons/work/form.py +0 -0
  155. {krons-0.2.2 → krons-0.2.3}/src/krons/work/operations/__init__.py +0 -0
  156. {krons-0.2.2 → krons-0.2.3}/src/krons/work/operations/builder.py +0 -0
  157. {krons-0.2.2 → krons-0.2.3}/src/krons/work/operations/context.py +0 -0
  158. {krons-0.2.2 → krons-0.2.3}/src/krons/work/operations/flow.py +0 -0
  159. {krons-0.2.2 → krons-0.2.3}/src/krons/work/operations/node.py +0 -0
  160. {krons-0.2.2 → krons-0.2.3}/src/krons/work/operations/registry.py +0 -0
  161. {krons-0.2.2 → krons-0.2.3}/src/krons/work/report.py +0 -0
  162. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/__init__.py +0 -0
  163. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/common/__init__.py +0 -0
  164. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/common/boolean.py +0 -0
  165. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/common/choice.py +0 -0
  166. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/common/mapping.py +0 -0
  167. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/common/model.py +0 -0
  168. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/common/number.py +0 -0
  169. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/common/string.py +0 -0
  170. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/registry.py +0 -0
  171. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/rule.py +0 -0
  172. {krons-0.2.2 → krons-0.2.3}/src/krons/work/rules/validator.py +0 -0
  173. {krons-0.2.2 → krons-0.2.3}/src/krons/work/worker.py +0 -0
  174. {krons-0.2.2 → krons-0.2.3}/tests/__init__.py +0 -0
  175. {krons-0.2.2 → krons-0.2.3}/tests/agent/__init__.py +0 -0
  176. {krons-0.2.2 → krons-0.2.3}/tests/agent/test_mcps_unit.py +0 -0
  177. {krons-0.2.2 → krons-0.2.3}/tests/agent/test_message.py +0 -0
  178. {krons-0.2.2 → krons-0.2.3}/tests/agent/test_providers_e2e.py +0 -0
  179. {krons-0.2.2 → krons-0.2.3}/tests/agent/test_providers_unit.py +0 -0
  180. {krons-0.2.2 → krons-0.2.3}/tests/agent/test_tool_unit.py +0 -0
  181. {krons-0.2.2 → krons-0.2.3}/tests/conftest.py +0 -0
  182. {krons-0.2.2 → krons-0.2.3}/tests/core/__init__.py +0 -0
  183. {krons-0.2.2 → krons-0.2.3}/tests/core/test_broadcaster.py +0 -0
  184. {krons-0.2.2 → krons-0.2.3}/tests/core/test_element.py +0 -0
  185. {krons-0.2.2 → krons-0.2.3}/tests/core/test_error_paths.py +0 -0
  186. {krons-0.2.2 → krons-0.2.3}/tests/core/test_event.py +0 -0
  187. {krons-0.2.2 → krons-0.2.3}/tests/core/test_event_status_race.py +0 -0
  188. {krons-0.2.2 → krons-0.2.3}/tests/core/test_eventbus.py +0 -0
  189. {krons-0.2.2 → krons-0.2.3}/tests/core/test_flow.py +0 -0
  190. {krons-0.2.2 → krons-0.2.3}/tests/core/test_flow_edge_cases.py +0 -0
  191. {krons-0.2.2 → krons-0.2.3}/tests/core/test_graph.py +0 -0
  192. {krons-0.2.2 → krons-0.2.3}/tests/core/test_graph_event_loop.py +0 -0
  193. {krons-0.2.2 → krons-0.2.3}/tests/core/test_node.py +0 -0
  194. {krons-0.2.2 → krons-0.2.3}/tests/core/test_pile.py +0 -0
  195. {krons-0.2.2 → krons-0.2.3}/tests/core/test_pile_edge_cases.py +0 -0
  196. {krons-0.2.2 → krons-0.2.3}/tests/core/test_processor.py +0 -0
  197. {krons-0.2.2 → krons-0.2.3}/tests/core/test_processor_security.py +0 -0
  198. {krons-0.2.2 → krons-0.2.3}/tests/core/test_progression.py +0 -0
  199. {krons-0.2.2 → krons-0.2.3}/tests/core/test_progression_edge_cases.py +0 -0
  200. {krons-0.2.2 → krons-0.2.3}/tests/core/test_serialization_roundtrip.py +0 -0
  201. {krons-0.2.2 → krons-0.2.3}/tests/core/test_thread_safety_stress.py +0 -0
  202. {krons-0.2.2 → krons-0.2.3}/tests/operations/__init__.py +0 -0
  203. {krons-0.2.2 → krons-0.2.3}/tests/operations/test_builder.py +0 -0
  204. {krons-0.2.2 → krons-0.2.3}/tests/operations/test_flow.py +0 -0
  205. {krons-0.2.2 → krons-0.2.3}/tests/operations/test_node.py +0 -0
  206. {krons-0.2.2 → krons-0.2.3}/tests/operations/test_op_flow.py +0 -0
  207. {krons-0.2.2 → krons-0.2.3}/tests/operations/test_op_node.py +0 -0
  208. {krons-0.2.2 → krons-0.2.3}/tests/operations/test_registry.py +0 -0
  209. {krons-0.2.2 → krons-0.2.3}/tests/resource/__init__.py +0 -0
  210. {krons-0.2.2 → krons-0.2.3}/tests/resource/test_backend.py +0 -0
  211. {krons-0.2.2 → krons-0.2.3}/tests/resource/test_endpoint.py +0 -0
  212. {krons-0.2.2 → krons-0.2.3}/tests/resource/test_hook.py +0 -0
  213. {krons-0.2.2 → krons-0.2.3}/tests/resource/test_imodel.py +0 -0
  214. {krons-0.2.2 → krons-0.2.3}/tests/resource/test_registry.py +0 -0
  215. {krons-0.2.2 → krons-0.2.3}/tests/resource/utilities/__init__.py +0 -0
  216. {krons-0.2.2 → krons-0.2.3}/tests/resource/utilities/test_header_factory.py +0 -0
  217. {krons-0.2.2 → krons-0.2.3}/tests/resource/utilities/test_rate_limiter.py +0 -0
  218. {krons-0.2.2 → krons-0.2.3}/tests/resource/utilities/test_resilience.py +0 -0
  219. {krons-0.2.2 → krons-0.2.3}/tests/resource/utilities/test_resilience_retry.py +0 -0
  220. {krons-0.2.2 → krons-0.2.3}/tests/rules/__init__.py +0 -0
  221. {krons-0.2.2 → krons-0.2.3}/tests/rules/test_base.py +0 -0
  222. {krons-0.2.2 → krons-0.2.3}/tests/rules/test_builtin.py +0 -0
  223. {krons-0.2.2 → krons-0.2.3}/tests/rules/test_registry.py +0 -0
  224. {krons-0.2.2 → krons-0.2.3}/tests/rules/test_rule_params_edge_cases.py +0 -0
  225. {krons-0.2.2 → krons-0.2.3}/tests/rules/test_rules_comprehensive.py +0 -0
  226. {krons-0.2.2 → krons-0.2.3}/tests/rules/test_validator.py +0 -0
  227. {krons-0.2.2 → krons-0.2.3}/tests/session/__init__.py +0 -0
  228. {krons-0.2.2 → krons-0.2.3}/tests/session/test_exchange.py +0 -0
  229. {krons-0.2.2 → krons-0.2.3}/tests/session/test_message.py +0 -0
  230. {krons-0.2.2 → krons-0.2.3}/tests/session/test_message_edge_cases.py +0 -0
  231. {krons-0.2.2 → krons-0.2.3}/tests/session/test_session.py +0 -0
  232. {krons-0.2.2 → krons-0.2.3}/tests/session/test_session_edge_cases.py +0 -0
  233. {krons-0.2.2 → krons-0.2.3}/tests/specs/__init__.py +0 -0
  234. {krons-0.2.2 → krons-0.2.3}/tests/specs/test_catalog.py +0 -0
  235. {krons-0.2.2 → krons-0.2.3}/tests/specs/test_factory.py +0 -0
  236. {krons-0.2.2 → krons-0.2.3}/tests/test_protocols.py +0 -0
  237. {krons-0.2.2 → krons-0.2.3}/tests/types/__init__.py +0 -0
  238. {krons-0.2.2 → krons-0.2.3}/tests/types/conftest.py +0 -0
  239. {krons-0.2.2 → krons-0.2.3}/tests/types/spec_adapters/__init__.py +0 -0
  240. {krons-0.2.2 → krons-0.2.3}/tests/types/spec_adapters/test_adapters_py311.py +0 -0
  241. {krons-0.2.2 → krons-0.2.3}/tests/types/spec_adapters/test_protocol.py +0 -0
  242. {krons-0.2.2 → krons-0.2.3}/tests/types/spec_adapters/test_pydantic_field.py +0 -0
  243. {krons-0.2.2 → krons-0.2.3}/tests/types/spec_adapters/test_sql_ddl_specs.py +0 -0
  244. {krons-0.2.2 → krons-0.2.3}/tests/types/test_db_types.py +0 -0
  245. {krons-0.2.2 → krons-0.2.3}/tests/types/test_identity.py +0 -0
  246. {krons-0.2.2 → krons-0.2.3}/tests/types/test_model.py +0 -0
  247. {krons-0.2.2 → krons-0.2.3}/tests/types/test_operable.py +0 -0
  248. {krons-0.2.2 → krons-0.2.3}/tests/types/test_sentinel.py +0 -0
  249. {krons-0.2.2 → krons-0.2.3}/tests/types/test_spec.py +0 -0
  250. {krons-0.2.2 → krons-0.2.3}/tests/types/test_types.py +0 -0
  251. {krons-0.2.2 → krons-0.2.3}/tests/types/test_types_py311.py +0 -0
  252. {krons-0.2.2 → krons-0.2.3}/tests/utils/__init__.py +0 -0
  253. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/__init__.py +0 -0
  254. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_async_call.py +0 -0
  255. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_cancel.py +0 -0
  256. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_errors.py +0 -0
  257. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_patterns.py +0 -0
  258. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_primitives.py +0 -0
  259. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_priority_queue.py +0 -0
  260. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_run_async.py +0 -0
  261. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_task.py +0 -0
  262. {krons-0.2.2 → krons-0.2.3}/tests/utils/concurrency/test_utils.py +0 -0
  263. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_extract_json.py +0 -0
  264. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_fuzzy_json.py +0 -0
  265. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_fuzzy_match.py +0 -0
  266. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_hash.py +0 -0
  267. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_json_dump.py +0 -0
  268. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_lazy_init.py +0 -0
  269. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_sql_validation.py +0 -0
  270. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_string_similarity.py +0 -0
  271. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_to_dict.py +0 -0
  272. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_to_list.py +0 -0
  273. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_to_num.py +0 -0
  274. {krons-0.2.2 → krons-0.2.3}/tests/utils/test_utils.py +0 -0
  275. {krons-0.2.2 → krons-0.2.3}/tests/work/__init__.py +0 -0
  276. {krons-0.2.2 → krons-0.2.3}/tests/work/test_engine.py +0 -0
  277. {krons-0.2.2 → krons-0.2.3}/tests/work/test_form.py +0 -0
  278. {krons-0.2.2 → krons-0.2.3}/tests/work/test_report.py +0 -0
  279. {krons-0.2.2 → krons-0.2.3}/tests/work/test_worker.py +0 -0
@@ -206,4 +206,5 @@ marimo/_static/
206
206
  marimo/_lsp/
207
207
  __marimo__/
208
208
 
209
- *.DS_Store
209
+ *.DS_Store
210
+ *.*_cache/
krons-0.2.3/CLAUDE.md ADDED
@@ -0,0 +1,183 @@
1
+ # krons - Spec-Based Composable Framework
2
+
3
+ ## Quick Reference
4
+
5
+ ```text
6
+ krons/
7
+ ├── core/ # Element, Node, Flow, Graph, Pile, Event
8
+ ├── session/ # Session, Branch, Message (conversation orchestration)
9
+ ├── resource/ # iModel, Endpoint, HookRegistry (API backends)
10
+ ├── agent/ # Operations pipeline (generate → parse → structure → operate → react)
11
+ ├── work/ # Report, Worker, Form (workflow orchestration)
12
+ └── utils/ # JSON, concurrency, display helpers
13
+ ```
14
+
15
+ ## Core Primitives
16
+
17
+ ### Element — Base identity class
18
+
19
+ ```python
20
+ from krons.core import Element
21
+
22
+ class MyEntity(Element):
23
+ name: str
24
+
25
+ e = MyEntity(name="test")
26
+ e.id # UUID (frozen)
27
+ e.created_at # datetime (frozen)
28
+ e.to_dict(mode="json") # Serialization with kron_class for polymorphic restore
29
+ Element.from_dict(data) # Polymorphic deserialization
30
+ ```
31
+
32
+ ### Pile — Thread-safe typed collection
33
+
34
+ ```python
35
+ from krons.core import Pile
36
+
37
+ pile = Pile[MyEntity](item_type={MyEntity})
38
+ pile.add(entity)
39
+ pile.get(uuid_or_str)
40
+ pile[0] # By index
41
+ pile[lambda x: x.name] # Filter
42
+ async with pile: # Async lock
43
+ ...
44
+ ```
45
+
46
+ ### Node — Polymorphic content container
47
+
48
+ ```python
49
+ from krons.core import Node, create_node
50
+
51
+ # Dynamic node
52
+ node = Node(content={"key": "value"})
53
+
54
+ # Typed node with DB features
55
+ MyNode = create_node("MyNode", content=MyContent, flatten_content=True)
56
+ ```
57
+
58
+ ## Session — Conversation Orchestration
59
+
60
+ ```python
61
+ from krons.session import Session, SessionConfig
62
+
63
+ session = Session(config=SessionConfig(
64
+ default_branch_name="main",
65
+ default_gen_model="gpt-4",
66
+ log_persist_dir="./logs", # Enable persistence (None = disabled)
67
+ log_auto_save_on_exit=True, # atexit handler
68
+ ))
69
+
70
+ # Register model
71
+ session.resources.register(imodel)
72
+
73
+ # Execute operations
74
+ op = await session.conduct("generate", params=GenerateParams(...))
75
+ op = await session.conduct("structure", params=StructureParams(...))
76
+
77
+ # Access messages
78
+ session.messages # Pile[Message]
79
+ session.dump() # Sync dump full session to JSON
80
+ await session.adump() # Async dump
81
+
82
+ # Restore from dump
83
+ restored = Session.from_dict(data) # Then re-register resources
84
+ ```
85
+
86
+ ## Resource — API Backends
87
+
88
+ ```python
89
+ from krons.resource import iModel, Endpoint, EndpointConfig
90
+
91
+ config = EndpointConfig(
92
+ name="gpt-4",
93
+ provider="openai",
94
+ endpoint="chat/completions",
95
+ base_url="https://api.openai.com/v1",
96
+ api_key="...",
97
+ )
98
+ model = iModel(backend=Endpoint(config=config))
99
+ calling = await model.invoke(messages=[...])
100
+ ```
101
+
102
+ ## Agent Operations
103
+
104
+ Pipeline: `generate → parse → structure → operate → react`
105
+
106
+ ```python
107
+ from krons.agent.operations import GenerateParams, StructureParams
108
+
109
+ # Generate raw LLM response
110
+ op = await session.conduct("generate", params=GenerateParams(
111
+ primary="Your prompt",
112
+ imodel="gpt-4",
113
+ ))
114
+
115
+ # Structured output with validation
116
+ op = await session.conduct("structure", params=StructureParams(
117
+ generate_params=GenerateParams(primary="...", request_model=MyModel),
118
+ validator=Validator(),
119
+ operable=Operable.from_structure(MyModel),
120
+ ))
121
+ ```
122
+
123
+ ## Work — Workflow Orchestration
124
+
125
+ ### Report (declarative workflow)
126
+
127
+ ```python
128
+ from krons.work import Report
129
+
130
+ class MyReport(Report):
131
+ result: str | None = None
132
+ assignment: str = "input -> result"
133
+ form_assignments: list[str] = [
134
+ "step1: input -> intermediate",
135
+ "step2: intermediate -> result",
136
+ ]
137
+ ```
138
+
139
+ ### Worker (execution capability)
140
+
141
+ ```python
142
+ from krons.work import Worker, work
143
+
144
+ class MyWorker(Worker):
145
+ name = "processor"
146
+
147
+ @work(assignment="input -> output")
148
+ async def process(self, input, **kwargs):
149
+ return transform(input)
150
+ ```
151
+
152
+ ## Protocols
153
+
154
+ ```python
155
+ from krons.protocols import implements, Serializable
156
+
157
+ @implements(Serializable, signature_check="error")
158
+ class MyClass:
159
+ def to_dict(self, **kwargs): ...
160
+ ```
161
+
162
+ ## Testing
163
+
164
+ ```bash
165
+ uv run pytest tests/ -q # All tests
166
+ uv run pytest tests/core/ -xvs # Core module verbose
167
+ uv run pytest -k "test_session" -v # Pattern match
168
+ ```
169
+
170
+ ```python
171
+ import pytest
172
+
173
+ @pytest.mark.anyio
174
+ async def test_async():
175
+ result = await some_call()
176
+ assert result
177
+ ```
178
+
179
+ ## Code Style
180
+
181
+ - Python 3.11+, Pydantic v2, anyio
182
+ - `uv run ruff check src/ --fix && uv run ruff format src/`
183
+ - Type hints required on public APIs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: krons
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Spec-based composable framework for building type-safe systems
5
5
  Project-URL: Homepage, https://github.com/khive-ai/krons
6
6
  Project-URL: Repository, https://github.com/khive-ai/krons
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """krons Usage Examples
5
+
6
+ This module provides practical, runnable examples demonstrating krons patterns
7
+ with Claude Code integration.
8
+
9
+ Multi-Agent Patterns (parallel Claude Code):
10
+ code_review_panel - 3 specialist reviewers + moderator synthesis
11
+ tech_debate - Adversarial debate with advocate/skeptic/pragmatist + judge
12
+
13
+ Worker Patterns (declarative workflows):
14
+ validation_loop - Self-correcting LLM generation with bounded retries
15
+ codegen_pipeline - Code generation with error recovery workflow
16
+ research_agent - Adaptive depth research with confidence-based routing
17
+
18
+ Exchange Patterns (async message passing):
19
+ pipeline_router - Multi-stage data processing pipeline
20
+ event_sourcing - Event persistence, snapshots, and replay
21
+
22
+ Specs Patterns (dynamic schema composition):
23
+ dynamic_response - Runtime Pydantic model generation with validators
24
+
25
+ Combined Patterns:
26
+ multi_agent_orchestration - Supervisor-worker coordination via Exchange + Worker
27
+
28
+ Run any example:
29
+ uv run python examples/code_review_panel.py
30
+ uv run python examples/tech_debate.py
31
+ uv run python examples/validation_loop.py
32
+ # etc.
33
+ """
34
+
35
+ __all__ = [
36
+ # Multi-agent patterns (featured)
37
+ "code_review_panel",
38
+ "tech_debate",
39
+ # Worker patterns
40
+ "validation_loop",
41
+ "codegen_pipeline",
42
+ "research_agent",
43
+ # Exchange patterns
44
+ "pipeline_router",
45
+ "event_sourcing",
46
+ # Specs patterns
47
+ "dynamic_response",
48
+ # Combined patterns
49
+ "multi_agent_orchestration",
50
+ ]
@@ -0,0 +1,452 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Multi-Agent Code Review Panel with Claude Code.
5
+
6
+ A panel of specialized reviewers analyze code from different perspectives,
7
+ then a moderator synthesizes their findings into actionable feedback.
8
+
9
+ Architecture:
10
+ Moderator (orchestrator)
11
+ └─> SecurityReviewer ─┐
12
+ └─> PerformanceReviewer ├─> Moderator synthesizes
13
+ └─> ArchitectureReviewer┘
14
+
15
+ Features:
16
+ - Structured outputs with Pydantic models
17
+ - Parallel fan-out to specialist agents
18
+ - Severity-scored findings
19
+ - Synthesized final verdict
20
+
21
+ Usage:
22
+ uv run python examples/code_review_panel.py
23
+ uv run python examples/code_review_panel.py --file path/to/code.py
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import sys
29
+ from enum import Enum
30
+
31
+ import anyio
32
+ from pydantic import BaseModel, Field
33
+
34
+ from krons.agent.operations import GenerateParams, ReturnAs
35
+ from krons.agent.providers.claude_code import (
36
+ ClaudeCodeEndpoint,
37
+ create_claude_code_config,
38
+ )
39
+ from krons.resource import iModel
40
+ from krons.session import Session, SessionConfig
41
+ from krons.utils.display import Timer, as_readable, display, phase, status
42
+ from krons.utils.fuzzy import extract_json, fuzzy_validate_mapping
43
+
44
+ CC_WORKSPACE = ".khive/examples/code_review_panel"
45
+ VERBOSE = True
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Structured Output Models
50
+ # ---------------------------------------------------------------------------
51
+
52
+
53
+ class Severity(str, Enum):
54
+ """Issue severity levels."""
55
+
56
+ CRITICAL = "critical"
57
+ HIGH = "high"
58
+ MEDIUM = "medium"
59
+ LOW = "low"
60
+ INFO = "info"
61
+
62
+
63
+ class ReviewFinding(BaseModel):
64
+ """A single finding from a reviewer."""
65
+
66
+ title: str = Field(description="Short title of the finding")
67
+ severity: Severity = Field(description="Severity level")
68
+ description: str = Field(description="Detailed description of the issue")
69
+ suggestion: str = Field(description="How to fix or improve")
70
+ line_hint: str | None = Field(
71
+ default=None, description="Relevant code location hint"
72
+ )
73
+
74
+
75
+ class SpecialistReview(BaseModel):
76
+ """Complete review from a specialist."""
77
+
78
+ perspective: str = Field(
79
+ description="The review perspective (security/performance/architecture)"
80
+ )
81
+ summary: str = Field(description="Executive summary of findings")
82
+ findings: list[ReviewFinding] = Field(description="List of specific findings")
83
+ score: int = Field(ge=0, le=100, description="Overall score 0-100")
84
+
85
+
86
+ class PanelVerdict(BaseModel):
87
+ """Final synthesized verdict from the panel."""
88
+
89
+ overall_assessment: str = Field(description="High-level assessment of code quality")
90
+ critical_issues: list[str] = Field(description="Must-fix issues before merge")
91
+ recommendations: list[str] = Field(description="Suggested improvements")
92
+ approval_status: str = Field(description="APPROVED, NEEDS_CHANGES, or REJECTED")
93
+ confidence: float = Field(ge=0, le=1, description="Panel confidence in verdict")
94
+
95
+
96
+ # ---------------------------------------------------------------------------
97
+ # Claude Code Factory
98
+ # ---------------------------------------------------------------------------
99
+
100
+
101
+ def create_cc(
102
+ name: str, subdir: str, system_prompt: str | None = None, **kwargs
103
+ ) -> iModel:
104
+ """Create a Claude Code iModel with optional system prompt."""
105
+ config = create_claude_code_config(name=name)
106
+ config.update({"ws": f"{CC_WORKSPACE}/{subdir}", "max_turns": 3, **kwargs})
107
+ if system_prompt:
108
+ config["system_prompt"] = system_prompt
109
+ endpoint = ClaudeCodeEndpoint(config=config)
110
+ return iModel(backend=endpoint)
111
+
112
+
113
+ # ---------------------------------------------------------------------------
114
+ # Reviewer Personas
115
+ # ---------------------------------------------------------------------------
116
+
117
+ SECURITY_PERSONA = """\
118
+ You are a senior security engineer conducting a security-focused code review.
119
+ Focus on: input validation, injection vulnerabilities, authentication/authorization,
120
+ secrets handling, cryptography usage, and OWASP Top 10 issues.
121
+ Be thorough but practical - prioritize real risks over theoretical concerns."""
122
+
123
+ PERFORMANCE_PERSONA = """\
124
+ You are a performance engineer reviewing code for efficiency.
125
+ Focus on: algorithmic complexity, memory usage, I/O patterns, caching opportunities,
126
+ unnecessary allocations, blocking operations, and scalability concerns.
127
+ Consider both micro-optimizations and architectural performance."""
128
+
129
+ ARCHITECTURE_PERSONA = """\
130
+ You are a software architect reviewing code structure and design.
131
+ Focus on: SOLID principles, separation of concerns, dependency management,
132
+ testability, maintainability, API design, and code organization.
133
+ Balance pragmatism with clean architecture principles."""
134
+
135
+
136
+ # ---------------------------------------------------------------------------
137
+ # Prompts
138
+ # ---------------------------------------------------------------------------
139
+
140
+
141
+ def make_review_prompt(code: str, perspective: str) -> str:
142
+ """Create a review prompt for a specific perspective."""
143
+ return f"""\
144
+ Review the following code from a {perspective} perspective.
145
+
146
+ ```python
147
+ {code}
148
+ ```
149
+
150
+ Provide your review as structured JSON matching this schema:
151
+ - perspective: "{perspective}"
152
+ - summary: Executive summary (2-3 sentences)
153
+ - findings: List of findings, each with:
154
+ - title: Short issue title
155
+ - severity: one of "critical", "high", "medium", "low", "info"
156
+ - description: What's wrong and why it matters
157
+ - suggestion: How to fix it
158
+ - line_hint: Approximate location (optional)
159
+ - score: Overall score 0-100 for this perspective
160
+
161
+ Be specific and actionable. Return valid JSON only."""
162
+
163
+
164
+ SYNTHESIS_PROMPT = """\
165
+ You are the panel moderator. Synthesize the specialist reviews into a final verdict.
166
+
167
+ Reviews from specialists:
168
+ {reviews}
169
+
170
+ Provide your verdict as structured JSON:
171
+ - overall_assessment: 2-3 sentence summary of code quality
172
+ - critical_issues: List of must-fix issues (empty if none)
173
+ - recommendations: List of suggested improvements
174
+ - approval_status: "APPROVED", "NEEDS_CHANGES", or "REJECTED"
175
+ - confidence: 0.0 to 1.0 confidence in this verdict
176
+
177
+ Consider all perspectives equally. Be decisive but fair. Return valid JSON only."""
178
+
179
+
180
+ # ---------------------------------------------------------------------------
181
+ # Parsing Helpers
182
+ # ---------------------------------------------------------------------------
183
+
184
+
185
+ def parse_review(text: str, perspective: str) -> SpecialistReview:
186
+ """Parse a specialist review from LLM output."""
187
+ extracted = extract_json(text, fuzzy_parse=True)
188
+ if not extracted:
189
+ # Fallback for unparseable responses
190
+ return SpecialistReview(
191
+ perspective=perspective,
192
+ summary="Review could not be parsed",
193
+ findings=[],
194
+ score=50,
195
+ )
196
+
197
+ block = extracted[0] if isinstance(extracted, list) else extracted
198
+ target_keys = list(SpecialistReview.model_fields.keys())
199
+ validated = fuzzy_validate_mapping(block, target_keys)
200
+ validated["perspective"] = perspective # Ensure correct perspective
201
+ return SpecialistReview.model_validate(validated)
202
+
203
+
204
+ def parse_verdict(text: str) -> PanelVerdict:
205
+ """Parse the final verdict from moderator."""
206
+ extracted = extract_json(text, fuzzy_parse=True)
207
+ if not extracted:
208
+ return PanelVerdict(
209
+ overall_assessment="Verdict could not be parsed",
210
+ critical_issues=[],
211
+ recommendations=[],
212
+ approval_status="NEEDS_CHANGES",
213
+ confidence=0.5,
214
+ )
215
+
216
+ block = extracted[0] if isinstance(extracted, list) else extracted
217
+ target_keys = list(PanelVerdict.model_fields.keys())
218
+ validated = fuzzy_validate_mapping(block, target_keys)
219
+ return PanelVerdict.model_validate(validated)
220
+
221
+
222
+ # ---------------------------------------------------------------------------
223
+ # Sample Code for Review
224
+ # ---------------------------------------------------------------------------
225
+
226
+ SAMPLE_CODE = """\
227
+ import sqlite3
228
+ import hashlib
229
+ from flask import Flask, request, jsonify
230
+
231
+ app = Flask(__name__)
232
+ db = sqlite3.connect("users.db", check_same_thread=False)
233
+
234
+ @app.route("/login", methods=["POST"])
235
+ def login():
236
+ username = request.form["username"]
237
+ password = request.form["password"]
238
+
239
+ # Hash password
240
+ pw_hash = hashlib.md5(password.encode()).hexdigest()
241
+
242
+ # Check credentials
243
+ cursor = db.cursor()
244
+ query = f"SELECT * FROM users WHERE username='{username}' AND password='{pw_hash}'"
245
+ result = cursor.execute(query).fetchone()
246
+
247
+ if result:
248
+ return jsonify({"status": "success", "user": result})
249
+ return jsonify({"status": "failed"})
250
+
251
+ @app.route("/users")
252
+ def get_users():
253
+ cursor = db.cursor()
254
+ users = cursor.execute("SELECT * FROM users").fetchall()
255
+ return jsonify(users)
256
+
257
+ def process_data(items):
258
+ result = []
259
+ for item in items:
260
+ for i in range(len(items)):
261
+ if items[i] == item:
262
+ result.append(item * 2)
263
+ return list(set(result))
264
+
265
+ if __name__ == "__main__":
266
+ app.run(debug=True, host="0.0.0.0")
267
+ """
268
+
269
+
270
+ # ---------------------------------------------------------------------------
271
+ # Main
272
+ # ---------------------------------------------------------------------------
273
+
274
+
275
+ async def main(code: str | None = None):
276
+ """Run the code review panel."""
277
+ code_to_review = code or SAMPLE_CODE
278
+
279
+ print("=" * 70)
280
+ print(" Multi-Agent Code Review Panel")
281
+ print("=" * 70)
282
+ print()
283
+
284
+ # --- Setup Session ---
285
+ phase("Setting up review panel")
286
+
287
+ moderator = create_cc("moderator", "moderator")
288
+ session = Session(
289
+ config=SessionConfig(
290
+ default_branch_name="panel",
291
+ default_gen_model="moderator",
292
+ shared_resources={"moderator"},
293
+ )
294
+ )
295
+ session.resources.register(moderator)
296
+
297
+ panel_branch = session.default_branch
298
+ status(f"Panel ready: {len(session.resources)} resources")
299
+
300
+ # --- Phase 1: Parallel Specialist Reviews ---
301
+ phase("Phase 1: Specialist Reviews (parallel)")
302
+
303
+ reviewers = [
304
+ ("security", SECURITY_PERSONA),
305
+ ("performance", PERFORMANCE_PERSONA),
306
+ ("architecture", ARCHITECTURE_PERSONA),
307
+ ]
308
+
309
+ reviews: dict[str, SpecialistReview] = {}
310
+
311
+ async def run_review(perspective: str, persona: str) -> None:
312
+ name = f"reviewer_{perspective}"
313
+ reviewer = create_cc(name, name, system_prompt=persona)
314
+ session.resources.register(reviewer, update=True)
315
+ branch = session.create_branch(name=name, resources={name})
316
+
317
+ prompt = make_review_prompt(code_to_review, perspective)
318
+
319
+ with Timer() as t:
320
+ op = await session.conduct(
321
+ "generate",
322
+ branch,
323
+ GenerateParams(
324
+ primary=prompt,
325
+ request_model=SpecialistReview,
326
+ imodel=name,
327
+ return_as=ReturnAs.TEXT,
328
+ ),
329
+ verbose=VERBOSE,
330
+ )
331
+
332
+ review = parse_review(op.execution.response, perspective)
333
+ reviews[perspective] = review
334
+
335
+ status(
336
+ f"[{perspective.upper()}] Score: {review.score}/100, "
337
+ f"Findings: {len(review.findings)} ({t.elapsed:.1f}s)",
338
+ style="success" if review.score >= 70 else "warning",
339
+ )
340
+
341
+ async with anyio.create_task_group() as tg:
342
+ for perspective, persona in reviewers:
343
+ tg.start_soon(run_review, perspective, persona)
344
+
345
+ # --- Display Individual Reviews ---
346
+ print()
347
+ for perspective, review in reviews.items():
348
+ print(f"┌─ {perspective.upper()} REVIEW (Score: {review.score}/100)")
349
+ print(f"│ {review.summary}")
350
+ for finding in review.findings[:3]: # Show top 3 findings
351
+ severity_icon = {
352
+ Severity.CRITICAL: "🔴",
353
+ Severity.HIGH: "🟠",
354
+ Severity.MEDIUM: "🟡",
355
+ Severity.LOW: "🟢",
356
+ Severity.INFO: "ℹ️",
357
+ }.get(finding.severity, "•")
358
+ print(f"│ {severity_icon} [{finding.severity.value}] {finding.title}")
359
+ if len(review.findings) > 3:
360
+ print(f"│ ... and {len(review.findings) - 3} more findings")
361
+ print("└" + "─" * 60)
362
+ print()
363
+
364
+ # --- Phase 2: Moderator Synthesis ---
365
+ phase("Phase 2: Panel Synthesis")
366
+
367
+ reviews_text = "\n\n".join(
368
+ f"=== {p.upper()} REVIEWER (Score: {r.score}/100) ===\n"
369
+ f"Summary: {r.summary}\n"
370
+ f"Findings:\n"
371
+ + "\n".join(
372
+ f"- [{f.severity.value}] {f.title}: {f.description}" for f in r.findings
373
+ )
374
+ for p, r in reviews.items()
375
+ )
376
+
377
+ synthesis_prompt = SYNTHESIS_PROMPT.format(reviews=reviews_text)
378
+
379
+ with Timer() as t:
380
+ synth_op = await session.conduct(
381
+ "generate",
382
+ panel_branch,
383
+ GenerateParams(
384
+ primary=synthesis_prompt,
385
+ request_model=PanelVerdict,
386
+ imodel="moderator",
387
+ return_as=ReturnAs.TEXT,
388
+ ),
389
+ verbose=VERBOSE,
390
+ )
391
+
392
+ verdict = parse_verdict(synth_op.execution.response)
393
+ status(f"Synthesis complete ({t.elapsed:.1f}s)")
394
+
395
+ # --- Final Verdict ---
396
+ print()
397
+ print("=" * 70)
398
+ print(" PANEL VERDICT")
399
+ print("=" * 70)
400
+ print()
401
+
402
+ status_icon = {
403
+ "APPROVED": "✅",
404
+ "NEEDS_CHANGES": "⚠️",
405
+ "REJECTED": "❌",
406
+ }.get(verdict.approval_status, "❓")
407
+
408
+ print(f"Status: {status_icon} {verdict.approval_status}")
409
+ print(f"Confidence: {verdict.confidence:.0%}")
410
+ print()
411
+ print("Assessment:")
412
+ print(f" {verdict.overall_assessment}")
413
+ print()
414
+
415
+ if verdict.critical_issues:
416
+ print("Critical Issues (must fix):")
417
+ for issue in verdict.critical_issues:
418
+ print(f" 🔴 {issue}")
419
+ print()
420
+
421
+ if verdict.recommendations:
422
+ print("Recommendations:")
423
+ for rec in verdict.recommendations:
424
+ print(f" 💡 {rec}")
425
+ print()
426
+
427
+ # --- Summary Stats ---
428
+ avg_score = sum(r.score for r in reviews.values()) / len(reviews)
429
+ total_findings = sum(len(r.findings) for r in reviews.values())
430
+ critical_count = sum(
431
+ 1
432
+ for r in reviews.values()
433
+ for f in r.findings
434
+ if f.severity == Severity.CRITICAL
435
+ )
436
+
437
+ print("─" * 70)
438
+ print(f"Average Score: {avg_score:.0f}/100")
439
+ print(f"Total Findings: {total_findings}")
440
+ print(f"Critical Issues: {critical_count}")
441
+ print("─" * 70)
442
+
443
+
444
+ if __name__ == "__main__":
445
+ code = None
446
+ if "--file" in sys.argv:
447
+ idx = sys.argv.index("--file")
448
+ if idx + 1 < len(sys.argv):
449
+ with open(sys.argv[idx + 1]) as f:
450
+ code = f.read()
451
+
452
+ anyio.run(main, code)