claude-code-conductor 2.25.0__tar.gz → 2.27.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.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/select_tier.py +190 -25
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/CHANGELOG.md +45 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/PKG-INFO +2 -2
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/README.md +1 -1
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/__init__.py +1 -1
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli_tier.py +20 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/db.py +131 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_select_tier.py +987 -3
- claude_code_conductor-2.27.0/tests/hooks/test_select_tier_escalation.py +464 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_tier.py +106 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_db.py +229 -0
- claude_code_conductor-2.25.0/tests/hooks/test_select_tier_escalation.py +0 -229
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/breaking-changes.txt +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/deletions.txt +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/docs/config-policy.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/_hook_utils.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/check_agent_invocation.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/planner_check.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/pre_compact.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/stop.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/settings.json +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/dev-workflow/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/dev-workflow/scripts/record_tier_outcome.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/.gitignore +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/LICENSE +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/LICENSES/chroma-hnswlib-LICENSE +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/LICENSES/chroma-hnswlib-NOTICE +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/hatch_build.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/pyproject.toml +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/question.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_hook_utils.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_record_tier_outcome.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_stop_additional.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_usage_ingester.py +0 -0
- {claude_code_conductor-2.25.0 → claude_code_conductor-2.27.0}/tests/test_worktree_guard.py +0 -0
|
@@ -49,9 +49,17 @@ try:
|
|
|
49
49
|
from c3 import db as _c3_db_const # type: ignore[import-not-found]
|
|
50
50
|
LEARNING_THRESHOLD: int = _c3_db_const.LEARNING_THRESHOLD
|
|
51
51
|
EPSILON: float = _c3_db_const.EPSILON_TIEBREAK
|
|
52
|
+
ESCALATION_THRESHOLD: float = _c3_db_const.ESCALATION_THRESHOLD_DEFAULT
|
|
53
|
+
COST_LAMBDA_DEFAULT: float | None = _c3_db_const.COST_LAMBDA_DEFAULT
|
|
54
|
+
COST_LAMBDA_MIN: float = _c3_db_const.COST_LAMBDA_MIN
|
|
55
|
+
COST_LAMBDA_MAX: float = _c3_db_const.COST_LAMBDA_MAX
|
|
52
56
|
except ImportError:
|
|
53
57
|
LEARNING_THRESHOLD = 30
|
|
54
58
|
EPSILON = 0.05
|
|
59
|
+
ESCALATION_THRESHOLD = 0.5
|
|
60
|
+
COST_LAMBDA_DEFAULT = None
|
|
61
|
+
COST_LAMBDA_MIN = 0.0
|
|
62
|
+
COST_LAMBDA_MAX = 5.0
|
|
55
63
|
|
|
56
64
|
# 複雑度推定のキーワード
|
|
57
65
|
SIMPLE_KEYWORDS = frozenset({
|
|
@@ -127,12 +135,16 @@ class SelectionResult(NamedTuple):
|
|
|
127
135
|
cost_tiebreak: Thompson 分岐で cost tie-break が発動した場合 True。
|
|
128
136
|
contenders: 拮抗判定に入った tier のタプル(observability/デバッグ用)。
|
|
129
137
|
frozen 安全のため list ではなく tuple を使用。
|
|
138
|
+
cost_weighted: λ>0 全 tier weighting が適用された場合 True(v2.26.0)。
|
|
139
|
+
lam=None(env 未設定)または cost_map=None の場合は False のまま。
|
|
140
|
+
uniform 分岐では常に False(探索保護・不可侵)。
|
|
130
141
|
"""
|
|
131
142
|
|
|
132
143
|
tier: str
|
|
133
144
|
mode: str
|
|
134
145
|
cost_tiebreak: bool = False
|
|
135
146
|
contenders: tuple[str, ...] = ()
|
|
147
|
+
cost_weighted: bool = False
|
|
136
148
|
|
|
137
149
|
|
|
138
150
|
def _cost_tiebreak(
|
|
@@ -140,41 +152,80 @@ def _cost_tiebreak(
|
|
|
140
152
|
cost_map: dict[str, float] | None,
|
|
141
153
|
*,
|
|
142
154
|
epsilon: float = EPSILON,
|
|
155
|
+
lam: float | None = None,
|
|
143
156
|
) -> tuple[str, bool, tuple[str, ...]]:
|
|
144
|
-
"""Thompson
|
|
157
|
+
"""Thompson サンプルから cost を考慮して最適な tier を返す(3 経路)。
|
|
158
|
+
|
|
159
|
+
lam(λ)の値によって 3 つの経路に分岐する:
|
|
160
|
+
- 経路 0(cost_map is None or lam == 0): cost を見ない。argmax(sample) を返す。
|
|
161
|
+
lam=None を渡しても cost_map が None なら経路 0。lam=0.0 の明示オプトも経路 0。
|
|
162
|
+
- 経路 1(lam is None・既定): v2.25.0 の ε-gated min-max 最安(後方互換)。
|
|
163
|
+
contenders ≤ 1 は argmax、複数 contenders は min-max 正規化コスト最安を選ぶ。
|
|
164
|
+
lam=None(env 未設定センチネル)がデフォルト→ v2.25.0 挙動と完全一致。
|
|
165
|
+
- 経路 2(lam > 0): 全 tier weighting。
|
|
166
|
+
score[t] = sample[t] - lam * cost_norm[t] で全 tier を比較し最大を選ぶ。
|
|
167
|
+
cost_norm は全 tier の min-max 正規化(最安→0・最高→1)。
|
|
145
168
|
|
|
146
169
|
Args:
|
|
147
170
|
samples: {tier: beta_sample} の dict(Thompson Sampling 結果)。
|
|
148
|
-
cost_map: {tier: cost} の dict。None なら cost
|
|
171
|
+
cost_map: {tier: cost} の dict。None なら cost を見ず従来挙動(経路 0)。
|
|
149
172
|
cost は実測 rate_usd_per_mtok または静的参照単価(ハイブリッド)。
|
|
150
173
|
v2.24.0 で rate 化(USD/MTok)により実測・静的とも同次元で整合済み。
|
|
151
|
-
``cost_map`` は None、または
|
|
152
|
-
|
|
174
|
+
``cost_map`` は None、または samples の全 tier キーを含む dict を渡すこと。
|
|
175
|
+
partial dict を渡すと ``cost_map[t]`` で KeyError が発生する。
|
|
153
176
|
``select_tier_detailed`` 経由では呼び出し側(main)が全 TIERS 分を
|
|
154
177
|
構築して保証する。
|
|
155
|
-
epsilon: 拮抗判定の閾値(デフォルト EPSILON=0.05
|
|
178
|
+
epsilon: 拮抗判定の閾値(デフォルト EPSILON=0.05)。経路 0/1 で使用。
|
|
179
|
+
経路 2 では contenders 算出にのみ使用(選択自体は全 tier score 比較)。
|
|
180
|
+
lam: cost weighting 係数(λ)。None=センチネル(経路 1・後方互換)、
|
|
181
|
+
0.0=cost 無視明示(経路 0)、0 < lam <= COST_LAMBDA_MAX=全 tier weighting(経路 2)。
|
|
182
|
+
デフォルト None で既存 2 引数呼び出しの挙動・シグネチャを完全不変にする。
|
|
156
183
|
|
|
157
184
|
Returns:
|
|
158
185
|
(chosen, did_tiebreak, contenders) のタプル。
|
|
159
186
|
- chosen: 選択された tier 名。
|
|
160
|
-
- did_tiebreak: cost
|
|
161
|
-
|
|
187
|
+
- did_tiebreak: cost が選択に影響した場合 True。
|
|
188
|
+
経路 1: contenders 内 min-max で安い方を選んだ場合 True(全 tier コスト同値時は False)。
|
|
189
|
+
経路 2: 全 tier weighting で argmax(sample) と異なる選択になった場合 True。
|
|
190
|
+
- contenders: ε 拮抗判定に入った tier のタプル(observability 用)。
|
|
162
191
|
"""
|
|
163
192
|
max_sample = max(samples.values())
|
|
164
193
|
contenders = [t for t in samples if max_sample - samples[t] <= epsilon]
|
|
165
194
|
|
|
166
|
-
|
|
167
|
-
|
|
195
|
+
# 経路 0: cost を見ない(cost_map なし or λ=0 明示)。
|
|
196
|
+
# None == 0 は Python では False のため lam == 0 は float 0.0 のみ真(意図通り)。
|
|
197
|
+
if cost_map is None or lam == 0:
|
|
168
198
|
chosen = max(samples, key=lambda t: samples[t])
|
|
169
199
|
return chosen, False, tuple(contenders)
|
|
170
200
|
|
|
171
|
-
#
|
|
172
|
-
|
|
201
|
+
# 経路 1(lam=None・既定): v2.25.0 の ε-gated min-max 最安(現行ロジック完全踏襲)。
|
|
202
|
+
if lam is None:
|
|
203
|
+
if len(contenders) <= 1:
|
|
204
|
+
chosen = max(samples, key=lambda t: samples[t])
|
|
205
|
+
return chosen, False, tuple(contenders)
|
|
206
|
+
# 拮抗群内で min-max 正規化コストを計算し最安 tier を選ぶ
|
|
207
|
+
costs = {t: cost_map[t] for t in contenders}
|
|
208
|
+
lo, hi = min(costs.values()), max(costs.values())
|
|
209
|
+
if hi == lo:
|
|
210
|
+
# [CR-Q-001] 全 tier コスト同値: cost は選択に無関与。
|
|
211
|
+
# argmax(sample) を返し did_tiebreak=False(observability 精緻化・v2.27.0 で精緻化済み)。
|
|
212
|
+
chosen = max(samples, key=lambda t: samples[t])
|
|
213
|
+
return chosen, False, tuple(contenders)
|
|
214
|
+
norm = {t: (costs[t] - lo) / (hi - lo) for t in contenders}
|
|
215
|
+
chosen = min(contenders, key=lambda t: (norm[t], -samples[t]))
|
|
216
|
+
return chosen, True, tuple(contenders)
|
|
217
|
+
|
|
218
|
+
# 経路 2(lam > 0): 全 tier weighting。
|
|
219
|
+
# cost_norm は全 tier で min-max 正規化(最安→0・最高→1)。
|
|
220
|
+
costs = {t: cost_map[t] for t in samples}
|
|
173
221
|
lo, hi = min(costs.values()), max(costs.values())
|
|
174
|
-
norm = {t: ((costs[t] - lo) / (hi - lo) if hi > lo else 0.0) for t in
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
222
|
+
norm = {t: ((costs[t] - lo) / (hi - lo) if hi > lo else 0.0) for t in samples}
|
|
223
|
+
score = {t: samples[t] - lam * norm[t] for t in samples}
|
|
224
|
+
# 同点は sample 大優先(決定論)
|
|
225
|
+
chosen = max(samples, key=lambda t: (score[t], samples[t]))
|
|
226
|
+
pure_max = max(samples, key=lambda t: samples[t])
|
|
227
|
+
cost_tiebreak = chosen != pure_max
|
|
228
|
+
return chosen, cost_tiebreak, tuple(contenders)
|
|
178
229
|
|
|
179
230
|
|
|
180
231
|
def _prompt_prefix_and_hash(prompt: str) -> tuple[str, str]:
|
|
@@ -298,6 +349,7 @@ def select_tier_detailed(
|
|
|
298
349
|
rng: random.Random | None = None,
|
|
299
350
|
cost_map: dict[str, float] | None = None,
|
|
300
351
|
epsilon: float | None = None,
|
|
352
|
+
lam: float | None = None,
|
|
301
353
|
) -> SelectionResult:
|
|
302
354
|
"""Beta サンプリングまたは uniform 選択で推奨 Tier を SelectionResult で返す。
|
|
303
355
|
|
|
@@ -311,15 +363,20 @@ def select_tier_detailed(
|
|
|
311
363
|
(呼び出し側が全 TIERS 分を構築して保証する)。
|
|
312
364
|
uniform 分岐では cost_map の有無に関わらず完全無視する(探索保護)。
|
|
313
365
|
epsilon: 拮抗判定閾値。None なら module 定数 EPSILON を使う(C3_TIER_EPSILON で上書き可)。
|
|
366
|
+
lam: cost weighting 係数(λ)。None=センチネル(v2.25.0 ε-gated 後方互換)、
|
|
367
|
+
0.0=cost 無視明示、0<lam<=COST_LAMBDA_MAX=全 tier weighting 発動(C3_TIER_COST_LAMBDA で上書き可)。
|
|
368
|
+
None(デフォルト)では env 未設定時と完全一致する(後方互換の核心)。
|
|
369
|
+
uniform 分岐では lam の値に関わらず完全無視する(探索保護・不可侵)。
|
|
314
370
|
|
|
315
371
|
Returns:
|
|
316
|
-
SelectionResult(tier, mode, cost_tiebreak, contenders)。
|
|
372
|
+
SelectionResult(tier, mode, cost_tiebreak, contenders, cost_weighted)。
|
|
317
373
|
mode は ``"thompson"`` / ``"uniform"``。
|
|
374
|
+
cost_weighted は lam>0 かつ cost_map が有効な場合のみ True。
|
|
318
375
|
"""
|
|
319
376
|
rng = rng or random
|
|
320
377
|
total_trials = sum(p[2] for p in params.values())
|
|
321
378
|
if total_trials < LEARNING_THRESHOLD:
|
|
322
|
-
# uniform: cost
|
|
379
|
+
# uniform: cost/λ を完全無視・従来挙動完全維持(不可侵)
|
|
323
380
|
return SelectionResult(rng.choice(TIERS), "uniform", False, ())
|
|
324
381
|
|
|
325
382
|
# Thompson Sampling: rng の消費順序を従来 select_tier と完全一致させる
|
|
@@ -328,8 +385,9 @@ def select_tier_detailed(
|
|
|
328
385
|
for tier, p in params.items()
|
|
329
386
|
}
|
|
330
387
|
eff_epsilon = epsilon if epsilon is not None else EPSILON
|
|
331
|
-
chosen, did_tiebreak, contenders = _cost_tiebreak(samples, cost_map, epsilon=eff_epsilon)
|
|
332
|
-
|
|
388
|
+
chosen, did_tiebreak, contenders = _cost_tiebreak(samples, cost_map, epsilon=eff_epsilon, lam=lam)
|
|
389
|
+
cost_weighted = (cost_map is not None and lam is not None and lam > 0)
|
|
390
|
+
return SelectionResult(chosen, "thompson", did_tiebreak, contenders, cost_weighted)
|
|
333
391
|
|
|
334
392
|
|
|
335
393
|
def select_tier(
|
|
@@ -338,6 +396,7 @@ def select_tier(
|
|
|
338
396
|
rng: random.Random | None = None,
|
|
339
397
|
cost_map: dict[str, float] | None = None,
|
|
340
398
|
epsilon: float | None = None,
|
|
399
|
+
lam: float | None = None,
|
|
341
400
|
) -> tuple[str, str]:
|
|
342
401
|
"""Beta サンプリングまたは uniform 選択で推奨 Tier を返す。
|
|
343
402
|
|
|
@@ -350,13 +409,15 @@ def select_tier(
|
|
|
350
409
|
uniform 分岐では cost_map の有無に関わらず完全無視する。
|
|
351
410
|
詳細は :func:`select_tier_detailed` を参照。
|
|
352
411
|
epsilon: 拮抗判定閾値。None なら module 定数 EPSILON を使う。
|
|
412
|
+
lam: cost weighting 係数(λ)。select_tier_detailed に委譲する。
|
|
413
|
+
None=センチネル(v2.25.0 後方互換)、0.0=cost 無視、0<lam<=1=全 tier weighting。
|
|
353
414
|
|
|
354
415
|
Returns:
|
|
355
416
|
``(tier, mode)`` のタプル。``mode`` は ``"thompson"`` / ``"uniform"`` で、
|
|
356
417
|
プロンプトに「学習データ収集中」と表示するかの分岐に使う。
|
|
357
418
|
戻り値型は v2.22.0 以前と完全に不変。
|
|
358
419
|
"""
|
|
359
|
-
result = select_tier_detailed(params, rng=rng, cost_map=cost_map, epsilon=epsilon)
|
|
420
|
+
result = select_tier_detailed(params, rng=rng, cost_map=cost_map, epsilon=epsilon, lam=lam)
|
|
360
421
|
return result.tier, result.mode
|
|
361
422
|
|
|
362
423
|
|
|
@@ -368,7 +429,7 @@ _ESCALATION_MAP: dict[str, str] = {
|
|
|
368
429
|
}
|
|
369
430
|
|
|
370
431
|
# Phase 2-B: failure rate がこの値以上で escalation 判定。
|
|
371
|
-
|
|
432
|
+
# SSOT: db.ESCALATION_THRESHOLD_DEFAULT 由来(import 部で取得)。C3_ESCALATION_THRESHOLD env で上書き可(v2.26.0)。
|
|
372
433
|
|
|
373
434
|
|
|
374
435
|
def _db_failure_rate(complexity: str, tier: str) -> tuple:
|
|
@@ -387,6 +448,7 @@ def maybe_escalate(
|
|
|
387
448
|
chosen_tier: str,
|
|
388
449
|
*,
|
|
389
450
|
failure_rate_fn=None,
|
|
451
|
+
threshold: float | None = None,
|
|
390
452
|
) -> tuple[str, str | None]:
|
|
391
453
|
"""Phase 2-B: failure rate が高ければ 1 段昇格する。
|
|
392
454
|
|
|
@@ -396,6 +458,9 @@ def maybe_escalate(
|
|
|
396
458
|
failure_rate_fn: テスト用に注入可能な
|
|
397
459
|
``(complexity, tier) -> (rate_or_None, sample_count)``。
|
|
398
460
|
省略時は :func:`_db_failure_rate` を使う。
|
|
461
|
+
threshold: escalation 閾値(failure rate がこの値以上で昇格)。
|
|
462
|
+
None のとき module 定数 ``ESCALATION_THRESHOLD`` を使う。
|
|
463
|
+
``main()`` は ``_resolve_escalation_threshold()`` で解決した値を渡す。
|
|
399
464
|
|
|
400
465
|
Returns:
|
|
401
466
|
``(effective_tier, escalation_reason)``。
|
|
@@ -407,7 +472,8 @@ def maybe_escalate(
|
|
|
407
472
|
|
|
408
473
|
effective_fn = failure_rate_fn or _db_failure_rate
|
|
409
474
|
rate, samples = effective_fn(complexity, chosen_tier)
|
|
410
|
-
if
|
|
475
|
+
eff_threshold = threshold if threshold is not None else ESCALATION_THRESHOLD
|
|
476
|
+
if rate is None or rate < eff_threshold:
|
|
411
477
|
return chosen_tier, None
|
|
412
478
|
|
|
413
479
|
escalated = _ESCALATION_MAP[chosen_tier]
|
|
@@ -429,6 +495,8 @@ def write_tier_selection(
|
|
|
429
495
|
prompt_hash: str | None = None,
|
|
430
496
|
session_id: str | None = None,
|
|
431
497
|
cost_tiebreak: bool = False,
|
|
498
|
+
cost_weighted: bool = False,
|
|
499
|
+
cost_lambda: float | None = None,
|
|
432
500
|
) -> None:
|
|
433
501
|
"""直近の選択結果を ``tier_selection.json`` に書く。
|
|
434
502
|
|
|
@@ -448,6 +516,14 @@ def write_tier_selection(
|
|
|
448
516
|
``cost_tiebreak`` を任意で含める(v2.23.0)。
|
|
449
517
|
Thompson Sampling の拮抗群内で cost tie-break が発動した場合のみ True。
|
|
450
518
|
False のときはキー自体を省略する(escalated/session_id と同パターン)。
|
|
519
|
+
|
|
520
|
+
``cost_weighted`` を任意で含める(v2.26.0)。
|
|
521
|
+
λ>0 の全 tier weighting が適用された場合のみ True。
|
|
522
|
+
False のときはキー自体を省略する(cost_tiebreak と同パターン)。
|
|
523
|
+
|
|
524
|
+
``cost_lambda`` を任意で含める(v2.26.0)。
|
|
525
|
+
``_resolve_cost_lambda()`` で解決した λ 値(None 以外のとき出力)。
|
|
526
|
+
None のときはキー自体を省略する(env 未設定時の後方互換)。
|
|
451
527
|
"""
|
|
452
528
|
os.makedirs(os.path.dirname(TIER_SELECTION_PATH), exist_ok=True)
|
|
453
529
|
payload: dict[str, object] = {
|
|
@@ -471,6 +547,10 @@ def write_tier_selection(
|
|
|
471
547
|
payload["session_id"] = session_id
|
|
472
548
|
if cost_tiebreak:
|
|
473
549
|
payload["cost_tiebreak"] = True
|
|
550
|
+
if cost_weighted:
|
|
551
|
+
payload["cost_weighted"] = True
|
|
552
|
+
if cost_lambda is not None:
|
|
553
|
+
payload["cost_lambda"] = cost_lambda
|
|
474
554
|
try:
|
|
475
555
|
with open(TIER_SELECTION_PATH, "w", encoding="utf-8") as f:
|
|
476
556
|
json.dump(payload, f, ensure_ascii=False)
|
|
@@ -488,11 +568,16 @@ def build_additional_context(
|
|
|
488
568
|
escalation_reason: str | None = None,
|
|
489
569
|
complexity_source: str | None = None,
|
|
490
570
|
cost_tiebreak: bool = False,
|
|
571
|
+
cost_weighted: bool = False,
|
|
491
572
|
) -> str:
|
|
492
573
|
"""親 Claude に追加注入する文字列を組み立てる。
|
|
493
574
|
|
|
494
575
|
``cost_tiebreak`` が True のとき、suffix に cost-aware 発動を示す文言を追加する(v2.23.0)。
|
|
495
576
|
False のときは不変(既存文言と完全一致)。
|
|
577
|
+
|
|
578
|
+
``cost_weighted`` が True のとき、cost-weighted 文言を suffix に追加する(v2.26.0)。
|
|
579
|
+
True のときは cost_tiebreak の文言より優先される(λ>0 全 tier weighting を明示)。
|
|
580
|
+
False のときは cost_tiebreak による既存文言のみ(v2.25.0 以前と完全一致)。
|
|
496
581
|
"""
|
|
497
582
|
trials = sum(p[2] for p in params.values())
|
|
498
583
|
if mode == "uniform":
|
|
@@ -506,7 +591,9 @@ def build_additional_context(
|
|
|
506
591
|
suffix += f" [Phase 2-B 昇格: {escalation_reason}]"
|
|
507
592
|
if complexity_source:
|
|
508
593
|
suffix += f" [複雑度判定: {complexity_source}]"
|
|
509
|
-
if
|
|
594
|
+
if cost_weighted:
|
|
595
|
+
suffix += " [cost-weighted: 成功率とコストを加重して選択]"
|
|
596
|
+
elif cost_tiebreak:
|
|
510
597
|
suffix += " [cost-aware: 成功率拮抗のため低コスト Tier を選択]"
|
|
511
598
|
|
|
512
599
|
return (
|
|
@@ -564,6 +651,79 @@ def _resolve_epsilon() -> float:
|
|
|
564
651
|
return x
|
|
565
652
|
|
|
566
653
|
|
|
654
|
+
def _resolve_escalation_threshold() -> float:
|
|
655
|
+
"""``C3_ESCALATION_THRESHOLD`` を安全に解決する。
|
|
656
|
+
|
|
657
|
+
不正値(非数値 / 0 以下 / 1 超 / NaN)は受け付けず、stderr 警告 + デフォルト(ESCALATION_THRESHOLD)に戻す。
|
|
658
|
+
未設定 / 空文字は無警告でデフォルトを返す。
|
|
659
|
+
妥当域: 0 < x <= 1(_resolve_epsilon と同じ範囲)。区間表記: (0, 1](x=0 拒否のため半開区間)。
|
|
660
|
+
"""
|
|
661
|
+
raw = os.environ.get("C3_ESCALATION_THRESHOLD")
|
|
662
|
+
if raw is None or raw == "":
|
|
663
|
+
return ESCALATION_THRESHOLD
|
|
664
|
+
try:
|
|
665
|
+
x = float(raw)
|
|
666
|
+
except ValueError:
|
|
667
|
+
print(
|
|
668
|
+
f"[select_tier:escalation] invalid C3_ESCALATION_THRESHOLD={raw!r}, "
|
|
669
|
+
f"using default {ESCALATION_THRESHOLD}",
|
|
670
|
+
file=sys.stderr,
|
|
671
|
+
)
|
|
672
|
+
return ESCALATION_THRESHOLD
|
|
673
|
+
if math.isnan(x):
|
|
674
|
+
print(
|
|
675
|
+
f"[select_tier:escalation] C3_ESCALATION_THRESHOLD={raw!r} is NaN, "
|
|
676
|
+
f"using default {ESCALATION_THRESHOLD}",
|
|
677
|
+
file=sys.stderr,
|
|
678
|
+
)
|
|
679
|
+
return ESCALATION_THRESHOLD
|
|
680
|
+
if x <= 0 or x > 1:
|
|
681
|
+
print(
|
|
682
|
+
f"[select_tier:escalation] C3_ESCALATION_THRESHOLD={x!r} out of range (0, 1], "
|
|
683
|
+
f"using default {ESCALATION_THRESHOLD}",
|
|
684
|
+
file=sys.stderr,
|
|
685
|
+
)
|
|
686
|
+
return ESCALATION_THRESHOLD
|
|
687
|
+
return x
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def _resolve_cost_lambda() -> float | None:
|
|
691
|
+
"""``C3_TIER_COST_LAMBDA`` を安全に解決する。
|
|
692
|
+
|
|
693
|
+
不正値(非数値 / 0 未満 / COST_LAMBDA_MAX 超 / NaN)は受け付けず、stderr 警告 + デフォルト(COST_LAMBDA_DEFAULT)に戻す。
|
|
694
|
+
未設定 / 空文字は無警告でデフォルト(None)を返す。
|
|
695
|
+
妥当域: 0 <= x <= COST_LAMBDA_MAX(x == 0 は許容=cost 無視の明示オプト・_resolve_epsilon と異なり下限を含む)。区間表記: [0, COST_LAMBDA_MAX](x=0 許容のため閉区間)。
|
|
696
|
+
戻り値が None の場合は v2.25.0 互換の ε tie-break 経路を維持する(センチネル)。
|
|
697
|
+
"""
|
|
698
|
+
raw = os.environ.get("C3_TIER_COST_LAMBDA")
|
|
699
|
+
if raw is None or raw == "":
|
|
700
|
+
return COST_LAMBDA_DEFAULT
|
|
701
|
+
try:
|
|
702
|
+
x = float(raw)
|
|
703
|
+
except ValueError:
|
|
704
|
+
print(
|
|
705
|
+
f"[select_tier:cost_lambda] invalid C3_TIER_COST_LAMBDA={raw!r}, "
|
|
706
|
+
f"using default {COST_LAMBDA_DEFAULT}",
|
|
707
|
+
file=sys.stderr,
|
|
708
|
+
)
|
|
709
|
+
return COST_LAMBDA_DEFAULT
|
|
710
|
+
if math.isnan(x):
|
|
711
|
+
print(
|
|
712
|
+
f"[select_tier:cost_lambda] C3_TIER_COST_LAMBDA={raw!r} is NaN, "
|
|
713
|
+
f"using default {COST_LAMBDA_DEFAULT}",
|
|
714
|
+
file=sys.stderr,
|
|
715
|
+
)
|
|
716
|
+
return COST_LAMBDA_DEFAULT
|
|
717
|
+
if x < COST_LAMBDA_MIN or x > COST_LAMBDA_MAX:
|
|
718
|
+
print(
|
|
719
|
+
f"[select_tier:cost_lambda] C3_TIER_COST_LAMBDA={x!r} out of range [0, {COST_LAMBDA_MAX}], "
|
|
720
|
+
f"using default {COST_LAMBDA_DEFAULT}",
|
|
721
|
+
file=sys.stderr,
|
|
722
|
+
)
|
|
723
|
+
return COST_LAMBDA_DEFAULT
|
|
724
|
+
return x
|
|
725
|
+
|
|
726
|
+
|
|
567
727
|
def main() -> int:
|
|
568
728
|
try:
|
|
569
729
|
payload = json.loads(sys.stdin.read())
|
|
@@ -619,12 +779,14 @@ def main() -> int:
|
|
|
619
779
|
cost_map = None
|
|
620
780
|
|
|
621
781
|
eps = _resolve_epsilon()
|
|
622
|
-
|
|
782
|
+
lam = _resolve_cost_lambda()
|
|
783
|
+
result = select_tier_detailed(params, cost_map=cost_map, epsilon=eps, lam=lam)
|
|
623
784
|
tier, mode = result.tier, result.mode
|
|
624
785
|
cost_tiebreak = result.cost_tiebreak
|
|
625
786
|
|
|
626
787
|
# Phase 2-B: failure rate に基づく escalation
|
|
627
|
-
|
|
788
|
+
esc_thr = _resolve_escalation_threshold()
|
|
789
|
+
effective_tier, escalation_reason = maybe_escalate(complexity, tier, threshold=esc_thr)
|
|
628
790
|
escalated = effective_tier != tier
|
|
629
791
|
|
|
630
792
|
write_tier_selection(
|
|
@@ -634,6 +796,8 @@ def main() -> int:
|
|
|
634
796
|
prompt_hash=prompt_hash,
|
|
635
797
|
session_id=session_id,
|
|
636
798
|
cost_tiebreak=cost_tiebreak,
|
|
799
|
+
cost_weighted=result.cost_weighted,
|
|
800
|
+
cost_lambda=lam,
|
|
637
801
|
)
|
|
638
802
|
|
|
639
803
|
context_text = build_additional_context(
|
|
@@ -641,6 +805,7 @@ def main() -> int:
|
|
|
641
805
|
escalation_reason=escalation_reason,
|
|
642
806
|
complexity_source=complexity_source,
|
|
643
807
|
cost_tiebreak=cost_tiebreak,
|
|
808
|
+
cost_weighted=result.cost_weighted,
|
|
644
809
|
)
|
|
645
810
|
output = {
|
|
646
811
|
"hookSpecificOutput": {
|
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.27.0] - 2026-05-26
|
|
4
|
+
|
|
5
|
+
**tier-routing λ 機能拡張(CR-Q-001 精緻化・λ 上限 5.0・cli_tier routing パラメータ表示)**: v2.26.0 で繰り越した 3 項目を解消。λ の上限を 1.0 から 5.0 に拡張、cost-aware tie-break の observability フラグを精緻化、`c3 tier stats` に現在の routing パラメータ(λ/ε/escalation)を表示。環境変数未設定時の routing 出力は v2.26.0 と一致。**破壊的変更なし**。
|
|
6
|
+
|
|
7
|
+
### 機能追加
|
|
8
|
+
|
|
9
|
+
- **`src/c3/db.py`: `COST_LAMBDA_MIN`(0.0)/ `COST_LAMBDA_MAX`(5.0)定数を追加(SSOT)**: cost-weighted Thompson の λ 妥当域の上限を 1.0 から 5.0 に拡張。これにより最高コスト tier の sample をより強く減点でき、cost を成功率より優先させる調整が可能になる。既存の `[0, 1]` の λ 値は引き続き有効(後方互換)。
|
|
10
|
+
- **`src/c3/db.py`: 公開 `resolve_cost_lambda` / `resolve_epsilon` / `resolve_escalation_threshold` を追加**: 環境変数(`C3_TIER_COST_LAMBDA` / `C3_TIER_EPSILON` / `C3_ESCALATION_THRESHOLD`)の解決ロジックを db.py に SSOT として新設。`cli_tier.py` が現在有効な routing パラメータを表示するために参照する。挙動は hook(`select_tier.py`)の既存 `_resolve_*` と一致し、parity テストで戻り値の一致を担保。
|
|
11
|
+
- **`src/c3/cli_tier.py`: `c3 tier stats` に「routing パラメータ」セクションを追加**: 現在有効な λ(`C3_TIER_COST_LAMBDA`)・ε(`C3_TIER_EPSILON`)・escalation threshold(`C3_ESCALATION_THRESHOLD`)を表示。λ は未設定(v2.25.0 互換)/ 0.0(cost 無視)/ 0 < x ≤ 5(全 tier weighting)で文言を分岐。`--json` 出力にも `routing_params` キーを追加。
|
|
12
|
+
|
|
13
|
+
### 変更
|
|
14
|
+
|
|
15
|
+
- **`.claude/hooks/select_tier.py`: `_resolve_cost_lambda` の上限を `COST_LAMBDA_MAX`(5.0)参照に変更**: 従来ハードコードの上限 `1` を db.py の SSOT 定数参照に変更(import 失敗時フォールバック 5.0)。下限も `COST_LAMBDA_MIN`(0.0)参照に統一。
|
|
16
|
+
- **`.claude/hooks/select_tier.py`: CR-Q-001 — `_cost_tiebreak` 経路1 の observability フラグ精緻化**: v2.25.0 互換の ε tie-break 経路で、拮抗群の全 tier コストが同値(`hi == lo`)の場合に `did_tiebreak=False` を返すよう変更。**選ばれる tier は不変**(`argmax(sample)`)で、変わるのは observability のみ。
|
|
17
|
+
|
|
18
|
+
### 後方互換
|
|
19
|
+
|
|
20
|
+
- 環境変数未設定時の routing 挙動・選ばれる tier は v2.26.0 と完全一致。
|
|
21
|
+
- λ の既存値(`[0, 1]`)は引き続き有効。上限拡張は許容域の拡大のみ。
|
|
22
|
+
- **observability 出力の差分(CR-Q-001)**: cost-aware tie-break で**全 tier コストが同値**の特定ケースに限り、`tier_selection.json` の `cost_tiebreak: true` キーが**省略**されるようになる(従来は `true` を出力)。routing 決定(選ばれる tier)には影響しない。
|
|
23
|
+
- migration 不要。**破壊的変更なし**。
|
|
24
|
+
|
|
25
|
+
## [2.26.0] - 2026-05-26
|
|
26
|
+
|
|
27
|
+
**cost-weighted Thompson 本格統合(全 tier)・ESCALATION_THRESHOLD 調整可能化**: Thompson Sampling のサンプル値を全 tier でコスト重み付けして routing する機能を導入。failure-rate escalation 閾値を環境変数で調整可能にする。環境変数 3 種すべて未設定で v2.25.0 と完全一致。**破壊的変更なし**。
|
|
28
|
+
|
|
29
|
+
### 機能追加
|
|
30
|
+
|
|
31
|
+
- **`.claude/hooks/select_tier.py`: cost-weighted Thompson Sampling(全 tier 対象)**: スコアを `score = sample − λ·cost_norm`(cost_norm は全 tier の min-max 正規化)とし最大 tier を選ぶ routing を導入。λ は環境変数 `C3_TIER_COST_LAMBDA`(0〜1)で設定。λ>0 で発動し、安い tier が成功率優位な tier に勝ちうる(成功率 vs コストのトレードオフ)。λ=0 明示でコスト無視(純 Thompson)。`db.COST_LAMBDA_DEFAULT`(None)が SSOT。
|
|
32
|
+
|
|
33
|
+
- **`.claude/hooks/select_tier.py`: `C3_ESCALATION_THRESHOLD` 対応**: failure-rate escalation の閾値を環境変数 `C3_ESCALATION_THRESHOLD`(0 < x ≤ 1)で設定可能に。`db.ESCALATION_THRESHOLD_DEFAULT`(0.5)が SSOT。NaN・範囲外・非数値は default 値に fallback。
|
|
34
|
+
|
|
35
|
+
### 変更
|
|
36
|
+
|
|
37
|
+
- **`.claude/hooks/select_tier.py`**: `SelectionResult` に `cost_weighted: bool` / `cost_lambda: float | None` を末尾追加(既存フィールド不変)。`write_tier_selection`/`build_additional_context` に cost-weighted 発動時 `cost_weighted` / `cost_lambda` を記録(既存 `cost_tiebreak` キーは不変)。
|
|
38
|
+
|
|
39
|
+
- **`src/c3/db.py`**: `COST_LAMBDA_DEFAULT = None`・`ESCALATION_THRESHOLD_DEFAULT = 0.5` 定数を追加(環境変数オーバーライドの SSOT)。
|
|
40
|
+
|
|
41
|
+
### 後方互換
|
|
42
|
+
|
|
43
|
+
- `C3_TIER_COST_LAMBDA` 未設定(デフォルト None)時は v2.25.0 の ε tie-break 挙動を完全維持(routing 出力はバイト互換)。
|
|
44
|
+
- `C3_TIER_EPSILON`・`C3_ESCALATION_THRESHOLD` 未設定時も v2.25.0 と完全一致。
|
|
45
|
+
- 既存関数シグネチャ不変(`select_tier_detailed`/`select_tier` の λ は optional kwarg・`SelectionResult` は末尾フィールド追加)。
|
|
46
|
+
- migration 不要(v2.22.0 の 003 で列確保済み)。**破壊的変更なし**。
|
|
47
|
+
|
|
3
48
|
## [2.25.0] - 2026-05-26
|
|
4
49
|
|
|
5
50
|
**tier_bandit cost 蓄積・EPSILON 調整可能化・例外ログ統一**: v2.22.0 で列確保済みの `tier_bandit.total_cost_usd`/`cost_samples` へ実測値を materialize する同期関数を追加。cost-aware tie-break の拮抗判定閾値を定数 SSOT 化し環境変数で上書き可能にする。db.py 既存 6 関数の例外ログを型名統一(SR-R-001)。routing 挙動は不変。cost-weighted Thompson 本格統合は v2.26.0。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-conductor
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.27.0
|
|
4
4
|
Summary: Multi-agent orchestration framework for Claude Code with Codex/Cursor adapters (C3)
|
|
5
5
|
Project-URL: Homepage, https://github.com/satoh-y-0323/claude-code-conductor
|
|
6
6
|
Project-URL: Repository, https://github.com/satoh-y-0323/claude-code-conductor
|
|
@@ -201,7 +201,7 @@ C3 のスラッシュコマンドはすべてスキル(`skills/{name}/SKILL.md
|
|
|
201
201
|
| `c3 recall search "<query>"` または `c3 recall "<query>"` | `.claude/memory/sessions/` 等から類似チャンクを意味検索 |
|
|
202
202
|
| `c3 recall rebuild [--force]` | HNSW インデックスを再構築(初回は fastembed が ~220MB のモデルを取得) |
|
|
203
203
|
| `c3 recall stats` | チャンク数・モデル名・最終 rebuild 日時を表示 |
|
|
204
|
-
| `c3 tier stats` | tier-routing(複雑度に応じた Tier 自動ルーティング)の学習データ・Tier
|
|
204
|
+
| `c3 tier stats` | tier-routing(複雑度に応じた Tier 自動ルーティング)の学習データ・Tier 別コスト・現在の routing パラメータ(λ/ε/escalation・v2.27.0〜)を表形式で表示(`--json` で機械可読出力・`--recent N` で直近 outcome 件数指定)。ルーティング挙動は環境変数 `C3_TIER_COST_LAMBDA`(cost-weighted の重み・`0 ≤ λ ≤ 5`・v2.26.0〜、上限拡張 v2.27.0)/ `C3_TIER_EPSILON` / `C3_ESCALATION_THRESHOLD` で調整可([CLI リファレンス](https://satoh-y-0323.github.io/claude-code-conductor/cli-reference/)参照) |
|
|
205
205
|
|
|
206
206
|
### 基本的な使い方
|
|
207
207
|
|
|
@@ -154,7 +154,7 @@ C3 のスラッシュコマンドはすべてスキル(`skills/{name}/SKILL.md
|
|
|
154
154
|
| `c3 recall search "<query>"` または `c3 recall "<query>"` | `.claude/memory/sessions/` 等から類似チャンクを意味検索 |
|
|
155
155
|
| `c3 recall rebuild [--force]` | HNSW インデックスを再構築(初回は fastembed が ~220MB のモデルを取得) |
|
|
156
156
|
| `c3 recall stats` | チャンク数・モデル名・最終 rebuild 日時を表示 |
|
|
157
|
-
| `c3 tier stats` | tier-routing(複雑度に応じた Tier 自動ルーティング)の学習データ・Tier
|
|
157
|
+
| `c3 tier stats` | tier-routing(複雑度に応じた Tier 自動ルーティング)の学習データ・Tier 別コスト・現在の routing パラメータ(λ/ε/escalation・v2.27.0〜)を表形式で表示(`--json` で機械可読出力・`--recent N` で直近 outcome 件数指定)。ルーティング挙動は環境変数 `C3_TIER_COST_LAMBDA`(cost-weighted の重み・`0 ≤ λ ≤ 5`・v2.26.0〜、上限拡張 v2.27.0)/ `C3_TIER_EPSILON` / `C3_ESCALATION_THRESHOLD` で調整可([CLI リファレンス](https://satoh-y-0323.github.io/claude-code-conductor/cli-reference/)参照) |
|
|
158
158
|
|
|
159
159
|
### 基本的な使い方
|
|
160
160
|
|
|
@@ -145,6 +145,11 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
|
|
|
145
145
|
"agent_cost": agent_cost,
|
|
146
146
|
"tier_cost": tier_cost,
|
|
147
147
|
"tier_cost_rate": tier_cost_rate,
|
|
148
|
+
"routing_params": {
|
|
149
|
+
"cost_lambda": c3_db.resolve_cost_lambda(),
|
|
150
|
+
"epsilon": c3_db.resolve_epsilon(),
|
|
151
|
+
"escalation_threshold": c3_db.resolve_escalation_threshold(),
|
|
152
|
+
},
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
|
|
@@ -247,3 +252,18 @@ def _render_human(snapshot: dict[str, Any]) -> None:
|
|
|
247
252
|
f"{row['rate_usd_per_mtok']:>18.4f}"
|
|
248
253
|
)
|
|
249
254
|
print()
|
|
255
|
+
|
|
256
|
+
print("== routing パラメータ(環境変数で調整可) ==")
|
|
257
|
+
rp = snapshot.get("routing_params", {})
|
|
258
|
+
cost_lambda = rp.get("cost_lambda")
|
|
259
|
+
epsilon = rp.get("epsilon", c3_db.EPSILON_TIEBREAK)
|
|
260
|
+
escalation_threshold = rp.get("escalation_threshold", c3_db.ESCALATION_THRESHOLD_DEFAULT)
|
|
261
|
+
if cost_lambda is None:
|
|
262
|
+
print("λ (C3_TIER_COST_LAMBDA): 未設定 → v2.25.0 互換(ε tie-break のみ)")
|
|
263
|
+
elif cost_lambda == 0.0:
|
|
264
|
+
print("λ: 0.0(cost 無視・純 Thompson)")
|
|
265
|
+
else:
|
|
266
|
+
print(f"λ: {cost_lambda}(全 tier weighting 有効)")
|
|
267
|
+
print(f"ε (C3_TIER_EPSILON): {epsilon}")
|
|
268
|
+
print(f"escalation threshold (C3_ESCALATION_THRESHOLD): {escalation_threshold}")
|
|
269
|
+
print()
|