feat: Add --renumber-sections with automatic section reference renaming#25
Open
feat: Add --renumber-sections with automatic section reference renaming#25
Conversation
- Initialize tbd with fm- prefix for issue IDs - Add Claude Code skill file and hooks integration - Configure docs cache with shortcuts and guidelines https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Design document for automatic section heading renumbering: - Detection logic for documents with numbered sections - SectionNumberingConvention data structure to infer conventions - Regex pattern for numeric prefixes (1, 1.2, 7.18.3, etc.) - Implementation phases and acceptance criteria https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
- SectionNumStyle enum: none, arabic_integer, arabic_decimal - SectionLevelConfig: per-level style + trailing_char - SectionNumConvention: tuple of 6 level configs - Require 2+ headers at a level to activate (low false positives) - Include in --auto flag (reliable like smart quotes) - Always normalize trailing punctuation to period https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Clarifies that when only some H2s have numeric prefixes: - H2 level is `none` (doesn't qualify) - Numbered H2s keep their ORIGINAL numbers (no renumbering) - Only levels where ALL headings are numbered get renumbered https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
- Qualification rule: first-two + two-thirds threshold - First two headings at level must have matching prefix - At least 2/3 of all headings at level must have prefix - Hierarchical constraint: conventions must be contiguous from H1 - Self-contained module: src/flowmark/transforms/section_numbering.py - Detailed implementation plan with unit test requirements - Documentation plan: README, CLI help, API docstrings - Updated test cases for new qualification rules - Feature included in --auto flag https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
- Explicitly document that ONLY arabic_integer and arabic_decimal are recognized (Roman numerals, alphabetic are future extensions) - Patterns: integer with optional . or ), decimal with optional . or ) - Normalization: trailing ) or missing separator → period - Clear examples of what is/isn't recognized - Document that unrecognized patterns pass through unchanged https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
- Replace SectionNumStyle enum with explicit format string representation
- Format strings: "{h1:arabic}.", "{h1:arabic}.{h2:arabic}", etc.
- New data structures:
- NumberStyle enum (arabic, future: roman, alpha)
- FormatComponent (level + style)
- SectionNumFormat (components + trailing, with format_string/format_number methods)
- SectionNumConvention (tuple of 6 SectionNumFormat | None)
- extract_section_prefix() now returns (number_parts, trailing, title)
- number_parts: [1] or [1, 2] or [1, 2, 3]
- trailing: "." or ")" or ""
- More self-documenting and extensible design
https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Update the --renumber-sections spec to implement all numbering styles from the start, using a general regex approach: - Add NumberStyle enum: arabic, roman_upper, roman_lower, alpha_upper, alpha_lower - Add general regex pattern to recognize any combination of styles - Add style inference logic to determine style from parsed components - Add conversion functions: int_to_roman, roman_to_int, int_to_alpha, alpha_to_int - Update Implementation Plan phases with all-style examples - Add comprehensive test cases for Roman and alphabetic numbering - Update Common Patterns table with mixed style examples - Update normalization examples to include all styles - Remove Roman/alphabetic from Future Extensions (now implemented) https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Review fixes: - Replace legacy arabic_integer/arabic_decimal with style-based terminology - Update Pattern Consistency examples to show style matching - Fix Module Structure to show all supported styles - Update Key Functions table with correct return types (strings, not ints) - Fix FormatComponent docstring to show multi-style examples - Fix extract_section_prefix signature to return ParsedPrefix - Update format_number to use to_number() for style conversion - Add note about Roman/Alpha ambiguity for single letters (I, V, X, etc.) - Update format string description to reflect all styles are supported https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
When headings at a level contain both Roman-only letters (I, V, X, C, D, M) and non-Roman letters (A, B, E, F, etc.), treat the entire level as alphabetic. This prevents "C" and "D" from being misinterpreted as Roman numerals in sequences like "A, B, C, D". https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Implement NumberStyle enum, FormatComponent, SectionNumFormat, and SectionNumConvention dataclasses with comprehensive unit tests. Key components: - NumberStyle: arabic, roman_upper/lower, alpha_upper/lower - FormatComponent: level + style for one component - SectionNumFormat: format_string() and format_number() methods - SectionNumConvention: max_depth, is_active properties - Conversion functions: int_to_roman, roman_to_int, int_to_alpha, alpha_to_int Closes fm-w9ti https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Implement ParsedPrefix dataclass, infer_style() function, and extract_section_prefix() function with comprehensive unit tests. Features: - ParsedPrefix: holds parsed components, styles, trailing, title - infer_style(): infers NumberStyle from component string - extract_section_prefix(): parses heading text into ParsedPrefix - Regex-based parsing supporting all number styles - Handles mixed styles like "1.a.i" (arabic.alpha.roman) 29 new tests for Phase 2, total 49 tests passing. Closes fm-e7bo https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
…ering Implements automatic section heading renumbering for Markdown documents. Key features: - Detects numbered section conventions (arabic, roman, alphabetic) - First-Two + Two-Thirds qualification rules for robustness - Hierarchical constraint ensures contiguous numbering from H1 - Normalizes trailing separators to periods - Supports mixed styles (e.g., Roman H1 + Alpha H2) CLI: - New --renumber-sections flag - Included in --auto convenience flag Implementation (9 phases): 1. Core data structures (NumberStyle, FormatComponent, SectionNumFormat) 2. Prefix extraction with regex parsing 3. Convention inference with disambiguation 4. Hierarchical constraint enforcement 5. Trailing character normalization 6. SectionRenumberer class for stateful renumbering 7. Integration with fill_markdown and reformat_api 8. CLI integration 9. End-to-end testing (122 tests) All 294 project tests pass. https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
- Use getattr() instead of direct attribute access on object types - Use __new__() to create RawText instances without constructor args - Add explicit 'is not None' assertions in tests before accessing optional members https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
- Add type annotations to local lists and class attributes - Fix unreachable code by removing redundant else clauses - Add pyright ignore comments for dynamic typing with marko elements - Add @OverRide ignore comment for __str__ method https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Based on senior engineering review: - Extract magic numbers to named constants (MAX_HEADING_LEVELS, TWO_THIRDS_THRESHOLD, MIN_HEADINGS_FOR_INFERENCE, ALPHABET_SIZE) - Remove decorative section comment headers per comment rules - Remove trivial tests that just check object instantiation per testing rules https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
When a document has only one H1 heading (common for title-only documents), H1 is excluded from the hierarchical constraint check, allowing H2+ to be numbered independently. Changes: - Update apply_hierarchical_constraint to accept headings parameter - Skip H1 from hierarchy check when there's only one H1 - Calculate component levels based on heading level (fixes format inference) - Pre-initialize H1 counter when single-H1 with deep level references This enables renumbering for documents like: # My Document Title ## 1. Introduction ## 3. Background <- renumbered to 2. https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Adds comprehensive documentation for automatic section reference renaming when section headings are renumbered: - GitHub slugging algorithm (heading_to_slug, GithubSlugger) - Section reference detection (find_section_references) - Atomic rename primitive (rename_section_references) - Integration with renumbering pipeline - Test cases for reference renaming - Updated acceptance criteria (9 new requirements) - Resolved questions about strict vs best-effort mode - Open questions about validation and HTML anchors Key design decisions: - Best-effort by default (log warnings, don't fail) - Only modify internal #slug references - Leave external URLs and cross-file references unchanged - Atomic replacement to handle swaps correctly https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
- Mark HTML anchor tags as explicitly OUT OF SCOPE (not future work) - Emphasize RenameResult as clean data structure for all warnings - Clarify that logging is separate from core logic (no embedded logging) - Note that duplicates are rare for numbered sections (numbers make slugs unique) - Add Phase 10.6 for end-to-end document testing - Update Open Questions to reflect formatter vs linter distinction - Fix duplicate heading example to show numbered slugs correctly https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Add section_references.py module with: - heading_to_slug(): Convert heading text to GitHub-compatible anchor slug - Lowercase conversion - Remove special chars (keep alphanumeric, hyphens, unicode letters) - Replace spaces with hyphens - Collapse multiple hyphens - Remove leading/trailing hyphens - GithubSlugger class: Stateful slugger tracking duplicates within document - Appends -1, -2, etc. for duplicate headings - reset() method to clear tracking Includes 25 comprehensive tests covering: - Basic slug generation - Special character handling - Unicode letter preservation - Duplicate heading tracking - Case insensitivity Closes fm-5pyf https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Add to section_references.py module: - SectionRef dataclass: Holds link element, slug, and is_internal flag - find_section_references(): Traverse Marko document to find internal links - Returns all links starting with # (internal anchors) - Excludes external URLs and cross-file references - Works with inline and reference-style links - Handles nested structures (lists, blockquotes) Includes 11 additional tests for reference detection covering: - Inline and reference-style links - External URL exclusion - Cross-file reference exclusion - Nested document structures Closes fm-y3dr https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Add to section_references.py module: - SectionRename dataclass: Holds old_slug and new_slug pair - RenameResult dataclass: Clean data structure with links_modified count and warnings list (no embedded logging, separation of concerns) - rename_section_references(): Atomically rename section references - Builds complete old→new mapping before any changes - Case-insensitive slug matching - Handles swaps (A→B, B→A) correctly via atomic mapping - Collects warnings for unmatched references (no dynamic logging) - Modifies document in place Includes 12 additional tests for rename functionality covering: - Single and multiple renames - Atomic swap handling - Case-insensitive matching - Partial match rejection (no false positives) - Warning collection for unmatched refs - Clean data structure verification Closes fm-u8zt https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Update apply_section_renumbering() to automatically rename internal section references when headings are renumbered: - Collect old/new heading text for each renumbered heading - Calculate old/new slugs using heading_to_slug() - Only add renames when slug actually changes - Call rename_section_references() after heading updates - Return RenameResult with links_modified count and warnings - Return None when document doesn't qualify for renumbering Integration ensures: - References automatically follow renumbered headings - External links are never modified - Warnings collected for unknown references (no dynamic logging) Includes 7 integration tests covering: - Reference updates on renumbering - Multiple references to same section - No-op when numbers don't change - External link preservation - Warning collection for unknown refs - Non-numbered document handling Closes fm-lb2v https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Thread rename_references parameter through the entire API: - apply_section_renumbering(doc, rename_references=True) - fill_markdown(..., rename_references=True) - reformat_text(..., rename_references=True) - reformat_file(..., rename_references=True) - reformat_files(..., rename_references=True) Add --no-rename-references CLI flag: - Disables automatic section reference updating when renumbering - By default (without flag), references are updated - Useful when users want manual control over link updates When rename_references=False: - Headings are still renumbered - Links are NOT updated - Returns RenameResult with links_modified=0 Includes 1 new test for the flag behavior. Closes fm-5g8u https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Create comprehensive document-based tests for section renumbering: - tests/testdocs/section_numbering.orig.md: Input with misnumbered sections - tests/testdocs/section_numbering.expected.md: Golden output - tests/test_section_numbering_docs.py: Test suite Tests cover: - Full document renumbering (3->2, 5->3, 7->4) - Internal reference updates (#3-design -> #2-design) - Cross-file references preserved - External URLs preserved - Unknown reference warnings collected - rename_references=False disables link updates - RenameResult is clean data structure verification Closes fm-jesh https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
- Add type annotation to `cleaned` list to fix reportUnknownArgumentType - Implement `strict` parameter in rename_section_references() to fix reportUnusedParameter (raises ValueError instead of warning if True) https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Section reference renaming is now always performed when renumbering sections, since there's no sensible use case for renumbering headings without updating internal links (which would break them). Changes: - Remove --no-rename-references CLI flag - Remove rename_references parameter from API chain - Remove related tests for the now-removed feature - Simplify apply_section_renumbering() to always rename references https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Updated docstrings to better explain: - Numbered levels must be contiguous (H2+H3 valid, H2+H4 gap invalid) - Single-H1 exception: when only one H1 exists (title), H2+ can be numbered independently without requiring H1 to be numbered https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Documents the complete behavior of the --renumber-sections transform: - Activation criteria (2/3 threshold, minimum 2 headings) - Recognized prefix styles (decimal, Roman, alphabetic, hierarchical) - Hierarchical constraint (contiguous levels required) - Single-H1 exception (title excluded from hierarchy) - Reference renaming behavior (GitHub slugs, cross-file preservation) - Pass-through behavior for non-qualifying headings https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
Apply flowmark formatting to the specification docstring for cleaner Markdown with proper line wrapping and semantic breaks. https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements the
--renumber-sectionsCLI option for automatic section heading renumbering, with intelligent detection of numbering conventions (decimal, Roman numerals, alphabetic) and automatic updating of internal section references to match new heading slugs.The feature handles complex numbering schemes across multiple heading levels (e.g., H1 decimal + H2 alphabetic + H3 Roman) while preserving document structure and ensuring internal links remain valid after renumbering.
Changes
Core Section Numbering (Phase 1-9)
--renumber-sectionsCLI flag andrenumber_sectionsAPI parameterSection Reference Renaming (Phase 10)
heading_to_slug(),GithubSlugger)find_section_references())rename_section_references())RenameResultdata structure with warnings for unknown references./other.md#section) and external URLs are preservedAPI and Integration
fill_markdown(),reformat_text(),reformat_file(),reformat_files()apply_section_renumbering()Documentation
docs/project/specs/active/plan-2026-01-29-renumber-sections.md)Test Plan
Manual Testing Scenarios
flowmark --renumber-sections file.md#3-designlinks update to#2-designafter renumbering./other.md#sectionremain unchangedEdge Cases Covered
Related Issues
All implementation issues have been closed.
https://claude.ai/code/session_01U4WwmYorLGxdNd6FdgEjFC