---
name: parse_md_frontmatter
kind: function
lang: cpp
domain: core
version: "1.0.0"
purity: pure
signature: "Frontmatter parse_md_frontmatter(const std::string& content)"
description: "Parsea frontmatter YAML simple (subset: key:value, listas inline [a,b], listas multilinea con - item) de un .md y devuelve struct con fields map + body. Pure, sin dependencias externas."
tags: [markdown, frontmatter, yaml, parser, issues, meta, registry]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: ["core/parse_md_frontmatter.h"]
example: |
  #include "core/parse_md_frontmatter.h"
  #include <fstream>
  #include <sstream>

  std::ifstream f("/home/lucas/fn_registry/dev/issues/0109-skill-tree-app-roadmap.md");
  std::stringstream ss; ss << f.rdbuf();
  auto fm = fn_md::parse_md_frontmatter(ss.str());
  auto title  = std::get<std::string>(fm.fields["title"]);
  auto domain = std::get<std::vector<std::string>>(fm.fields["domain"]);
tested: true
tests:
  - "parses_no_frontmatter"
  - "parses_simple_key_value"
  - "parses_quoted_strings"
  - "parses_inline_list"
  - "parses_multiline_list"
  - "parses_body_after_frontmatter"
  - "parses_empty_inline_list"
  - "parses_strips_trailing_comment"
  - "parses_real_issue_0109"
  - "parses_real_issues_golden"
test_file_path: "cpp/tests/test_parse_md_frontmatter.cpp"
file_path: "cpp/functions/core/parse_md_frontmatter.cpp"
params:
  - name: content
    desc: "Contenido completo del archivo .md como string. Puede o no comenzar con bloque frontmatter `---...---`."
output: "Struct Frontmatter con: fields (unordered_map<string, YamlValue>) donde YamlValue = monostate|string|vector<string>; body (string con contenido despues del segundo ---); has_frontmatter (bool)."
notes: |
  Subset YAML deliberado — los frontmatters de issues y flows del registry son planos. Si en el futuro hace falta soportar mapas anidados, considerar embeber yaml-cpp en cpp/vendor/ antes que extender este parser.
---

## Cuando usarla

Cuando una app C++ necesita leer metadata de archivos Markdown del registry (issues, flows, app.md, analysis.md, type.md). No requiere libreria externa ni yaml-cpp.

## Gotchas

- Subset YAML: NO soporta mapas anidados, NO soporta block scalars `|` ni `>` (devuelve string vacio en esos casos).
- Si un value contiene `: ` literal debe ir entre comillas: `description: "foo: bar"`.
- Comentarios solo se eliminan al final de linea en valores no citados. Un `# comentario` dentro de comillas se preserva como literal.
- Block list: las lineas de items deben estar indentadas con al menos un espacio antes de `- `. Un `- item` sin indentacion al nivel raiz se ignora (no es list item de la clave anterior).
- `YamlValue` es `std::variant` — usar `std::get<std::string>` o `std::get<std::vector<std::string>>` segun el campo. `std::get_if` es mas seguro si el tipo no es conocido a priori.

## Ejemplo

```cpp
#include "core/parse_md_frontmatter.h"
#include <fstream>
#include <sstream>

std::ifstream f("/home/lucas/fn_registry/dev/issues/0109-skill-tree-app-roadmap.md");
std::stringstream ss; ss << f.rdbuf();
auto fm = fn_md::parse_md_frontmatter(ss.str());

// Scalar string
auto id     = std::get<std::string>(fm.fields["id"]);      // "0109"
auto status = std::get<std::string>(fm.fields["status"]);  // "in-progress"

// List
auto domain = std::get<std::vector<std::string>>(fm.fields["domain"]);
// domain[0] == "meta", domain[1] == "cpp-stack"

// Body (content after closing ---)
// fm.body starts with "\n# 0109 — skill_tree app..."
```
