From 4d3583b81a657b8f078b650079d444e707cb1595 Mon Sep 17 00:00:00 2001 From: Emanuel Almeida Date: Sun, 28 Jun 2026 22:00:36 +0100 Subject: [PATCH] fix(okf-normalize): so tratar bloco --- como frontmatter se for mapa YAML valido Bug: parse_frontmatter aceitava qualquer bloco ---...--- inicial como frontmatter. Ficheiros que comecam com --- como regua/separador (ex: MEMORY.md seguido de ## heading) tinham o corpo tratado como frontmatter, e o ramo 'tem frontmatter' injectava campos OKF la dentro -> YAML partido (5 casos no run do Cloud). Fix: _looks_like_yaml_mapping() valida via PyYAML (isinstance dict) com fallback heuristico (1a linha = chave, sem heading markdown no topo). Tambem: report_path passou a escrever no dir do script (era path Hub hardcoded inexistente). Validado: caso-bug + regressao (com/sem frontmatter) -> 0 erros, YAML valido. Co-Authored-By: Claude Opus 4.8 (1M context) --- okf-hub/okf-normalize.py | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/okf-hub/okf-normalize.py b/okf-hub/okf-normalize.py index 41411c6..7124566 100644 --- a/okf-hub/okf-normalize.py +++ b/okf-hub/okf-normalize.py @@ -81,14 +81,46 @@ def get_git_timestamp(filepath: Path, hub: Path) -> str: return datetime.fromtimestamp(mtime, tz=timezone.utc).isoformat() +def _looks_like_yaml_mapping(fm: str) -> bool: + """True só se o bloco for um mapa YAML válido (evita tratar régua '---' + corpo + como frontmatter — causa de corrupção em MEMORY.md e afins).""" + if not fm.strip(): + return False + try: + import yaml # type: ignore + data = yaml.safe_load(fm) + return isinstance(data, dict) + except ImportError: + pass + except Exception: + return False + # Fallback sem PyYAML: a 1ª linha não-vazia tem de ser uma chave `nome:`; + # nenhuma linha de topo (não-indentada, não-vazia) pode ser heading markdown. + first = None + for ln in fm.split("\n"): + if not ln.strip(): + continue + if first is None: + first = ln + if ln[:1] == "#": # heading markdown ao nível de topo + return False + if first is None: + return False + return bool(re.match(r"^[A-Za-z_][\w\-]*:", first)) + + def parse_frontmatter(content: str): - """Retorna (frontmatter_str, body_str, has_fm) ou (None, content, False).""" + """Retorna (frontmatter_str, body_str, has_fm) ou (None, content, False). + + Só considera frontmatter um bloco `---…---` inicial que seja um MAPA YAML válido. + Um `---` inicial usado como régua/separador (seguido de corpo) NÃO é frontmatter.""" if content.startswith("---\n"): end = content.find("\n---\n", 4) if end != -1: fm = content[4:end] - body = content[end + 5:] - return fm, body, True + if _looks_like_yaml_mapping(fm): + body = content[end + 5:] + return fm, body, True return None, content, False