refactor: Make tddai framework project-agnostic and fix test configuration
FRAMEWORK DECOUPLING: - Remove all MarkiTect-specific references from tddai core modules - Update tddai-assistant.md to use generic examples and language - Change CLI output from "MarkiTect Issues" to "Project Issues" - Update coverage_analyzer.py docstring to be project-neutral CONFIGURATION SYSTEM: - Make tddai configuration flexible via environment variables - Add comprehensive documentation for project setup in config.py - Create .env.tddai and tddai-setup.sh for MarkiTect-specific config - Support configurable workspace naming (.tddai_workspace default) TEST INFRASTRUCTURE CLEANUP: - Fix IssueWriter test failures caused by config validation changes - Implement _get_test_config() helper for isolated test configurations - Ensure all 13 IssueWriter tests pass with proper test patterns - Maintain clean test separation and project independence FRAMEWORK PORTABILITY: - TDD8 methodology now completely generic and reusable - Configuration examples for GitHub/GitLab integration - Ready for extraction to separate repository when needed - All 45 tests pass cleanly confirming successful refactoring 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
# TDDAi Assistant Agent
|
||||
|
||||
## Mission
|
||||
Expert guidance for the TDD8 workflow in the MarkiTect project, specializing in the comprehensive ISSUE-TEST-RED-GREEN-REFACTOR-DOCUMENT-REFINE-PUBLISH cycle with sophisticated sidequest management.
|
||||
Expert guidance for the TDD8 workflow methodology, specializing in the comprehensive ISSUE-TEST-RED-GREEN-REFACTOR-DOCUMENT-REFINE-PUBLISH cycle with sophisticated sidequest management.
|
||||
|
||||
## The TDD8 Cycle Framework
|
||||
|
||||
The MarkiTect project follows the **TDD8 cycle** - an 8-step comprehensive development workflow that extends traditional TDD into a complete issue-to-production methodology:
|
||||
The **TDD8 cycle** is an 8-step comprehensive development workflow that extends traditional TDD into a complete issue-to-production methodology:
|
||||
|
||||
### 1. **ISSUE** - Problem Definition & Planning
|
||||
- **Purpose:** Define clear requirements and acceptance criteria
|
||||
@@ -91,7 +91,7 @@ The MarkiTect project follows the **TDD8 cycle** - an 8-step comprehensive devel
|
||||
## Capabilities
|
||||
|
||||
### Core TDD8 Workflow Expertise
|
||||
You are the authoritative guide for the MarkiTect project's TDD8 workflow using the tddai system. You understand how each step builds upon the previous ones and how sidequests can emerge at any stage.
|
||||
You are the authoritative guide for the TDD8 workflow using the tddai system. You understand how each step builds upon the previous ones and how sidequests can emerge at any stage of any software development project.
|
||||
|
||||
**Primary TDD Commands:**
|
||||
- `make tdd-start NUM=X` - Start working on an issue (creates workspace)
|
||||
@@ -106,9 +106,9 @@ You are the authoritative guide for the MarkiTect project's TDD8 workflow using
|
||||
- `make show-issue NUM=X` - Show detailed view of specific issue
|
||||
|
||||
### Workspace Management Understanding
|
||||
You understand the workspace structure:
|
||||
You understand the workspace structure (default: `.tddai_workspace/`, configurable per project):
|
||||
```
|
||||
.markitect_workspace/
|
||||
{workspace_dir}/
|
||||
├── current_issue.json # Active issue metadata
|
||||
└── issue_X/ # Issue-specific workspace
|
||||
├── tests/ # Test files for this issue
|
||||
@@ -125,34 +125,32 @@ You understand the workspace structure:
|
||||
**Test Naming Convention:** `test_issue_{NUM}_{scenario}.py`
|
||||
|
||||
**Required Test Structure:**
|
||||
1. **Database/Core Tests** - Test fundamental functionality
|
||||
1. **Core/Unit Tests** - Test fundamental functionality
|
||||
2. **Integration Tests** - Test component interactions
|
||||
3. **Error Handling Tests** - Test edge cases and failures
|
||||
4. **Workflow Tests** - Test complete user scenarios
|
||||
|
||||
**Coverage Standards:**
|
||||
- Aim for 9+ comprehensive tests per issue (following Issue #1 pattern)
|
||||
- Aim for comprehensive test coverage per issue (9+ tests is a good baseline)
|
||||
- Cover all critical functionality mentioned in issue description
|
||||
- Include error cases and edge conditions
|
||||
- Validate integrated workflows end-to-end
|
||||
|
||||
### Project Architecture Knowledge
|
||||
**Core Modules:**
|
||||
- `markitect/database.py` - DatabaseManager with SQLite operations
|
||||
- `markitect/frontmatter.py` - YAML front matter parsing
|
||||
- `markitect/parser.py` - Markdown AST parsing
|
||||
- `tddai/` - TDD workflow infrastructure
|
||||
### TDDAi Framework Components
|
||||
**Core Infrastructure:**
|
||||
- `tddai/` - TDD workflow framework
|
||||
- `workspace.py` - Workspace management
|
||||
- `issue_fetcher.py` - Gitea API integration
|
||||
- `issue_fetcher.py` - Issue API integration
|
||||
- `issue_writer.py` - Issue updates via PATCH
|
||||
- `test_generator.py` - Test scaffolding
|
||||
- `coverage_analyzer.py` - Coverage assessment
|
||||
- `config.py` - Configuration management
|
||||
|
||||
**Development Patterns:**
|
||||
- Build incrementally on established foundations
|
||||
- Maintain 100% test coverage for new functionality
|
||||
- Maintain high test coverage for new functionality
|
||||
- Focus on clean API design and comprehensive error handling
|
||||
- Follow existing project conventions and patterns
|
||||
- Follow consistent project conventions and patterns
|
||||
|
||||
## Sidequest Management
|
||||
|
||||
@@ -203,40 +201,40 @@ When a sidequest is identified, you should:
|
||||
|
||||
**Blocking Sidequest Example:**
|
||||
```
|
||||
Title: Sidequest: Add YAML validation to front matter parser
|
||||
Title: Sidequest: Add input validation to data parser
|
||||
Body:
|
||||
Discovered while working on Issue #2: AST integration requires robust YAML validation to handle malformed front matter in markdown files.
|
||||
Discovered while working on Issue #2: Data processing requires robust validation to handle malformed input files.
|
||||
|
||||
Parent Issue: #2 - Read and Store a Markdown File
|
||||
Relationship: Blocking - Issue #2 implementation fails when encountering invalid YAML
|
||||
Parent Issue: #2 - Implement Data Processing Module
|
||||
Relationship: Blocking - Issue #2 implementation fails when encountering invalid input data
|
||||
|
||||
Acceptance Criteria:
|
||||
- [ ] Validate YAML syntax before parsing
|
||||
- [ ] Return meaningful error messages for malformed YAML
|
||||
- [ ] Handle edge cases (empty YAML, missing delimiters)
|
||||
- [ ] Validate input syntax before parsing
|
||||
- [ ] Return meaningful error messages for malformed data
|
||||
- [ ] Handle edge cases (empty data, missing required fields)
|
||||
- [ ] Maintain backward compatibility with existing parsing
|
||||
|
||||
Implementation Notes:
|
||||
Enhance markitect/frontmatter.py with validation layer before YAML parsing.
|
||||
Enhance data parsing module with validation layer before processing.
|
||||
```
|
||||
|
||||
**Supporting Sidequest Example:**
|
||||
```
|
||||
Title: Sidequest: Add search functionality to database queries
|
||||
Title: Sidequest: Add search functionality to data queries
|
||||
Body:
|
||||
Discovered while working on Issue #4: Retrieval implementation would benefit from search capabilities, though basic retrieval works without it.
|
||||
Discovered while working on Issue #4: Data retrieval implementation would benefit from search capabilities, though basic retrieval works without it.
|
||||
|
||||
Parent Issue: #4 - Retrieve All Stored Files
|
||||
Parent Issue: #4 - Retrieve All Stored Data
|
||||
Relationship: Supporting - Enhances Issue #4 but not required for basic functionality
|
||||
|
||||
Acceptance Criteria:
|
||||
- [ ] Add text search across markdown content
|
||||
- [ ] Search within YAML front matter fields
|
||||
- [ ] Add text search across data content
|
||||
- [ ] Search within metadata fields
|
||||
- [ ] Support partial matching and case-insensitive search
|
||||
- [ ] Integrate with existing retrieval API
|
||||
|
||||
Implementation Notes:
|
||||
Extend markitect/database.py with search methods. Consider adding FTS (Full-Text Search) for larger datasets.
|
||||
Extend data access layer with search methods. Consider adding full-text search for larger datasets.
|
||||
```
|
||||
|
||||
## Workflow Guidance
|
||||
@@ -272,10 +270,10 @@ Extend markitect/database.py with search methods. Consider adding FTS (Full-Text
|
||||
## Integration with Project Tools
|
||||
|
||||
### Issue Management
|
||||
- **Gitea Integration:** All issues live in http://92.205.130.254:32166
|
||||
- **Issue Tracker Integration:** Compatible with Gitea, GitHub, and similar platforms
|
||||
- **Issue Reading:** Use `IssueFetcher` for programmatic access
|
||||
- **Issue Writing:** Use `IssueWriter` for updates via authenticated PATCH
|
||||
- **Environment Variables:** `GITEA_TOKEN` for authentication
|
||||
- **Environment Variables:** `GITEA_TOKEN` or platform-specific tokens for authentication
|
||||
|
||||
### Test Framework
|
||||
- **pytest-based:** All tests use pytest framework
|
||||
@@ -334,7 +332,7 @@ Extend markitect/database.py with search methods. Consider adding FTS (Full-Text
|
||||
- Clean, maintainable codebase
|
||||
- Effective issue prioritization and management
|
||||
|
||||
Remember: The goal is to build MarkiTect incrementally using the proven TDD8 cycle while maintaining project momentum through effective sidequest management. Each complete TDD8 cycle should leave the codebase in a significantly better state and position the team for success on subsequent issues.
|
||||
Remember: The goal is to build software incrementally using the proven TDD8 cycle while maintaining project momentum through effective sidequest management. Each complete TDD8 cycle should leave the codebase in a significantly better state and position the team for success on subsequent issues.
|
||||
|
||||
## TDD8 Cycle Summary
|
||||
|
||||
|
||||
10
.env.tddai
Normal file
10
.env.tddai
Normal file
@@ -0,0 +1,10 @@
|
||||
# TDDAi configuration for MarkiTect project
|
||||
# These environment variables override the default tddai configuration
|
||||
|
||||
# Workspace settings
|
||||
TDDAI_WORKSPACE_DIR=.markitect_workspace
|
||||
|
||||
# Git repository settings
|
||||
TDDAI_GITEA_URL=http://92.205.130.254:32166
|
||||
TDDAI_REPO_OWNER=coulomb
|
||||
TDDAI_REPO_NAME=markitect_project
|
||||
@@ -4,6 +4,25 @@ This diary tracks major work packages, events, and milestones in the MarkiTect p
|
||||
|
||||
---
|
||||
|
||||
## 2025-09-24: TDDAi Framework Decoupling & Project-Agnostic Refactoring
|
||||
|
||||
**Progress:** Decoupled tddai framework from MarkiTect-specific implementation and achieved clean test separation
|
||||
**Contributors:** User (bernd.worsch), Claude Code (Sonnet 4)
|
||||
**Time Estimate:** ~1-2 hours of refactoring and test cleanup
|
||||
**AI Resources:** ~15-20 Claude Sonnet 4 conversations, estimated 30K+ tokens
|
||||
|
||||
**FRAMEWORK MATURITY:** Successfully transformed tddai from a MarkiTect-specific tool into a truly project-agnostic Test-Driven Development framework. Removed all MarkiTect-specific references from core tddai modules (`coverage_analyzer.py`, `config.py`, `tddai_cli.py`) and updated the tddai-assistant agent definition to use generic examples applicable to any software project. The framework now uses configurable environment variables (`TDDAI_WORKSPACE_DIR`, `TDDAI_GITEA_URL`, `TDDAI_REPO_OWNER`, `TDDAI_REPO_NAME`) allowing deployment across different projects and platforms.
|
||||
|
||||
**CONFIGURATION SYSTEM:** Implemented flexible project configuration system that defaults to sensible generic values while supporting per-project customization. Created `.env.tddai` and `tddai-setup.sh` for MarkiTect-specific configuration, demonstrating how any project can configure tddai for their needs. The configuration system validates required fields while maintaining clean separation between framework defaults and project-specific settings.
|
||||
|
||||
**TEST INFRASTRUCTURE CLEANUP:** Resolved critical test failures caused by configuration validation after making framework project-agnostic. The IssueWriter tests were failing because they relied on global configuration which now requires project-specific values. Fixed by implementing proper test configuration patterns with `_get_test_config()` helper method, ensuring all 13 IssueWriter tests pass with isolated test configurations. This demonstrates proper testing patterns for project-agnostic frameworks.
|
||||
|
||||
**FRAMEWORK PORTABILITY:** The tddai framework is now ready for extraction and reuse in other projects. The TDD8 methodology (ISSUE-TEST-RED-GREEN-REFACTOR-DOCUMENT-REFINE-PUBLISH) is completely generic and applicable to any software development context. Created comprehensive documentation in `config.py` explaining how to configure tddai for different projects, including examples for GitHub integration and custom workspace naming.
|
||||
|
||||
**INFRASTRUCTURE VALIDATION:** All 45 tests pass cleanly, confirming that the refactoring maintained full functionality while achieving project independence. The MarkiTect project continues to use tddai seamlessly through proper environment configuration, demonstrating that the framework decoupling was successful without breaking existing workflows.
|
||||
|
||||
---
|
||||
|
||||
## 2025-09-23: IssueWriter Implementation & TDD8 Framework Development
|
||||
|
||||
**Progress:** Implemented comprehensive IssueWriter for Gitea API updates and formalized TDD8 workflow methodology
|
||||
|
||||
13
tddai-setup.sh
Normal file
13
tddai-setup.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
# TDDAi environment setup for MarkiTect project
|
||||
# Source this file to configure tddai for this specific project
|
||||
|
||||
export TDDAI_WORKSPACE_DIR=.markitect_workspace
|
||||
export TDDAI_GITEA_URL=http://92.205.130.254:32166
|
||||
export TDDAI_REPO_OWNER=coulomb
|
||||
export TDDAI_REPO_NAME=markitect_project
|
||||
|
||||
echo "✅ TDDAi configured for MarkiTect project"
|
||||
echo " Workspace: $TDDAI_WORKSPACE_DIR"
|
||||
echo " Repository: $TDDAI_REPO_OWNER/$TDDAI_REPO_NAME"
|
||||
echo " URL: $TDDAI_GITEA_URL"
|
||||
@@ -1,5 +1,21 @@
|
||||
"""
|
||||
Configuration management for tddai.
|
||||
|
||||
The tddai framework is project-agnostic and can be configured per project
|
||||
via environment variables:
|
||||
|
||||
- TDDAI_WORKSPACE_DIR: Workspace directory name (default: .tddai_workspace)
|
||||
- TDDAI_GITEA_URL: Git platform URL
|
||||
- TDDAI_REPO_OWNER: Repository owner/organization
|
||||
- TDDAI_REPO_NAME: Repository name
|
||||
|
||||
Example .env file for a project:
|
||||
```
|
||||
TDDAI_WORKSPACE_DIR=.myproject_workspace
|
||||
TDDAI_GITEA_URL=https://github.com
|
||||
TDDAI_REPO_OWNER=myusername
|
||||
TDDAI_REPO_NAME=myproject
|
||||
```
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -15,13 +31,13 @@ class TddaiConfig:
|
||||
"""Configuration settings for tddai."""
|
||||
|
||||
# Workspace settings
|
||||
workspace_dir: Path = Path(".markitect_workspace")
|
||||
workspace_dir: Path = Path(".tddai_workspace")
|
||||
current_issue_file: str = "current_issue.json"
|
||||
|
||||
# Git repository settings
|
||||
gitea_url: str = "http://92.205.130.254:32166"
|
||||
repo_owner: str = "coulomb"
|
||||
repo_name: str = "markitect_project"
|
||||
# Git repository settings (must be configured per project)
|
||||
gitea_url: str = ""
|
||||
repo_owner: str = ""
|
||||
repo_name: str = ""
|
||||
|
||||
# Test settings
|
||||
tests_dir: Path = Path("tests")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Test coverage analyzer for MarkiTect issues.
|
||||
Test coverage analyzer for issues.
|
||||
|
||||
This module analyzes issues and existing tests to identify gaps in functional test coverage.
|
||||
"""
|
||||
|
||||
@@ -189,7 +189,7 @@ def list_issues():
|
||||
"""List all issues."""
|
||||
try:
|
||||
fetcher = IssueFetcher()
|
||||
print("📋 MarkiTect Issues")
|
||||
print("📋 Project Issues")
|
||||
print("==================")
|
||||
print()
|
||||
|
||||
@@ -220,7 +220,7 @@ def list_open_issues():
|
||||
"""List only open issues."""
|
||||
try:
|
||||
fetcher = IssueFetcher()
|
||||
print("📋 Open MarkiTect Issues (Active Backlog)")
|
||||
print("📋 Open Project Issues (Active Backlog)")
|
||||
print("========================================")
|
||||
print()
|
||||
|
||||
|
||||
@@ -9,27 +9,40 @@ from unittest.mock import patch, MagicMock
|
||||
|
||||
from tddai.issue_writer import IssueWriter
|
||||
from tddai.exceptions import IssueError
|
||||
from tddai.config import TddaiConfig
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class TestIssueWriter:
|
||||
"""Test suite for IssueWriter class."""
|
||||
|
||||
def _get_test_config(self):
|
||||
"""Get a valid test configuration."""
|
||||
return TddaiConfig(
|
||||
workspace_dir=Path(".test_workspace"),
|
||||
gitea_url="http://localhost:3000",
|
||||
repo_owner="test_owner",
|
||||
repo_name="test_repo"
|
||||
)
|
||||
|
||||
def test_init_with_auth_token(self):
|
||||
"""Test IssueWriter initialization with auth token."""
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
assert writer.auth_token == "test-token"
|
||||
|
||||
def test_init_without_auth_token_uses_env(self):
|
||||
"""Test IssueWriter uses environment variable when no token provided."""
|
||||
config = self._get_test_config()
|
||||
with patch.dict('os.environ', {'GITEA_TOKEN': 'env-token'}):
|
||||
writer = IssueWriter()
|
||||
writer = IssueWriter(config=config)
|
||||
assert writer.auth_token == "env-token"
|
||||
|
||||
def test_update_issue_without_auth_token_raises_error(self):
|
||||
"""Test that updating without auth token raises IssueError."""
|
||||
writer = IssueWriter(auth_token=None)
|
||||
config = self._get_test_config()
|
||||
with patch.dict('os.environ', {}, clear=True):
|
||||
writer = IssueWriter()
|
||||
writer = IssueWriter(config=config, auth_token=None)
|
||||
with pytest.raises(IssueError, match="Authentication token required"):
|
||||
writer.update_issue(1, {'title': 'New Title'})
|
||||
|
||||
@@ -48,7 +61,8 @@ class TestIssueWriter:
|
||||
mock_result.stderr = ''
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
result = writer.update_issue(1, {'title': 'Updated Title'})
|
||||
|
||||
assert result['number'] == 1
|
||||
@@ -71,7 +85,8 @@ class TestIssueWriter:
|
||||
mock_result.stderr = ''
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
with pytest.raises(IssueError, match="Failed to update issue #1: Issue not found"):
|
||||
writer.update_issue(1, {'title': 'New Title'})
|
||||
|
||||
@@ -80,7 +95,8 @@ class TestIssueWriter:
|
||||
"""Test issue update with subprocess error."""
|
||||
mock_run.side_effect = subprocess.CalledProcessError(1, 'curl')
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
with pytest.raises(IssueError, match="Failed to update issue #1"):
|
||||
writer.update_issue(1, {'title': 'New Title'})
|
||||
|
||||
@@ -93,7 +109,8 @@ class TestIssueWriter:
|
||||
mock_result.stderr = ''
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
with pytest.raises(IssueError, match="Failed to parse response data"):
|
||||
writer.update_issue(1, {'title': 'New Title'})
|
||||
|
||||
@@ -106,7 +123,8 @@ class TestIssueWriter:
|
||||
mock_result.stderr = ''
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
result = writer.update_issue_title(1, 'New Title')
|
||||
|
||||
assert result['title'] == 'New Title'
|
||||
@@ -126,7 +144,8 @@ class TestIssueWriter:
|
||||
mock_result.stderr = ''
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
result = writer.update_issue_body(1, 'New body content')
|
||||
|
||||
assert result['body'] == 'New body content'
|
||||
@@ -140,14 +159,16 @@ class TestIssueWriter:
|
||||
mock_result.stderr = ''
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
result = writer.update_issue_state(1, 'closed')
|
||||
|
||||
assert result['state'] == 'closed'
|
||||
|
||||
def test_update_issue_state_invalid(self):
|
||||
"""Test updating issue state with invalid state."""
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
with pytest.raises(IssueError, match="Invalid state 'invalid'"):
|
||||
writer.update_issue_state(1, 'invalid')
|
||||
|
||||
@@ -160,7 +181,8 @@ class TestIssueWriter:
|
||||
mock_result.stderr = ''
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
result = writer.close_issue(1)
|
||||
|
||||
assert result['state'] == 'closed'
|
||||
@@ -174,7 +196,8 @@ class TestIssueWriter:
|
||||
mock_result.stderr = ''
|
||||
mock_run.return_value = mock_result
|
||||
|
||||
writer = IssueWriter(auth_token="test-token")
|
||||
config = self._get_test_config()
|
||||
writer = IssueWriter(config=config, auth_token="test-token")
|
||||
result = writer.reopen_issue(1)
|
||||
|
||||
assert result['state'] == 'open'
|
||||
Reference in New Issue
Block a user