---
title: "Relationship Mapping"
description: "Creating, managing, and maintaining links between artifacts in TraceRTM"
---

# Relationship Mapping

Relationship mapping is the process of establishing and maintaining links between artifacts. A well-mapped traceability graph enables impact analysis, coverage reporting, and compliance verification.

## Creating Links

### Manual Linking

```python
from tracertm import TraceRTMClient

client = TraceRTMClient()

# Create a single link
client.create_link(
    source_id="CODE-001",
    target_id="FEAT-001",
    link_type="realizes"
)

# Create link with metadata
client.create_link(
    source_id="TEST-001",
    target_id="FEAT-001",
    link_type="verifies",
    metadata={
        "coverage_percentage": 85,
        "test_type": "integration",
        "automated": True
    }
)
```

### CLI Linking

```bash
# Create link
tracertm link CODE-001 FEAT-001 --type realizes

# Link with metadata
tracertm link TEST-001 FEAT-001 --type verifies \
  --metadata '{"coverage_percentage": 85}'

# Create multiple links at once
tracertm link CODE-001 FEAT-001,FEAT-002,FEAT-003 --type realizes

# Interactive linking
tracertm link --interactive
# > Source: CODE-001
# > Target: FEAT-001
# > Type: realizes
# > Created: CODE-001 --realizes--> FEAT-001
```

### Bulk Linking

```python
# Link multiple items
links = [
    ("CODE-001", "FEAT-001", "realizes"),
    ("CODE-002", "FEAT-001", "realizes"),
    ("CODE-003", "FEAT-002", "realizes"),
    ("TEST-001", "FEAT-001", "verifies"),
    ("TEST-002", "FEAT-001", "verifies"),
]

for source, target, link_type in links:
    client.create_link(source, target, link_type)

# Or use bulk API
client.create_links_bulk([
    {"source": "CODE-001", "target": "FEAT-001", "type": "realizes"},
    {"source": "CODE-002", "target": "FEAT-001", "type": "realizes"},
    {"source": "TEST-001", "target": "FEAT-001", "type": "verifies"},
])
```

## Auto-Linking

### From Code Comments

TraceRTM can scan code for traceability markers:

```python
# src/auth/login.py
"""
Authentication handler module.

Realizes: FEAT-001 (User Authentication)
Implements: DES-001 (Login Flow Design)
"""

def login(credentials):
    """
    Process login request.

    Realizes: FEAT-001-AC1 (Valid credentials return token)
    Tests: TEST-001, TEST-002
    """
    pass
```

### Scanner Configuration

```yaml
# .tracertm/scanner.yaml
auto_link:
  enabled: true

  # Scan patterns
  sources:
    - path: "src/**/*.py"
      markers:
        realizes: "Realizes:\\s*([\\w-,\\s]+)"
        implements: "Implements:\\s*([\\w-,\\s]+)"
        tests: "Tests:\\s*([\\w-,\\s]+)"

    - path: "tests/**/*.py"
      markers:
        verifies: "Verifies:\\s*([\\w-,\\s]+)"
        tests: "Tests:\\s*([\\w-,\\s]+)"

  # File naming conventions
  patterns:
    # test_FEAT_001_login.py → verifies FEAT-001
    - regex: "test_([A-Z]+)_?(\\d+)"
      link_type: verifies
      target_format: "{1}-{2}"
```

### Running Auto-Link

```bash
# Scan and create links
tracertm scan --auto-link

# Output:
# Scanning src/...
# Found 45 traceability markers
#
# Creating links:
#   CODE-001 → FEAT-001 (realizes)
#   CODE-002 → FEAT-001 (realizes)
#   CODE-003 → DES-001 (implements)
#   ...
#
# Created: 38 new links
# Updated: 7 existing links
# Skipped: 0 (no changes)

# Dry run (preview only)
tracertm scan --auto-link --dry-run

# Scan specific path
tracertm scan src/auth/ --auto-link
```

## Link Inference

### From Test Execution

```python
# Infer links from test execution results
def link_from_test_results(results_file: str):
    with open(results_file) as f:
        results = json.load(f)

    for test_result in results["tests"]:
        test_file = test_result["file"]

        # Find or create test artifact
        test = client.find_or_create_item(
            item_type="test",
            metadata={"file_path": test_file}
        )

        # Link based on coverage
        for covered_file in test_result.get("covered_files", []):
            code = client.find_item(
                item_type="code",
                metadata={"file_path": covered_file}
            )
            if code:
                client.create_link(test.id, code.id, "tests")
```

### From Import Analysis

```python
# Infer dependencies from imports
def link_from_imports(code_artifact):
    imports = analyze_imports(code_artifact.metadata["file_path"])

    for imported_module in imports:
        target = client.find_item(
            item_type="code",
            metadata={"module": imported_module}
        )
        if target:
            client.create_link(
                code_artifact.id,
                target.id,
                "depends_on"
            )
```

## Mapping Strategies

### Top-Down Mapping

Start from requirements and map downward:

```
Requirements → Design → Code → Tests
```

```python
# Map feature to its implementation chain
def map_top_down(feature_id: str):
    feature = client.get_item(feature_id)

    # 1. Link to designs
    designs = find_designs_for_feature(feature)
    for design in designs:
        client.create_link(design.id, feature_id, "realizes")

        # 2. Link designs to code
        code_modules = find_code_for_design(design)
        for code in code_modules:
            client.create_link(code.id, design.id, "implements")

            # 3. Link code to tests
            tests = find_tests_for_code(code)
            for test in tests:
                client.create_link(test.id, code.id, "tests")
                client.create_link(test.id, feature_id, "verifies")
```

### Bottom-Up Mapping

Start from code and map upward:

```
Code → Design → Requirements
```

```python
# Map code to its requirements
def map_bottom_up(code_id: str):
    code = client.get_item(code_id)

    # 1. Find implementing design
    design = infer_design_from_code(code)
    if design:
        client.create_link(code_id, design.id, "implements")

        # 2. Find realized requirement
        feature = infer_feature_from_design(design)
        if feature:
            client.create_link(design.id, feature.id, "realizes")
            client.create_link(code_id, feature.id, "realizes")
```

### Meet-in-the-Middle

Map from both directions and reconcile:

```python
# Map from both ends
def map_bidirectional(project_id: str):
    # Top-down: explicit mappings from specs
    requirements = client.list_items(
        project_id=project_id,
        item_type="feature"
    )
    for req in requirements:
        map_top_down(req.id)

    # Bottom-up: infer from code analysis
    code_modules = client.list_items(
        project_id=project_id,
        item_type="code"
    )
    for code in code_modules:
        map_bottom_up(code.id)

    # Reconcile and fill gaps
    reconcile_mappings(project_id)
```

## Mapping Coverage

### Coverage Reports

```bash
# Check mapping coverage
tracertm coverage

# Output:
# Traceability Coverage Report
# ════════════════════════════
#
# Feature → Code (realizes)
#   Coverage: 85% (51/60)
#   Missing: FEAT-042, FEAT-055, FEAT-067...
#
# Feature → Test (verifies)
#   Coverage: 92% (55/60)
#   Missing: FEAT-042, FEAT-089...
#
# Code → Test (tested_by)
#   Coverage: 78% (156/200)
#   Missing: CODE-045, CODE-089...
#
# Design → Code (implemented_by)
#   Coverage: 95% (38/40)
#   Missing: DES-033, DES-038

# Coverage for specific relationship
tracertm coverage --from feature --to code --type realizes
```

### Gap Analysis

```python
# Find unmapped artifacts
def find_mapping_gaps():
    # Features without code
    features = client.list_items(item_type="feature")
    for feature in features:
        code = client.trace_backward(feature.id, "realizes", "code")
        if not code:
            print(f"Gap: {feature.id} has no implementing code")

    # Code without tests
    code_items = client.list_items(item_type="code")
    for code in code_items:
        tests = client.trace_backward(code.id, "tests", "test")
        if not tests:
            print(f"Gap: {code.id} has no tests")

    # Features without tests
    for feature in features:
        tests = client.trace_backward(feature.id, "verifies", "test")
        if not tests:
            print(f"Gap: {feature.id} has no verifying tests")
```

## Link Maintenance

### Updating Links

```python
# Update link metadata
client.update_link(
    source_id="TEST-001",
    target_id="FEAT-001",
    link_type="verifies",
    metadata={
        "coverage_percentage": 95,  # Updated
        "last_verified": "2024-12-01"
    }
)
```

### Removing Links

```python
# Remove specific link
client.delete_link(
    source_id="CODE-001",
    target_id="FEAT-001",
    link_type="realizes"
)

# Remove all links of a type from an item
client.delete_links(
    source_id="CODE-001",
    link_type="realizes"
)
```

```bash
# CLI
tracertm unlink CODE-001 FEAT-001 --type realizes

# Remove with confirmation
tracertm unlink CODE-001 FEAT-001 --type realizes --confirm
```

### Pruning Stale Links

```python
# Find and remove stale links
def prune_stale_links():
    """Remove links to deleted or archived items."""

    all_links = client.list_links()
    stale = []

    for link in all_links:
        source = client.get_item(link.source_id)
        target = client.get_item(link.target_id)

        # Check if either end is missing or archived
        if not source or not target:
            stale.append(link)
        elif source.status == "archived" or target.status == "archived":
            stale.append(link)

    # Remove stale links
    for link in stale:
        client.delete_link(link.source_id, link.target_id, link.link_type)
        print(f"Pruned: {link.source_id} → {link.target_id}")

    return len(stale)
```

```bash
# CLI pruning
tracertm links prune --stale

# Output:
# Scanning for stale links...
# Found 12 stale links
#
# Stale links:
#   CODE-045 → FEAT-001 (realizes) - CODE-045 deleted
#   TEST-089 → CODE-045 (tests) - CODE-045 deleted
#   ...
#
# Remove these links? [y/N] y
# Removed 12 stale links
```

## Link Validation

### Validation Rules

```yaml
# .tracertm/config.yaml
link_validation:
  rules:
    # Every feature must have at least one test
    - source_type: feature
      link_type: verified_by
      min_count: 1
      severity: error
      message: "Feature must have at least one verifying test"

    # Code should implement a design
    - source_type: code
      link_type: implements
      min_count: 1
      severity: warning
      message: "Code should link to its design"

    # Prevent circular dependencies
    - link_type: depends_on
      no_cycles: true
      severity: error
      message: "Circular dependency detected"
```

### Running Validation

```bash
# Validate all links
tracertm validate links

# Output:
# Link Validation Report
# ═════════════════════════
#
# Errors (3):
#   FEAT-042: Missing verified_by link (requires at least 1 test)
#   FEAT-055: Missing verified_by link (requires at least 1 test)
#   Cycle detected: FEAT-001 → FEAT-002 → FEAT-003 → FEAT-001
#
# Warnings (5):
#   CODE-045: Missing implements link (should link to design)
#   CODE-089: Missing implements link (should link to design)
#   ...
#
# Summary:
#   Total links: 780
#   Valid: 772
#   Errors: 3
#   Warnings: 5
```

## Import/Export

### Export Links

```bash
# Export all links
tracertm links export --format csv --output links.csv

# Export specific types
tracertm links export --type realizes,verifies --format json

# Export with items
tracertm export --include-links --format yaml
```

### Import Links

```python
# Import links from CSV
import csv

with open("links.csv") as f:
    reader = csv.DictReader(f)
    for row in reader:
        client.create_link(
            source_id=row["source"],
            target_id=row["target"],
            link_type=row["type"]
        )
```

```bash
# CLI import
tracertm links import links.csv

# Import with validation
tracertm links import links.csv --validate --dry-run
```

### Migration

```python
# Migrate links from external system
def migrate_from_jira(jira_links):
    type_mapping = {
        "Blocks": "blocks",
        "is blocked by": "blocked_by",
        "implements": "realizes",
        "is tested by": "verified_by",
        "duplicates": "related_to"
    }

    for jira_link in jira_links:
        tracertm_type = type_mapping.get(jira_link["type"])
        if tracertm_type:
            # Map Jira keys to TraceRTM IDs
            source = client.find_item(
                metadata={"jira_key": jira_link["source"]}
            )
            target = client.find_item(
                metadata={"jira_key": jira_link["target"]}
            )

            if source and target:
                client.create_link(source.id, target.id, tracertm_type)
```

## Best Practices

### 1. Link Early and Often

```python
# Create links when creating artifacts
code = client.create_item(
    title="Auth Handler",
    item_type="code",
    metadata={"file_path": "src/auth/handler.py"}
)

# Immediately link to what it implements
client.create_link(code.id, "DES-001", "implements")
client.create_link(code.id, "FEAT-001", "realizes")
```

### 2. Use Auto-Linking Where Possible

```yaml
# Configure auto-linking for consistent results
auto_link:
  enabled: true
  on_commit: true  # Run on every commit
  on_scan: true    # Run during tracertm scan
```

### 3. Validate Regularly

```yaml
# CI/CD integration
# .github/workflows/traceability.yml
- name: Validate Links
  run: |
    tracertm validate links --fail-on-error
```

### 4. Document Mapping Decisions

```python
# Add metadata explaining link rationale
client.create_link(
    "CODE-001",
    "FEAT-001",
    "realizes",
    metadata={
        "rationale": "Handles authentication flow for FEAT-001",
        "mapped_by": "auto-scanner",
        "confidence": 0.95
    }
)
```

## Related Topics

- [Relationship Types](/docs/wiki/concepts/relationships/types) - Link type reference
- [Visualization](/docs/wiki/concepts/relationships/visualization) - Viewing mappings
- [Impact Analysis](/docs/wiki/concepts/relationships/impact-analysis) - Using mappings
- [Coverage Reports](/docs/getting-started/first-project/reports) - Gap analysis

