Skip to content

Commit 42e57f0

Browse files
rammiczclaude
andcommitted
fix(compress): match fences nested in list items
FENCE_OPEN_REGEX bounded indent at 0-3, correct only for top-level fences. Inside a list item indent counts from the item's content column, so a fence under "- " sits at 4 absolute and was missed. Two effects. Its body backticks leaked into inline-code pairing, so whole paragraphs came back as lost inline code and validation failed unfixably. And extract_code_blocks never saw the block, so validate_code_blocks could not tell it had been rewritten - the copied-EXACTLY guarantee did not hold for any fence under a bullet. Refs #619 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent ec83e5b commit 42e57f0

3 files changed

Lines changed: 69 additions & 1 deletion

File tree

skills/caveman-compress/scripts/validate.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from pathlib import Path
55

66
URL_REGEX = re.compile(r"https?://[^\s)]+")
7-
FENCE_OPEN_REGEX = re.compile(r"^(\s{0,3})(`{3,}|~{3,})(.*)$")
7+
# A list item's fence indents from its content column, so one under "- " is at 4.
8+
FENCE_OPEN_REGEX = re.compile(r"^([ \t]*)(`{3,}|~{3,})(.*)$")
89
HEADING_REGEX = re.compile(r"^(#{1,6})\s+(.*)", re.MULTILINE)
910
BULLET_REGEX = re.compile(r"^\s*[-*+]\s+", re.MULTILINE)
1011

tests/test_validate_code_blocks.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import sys
2+
import unittest
3+
from pathlib import Path
4+
5+
REPO_ROOT = Path(__file__).resolve().parent.parent
6+
sys.path.insert(0, str(REPO_ROOT / "skills" / "caveman-compress"))
7+
8+
from scripts.validate import ( # noqa: E402
9+
ValidationResult,
10+
extract_code_blocks,
11+
validate_code_blocks,
12+
)
13+
14+
MANGLED = "- step:\n{i}```\n{i}rm -rf / # rewritten\n{i}```\n"
15+
INTACT = "- step:\n{i}```\n{i}rm -rf /important\n{i}```\n"
16+
17+
18+
class TestExtractCodeBlocks(unittest.TestCase):
19+
def test_top_level_fence(self):
20+
self.assertEqual(len(extract_code_blocks("```\ncode\n```\n")), 1)
21+
22+
def test_fence_indented_within_list_item(self):
23+
self.assertEqual(len(extract_code_blocks(INTACT.format(i=" "))), 1)
24+
25+
def test_fence_indented_within_nested_list_item(self):
26+
text = "- a:\n - b:\n ```\n code\n ```\n"
27+
self.assertEqual(len(extract_code_blocks(text)), 1)
28+
29+
def test_tilde_fence_indented_within_list_item(self):
30+
text = "- step:\n ~~~\n code\n ~~~\n"
31+
self.assertEqual(len(extract_code_blocks(text)), 1)
32+
33+
34+
class TestValidateCodeBlocks(unittest.TestCase):
35+
def test_rewritten_block_is_rejected_at_every_indent(self):
36+
# A block the extractor cannot see is one the compressor rewrites
37+
# unchecked, breaking SKILL.md's copied-EXACTLY guarantee.
38+
for indent in ("", " ", " ", " "):
39+
with self.subTest(indent=len(indent)):
40+
result = ValidationResult()
41+
validate_code_blocks(
42+
INTACT.format(i=indent), MANGLED.format(i=indent), result
43+
)
44+
self.assertFalse(result.is_valid)
45+
46+
def test_untouched_block_passes(self):
47+
for indent in ("", " ", " ", " "):
48+
with self.subTest(indent=len(indent)):
49+
result = ValidationResult()
50+
text = INTACT.format(i=indent)
51+
validate_code_blocks(text, text, result)
52+
self.assertTrue(result.is_valid)
53+
54+
55+
if __name__ == "__main__":
56+
unittest.main()

tests/test_validate_inline.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ def test_indented_fence_backtick_not_leaked_as_inline(self):
5252
result = extract_inline_codes(text)
5353
self.assertEqual(result, ["inline"])
5454

55+
def test_list_nested_fence_backtick_not_leaked_as_inline(self):
56+
# Fence under "- " sits at 4, past the old 0-3 bound.
57+
text = "- step:\n ```\n `weird`\n ```\nReal `inline` span here."
58+
result = extract_inline_codes(text)
59+
self.assertEqual(result, ["inline"])
60+
61+
def test_deeply_nested_fence_backtick_not_leaked_as_inline(self):
62+
text = "- a:\n - b:\n ```\n `weird`\n ```\nReal `inline` span."
63+
result = extract_inline_codes(text)
64+
self.assertEqual(result, ["inline"])
65+
5566

5667
class TestValidateInlineCodes(unittest.TestCase):
5768
def test_match(self):

0 commit comments

Comments
 (0)