# How Book-to-Skill Distinguishes Chapter Headings from Numbered List Items

> Learn how Book-to-Skill distinguishes chapter headings from list items using its unique three-stage filtering system. Discover the accuracy in identifying text structures.

- Repository: [Virgilio Junior/book-to-skill](https://github.com/virgiliojr94/book-to-skill)
- Tags: internals
- Published: 2026-08-31

---

**Book-to-Skill uses a three-stage filtering system in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) that checks spacing patterns, section body length thresholds, and trailing punctuation to accurately identify chapter headings while ignoring numbered list items.**

The `virgiliojr94/book-to-skill` repository implements a robust text analysis pipeline that must differentiate genuine chapter headings from ordinary numbered lists when parsing book content. Accurately distinguishing between chapter headings and list items ensures the tool correctly counts distinct chapters regardless of language or document formatting. This discrimination relies on specific heuristics implemented in the utility module that examine both formatting patterns and content structure.

## Three-Stage Heading Detection Logic

The core discrimination mechanism operates inside **[`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py)** through three complementary validation stages. Each stage targets a specific false-positive scenario while preserving legitimate chapter headings.

### Stage 1: Spacing-Based Filtering with `_match_chapter_number`

The first filter eliminates list items by enforcing strict whitespace requirements. In the `_match_chapter_number` function, a line qualifies as a chapter heading only if the chapter number is followed by **at least two spaces** before any non-whitespace text.

```python

# Require two spaces after the number → "1  Introduction" is a heading,

# "1. Item" (single space) is a list item.

plain = re.match(r"^([1-9]\d{0,2})\s{2,}\S", s)
if plain:
    return int(plain.group(1))

```

This regex pattern `^([1-9]\d{0,2})\s{2,}\S` eliminates the majority of list items that use single spaces or periods after numerals. A line such as `1  Introduction` matches and returns chapter `1`, while `1. Install the package` fails the pattern and is ignored.

### Stage 2: Body Length Validation via `_numbered_titles_are_structural`

When documents contain many digit-led titles (e.g., `## 1 Introduction`, `## 2 Setup`), Book-to-Skill does not rely on numbering alone. The helper `_numbered_titles_are_structural` examines the **body length** of each section to determine if the series represents genuine chapters or tutorial steps.

```python
def _numbered_titles_are_structural(entries, heading_lines, lines):
    # …collect body sizes…

    return statistics.median(bodies) >= _MIN_NUMBERED_BODY_CHARS

```

This function calculates the median body length across all numbered sections. Only if the median exceeds **`_MIN_NUMBERED_BODY_CHARS = 200`** does the system treat the series as structural chapter headings. This threshold prevents tightly-packed list items or short tutorial steps from being miscounted as chapters.

### Stage 3: Cross-Reference Filtering with `_HEADING_TAIL`

The final guard validates the content following explicit "Chapter N" patterns. The regex `_HEADING_TAIL` ensures the line ends with punctuation, whitespace, or a capitalized word rather than continuing with lowercase prose.

```python
_HEADING_TAIL = re.compile(r"^\s*$|^\s*[.:\-—–]|^\s+(?![a-z])")

```

If a line reads `Chapter 6 explores Python basics`, the trailing lowercase text causes the match to be rejected as a cross-reference rather than a heading. This filter catches narrative references that mention chapter numbers without representing actual structural boundaries.

## Practical Examples

The following examples demonstrate how these rules apply to real document scenarios:

**Accepted Chapter Heading**

```text
1  Introduction

```

The double space after `1` satisfies the `plain` regex in `_match_chapter_number`, returning chapter `1`.

**Rejected List Item**

```text
1. Install the package

```

The single space and period after the numeral fail the spacing requirement, so the line is classified as a list item.

**Structural Numeric Headings**

```markdown

## 1 Overview

Long explanatory paragraph providing detailed context about the chapter content (approximately 800 characters)

## 2 Details

Another comprehensive section with substantial information (approximately 1200 characters)

```

`_numbered_titles_are_structural` calculates a median body size exceeding 200 characters, marking both headings as genuine chapters.

**Tutorial Steps Treated as List Items**

```markdown

## 1 Step

Brief instruction text

## 2 Step

Another short description

```

With median body length below 200 characters, Book-to-Skill interprets this block as a tutorial list rather than book chapters.

## Implementation Details in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py)

The detection logic resides primarily in the `detect_structure` function and its helpers within **[`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py)**. The module uses Python's `statistics` library to calculate median body lengths efficiently across document sections. The constant `_MIN_NUMBERED_BODY_CHARS` is hardcoded to `200` based on empirical analysis of technical documentation patterns.

The regex `r"^([1-9]\d{0,2})"` restricts chapter numbers to 1-999, preventing false matches on years or IDs. When combined with the spacing requirement `\s{2,}`, this creates a precise signature for manually numbered chapters that distinguishes them from auto-generated list markers.

## Summary

- **Spacing enforcement**: Chapter headings require **two or more spaces** after the number in `_match_chapter_number`, eliminating single-spaced list items.
- **Body length threshold**: The `_numbered_titles_are_structural` function requires a **median body length of at least 200 characters** to treat numbered headings as chapters.
- **Tail validation**: The `_HEADING_TAIL` regex rejects lines ending with lowercase text to prevent cross-references from being counted as headings.
- **File location**: All detection logic is implemented in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) with comprehensive test coverage in [`tests/test_numbered_headings.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tests/test_numbered_headings.py).

## Frequently Asked Questions

### What spacing pattern identifies a chapter heading versus a list item?

Book-to-Skill requires **at least two spaces** between the number and the text content. A line like `1  Introduction` qualifies as a chapter heading, while `1. Introduction` or `1 Introduction` (single space) is treated as a list item according to the regex in `_match_chapter_number`.

### How does Book-to-Skill prevent short tutorials from being counted as chapters?

The system analyzes the **median body length** of all numbered sections through `_numbered_titles_are_structural`. If the median content length falls below `_MIN_NUMBERED_BODY_CHARS` (200 characters), the entire numbered series is classified as tutorial steps or list items rather than book chapters.

### What is the minimum body length threshold for structural headings?

The constant **`_MIN_NUMBERED_BODY_CHARS = 200`** defines the minimum median character count required for numbered headings to be considered structural chapters. This threshold filters out quick-start guides and enumerated instructions while preserving substantial book chapters.

### How does the heading-tail guard filter out cross-references?

The `_HEADING_TAIL` regex validates that lines end with punctuation, whitespace, or capitalized words. If a line continues with lowercase text after a number (e.g., "Chapter 6 explains"), the pattern rejects it as a narrative cross-reference rather than a formal chapter heading boundary.