claude-code-conductor 2.43.0__tar.gz → 2.45.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.45.0/.claude/hooks/pre_compact.py +205 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/stop.py +44 -1
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/CHANGELOG.md +26 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/PKG-INFO +1 -1
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/__init__.py +1 -1
- claude_code_conductor-2.45.0/tests/_pre_compact_helpers.py +88 -0
- claude_code_conductor-2.45.0/tests/test_pre_compact.py +1014 -0
- claude_code_conductor-2.45.0/tests/test_precompact_additional.py +149 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_stop_additional.py +510 -0
- claude_code_conductor-2.43.0/.claude/hooks/pre_compact.py +0 -61
- claude_code_conductor-2.43.0/tests/test_pre_compact.py +0 -265
- claude_code_conductor-2.43.0/tests/test_precompact_additional.py +0 -135
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/design-critic.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/breaking-changes.txt +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/deletions.txt +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/docs/config-policy.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/_hook_utils.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/check_agent_invocation.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/planner_check.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/recall_autorebuild.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/select_tier.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/settings.json +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/brainstorm/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/init-session/scripts/session_guard.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/.gitignore +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/ARCHITECTURE.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSE +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/numpy-LICENSE +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/README.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/hatch_build.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/pyproject.toml +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/_db_params.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_tier.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/db.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/gitutil.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/004_agent_outcomes.sql +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/question.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_hook_utils.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_recall_autorebuild.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_record_agent_outcome.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_select_tier.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_session_guard.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_check_deletions.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_doctor.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_tier.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_db.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_gitutil.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_usage_ingester.py +0 -0
- {claude_code_conductor-2.43.0 → claude_code_conductor-2.45.0}/tests/test_worktree_guard.py +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""PreCompact hook: append checkpoint marker (デバウンス付き)。
|
|
3
|
+
|
|
4
|
+
直近 DEBOUNCE_WINDOW_SECONDS 秒以内に PreCompact checkpoint が既に存在する場合は、
|
|
5
|
+
checkpoint の追記をスキップする(PreCompact の連続起動による重複追記を防ぐ
|
|
6
|
+
デバウンス機構)。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
import sys
|
|
13
|
+
from datetime import datetime, timedelta, timezone
|
|
14
|
+
|
|
15
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
16
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
17
|
+
|
|
18
|
+
from session_utils import SESSION_JSON_MARKER, append_checkpoint, is_worktree, SESSIONS_DIR
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# デバウンス窓幅(秒)。直近の PreCompact checkpoint からこの秒数以内の再実行は
|
|
22
|
+
# 追記をスキップする(4連続起動の重複防止)。
|
|
23
|
+
DEBOUNCE_WINDOW_SECONDS = 10
|
|
24
|
+
|
|
25
|
+
# checkpoint 行抽出のため splitlines() に渡す前に除去する行区切り文字パターン。
|
|
26
|
+
# stop.py::_INHERIT_SANITIZE_RE / session_utils._VALUE_SANITIZE_RE と同一範囲(対称)。
|
|
27
|
+
# str.splitlines() は \n 以外に \v / \f / \x1c-\x1e / \x85 / U+2028 / U+2029 でも
|
|
28
|
+
# 行分割する。append_checkpoint が書き込む summary body(未サニタイズの trigger 等)に
|
|
29
|
+
# これらの文字+checkpoint 行に酷似する文字列が混入すると、splitlines() が偽の行を
|
|
30
|
+
# 生成し _PRECOMPACT_CHECKPOINT_RE が誤マッチしてデバウンスの偽陽性を起こし得る。
|
|
31
|
+
# stop.py の _inherit_pending_tasks と同趣旨で、分割前に事前除去する。
|
|
32
|
+
# raw string は \uXXXX を解釈しないため U+2028 / U+2029 は chr() で連結する。
|
|
33
|
+
_LINE_SEPARATOR_SANITIZE_RE = re.compile(
|
|
34
|
+
r'[\x00-\x08\x0b-\x1f\x7f-\x9f' + chr(0x2028) + chr(0x2029) + r']'
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# checkpoint 行の固定プレフィックス。PreCompact ラベルへのアンカーとして使う
|
|
38
|
+
# (他ラベル、例えば Wave checkpoint を除外する)。
|
|
39
|
+
_PRECOMPACT_CHECKPOINT_PREFIX = '## [Checkpoint: PreCompact:'
|
|
40
|
+
|
|
41
|
+
# checkpoint 行として許容する最大長(fix-cycle-2, security-review-report-20260704-075817.md
|
|
42
|
+
# [SR-NEW])。正規の checkpoint ヘッダ行は数十〜数百文字で十分なため、これを超える行は
|
|
43
|
+
# パース対象から除外する(regex 撤去後の belt-and-suspenders ガード)。
|
|
44
|
+
MAX_CHECKPOINT_LINE_LEN = 512
|
|
45
|
+
|
|
46
|
+
# parse 成功した timestamp が `now` をこの秒数を超えて上回る場合、構造的異常値として
|
|
47
|
+
# 棄却する(fail-open)。checkpoint の timestamp は append_checkpoint が同一ホストの
|
|
48
|
+
# UTC 実時刻で書くため、書き込み時刻は必ず読み取り時刻以前になるはず。
|
|
49
|
+
#
|
|
50
|
+
# 60 秒は独立したセキュリティ判断としての保守値である。正当な checkpoint 行の
|
|
51
|
+
# timestamp が読み取り時刻をわずかに上回り得る唯一の現実的経路は、NTP による
|
|
52
|
+
# クライアント時計の後方ステップ補正(書き込み時点で実時刻より進んでいた時計が、
|
|
53
|
+
# 読み取り時点までに巻き戻される)であり、この巻き戻し幅を吸収するために許容する。
|
|
54
|
+
# 通常の NTP ステップ補正は秒〜十数秒規模のため 60 秒あれば十分に吸収できる。
|
|
55
|
+
#
|
|
56
|
+
# この値を大きく取り過ぎると、許容スキュー以内の未来日時 checkpoint を注入された
|
|
57
|
+
# 場合に、実時刻がその timestamp に追いつくまでデバウンスが効き続け、checkpoint 追記
|
|
58
|
+
# が最大で許容秒数ぶん停止する([SR-V-001])。60 秒に
|
|
59
|
+
# 抑えることで、悪意ある注入・データ破損いずれの場合も最大デバウンス停止時間を
|
|
60
|
+
# 60 秒以内に限定する。
|
|
61
|
+
#
|
|
62
|
+
# 未来スキューを跨いだ許容/棄却境界はテスト側で module.FUTURE_SKEW_TOLERANCE_SECONDS
|
|
63
|
+
# を参照した相対時刻テストとして固定しており、テストは now= を明示指定して壁時計に
|
|
64
|
+
# 依存しない(値をテストフィクスチャ都合で膨らませる必要はない)。
|
|
65
|
+
FUTURE_SKEW_TOLERANCE_SECONDS = 60
|
|
66
|
+
|
|
67
|
+
# parse 失敗時の stderr 診断ログに出す捕捉テキストの最大長
|
|
68
|
+
# (fix-cycle-2, security-review-report-20260704-075817.md [SR-R-001])。
|
|
69
|
+
DIAGNOSTIC_MAX_LEN = 64
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _last_precompact_checkpoint_dt(session_file, now=None):
|
|
73
|
+
"""session_file 内の最後の PreCompact checkpoint 行から aware datetime を返す。
|
|
74
|
+
|
|
75
|
+
ファイル無し・PreCompact 行無し・parse 不能・naive datetime・未来日時(許容
|
|
76
|
+
スキュー超)など、あらゆる異常系で None を返す(fail-open の中核)。
|
|
77
|
+
|
|
78
|
+
`now` は未来日時ガードの基準時刻(省略時は呼び出し時点の UTC 実時刻)。
|
|
79
|
+
呼び出し元(main)と判定基準を一致させたい場合は明示的に渡すこと。
|
|
80
|
+
"""
|
|
81
|
+
if now is None:
|
|
82
|
+
now = datetime.now(timezone.utc)
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
with open(session_file, encoding='utf-8') as f:
|
|
86
|
+
content = f.read()
|
|
87
|
+
except (OSError, ValueError, TypeError):
|
|
88
|
+
# 「ファイル無し」は初回起動時の通常ケース。ほぼ毎日 1 回 stderr へ
|
|
89
|
+
# 出すとスパムになるため、ここでは無音のまま fail-open する(CR-L-003)。
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
# splitlines() が特殊行区切り文字(\x85 / U+2028 / U+2029 等)で偽の checkpoint
|
|
93
|
+
# 行を生成しないよう、分割前に除去する(stop.py と対称なガード / CR-NEW Medium)。
|
|
94
|
+
lines = _LINE_SEPARATOR_SANITIZE_RE.sub('', content).splitlines()
|
|
95
|
+
|
|
96
|
+
for line in reversed(lines):
|
|
97
|
+
# regex(`.* - (.+)`)の二重ワイルドカードによる多項式時間バックトラッキング
|
|
98
|
+
# (ReDoS)を根絶するため、手続き的パースに置き換える
|
|
99
|
+
# (fix-cycle-2, security-review-report-20260704-075817.md [SR-NEW])。
|
|
100
|
+
stripped = line.rstrip()
|
|
101
|
+
if len(stripped) > MAX_CHECKPOINT_LINE_LEN:
|
|
102
|
+
# 行長ガード。異常に長い行は正規の checkpoint ではあり得ないため
|
|
103
|
+
# 以降のパース処理(rfind 等)に進む前にスキップする。
|
|
104
|
+
continue
|
|
105
|
+
if not stripped.startswith(_PRECOMPACT_CHECKPOINT_PREFIX):
|
|
106
|
+
continue
|
|
107
|
+
if not stripped.endswith(']'):
|
|
108
|
+
continue
|
|
109
|
+
# プレフィックスと末尾 `]` を除いた内側の文字列から、最後の ` - ` を
|
|
110
|
+
# timestamp との区切りとみなす(旧 greedy regex `.* - (.+)]` の「最後の
|
|
111
|
+
# ` - ` フィールドを timestamp とする」挙動と等価。label 内の偶発的な
|
|
112
|
+
# ` - ` があっても安全側に倒れる)。
|
|
113
|
+
# 旧 regex は `(.+)` が 1 文字以上を要求するため、timestamp 欄が完全空文字の
|
|
114
|
+
# 行(例: `## [Checkpoint: PreCompact: manual - ]`)を非マッチとして扱い、
|
|
115
|
+
# より古い有効な checkpoint 行を探し続けていた。空 ts_text を continue で
|
|
116
|
+
# スキップすることで、この空文字境界でも旧 regex と等価に振る舞う。
|
|
117
|
+
inner = stripped[len(_PRECOMPACT_CHECKPOINT_PREFIX):-1]
|
|
118
|
+
sep_idx = inner.rfind(' - ')
|
|
119
|
+
if sep_idx == -1:
|
|
120
|
+
continue
|
|
121
|
+
ts_text = inner[sep_idx + len(' - '):].strip()
|
|
122
|
+
if not ts_text:
|
|
123
|
+
# 空文字 timestamp は旧 greedy regex `(.+)` の非マッチに相当する。
|
|
124
|
+
# 診断ログは出さず(破損ではなく単なる欠落のため)、より古い有効行の
|
|
125
|
+
# 探索を継続する。
|
|
126
|
+
continue
|
|
127
|
+
try:
|
|
128
|
+
dt = datetime.fromisoformat(ts_text)
|
|
129
|
+
except (ValueError, TypeError):
|
|
130
|
+
# 「timestamp が壊れている」のは本来発生しない想定外の破損ケース。
|
|
131
|
+
# ファイル無しの通常ケースと異なり、将来のトラブルシュートのため
|
|
132
|
+
# このケースに限り stderr へ診断ログを 1 行出す(CR-L-003・非対称仕様)。
|
|
133
|
+
# 捕捉テキストは固定長に切り詰めて出力する(SR-R-001)。
|
|
134
|
+
# 戻り値は引き続き None(fail-open で追記継続)。
|
|
135
|
+
print(
|
|
136
|
+
'[PreCompact] 診断: checkpoint 行の timestamp を parse できませんでした'
|
|
137
|
+
f'(fail-open で追記を継続します): {ts_text[:DIAGNOSTIC_MAX_LEN]!r}',
|
|
138
|
+
file=sys.stderr,
|
|
139
|
+
)
|
|
140
|
+
return None
|
|
141
|
+
if dt.tzinfo is None:
|
|
142
|
+
return None
|
|
143
|
+
if dt > now + timedelta(seconds=FUTURE_SKEW_TOLERANCE_SECONDS):
|
|
144
|
+
# 未来日時(許容スキュー超)は構造的異常値として棄却する(fail-open)。
|
|
145
|
+
# 悪意ある注入・データ破損いずれの場合も、デバウンスの恒久停止を
|
|
146
|
+
# 招かないよう None を返して追記を継続する
|
|
147
|
+
# (fix-cycle-2, security-review-report-20260704-075817.md [SR-V-001])。
|
|
148
|
+
return None
|
|
149
|
+
return dt
|
|
150
|
+
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def main():
|
|
155
|
+
try:
|
|
156
|
+
payload = json.loads(sys.stdin.read())
|
|
157
|
+
except (json.JSONDecodeError, ValueError):
|
|
158
|
+
payload = {}
|
|
159
|
+
|
|
160
|
+
cwd = os.getcwd()
|
|
161
|
+
if is_worktree(cwd):
|
|
162
|
+
sys.exit(0)
|
|
163
|
+
|
|
164
|
+
trigger = payload.get('trigger', 'unknown')
|
|
165
|
+
context_items_before = payload.get('context_items_before')
|
|
166
|
+
context_items_before_str = 'N/A' if context_items_before is None else str(context_items_before)
|
|
167
|
+
|
|
168
|
+
now = datetime.now(timezone.utc)
|
|
169
|
+
# 既知の構造的限界(CR-NEW Low・修正しない): session_file は now の UTC 日付から
|
|
170
|
+
# 算出する。PreCompact の連続起動(実測 約1.2 秒スパン)が UTC 00:00:00 ちょうどを
|
|
171
|
+
# またぐ極稀ケースでは、前後の呼び出しが別日の session_file を参照するため
|
|
172
|
+
# デバウンス判定が効かず checkpoint が二重追記され得る。発生確率は極小のため
|
|
173
|
+
# コードでは対処せず既知の限界として許容する。
|
|
174
|
+
date_str = now.strftime('%Y%m%d')
|
|
175
|
+
session_file = os.path.join(SESSIONS_DIR, f'{date_str}.tmp')
|
|
176
|
+
|
|
177
|
+
last = _last_precompact_checkpoint_dt(session_file, now=now)
|
|
178
|
+
if last is not None and (now - last) < timedelta(seconds=DEBOUNCE_WINDOW_SECONDS):
|
|
179
|
+
print('[PreCompact] debounce: 直近の checkpoint を検出したため追記をスキップしました', file=sys.stderr)
|
|
180
|
+
return
|
|
181
|
+
|
|
182
|
+
# セキュリティ非対称性の記録(fix-cycle-2, security-review-report-20260704-121824.md
|
|
183
|
+
# [SR-V-001]・情報提供・現状は対応不要): 以下の summary(body)は
|
|
184
|
+
# session_utils.append_checkpoint の設計上 sanitize_value() 非適用で書き込まれる
|
|
185
|
+
# (複数行 Markdown を保持するための承認済み設計判断・session_utils.py L147 参照)。
|
|
186
|
+
# そのため body に埋め込む trigger(L164)は未サニタイズのまま checkpoint に書かれる。
|
|
187
|
+
# 一方 label(下の f'PreCompact: {trigger}')は append_checkpoint 内で
|
|
188
|
+
# sanitize_value() を通るため、body と label で sanitize 適用範囲に非対称性がある。
|
|
189
|
+
# 現状 trigger は Claude Code ハーネスが設定する列挙的な値(manual/auto 等)で、
|
|
190
|
+
# 外部入力・LLM 自由記述からの直接汚染経路が無いため実害の確信度は低い。
|
|
191
|
+
# 【将来の拡張時の注意】trigger の由来をハーネス以外(プラグイン・カスタムフック
|
|
192
|
+
# 連携等)に拡張する場合は、偽 checkpoint 注入経路化を防ぐため body 側にも
|
|
193
|
+
# sanitize_value() 適用を検討すること。
|
|
194
|
+
summary = (
|
|
195
|
+
f"- trigger: {trigger}\n"
|
|
196
|
+
f"- context_items_before: {context_items_before_str}\n"
|
|
197
|
+
f"- このポイント以前の詳細な文脈は圧縮により失われます。"
|
|
198
|
+
)
|
|
199
|
+
append_checkpoint(session_file, f'PreCompact: {trigger}', summary)
|
|
200
|
+
|
|
201
|
+
print(f'[PreCompact] セッション状態を {session_file} に保存しました', file=sys.stderr)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == '__main__':
|
|
205
|
+
main()
|
|
@@ -29,6 +29,9 @@ PROMOTION_THRESHOLD = 0.8
|
|
|
29
29
|
COOLING_DAYS = 3
|
|
30
30
|
MAX_ID_LENGTH = 64
|
|
31
31
|
MAX_DESCRIPTION_LENGTH = 500
|
|
32
|
+
# 取り込みガード(500)とは別レイヤの既存エントリ肥大検知線。直接 Edit 経路で
|
|
33
|
+
# patterns.json に載った肥大 description を毎 Stop で警告するための閾値(strict greater)。
|
|
34
|
+
DESCRIPTION_WARN_LENGTH = 1000
|
|
32
35
|
MAX_LAST_MSG = 500
|
|
33
36
|
|
|
34
37
|
# 過去セッションファイルから引き継ぐ - [ ] 行のサニタイズ用パターン。
|
|
@@ -335,6 +338,33 @@ def save_patterns(data: dict) -> None:
|
|
|
335
338
|
os.unlink(tmp_path)
|
|
336
339
|
|
|
337
340
|
|
|
341
|
+
def _warn_oversized_descriptions(patterns: list) -> None:
|
|
342
|
+
"""既存エントリの description が DESCRIPTION_WARN_LENGTH を超えていたら stderr 警告する。
|
|
343
|
+
|
|
344
|
+
削除・切り詰めは行わない(警告のみ・read-only)。1 エントリ 1 行で出力する。
|
|
345
|
+
本関数は例外を出さない防御的実装だが、呼び出し側でも try/except で二重に保護する。
|
|
346
|
+
"""
|
|
347
|
+
for p in patterns:
|
|
348
|
+
if not isinstance(p, dict):
|
|
349
|
+
continue
|
|
350
|
+
desc = p.get('description', '')
|
|
351
|
+
if not isinstance(desc, str):
|
|
352
|
+
continue
|
|
353
|
+
if len(desc) > DESCRIPTION_WARN_LENGTH:
|
|
354
|
+
pid = p.get('id', '(unknown)')
|
|
355
|
+
# [SR-V-001] pid は直接 Edit 経路の値で未検証のため、出力前に
|
|
356
|
+
# 制御文字/ANSI 等を除去し MAX_ID_LENGTH で切り詰めて無害化する。
|
|
357
|
+
if not isinstance(pid, str):
|
|
358
|
+
pid = str(pid)
|
|
359
|
+
pid = _INHERIT_SANITIZE_RE.sub('', pid)[:MAX_ID_LENGTH]
|
|
360
|
+
print(
|
|
361
|
+
f'[Stop] patterns.json エントリの description が肥大しています '
|
|
362
|
+
f'(id={pid}, {len(desc)}字 > {DESCRIPTION_WARN_LENGTH}字)。'
|
|
363
|
+
f'内容を見直すか、日次評価が原因の場合は .dev/changelog-evals.md へ移してください',
|
|
364
|
+
file=sys.stderr,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
|
|
338
368
|
def update_patterns(date_str: str) -> None:
|
|
339
369
|
new_observations = extract_session_patterns(date_str)
|
|
340
370
|
data = load_patterns()
|
|
@@ -347,7 +377,9 @@ def update_patterns(date_str: str) -> None:
|
|
|
347
377
|
description = obs.get('description', '')
|
|
348
378
|
if len(description) > MAX_DESCRIPTION_LENGTH:
|
|
349
379
|
continue
|
|
350
|
-
|
|
380
|
+
# isinstance ガード: 非 dict エントリは検索対象外として無視し、
|
|
381
|
+
# 該当 id なし扱いで新規パターンとして追加される(削除はしない)[CR-E-001][SR-V-001]。
|
|
382
|
+
existing = next((p for p in data['patterns'] if isinstance(p, dict) and p.get('id') == pid), None)
|
|
351
383
|
if existing is None:
|
|
352
384
|
data['patterns'].append({
|
|
353
385
|
"id": pid,
|
|
@@ -368,6 +400,11 @@ def update_patterns(date_str: str) -> None:
|
|
|
368
400
|
|
|
369
401
|
active = []
|
|
370
402
|
for pattern in data['patterns']:
|
|
403
|
+
if not isinstance(pattern, dict):
|
|
404
|
+
# 型が dict でないエントリは trust/expiry 計算不能。
|
|
405
|
+
# registered_date parse 不能時と同様に保持して継続(クラッシュ回避)[SR-V-001]。
|
|
406
|
+
active.append(pattern)
|
|
407
|
+
continue
|
|
371
408
|
if pattern.get('promoted', False):
|
|
372
409
|
active.append(pattern)
|
|
373
410
|
continue
|
|
@@ -393,6 +430,12 @@ def update_patterns(date_str: str) -> None:
|
|
|
393
430
|
active.append(pattern)
|
|
394
431
|
|
|
395
432
|
data['patterns'] = active
|
|
433
|
+
# 肥大検知ガード(警告のみ・read-only)。誤動作しても Stop を止めないよう
|
|
434
|
+
# 防御的実装に加えて呼び出し側でも try/except で二重に握る(belt-and-suspenders)。
|
|
435
|
+
try:
|
|
436
|
+
_warn_oversized_descriptions(active)
|
|
437
|
+
except Exception:
|
|
438
|
+
pass
|
|
396
439
|
save_patterns(data)
|
|
397
440
|
|
|
398
441
|
print('[Stop] セッション終了処理が完了しました', file=sys.stderr)
|
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.45.0] - 2026-07-05
|
|
4
|
+
|
|
5
|
+
### 追加
|
|
6
|
+
|
|
7
|
+
- **stop.py に patterns.json 肥大検知ガードを新設**: `DESCRIPTION_WARN_LENGTH = 1000` を超える description を持つ既存エントリを、毎 Stop 時に stderr で警告する(削除・切り詰めはしない警告のみ・promoted 含む全件監査)。取り込みガード `MAX_DESCRIPTION_LENGTH=500` は session.tmp 経由の正規記録経路にしか効かず、LLM が patterns.json を直接 Edit する経路の肥大を検知できなかった穴を埋める別レイヤの機械防御(配布元で実際に 1 件 2,000〜3,000 字 × 19 件 = 98KB まで肥大し init-session の Read が上限超過した事故が契機)。fail-safe 二段(防御的実装 + 呼び出し側 try/except)で Stop 処理自体は止めない。
|
|
8
|
+
|
|
9
|
+
### 修正
|
|
10
|
+
|
|
11
|
+
- **patterns.json に非 dict エントリが混入すると毎 Stop で永続クラッシュする問題を修正**: `update_patterns` の expiry/trust ループと新規観測 ingestion マージループがともに dict 前提で、直接 Edit・マージ事故等で文字列や null が混入すると `AttributeError`/`TypeError` で異常終了していた。クラッシュ地点が `save_patterns` より前のため不正エントリは修復されず、**以降の全 Stop 実行が毎回失敗して学習記録(新規観測・trust 再計算)が更新されない自己修復不能な障害状態**になっていた。両ループに isinstance ガードを追加し、非 dict エントリは保持スキップ(`registered_date` parse 不能時の既存慣行と対称)で完走する。
|
|
12
|
+
- **肥大警告出力の id を無害化**: 直接 Edit 経路由来の id は長さ・型検証を通らないため、`_INHERIT_SANITIZE_RE`(制御文字・U+2028/U+2029 除去)+ `MAX_ID_LENGTH` 切り詰めを適用してから stderr へ出力する(ANSI エスケープ等によるターミナル出力改ざんの防止)。
|
|
13
|
+
|
|
14
|
+
### 後方互換
|
|
15
|
+
|
|
16
|
+
- `.claude/hooks/stop.py` とそのテストのみの変更で `src/c3/` に変更なし。**破壊的変更なし**。警告は stderr 1 行のみで patterns.json の内容・スキーマ・既存の expiry/trust/promotion_candidate 計算は不変。
|
|
17
|
+
|
|
18
|
+
## [2.44.0] - 2026-07-04
|
|
19
|
+
|
|
20
|
+
### 修正
|
|
21
|
+
|
|
22
|
+
- **PreCompact checkpoint の多重発火をデバウンスで冪等化**: Claude Code ハーネスは 1 回の `/compact` で PreCompact hook を複数回起動することがあり(実測 2〜4 回・payload 違い・公式 docs 非記載の undocumented 挙動)、セッションファイルに同一 checkpoint ブロックが堆積していた(実測で 1 日分ファイルの約 8 割がノイズ化し、翌セッション復元時の読み込みコンテキストを浪費)。`pre_compact.py` が追記前に当日セッションファイル末尾の PreCompact checkpoint timestamp を読み、10 秒以内の再起動なら追記をスキップする。timestamp 不在・parse 不能時は fail-open(従来どおり追記)。実装には ReDoS 対策(行長 512 上限・正規表現を使わない手続きパース)・未来日時 checkpoint の棄却(スキュー許容 60 秒・デバウンス恒久停止の防止)・行区切り文字サニタイズ・stderr 診断ログの切り詰め(64 字)を含む。実機 E2E で 1 compact = 1 ブロックを確認済み。
|
|
23
|
+
- **PreCompact hook の仕様外 additionalContext stdout 出力を撤去**: PreCompact は公式仕様上 `hookSpecificOutput.additionalContext` をサポートしない(サポートされるのは top-level `decision: "block"` + `reason` のみ)。C3 は従来この仕様外 JSON を stdout に出力しており、現行ハーネスの `/compact` で「PreCompact hook JSON output validation failed」警告を誘発していた(additionalContext の注入自体もモデルに届いていた保証がない)。`pre_compact.py` を checkpoint 追記専念に変更し、JSON stdout 出力と `SAVE_INSTRUCTION` 定数を削除。セッションファイルの更新は運用ルール(現在地の常時更新・タスク完了ごとの更新)で担保されているため機能上の後退はない。実機で警告の消滅を確認済み。
|
|
24
|
+
|
|
25
|
+
### 後方互換
|
|
26
|
+
|
|
27
|
+
- `.claude/hooks/pre_compact.py` とそのテストのみの変更で `src/c3/` に変更なし。**破壊的変更なし**。`session_utils.append_checkpoint()` の契約(parallel-agents の wave checkpoint と共用)・`settings.json` の hook 登録は不変。
|
|
28
|
+
|
|
3
29
|
## [2.43.0] - 2026-07-03
|
|
4
30
|
|
|
5
31
|
### 変更
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-code-conductor
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.45.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
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tests/_pre_compact_helpers.py
|
|
3
|
+
|
|
4
|
+
.claude/hooks/pre_compact.py 系テスト(tests/test_pre_compact.py /
|
|
5
|
+
tests/test_precompact_additional.py)で共通利用するヘルパー。
|
|
6
|
+
|
|
7
|
+
- WORKTREE_ROOT / PRE_COMPACT_PY: pre_compact.py の絶対パス
|
|
8
|
+
- _load_pre_compact_module(): pre_compact.py をモジュールとしてロードする(__main__ 実行なし)
|
|
9
|
+
- _run_main_in_process(): pre_compact.py の main() を in-process 実行し、実 sessions dir に
|
|
10
|
+
一切触れない(architecture-report-20260704-065052.md §4.2 / §8-1 案 A の in-process 方式)
|
|
11
|
+
|
|
12
|
+
意図: 旧来は tests/test_pre_compact.py と tests/test_precompact_additional.py の両方で
|
|
13
|
+
`_load_pre_compact_module()` / `_run_main_in_process()` をほぼ同一実装のまま重複定義していた
|
|
14
|
+
(CR-M-001)。前例 `tests/skills/_skill_helpers.py` に倣い、本モジュールへ一本化する。
|
|
15
|
+
|
|
16
|
+
`_run_main_in_process()` の戻り値は `(module, sessions_dir, fake_stdout)` の 3-tuple。
|
|
17
|
+
`test_pre_compact.py` は 3 値すべてを使用する。`test_precompact_additional.py` は
|
|
18
|
+
`fake_stdout` のみを使うため、呼び出し側で `_, _, fake_stdout = _run_main_in_process(...)`
|
|
19
|
+
のように必要な要素だけ取り出す(stdout の値・main() の実行内容は従来と不変更)。
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import importlib.util
|
|
25
|
+
import io
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
import types
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
import pytest
|
|
33
|
+
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
# Paths
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
WORKTREE_ROOT = Path(__file__).resolve().parent.parent
|
|
39
|
+
PRE_COMPACT_PY = WORKTREE_ROOT / ".claude" / "hooks" / "pre_compact.py"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
# Helpers
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _load_pre_compact_module() -> types.ModuleType:
|
|
48
|
+
"""pre_compact.py をモジュールとしてロードする(__main__ 実行なし)。
|
|
49
|
+
|
|
50
|
+
pre_compact.py はモジュールレベルで session_utils を import するため、
|
|
51
|
+
sys.path に hooks ディレクトリを追加してからロードする。
|
|
52
|
+
"""
|
|
53
|
+
hooks_dir = str(PRE_COMPACT_PY.parent)
|
|
54
|
+
if hooks_dir not in sys.path:
|
|
55
|
+
sys.path.insert(0, hooks_dir)
|
|
56
|
+
spec = importlib.util.spec_from_file_location("pre_compact", PRE_COMPACT_PY)
|
|
57
|
+
assert spec is not None and spec.loader is not None
|
|
58
|
+
module = importlib.util.module_from_spec(spec)
|
|
59
|
+
spec.loader.exec_module(module) # type: ignore[attr-defined]
|
|
60
|
+
return module
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _run_main_in_process(
|
|
64
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, payload: dict
|
|
65
|
+
) -> tuple[types.ModuleType, Path, io.StringIO]:
|
|
66
|
+
"""pre_compact.py の main() を in-process 実行し、実 sessions dir に一切触れない。
|
|
67
|
+
|
|
68
|
+
architecture-report-20260704-065052.md §4.2 の in-process 方式(T1 の
|
|
69
|
+
TestMainDebounce と同一パターン):
|
|
70
|
+
- `mod.SESSIONS_DIR` を tmp_path 配下に override
|
|
71
|
+
- `sys.stdin` を JSON payload の StringIO に差し替え
|
|
72
|
+
- `os.getcwd` を tmp_path に固定して非 worktree 経路を保証
|
|
73
|
+
- `sys.stdout` を明示的に StringIO に差し替えて捕捉する
|
|
74
|
+
(capsys は使わない。reconfigure 済み hook では capsys が効かない
|
|
75
|
+
場合があるため。tests/agent-memory の I-01 パターンに準拠)
|
|
76
|
+
"""
|
|
77
|
+
module = _load_pre_compact_module()
|
|
78
|
+
sessions_dir = tmp_path / "sessions"
|
|
79
|
+
sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
monkeypatch.setattr(module, "SESSIONS_DIR", str(sessions_dir))
|
|
81
|
+
monkeypatch.setattr(os, "getcwd", lambda: str(tmp_path))
|
|
82
|
+
monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps(payload)))
|
|
83
|
+
fake_stdout = io.StringIO()
|
|
84
|
+
monkeypatch.setattr(sys, "stdout", fake_stdout)
|
|
85
|
+
|
|
86
|
+
module.main()
|
|
87
|
+
|
|
88
|
+
return module, sessions_dir, fake_stdout
|