fix: quote YAML edge values containing colons, skip unparseable files in reweave merge

Root cause of 84% reweave PR rejection rate: claim titles with colons
(e.g., "COAL: Meta-PoW: The ORE Treasury Protocol") written as bare
YAML list items, causing yaml.safe_load to fail during merge.

Three changes:
1. frontmatter.py: _yaml_quote() wraps colon-containing values in double quotes
2. reweave.py: _write_edge_regex uses _yaml_quote for new edges
3. merge.py: skip individual files with parse failures instead of aborting entire PR

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
m3taversal 2026-04-18 12:07:28 +01:00
parent ae860a1d06
commit 83526bc90e
3 changed files with 16 additions and 7 deletions

View file

@ -9,6 +9,15 @@ Extracted from merge.py Phase 6 of decomposition (Ganymede-approved plan).
import yaml
def _yaml_quote(value: str) -> str:
"""Quote a YAML list value if it contains characters that would break parsing."""
s = str(value)
if ":" in s or s.startswith(("{", "[", "'", '"', "*", "&", "!", "|", ">")):
escaped = s.replace('"', '\\"')
return f'"{escaped}"'
return s
# Edge field names recognized in claim frontmatter.
# Order matters: serialize_edge_fields writes them in this order when appending new fields.
REWEAVE_EDGE_FIELDS = ("supports", "challenges", "challenged_by", "depends_on", "related", "reweave_edges")
@ -101,7 +110,7 @@ def serialize_edge_fields(raw_fm_text: str, merged_edges: dict[str, list]) -> st
if edges:
result_lines.append(f"{matched_field}:")
for edge in edges:
result_lines.append(f"- {edge}")
result_lines.append(f"- {_yaml_quote(edge)}")
# Don't increment i — it's already past the old field
continue
else:
@ -115,7 +124,7 @@ def serialize_edge_fields(raw_fm_text: str, merged_edges: dict[str, list]) -> st
if edges:
result_lines.append(f"{field}:")
for edge in edges:
result_lines.append(f"- {edge}")
result_lines.append(f"- {_yaml_quote(edge)}")
return "\n".join(result_lines)

View file

@ -508,9 +508,8 @@ async def _merge_reweave_pr(branch: str) -> tuple[bool, str]:
branch_fm, _branch_raw_fm, branch_body = parse_yaml_frontmatter(branch_content)
if main_fm is None or branch_fm is None:
# Parse failure = something unexpected. Fail the merge, don't fallback
# to cherry-pick. (Theseus: loud failure, not silent retry)
return False, f"frontmatter parse failed on {fpath} — manual review needed"
logger.warning("Reweave merge: frontmatter parse failed on %s — skipping file, continuing PR", fpath)
continue
# Superset assertion + merge in one pass.
# Reweave only adds edges. If branch is missing an edge that main has,

View file

@ -535,8 +535,9 @@ def _write_edge_regex(neighbor_path: Path, fm_text: str, body_text: str,
field_re = re.compile(rf"^{edge_type}:\s*$", re.MULTILINE)
inline_re = re.compile(rf'^{edge_type}:\s*\[', re.MULTILINE)
entry_line = f'- {orphan_title}'
rw_line = f'- {orphan_title}|{edge_type}|{date_str}'
from lib.frontmatter import _yaml_quote
entry_line = f'- {_yaml_quote(orphan_title)}'
rw_line = f'- {_yaml_quote(orphan_title + "|" + edge_type + "|" + date_str)}'
if field_re.search(fm_text):
# Multi-line list exists — find end of list, append