Fix hard-wrap autocorrect joining lines inside fenced code blocks #297

Closed
opened 2026-08-06 20:41:30 +00:00 by jared · 2 comments
Owner

Spec: hard-wrap autocorrect must treat fenced code blocks as opaque

Problem Statement

Running the os-aidd-lint fixer over an AiDD artifact that contains fenced code blocks nested inside list bullets silently destroys those blocks: multi-line yaml/bash/python examples are joined into a single unreadable line. The damage is silent — the fixer reports a successful hard-wrap correction, and the corruption is only noticed later by a human reading the doc (observed 2026-08-06; three files corrupted, repaired by hand).

Solution

The fence-detection mask recognizes fenced code blocks regardless of indentation, so the hard-wrap cop (and any other consumer of the document's skip mask) treats fence contents as opaque. Running the fixer over a document with bullet-nested code fences leaves every fenced line byte-identical; prose unwrapping continues to work exactly as before.

User Stories

  1. As a cc-os maintainer, I want the hard-wrap autocorrect to never modify lines inside fenced code blocks, so that running the fixer is always safe on docs with examples.
  2. As a cc-os maintainer, I want bullet-nested (indented) code fences recognized as fences, so that registry-style docs with paste-ready examples under list items survive the fixer.
  3. As a cc-os maintainer, I want tilde (~~~) fences given the same protection as backtick fences at any indentation, so that fence style doesn't change safety.
  4. As a cc-os maintainer, I want the hard-wrap offense detector (not just the autocorrect) to ignore long lines inside indented fences, so that code examples aren't flagged as wrap violations.
  5. As a cc-os maintainer, I want prose lines outside fences to keep being unwrapped exactly as today, so that the fix doesn't regress the cop's core behavior.
  6. As a cc-os maintainer, I want an indented closing fence to correctly end the block, so that prose after a bullet-nested example is still linted.
  7. As a cc-os maintainer, I want other mask consumers (table detection, any future cop using the skip mask) to inherit the corrected fence detection, so that the fix lands once at the shared layer rather than per-cop.
  8. As a skill author, I want to nest paste-ready examples under list bullets in reference docs, so that I can structure docs naturally without fearing the linter.
  9. As an agent running the fix skill, I want corrected output to be trustworthy without a manual fence audit, so that autocorrect can be applied unattended.
  10. As a cc-os maintainer, I want a regression test reproducing the exact observed damage shape (a fenced yaml block indented under a bullet), so that this specific corruption can never silently return.

Implementation Decisions

  • The root cause is in the shared fence-detection mask, which only recognizes fence delimiters at column 0; the hard-wrap cop already consults the document's combined skip mask correctly. The fix therefore lands in the fence mask, not in the cop.
  • The fence-open/close matcher accepts leading whitespace before the backtick or tilde delimiter. Closing-fence matching keeps the existing toggle semantics (a fence line while inside closes the block) — no attempt to model CommonMark's full indentation/length-matching rules; cc-os docs only need the toggle to be indentation-blind.
  • The hard-wrap cop itself changes only if its paragraph builder needs no change once the mask is correct — expected: zero cop changes.
  • The autocorrect pipeline (corrector composing autocorrectable cops over a re-parsed document) is untouched.
  • No new configuration: fence opacity is unconditional, not a tunable.

Testing Decisions

  • All tests go through the existing cop-level seam: the shared test helpers that run a cop against inline markdown source and return offenses or corrected text. No new seams; the fence mask is exercised only via the cop's external behavior (per user decision, 2026-08-06).
  • Tests live alongside the existing hard-wrap cop tests, following their source-in/expected-out shape.
  • Cases: bullet-nested backtick fence is byte-identical after autocorrect (the regression case); indented tilde fence likewise; long lines inside an indented fence produce no offenses; prose before/after an indented fence still unwraps; a document mixing top-level and nested fences corrects only the prose.
  • Good tests here assert only external behavior: corrected text equality and offense presence/absence — never mask internals.

Out of Scope

  • Full CommonMark fence semantics (info strings, fence-length matching, four-space indented code blocks as fences).
  • Protecting indented (non-fenced) code blocks — no observed damage; capture separately if it surfaces.
  • Any change to the second autocorrectable cop (frontmatter name deletion) or the Tier-2 judge.
  • Re-running the fixer over the previously damaged files (already repaired by hand).

Further Notes

Fixing the mask hardens every current and future consumer of the skip mask in one place — table masking and any future autocorrectable cop get indentation-safe fence detection for free.

# Spec: hard-wrap autocorrect must treat fenced code blocks as opaque ## Problem Statement Running the os-aidd-lint fixer over an AiDD artifact that contains fenced code blocks nested inside list bullets silently destroys those blocks: multi-line yaml/bash/python examples are joined into a single unreadable line. The damage is silent — the fixer reports a successful hard-wrap correction, and the corruption is only noticed later by a human reading the doc (observed 2026-08-06; three files corrupted, repaired by hand). ## Solution The fence-detection mask recognizes fenced code blocks regardless of indentation, so the hard-wrap cop (and any other consumer of the document's skip mask) treats fence contents as opaque. Running the fixer over a document with bullet-nested code fences leaves every fenced line byte-identical; prose unwrapping continues to work exactly as before. ## User Stories 1. As a cc-os maintainer, I want the hard-wrap autocorrect to never modify lines inside fenced code blocks, so that running the fixer is always safe on docs with examples. 2. As a cc-os maintainer, I want bullet-nested (indented) code fences recognized as fences, so that registry-style docs with paste-ready examples under list items survive the fixer. 3. As a cc-os maintainer, I want tilde (`~~~`) fences given the same protection as backtick fences at any indentation, so that fence style doesn't change safety. 4. As a cc-os maintainer, I want the hard-wrap offense detector (not just the autocorrect) to ignore long lines inside indented fences, so that code examples aren't flagged as wrap violations. 5. As a cc-os maintainer, I want prose lines outside fences to keep being unwrapped exactly as today, so that the fix doesn't regress the cop's core behavior. 6. As a cc-os maintainer, I want an indented closing fence to correctly end the block, so that prose after a bullet-nested example is still linted. 7. As a cc-os maintainer, I want other mask consumers (table detection, any future cop using the skip mask) to inherit the corrected fence detection, so that the fix lands once at the shared layer rather than per-cop. 8. As a skill author, I want to nest paste-ready examples under list bullets in reference docs, so that I can structure docs naturally without fearing the linter. 9. As an agent running the fix skill, I want corrected output to be trustworthy without a manual fence audit, so that autocorrect can be applied unattended. 10. As a cc-os maintainer, I want a regression test reproducing the exact observed damage shape (a fenced yaml block indented under a bullet), so that this specific corruption can never silently return. ## Implementation Decisions - The root cause is in the shared fence-detection mask, which only recognizes fence delimiters at column 0; the hard-wrap cop already consults the document's combined skip mask correctly. The fix therefore lands in the fence mask, not in the cop. - The fence-open/close matcher accepts leading whitespace before the backtick or tilde delimiter. Closing-fence matching keeps the existing toggle semantics (a fence line while inside closes the block) — no attempt to model CommonMark's full indentation/length-matching rules; cc-os docs only need the toggle to be indentation-blind. - The hard-wrap cop itself changes only if its paragraph builder needs no change once the mask is correct — expected: zero cop changes. - The autocorrect pipeline (corrector composing autocorrectable cops over a re-parsed document) is untouched. - No new configuration: fence opacity is unconditional, not a tunable. ## Testing Decisions - All tests go through the existing cop-level seam: the shared test helpers that run a cop against inline markdown source and return offenses or corrected text. No new seams; the fence mask is exercised only via the cop's external behavior (per user decision, 2026-08-06). - Tests live alongside the existing hard-wrap cop tests, following their source-in/expected-out shape. - Cases: bullet-nested backtick fence is byte-identical after autocorrect (the regression case); indented tilde fence likewise; long lines inside an indented fence produce no offenses; prose before/after an indented fence still unwraps; a document mixing top-level and nested fences corrects only the prose. - Good tests here assert only external behavior: corrected text equality and offense presence/absence — never mask internals. ## Out of Scope - Full CommonMark fence semantics (info strings, fence-length matching, four-space indented code blocks as fences). - Protecting indented (non-fenced) code blocks — no observed damage; capture separately if it surfaces. - Any change to the second autocorrectable cop (frontmatter name deletion) or the Tier-2 judge. - Re-running the fixer over the previously damaged files (already repaired by hand). ## Further Notes Fixing the mask hardens every current and future consumer of the skip mask in one place — table masking and any future autocorrectable cop get indentation-safe fence detection for free.
Author
Owner

Work starting via /os-sdlc:implement pipeline on branch fix/297-fence-mask-indented-fences.

Work starting via /os-sdlc:implement pipeline on branch fix/297-fence-mask-indented-fences.
Author
Owner

Resolution

Done: Fence mask now recognizes indented backtick/tilde fences; hard-wrap autocorrect and offense detection treat their contents as opaque; six regression tests added.

Evidence: Commits d9d90ad + 84adac5 on main; full suite green (492 runs, 0 failures); opus reviewer approved with no blocking findings.

Follow-ups: Nested-fence toggle edge captured as issue #298; charter-hash bookkeeping landed as 84adac5; all other reviewer suggestions applied.

## Resolution **Done:** Fence mask now recognizes indented backtick/tilde fences; hard-wrap autocorrect and offense detection treat their contents as opaque; six regression tests added. **Evidence:** Commits d9d90ad + 84adac5 on main; full suite green (492 runs, 0 failures); opus reviewer approved with no blocking findings. **Follow-ups:** Nested-fence toggle edge captured as issue #298; charter-hash bookkeeping landed as 84adac5; all other reviewer suggestions applied.
jared closed this issue 2026-08-06 21:16:45 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#297
No description provided.