Skip to content
Back to skills

890 Code Adaptation Ab978a8a

ASecurity

When and how to modify ADAPTABLE code when configuration isn't enough.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
testingpythongobashtestinggit

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 890-code-adaptation_ab978a8a --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 890 Code Adaptation Ab978a8a?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 890 Code Adaptation Ab978a8a
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-890-code-adaptation-ab978a8a/badge)](https://www.skillsdirectory.com/skills/tools-only-890-code-adaptation-ab978a8a)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Code Adaptation Guide

When and how to modify ADAPTABLE code when configuration isn't enough.

## When to Modify Code

Only modify code when:

1. Configuration changes have been tried (2-3 attempts)
2. The problem is clearly in heuristics, not data
3. The required change is isolated to ADAPTABLE files

**Never modify FROZEN files** (see [architecture.md](architecture.md)).

---

## ADAPTABLE Files Reference

| File | Purpose | Common Modifications |
|------|---------|---------------------|
| `strategies/simple_strategy.py` | Main conversion logic | Add preprocessing steps |
| `strategies/base_strategy.py` | Template pattern | Add new hook methods |
| `detectors/structure_classifier.py` | Role detection | Tune heading heuristics |
| `detectors/reading_order/y_sorter.py` | Single-column ordering | Custom sort logic |
| `detectors/reading_order/xy_cut_sorter.py` | Multi-column ordering | Threshold adjustments |
| `detectors/footnote_detector.py` | Footnote patterns | Add new patterns |
| `detectors/endnote_formatter.py` | HTML output | Custom styling |

---

## Pattern 1: Adding a New Strategy

When the default `SimpleStrategy` doesn't work for a specific PDF type.

### Steps

1. Create new file in `strategies/`:

```python
# strategies/academic_strategy.py

from .base_strategy import BaseStrategy
from detectors.reading_order.xy_cut_sorter import XYCutSorter

class AcademicStrategy(BaseStrategy):
    """Strategy for academic papers with columns and citations."""

    def order_blocks(self, blocks, config):
        # Always use XY-cut for academic papers
        sorter = XYCutSorter()
        ordered, confidence = sorter.sort(blocks)
        return ordered, confidence

    def detect_structure(self, blocks, config, images, metadata):
        # Custom structure detection for academic format
        # ... your implementation
        pass
```

2. Register in `converter.py`:

```python
from .strategies.academic_strategy import AcademicStrategy

STRATEGY_REGISTRY = {
    "simple": SimpleStrategy,
    "academic": AcademicStrategy,  # Add this
}
```

3. Use the new strategy:

```python
converter = Converter(strategy="academic")
result = converter.convert(pdf_path, epub_path, config)
```

---

## Pattern 2: Adding a Footnote Pattern

When footnotes use a non-standard format.

### Steps

1. Identify the pattern in the PDF:

```python
import fitz
doc = fitz.open("input.pdf")
text = doc[5].get_text()
# Look for: †1, *1, ^1, etc.
```

2. Add pattern to `footnote_detector.py`:

```python
# In detectors/footnote_detector.py

PATTERNS = {
    'bracket': re.compile(r'\[(\d{1,2})\]'),
    'paren': re.compile(r'\((\d{1,2})\)'),
    'period': re.compile(r'(?<=\w)\.(\d{1,2})(?=\s|$)'),
    'superscript': re.compile(r'(?<=[a-zA-Z])(\d{1,2})(?=\s|$)'),
    # Add your pattern:
    'dagger': re.compile(r'†(\d{1,2})'),
    'asterisk': re.compile(r'\*(\d{1,2})'),
}
```

3. Test:

```python
from conversion.detectors.footnote_detector import FootnoteDetector

detector = FootnoteDetector(patterns=['dagger'])
refs = detector.find_references("This statement†1 is cited.")
print(refs)  # [FootnoteRef(number=1, original_text='†1', ...)]
```

---

## Pattern 3: Tuning Heading Detection

When the default heuristics don't identify headings correctly.

### Steps

1. Understand the current scoring in `structure_classifier.py`:

```python
def _calculate_heading_score(self, block, prev, next_b, body_size, body_flags):
    score = 0.0
    signals = []

    # Font size check
    if block.font_size > body_size * self.heading_threshold:
        score += 0.4
        signals.append(f"font_size +0.4")

    # Bold check
    if block.flags & 16:  # Bold flag
        score += 0.2
        signals.append("bold +0.2")

    # Short text (headings are usually short)
    if len(block.text) < 100:
        score += 0.1
        signals.append("short +0.1")

    # ... more signals
    return score, signals
```

2. Add new signals or adjust weights:

```python
def _calculate_heading_score(self, block, prev, next_b, body_size, body_flags):
    score = 0.0
    signals = []

    # Existing checks...

    # NEW: Check for all-caps (common heading style)
    if block.text.isupper() and len(block.text) < 50:
        score += 0.3
        signals.append("all_caps +0.3")

    # NEW: Check for centered text (approximate)
    page_center = 0.5  # Assume page center
    block_center = (block.x0 + block.x1) / 2 / page_width
    if abs(block_center - page_center) < 0.1:
        score += 0.15
        signals.append("centered +0.15")

    return score, signals
```

3. Test with debug output:

```python
classifier = StructureClassifier()
blocks = classifier.classify(text_blocks)

for block in blocks:
    if block.score > 0:
        print(f"{block.role}: {block.original_block.text[:50]}")
        print(f"  Score: {block.score}, Signals: {block.debug_signals}")
```

---

## Pattern 4: Custom Reading Order

When neither `y_sort` nor `xy_cut` produces correct order.

### Steps

1. Create new sorter in `reading_order/`:

```python
# detectors/reading_order/custom_sorter.py

from .base import BlockSorter

class CustomSorter(BlockSorter):
    """Custom reading order for specific layout type."""

    def sort(self, blocks):
        # Your custom sorting logic
        # Example: Sort by sections first, then by position within section

        sections = self._detect_sections(blocks)
        ordered = []

        for section in sections:
            # Sort blocks within section by y, then x
            section_blocks = sorted(
                [b for b in blocks if self._in_section(b, section)],
                key=lambda b: (b.y0, b.x0)
            )
            ordered.extend(section_blocks)

        confidence = self._calculate_confidence(ordered)
        return ordered, confidence

    def _detect_sections(self, blocks):
        # Detect logical sections (e.g., by horizontal lines)
        ...

    def _in_section(self, block, section):
        # Check if block belongs to section
        ...

    def _calculate_confidence(self, ordered):
        # Estimate confidence in ordering
        return 0.85
```

2. Register sorter:

```python
# In simple_strategy.py or base_strategy.py
from .detectors.reading_order.custom_sorter import CustomSorter

def order_blocks(self, blocks, config):
    if config.reading_order_strategy == "custom":
        sorter = CustomSorter()
    elif config.reading_order_strategy == "xy_cut":
        sorter = XYCutSorter()
    else:
        sorter = YSorter()

    return sorter.sort(blocks)
```

---

## Pattern 5: Modifying HTML Output

When EPUB styling needs customization.

### EndnoteFormatter Customization

```python
# In detectors/endnote_formatter.py

class EndnoteFormatter:
    def format_endnotes(self, blocks):
        html_parts = []

        for block in blocks:
            if block.role != "endnote":
                continue

            num = block.endnote_num
            content = self._extract_content(block)

            # Customize the HTML output:
            html_parts.append(
                f'<div class="endnote" id="note{num}">'
                f'  <span class="endnote-number">{num}</span>'
                f'  <span class="endnote-text">{content}</span>'
                f'</div>'
            )

        return '\n'.join(html_parts)
```

### EPUBBuilder Stylesheet

To modify default styles, edit `core/epub_builder.py`:

```python
def _write_stylesheet(self, temp_path):
    css = '''
    /* Custom styles */
    .endnote {
        margin: 1em 0;
        padding-left: 2em;
        text-indent: -2em;
    }

    .endnote-number {
        font-weight: bold;
        color: #0066cc;
    }
    '''
    # ... write to file
```

**Note:** `epub_builder.py` is technically FROZEN, but stylesheet changes are cosmetic and safe.

---

## Testing Your Changes

After any code modification:

1. Run unit tests:
```bash
python -m pytest tests/ -v
```

2. Run specific test file:
```bash
python -m pytest tests/conversion/test_structure_classifier.py -v
```

3. Test on real PDF:
```bash
python -m scripts.convert input.pdf output.epub
python -m scripts.validate input.pdf output.epub
```

4. Verify validation passes:
```
Completeness: > 95%
Order score: > 80%
```

---

## Documenting Changes

When you modify code:

1. Add comment explaining the change:
```python
# MODIFIED: Added all-caps heading detection for Book XYZ
# Original threshold was insufficient for this PDF style
if block.text.isupper():
    score += 0.3
```

2. Update tests if behavior changes
3. Consider if change should be configurable instead

---

## Rollback Strategy

If changes break things:

1. Git revert:
```bash
git checkout -- path/to/file.py
```

2. Or restore from known good state:
```bash
git stash
python -m pytest tests/  # Verify tests pass
git stash pop  # Re-apply changes
```

---

## When to Ask for Help

If you're unsure about a change:
- It affects multiple files
- It changes fundamental algorithms
- Tests start failing
- You need to modify FROZEN files

Stop and reconsider the approach. Often there's a configuration solution that was missed.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.