Fixing the Comment Flag Insertion Bug

During our development session today, we discovered an interesting bug in how Aditi handles comment flags for non-deterministic violations. The question that led to this discovery was simple but insightful: “Does Aditi work from bottom to top when inserting comments?”

The Discovery

When examining the code to answer this question, we found that while Aditi correctly sorts fixes in reverse order (bottom-to-top), there was a fundamental issue with how comment flags were being applied.

Expected behavior: When flagging a violation, insert a comment on the line above:

// AsciiDocDITA.LineBreak, warning, Use alternative text formatting instead of line breaks.
This line has a +
line break violation.

Actual behavior: The comment was replacing the violation text itself!

Root Cause Analysis

The bug was in the _apply_fixes_to_file method in processor.py. Here’s what was happening:

  1. Non-deterministic rules create Fix objects with replacement_text containing the comment
  2. The processor treats all fixes as replacements by default
  3. Comment flags were replacing the original text instead of being inserted above it

The code had special handling for insert_at_line and line_to_replace attributes, but comment flags weren’t using either of these mechanisms.

The Fix

We added detection for comment flags using the existing is_comment_flag property:

if fix.is_comment_flag:
    # Insert comment on the line above the violation
    lines = content.splitlines(keepends=True)
    line_idx = fix.violation.line - 1
    
    if 0 <= line_idx < len(lines):
        # Insert the comment before the violation line
        lines.insert(line_idx, fix.replacement_text + '\n')
        content = ''.join(lines)
        applied_fixes.append(fix)

Why Bottom-to-Top Matters

The existing bottom-to-top processing (sorting fixes by line number in reverse) is crucial for maintaining line number accuracy:

# Sort fixes by line number (reverse order to avoid offset issues)
sorted_fixes = sorted(fixes, key=lambda f: f.violation.line, reverse=True)

When you insert a line at position 10, all subsequent lines (11, 12, 13…) shift down by one. By processing from bottom to top:

  • Insert at line 50 → doesn’t affect lines 1-49
  • Insert at line 30 → doesn’t affect lines 1-29
  • Insert at line 10 → all previous insertions remain accurate

Lessons Learned

  1. Question assumptions: The user’s question revealed our assumption that fixes were always replacements
  2. Semantic clarity: Having an is_comment_flag property made the fix clean and obvious
  3. Test edge cases: Comment insertion is fundamentally different from text replacement
  4. Bottom-to-top is essential: For any operation that changes line numbers

This fix ensures that technical writers reviewing flagged violations will see clear comments above each issue, making the review process much more intuitive.


Copyright © 2025 Aditi. Distributed under the MIT License.