claude-code-conductor 2.29.1__tar.gz → 2.29.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 (196) hide show
  1. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/CHANGELOG.md +20 -0
  2. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/PKG-INFO +1 -1
  3. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/__init__.py +1 -1
  4. claude_code_conductor-2.29.2/src/c3/_db_params.py +143 -0
  5. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/db.py +47 -140
  6. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/usage_ingester.py +37 -29
  7. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_db.py +113 -1
  8. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_usage_ingester.py +53 -7
  9. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/CLAUDE.md +0 -0
  10. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/architect.md +0 -0
  11. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/code-reviewer.md +0 -0
  12. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/developer.md +0 -0
  13. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/doc-writer.md +0 -0
  14. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/interviewer.md +0 -0
  15. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/planner.md +0 -0
  16. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/project-setup.md +0 -0
  17. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/security-reviewer.md +0 -0
  18. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/systematic-debugger.md +0 -0
  19. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/tester.md +0 -0
  20. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/wt_developer.md +0 -0
  21. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/wt_systematic-debugger.md +0 -0
  22. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/agents/wt_tester.md +0 -0
  23. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/breaking-changes.txt +0 -0
  24. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/deletions.txt +0 -0
  25. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/docs/config-policy.md +0 -0
  26. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/docs/parallel-agents-setup.md +0 -0
  27. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/docs/platform-adapters.md +0 -0
  28. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/docs/settings.json.md +0 -0
  29. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/_hook_utils.py +0 -0
  30. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/check_agent_invocation.py +0 -0
  31. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/consolidate_memory.py +0 -0
  32. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/permission_handler.py +0 -0
  33. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/permission_handler_toast.py +0 -0
  34. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/planner_check.py +0 -0
  35. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/post_tool.py +0 -0
  36. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/pre_compact.py +0 -0
  37. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/pre_tool.py +0 -0
  38. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/recall_inject.py +0 -0
  39. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/restore_session.py +0 -0
  40. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/select_tier.py +0 -0
  41. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/session_start.py +0 -0
  42. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/session_stop.py +0 -0
  43. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/session_utils.py +0 -0
  44. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/statusline.py +0 -0
  45. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/stop.py +0 -0
  46. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/hooks/worktree_guard.py +0 -0
  47. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/memory/.gitkeep +0 -0
  48. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/permission_rules.json +0 -0
  49. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/rules/promoted/index.md +0 -0
  50. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/settings.json +0 -0
  51. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/brainstorm/SKILL.md +0 -0
  52. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/codex-review/SKILL.md +0 -0
  53. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/dev-workflow/SKILL.md +0 -0
  54. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
  55. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
  56. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
  57. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
  58. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/dev-workflow/scripts/record_tier_outcome.py +0 -0
  59. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
  60. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/develop/SKILL.md +0 -0
  61. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/doc/SKILL.md +0 -0
  62. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/extract-lib/SKILL.md +0 -0
  63. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/init-session/SKILL.md +0 -0
  64. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/mcp-config/SKILL.md +0 -0
  65. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/parallel-agents/SKILL.md +0 -0
  66. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/pattern-status/SKILL.md +0 -0
  67. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/promote-pattern/SKILL.md +0 -0
  68. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/recall/SKILL.md +0 -0
  69. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/report-timestamp/SKILL.md +0 -0
  70. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
  71. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/review-phase/SKILL.md +0 -0
  72. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/setup/SKILL.md +0 -0
  73. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/setup/reference.md +0 -0
  74. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
  75. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
  76. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/skills/start/SKILL.md +0 -0
  77. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.claude/state/.gitkeep +0 -0
  78. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/.gitignore +0 -0
  79. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/LICENSE +0 -0
  80. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/LICENSES/chroma-hnswlib-LICENSE +0 -0
  81. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/LICENSES/chroma-hnswlib-NOTICE +0 -0
  82. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/LICENSES/fastembed-LICENSE +0 -0
  83. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/LICENSES/fastembed-NOTICE +0 -0
  84. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/LICENSES/onnxruntime-LICENSE +0 -0
  85. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
  86. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/README.md +0 -0
  87. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/hatch_build.py +0 -0
  88. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/pyproject.toml +0 -0
  89. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/__main__.py +0 -0
  90. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/_excludes.py +0 -0
  91. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/_terminal.py +0 -0
  92. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/adapters.py +0 -0
  93. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli.py +0 -0
  94. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli_ask.py +0 -0
  95. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli_doctor.py +0 -0
  96. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli_init.py +0 -0
  97. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli_list.py +0 -0
  98. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli_plan.py +0 -0
  99. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli_recall.py +0 -0
  100. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli_tier.py +0 -0
  101. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/cli_update.py +0 -0
  102. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/embedding.py +0 -0
  103. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/mcp_server.py +0 -0
  104. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/migrate.py +0 -0
  105. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/migrations/001_initial.sql +0 -0
  106. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
  107. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/migrations/003_tier_cost.sql +0 -0
  108. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/migrations/README.md +0 -0
  109. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/migrations/__init__.py +0 -0
  110. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/paths.py +0 -0
  111. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/plan_validator.py +0 -0
  112. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/platforms.py +0 -0
  113. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/pricing.py +0 -0
  114. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/question.py +0 -0
  115. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/recall_chunker.py +0 -0
  116. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/src/c3/recall_index.py +0 -0
  117. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/__init__.py +0 -0
  118. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/conftest.py +0 -0
  119. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/fixtures/usage/README.md +0 -0
  120. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/fixtures/usage/mainline.jsonl +0 -0
  121. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
  122. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
  123. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/__init__.py +0 -0
  124. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_check_agent_invocation.py +0 -0
  125. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_consolidate_memory.py +0 -0
  126. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_hook_utils.py +0 -0
  127. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_permission_handler.py +0 -0
  128. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_permission_handler_toast.py +0 -0
  129. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
  130. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_planner_check.py +0 -0
  131. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_planner_check_dev.py +0 -0
  132. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_post_tool.py +0 -0
  133. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_pre_tool.py +0 -0
  134. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_recall_inject.py +0 -0
  135. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_record_review_decision.py +0 -0
  136. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_record_tier_outcome.py +0 -0
  137. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_restore_session.py +0 -0
  138. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_review_hint_inject.py +0 -0
  139. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_select_tier.py +0 -0
  140. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_select_tier_escalation.py +0 -0
  141. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_session_start.py +0 -0
  142. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_session_stop.py +0 -0
  143. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_session_utils.py +0 -0
  144. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
  145. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_similarity_boost.py +0 -0
  146. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_statusline.py +0 -0
  147. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_statusline_template_sync.py +0 -0
  148. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_sync_check.py +0 -0
  149. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/hooks/test_template_guard.py +0 -0
  150. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/__init__.py +0 -0
  151. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/_skill_helpers.py +0 -0
  152. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
  153. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_init_session_no_task_type.py +0 -0
  154. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_planner_lightweight.py +0 -0
  155. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_recall_skill.py +0 -0
  156. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_session_backlog_reconciliation.py +0 -0
  157. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_setup_templates.py +0 -0
  158. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
  159. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_start_skill_new_flow.py +0 -0
  160. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
  161. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_adapters.py +0 -0
  162. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_check_deletions.py +0 -0
  163. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_ask.py +0 -0
  164. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_entry.py +0 -0
  165. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_init.py +0 -0
  166. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_list.py +0 -0
  167. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_plan.py +0 -0
  168. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_recall.py +0 -0
  169. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_tier.py +0 -0
  170. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_update_breaking_changes.py +0 -0
  171. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_cli_update_deletions.py +0 -0
  172. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_docstring_consistency.py +0 -0
  173. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_embedding.py +0 -0
  174. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_excludes.py +0 -0
  175. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_extract_breaking_changes.py +0 -0
  176. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_mcp_server_elicit.py +0 -0
  177. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_migrate.py +0 -0
  178. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_paths.py +0 -0
  179. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_plan_validator.py +0 -0
  180. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_pre_compact.py +0 -0
  181. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_pre_tool_hook.py +0 -0
  182. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_precompact_additional.py +0 -0
  183. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_precompact_toctou_fixes.py +0 -0
  184. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_pricing.py +0 -0
  185. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_recall_chunker.py +0 -0
  186. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_recall_index.py +0 -0
  187. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_references_migration.py +0 -0
  188. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_session_utils_additional.py +0 -0
  189. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_skill_no_builtin_conflict.py +0 -0
  190. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_statusline.py +0 -0
  191. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_stop_additional.py +0 -0
  192. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_stop_hook.py +0 -0
  193. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_stop_precompact_fixes.py +0 -0
  194. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_sync_template_stop.py +0 -0
  195. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_template_pre_tool_hook.py +0 -0
  196. {claude_code_conductor-2.29.1 → claude_code_conductor-2.29.2}/tests/test_worktree_guard.py +0 -0
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.29.2] - 2026-05-28
4
+
5
+ **内部品質リファクタ(振る舞い不変・公開 API 不変・破壊的変更なし)**: v2.29.1 以降に進めたコード負債返済をまとめてリリース。新機能・バグ修正・破壊的変更はなく、`c3.db` などの公開 import は従来どおり動作する。観測可能な変化は「想定内のテーブル未作成(`sqlite3.OperationalError`)のログレベルが warning → debug に下がる」のみ。各リファクタは全テスト緑(1339 passed)・0 regression・全 Python(3.10/3.11/3.12)CI green を確認済み。
6
+
7
+ ### 変更
8
+
9
+ - **`db.py`: 環境変数解決 3 関数を `_resolve_float_env` に集約(DRY)**: `resolve_cost_lambda` / `resolve_epsilon` / `resolve_escalation_threshold` のほぼ同一だった実装(env 名・デフォルト・有効域だけが違う)を共通ヘルパーに統合。型ナローイングは `typing.cast` で表現。
10
+ - **`db.py`: tier-routing パラメータを `_db_params.py` へ分離**: SSOT 定数(`LEARNING_THRESHOLD` / `EPSILON_TIEBREAK` / `COST_LAMBDA_*` / `ESCALATION_THRESHOLD_DEFAULT`)と `resolve_*` を新規モジュールへ。`db.py` は後方互換のため re-export を維持し、`from c3.db import ...` / `from c3 import db; db.X` の両形式が従来どおり動作する(`dir(c3.db)` の公開シンボル集合は分割前と一致)。
11
+ - **`usage_ingester.py`: パス traversal 検証の重複を `_safe_resolved_file` に集約(DRY)**: `_ingest_jsonl` / `_read_agent_meta` で重複していた symlink + resolve + project_dir 配下検証を 1 ヘルパーに統合。`cli_update` / `mcp_server` のパス検証は意味(段階別エラー返却・strict 存在必須)が異なるため共通化対象外とした。
12
+ - **`db.py`: 例外分類を 10 関数で統一**: 想定内の `sqlite3.OperationalError`(テーブル未作成)は debug、想定外の `Exception` は warning に分類(既存 4 関数のパターンに合わせる)。graceful degradation の catch-all は意図的に維持(狭めない)。情報漏洩防止のため型名のみログ(生メッセージは出さない・既存方針を踏襲)。
13
+
14
+ ### ドキュメント
15
+
16
+ - **`/ARCHITECTURE.md` を新設(リポジトリルート・配布対象外)**: `c3` パッケージと `.claude/` フレームワークの二層構造・ランタイムのオーケストレーション・hook ライフサイクル(settings.json と照合済み)・知能基盤(c3.db / recall)・ビルド/配布パイプラインを 1 枚に集約。既存の taxonomy.md / config-policy.md への索引役に徹する。
17
+
18
+ ### 後方互換
19
+
20
+ - 公開 API(`c3.db` の import)・CLI・DB スキーマに変更なし。migration 不要。**破壊的変更なし**。
21
+ - 唯一の観測可能な差分: 想定内 missing-table のログレベルが warning → debug(ログノイズ削減)。
22
+
3
23
  ## [2.29.1] - 2026-05-27
4
24
 
5
25
  **`deletions.txt` 遡及追記(rules→references 移動 3 ファイル)**: v2.15.0 で `.claude/rules/` から `.claude/skills/dev-workflow/references/` へ移動した 3 つのチェックリストが `deletions.txt` に未記載だった漏れを修正。配布先が次回 `c3 update` で旧 `.claude/rules/` の常時ロード残骸を除去できるようになる。**コード変更なし・破壊的変更なし・migration なし**。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-conductor
3
- Version: 2.29.1
3
+ Version: 2.29.2
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
@@ -1,3 +1,3 @@
1
1
  """Claude Code Conductor (C3) - multi-agent orchestration framework for Claude Code."""
2
2
 
3
- __version__ = "2.29.1"
3
+ __version__ = "2.29.2"
@@ -0,0 +1,143 @@
1
+ """C3 tier-routing パラメータ解決(env override + SSOT 定数)。
2
+
3
+ ``db.py``(SQLite read/write helpers)から分離した tier-routing の tunable 群。
4
+ ``LEARNING_THRESHOLD`` / ``EPSILON_TIEBREAK`` / ``COST_LAMBDA_*`` /
5
+ ``ESCALATION_THRESHOLD_DEFAULT`` の SSOT 定数と、それらを環境変数で上書き解決する
6
+ ``resolve_*`` を提供する。DB I/O には依存しない(純粋な env パース)。
7
+
8
+ 後方互換のため、これらは ``c3.db`` からも re-export される。cli_tier.py /
9
+ select_tier.py は従来どおり ``c3.db`` 経由でも、本モジュール直接でも参照できる。
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import math
15
+ import os
16
+ import sys
17
+ from typing import cast
18
+
19
+ # tier-routing: 学習データ収集期の閾値(合計試行数がこの値未満なら uniform 選択)。
20
+ # SSOT: cli_tier.py / select_tier.py はここから参照する(CR-M-002)。
21
+ LEARNING_THRESHOLD = 30
22
+ # cost-aware tie-break の拮抗判定閾値。Beta サンプルは 0〜1 スケールで、
23
+ # 成功率 5pt(=0.05)以内を拮抗とみなす。本定数が SSOT。
24
+ # 過大にすると成功率を犠牲にするリスク、過小にすると無発動になる。
25
+ # C3_TIER_EPSILON 環境変数で上書き可(v2.25.0)。
26
+ EPSILON_TIEBREAK = 0.05
27
+ # cost-weighted Thompson の重み係数 λ の既定値。
28
+ # None = v2.25.0 互換モード(ε tie-break を維持し全 tier weighting を発動しない)。
29
+ # C3_TIER_COST_LAMBDA 環境変数で上書き可(v2.26.0)。
30
+ # λ>0 で全 tier の score=sample-λ*cost_norm weighting が発動、λ=0 明示で cost 無視(純 Thompson)。
31
+ # 本定数が SSOT。
32
+ COST_LAMBDA_DEFAULT = None
33
+ # failure rate がこの値以上で 1 段上位 tier へ escalation する閾値。
34
+ # C3_ESCALATION_THRESHOLD 環境変数で上書き可(v2.26.0)。
35
+ # 本定数が SSOT(select_tier.py はここから参照)。
36
+ ESCALATION_THRESHOLD_DEFAULT = 0.5
37
+
38
+ # cost-weighted Thompson の λ 有効範囲(v2.27.0: 上限を 1.0→5.0 に拡張)。
39
+ # cost を成功率より強く効かせる余地を確保するため上限を 5.0 に設定。
40
+ # select_tier.py の _resolve_cost_lambda はここを SSOT として参照する。
41
+ COST_LAMBDA_MIN = 0.0
42
+ COST_LAMBDA_MAX = 5.0
43
+
44
+
45
+ def _resolve_float_env(
46
+ env_key: str,
47
+ default: float | None,
48
+ *,
49
+ min_val: float,
50
+ max_val: float,
51
+ min_inclusive: bool,
52
+ log_prefix: str,
53
+ ) -> float | None:
54
+ """env 変数を float として安全に解決する共通ヘルパー(resolve_* の SSOT 実体)。
55
+
56
+ 挙動(resolve_cost_lambda / resolve_epsilon / resolve_escalation_threshold で共通):
57
+ - 未設定 / 空文字 → 無警告で ``default`` を返す。
58
+ - 非数値 / NaN / 範囲外 → stderr に env 名入りの警告を出し ``default`` に戻す。
59
+ - 妥当域: 上限は常に閉区間 ``<= max_val``。下限は ``min_inclusive`` で開閉を切替
60
+ (True なら ``>= min_val`` の閉区間、False なら ``> min_val`` の半開区間)。
61
+ """
62
+ raw = os.environ.get(env_key)
63
+ if raw is None or raw == "":
64
+ return default
65
+ bracket = (
66
+ f"[{min_val}, {max_val}]" if min_inclusive else f"({min_val}, {max_val}]"
67
+ )
68
+ try:
69
+ x = float(raw)
70
+ except ValueError:
71
+ print(
72
+ f"{log_prefix} invalid {env_key}={raw!r}, using default {default}",
73
+ file=sys.stderr,
74
+ )
75
+ return default
76
+ if math.isnan(x):
77
+ print(
78
+ f"{log_prefix} {env_key}={raw!r} is NaN, using default {default}",
79
+ file=sys.stderr,
80
+ )
81
+ return default
82
+ low_ok = x >= min_val if min_inclusive else x > min_val
83
+ if not low_ok or x > max_val:
84
+ print(
85
+ f"{log_prefix} {env_key}={x!r} out of range {bracket}, "
86
+ f"using default {default}",
87
+ file=sys.stderr,
88
+ )
89
+ return default
90
+ return x
91
+
92
+
93
+ def resolve_cost_lambda() -> float | None:
94
+ """``C3_TIER_COST_LAMBDA`` を安全に解決する(cli_tier 用 SSOT)。
95
+
96
+ 妥当域: [COST_LAMBDA_MIN, COST_LAMBDA_MAX](x=0 許容の閉区間)。
97
+ 戻り値が None の場合は v2.25.0 互換の ε tie-break 経路を維持する(センチネル)。
98
+ 詳細な共通挙動は :func:`_resolve_float_env` を参照。
99
+ """
100
+ return _resolve_float_env(
101
+ "C3_TIER_COST_LAMBDA",
102
+ COST_LAMBDA_DEFAULT,
103
+ min_val=COST_LAMBDA_MIN,
104
+ max_val=COST_LAMBDA_MAX,
105
+ min_inclusive=True,
106
+ log_prefix="[c3:cost_lambda]",
107
+ )
108
+
109
+
110
+ def resolve_epsilon() -> float:
111
+ """``C3_TIER_EPSILON`` を安全に解決する(cli_tier 用 SSOT)。
112
+
113
+ 妥当域: (0, 1](x=0 拒否の半開区間)。default が float のため戻り値は常に float。
114
+ 詳細な共通挙動は :func:`_resolve_float_env` を参照。
115
+ """
116
+ value = _resolve_float_env(
117
+ "C3_TIER_EPSILON",
118
+ EPSILON_TIEBREAK,
119
+ min_val=0.0,
120
+ max_val=1.0,
121
+ min_inclusive=False,
122
+ log_prefix="[c3:epsilon]",
123
+ )
124
+ # default=EPSILON_TIEBREAK のため None になり得ない(戻り値型を float に絞る)
125
+ return cast(float, value)
126
+
127
+
128
+ def resolve_escalation_threshold() -> float:
129
+ """``C3_ESCALATION_THRESHOLD`` を安全に解決する(cli_tier 用 SSOT)。
130
+
131
+ 妥当域: (0, 1](x=0 拒否の半開区間)。default が float のため戻り値は常に float。
132
+ 詳細な共通挙動は :func:`_resolve_float_env` を参照。
133
+ """
134
+ value = _resolve_float_env(
135
+ "C3_ESCALATION_THRESHOLD",
136
+ ESCALATION_THRESHOLD_DEFAULT,
137
+ min_val=0.0,
138
+ max_val=1.0,
139
+ min_inclusive=False,
140
+ log_prefix="[c3:escalation]",
141
+ )
142
+ # default=ESCALATION_THRESHOLD_DEFAULT のため None になり得ない(戻り値型を float に絞る)
143
+ return cast(float, value)
@@ -9,6 +9,9 @@ DB が見つからない場合・書き込みエラー時は静かにスキッ
9
9
  書き込みは Python 標準の `sqlite3` で行う(WAL モード)。
10
10
  読み・分析は別途 DuckDB の sqlite_scanner で ATTACH する想定(duckdb-hybrid と整合)。
11
11
 
12
+ tier-routing の tunable 定数・resolve_* は `_db_params` が SSOT。後方互換のため
13
+ 本モジュールからも re-export される(既存の `from c3.db import ...` を壊さないため)。
14
+
12
15
  履歴: v1.11.0 までは `src/parallel_orchestra/c3_db.py` に置かれていたが、
13
16
  PO 廃止計画(plan: atomic-foraging-sprout)の Step 1 で本ファイルに物理移動し、
14
17
  v2.0.0 で PO 専用ヘルパー(record_task_results / fetch_po_results /
@@ -18,13 +21,25 @@ upsert_po_status / fetch_po_status)も同時に削除した。
18
21
  from __future__ import annotations
19
22
 
20
23
  import logging
21
- import math
22
24
  import os
23
25
  import sqlite3
24
- import sys
25
26
  from datetime import datetime
26
27
  from pathlib import Path
27
28
 
29
+ # tier-routing パラメータ(定数 + env 解決)は _db_params.py が SSOT。
30
+ # 後方互換のため c3.db からも参照可能にする(cli_tier.py / select_tier.py 等)。
31
+ from c3._db_params import (
32
+ COST_LAMBDA_DEFAULT as COST_LAMBDA_DEFAULT,
33
+ COST_LAMBDA_MAX as COST_LAMBDA_MAX,
34
+ COST_LAMBDA_MIN as COST_LAMBDA_MIN,
35
+ EPSILON_TIEBREAK as EPSILON_TIEBREAK,
36
+ ESCALATION_THRESHOLD_DEFAULT as ESCALATION_THRESHOLD_DEFAULT,
37
+ LEARNING_THRESHOLD as LEARNING_THRESHOLD,
38
+ resolve_cost_lambda as resolve_cost_lambda,
39
+ resolve_epsilon as resolve_epsilon,
40
+ resolve_escalation_threshold as resolve_escalation_threshold,
41
+ )
42
+
28
43
  logger = logging.getLogger(__name__)
29
44
 
30
45
 
@@ -34,144 +49,6 @@ logger = logging.getLogger(__name__)
34
49
  BUSY_TIMEOUT_MS = 5000
35
50
  _BUSY_TIMEOUT_MS = BUSY_TIMEOUT_MS # 内部互換エイリアス(既存コードへの影響なし)
36
51
 
37
- # tier-routing: 学習データ収集期の閾値(合計試行数がこの値未満なら uniform 選択)。
38
- # SSOT: cli_tier.py / select_tier.py はここから参照する(CR-M-002)。
39
- LEARNING_THRESHOLD = 30
40
- # cost-aware tie-break の拮抗判定閾値。Beta サンプルは 0〜1 スケールで、
41
- # 成功率 5pt(=0.05)以内を拮抗とみなす。本定数が SSOT。
42
- # 過大にすると成功率を犠牲にするリスク、過小にすると無発動になる。
43
- # C3_TIER_EPSILON 環境変数で上書き可(v2.25.0)。
44
- EPSILON_TIEBREAK = 0.05
45
- # cost-weighted Thompson の重み係数 λ の既定値。
46
- # None = v2.25.0 互換モード(ε tie-break を維持し全 tier weighting を発動しない)。
47
- # C3_TIER_COST_LAMBDA 環境変数で上書き可(v2.26.0)。
48
- # λ>0 で全 tier の score=sample-λ*cost_norm weighting が発動、λ=0 明示で cost 無視(純 Thompson)。
49
- # 本定数が SSOT。
50
- COST_LAMBDA_DEFAULT = None
51
- # failure rate がこの値以上で 1 段上位 tier へ escalation する閾値。
52
- # C3_ESCALATION_THRESHOLD 環境変数で上書き可(v2.26.0)。
53
- # 本定数が SSOT(select_tier.py はここから参照)。
54
- ESCALATION_THRESHOLD_DEFAULT = 0.5
55
-
56
- # cost-weighted Thompson の λ 有効範囲(v2.27.0: 上限を 1.0→5.0 に拡張)。
57
- # cost を成功率より強く効かせる余地を確保するため上限を 5.0 に設定。
58
- # select_tier.py の _resolve_cost_lambda はここを SSOT として参照する。
59
- COST_LAMBDA_MIN = 0.0
60
- COST_LAMBDA_MAX = 5.0
61
-
62
-
63
- def resolve_cost_lambda() -> float | None:
64
- """``C3_TIER_COST_LAMBDA`` を安全に解決する(cli_tier 用 SSOT)。
65
-
66
- 不正値(非数値 / 0 未満 / COST_LAMBDA_MAX 超 / NaN)は受け付けず、
67
- stderr 警告 + デフォルト(COST_LAMBDA_DEFAULT = None)に戻す。
68
- 未設定 / 空文字は無警告でデフォルト(None)を返す。
69
- 妥当域: [COST_LAMBDA_MIN, COST_LAMBDA_MAX](x=0 許容の閉区間)。
70
- 戻り値が None の場合は v2.25.0 互換の ε tie-break 経路を維持する(センチネル)。
71
- """
72
- raw = os.environ.get("C3_TIER_COST_LAMBDA")
73
- if raw is None or raw == "":
74
- return COST_LAMBDA_DEFAULT
75
- try:
76
- x = float(raw)
77
- except ValueError:
78
- print(
79
- f"[c3:cost_lambda] invalid C3_TIER_COST_LAMBDA={raw!r}, "
80
- f"using default {COST_LAMBDA_DEFAULT}",
81
- file=sys.stderr,
82
- )
83
- return COST_LAMBDA_DEFAULT
84
- if math.isnan(x):
85
- print(
86
- f"[c3:cost_lambda] C3_TIER_COST_LAMBDA={raw!r} is NaN, "
87
- f"using default {COST_LAMBDA_DEFAULT}",
88
- file=sys.stderr,
89
- )
90
- return COST_LAMBDA_DEFAULT
91
- if x < COST_LAMBDA_MIN or x > COST_LAMBDA_MAX:
92
- print(
93
- f"[c3:cost_lambda] C3_TIER_COST_LAMBDA={x!r} out of range "
94
- f"[{COST_LAMBDA_MIN}, {COST_LAMBDA_MAX}], "
95
- f"using default {COST_LAMBDA_DEFAULT}",
96
- file=sys.stderr,
97
- )
98
- return COST_LAMBDA_DEFAULT
99
- return x
100
-
101
-
102
- def resolve_epsilon() -> float:
103
- """``C3_TIER_EPSILON`` を安全に解決する(cli_tier 用 SSOT)。
104
-
105
- 不正値(非数値 / 0 以下 / 1 超 / NaN)は受け付けず、
106
- stderr 警告 + デフォルト(EPSILON_TIEBREAK)に戻す。
107
- 未設定 / 空文字は無警告でデフォルトを返す。
108
- 妥当域: (0, 1](x=0 拒否の半開区間)。
109
- """
110
- raw = os.environ.get("C3_TIER_EPSILON")
111
- if raw is None or raw == "":
112
- return EPSILON_TIEBREAK
113
- try:
114
- x = float(raw)
115
- except ValueError:
116
- print(
117
- f"[c3:epsilon] invalid C3_TIER_EPSILON={raw!r}, "
118
- f"using default {EPSILON_TIEBREAK}",
119
- file=sys.stderr,
120
- )
121
- return EPSILON_TIEBREAK
122
- if math.isnan(x):
123
- print(
124
- f"[c3:epsilon] C3_TIER_EPSILON={raw!r} is NaN, "
125
- f"using default {EPSILON_TIEBREAK}",
126
- file=sys.stderr,
127
- )
128
- return EPSILON_TIEBREAK
129
- if x <= 0 or x > 1:
130
- print(
131
- f"[c3:epsilon] C3_TIER_EPSILON={x!r} out of range (0, 1], "
132
- f"using default {EPSILON_TIEBREAK}",
133
- file=sys.stderr,
134
- )
135
- return EPSILON_TIEBREAK
136
- return x
137
-
138
-
139
- def resolve_escalation_threshold() -> float:
140
- """``C3_ESCALATION_THRESHOLD`` を安全に解決する(cli_tier 用 SSOT)。
141
-
142
- 不正値(非数値 / 0 以下 / 1 超 / NaN)は受け付けず、
143
- stderr 警告 + デフォルト(ESCALATION_THRESHOLD_DEFAULT)に戻す。
144
- 未設定 / 空文字は無警告でデフォルトを返す。
145
- 妥当域: (0, 1](x=0 拒否の半開区間)。
146
- """
147
- raw = os.environ.get("C3_ESCALATION_THRESHOLD")
148
- if raw is None or raw == "":
149
- return ESCALATION_THRESHOLD_DEFAULT
150
- try:
151
- x = float(raw)
152
- except ValueError:
153
- print(
154
- f"[c3:escalation] invalid C3_ESCALATION_THRESHOLD={raw!r}, "
155
- f"using default {ESCALATION_THRESHOLD_DEFAULT}",
156
- file=sys.stderr,
157
- )
158
- return ESCALATION_THRESHOLD_DEFAULT
159
- if math.isnan(x):
160
- print(
161
- f"[c3:escalation] C3_ESCALATION_THRESHOLD={raw!r} is NaN, "
162
- f"using default {ESCALATION_THRESHOLD_DEFAULT}",
163
- file=sys.stderr,
164
- )
165
- return ESCALATION_THRESHOLD_DEFAULT
166
- if x <= 0 or x > 1:
167
- print(
168
- f"[c3:escalation] C3_ESCALATION_THRESHOLD={x!r} out of range (0, 1], "
169
- f"using default {ESCALATION_THRESHOLD_DEFAULT}",
170
- file=sys.stderr,
171
- )
172
- return ESCALATION_THRESHOLD_DEFAULT
173
- return x
174
-
175
52
 
176
53
  def _apply_busy_timeout(conn: sqlite3.Connection) -> None:
177
54
  # PRAGMA はパラメータバインドできないため値が整数であることを int() で強制する。
@@ -274,6 +151,9 @@ def fetch_review_decisions(
274
151
  return [dict(r) for r in rows]
275
152
  finally:
276
153
  conn.close()
154
+ except sqlite3.OperationalError as exc:
155
+ logger.debug("fetch_review_decisions: table not found or inaccessible: %s", type(exc).__name__)
156
+ return []
277
157
  except Exception as exc: # noqa: BLE001
278
158
  logger.warning("failed to fetch review_decisions: %s", type(exc).__name__)
279
159
  return []
@@ -339,6 +219,9 @@ def insert_review_decision(
339
219
  finally:
340
220
  conn.close()
341
221
  return True
222
+ except sqlite3.OperationalError as exc:
223
+ logger.debug("insert_review_decision: table not found or inaccessible: %s", type(exc).__name__)
224
+ return False
342
225
  except Exception as exc: # noqa: BLE001
343
226
  logger.warning("failed to insert review_decision: %s", type(exc).__name__)
344
227
  return False
@@ -405,6 +288,9 @@ def read_tier_params(
405
288
  ).fetchall()
406
289
  finally:
407
290
  conn.close()
291
+ except sqlite3.OperationalError as exc:
292
+ logger.debug("read_tier_params: table not found or inaccessible: %s", type(exc).__name__)
293
+ return defaults
408
294
  except Exception as exc: # noqa: BLE001
409
295
  logger.warning("failed to read tier_params: %s", type(exc).__name__)
410
296
  return defaults
@@ -477,6 +363,9 @@ def update_tier_params(
477
363
  finally:
478
364
  conn.close()
479
365
  return True
366
+ except sqlite3.OperationalError as exc:
367
+ logger.debug("update_tier_params: table not found or inaccessible: %s", type(exc).__name__)
368
+ return False
480
369
  except Exception as exc: # noqa: BLE001
481
370
  logger.warning("failed to update tier_params: %s", type(exc).__name__)
482
371
  return False
@@ -534,6 +423,9 @@ def record_tier_recent_outcome(
534
423
  finally:
535
424
  conn.close()
536
425
  return True
426
+ except sqlite3.OperationalError as exc:
427
+ logger.debug("record_tier_recent_outcome: table not found or inaccessible: %s", type(exc).__name__)
428
+ return False
537
429
  except Exception as exc: # noqa: BLE001
538
430
  logger.warning("failed to record tier_recent_outcome: %s", type(exc).__name__)
539
431
  return False
@@ -628,6 +520,9 @@ def read_tier_failure_rate(
628
520
  ).fetchall()
629
521
  finally:
630
522
  conn.close()
523
+ except sqlite3.OperationalError as exc:
524
+ logger.debug("read_tier_failure_rate: table not found or inaccessible: %s", type(exc).__name__)
525
+ return None, 0
631
526
  except Exception as exc: # noqa: BLE001
632
527
  logger.warning("failed to read tier_failure_rate: %s", type(exc).__name__)
633
528
  return None, 0
@@ -723,6 +618,9 @@ def insert_agent_cost_run(
723
618
  finally:
724
619
  conn.close()
725
620
  return True
621
+ except sqlite3.OperationalError as exc:
622
+ logger.debug("insert_agent_cost_run: table not found or inaccessible: %s", type(exc).__name__)
623
+ return False
726
624
  except Exception as exc: # noqa: BLE001
727
625
  logger.warning("failed to insert_agent_cost_run: %s", type(exc).__name__)
728
626
  return False
@@ -1243,6 +1141,9 @@ def set_ingest_offset(
1243
1141
  finally:
1244
1142
  conn.close()
1245
1143
  return True
1144
+ except sqlite3.OperationalError as exc:
1145
+ logger.debug("set_ingest_offset: table not found or inaccessible: %s", type(exc).__name__)
1146
+ return False
1246
1147
  except Exception as exc: # noqa: BLE001
1247
1148
  logger.warning("failed to set_ingest_offset: %s", type(exc).__name__)
1248
1149
  return False
@@ -1279,6 +1180,9 @@ def read_tier_bandit_cost(
1279
1180
  ).fetchall()
1280
1181
  finally:
1281
1182
  conn.close()
1183
+ except sqlite3.OperationalError as exc:
1184
+ logger.debug("read_tier_bandit_cost: table not found or inaccessible: %s", type(exc).__name__)
1185
+ return {}
1282
1186
  except Exception as exc: # noqa: BLE001
1283
1187
  logger.warning("failed to read tier_bandit_cost: %s", type(exc).__name__)
1284
1188
  return {}
@@ -1356,6 +1260,9 @@ def sync_tier_bandit_cost(
1356
1260
  conn.commit()
1357
1261
  finally:
1358
1262
  conn.close()
1263
+ except sqlite3.OperationalError as exc:
1264
+ logger.debug("sync_tier_bandit_cost: table not found or inaccessible: %s", type(exc).__name__)
1265
+ return 0
1359
1266
  except Exception as exc: # noqa: BLE001
1360
1267
  logger.warning("failed to sync_tier_bandit_cost: %s", type(exc).__name__)
1361
1268
  return 0
@@ -21,7 +21,6 @@ import logging
21
21
  import re
22
22
  from dataclasses import dataclass, field
23
23
  from pathlib import Path
24
- from typing import Generator
25
24
 
26
25
  from c3.db import (
27
26
  get_ingest_offset,
@@ -133,6 +132,29 @@ def ingest_session(
133
132
  return result
134
133
 
135
134
 
135
+ def _safe_resolved_file(
136
+ path: Path, project_dir: Path, *, log_label: str
137
+ ) -> Path | None:
138
+ """symlink でない実在ファイルを resolve し、project_dir 配下なら resolved Path を返す。
139
+
140
+ 検証に失敗したら None を返す(symlink / 非ファイル / project_dir 外)。symlink・
141
+ 範囲外は debug ログを出す。``is_symlink()`` / ``is_file()`` / ``resolve()`` の
142
+ 例外(マウント切れ等の稀な OSError)は送出し、呼び出し側の try で扱う
143
+ (_ingest_jsonl は result.errors に記録、_read_agent_meta は debug ログ)。
144
+ SR-V-002 のパス traversal 対策を 1 箇所に集約する。
145
+ """
146
+ if path.is_symlink():
147
+ logger.debug("%s: symlink skipped", log_label)
148
+ return None
149
+ if not path.is_file():
150
+ return None
151
+ resolved = path.resolve()
152
+ if not resolved.is_relative_to(project_dir):
153
+ logger.debug("%s: path outside project_dir, skipped", log_label)
154
+ return None
155
+ return resolved
156
+
157
+
136
158
  def _ingest_jsonl(
137
159
  *,
138
160
  jsonl_path: Path,
@@ -148,24 +170,17 @@ def _ingest_jsonl(
148
170
  - 存在しない / symlink / project_dir 外のパスはスキップ
149
171
  - parse error なく読み切れた時のみ offset を更新(冪等性保証)
150
172
  """
151
- # symlink チェック
152
- if jsonl_path.is_symlink():
153
- logger.debug("_ingest_jsonl: symlink skipped")
154
- return
155
-
156
- # ファイル存在チェック
157
- if not jsonl_path.is_file():
158
- return
159
-
160
- # パス traversal チェック: resolve 後 project_dir 配下であることを確認
173
+ # symlink でない project_dir 配下の実在ファイルか検証し、resolved パスを得る。
174
+ # resolve() の例外のみ errors に記録する(symlink/非ファイル/範囲外は静かにスキップ)。
161
175
  try:
162
- resolved_jsonl = jsonl_path.resolve()
163
- if not resolved_jsonl.is_relative_to(project_dir):
164
- logger.debug("_ingest_jsonl: path outside project_dir, skipped")
165
- return
176
+ resolved_jsonl = _safe_resolved_file(
177
+ jsonl_path, project_dir, log_label="_ingest_jsonl"
178
+ )
166
179
  except Exception as exc: # noqa: BLE001
167
180
  result.errors.append(type(exc).__name__)
168
181
  return
182
+ if resolved_jsonl is None:
183
+ return
169
184
 
170
185
  file_key = f"{session_id}:{agent_id}"
171
186
  start_offset = get_ingest_offset(file_key, db_path=db_path)
@@ -345,24 +360,17 @@ def _read_agent_meta(
345
360
  - parse エラー: (None, None)
346
361
  - agentType なし: (None, description_or_None)
347
362
  """
348
- # symlink チェック
349
- if meta_path.is_symlink():
350
- logger.debug("_read_agent_meta: symlink skipped")
351
- return None, None
352
-
353
- # ファイル存在チェック
354
- if not meta_path.is_file():
355
- return None, None
356
-
357
- # パス traversal チェック
363
+ # symlink でない project_dir 配下の実在ファイルか検証し、resolved パスを得る。
364
+ # resolve() の例外は debug ログのみ(symlink/非ファイル/範囲外は静かにスキップ)。
358
365
  try:
359
- resolved_meta = meta_path.resolve()
360
- if not resolved_meta.is_relative_to(project_dir):
361
- logger.debug("_read_agent_meta: path outside project_dir, skipped")
362
- return None, None
366
+ resolved_meta = _safe_resolved_file(
367
+ meta_path, project_dir, log_label="_read_agent_meta"
368
+ )
363
369
  except Exception as exc: # noqa: BLE001
364
370
  logger.debug("_read_agent_meta: resolve error: %s", type(exc).__name__)
365
371
  return None, None
372
+ if resolved_meta is None:
373
+ return None, None
366
374
 
367
375
  try:
368
376
  data = json.loads(resolved_meta.read_text(encoding="utf-8"))