Skip to content

Comments

fix(cli): check .pre-commit-config.yaml for conventional commit#310

Open
dbasunag wants to merge 2 commits intoambient-code:mainfrom
dbasunag:feature/conventional_commit
Open

fix(cli): check .pre-commit-config.yaml for conventional commit#310
dbasunag wants to merge 2 commits intoambient-code:mainfrom
dbasunag:feature/conventional_commit

Conversation

@dbasunag
Copy link
Contributor

@dbasunag dbasunag commented Feb 19, 2026

Description

Add support for conventional commit check via pre-commit config.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test coverage improvement

Related Issues

Fixes #
Relates to #

Changes Made

Conventional_commit check now identifies

  • package.json with "commitlint" configuration
  • Pre-commit hooks with conventional commit tools
  • Added tests for both package.json and pre-commit config files

Testing

  • Unit tests pass (pytest)
  • Integration tests pass
  • Manual testing performed
  • No new warnings or errors

Checklist

  • My code follows the project's code style
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Screenshots (if applicable)

Additional Notes

@github-actions
Copy link
Contributor

AgentReady Code Review

Summary

This PR extends ConventionalCommitsAssessor to detect conventional commit linting configured via .pre-commit-config.yaml. This is a meaningful improvement for Python-first projects that rely on pre-commit rather than JS-based tools (commitlint/husky). Notably, this fix directly improves AgentReady's own self-assessment — AgentReady uses conventional-pre-commit from compilerla in its own .pre-commit-config.yaml, which matches the added "conventional-pre-commit" pattern.


Attribute Impact Analysis

Attribute Current Score Projected Score Change
conventional_commits (T2) 0/100 100/100 +100
Overall Score 80.0 (Gold) ~80.9 (Gold) +~0.9

The conventional_commits attribute carries default_weight=0.03 (Tier 2 = 30% pool). Fixing this from 0 to 100 adds roughly 3 weighted points to the overall score.


Security Review

No security issues found. The file read is wrapped in a broad except Exception: pass which prevents any crash from permission errors or other I/O failures. No user-controlled input is executed.


Code Quality

Issues to address:

1. Overly broad exception type (stub_assessors.py, new assess method)

except Exception:
    pass

Since the only operation inside the try block is precommit_config.read_text(), this should be narrowed to OSError (which covers PermissionError, FileNotFoundError, etc.). Using bare except Exception masks unexpected errors like MemoryError or programming mistakes.

# Suggested fix
except OSError:
    pass

2. Misleading test name (test_precommit_invalid_yaml)

The test writes "invalid: yaml: content: [" and expects a fail result. However, the implementation uses string matching (not YAML parsing), so this test is actually verifying "content that doesn't contain the patterns returns fail" — not "invalid YAML is handled gracefully." The test passes for the right behaviour, but the name implies YAML parsing exception handling that doesn't exist.

Suggested rename: test_precommit_no_matching_patterns or add a comment clarifying the intent.

3. Remediation steps not updated for the new detection scope

The fail case remediation still only suggests JS-centric tooling:

remediation=Remediation(
    summary="Configure conventional commits with commitlint",
    steps=["Install commitlint", "Configure husky for commit-msg hook"],
    tools=["commitlint", "husky"],
    commands=["npm install --save-dev @commitlint/cli @commitlint/config-conventional husky"],
    ...
)

Now that the assessor detects Python-native pre-commit hooks, the remediation should offer a Python-friendly alternative. Suggested addition:

steps=[
    "Option A (Python/pre-commit): Add conventional-pre-commit to .pre-commit-config.yaml",
    "Option B (JS/commitlint): Install commitlint and configure husky for commit-msg hook",
],
tools=["pre-commit", "conventional-pre-commit", "commitlint", "husky"],
commands=[
    "# Python (pre-commit):",
    "pip install pre-commit && pre-commit install --hook-type commit-msg",
    "# JS (commitlint + husky):",
    "npm install --save-dev @commitlint/cli @commitlint/config-conventional husky",
],

4. PR description is empty

All checkboxes are unchecked and the description fields contain only the template placeholders. This makes it harder to review intent and blocks automated PR comment generation in CI. Please fill in the description, type of change, and testing notes.


AgentReady Pattern Compliance

Check Status
Inherits from BaseAssessor Pass
Uses Finding directly (consistent with existing assessor style) Pass
Graceful degradation on missing/unreadable file Pass
calculate_proportional_score() not applicable (binary pass/fail) Pass
New tests added for all new code paths Pass — 10 test cases
Tests use tmp_path fixture correctly Pass

Coordination Note

Related PR #308 (fix: Check for all official commitlint config file formats) addresses the complementary gap: missing .commitlintrc.yml, .commitlintrc.yaml, commitlint.config.js, etc. These two PRs touch the same assessor. Whichever lands first will need a rebase. Consider whether they should be merged together or sequenced explicitly.


Recommendations

  1. Narrow the exception from except Exception to except OSError in assess()
  2. Update remediation steps to include pre-commit + conventional-pre-commit as a Python-native option
  3. Rename or add a comment to test_precommit_invalid_yaml to reflect actual intent
  4. Fill in the PR description with what changed and why
  5. Coordinate with PR fix: Check for all official commitlint config file formats #308 to avoid rebase conflicts on stub_assessors.py

Verdict

REQUEST CHANGES — The core detection logic is correct and the test coverage is thorough. Items 1–4 above should be addressed before merge, with item 2 (remediation update) being the most impactful for end users.


Generated by AgentReady Review Agent

@dbasunag dbasunag force-pushed the feature/conventional_commit branch from 7884f7a to 4bd482e Compare February 19, 2026 18:17
@github-actions
Copy link
Contributor

AgentReady Code Review

Note: This review reflects the current diff state. A prior automated review was posted earlier; most of its requested changes appear to have been incorporated into this PR.


Summary

This PR extends ConventionalCommitsAssessor to detect conventional commit enforcement configured via .pre-commit-config.yaml. It's a meaningful fix for Python-first projects that rely on pre-commit rather than Node.js-based tooling (commitlint/husky). AgentReady itself uses conventional-pre-commit from compilerla, so this fix directly corrects a false-negative in the project's own self-assessment.


Attribute Impact Analysis

Attribute main Score After Merge Delta
conventional_commits (T2, weight=0.03) 0/100 100/100 +100
Overall Score 80.0 (Gold) ~83.0 (Gold) +3.0

Prior Review Status

Issue from Previous Review Status
Narrow except Exceptionexcept OSError ✅ Addressed — diff uses except OSError
Update remediation to include Python/pre-commit options ✅ Addressed — remediation updated with two-path guide
Rename misleading test ✅ Addressed — renamed to test_precommit_no_matching_patterns
Fill in PR description ❌ Still empty — template placeholders remain

Remaining Issues

1. Empty PR description (minor, process)

All description fields are still template placeholders. At a minimum, fill in the "Changes Made" section and the "Related Issues" field. This blocks traceability and makes the automated CI comment generation less useful.

2. Test docstring/name mismatch

test_precommit_no_matching_patterns has this docstring:

"""Test that invalid YAML in pre-commit config is handled gracefully."""

But since the implementation uses string matching (not YAML parsing), the test is actually verifying that content without the target patterns returns fail. The behaviour is correct, but the docstring describes something that doesn't happen. Update the docstring to match the actual intent:

"""Test that pre-commit config without conventional commit patterns fails."""

3. test_precommit_file_permission_error may be silently skipped as root

os.chmod(precommit_config, 0o000) on Linux does not prevent reading by root. GitHub Actions runners typically run as a non-root user, so this should be fine in practice. Still worth a comment in the test:

# Note: This test is a no-op when run as root (root ignores file permissions)

4. Pattern-matching scope

The three patterns cover the main pre-commit hook libraries:

  • conventional-precommit-linter (compilerla)
  • conventional-pre-commit (compilerla via URL)
  • commitlint-pre-commit-hook

Not covered: gitlint, committed, or custom repo hooks that enforce conventional commits. This is acceptable for a simplified assessor, but worth a comment in the code for future expansion.

5. Rebase conflict risk with PR #308

PR #308 (fix: Check for all official commitlint config file formats) modifies the same assess() method in ConventionalCommitsAssessor. Whichever lands first will require the other to rebase. The changes are complementary — #308 expands has_commitlint detection, #310 adds has_precommit_conventional. Suggest coordinating merge order explicitly.


Code Quality

Check Status
Inherits from BaseAssessor
except OSError (not bare except)
Graceful degradation on unreadable file
Updated remediation with Python-native path
Updated inline comment (no longer says "Simplified: commitlint or husky only")
has_precommit_conventional = False initialized before if block ✅ — avoids UnboundLocalError
Test coverage: all three new patterns tested individually
finally block restores permissions in permission error test
PR description filled in
Test docstring matches behaviour

Security Review

No issues. File content is read and matched against static string patterns — no execution, no user-controlled input, no subprocess calls. The OSError guard correctly handles I/O failures without exposing the exception to callers.


Verdict

APPROVE with minor comments — The core logic is correct, exception handling is appropriately scoped, and test coverage is thorough (10 cases). The two items to address before merge are:

  1. Fill in the PR description (especially "Changes Made" and "Related Issues")
  2. Fix the test docstring in test_precommit_no_matching_patterns

Neither blocks correctness. Coordinate merge order with PR #308 to avoid a rebase collision.


Generated by AgentReady Review Agent

@dbasunag dbasunag force-pushed the feature/conventional_commit branch from 4bd482e to 0dbfa79 Compare February 19, 2026 18:28
@github-actions
Copy link
Contributor

AgentReady Code Review — PR #310

Reviewer: Claude Code (AgentReady Development Agent)
Date: 2026-02-19
Scope: AgentReady attribute compliance · Security · Code quality · Best practices


Summary

This PR extends ConventionalCommitsAssessor to detect conventional commit enforcement via pre-commit hooks (.pre-commit-config.yaml), filling a gap that caused Python-first repos to fail the conventional_commits attribute unfairly. The test suite addition is thorough and well-structured.

Overall verdict: Approve with minor issues noted below.


AgentReady Attribute Impact

Attribute Effect
conventional_commits ✅ Improves recall — Python repos using pre-commit now score correctly
test_coverage ✅ +362 lines of new tests strengthen coverage for this assessor
type_annotations Neutral — consistent with existing assessor style

Issues

🔴 Conflict with PR #308 — Expanded commitlint formats not included

PR #310 still only checks for .commitlintrc.json, while PR #308 (also open) expands this to 14 commitlint config formats (.commitlintrc.yaml, .commitlintrc.ts, commitlint.config.js, etc.). These two PRs modify the exact same lines and will conflict on merge.

Recommendation: One PR should absorb the other. Specifically, PR #310 should incorporate the expanded commitlint format list from PR #308 before merging. If both land separately, whichever is second will need a rebase.

# PR #310 currently has (line ~240):
has_commitlint = (repository.path / ".commitlintrc.json").exists()

# Should be (incorporating PR #308's list):
commitlint_configs = [
    ".commitlintrc", ".commitlintrc.json", ".commitlintrc.yaml",
    ".commitlintrc.yml", ".commitlintrc.js", ".commitlintrc.cjs",
    ".commitlintrc.mjs", ".commitlintrc.ts", ".commitlintrc.cts",
    "commitlint.config.js", "commitlint.config.cjs", "commitlint.config.mjs",
    "commitlint.config.ts", "commitlint.config.cts",
]
has_commitlint = any((repository.path / cfg).exists() for cfg in commitlint_configs)

🟡 import os inside test function body

In test_precommit_file_permission_error, import os is placed inside the test function. Per PEP 8 and project code style, imports belong at the top of the module.

# current (inside test function):
import os
os.chmod(precommit_config, 0o000)

# fix: move to top of test file
import os  # already in use elsewhere in the file, just move it up

🟡 Test docstring mismatch in test_precommit_no_matching_patterns

The docstring reads "Test that invalid YAML in pre-commit config is handled gracefully", but the test name and intent are about config with no matching patterns. The implementation uses plain string matching (not YAML parsing), so "invalid YAML" is handled without any special logic. The docstring should match the test's actual purpose:

def test_precommit_no_matching_patterns(self, tmp_path):
    """Test that pre-commit config without conventional commit patterns fails."""

🟡 Evidence string doesn't indicate detected mechanism

On pass, evidence=["Commit linting configured"] is returned regardless of which mechanism triggered the pass. More informative evidence would help users understand their assessment:

# Instead of always returning "Commit linting configured",
# surface which mechanism was detected:
if has_commitlint:
    evidence_msg = "commitlint configuration file detected"
elif has_husky:
    evidence_msg = ".husky/ directory detected"
else:
    evidence_msg = "conventional commit pre-commit hook detected"

🟢 Missing package.json commitlint check (nice-to-have, not a blocker)

A common pattern is configuring commitlint in package.json via the "commitlint" key. This is not addressed by either open PR. Consider as a follow-up.


Security

No issues found. The assessor reads local files only, uses no user input, and correctly catches OSError. The string-matching approach to .pre-commit-config.yaml is appropriate and avoids YAML injection risk.


Code Quality: Positives

  • ✅ OSError handling (try/except OSError: pass) is correct and consistent with project patterns
  • ✅ String-search approach to .pre-commit-config.yaml is pragmatic — resilient to malformed YAML, no extra dependency
  • ✅ Test coverage is comprehensive: no-config, commitlint, husky, three pre-commit patterns, no-match, empty, permission error, multi-tool
  • ✅ Remediation steps now include both Python (pre-commit) and JS (husky+commitlint) options — good for polyglot users
  • ✅ PR title follows conventional commit format

Required Before Merge

  1. Resolve the conflict/overlap with PR fix: Check for all official commitlint config file formats #308 (either absorb it or coordinate merge order)
  2. Move import os to top of test file

The docstring and evidence nits are minor but worth fixing while the file is open.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

Outdated review (click to expand)
Outdated review (click to expand)
Outdated review (click to expand)
Outdated review (click to expand)
Outdated review (click to expand)

🤖 AgentReady Code Review

PR Status: 6 issues found (3 🔴 Critical, 2 🟡 Major, 1 🔵 Minor)
Score Impact: Current 80.0/100 → ~83.0/100 if all issues fixed and this PR lands
Certification: Gold → Gold (score improves, tier unchanged)

Review context: This review reflects the current diff state as of commit 0dbfa79. Three prior automated reviews addressed except Exceptionexcept OSError, remediation updates, and test renaming. This review confirms outstanding items and adds three new findings.


🔴 Critical Issues (Confidence ≥90) — Auto-Fix Recommended

1. import os inside test function body

Attribute: code_quality (Tier 3) — CLAUDE.md: Code Quality
Confidence: 95%
Score Impact: CI linting failure risk (ruff/isort will flag this)
Location: tests/unit/test_assessors_stub.pytest_precommit_file_permission_error

Issue Details:
The import os statement is scoped inside the test function body, violating PEP 8 and the project's ruff/isort linting requirements. This was flagged in the prior review but remains unresolved.

# current — import inside function (fails ruff E402)
def test_precommit_file_permission_error(self, tmp_path):
    ...
    import os
    os.chmod(precommit_config, 0o000)

Remediation:

# Move import os to module-level (line 3, alongside import subprocess)
# Then run:
isort tests/unit/test_assessors_stub.py && ruff check tests/unit/test_assessors_stub.py

2. Test docstring mismatch — test_precommit_no_matching_patterns

Attribute: inline_documentation (Tier 3)
Confidence: 92%
Score Impact: −0.0 points (documentation quality)
Location: tests/unit/test_assessors_stub.py

Issue Details:
The test was correctly renamed from test_precommit_invalid_yaml (prior review feedback) but its docstring was not updated. The docstring still describes the old intent:

def test_precommit_no_matching_patterns(self, tmp_path):
    """Test that invalid YAML in pre-commit config is handled gracefully."""  # ← stale

The implementation uses plain string matching (no YAML parsing), so "invalid YAML" is handled without any special logic. The docstring describes behavior that does not exist.

Remediation:

def test_precommit_no_matching_patterns(self, tmp_path):
    """Test that pre-commit config without conventional commit patterns fails."""

3. Hard merge conflict with PR #308

Attribute: conventional_commits (Tier 2)
Confidence: 93%
Score Impact: Functional regression if conflict resolved incorrectly
Location: src/agentready/assessors/stub_assessors.pyConventionalCommitsAssessor.assess()

Issue Details:
Both PR #308 and PR #310 modify the same line in ConventionalCommitsAssessor.assess():

# PR #310 keeps (unchanged from base):
has_commitlint = (repository.path / ".commitlintrc.json").exists()

# PR #308 replaces it with a 14-format list:
commitlint_configs = [".commitlintrc", ".commitlintrc.json", ".commitlintrc.yaml", ...]
has_commitlint = any((repository.path / cfg).exists() for cfg in commitlint_configs)

Git will produce a textual conflict regardless of merge order. The correct combined result is PR #308's expanded has_commitlint list + PR #310's pre-commit detection block.

Recommended merge order: Land PR #308 first (no test changes, smaller surface area), then rebase PR #310 on top. The single conflict block in has_commitlint resolves cleanly by keeping PR #308's list.

Remediation:

# After PR #308 merges:
git fetch origin main
git rebase origin/main
# Resolve conflict in stub_assessors.py: keep PR #308's commitlint_configs list
# + keep PR #310's precommit_config detection block below it

🟡 Major Issues (Confidence 80–89) — Manual Review Required

4. Missing encoding="utf-8" in read_text()

Attribute: code_quality (Tier 3)
Confidence: 85%
Score Impact: Silent false-negative risk on Windows with non-UTF-8 locale
Location: src/agentready/assessors/stub_assessors.py

Issue Details:
precommit_config.read_text() uses the system default encoding. On Windows with a non-UTF-8 locale (e.g., cp1252), this may fail to decode UTF-8 YAML content, raising UnicodeDecodeError — which is not caught by except OSError. The result would be an uncaught exception propagating up rather than graceful degradation.

# current — system-default encoding (UnicodeDecodeError not caught)
content = precommit_config.read_text()

# fix — explicit UTF-8 with graceful degradation
content = precommit_config.read_text(encoding="utf-8")
# also consider broadening: except (OSError, UnicodeDecodeError):

5. Substring matching creates false positive risk

Attribute: conventional_commits (Tier 2) — assessment accuracy
Confidence: 88%
Score Impact: Over-counting conventional_commits passes in edge cases
Location: src/agentready/assessors/stub_assessors.py

Issue Details:
The three patterns are matched anywhere in the file content, including in YAML comments:

# A file with this comment would produce a false positive:
# conventional-pre-commit was removed from this project in 2024

The patterns also overlap: "conventional-pre-commit" is a substring of "conventional-precommit-linter", so any() short-circuits on whichever appears first — functionally correct but potentially confusing.

Remediation (pragmatic, no new dependency):

# Filter out comment lines before matching
non_comment_lines = "\n".join(
    line for line in content.splitlines() if not line.strip().startswith("#")
)
has_precommit_conventional = any(pattern in non_comment_lines for pattern in patterns)

🔵 Minor Issues

6. Empty PR description

Confidence: 99%

All "Changes Made", "Related Issues", and "Testing" fields remain as template placeholders. This has been flagged in two prior reviews and remains unresolved. Please fill in at minimum the "Changes Made" section.


Summary

# Issue Attribute Tier Confidence Status
1 import os inside function code_quality 3 95% 🔴 Unresolved from prior review
2 Docstring mismatch inline_documentation 3 92% 🔴 Unresolved from prior review
3 Merge conflict with PR #308 conventional_commits 2 93% 🔴 Unresolved from prior review
4 Missing encoding="utf-8" code_quality 3 85% 🟡 New finding
5 Pattern false positives conventional_commits 2 88% 🟡 New finding
6 Empty PR description documentation 3 99% 🔵 Unresolved from prior review

Prior review items confirmed resolved ✅: except OSError (not bare except), remediation updated with Python-native pre-commit path, test renamed to test_precommit_no_matching_patterns.

  • Auto-Fix Candidates: 3 critical issues flagged for resolution
  • Manual Review: 2 major issues require human judgment
  • Net Score Improvement Potential: +3.0 points from conventional_commits fix → 83.0/100 (Gold)
  • AgentReady Assessment: Run agentready assess . after merge to verify score

🤖 Generated with Claude Code

If this review was useful, react with 👍. Otherwise, react with 👎.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AgentReady Code Review

Summary

PR #310 extends ConventionalCommitsAssessor to detect conventional commit tooling configured via .pre-commit-config.yaml, broadening coverage to Python-first repos. The implementation is largely correct and well-tested, but there are a few issues worth addressing before merge — including a latent conflict with PR #308 and a false-positive risk in the pattern-matching approach.


AgentReady Attribute Impact

Attribute Impact Notes
test_coverage ✅ Positive 10 new unit tests added covering all meaningful branches
pre_commit_hooks ✅ Positive Assessor now correctly recognises Python pre-commit ecosystem
type_annotations ➡️ Neutral No new unannotated functions introduced
code_complexity ⚠️ Minor concern Text-based pattern matching is fragile; see below

Estimated Score Impact: +1–2 points (improved detection reduces false-fail penalty for Python repos)


Issues Found

🔴 Critical

  • Latent merge conflict with PR #308 (stub_assessors.py:236): PR #310 retains has_commitlint = (repository.path / ".commitlintrc.json").exists(), checking only one config format. PR #308 (open) expands this to all 14 official commitlint formats by modifying the exact same lines. These two PRs will conflict at merge time and the fix from #308 will be lost if #310 merges first. Coordinate merge order or incorporate #308's commitlint list directly.

🟡 Warnings

  • False-positive risk from substring matching (stub_assessors.py:248–255): The check uses any(pattern in content for pattern in [...]). A comment like # We chose NOT to use conventional-precommit-linter would trigger a false positive pass. Prefer checking the id: field values after YAML-parsing the config, or at minimum narrow the pattern to id: conventional-precommit-linter (with the id: prefix) to reduce false matches.

  • Missing package.json commitlint field: Both this PR and the original code ignore package.json with a "commitlint" key, which is an officially supported config location per the commitlint docs. Low urgency but worth noting for completeness.

  • Non-standard import placement (tests/unit/test_assessors_stub.py:1049): import os is declared inside the test function test_precommit_file_permission_error. Move to the top of the file per PEP 8 / project style.

🟢 Suggestions

  • Misleading test name (test_precommit_no_matching_patterns): The docstring says "invalid YAML" but the test name says "no matching patterns." Since the implementation uses text search (not YAML parsing), invalid YAML isn't actually tested as an error condition — it just fails to match patterns. Rename to test_precommit_no_conventional_patterns_invalid_yaml or split into two distinct tests to clarify intent.

  • Cosmetic-only diff noise: Many of the changes in test_assessors_stub.py are pure formatting rewrites of multi-line string calls (e.g., write_text("""...""")write_text(\n"""..."""\n)). These inflate the diff significantly and obscure the functional changes. Separate formatting-only commits from functional ones, or omit reformatting unrelated code.

  • Pattern list coverage: The three patterns checked (conventional-precommit-linter, conventional-pre-commit, commitlint-pre-commit-hook) are reasonable, but jorisroovers/gitlint with conventional-commits type is also widely used in Python repos and worth adding.


Security Assessment

No security issues identified. The OSError catch on read_text() is appropriate; the code does not execute the config file contents.


Positive Changes

  • Strong test coverage with both happy-path and edge-case scenarios (permission error, empty config, invalid YAML, multiple tools configured simultaneously)
  • Remediation guidance updated to present both Python (pre-commit) and JS (commitlint + husky) options — improves actionability for Python-first users
  • Evidence message in the fail path correctly updated to reflect expanded detection scope

Recommendation

REQUEST_CHANGES — Coordinate with PR #308 to avoid losing the multi-format commitlint fix, and tighten the pattern-matching to reduce false positives. The test suite is solid; the implementation just needs the two targeted fixes above before merge.

@dbasunag dbasunag force-pushed the feature/conventional_commit branch from 0dbfa79 to 18742ad Compare February 19, 2026 20:03
@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

📈 Test Coverage Report

Branch Coverage
This PR 66.2%
Main 66.1%
Diff ✅ +0.1%

Coverage calculated from unit tests only

@github-actions
Copy link
Contributor

AgentReady Code Review — PR #310

Reviewer: Claude Code (AgentReady Review Agent)
Date: 2026-02-19
Score Impact: Positive — improves conventional_commits assessor accuracy for Python-first repos


Summary

This PR extends ConventionalCommitsAssessor to detect pre-commit-based conventional commit tooling (.pre-commit-config.yaml). This is a meaningful improvement since Python projects commonly enforce conventional commits via pre-commit hooks rather than JS-centric commitlint/husky. The test suite is thorough.


Strengths

  • Graceful error handling: OSError is caught when reading .pre-commit-config.yaml, preventing crashes on permission-restricted files.
  • Test coverage: 10 test cases covering the happy path, edge cases (empty config, unreadable file, pre-commit without conventional tools, multiple tools configured).
  • Improved remediation: The updated steps correctly present both Python and JS options, which benefits polyglot repositories.
  • No false negatives on existing behaviour: Existing .commitlintrc.json / .husky detection is unchanged.

Issues

Medium

1. Conflict with PR #308 (stub_assessors.py, line 239)

PR #308 replaces the single .commitlintrc.json check with a list of 14 supported commitlint config formats. This PR leaves .commitlintrc.json as the only format checked. If both PRs target main, whoever merges second will silently drop the other's fix. These need to be co-ordinated or combined before either merges.

2. String-based pattern matching can produce false positives (line 246–252)

has_precommit_conventional = any(
    pattern in content
    for pattern in [
        "conventional-precommit-linter",
        "conventional-pre-commit",
        "commitlint-pre-commit-hook",
    ]
)

A commented-out hook or a documentation string containing "conventional-pre-commit" would trigger a pass. Parsing the YAML and inspecting hooks[*].id / repos[*].repo is more reliable. Since pyyaml is likely already available (pre-commit itself depends on it), this is low-cost:

import yaml
try:
    data = yaml.safe_load(content)
    repos = data.get("repos", []) if isinstance(data, dict) else []
    for repo in repos:
        for hook in repo.get("hooks", []):
            if hook.get("id", "") in {"conventional-precommit-linter", ...}:
                has_precommit_conventional = True
except yaml.YAMLError:
    pass  # fall back to string matching or return False

Minor

3. Incomplete tool coverage

Common conventional-commit tools not detected:

  • commitizen (via cz-git, czg, or pyproject.toml [tool.commitizen])
  • git-conventional-commits hook id
  • repo: local hooks that call commitlint

These could be follow-up issues rather than blocking this PR.

4. Misleading test name (tests/unit/test_assessors_stub.py, line ~787)

def test_precommit_no_matching_patterns(self, tmp_path):
    """Test that invalid YAML in pre-commit config is handled gracefully."""

The test name says "no matching patterns" but the docstring says "invalid YAML". The file "invalid: yaml: content: [" is syntactically invalid YAML, but since the assessor uses string matching (not YAML parsing), it silently passes. This test is not actually exercising YAML error handling — it happens to pass because the content has no pattern matches. The name/docstring should be aligned. If the intent is to test invalid YAML resilience, a future YAML-parsing implementation would need this renamed to stay meaningful.

5. Permission test may fail as root (line ~895)

os.chmod(precommit_config, 0o000)

If tests run as root (e.g., some CI containers), chmod 000 does not prevent reads. Consider skipping this test when os.getuid() == 0 or marking it @pytest.mark.skipif(os.getuid() == 0, reason="root bypasses permissions").


AgentReady Attribute Compliance

Attribute Status Notes
conventional_commits Improved Assessor now covers Python-ecosystem tools
test_coverage Good 350 lines of new tests; all cases meaningful
code_quality Pass Follows existing patterns; no style deviations
type_annotations N/A Assessor file uses existing typing conventions

Recommendation

Request changes on the conflict with PR #308 before merging. The string-matching false-positive risk is medium severity and worth addressing. The other items are minor and acceptable as follow-up issues.

@dbasunag dbasunag force-pushed the feature/conventional_commit branch 2 times, most recently from 702ba37 to 18b8b4f Compare February 19, 2026 20:27
@github-actions
Copy link
Contributor

AgentReady Code Review — PR #310

Reviewed by: Claude Code (AgentReady Review Agent)
Date: 2026-02-19


Summary

This PR extends ConventionalCommitsAssessor to detect conventional commit tooling configured via .pre-commit-config.yaml, covering Python-ecosystem projects that use pre-commit hooks instead of JS-based commitlint/husky. It also adds 350 lines of unit tests — a substantial test contribution.


AgentReady Attribute Impact

Attribute Tier Direction Notes
conventional_commits 2 ✅ Improved detection Eliminates false negatives for Python repos using pre-commit
test_coverage 2 ✅ Positive 350 new test lines; well above project baseline
pre_commit_hooks 2 Neutral This assessor is separate, not affected

Score Impact: This fix directly benefits Python-first repositories (the primary AgentReady target audience) that use pre-commit with conventional commit hooks. These repos were incorrectly scoring 0/100 on conventional_commits (Tier 2, ~3% of total score).


Code Quality

Strengths:

  • Excellent test coverage: 10 targeted test cases covering happy paths, edge cases, and error handling
  • OSError catch is appropriate — covers both PermissionError and FileNotFoundError
  • The string-pattern approach keeps the assessor dependency-free (no pyyaml required)
  • Remediation now includes Python-ecosystem guidance alongside JS tooling

Issues:

1. False positive risk with string matching (medium)

The assessor reads the YAML file as raw text and does substring matching:

has_precommit_conventional = any(
    pattern in content
    for pattern in [
        "conventional-precommit-linter",
        "conventional-pre-commit",
        "commitlint-pre-commit-hook",
    ]
)

This will match on:

  • Comments (e.g., # TODO: add conventional-pre-commit)
  • Disabled/commented-out entries
  • Any YAML value mentioning these strings (e.g., a description: field)

A YAML-parse approach would be more accurate, though it adds a dependency. Alternatively, restricting the pattern match to known YAML structure (e.g., checking for the pattern only in repo: or id: lines) would reduce false positives without adding dependencies.

2. read_text() uses default encoding (low)

content = precommit_config.read_text()

Should be explicit: read_text(encoding="utf-8"). On some platforms, the default encoding differs. YAML files are always UTF-8.

3. Conflict with PR #308 (blocking for one of them)

This PR's diff is based on the original code (has_commitlint = (repository.path / ".commitlintrc.json").exists()), while PR #308 expands the commitlint format detection to 14 formats. These PRs should be merged in order or rebased. Whichever merges second will need to incorporate both changes:

4. Limited pre-commit hook coverage (minor)

The three patterns detected are reasonable but incomplete. Notable missing hooks:

  • jorisroovers/gitlint — popular Python alternative to commitlint
  • Direct use of commitizen via pre-commit

This is acceptable as a first pass — the attribute description says "≥80% of recent commits follow convention", so this is still an improvement over the status quo.

5. Test: test_precommit_no_matching_patterns naming mismatch (minor)

The test is named "no matching patterns" but the docstring says "invalid YAML" and uses malformed YAML as input. These are two different scenarios. Consider splitting or renaming.


Security

No security concerns. File access is limited to .pre-commit-config.yaml within the already-trusted repository.path. The OSError catch prevents crash-on-permission-denied. No subprocess calls or external network access.


Best Practices

  • ✅ Follows BaseAssessor pattern
  • ✅ Graceful degradation (OSError → fail, don't crash)
  • ✅ Test coverage meets and exceeds project standards
  • ✅ Remediation is actionable and includes both Python and JS options
  • ⚠️ Needs rebase/coordination with PR fix: Check for all official commitlint config file formats #308
  • ⚠️ String matching approach could produce false positives (recommend adding encoding="utf-8" and a comment noting the trade-off)

Specific Change Requests

# 1. Add encoding parameter
content = precommit_config.read_text(encoding="utf-8")

# 2. Consider adding a comment explaining the string-match trade-off
# Note: Using string matching rather than YAML parsing to avoid adding
# a pyyaml dependency. This may match commented-out entries.

Verdict

Approve with minor changes — This is a valuable contribution that addresses a real gap for Python repositories. The test suite is thorough and the logic is sound. Address the encoding parameter and coordinate with PR #308 before final merge. The false-positive risk from string matching is acceptable as a documented trade-off.

@github-actions
Copy link
Contributor

AgentReady Code Review

PR: fix(cli): check .pre-commit-config.yaml for conventional commit
Reviewer: Claude (automated review)


Summary

This PR extends ConventionalCommitsAssessor to detect conventional commit enforcement configured through .pre-commit-config.yaml. It adds YAML-first parsing with a string-matching fallback and ships a comprehensive test suite (10 test cases). This is a meaningful improvement for Python-first repositories that use pre-commit instead of JS tooling.

Overall: Strong implementation with good test coverage, but has a few false-positive risks and one CI-reliability concern.


AgentReady Attribute Compliance

Attribute Status Notes
Test Coverage ✅ Pass 10 test cases added, covers edge cases
Code Style ✅ Pass Consistent with existing patterns
Type Annotations ✅ Pass Inherited from base class patterns
Dependencies ✅ Pass pyyaml>=6.0 already in pyproject.toml
Conventional Commits ✅ Pass Commit message follows convention
Documentation ⚠️ Partial PR description "Changes Made" section is empty

Issues

🔴 Critical

None.

🟡 Important

1. gitlint detection may produce false positives

gitlint is included in conventional_patterns:

conventional_patterns = {
    "conventional-precommit-linter",
    "conventional-pre-commit",
    "commitlint-pre-commit-hook",
    "gitlint",          # ← concern
    "committed",        # ← concern
}

gitlint is a configurable commit message linter — it does not enforce conventional commits by default. A repo using gitlint with custom rules (e.g., enforcing ticket numbers in subject lines) would receive a pass for this attribute even though it doesn't enforce conventional commits. The same applies to committed.

Recommendation: Remove gitlint and committed from the pattern set, or add documentation explaining this is an intentional heuristic (and lower confidence accordingly).

2. String fallback can match comments

The YAML error fallback uses content-level string matching:

except yaml.YAMLError:
    has_precommit_conventional = any(
        pattern in content for pattern in conventional_patterns
    )

A .pre-commit-config.yaml containing a comment like # conventional-pre-commit is NOT configured here would incorrectly match. Consider stripping comment lines before string matching in the fallback, or simply returning False on YAML parse failure (the fallback provides minimal additional value for genuinely malformed files).

3. Permission error test is a no-op when running as root

def test_precommit_file_permission_error(self, tmp_path):
    os.chmod(precommit_config, 0o000)
    # "Note: This test is a no-op when run as root"

CI containers often run as root, making this test silently pass regardless of the OSError handling code. The test neither validates the production path nor fails — it gives false confidence in coverage. Recommend:

import pytest
@pytest.mark.skipif(os.getuid() == 0, reason="chmod has no effect as root")
def test_precommit_file_permission_error(self, tmp_path):
    ...

🟢 Suggestions

4. Conflict with PR #308

Both this PR and PR #308 modify the same assess() method. PR #308 adds 13 new commitlint config file formats (.commitlintrc.yaml, .commitlintrc.ts, etc.) that this PR does not include. Whichever merges second will need to rebase and incorporate the other's changes.

5. Missing package.json commitlint detection

A common pattern in Node.js monorepos: commitlint config lives in package.json under a "commitlint" key. Neither this PR nor #308 addresses this. Consider a follow-up.

6. Empty PR description

The "Changes Made" section has three empty bullets (-\n-\n-). Please fill this in before marking the PR ready — it helps reviewers and makes the git history more useful.


Strengths

  • YAML-first parsing avoids false positives from comments in well-formed files — good design choice.
  • Pattern matching against repo_url (the repo: key) is the right approach; hook IDs are not reliably named.
  • Remediation steps now cover both Python and JS ecosystems — significantly more useful than the original JS-only guidance.
  • The test suite is genuinely comprehensive: covers all detection methods, empty YAML, invalid YAML, permission errors, and multi-tool configurations.
  • OSError handling for unreadable files is a good defensive pattern.

Verdict

Request changes — small issues to address before merge:

  1. Remove or document the gitlint/committed false-positive risk
  2. Add pytest.mark.skipif(os.getuid() == 0, ...) to the permission error test
  3. Fill in the PR description

The core logic and test coverage are solid.

@dbasunag dbasunag force-pushed the feature/conventional_commit branch from 18b8b4f to e6d0d86 Compare February 19, 2026 21:22
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AgentReady Code Review — PR #310

fix(cli): check .pre-commit-config.yaml for conventional commit

Summary

This PR meaningfully expands ConventionalCommitsAssessor to detect conventional commit tooling in Python-first workflows — specifically package.json commitlint config and pre-commit hooks. The YAML-based parsing approach (vs. naive string matching) is the right call and avoids false positives from comments. Test coverage is excellent at 14 new cases. One required change: this PR should also incorporate the full commitlint config format list from PR #308 to avoid a regression.


AgentReady Attribute Compliance

Attribute affected: conventional_commits (Tier 2, weight ~0.03)

Aspect Assessment
Fix correctness ✅ Correct detection logic for both new paths
YAML parsing approach ✅ Correctly uses yaml.safe_load not string matching
Error handling JSONDecodeError, OSError, YAMLError all caught
pyyaml dependency ✅ Already in pyproject.toml (pyyaml>=6.0)
Test coverage ✅ 14 new test cases covering all key paths
Remediation quality ✅ Improved with language-specific options (Python vs JS)
Score impact conventional_commits 0 → 100 for pre-commit users (+~0.9 overall)

Security

No security issues found.

  • File reads are wrapped in OSError exception handlers
  • yaml.safe_load() used (not yaml.load() — avoids arbitrary code execution)
  • No subprocess calls, no user-controlled execution paths

Code Quality

Strengths:

  • YAML-parsed URL matching is more robust than string/regex matching against raw file content
  • yaml.safe_load() is correctly chosen over yaml.load()
  • The conventional_patterns set accurately avoids generic linters (gitlint, committed) that don't enforce conventional format by default — the inline comment explains this decision well
  • test_precommit_file_permission_error with @pytest.mark.skipif(os.getuid() == 0, ...) is a thoughtful edge case

Issues:

1. [Required] Missing commitlint config formats from PR #308

This PR only checks .commitlintrc.json, but PR #308 identified 14 valid formats (.commitlintrc, .commitlintrc.yaml, .commitlintrc.ts, commitlint.config.js, etc.). If this PR merges without those additions, false negatives for those formats remain. Either rebase on top of PR #308 or incorporate the full list.

2. [Minor] Redundant assignment in except yaml.YAMLError block

has_precommit_conventional is already initialized to False before the try block. The explicit reset inside except yaml.YAMLError is dead code. Remove it or replace with pass.

3. [Minor] Misleading test docstring

test_precommit_invalid_yaml_fallback says 'falls back to string matching' but the implementation does not fall back to string matching — it fails gracefully with False. Update the docstring.

4. [Non-blocking] conventional_patterns as a class-level constant

The patterns set is redefined inside assess() on every call. Since it is a fixed constant, a class-level attribute would be cleaner.

5. [Non-blocking] Pass evidence doesn't identify which tool was detected

All pass paths return 'Commit linting configured' regardless of detection method. Including the specific detection path in evidence would help with diagnostics.


Best Practices

  • ✅ Library-first: no global state introduced
  • ✅ Strategy pattern preserved
  • ✅ Graceful degradation: missing/unreadable files do not crash
  • ✅ Conventional commit style in PR title (fix(cli):)
  • ✅ Tests confirm behavior, not just execution

Required Changes

  1. Incorporate the full commitlint config format list (14 formats from PR #308) to avoid false negatives for .commitlintrc.yaml, .commitlintrc.ts, commitlint.config.js, etc.

Suggestions (non-blocking)

  • Remove redundant has_precommit_conventional = False in except yaml.YAMLError
  • Fix misleading docstring in test_precommit_invalid_yaml_fallback
  • Extract conventional_patterns to a class-level constant
  • Add evidence specificity to identify which config method was detected

Verdict

REQUEST CHANGES — The core logic is sound and the test coverage is a significant improvement. One required change before merge: incorporate the full commitlint config format list from PR #308. The remaining items are minor/non-blocking.

@kami619
Copy link
Collaborator

kami619 commented Feb 20, 2026

@dbasunag with #308 merged, you might want to see the impact of the conflicts and resolve them.

@github-actions
Copy link
Contributor

AgentReady Code Review — PR #310

fix(cli): check .pre-commit-config.yaml for conventional commit
src/agentready/assessors/stub_assessors.py + tests


Summary

Good implementation that expands conventional commit detection to Python-ecosystem tooling. Proper YAML parsing (avoiding false positives from string matching), solid error handling, and thorough test coverage. A few things to address.


Issues

Medium

Misleading test docstring for test_precommit_invalid_yaml_fallback

The docstring says "falls back to string matching" but the implementation returns False (no fallback). Either update the implementation to add a fallback or correct the docstring to say "fails gracefully without crashing".

Tests in tests/unit/ spawn git init subprocesses

Tests using subprocess.run(["git", "init"], ...) and real filesystem operations are integration tests by convention. They work correctly but are misclassified. Consider moving to tests/integration/ or mocking the filesystem with unittest.mock.

Minor

conventional_patterns set is incomplete

Notable missing tools:

  • commitizen-tools/commitizen (popular Python tool)
  • alessandrojcm/commitlint-pre-commit-hook (common commitlint hook for pre-commit)
  • jorisroovers/gitlint is acknowledged as excluded (good), but the comment could link to why: gitlint requires explicit types: [commit-msg] AND conventional rule config to enforce the convention

"commitlint" in package_data checks top-level keys only

This correctly detects {"commitlint": {...}} in package.json but would not detect {"scripts": {"commitlint": "..."}}. This is intentional (commitlint as a script is not a configuration) — worth a brief comment confirming the intent.

No detection of commitizen in pyproject.toml or setup.cfg

Commitizen is configured in [tool.commitizen] in pyproject.toml. Python projects are a primary AgentReady target and this is a common configuration pattern.


What's Good

  • YAML parsing over string matching is the right approach — avoids false positives from comments
  • Graceful degradation: OSError, json.JSONDecodeError, and yaml.YAMLError all handled
  • Comment explaining why gitlint/committed are excluded is valuable documentation
  • 15 test cases covering all detection paths including edge cases (malformed YAML, permission errors)
  • pytest.mark.skipif(os.getuid() == 0, ...) for the chmod test is thoughtful
  • Remediation updated with both Python (pre-commit) and JS (commitlint+husky) options

Attribute Impact

Attribute Impact
conventional_commits Positive — detection rate improves for Python-ecosystem repos
test_coverage Positive — 15 new tests

Verdict: Approve with minor fixes. Fix the misleading docstring before merge. Consider adding commitizen detection as a follow-up issue rather than blocking this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants