Skip to content

feat: Implement InlineDocumentationAssessor #77

@jeremyeder

Description

@jeremyeder

feat: Implement InlineDocumentationAssessor

Attribute Definition

Attribute ID: inline_documentation (Attribute #14 - Tier 2)

Definition: Function, class, and module-level documentation using language-specific conventions (Python docstrings, JSDoc/TSDoc).

Why It Matters: Type hints significantly improve LLM experience. Well-typed code directs LLMs into latent space regions corresponding to higher code quality—similar to how LaTeX-formatted math problems get better results.

Impact on Agent Behavior:

  • Understanding function purpose without reading implementation
  • Better parameter validation suggestions
  • More accurate return type predictions
  • Improved test generation
  • Enhanced refactoring confidence

Measurable Criteria:

  • All public functions/methods have docstrings
  • Docstrings include: description, parameters, return values, exceptions, examples
  • Python: PEP 257 compliant
  • JavaScript/TypeScript: JSDoc or TSDoc
  • Coverage: >80% of public API documented
  • Tools: pydocstyle, documentation-js

Implementation Requirements

File Location: src/agentready/assessors/documentation.py

Class Name: InlineDocumentationAssessor

Tier: 2 (Critical)

Default Weight: 0.03 (3% of total score)

Assessment Logic

Scoring Approach: Language-specific AST parsing for accurate detection

Evidence to Check (Python):

  1. Parse Python files with AST
  2. Count public functions/classes (not starting with _)
  3. Check for docstring presence (ast.get_docstring())
  4. Calculate coverage percentage
  5. Optionally check docstring quality (has Args, Returns, etc.)

Evidence to Check (JavaScript/TypeScript):

  1. Check for JSDoc comments (/** ... */)
  2. Parse with regex or esprima/babel parser
  3. Count functions with JSDoc vs. total functions
  4. Calculate coverage percentage

Scoring Logic:

documented_functions = count_functions_with_docstrings(files)
total_public_functions = count_public_functions(files)

coverage_percent = (documented_functions / total_public_functions) * 100

score = self.calculate_proportional_score(
    measured_value=coverage_percent,
    threshold=80.0,
    higher_is_better=True,
)

status = "pass" if score >= 75 else "fail"

Code Pattern to Follow

Reference: TypeAnnotationsAssessor in code_quality.py

Pattern:

  1. Check is_applicable() for supported languages
  2. Use AST parsing for Python (similar to TypeAnnotationsAssessor)
  3. Walk AST tree to find function/class definitions
  4. Check for docstring using ast.get_docstring(node)
  5. Calculate proportional score based on coverage

Example Finding Responses

Pass (Score: 88)

Finding(
    attribute=self.attribute,
    status="pass",
    score=88.0,
    measured_value="88%",
    threshold="≥80%",
    evidence=[
        "Documented functions: 45/51",
        "Coverage: 88.2%",
        "All public classes have docstrings",
        "6 private functions missing docstrings (acceptable)",
    ],
    remediation=None,
    error_message=None,
)

Fail (Score: 42)

Finding(
    attribute=self.attribute,
    status="fail",
    score=42.0,
    measured_value="34%",
    threshold="≥80%",
    evidence=[
        "Documented functions: 18/53",
        "Coverage: 34.0%",
        "Many public functions lack docstrings",
        "No module-level documentation",
    ],
    remediation=self._create_remediation(),
    error_message=None,
)

Not Applicable

Finding.not_applicable(
    self.attribute,
    reason="No Python or JavaScript code found in repository"
)

Registration

Add to src/agentready/services/scanner.py in create_all_assessors():

from ..assessors.documentation import (
    CLAUDEmdAssessor,
    READMEAssessor,
    ConciseDocumentationAssessor,
    InlineDocumentationAssessor,  # Add this import
)

def create_all_assessors() -> List[BaseAssessor]:
    return [
        # ... existing assessors ...
        InlineDocumentationAssessor(),  # Add this line
    ]

Testing Guidance

Test File: tests/unit/test_assessors_documentation.py

Test Cases to Add:

  1. test_inline_docs_pass_high_coverage: Python files with >80% docstring coverage
  2. test_inline_docs_fail_low_coverage: Python files with <50% coverage
  3. test_inline_docs_partial_score: 65% coverage (proportional score)
  4. test_inline_docs_ignore_private: Private functions (starting with _) not required
  5. test_inline_docs_no_code: Empty repository returns not_applicable

Note: AgentReady codebase has good docstring coverage in assessors/ and models/, should score well.

Dependencies

External Tools: None (uses AST parsing)

Python Standard Library:

  • ast for parsing Python code
  • ast.get_docstring() for extracting docstrings
  • pathlib.Path for file iteration

Optional Enhancement: Use pydocstyle for quality checking (not just presence)

Remediation Steps

def _create_remediation(self) -> Remediation:
    return Remediation(
        summary="Add docstrings to public functions and classes",
        steps=[
            "Identify functions/classes without docstrings",
            "Add PEP 257 compliant docstrings for Python",
            "Add JSDoc comments for JavaScript/TypeScript",
            "Include: description, parameters, return values, exceptions",
            "Add examples for complex functions",
            "Run pydocstyle to validate docstring format",
        ],
        tools=["pydocstyle", "jsdoc"],
        commands=[
            "# Install pydocstyle",
            "pip install pydocstyle",
            "",
            "# Check docstring coverage",
            "pydocstyle src/",
            "",
            "# Generate documentation",
            "pip install sphinx",
            "sphinx-apidoc -o docs/ src/",
        ],
        examples=[
            '''# Python - Good docstring
def calculate_discount(price: float, discount_percent: float) -> float:
    """
    Calculate discounted price.

    Args:
        price: Original price in USD
        discount_percent: Discount percentage (0-100)

    Returns:
        Discounted price

    Raises:
        ValueError: If discount_percent not in 0-100 range

    Example:
        >>> calculate_discount(100.0, 20.0)
        80.0
    """
    if not 0 <= discount_percent <= 100:
        raise ValueError("Discount must be 0-100")
    return price * (1 - discount_percent / 100)
''',
            '''// JavaScript - Good JSDoc
/**
 * Calculate discounted price
 *
 * @param {number} price - Original price in USD
 * @param {number} discountPercent - Discount percentage (0-100)
 * @returns {number} Discounted price
 * @throws {Error} If discountPercent not in 0-100 range
 * @example
 * calculateDiscount(100.0, 20.0)
 * // Returns: 80.0
 */
function calculateDiscount(price, discountPercent) {
    if (discountPercent < 0 || discountPercent > 100) {
        throw new Error("Discount must be 0-100");
    }
    return price * (1 - discountPercent / 100);
}
''',
        ],
        citations=[
            Citation(
                source="Python.org",
                title="PEP 257 - Docstring Conventions",
                url="https://peps.python.org/pep-0257/",
                relevance="Python docstring standards",
            ),
            Citation(
                source="TypeScript",
                title="TSDoc Reference",
                url="https://tsdoc.org/",
                relevance="TypeScript documentation standard",
            ),
        ],
    )

Implementation Notes

  1. AST Parsing: Use ast.parse() and ast.walk() to traverse code
  2. Docstring Extraction: Use ast.get_docstring(node) which returns None if missing
  3. Public vs Private: Exclude functions/classes starting with _
  4. Module Docstrings: Also check module-level docstrings (ast.get_docstring(tree))
  5. File Filtering: Use git ls-files *.py to get tracked Python files
  6. Error Handling: Skip files with syntax errors gracefully
  7. Edge Cases: Handle empty functions, property decorators, async functions

Quality Checks (Optional Enhancement):

  • Check if docstring has "Args:" section
  • Check if docstring has "Returns:" section
  • Check if docstring has example
  • Use pydocstyle for PEP 257 compliance

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions