claude-code-conductor 2.66.0__tar.gz → 2.68.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 (269) hide show
  1. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/code-reviewer.md +1 -0
  2. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/developer.md +1 -1
  3. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/security-reviewer.md +1 -0
  4. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/tester.md +2 -1
  5. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/wt_developer.md +1 -1
  6. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/wt_tester.md +2 -1
  7. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/breaking-changes.txt +1 -0
  8. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/_hook_utils.py +22 -0
  9. claude_code_conductor-2.68.0/.claude/hooks/report_contract_check.py +212 -0
  10. claude_code_conductor-2.68.0/.claude/hooks/report_path_guard.py +283 -0
  11. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/settings.json +5 -0
  12. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/SKILL.md +3 -3
  13. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/ARCHITECTURE.md +2 -1
  14. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/CHANGELOG.md +59 -0
  15. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/PKG-INFO +2 -2
  16. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/__init__.py +1 -1
  17. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_hook_utils.py +96 -0
  18. claude_code_conductor-2.68.0/tests/hooks/test_report_path_guard.py +1126 -0
  19. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_report_contract_check.py +390 -10
  20. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_settings_hooks_registration.py +91 -0
  21. claude_code_conductor-2.66.0/.claude/hooks/report_contract_check.py +0 -131
  22. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/.gitignore +0 -0
  23. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/CLAUDE.md +0 -0
  24. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/architect.md +0 -0
  25. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/design-critic.md +0 -0
  26. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/doc-writer.md +0 -0
  27. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/interviewer.md +0 -0
  28. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/planner.md +0 -0
  29. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/project-setup.md +0 -0
  30. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/systematic-debugger.md +0 -0
  31. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/agents/wt_systematic-debugger.md +0 -0
  32. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/deletions.txt +0 -0
  33. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/docs/autonomous-mode-onboarding.md +0 -0
  34. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/docs/config-policy.md +0 -0
  35. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/docs/nul-boundary.md +0 -0
  36. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/docs/parallel-agents-setup.md +0 -0
  37. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/docs/platform-adapters.md +0 -0
  38. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/docs/settings.json.md +0 -0
  39. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/check_agent_invocation.py +0 -0
  40. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/consolidate_memory.py +0 -0
  41. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/patterns_guard.py +0 -0
  42. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/permission_handler.py +0 -0
  43. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/permission_handler_toast.py +0 -0
  44. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/planner_check.py +0 -0
  45. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/post_tool.py +0 -0
  46. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/pre_compact.py +0 -0
  47. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/pre_tool.py +0 -0
  48. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/recall_autorebuild.py +0 -0
  49. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/recall_inject.py +0 -0
  50. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/restore_session.py +0 -0
  51. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/select_tier.py +0 -0
  52. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/session_mode_watch.py +0 -0
  53. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/session_start.py +0 -0
  54. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/session_stop.py +0 -0
  55. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/session_utils.py +0 -0
  56. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/statusline.py +0 -0
  57. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/stop.py +0 -0
  58. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/tier_autoapply.py +0 -0
  59. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/tier_gap_check.py +0 -0
  60. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/hooks/worktree_guard.py +0 -0
  61. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/memory/.gitkeep +0 -0
  62. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/permission_rules.json +0 -0
  63. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/rules/promoted/index.md +0 -0
  64. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/autonomous-mode/SKILL.md +0 -0
  65. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/autonomous-mode/scripts/mode_line.py +0 -0
  66. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/brainstorm/SKILL.md +0 -0
  67. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/codex-review/SKILL.md +0 -0
  68. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/c3-omission-declaration.md +0 -0
  69. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
  70. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
  71. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
  72. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
  73. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
  74. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/reachability-map.md +0 -0
  75. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/record-protocol.md +0 -0
  76. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
  77. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/scripts/detect_execution_verification.py +0 -0
  78. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
  79. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
  80. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
  81. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/develop/SKILL.md +0 -0
  82. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/doc/SKILL.md +0 -0
  83. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/extract-lib/SKILL.md +0 -0
  84. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/init-session/SKILL.md +0 -0
  85. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/init-session/scripts/session_guard.py +0 -0
  86. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/mcp-config/SKILL.md +0 -0
  87. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
  88. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/pattern-status/SKILL.md +0 -0
  89. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
  90. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/recall/SKILL.md +0 -0
  91. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
  92. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
  93. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/review-phase/SKILL.md +0 -0
  94. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/setup/SKILL.md +0 -0
  95. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/setup/reference.md +0 -0
  96. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
  97. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
  98. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/start/SKILL.md +0 -0
  99. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/skills/start/scripts/archive_reports.py +0 -0
  100. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.claude/state/.gitkeep +0 -0
  101. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/.gitignore +0 -0
  102. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/LICENSE +0 -0
  103. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/LICENSES/fastembed-LICENSE +0 -0
  104. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/LICENSES/fastembed-NOTICE +0 -0
  105. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/LICENSES/numpy-LICENSE +0 -0
  106. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/LICENSES/onnxruntime-LICENSE +0 -0
  107. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
  108. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/README.md +0 -0
  109. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/hatch_build.py +0 -0
  110. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/pyproject.toml +0 -0
  111. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/__main__.py +0 -0
  112. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/_db_params.py +0 -0
  113. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/_excludes.py +0 -0
  114. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/_terminal.py +0 -0
  115. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/adapters.py +0 -0
  116. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli.py +0 -0
  117. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_ask.py +0 -0
  118. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_doctor.py +0 -0
  119. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_init.py +0 -0
  120. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_list.py +0 -0
  121. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_metrics.py +0 -0
  122. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_plan.py +0 -0
  123. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_recall.py +0 -0
  124. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_run.py +0 -0
  125. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_tier.py +0 -0
  126. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/cli_update.py +0 -0
  127. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/db.py +0 -0
  128. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/embedding.py +0 -0
  129. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/gitutil.py +0 -0
  130. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/mcp_server.py +0 -0
  131. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrate.py +0 -0
  132. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/001_initial.sql +0 -0
  133. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
  134. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/003_tier_cost.sql +0 -0
  135. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/004_agent_outcomes.sql +0 -0
  136. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
  137. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/006_review_decisions_severity.sql +0 -0
  138. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/007_review_decisions_resolution.sql +0 -0
  139. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/008_drop_agent_runs.sql +0 -0
  140. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/README.md +0 -0
  141. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/migrations/__init__.py +0 -0
  142. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/paths.py +0 -0
  143. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/plan_validator.py +0 -0
  144. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/platforms.py +0 -0
  145. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/pricing.py +0 -0
  146. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/question.py +0 -0
  147. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/recall_chunker.py +0 -0
  148. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/recall_index.py +0 -0
  149. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/refgraph.py +0 -0
  150. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/src/c3/usage_ingester.py +0 -0
  151. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/__init__.py +0 -0
  152. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/_pre_compact_helpers.py +0 -0
  153. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/conftest.py +0 -0
  154. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/fixtures/usage/README.md +0 -0
  155. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/fixtures/usage/mainline.jsonl +0 -0
  156. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
  157. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
  158. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/__init__.py +0 -0
  159. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_check_agent_invocation.py +0 -0
  160. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_consolidate_memory.py +0 -0
  161. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_detect_execution_verification.py +0 -0
  162. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_permission_handler.py +0 -0
  163. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_permission_handler_toast.py +0 -0
  164. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
  165. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_planner_check.py +0 -0
  166. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_planner_check_dev.py +0 -0
  167. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_post_tool.py +0 -0
  168. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_pre_tool.py +0 -0
  169. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_recall_autorebuild.py +0 -0
  170. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_recall_inject.py +0 -0
  171. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_record_agent_outcome.py +0 -0
  172. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_record_review_decision.py +0 -0
  173. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_restore_session.py +0 -0
  174. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_review_hint_inject.py +0 -0
  175. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_select_tier.py +0 -0
  176. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_select_tier_escalation.py +0 -0
  177. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_session_start.py +0 -0
  178. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_session_stop.py +0 -0
  179. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_session_utils.py +0 -0
  180. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
  181. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_settings_tier_autoapply_registration.py +0 -0
  182. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_similarity_boost.py +0 -0
  183. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_statusline.py +0 -0
  184. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_statusline_template_sync.py +0 -0
  185. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_sync_check.py +0 -0
  186. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_template_guard.py +0 -0
  187. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_tier_autoapply.py +0 -0
  188. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_tier_gap_check.py +0 -0
  189. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/hooks/test_tier_security_fixes.py +0 -0
  190. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/__init__.py +0 -0
  191. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/_skill_helpers.py +0 -0
  192. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_c3_omission_declaration.py +0 -0
  193. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
  194. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_init_session_no_task_type.py +0 -0
  195. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_marker_compliance.py +0 -0
  196. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_parallel_agents_skill_tier.py +0 -0
  197. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_planner_lightweight.py +0 -0
  198. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_reachability_map.py +0 -0
  199. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_recall_skill.py +0 -0
  200. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
  201. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_session_guard.py +0 -0
  202. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_setup_templates.py +0 -0
  203. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
  204. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_start_skill_new_flow.py +0 -0
  205. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
  206. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_adapters.py +0 -0
  207. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_archive_reports.py +0 -0
  208. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_audit_review_decisions.py +0 -0
  209. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_check_deletions.py +0 -0
  210. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_ci_workflows.py +0 -0
  211. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_claude_gitignore.py +0 -0
  212. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_ask.py +0 -0
  213. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_doctor.py +0 -0
  214. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_doctor_launcher.py +0 -0
  215. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_entry.py +0 -0
  216. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_init.py +0 -0
  217. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_list.py +0 -0
  218. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_metrics.py +0 -0
  219. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_plan.py +0 -0
  220. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_recall.py +0 -0
  221. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_run.py +0 -0
  222. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_tier.py +0 -0
  223. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_update_breaking_changes.py +0 -0
  224. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_cli_update_deletions.py +0 -0
  225. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_db.py +0 -0
  226. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_db_connect_lint.py +0 -0
  227. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_docstring_consistency.py +0 -0
  228. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_embedding.py +0 -0
  229. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_excludes.py +0 -0
  230. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_extract_breaking_changes.py +0 -0
  231. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_gitutil.py +0 -0
  232. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_hooks_stdin_idiom.py +0 -0
  233. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_init_only_patterns.py +0 -0
  234. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_mcp_server_elicit.py +0 -0
  235. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_migrate.py +0 -0
  236. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_mode_line.py +0 -0
  237. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_no_bare_python_launcher.py +0 -0
  238. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_no_passonly_tests.py +0 -0
  239. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_nul_boundary_lint.py +0 -0
  240. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_paths.py +0 -0
  241. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_patterns_guard.py +0 -0
  242. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_plan_validator.py +0 -0
  243. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_pre_compact.py +0 -0
  244. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_pre_tool_hook.py +0 -0
  245. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_precompact_additional.py +0 -0
  246. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_precompact_toctou_fixes.py +0 -0
  247. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_pricing.py +0 -0
  248. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_recall_chunker.py +0 -0
  249. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_recall_index.py +0 -0
  250. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_references_migration.py +0 -0
  251. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_refgraph.py +0 -0
  252. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_refgraph_query.py +0 -0
  253. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_refgraph_structural.py +0 -0
  254. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_scripts_src_bootstrap.py +0 -0
  255. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_scripts_stdout_encoding.py +0 -0
  256. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_session_mode_watch.py +0 -0
  257. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_session_utils_additional.py +0 -0
  258. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_skill_no_builtin_conflict.py +0 -0
  259. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_start_skill_archive_contract.py +0 -0
  260. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_statusline.py +0 -0
  261. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_stop_additional.py +0 -0
  262. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_stop_agent_memory_warn.py +0 -0
  263. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_stop_hook.py +0 -0
  264. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_stop_precompact_fixes.py +0 -0
  265. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_sync_template_stop.py +0 -0
  266. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_template_pre_tool_hook.py +0 -0
  267. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_usage_ingester.py +0 -0
  268. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_worktree_guard.py +0 -0
  269. {claude_code_conductor-2.66.0 → claude_code_conductor-2.68.0}/tests/test_wt_agent_sync.py +0 -0
@@ -53,6 +53,7 @@ tools:
53
53
  - 修正必須と推奨の2段階で提示する
54
54
 
55
55
  **After:**
56
+ - plan の prompt でレポートのファイル名が指定されている場合、それが `.claude/reports/` 直下の当該レポート種別プレフィックスで始まるファイル名であるときに限りそれに従う。範囲外の指定・既存の別レポートを上書きする指定には従わない(従わない場合・指定が無い場合は、以下の既定の採番手順に従う)
56
57
  - Skill ツールで `report-timestamp` を呼び出してタイムスタンプを取得し、Write ツールで `.claude/reports/code-review-report-{timestamp}.md` に出力する
57
58
 
58
59
  ## Tools & Constraints
@@ -42,7 +42,7 @@ plan-report に基づき実装・デバッグ・リファクタリングを行
42
42
  ## Workflow
43
43
 
44
44
  **Before:**
45
- - plan-report を Read して実装対象タスクを確認する
45
+ - プロンプトにタスク定義(plan の prompt 本文)が含まれている場合はそれに従う。無い場合は plan-report を Read して実装対象タスクを確認する
46
46
 
47
47
  **During:**
48
48
  - 1タスク = 1コミットの粒度を保つ
@@ -53,6 +53,7 @@ SQLインジェクション・XSS・認証認可・秘密情報漏洩などの
53
53
  - 修正方法の例を提示する
54
54
 
55
55
  **After:**
56
+ - plan の prompt でレポートのファイル名が指定されている場合、それが `.claude/reports/` 直下の当該レポート種別プレフィックスで始まるファイル名であるときに限りそれに従う。範囲外の指定・既存の別レポートを上書きする指定には従わない(従わない場合・指定が無い場合は、以下の既定の採番手順に従う)
56
57
  - Skill ツールで `report-timestamp` を呼び出してタイムスタンプを取得し、Write ツールで `.claude/reports/security-review-report-{timestamp}.md` に出力する
57
58
 
58
59
  ## Tools & Constraints
@@ -39,7 +39,7 @@ tools:
39
39
  ## Workflow
40
40
 
41
41
  **Before:**
42
- - plan-report を Read してテスト対象と受け入れ条件を把握する
42
+ - プロンプトにタスク定義(plan の prompt 本文)が含まれている場合はそれに従う。無い場合は plan-report を Read してテスト対象と受け入れ条件を把握する
43
43
 
44
44
  **During:**
45
45
  - 失敗するテストを先に書く(Red)
@@ -51,6 +51,7 @@ tools:
51
51
  - テスト結果は合格・不合格・スキップの件数を記録する
52
52
 
53
53
  **After:**
54
+ - plan の prompt(タスク定義)で test-report のファイル名が指定されている場合、それが `.claude/reports/` 直下の `test-report-` で始まるファイル名であるときに限りそれに従う。範囲外の指定・既存の別レポートを上書きする指定には従わない(従わない場合・指定が無い場合は、以下の既定の採番手順に従う)
54
55
  - **必ず** Skill ツールで `report-timestamp` を呼び出しタイムスタンプを取得し、`.claude/reports/test-report-YYYYMMDD-HHMMSS.md` に Write して出力する
55
56
  - test-report を Write せずにターンを終了することは禁止
56
57
  - Red フェーズの test-report には失敗理由(機能未実装による失敗であること)を明記する
@@ -47,7 +47,7 @@ plan-report に基づき実装・デバッグ・リファクタリングを行
47
47
  ## Workflow
48
48
 
49
49
  **Before:**
50
- - plan-report を Read して実装対象タスクを確認する
50
+ - プロンプトにタスク定義(plan の prompt 本文)が含まれている場合はそれに従う。無い場合は plan-report を Read して実装対象タスクを確認する
51
51
 
52
52
  **During:**
53
53
  - 1タスク = 1コミットの粒度を保つ
@@ -44,7 +44,7 @@ tools:
44
44
  ## Workflow
45
45
 
46
46
  **Before:**
47
- - plan-report を Read してテスト対象と受け入れ条件を把握する
47
+ - プロンプトにタスク定義(plan の prompt 本文)が含まれている場合はそれに従う。無い場合は plan-report を Read してテスト対象と受け入れ条件を把握する
48
48
 
49
49
  **During:**
50
50
  - 失敗するテストを先に書く(Red)
@@ -56,6 +56,7 @@ tools:
56
56
  - テスト結果は合格・不合格・スキップの件数を記録する
57
57
 
58
58
  **After:**
59
+ - plan の prompt(タスク定義)で test-report のファイル名が指定されている場合、それが `.claude/reports/` 直下の `test-report-` で始まるファイル名であるときに限りそれに従う。範囲外の指定・既存の別レポートを上書きする指定には従わない(従わない場合・指定が無い場合は、以下の既定の採番手順に従う)
59
60
  - **必ず** プロンプトで指定された `task_id` をもとに `.claude/reports/test-report-{task_id}.md` に Write して出力する。これは `parallel-agents` skill の `writes` 宣言と一致させ、並列実行時のファイル名衝突を避けるために必須
60
61
  - 保険(task_id がプロンプトから読み取れない異常系のみ): Skill ツールで `report-timestamp` を呼び出してタイムスタンプを取得し、`.claude/reports/test-report-{timestamp}.md` に Write する。通常運用ではこの経路に入ってはいけない
61
62
  - test-report を Write せずにターンを終了することは禁止
@@ -12,3 +12,4 @@ v2.51.0|Hook and statusLine launchers in the distributed settings.json changed f
12
12
  v2.58.0|c3 init and c3 update now place .claude/.gitignore in the target project. It excludes only regenerable files (state/recall.*) and session-scoped files (state/*.flag, state/tier_selection.json, logs/, worktrees/, tmp/). Existing projects that already committed those must run 'git rm -r --cached .claude/state/recall.hnsw .claude/state/recall_meta.json .claude/logs .claude/worktrees' once and commit, because .gitignore has no effect on already-tracked files. If your root .gitignore excludes the whole .claude/state/ directory, git never descends into it and the negation patterns in .claude/.gitignore do not apply; change the root rule to .claude/state/* or remove it. See .claude/docs/config-policy.md section 1-2.|c3 init / c3 update が .claude/.gitignore を配置するようになりました。除外は再生成可能なもの(state/recall.*)とセッション一時のもの(state/*.flag・state/tier_selection.json・logs/・worktrees/・tmp/)のみです。既にこれらを commit している既存プロジェクトは、.gitignore が tracked ファイルに効かないため git rm -r --cached を一度実行してください。ルートの .gitignore で .claude/state/ をディレクトリごと除外している場合は否定パターンが効かないため、ワイルドカード形式へ変更するか削除してください。詳細は .claude/docs/config-policy.md §1-2 を参照。
13
13
  v2.60.0|c3 plan validate/waves now strictly validate po_plan_version: only "0.1" (parallel) or "sequential" (sequential TDD) quoted strings are accepted; unknown or non-string values fail with exit 2. Migrate existing plan-reports by setting po_plan_version: "0.1" or "sequential" (quoted).|po_plan_version の値を厳格検査("0.1"/"sequential" のみ受理・他は exit 2)。既存 plan-report は引用符付きの正値へ修正する。
14
14
  v2.66.0|record_review_decision.py: --severity is now a required argument (omitting it exits 2 and records nothing; out-of-vocabulary values still fail-safe to NULL with a warning). --finding now rejects empty or whitespace-only values (exit 2). Update any custom invocations to pass --severity with one of critical/high/medium/low. Shipped SKILL.md call sites and docstrings are already updated.|
15
+ v2.68.0|New PreToolUse Write hook report_path_guard.py is registered in the distributed settings.json. It blocks (exit 2) writes of report-prefixed filenames (requirements-report-, architecture-report-, plan-report-, design-review-report-, code-review-report-, security-review-report-, test-report-, debug-analysis-, debug-needed-) outside .claude/reports/, overwrites of existing timestamp-named reports, and non-timestamp naming for the strict 4 report types. Ordinary files whose names start with those prefixes can no longer be written outside .claude/reports/. Set C3_REPORT_GUARD_DISABLE=1 to bypass, or remove the hook registration from settings.json.|レポート 9 prefix 名ファイルの .claude/reports/ 外への Write・タイムスタンプ形式既存レポートの上書き・strict-4 の非タイムスタンプ命名を exit 2 で block する report_path_guard hook を配布 settings.json に登録。対象 prefix 名の一般ファイルも block されうる。回避は C3_REPORT_GUARD_DISABLE=1 または settings.json から登録を外す。
@@ -13,6 +13,11 @@ PYTHONPATH に追加してから `from _hook_utils import ...` する経路を
13
13
  fail-safe な書き込みヘルパー。
14
14
  - ``sanitize_for_terminal(text)`` — 端末出力(stderr / stdout JSON)へ載せる前に
15
15
  C0 + DEL + C1 制御文字に加え、双方向制御・ゼロ幅・行区切り文字を除去するヘルパー。
16
+ - ``STRICT4_PREFIXES`` — タイムスタンプ形式のみ許容する prefix の tuple。
17
+ ``timestamp_pattern()`` と組み合わせて使用(report_path_guard / report_contract_check)。
18
+ - ``timestamp_pattern(prefix)`` — ``{prefix}YYYYMMDD-HHMMSS.md`` のフルマッチ用
19
+ 正規表現(``re.compile`` オブジェクト)を返す。全角数字偽装防止のため ``re.ASCII``
20
+ を指定。
16
21
  """
17
22
 
18
23
  from __future__ import annotations
@@ -54,6 +59,15 @@ _TERMINAL_SANITIZE_RE = re.compile(
54
59
  + r"]"
55
60
  )
56
61
 
62
+ # strict-4: タイムスタンプ形式のみ許容する prefix の tuple。
63
+ # report_path_guard.py / report_contract_check.py が共有する。
64
+ STRICT4_PREFIXES = (
65
+ 'requirements-report-',
66
+ 'architecture-report-',
67
+ 'plan-report-',
68
+ 'design-review-report-',
69
+ )
70
+
57
71
 
58
72
  # NOTE: 同一ロジックが src/c3/cli_update.py::_validate_deletion_path の step 13 に
59
73
  # 複製されている(import 経路がないため)。変更時は両方を揃えること。
@@ -79,6 +93,14 @@ def sanitize_for_terminal(text: str) -> str:
79
93
  return _TERMINAL_SANITIZE_RE.sub("", str(text))
80
94
 
81
95
 
96
+ def timestamp_pattern(prefix: str) -> re.Pattern:
97
+ """``{prefix}YYYYMMDD-HHMMSS.md`` のフルマッチ用パターンを返す。
98
+
99
+ re.ASCII を指定してタイムスタンプを ASCII 数字に限定し、全角数字による偽装を防止する。
100
+ """
101
+ return re.compile(re.escape(prefix) + r'\d{8}-\d{6}\.md', re.ASCII)
102
+
103
+
82
104
  def write_debug_log(log_path: Path, line: str) -> None:
83
105
  """``C3_HOOK_DEBUG=1`` のとき、ログファイルに ``ISO8601 line`` を 1 行追記する。
84
106
 
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env python3
2
+ """PostToolUse hook: タイムスタンプ報告契約の検査(P2・配布対象)。
3
+
4
+ `.claude/reports/` 直下への Write で、以下 2 レジームのファイル名契約逸脱を warn する。
5
+
6
+ **strict-4(タイムスタンプ形式のみ許容)** — 以下 4 prefix:
7
+ - requirements-report-
8
+ - architecture-report-
9
+ - plan-report-
10
+ - design-review-report-
11
+
12
+ 判定は basename 全体のフルマッチ `^{prefix}\\d{8}-\\d{6}\\.md$` で行う
13
+ (prefix 一致と suffix 一致の独立 2 条件ではない。`plan-report-badname-extra-20260725-180252.md`
14
+ のように prefix と タイムスタンプの間へ任意文字列を挟む名前も逸脱として検出するため)。
15
+
16
+ **loose-3(タイムスタンプ形式 または task_id 形式のどちらかを許容=緩検査)** — 以下 3 prefix:
17
+ - code-review-report-
18
+ - security-review-report-
19
+ - test-report-
20
+
21
+ CR / SR / test レポートは「タイムスタンプ採番」と「task_id 採番(並列 wave のファイル名衝突
22
+ 回避)」の 2 レジームを持つため、どちらか一方を強制しない。以下 2 形式のいずれかに一致すれば
23
+ 沈黙し、どちらにも一致しなければ警告する:
24
+ - タイムスタンプ形式: `^{prefix}\\d{8}-\\d{6}\\.md$`
25
+ - task_id 形式: `^{prefix}[英数 . _ -]{1,200}\\.md$`
26
+
27
+ `debug-analysis-` / `debug-needed-` はどちらのレジームにも属さず警告対象外。
28
+ 不一致の場合は 2 経路で警告: stderr(人間向け)+ stdout JSON(LLM向け)。
29
+
30
+ ## ディレクトリ判定とケース非依存化
31
+
32
+ reports ディレクトリの判定は `.claude/reports/` 直下かどうかを名前ベースで確認する。
33
+ Windows・macOS の既定(ケース非依存 FS)では `.CLAUDE` や `REPORTS` は `.claude` / `reports` と
34
+ 同一実体の別名であり、名前比較は `.lower()` を使用してケース非依存で行う。
35
+ basename の判定は 2 レジームとも厳密に行う(`re.IGNORECASE` 不使用・`re.ASCII` で Unicode 偽装を
36
+ 防止)。strict-4 は prefix・タイムスタンプ・`.md` の厳密フルマッチ、loose-3 は
37
+ タイムスタンプ形式 または task_id 形式(許容文字は ASCII 英数と `.` `_` `-` のみ)の
38
+ どちらかへの厳密フルマッチであり、緩いのは「許容形式が 2 つある」点のみで
39
+ 各形式の照合自体は厳密である。
40
+
41
+ ## 出力契約(PostToolUse warn の標準形)
42
+
43
+ exit 0 でも stdout JSON が LLM コンテキストへ additionalContext として注入される。
44
+ stderr は人間にのみ届く。詳細は Claude Code 公式仕様を参照:
45
+ https://code.claude.com/docs/en/hooks
46
+ """
47
+
48
+ import json
49
+ import os
50
+ import re
51
+ import sys
52
+ from pathlib import Path
53
+
54
+ # 共通ヘルパー (_hook_utils.sanitize_for_terminal, norm_component, STRICT4_PREFIXES,
55
+ # timestamp_pattern) を hooks/ 経由で import するため、このスクリプトのディレクトリを
56
+ # PYTHONPATH に追加する。
57
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
58
+ from _hook_utils import ( # noqa: E402
59
+ norm_component,
60
+ sanitize_for_terminal,
61
+ STRICT4_PREFIXES,
62
+ timestamp_pattern,
63
+ )
64
+
65
+ try:
66
+ sys.stdin.reconfigure(encoding='utf-8')
67
+ sys.stdout.reconfigure(encoding='utf-8')
68
+ sys.stderr.reconfigure(encoding='utf-8')
69
+ except AttributeError:
70
+ pass
71
+
72
+ # strict-4: STRICT4_PREFIXES は _hook_utils から import。
73
+ # loose-3: タイムスタンプ形式 または task_id 形式のどちらかを許容する prefix。
74
+ # 注: タイムスタンプ形式(YYYYMMDD-HHMMSS)は task_id 形式の真部分集合であり、
75
+ # 判定としては task_id 形式 1 本で足りる(2 形式併記は利用者向けの説明のため)。
76
+ LOOSE3_PREFIXES = (
77
+ 'code-review-report-',
78
+ 'security-review-report-',
79
+ 'test-report-',
80
+ )
81
+
82
+ # task_id の許容文字集合と長さ上限(英数と `.` `_` `-` のみ 1〜200 文字)。
83
+ TASK_ID_MAX_LEN = 200
84
+
85
+
86
+ def task_id_pattern(prefix):
87
+ """`{prefix}{task_id}.md` のフルマッチ用パターンを返す。
88
+
89
+ task_id は ASCII 英数と `.` `_` `-` のみ 1〜TASK_ID_MAX_LEN 文字。
90
+ 文字集合をリテラルで ASCII に限定したうえで、`re.ASCII` も明示して意図を固定する。
91
+ """
92
+ return re.compile(
93
+ re.escape(prefix) + r'[A-Za-z0-9._-]{1,' + str(TASK_ID_MAX_LEN) + r'}\.md',
94
+ re.ASCII,
95
+ )
96
+
97
+
98
+ def emit_warning(basename, expected_format, stderr_hint, context_hint):
99
+ """契約逸脱を 2 経路(stderr = 人間向け / stdout JSON = LLM 向け)で警告する。
100
+
101
+ basename は外部由来のため、両経路とも sanitize_for_terminal() を通してから出力する。
102
+ strict-4 / loose-3 で期待形式・採番案内の文面のみが変わり、出力の骨格は共通。
103
+ """
104
+ safe_basename = sanitize_for_terminal(basename)
105
+
106
+ # stderr(人間向け)
107
+ print(
108
+ f'[ReportContract WARN] {safe_basename} '
109
+ f'のファイル名形式が違反しています。'
110
+ f'期待形式: {expected_format}\n'
111
+ f'{stderr_hint}',
112
+ file=sys.stderr
113
+ )
114
+
115
+ # stdout JSON(LLM向け)
116
+ context = (
117
+ f'ファイル名: {safe_basename}\n'
118
+ f'期待形式: {expected_format}\n'
119
+ f'採番方法: {context_hint}'
120
+ )
121
+ output = {
122
+ 'hookSpecificOutput': {
123
+ 'hookEventName': 'PostToolUse',
124
+ 'additionalContext': context
125
+ }
126
+ }
127
+ print(json.dumps(output), file=sys.stdout)
128
+
129
+
130
+ def main():
131
+ # JSON payload の読み込み(fail-open)
132
+ try:
133
+ payload = json.loads(sys.stdin.read())
134
+ except (json.JSONDecodeError, ValueError):
135
+ sys.exit(0)
136
+
137
+ # tool_name の確認(Write 以外は対象外=新規命名は Write でのみ発生)
138
+ tool_name = payload.get('tool_name', '')
139
+ if tool_name != 'Write':
140
+ sys.exit(0)
141
+
142
+ # file_path の抽出(キー欠落は fail-open)
143
+ file_path = payload.get('tool_input', {}).get('file_path', '')
144
+ if not file_path:
145
+ sys.exit(0)
146
+
147
+ # `.claude/reports/` 直下かどうかの判定
148
+ path_obj = Path(file_path)
149
+ basename = path_obj.name
150
+ parent = path_obj.parent
151
+
152
+ # 構造判定: 親が `reports` かつ祖父が `.claude`(= `.claude/reports/` 直下)。
153
+ # 上流に同名の `reports/` があっても誤アンカーしない(resolve は使わない)。
154
+ # ケース非依存 FS(Windows・macOS)では `.CLAUDE` や `REPORTS` は `.claude` / `reports` と
155
+ # 同一実体の別名のため、比較は norm_component() で正規化してから行う。
156
+ # Windows の末尾ドット・スペースも norm_component() が除去する。
157
+ # 例: `/foo/.claude/reports/plan-report-20260725-180252.md` → 対象
158
+ # 例: `/foo/.claude/reports/archive/plan-report-final.md` → 対象外(直下でない)
159
+ if norm_component(parent.name) != 'reports' or norm_component(parent.parent.name) != '.claude':
160
+ sys.exit(0)
161
+
162
+ # basename を正規化(ケース非依存化・末尾ドット/スペース除去)してから prefix 判定。
163
+ # report_path_guard.py と同じポリシーで統一。
164
+ # **重要**: prefix 判定は norm_basename で行うが、形式マッチング(拡張子検証含む)は
165
+ # 元の basename に対して行う。これにより大文字混じり prefix(Test-Report-)は検出
166
+ # しつつ、`.MD` 拡張子のような大文字混じりは厳密に reject できる。
167
+ norm_basename = norm_component(basename)
168
+
169
+ # strict-4: 対象 prefix の確認 → basename 全体のフルマッチ確認
170
+ # (`{prefix}YYYYMMDD-HHMMSS.md` のみ許容)
171
+ for prefix in STRICT4_PREFIXES:
172
+ if not norm_basename.startswith(prefix):
173
+ continue
174
+ # 形式マッチングは元の basename に対して行う(`.md` 拡張子の厳密化)
175
+ if timestamp_pattern(prefix).fullmatch(basename):
176
+ # 正しい形式
177
+ sys.exit(0)
178
+ emit_warning(
179
+ basename,
180
+ '{prefix}YYYYMMDD-HHMMSS.md',
181
+ '採番は report-timestamp skill を使用してください。',
182
+ 'report-timestamp skill で取得したタイムスタンプを使用してください。',
183
+ )
184
+ sys.exit(0)
185
+
186
+ # loose-3: 対象 prefix の確認 → 2 形式のいずれかへのフルマッチ確認
187
+ # (`{prefix}YYYYMMDD-HHMMSS.md` または `{prefix}{task_id}.md`)
188
+ for prefix in LOOSE3_PREFIXES:
189
+ if not norm_basename.startswith(prefix):
190
+ continue
191
+ # 形式マッチングは元の basename に対して行う(`.md` 拡張子の厳密化)
192
+ if (timestamp_pattern(prefix).fullmatch(basename)
193
+ or task_id_pattern(prefix).fullmatch(basename)):
194
+ # どちらかの正しい形式
195
+ sys.exit(0)
196
+ emit_warning(
197
+ basename,
198
+ '{prefix}YYYYMMDD-HHMMSS.md または {prefix}{task_id}.md'
199
+ f'(task_id は英数と . _ - のみ 1〜{TASK_ID_MAX_LEN} 文字)',
200
+ 'タイムスタンプ採番は report-timestamp skill を、'
201
+ 'task_id 採番は plan-report の task_id を使用してください。',
202
+ 'タイムスタンプ形式なら report-timestamp skill で取得した値を、'
203
+ 'task_id 形式なら plan-report の task_id を使用してください。',
204
+ )
205
+ sys.exit(0)
206
+
207
+ # どちらのレジームにも属さない prefix(debug 系・自由域)は対象外
208
+ sys.exit(0)
209
+
210
+
211
+ if __name__ == '__main__':
212
+ main()
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env python3
2
+ """PreToolUse hook: レポートの書き先パスを守るガード(P4・配布対象)。
3
+
4
+ Write ツールで作成されるレポートファイル(対象 9 prefix)について、
5
+ 書き先が `.claude/reports/` 直下であること・既存レポートを上書きしないこと・
6
+ strict-4 prefix がタイムスタンプ命名であることを検査し、逸脱を exit 2 でブロックする。
7
+
8
+ ## 発火条件
9
+
10
+ `tool_name` == "Write" かつ、basename を `_hook_utils.norm_component()`
11
+ (lower + 末尾ドット/スペース除去)で正規化した値が対象 9 prefix のいずれかで
12
+ 始まる場合のみ。非対象 basename・Write 以外は沈黙(exit 0・stderr 空)。
13
+
14
+ ## 判定入力の一本化
15
+
16
+ target = `file_path` が絶対ならそのまま、相対なら `os.getcwd()` と結合したパス。
17
+ 第 1 層・第 2 層とも同じ target を判定する(判定対象が経路によってブレないようにする)。
18
+
19
+ ## 検査 1: 封じ込め(二層)
20
+
21
+ - 第 1 層(名前判定): target の親が `reports`・祖父が `.claude`
22
+ (`norm_component` 正規化で比較。ケース非依存 FS の別名・末尾ドットを吸収する)
23
+ - 第 2 層(実体照合): `realpath(target)` の親ディレクトリが許可 root と
24
+ `os.path.samefile` で一致すること
25
+
26
+ 許可 root は 2 つ:
27
+ - root A = `realpath(os.getcwd())/.claude/reports`(一次)
28
+ - root B = `realpath($CLAUDE_PROJECT_DIR)/.claude/reports`(env が非空のときのみ)
29
+
30
+ env 未設定・空なら root A のみで判定する(縮退。root B が無いことを理由に block しない)。
31
+ 両層 AND で許可し、不成立・両 root 不在・親不在は block(fail-closed)。
32
+
33
+ ### 免責(脅威モデル外)
34
+
35
+ 許可 root ディレクトリ自体(`.claude/reports` そのもの)が外部へのリンクに
36
+ 差し替えられているケースは、expected(root A/B)と resolved(target の親)が
37
+ 同一実体になるため block しない。root そのものを差し替えられる権限を持つ相手は
38
+ 本 hook の防御対象ではなく、ここで block すると正規運用(reports を別ボリュームへ
39
+ 逃がす構成)を壊すだけになる。
40
+
41
+ ## 判定の分割(正規化 basename と元 basename)
42
+
43
+ report_contract_check.py と統一したポリシー: **prefix 一致判定は
44
+ `norm_component()` 正規化後の basename・形式(timestamp フルマッチ)判定は
45
+ 元の basename** に対して行う。これにより大文字混じり prefix(`Plan-Report-`)は
46
+ prefix 一致として検出しつつ、`.MD` のような大文字混じり拡張子は timestamp 形式として
47
+ 誤許可しない(ケース非依存 FS では正規名と同一実体になりうるため、この場合は
48
+ strict-4 なら検査 3 で block・非 strict-4 なら通常の新規作成として許可される)。
49
+
50
+ ## 検査 2: 上書き block
51
+
52
+ 元の basename が timestamp 形式(prefix + `\\d{8}-\\d{6}\\.md` の `re.ASCII`
53
+ フルマッチ)かつ書き込み先実体が既存 → block。task_id 形式(非 timestamp)の
54
+ 既存上書きは許可する(同一 task の再実行で更新する正規経路のため)。
55
+
56
+ ## 検査 3: strict-4 形式 block
57
+
58
+ strict-4 prefix(requirements / architecture / plan / design-review)に一致する
59
+ (正規化 basename が prefix で始まる)ファイルの、元の basename が timestamp 形式に
60
+ フルマッチしない場合は新規作成でも block。CR / SR / test / debug 系は
61
+ task_id 命名が正規のため非適用。
62
+
63
+ ## fail 方針
64
+
65
+ - 壊れた stdin JSON / payload 非オブジェクト / キー欠落 / 空 file_path は fail-open
66
+ (対象かどうかすら判定できない入力で他の Write を巻き込まない)
67
+ - 対象と判定した後のパス判定で例外を誘発する入力・NUL 混入は fail-closed(block)
68
+
69
+ ## bypass
70
+
71
+ 環境変数 `C3_REPORT_GUARD_DISABLE=1` で全検査をスキップする。
72
+ """
73
+
74
+ import json
75
+ import os
76
+ import re
77
+ import sys
78
+ from pathlib import Path
79
+
80
+ # 共通ヘルパー (_hook_utils.norm_component, sanitize_for_terminal, STRICT4_PREFIXES,
81
+ # timestamp_pattern) を hooks/ 経由で import するため、このスクリプトのディレクトリを
82
+ # PYTHONPATH に追加する。
83
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
84
+ from _hook_utils import ( # noqa: E402
85
+ norm_component,
86
+ sanitize_for_terminal,
87
+ STRICT4_PREFIXES,
88
+ timestamp_pattern,
89
+ )
90
+
91
+ try:
92
+ sys.stdin.reconfigure(encoding='utf-8')
93
+ sys.stdout.reconfigure(encoding='utf-8')
94
+ sys.stderr.reconfigure(encoding='utf-8')
95
+ except AttributeError:
96
+ pass
97
+
98
+ # 対象 9 prefix(正規化済み basename に対して startswith で判定する)
99
+ # strict-4 の先頭 4 件は _hook_utils.STRICT4_PREFIXES から導出し、二重管理を避ける。
100
+ TARGET_PREFIXES = STRICT4_PREFIXES + (
101
+ 'code-review-report-',
102
+ 'security-review-report-',
103
+ 'test-report-',
104
+ 'debug-analysis-',
105
+ 'debug-needed-',
106
+ )
107
+
108
+ REPORTS_DIR_NAME = 'reports'
109
+ CLAUDE_DIR_NAME = '.claude'
110
+ PROJECT_DIR_ENV = 'CLAUDE_PROJECT_DIR'
111
+ DISABLE_ENV = 'C3_REPORT_GUARD_DISABLE'
112
+
113
+ NUL = '\x00'
114
+
115
+
116
+ def _match_prefix(norm_basename):
117
+ """正規化済み basename が対象 9 prefix のどれで始まるかを返す(無ければ None)。"""
118
+ for prefix in TARGET_PREFIXES:
119
+ if norm_basename.startswith(prefix):
120
+ return prefix
121
+ return None
122
+
123
+
124
+ def _is_timestamp_name(basename, prefix):
125
+ """`{prefix}YYYYMMDD-HHMMSS.md` のフルマッチか。
126
+
127
+ _hook_utils.timestamp_pattern() へ委譲。**元の(正規化前の)basename** に対して
128
+ 判定する(report_contract_check.py と統一。prefix 一致は正規化後 basename で行い、
129
+ 形式判定は元の basename で行う分割により、`.MD` 等の大文字混じり拡張子を
130
+ timestamp 形式として誤許可しない)。
131
+ """
132
+ return timestamp_pattern(prefix).fullmatch(basename) is not None
133
+
134
+
135
+ def _allowed_roots():
136
+ """許可 root(root A / root B)の候補パスを返す。
137
+
138
+ root B は `CLAUDE_PROJECT_DIR` が非空のときのみ加える。存在確認はここでは行わず、
139
+ 照合側(samefile)に委ねる。
140
+ """
141
+ roots = [
142
+ os.path.join(
143
+ os.path.realpath(os.getcwd()), CLAUDE_DIR_NAME, REPORTS_DIR_NAME
144
+ )
145
+ ]
146
+ project_dir = os.environ.get(PROJECT_DIR_ENV, '')
147
+ if project_dir:
148
+ roots.append(
149
+ os.path.join(
150
+ os.path.realpath(project_dir), CLAUDE_DIR_NAME, REPORTS_DIR_NAME
151
+ )
152
+ )
153
+ return roots
154
+
155
+
156
+ def _is_contained(target):
157
+ """target が許可 root 直下に封じ込められているか(二層 AND)。"""
158
+ # 第 1 層: 名前判定(`.claude/reports/` 直下の形をしているか)。
159
+ # `..` を含む脱出パスは親の名前が `..` になるためここで落ちる。
160
+ parent = target.parent
161
+ if norm_component(parent.name) != REPORTS_DIR_NAME:
162
+ return False
163
+ if norm_component(parent.parent.name) != CLAUDE_DIR_NAME:
164
+ return False
165
+
166
+ # 第 2 層: 実体照合(リンク偽装で名前だけ揃えた経路をここで落とす)。
167
+ resolved_parent = os.path.dirname(os.path.realpath(str(target)))
168
+ if NUL in resolved_parent:
169
+ return False
170
+ for root in _allowed_roots():
171
+ try:
172
+ if os.path.samefile(resolved_parent, root):
173
+ return True
174
+ except OSError:
175
+ # root 不在・親不在は「一致しない」として扱う(fail-closed)。
176
+ continue
177
+ return False
178
+
179
+
180
+ def _block(kind_line, guidance):
181
+ """違反種別ごとのメッセージを stderr へ出して exit 2 する。"""
182
+ print(
183
+ f'[ReportPathGuard BLOCK] {kind_line}\n'
184
+ f'{guidance}\n'
185
+ f'恒久 bypass: {DISABLE_ENV}=1 環境変数を設定してください',
186
+ file=sys.stderr,
187
+ )
188
+ sys.exit(2)
189
+
190
+
191
+ def _block_containment(display_name):
192
+ _block(
193
+ f'封じ込め違反: {display_name} の書き先が許可された reports ディレクトリの外です。',
194
+ 'レポートは .claude/reports/ 直下に作成してください。\n'
195
+ '書き先パスをその形へ修正し、cwd がプロジェクトルート外なら'
196
+ 'プロジェクトルートへ cd してから再実行してください。',
197
+ )
198
+
199
+
200
+ def _block_overwrite(display_name):
201
+ _block(
202
+ f'上書き違反: {display_name} は既存のタイムスタンプ形式レポートです。',
203
+ '既存レポートの上書きは禁止です。'
204
+ 'report-timestamp skill で新しいタイムスタンプを再採番し、'
205
+ '別名のファイルとして作成してください。',
206
+ )
207
+
208
+
209
+ def _block_format(display_name, prefix):
210
+ _block(
211
+ f'形式違反: {display_name} は {prefix}YYYYMMDD-HHMMSS.md の形式ではありません。',
212
+ 'report-timestamp skill でタイムスタンプを再採番し、'
213
+ 'その値をファイル名に使用してください。',
214
+ )
215
+
216
+
217
+ def main():
218
+ # bypass: 恒久 disable 環境変数の確認(`1` 以外の値では無効化しない)
219
+ if os.environ.get(DISABLE_ENV) == '1':
220
+ sys.exit(0)
221
+
222
+ # JSON payload の読み込み(fail-open)
223
+ try:
224
+ payload = json.loads(sys.stdin.read())
225
+ except (json.JSONDecodeError, ValueError):
226
+ sys.exit(0)
227
+ if not isinstance(payload, dict):
228
+ sys.exit(0)
229
+
230
+ # tool_name の確認(Write 以外は対象外)
231
+ if payload.get('tool_name') != 'Write':
232
+ sys.exit(0)
233
+
234
+ # file_path の抽出(キー欠落・空文字は fail-open)
235
+ tool_input = payload.get('tool_input')
236
+ if not isinstance(tool_input, dict):
237
+ sys.exit(0)
238
+ file_path = tool_input.get('file_path')
239
+ if not isinstance(file_path, str) or not file_path:
240
+ sys.exit(0)
241
+
242
+ # 発火条件: 正規化 basename が対象 9 prefix で始まるか。
243
+ # ここまでは純粋な文字列操作であり、対象外なら一切副作用を出さずに抜ける。
244
+ raw_basename = os.path.basename(file_path)
245
+ norm_basename = norm_component(raw_basename)
246
+ prefix = _match_prefix(norm_basename)
247
+ if prefix is None:
248
+ sys.exit(0)
249
+
250
+ display_name = sanitize_for_terminal(raw_basename)
251
+
252
+ # ここから先はパス判定。例外を誘発する入力は fail-closed で block する。
253
+ try:
254
+ if os.path.isabs(file_path):
255
+ target = Path(file_path)
256
+ else:
257
+ target = Path(os.getcwd()) / file_path
258
+
259
+ # NUL 混入は realpath / samefile の挙動が環境依存になるため明示 block
260
+ if NUL in str(target):
261
+ _block_containment(display_name)
262
+
263
+ # 検査 1: 封じ込め
264
+ if not _is_contained(target):
265
+ _block_containment(display_name)
266
+
267
+ is_timestamp = _is_timestamp_name(raw_basename, prefix)
268
+
269
+ # 検査 2: 上書き block(timestamp 形式の既存のみ)
270
+ if is_timestamp and os.path.exists(str(target)):
271
+ _block_overwrite(display_name)
272
+
273
+ # 検査 3: strict-4 形式 block
274
+ if prefix in STRICT4_PREFIXES and not is_timestamp:
275
+ _block_format(display_name, prefix)
276
+ except (OSError, ValueError):
277
+ _block_containment(display_name)
278
+
279
+ sys.exit(0)
280
+
281
+
282
+ if __name__ == '__main__':
283
+ main()
@@ -87,6 +87,11 @@
87
87
  "type": "command",
88
88
  "command": "c3",
89
89
  "args": ["run", "${CLAUDE_PROJECT_DIR}/.claude/hooks/patterns_guard.py"]
90
+ },
91
+ {
92
+ "type": "command",
93
+ "command": "c3",
94
+ "args": ["run", "${CLAUDE_PROJECT_DIR}/.claude/hooks/report_path_guard.py"]
90
95
  }
91
96
  ]
92
97
  },
@@ -566,7 +566,7 @@ legacy TDD モードまたは parallel-agents モードで実装される。
566
566
 
567
567
  ### D-1: tester(Red フェーズ)
568
568
 
569
- Agent ツールで `tester` エージェントを起動する。→ 失敗するテストを先に作成する。**必ず `.claude/reports/test-report-YYYYMMDD-HHMMSS.md` を Write してから終了すること。**
569
+ Agent ツールで `tester` エージェントを起動する。→ 失敗するテストを先に作成する。**必ず `.claude/reports/test-report-YYYYMMDD-HHMMSS.md` を Write してから終了すること。**(plan の prompt でファイル名が指定されている場合は、tester agent 定義 After の但し書き〔封じ込め条件付き〕に従いそちらを優先する)
570
570
 
571
571
  **Red 起動プロンプトのマーカー規約(必須)**: tester を Agent ツールで起動するプロンプトの**1 行目(文字列先頭)**に、以下の機械可読マーカー行を**必須**で 1 行含める(省略可能な推奨ではない・parallel 経路の同型マーカーの逐次版)。`tier_autoapply.py` の抽出正規表現は文字列先頭アンカー `\A` のため、2 行目以降・本文中・フェンス内に置くと抽出されず注入されない(SR-AI-001):
572
572
 
@@ -652,7 +652,7 @@ c3 run .claude/skills/dev-workflow/scripts/record_agent_outcome.py \
652
652
 
653
653
  ### D-3: tester(確認)
654
654
 
655
- Agent ツールで `tester` エージェントを起動する。→ 全テストの合否を確認する。**必ず `.claude/reports/test-report-YYYYMMDD-HHMMSS.md` を Write してから終了すること。**
655
+ Agent ツールで `tester` エージェントを起動する。→ 全テストの合否を確認する。**必ず `.claude/reports/test-report-YYYYMMDD-HHMMSS.md` を Write してから終了すること。**(plan の prompt でファイル名が指定されている場合は、tester agent 定義 After の但し書き〔封じ込め条件付き〕に従いそちらを優先する)
656
656
 
657
657
  AskUserQuestion で確認する:
658
658
  ```json
@@ -705,7 +705,7 @@ Agent ツールで `developer` エージェントを起動する(`model:` は
705
705
 
706
706
  ### D-5: tester(最終確認)
707
707
 
708
- Agent ツールで `tester` エージェントを起動する。**必ず `.claude/reports/test-report-YYYYMMDD-HHMMSS.md` を Write してから終了すること。**
708
+ Agent ツールで `tester` エージェントを起動する。**必ず `.claude/reports/test-report-YYYYMMDD-HHMMSS.md` を Write してから終了すること。**(plan の prompt でファイル名が指定されている場合は、tester agent 定義 After の但し書き〔封じ込め条件付き〕に従いそちらを優先する)
709
709
 
710
710
  AskUserQuestion で確認する:
711
711
  ```json