项目文件夹

文件
kazuki 5463aff45a feat(confidence): implement skill checks in TS + enrich Python, add CLI/install tests
Complete the confidence-check skill's TypeScript implementation (4 of its 5
checks were placeholder stubs that only read context flags) and enrich the
Python ConfidenceChecker with real tech-stack detection, architecture
anti-pattern warnings, and multi-language duplicate search.

Logic is ported from the kazukinakai fork. The dead airis-agent import layer
from the fork is intentionally dropped: the checks work with zero optional
dependencies, and each check still honors its explicit *_complete / *_verified
context-flag override for testing and pre-checked scenarios.

- confidence.ts: implement noDuplicates / architectureCompliant /
  hasOssReference / rootCauseIdentified with project scanning, CLAUDE.md
  tech-stack parsing, and regex-based uncertainty detection. Synced across all
  four tracked skill copies (src CLI source, plugin source, repo-root, .claude).
- confidence.py: add _read_tech_stack and _check_architecture_anti_patterns;
  multi-language (py/ts/js) + content-grep _search_codebase; richer root-cause
  validation. Flat single-class structure preserved; accepts both upstream
  (target_name/root_cause) and fork (feature_name/proposed_technology) keys.
- tests: add test_cli_main, test_doctor, test_execution_init, test_install_mcp,
  test_install_skill, e2e/test_cli_e2e; expand test_confidence to a superset.

All ported tests pass against the existing upstream modules. fork-only
init/check CLI tests and circular full-suite meta-tests were excluded.

NOTE: the confidence-check skill is duplicated across four tracked locations;
a follow-up should collapse these to a single source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 20:40:13 +09:00

506 行
17 KiB
Python

"""
Unit tests for ConfidenceChecker
Tests pre-execution confidence assessment functionality.
"""
import pytest
from superclaude.pm_agent.confidence import ConfidenceChecker
class TestConfidenceChecker:
"""Test suite for ConfidenceChecker class"""
def test_high_confidence_scenario(self, sample_context):
"""
Test that a well-prepared context returns high confidence (≥90%)
All checks pass:
- No duplicates (25%)
- Architecture compliant (25%)
- Official docs verified (20%)
- OSS reference found (15%)
- Root cause identified (15%)
Total: 100%
"""
checker = ConfidenceChecker()
confidence = checker.assess(sample_context)
assert confidence >= 0.9, f"Expected high confidence ≥0.9, got {confidence}"
assert confidence == 1.0, "All checks passed should give 100% confidence"
def test_low_confidence_scenario(self, low_confidence_context):
"""
Test that an unprepared context returns low confidence (<70%)
No checks pass: 0%
"""
checker = ConfidenceChecker()
confidence = checker.assess(low_confidence_context)
assert confidence < 0.7, f"Expected low confidence <0.7, got {confidence}"
assert confidence == 0.0, "No checks passed should give 0% confidence"
def test_medium_confidence_scenario(self):
"""
Test medium confidence scenario (70-89%)
Some checks pass, some don't
"""
checker = ConfidenceChecker()
context = {
"test_name": "test_feature",
"duplicate_check_complete": True, # 25%
"architecture_check_complete": True, # 25%
"official_docs_verified": True, # 20%
"oss_reference_complete": False, # 0%
"root_cause_identified": False, # 0%
}
confidence = checker.assess(context)
assert 0.7 <= confidence < 0.9, (
f"Expected medium confidence 0.7-0.9, got {confidence}"
)
assert confidence == 0.7, "Should be exactly 70%"
def test_confidence_checks_recorded(self, sample_context):
"""Test that confidence checks are recorded in context"""
checker = ConfidenceChecker()
checker.assess(sample_context)
assert "confidence_checks" in sample_context
assert isinstance(sample_context["confidence_checks"], list)
assert len(sample_context["confidence_checks"]) == 5
# All checks should pass
for check in sample_context["confidence_checks"]:
assert check.startswith("✅"), f"Expected passing check, got: {check}"
def test_get_recommendation_high(self):
"""Test recommendation for high confidence"""
checker = ConfidenceChecker()
recommendation = checker.get_recommendation(0.95)
assert "High confidence" in recommendation
assert "Proceed" in recommendation
def test_get_recommendation_medium(self):
"""Test recommendation for medium confidence"""
checker = ConfidenceChecker()
recommendation = checker.get_recommendation(0.75)
assert "Medium confidence" in recommendation
assert "Continue investigation" in recommendation
def test_get_recommendation_low(self):
"""Test recommendation for low confidence"""
checker = ConfidenceChecker()
recommendation = checker.get_recommendation(0.5)
assert "Low confidence" in recommendation
assert "STOP" in recommendation
def test_has_official_docs_with_flag(self):
"""Test official docs check with direct flag"""
checker = ConfidenceChecker()
context = {"official_docs_verified": True}
result = checker._has_official_docs(context)
assert result is True
def test_no_duplicates_check(self):
"""Test duplicate check validation"""
checker = ConfidenceChecker()
# With flag
context_pass = {"duplicate_check_complete": True}
assert checker._no_duplicates(context_pass) is True
# Without flag
context_fail = {"duplicate_check_complete": False}
assert checker._no_duplicates(context_fail) is False
def test_architecture_compliance_check(self):
"""Test architecture compliance validation"""
checker = ConfidenceChecker()
# With flag
context_pass = {"architecture_check_complete": True}
assert checker._architecture_compliant(context_pass) is True
# Without flag
context_fail = {}
assert checker._architecture_compliant(context_fail) is False
def test_oss_reference_check(self):
"""Test OSS reference validation"""
checker = ConfidenceChecker()
# With flag
context_pass = {"oss_reference_complete": True}
assert checker._has_oss_reference(context_pass) is True
# Without flag
context_fail = {"oss_reference_complete": False}
assert checker._has_oss_reference(context_fail) is False
def test_root_cause_check(self):
"""Test root cause identification validation"""
checker = ConfidenceChecker()
# With flag
context_pass = {"root_cause_identified": True}
assert checker._root_cause_identified(context_pass) is True
# Without flag
context_fail = {}
assert checker._root_cause_identified(context_fail) is False
@pytest.mark.confidence_check
def test_confidence_check_marker_integration(confidence_checker):
"""
Test that confidence_check marker works with pytest plugin fixture
This test should skip if confidence < 70%
"""
context = {
"test_name": "test_confidence_check_marker_integration",
"has_official_docs": True,
"duplicate_check_complete": True,
"architecture_check_complete": True,
"official_docs_verified": True,
"oss_reference_complete": True,
"root_cause_identified": True,
}
confidence = confidence_checker.assess(context)
assert confidence >= 0.7, "Confidence should be high enough to not skip"
class TestCodebaseSearchImplementation:
"""Test actual codebase search logic (not just flag-based)"""
def test_find_project_root_from_test_file(self, tmp_path):
"""Test finding project root from test file path"""
# Create project structure
(tmp_path / "pyproject.toml").touch()
(tmp_path / "src").mkdir()
test_file = tmp_path / "tests" / "test_example.py"
test_file.parent.mkdir()
test_file.touch()
checker = ConfidenceChecker()
context = {"test_file": str(test_file)}
root = checker._find_project_root(context)
assert root == tmp_path
def test_find_project_root_from_claude_md(self, tmp_path):
"""Test finding project root via CLAUDE.md"""
(tmp_path / "CLAUDE.md").touch()
test_file = tmp_path / "deep" / "nested" / "test.py"
test_file.parent.mkdir(parents=True)
test_file.touch()
checker = ConfidenceChecker()
context = {"test_file": str(test_file)}
root = checker._find_project_root(context)
assert root == tmp_path
def test_find_project_root_explicit(self, tmp_path):
"""Test explicit project_root in context"""
checker = ConfidenceChecker()
context = {"project_root": str(tmp_path)}
root = checker._find_project_root(context)
assert root == tmp_path
def test_search_codebase_finds_similar_files(self, tmp_path):
"""Test searching for similar file names"""
(tmp_path / "auth_handler.py").write_text("class AuthHandler: pass")
(tmp_path / "authentication.py").write_text("def authenticate(): pass")
checker = ConfidenceChecker()
results = checker._search_codebase(
tmp_path,
"auth",
patterns=["**/*.py"],
exclude_dirs=[],
)
assert len(results) >= 1
assert any("auth" in r.lower() for r in results)
def test_search_codebase_finds_function_definitions(self, tmp_path):
"""Test finding function definitions in files"""
(tmp_path / "utils.py").write_text(
"def validate_token(token):\n return True"
)
checker = ConfidenceChecker()
results = checker._search_codebase(
tmp_path,
"validate_token",
patterns=["**/*.py"],
exclude_dirs=[],
)
assert "utils.py" in results
def test_search_codebase_excludes_directories(self, tmp_path):
"""Test excluding node_modules and similar directories"""
(tmp_path / "node_modules").mkdir()
(tmp_path / "node_modules" / "auth.py").write_text("class Auth: pass")
(tmp_path / "src" / "auth.py").parent.mkdir()
(tmp_path / "src" / "auth.py").write_text("class Auth: pass")
checker = ConfidenceChecker()
results = checker._search_codebase(
tmp_path,
"auth",
patterns=["**/*.py"],
exclude_dirs=["node_modules"],
)
assert not any("node_modules" in r for r in results)
assert any("src" in r for r in results)
def test_no_duplicates_actual_search(self, tmp_path):
"""Test duplicate detection via actual codebase search"""
# Create project structure with potential duplicate
(tmp_path / "pyproject.toml").touch()
(tmp_path / "src").mkdir()
(tmp_path / "src" / "auth_manager.py").write_text(
"class AuthManager:\n pass"
)
checker = ConfidenceChecker()
context = {
"project_root": str(tmp_path),
"feature_name": "auth_manager",
}
result = checker._no_duplicates(context)
# Should find potential duplicate
assert result is False
assert "potential_duplicates" in context
assert len(context["potential_duplicates"]) > 0
def test_no_duplicates_no_feature_name(self):
"""Test duplicate check returns False without feature name"""
checker = ConfidenceChecker()
context = {"project_root": "/tmp"}
result = checker._no_duplicates(context)
assert result is False
class TestTechStackDetection:
"""Test tech stack detection from CLAUDE.md"""
def test_read_tech_stack_from_claude_md(self, tmp_path):
"""Test reading tech stack from CLAUDE.md"""
claude_md = tmp_path / "CLAUDE.md"
claude_md.write_text("""# Project
This project uses Supabase for backend.
Built with Next.js and TypeScript.
Uses UV for Python package management.
""")
checker = ConfidenceChecker()
tech_stack = checker._read_tech_stack(tmp_path)
assert tech_stack.get("has_claude_md") is True
assert tech_stack.get("supabase") is True
assert tech_stack.get("nextjs") is True
assert tech_stack.get("typescript") is True
assert tech_stack.get("uv") is True
def test_read_tech_stack_detects_package_files(self, tmp_path):
"""Test detecting tech stack from package files"""
(tmp_path / "pyproject.toml").touch()
(tmp_path / "package.json").touch()
(tmp_path / "turbo.json").touch()
checker = ConfidenceChecker()
tech_stack = checker._read_tech_stack(tmp_path)
assert tech_stack.get("python_project") is True
assert tech_stack.get("node_project") is True
assert tech_stack.get("turborepo") is True
def test_check_architecture_anti_patterns_supabase(self):
"""Test detecting Supabase anti-patterns"""
checker = ConfidenceChecker()
tech_stack = {"supabase": True}
warnings = checker._check_architecture_anti_patterns(
tech_stack, "custom api with express"
)
assert len(warnings) > 0
assert any("Supabase" in w for w in warnings)
def test_check_architecture_anti_patterns_nextjs(self):
"""Test detecting Next.js anti-patterns"""
checker = ConfidenceChecker()
tech_stack = {"nextjs": True}
warnings = checker._check_architecture_anti_patterns(
tech_stack, "custom routing solution"
)
assert len(warnings) > 0
assert any("Next.js" in w for w in warnings)
def test_check_architecture_anti_patterns_uv(self):
"""Test detecting UV anti-patterns"""
checker = ConfidenceChecker()
tech_stack = {"uv": True}
warnings = checker._check_architecture_anti_patterns(
tech_stack, "pip install requests"
)
assert len(warnings) > 0
assert any("UV" in w or "uv" in w for w in warnings)
def test_architecture_compliant_actual_check(self, tmp_path):
"""Test architecture compliance with actual tech stack check"""
(tmp_path / "CLAUDE.md").write_text("Uses Supabase for backend")
(tmp_path / "pyproject.toml").touch()
checker = ConfidenceChecker()
context = {
"project_root": str(tmp_path),
"proposed_technology": "custom api with express",
}
result = checker._architecture_compliant(context)
assert result is False
assert "architecture_warnings" in context
class TestRootCauseAnalysis:
"""Test root cause identification logic"""
def test_root_cause_with_uncertainty_language(self):
"""Test detecting uncertainty language in root cause"""
checker = ConfidenceChecker()
uncertain_phrases = [
"The issue is probably in the auth module",
"Maybe the database connection is timing out",
"It might be a race condition",
"Could be an issue with the cache",
"Possibly related to memory leak",
"Not sure but I think it's in the parser",
"I guess the config is wrong",
"I assume the API changed",
]
for phrase in uncertain_phrases:
context = {
"root_cause": phrase,
"proposed_solution": "A sufficiently detailed solution here",
}
result = checker._root_cause_identified(context)
assert result is False, f"Should reject uncertainty: {phrase}"
assert "root_cause_warning" in context
def test_root_cause_confident_language(self):
"""Test accepting confident root cause statements"""
checker = ConfidenceChecker()
context = {
"root_cause": "The authentication fails because the token expiration check uses UTC time but the token contains local time. Line 45 in auth.py.",
"proposed_solution": "Convert token timestamp to UTC before comparison in validate_token function.",
}
result = checker._root_cause_identified(context)
assert result is True
def test_root_cause_missing_solution(self):
"""Test rejecting when no solution provided"""
checker = ConfidenceChecker()
context = {
"root_cause": "The database query is inefficient",
}
result = checker._root_cause_identified(context)
assert result is False
assert "No proposed solution" in context.get("root_cause_warning", "")
def test_root_cause_brief_solution(self):
"""Test rejecting too brief solutions"""
checker = ConfidenceChecker()
context = {
"root_cause": "Token validation fails",
"proposed_solution": "Fix token",
}
result = checker._root_cause_identified(context)
assert result is False
assert "too brief" in context.get("root_cause_warning", "")
class TestOSSReferenceCheck:
"""Test OSS reference validation logic"""
def test_has_oss_reference_with_urls(self):
"""Test detecting OSS references via URLs"""
checker = ConfidenceChecker()
context = {
"oss_references": [
"https://github.com/example/project",
],
}
result = checker._has_oss_reference(context)
assert result is True
def test_has_oss_reference_with_doc_urls(self):
"""Test detecting documentation URLs"""
checker = ConfidenceChecker()
context = {
"documentation_urls": [
"https://docs.example.com/api",
],
}
result = checker._has_oss_reference(context)
assert result is True
def test_has_oss_reference_with_research_notes(self):
"""Test detecting research notes"""
checker = ConfidenceChecker()
context = {
"research_notes": "Investigated the official documentation. The recommended approach is to use async/await pattern as shown in the examples."
}
result = checker._has_oss_reference(context)
assert result is True
def test_has_oss_reference_empty(self):
"""Test missing OSS references"""
checker = ConfidenceChecker()
context = {}
result = checker._has_oss_reference(context)
assert result is False
assert "oss_recommendation" in context