How book-to-skill Detects Chapters with Digit-Led Titles: A Deep Dive into the Structural Analysis Algorithm
book-to-skill uses a two-stage validation process in book_to_skill/utils.py that examines both heading frequency and body text length to distinguish true chapter headings from numbered lists.
The open-source book-to-skill repository by virgiliojr94 provides intelligent chapter detection for Markdown and AsciiDoc books. When processing digit-led titles like ## 5 Setup or 1 Introduction, the system must avoid false positives from tutorial step numbers or simple list items. This article explains how the structural scanning algorithm validates these headings before accepting them as genuine chapters.
The Problem: Digit-Led Titles vs. Numbered Lists
Books frequently use numbers in chapter titles—Chapter 5, 5. The Journey Begins, or simply 5 Setup. However, technical documentation also contains numbered lists (1. Install Python) and tutorial steps that must not be mistaken for chapter boundaries. The detect_structure function in book_to_skill/utils.py (lines [221-272]) orchestrates multiple detection strategies to resolve this ambiguity.
Stage 1: Structural Scanning with Temporary Hold
The core validation lives in _structural_chapter_count (lines [85-94] of utils.py). During the initial pass through the source document, the scanner:
-
Collects all ATX headings (
# Heading) and setext headings (===/---) -
Temporarily sets aside digit-led titles in a
numbereddictionary -
Requires further validation before merging them into the final
levelsmap
This cautious approach prevents premature classification of suspect headings.
The Validation Helper: _numbered_titles_are_structural
Digit-led titles undergo rigorous scrutiny through _numbered_titles_are_structural (lines [82-101]):
def _numbered_titles_are_structural(
entries: list[tuple[str, int]], heading_lines: list[int], lines: list[str]
) -> bool:
Two conditions must both pass:
| Criterion | Threshold | Purpose |
|---|---|---|
| Minimum distinct numbered headings | _MIN_NUMBERED_TITLES = 3 |
Ensures systematic chapter numbering, not isolated instances |
| Median body text length | _MIN_NUMBERED_BODY_CHARS = 200 |
Rejects short "step" headings typical of tutorials |
The median-body test is particularly clever. Short numbered headings with minimal intervening text—common in how-to guides—produce a low median and fail validation. Substantial chapters with hundreds of lines between headings pass cleanly.
Passing headings merge into levels at lines [71-73], becoming genuine chapter markers.
Stage 2: Plain Numbered Heading Detection
For simpler "Chapter N" formats without explicit text like "Chapter", _match_chapter_number provides a fast path (lines [441-449]):
plain = re.match(r"^([1-9]\d{0,2})\s{2,}\S", s)
if plain:
return int(plain.group(1))
The regex pattern ^([1-9]\d{0,2})\s{2,}\S encodes critical safeguards:
^— Anchors to start of line[1-9]— No leading zeros permitted\d{0,2}— Allows 1-3 digit numbers (1-999)\s{2,}— Requires at least two spaces after the number\S— Demands non-whitespace content follows
The two-space requirement specifically excludes list items formatted as 1. Item or 1) Step, which typically use one space or punctuation immediately after the digit.
Integration: The detect_structure Entry Point
The public API detect_structure (lines [221-272]) orchestrates detection with intelligent fallback:
- First attempts explicit "Chapter N" pattern matching
- If fewer than two numeric headings found, falls back to structural scanning
- Returns final count as
chapters_detectedand method used aschapters_method
Practical Code Examples
Example 1: Detecting Digit-Led Chapters in Markdown
from book_to_skill.utils import detect_structure
md = """# My Book
## 1 Introduction
Some intro text well over two hundred characters long to ensure the median body length test passes with substantial content here...
## 2 Getting Started
Similarly lengthy content for chapter two that demonstrates real chapter structure rather than tutorial steps...
## 3 Advanced Topics
More substantial content that validates this as a genuine three-chapter book structure...
"""
info = detect_structure(md)
print(info['chapters_detected']) # → 3
print(info['chapters_method']) # → structural
The three numbered headings with substantial intervening text pass both validation criteria.
Example 2: Plain Numbered Heading with Two Spaces
md = "1 Chapter One\n\nText of chapter one.\n"
info = detect_structure(md)
print(info['chapters_detected']) # → 1 (numeric branch)
The double space after 1 satisfies the regex, routing through the numeric detection branch.
Example 3: List-Style Numbering Rejected
md = "1. Item one\n2. Item two\n"
info = detect_structure(md)
print(info['chapters_detected']) # → 0
Single space after number plus period causes regex match failure—correctly ignored as list items.
Key Source Files and Test Coverage
| File | Responsibility |
|---|---|
book_to_skill/utils.py |
Core detection logic: _structural_chapter_count, _numbered_titles_are_structural, _match_chapter_number, detect_structure |
book_to_skill/parsers/text.py |
Plain-text extraction feeding detection |
tests/test_numbered_headings.py |
Unit tests for digit-led title scenarios |
tests/test_structural_chapter_count.py |
Structural detection validation |
Direct source links:
- Structural count implementation
- Numbered-titles validation helper
- Plain numbered regex
detect_structureentry point
Summary
- book-to-skill detects digit-led chapter titles through a two-stage validation in
book_to_skill/utils.pythat combines structural scanning with body-length analysis - Temporary hold pattern: Digit-led headings isolate in
numbereddict until_numbered_titles_are_structuralvalidates minimum count (3+) and median body length (200+ chars) - Regex safeguards: The
plainpattern in_match_chapter_numberrequires double spaces after digits, filtering list-item false positives - Intelligent fallback:
detect_structuretries explicit patterns first, then structural scanning, returning method provenance for transparency - Well-tested: Dedicated test files verify both numeric and structural detection paths
Frequently Asked Questions
How does book-to-skill avoid mistaking tutorial steps for chapters?
The _numbered_titles_are_structural function applies a median body length threshold of 200 characters (_MIN_NUMBERED_BODY_CHARS). Tutorial steps typically have short explanatory text between headings, producing a low median that fails validation. Substantial chapters with hundreds of lines pass this test.
Why does the plain numbered regex require two spaces after the number?
The pattern r"^([1-9]\d{0,2})\s{2,}\S" uses \s{2,} to distinguish chapter titles like 1 Introduction from list items formatted as 1. Item or 1) Step. Single-space or punctuation-immediate formats fail matching, preventing false positives.
What happens if a book has only one or two numbered headings?
Fewer than _MIN_NUMBERED_TITLES = 3 distinct numbered headings causes _numbered_titles_are_structural to return False. These headings remain excluded from the levels map. However, detect_structure may still identify chapters through explicit "Chapter N" pattern matching before falling back to structural methods.
Can leading zeros appear in detected chapter numbers?
No. The regex [1-9]\d{0,2} explicitly requires the first digit to be 1-9, excluding formats like 01 or 005. This matches conventional chapter numbering while rejecting zero-padded list indices.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →