Files
markitect-main/docs/architecture/CAPABILITIES_ARCHITECTURE.md
tegwick aa0ac626c5 docs: add comprehensive capabilities architecture documentation
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>
2025-12-16 00:15:57 +01:00

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:

  1. Python Package Interface: Clean import and API
  2. Configuration: Via pyproject.toml dependencies
  3. Plugin System: Via plugin registration and metadata
  4. 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

Scenario: Need to add feature to testdrive-jsui

  1. 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
    
  2. 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

  1. README.md - Must include:

    • Purpose and description
    • Installation instructions
    • Usage examples
    • API documentation
    • Integration guide
  2. pyproject.toml - Must define:

    • Package metadata
    • Dependencies
    • Build system
    • Entry points
  3. 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

  1. 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
    
  2. Remove from main repo

    cd markitect_project
    git rm -rf capabilities/capability-name
    git commit -m "chore: remove capability-name for submodule conversion"
    
  3. 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

  1. Develop capabilities independently

    • Use separate Claude instances
    • Maintain clear context boundaries
    • Follow semantic versioning
  2. Use documented interfaces

    • Import via Python packages
    • Use plugin APIs
    • Follow configuration patterns
  3. Test integration points

    • Verify capability imports
    • Test plugin discovery
    • Validate configurations
  4. Update submodules explicitly

    • Pull capability changes deliberately
    • Review updates before committing
    • Update main repo pointer
  5. Document integration

    • How to use the capability
    • Configuration options
    • Example usage

DON'T

  1. Edit capability code from main repo

    • Opens separate repo/instance instead
    • Respects submodule boundary
    • Maintains clean separation
  2. Hardcode capability paths

    • Use self-declaration instead
    • Generic discovery patterns
    • Interface-based access
  3. Create circular dependencies

    • Main depends on capabilities
    • Capabilities should NOT depend on main
    • Keep dependency graph acyclic
  4. 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


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.