diff --git a/Shared/Infrastructure/Scripts/validate-fix-lists.php b/Shared/Infrastructure/Scripts/validate-fix-lists.php
new file mode 100644
index 00000000..2d4026ff
--- /dev/null
+++ b/Shared/Infrastructure/Scripts/validate-fix-lists.php
@@ -0,0 +1,347 @@
+ 'localhost',
+ 'database' => 'preciosunitarios_seo',
+ 'username' => 'preciosunitarios_seo',
+ 'password' => 'ACl%EEFd=V-Yvb??',
+ 'charset' => 'utf8mb4'
+];
+
+$output_dir = '/tmp/list-fix-validation';
+$sample_size = 5;
+
+echo "==============================================\n";
+echo " VALIDADOR DE CORRECCIONES\n";
+echo " Fecha: " . date('Y-m-d H:i:s') . "\n";
+echo "==============================================\n\n";
+
+// Crear directorio de salida
+if (!is_dir($output_dir)) {
+ mkdir($output_dir, 0755, true);
+}
+
+// Limpiar archivos anteriores
+array_map('unlink', glob("$output_dir/*.html"));
+
+/**
+ * Detectar problemas en HTML
+ */
+function detectIssues(string $html): array {
+ $issues = [];
+ libxml_use_internal_errors(true);
+
+ $doc = new DOMDocument('1.0', 'UTF-8');
+ $wrapped = '
' . $html . '
';
+ $doc->loadHTML('' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+ libxml_clear_errors();
+
+ $validChildren = ['li', 'script', 'template'];
+
+ foreach (['ul', 'ol'] as $listTag) {
+ foreach ($doc->getElementsByTagName($listTag) as $list) {
+ foreach ($list->childNodes as $child) {
+ if ($child->nodeType === XML_ELEMENT_NODE) {
+ $tagName = strtolower($child->nodeName);
+ if (!in_array($tagName, $validChildren)) {
+ $issues[] = "<$listTag> contiene <$tagName>";
+ }
+ }
+ }
+ }
+ }
+
+ return $issues;
+}
+
+/**
+ * Corregir listas mal formadas
+ */
+function fixMalformedLists(string $html): array {
+ $result = ['fixed' => false, 'html' => $html, 'changes' => 0, 'details' => []];
+
+ libxml_use_internal_errors(true);
+ $doc = new DOMDocument('1.0', 'UTF-8');
+ $wrapped = '' . $html . '
';
+ $doc->loadHTML('' . $wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+ libxml_clear_errors();
+
+ $lists = [];
+ foreach ($doc->getElementsByTagName('ul') as $ul) { $lists[] = $ul; }
+ foreach ($doc->getElementsByTagName('ol') as $ol) { $lists[] = $ol; }
+
+ $changes = 0;
+ $validChildren = ['li', 'script', 'template'];
+
+ foreach ($lists as $list) {
+ $nodesToProcess = [];
+ foreach ($list->childNodes as $child) {
+ if ($child->nodeType === XML_ELEMENT_NODE) {
+ $tagName = strtolower($child->nodeName);
+ if (!in_array($tagName, $validChildren)) {
+ $nodesToProcess[] = $child;
+ }
+ }
+ }
+
+ foreach ($nodesToProcess as $node) {
+ $tagName = strtolower($node->nodeName);
+ $prevLi = null;
+ $prev = $node->previousSibling;
+
+ while ($prev) {
+ if ($prev->nodeType === XML_ELEMENT_NODE && strtolower($prev->nodeName) === 'li') {
+ $prevLi = $prev;
+ break;
+ }
+ $prev = $prev->previousSibling;
+ }
+
+ if ($prevLi) {
+ $prevLi->appendChild($node);
+ $result['details'][] = "Movido <$tagName> dentro del anterior";
+ $changes++;
+ } else {
+ $newLi = $doc->createElement('li');
+ $list->insertBefore($newLi, $node);
+ $newLi->appendChild($node);
+ $result['details'][] = "Envuelto <$tagName> en nuevo ";
+ $changes++;
+ }
+ }
+ }
+
+ if ($changes > 0) {
+ $wrapper = $doc->getElementById('temp-wrapper');
+ if ($wrapper) {
+ $innerHTML = '';
+ foreach ($wrapper->childNodes as $child) {
+ $innerHTML .= $doc->saveHTML($child);
+ }
+ $result['html'] = $innerHTML;
+ $result['fixed'] = true;
+ $result['changes'] = $changes;
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Generar HTML wrapper para visualización
+ */
+function wrapForVisualization(string $content, string $title, string $status): string {
+ $statusColor = $status === 'error' ? '#dc3545' : '#28a745';
+ return <<
+
+
+
+
+ $title
+
+
+
+ $status
+
+ $content
+
+
+
+HTML;
+}
+
+// Conectar a DB
+$conn = new mysqli($db_config['host'], $db_config['username'], $db_config['password'], $db_config['database']);
+$conn->set_charset($db_config['charset']);
+
+if ($conn->connect_error) {
+ die("Error de conexión: " . $conn->connect_error);
+}
+
+echo "✓ Conexión establecida\n\n";
+
+// Buscar posts con problemas
+$query = "SELECT id, page, html FROM datos_seo_pagina WHERE html IS NOT NULL AND html != '' ORDER BY id LIMIT 500";
+$result = $conn->query($query);
+
+$samples = [];
+while ($row = $result->fetch_assoc()) {
+ $issues = detectIssues($row['html']);
+ if (!empty($issues) && count($samples) < $sample_size) {
+ $samples[] = $row;
+ }
+}
+
+echo "Encontrados " . count($samples) . " posts con problemas para validar\n\n";
+
+$comparison_data = [];
+
+foreach ($samples as $idx => $post) {
+ $id = $post['id'];
+ $url = $post['page'];
+ $html_before = $post['html'];
+
+ echo "─────────────────────────────────\n";
+ echo "POST $id: $url\n";
+
+ // Detectar problemas antes
+ $issues_before = detectIssues($html_before);
+ echo " Problemas ANTES: " . count($issues_before) . "\n";
+
+ // Aplicar corrección
+ $fixResult = fixMalformedLists($html_before);
+ $html_after = $fixResult['html'];
+
+ // Detectar problemas después
+ $issues_after = detectIssues($html_after);
+ echo " Problemas DESPUÉS: " . count($issues_after) . "\n";
+ echo " Cambios aplicados: " . $fixResult['changes'] . "\n";
+
+ // Guardar archivos HTML
+ $file_before = "$output_dir/post_{$id}_BEFORE.html";
+ $file_after = "$output_dir/post_{$id}_AFTER.html";
+
+ file_put_contents($file_before, wrapForVisualization(
+ $html_before,
+ "Post $id - ANTES (con errores)",
+ "ANTES: " . count($issues_before) . " problemas de listas"
+ ));
+
+ file_put_contents($file_after, wrapForVisualization(
+ $html_after,
+ "Post $id - DESPUÉS (corregido)",
+ "DESPUÉS: " . count($issues_after) . " problemas - " . $fixResult['changes'] . " correcciones aplicadas"
+ ));
+
+ echo " ✓ Archivos generados:\n";
+ echo " - $file_before\n";
+ echo " - $file_after\n";
+
+ // Guardar datos para reporte
+ $comparison_data[] = [
+ 'id' => $id,
+ 'url' => $url,
+ 'issues_before' => count($issues_before),
+ 'issues_after' => count($issues_after),
+ 'changes' => $fixResult['changes'],
+ 'file_before' => "post_{$id}_BEFORE.html",
+ 'file_after' => "post_{$id}_AFTER.html"
+ ];
+}
+
+// Generar reporte comparativo
+$report_html = <<
+
+
+
+ Reporte de Validación - Corrección de Listas
+
+
+
+ Reporte de Validación - Corrección de Listas HTML
+
+
+
Instrucciones:
+
+ - Abre cada par de archivos (ANTES/DESPUÉS) en el navegador
+ - Verifica que el contenido se muestre correctamente
+ - Las listas (fondo amarillo) deben contener solo items (fondo verde)
+ - Si todo se ve bien, la corrección es segura
+
+
+
+
+
+
+ | ID |
+ URL |
+ Problemas Antes |
+ Problemas Después |
+ Cambios |
+ Archivos |
+
+
+
+HTML;
+
+foreach ($comparison_data as $data) {
+ $status_class = $data['issues_after'] == 0 ? 'success' : ($data['issues_after'] < $data['issues_before'] ? 'warning' : 'error');
+
+ $report_html .= <<
+ {$data['id']} |
+ {$data['url']} |
+ {$data['issues_before']} |
+ {$data['issues_after']} |
+ {$data['changes']} |
+
+ ANTES |
+ DESPUÉS
+ |
+
+HTML;
+}
+
+$report_html .= <<
+
+
+ Generado: {$_SERVER['REQUEST_TIME_FLOAT']}
+
+
+HTML;
+
+$report_file = "$output_dir/comparison_report.html";
+file_put_contents($report_file, $report_html);
+
+echo "\n─────────────────────────────────\n";
+echo "REPORTE GENERADO:\n";
+echo " $report_file\n\n";
+echo "Para revisar, descarga el directorio:\n";
+echo " scp -r VPSContabo:$output_dir ./validation/\n\n";
+
+$conn->close();
+echo "✓ Validación completada.\n";