Use when deploying MkDocs documentation to GitHub Pages with GitHub Actions - covers Python-Markdown gotchas (indentation, footnotes, grid tables), workflow configuration, and systematic markdown fixing
Handles MkDocs to GitHub Pages deployment, fixing Python-Markdown rendering issues like grid tables, nested list indentation, and footnote formatting. Use when documentation fails to build or render correctly due to markdown syntax problems.
/plugin marketplace add SecurityRonin/ronin-marketplace/plugin install docs-skills@ronin-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
This skill documents lessons learned from deploying MkDocs documentation to GitHub Pages using GitHub Actions, including common pitfalls and their solutions.
mkdocs gh-deploy (Deprecated)- name: Deploy to GitHub Pages
run: mkdocs gh-deploy --force --clean --verbose
Issues:
permissions:
contents: write
pages: write # Required for GitHub Pages
id-token: write # Required for OIDC authentication
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build MkDocs site
run: mkdocs build --site-dir output/site
- name: Upload artifact for GitHub Pages
uses: actions/upload-pages-artifact@v3
with:
path: output/site
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
Benefits:
CRITICAL: In GitHub repository settings:
Problem: Pandoc-style grid tables (used in policy documents) not rendering correctly.
Example of Grid Table Syntax:
+-----------------+-----------------+--------------------------------------------------------------+
| **Consequence** | **Consequence** | **Description** |
| **Level** | **Score** | |
+=================+=================+==============================================================+
| Low | 0 | Loss of confidentiality... |
+-----------------+-----------------+--------------------------------------------------------------+
Solution: Add markdown-grid-tables extension
# mkdocs.yml
markdown_extensions:
- markdown_grid_tables # Supports Pandoc/reStructuredText grid tables
Installation:
pip install markdown-grid-tables
In GitHub Actions:
- name: Install MkDocs and extensions
run: |
pip install mkdocs-material markdown-grid-tables
Problem: Nested list items and bullets rendering with line numbers in gray code blocks.
Root Cause:
i. as list markers (roman numerals not supported)Example of Broken Markdown:
a. **Side-Channel Attack Mitigations:**
i. **Timing Attacks:**
- All cryptographic operations must be constant-time
- Hardware wallets use secure elements
What Happens:
i. **Timing Attacks:** (4 spaces) is treated as a code blockSolution Part 1: Fix Indentation Change 4-space indent to 3-space indent:
a. **Side-Channel Attack Mitigations:**
i. **Timing Attacks:**
- All cryptographic operations must be constant-time
- Hardware wallets use secure elements
Solution Part 2: Use Standard List Markers
Convert i. to 1. (Python-Markdown recognizes numbered lists):
a. **Side-Channel Attack Mitigations:**
1. **Timing Attacks:**
- All cryptographic operations must be constant-time
- Hardware wallets use secure elements
Automation Script:
#!/usr/bin/env python3
import re
from pathlib import Path
def fix_lists(content):
"""Fix list markers and indentation."""
lines = content.split('\n')
fixed_lines = []
for line in lines:
# Replace 'i.' with '1.' at any indentation
if re.match(r'^(\s+)i\.\s', line):
fixed_line = re.sub(r'^(\s+)i\.', r'\g<1>1.', line)
fixed_lines.append(fixed_line)
else:
fixed_lines.append(line)
return '\n'.join(fixed_lines)
# Apply to all markdown files
for md_file in Path('policies').glob('*.md'):
content = md_file.read_text()
fixed = fix_lists(content)
md_file.write_text(fixed)
Problem: Footnotes/endnotes render with only the heading visible, missing detailed WHAT/WHY/WHERE content.
Root Cause: Python-Markdown footnotes extension requires ALL continuation lines (after the first line) to be indented with exactly 4 spaces.
Example of Broken Markdown:
[^15]: ### Side-Channel Attack Mitigations
#### WHAT: Requirement Definition
Protection against timing attacks...
Result: Only "### Side-Channel Attack Mitigations" renders in the footnote.
Solution: Indent ALL continuation lines with 4 spaces:
[^15]: ### Side-Channel Attack Mitigations
#### WHAT: Requirement Definition
Protection against timing attacks...
#### WHY: Security Rationale
- **Timing attacks**: Details here...
Automated Fix:
See ~/.claude/skills/mkdocs-footnotes-formatting.md for comprehensive guide and automation scripts.
Quick Fix Script:
import re
from pathlib import Path
def fix_footnote_indentation(content: str) -> str:
lines = content.split('\n')
result = []
in_footnote = False
for line in lines:
if re.match(r'^\[\^[0-9]+\]:', line):
in_footnote = True
result.append(line)
continue
if in_footnote:
if (line.strip() == '---' or
re.match(r'^\[\^[0-9]+\]:', line) or
re.match(r'^#{1,2}\s+[^#]', line)):
in_footnote = False
result.append(line)
continue
if line.startswith(' ') or line.strip() == '':
result.append(line)
else:
result.append(' ' + line)
else:
result.append(line)
return '\n'.join(result)
Impact: 12 files fixed, 855 lines indented for proper footnote rendering.
Problem: Citation paragraphs in WHERE: Authoritative Sources sections lack visual hierarchy, making it difficult to distinguish individual sources.
Example of Problematic Formatting:
#### WHERE: Authoritative Sources
Citation text here
— Source, Date
Another citation
— Source, Date
Result: Renders as plain paragraphs without clear separation between citations.
Solution: Convert citations to bulleted lists:
#### WHERE: Authoritative Sources
- Citation text here
— Source, Date
- Another citation
— Source, Date
Automated Fix:
import re
from pathlib import Path
def fix_where_citations(content: str) -> str:
"""Convert WHERE section citations to bullet points."""
lines = content.split('\n')
result = []
in_where_section = False
i = 0
while i < len(lines):
line = lines[i]
if '#### WHERE: Authoritative Sources' in line:
in_where_section = True
result.append(line)
i += 1
# Skip blank line after header
if i < len(lines) and lines[i].strip() == '':
result.append(lines[i])
i += 1
# Process citations
while i < len(lines):
current_line = lines[i]
# Exit on section boundary
if (current_line.strip() == '---' or
current_line.strip().startswith('####') or
re.match(r'^\[\^[0-9]+\]:', current_line)):
in_where_section = False
break
# Add bullet to citation text
if (current_line.strip() != '' and
not current_line.lstrip().startswith('—') and
not current_line.lstrip().startswith('-')):
indent = len(current_line) - len(current_line.lstrip())
citation_text = current_line.strip()
result.append(' ' * indent + '- ' + citation_text + ' ')
i += 1
# Handle attribution line (starts with —)
if i < len(lines) and lines[i].lstrip().startswith('—'):
attribution_line = lines[i]
attr_indent = len(attribution_line) - len(attribution_line.lstrip())
attribution_text = attribution_line.strip()
result.append(' ' * (indent + 2) + attribution_text)
i += 1
continue
result.append(current_line)
i += 1
else:
result.append(line)
i += 1
return '\n'.join(result)
Impact: Better visual hierarchy, clearer source attribution, proper HTML <ul> rendering.
Problem: LaTeX commands like \pagebreak appearing as plain text in rendered HTML, creating visual artifacts.
Root Cause: PDF-specific LaTeX commands included in markdown source files that are used for both PDF and HTML generation.
Example of Problem:
a. The organization must protect systems as defined in Table 3:
\pagebreak
+---------------------+-------------------+
| **Name of System** | **Cryptographic** |
Result: The text \pagebreak appears literally in HTML between the paragraph and table.
Solution: Remove LaTeX commands from markdown source:
import re
from pathlib import Path
def remove_pagebreaks(content: str) -> str:
"""Remove lines containing \\pagebreak commands."""
lines = content.split('\n')
result = []
skip_next_blank = False
for line in lines:
# Check if line contains \pagebreak
if '\\pagebreak' in line:
skip_next_blank = True
continue
# Skip blank line after pagebreak
if skip_next_blank and line.strip() == '':
skip_next_blank = False
continue
skip_next_blank = False
result.append(line)
return '\n'.join(result)
Best Practice:
Impact: 8 pagebreak commands removed from 6 files, clean HTML rendering.
Problem: Multi-level bullet points rendering as flat lists instead of nested hierarchies.
Example of Broken Markdown:
**Code Verification**:
- Main bullet point
* This nested bullet renders at same level (wrong!)
* Another nested bullet also flat
Root Causes:
What Happens:
* nested renders as sibling, not childSolution Part 1: Use 4-Space Indent for Nested Bullets
Python-Markdown requires 4 spaces for nested list items, not 2:
**Code Verification**:
- Main bullet point
* This nested bullet now renders properly nested
* Another nested bullet also nested correctly
Indentation Rules:
Testing the difference:
# ❌ WRONG: 2-space indent (renders as flat list)
- Item 1
* Subitem A ← Treated as sibling
# ✅ CORRECT: 4-space indent (renders as nested)
- Item 1
* Subitem A ← Properly nested
Solution Part 2: Add Blank Lines Before Lists
Markdown requires a blank line before a list to recognize it as a block-level element:
# ❌ WRONG: No blank line
**Code Verification**:
- Bullet 1 ← Treated as paragraph continuation
# ✅ CORRECT: Blank line before list
**Code Verification**:
- Bullet 1 ← Recognized as list
Automation Scripts:
Fix nested bullet indentation (2→4 spaces):
#!/usr/bin/env python3
import re
from pathlib import Path
def fix_nested_bullets(content):
"""Change 2-space indent to 4-space for nested bullets."""
lines = content.split('\n')
fixed_lines = []
prev_was_bullet = False
for line in lines:
if re.match(r'^(-|\*|\d+\.)\s', line):
prev_was_bullet = True
fixed_lines.append(line)
elif re.match(r'^ \*\s', line) and prev_was_bullet:
# Change 2-space indent to 4-space
fixed_line = ' ' + line # Add 2 more spaces
fixed_lines.append(fixed_line)
else:
if line.strip() == '':
prev_was_bullet = False
fixed_lines.append(line)
return '\n'.join(fixed_lines)
for md_file in Path('policies').glob('*.md'):
content = md_file.read_text()
fixed = fix_nested_bullets(content)
md_file.write_text(fixed)
Add blank lines before lists:
#!/usr/bin/env python3
import re
from pathlib import Path
def fix_list_spacing(content):
"""Add blank lines before lists."""
lines = content.split('\n')
fixed_lines = []
i = 0
while i < len(lines):
current_line = lines[i]
fixed_lines.append(current_line)
if i + 1 < len(lines):
next_line = lines[i + 1]
# Insert blank line if:
# - Current line has content
# - Current line is not a list
# - Next line IS a root-level list item
current_is_content = current_line.strip() != ''
current_is_not_list = not re.match(r'^\s*(-|\*|\d+\.)\s', current_line)
next_is_list = re.match(r'^(-|\*|\d+\.)\s', next_line)
if current_is_content and current_is_not_list and next_is_list:
fixed_lines.append('')
i += 1
return '\n'.join(fixed_lines)
for md_file in Path('policies').glob('*.md'):
content = md_file.read_text()
fixed = fix_list_spacing(content)
md_file.write_text(fixed)
Impact of Fixes:
<ul><li>Parent<ul><li>Child</li></ul></li></ul># mkdocs.yml
markdown_extensions:
# Core extensions
- meta # YAML frontmatter support
- admonition # Note/warning boxes
- tables # Standard GFM tables
- attr_list # Add attributes to elements
- md_in_html # Markdown inside HTML
- def_list # Definition lists
- footnotes # Footnote support
- abbr # Abbreviations with tooltips
# Table of contents
- toc:
permalink: true
toc_depth: 3
# PyMdown Extensions for enhanced features
- pymdownx.details # Collapsible content blocks
- pymdownx.superfences # Enhanced code blocks
- pymdownx.highlight: # Code syntax highlighting
use_pygments: true
linenums: true
- pymdownx.inlinehilite # Inline code highlighting
- pymdownx.tabbed: # Tabbed content
alternate_style: true
- pymdownx.tasklist: # Task lists with checkboxes
custom_checkbox: true
- pymdownx.emoji: # Emoji support
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
# Grid table support for Pandoc-style tables
- markdown_grid_tables
# ✅ CORRECT: 3-space indent for nested lists
a. First item
1. Nested item
- Bullet under nested item
- Another bullet
# ❌ WRONG: 4-space indent creates code block
a. First item
1. Nested item
- This becomes a code block!
# ✅ SUPPORTED by Python-Markdown
1. Numbered list (Arabic numerals)
2. Second item
a. Lettered list (lowercase)
b. Second item
- Unordered list (dashes)
- Second item
* Unordered list (asterisks)
* Second item
# ❌ NOT SUPPORTED by Python-Markdown
i. Roman numerals (lowercase)
ii. Second item
I. Roman numerals (uppercase)
II. Second item
Key Insight: Always use 1. for numbered sublists, not i.
Problem: When adding citations and references to documentation, it's tempting to generate plausible-sounding URLs based on patterns, but this creates broken links and damages credibility.
Example of Hallucination:
❌ BAD: Generated URL based on assumptions
— [Organization, project-name - Feature (Lines 89-124)](https://github.com/Org/project/blob/main/src/file.c#L89-L124)
Result: 404 error - repository doesn't exist, file doesn't exist
Correct Approach:
✅ GOOD: Verified URL pointing to actual resource
— [Actual Organization, actual-project - Actual Implementation (Lines 47-122)](https://github.com/ActualOrg/actual-project/blob/main/src/realfile.c#L47-L122)
Result: 200 OK - real file with actual implementation
ALWAYS follow this sequence when adding citations:
Search for the actual resource first
# Use web search to find the correct repository
# Browse the actual file structure
# Find the real files and functions
Verify URLs return 200 OK
curl -s -o /dev/null -w "%{http_code}" "URL_HERE"
# Must return 200 before adding to documentation
Copy actual URLs, don't construct them
If source doesn't exist, say so
Pattern 1: Assuming Repository Names
❌ OrgName/assumed-project # Sounds right, doesn't exist
✅ ActualOrg/actual-project # Actual repository
Pattern 2: Assuming File Names
❌ src/signTransaction.c # Common name, doesn't exist
✅ src/signTransfer.c # Actual file name
Pattern 3: Inventing Line Numbers
❌ Lines 89-124 # Plausible range, made up
✅ Lines 47-122 # Actual function location
Before committing documentation with URLs:
🚩 "This URL looks right to me" - CHECK IT 🚩 "It follows the pattern" - VERIFY IT 🚩 "It's probably at line X" - FIND THE ACTUAL LINE 🚩 "The repository should be named Y" - SEARCH FOR IT 🚩 Multiple 404 errors found during review - YOU HALLUCINATED
If you discover hallucinated URLs:
Acknowledge the error immediately
Find the correct URLs
Document the correction
git commit -m "Fix hallucinated URLs with verified repository paths"
Update the skill doc (like this section)
Documentation credibility depends on accuracy. A single hallucinated URL:
The correct answer is always:
# Install dependencies
pip install mkdocs-material markdown-grid-tables
# Build site
mkdocs build --strict --site-dir site-test
# Check for errors (--strict makes warnings into errors)
# Look for:
# - "Error rendering grid table"
# - Broken symlinks
# - Missing pages in nav
# Serve locally for visual inspection
mkdocs serve
# Clean up test build
rm -rf site-test
<ol>, <ul>, <li>)on:
push:
branches: [master, main]
paths:
- 'policies/**'
- 'narratives/**'
- 'procedures/**'
- '.github/workflows/build-policies.yml' # Include workflow itself!
- 'mkdocs.yml' # Include config!
Important: Include the workflow file and MkDocs config in paths so changes to these files trigger rebuilds.
on:
workflow_dispatch: # Allow manual triggering from GitHub UI
Problem: Changed mkdocs.yml or .github/workflows/build-policies.yml but workflow didn't run.
Solution: Add these paths to trigger configuration:
paths:
- '.github/workflows/build-policies.yml'
- 'mkdocs.yml'
+ and - charactersProblem: Grid tables not rendering.
Solution: Install markdown-grid-tables extension (see Issue 1 above).
Problem: Content being rendered as code blocks due to indentation.
Solution: Reduce indentation from 4 spaces to 3 spaces (see Issue 2 above).
Problem: Using i. or ii. for sublists.
Solution: Use 1. for all numbered lists (Python-Markdown doesn't support roman numerals).
Problem: Multi-level bullets showing at same indentation level instead of nested.
Solution:
Problem: Bullet points showing as plain text within paragraphs.
Solution: Add blank line before list starts (Markdown requires separation from previous content).
Problem: GitHub Pages cache.
Solution:
- name: Build MkDocs site
run: |
# Use --strict to catch issues early
mkdocs build --strict --site-dir output/site
- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
Python-Markdown has different indentation rules than GitHub Flavored Markdown:
| Context | Correct Indent | Wrong Indent | Result if Wrong |
|---|---|---|---|
Numbered list under a. | 3 spaces | 4 spaces | Renders as code block |
Nested bullet under - | 4 spaces | 2 spaces | Renders flat (same level) |
| Continuation line | 4 spaces | Any other | Breaks list structure |
| Triple nested | 8 spaces | 6 spaces | Renders at wrong level |
Numbered Lists Under Letter Items:
# ✅ CORRECT: 3-space indent
a. First policy requirement
1. Sub-requirement one
- Detail A
- Detail B
2. Sub-requirement two
- Detail C
# ❌ WRONG: 4-space indent (creates code block)
a. First policy requirement
1. Sub-requirement one
- Will render with line numbers in gray box!
Nested Bullets:
# ✅ CORRECT: 4-space indent
**Code Verification**:
- Main verification point
* Supporting evidence (properly nested)
* Additional evidence (properly nested)
- Another main point
* More nested content
# ❌ WRONG: 2-space indent (renders flat)
**Code Verification**:
- Main verification point
* Supporting evidence (renders as sibling, not child)
* Additional evidence (also renders as sibling)
Mixed Lists:
# ✅ CORRECT: Different indents for different contexts
a. **Policy Section:**
1. Numbered requirement
- Bullet detail (7 spaces: 3 + 4)
- Another bullet (7 spaces: 3 + 4)
2. Second requirement
- More details
**Code Verification**:
- Root level item
* Nested bullet (4 spaces)
+ Double nested (8 spaces)
Is it nested under a letter item (a., b., c.)?
│
├─ YES → Check indentation
│ │
│ ├─ Has 4 spaces? → WRONG! Change to 3 spaces
│ └─ Has 3 spaces? → Check if it's a list item (1., 2., etc.)
│ └─ Not a list? → Might be intentional code block
│
└─ NO → Check if it's meant to be a list
│
├─ Should be a list? → Add blank line before list
└─ Should be code? → This is correct (leave as-is)
Is it using * or - as bullet marker?
│
├─ YES → Check indentation
│ │
│ ├─ Has 2 spaces? → WRONG! Change to 4 spaces
│ ├─ Has 3 spaces? → WRONG! Change to 4 spaces
│ └─ Has 4 spaces? → Check for blank line before parent list
│ │
│ └─ No blank line? → Add blank line before list
│
└─ NO → Is it using number (1., 2.)?
└─ See numbered list rules above
Is there a blank line before the list?
│
├─ NO → ADD blank line before first bullet
│ └─ This is the most common cause
│
└─ YES → Check list marker syntax
│
├─ Using i., ii., iii.? → WRONG! Change to 1., 2., 3.
├─ Using I., II., III.? → WRONG! Change to 1., 2., 3.
└─ Using -, *, or 1.? → Check indentation levels
# Numbered lists under letter items (a., b., c.)
a. Item
1. Sub-item (3 spaces)
- Detail (7 spaces = 3 + 4)
# Bullets under bullets
- Item
* Sub-item (4 spaces)
+ Detail (8 spaces)
# Continuation lines
- Item text that wraps
to next line (4 spaces)
# Before root-level lists
**Header text:**
- First bullet (blank line required above)
# NOT needed between list items
- Item 1
- Item 2 (no blank line needed)
- Item 3
| Marker | Supported? | Use For |
|---|---|---|
1., 2., 3. | ✅ YES | Numbered lists |
- | ✅ YES | Unordered lists |
* | ✅ YES | Unordered lists (nested) |
a., b., c. | ✅ YES | Letter lists |
i., ii., iii. | ❌ NO | Convert to 1. |
I., II., III. | ❌ NO | Convert to 1. |
Code Verification Sections:
**Code Verification**:
- ✅ VERIFIED: Description
* File: path/to/file.rs line 123
* URL: https://github.com/repo/blob/main/file.rs
- ⚠️ PARTIAL: Description
* Supporting detail
* Another detail
Policy Requirements:
a. **Policy Title:**
1. First requirement with details
- Implementation note
- Another note
2. Second requirement
- Detail here
When you encounter markdown rendering issues in MkDocs, follow this systematic approach:
Build and observe
mkdocs build --strict --site-dir test-site
mkdocs serve # View at http://127.0.0.1:8000
Categorize the issue
Find affected files
# Search for specific patterns
git grep -n "^ i\." policies/ # 4-space numbered lists
git grep -n "^ \*" policies/ # 2-space bullets
git grep -n "^\+---" policies/ # Grid tables
Based on the issue, create targeted Python scripts:
# Template for systematic fixes
import re
from pathlib import Path
def fix_pattern(content):
"""Fix specific markdown pattern."""
lines = content.split('\n')
fixed_lines = []
for line in lines:
# Apply transformation
fixed_line = transform(line)
fixed_lines.append(fixed_line)
return '\n'.join(fixed_lines)
# Apply to all files
for md_file in Path('policies').glob('*.md'):
content = md_file.read_text()
fixed = fix_pattern(content)
if content != fixed:
md_file.write_text(fixed)
print(f"Fixed {md_file}")
# Test on one file first
cp policies/sample.md /tmp/sample-test.md
python3 fix-script.py /tmp/sample-test.md
# Build and check
mkdocs build --strict
# Visually inspect: http://127.0.0.1:8000/policies/sample/
# Run on all files
find policies narratives procedures -name "*.md" -type f | \
xargs python3 fix-script.py
# Verify changes
git diff --stat
git diff policies/sample.md # Spot check
# Full build test
rm -rf test-site
mkdocs build --strict --site-dir test-site
# Check for errors
mkdocs build --strict 2>&1 | grep -i "error\|warning"
# Spot check multiple files
open test-site/policies/encryption/index.html
open test-site/policies/access/index.html
open test-site/policies/risk/index.html
git add policies/*.md narratives/*.md
git commit -m "Fix [specific issue] in MkDocs rendering
Root cause: [explain the markdown issue]
Solution: [explain the fix]
Impact: [number of files/lines changed]
[Technical details]"
Why This Approach Works:
Before pushing markdown changes:
mkdocs build --strict --site-dir test-siterm -rf test-sitemkdocs gh-deploymarkdown-grid-tables for Pandoc table support1. for numbered lists, not i. (roman numerals not supported)\pagebreak, etc.) from markdown sourcemkdocs serve and visually inspect rendered output1. not i. for numbered listsmarkdown-grid-tables) - Not supported by defaultmkdocs gh-deploy commanda.: Use 3-space indent\pagebreak) from markdown source filesThis section documents the complete journey of fixing documentation for MkDocs, including all discoveries made throughout the process.
| Metric | Count |
|---|---|
| Total files modified | 28 files (25 policies + 3 narratives) |
| Total lines changed | 5,500+ lines |
| Time invested | ~10 hours (discovery, fixing, testing, documentation) |
| Commits created | 6 commits |
| Issues discovered | 9 distinct rendering issues |
| Scripts created | 7 automation scripts |
| Issue # | Problem | Root Cause | Solution | Impact |
|---|---|---|---|---|
| 1 | Grid tables as plain text | Missing extension | Added markdown-grid-tables | All tables |
| 2 | Code blocks instead of lists | 4-space indent on numbered lists | Changed 4→3 spaces | 2,355 lines |
| 3 | Roman numerals not rendering | i. not supported | Changed i.→1. | 812 markers |
| 4 | Nested bullets rendering flat | 2-space indent | Changed 2→4 spaces | 1,445 bullets |
| 5 | Lists not recognized | Missing blank lines | Added blank lines | 515 locations |
| 6 | YAML frontmatter not parsed | Missing meta extension | Added to config | All files |
| 7 | Footnotes only showing first line | Missing 4-space indent on continuation | Added 4-space indent | 12 files, 855 lines |
| 8 | WHERE citations lack bullets | Plain paragraph formatting | Added bullet points | 1 file, 20 endnotes |
| 9 | LaTeX \pagebreak in HTML | PDF commands in markdown | Removed \pagebreak | 6 files, 8 commands |
1. Fix indentation (4→3 spaces for numbered lists):
2. Convert list markers (i.→1.):
3. Fix nested bullets (2→4 spaces):
4. Add blank lines before lists:
5. Fix footnote indentation:
6. Convert WHERE citations to bullets:
7. Remove LaTeX pagebreak commands:
Before:
+ and | characters\pagebreak commands appearing as plain text in HTMLAfter:
The most important lesson: Python-Markdown has context-dependent indentation rules
Context | Indent | Reason
---------------------------------|--------|----------------------------------
Numbered list under letter (a.) | 3 | 4 creates code block
Nested bullet under bullet (-) | 4 | 2 renders flat (same level)
Continuation of list item | 4 | Aligns with content
This is different from GitHub Flavored Markdown which is more forgiving with indentation.
First Issue (Tables):
Second Issue (Code Blocks):
Third Issue (Nested Bullets):
Total efficiency gain: 75% time reduction from first to third issue.
To avoid similar issues in future projects:
Status: Validated and working (all fixes applied and tested)