fix(convert): track code fences when splitting OpenClaw sections, and pin it with an invariant (#855)

convert_openclaw() split on `## ` without tracking fenced code blocks, tearing six fenced examples across SOUL.md/AGENTS.md in five agents and leaving dangling fences in each. Implementation from #854 (@guozi-lab): fence helpers in lib.sh used by convert.sh and lint-agents.sh, CommonMark indent aware. Regression test from #853 (@dajiaohuang): every source fenced block must land whole in exactly one output file — verified to fail on the unfixed converter.

Reported by @AmineOzil. Closes #849. Supersedes #853 and #854.

Co-Authored-By: fruit <200041037+guozi-lab@users.noreply.github.com>
Co-Authored-By: Wu Shuwen <108231307+dajiaohuang@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Michael Sitarzewski
2026-09-12 11:57:20 -05:00
committed by GitHub
co-authored by fruit Wu Shuwen Claude Opus 5
parent 6d29a9b087
commit ad9264e309
5 changed files with 140 additions and 5 deletions
+21
View File
@@ -328,8 +328,29 @@ convert_openclaw() {
local current_target="agents" # default bucket
local current_section=""
# While fence_marker is set, ## lines are code content, not section
# boundaries (issue #849). See lib.sh fence_open_p / fence_closes_p.
local fence_marker="" fence_len=0 fence_indent=0
while IFS= read -r line; do
if [[ -n "$fence_marker" ]]; then
current_section+="$line"$'\n'
if fence_closes_p "$line" "$fence_marker" "$fence_len" "$fence_indent"; then
fence_marker=""
fence_len=0
fence_indent=0
fi
continue
fi
if fence_open_p "$line"; then
fence_marker="${BASH_REMATCH[2]:0:1}"
fence_len=${#BASH_REMATCH[2]}
fence_indent=${#BASH_REMATCH[1]}
current_section+="$line"$'\n'
continue
fi
# Detect ## headers (with or without emoji prefixes)
if [[ "$line" =~ ^##[[:space:]] ]]; then
# Flush previous section