feat: Implement BranchProtectionAssessor
Attribute Definition
Attribute ID: branch_protection (Attribute #25 - Tier 3)
Definition: Required status checks and review approvals before merging to main/production branches.
Why It Matters: Prevents broken code from reaching production. Provides safety net for AI-generated code. Ensures quality gates are enforced.
Impact on Agent Behavior:
- Understanding of merge requirements
- Awareness of quality gates
- Suggestions aligned with branch policies
- Better PR creation (ensuring checks pass)
Measurable Criteria:
- Branch protection enabled for main/master/production
- Required status checks:
- All tests passing
- Linting/formatting passing
- Code coverage threshold met
- Security scanning passing
- Required reviews: At least 1 approval
- No force pushes to protected branches
- No direct commits to protected branches
- Up-to-date branch requirement (rebase/merge before merging)
Implementation Requirements
File Location: src/agentready/assessors/testing.py
Class Name: BranchProtectionAssessor
Tier: 3 (Important)
Default Weight: 0.015 (1.5% of total score)
Assessment Logic
Scoring Approach: Query GitHub API for branch protection rules
Evidence to Check (score components):
-
Branch protection enabled (50%)
- Use GitHub CLI or API to check protection rules
- Check for main/master branch
-
Required status checks (30%)
- Verify status checks are required
- Check for meaningful checks (not just empty list)
-
Review requirements (20%)
- Required approvals count
- Dismiss stale reviews
- Code owner reviews
Scoring Logic:
if branch_protection_enabled:
protection_score = 50
# Status checks
if required_status_checks:
status_score = 30
else:
status_score = 0
# Reviews
if required_approvals >= 1:
review_score = 20
else:
review_score = 0
total_score = protection_score + status_score + review_score
else:
total_score = 0
status = "pass" if total_score >= 75 else "fail"
IMPORTANT: This assessor requires GitHub API access. If GitHub integration is not available, return not_applicable.
Code Pattern to Follow
Reference: New pattern - GitHub API integration
Pattern:
- Check if repository is GitHub-hosted (
.git/config contains github.com)
- Use GitHub CLI (
gh api) to query branch protection
- Parse JSON response for protection rules
- Calculate score based on protection level
- Handle API errors gracefully (return not_applicable if API unavailable)
Example Finding Responses
Pass (Score: 100)
Finding(
attribute=self.attribute,
status="pass",
score=100.0,
measured_value="full protection enabled",
threshold="protection with checks and reviews",
evidence=[
"Branch protection enabled on 'main'",
"Required status checks: 3 (tests, lint, security)",
"Required reviews: 1 approval",
"Force push disabled",
"Direct commits disabled",
],
remediation=None,
error_message=None,
)
Fail (Score: 50)
Finding(
attribute=self.attribute,
status="fail",
score=50.0,
measured_value="basic protection only",
threshold="protection with checks and reviews",
evidence=[
"Branch protection enabled on 'main'",
"No required status checks configured",
"No required reviews",
"Force push disabled",
],
remediation=self._create_remediation(),
error_message=None,
)
Not Applicable
Finding.not_applicable(
self.attribute,
reason="Not a GitHub repository or GitHub API unavailable"
)
Registration
Add to src/agentready/services/scanner.py in create_all_assessors():
from ..assessors.testing import (
TestCoverageAssessor,
PreCommitHooksAssessor,
TestNamingConventionsAssessor,
CICDPipelineVisibilityAssessor,
BranchProtectionAssessor, # Add this import
)
def create_all_assessors() -> List[BaseAssessor]:
return [
# ... existing assessors ...
BranchProtectionAssessor(), # Add this line
]
Testing Guidance
Test File: tests/unit/test_assessors_testing.py
Test Cases:
- SKIP unit tests for this assessor (requires GitHub API)
- Integration tests would require test repositories
- Mock GitHub API responses for basic validation
Note: This assessor is unique - it requires external API access. Consider making it optional or providing clear messaging when GitHub integration is unavailable.
Dependencies
External Tools:
- GitHub CLI (
gh) or GitHub API client
- Requires authentication
Python Packages:
requests for GitHub API calls (optional)
- Or use
subprocess to call gh api
Installation Check:
def is_applicable(self, repository: Repository) -> bool:
# Check if GitHub repo
git_config = repository.path / ".git" / "config"
if not git_config.exists():
return False
with open(git_config) as f:
if "github.com" not in f.read():
return False
# Check if gh CLI available
try:
result = subprocess.run(
["gh", "--version"],
capture_output=True,
timeout=5,
)
return result.returncode == 0
except (FileNotFoundError, subprocess.TimeoutExpired):
return False
Remediation Steps
def _create_remediation(self) -> Remediation:
return Remediation(
summary="Enable branch protection with required checks and reviews",
steps=[
"Go to GitHub repository settings",
"Navigate to Branches > Branch protection rules",
"Add rule for 'main' branch",
"Enable: Require pull request reviews before merging",
"Enable: Require status checks to pass",
"Add status checks: tests, lint, coverage",
"Disable: Allow force pushes",
"Disable: Allow deletions",
],
tools=["github-cli"],
commands=[
"# Enable branch protection via GitHub CLI",
"gh api repos/:owner/:repo/branches/main/protection \\",
" --method PUT \\",
" --field required_status_checks='{'strict':true,'contexts':['test','lint']}' \\",
" --field required_pull_request_reviews='{'required_approving_review_count':1}' \\",
" --field enforce_admins=true \\",
" --field restrictions=null",
"",
"# Check current protection",
"gh api repos/:owner/:repo/branches/main/protection",
],
examples=[
"""# Example branch protection configuration (GitHub UI)
**Branch name pattern**: main
**Protect matching branches**:
- ✓ Require a pull request before merging
- ✓ Require approvals: 1
- ✓ Dismiss stale pull request approvals when new commits are pushed
- ✓ Require status checks to pass before merging
- ✓ Require branches to be up to date before merging
- Status checks:
- test
- lint
- coverage
- security-scan
- ✓ Do not allow bypassing the above settings
- ✓ Restrict who can push to matching branches
**Rules applied to everyone including administrators**:
- ✓ Block force pushes
- ✓ Do not allow deletions
""",
],
citations=[
Citation(
source="GitHub Docs",
title="About protected branches",
url="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches",
relevance="Official documentation on branch protection",
),
Citation(
source="GitHub Docs",
title="Managing a branch protection rule",
url="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule",
relevance="Guide to configuring branch protection",
),
],
)
Implementation Notes
- GitHub Detection: Parse
.git/config to check for github.com remote
- API Authentication: Use
gh CLI which handles auth automatically
- API Call:
gh api repos/:owner/:repo/branches/main/protection
- Response Parsing: Parse JSON for protection rules
- Error Handling: Return not_applicable if:
- Not a GitHub repo
- GitHub CLI not installed
- API call fails (permissions, network)
- Rate Limiting: GitHub API has rate limits, cache results if possible
- Alternative Approach: Check for
.github/settings.yml (if using Settings app)
GitHub API Endpoint:
GET /repos/{owner}/{repo}/branches/{branch}/protection
Expected Response Structure:
{
"required_status_checks": {
"strict": true,
"contexts": ["test", "lint", "coverage"]
},
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"dismiss_stale_reviews": true
},
"enforce_admins": {
"enabled": true
}
}
Special Note: This is an advanced assessor that depends on external services. Consider making it optional or providing clear guidance when GitHub integration is unavailable.
feat: Implement BranchProtectionAssessor
Attribute Definition
Attribute ID:
branch_protection(Attribute #25 - Tier 3)Definition: Required status checks and review approvals before merging to main/production branches.
Why It Matters: Prevents broken code from reaching production. Provides safety net for AI-generated code. Ensures quality gates are enforced.
Impact on Agent Behavior:
Measurable Criteria:
Implementation Requirements
File Location:
src/agentready/assessors/testing.pyClass Name:
BranchProtectionAssessorTier: 3 (Important)
Default Weight: 0.015 (1.5% of total score)
Assessment Logic
Scoring Approach: Query GitHub API for branch protection rules
Evidence to Check (score components):
Branch protection enabled (50%)
Required status checks (30%)
Review requirements (20%)
Scoring Logic:
IMPORTANT: This assessor requires GitHub API access. If GitHub integration is not available, return
not_applicable.Code Pattern to Follow
Reference: New pattern - GitHub API integration
Pattern:
.git/configcontains github.com)gh api) to query branch protectionExample Finding Responses
Pass (Score: 100)
Fail (Score: 50)
Not Applicable
Registration
Add to
src/agentready/services/scanner.pyincreate_all_assessors():Testing Guidance
Test File:
tests/unit/test_assessors_testing.pyTest Cases:
Note: This assessor is unique - it requires external API access. Consider making it optional or providing clear messaging when GitHub integration is unavailable.
Dependencies
External Tools:
gh) or GitHub API clientPython Packages:
requestsfor GitHub API calls (optional)subprocessto callgh apiInstallation Check:
Remediation Steps
Implementation Notes
.git/configto check for github.com remoteghCLI which handles auth automaticallygh api repos/:owner/:repo/branches/main/protection.github/settings.yml(if using Settings app)GitHub API Endpoint:
Expected Response Structure:
{ "required_status_checks": { "strict": true, "contexts": ["test", "lint", "coverage"] }, "required_pull_request_reviews": { "required_approving_review_count": 1, "dismiss_stale_reviews": true }, "enforce_admins": { "enabled": true } }Special Note: This is an advanced assessor that depends on external services. Consider making it optional or providing clear guidance when GitHub integration is unavailable.