claude-code-conductor 2.43.0__tar.gz → 2.45.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 (213) hide show
  1. claude_code_conductor-2.45.0/.claude/hooks/pre_compact.py +205 -0
  2. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/stop.py +44 -1
  3. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/CHANGELOG.md +26 -0
  4. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/PKG-INFO +1 -1
  5. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/__init__.py +1 -1
  6. claude_code_conductor-2.45.0/tests/_pre_compact_helpers.py +88 -0
  7. claude_code_conductor-2.45.0/tests/test_pre_compact.py +1014 -0
  8. claude_code_conductor-2.45.0/tests/test_precompact_additional.py +149 -0
  9. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_stop_additional.py +510 -0
  10. claude_code_conductor-2.43.0/.claude/hooks/pre_compact.py +0 -61
  11. claude_code_conductor-2.43.0/tests/test_pre_compact.py +0 -265
  12. claude_code_conductor-2.43.0/tests/test_precompact_additional.py +0 -135
  13. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/CLAUDE.md +0 -0
  14. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/architect.md +0 -0
  15. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/code-reviewer.md +0 -0
  16. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/design-critic.md +0 -0
  17. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/developer.md +0 -0
  18. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/doc-writer.md +0 -0
  19. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/interviewer.md +0 -0
  20. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/planner.md +0 -0
  21. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/project-setup.md +0 -0
  22. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/security-reviewer.md +0 -0
  23. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/systematic-debugger.md +0 -0
  24. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/tester.md +0 -0
  25. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_developer.md +0 -0
  26. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_systematic-debugger.md +0 -0
  27. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_tester.md +0 -0
  28. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/breaking-changes.txt +0 -0
  29. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/deletions.txt +0 -0
  30. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/docs/config-policy.md +0 -0
  31. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/docs/parallel-agents-setup.md +0 -0
  32. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/docs/platform-adapters.md +0 -0
  33. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/docs/settings.json.md +0 -0
  34. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/_hook_utils.py +0 -0
  35. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/check_agent_invocation.py +0 -0
  36. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/consolidate_memory.py +0 -0
  37. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/permission_handler.py +0 -0
  38. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/permission_handler_toast.py +0 -0
  39. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/planner_check.py +0 -0
  40. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/post_tool.py +0 -0
  41. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/pre_tool.py +0 -0
  42. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/recall_autorebuild.py +0 -0
  43. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/recall_inject.py +0 -0
  44. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/restore_session.py +0 -0
  45. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/select_tier.py +0 -0
  46. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_start.py +0 -0
  47. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_stop.py +0 -0
  48. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_utils.py +0 -0
  49. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/statusline.py +0 -0
  50. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/worktree_guard.py +0 -0
  51. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/memory/.gitkeep +0 -0
  52. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/permission_rules.json +0 -0
  53. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/rules/promoted/index.md +0 -0
  54. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/settings.json +0 -0
  55. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/brainstorm/SKILL.md +0 -0
  56. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/codex-review/SKILL.md +0 -0
  57. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/SKILL.md +0 -0
  58. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
  59. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
  60. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
  61. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
  62. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
  63. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
  64. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
  65. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
  66. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
  67. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/develop/SKILL.md +0 -0
  68. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/doc/SKILL.md +0 -0
  69. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/extract-lib/SKILL.md +0 -0
  70. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/init-session/SKILL.md +0 -0
  71. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/init-session/scripts/session_guard.py +0 -0
  72. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/mcp-config/SKILL.md +0 -0
  73. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
  74. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/pattern-status/SKILL.md +0 -0
  75. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
  76. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/recall/SKILL.md +0 -0
  77. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
  78. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
  79. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/review-phase/SKILL.md +0 -0
  80. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/SKILL.md +0 -0
  81. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/reference.md +0 -0
  82. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
  83. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
  84. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/start/SKILL.md +0 -0
  85. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/state/.gitkeep +0 -0
  86. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.gitignore +0 -0
  87. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/ARCHITECTURE.md +0 -0
  88. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSE +0 -0
  89. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/fastembed-LICENSE +0 -0
  90. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/fastembed-NOTICE +0 -0
  91. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/numpy-LICENSE +0 -0
  92. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/onnxruntime-LICENSE +0 -0
  93. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
  94. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/README.md +0 -0
  95. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/hatch_build.py +0 -0
  96. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/pyproject.toml +0 -0
  97. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/__main__.py +0 -0
  98. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/_db_params.py +0 -0
  99. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/_excludes.py +0 -0
  100. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/_terminal.py +0 -0
  101. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/adapters.py +0 -0
  102. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli.py +0 -0
  103. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_ask.py +0 -0
  104. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_doctor.py +0 -0
  105. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_init.py +0 -0
  106. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_list.py +0 -0
  107. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_plan.py +0 -0
  108. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_recall.py +0 -0
  109. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_tier.py +0 -0
  110. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_update.py +0 -0
  111. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/db.py +0 -0
  112. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/embedding.py +0 -0
  113. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/gitutil.py +0 -0
  114. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/mcp_server.py +0 -0
  115. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrate.py +0 -0
  116. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/001_initial.sql +0 -0
  117. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
  118. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/003_tier_cost.sql +0 -0
  119. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/004_agent_outcomes.sql +0 -0
  120. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
  121. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/README.md +0 -0
  122. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/__init__.py +0 -0
  123. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/paths.py +0 -0
  124. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/plan_validator.py +0 -0
  125. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/platforms.py +0 -0
  126. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/pricing.py +0 -0
  127. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/question.py +0 -0
  128. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/recall_chunker.py +0 -0
  129. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/recall_index.py +0 -0
  130. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/usage_ingester.py +0 -0
  131. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/__init__.py +0 -0
  132. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/conftest.py +0 -0
  133. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/README.md +0 -0
  134. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/mainline.jsonl +0 -0
  135. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
  136. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
  137. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/__init__.py +0 -0
  138. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_check_agent_invocation.py +0 -0
  139. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_consolidate_memory.py +0 -0
  140. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_hook_utils.py +0 -0
  141. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_permission_handler.py +0 -0
  142. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_permission_handler_toast.py +0 -0
  143. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
  144. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_planner_check.py +0 -0
  145. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_planner_check_dev.py +0 -0
  146. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_post_tool.py +0 -0
  147. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_pre_tool.py +0 -0
  148. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_recall_autorebuild.py +0 -0
  149. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_recall_inject.py +0 -0
  150. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_record_agent_outcome.py +0 -0
  151. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_record_review_decision.py +0 -0
  152. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_restore_session.py +0 -0
  153. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_review_hint_inject.py +0 -0
  154. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_select_tier.py +0 -0
  155. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_select_tier_escalation.py +0 -0
  156. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_start.py +0 -0
  157. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_stop.py +0 -0
  158. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_utils.py +0 -0
  159. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
  160. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_similarity_boost.py +0 -0
  161. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_statusline.py +0 -0
  162. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_statusline_template_sync.py +0 -0
  163. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_sync_check.py +0 -0
  164. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_template_guard.py +0 -0
  165. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/__init__.py +0 -0
  166. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/_skill_helpers.py +0 -0
  167. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
  168. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_init_session_no_task_type.py +0 -0
  169. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_planner_lightweight.py +0 -0
  170. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_recall_skill.py +0 -0
  171. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
  172. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_session_guard.py +0 -0
  173. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_setup_templates.py +0 -0
  174. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
  175. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_new_flow.py +0 -0
  176. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
  177. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_adapters.py +0 -0
  178. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_check_deletions.py +0 -0
  179. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_ask.py +0 -0
  180. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_doctor.py +0 -0
  181. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_entry.py +0 -0
  182. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_init.py +0 -0
  183. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_list.py +0 -0
  184. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_plan.py +0 -0
  185. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_recall.py +0 -0
  186. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_tier.py +0 -0
  187. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_update_breaking_changes.py +0 -0
  188. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_update_deletions.py +0 -0
  189. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_db.py +0 -0
  190. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_docstring_consistency.py +0 -0
  191. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_embedding.py +0 -0
  192. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_excludes.py +0 -0
  193. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_extract_breaking_changes.py +0 -0
  194. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_gitutil.py +0 -0
  195. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_mcp_server_elicit.py +0 -0
  196. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_migrate.py +0 -0
  197. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_paths.py +0 -0
  198. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_plan_validator.py +0 -0
  199. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_pre_tool_hook.py +0 -0
  200. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_precompact_toctou_fixes.py +0 -0
  201. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_pricing.py +0 -0
  202. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_recall_chunker.py +0 -0
  203. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_recall_index.py +0 -0
  204. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_references_migration.py +0 -0
  205. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_session_utils_additional.py +0 -0
  206. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_skill_no_builtin_conflict.py +0 -0
  207. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_statusline.py +0 -0
  208. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_stop_hook.py +0 -0
  209. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_stop_precompact_fixes.py +0 -0
  210. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_sync_template_stop.py +0 -0
  211. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_template_pre_tool_hook.py +0 -0
  212. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_usage_ingester.py +0 -0
  213. {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_worktree_guard.py +0 -0
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env python3
2
+ """PreCompact hook: append checkpoint marker (デバウンス付き)。
3
+
4
+ 直近 DEBOUNCE_WINDOW_SECONDS 秒以内に PreCompact checkpoint が既に存在する場合は、
5
+ checkpoint の追記をスキップする(PreCompact の連続起動による重複追記を防ぐ
6
+ デバウンス機構)。
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import re
12
+ import sys
13
+ from datetime import datetime, timedelta, timezone
14
+
15
+ sys.stdout.reconfigure(encoding='utf-8')
16
+ sys.stderr.reconfigure(encoding='utf-8')
17
+
18
+ from session_utils import SESSION_JSON_MARKER, append_checkpoint, is_worktree, SESSIONS_DIR
19
+
20
+
21
+ # デバウンス窓幅(秒)。直近の PreCompact checkpoint からこの秒数以内の再実行は
22
+ # 追記をスキップする(4連続起動の重複防止)。
23
+ DEBOUNCE_WINDOW_SECONDS = 10
24
+
25
+ # checkpoint 行抽出のため splitlines() に渡す前に除去する行区切り文字パターン。
26
+ # stop.py::_INHERIT_SANITIZE_RE / session_utils._VALUE_SANITIZE_RE と同一範囲(対称)。
27
+ # str.splitlines() は \n 以外に \v / \f / \x1c-\x1e / \x85 / U+2028 / U+2029 でも
28
+ # 行分割する。append_checkpoint が書き込む summary body(未サニタイズの trigger 等)に
29
+ # これらの文字+checkpoint 行に酷似する文字列が混入すると、splitlines() が偽の行を
30
+ # 生成し _PRECOMPACT_CHECKPOINT_RE が誤マッチしてデバウンスの偽陽性を起こし得る。
31
+ # stop.py の _inherit_pending_tasks と同趣旨で、分割前に事前除去する。
32
+ # raw string は \uXXXX を解釈しないため U+2028 / U+2029 は chr() で連結する。
33
+ _LINE_SEPARATOR_SANITIZE_RE = re.compile(
34
+ r'[\x00-\x08\x0b-\x1f\x7f-\x9f' + chr(0x2028) + chr(0x2029) + r']'
35
+ )
36
+
37
+ # checkpoint 行の固定プレフィックス。PreCompact ラベルへのアンカーとして使う
38
+ # (他ラベル、例えば Wave checkpoint を除外する)。
39
+ _PRECOMPACT_CHECKPOINT_PREFIX = '## [Checkpoint: PreCompact:'
40
+
41
+ # checkpoint 行として許容する最大長(fix-cycle-2, security-review-report-20260704-075817.md
42
+ # [SR-NEW])。正規の checkpoint ヘッダ行は数十〜数百文字で十分なため、これを超える行は
43
+ # パース対象から除外する(regex 撤去後の belt-and-suspenders ガード)。
44
+ MAX_CHECKPOINT_LINE_LEN = 512
45
+
46
+ # parse 成功した timestamp が `now` をこの秒数を超えて上回る場合、構造的異常値として
47
+ # 棄却する(fail-open)。checkpoint の timestamp は append_checkpoint が同一ホストの
48
+ # UTC 実時刻で書くため、書き込み時刻は必ず読み取り時刻以前になるはず。
49
+ #
50
+ # 60 秒は独立したセキュリティ判断としての保守値である。正当な checkpoint 行の
51
+ # timestamp が読み取り時刻をわずかに上回り得る唯一の現実的経路は、NTP による
52
+ # クライアント時計の後方ステップ補正(書き込み時点で実時刻より進んでいた時計が、
53
+ # 読み取り時点までに巻き戻される)であり、この巻き戻し幅を吸収するために許容する。
54
+ # 通常の NTP ステップ補正は秒〜十数秒規模のため 60 秒あれば十分に吸収できる。
55
+ #
56
+ # この値を大きく取り過ぎると、許容スキュー以内の未来日時 checkpoint を注入された
57
+ # 場合に、実時刻がその timestamp に追いつくまでデバウンスが効き続け、checkpoint 追記
58
+ # が最大で許容秒数ぶん停止する([SR-V-001])。60 秒に
59
+ # 抑えることで、悪意ある注入・データ破損いずれの場合も最大デバウンス停止時間を
60
+ # 60 秒以内に限定する。
61
+ #
62
+ # 未来スキューを跨いだ許容/棄却境界はテスト側で module.FUTURE_SKEW_TOLERANCE_SECONDS
63
+ # を参照した相対時刻テストとして固定しており、テストは now= を明示指定して壁時計に
64
+ # 依存しない(値をテストフィクスチャ都合で膨らませる必要はない)。
65
+ FUTURE_SKEW_TOLERANCE_SECONDS = 60
66
+
67
+ # parse 失敗時の stderr 診断ログに出す捕捉テキストの最大長
68
+ # (fix-cycle-2, security-review-report-20260704-075817.md [SR-R-001])。
69
+ DIAGNOSTIC_MAX_LEN = 64
70
+
71
+
72
+ def _last_precompact_checkpoint_dt(session_file, now=None):
73
+ """session_file 内の最後の PreCompact checkpoint 行から aware datetime を返す。
74
+
75
+ ファイル無し・PreCompact 行無し・parse 不能・naive datetime・未来日時(許容
76
+ スキュー超)など、あらゆる異常系で None を返す(fail-open の中核)。
77
+
78
+ `now` は未来日時ガードの基準時刻(省略時は呼び出し時点の UTC 実時刻)。
79
+ 呼び出し元(main)と判定基準を一致させたい場合は明示的に渡すこと。
80
+ """
81
+ if now is None:
82
+ now = datetime.now(timezone.utc)
83
+
84
+ try:
85
+ with open(session_file, encoding='utf-8') as f:
86
+ content = f.read()
87
+ except (OSError, ValueError, TypeError):
88
+ # 「ファイル無し」は初回起動時の通常ケース。ほぼ毎日 1 回 stderr へ
89
+ # 出すとスパムになるため、ここでは無音のまま fail-open する(CR-L-003)。
90
+ return None
91
+
92
+ # splitlines() が特殊行区切り文字(\x85 / U+2028 / U+2029 等)で偽の checkpoint
93
+ # 行を生成しないよう、分割前に除去する(stop.py と対称なガード / CR-NEW Medium)。
94
+ lines = _LINE_SEPARATOR_SANITIZE_RE.sub('', content).splitlines()
95
+
96
+ for line in reversed(lines):
97
+ # regex(`.* - (.+)`)の二重ワイルドカードによる多項式時間バックトラッキング
98
+ # (ReDoS)を根絶するため、手続き的パースに置き換える
99
+ # (fix-cycle-2, security-review-report-20260704-075817.md [SR-NEW])。
100
+ stripped = line.rstrip()
101
+ if len(stripped) > MAX_CHECKPOINT_LINE_LEN:
102
+ # 行長ガード。異常に長い行は正規の checkpoint ではあり得ないため
103
+ # 以降のパース処理(rfind 等)に進む前にスキップする。
104
+ continue
105
+ if not stripped.startswith(_PRECOMPACT_CHECKPOINT_PREFIX):
106
+ continue
107
+ if not stripped.endswith(']'):
108
+ continue
109
+ # プレフィックスと末尾 `]` を除いた内側の文字列から、最後の ` - ` を
110
+ # timestamp との区切りとみなす(旧 greedy regex `.* - (.+)]` の「最後の
111
+ # ` - ` フィールドを timestamp とする」挙動と等価。label 内の偶発的な
112
+ # ` - ` があっても安全側に倒れる)。
113
+ # 旧 regex は `(.+)` が 1 文字以上を要求するため、timestamp 欄が完全空文字の
114
+ # 行(例: `## [Checkpoint: PreCompact: manual - ]`)を非マッチとして扱い、
115
+ # より古い有効な checkpoint 行を探し続けていた。空 ts_text を continue で
116
+ # スキップすることで、この空文字境界でも旧 regex と等価に振る舞う。
117
+ inner = stripped[len(_PRECOMPACT_CHECKPOINT_PREFIX):-1]
118
+ sep_idx = inner.rfind(' - ')
119
+ if sep_idx == -1:
120
+ continue
121
+ ts_text = inner[sep_idx + len(' - '):].strip()
122
+ if not ts_text:
123
+ # 空文字 timestamp は旧 greedy regex `(.+)` の非マッチに相当する。
124
+ # 診断ログは出さず(破損ではなく単なる欠落のため)、より古い有効行の
125
+ # 探索を継続する。
126
+ continue
127
+ try:
128
+ dt = datetime.fromisoformat(ts_text)
129
+ except (ValueError, TypeError):
130
+ # 「timestamp が壊れている」のは本来発生しない想定外の破損ケース。
131
+ # ファイル無しの通常ケースと異なり、将来のトラブルシュートのため
132
+ # このケースに限り stderr へ診断ログを 1 行出す(CR-L-003・非対称仕様)。
133
+ # 捕捉テキストは固定長に切り詰めて出力する(SR-R-001)。
134
+ # 戻り値は引き続き None(fail-open で追記継続)。
135
+ print(
136
+ '[PreCompact] 診断: checkpoint 行の timestamp を parse できませんでした'
137
+ f'(fail-open で追記を継続します): {ts_text[:DIAGNOSTIC_MAX_LEN]!r}',
138
+ file=sys.stderr,
139
+ )
140
+ return None
141
+ if dt.tzinfo is None:
142
+ return None
143
+ if dt > now + timedelta(seconds=FUTURE_SKEW_TOLERANCE_SECONDS):
144
+ # 未来日時(許容スキュー超)は構造的異常値として棄却する(fail-open)。
145
+ # 悪意ある注入・データ破損いずれの場合も、デバウンスの恒久停止を
146
+ # 招かないよう None を返して追記を継続する
147
+ # (fix-cycle-2, security-review-report-20260704-075817.md [SR-V-001])。
148
+ return None
149
+ return dt
150
+
151
+ return None
152
+
153
+
154
+ def main():
155
+ try:
156
+ payload = json.loads(sys.stdin.read())
157
+ except (json.JSONDecodeError, ValueError):
158
+ payload = {}
159
+
160
+ cwd = os.getcwd()
161
+ if is_worktree(cwd):
162
+ sys.exit(0)
163
+
164
+ trigger = payload.get('trigger', 'unknown')
165
+ context_items_before = payload.get('context_items_before')
166
+ context_items_before_str = 'N/A' if context_items_before is None else str(context_items_before)
167
+
168
+ now = datetime.now(timezone.utc)
169
+ # 既知の構造的限界(CR-NEW Low・修正しない): session_file は now の UTC 日付から
170
+ # 算出する。PreCompact の連続起動(実測 約1.2 秒スパン)が UTC 00:00:00 ちょうどを
171
+ # またぐ極稀ケースでは、前後の呼び出しが別日の session_file を参照するため
172
+ # デバウンス判定が効かず checkpoint が二重追記され得る。発生確率は極小のため
173
+ # コードでは対処せず既知の限界として許容する。
174
+ date_str = now.strftime('%Y%m%d')
175
+ session_file = os.path.join(SESSIONS_DIR, f'{date_str}.tmp')
176
+
177
+ last = _last_precompact_checkpoint_dt(session_file, now=now)
178
+ if last is not None and (now - last) < timedelta(seconds=DEBOUNCE_WINDOW_SECONDS):
179
+ print('[PreCompact] debounce: 直近の checkpoint を検出したため追記をスキップしました', file=sys.stderr)
180
+ return
181
+
182
+ # セキュリティ非対称性の記録(fix-cycle-2, security-review-report-20260704-121824.md
183
+ # [SR-V-001]・情報提供・現状は対応不要): 以下の summary(body)は
184
+ # session_utils.append_checkpoint の設計上 sanitize_value() 非適用で書き込まれる
185
+ # (複数行 Markdown を保持するための承認済み設計判断・session_utils.py L147 参照)。
186
+ # そのため body に埋め込む trigger(L164)は未サニタイズのまま checkpoint に書かれる。
187
+ # 一方 label(下の f'PreCompact: {trigger}')は append_checkpoint 内で
188
+ # sanitize_value() を通るため、body と label で sanitize 適用範囲に非対称性がある。
189
+ # 現状 trigger は Claude Code ハーネスが設定する列挙的な値(manual/auto 等)で、
190
+ # 外部入力・LLM 自由記述からの直接汚染経路が無いため実害の確信度は低い。
191
+ # 【将来の拡張時の注意】trigger の由来をハーネス以外(プラグイン・カスタムフック
192
+ # 連携等)に拡張する場合は、偽 checkpoint 注入経路化を防ぐため body 側にも
193
+ # sanitize_value() 適用を検討すること。
194
+ summary = (
195
+ f"- trigger: {trigger}\n"
196
+ f"- context_items_before: {context_items_before_str}\n"
197
+ f"- このポイント以前の詳細な文脈は圧縮により失われます。"
198
+ )
199
+ append_checkpoint(session_file, f'PreCompact: {trigger}', summary)
200
+
201
+ print(f'[PreCompact] セッション状態を {session_file} に保存しました', file=sys.stderr)
202
+
203
+
204
+ if __name__ == '__main__':
205
+ main()
@@ -29,6 +29,9 @@ PROMOTION_THRESHOLD = 0.8
29
29
  COOLING_DAYS = 3
30
30
  MAX_ID_LENGTH = 64
31
31
  MAX_DESCRIPTION_LENGTH = 500
32
+ # 取り込みガード(500)とは別レイヤの既存エントリ肥大検知線。直接 Edit 経路で
33
+ # patterns.json に載った肥大 description を毎 Stop で警告するための閾値(strict greater)。
34
+ DESCRIPTION_WARN_LENGTH = 1000
32
35
  MAX_LAST_MSG = 500
33
36
 
34
37
  # 過去セッションファイルから引き継ぐ - [ ] 行のサニタイズ用パターン。
@@ -335,6 +338,33 @@ def save_patterns(data: dict) -> None:
335
338
  os.unlink(tmp_path)
336
339
 
337
340
 
341
+ def _warn_oversized_descriptions(patterns: list) -> None:
342
+ """既存エントリの description が DESCRIPTION_WARN_LENGTH を超えていたら stderr 警告する。
343
+
344
+ 削除・切り詰めは行わない(警告のみ・read-only)。1 エントリ 1 行で出力する。
345
+ 本関数は例外を出さない防御的実装だが、呼び出し側でも try/except で二重に保護する。
346
+ """
347
+ for p in patterns:
348
+ if not isinstance(p, dict):
349
+ continue
350
+ desc = p.get('description', '')
351
+ if not isinstance(desc, str):
352
+ continue
353
+ if len(desc) > DESCRIPTION_WARN_LENGTH:
354
+ pid = p.get('id', '(unknown)')
355
+ # [SR-V-001] pid は直接 Edit 経路の値で未検証のため、出力前に
356
+ # 制御文字/ANSI 等を除去し MAX_ID_LENGTH で切り詰めて無害化する。
357
+ if not isinstance(pid, str):
358
+ pid = str(pid)
359
+ pid = _INHERIT_SANITIZE_RE.sub('', pid)[:MAX_ID_LENGTH]
360
+ print(
361
+ f'[Stop] patterns.json エントリの description が肥大しています '
362
+ f'(id={pid}, {len(desc)}字 > {DESCRIPTION_WARN_LENGTH}字)。'
363
+ f'内容を見直すか、日次評価が原因の場合は .dev/changelog-evals.md へ移してください',
364
+ file=sys.stderr,
365
+ )
366
+
367
+
338
368
  def update_patterns(date_str: str) -> None:
339
369
  new_observations = extract_session_patterns(date_str)
340
370
  data = load_patterns()
@@ -347,7 +377,9 @@ def update_patterns(date_str: str) -> None:
347
377
  description = obs.get('description', '')
348
378
  if len(description) > MAX_DESCRIPTION_LENGTH:
349
379
  continue
350
- existing = next((p for p in data['patterns'] if p['id'] == pid), None)
380
+ # isinstance ガード: dict エントリは検索対象外として無視し、
381
+ # 該当 id なし扱いで新規パターンとして追加される(削除はしない)[CR-E-001][SR-V-001]。
382
+ existing = next((p for p in data['patterns'] if isinstance(p, dict) and p.get('id') == pid), None)
351
383
  if existing is None:
352
384
  data['patterns'].append({
353
385
  "id": pid,
@@ -368,6 +400,11 @@ def update_patterns(date_str: str) -> None:
368
400
 
369
401
  active = []
370
402
  for pattern in data['patterns']:
403
+ if not isinstance(pattern, dict):
404
+ # 型が dict でないエントリは trust/expiry 計算不能。
405
+ # registered_date parse 不能時と同様に保持して継続(クラッシュ回避)[SR-V-001]。
406
+ active.append(pattern)
407
+ continue
371
408
  if pattern.get('promoted', False):
372
409
  active.append(pattern)
373
410
  continue
@@ -393,6 +430,12 @@ def update_patterns(date_str: str) -> None:
393
430
  active.append(pattern)
394
431
 
395
432
  data['patterns'] = active
433
+ # 肥大検知ガード(警告のみ・read-only)。誤動作しても Stop を止めないよう
434
+ # 防御的実装に加えて呼び出し側でも try/except で二重に握る(belt-and-suspenders)。
435
+ try:
436
+ _warn_oversized_descriptions(active)
437
+ except Exception:
438
+ pass
396
439
  save_patterns(data)
397
440
 
398
441
  print('[Stop] セッション終了処理が完了しました', file=sys.stderr)
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.45.0] - 2026-07-05
4
+
5
+ ### 追加
6
+
7
+ - **stop.py に patterns.json 肥大検知ガードを新設**: `DESCRIPTION_WARN_LENGTH = 1000` を超える description を持つ既存エントリを、毎 Stop 時に stderr で警告する(削除・切り詰めはしない警告のみ・promoted 含む全件監査)。取り込みガード `MAX_DESCRIPTION_LENGTH=500` は session.tmp 経由の正規記録経路にしか効かず、LLM が patterns.json を直接 Edit する経路の肥大を検知できなかった穴を埋める別レイヤの機械防御(配布元で実際に 1 件 2,000〜3,000 字 × 19 件 = 98KB まで肥大し init-session の Read が上限超過した事故が契機)。fail-safe 二段(防御的実装 + 呼び出し側 try/except)で Stop 処理自体は止めない。
8
+
9
+ ### 修正
10
+
11
+ - **patterns.json に非 dict エントリが混入すると毎 Stop で永続クラッシュする問題を修正**: `update_patterns` の expiry/trust ループと新規観測 ingestion マージループがともに dict 前提で、直接 Edit・マージ事故等で文字列や null が混入すると `AttributeError`/`TypeError` で異常終了していた。クラッシュ地点が `save_patterns` より前のため不正エントリは修復されず、**以降の全 Stop 実行が毎回失敗して学習記録(新規観測・trust 再計算)が更新されない自己修復不能な障害状態**になっていた。両ループに isinstance ガードを追加し、非 dict エントリは保持スキップ(`registered_date` parse 不能時の既存慣行と対称)で完走する。
12
+ - **肥大警告出力の id を無害化**: 直接 Edit 経路由来の id は長さ・型検証を通らないため、`_INHERIT_SANITIZE_RE`(制御文字・U+2028/U+2029 除去)+ `MAX_ID_LENGTH` 切り詰めを適用してから stderr へ出力する(ANSI エスケープ等によるターミナル出力改ざんの防止)。
13
+
14
+ ### 後方互換
15
+
16
+ - `.claude/hooks/stop.py` とそのテストのみの変更で `src/c3/` に変更なし。**破壊的変更なし**。警告は stderr 1 行のみで patterns.json の内容・スキーマ・既存の expiry/trust/promotion_candidate 計算は不変。
17
+
18
+ ## [2.44.0] - 2026-07-04
19
+
20
+ ### 修正
21
+
22
+ - **PreCompact checkpoint の多重発火をデバウンスで冪等化**: Claude Code ハーネスは 1 回の `/compact` で PreCompact hook を複数回起動することがあり(実測 2〜4 回・payload 違い・公式 docs 非記載の undocumented 挙動)、セッションファイルに同一 checkpoint ブロックが堆積していた(実測で 1 日分ファイルの約 8 割がノイズ化し、翌セッション復元時の読み込みコンテキストを浪費)。`pre_compact.py` が追記前に当日セッションファイル末尾の PreCompact checkpoint timestamp を読み、10 秒以内の再起動なら追記をスキップする。timestamp 不在・parse 不能時は fail-open(従来どおり追記)。実装には ReDoS 対策(行長 512 上限・正規表現を使わない手続きパース)・未来日時 checkpoint の棄却(スキュー許容 60 秒・デバウンス恒久停止の防止)・行区切り文字サニタイズ・stderr 診断ログの切り詰め(64 字)を含む。実機 E2E で 1 compact = 1 ブロックを確認済み。
23
+ - **PreCompact hook の仕様外 additionalContext stdout 出力を撤去**: PreCompact は公式仕様上 `hookSpecificOutput.additionalContext` をサポートしない(サポートされるのは top-level `decision: "block"` + `reason` のみ)。C3 は従来この仕様外 JSON を stdout に出力しており、現行ハーネスの `/compact` で「PreCompact hook JSON output validation failed」警告を誘発していた(additionalContext の注入自体もモデルに届いていた保証がない)。`pre_compact.py` を checkpoint 追記専念に変更し、JSON stdout 出力と `SAVE_INSTRUCTION` 定数を削除。セッションファイルの更新は運用ルール(現在地の常時更新・タスク完了ごとの更新)で担保されているため機能上の後退はない。実機で警告の消滅を確認済み。
24
+
25
+ ### 後方互換
26
+
27
+ - `.claude/hooks/pre_compact.py` とそのテストのみの変更で `src/c3/` に変更なし。**破壊的変更なし**。`session_utils.append_checkpoint()` の契約(parallel-agents の wave checkpoint と共用)・`settings.json` の hook 登録は不変。
28
+
3
29
  ## [2.43.0] - 2026-07-03
4
30
 
5
31
  ### 変更
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-conductor
3
- Version: 2.43.0
3
+ Version: 2.45.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
@@ -1,3 +1,3 @@
1
1
  """Claude Code Conductor (C3) - multi-agent orchestration framework for Claude Code."""
2
2
 
3
- __version__ = "2.43.0"
3
+ __version__ = "2.45.0"
@@ -0,0 +1,88 @@
1
+ """
2
+ tests/_pre_compact_helpers.py
3
+
4
+ .claude/hooks/pre_compact.py 系テスト(tests/test_pre_compact.py /
5
+ tests/test_precompact_additional.py)で共通利用するヘルパー。
6
+
7
+ - WORKTREE_ROOT / PRE_COMPACT_PY: pre_compact.py の絶対パス
8
+ - _load_pre_compact_module(): pre_compact.py をモジュールとしてロードする(__main__ 実行なし)
9
+ - _run_main_in_process(): pre_compact.py の main() を in-process 実行し、実 sessions dir に
10
+ 一切触れない(architecture-report-20260704-065052.md §4.2 / §8-1 案 A の in-process 方式)
11
+
12
+ 意図: 旧来は tests/test_pre_compact.py と tests/test_precompact_additional.py の両方で
13
+ `_load_pre_compact_module()` / `_run_main_in_process()` をほぼ同一実装のまま重複定義していた
14
+ (CR-M-001)。前例 `tests/skills/_skill_helpers.py` に倣い、本モジュールへ一本化する。
15
+
16
+ `_run_main_in_process()` の戻り値は `(module, sessions_dir, fake_stdout)` の 3-tuple。
17
+ `test_pre_compact.py` は 3 値すべてを使用する。`test_precompact_additional.py` は
18
+ `fake_stdout` のみを使うため、呼び出し側で `_, _, fake_stdout = _run_main_in_process(...)`
19
+ のように必要な要素だけ取り出す(stdout の値・main() の実行内容は従来と不変更)。
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import importlib.util
25
+ import io
26
+ import json
27
+ import os
28
+ import sys
29
+ import types
30
+ from pathlib import Path
31
+
32
+ import pytest
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # Paths
36
+ # ---------------------------------------------------------------------------
37
+
38
+ WORKTREE_ROOT = Path(__file__).resolve().parent.parent
39
+ PRE_COMPACT_PY = WORKTREE_ROOT / ".claude" / "hooks" / "pre_compact.py"
40
+
41
+
42
+ # ---------------------------------------------------------------------------
43
+ # Helpers
44
+ # ---------------------------------------------------------------------------
45
+
46
+
47
+ def _load_pre_compact_module() -> types.ModuleType:
48
+ """pre_compact.py をモジュールとしてロードする(__main__ 実行なし)。
49
+
50
+ pre_compact.py はモジュールレベルで session_utils を import するため、
51
+ sys.path に hooks ディレクトリを追加してからロードする。
52
+ """
53
+ hooks_dir = str(PRE_COMPACT_PY.parent)
54
+ if hooks_dir not in sys.path:
55
+ sys.path.insert(0, hooks_dir)
56
+ spec = importlib.util.spec_from_file_location("pre_compact", PRE_COMPACT_PY)
57
+ assert spec is not None and spec.loader is not None
58
+ module = importlib.util.module_from_spec(spec)
59
+ spec.loader.exec_module(module) # type: ignore[attr-defined]
60
+ return module
61
+
62
+
63
+ def _run_main_in_process(
64
+ monkeypatch: pytest.MonkeyPatch, tmp_path: Path, payload: dict
65
+ ) -> tuple[types.ModuleType, Path, io.StringIO]:
66
+ """pre_compact.py の main() を in-process 実行し、実 sessions dir に一切触れない。
67
+
68
+ architecture-report-20260704-065052.md §4.2 の in-process 方式(T1 の
69
+ TestMainDebounce と同一パターン):
70
+ - `mod.SESSIONS_DIR` を tmp_path 配下に override
71
+ - `sys.stdin` を JSON payload の StringIO に差し替え
72
+ - `os.getcwd` を tmp_path に固定して非 worktree 経路を保証
73
+ - `sys.stdout` を明示的に StringIO に差し替えて捕捉する
74
+ (capsys は使わない。reconfigure 済み hook では capsys が効かない
75
+ 場合があるため。tests/agent-memory の I-01 パターンに準拠)
76
+ """
77
+ module = _load_pre_compact_module()
78
+ sessions_dir = tmp_path / "sessions"
79
+ sessions_dir.mkdir(parents=True, exist_ok=True)
80
+ monkeypatch.setattr(module, "SESSIONS_DIR", str(sessions_dir))
81
+ monkeypatch.setattr(os, "getcwd", lambda: str(tmp_path))
82
+ monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps(payload)))
83
+ fake_stdout = io.StringIO()
84
+ monkeypatch.setattr(sys, "stdout", fake_stdout)
85
+
86
+ module.main()
87
+
88
+ return module, sessions_dir, fake_stdout