claude-code-conductor 2.24.0__tar.gz → 2.25.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.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/select_tier.py +49 -6
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/session_stop.py +2 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/CHANGELOG.md +23 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/PKG-INFO +2 -1
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/README.md +1 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/__init__.py +1 -1
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli_tier.py +17 -3
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/db.py +127 -7
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_select_tier.py +229 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_session_stop.py +55 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_tier.py +146 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_db.py +401 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/breaking-changes.txt +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/deletions.txt +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/docs/config-policy.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/_hook_utils.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/check_agent_invocation.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/planner_check.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/pre_compact.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/stop.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/settings.json +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/scripts/record_tier_outcome.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/.gitignore +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/LICENSE +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/LICENSES/chroma-hnswlib-LICENSE +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/LICENSES/chroma-hnswlib-NOTICE +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/hatch_build.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/pyproject.toml +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/question.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_hook_utils.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_record_tier_outcome.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_stop_additional.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_usage_ingester.py +0 -0
- {claude_code_conductor-2.24.0 → claude_code_conductor-2.25.0}/tests/test_worktree_guard.py +0 -0
|
@@ -27,6 +27,7 @@ import collections
|
|
|
27
27
|
import difflib
|
|
28
28
|
import hashlib
|
|
29
29
|
import json
|
|
30
|
+
import math
|
|
30
31
|
import os
|
|
31
32
|
import random
|
|
32
33
|
import re
|
|
@@ -47,8 +48,10 @@ except AttributeError:
|
|
|
47
48
|
try:
|
|
48
49
|
from c3 import db as _c3_db_const # type: ignore[import-not-found]
|
|
49
50
|
LEARNING_THRESHOLD: int = _c3_db_const.LEARNING_THRESHOLD
|
|
51
|
+
EPSILON: float = _c3_db_const.EPSILON_TIEBREAK
|
|
50
52
|
except ImportError:
|
|
51
53
|
LEARNING_THRESHOLD = 30
|
|
54
|
+
EPSILON = 0.05
|
|
52
55
|
|
|
53
56
|
# 複雑度推定のキーワード
|
|
54
57
|
SIMPLE_KEYWORDS = frozenset({
|
|
@@ -111,10 +114,9 @@ _PROMPT_HISTORY_SCAN_LINES = 1000
|
|
|
111
114
|
|
|
112
115
|
TIERS: tuple[str, ...] = ("haiku", "sonnet", "opus")
|
|
113
116
|
|
|
114
|
-
# cost-aware tie-break の拮抗判定閾値(v2.23.0)。
|
|
117
|
+
# cost-aware tie-break の拮抗判定閾値(v2.23.0・SSOT は db.EPSILON_TIEBREAK)。
|
|
115
118
|
# Beta サンプルは 0〜1 スケール。成功率 5pt 以内=実質同等とみなす拮抗判定閾値。
|
|
116
|
-
#
|
|
117
|
-
EPSILON: float = 0.05
|
|
119
|
+
# 過大は成功率犠牲リスク、過小は無発動。C3_TIER_EPSILON env で上書き可(v2.25.0)。
|
|
118
120
|
|
|
119
121
|
|
|
120
122
|
class SelectionResult(NamedTuple):
|
|
@@ -295,6 +297,7 @@ def select_tier_detailed(
|
|
|
295
297
|
*,
|
|
296
298
|
rng: random.Random | None = None,
|
|
297
299
|
cost_map: dict[str, float] | None = None,
|
|
300
|
+
epsilon: float | None = None,
|
|
298
301
|
) -> SelectionResult:
|
|
299
302
|
"""Beta サンプリングまたは uniform 選択で推奨 Tier を SelectionResult で返す。
|
|
300
303
|
|
|
@@ -307,6 +310,7 @@ def select_tier_detailed(
|
|
|
307
310
|
含む完全な dict」のいずれか。partial dict は渡されない前提
|
|
308
311
|
(呼び出し側が全 TIERS 分を構築して保証する)。
|
|
309
312
|
uniform 分岐では cost_map の有無に関わらず完全無視する(探索保護)。
|
|
313
|
+
epsilon: 拮抗判定閾値。None なら module 定数 EPSILON を使う(C3_TIER_EPSILON で上書き可)。
|
|
310
314
|
|
|
311
315
|
Returns:
|
|
312
316
|
SelectionResult(tier, mode, cost_tiebreak, contenders)。
|
|
@@ -323,7 +327,8 @@ def select_tier_detailed(
|
|
|
323
327
|
tier: rng.betavariate(p[0], p[1])
|
|
324
328
|
for tier, p in params.items()
|
|
325
329
|
}
|
|
326
|
-
|
|
330
|
+
eff_epsilon = epsilon if epsilon is not None else EPSILON
|
|
331
|
+
chosen, did_tiebreak, contenders = _cost_tiebreak(samples, cost_map, epsilon=eff_epsilon)
|
|
327
332
|
return SelectionResult(chosen, "thompson", did_tiebreak, contenders)
|
|
328
333
|
|
|
329
334
|
|
|
@@ -332,6 +337,7 @@ def select_tier(
|
|
|
332
337
|
*,
|
|
333
338
|
rng: random.Random | None = None,
|
|
334
339
|
cost_map: dict[str, float] | None = None,
|
|
340
|
+
epsilon: float | None = None,
|
|
335
341
|
) -> tuple[str, str]:
|
|
336
342
|
"""Beta サンプリングまたは uniform 選択で推奨 Tier を返す。
|
|
337
343
|
|
|
@@ -343,13 +349,14 @@ def select_tier(
|
|
|
343
349
|
None なら cost を見ず従来の Thompson Sampling と完全一致。
|
|
344
350
|
uniform 分岐では cost_map の有無に関わらず完全無視する。
|
|
345
351
|
詳細は :func:`select_tier_detailed` を参照。
|
|
352
|
+
epsilon: 拮抗判定閾値。None なら module 定数 EPSILON を使う。
|
|
346
353
|
|
|
347
354
|
Returns:
|
|
348
355
|
``(tier, mode)`` のタプル。``mode`` は ``"thompson"`` / ``"uniform"`` で、
|
|
349
356
|
プロンプトに「学習データ収集中」と表示するかの分岐に使う。
|
|
350
357
|
戻り値型は v2.22.0 以前と完全に不変。
|
|
351
358
|
"""
|
|
352
|
-
result = select_tier_detailed(params, rng=rng, cost_map=cost_map)
|
|
359
|
+
result = select_tier_detailed(params, rng=rng, cost_map=cost_map, epsilon=epsilon)
|
|
353
360
|
return result.tier, result.mode
|
|
354
361
|
|
|
355
362
|
|
|
@@ -522,6 +529,41 @@ def _load_c3_db_module():
|
|
|
522
529
|
return None
|
|
523
530
|
|
|
524
531
|
|
|
532
|
+
def _resolve_epsilon() -> float:
|
|
533
|
+
"""``C3_TIER_EPSILON`` を安全に解決する。
|
|
534
|
+
|
|
535
|
+
不正値(非数値 / 0 以下 / 1 超 / NaN)は受け付けず、stderr 警告 + デフォルト(EPSILON)に戻す。
|
|
536
|
+
未設定 / 空文字は無警告でデフォルトを返す([SR-V-001])。
|
|
537
|
+
"""
|
|
538
|
+
raw = os.environ.get("C3_TIER_EPSILON")
|
|
539
|
+
if raw is None or raw == "":
|
|
540
|
+
return EPSILON
|
|
541
|
+
try:
|
|
542
|
+
x = float(raw)
|
|
543
|
+
except ValueError:
|
|
544
|
+
print(
|
|
545
|
+
f"[select_tier:epsilon] invalid C3_TIER_EPSILON={raw!r}, "
|
|
546
|
+
f"using default {EPSILON}",
|
|
547
|
+
file=sys.stderr,
|
|
548
|
+
)
|
|
549
|
+
return EPSILON
|
|
550
|
+
if math.isnan(x):
|
|
551
|
+
print(
|
|
552
|
+
f"[select_tier:epsilon] C3_TIER_EPSILON={raw!r} is NaN, "
|
|
553
|
+
f"using default {EPSILON}",
|
|
554
|
+
file=sys.stderr,
|
|
555
|
+
)
|
|
556
|
+
return EPSILON
|
|
557
|
+
if x <= 0 or x > 1:
|
|
558
|
+
print(
|
|
559
|
+
f"[select_tier:epsilon] C3_TIER_EPSILON={x} out of range (0, 1], "
|
|
560
|
+
f"using default {EPSILON}",
|
|
561
|
+
file=sys.stderr,
|
|
562
|
+
)
|
|
563
|
+
return EPSILON
|
|
564
|
+
return x
|
|
565
|
+
|
|
566
|
+
|
|
525
567
|
def main() -> int:
|
|
526
568
|
try:
|
|
527
569
|
payload = json.loads(sys.stdin.read())
|
|
@@ -576,7 +618,8 @@ def main() -> int:
|
|
|
576
618
|
except ImportError:
|
|
577
619
|
cost_map = None
|
|
578
620
|
|
|
579
|
-
|
|
621
|
+
eps = _resolve_epsilon()
|
|
622
|
+
result = select_tier_detailed(params, cost_map=cost_map, epsilon=eps)
|
|
580
623
|
tier, mode = result.tier, result.mode
|
|
581
624
|
cost_tiebreak = result.cost_tiebreak
|
|
582
625
|
|
|
@@ -103,6 +103,8 @@ def main() -> int:
|
|
|
103
103
|
from c3.usage_ingester import ingest_session # noqa: PLC0415
|
|
104
104
|
project_dir = _Path(transcript_path).parent
|
|
105
105
|
ingest_session(session_id=session_id, project_dir=project_dir)
|
|
106
|
+
from c3.db import sync_tier_bandit_cost # noqa: PLC0415
|
|
107
|
+
sync_tier_bandit_cost()
|
|
106
108
|
except Exception as e:
|
|
107
109
|
print(f"[session_stop:usage_ingester] failed: {type(e).__name__}", file=sys.stderr)
|
|
108
110
|
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.25.0] - 2026-05-26
|
|
4
|
+
|
|
5
|
+
**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。
|
|
6
|
+
|
|
7
|
+
### 機能追加
|
|
8
|
+
|
|
9
|
+
- **`src/c3/db.py`: `sync_tier_bandit_cost(*, db_path=None) -> None`(新規)**: `read_tier_cost_rate_summary` 由来の model 一致集計値を `tier_bandit` テーブルへ materialize する冪等な同期関数。「全クリア(total_cost_usd=0, cost_samples=0) → 集計 SET」の UPDATE-only 実装(INSERT なし)。session_stop の usage ingest 直後に実行。tier_bandit 行が存在しない複合キー (complexity, tier) は無視(INSERT は行わない)。
|
|
10
|
+
|
|
11
|
+
- **`.claude/hooks/select_tier.py`: `db.EPSILON_TIEBREAK`(0.05)定数 SSOT 化・環境変数 `C3_TIER_EPSILON` 対応**: 従来 `EPSILON=0.05` をモジュールローカルで持っていた値を `c3.db.EPSILON_TIEBREAK` に移し Single Source of Truth 化。`select_tier.py` は `db.EPSILON_TIEBREAK` を参照するよう変更。環境変数 `C3_TIER_EPSILON` に数値が設定されている場合は実行時にその値で上書き可能。NaN・範囲外(0 < x <= 1 の外)・非数値は default 値(0.05)に fallback。env 未設定時の routing 挙動は v2.24.0 と完全一致。
|
|
12
|
+
|
|
13
|
+
### 変更
|
|
14
|
+
|
|
15
|
+
- **`src/c3/cli_tier.py`**: `c3 tier stats` の tier_bandit セクションに `total_cost_usd`(合計コスト USD)および `cost_samples`(計上セッション数)列を追加。値は `sync_tier_bandit_cost` 蓄積値を表示。データ未蓄積(0/0)時はセルを「-」表示。
|
|
16
|
+
|
|
17
|
+
- **`src/c3/db.py`: 例外ログ型名統一(SR-R-001)**: 既存 6 関数 7 箇所の `except Exception as exc` ログを `logger.warning("...: %s", exc)` から `logger.warning("...: %s", type(exc).__name__)` に統一。生 exc message(外部 path・SQL 文・行データ等)の意図しない流出を防止。関数シグネチャ・戻り値・呼び出し元への影響なし。
|
|
18
|
+
|
|
19
|
+
### 後方互換
|
|
20
|
+
|
|
21
|
+
- 既存関数シグネチャ不変(`read_tier_params`・`read_tier_cost_rate_summary` 等)。
|
|
22
|
+
- `select_tier_detailed`/`select_tier` の `epsilon` は optional kwarg(省略時 `db.EPSILON_TIEBREAK` を参照)。
|
|
23
|
+
- migration 不要(`tier_bandit` の cost 列は v2.22.0 の 003 migration で確保済み)。
|
|
24
|
+
- **破壊的変更なし**。
|
|
25
|
+
|
|
3
26
|
## [2.24.0] - 2026-05-25
|
|
4
27
|
|
|
5
28
|
**tier-routing cost 精度向上**: tie-break が使う cost データを model 一致集計・USD/MTok レート化により信頼できるものにする。新関数 2 つを追加し、`select_tier` の cost_map 源を rate 関数へ切替。既存関数・tie-break ロジックは完全不変。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-conductor
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.25.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,6 +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 別コストを表形式で表示(`--json` で機械可読出力・`--recent N` で直近 outcome 件数指定) |
|
|
204
205
|
|
|
205
206
|
### 基本的な使い方
|
|
206
207
|
|
|
@@ -154,6 +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 別コストを表形式で表示(`--json` で機械可読出力・`--recent N` で直近 outcome 件数指定) |
|
|
157
158
|
|
|
158
159
|
### 基本的な使い方
|
|
159
160
|
|
|
@@ -94,6 +94,11 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
|
|
|
94
94
|
bandit_rows: list[dict[str, Any]] = []
|
|
95
95
|
total_trials = 0
|
|
96
96
|
|
|
97
|
+
# cost 列は別 SELECT(read_tier_params は alpha/beta/trials 専用を維持)
|
|
98
|
+
bandit_cost: dict[tuple[str, str], tuple[float, int]] = c3_db.read_tier_bandit_cost(
|
|
99
|
+
db_path=db_path,
|
|
100
|
+
)
|
|
101
|
+
|
|
97
102
|
for complexity in _COMPLEXITIES:
|
|
98
103
|
params = c3_db.read_tier_params(complexity, db_path=db_path)
|
|
99
104
|
for tier in _TIERS:
|
|
@@ -101,6 +106,7 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
|
|
|
101
106
|
total_trials += trials
|
|
102
107
|
denom = alpha + beta
|
|
103
108
|
expected = alpha / denom if denom > 0 else 0.5
|
|
109
|
+
cost_usd, cost_samples = bandit_cost.get((complexity, tier), (0.0, 0))
|
|
104
110
|
bandit_rows.append({
|
|
105
111
|
"complexity": complexity,
|
|
106
112
|
"tier": tier,
|
|
@@ -108,6 +114,8 @@ def _collect_snapshot(db_path, recent_limit: int) -> dict[str, Any]:
|
|
|
108
114
|
"beta": beta,
|
|
109
115
|
"trials": trials,
|
|
110
116
|
"expected_success_rate": expected,
|
|
117
|
+
"total_cost_usd": cost_usd,
|
|
118
|
+
"cost_samples": cost_samples,
|
|
111
119
|
})
|
|
112
120
|
|
|
113
121
|
recent_outcomes: list[dict[str, Any]] = c3_db.read_recent_outcomes(
|
|
@@ -154,12 +162,18 @@ def _render_human(snapshot: dict[str, Any]) -> None:
|
|
|
154
162
|
print()
|
|
155
163
|
|
|
156
164
|
print("== Tier 別累積(tier_bandit) ==")
|
|
157
|
-
print(
|
|
165
|
+
print(
|
|
166
|
+
f"{'complexity':<12} {'tier':<8} {'trials':>6} {'alpha':>5} {'beta':>5} "
|
|
167
|
+
f"{'期待成功率':>10} {'cost_usd':>10} {'cost_samples':>12}"
|
|
168
|
+
)
|
|
158
169
|
for row in snapshot["tier_bandit"]:
|
|
170
|
+
complexity_safe = sanitize_terminal_text(str(row["complexity"]))
|
|
171
|
+
tier_safe = sanitize_terminal_text(str(row["tier"]))
|
|
159
172
|
print(
|
|
160
|
-
f"{
|
|
173
|
+
f"{complexity_safe:<12} {tier_safe:<8} "
|
|
161
174
|
f"{row['trials']:>6} {row['alpha']:>5.2f} {row['beta']:>5.2f} "
|
|
162
|
-
f"{row['expected_success_rate'] * 100:>9.2f}%"
|
|
175
|
+
f"{row['expected_success_rate'] * 100:>9.2f}% "
|
|
176
|
+
f"${row['total_cost_usd']:>9.4f} {row['cost_samples']:>12}"
|
|
163
177
|
)
|
|
164
178
|
print()
|
|
165
179
|
|
|
@@ -35,6 +35,11 @@ _BUSY_TIMEOUT_MS = BUSY_TIMEOUT_MS # 内部互換エイリアス(既存コー
|
|
|
35
35
|
# tier-routing: 学習データ収集期の閾値(合計試行数がこの値未満なら uniform 選択)。
|
|
36
36
|
# SSOT: cli_tier.py / select_tier.py はここから参照する(CR-M-002)。
|
|
37
37
|
LEARNING_THRESHOLD = 30
|
|
38
|
+
# cost-aware tie-break の拮抗判定閾値。Beta サンプルは 0〜1 スケールで、
|
|
39
|
+
# 成功率 5pt(=0.05)以内を拮抗とみなす。本定数が SSOT。
|
|
40
|
+
# 過大にすると成功率を犠牲にするリスク、過小にすると無発動になる。
|
|
41
|
+
# C3_TIER_EPSILON 環境変数で上書き可(v2.25.0)。
|
|
42
|
+
EPSILON_TIEBREAK = 0.05
|
|
38
43
|
|
|
39
44
|
|
|
40
45
|
def _apply_busy_timeout(conn: sqlite3.Connection) -> None:
|
|
@@ -139,7 +144,7 @@ def fetch_review_decisions(
|
|
|
139
144
|
finally:
|
|
140
145
|
conn.close()
|
|
141
146
|
except Exception as exc: # noqa: BLE001
|
|
142
|
-
logger.warning("failed to fetch review_decisions: %s", exc)
|
|
147
|
+
logger.warning("failed to fetch review_decisions: %s", type(exc).__name__)
|
|
143
148
|
return []
|
|
144
149
|
|
|
145
150
|
|
|
@@ -204,7 +209,7 @@ def insert_review_decision(
|
|
|
204
209
|
conn.close()
|
|
205
210
|
return True
|
|
206
211
|
except Exception as exc: # noqa: BLE001
|
|
207
|
-
logger.warning("failed to insert review_decision: %s", exc)
|
|
212
|
+
logger.warning("failed to insert review_decision: %s", type(exc).__name__)
|
|
208
213
|
return False
|
|
209
214
|
|
|
210
215
|
|
|
@@ -270,7 +275,7 @@ def read_tier_params(
|
|
|
270
275
|
finally:
|
|
271
276
|
conn.close()
|
|
272
277
|
except Exception as exc: # noqa: BLE001
|
|
273
|
-
logger.warning("failed to read tier_params: %s", exc)
|
|
278
|
+
logger.warning("failed to read tier_params: %s", type(exc).__name__)
|
|
274
279
|
return defaults
|
|
275
280
|
|
|
276
281
|
result = dict(defaults)
|
|
@@ -342,7 +347,7 @@ def update_tier_params(
|
|
|
342
347
|
conn.close()
|
|
343
348
|
return True
|
|
344
349
|
except Exception as exc: # noqa: BLE001
|
|
345
|
-
logger.warning("failed to update tier_params: %s", exc)
|
|
350
|
+
logger.warning("failed to update tier_params: %s", type(exc).__name__)
|
|
346
351
|
return False
|
|
347
352
|
|
|
348
353
|
|
|
@@ -439,10 +444,10 @@ def read_recent_outcomes(
|
|
|
439
444
|
finally:
|
|
440
445
|
conn.close()
|
|
441
446
|
except sqlite3.OperationalError as exc:
|
|
442
|
-
logger.debug("read_recent_outcomes: table not found or inaccessible: %s", exc)
|
|
447
|
+
logger.debug("read_recent_outcomes: table not found or inaccessible: %s", type(exc).__name__)
|
|
443
448
|
return []
|
|
444
449
|
except Exception as exc: # noqa: BLE001
|
|
445
|
-
logger.warning("read_recent_outcomes: unexpected error: %s", exc)
|
|
450
|
+
logger.warning("read_recent_outcomes: unexpected error: %s", type(exc).__name__)
|
|
446
451
|
return []
|
|
447
452
|
|
|
448
453
|
return [
|
|
@@ -493,7 +498,7 @@ def read_tier_failure_rate(
|
|
|
493
498
|
finally:
|
|
494
499
|
conn.close()
|
|
495
500
|
except Exception as exc: # noqa: BLE001
|
|
496
|
-
logger.warning("failed to read tier_failure_rate: %s", exc)
|
|
501
|
+
logger.warning("failed to read tier_failure_rate: %s", type(exc).__name__)
|
|
497
502
|
return None, 0
|
|
498
503
|
|
|
499
504
|
sample_count = len(rows)
|
|
@@ -1110,3 +1115,118 @@ def set_ingest_offset(
|
|
|
1110
1115
|
except Exception as exc: # noqa: BLE001
|
|
1111
1116
|
logger.warning("failed to set_ingest_offset: %s", type(exc).__name__)
|
|
1112
1117
|
return False
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
def read_tier_bandit_cost(
|
|
1121
|
+
*,
|
|
1122
|
+
db_path: Path | None = None,
|
|
1123
|
+
) -> dict[tuple[str, str], tuple[float, int]]:
|
|
1124
|
+
"""tier_bandit テーブルから cost 列を読む。
|
|
1125
|
+
|
|
1126
|
+
cli_tier.py の _collect_snapshot が alpha/beta/trials(read_tier_params 由来)と
|
|
1127
|
+
別 SELECT で cost を取得するために使う。
|
|
1128
|
+
|
|
1129
|
+
Args:
|
|
1130
|
+
db_path: c3.db のパス。省略時は locate_c3_db() で探索。
|
|
1131
|
+
|
|
1132
|
+
Returns:
|
|
1133
|
+
``{(task_complexity, tier): (total_cost_usd, cost_samples)}`` の dict。
|
|
1134
|
+
テーブル不在 / DB 不在 / エラー時は空 dict を返す。
|
|
1135
|
+
"""
|
|
1136
|
+
if db_path is None:
|
|
1137
|
+
db_path = locate_c3_db()
|
|
1138
|
+
if db_path is None:
|
|
1139
|
+
return {}
|
|
1140
|
+
|
|
1141
|
+
try:
|
|
1142
|
+
conn = sqlite3.connect(str(db_path))
|
|
1143
|
+
try:
|
|
1144
|
+
_apply_busy_timeout(conn)
|
|
1145
|
+
rows = conn.execute(
|
|
1146
|
+
"SELECT task_complexity, tier, total_cost_usd, cost_samples "
|
|
1147
|
+
"FROM tier_bandit"
|
|
1148
|
+
).fetchall()
|
|
1149
|
+
finally:
|
|
1150
|
+
conn.close()
|
|
1151
|
+
except Exception as exc: # noqa: BLE001
|
|
1152
|
+
logger.warning("failed to read tier_bandit_cost: %s", type(exc).__name__)
|
|
1153
|
+
return {}
|
|
1154
|
+
|
|
1155
|
+
return {
|
|
1156
|
+
(row[0], row[1]): (float(row[2]), int(row[3]))
|
|
1157
|
+
for row in rows
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
def sync_tier_bandit_cost(
|
|
1162
|
+
*,
|
|
1163
|
+
db_path: Path | None = None,
|
|
1164
|
+
) -> int:
|
|
1165
|
+
"""tier_bandit テーブルの cost 列を rate_summary 集計値で同期する(冪等 SET 同期)。
|
|
1166
|
+
|
|
1167
|
+
冪等性: 全行の cost 列を 0.0/0 にクリアしてから SET するため、
|
|
1168
|
+
何度呼び出しても結果が同じになる(全クリア→SET の SET 同期)。
|
|
1169
|
+
|
|
1170
|
+
動作:
|
|
1171
|
+
1. ``read_tier_cost_rate_summary`` で (complexity, tier) 別集計を取得。
|
|
1172
|
+
2. 1 トランザクション内で:
|
|
1173
|
+
a. 全行 cost 列クリア: ``UPDATE tier_bandit SET total_cost_usd=0.0, cost_samples=0``
|
|
1174
|
+
(alpha/beta/trials/last_updated は一切変更しない)。
|
|
1175
|
+
b. 各集計行を ``UPDATE tier_bandit SET total_cost_usd=?, cost_samples=?
|
|
1176
|
+
WHERE task_complexity=? AND tier=?`` で SET。
|
|
1177
|
+
``cost_samples`` は DISTINCT session 数(``read_tier_cost_rate_summary``
|
|
1178
|
+
の ``sessions`` フィールド)を格納する。
|
|
1179
|
+
c. ``commit()``。クリア後 SET 前に別プロセスが読む瞬間を作らないため
|
|
1180
|
+
途中で commit しない(R1 冪等性保証)。
|
|
1181
|
+
3. UPDATE-only: tier_bandit に行が存在しない (complexity, tier) は INSERT しない。
|
|
1182
|
+
alpha/beta のない半端な bandit 行の生成を防ぐ(R4 回避)。
|
|
1183
|
+
|
|
1184
|
+
制限:
|
|
1185
|
+
USD/MTok レートの再現には billable_tokens が必要であり、本テーブル単独では
|
|
1186
|
+
再現不可。レートが必要な箇所は ``read_tier_cost_rate_summary`` /
|
|
1187
|
+
``read_tier_cost_rate_for_complexity`` を使うこと。
|
|
1188
|
+
|
|
1189
|
+
Args:
|
|
1190
|
+
db_path: c3.db のパス。省略時は locate_c3_db() で探索。
|
|
1191
|
+
None(locate_c3_db が None を返す)の場合は 0 を返す。
|
|
1192
|
+
|
|
1193
|
+
Returns:
|
|
1194
|
+
cost を SET できた行数(rowcount > 0 の UPDATE 件数合計)。
|
|
1195
|
+
DB 不在 / エラー時は 0 を返す(例外を投げない)。
|
|
1196
|
+
例外発生時は ``finally: conn.close()`` 経由で rollback され、
|
|
1197
|
+
cost 列がクリアされた状態で残る可能性がある。
|
|
1198
|
+
戻り値 0 を受け取った呼び出し元は次回 session_stop での再試行に委ねる。
|
|
1199
|
+
"""
|
|
1200
|
+
if db_path is None:
|
|
1201
|
+
db_path = locate_c3_db()
|
|
1202
|
+
if db_path is None:
|
|
1203
|
+
return 0
|
|
1204
|
+
|
|
1205
|
+
rows = read_tier_cost_rate_summary(db_path=db_path)
|
|
1206
|
+
|
|
1207
|
+
set_count = 0
|
|
1208
|
+
try:
|
|
1209
|
+
conn = sqlite3.connect(str(db_path))
|
|
1210
|
+
try:
|
|
1211
|
+
conn.execute("PRAGMA journal_mode=WAL")
|
|
1212
|
+
_apply_busy_timeout(conn)
|
|
1213
|
+
# Step 1: 全行 cost 列クリア(alpha/beta/trials/last_updated は触らない)
|
|
1214
|
+
conn.execute("UPDATE tier_bandit SET total_cost_usd = 0.0, cost_samples = 0")
|
|
1215
|
+
# Step 2: 各集計行を SET(UPDATE-only: tier_bandit に行が無い場合は rowcount=0)
|
|
1216
|
+
for row in rows:
|
|
1217
|
+
cur = conn.execute(
|
|
1218
|
+
"UPDATE tier_bandit "
|
|
1219
|
+
"SET total_cost_usd = ?, cost_samples = ? "
|
|
1220
|
+
"WHERE task_complexity = ? AND tier = ?",
|
|
1221
|
+
(row["total_cost_usd"], row["sessions"], row["complexity"], row["tier"]),
|
|
1222
|
+
)
|
|
1223
|
+
set_count += cur.rowcount
|
|
1224
|
+
# Step 3: 全 SET が完了してから一括 commit(途中 commit しない)
|
|
1225
|
+
conn.commit()
|
|
1226
|
+
finally:
|
|
1227
|
+
conn.close()
|
|
1228
|
+
except Exception as exc: # noqa: BLE001
|
|
1229
|
+
logger.warning("failed to sync_tier_bandit_cost: %s", type(exc).__name__)
|
|
1230
|
+
return 0
|
|
1231
|
+
|
|
1232
|
+
return set_count
|