aidd-lint hard-wrap cop joins code lines inside a nested fence #563

Closed
opened 2026-09-12 21:55:13 +00:00 by jared · 3 comments
Owner

Context

The os-aidd-lint hard-wrap cop does not track fence depth. It should skip lines inside code fences of any depth, but currently treats code inside nested fences as prose.

Observed

plugins/os-aidd-lint/bin/aidd-lint --fix plugins/os-sdlc/agents/responsibility-auditor.md joined three code lines into one line, breaking the Ruby sketch inside a nested fence.
After manual restoration, the plain check still reports: plugins/os-sdlc/agents/responsibility-auditor.md:78: hard-wrap: paragraph (lines 78-80) is hard-wrapped.

Reproduce

plugins/os-aidd-lint/bin/aidd-lint plugins/os-sdlc/agents/responsibility-auditor.md

Expected

The cop should track fence depth. Lines inside an inner fence (of any backtick count) must be skipped by both check and autofix.

Illustration

# current bug: treats nested fence as prose
````markdown
  ```ruby
  class Project::Layout
    # nest under the concept it refines
  end

# expected: skip lines inside the inner ``` fence
```

## Origin
- Trigger: Running the fix skill on the responsibility-audit flow
- Improvised this session: Manual restoration of the three joined lines
- Chain: hard-wrap cop lacks fence-depth tracking ← design gap
- Root candidate: none (this is the root)
- Where: plugins/os-aidd-lint/lib (hard-wrap cop class)
- Session: 6d38ceb5-9d7a-4917-ba3c-1b3ceabd5c4b
- Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/6d38ceb5-9d7a-4917-ba3c-1b3ceabd5c4b.jsonl

VERDICT: CREATE — Reproducible bug in aidd-lint hard-wrap cop (data-loss risk: autofix corrupts code), root-cause identified, complete Origin chain with no upstream root; data-corruption issues clear the hard floor regardless.

## Owner sketch

`AiddLint::FenceMask#mask` in `plugins/os-aidd-lint/lib/aidd_lint/fence_mask.rb` owns fence tracking. Today it flips one boolean on every line that matches `FENCE = /\A\s*(```+|~~~+)/`. Change it to remember the opening marker (character and length). A line closes the fence only when it uses the same character with at least that many marks. Any other marker line inside the fence is content and stays masked. No stack is needed. `hard_wrap.rb` keeps consuming `document.skip_mask` unchanged.

## Cases

1. A ````markdown outer fence contains a ```ruby inner fence with three lines `a = 1`, `b = 2`, `c = 3`. The hard-wrap check reports nothing for those lines, and `--fix` leaves them unchanged.
2. Two sequential same-length ``` fences: lines inside each are masked, lines between them are prose. Same as today.
3. A `~~~` marker line inside an open ``` fence does not close it; the fence closes at the next ``` line.
4. A closing marker longer than the opener (``` opened, ```` closes) closes the fence.
## Context The os-aidd-lint hard-wrap cop does not track fence depth. It should skip lines inside code fences of any depth, but currently treats code inside nested fences as prose. ## Observed `plugins/os-aidd-lint/bin/aidd-lint --fix plugins/os-sdlc/agents/responsibility-auditor.md` joined three code lines into one line, breaking the Ruby sketch inside a nested fence. After manual restoration, the plain check still reports: `plugins/os-sdlc/agents/responsibility-auditor.md:78: hard-wrap: paragraph (lines 78-80) is hard-wrapped.` ## Reproduce ``` plugins/os-aidd-lint/bin/aidd-lint plugins/os-sdlc/agents/responsibility-auditor.md ``` ## Expected The cop should track fence depth. Lines inside an inner fence (of any backtick count) must be skipped by both check and autofix. ## Illustration ```ruby # current bug: treats nested fence as prose ````markdown ```ruby class Project::Layout # nest under the concept it refines end ``` ```` # expected: skip lines inside the inner ``` fence ``` ## Origin - Trigger: Running the fix skill on the responsibility-audit flow - Improvised this session: Manual restoration of the three joined lines - Chain: hard-wrap cop lacks fence-depth tracking ← design gap - Root candidate: none (this is the root) - Where: plugins/os-aidd-lint/lib (hard-wrap cop class) - Session: 6d38ceb5-9d7a-4917-ba3c-1b3ceabd5c4b - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/6d38ceb5-9d7a-4917-ba3c-1b3ceabd5c4b.jsonl VERDICT: CREATE — Reproducible bug in aidd-lint hard-wrap cop (data-loss risk: autofix corrupts code), root-cause identified, complete Origin chain with no upstream root; data-corruption issues clear the hard floor regardless. ## Owner sketch `AiddLint::FenceMask#mask` in `plugins/os-aidd-lint/lib/aidd_lint/fence_mask.rb` owns fence tracking. Today it flips one boolean on every line that matches `FENCE = /\A\s*(```+|~~~+)/`. Change it to remember the opening marker (character and length). A line closes the fence only when it uses the same character with at least that many marks. Any other marker line inside the fence is content and stays masked. No stack is needed. `hard_wrap.rb` keeps consuming `document.skip_mask` unchanged. ## Cases 1. A ````markdown outer fence contains a ```ruby inner fence with three lines `a = 1`, `b = 2`, `c = 3`. The hard-wrap check reports nothing for those lines, and `--fix` leaves them unchanged. 2. Two sequential same-length ``` fences: lines inside each are masked, lines between them are prose. Same as today. 3. A `~~~` marker line inside an open ``` fence does not close it; the fence closes at the next ``` line. 4. A closing marker longer than the opener (``` opened, ```` closes) closes the fence.
Author
Owner

Triage 2026-09-21

Still reproduces. Verified today on the live file and on a minimal case.

Evidence

  • plugins/os-aidd-lint/bin/aidd-lint plugins/os-sdlc/agents/responsibility-auditor.md reports 79: hard-wrap: paragraph (lines 79-81) is hard-wrapped (line shifted by one since filing; nested fence spans lines 66-107).
  • Minimal case: a ````markdown outer fence with a ```ruby inner fence holding a = 1 / b = 2 / c = 3. Check flags lines 11-13; --fix rewrites them to a = 1 b = 2 c = 3.
  • No logic change to the cop since filing (only comment cleanup df09b557).

Root cause
plugins/os-aidd-lint/lib/aidd_lint/fence_mask.rb — FENCE = /\A\s*(```+|~~~+)/ and inside = !inside on every match. One boolean, no marker length. The inner ``` closes the outer ```` fence.

Solution
Follow the CommonMark rule: remember the opening fence character and length; a line closes the fence only if it uses the same character with at least that many marks. Lines inside stay masked. No stack is needed: a shorter inner marker is content, not a new fence. hard_wrap.rb consumes the mask via document.skip_mask unchanged.

Cases

  • Nested fence with a shorter inner marker: inner lines skipped by check and by --fix.
  • Same-length fences in sequence: behaves as today.
  • A ~~~ marker inside a backtick fence: does not close it.

Regression test in plugins/os-aidd-lint/tests/cops/hard_wrap_test.rb (or a new fence_mask test) covering the nested case.

## Triage 2026-09-21 Still reproduces. Verified today on the live file and on a minimal case. **Evidence** - `plugins/os-aidd-lint/bin/aidd-lint plugins/os-sdlc/agents/responsibility-auditor.md` reports `79: hard-wrap: paragraph (lines 79-81) is hard-wrapped` (line shifted by one since filing; nested fence spans lines 66-107). - Minimal case: a ````markdown outer fence with a ```ruby inner fence holding `a = 1` / `b = 2` / `c = 3`. Check flags lines 11-13; `--fix` rewrites them to `a = 1 b = 2 c = 3`. - No logic change to the cop since filing (only comment cleanup df09b557). **Root cause** `plugins/os-aidd-lint/lib/aidd_lint/fence_mask.rb` — `FENCE = /\A\s*(```+|~~~+)/` and `inside = !inside` on every match. One boolean, no marker length. The inner ``` closes the outer ```` fence. **Solution** Follow the CommonMark rule: remember the opening fence character and length; a line closes the fence only if it uses the same character with at least that many marks. Lines inside stay masked. No stack is needed: a shorter inner marker is content, not a new fence. `hard_wrap.rb` consumes the mask via `document.skip_mask` unchanged. **Cases** - Nested fence with a shorter inner marker: inner lines skipped by check and by --fix. - Same-length fences in sequence: behaves as today. - A `~~~` marker inside a backtick fence: does not close it. Regression test in `plugins/os-aidd-lint/tests/cops/hard_wrap_test.rb` (or a new fence_mask test) covering the nested case.
Author
Owner

Work started via /os-sdlc:implement on branch ticket-563 (session 8858ead3-4a18-4793-a17b-b548ce11a3a9).

Work started via /os-sdlc:implement on branch ticket-563 (session 8858ead3-4a18-4793-a17b-b548ce11a3a9).
Author
Owner

Resolution

Done: FenceMask now tracks the opening fence marker character and length; a line closes the fence only on a same-character marker of at least that length, so a nested shorter fence (e.g. ```ruby inside ````markdown) stays masked from both the hard-wrap check and --fix

Evidence: commit 1ab89c6d70, plugins/os-aidd-lint/lib/aidd_lint/fence_mask.rb + plugins/os-aidd-lint/tests/fence_mask_test.rb; ruby -Iplugins/os-aidd-lint/lib -Iplugins/os-aidd-lint/tests plugins/os-aidd-lint/tests/fence_mask_test.rb -> 5 runs, 5 assertions, 0 failures

Follow-ups: #620 contract-auditor verdict, #621 dispatch-id settlement gap (both already filed)

## Resolution **Done:** FenceMask now tracks the opening fence marker character and length; a line closes the fence only on a same-character marker of at least that length, so a nested shorter fence (e.g. ```ruby inside ````markdown) stays masked from both the hard-wrap check and --fix **Evidence:** commit 1ab89c6d70a91ad2ca2842c99d3ce6291832d652, plugins/os-aidd-lint/lib/aidd_lint/fence_mask.rb + plugins/os-aidd-lint/tests/fence_mask_test.rb; ruby -Iplugins/os-aidd-lint/lib -Iplugins/os-aidd-lint/tests plugins/os-aidd-lint/tests/fence_mask_test.rb -> 5 runs, 5 assertions, 0 failures **Follow-ups:** #620 contract-auditor verdict, #621 dispatch-id settlement gap (both already filed)
jared closed this issue 2026-09-22 17:13:51 +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#563
No description provided.