claude-code-conductor 2.23.0__tar.gz → 2.25.0__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 (195) hide show
  1. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/select_tier.py +54 -10
  2. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/session_stop.py +2 -0
  3. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/CHANGELOG.md +50 -0
  4. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/PKG-INFO +2 -1
  5. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/README.md +1 -0
  6. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/__init__.py +1 -1
  7. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli_tier.py +46 -8
  8. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/db.py +362 -7
  9. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_select_tier.py +310 -4
  10. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_session_stop.py +55 -0
  11. claude_code_conductor-2.25.0/tests/test_cli_tier.py +1037 -0
  12. claude_code_conductor-2.25.0/tests/test_db.py +1723 -0
  13. claude_code_conductor-2.23.0/tests/test_cli_tier.py +0 -507
  14. claude_code_conductor-2.23.0/tests/test_db.py +0 -664
  15. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/CLAUDE.md +0 -0
  16. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/architect.md +0 -0
  17. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/code-reviewer.md +0 -0
  18. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/developer.md +0 -0
  19. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/doc-writer.md +0 -0
  20. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/interviewer.md +0 -0
  21. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/planner.md +0 -0
  22. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/project-setup.md +0 -0
  23. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/security-reviewer.md +0 -0
  24. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/systematic-debugger.md +0 -0
  25. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/tester.md +0 -0
  26. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/wt_developer.md +0 -0
  27. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/wt_systematic-debugger.md +0 -0
  28. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/agents/wt_tester.md +0 -0
  29. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/breaking-changes.txt +0 -0
  30. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/deletions.txt +0 -0
  31. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/docs/config-policy.md +0 -0
  32. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/docs/parallel-agents-setup.md +0 -0
  33. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/docs/platform-adapters.md +0 -0
  34. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/docs/settings.json.md +0 -0
  35. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/_hook_utils.py +0 -0
  36. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/check_agent_invocation.py +0 -0
  37. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/consolidate_memory.py +0 -0
  38. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/permission_handler.py +0 -0
  39. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/permission_handler_toast.py +0 -0
  40. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/planner_check.py +0 -0
  41. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/post_tool.py +0 -0
  42. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/pre_compact.py +0 -0
  43. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/pre_tool.py +0 -0
  44. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/recall_inject.py +0 -0
  45. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/restore_session.py +0 -0
  46. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/session_start.py +0 -0
  47. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/session_utils.py +0 -0
  48. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/statusline.py +0 -0
  49. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/stop.py +0 -0
  50. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/hooks/worktree_guard.py +0 -0
  51. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/memory/.gitkeep +0 -0
  52. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/permission_rules.json +0 -0
  53. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/rules/promoted/index.md +0 -0
  54. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/settings.json +0 -0
  55. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/codex-review/SKILL.md +0 -0
  56. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/SKILL.md +0 -0
  57. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
  58. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
  59. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
  60. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
  61. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/scripts/record_tier_outcome.py +0 -0
  62. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
  63. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/develop/SKILL.md +0 -0
  64. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/doc/SKILL.md +0 -0
  65. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/extract-lib/SKILL.md +0 -0
  66. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/init-session/SKILL.md +0 -0
  67. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/mcp-config/SKILL.md +0 -0
  68. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
  69. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/pattern-status/SKILL.md +0 -0
  70. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
  71. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/recall/SKILL.md +0 -0
  72. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
  73. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
  74. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/review-phase/SKILL.md +0 -0
  75. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/setup/SKILL.md +0 -0
  76. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/setup/reference.md +0 -0
  77. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
  78. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
  79. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/skills/start/SKILL.md +0 -0
  80. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.claude/state/.gitkeep +0 -0
  81. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/.gitignore +0 -0
  82. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/LICENSE +0 -0
  83. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/LICENSES/chroma-hnswlib-LICENSE +0 -0
  84. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/LICENSES/chroma-hnswlib-NOTICE +0 -0
  85. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/LICENSES/fastembed-LICENSE +0 -0
  86. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/LICENSES/fastembed-NOTICE +0 -0
  87. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/LICENSES/onnxruntime-LICENSE +0 -0
  88. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
  89. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/hatch_build.py +0 -0
  90. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/pyproject.toml +0 -0
  91. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/__main__.py +0 -0
  92. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/_excludes.py +0 -0
  93. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/_terminal.py +0 -0
  94. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/adapters.py +0 -0
  95. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli.py +0 -0
  96. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli_ask.py +0 -0
  97. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli_doctor.py +0 -0
  98. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli_init.py +0 -0
  99. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli_list.py +0 -0
  100. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli_plan.py +0 -0
  101. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli_recall.py +0 -0
  102. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/cli_update.py +0 -0
  103. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/embedding.py +0 -0
  104. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/mcp_server.py +0 -0
  105. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/migrate.py +0 -0
  106. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/migrations/001_initial.sql +0 -0
  107. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
  108. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/migrations/003_tier_cost.sql +0 -0
  109. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/migrations/README.md +0 -0
  110. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/migrations/__init__.py +0 -0
  111. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/paths.py +0 -0
  112. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/plan_validator.py +0 -0
  113. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/platforms.py +0 -0
  114. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/pricing.py +0 -0
  115. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/question.py +0 -0
  116. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/recall_chunker.py +0 -0
  117. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/recall_index.py +0 -0
  118. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/src/c3/usage_ingester.py +0 -0
  119. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/__init__.py +0 -0
  120. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/conftest.py +0 -0
  121. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/fixtures/usage/README.md +0 -0
  122. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/fixtures/usage/mainline.jsonl +0 -0
  123. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
  124. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
  125. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/__init__.py +0 -0
  126. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_check_agent_invocation.py +0 -0
  127. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_consolidate_memory.py +0 -0
  128. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_hook_utils.py +0 -0
  129. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_permission_handler.py +0 -0
  130. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_permission_handler_toast.py +0 -0
  131. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
  132. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_planner_check.py +0 -0
  133. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_planner_check_dev.py +0 -0
  134. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_post_tool.py +0 -0
  135. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_pre_tool.py +0 -0
  136. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_recall_inject.py +0 -0
  137. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_record_review_decision.py +0 -0
  138. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_record_tier_outcome.py +0 -0
  139. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_restore_session.py +0 -0
  140. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_review_hint_inject.py +0 -0
  141. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_select_tier_escalation.py +0 -0
  142. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_session_start.py +0 -0
  143. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_session_utils.py +0 -0
  144. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
  145. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_similarity_boost.py +0 -0
  146. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_statusline.py +0 -0
  147. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_statusline_template_sync.py +0 -0
  148. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_sync_check.py +0 -0
  149. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/hooks/test_template_guard.py +0 -0
  150. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/__init__.py +0 -0
  151. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/_skill_helpers.py +0 -0
  152. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
  153. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_init_session_no_task_type.py +0 -0
  154. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_planner_lightweight.py +0 -0
  155. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_recall_skill.py +0 -0
  156. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
  157. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_setup_templates.py +0 -0
  158. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
  159. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_start_skill_new_flow.py +0 -0
  160. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
  161. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_adapters.py +0 -0
  162. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_cli_ask.py +0 -0
  163. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_cli_entry.py +0 -0
  164. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_cli_init.py +0 -0
  165. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_cli_list.py +0 -0
  166. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_cli_plan.py +0 -0
  167. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_cli_recall.py +0 -0
  168. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_cli_update_breaking_changes.py +0 -0
  169. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_cli_update_deletions.py +0 -0
  170. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_docstring_consistency.py +0 -0
  171. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_embedding.py +0 -0
  172. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_excludes.py +0 -0
  173. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_extract_breaking_changes.py +0 -0
  174. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_mcp_server_elicit.py +0 -0
  175. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_migrate.py +0 -0
  176. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_paths.py +0 -0
  177. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_plan_validator.py +0 -0
  178. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_pre_compact.py +0 -0
  179. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_pre_tool_hook.py +0 -0
  180. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_precompact_additional.py +0 -0
  181. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_precompact_toctou_fixes.py +0 -0
  182. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_pricing.py +0 -0
  183. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_recall_chunker.py +0 -0
  184. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_recall_index.py +0 -0
  185. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_references_migration.py +0 -0
  186. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_session_utils_additional.py +0 -0
  187. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_skill_no_builtin_conflict.py +0 -0
  188. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_statusline.py +0 -0
  189. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_stop_additional.py +0 -0
  190. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_stop_hook.py +0 -0
  191. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_stop_precompact_fixes.py +0 -0
  192. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_sync_template_stop.py +0 -0
  193. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_template_pre_tool_hook.py +0 -0
  194. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_usage_ingester.py +0 -0
  195. {claude_code_conductor-2.23.0 → claude_code_conductor-2.25.0}/tests/test_worktree_guard.py +0 -0
@@ -27,6 +27,7 @@ import collections
27
27
  import difflib
28
28
  import hashlib
29
29
  import json
30
+ import math
30
31
  import os
31
32
  import random
32
33
  import re
@@ -47,8 +48,10 @@ except AttributeError:
47
48
  try:
48
49
  from c3 import db as _c3_db_const # type: ignore[import-not-found]
49
50
  LEARNING_THRESHOLD: int = _c3_db_const.LEARNING_THRESHOLD
51
+ EPSILON: float = _c3_db_const.EPSILON_TIEBREAK
50
52
  except ImportError:
51
53
  LEARNING_THRESHOLD = 30
54
+ EPSILON = 0.05
52
55
 
53
56
  # 複雑度推定のキーワード
54
57
  SIMPLE_KEYWORDS = frozenset({
@@ -111,10 +114,9 @@ _PROMPT_HISTORY_SCAN_LINES = 1000
111
114
 
112
115
  TIERS: tuple[str, ...] = ("haiku", "sonnet", "opus")
113
116
 
114
- # cost-aware tie-break の拮抗判定閾値(v2.23.0)。
117
+ # cost-aware tie-break の拮抗判定閾値(v2.23.0・SSOT は db.EPSILON_TIEBREAK)。
115
118
  # Beta サンプルは 0〜1 スケール。成功率 5pt 以内=実質同等とみなす拮抗判定閾値。
116
- # 過大は成功率犠牲リスク、過小は無発動。調整可能化は v2.24.0
117
- EPSILON: float = 0.05
119
+ # 過大は成功率犠牲リスク、過小は無発動。C3_TIER_EPSILON env で上書き可(v2.25.0)。
118
120
 
119
121
 
120
122
  class SelectionResult(NamedTuple):
@@ -144,8 +146,8 @@ def _cost_tiebreak(
144
146
  Args:
145
147
  samples: {tier: beta_sample} の dict(Thompson Sampling 結果)。
146
148
  cost_map: {tier: cost} の dict。None なら cost を見ず従来挙動。
147
- cost は実測 avg_cost_usd または静的参照単価(ハイブリッド)。
148
- 混在スケール(USD vs per-MTok)の厳密化は v2.24.0
149
+ cost は実測 rate_usd_per_mtok または静的参照単価(ハイブリッド)。
150
+ v2.24.0 で rate 化(USD/MTok)により実測・静的とも同次元で整合済み。
149
151
  ``cost_map`` は None、または contenders 全件をキーとして含む dict を
150
152
  渡すこと。partial dict を渡すと ``cost_map[t]`` で KeyError が発生する。
151
153
  ``select_tier_detailed`` 経由では呼び出し側(main)が全 TIERS 分を
@@ -295,6 +297,7 @@ def select_tier_detailed(
295
297
  *,
296
298
  rng: random.Random | None = None,
297
299
  cost_map: dict[str, float] | None = None,
300
+ epsilon: float | None = None,
298
301
  ) -> SelectionResult:
299
302
  """Beta サンプリングまたは uniform 選択で推奨 Tier を SelectionResult で返す。
300
303
 
@@ -307,6 +310,7 @@ def select_tier_detailed(
307
310
  含む完全な dict」のいずれか。partial dict は渡されない前提
308
311
  (呼び出し側が全 TIERS 分を構築して保証する)。
309
312
  uniform 分岐では cost_map の有無に関わらず完全無視する(探索保護)。
313
+ epsilon: 拮抗判定閾値。None なら module 定数 EPSILON を使う(C3_TIER_EPSILON で上書き可)。
310
314
 
311
315
  Returns:
312
316
  SelectionResult(tier, mode, cost_tiebreak, contenders)。
@@ -323,7 +327,8 @@ def select_tier_detailed(
323
327
  tier: rng.betavariate(p[0], p[1])
324
328
  for tier, p in params.items()
325
329
  }
326
- chosen, did_tiebreak, contenders = _cost_tiebreak(samples, cost_map)
330
+ eff_epsilon = epsilon if epsilon is not None else EPSILON
331
+ chosen, did_tiebreak, contenders = _cost_tiebreak(samples, cost_map, epsilon=eff_epsilon)
327
332
  return SelectionResult(chosen, "thompson", did_tiebreak, contenders)
328
333
 
329
334
 
@@ -332,6 +337,7 @@ def select_tier(
332
337
  *,
333
338
  rng: random.Random | None = None,
334
339
  cost_map: dict[str, float] | None = None,
340
+ epsilon: float | None = None,
335
341
  ) -> tuple[str, str]:
336
342
  """Beta サンプリングまたは uniform 選択で推奨 Tier を返す。
337
343
 
@@ -343,13 +349,14 @@ def select_tier(
343
349
  None なら cost を見ず従来の Thompson Sampling と完全一致。
344
350
  uniform 分岐では cost_map の有無に関わらず完全無視する。
345
351
  詳細は :func:`select_tier_detailed` を参照。
352
+ epsilon: 拮抗判定閾値。None なら module 定数 EPSILON を使う。
346
353
 
347
354
  Returns:
348
355
  ``(tier, mode)`` のタプル。``mode`` は ``"thompson"`` / ``"uniform"`` で、
349
356
  プロンプトに「学習データ収集中」と表示するかの分岐に使う。
350
357
  戻り値型は v2.22.0 以前と完全に不変。
351
358
  """
352
- result = select_tier_detailed(params, rng=rng, cost_map=cost_map)
359
+ result = select_tier_detailed(params, rng=rng, cost_map=cost_map, epsilon=epsilon)
353
360
  return result.tier, result.mode
354
361
 
355
362
 
@@ -522,6 +529,41 @@ def _load_c3_db_module():
522
529
  return None
523
530
 
524
531
 
532
+ def _resolve_epsilon() -> float:
533
+ """``C3_TIER_EPSILON`` を安全に解決する。
534
+
535
+ 不正値(非数値 / 0 以下 / 1 超 / NaN)は受け付けず、stderr 警告 + デフォルト(EPSILON)に戻す。
536
+ 未設定 / 空文字は無警告でデフォルトを返す([SR-V-001])。
537
+ """
538
+ raw = os.environ.get("C3_TIER_EPSILON")
539
+ if raw is None or raw == "":
540
+ return EPSILON
541
+ try:
542
+ x = float(raw)
543
+ except ValueError:
544
+ print(
545
+ f"[select_tier:epsilon] invalid C3_TIER_EPSILON={raw!r}, "
546
+ f"using default {EPSILON}",
547
+ file=sys.stderr,
548
+ )
549
+ return EPSILON
550
+ if math.isnan(x):
551
+ print(
552
+ f"[select_tier:epsilon] C3_TIER_EPSILON={raw!r} is NaN, "
553
+ f"using default {EPSILON}",
554
+ file=sys.stderr,
555
+ )
556
+ return EPSILON
557
+ if x <= 0 or x > 1:
558
+ print(
559
+ f"[select_tier:epsilon] C3_TIER_EPSILON={x} out of range (0, 1], "
560
+ f"using default {EPSILON}",
561
+ file=sys.stderr,
562
+ )
563
+ return EPSILON
564
+ return x
565
+
566
+
525
567
  def main() -> int:
526
568
  try:
527
569
  payload = json.loads(sys.stdin.read())
@@ -556,14 +598,15 @@ def main() -> int:
556
598
  else:
557
599
  params = c3_db.read_tier_params(complexity)
558
600
 
559
- # v2.23.0: cost_map をハイブリッド解決(実測 avg_cost を主に、欠損 tier は静的単価で補完)。
601
+ # v2.24.0: cost_map をハイブリッド解決(実測 rate USD/MTok を主に、欠損 tier は静的単価で補完)。
602
+ # rate 化により measured(実測)と tier_reference_cost(静的)が同次元(USD/MTok)になり単位整合済み。
560
603
  # c3_db が None または pricing import 失敗時は cost_map=None で従来 Thompson にデグレード。
561
604
  # cost_map=None の場合、select_tier_detailed/_cost_tiebreak は従来 Thompson 挙動と完全一致(引数定義参照)。
562
605
  cost_map = None
563
606
  if c3_db is not None:
564
607
  try:
565
608
  from c3 import pricing # type: ignore[import-not-found]
566
- measured = c3_db.read_tier_cost_for_complexity(complexity) # {tier: avg} 実測>0 のみ
609
+ measured = c3_db.read_tier_cost_rate_for_complexity(complexity) # {tier: rate USD/MTok} 実測>0 のみ
567
610
  cost_map = {}
568
611
  for tier_name in TIERS:
569
612
  if tier_name in measured and measured[tier_name] > 0:
@@ -575,7 +618,8 @@ def main() -> int:
575
618
  except ImportError:
576
619
  cost_map = None
577
620
 
578
- result = select_tier_detailed(params, cost_map=cost_map)
621
+ eps = _resolve_epsilon()
622
+ result = select_tier_detailed(params, cost_map=cost_map, epsilon=eps)
579
623
  tier, mode = result.tier, result.mode
580
624
  cost_tiebreak = result.cost_tiebreak
581
625
 
@@ -103,6 +103,8 @@ def main() -> int:
103
103
  from c3.usage_ingester import ingest_session # noqa: PLC0415
104
104
  project_dir = _Path(transcript_path).parent
105
105
  ingest_session(session_id=session_id, project_dir=project_dir)
106
+ from c3.db import sync_tier_bandit_cost # noqa: PLC0415
107
+ sync_tier_bandit_cost()
106
108
  except Exception as e:
107
109
  print(f"[session_stop:usage_ingester] failed: {type(e).__name__}", file=sys.stderr)
108
110
 
@@ -1,5 +1,55 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.25.0] - 2026-05-26
4
+
5
+ **tier_bandit cost 蓄積・EPSILON 調整可能化・例外ログ統一**: v2.22.0 で列確保済みの `tier_bandit.total_cost_usd`/`cost_samples` へ実測値を materialize する同期関数を追加。cost-aware tie-break の拮抗判定閾値を定数 SSOT 化し環境変数で上書き可能にする。db.py 既存 6 関数の例外ログを型名統一(SR-R-001)。routing 挙動は不変。cost-weighted Thompson 本格統合は v2.26.0。
6
+
7
+ ### 機能追加
8
+
9
+ - **`src/c3/db.py`: `sync_tier_bandit_cost(*, db_path=None) -> None`(新規)**: `read_tier_cost_rate_summary` 由来の model 一致集計値を `tier_bandit` テーブルへ materialize する冪等な同期関数。「全クリア(total_cost_usd=0, cost_samples=0) → 集計 SET」の UPDATE-only 実装(INSERT なし)。session_stop の usage ingest 直後に実行。tier_bandit 行が存在しない複合キー (complexity, tier) は無視(INSERT は行わない)。
10
+
11
+ - **`.claude/hooks/select_tier.py`: `db.EPSILON_TIEBREAK`(0.05)定数 SSOT 化・環境変数 `C3_TIER_EPSILON` 対応**: 従来 `EPSILON=0.05` をモジュールローカルで持っていた値を `c3.db.EPSILON_TIEBREAK` に移し Single Source of Truth 化。`select_tier.py` は `db.EPSILON_TIEBREAK` を参照するよう変更。環境変数 `C3_TIER_EPSILON` に数値が設定されている場合は実行時にその値で上書き可能。NaN・範囲外(0 < x <= 1 の外)・非数値は default 値(0.05)に fallback。env 未設定時の routing 挙動は v2.24.0 と完全一致。
12
+
13
+ ### 変更
14
+
15
+ - **`src/c3/cli_tier.py`**: `c3 tier stats` の tier_bandit セクションに `total_cost_usd`(合計コスト USD)および `cost_samples`(計上セッション数)列を追加。値は `sync_tier_bandit_cost` 蓄積値を表示。データ未蓄積(0/0)時はセルを「-」表示。
16
+
17
+ - **`src/c3/db.py`: 例外ログ型名統一(SR-R-001)**: 既存 6 関数 7 箇所の `except Exception as exc` ログを `logger.warning("...: %s", exc)` から `logger.warning("...: %s", type(exc).__name__)` に統一。生 exc message(外部 path・SQL 文・行データ等)の意図しない流出を防止。関数シグネチャ・戻り値・呼び出し元への影響なし。
18
+
19
+ ### 後方互換
20
+
21
+ - 既存関数シグネチャ不変(`read_tier_params`・`read_tier_cost_rate_summary` 等)。
22
+ - `select_tier_detailed`/`select_tier` の `epsilon` は optional kwarg(省略時 `db.EPSILON_TIEBREAK` を参照)。
23
+ - migration 不要(`tier_bandit` の cost 列は v2.22.0 の 003 migration で確保済み)。
24
+ - **破壊的変更なし**。
25
+
26
+ ## [2.24.0] - 2026-05-25
27
+
28
+ **tier-routing cost 精度向上**: tie-break が使う cost データを model 一致集計・USD/MTok レート化により信頼できるものにする。新関数 2 つを追加し、`select_tier` の cost_map 源を rate 関数へ切替。既存関数・tie-break ロジックは完全不変。
29
+
30
+ **スコープ注記**:
31
+ - `tier_bandit.total_cost_usd`/`cost_samples` への書き込み・cost-weighted Thompson 本格統合は **v2.25.0+**。
32
+ - migration なし。**破壊的変更なし**(新関数追加・cost_map 値の意味変更のみ・既存関数 docstring 含め完全不変)。
33
+
34
+ ### 機能追加
35
+
36
+ - **`src/c3/db.py`: `read_tier_cost_rate_summary(*, db_path=None) -> list[dict]`(新規)**: model 一致集計・(session, tier) 重複排除・USD/MTok レート化を行う精度向上版の cost 集計関数。`agent_cost_runs` を `agent_type <> 'mainline'` で読み、Python 側で `pricing.resolve_tier(model)` により tier 振り分け。`(session_id, tier)` 粒度で集約後、`tier_recent_outcomes` と突合して `(complexity, tier)` 別に集計。`rate_usd_per_mtok = total_cost_usd / (billable_tokens / 1_000_000)`(`billable_tokens = input_tokens + output_tokens`)で `tier_reference_cost`(USD/MTok)と同次元にする。`billable_tokens == 0` の (complexity,tier) は除外。未知 model 行はスキップ。戻り値 dict キー: `complexity / tier / sessions / total_cost_usd / billable_tokens / rate_usd_per_mtok`。内部畳み込み部は DB 非依存の純関数 `_compute_tier_cost_rate_summary` に分離。
37
+
38
+ - **`src/c3/db.py`: `read_tier_cost_rate_for_complexity(complexity, *, db_path=None) -> dict[str, float]`(新規)**: `read_tier_cost_rate_summary` を complexity 一致 & `rate_usd_per_mtok > 0` でフィルタし `{tier: rate_usd_per_mtok}` を返す薄いラッパー(v2.23.0 の `read_tier_cost_for_complexity` と対称)。データ/DB 不在で `{}`。
39
+
40
+ ### 変更
41
+
42
+ - **`.claude/hooks/select_tier.py`**: `main()` の cost_map 構築で `read_tier_cost_for_complexity` → `read_tier_cost_rate_for_complexity` に切替。tie-break ロジック不変・cost_map の値の意味が絶対 USD → USD/MTok レートに変化し、静的 `tier_reference_cost` と単位整合。コメント更新(「混在スケール・厳密化は v2.24.0」→「v2.24.0 で rate 化により整合済み」)。
43
+
44
+ - **`src/c3/cli_tier.py`**: `c3 tier stats` に「Tier 別 USD/MTok レート(model 一致・tie-break が使用)」セクションを追加(complexity/tier/sessions/rate_usd_per_mtok 表示・データなしで「(rate データ未収集)」)。既存 session 合計 USD セクションも維持。「精度向上は v2.24.0」注記を「(粗い概算・session 合計 USD)」に更新。`--json` 出力に `tier_cost_rate` キー自動反映。DB 由来テキストへ `sanitize_terminal_text` 適用。
45
+
46
+ - **`src/c3/__init__.py`**: `__version__` を `"2.23.0"` から `"2.24.0"` に更新。
47
+
48
+ ### 後方互換
49
+
50
+ - 既存 `read_tier_cost_summary` / `read_tier_cost_for_complexity` は docstring 含め完全不変。
51
+ - migration なし(`agent_cost_runs` の token 4 列 + model 列は v2.21.0 (002 migration) で既存)。
52
+
3
53
  ## [2.23.0] - 2026-05-25
4
54
 
5
55
  **tier-routing cost-aware tie-break**: `select_tier` の Thompson Sampling 分岐に「拮抗 tier 群内コスト tie-break」を追加する。サンプル最大から ε(=0.05) 以内の拮抗 tier が複数ある場合のみ、min-max 正規化コストが最安の tier を選ぶ。単独最大なら従来通り(挙動不変)。成功率を犠牲にしない最小スコープの cost 統合。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-conductor
3
- Version: 2.23.0
3
+ Version: 2.25.0
4
4
  Summary: Multi-agent orchestration framework for Claude Code with Codex/Cursor adapters (C3)
5
5
  Project-URL: Homepage, https://github.com/satoh-y-0323/claude-code-conductor
6
6
  Project-URL: Repository, https://github.com/satoh-y-0323/claude-code-conductor
@@ -201,6 +201,7 @@ C3 のスラッシュコマンドはすべてスキル(`skills/{name}/SKILL.md
201
201
  | `c3 recall search "<query>"` または `c3 recall "<query>"` | `.claude/memory/sessions/` 等から類似チャンクを意味検索 |
202
202
  | `c3 recall rebuild [--force]` | HNSW インデックスを再構築(初回は fastembed が ~220MB のモデルを取得) |
203
203
  | `c3 recall stats` | チャンク数・モデル名・最終 rebuild 日時を表示 |
204
+ | `c3 tier stats` | tier-routing(複雑度に応じた Tier 自動ルーティング)の学習データ・Tier 別コストを表形式で表示(`--json` で機械可読出力・`--recent N` で直近 outcome 件数指定) |
204
205
 
205
206
  ### 基本的な使い方
206
207
 
@@ -154,6 +154,7 @@ C3 のスラッシュコマンドはすべてスキル(`skills/{name}/SKILL.md
154
154
  | `c3 recall search "<query>"` または `c3 recall "<query>"` | `.claude/memory/sessions/` 等から類似チャンクを意味検索 |
155
155
  | `c3 recall rebuild [--force]` | HNSW インデックスを再構築(初回は fastembed が ~220MB のモデルを取得) |
156
156
  | `c3 recall stats` | チャンク数・モデル名・最終 rebuild 日時を表示 |
157
+ | `c3 tier stats` | tier-routing(複雑度に応じた Tier 自動ルーティング)の学習データ・Tier 別コストを表形式で表示(`--json` で機械可読出力・`--recent N` で直近 outcome 件数指定) |
157
158
 
158
159
  ### 基本的な使い方
159
160
 
@@ -1,3 +1,3 @@
1
1
  """Claude Code Conductor (C3) - multi-agent orchestration framework for Claude Code."""
2
2
 
3
- __version__ = "2.23.0"
3
+ __version__ = "2.25.0"
@@ -22,6 +22,7 @@ import sys
22
22
  from typing import Any
23
23
 
24
24
  from c3 import db as c3_db
25
+ from c3._terminal import sanitize_terminal_text
25
26
 
26
27
 
27
28
  logger = logging.getLogger(__name__)
@@ -93,6 +94,11 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
93
94
  bandit_rows: list[dict[str, Any]] = []
94
95
  total_trials = 0
95
96
 
97
+ # cost 列は別 SELECT(read_tier_params は alpha/beta/trials 専用を維持)
98
+ bandit_cost: dict[tuple[str, str], tuple[float, int]] = c3_db.read_tier_bandit_cost(
99
+ db_path=db_path,
100
+ )
101
+
96
102
  for complexity in _COMPLEXITIES:
97
103
  params = c3_db.read_tier_params(complexity, db_path=db_path)
98
104
  for tier in _TIERS:
@@ -100,6 +106,7 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
100
106
  total_trials += trials
101
107
  denom = alpha + beta
102
108
  expected = alpha / denom if denom > 0 else 0.5
109
+ cost_usd, cost_samples = bandit_cost.get((complexity, tier), (0.0, 0))
103
110
  bandit_rows.append({
104
111
  "complexity": complexity,
105
112
  "tier": tier,
@@ -107,6 +114,8 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
107
114
  "beta": beta,
108
115
  "trials": trials,
109
116
  "expected_success_rate": expected,
117
+ "total_cost_usd": cost_usd,
118
+ "cost_samples": cost_samples,
110
119
  })
111
120
 
112
121
  recent_outcomes: list[dict[str, Any]] = c3_db.read_recent_outcomes(
@@ -118,6 +127,8 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
118
127
 
119
128
  tier_cost: list[dict[str, Any]] = c3_db.read_tier_cost_summary(db_path=db_path)
120
129
 
130
+ tier_cost_rate: list[dict[str, Any]] = c3_db.read_tier_cost_rate_summary(db_path=db_path)
131
+
121
132
  if total_trials < _LEARNING_THRESHOLD:
122
133
  mode = "uniform"
123
134
  else:
@@ -133,6 +144,7 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
133
144
  "recent_outcomes": recent_outcomes,
134
145
  "agent_cost": agent_cost,
135
146
  "tier_cost": tier_cost,
147
+ "tier_cost_rate": tier_cost_rate,
136
148
  }
137
149
 
138
150
 
@@ -150,12 +162,18 @@ def _render_human(snapshot: dict[str, Any]) -> None:
150
162
  print()
151
163
 
152
164
  print("== Tier 別累積(tier_bandit) ==")
153
- print(f"{'complexity':<12} {'tier':<8} {'trials':>6} {'alpha':>5} {'beta':>5} {'期待成功率':>10}")
165
+ print(
166
+ f"{'complexity':<12} {'tier':<8} {'trials':>6} {'alpha':>5} {'beta':>5} "
167
+ f"{'期待成功率':>10} {'cost_usd':>10} {'cost_samples':>12}"
168
+ )
154
169
  for row in snapshot["tier_bandit"]:
170
+ complexity_safe = sanitize_terminal_text(str(row["complexity"]))
171
+ tier_safe = sanitize_terminal_text(str(row["tier"]))
155
172
  print(
156
- f"{row['complexity']:<12} {row['tier']:<8} "
173
+ f"{complexity_safe:<12} {tier_safe:<8} "
157
174
  f"{row['trials']:>6} {row['alpha']:>5.2f} {row['beta']:>5.2f} "
158
- f"{row['expected_success_rate'] * 100:>9.2f}%"
175
+ f"{row['expected_success_rate'] * 100:>9.2f}% "
176
+ f"${row['total_cost_usd']:>9.4f} {row['cost_samples']:>12}"
159
177
  )
160
178
  print()
161
179
 
@@ -184,9 +202,10 @@ def _render_human(snapshot: dict[str, Any]) -> None:
184
202
  f"{'in_tok':>9} {'out_tok':>9} {'cache_r':>9} {'cache_w':>9}"
185
203
  )
186
204
  for row in agent_cost:
187
- note = " (マクロ集計・tier 学習対象外)" if row["agent_type"] == "mainline" else ""
205
+ agent_type_safe = sanitize_terminal_text(str(row["agent_type"]))
206
+ note = " (マクロ集計・tier 学習対象外)" if agent_type_safe == "mainline" else ""
188
207
  print(
189
- f"{row['agent_type']:<16} {row['runs']:>5} "
208
+ f"{agent_type_safe:<16} {row['runs']:>5} "
190
209
  f"${row['total_cost_usd']:>9.4f} "
191
210
  f"{row['input_tokens']:>9} {row['output_tokens']:>9} "
192
211
  f"{row['cache_read_tokens']:>9} {row['cache_create_tokens']:>9}"
@@ -194,18 +213,37 @@ def _render_human(snapshot: dict[str, Any]) -> None:
194
213
  )
195
214
  print()
196
215
 
197
- print("== Tier 別平均コスト(粗い概算 / 精度向上は v2.24.0) ==")
216
+ print("== Tier 別平均コスト(粗い概算・session 合計 USD) ==")
198
217
  tier_cost = snapshot.get("tier_cost", [])
199
218
  if not tier_cost:
200
219
  print("(cost 紐づけデータ未収集)")
201
220
  else:
202
221
  print(f"{'complexity':<12} {'tier':<8} {'sessions':>8} {'avg_usd':>10} {'total_usd':>10}")
203
222
  for row in tier_cost:
223
+ complexity_safe = sanitize_terminal_text(str(row["complexity"]))
224
+ tier_safe = sanitize_terminal_text(str(row["tier"]))
204
225
  print(
205
- f"{row['complexity']:<12} {row['tier']:<8} "
226
+ f"{complexity_safe:<12} {tier_safe:<8} "
206
227
  f"{row['sessions']:>8} "
207
228
  f"${row['avg_cost_usd']:>9.4f} "
208
229
  f"${row['total_cost_usd']:>9.4f}"
209
230
  )
210
231
  print()
211
- print("(注: cost-aware routing 本体(tie-break)実装済み。精度向上は v2.24.0 予定)")
232
+ print("(注: 粗い概算・session 合計 USD。v2.24.0 より rate セクション追加済み)")
233
+ print()
234
+
235
+ print("== Tier 別 USD/MTok レート(model 一致・tie-break が使用) ==")
236
+ tier_cost_rate = snapshot.get("tier_cost_rate", [])
237
+ if not tier_cost_rate:
238
+ print("(rate データ未収集)")
239
+ else:
240
+ print(f"{'complexity':<12} {'tier':<8} {'sessions':>8} {'rate_usd_per_mtok':>18}")
241
+ for row in tier_cost_rate:
242
+ complexity_safe = sanitize_terminal_text(str(row["complexity"]))
243
+ tier_safe = sanitize_terminal_text(str(row["tier"]))
244
+ print(
245
+ f"{complexity_safe:<12} {tier_safe:<8} "
246
+ f"{row['sessions']:>8} "
247
+ f"{row['rate_usd_per_mtok']:>18.4f}"
248
+ )
249
+ print()