claude-code-conductor 2.23.0__tar.gz → 2.24.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.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/select_tier.py +5 -4
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/CHANGELOG.md +27 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/PKG-INFO +1 -1
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/__init__.py +1 -1
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli_tier.py +29 -5
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/db.py +235 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_select_tier.py +81 -4
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_tier.py +384 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_db.py +658 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/breaking-changes.txt +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/deletions.txt +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/docs/config-policy.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/_hook_utils.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/check_agent_invocation.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/planner_check.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/pre_compact.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/stop.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/settings.json +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/dev-workflow/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/dev-workflow/scripts/record_tier_outcome.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/.gitignore +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/LICENSE +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/LICENSES/chroma-hnswlib-LICENSE +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/LICENSES/chroma-hnswlib-NOTICE +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/README.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/hatch_build.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/pyproject.toml +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/question.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_hook_utils.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_record_tier_outcome.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_stop_additional.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_usage_ingester.py +0 -0
- {claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/test_worktree_guard.py +0 -0
|
@@ -144,8 +144,8 @@ def _cost_tiebreak(
|
|
|
144
144
|
Args:
|
|
145
145
|
samples: {tier: beta_sample} の dict(Thompson Sampling 結果)。
|
|
146
146
|
cost_map: {tier: cost} の dict。None なら cost を見ず従来挙動。
|
|
147
|
-
cost は実測
|
|
148
|
-
|
|
147
|
+
cost は実測 rate_usd_per_mtok または静的参照単価(ハイブリッド)。
|
|
148
|
+
v2.24.0 で rate 化(USD/MTok)により実測・静的とも同次元で整合済み。
|
|
149
149
|
``cost_map`` は None、または contenders 全件をキーとして含む dict を
|
|
150
150
|
渡すこと。partial dict を渡すと ``cost_map[t]`` で KeyError が発生する。
|
|
151
151
|
``select_tier_detailed`` 経由では呼び出し側(main)が全 TIERS 分を
|
|
@@ -556,14 +556,15 @@ def main() -> int:
|
|
|
556
556
|
else:
|
|
557
557
|
params = c3_db.read_tier_params(complexity)
|
|
558
558
|
|
|
559
|
-
# v2.
|
|
559
|
+
# v2.24.0: cost_map をハイブリッド解決(実測 rate USD/MTok を主に、欠損 tier は静的単価で補完)。
|
|
560
|
+
# rate 化により measured(実測)と tier_reference_cost(静的)が同次元(USD/MTok)になり単位整合済み。
|
|
560
561
|
# c3_db が None または pricing import 失敗時は cost_map=None で従来 Thompson にデグレード。
|
|
561
562
|
# cost_map=None の場合、select_tier_detailed/_cost_tiebreak は従来 Thompson 挙動と完全一致(引数定義参照)。
|
|
562
563
|
cost_map = None
|
|
563
564
|
if c3_db is not None:
|
|
564
565
|
try:
|
|
565
566
|
from c3 import pricing # type: ignore[import-not-found]
|
|
566
|
-
measured = c3_db.
|
|
567
|
+
measured = c3_db.read_tier_cost_rate_for_complexity(complexity) # {tier: rate USD/MTok} 実測>0 のみ
|
|
567
568
|
cost_map = {}
|
|
568
569
|
for tier_name in TIERS:
|
|
569
570
|
if tier_name in measured and measured[tier_name] > 0:
|
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.24.0] - 2026-05-25
|
|
4
|
+
|
|
5
|
+
**tier-routing cost 精度向上**: tie-break が使う cost データを model 一致集計・USD/MTok レート化により信頼できるものにする。新関数 2 つを追加し、`select_tier` の cost_map 源を rate 関数へ切替。既存関数・tie-break ロジックは完全不変。
|
|
6
|
+
|
|
7
|
+
**スコープ注記**:
|
|
8
|
+
- `tier_bandit.total_cost_usd`/`cost_samples` への書き込み・cost-weighted Thompson 本格統合は **v2.25.0+**。
|
|
9
|
+
- migration なし。**破壊的変更なし**(新関数追加・cost_map 値の意味変更のみ・既存関数 docstring 含め完全不変)。
|
|
10
|
+
|
|
11
|
+
### 機能追加
|
|
12
|
+
|
|
13
|
+
- **`src/c3/db.py`: `read_tier_cost_rate_summary(*, db_path=None) -> list[dict]`(新規)**: model 一致集計・(session, tier) 重複排除・USD/MTok レート化を行う精度向上版の cost 集計関数。`agent_cost_runs` を `agent_type <> 'mainline'` で読み、Python 側で `pricing.resolve_tier(model)` により tier 振り分け。`(session_id, tier)` 粒度で集約後、`tier_recent_outcomes` と突合して `(complexity, tier)` 別に集計。`rate_usd_per_mtok = total_cost_usd / (billable_tokens / 1_000_000)`(`billable_tokens = input_tokens + output_tokens`)で `tier_reference_cost`(USD/MTok)と同次元にする。`billable_tokens == 0` の (complexity,tier) は除外。未知 model 行はスキップ。戻り値 dict キー: `complexity / tier / sessions / total_cost_usd / billable_tokens / rate_usd_per_mtok`。内部畳み込み部は DB 非依存の純関数 `_compute_tier_cost_rate_summary` に分離。
|
|
14
|
+
|
|
15
|
+
- **`src/c3/db.py`: `read_tier_cost_rate_for_complexity(complexity, *, db_path=None) -> dict[str, float]`(新規)**: `read_tier_cost_rate_summary` を complexity 一致 & `rate_usd_per_mtok > 0` でフィルタし `{tier: rate_usd_per_mtok}` を返す薄いラッパー(v2.23.0 の `read_tier_cost_for_complexity` と対称)。データ/DB 不在で `{}`。
|
|
16
|
+
|
|
17
|
+
### 変更
|
|
18
|
+
|
|
19
|
+
- **`.claude/hooks/select_tier.py`**: `main()` の cost_map 構築で `read_tier_cost_for_complexity` → `read_tier_cost_rate_for_complexity` に切替。tie-break ロジック不変・cost_map の値の意味が絶対 USD → USD/MTok レートに変化し、静的 `tier_reference_cost` と単位整合。コメント更新(「混在スケール・厳密化は v2.24.0」→「v2.24.0 で rate 化により整合済み」)。
|
|
20
|
+
|
|
21
|
+
- **`src/c3/cli_tier.py`**: `c3 tier stats` に「Tier 別 USD/MTok レート(model 一致・tie-break が使用)」セクションを追加(complexity/tier/sessions/rate_usd_per_mtok 表示・データなしで「(rate データ未収集)」)。既存 session 合計 USD セクションも維持。「精度向上は v2.24.0」注記を「(粗い概算・session 合計 USD)」に更新。`--json` 出力に `tier_cost_rate` キー自動反映。DB 由来テキストへ `sanitize_terminal_text` 適用。
|
|
22
|
+
|
|
23
|
+
- **`src/c3/__init__.py`**: `__version__` を `"2.23.0"` から `"2.24.0"` に更新。
|
|
24
|
+
|
|
25
|
+
### 後方互換
|
|
26
|
+
|
|
27
|
+
- 既存 `read_tier_cost_summary` / `read_tier_cost_for_complexity` は docstring 含め完全不変。
|
|
28
|
+
- migration なし(`agent_cost_runs` の token 4 列 + model 列は v2.21.0 (002 migration) で既存)。
|
|
29
|
+
|
|
3
30
|
## [2.23.0] - 2026-05-25
|
|
4
31
|
|
|
5
32
|
**tier-routing cost-aware tie-break**: `select_tier` の Thompson Sampling 分岐に「拮抗 tier 群内コスト tie-break」を追加する。サンプル最大から ε(=0.05) 以内の拮抗 tier が複数ある場合のみ、min-max 正規化コストが最安の tier を選ぶ。単独最大なら従来通り(挙動不変)。成功率を犠牲にしない最小スコープの cost 統合。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-conductor
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.24.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
|
|
@@ -22,6 +22,7 @@ import sys
|
|
|
22
22
|
from typing import Any
|
|
23
23
|
|
|
24
24
|
from c3 import db as c3_db
|
|
25
|
+
from c3._terminal import sanitize_terminal_text
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
logger = logging.getLogger(__name__)
|
|
@@ -118,6 +119,8 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
|
|
|
118
119
|
|
|
119
120
|
tier_cost: list[dict[str, Any]] = c3_db.read_tier_cost_summary(db_path=db_path)
|
|
120
121
|
|
|
122
|
+
tier_cost_rate: list[dict[str, Any]] = c3_db.read_tier_cost_rate_summary(db_path=db_path)
|
|
123
|
+
|
|
121
124
|
if total_trials < _LEARNING_THRESHOLD:
|
|
122
125
|
mode = "uniform"
|
|
123
126
|
else:
|
|
@@ -133,6 +136,7 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
|
|
|
133
136
|
"recent_outcomes": recent_outcomes,
|
|
134
137
|
"agent_cost": agent_cost,
|
|
135
138
|
"tier_cost": tier_cost,
|
|
139
|
+
"tier_cost_rate": tier_cost_rate,
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
|
|
@@ -184,9 +188,10 @@ def _render_human(snapshot: dict[str, Any]) -> None:
|
|
|
184
188
|
f"{'in_tok':>9} {'out_tok':>9} {'cache_r':>9} {'cache_w':>9}"
|
|
185
189
|
)
|
|
186
190
|
for row in agent_cost:
|
|
187
|
-
|
|
191
|
+
agent_type_safe = sanitize_terminal_text(str(row["agent_type"]))
|
|
192
|
+
note = " (マクロ集計・tier 学習対象外)" if agent_type_safe == "mainline" else ""
|
|
188
193
|
print(
|
|
189
|
-
f"{
|
|
194
|
+
f"{agent_type_safe:<16} {row['runs']:>5} "
|
|
190
195
|
f"${row['total_cost_usd']:>9.4f} "
|
|
191
196
|
f"{row['input_tokens']:>9} {row['output_tokens']:>9} "
|
|
192
197
|
f"{row['cache_read_tokens']:>9} {row['cache_create_tokens']:>9}"
|
|
@@ -194,18 +199,37 @@ def _render_human(snapshot: dict[str, Any]) -> None:
|
|
|
194
199
|
)
|
|
195
200
|
print()
|
|
196
201
|
|
|
197
|
-
print("== Tier
|
|
202
|
+
print("== Tier 別平均コスト(粗い概算・session 合計 USD) ==")
|
|
198
203
|
tier_cost = snapshot.get("tier_cost", [])
|
|
199
204
|
if not tier_cost:
|
|
200
205
|
print("(cost 紐づけデータ未収集)")
|
|
201
206
|
else:
|
|
202
207
|
print(f"{'complexity':<12} {'tier':<8} {'sessions':>8} {'avg_usd':>10} {'total_usd':>10}")
|
|
203
208
|
for row in tier_cost:
|
|
209
|
+
complexity_safe = sanitize_terminal_text(str(row["complexity"]))
|
|
210
|
+
tier_safe = sanitize_terminal_text(str(row["tier"]))
|
|
204
211
|
print(
|
|
205
|
-
f"{
|
|
212
|
+
f"{complexity_safe:<12} {tier_safe:<8} "
|
|
206
213
|
f"{row['sessions']:>8} "
|
|
207
214
|
f"${row['avg_cost_usd']:>9.4f} "
|
|
208
215
|
f"${row['total_cost_usd']:>9.4f}"
|
|
209
216
|
)
|
|
210
217
|
print()
|
|
211
|
-
print("(注:
|
|
218
|
+
print("(注: 粗い概算・session 合計 USD。v2.24.0 より rate セクション追加済み)")
|
|
219
|
+
print()
|
|
220
|
+
|
|
221
|
+
print("== Tier 別 USD/MTok レート(model 一致・tie-break が使用) ==")
|
|
222
|
+
tier_cost_rate = snapshot.get("tier_cost_rate", [])
|
|
223
|
+
if not tier_cost_rate:
|
|
224
|
+
print("(rate データ未収集)")
|
|
225
|
+
else:
|
|
226
|
+
print(f"{'complexity':<12} {'tier':<8} {'sessions':>8} {'rate_usd_per_mtok':>18}")
|
|
227
|
+
for row in tier_cost_rate:
|
|
228
|
+
complexity_safe = sanitize_terminal_text(str(row["complexity"]))
|
|
229
|
+
tier_safe = sanitize_terminal_text(str(row["tier"]))
|
|
230
|
+
print(
|
|
231
|
+
f"{complexity_safe:<12} {tier_safe:<8} "
|
|
232
|
+
f"{row['sessions']:>8} "
|
|
233
|
+
f"{row['rate_usd_per_mtok']:>18.4f}"
|
|
234
|
+
)
|
|
235
|
+
print()
|
|
@@ -792,6 +792,241 @@ def read_tier_cost_for_complexity(
|
|
|
792
792
|
}
|
|
793
793
|
|
|
794
794
|
|
|
795
|
+
# ---------------------------------------------------------------------------
|
|
796
|
+
# v2.24.0: model 一致集計・USD/MTok レート化(精度向上版)
|
|
797
|
+
# ---------------------------------------------------------------------------
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def _compute_tier_cost_rate_summary(
|
|
801
|
+
cost_rows: list[tuple],
|
|
802
|
+
outcome_rows: list[tuple],
|
|
803
|
+
) -> list[dict]:
|
|
804
|
+
"""agent_cost_runs 行リストと tier_recent_outcomes 行リストから
|
|
805
|
+
(complexity, tier) 別 USD/MTok レートを集計して返す(DB 非依存の純関数)。
|
|
806
|
+
|
|
807
|
+
DB に依存しないため、単体テストで任意のデータを直接渡せる。
|
|
808
|
+
|
|
809
|
+
集計ステップ:
|
|
810
|
+
1. cost_rows の各行を pricing.resolve_tier(model) で tier に振り分け。
|
|
811
|
+
resolve_tier が None の未知モデル行はスキップ。
|
|
812
|
+
2. (session_id, tier) 粒度で cost_sum と billable_tokens を集約。
|
|
813
|
+
PK=(session_id, agent_id, model) のため行は元々一意 → 重複排除は構造的に成立。
|
|
814
|
+
3. outcome_rows の各行 (session_id, task_complexity, tier) を
|
|
815
|
+
(session_id, tier) バケットと突合し (complexity, tier) 別に集約。
|
|
816
|
+
4. rate 化: rate_usd_per_mtok = total_cost_usd / (billable_tokens / 1_000_000)。
|
|
817
|
+
billable_tokens == 0 の (complexity, tier) は除外。
|
|
818
|
+
|
|
819
|
+
分母の定義:
|
|
820
|
+
billable_tokens = input_tokens + output_tokens のみ(cache tokens 除外)。
|
|
821
|
+
tier_reference_cost が input+output 単価和である次元に揃えるため。
|
|
822
|
+
|
|
823
|
+
分子の注記:
|
|
824
|
+
total_cost_usd は cache コストも含むが、cache 単価は input の約 1/10 と小さく
|
|
825
|
+
tier 間順位(min-max が見る対象)の単調性は保たれる。
|
|
826
|
+
厳密な cache 分離は過剰最適化のためスコープ外(v2.24.0 許容)。
|
|
827
|
+
|
|
828
|
+
cross-outcome 重複の残存許容:
|
|
829
|
+
1 session が同一 tier で複数の complexity outcome を持つレアケースでは、
|
|
830
|
+
その session のコストが複数 (complexity, tier) に重複加算される。
|
|
831
|
+
session_id JOIN では帰属先 complexity が原理的に不明なため按分は恣意的であり
|
|
832
|
+
精度を逆に損なう。残存許容・docstring 明記(按分は将来検討)。
|
|
833
|
+
|
|
834
|
+
Args:
|
|
835
|
+
cost_rows: agent_cost_runs の行タプル。
|
|
836
|
+
各要素: (session_id, model, total_cost_usd, input_tokens, output_tokens)。
|
|
837
|
+
outcome_rows: tier_recent_outcomes の行タプル。
|
|
838
|
+
各要素: (session_id, task_complexity, tier)。
|
|
839
|
+
DISTINCT 化・session_id NOT NULL フィルタ済み想定。
|
|
840
|
+
|
|
841
|
+
Returns:
|
|
842
|
+
dict リスト。キー: complexity / tier / sessions / total_cost_usd /
|
|
843
|
+
billable_tokens / rate_usd_per_mtok。
|
|
844
|
+
billable_tokens == 0 の (complexity, tier) は除外。
|
|
845
|
+
返却順は ``rate_usd_per_mtok`` 降順(既存 ``read_tier_cost_summary`` の
|
|
846
|
+
``ORDER BY total_cost_usd DESC`` と対称)。
|
|
847
|
+
"""
|
|
848
|
+
from c3 import pricing # noqa: PLC0415
|
|
849
|
+
|
|
850
|
+
# Step 1-2: (session_id, tier) 粒度でコスト集約
|
|
851
|
+
# bucket: {(session_id, tier): {"cost_sum": float, "billable": int}}
|
|
852
|
+
bucket: dict[tuple[str, str], dict] = {}
|
|
853
|
+
for row in cost_rows:
|
|
854
|
+
sess_id, model, total_cost, input_tok, output_tok = row
|
|
855
|
+
tier = pricing.resolve_tier(model)
|
|
856
|
+
if tier is None:
|
|
857
|
+
continue # 未知モデルはスキップ(read 規約: ログなし)
|
|
858
|
+
key = (sess_id, tier)
|
|
859
|
+
if key not in bucket:
|
|
860
|
+
bucket[key] = {"cost_sum": 0.0, "billable": 0}
|
|
861
|
+
bucket[key]["cost_sum"] += float(total_cost) if total_cost is not None else 0.0
|
|
862
|
+
bucket[key]["billable"] += (
|
|
863
|
+
(int(input_tok) if input_tok else 0)
|
|
864
|
+
+ (int(output_tok) if output_tok else 0)
|
|
865
|
+
)
|
|
866
|
+
|
|
867
|
+
# Step 3: outcome を (complexity, tier) 別に集約
|
|
868
|
+
# agg: {(complexity, tier): {"sessions": int, "cost_sum": float, "billable": int}}
|
|
869
|
+
agg: dict[tuple[str, str], dict] = {}
|
|
870
|
+
for row in outcome_rows:
|
|
871
|
+
sess_id, complexity, tier = row
|
|
872
|
+
key_bucket = (sess_id, tier)
|
|
873
|
+
if key_bucket not in bucket:
|
|
874
|
+
continue # cost データがない session は集計対象外
|
|
875
|
+
agg_key = (complexity, tier)
|
|
876
|
+
if agg_key not in agg:
|
|
877
|
+
agg[agg_key] = {"sessions": 0, "cost_sum": 0.0, "billable": 0}
|
|
878
|
+
agg[agg_key]["sessions"] += 1
|
|
879
|
+
agg[agg_key]["cost_sum"] += bucket[key_bucket]["cost_sum"]
|
|
880
|
+
agg[agg_key]["billable"] += bucket[key_bucket]["billable"]
|
|
881
|
+
|
|
882
|
+
# Step 4: rate 化・billable == 0 を除外
|
|
883
|
+
result = []
|
|
884
|
+
for (complexity, tier), vals in agg.items():
|
|
885
|
+
billable = vals["billable"]
|
|
886
|
+
if billable == 0:
|
|
887
|
+
continue
|
|
888
|
+
rate = vals["cost_sum"] / (billable / 1_000_000)
|
|
889
|
+
result.append(
|
|
890
|
+
{
|
|
891
|
+
"complexity": complexity,
|
|
892
|
+
"tier": tier,
|
|
893
|
+
"sessions": vals["sessions"],
|
|
894
|
+
"total_cost_usd": vals["cost_sum"],
|
|
895
|
+
"billable_tokens": billable,
|
|
896
|
+
"rate_usd_per_mtok": rate,
|
|
897
|
+
}
|
|
898
|
+
)
|
|
899
|
+
result.sort(key=lambda r: r["rate_usd_per_mtok"], reverse=True)
|
|
900
|
+
return result
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
def read_tier_cost_rate_summary(
|
|
904
|
+
*,
|
|
905
|
+
db_path: Path | None = None,
|
|
906
|
+
) -> list[dict]:
|
|
907
|
+
"""model 一致集計で (complexity, tier) 別 USD/MTok レートを返す(精度向上版)。
|
|
908
|
+
|
|
909
|
+
v2.23.0 までの ``read_tier_cost_summary`` は session 全体の非 mainline コストを
|
|
910
|
+
合算しており、outcome の tier の model に限定していなかった(H5 テストが確認)。
|
|
911
|
+
本関数はこの不正確性を解消し、``pricing.resolve_tier(model)`` による model 一致
|
|
912
|
+
集計と USD/MTok レート化(``tier_reference_cost`` と同次元)を行う。
|
|
913
|
+
|
|
914
|
+
集計アルゴリズム(内部実装は :func:`_compute_tier_cost_rate_summary` 参照):
|
|
915
|
+
1. ``agent_cost_runs`` を ``agent_type <> 'mainline'`` で素読み。
|
|
916
|
+
2. Python 側で各行を ``pricing.resolve_tier(model)`` で tier に振り分け。
|
|
917
|
+
``resolve_tier`` が ``None`` の未知モデル行はスキップ。
|
|
918
|
+
3. ``(session_id, tier)`` 粒度で ``cost_sum`` と
|
|
919
|
+
``billable_tokens = input_tokens + output_tokens`` を集約。
|
|
920
|
+
PK=(session_id, agent_id, model) のため行は元々一意 → 重複排除は構造的に成立。
|
|
921
|
+
4. ``tier_recent_outcomes`` を
|
|
922
|
+
``DISTINCT (session_id, task_complexity, tier)``(``session_id IS NOT NULL``)で読み、
|
|
923
|
+
``(session_id, tier)`` バケットと突合して ``(complexity, tier)`` 別に集約。
|
|
924
|
+
5. rate 化: ``rate_usd_per_mtok = total_cost_usd / (billable_tokens / 1_000_000)``。
|
|
925
|
+
``billable_tokens == 0`` の ``(complexity, tier)`` は除外。
|
|
926
|
+
|
|
927
|
+
分母の定義:
|
|
928
|
+
``billable_tokens = input_tokens + output_tokens`` のみ(cache tokens 除外)。
|
|
929
|
+
``tier_reference_cost`` が input+output 単価和である次元に揃えるため。
|
|
930
|
+
|
|
931
|
+
分子の注記:
|
|
932
|
+
``total_cost_usd`` は cache コストも含むが、cache 単価は input の約 1/10 と小さく
|
|
933
|
+
tier 間順位(min-max が見る対象)の単調性は保たれる(v2.24.0 許容)。
|
|
934
|
+
|
|
935
|
+
cross-outcome 重複の残存許容:
|
|
936
|
+
1 session が同一 tier で複数の complexity outcome を持つレアケースでは、
|
|
937
|
+
その session のコストが複数 ``(complexity, tier)`` に重複加算される。
|
|
938
|
+
session_id JOIN では帰属先 complexity が原理的に不明なため按分は恣意的であり
|
|
939
|
+
精度を逆に損なう。残存許容・docstring 明記(按分は将来検討)。
|
|
940
|
+
|
|
941
|
+
Args:
|
|
942
|
+
db_path: c3.db のパス。省略時は :func:`locate_c3_db` で探索。
|
|
943
|
+
|
|
944
|
+
Returns:
|
|
945
|
+
各行を dict にしたリスト。キー:
|
|
946
|
+
``complexity``(str) / ``tier``(str) / ``sessions``(int) /
|
|
947
|
+
``total_cost_usd``(float) / ``billable_tokens``(int) /
|
|
948
|
+
``rate_usd_per_mtok``(float)。
|
|
949
|
+
``billable_tokens == 0`` の行は除外。
|
|
950
|
+
テーブル不在 / データ不在 / DB 不在 / エラー時は空リストを返す。
|
|
951
|
+
"""
|
|
952
|
+
if db_path is None:
|
|
953
|
+
db_path = locate_c3_db()
|
|
954
|
+
if db_path is None:
|
|
955
|
+
return []
|
|
956
|
+
|
|
957
|
+
try:
|
|
958
|
+
conn = sqlite3.connect(str(db_path))
|
|
959
|
+
try:
|
|
960
|
+
# WAL は書き込みヘルパー呼び出し時または migrate 時に設定済みの前提
|
|
961
|
+
# (read_agent_cost_summary 等の read ヘルパーと同方針 / CR-M-001)
|
|
962
|
+
_apply_busy_timeout(conn)
|
|
963
|
+
cost_rows = conn.execute(
|
|
964
|
+
"SELECT session_id, model, total_cost_usd, "
|
|
965
|
+
" input_tokens, output_tokens "
|
|
966
|
+
"FROM agent_cost_runs "
|
|
967
|
+
"WHERE agent_type <> 'mainline'"
|
|
968
|
+
).fetchall()
|
|
969
|
+
outcome_rows = conn.execute(
|
|
970
|
+
"SELECT DISTINCT session_id, task_complexity, tier "
|
|
971
|
+
"FROM tier_recent_outcomes "
|
|
972
|
+
"WHERE session_id IS NOT NULL"
|
|
973
|
+
).fetchall()
|
|
974
|
+
finally:
|
|
975
|
+
conn.close()
|
|
976
|
+
except sqlite3.OperationalError as exc:
|
|
977
|
+
# テーブル不在(no such table)は [] を返す(DB 未初期化でも止めない)
|
|
978
|
+
logger.debug(
|
|
979
|
+
"read_tier_cost_rate_summary: table not found or inaccessible: %s",
|
|
980
|
+
type(exc).__name__,
|
|
981
|
+
)
|
|
982
|
+
return []
|
|
983
|
+
except Exception as exc: # noqa: BLE001
|
|
984
|
+
logger.warning(
|
|
985
|
+
"read_tier_cost_rate_summary: unexpected error: %s", type(exc).__name__
|
|
986
|
+
)
|
|
987
|
+
return []
|
|
988
|
+
|
|
989
|
+
return _compute_tier_cost_rate_summary(cost_rows, outcome_rows)
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
def read_tier_cost_rate_for_complexity(
|
|
993
|
+
complexity: str,
|
|
994
|
+
*,
|
|
995
|
+
db_path: Path | None = None,
|
|
996
|
+
) -> dict[str, float]:
|
|
997
|
+
"""complexity 別の tier USD/MTok レートを {tier: rate_usd_per_mtok} で返す(精度向上版)。
|
|
998
|
+
|
|
999
|
+
tie-break のハイブリッド cost 源(model 一致集計・rate 化)。
|
|
1000
|
+
complexity 一致 & rate_usd_per_mtok > 0 のみ。
|
|
1001
|
+
``read_tier_cost_rate_summary`` の薄いラッパー。
|
|
1002
|
+
|
|
1003
|
+
v2.23.0 の ``read_tier_cost_for_complexity``(session 合計 avg_cost_usd)と対称な構造を持ち、
|
|
1004
|
+
cost_map の値を「絶対 USD」から「USD/MTok レート」へ精度向上させた代替関数。
|
|
1005
|
+
これにより fallback の ``tier_reference_cost``(静的 per-MTok 単価)と同次元になり、
|
|
1006
|
+
tie-break の min-max 正規化が同スケールで比較可能になる。
|
|
1007
|
+
|
|
1008
|
+
DB アクセスは ``read_tier_cost_rate_summary`` に委譲するため、
|
|
1009
|
+
DB 例外処理・busy_timeout・read 規約は同関数から継承される。
|
|
1010
|
+
データ/DB 不在で ``read_tier_cost_rate_summary`` が ``[]`` を返す場合、
|
|
1011
|
+
本関数は ``{}`` を返す。
|
|
1012
|
+
|
|
1013
|
+
Args:
|
|
1014
|
+
complexity: フィルタ対象の complexity 値("simple" / "medium" / "complex")。
|
|
1015
|
+
db_path: c3.db のパス。省略時は :func:`locate_c3_db` で探索
|
|
1016
|
+
(``read_tier_cost_rate_summary`` に委譲)。
|
|
1017
|
+
|
|
1018
|
+
Returns:
|
|
1019
|
+
``{tier: rate_usd_per_mtok}`` の dict。
|
|
1020
|
+
該当データ不在・DB 不在・エラー時は ``{}``。
|
|
1021
|
+
"""
|
|
1022
|
+
rows = read_tier_cost_rate_summary(db_path=db_path)
|
|
1023
|
+
return {
|
|
1024
|
+
r["tier"]: r["rate_usd_per_mtok"]
|
|
1025
|
+
for r in rows
|
|
1026
|
+
if r["complexity"] == complexity and r["rate_usd_per_mtok"] > 0
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
|
|
795
1030
|
def get_ingest_offset(
|
|
796
1031
|
file_key: str,
|
|
797
1032
|
*,
|
{claude_code_conductor-2.23.0 → claude_code_conductor-2.24.0}/tests/hooks/test_select_tier.py
RENAMED
|
@@ -923,7 +923,7 @@ class TestMainCostMapIntegration:
|
|
|
923
923
|
) -> None:
|
|
924
924
|
"""cost_tiebreak が発動する状況で main 経由の tier_selection.json に cost_tiebreak=true が出る(AC-2)。
|
|
925
925
|
|
|
926
|
-
c3_db.
|
|
926
|
+
c3_db.read_tier_cost_rate_for_complexity をモックして measured を注入し、
|
|
927
927
|
拮抗する params + 安い tier が選ばれるケースを検証する。
|
|
928
928
|
"""
|
|
929
929
|
mod = _load_hook_module()
|
|
@@ -931,7 +931,7 @@ class TestMainCostMapIntegration:
|
|
|
931
931
|
monkeypatch.setattr(mod, "TIER_SELECTION_PATH", str(target))
|
|
932
932
|
|
|
933
933
|
# haiku と sonnet が拮抗する params (同一 Beta パラメータ、trials >= 30)
|
|
934
|
-
#
|
|
934
|
+
# read_tier_cost_rate_for_complexity は measured={} を返す(全て静的 fallback)
|
|
935
935
|
tiebreak_params = {
|
|
936
936
|
"haiku": (10.0, 10.0, 30),
|
|
937
937
|
"sonnet": (10.0, 10.0, 30),
|
|
@@ -940,7 +940,7 @@ class TestMainCostMapIntegration:
|
|
|
940
940
|
|
|
941
941
|
mock_c3_db = types.SimpleNamespace(
|
|
942
942
|
read_tier_params=lambda complexity, **kw: tiebreak_params,
|
|
943
|
-
|
|
943
|
+
read_tier_cost_rate_for_complexity=lambda complexity, **kw: {}, # 実測なし → 全て静的 fallback
|
|
944
944
|
read_tier_failure_rate=lambda complexity, tier: (None, 0), # escalation しない
|
|
945
945
|
)
|
|
946
946
|
monkeypatch.setattr(mod, "_load_c3_db_module", lambda: mock_c3_db)
|
|
@@ -988,7 +988,7 @@ class TestMainCostMapIntegration:
|
|
|
988
988
|
}
|
|
989
989
|
mock_c3_db = types.SimpleNamespace(
|
|
990
990
|
read_tier_params=lambda complexity, **kw: dominant_params,
|
|
991
|
-
|
|
991
|
+
read_tier_cost_rate_for_complexity=lambda complexity, **kw: {},
|
|
992
992
|
read_tier_failure_rate=lambda complexity, tier: (None, 0), # escalation しない
|
|
993
993
|
)
|
|
994
994
|
monkeypatch.setattr(mod, "_load_c3_db_module", lambda: mock_c3_db)
|
|
@@ -1003,3 +1003,80 @@ class TestMainCostMapIntegration:
|
|
|
1003
1003
|
# 単独最大 → cost_tiebreak キーが出ないはず
|
|
1004
1004
|
assert "cost_tiebreak" not in sel
|
|
1005
1005
|
assert sel["tier"] == "haiku"
|
|
1006
|
+
|
|
1007
|
+
def test_main_rate_cost_map_no_crash(
|
|
1008
|
+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture
|
|
1009
|
+
) -> None:
|
|
1010
|
+
"""main() が read_tier_cost_rate_for_complexity を呼び AttributeError なく完了する(AC-6 単位整合)。
|
|
1011
|
+
|
|
1012
|
+
v2.24.0 で cost_map 源を read_tier_cost_for_complexity から
|
|
1013
|
+
read_tier_cost_rate_for_complexity に切り替えたため、
|
|
1014
|
+
SimpleNamespace に read_tier_cost_rate_for_complexity が存在しないと
|
|
1015
|
+
AttributeError が発生する。この属性名が正しく解決されることを確認。
|
|
1016
|
+
"""
|
|
1017
|
+
mod = _load_hook_module()
|
|
1018
|
+
target = tmp_path / "tier_selection.json"
|
|
1019
|
+
monkeypatch.setattr(mod, "TIER_SELECTION_PATH", str(target))
|
|
1020
|
+
|
|
1021
|
+
params = {
|
|
1022
|
+
"haiku": (5.0, 5.0, 20),
|
|
1023
|
+
"sonnet": (5.0, 5.0, 20),
|
|
1024
|
+
"opus": (5.0, 5.0, 20),
|
|
1025
|
+
}
|
|
1026
|
+
# read_tier_cost_rate_for_complexity(旧名なし)のみ定義 → 旧名があると AttributeError
|
|
1027
|
+
mock_c3_db = types.SimpleNamespace(
|
|
1028
|
+
read_tier_params=lambda complexity, **kw: params,
|
|
1029
|
+
read_tier_cost_rate_for_complexity=lambda complexity, **kw: {}, # rate 関数・実測なし
|
|
1030
|
+
read_tier_failure_rate=lambda complexity, tier: (None, 0),
|
|
1031
|
+
)
|
|
1032
|
+
monkeypatch.setattr(mod, "_load_c3_db_module", lambda: mock_c3_db)
|
|
1033
|
+
|
|
1034
|
+
monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps({"prompt": "テスト"})))
|
|
1035
|
+
random.seed(0)
|
|
1036
|
+
|
|
1037
|
+
# AttributeError が発生しないこと、かつ rc=0 であること
|
|
1038
|
+
rc = mod.main()
|
|
1039
|
+
assert rc == 0
|
|
1040
|
+
assert target.is_file()
|
|
1041
|
+
|
|
1042
|
+
def test_main_rate_cost_map_with_measured_uses_rate(
|
|
1043
|
+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture
|
|
1044
|
+
) -> None:
|
|
1045
|
+
"""main() が rate 実測値を cost_map に使い、静的 fallback と同次元で比較する(AC-6)。
|
|
1046
|
+
|
|
1047
|
+
read_tier_cost_rate_for_complexity が haiku=6.0 USD/MTok を返す場合、
|
|
1048
|
+
cost_map['haiku'] は 6.0 になる(tier_reference_cost('haiku')=6.0 の静的値と同次元)。
|
|
1049
|
+
単位混在が解消されており crash なく rc=0 で完了することを確認。
|
|
1050
|
+
|
|
1051
|
+
NOTE: cost_tiebreak の発動 assert は
|
|
1052
|
+
``test_main_cost_tiebreak_appears_in_json_when_triggered`` で担保。
|
|
1053
|
+
本テストは rate 関数への切替が AttributeError なく完了することの確認が主眼。
|
|
1054
|
+
"""
|
|
1055
|
+
mod = _load_hook_module()
|
|
1056
|
+
target = tmp_path / "tier_selection.json"
|
|
1057
|
+
monkeypatch.setattr(mod, "TIER_SELECTION_PATH", str(target))
|
|
1058
|
+
|
|
1059
|
+
# haiku に実測 rate を返す(tier_reference_cost と同次元 USD/MTok)
|
|
1060
|
+
measured_rates = {"haiku": 6.0}
|
|
1061
|
+
|
|
1062
|
+
params = {
|
|
1063
|
+
"haiku": (10.0, 10.0, 30),
|
|
1064
|
+
"sonnet": (10.0, 10.0, 30),
|
|
1065
|
+
"opus": (1.0, 10.0, 10),
|
|
1066
|
+
}
|
|
1067
|
+
mock_c3_db = types.SimpleNamespace(
|
|
1068
|
+
read_tier_params=lambda complexity, **kw: params,
|
|
1069
|
+
read_tier_cost_rate_for_complexity=lambda complexity, **kw: dict(measured_rates),
|
|
1070
|
+
read_tier_failure_rate=lambda complexity, tier: (None, 0),
|
|
1071
|
+
)
|
|
1072
|
+
monkeypatch.setattr(mod, "_load_c3_db_module", lambda: mock_c3_db)
|
|
1073
|
+
|
|
1074
|
+
monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps({"prompt": "テスト"})))
|
|
1075
|
+
random.seed(0)
|
|
1076
|
+
|
|
1077
|
+
rc = mod.main()
|
|
1078
|
+
assert rc == 0
|
|
1079
|
+
assert target.is_file()
|
|
1080
|
+
sel = json.loads(target.read_text(encoding="utf-8"))
|
|
1081
|
+
# 拮抗群が存在するので cost_tiebreak が発動する可能性があるが crash しないことが重要
|
|
1082
|
+
assert sel.get("tier") in ("haiku", "sonnet", "opus")
|