claude-code-conductor 2.44.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.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/stop.py +44 -1
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/CHANGELOG.md +15 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/PKG-INFO +1 -1
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/__init__.py +1 -1
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_stop_additional.py +510 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/CLAUDE.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/architect.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/code-reviewer.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/design-critic.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/developer.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/doc-writer.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/interviewer.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/planner.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/project-setup.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/security-reviewer.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/systematic-debugger.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/tester.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_developer.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_systematic-debugger.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_tester.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/breaking-changes.txt +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/deletions.txt +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/docs/config-policy.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/docs/parallel-agents-setup.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/docs/platform-adapters.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/docs/settings.json.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/_hook_utils.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/check_agent_invocation.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/consolidate_memory.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/permission_handler.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/permission_handler_toast.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/planner_check.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/post_tool.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/pre_compact.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/pre_tool.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/recall_autorebuild.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/recall_inject.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/restore_session.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/select_tier.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_start.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_stop.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/session_utils.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/statusline.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/hooks/worktree_guard.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/memory/.gitkeep +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/permission_rules.json +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/rules/promoted/index.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/settings.json +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/brainstorm/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/codex-review/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/code-review-checklist.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/design-critic-rubric.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/design-rubric.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/interview-rubric.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/plan-design-guidelines.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/references/security-review-checklist.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/record_agent_outcome.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/record_review_decision.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/dev-workflow/scripts/review_hint_inject.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/develop/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/doc/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/extract-lib/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/init-session/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/init-session/scripts/session_guard.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/mcp-config/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/parallel-agents/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/pattern-status/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/promote-pattern/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/recall/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/report-timestamp/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/report-timestamp/scripts/get_timestamp.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/review-phase/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/reference.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/templates/coding-standards-template.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/setup/templates/project-conventions-template.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/skills/start/SKILL.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/state/.gitkeep +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.gitignore +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/ARCHITECTURE.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/LICENSE +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/LICENSES/fastembed-LICENSE +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/LICENSES/fastembed-NOTICE +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/LICENSES/numpy-LICENSE +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/LICENSES/onnxruntime-LICENSE +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/LICENSES/paraphrase-multilingual-MiniLM-L12-v2-LICENSE +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/README.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/hatch_build.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/pyproject.toml +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/__main__.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/_db_params.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/_excludes.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/_terminal.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/adapters.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli_ask.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli_doctor.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli_init.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli_list.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli_plan.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli_recall.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli_tier.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/cli_update.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/db.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/embedding.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/gitutil.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/mcp_server.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/migrate.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/migrations/001_initial.sql +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/migrations/002_agent_cost_runs.sql +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/migrations/003_tier_cost.sql +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/migrations/004_agent_outcomes.sql +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/migrations/005_drop_agent_tier_bandit.sql +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/migrations/README.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/migrations/__init__.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/paths.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/plan_validator.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/platforms.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/pricing.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/question.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/recall_chunker.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/recall_index.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/src/c3/usage_ingester.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/__init__.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/_pre_compact_helpers.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/conftest.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/README.md +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/mainline.jsonl +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/subagents/agent-deadbeef.jsonl +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/fixtures/usage/subagents/agent-deadbeef.meta.json +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/__init__.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_check_agent_invocation.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_consolidate_memory.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_hook_utils.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_permission_handler.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_permission_handler_toast.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_pip_reinstall_reminder.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_planner_check.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_planner_check_dev.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_post_tool.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_pre_tool.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_recall_autorebuild.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_recall_inject.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_record_agent_outcome.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_record_review_decision.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_restore_session.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_review_hint_inject.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_select_tier.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_select_tier_escalation.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_start.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_stop.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_session_utils.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_settings_local_absolute_paths.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_similarity_boost.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_statusline.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_statusline_template_sync.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_sync_check.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/hooks/test_template_guard.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/__init__.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/_skill_helpers.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_dev_workflow_no_task_type.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_init_session_no_task_type.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_planner_lightweight.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_recall_skill.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_session_backlog_reconciliation.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_session_guard.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_setup_templates.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_bugfix_flow.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_new_flow.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/skills/test_start_skill_security_audit_phase.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_adapters.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_check_deletions.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_ask.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_doctor.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_entry.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_init.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_list.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_plan.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_recall.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_tier.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_update_breaking_changes.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_cli_update_deletions.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_db.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_docstring_consistency.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_embedding.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_excludes.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_extract_breaking_changes.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_gitutil.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_mcp_server_elicit.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_migrate.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_paths.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_plan_validator.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_pre_compact.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_precompact_additional.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_precompact_toctou_fixes.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_pricing.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_recall_chunker.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_recall_index.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_references_migration.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_session_utils_additional.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_skill_no_builtin_conflict.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_statusline.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_stop_hook.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_stop_precompact_fixes.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_sync_template_stop.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_template_pre_tool_hook.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_usage_ingester.py +0 -0
- {claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/tests/test_worktree_guard.py +0 -0
|
@@ -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,20 @@
|
|
|
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
|
+
|
|
3
18
|
## [2.44.0] - 2026-07-04
|
|
4
19
|
|
|
5
20
|
### 修正
|
|
@@ -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
|
|
@@ -1071,3 +1071,513 @@ class TestInheritBacklogNewPathOSErrorGuard:
|
|
|
1071
1071
|
"期待: 例外を伝播させず黙って return する。\n"
|
|
1072
1072
|
f"実際の例外: {exc}"
|
|
1073
1073
|
) from exc
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
# ---------------------------------------------------------------------------
|
|
1077
|
+
# [Regression guards (originally Red-phase)] TestDescriptionBloatWarning
|
|
1078
|
+
# (plan-report T1: test-stop-guard / T2: impl-stop-guard)
|
|
1079
|
+
# ---------------------------------------------------------------------------
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
class TestDescriptionBloatWarning:
|
|
1083
|
+
"""patterns.json description 肥大検知ガード(implemented; originally written
|
|
1084
|
+
Red-first)を固定する回帰テスト。
|
|
1085
|
+
|
|
1086
|
+
plan-report-20260705-153010.md §2 T1 / architecture-report-20260705-152313.md
|
|
1087
|
+
§4・§ADR-1〜4 に対応。対象は `_warn_oversized_descriptions` ヘルパーと
|
|
1088
|
+
`DESCRIPTION_WARN_LENGTH` 定数(値 1000・strict greater・description 長のみ)。
|
|
1089
|
+
|
|
1090
|
+
T2(impl-stop-guard)で実装済み。Red 定義時点では、本クラスの T1/T4/T5/T6/T7 は
|
|
1091
|
+
「未実装ゆえの失敗」(AssertionError: 期待する警告が出ない / AttributeError:
|
|
1092
|
+
_warn_oversized_descriptions が存在しない)で落ちる設計意図だった。この経緯は
|
|
1093
|
+
テスト設計の記録として残しているのみで、実装済みの現在は全テストが green で
|
|
1094
|
+
回帰防止として機能する。
|
|
1095
|
+
|
|
1096
|
+
T2/T3 は「警告が出ないこと」を検証する境界・回帰テストで、strict-greater
|
|
1097
|
+
境界(ちょうど1000字・実測最大706字)の誤警告ゼロを担保する。
|
|
1098
|
+
"""
|
|
1099
|
+
|
|
1100
|
+
def _setup(self, tmp_path: Path, tag: str) -> tuple:
|
|
1101
|
+
"""Create isolated temp dirs, seed a no-op session file, and return a
|
|
1102
|
+
fresh stop module with overridden paths.
|
|
1103
|
+
|
|
1104
|
+
セッション側に新規観測は無い(_write_session_no_json)。これにより
|
|
1105
|
+
update_patterns は patterns.json に既に載っている既存エントリのみを
|
|
1106
|
+
再評価する経路を通る。これは architecture-report §1 が言う「直接 Edit
|
|
1107
|
+
経路(MAX_DESCRIPTION_LENGTH=500 の取り込みガードが効かない穴)」を
|
|
1108
|
+
シミュレートするための意図的な設計。
|
|
1109
|
+
"""
|
|
1110
|
+
sessions_dir = tmp_path / "sessions"
|
|
1111
|
+
sessions_dir.mkdir()
|
|
1112
|
+
patterns_file = tmp_path / "patterns.json"
|
|
1113
|
+
mod = _load_stop_module(f"_stop_bloat_{tag}_{tmp_path.name}")
|
|
1114
|
+
mod.SESSIONS_DIR = str(sessions_dir)
|
|
1115
|
+
mod.PATTERNS_FILE = str(patterns_file)
|
|
1116
|
+
_write_session_no_json(sessions_dir, TODAY_STR)
|
|
1117
|
+
return mod, sessions_dir, patterns_file
|
|
1118
|
+
|
|
1119
|
+
@staticmethod
|
|
1120
|
+
def _entry(pid: str, description) -> dict:
|
|
1121
|
+
"""既存の expiry/trust ループ(registered_date/observations 必須)を安全に
|
|
1122
|
+
通過できる、有効な patterns.json エントリを 1 件組み立てる。
|
|
1123
|
+
description のみを可変にすることで、テスト対象を description 判定に絞る。
|
|
1124
|
+
"""
|
|
1125
|
+
return {
|
|
1126
|
+
"id": pid,
|
|
1127
|
+
"description": description,
|
|
1128
|
+
"registered_date": TODAY_STR,
|
|
1129
|
+
"trust_score": 0.5,
|
|
1130
|
+
"promotion_candidate": False,
|
|
1131
|
+
"observations": [{"date": TODAY_STR}],
|
|
1132
|
+
"last_updated": TODAY_STR,
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
# -- T1 -----------------------------------------------------------------
|
|
1136
|
+
|
|
1137
|
+
def test_t1_warns_when_description_exceeds_threshold(self, tmp_path, capsys):
|
|
1138
|
+
"""T1: description 1500字のエントリ -> stderr に [Stop] 接頭辞 + 該当 id +
|
|
1139
|
+
字数を含む警告が1行出る。"""
|
|
1140
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t1")
|
|
1141
|
+
patterns_data = {"patterns": [self._entry("bloat-pat-1", "A" * 1500)]}
|
|
1142
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1143
|
+
|
|
1144
|
+
mod.update_patterns(TODAY_STR)
|
|
1145
|
+
|
|
1146
|
+
err = capsys.readouterr().err
|
|
1147
|
+
warning_lines = [
|
|
1148
|
+
ln for ln in err.splitlines() if "[Stop]" in ln and "bloat-pat-1" in ln
|
|
1149
|
+
]
|
|
1150
|
+
assert len(warning_lines) == 1, (
|
|
1151
|
+
f"[T1] description 1500字のエントリに対する [Stop] 警告が1行出るべき。"
|
|
1152
|
+
f"Got stderr: {err!r}"
|
|
1153
|
+
)
|
|
1154
|
+
assert "1500" in warning_lines[0], (
|
|
1155
|
+
f"[T1] 警告メッセージに字数(1500)が含まれるべき。Got: {warning_lines[0]!r}"
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1158
|
+
# -- T2 -----------------------------------------------------------------
|
|
1159
|
+
|
|
1160
|
+
def test_t2_no_warning_at_exact_threshold_boundary(self, tmp_path, capsys):
|
|
1161
|
+
"""T2: description ちょうど1000字 -> 警告なし(strict greater の境界固定)。"""
|
|
1162
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t2")
|
|
1163
|
+
patterns_data = {"patterns": [self._entry("boundary-pat", "B" * 1000)]}
|
|
1164
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1165
|
+
|
|
1166
|
+
mod.update_patterns(TODAY_STR)
|
|
1167
|
+
|
|
1168
|
+
err = capsys.readouterr().err
|
|
1169
|
+
assert "boundary-pat" not in err, (
|
|
1170
|
+
f"[T2] ちょうど1000字(strict greater 境界)では警告が出てはいけない。"
|
|
1171
|
+
f"Got stderr: {err!r}"
|
|
1172
|
+
)
|
|
1173
|
+
|
|
1174
|
+
# -- T3 -----------------------------------------------------------------
|
|
1175
|
+
|
|
1176
|
+
def test_t3_no_warning_for_regular_pattern_length(self, tmp_path, capsys):
|
|
1177
|
+
"""T3: 正規パターン実測最大長706字 -> 警告なし(誤警告ゼロの回帰防止)。"""
|
|
1178
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t3")
|
|
1179
|
+
patterns_data = {"patterns": [self._entry("regular-pat-706", "C" * 706)]}
|
|
1180
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1181
|
+
|
|
1182
|
+
mod.update_patterns(TODAY_STR)
|
|
1183
|
+
|
|
1184
|
+
err = capsys.readouterr().err
|
|
1185
|
+
assert "regular-pat-706" not in err, (
|
|
1186
|
+
"[T3] 実測最大長706字(learning_signal_attribution_verification 相当)"
|
|
1187
|
+
f"では警告が出てはいけない。Got stderr: {err!r}"
|
|
1188
|
+
)
|
|
1189
|
+
|
|
1190
|
+
# -- T4 -----------------------------------------------------------------
|
|
1191
|
+
|
|
1192
|
+
def test_t4_multiple_bloated_entries_each_get_own_warning_line(
|
|
1193
|
+
self, tmp_path, capsys
|
|
1194
|
+
):
|
|
1195
|
+
"""T4: 1500字 x 2件 -> 警告2行(id ごとに個別出力)。"""
|
|
1196
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t4")
|
|
1197
|
+
patterns_data = {
|
|
1198
|
+
"patterns": [
|
|
1199
|
+
self._entry("bloat-pat-a", "D" * 1500),
|
|
1200
|
+
self._entry("bloat-pat-b", "E" * 1500),
|
|
1201
|
+
]
|
|
1202
|
+
}
|
|
1203
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1204
|
+
|
|
1205
|
+
mod.update_patterns(TODAY_STR)
|
|
1206
|
+
|
|
1207
|
+
err = capsys.readouterr().err
|
|
1208
|
+
lines_a = [ln for ln in err.splitlines() if "[Stop]" in ln and "bloat-pat-a" in ln]
|
|
1209
|
+
lines_b = [ln for ln in err.splitlines() if "[Stop]" in ln and "bloat-pat-b" in ln]
|
|
1210
|
+
assert len(lines_a) == 1, (
|
|
1211
|
+
f"[T4] bloat-pat-a 用の警告が1行出るべき。Got stderr: {err!r}"
|
|
1212
|
+
)
|
|
1213
|
+
assert len(lines_b) == 1, (
|
|
1214
|
+
f"[T4] bloat-pat-b 用の警告が1行出るべき。Got stderr: {err!r}"
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
# -- T5 -----------------------------------------------------------------
|
|
1218
|
+
|
|
1219
|
+
def test_t5_warning_only_does_not_mutate_description(self, tmp_path, capsys):
|
|
1220
|
+
"""T5: 警告のみ・非破壊 -> update_patterns 後も対象 description が不変で残る。
|
|
1221
|
+
|
|
1222
|
+
警告そのものが出ることも同時に assert する。さもないと『何もしない』実装でも
|
|
1223
|
+
非破壊性アサーションだけは現状既に通ってしまい、Red として機能しない。
|
|
1224
|
+
"""
|
|
1225
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t5")
|
|
1226
|
+
original_desc = "F" * 1500
|
|
1227
|
+
patterns_data = {"patterns": [self._entry("nondestructive-pat", original_desc)]}
|
|
1228
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1229
|
+
|
|
1230
|
+
mod.update_patterns(TODAY_STR)
|
|
1231
|
+
|
|
1232
|
+
err = capsys.readouterr().err
|
|
1233
|
+
assert "nondestructive-pat" in err, (
|
|
1234
|
+
"[T5] 肥大エントリに警告が出るべき(警告そのものが出ないと非破壊性の"
|
|
1235
|
+
f"検証にならない)。Got stderr: {err!r}"
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
data = json.loads(patterns_file.read_text(encoding="utf-8"))
|
|
1239
|
+
stored = next(
|
|
1240
|
+
(p for p in data["patterns"] if p["id"] == "nondestructive-pat"), None
|
|
1241
|
+
)
|
|
1242
|
+
assert stored is not None, "[T5] 肥大エントリは削除されず patterns.json に残るべき"
|
|
1243
|
+
assert stored["description"] == original_desc, (
|
|
1244
|
+
"[T5] 警告は description を変更・切り詰めしてはいけない(非破壊)。"
|
|
1245
|
+
f"Got len={len(stored['description'])}, expected len={len(original_desc)}"
|
|
1246
|
+
)
|
|
1247
|
+
|
|
1248
|
+
# -- T6 -----------------------------------------------------------------
|
|
1249
|
+
|
|
1250
|
+
def test_t6_fail_safe_survives_malformed_description_fields(
|
|
1251
|
+
self, tmp_path, capsys
|
|
1252
|
+
):
|
|
1253
|
+
"""T6: description キー欠損 / 非str(None・数値) が混在しても例外を伝播させず
|
|
1254
|
+
update_patterns が正常完了する。
|
|
1255
|
+
|
|
1256
|
+
併せて、同時に混在させた正規の肥大エントリへの警告も出ることを assert する。
|
|
1257
|
+
さもないと『何もしない』実装でも fail-safe アサーションだけは現状既に通って
|
|
1258
|
+
しまい、Red として機能しない。
|
|
1259
|
+
|
|
1260
|
+
非 dict エントリ(例: 文字列そのもの)は、fix-cycle-3 で ingestion マージ
|
|
1261
|
+
ループ(stop.py L380 付近)と expiry/trust ループ(同 L400 以降)の両経路に
|
|
1262
|
+
非 dict ガードが入ったことで、新規観測の有無に関わらず update_patterns
|
|
1263
|
+
経由でも crash しなくなった(TestDescriptionBloatWarning の T9 / T11 回帰
|
|
1264
|
+
テストでこの挙動を固定)。本テスト(T6)は対象を description 判定に絞る
|
|
1265
|
+
ための意図的な選定として、引き続き description キー欠損・非 str の
|
|
1266
|
+
2 パターンのみを扱う(非 dict エントリの網羅は T9 / T11 / T7 側の責務)。
|
|
1267
|
+
"""
|
|
1268
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t6")
|
|
1269
|
+
entry_missing_desc = {
|
|
1270
|
+
"id": "nodesc-pat",
|
|
1271
|
+
# description キーそのものが無い
|
|
1272
|
+
"registered_date": TODAY_STR,
|
|
1273
|
+
"trust_score": 0.5,
|
|
1274
|
+
"promotion_candidate": False,
|
|
1275
|
+
"observations": [{"date": TODAY_STR}],
|
|
1276
|
+
"last_updated": TODAY_STR,
|
|
1277
|
+
}
|
|
1278
|
+
patterns_data = {
|
|
1279
|
+
"patterns": [
|
|
1280
|
+
entry_missing_desc,
|
|
1281
|
+
self._entry("none-desc-pat", None),
|
|
1282
|
+
self._entry("num-desc-pat", 12345),
|
|
1283
|
+
self._entry("valid-bloat-pat", "G" * 1500),
|
|
1284
|
+
]
|
|
1285
|
+
}
|
|
1286
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1287
|
+
|
|
1288
|
+
# 例外が伝播しないこと自体が fail-safe の核心的な検証
|
|
1289
|
+
mod.update_patterns(TODAY_STR)
|
|
1290
|
+
|
|
1291
|
+
err = capsys.readouterr().err
|
|
1292
|
+
assert "valid-bloat-pat" in err, (
|
|
1293
|
+
"[T6] 混在する不正エントリに関わらず、正規の肥大エントリには警告が出る"
|
|
1294
|
+
f"べき。Got stderr: {err!r}"
|
|
1295
|
+
)
|
|
1296
|
+
|
|
1297
|
+
data = json.loads(patterns_file.read_text(encoding="utf-8"))
|
|
1298
|
+
ids = {p["id"] for p in data["patterns"]}
|
|
1299
|
+
assert {
|
|
1300
|
+
"nodesc-pat", "none-desc-pat", "num-desc-pat", "valid-bloat-pat"
|
|
1301
|
+
} <= ids, (
|
|
1302
|
+
"[T6] 不正な description を持つエントリも削除されず patterns.json に"
|
|
1303
|
+
f"残るべき(fail-safe は警告をスキップするだけで削除はしない)。"
|
|
1304
|
+
f"Got ids: {ids!r}"
|
|
1305
|
+
)
|
|
1306
|
+
|
|
1307
|
+
# -- T7 -----------------------------------------------------------------
|
|
1308
|
+
|
|
1309
|
+
def test_t7_helper_unit_threshold_and_stderr(self, tmp_path, capsys):
|
|
1310
|
+
"""T7(任意): _warn_oversized_descriptions に list を直接渡し、閾値判定と
|
|
1311
|
+
stderr 出力をヘルパー単体で検証する。非 dict エントリの fail-safe も
|
|
1312
|
+
ここで検証する(T6 のコメント参照)。
|
|
1313
|
+
"""
|
|
1314
|
+
mod, _sessions_dir, _patterns_file = self._setup(tmp_path, "t7")
|
|
1315
|
+
|
|
1316
|
+
patterns = [
|
|
1317
|
+
{"id": "helper-bloat", "description": "H" * 1500},
|
|
1318
|
+
{"id": "helper-ok", "description": "I" * 706},
|
|
1319
|
+
"not a dict",
|
|
1320
|
+
{"id": "helper-nodesc"},
|
|
1321
|
+
{"id": "helper-none-desc", "description": None},
|
|
1322
|
+
]
|
|
1323
|
+
|
|
1324
|
+
# 例外を出さないこと自体が fail-safe の検証(非 dict エントリを含む)
|
|
1325
|
+
mod._warn_oversized_descriptions(patterns)
|
|
1326
|
+
|
|
1327
|
+
err = capsys.readouterr().err
|
|
1328
|
+
assert "helper-bloat" in err and "[Stop]" in err, (
|
|
1329
|
+
f"[T7] 閾値超エントリには [Stop] 警告が出るべき。Got stderr: {err!r}"
|
|
1330
|
+
)
|
|
1331
|
+
assert "helper-ok" not in err, (
|
|
1332
|
+
f"[T7] 706字の正規エントリには警告が出てはいけない。Got stderr: {err!r}"
|
|
1333
|
+
)
|
|
1334
|
+
|
|
1335
|
+
# -- T8 -----------------------------------------------------------------
|
|
1336
|
+
|
|
1337
|
+
def test_t8_promoted_entry_still_warns(self, tmp_path, capsys):
|
|
1338
|
+
"""T8: promoted: true のエントリでも description 肥大なら警告される
|
|
1339
|
+
(architecture-report §ADR-3: `_warn_oversized_descriptions` は promoted を
|
|
1340
|
+
含む active 全件を一律監査し、promoted か否かで除外しないという設計判断の
|
|
1341
|
+
回帰固定)。promoted エントリが expiry/trust 判定をスキップして無条件で
|
|
1342
|
+
active に残る経路(stop.py L396-398)も本テストで自然に検証される。
|
|
1343
|
+
"""
|
|
1344
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t8")
|
|
1345
|
+
entry = self._entry("promoted-bloat-pat", "J" * 1500)
|
|
1346
|
+
entry["promoted"] = True
|
|
1347
|
+
patterns_data = {"patterns": [entry]}
|
|
1348
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1349
|
+
|
|
1350
|
+
mod.update_patterns(TODAY_STR)
|
|
1351
|
+
|
|
1352
|
+
err = capsys.readouterr().err
|
|
1353
|
+
warning_lines = [
|
|
1354
|
+
ln for ln in err.splitlines()
|
|
1355
|
+
if "[Stop]" in ln and "promoted-bloat-pat" in ln
|
|
1356
|
+
]
|
|
1357
|
+
assert len(warning_lines) == 1, (
|
|
1358
|
+
"[T8] promoted:true でも description 肥大なら [Stop] 警告が1行出るべき"
|
|
1359
|
+
f"(promoted 除外禁止の設計固定)。Got stderr: {err!r}"
|
|
1360
|
+
)
|
|
1361
|
+
assert "1500" in warning_lines[0], (
|
|
1362
|
+
f"[T8] 警告メッセージに字数(1500)が含まれるべき。Got: {warning_lines[0]!r}"
|
|
1363
|
+
)
|
|
1364
|
+
|
|
1365
|
+
data = json.loads(patterns_file.read_text(encoding="utf-8"))
|
|
1366
|
+
stored = next(
|
|
1367
|
+
(p for p in data["patterns"] if p["id"] == "promoted-bloat-pat"), None
|
|
1368
|
+
)
|
|
1369
|
+
assert stored is not None, (
|
|
1370
|
+
"[T8] promoted エントリは active に残るべき(expiry/trust 判定スキップ経路)"
|
|
1371
|
+
)
|
|
1372
|
+
assert stored.get("promoted") is True, (
|
|
1373
|
+
"[T8] promoted フラグ自体は変更されず維持されるべき"
|
|
1374
|
+
)
|
|
1375
|
+
|
|
1376
|
+
# -- T9 (fix-cycle-2 / FB1, originally Red-a) ---------------------------
|
|
1377
|
+
|
|
1378
|
+
def test_t9_non_dict_pattern_entries_do_not_crash_and_are_retained(
|
|
1379
|
+
self, tmp_path
|
|
1380
|
+
):
|
|
1381
|
+
"""T9 [SR-V-001 Medium / originally Red-a]: data['patterns'] 直下に非 dict エントリ
|
|
1382
|
+
(文字列・None)が混在していても update_patterns が例外を伝播させず完走し、
|
|
1383
|
+
正規 dict エントリの処理・save が行われることを固定する。
|
|
1384
|
+
|
|
1385
|
+
plan-report-20260705-162421.md の決定(非 dict エントリの扱い = 保持)に
|
|
1386
|
+
従い、非 dict エントリも save 後の patterns.json に残っていることを assert
|
|
1387
|
+
する(stop.py L400-404 の registered_date parse 不能時の「保持して継続」
|
|
1388
|
+
慣行と対称)。
|
|
1389
|
+
|
|
1390
|
+
fix-cycle-2 適用前の実装では expiry/trust ループ先頭の
|
|
1391
|
+
`pattern.get('promoted', False)` が非 dict エントリで
|
|
1392
|
+
AttributeError(例: 'str' object has no attribute 'get')を送出し、
|
|
1393
|
+
save に到達せず update_patterns 自体が異常終了するため、originally
|
|
1394
|
+
Red-first で赤だった。
|
|
1395
|
+
"""
|
|
1396
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t9")
|
|
1397
|
+
valid_entry = self._entry("valid-pat-t9", "normal description")
|
|
1398
|
+
patterns_data = {"patterns": ["not a dict", None, valid_entry]}
|
|
1399
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1400
|
+
|
|
1401
|
+
# 例外が伝播しないこと自体が Red-a の核心的な検証
|
|
1402
|
+
# (修正前の実装は 'not a dict'.get(...) の AttributeError で crash していた)。
|
|
1403
|
+
mod.update_patterns(TODAY_STR)
|
|
1404
|
+
|
|
1405
|
+
data = json.loads(patterns_file.read_text(encoding="utf-8"))
|
|
1406
|
+
stored_patterns = data["patterns"]
|
|
1407
|
+
|
|
1408
|
+
valid_stored = next(
|
|
1409
|
+
(
|
|
1410
|
+
p for p in stored_patterns
|
|
1411
|
+
if isinstance(p, dict) and p.get("id") == "valid-pat-t9"
|
|
1412
|
+
),
|
|
1413
|
+
None,
|
|
1414
|
+
)
|
|
1415
|
+
assert valid_stored is not None, (
|
|
1416
|
+
"[T9] 正規 dict エントリは save 後の patterns.json に残るべき"
|
|
1417
|
+
f"(save 到達確認)。Got stored_patterns: {stored_patterns!r}"
|
|
1418
|
+
)
|
|
1419
|
+
|
|
1420
|
+
assert "not a dict" in stored_patterns, (
|
|
1421
|
+
"[T9] 非 dict エントリ(文字列)は削除されず保持されるべき"
|
|
1422
|
+
"(planner 決定: registered_date parse 不能時の保持慣行と対称)。"
|
|
1423
|
+
f"Got stored_patterns: {stored_patterns!r}"
|
|
1424
|
+
)
|
|
1425
|
+
assert None in stored_patterns, (
|
|
1426
|
+
"[T9] 非 dict エントリ(None)は削除されず保持されるべき。"
|
|
1427
|
+
f"Got stored_patterns: {stored_patterns!r}"
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
# -- T10 (fix-cycle-2 / FB1, originally Red-b) ---------------------------
|
|
1431
|
+
|
|
1432
|
+
def test_t10_pid_control_chars_and_overlong_id_are_sanitized_in_stderr(
|
|
1433
|
+
self, tmp_path, capsys
|
|
1434
|
+
):
|
|
1435
|
+
"""T10 [SR-V-001 Low / originally Red-b]: 肥大警告 (`_warn_oversized_descriptions`) が
|
|
1436
|
+
出力する pid(id フィールド)に ANSI エスケープ・NUL・CR(制御文字)を含み
|
|
1437
|
+
MAX_ID_LENGTH(64) を超える長さの場合でも、stderr 出力ではこれらが
|
|
1438
|
+
`_INHERIT_SANITIZE_RE` 相当でサニタイズされ、MAX_ID_LENGTH 以内に
|
|
1439
|
+
切り詰められていることを固定する。
|
|
1440
|
+
|
|
1441
|
+
制御文字の代表として CR (\\r, 0x0D) を用いる。LF (\\n, 0x0A) は
|
|
1442
|
+
`_INHERIT_SANITIZE_RE`(FB2 で再利用が指示されている既存正規表現。
|
|
1443
|
+
stop.py L41-43)の除去対象文字クラス(\\x00-\\x08, \\x0b-\\x1f, ...)の
|
|
1444
|
+
範囲外であり、過去セッションの複数行バックログ引き継ぎでは意図的に
|
|
1445
|
+
タブ・通常スペースと同様に温存される設計になっている。この既存正規表現を
|
|
1446
|
+
そのまま再利用する前提(plan-report 明記)では LF は除去対象外のため、
|
|
1447
|
+
LF を含めると FB2 の想定実装後も解消されない「偽の Red」になってしまう。
|
|
1448
|
+
よってここでは確実に除去される CR を制御文字/改行系の代表として使う。
|
|
1449
|
+
|
|
1450
|
+
fix-cycle-2 適用前の実装は pid をサニタイズせずそのまま f-string に
|
|
1451
|
+
埋め込んで出力していたため、originally Red-first で赤だった。
|
|
1452
|
+
"""
|
|
1453
|
+
mod, _sessions_dir, patterns_file = self._setup(tmp_path, "t10")
|
|
1454
|
+
ansi_escape = "\x1b[31m"
|
|
1455
|
+
null_char = "\x00"
|
|
1456
|
+
cr_char = "\r"
|
|
1457
|
+
long_tail = "Q" * 100 # サニタイズ後も MAX_ID_LENGTH(64) 超の長さを保証する
|
|
1458
|
+
dirty_id = f"{ansi_escape}BADPID{null_char}{cr_char}{long_tail}"
|
|
1459
|
+
|
|
1460
|
+
entry = self._entry(dirty_id, "L" * 1500)
|
|
1461
|
+
entry["promoted"] = True # expiry/trust 判定をスキップし確実に active へ入れる
|
|
1462
|
+
patterns_data = {"patterns": [entry]}
|
|
1463
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1464
|
+
|
|
1465
|
+
mod.update_patterns(TODAY_STR)
|
|
1466
|
+
|
|
1467
|
+
err = capsys.readouterr().err
|
|
1468
|
+
|
|
1469
|
+
# 肥大検知自体は動く([Stop] 警告 + 字数が出る)
|
|
1470
|
+
assert "[Stop]" in err and "1500" in err, (
|
|
1471
|
+
f"[T10] 肥大警告自体は出力されるべき。Got stderr: {err!r}"
|
|
1472
|
+
)
|
|
1473
|
+
|
|
1474
|
+
# 生の ANSI エスケープ・NUL・CR が stderr に含まれない
|
|
1475
|
+
assert "\x1b" not in err, (
|
|
1476
|
+
"[T10] ANSI エスケープ (\\x1b) がサニタイズされず stderr に出力されている。"
|
|
1477
|
+
f"Got stderr: {err!r}"
|
|
1478
|
+
)
|
|
1479
|
+
assert "\x00" not in err, (
|
|
1480
|
+
"[T10] NUL 文字 (\\x00) がサニタイズされず stderr に出力されている。"
|
|
1481
|
+
f"Got stderr: {err!r}"
|
|
1482
|
+
)
|
|
1483
|
+
assert "\r" not in err, (
|
|
1484
|
+
"[T10] CR (\\r) がサニタイズされず stderr に出力されている。"
|
|
1485
|
+
f"Got stderr: {err!r}"
|
|
1486
|
+
)
|
|
1487
|
+
|
|
1488
|
+
# id 部分が _INHERIT_SANITIZE_RE 相当でサニタイズ済みかつ
|
|
1489
|
+
# MAX_ID_LENGTH(64) 以内に切り詰められている
|
|
1490
|
+
expected_sanitized_id = mod._INHERIT_SANITIZE_RE.sub("", dirty_id)[
|
|
1491
|
+
: mod.MAX_ID_LENGTH
|
|
1492
|
+
]
|
|
1493
|
+
assert expected_sanitized_id in err, (
|
|
1494
|
+
f"[T10] サニタイズ済み id ({expected_sanitized_id!r}) が stderr に"
|
|
1495
|
+
f"含まれるべき。Got stderr: {err!r}"
|
|
1496
|
+
)
|
|
1497
|
+
# 未サニタイズの生 pid がそのまま出力されていないこと(真の Red 確認)
|
|
1498
|
+
assert dirty_id not in err, (
|
|
1499
|
+
"[T10] 生の pid がサニタイズされず stderr にそのまま出力されている。"
|
|
1500
|
+
f"Got stderr: {err!r}"
|
|
1501
|
+
)
|
|
1502
|
+
|
|
1503
|
+
# -- T11 (fix-cycle-3 / FC1) ----------------------------------------------
|
|
1504
|
+
|
|
1505
|
+
def test_t11_new_observations_with_non_dict_patterns_do_not_crash(
|
|
1506
|
+
self, tmp_path
|
|
1507
|
+
):
|
|
1508
|
+
"""T11 [SR-V-001 / CR-E-001 Medium]: 新規観測ありセッション(new_observations
|
|
1509
|
+
が非空)+ patterns.json に非 dict エントリ混在という条件でも update_patterns
|
|
1510
|
+
が例外を伝播せず完走し、正規 dict エントリ・非 dict エントリ・新規観測の
|
|
1511
|
+
いずれも save 後に残ることを固定する。
|
|
1512
|
+
|
|
1513
|
+
T9 は `_write_session_no_json`(new_observations=[])を使うため、ingestion
|
|
1514
|
+
マージループ(stop.py L380 `existing = next((p for p in data['patterns']
|
|
1515
|
+
if p['id'] == pid), None)`)自体が実行されず、この経路の残存クラッシュを
|
|
1516
|
+
検出できない(security-review-report-20260705-164112.md [SR-V-001] /
|
|
1517
|
+
code-review-report-20260705-164113.md [CR-E-001] で指摘された回帰テストの
|
|
1518
|
+
盲点)。本テストは `_write_session_with_patterns` で新規観測を 1 件以上
|
|
1519
|
+
与えることで ingestion マージループを実際に通過させ、この盲点を埋める。
|
|
1520
|
+
|
|
1521
|
+
fix-cycle-3 の本 Red 追加時点(stop.py 未修正)では、L380 の
|
|
1522
|
+
`p['id']` が非 dict エントリ(文字列・None)への添字アクセスとなるため
|
|
1523
|
+
`TypeError: string indices must be integers, not 'str'` を送出し、
|
|
1524
|
+
update_patterns 自体が異常終了していた。よって本テストは、FC2 で
|
|
1525
|
+
isinstance ガードが入るまで赤だった。
|
|
1526
|
+
"""
|
|
1527
|
+
mod, sessions_dir, patterns_file = self._setup(tmp_path, "t11")
|
|
1528
|
+
valid_entry = self._entry("valid-pat-t11", "normal description")
|
|
1529
|
+
patterns_data = {"patterns": ["not a dict", None, valid_entry]}
|
|
1530
|
+
patterns_file.write_text(json.dumps(patterns_data), encoding="utf-8")
|
|
1531
|
+
|
|
1532
|
+
# _setup は _write_session_no_json(new_observations=[])でセッション
|
|
1533
|
+
# ファイルを作るため、同じ日付のファイルを新規観測ありで上書きし、
|
|
1534
|
+
# ingestion マージループ(L373 の for obs in new_observations:)を
|
|
1535
|
+
# 実際に通過させる。
|
|
1536
|
+
_write_session_with_patterns(
|
|
1537
|
+
sessions_dir, TODAY_STR, [
|
|
1538
|
+
{"id": "new-obs-pat-t11", "description": "newly observed pattern"}
|
|
1539
|
+
]
|
|
1540
|
+
)
|
|
1541
|
+
|
|
1542
|
+
# 例外が伝播しないこと自体が本テストの核心的な検証
|
|
1543
|
+
# (修正前の実装は ingestion マージループの p['id'] で TypeError を
|
|
1544
|
+
# 送出していた)。
|
|
1545
|
+
mod.update_patterns(TODAY_STR)
|
|
1546
|
+
|
|
1547
|
+
data = json.loads(patterns_file.read_text(encoding="utf-8"))
|
|
1548
|
+
stored_patterns = data["patterns"]
|
|
1549
|
+
|
|
1550
|
+
valid_stored = next(
|
|
1551
|
+
(
|
|
1552
|
+
p for p in stored_patterns
|
|
1553
|
+
if isinstance(p, dict) and p.get("id") == "valid-pat-t11"
|
|
1554
|
+
),
|
|
1555
|
+
None,
|
|
1556
|
+
)
|
|
1557
|
+
assert valid_stored is not None, (
|
|
1558
|
+
"[T11] 正規 dict エントリは save 後の patterns.json に残るべき"
|
|
1559
|
+
f"(save 到達確認)。Got stored_patterns: {stored_patterns!r}"
|
|
1560
|
+
)
|
|
1561
|
+
|
|
1562
|
+
new_obs_stored = next(
|
|
1563
|
+
(
|
|
1564
|
+
p for p in stored_patterns
|
|
1565
|
+
if isinstance(p, dict) and p.get("id") == "new-obs-pat-t11"
|
|
1566
|
+
),
|
|
1567
|
+
None,
|
|
1568
|
+
)
|
|
1569
|
+
assert new_obs_stored is not None, (
|
|
1570
|
+
"[T11] 新規観測は ingestion マージループを通過し、save 後の "
|
|
1571
|
+
"patterns.json に新規エントリとして残るべき。"
|
|
1572
|
+
f"Got stored_patterns: {stored_patterns!r}"
|
|
1573
|
+
)
|
|
1574
|
+
|
|
1575
|
+
assert "not a dict" in stored_patterns, (
|
|
1576
|
+
"[T11] 非 dict エントリ(文字列)は削除されず保持されるべき"
|
|
1577
|
+
"(T9 と同じ保持方針)。"
|
|
1578
|
+
f"Got stored_patterns: {stored_patterns!r}"
|
|
1579
|
+
)
|
|
1580
|
+
assert None in stored_patterns, (
|
|
1581
|
+
"[T11] 非 dict エントリ(None)は削除されず保持されるべき。"
|
|
1582
|
+
f"Got stored_patterns: {stored_patterns!r}"
|
|
1583
|
+
)
|
|
File without changes
|
|
File without changes
|
{claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/code-reviewer.md
RENAMED
|
File without changes
|
{claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/design-critic.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/project-setup.md
RENAMED
|
File without changes
|
{claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/security-reviewer.md
RENAMED
|
File without changes
|
{claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/systematic-debugger.md
RENAMED
|
File without changes
|
|
File without changes
|
{claude_code_conductor-2.44.0 → claude_code_conductor-2.45.0}/.claude/agents/wt_developer.md
RENAMED
|
File without changes
|