claude-code-conductor 2.53.0__tar.gz → 2.55.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.
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/_hook_utils.py +25 -0
- claude_code_conductor-2.55.0/.claude/hooks/patterns_guard.py +126 -0
- claude_code_conductor-2.55.0/.claude/hooks/report_contract_check.py +130 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/select_tier.py +74 -9
- claude_code_conductor-2.55.0/.claude/hooks/session_mode_watch.py +275 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/tier_autoapply.py +82 -8
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/tier_gap_check.py +4 -3
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/settings.json +20 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/SKILL.md +48 -5
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +113 -22
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/parallel-agents/SKILL.md +41 -16
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/promote-pattern/SKILL.md +7 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/ARCHITECTURE.md +4 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/CHANGELOG.md +38 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/PKG-INFO +21 -2
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/README.md +20 -1
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/__init__.py +1 -1
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/_db_params.py +32 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/db.py +35 -18
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_hook_utils.py +132 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_record_agent_outcome.py +243 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_select_tier.py +143 -2
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_tier_autoapply.py +296 -0
- claude_code_conductor-2.55.0/tests/skills/test_marker_compliance.py +153 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_tier.py +4 -1
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_db.py +151 -0
- claude_code_conductor-2.55.0/tests/test_patterns_guard.py +598 -0
- claude_code_conductor-2.55.0/tests/test_report_contract_check.py +620 -0
- claude_code_conductor-2.55.0/tests/test_session_mode_watch.py +825 -0
- claude_code_conductor-2.55.0/tests/test_settings_hooks_registration.py +292 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/design-critic.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/breaking-changes.txt +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/deletions.txt +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/docs/autonomous-mode-onboarding.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/docs/config-policy.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/docs/nul-boundary.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/check_agent_invocation.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/planner_check.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/pre_compact.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/recall_autorebuild.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/stop.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/autonomous-mode/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/autonomous-mode/scripts/mode_line.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/brainstorm/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/init-session/scripts/session_guard.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/.gitignore +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/LICENSE +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/LICENSES/numpy-LICENSE +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/hatch_build.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/pyproject.toml +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_metrics.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_run.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_tier.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/gitutil.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrations/004_agent_outcomes.sql +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrations/006_review_decisions_severity.sql +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/question.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/_pre_compact_helpers.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_recall_autorebuild.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_settings_tier_autoapply_registration.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_tier_gap_check.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/hooks/test_tier_security_fixes.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_parallel_agents_skill_tier.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_session_guard.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_check_deletions.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_doctor.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_doctor_launcher.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_metrics.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_run.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_gitutil.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_mode_line.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_no_bare_python_launcher.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_nul_boundary_lint.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_stop_additional.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_usage_ingester.py +0 -0
- {claude_code_conductor-2.53.0 → claude_code_conductor-2.55.0}/tests/test_worktree_guard.py +0 -0
|
@@ -7,8 +7,12 @@ PYTHONPATH に追加してから `from _hook_utils import ...` する経路を
|
|
|
7
7
|
|
|
8
8
|
## Exports
|
|
9
9
|
|
|
10
|
+
- ``norm_component(s)`` — パス成分を正規化する(`.lower().rstrip('. ')`)。
|
|
11
|
+
ディレクトリ比較・拡張子判定の際にケース非依存化・末尾ドット/スペース除去を行う。
|
|
10
12
|
- ``write_debug_log(log_path, line)`` — ``C3_HOOK_DEBUG=1`` のときのみログを追記する
|
|
11
13
|
fail-safe な書き込みヘルパー。
|
|
14
|
+
- ``sanitize_for_terminal(text)`` — 端末出力(stderr / stdout JSON)へ載せる前に
|
|
15
|
+
C0 + DEL + C1 制御文字を除去するヘルパー。
|
|
12
16
|
"""
|
|
13
17
|
|
|
14
18
|
from __future__ import annotations
|
|
@@ -32,6 +36,27 @@ DEBUG_ENV = "C3_HOOK_DEBUG"
|
|
|
32
36
|
_CONTROL_CHARS_RE = re.compile(r"[\x00-\x1f\x7f-\x9f]")
|
|
33
37
|
|
|
34
38
|
|
|
39
|
+
def norm_component(s: str) -> str:
|
|
40
|
+
"""パス成分を正規化する(ケース非依存化・末尾ドット/スペース除去)。
|
|
41
|
+
|
|
42
|
+
Windows・macOS の既定(ケース非依存 FS)では大文字・小文字の違い、
|
|
43
|
+
末尾のドットやスペースは同一実体を示す。複数ディレクトリ・拡張子判定の際に
|
|
44
|
+
`.lower().rstrip('. ')` で正規化してから比較する。
|
|
45
|
+
"""
|
|
46
|
+
return str(s).lower().rstrip('. ')
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def sanitize_for_terminal(text: str) -> str:
|
|
50
|
+
"""端末インジェクション対策: C0 制御文字 / DEL / C1 制御文字を除去する。
|
|
51
|
+
|
|
52
|
+
- 除去範囲は ``_CONTROL_CHARS_RE``(C0 + DEL + C1)と共通。ANSI エスケープの
|
|
53
|
+
ESC (``\\x1b``) と CSI (``\\x9b``) の双方をカバーする。
|
|
54
|
+
- hook が stderr / stdout JSON(additionalContext)へ外部由来の文字列
|
|
55
|
+
(ファイル名など)を載せる前に通す。
|
|
56
|
+
"""
|
|
57
|
+
return _CONTROL_CHARS_RE.sub("", str(text))
|
|
58
|
+
|
|
59
|
+
|
|
35
60
|
def write_debug_log(log_path: Path, line: str) -> None:
|
|
36
61
|
"""``C3_HOOK_DEBUG=1`` のとき、ログファイルに ``ISO8601 line`` を 1 行追記する。
|
|
37
62
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""PreToolUse hook: patterns.json への直接編集を事故防止ガード(P1・配布対象)。
|
|
3
|
+
|
|
4
|
+
Write / Edit ツールで `.claude/memory/patterns.json` への書き込みを検出し、
|
|
5
|
+
許可フラグがなければブロックする(TTL 型・600 秒)。正規経路は session.tmp 経由の
|
|
6
|
+
promote-pattern skill。意図的な編集は `.claude/state/patterns_guard_allow.flag` で
|
|
7
|
+
一時許可できる。
|
|
8
|
+
|
|
9
|
+
## 射程の明記(設計の限界)
|
|
10
|
+
|
|
11
|
+
- 対象: Write / Edit ツール呼び出しのみ
|
|
12
|
+
- 対象外: Bash(python -c・sed 等)/ NotebookEdit 経由の書き込み
|
|
13
|
+
- worktree 実行時: 基準が main/worktree のどちらになるかは環境解決に依存
|
|
14
|
+
(本 hook の基準は main の patterns.json 本体の事故防止。main 側保護が
|
|
15
|
+
保護されない場合は検知が働かない限界あり)
|
|
16
|
+
- resolve() による大小文字の正準化は実在ファイルのみ(Windows nt._getfinalpathname)。
|
|
17
|
+
patterns.json が未作成の初期環境(c3 init 直後)ではケース違い指定が素通りする
|
|
18
|
+
- Windows の末尾ドット・スペース正規化も実在パス限定。非実在パスでは本 hook が検知できない限界
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import sys
|
|
24
|
+
import time
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
# 共通ヘルパー (_hook_utils.sanitize_for_terminal) を hooks/ 経由で import するため、
|
|
28
|
+
# このスクリプトのディレクトリを PYTHONPATH に追加する。
|
|
29
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
30
|
+
from _hook_utils import sanitize_for_terminal # noqa: E402
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
34
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
35
|
+
except AttributeError:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
# 許可フラグの有効期間(秒)。判定式はこの定数を参照する。
|
|
39
|
+
TTL_SECONDS = 600
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main():
|
|
43
|
+
# bypass: 恒久 disable 環境変数の確認
|
|
44
|
+
if os.environ.get('C3_PATTERNS_GUARD_DISABLE') == '1':
|
|
45
|
+
sys.exit(0)
|
|
46
|
+
|
|
47
|
+
# JSON payload の読み込み(fail-open)
|
|
48
|
+
try:
|
|
49
|
+
payload = json.loads(sys.stdin.read())
|
|
50
|
+
except (json.JSONDecodeError, ValueError):
|
|
51
|
+
sys.exit(0)
|
|
52
|
+
|
|
53
|
+
# tool_name の確認(Write/Edit 以外は対象外)
|
|
54
|
+
tool_name = payload.get('tool_name', '')
|
|
55
|
+
if tool_name not in ('Write', 'Edit'):
|
|
56
|
+
sys.exit(0)
|
|
57
|
+
|
|
58
|
+
# file_path の抽出(キー欠落は fail-open)
|
|
59
|
+
file_path = payload.get('tool_input', {}).get('file_path', '')
|
|
60
|
+
if not file_path:
|
|
61
|
+
sys.exit(0)
|
|
62
|
+
|
|
63
|
+
# プロジェクトルートと保護対象パスの導出(Path(__file__) ベース)
|
|
64
|
+
hook_dir = Path(__file__).resolve().parent
|
|
65
|
+
project_root = hook_dir.parent.parent
|
|
66
|
+
flag_path = project_root / ".claude" / "state" / "patterns_guard_allow.flag"
|
|
67
|
+
|
|
68
|
+
# file_path と保護対象を同じ正規化(realpath)にそろえて比較する。
|
|
69
|
+
# NUL byte 混入など不正なパス文字列は OSError / ValueError になるため fail-open。
|
|
70
|
+
try:
|
|
71
|
+
protected_path = (
|
|
72
|
+
project_root / ".claude" / "memory" / "patterns.json"
|
|
73
|
+
).resolve()
|
|
74
|
+
if os.path.isabs(file_path):
|
|
75
|
+
resolved = Path(file_path).resolve()
|
|
76
|
+
else:
|
|
77
|
+
# 相対パスは CWD を基準に解決(worktree 実行時は worktree CWD)
|
|
78
|
+
resolved = (Path.cwd() / file_path).resolve()
|
|
79
|
+
except (OSError, ValueError):
|
|
80
|
+
sys.exit(0)
|
|
81
|
+
|
|
82
|
+
# realpath 一致チェック
|
|
83
|
+
if resolved != protected_path:
|
|
84
|
+
sys.exit(0)
|
|
85
|
+
|
|
86
|
+
# patterns.json への書き込みが検出された → フラグチェック
|
|
87
|
+
# フラグが存在し TTL 内なら allow(削除しない=複数操作可)
|
|
88
|
+
#
|
|
89
|
+
# TOCTOU 免責: exists() / getmtime() / unlink() の間にフラグが差し替えられる
|
|
90
|
+
# 競合は原理的に残る。本 hook は「事故防止ガード」であり悪意ある同時操作に対する
|
|
91
|
+
# セキュリティ境界ではないため、この競合は許容する。
|
|
92
|
+
if flag_path.exists():
|
|
93
|
+
mtime = os.path.getmtime(flag_path)
|
|
94
|
+
age = time.time() - mtime
|
|
95
|
+
if age <= TTL_SECONDS:
|
|
96
|
+
# TTL 内: 許可
|
|
97
|
+
sys.exit(0)
|
|
98
|
+
else:
|
|
99
|
+
# TTL 超過: フラグを削除して block
|
|
100
|
+
try:
|
|
101
|
+
flag_path.unlink()
|
|
102
|
+
except OSError:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
# ブロック: stderr と exit 2
|
|
106
|
+
flag_create_instruction = (
|
|
107
|
+
f".claude/state/patterns_guard_allow.flag を作成してください(空ファイルで可)。\n"
|
|
108
|
+
f" 例 Bash: touch {sanitize_for_terminal(str(flag_path))}\n"
|
|
109
|
+
f" 例 PowerShell: New-Item -ItemType File -Force "
|
|
110
|
+
f"{sanitize_for_terminal(str(flag_path))}"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
message = (
|
|
114
|
+
f"[PatternGuard BLOCK] patterns.json への直接編集がブロックされました。\n"
|
|
115
|
+
f"正規経路: session.tmp の「##残タスク」へ記載後、promote-pattern skill を実行してください\n"
|
|
116
|
+
f" または LLM が patterns.json を直接編集する場合は以下を実行:\n"
|
|
117
|
+
f"{flag_create_instruction}\n"
|
|
118
|
+
f"恒久 bypass: C3_PATTERNS_GUARD_DISABLE=1 環境変数を設定してください"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
print(message, file=sys.stderr)
|
|
122
|
+
sys.exit(2)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == '__main__':
|
|
126
|
+
main()
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""PostToolUse hook: タイムスタンプ報告契約の検査(P2・配布対象)。
|
|
3
|
+
|
|
4
|
+
`.claude/reports/` 直下への Write で、以下 4 prefix のタイムスタンプ契約逸脱を warn する:
|
|
5
|
+
- requirements-report-
|
|
6
|
+
- architecture-report-
|
|
7
|
+
- plan-report-
|
|
8
|
+
- design-review-report-
|
|
9
|
+
|
|
10
|
+
判定は basename 全体のフルマッチ `^{prefix}\\d{8}-\\d{6}\\.md$` で行う
|
|
11
|
+
(prefix 一致と suffix 一致の独立 2 条件ではない。`plan-report-badname-extra-20260725-180252.md`
|
|
12
|
+
のように prefix と タイムスタンプの間へ任意文字列を挟む名前も逸脱として検出するため)。
|
|
13
|
+
不一致の場合は 2 経路で警告: stderr(人間向け)+ stdout JSON(LLM向け)。
|
|
14
|
+
|
|
15
|
+
## ディレクトリ判定とケース非依存化
|
|
16
|
+
|
|
17
|
+
reports ディレクトリの判定は `.claude/reports/` 直下かどうかを名前ベースで確認する。
|
|
18
|
+
Windows・macOS の既定(ケース非依存 FS)では `.CLAUDE` や `REPORTS` は `.claude` / `reports` と
|
|
19
|
+
同一実体の別名であり、名前比較は `.lower()` を使用してケース非依存で行う。
|
|
20
|
+
basename(prefix・タイムスタンプ・`.md`)の判定は厳密に行う(`re.ASCII` で Unicode 偽装を防止)。
|
|
21
|
+
|
|
22
|
+
## 出力契約(PostToolUse warn の標準形)
|
|
23
|
+
|
|
24
|
+
exit 0 でも stdout JSON が LLM コンテキストへ additionalContext として注入される。
|
|
25
|
+
stderr は人間にのみ届く。詳細は Claude Code 公式仕様を参照:
|
|
26
|
+
https://code.claude.com/docs/en/hooks
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import json
|
|
30
|
+
import os
|
|
31
|
+
import re
|
|
32
|
+
import sys
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
# 共通ヘルパー (_hook_utils.sanitize_for_terminal, norm_component) を hooks/ 経由で import するため、
|
|
36
|
+
# このスクリプトのディレクトリを PYTHONPATH に追加する。
|
|
37
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
38
|
+
from _hook_utils import norm_component, sanitize_for_terminal # noqa: E402
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
42
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
43
|
+
except AttributeError:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def main():
|
|
48
|
+
# JSON payload の読み込み(fail-open)
|
|
49
|
+
try:
|
|
50
|
+
payload = json.loads(sys.stdin.read())
|
|
51
|
+
except (json.JSONDecodeError, ValueError):
|
|
52
|
+
sys.exit(0)
|
|
53
|
+
|
|
54
|
+
# tool_name の確認(Write 以外は対象外=新規命名は Write でのみ発生)
|
|
55
|
+
tool_name = payload.get('tool_name', '')
|
|
56
|
+
if tool_name != 'Write':
|
|
57
|
+
sys.exit(0)
|
|
58
|
+
|
|
59
|
+
# file_path の抽出(キー欠落は fail-open)
|
|
60
|
+
file_path = payload.get('tool_input', {}).get('file_path', '')
|
|
61
|
+
if not file_path:
|
|
62
|
+
sys.exit(0)
|
|
63
|
+
|
|
64
|
+
# `.claude/reports/` 直下かどうかの判定
|
|
65
|
+
path_obj = Path(file_path)
|
|
66
|
+
basename = path_obj.name
|
|
67
|
+
parent = path_obj.parent
|
|
68
|
+
|
|
69
|
+
# 構造判定: 親が `reports` かつ祖父が `.claude`(= `.claude/reports/` 直下)。
|
|
70
|
+
# 上流に同名の `reports/` があっても誤アンカーしない(resolve は使わない)。
|
|
71
|
+
# ケース非依存 FS(Windows・macOS)では `.CLAUDE` や `REPORTS` は `.claude` / `reports` と
|
|
72
|
+
# 同一実体の別名のため、比較は norm_component() で正規化してから行う。
|
|
73
|
+
# Windows の末尾ドット・スペースも norm_component() が除去する。
|
|
74
|
+
# 例: `/foo/.claude/reports/plan-report-20260725-180252.md` → 対象
|
|
75
|
+
# 例: `/foo/.claude/reports/archive/plan-report-final.md` → 対象外(直下でない)
|
|
76
|
+
if norm_component(parent.name) != 'reports' or norm_component(parent.parent.name) != '.claude':
|
|
77
|
+
sys.exit(0)
|
|
78
|
+
|
|
79
|
+
# strict-4: 対象 prefix の確認
|
|
80
|
+
strict4_prefixes = [
|
|
81
|
+
'requirements-report-',
|
|
82
|
+
'architecture-report-',
|
|
83
|
+
'plan-report-',
|
|
84
|
+
'design-review-report-',
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
matched_prefix = None
|
|
88
|
+
for prefix in strict4_prefixes:
|
|
89
|
+
if basename.startswith(prefix):
|
|
90
|
+
matched_prefix = prefix
|
|
91
|
+
break
|
|
92
|
+
if matched_prefix is None:
|
|
93
|
+
sys.exit(0)
|
|
94
|
+
|
|
95
|
+
# basename 全体のフルマッチ確認(`{prefix}YYYYMMDD-HHMMSS.md`)
|
|
96
|
+
# re.ASCII を指定してタイムスタンプを ASCII 数字に限定し、全角数字による偽装を防止する。
|
|
97
|
+
contract_pattern = re.compile(re.escape(matched_prefix) + r'\d{8}-\d{6}\.md', re.ASCII)
|
|
98
|
+
if contract_pattern.fullmatch(basename):
|
|
99
|
+
# 正しい形式
|
|
100
|
+
sys.exit(0)
|
|
101
|
+
|
|
102
|
+
# 違反: 警告を出す(2 経路)
|
|
103
|
+
# stderr(人間向け)
|
|
104
|
+
print(
|
|
105
|
+
f'[ReportContract WARN] {sanitize_for_terminal(basename)} '
|
|
106
|
+
f'のファイル名形式が違反しています。'
|
|
107
|
+
f'期待形式: {{prefix}}YYYYMMDD-HHMMSS.md\n'
|
|
108
|
+
f'採番は report-timestamp skill を使用してください。',
|
|
109
|
+
file=sys.stderr
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# stdout JSON(LLM向け)
|
|
113
|
+
context = (
|
|
114
|
+
f'ファイル名: {sanitize_for_terminal(basename)}\n'
|
|
115
|
+
f'期待形式: {{prefix}}YYYYMMDD-HHMMSS.md\n'
|
|
116
|
+
f'採番方法: report-timestamp skill で取得したタイムスタンプを使用してください。'
|
|
117
|
+
)
|
|
118
|
+
output = {
|
|
119
|
+
'hookSpecificOutput': {
|
|
120
|
+
'hookEventName': 'PostToolUse',
|
|
121
|
+
'additionalContext': context
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
print(json.dumps(output), file=sys.stdout)
|
|
125
|
+
|
|
126
|
+
sys.exit(0)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == '__main__':
|
|
130
|
+
main()
|
|
@@ -12,9 +12,17 @@ tier-routing MVP: タスクの複雑度を簡易ヒューリスティックで
|
|
|
12
12
|
推奨の提示と tier_selection.json への記録のみを担い、model の注入は
|
|
13
13
|
tier_autoapply.py が行う。明示指定された `model:` は tier_autoapply が
|
|
14
14
|
尊重して上書きしない。agent の `model:` フロントマター自体は変更しない
|
|
15
|
-
-
|
|
15
|
+
- **やる(ADR-3〜ADR-5 拡張)**: tester(Red) 推奨を additive な ``roles.tester``
|
|
16
|
+
キーとして tier_selection.json に併記する。tester / wt_tester は test- タスク
|
|
17
|
+
(Red フェーズ)に限り tier_autoapply が機械適用する(D-3/D-5 等の非 Red 起動は
|
|
18
|
+
従来どおり frontmatter 任せ)。
|
|
19
|
+
- **やらない**: developer / wt_developer / tester(Red) 以外の role への適用、
|
|
16
20
|
フォールバック(Haiku 失敗→Sonnet 自動昇格)
|
|
17
21
|
|
|
22
|
+
【opus 固定不変則(ADR-6)】機械適用(APPLY_ROLES / RED_APPLY_ROLES)に追加できるのは
|
|
23
|
+
frontmatter ``model: sonnet`` の role のみ。opus frontmatter の agent(architect /
|
|
24
|
+
planner / design-critic / doc-writer / project-setup)は tier レバーを持たず恒久対象外。
|
|
25
|
+
|
|
18
26
|
学習データ(帰属機械化・フェーズ3):
|
|
19
27
|
- 結果は ``.claude/state/tier_selection.json`` に直近 1 件のみ書き込む
|
|
20
28
|
- Agent 起動時に ``tier_autoapply.py`` が実適用 model を
|
|
@@ -451,15 +459,20 @@ _ESCALATION_MAP: dict[str, str] = {
|
|
|
451
459
|
# SSOT: db.ESCALATION_THRESHOLD_DEFAULT 由来(import 部で取得)。C3_ESCALATION_THRESHOLD env で上書き可(v2.26.0)。
|
|
452
460
|
|
|
453
461
|
|
|
454
|
-
def _db_failure_rate(complexity: str, tier: str) -> tuple:
|
|
462
|
+
def _db_failure_rate(complexity: str, tier: str, role: str = "developer") -> tuple:
|
|
455
463
|
"""DB から failure rate を読み取るデフォルト実装。
|
|
456
464
|
|
|
465
|
+
``role`` は集計対象の agent role(既定 ``"developer"``・後方互換)。
|
|
466
|
+
ADR-5: tester 推奨の escalation では ``role="tester"`` を渡し
|
|
467
|
+
``read_agent_failure_rate("tester", ...)`` を引く(role 別 gate 集合は
|
|
468
|
+
db 側 ``bandit_gates_for_role`` が解決する)。
|
|
469
|
+
|
|
457
470
|
c3_db のインポートに失敗した場合は ``(None, 0)`` を返す。
|
|
458
471
|
"""
|
|
459
472
|
c3_db = _load_c3_db_module()
|
|
460
473
|
if c3_db is None:
|
|
461
474
|
return None, 0
|
|
462
|
-
return c3_db.read_agent_failure_rate(
|
|
475
|
+
return c3_db.read_agent_failure_rate(role, complexity, tier)
|
|
463
476
|
|
|
464
477
|
|
|
465
478
|
def maybe_escalate(
|
|
@@ -468,6 +481,7 @@ def maybe_escalate(
|
|
|
468
481
|
*,
|
|
469
482
|
failure_rate_fn=None,
|
|
470
483
|
threshold: float | None = None,
|
|
484
|
+
role: str = "developer",
|
|
471
485
|
) -> tuple[str, str | None]:
|
|
472
486
|
"""Phase 2-B: failure rate が高ければ 1 段昇格する。
|
|
473
487
|
|
|
@@ -476,10 +490,15 @@ def maybe_escalate(
|
|
|
476
490
|
chosen_tier: select_tier が選んだ tier。
|
|
477
491
|
failure_rate_fn: テスト用に注入可能な
|
|
478
492
|
``(complexity, tier) -> (rate_or_None, sample_count)``。
|
|
479
|
-
省略時は :func:`_db_failure_rate`
|
|
493
|
+
省略時は :func:`_db_failure_rate` を使う。既存の注入契約(2 引数)を
|
|
494
|
+
保つため、注入時は ``role`` を渡さない(テストは戻り値を固定するため
|
|
495
|
+
role 分岐は不要)。role は既定実装 ``_db_failure_rate`` へのみ透過する。
|
|
480
496
|
threshold: escalation 閾値(failure rate がこの値以上で昇格)。
|
|
481
497
|
None のとき module 定数 ``ESCALATION_THRESHOLD`` を使う。
|
|
482
498
|
``main()`` は ``_resolve_escalation_threshold()`` で解決した値を渡す。
|
|
499
|
+
role: 集計対象の agent role(既定 ``"developer"``)。ADR-5: tester 推奨の
|
|
500
|
+
escalation では ``role="tester"`` を渡して ``_db_failure_rate`` 経由で
|
|
501
|
+
tester セルの failure rate を引く。
|
|
483
502
|
|
|
484
503
|
Returns:
|
|
485
504
|
``(effective_tier, escalation_reason)``。
|
|
@@ -489,8 +508,10 @@ def maybe_escalate(
|
|
|
489
508
|
if chosen_tier not in _ESCALATION_MAP:
|
|
490
509
|
return chosen_tier, None
|
|
491
510
|
|
|
492
|
-
|
|
493
|
-
|
|
511
|
+
if failure_rate_fn is not None:
|
|
512
|
+
rate, samples = failure_rate_fn(complexity, chosen_tier)
|
|
513
|
+
else:
|
|
514
|
+
rate, samples = _db_failure_rate(complexity, chosen_tier, role)
|
|
494
515
|
eff_threshold = threshold if threshold is not None else ESCALATION_THRESHOLD
|
|
495
516
|
if rate is None or rate < eff_threshold:
|
|
496
517
|
return chosen_tier, None
|
|
@@ -516,6 +537,7 @@ def write_tier_selection(
|
|
|
516
537
|
cost_tiebreak: bool = False,
|
|
517
538
|
cost_weighted: bool = False,
|
|
518
539
|
cost_lambda: float | None = None,
|
|
540
|
+
roles: dict[str, dict] | None = None,
|
|
519
541
|
) -> None:
|
|
520
542
|
"""直近の選択結果を ``tier_selection.json`` に書く。
|
|
521
543
|
|
|
@@ -543,6 +565,13 @@ def write_tier_selection(
|
|
|
543
565
|
``cost_lambda`` を任意で含める(v2.26.0)。
|
|
544
566
|
``_resolve_cost_lambda()`` で解決した λ 値(None 以外のとき出力)。
|
|
545
567
|
None のときはキー自体を省略する(env 未設定時の後方互換)。
|
|
568
|
+
|
|
569
|
+
``roles`` を任意で含める(ADR-3・additive role 別推奨)。
|
|
570
|
+
``{"tester": {"tier": <実効 tier>, "mode": <mode>, "escalated"?: True}}`` の形。
|
|
571
|
+
None(渡されない)のときはキー自体を省略する(session_id / cost_tiebreak と
|
|
572
|
+
同じ additive-omit パターン=トップレベルの bit 一致を保つ後方互換の核心)。
|
|
573
|
+
トップレベルの ``tier`` / ``mode`` は現行どおり developer 推奨を書き、
|
|
574
|
+
``roles.tester`` は新読者(tier_autoapply 注入・record 優先 3)だけが見る。
|
|
546
575
|
"""
|
|
547
576
|
os.makedirs(os.path.dirname(TIER_SELECTION_PATH), exist_ok=True)
|
|
548
577
|
payload: dict[str, object] = {
|
|
@@ -570,6 +599,8 @@ def write_tier_selection(
|
|
|
570
599
|
payload["cost_weighted"] = True
|
|
571
600
|
if cost_lambda is not None:
|
|
572
601
|
payload["cost_lambda"] = cost_lambda
|
|
602
|
+
if roles is not None:
|
|
603
|
+
payload["roles"] = roles
|
|
573
604
|
try:
|
|
574
605
|
with open(TIER_SELECTION_PATH, "w", encoding="utf-8") as f:
|
|
575
606
|
json.dump(payload, f, ensure_ascii=False)
|
|
@@ -588,6 +619,7 @@ def build_additional_context(
|
|
|
588
619
|
complexity_source: str | None = None,
|
|
589
620
|
cost_tiebreak: bool = False,
|
|
590
621
|
cost_weighted: bool = False,
|
|
622
|
+
tester_tier: str | None = None,
|
|
591
623
|
) -> str:
|
|
592
624
|
"""親 Claude に追加注入する文字列を組み立てる。
|
|
593
625
|
|
|
@@ -597,6 +629,11 @@ def build_additional_context(
|
|
|
597
629
|
``cost_weighted`` が True のとき、cost-weighted 文言を suffix に追加する(v2.26.0)。
|
|
598
630
|
True のときは cost_tiebreak の文言より優先される(λ>0 全 tier weighting を明示)。
|
|
599
631
|
False のときは cost_tiebreak による既存文言のみ(v2.25.0 以前と完全一致)。
|
|
632
|
+
|
|
633
|
+
``tester_tier`` が渡された場合、tester(Red) 推奨 Tier を併記する(ADR-4)。
|
|
634
|
+
None(既定)のときは併記行を省略する(build_additional_context を直接呼ぶ
|
|
635
|
+
既存テストの後方互換)。tester / wt_tester は test- タスク(Red)に限り機械適用
|
|
636
|
+
対象であることを対象説明に明記する。
|
|
600
637
|
"""
|
|
601
638
|
trials = sum(p[2] for p in params.values())
|
|
602
639
|
if mode == "uniform":
|
|
@@ -615,6 +652,10 @@ def build_additional_context(
|
|
|
615
652
|
elif cost_tiebreak:
|
|
616
653
|
suffix += " [cost-aware: 成功率拮抗のため低コスト Tier を選択]"
|
|
617
654
|
|
|
655
|
+
tester_note = ""
|
|
656
|
+
if tester_tier is not None:
|
|
657
|
+
tester_note = f" tester(Red) 推奨 Tier: {tester_tier}。"
|
|
658
|
+
|
|
618
659
|
return (
|
|
619
660
|
f"[tier-routing 推奨] 複雑度: {complexity} / 推奨 Tier: {tier}({confidence}・developer 基準)。"
|
|
620
661
|
f" developer / wt_developer を Agent ツールで起動する際は、PreToolUse hook"
|
|
@@ -622,9 +663,12 @@ def build_additional_context(
|
|
|
622
663
|
f"(親 Claude が model: を転記する必要はありません・学習データ収集中の期間も含め常に適用)。"
|
|
623
664
|
f" 推奨と異なる Tier を使いたい場合のみ、Agent 呼び出し時に model: を明示指定してください"
|
|
624
665
|
f"(明示指定は尊重され hook に上書きされません。fork は model 上書き不可のため対象外)。"
|
|
625
|
-
f"
|
|
626
|
-
f"
|
|
627
|
-
f"
|
|
666
|
+
f"{tester_note}"
|
|
667
|
+
f" tester / wt_tester は test- タスク(Red フェーズ)に限り機械適用対象です"
|
|
668
|
+
f"(C3_TASK_ID: test-... マーカー付き起動のみ・D-3/D-5 等の非 Red 起動は"
|
|
669
|
+
f" 従来どおり frontmatter 任せ)。親 Claude ペルソナで動かす role"
|
|
670
|
+
f"(interviewer / architect / planner)は tier レバーが無いため対象外で"
|
|
671
|
+
f" 従来どおりとする。{suffix}"
|
|
628
672
|
)
|
|
629
673
|
|
|
630
674
|
|
|
@@ -814,6 +858,25 @@ def main() -> int:
|
|
|
814
858
|
effective_tier, escalation_reason = maybe_escalate(complexity, tier, threshold=esc_thr)
|
|
815
859
|
escalated = effective_tier != tier
|
|
816
860
|
|
|
861
|
+
# ADR-3/ADR-4/ADR-5: tester(Red) 推奨を additive に算出する。complexity /
|
|
862
|
+
# cost_map / epsilon / lam は developer と同一解決値を使う(per-prompt 分類・
|
|
863
|
+
# DC-AS-001)。escalation は role="tester" で tester セルの failure rate を引く。
|
|
864
|
+
# developer 推奨を先に確定してから算出するため、tester 側の rng 消費は
|
|
865
|
+
# developer の結果に影響しない(トップレベル bit 一致の後方互換を保つ)。
|
|
866
|
+
if c3_db is None:
|
|
867
|
+
tester_params = {t: (1.0, 1.0, 0) for t in TIERS}
|
|
868
|
+
else:
|
|
869
|
+
tester_params = c3_db.read_agent_tier_params("tester", complexity)
|
|
870
|
+
tester_result = select_tier_detailed(tester_params, cost_map=cost_map, epsilon=eps, lam=lam)
|
|
871
|
+
tester_effective, _tester_esc_reason = maybe_escalate(
|
|
872
|
+
complexity, tester_result.tier, threshold=esc_thr, role="tester",
|
|
873
|
+
)
|
|
874
|
+
tester_escalated = tester_effective != tester_result.tier
|
|
875
|
+
tester_entry: dict[str, object] = {"tier": tester_effective, "mode": tester_result.mode}
|
|
876
|
+
if tester_escalated:
|
|
877
|
+
tester_entry["escalated"] = True
|
|
878
|
+
roles = {"tester": tester_entry}
|
|
879
|
+
|
|
817
880
|
write_tier_selection(
|
|
818
881
|
complexity, effective_tier, mode,
|
|
819
882
|
escalated=escalated, escalation_reason=escalation_reason,
|
|
@@ -823,6 +886,7 @@ def main() -> int:
|
|
|
823
886
|
cost_tiebreak=cost_tiebreak,
|
|
824
887
|
cost_weighted=result.cost_weighted,
|
|
825
888
|
cost_lambda=lam,
|
|
889
|
+
roles=roles,
|
|
826
890
|
)
|
|
827
891
|
|
|
828
892
|
context_text = build_additional_context(
|
|
@@ -831,6 +895,7 @@ def main() -> int:
|
|
|
831
895
|
complexity_source=complexity_source,
|
|
832
896
|
cost_tiebreak=cost_tiebreak,
|
|
833
897
|
cost_weighted=result.cost_weighted,
|
|
898
|
+
tester_tier=tester_effective,
|
|
834
899
|
)
|
|
835
900
|
output = {
|
|
836
901
|
"hookSpecificOutput": {
|