$k === 'show_main_menu' || str_starts_with($k, 'show_menu_'); $categorias = ['global' => []] + ($config['per_type'] ?? []); foreach ($categorias as $cat => $pt) { $flows = array_merge($config['flows'] ?? [], $pt['flows'] ?? []); $menus = array_merge($config['menus'] ?? [], $pt['menus'] ?? []); $cmds = array_merge($config['commands'] ?? [], $pt['commands'] ?? []); $donde = "cat {$cat}"; $existe = fn(string $k): bool => isset($flows[$k]) || isset($menus[$k]); foreach ($cmds as $palabra => $destino) { if ($destino === '__back') continue; // se resuelve en runtime if (!$existe($destino)) $errores[] = "{$donde}: comando '{$palabra}' → '{$destino}' no existe"; } foreach ($flows as $key => $flow) { foreach (['back', 'resolver', 'next_node'] as $campo) { $destino = $flow[$campo] ?? null; if ($destino !== null && !$existe($destino)) { $errores[] = "{$donde}: flow '{$key}'.{$campo} → '{$destino}' no existe"; } } if (!empty($flow['requires']) && empty($flow['resolver'])) { $errores[] = "{$donde}: flow '{$key}' declara requires sin resolver"; } if (($flow['type'] ?? '') === 'menu' && !isset($menus[$flow['menu'] ?? ''])) { $errores[] = "{$donde}: flow '{$key}' apunta al menú '{$flow['menu']}' que no existe"; } if (($flow['type'] ?? '') === 'menu' && !isset($flow['back']) && !$esRaiz($key)) { $avisos[] = "{$donde}: flow '{$key}' es menú y no define 'back'"; } } // Cada id de botón/fila debe resolver a un flow foreach ($menus as $mKey => $menu) { $ids = array_column($menu['buttons'] ?? [], 'id'); foreach ($menu['sections'] ?? [] as $sec) { $ids = array_merge($ids, array_column($sec['rows'] ?? [], 'id')); } foreach ($ids as $id) { if (!$existe($id)) $errores[] = "{$donde}: menú '{$mKey}' tiene opción '{$id}' sin flow"; } } } // Endpoints referenciados por los flows preg_match_all("/'key'\s*=>\s*'([^']+)'/", substr($seed, $end), $m); $endpoints = array_flip($m[1]); foreach ($config['flows'] ?? [] as $key => $flow) { foreach (['source_endpoint_key', 'source_endpoint_key_all'] as $campo) { $ep = $flow[$campo] ?? null; if ($ep !== null && !isset($endpoints[$ep])) $errores[] = "flow '{$key}'.{$campo} → endpoint '{$ep}' no registrado"; } $ep = $flow['params']['endpoint_key'] ?? null; if ($ep !== null && !isset($endpoints[$ep])) $errores[] = "flow '{$key}' → endpoint '{$ep}' no registrado"; } foreach (array_unique($avisos) as $a) echo "aviso: {$a}\n"; foreach (array_unique($errores) as $e) echo "ERROR: {$e}\n"; if ($errores) { echo "\n" . count(array_unique($errores)) . " error(es)\n"; exit(1); } echo "\nOK — grafo de navegación consistente\n";