-
Notifications
You must be signed in to change notification settings - Fork 29
Description
AgentReady Implementation Simplification Plan
Date: 2025-11-23
Goal: Keep all features, reduce implementation complexity through refactoring
Target: -30% LOC reduction (~1,880 lines) without removing features
Executive Summary
AgentReady has grown to 64 modules and ~6,300 LOC across 8 commands. While well-architected, it carries complexity debt from:
- Duplicated validation/security patterns across modules
- Over-engineered abstractions in some areas
- Scattered service initialization logic
- Template duplication across languages
- Test setup duplication
This plan reduces complexity through refactoring, not feature removal.
Current State Assessment
Codebase Metrics
- 64 Python modules across 7 packages
- 15 Jinja2 templates for bootstrap
- 169 test cases across 39 test files
- 8 CLI commands: assess, bootstrap, learn, align, assess-batch, demo, research, repomix
- 5 output formats: HTML, Markdown, JSON, CSV, Multi-HTML
Complexity Hotspots
- Scattered security validation - path validation duplicated across 5+ modules (~125 lines each)
- Reporter duplication - 5 reporters share 40% common code
- Service initialization - duplicated dependency injection patterns
- Config validation - 125 lines of manual validation in CLI
- Bootstrap template duplication - similar patterns across language templates
- Test fixture duplication - 169 tests with significant setup overlap
Phase 1: Consolidate Duplicated Patterns (Week 1-2)
1. Centralize Security Validation
Problem:
# cli/main.py (125 lines of validation)
# reporters/html.py (path sanitization)
# services/bootstrap.py (path validation)
# utils/privacy.py (path sanitization)
# models/repository.py (path validation)All modules duplicate path traversal checks, XSS prevention, and input validation.
Solution:
Create src/agentready/utils/security.py:
"""Centralized security validation."""
from pathlib import Path
from typing import Any
def validate_path(
path: str | Path,
allow_system_dirs: bool = False,
must_exist: bool = False
) -> Path:
"""Validate and sanitize file paths.
Args:
path: Path to validate
allow_system_dirs: Allow /etc, /usr, /bin, etc.
must_exist: Raise if path doesn't exist
Returns:
Resolved, validated Path
Raises:
ValueError: If path is invalid or unsafe
"""
# Path traversal prevention
# System directory checks
# Existence validation
# Return sanitized Path
def validate_config_dict(data: dict, schema: dict) -> dict:
"""Validate configuration dictionary against schema.
Args:
data: Config data to validate
schema: JSON schema or type specification
Returns:
Validated config dict
Raises:
ValueError: If validation fails
"""
# Type checking
# Unknown key rejection
# Required field validation
# Return validated dict
def sanitize_for_html(text: str) -> str:
"""Sanitize text for HTML output (XSS prevention).
Args:
text: Unsafe text
Returns:
HTML-safe text
"""
# XSS prevention
# Entity escaping
# Return safe text
def sanitize_for_json(text: str) -> str:
"""Sanitize text for JSON output.
Args:
text: Unsafe text
Returns:
JSON-safe text
"""
# JSON injection prevention
# Return safe textRefactor locations:
cli/main.py- replace 125-line validation withvalidate_config_dict()callreporters/html.py- replace XSS code withsanitize_for_html()services/bootstrap.py- replace path checks withvalidate_path()- All modules doing path validation
Impact:
- -200 LOC (net: +100 in utils, -300 in duplicated code)
- Improved consistency (single source of truth for security)
- Easier to audit (one module vs scattered code)
2. Create Shared Reporter Base Class
Problem:
All 5 reporters duplicate:
- Path handling (output directory, file naming)
- Metadata formatting (timestamp, repository name)
- File writing boilerplate
- Error handling
# Each reporter has ~40% duplicated code:
# - _ensure_output_dir()
# - _generate_filename()
# - _format_metadata()
# - _write_file()Solution:
Create src/agentready/reporters/base.py:
"""Base class for all reporters."""
from abc import ABC, abstractmethod
from pathlib import Path
from agentready.models import Assessment
class BaseReporter(ABC):
"""Base reporter with common functionality."""
def __init__(self, output_dir: Path):
self.output_dir = output_dir
def generate_report(self, assessment: Assessment) -> Path:
"""Template method for report generation."""
self._ensure_output_dir()
content = self._generate_content(assessment)
filepath = self._write_file(content, assessment)
return filepath
@abstractmethod
def _generate_content(self, assessment: Assessment) -> str | bytes:
"""Subclass implements format-specific generation."""
pass
@abstractmethod
def _get_file_extension(self) -> str:
"""Return file extension (.html, .md, .json, .csv)."""
pass
def _ensure_output_dir(self) -> None:
"""Create output directory if needed."""
# Common implementation
def _generate_filename(self, assessment: Assessment) -> str:
"""Generate filename with timestamp."""
# Common implementation
def _write_file(self, content: str | bytes, assessment: Assessment) -> Path:
"""Write content to file."""
# Common implementationRefactor reporters:
# html.py
class HTMLReporter(BaseReporter):
def _generate_content(self, assessment: Assessment) -> str:
# HTML-specific logic only
def _get_file_extension(self) -> str:
return ".html"
# markdown.py
class MarkdownReporter(BaseReporter):
def _generate_content(self, assessment: Assessment) -> str:
# Markdown-specific logic only
def _get_file_extension(self) -> str:
return ".md"Impact:
- -300 LOC (remove duplicated code from 5 reporters)
- DRY principle applied
- Easier to add new reporters (just implement
_generate_content())
3. Consolidate Service Initialization
Problem:
Services duplicate dependency injection patterns:
# scanner.py
class Scanner:
def __init__(self, config):
self.assessors = self._init_assessors()
self.language_detector = LanguageDetector()
self.repository_manager = RepositoryManager()
# learning_service.py
class LearningService:
def __init__(self, config):
self.pattern_extractor = PatternExtractor()
self.llm_enricher = self._init_llm() if api_key else NoneEvery service initializes dependencies in __init__ with duplicated logic.
Solution:
Create src/agentready/services/registry.py:
"""Service registry and dependency injection."""
from typing import Type, TypeVar, Callable
T = TypeVar('T')
class ServiceRegistry:
"""Simple DI container for services."""
def __init__(self):
self._services = {}
self._factories = {}
def register(self, interface: Type[T], factory: Callable[[], T]):
"""Register a service factory."""
self._factories[interface] = factory
def get(self, interface: Type[T]) -> T:
"""Get or create service instance (singleton)."""
if interface not in self._services:
factory = self._factories[interface]
self._services[interface] = factory()
return self._services[interface]
def clear(self):
"""Clear all services (for testing)."""
self._services.clear()
# Global registry
_registry = ServiceRegistry()
def get_service(interface: Type[T]) -> T:
"""Get service from global registry."""
return _registry.get(interface)Usage in services:
# scanner.py
from .registry import get_service
class Scanner:
def __init__(self):
self.language_detector = get_service(LanguageDetector)
self.repository_manager = get_service(RepositoryManager)
# No manual initialization neededImpact:
- -150 LOC (remove duplicated initialization across 13 services)
- Clearer service lifecycle
- Easier testing (can inject mocks via registry)
Phase 2: Simplify Over-Engineered Areas (Week 3-4)
4. Use Pydantic for Config Validation
Problem:
cli/main.py has 125 lines of manual config validation:
# Manually check every field type
if not isinstance(config.get("weights"), dict):
raise ValueError(...)
# Manually reject unknown keys
allowed_keys = {"weights", "theme", "excluded_attributes"}
for key in config:
if key not in allowed_keys:
raise ValueError(...)
# Manually validate nested structures
for attr_id, weight in config["weights"].items():
if not isinstance(weight, (int, float)):
raise ValueError(...)Solution:
Replace with Pydantic models in src/agentready/models/config.py:
"""Configuration models with validation."""
from pydantic import BaseModel, Field, field_validator
class ThemeConfig(BaseModel):
"""Theme configuration."""
name: str = "default"
primary_color: str = "#1a56db"
secondary_color: str = "#7c3aed"
# Auto-validates types, provides JSON schema
class AgentReadyConfig(BaseModel):
"""Main configuration model."""
weights: dict[str, float] = Field(default_factory=dict)
theme: ThemeConfig = Field(default_factory=ThemeConfig)
excluded_attributes: list[str] = Field(default_factory=list)
output_dir: str = ".agentready"
@field_validator("weights")
def validate_weights(cls, v):
"""Ensure weights are 0-100."""
for attr_id, weight in v.items():
if not 0 <= weight <= 100:
raise ValueError(f"Weight for {attr_id} must be 0-100")
return v
class Config:
extra = "forbid" # Reject unknown keys
# Usage
config = AgentReadyConfig.model_validate(yaml_data)Impact:
- -100 LOC (125 lines manual validation → 25 lines Pydantic models)
- Get JSON schema generation for free
- Better error messages
- Type hints for IDE autocomplete
5. Reduce Template Complexity via Inheritance
Problem:
Bootstrap has 15 Jinja2 templates with significant duplication:
templates/bootstrap/
├── python/
│ ├── github-actions-tests.yml # 80% similar to js/github-actions-tests.yml
│ ├── github-actions-security.yml # 80% similar to js/github-actions-security.yml
│ ├── pre-commit-config.yaml # Language-specific hooks
├── javascript/
│ ├── github-actions-tests.yml
│ ├── github-actions-security.yml
│ ├── pre-commit-config.yaml
├── go/
│ └── ...
Solution:
Use Jinja2 template inheritance:
templates/bootstrap/
├── _base/
│ ├── github-actions-tests.yml.j2 # Base template with blocks
│ ├── github-actions-security.yml.j2
│ └── pre-commit-config.yaml.j2
├── python/
│ ├── github-actions-tests.yml.j2 # {% extends "_base/..." %} + Python-specific blocks
│ └── pre-commit-config.yaml.j2
├── javascript/
│ ├── github-actions-tests.yml.j2 # {% extends "_base/..." %} + JS-specific blocks
│ └── pre-commit-config.yaml.j2
Base template example:
{# _base/github-actions-tests.yml.j2 #}
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
{% block setup_environment %}
{# Language-specific setup #}
{% endblock %}
{% block install_dependencies %}
{# Language-specific install #}
{% endblock %}
{% block run_tests %}
{# Language-specific test command #}
{% endblock %}Python template:
{# python/github-actions-tests.yml.j2 #}
{% extends "_base/github-actions-tests.yml.j2" %}
{% block setup_environment %}
- uses: actions/setup-python@v5
with:
python-version: '3.12'
{% endblock %}
{% block install_dependencies %}
- run: pip install -e ".[dev]"
{% endblock %}
{% block run_tests %}
- run: pytest --cov
{% endblock %}Impact:
- 15 templates → 8 templates (1 base set + 7 language-specific overrides)
- Easier to update common patterns (edit base template once)
- Less duplication
6. Simplify Theme System
Problem:
Current theme system has 84 config values (14 RGB colors × 6 presets):
# .agentready-config.yaml
theme:
name: custom
primary_color: "#1a56db"
secondary_color: "#7c3aed"
background_color: "#ffffff"
text_color: "#1f2937"
border_color: "#e5e7eb"
success_color: "#10b981"
warning_color: "#f59e0b"
error_color: "#ef4444"
info_color: "#3b82f6"
# ... 5 more colorsUsers rarely customize themes beyond dark/light mode.
Solution:
Use CSS variables + algorithmic color generation:
# reporters/themes.py
from dataclasses import dataclass
@dataclass
class Theme:
"""Theme defined by 2-3 base colors."""
name: str
primary: str # Main brand color
background: str # Light or dark background
def to_css_vars(self) -> dict[str, str]:
"""Generate full color palette from base colors."""
# Use color theory to derive:
# - secondary (complementary to primary)
# - success/warning/error (semantic colors)
# - text (contrast-safe against background)
# - borders (background + 10% brightness)
return {
"--primary": self.primary,
"--secondary": self._derive_secondary(self.primary),
"--background": self.background,
"--text": self._derive_text(self.background),
"--success": "#10b981", # Universal semantic colors
"--warning": "#f59e0b",
"--error": "#ef4444",
# ...
}
# Presets
THEMES = {
"default": Theme("default", "#1a56db", "#ffffff"),
"dark": Theme("dark", "#3b82f6", "#1f2937"),
}Impact:
- -150 LOC in theme system
- Config: 14 colors → 2-3 colors
- Easier theme creation (just pick primary + background)
- Still generates full palette
Phase 3: Better Abstractions (Week 5-6)
7. Create Assessor Registry Pattern
Problem:
cli/main.py manually imports and instantiates all assessors:
from agentready.assessors.documentation import (
ClaudeMdAssessor,
ReadmeAssessor,
ApiDocsAssessor,
# ... 10 more
)
from agentready.assessors.code_quality import (
TypeAnnotationsAssessor,
LinterConfigAssessor,
# ... 8 more
)
# Then manually create instances
assessors = [
ClaudeMdAssessor(),
ReadmeAssessor(),
ApiDocsAssessor(),
# ... 20+ more
]Solution:
Auto-discovery with decorator:
# assessors/base.py
_assessor_registry = {}
def register_assessor(attribute_id: str):
"""Decorator to auto-register assessors."""
def decorator(cls):
_assessor_registry[attribute_id] = cls
return cls
return decorator
def get_all_assessors() -> list[BaseAssessor]:
"""Get instances of all registered assessors."""
return [cls() for cls in _assessor_registry.values()]
# Usage in assessor files
@register_assessor("claude_md_file")
class ClaudeMdAssessor(BaseAssessor):
# Implementation
pass
@register_assessor("readme_present")
class ReadmeAssessor(BaseAssessor):
# Implementation
pass
# In cli/main.py - just import assessor modules, registry auto-populates
from agentready.assessors import documentation, code_quality, testing, structure
assessors = get_all_assessors() # Auto-discovered!Impact:
- -80 LOC in main.py (remove manual imports/instantiation)
- Easier to add assessors (just define class with decorator)
- No manual registry maintenance
8. Unify Batch and Single Assessment Paths
Problem:
batch_scanner.py is a thin wrapper:
# services/batch_scanner.py (200 lines)
class BatchScanner:
def scan_repositories(self, repo_paths: list[Path]):
results = []
for repo_path in repo_paths:
scanner = Scanner() # Create single scanner
result = scanner.scan(repo_path)
results.append(result)
return resultsThis is just a for-loop over single scanner.
Solution:
Make Scanner handle both:
# services/scanner.py
class Scanner:
def scan(self, repo_paths: Path | list[Path]) -> Assessment | list[Assessment]:
"""Scan single repo or batch."""
if isinstance(repo_paths, Path):
return self._scan_single(repo_paths)
else:
return [self._scan_single(p) for p in repo_paths]
def _scan_single(self, repo_path: Path) -> Assessment:
# Existing single-repo logic
passImpact:
- -200 LOC (delete
batch_scanner.pyentirely) - Single code path = easier maintenance
- Same API for CLI (scanner handles single vs batch internally)
9. Consolidate Research Service Operations
Problem:
Research operations split across 2 files:
# services/research_loader.py (150 lines)
class ResearchLoader:
def load_report(self) -> dict: ...
def validate_schema(self) -> bool: ...
# services/research_formatter.py (100 lines)
class ResearchFormatter:
def format_for_display(self, data: dict) -> str: ...
def format_citations(self, citations: list) -> str: ...These are tightly coupled (formatter needs loader data).
Solution:
Merge into single service:
# services/research_service.py (200 lines)
class ResearchService:
"""Unified research report operations."""
def load_report(self) -> dict:
# Loading logic
pass
def validate_schema(self) -> bool:
# Validation logic
pass
def format_for_display(self, data: dict) -> str:
# Formatting logic
pass
def format_citations(self, citations: list) -> str:
# Citation formatting
passImpact:
- -100 LOC (remove overhead of 2 separate classes)
- Clearer module boundaries (all research ops in one place)
- 13 services → 12 services
Phase 4: Improve Test Architecture (Week 7)
10. Create Shared Test Fixtures
Problem:
169 tests duplicate fixture setup:
# tests/test_scanner.py
def test_scan():
# Create temp repo
repo_path = tmp_path / "test-repo"
repo_path.mkdir()
(repo_path / "README.md").write_text("# Test")
(repo_path / "CLAUDE.md").write_text("# Context")
# ... 20 lines of setup
scanner = Scanner()
result = scanner.scan(repo_path)
assert result.score > 0
# tests/test_learning.py
def test_learn():
# Create temp repo (same setup!)
repo_path = tmp_path / "test-repo"
repo_path.mkdir()
(repo_path / "README.md").write_text("# Test")
(repo_path / "CLAUDE.md").write_text("# Context")
# ... 20 lines of setup
learning_service = LearningService()
# ...Solution:
Shared fixtures in tests/conftest.py:
# tests/conftest.py
import pytest
from pathlib import Path
@pytest.fixture
def sample_repo(tmp_path):
"""Create a sample repository with common files."""
repo_path = tmp_path / "test-repo"
repo_path.mkdir()
# Standard files
(repo_path / "README.md").write_text("# Test Project")
(repo_path / "CLAUDE.md").write_text("# Context")
(repo_path / ".gitignore").write_text("*.pyc\n__pycache__/")
# Python files
src_dir = repo_path / "src"
src_dir.mkdir()
(src_dir / "__init__.py").write_text("")
(src_dir / "main.py").write_text("def main(): pass")
# Tests
test_dir = repo_path / "tests"
test_dir.mkdir()
(test_dir / "test_main.py").write_text("def test_main(): pass")
return repo_path
@pytest.fixture
def sample_assessment(sample_repo):
"""Create a sample assessment result."""
from agentready.services.scanner import Scanner
scanner = Scanner()
return scanner.scan(sample_repo)
@pytest.fixture
def mock_anthropic_client():
"""Mock Anthropic API client."""
from unittest.mock import Mock
client = Mock()
client.messages.create.return_value = Mock(
content=[Mock(text='{"skill_description": "Test skill"}')]
)
return clientUsage:
# tests/test_scanner.py
def test_scan(sample_repo):
scanner = Scanner()
result = scanner.scan(sample_repo)
assert result.score > 0
# No setup needed!
# tests/test_learning.py
def test_learn(sample_assessment):
learning_service = LearningService()
skills = learning_service.extract_patterns(sample_assessment)
assert len(skills) > 0
# No setup needed!Impact:
- -400 LOC in tests (remove duplicated setup)
- Faster test execution (pytest caches fixtures)
- More maintainable tests
11. Reduce Integration Test Complexity
Problem:
Some integration tests spawn entire workflows when unit tests would suffice:
# tests/integration/test_full_workflow.py (300 lines)
def test_assess_to_report():
"""Test entire assess → report → align workflow."""
# Creates repo
# Runs scanner
# Generates all 5 report formats
# Runs all fixers
# Validates output files
# Takes 10+ secondsSolution:
Convert some integration tests to unit tests:
# tests/unit/test_scanner.py
def test_scanner_calls_assessors(mocker, sample_repo):
"""Unit test: scanner calls assessors correctly."""
mock_assessor = mocker.patch("agentready.services.scanner.get_all_assessors")
scanner = Scanner()
scanner.scan(sample_repo)
mock_assessor.assert_called_once()
# Fast unit test with mocks
# Keep only critical integration tests
# tests/integration/test_full_workflow.py
def test_assess_and_html_report_integration(sample_repo):
"""Integration test: assess + HTML report (most common path)."""
# Test only the most common user workflow
# Skip testing all 5 report formats (those are unit tests)Impact:
- -200 LOC in integration tests
- 2x faster test suite (unit tests run in milliseconds vs seconds)
- Still maintain coverage with focused unit tests
Summary of Expected Outcomes
Code Reduction
| Phase | Changes | LOC Saved |
|---|---|---|
| 1. Consolidate Patterns | Security utils, reporter base, service registry | -650 |
| 2. Simplify Over-Engineering | Pydantic config, template inheritance, theme system | -250 |
| 3. Better Abstractions | Assessor registry, unify batch, merge research | -380 |
| 4. Test Improvements | Shared fixtures, reduce integration tests | -600 |
| Total | -1,880 LOC |
Percentage: ~30% reduction (6,300 → 4,420 LOC)
Module Count
- Before: 64 modules + 15 templates = 79 files
- After: 58 modules + 8 templates = 66 files
- Reduction: -13 files (16%)
Maintainability Improvements
- ✅ Single source of truth for security validation
- ✅ DRY principle applied to reporters and services
- ✅ Clearer service boundaries and responsibilities
- ✅ Easier to add assessors (just use decorator)
- ✅ Faster test suite (2x improvement)
- ✅ Better type safety (Pydantic models)
Implementation Checklist
Week 1-2: Phase 1
- Create
utils/security.pywith centralized validation - Refactor all modules to use security utils
- Create
reporters/base.pywith shared reporter logic - Refactor 5 reporters to extend base class
- Create
services/registry.pyfor DI - Update services to use registry
- Run test suite (ensure all pass)
- Update documentation
Week 3-4: Phase 2
- Create Pydantic config models in
models/config.py - Replace manual validation in
cli/main.py - Refactor bootstrap templates to use inheritance
- Update
services/bootstrap.pyto use new templates - Simplify theme system to 2-3 base colors
- Update HTML reporter to use new theme system
- Run test suite
- Update configuration docs
Week 5-6: Phase 3
- Add
@register_assessordecorator toassessors/base.py - Annotate all assessor classes with decorator
- Remove manual imports from
cli/main.py - Update
Scannerto handle single/batch - Delete
batch_scanner.py - Merge
research_loader.py+research_formatter.py→research_service.py - Run test suite
- Update API docs
Week 7: Phase 4
- Create shared fixtures in
tests/conftest.py - Refactor tests to use fixtures
- Identify integration tests that can be unit tests
- Convert 10-15 integration → unit tests
- Run full test suite
- Verify 90%+ coverage maintained
- Update testing documentation
Risk Mitigation
Testing Strategy
- Before each phase: Run full test suite, ensure 100% pass
- After each refactor: Run affected tests
- End of each week: Full regression test
- CI/CD: All checks must pass before merging
Rollback Plan
- Use feature branches for each phase
- Keep original code until phase verified
- Tag stable points:
v1.0-pre-refactor,v1.1-phase1-complete, etc.
Breaking Changes
NONE - This is pure refactoring:
- CLI commands unchanged
- Config format unchanged (Pydantic validates same structure)
- Report outputs unchanged
- All features retained
Success Metrics
Quantitative
- ✅ Reduce LOC by 30% (6,300 → 4,420)
- ✅ Reduce file count by 16% (79 → 66)
- ✅ Improve test speed by 2x
- ✅ Maintain 90%+ test coverage
Qualitative
- ✅ Easier to onboard new contributors (clearer patterns)
- ✅ Easier to add assessors (decorator pattern)
- ✅ Easier to add reporters (base class)
- ✅ More consistent security (centralized validation)
- ✅ Better type safety (Pydantic)
Post-Simplification Next Steps
After completing this refactor:
- Documentation Sprint - Update all docs to reflect new patterns
- Performance Profiling - Identify any new bottlenecks from abstractions
- Community Feedback - Get input from contributors on new structure
- Feature Development - Resume adding features with cleaner codebase
Appendix: Key Files to Refactor
High Priority (Phase 1)
src/agentready/cli/main.py(512 lines) - config validationsrc/agentready/reporters/html.py(300+ lines) - security/base classsrc/agentready/reporters/markdown.py(150+ lines) - base classsrc/agentready/reporters/json_reporter.py(50+ lines) - base classsrc/agentready/reporters/csv_reporter.py(100+ lines) - base classsrc/agentready/reporters/multi_html.py(200+ lines) - base class
Medium Priority (Phase 2-3)
src/agentready/services/bootstrap.py(500 lines) - template inheritancesrc/agentready/services/batch_scanner.py(200 lines) - DELETEsrc/agentready/services/research_loader.py(150 lines) - MERGEsrc/agentready/services/research_formatter.py(100 lines) - MERGEtemplates/bootstrap/(15 files) - inheritance
Low Priority (Phase 4)
tests/(39 files, 169 tests) - shared fixtures
Cold Start Instructions for AI Agent
Context: You are refactoring AgentReady to reduce implementation complexity while keeping all features.
Starting Point:
- Read this document:
.plans/implementation-simplification-refactor.md - Review current architecture:
src/agentready/(64 modules) - Check test coverage:
pytest --cov(should be 90%+)
Execution:
- Start with Phase 1, Week 1-2
- Create feature branch:
git checkout -b refactor/phase-1-consolidate-patterns - Implement changes from checklist
- Run tests after each change:
pytest - Commit incrementally with clear messages
- When phase complete, open PR for review
Key Principles:
- ✅ Keep all features (no deletions)
- ✅ Maintain test coverage (90%+)
- ✅ Preserve CLI/config compatibility
- ✅ Focus on DRY and single responsibility
- ❌ Don't add new features (pure refactor)
- ❌ Don't change external APIs
Questions to Ask:
- Does this refactor maintain the same external behavior?
- Are tests still passing?
- Is the code more maintainable after this change?
- Could a new contributor understand this pattern?
End Goal: Same AgentReady functionality, 30% less code, better maintainability.