Created detailed documentation for capabilities concept and integration: - CAPABILITIES_ARCHITECTURE.md: Full guide on separation of concerns - CAPABILITIES_QUICK_REFERENCE.md: Quick reference for common tasks - Updated docs/README.md to reference new documentation Ensures future sessions respect capability boundaries and use separate Claude instances for capability development. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
Capabilities Architecture
Overview
MarkiTect uses a capabilities-based architecture where functionality is organized into independent, reusable packages called capabilities. Each capability is a self-contained git submodule with its own repository, enabling independent development, versioning, and reuse across projects.
Core Principles
1. Separation of Concerns
Critical Rule: The main repository (markitect_project) MUST NOT directly modify capability code.
- ✅ DO: Use capabilities as dependencies
- ✅ DO: Configure capabilities through documented interfaces
- ✅ DO: Report issues and feature requests to capability repos
- ❌ DON'T: Edit capability code from the main repo
- ❌ DON'T: Make commits directly in capability subdirectories
- ❌ DON'T: Bypass the submodule boundary
Why? Capabilities are independent repositories. Direct modifications create:
- Merge conflicts when syncing with upstream
- Broken dependency management
- Loss of independent versioning
- Confusion about source of truth
2. Git Submodule Architecture
Capabilities are integrated as git submodules, not regular directories:
markitect_project/
├── .gitmodules # Submodule configuration
├── capabilities/
│ ├── testdrive-jsui/ # Git submodule → separate repo
│ ├── issue-facade/ # Git submodule → separate repo
│ ├── kaizen-agentic/ # Git submodule → separate repo
│ ├── markitect-content/ # Local capability (legacy)
│ └── release-management/ # Local capability (legacy)
Submodule vs Local Capabilities:
- Submodules: Independent git repositories, separate development lifecycle
- Local: Part of main repo, shared lifecycle (being phased out)
Target State: All capabilities should eventually be submodules.
3. Independent Development Lifecycle
Each capability has its own:
- ✅ Git repository on gitea
- ✅ Version numbering (semantic versioning)
- ✅ Issue tracking and roadmap
- ✅ Testing and CI/CD pipeline
- ✅ Documentation and README
- ✅ Contributors and maintainers
4. Interface-Based Integration
Capabilities expose stable interfaces to the main project:
# markitect uses capability through documented interface
from testdrive_jsui import TestDriveJSUIEngine
engine = TestDriveJSUIEngine()
engine.render_document(content, mode='edit', config=config)
Integration Points:
- Python Package Interface: Clean import and API
- Configuration: Via
pyproject.tomldependencies - Plugin System: Via plugin registration and metadata
- Makefile Targets: Via capability discovery system
Development Workflow
Working on Capabilities
Rule: Each capability is developed in its own Claude Code session.
Main Repository Session
# In markitect_project/
cd /home/worsch/markitect_project
# Main repo tasks:
# - Integrate capabilities
# - Update dependencies
# - Configure capability usage
# - Test integration points
Capability Session
# In capability repository
cd /home/worsch/markitect_project/capabilities/testdrive-jsui
# OR clone separately
git clone http://gitea/coulomb/testdrive-jsui.git
cd testdrive-jsui
# Capability tasks:
# - Implement features
# - Fix bugs
# - Write tests
# - Update documentation
# - Make releases
Recommended Session Pattern
Scenario: Need to add feature to testdrive-jsui
-
Open separate Claude instance for testdrive-jsui
# In new terminal/session cd /path/to/testdrive-jsui # Work on feature git commit -m "feat: new feature" git push origin main -
Update main project (different Claude instance)
cd /home/worsch/markitect_project git submodule update --remote capabilities/testdrive-jsui git commit -m "chore: update testdrive-jsui submodule"
Why separate instances?
- Clear context boundaries
- Prevents accidental cross-contamination
- Respects repository independence
- Better commit hygiene
Updating Submodules
When a capability releases a new version:
# In main repo
cd /home/worsch/markitect_project
# Update specific capability
cd capabilities/testdrive-jsui
git pull origin main
cd ../..
git add capabilities/testdrive-jsui
git commit -m "chore: update testdrive-jsui to v1.2.0"
# Or update all capabilities
git submodule update --remote
git commit -am "chore: update all capabilities"
Adding New Capabilities
# 1. Create capability repository on gitea
# http://gitea/coulomb/new-capability
# 2. Add as submodule to main repo
cd /home/worsch/markitect_project
git submodule add http://gitea/coulomb/new-capability.git capabilities/new-capability
# 3. Add dependency to pyproject.toml
# dependencies = [
# "new-capability @ file:./capabilities/new-capability"
# ]
# 4. Commit submodule addition
git commit -m "feat: add new-capability as submodule"
Dependency Management
Capabilities are declared in pyproject.toml:
[project]
dependencies = [
# Core dependencies
"markdown-it-py",
"click>=8.0.0",
# Capabilities (file-based dependencies)
"release-management @ file:./capabilities/release-management",
"testdrive-jsui @ file:./capabilities/testdrive-jsui",
"issue-facade @ file:./capabilities/issue-facade",
]
Pattern for Capabilities:
"capability-name @ file:./capabilities/capability-name"
This enables:
- ✅ pip install with editable dependencies
- ✅ Proper package resolution
- ✅ Clean imports in code
- ✅ Development mode support
Capability Structure
Each capability should follow this structure:
capability-name/
├── README.md # Comprehensive documentation
├── pyproject.toml # Package configuration
├── Makefile # Build and test automation
├── .gitignore # Git ignore rules
├── src/capability_name/ # Python package source
│ ├── __init__.py
│ ├── core/ # Core functionality
│ ├── utils/ # Utilities
│ └── testing/ # Testing infrastructure
├── js/ # JavaScript (if applicable)
│ ├── components/
│ ├── controls/
│ └── tests/
├── static/ # Static assets (if applicable)
│ ├── css/
│ ├── images/
│ └── templates/
├── tests/ # Python tests
│ └── test_*.py
└── docs/ # Additional documentation
├── API.md
└── USAGE.md
Required Files
-
README.md - Must include:
- Purpose and description
- Installation instructions
- Usage examples
- API documentation
- Integration guide
-
pyproject.toml - Must define:
- Package metadata
- Dependencies
- Build system
- Entry points
-
Makefile - Should include:
- help target
- test targets
- install/setup targets
- capability-info target (for discovery)
Plugin System Integration
For capabilities that provide plugins (like testdrive-jsui):
Plugin Self-Declaration
class TestDriveJSUIEngine(RenderingEnginePlugin):
"""Plugin declares its own location - no hardcoded paths."""
def get_plugin_source_dir(self) -> Path:
"""Plugin knows where it lives."""
return Path(__file__).parent.parent.parent / "capabilities" / "testdrive-jsui"
def get_asset_paths(self) -> Dict[str, Path]:
"""Plugin declares its asset structure."""
base = self.get_plugin_source_dir()
return {
'js': base / 'js',
'css': base / 'static' / 'css',
'templates': base / 'static' / 'templates',
}
Plugin Discovery
The main repo uses generic discovery - no hardcoded capability names:
# ✅ Good: Generic discovery
if hasattr(engine, 'get_plugin_source_dir'):
source_dir = engine.get_plugin_source_dir()
# ❌ Bad: Hardcoded capability names
if engine_name == 'testdrive-jsui':
source_dir = Path('capabilities/testdrive-jsui')
Testing Strategy
Capability Tests
Each capability tests in isolation:
cd capabilities/testdrive-jsui
pytest tests/
npm test
Integration Tests
Main repo tests integration points:
# tests/integration/test_capabilities.py
def test_testdrive_jsui_integration():
"""Test that testdrive-jsui integrates correctly."""
engine = TestDriveJSUIEngine()
result = engine.render_document(sample_content, 'edit', config)
assert result is not None
Test Boundaries
- Capability tests: Internal functionality, unit tests, component tests
- Main repo tests: Integration, configuration, plugin discovery
Migration Path
Converting Local Capability to Submodule
-
Create separate git repo
cd /tmp cp -r markitect_project/capabilities/capability-name capability-name cd capability-name git init git add . git commit -m "Initial commit" git remote add origin http://gitea/coulomb/capability-name.git git push -u origin main -
Remove from main repo
cd markitect_project git rm -rf capabilities/capability-name git commit -m "chore: remove capability-name for submodule conversion" -
Add as submodule
git submodule add http://gitea/coulomb/capability-name.git capabilities/capability-name git commit -m "feat: add capability-name as submodule"
Best Practices
DO ✅
-
Develop capabilities independently
- Use separate Claude instances
- Maintain clear context boundaries
- Follow semantic versioning
-
Use documented interfaces
- Import via Python packages
- Use plugin APIs
- Follow configuration patterns
-
Test integration points
- Verify capability imports
- Test plugin discovery
- Validate configurations
-
Update submodules explicitly
- Pull capability changes deliberately
- Review updates before committing
- Update main repo pointer
-
Document integration
- How to use the capability
- Configuration options
- Example usage
DON'T ❌
-
Edit capability code from main repo
- Opens separate repo/instance instead
- Respects submodule boundary
- Maintains clean separation
-
Hardcode capability paths
- Use self-declaration instead
- Generic discovery patterns
- Interface-based access
-
Create circular dependencies
- Main depends on capabilities
- Capabilities should NOT depend on main
- Keep dependency graph acyclic
-
Bypass submodule system
- Don't manually copy files
- Use git submodule commands
- Respect version control
Troubleshooting
Submodule not updated
# Pull latest from capability repo
cd capabilities/testdrive-jsui
git pull origin main
# Update main repo pointer
cd ../..
git add capabilities/testdrive-jsui
git commit -m "chore: update testdrive-jsui"
Import errors
# Reinstall capabilities
pip install -e .
# Or specific capability
pip install -e ./capabilities/testdrive-jsui
Merge conflicts in submodules
# Don't merge in submodule from main repo!
# Instead, go to capability repo directly
cd /path/to/separate/testdrive-jsui
# or
cd capabilities/testdrive-jsui
# Resolve conflicts there
git pull origin main
# fix conflicts
git push origin main
# Then update main repo
cd ../../
git submodule update --remote
See Also
- Agent: Capability Manager - Automated capability management
- Plugin System - Plugin architecture documentation
- Git Submodules - Official git submodules guide
Remember: Capabilities are independent projects. Treat them with the same respect you'd give any external dependency - use their interfaces, don't modify their internals.