whatsincabo · Fase 6 del plan · auditoría de solo lectura · 5 sep 2026
Las 28 fichas con URL vieja: qué pasa si se migran a /listing/
Diecisiete campos de golf viven en /golf-courses/nombre/ y once tours en /tour/nombre/; la guía canónica manda que toda ficha viva en /listing/nombre/id/. Antes de mover nada, la guía exige una auditoría y un canary de cinco. Esto es la auditoría: cuatro inspectores (reescritura, datos estructurados y migas, enlaces internos, sitemap y antecedentes), cada hallazgo intentado refutar por un adversario que repitió la comprobación. Nada se tocó en el servidor.
16
hallazgos confirmados · 3 altos · 7 medios5
hallazgos refutados y descartados11
cosas que ya están bien
Cerrado el 12 de septiembre de 2026. Iván decidió no mover ninguna URL publicada: las 28 fichas se quedan donde están y esta migración queda cancelada. El informe se conserva como registro.
Veredicto. SE PUEDE PREPARAR EL CANARY, PERO HOY NO SE PUEDE EJECUTAR. El mecanismo está sano y probado: las 28 fichas (17 golf en /golf-courses/, 11 tours en /tour/) responden 200 con self-canonical, están en el sitemap, su URL neutral /listing/{slug}/{id}/ hace 301 de un salto de vuelta, ninguna tiene la bandera _wic_neutral_url ni canonical/robots fijos en base de datos, y Costa Palmas (3232, golf con módulo 3D) ya vive en /listing/ con canonical, og:url, schema, migas y sitemap correctos: es la prueba exacta de que el patrón funciona. Lo que falta es el pre-flight que exige la guía (§2.3, líneas 194-210, y Gate 11.2, líneas 948-962) y que hoy fallaría en 4 de los 11 puntos de verificación de la Fase 5.4: (1) hay 108 enlaces internos escritos a mano hacia las 28 fichas en el hub de golf, la guía best-golf, el mapa /map/ y el hub de tours, más llms.txt con las 11 de tours; (2) seis fichas de golf con módulo propio (Palmilla, Vidanta, Querencia, Puerto Los Cabos, Campestre y Cabo Real) llevan la URL vieja escrita a mano en el schema y en la miga, y Cabo Real además tiene el explorador 3D sellado a la ruta actual (con la bandera se apaga); (3) las migas de las 11 de tours cuelgan de /explore/, que es noindex; (4) LiteSpeed guarda 7 días tanto la ficha tipada como el 301 de la neutral y poner la bandera no purga nada, así que la verificación puede dar falsos resultados. Además la guía canónica solo contempla 17 fichas de golf: hay que actualizarla a 28 y congelar el mapa con las 28 antes del go/no-go. Nada de esto cambia el destino: son ediciones conocidas y de bajo coste (el patrón Costa Palmas es una línea por módulo), pero deben hacerse en la misma ventana que la bandera y con backup. Golf y /map/ siguen marcados fuera de límites en la memoria del proyecto: cualquier acción requiere OK explícito de Iván.
Qué te toca decidir
GO PARCIAL, en dos tiempos. Hoy: NO-GO para ejecutar (fallarían 4 de los 11 puntos: enlaces internos, schema de las 6 con módulo, migas de tours y caché), pero GO para preparar el pre-flight de las 28 y el canary de 5 fichas de bajo riesgo (3212 Solmar, 3220 Cabo San Lucas Country Club, 3224 Twin Dolphin, 5352 Kayak & Snorkel at the Arch, 5429 Small-Group Snorkeling with Cabo Trek), todas sin módulo propio, donde canonical, og:url, schema y migas se corrigen solos con la bandera. Quedan explícitamente fuera del canary Cabo Real (3201, explorador 3D sellado a la ruta) y las 5 con módulo 3D (3227, 3231, 3228, 3191, 3230) hasta que se edite su constante. Iván tiene que dar OK explícito a cinco cosas antes de que nadie toque nada, porque están fuera de límites o son regla del socio: (1) levantar la pausa de golf y /map/ para este trabajo; (2) regenerar wic-map-page.html con build-map-page.py; (3) añadir el hub /tours-activities/ a la miga de tours en wic-breadcrumb-schema.php; (4) limpiar los 5 hrefs viejos del post_content del home (357) y decidir qué hacer con la página /golf/ (5199); (5) aprobar el mapa congelado de 28 fichas y la actualización de la guía (§3.1 y §9.1). Después del canary y de 2-4 semanas limpias, segundo go/no-go para los lotes: resto sin módulo, luego las 5 con módulo 3D (Palmilla primero), y Cabo Real al final cuando exista su release nuevo. Si prefiere no abrir golf todavía, la alternativa mínima es un canary solo de tours (2-5 fichas) que valida el mecanismo en el namespace /tour/ sin tocar nada de golf ni /map/.
Canary recomendado (5 fichas de bajo riesgo)
Canary de 5 fichas de bajo riesgo, según la guía (Fase 5.4): 3 de golf SIN módulo 3D (variante A: schema del tema, canonical/og:url/@id/miga salen de get_permalink y se corrigen solos con la bandera) + 2 de tours (schema Product/TouristTrip y miga con get_permalink, se corrigen solos). Criterio para elegir las de golf: de las 11 sin módulo, las que NO aparecen en el post_content del hub /golf-courses/ (5512) tienen 4 enlaces a mano en vez de 6 y reducen la reescritura: 3212 Solmar Golf Links, 3220 Cabo San Lucas Country Club, 3224 Twin Dolphin Golf Club (alternativa: 3219 Quivira). Solmar además tiene la description con HTML y & en el schema del tema, lo que permite comparar el bloque completo antes/después como pide el hallazgo de schema sucio. Criterio para tours: dos que estén enlazadas desde el hub /tours-activities/ (así se prueba la reescritura del hub y el punto de inlinks) y que no dependan de módulos extra: 5352 Kayak & Snorkel at the Arch y 5429 Small-Group Snorkeling with Cabo Trek. EXCLUIR del canary a 3201 Cabo Real (explorador 3D sellado a la ruta + canonical/schema/miga a mano en wic-cabo-real-v3.php: requiere release nuevo) y a las 5 con módulo 3D (3227, 3231, 3228, 3191, 3230) hasta que se edite su constante línea 32 al patrón Costa Palmas. Palmilla (3227), aunque es la referencia del canon, va en el segundo lote, no en el canary. Ventana: 2-4 semanas de observación y segundo go/no-go de Iván antes de tocar las otras 23.
- 3212 Solmar Golf Links (golf sin módulo 3D, 4 enlaces a mano; schema del tema con description HTML para comparar antes/después)
- 3220 Cabo San Lucas Country Club (golf sin módulo 3D, 4 enlaces a mano)
- 3224 Twin Dolphin Golf Club (golf sin módulo 3D, 4 enlaces a mano; alternativa: 3219 Quivira Golf Club)
- 5352 Kayak & Snorkel at the Arch (tour enlazado desde el hub /tours-activities/, sin módulo propio)
- 5429 Small-Group Snorkeling with Cabo Trek (tour enlazado desde el hub /tours-activities/, sin módulo propio)
Hallazgos (16)
alta Cabo Real (3201): el explorador 3D está sellado a la ruta /golf-courses/cabo-real-golf-club/ y el plugin escribe a mano canonical, schema y migaEl runtime JS que carga producción (assets/cabo-real-v3-3.2.6/assets/cabo-real-explorer-runtime-CxyfwZ2h.js) lleva embebida una autorización con pathname "/golf-courses/cabo-real-golf-club/", release 3.2.5 y reuseAllowed=false, y la compara dos veces: contra su constante y contra window.location.pathname. Si la ficha pasa a /listing/cabo-real-golf-club/3201/, el módulo captura el error, marca loadError="invalid-contract" y muestra "The schematic data could not be verified, so the 3D view was not rendered.": la página no rompe, pero el 3D queda apagado hasta que exista un release nuevo con la ruta nueva y autorización nueva (carril de otro agente, según el propio plugin). El modo sin validación solo se activa si data-production-authorization va vacío, y wic-cabo-real-v3.php lo emite siempre (línea 294); el plugin se activa por ID de post (línea 44), no por URL, así que seguiría inyectándolo en la neutral. Además wic-cabo-real-v3.php escribe a mano la URL tipada en el canonical (línea 196, filtro rank_math/frontend/canonical a prioridad 10: ese sí lo pisa el cinturón de wic-neutral-urls a prioridad 99), en el schema GolfCourse (línea 303: @id, url, mainEntityOfPage) y en el item 3 del BreadcrumbList (línea 368); estos dos últimos no pasan por get_permalink y quedarían apuntando a una URL que hace 301.
Fichas: 3201 Cabo Real Golf Club
Qué cambiar antes: No incluir 3201 en el canary ni en el primer lote. Antes de migrarla: generar un release nuevo del explorador con pathname /listing/cabo-real-golf-club/3201/ y autorización nueva (carril del agente del explorador), y editar las URLs fijas de wic-cabo-real-v3.php (líneas 196, 303 y 368) con backup fechado y php -l, en la misma ventana que la bandera. Verificar en vivo que el 3D arranca y que schema @id/url y miga apuntan a la neutral.
Evidencia
ssh whatscabo "cd /home/u237948674/domains/whatsincabo.com/public_html/wp-content/mu-plugins && grep -oE '.{0,200}pathname.{0,200}' assets/cabo-real-v3-3.2.6/assets/cabo-real-explorer-runtime-CxyfwZ2h.js" -> T0="/golf-courses/cabo-real-golf-club/", e.pathname!==... throw "Production authorization is invalid", window.location.pathname!==e.pathname throw "Production authorization does not match this host, path, or release". grep -nE 'WIC_CR_V3_|get_queried_object' wic-cabo-real-v3.php -> línea 12 POST_ID=3201, 16 VERSION='3.2.5', 17 ASSET_DIR='cabo-real-v3-3.2.6', 18 PRODUCTION_AUTHORIZATION='cabo-real-3.2.5-owner-approved-2026-07-31', 44 get_queried_object_id()===3201; sed -n '192,198p;301,305p;366,369p' wic-cabo-real-v3.php -> canonical, $url del schema e item 3 fijos. curl -s -A "Mozilla/5.0" https://whatsincabo.com/golf-courses/cabo-real-golf-club/ -> data-production-authorization="cabo-real-3.2.5-owner-approved-2026-07-31"; "@id":"https://whatsincabo.com/golf-courses/cabo-real-golf-club/#golf-course"; "mainEntityOfPage":"https://whatsincabo.com/golf-courses/cabo-real-golf-club/".
alta Cinco módulos 3D de golf (Palmilla, Vidanta, Querencia, Puerto Los Cabos, Campestre) escriben la URL tipada en una constante y la meten en el schema y en la migawic-palmilla-3d.php (3227), wic-vidanta-3d.php (3231), wic-querencia-3d.php (3228), wic-puerto-los-cabos-3d.php (3191) y wic-campestre-3d.php (3230) definen en la línea 32 const WIC_<X>_CANONICAL = 'https://whatsincabo.com/golf-courses/<slug>/' y la usan en '@id' (línea 134, sufijo #golf-course), 'url' (136), '@id' del BreadcrumbList (176 en Palmilla / 191 en los otros) y en el item de posición 3 de la miga (194 / 209). El JSON-LD lo escriben ellos mismos con wp_json_encode, no pasa por Rank Math. Se activan por ID de post (línea 41), así que en /listing/ seguirían inyectando la URL vieja. Con la bandera puesta, canonical y og:url sí migrarían solos (salen de Rank Math/get_permalink; ningún módulo 3D toca esos filtros), pero fallarían dos de los 11 puntos: «Schema @id y url nuevos» y «Breadcrumbs correctos». La cura ya está probada en producción: wic-costa-palmas-3d.php tiene su constante en /listing/costa-palmas-golf-club/3232/ y la ficha migrada emite todo en la neutral. Los 6 módulos escriben el item 2 como /golf/ (que hace 301); lo corrige wic-golf-hub-unify.php por tipo de ficha (WIC_GHU_ACTIVO=true) y también funciona en /listing/ (Costa Palmas lo demuestra), así que esa dependencia debe seguir encendida.
Fichas: 3227 Palmilla, 3231 Vidanta, 3228 Querencia, 3191 Puerto Los Cabos, 3230 Club Campestre
Qué cambiar antes: Dejarlas fuera del canary. Para su lote: cambiar la línea 32 de cada módulo a https://whatsincabo.com/listing/<slug>/<id>/ (patrón Costa Palmas) o derivarla de get_permalink, con backup fechado y php -l, en el mismo despliegue que la bandera. Mantener wic-golf-hub-unify.php activo. Verificar en vivo @id, url e item 3 en la neutral, ficha por ficha.
Evidencia
ssh whatscabo "cd .../wp-content/mu-plugins && grep -nE '/golf-courses/|/golf/|/listing/' wic-palmilla-3d.php wic-vidanta-3d.php wic-querencia-3d.php wic-puerto-los-cabos-3d.php wic-campestre-3d.php wic-costa-palmas-3d.php" -> línea 32 en cada uno con la tipada (Costa Palmas: /listing/costa-palmas-golf-club/3232/); grep -n WIC_PALMILLA_CANONICAL wic-palmilla-3d.php -> 134, 136, 176, 194; grep -lE 'rank_math/frontend/canonical' wic-*-3d.php -> vacío. curl -s -A "Mozilla/5.0" https://whatsincabo.com/golf-courses/palmilla-golf-club/ -> "@id":"https://whatsincabo.com/golf-courses/palmilla-golf-club/#golf-course", item 3 tipado. curl -s -A "Mozilla/5.0" https://whatsincabo.com/listing/costa-palmas-golf-club/3232/ -> canonical, og:url, @id #golf-course, url e item 3 en /listing/; curl -s -D - -o /dev/null https://whatsincabo.com/golf-courses/costa-palmas-golf-club/ -> 301 a la neutral. wp post meta get 3232 _wic_neutral_url -> 1.
alta 108 enlaces internos escritos a mano hacia las 28 fichas, servidos hoy en 5 superficies, más llms.txtMedido en el HTML vivo: (1) post_content del hub /golf-courses/ (página 5512): 11 campos x 2 = 22 hrefs relativos; (2) post_content de /guides/best-golf-courses-in-cabo/ (5201, index, en page-sitemap.xml): 17 x 2 = 34 relativos (Costa Palmas ya aparece como /listing/, señal de que en julio se editó a mano); (3) wic-golf-polish.php, función wic_gp_enlace() línea 104, construye '<a href="/golf-courses/'.$slug.'/">' para los 17 campos en la prosa del hub (is_page 5512); (4) wic-map-page.html (estático, servido en /map/, se regenera SOLO con build-map-page.py; /map/ está fuera de límites en memoria): 17 hrefs absolutos en el <noscript>; (5) post_content del hub /tours-activities/ (5441): 9 tours x 2 = 18 relativos. Total 90 golf + 18 tours = 108. Además llms.txt lista las 11 URLs /tour/. Todos irían a un 301 de un salto, pero el Gate 11.2 (fila Inlinks) exige enlaces «sin depender de redirects» y el punto «Internal links apuntan a nueva» fallaría en las 28 desde su propio hub, su guía y el mapa. Los enlaces que salen de get_permalink (tarjetas del hub de golf: wic-golf-hub.php líneas 193/208/210; carrusel del home: functions.php 4100; hub de tours para las ya migradas) se corrigen solos. El tema padre, el tema hijo, _elementor_data, menús y opciones están limpios.
Fichas: Las 17 de golf (5512, 5201, golf-polish, /map/) y 9 de las 11 de tours (5441: todas menos 5129 y 5419); llms.txt las 11 de tours
Qué cambiar antes: Pre-flight obligatorio, congelado y con backup en ~/backups/exec/ como en julio: reescribir los hrefs del post_content de 5201, 5512 y 5441 derivando de get_permalink (abortar si un reemplazo no da exactamente 1, como en los lotes de julio); cambiar wic_gp_enlace() para que use get_permalink por slug; regenerar wic-map-page.html con build-map-page.py (requiere OK explícito de Iván: /map/ fuera de límites); regenerar llms.txt. Para el canary de 5 basta reescribir lo que apunta a esas 5 fichas, pero el mapa y el polish se tocan enteros. Usar el crawl vivo como fuente, no el índice de Rank Math.
Evidencia
ssh whatscabo "cd .../public_html && for id in 5512 5201 5441; do wp post get $id --field=post_content | grep -oE 'href=\"[^\"]*/(golf-courses|tour)/[a-z0-9-]+/?\"' | sort | uniq -c; done" -> 22 / 34 / 18. grep -nE 'function wic_gp_enlace|href="/golf-courses/' wp-content/mu-plugins/wic-golf-polish.php -> 103-104; grep -oE "wic_gp_enlace\( *'[a-z0-9-]+'" ... | sort -u | wc -l -> 17. grep -oE '/golf-courses/[a-z0-9-]+/' wp-content/mu-plugins/wic-map-page.html | sort -u | wc -l -> 17 (línea 1732, archivo de 158302 B modificado 5-sep 01:21). curl -s -A "Mozilla/5.0" https://whatsincabo.com/golf-courses/ | grep -oE 'href="/golf-courses/[a-z0-9-]+/"' | wc -l -> 39 relativos (+51 absolutos por the_permalink). curl -s https://whatsincabo.com/guides/best-golf-courses-in-cabo/ -> robots index, canonical propia, 34 hrefs. curl -s https://whatsincabo.com/tours-activities/ | grep -oE 'href="/(tour|listing)/[^"]+"' | sort | uniq -c -> 9 tours x 2. curl -s https://whatsincabo.com/map/ | grep -c 'href="https://whatsincabo.com/golf-courses/' -> 17. curl -s https://whatsincabo.com/llms.txt | grep -cE '/tour/[a-z]' -> 11. grep -rcE '/golf-courses/|/tour/|/golf/' wp-content/themes/my-listing-child wp-content/themes/my-listing --include='*.php' | grep -v ':0$' -> vacío.
media Las migas de las 11 fichas de tours cuelgan de /explore/ (noindex) y de un término de taxonomía noindex, no del hub /tours-activities/En las 11 fichas /tour/ el BreadcrumbList es Home > Tours & Activities (item https://whatsincabo.com/explore/, meta robots «noindex, follow») > término (/tours-taxonomy-activity-type/<término>/, 10 términos comprobados, todos «noindex, follow», taxonomía con sitemap off) > ficha. El hub real /tours-activities/ (200, «follow, index», self-canonical, en page-sitemap.xml) no aparece. Causa: wic-breadcrumb-schema.php, case 'tours-activities' (líneas 52-53) devuelve name+tax sin la clave 'hub'; solo 'transportation' la declara (línea 57) y la línea 91 cae a /explore/ cuando falta; el propio comentario del autor (2026-08-24) lo reconoce. Las 17 de golf sí cuelgan de /golf-courses/ (lo corrige wic-golf-hub-unify.php). No lo causa la migración, pero el punto «Breadcrumbs correctos» de la Fase 5.4 ya está en amarillo hoy, exactamente el mismo aviso que registró el paquete Gate 11.2 de julio («decidirlo ANTES de mover»). Además el inventario decía que /tours-activities/ no estaba en ningún sitemap: es falso, está en page-sitemap.xml.
Fichas: Las 11 de tours-activities (5121, 5129, 5135, 5352, 5366, 5377, 5386, 5393, 5409, 5419, 5429)
Qué cambiar antes: Decidirlo antes del canary, no después: añadir 'hub' => '/tours-activities/' al case 'tours-activities' de wic-breadcrumb-schema.php (misma cura que transportation; memoria del proyecto: pendiente del OK de Iván) y decidir si el nivel de taxonomía noindex sigue en la miga. Tocar ese archivo cambia el dato estructurado de las 11 fichas: OK de Iván.
Evidencia
for u in <las 11 URLs /tour/>; do curl -s -A "Mozilla/5.0" "$u" | grep -oE '"position":[0-9]+,"name":"[^"]*","item":"[^"]*"'; done -> las 11 con position 2 = https://whatsincabo.com/explore/. curl -s -A "Mozilla/5.0" https://whatsincabo.com/explore/ | grep -o '<meta name="robots" content="[^"]*"' -> noindex, follow. curl -s https://whatsincabo.com/tours-taxonomy-activity-type/arch-tour/ -> 200, noindex, follow. curl -s -D - https://whatsincabo.com/tours-activities/ -> 200, follow, index, canonical https://whatsincabo.com/tours-activities/; curl -s https://whatsincabo.com/page-sitemap.xml | grep tours-activities -> presente. ssh whatscabo "sed -n '46,92p' wp-content/mu-plugins/wic-breadcrumb-schema.php" -> línea 52-53 sin 'hub', 57 transportation con 'hub' => '/transportation/', 91 fallback a $explore. sed -n '43,49p' GATE-11.2-EVIDENCIA-CANARY-LISTING-2026-07-25.md -> «Breadcrumbs presentes ⚠️».
media LiteSpeed guarda 7 días las 28 fichas tipadas Y los 301 de las neutrales; poner la bandera no purga nada y el calentador sigue tocando las viejas hasta 6 horascache-ttl_pub = 604800 (7 días). Las 28 tipadas responden x-litespeed-cache: hit (recalentadas minutos antes por wic-cache-warmer.php, que corre cada 10 min y lee su lista del transient wic_warmer_urls de 6 h, hoy con las 28 tipadas). El guard que emitiría el 301 vive en PHP (template_redirect): una copia cacheada nunca pasa por él. `wp post meta update <id> _wic_neutral_url 1` no dispara purga: LiteSpeed Cache 7.9 solo purga por delete_post, wp_trash_post, wp_update_comment_count y transition_post_status, ningún gancho de meta. Peor: el 301 de la neutral SÍ se cachea (miss en la primera petición, hit en la segunda; LiteSpeed solo marca no-cache cuando el 301 apunta a la misma URL). Consecuencia: tras poner la bandera, la vieja puede seguir dando 200 y la nueva (que pasa a ser la canónica) puede seguir dando un 301 cacheado de vuelta a la vieja hasta 7 días; si se purga solo la tipada hay bucle tipada->neutral->tipada; si no se purga, Google ve lo contrario del objetivo. La respuesta 301 salió sin cabecera x-litespeed-tag, así que la purga por post podría no alcanzarla. La verificación de 11 puntos de la guía no menciona la caché; solo el rollback del Gate 11.2 dice «+ purge LiteSpeed». Memoria del proyecto: `litespeed-purge url` dice Success sin purgar; `wp post update` sí refresca.
Fichas: Las 28
Qué cambiar antes: Añadir la caché al procedimiento del canary: tras poner la bandera, purgar por ficha con un método que se compruebe (wp post update de la ficha o purga por etiqueta) y purgar TAMBIÉN la URL neutral; comprobar `x-litespeed-cache: miss` en la primera petición de ambas URLs y medir con cache-buster; repetir la medición a las 24 h; opcionalmente borrar el transient wic_warmer_urls para que el calentador lea el sitemap nuevo de inmediato. Probar antes el bucle con una ficha del canary.
Evidencia
ssh whatscabo "wp option get litespeed.conf.cache-ttl_pub" -> 604800. for u in <28 tipadas>: curl -s -A "Mozilla/5.0" -D - -o /dev/null "$u" | grep -iE '^(HTTP|x-litespeed-cache|etag)' -> 28/28 200 + hit (tabla en scratchpad/audit-cache-28.tsv). curl ... https://whatsincabo.com/listing/prueba-adversario-932/3227/ -> 1ª: 301 + miss (sin x-litespeed-tag); 2ª y 3ª: 301 + hit. sed -n '570,625p' wp-content/plugins/litespeed-cache/src/control.cls.php -> check_redirect solo set_nocache('301 to same url'). sed -n '76,92p' wp-content/plugins/litespeed-cache/src/purge.cls.php -> eventos de purga sin *_post_meta; grep -rnE "add_action\( *'(updated|added|deleted)_post_meta" wp-content/plugins/litespeed-cache/ -> vacío. wp transient get wic_warmer_urls | grep -oE 'https://whatsincabo.com/(golf-courses|tour)/[^/"]+/' | sort -u | wc -l -> 28; wic-cache-warmer.php línea 50 set_transient 6 h, línea 60 redirection => 2. grep -niE 'cach|purg|litespeed' en Fase 5 de la guía (líneas 860-1031) -> sin resultados.
media La guía canónica solo contempla 17 fichas de golf: /tour/ es un segundo namespace tipado con 11 fichas creadas después del 2026-07-26§3.1 (línea 368) dice «Único namespace tipado que queda: /golf-courses/{slug}/ 17 fichas» y §9.1 (línea 1199) limita el pre-flight a «las 17 fichas de golf, que son las únicas que quedan». Las 11 fichas de tours (IDs 5121-5429) se crearon el 13-14 de agosto de 2026 con el skill wic-activity-listing, en /tour/{slug}/ y sin bandera; el propio skill lo ordena (SKILL.md línea 14: «NO crear fichas con _wic_neutral_url durante el canary», smoke-test.sh asume /tour/{slug}/). Fue una decisión consciente, no un descuido, pero contradice la «regla vigente» de §3.1 y deja la guía desactualizada: si se migra, la Fase 5 aplica a 28 fichas, no a 17, y el mapa congelado y el go/no-go de Iván deben cubrir las 28. Hoy no hay daño SEO (200, self-canonical, 301 de un salto, en sitemap).
Fichas: Las 11 de tours-activities; de rebote, el alcance de todo el procedimiento (28, no 17)
Qué cambiar antes: Actualizar §3.1 y §9.1 de la guía a 28 fichas (17 golf + 11 tours) y congelar el mapa 1:1 con las 28 (este inventario ya lo es). Decidir con Iván si el skill wic-activity-listing (y wic-golf-listing) debe poner la bandera al crear fichas nuevas para no seguir generando deuda.
Evidencia
sed -n '358,396p;1195,1205p' "/config/workspace/whats in cabo /claude-code-template/.claude/docs/PLAN-SEO-FINAL-WIC-2026-06-30.md". ssh whatscabo "wp post list --post_type=job_listing --post_status=publish --post__in=5121,...,5429 --fields=ID,post_name,post_date --format=csv" -> 11 filas, post_date 2026-08-13/14. wp db query "SELECT pm.meta_value tipo, COUNT(*) FROM wp_posts p JOIN wp_postmeta pm ... LEFT JOIN wp_postmeta f ON f.meta_key='_wic_neutral_url' WHERE ... f.meta_id IS NULL GROUP BY tipo" -> golf-courses 17, tours-activities 11. grep -rn -iE 'neutral|/tour/' "/config/workspace/whats in cabo /claude-code-template/.claude/skills/wic-activity-listing" -> SKILL.md:14 y scripts/smoke-test.sh:14.
media Hay tres generadores distintos de schema para el mismo tipo GolfCourse (más el de tours): un canary de «1 golf» no cubre el mecanismo realVariante A (tema MyListing + wic-breadcrumb-schema): 11 fichas (3212, 3215, 3216, 3218, 3219, 3220, 3221, 3223, 3224, 3225, 3226), GolfCourse con @id = URL sin fragmento, BreadcrumbList sin @id; siguen a get_permalink. Variante B (wic-*-3d.php): 3191, 3227, 3228, 3230, 3231 (+ control 3232), @id #golf-course y #breadcrumb con constante a mano; 3191 añade FAQPage. Variante C (wic-cabo-real-v3.php): 3201, @id #golf-course, mainEntityOfPage, BreadcrumbList sin @id, y bloquea GolfCourse/BreadcrumbList/FAQPage de otros emisores (línea 381). Variante D (tours): Product+TouristTrip de wic-fix-theme-schema.php + FAQPage + BreadcrumbList, todo con get_permalink. Hay un quinto emisor latente (wic-golf-listing-v3.php) que hoy no actúa en ninguna ficha. Cada variante tiene su propia ruta de verificación y de rollback: en B y C el rollback exige revertir constantes PHP además de quitar la bandera.
Fichas: Las 17 de golf (11 A, 5 B, 1 C) y las 11 de tours (D)
Qué cambiar antes: Diseñar el canary y los lotes por variante: el canary de 5 cubre A y D; el segundo lote debe incluir al menos una ficha B (Palmilla, la referencia del canon) con su propia verificación de 11 puntos; Cabo Real (C) va sola y al final. Documentar en el mapa congelado qué archivo hay que revertir en cada ficha para el rollback.
Evidencia
Extracción de JSON-LD de las 17 golf + 3232 + 4 tours (curl -s -A "Mozilla/5.0", parseado con python; sd-extract.json en el scratchpad): 3212-3226 @id sin fragmento; 3191/3227/3228/3230/3231 @id #golf-course y #breadcrumb; 3201 mainEntityOfPage string y BreadcrumbList sin @id; tours ['Product','TouristTrip']. ssh whatscabo "grep -rlE 'GolfCourse|BreadcrumbList' wp-content/mu-plugins | grep -v backups" -> los 6 módulos 3D, wic-cabo-real-v3.php, wic-golf-listing-v3.php, wic-breadcrumb-schema.php, wic-fix-theme-schema.php. wp db query sobre case27_listing_type_settings_page del tipo 3190 -> "@type":"GolfCourse","@id":"[[:url]]". sed -n '378,384p' wic-cabo-real-v3.php -> $blocked = ['GolfCourse','BreadcrumbList','FAQPage'].
media 11 fichas de golf emiten el schema crudo del tema: @id sin fragmento, description con HTML y &, y name con & en Chileno Bay y El Doradowic-fix-theme-schema.php (líneas 553-557) solo limpia @type Restaurant y TouristTrip; el tipo GolfCourse pasa sin transformar. En 3212, 3215, 3216, 3218, 3219, 3220, 3221, 3223, 3224, 3225 y 3226 el GolfCourse lleva @id igual a la URL de la página (misma cadena que identificaría a la WebPage), description que empieza por <p> con <br /> y &, y en 3225 y 3226 el name es «Chileno Bay Golf & Beach Club» / «El Dorado Golf & Beach Club» mientras la miga de la misma página dice «Chileno Bay Golf & Beach Club». No bloquea la migración (el @id sale de [[:url]] y sigue al permalink), pero la verificación «schema @id/url nuevos» pasaría sobre un bloque que sigue sucio.
Fichas: 3212, 3215, 3216, 3218, 3219, 3220, 3221, 3223, 3224, 3225, 3226 (& en name solo 3225 y 3226)
Qué cambiar antes: No bloquea el canary. Incluir en la verificación de las 3 golf del canary la comparación del bloque GolfCourse completo antes/después (no solo @id/url), y anotar como deuda aparte una rama GolfCourse en wic-fix-theme-schema.php (decodificar entidades y quitar HTML).
Evidencia
curl -s -A 'Mozilla/5.0' https://whatsincabo.com/golf-courses/chileno-bay-golf-and-beach-club/ -> "@type": "GolfCourse", "@id": "https://whatsincabo.com/golf-courses/chileno-bay-golf-and-beach-club/", "name": "Chileno Bay Golf & Beach Club", "description": "<p>Chileno Bay Golf & Beach Club is a private..."; en la misma página el BreadcrumbList dice "Chileno Bay Golf & Beach Club". Barrido de las 17 (grep -oE '"@id": *"https://whatsincabo.com/golf-courses/[^"]*"' y grep -cE '"description": *"<p>') -> 11 sin fragmento + HTML. ssh whatscabo "sed -n '548,562p' wp-content/mu-plugins/wic-fix-theme-schema.php"; grep -rn GolfCourse wp-content/mu-plugins/*.php -> ninguna línea en wic-fix-theme-schema.php. grep -n '@id' wp-content/themes/my-listing/includes/src/listing-types/schema/LocalBusiness.php -> línea 6 '@id' => '[[:url]]'.
media El 301 del guard neutral se emite dentro de los buffers de salida de los módulos de golf: hoy sale limpio por casualidad, no por diseñowic-neutral-urls.php emite el 301 en template_redirect a prioridad 5. Antes ya han abierto ob_start() sobre la misma petición wic-golf-hub-unify.php (-3000), wic-golf-seo-fix.php (-2000), wic-cabo-real-v3.php (-1000), wic-controlled-listing-access.php (0), los seis módulos 3D y wic-listing-module-parity.php (1) y wic-golf-listing-v3.php (4). Al hacer wp_safe_redirect + exit todos reciben buffer vacío y todos lo devuelven intacto (verificado línea a línea), por eso el 301 actual va con cuerpo vacío, un salto y sin X-Robots-Tag. Ningún módulo futuro con buffer lo garantiza. Punto a favor: todos filtran por get_queried_object_id() o por _case27_listing_type, no por ruta, así que en /listing/ renderizan igual (Costa Palmas lo prueba). El riesgo de X-Robots-Tag: noindex del preview de Cabo Real hoy es nulo (WIC_CR_V3_LIVE=true).
Fichas: Las 17 de golf (seo-fix, hub-unify y golf-listing-v3 aplican a todas por tipo; buffers 3D en 6)
Qué cambiar antes: En la verificación del canary comprobar que el 301 de la URL vieja lleva cuerpo vacío, un solo salto y sin X-Robots-Tag. Regla para el runbook: si se toca cualquier módulo de golf, mantener el guard antes de cualquier buffer que pueda hacer exit.
Evidencia
ssh whatscabo "cd .../mu-plugins && grep -nE \"template_redirect|ob_start|exit;|wp_safe_redirect\" wic-golf-seo-fix.php wic-cabo-real-v3.php wic-golf-listing-v3.php wic-*-3d.php wic-golf-hub-unify.php wic-listing-module-parity.php wic-controlled-listing-access.php wic-neutral-urls.php" -> prioridades -3000/-2000/-1000/0/1/4 frente a 5; sed -n '271,273p;364,366p' wic-golf-seo-fix.php, '414,417p' wic-cabo-real-v3.php, '278,281p' wic-palmilla-3d.php, '803,805p' wic-golf-listing-v3.php -> if ('' === $html) return $html. curl -s -A "Mozilla/5.0" -D - -o body.bin https://whatsincabo.com/listing/palmilla-golf-club/3227/ (ídem 3219, 3201, 5121) -> 301, x-redirect-by: WordPress, content-length: 0, sin X-Robots-Tag; curl -s -L -w '%{num_redirects}' -> 1.
media Residuos de la migración de julio: una cadena de 2 saltos en un mapa de redirect fijo y 5 enlaces /tour/ viejos en el post_content del home(a) wic-qs-preserving-redirects.php línea 25 manda /things-to-do/cabo-adventures/ a /tour-operator/cabo-adventures/ escrito a mano; la ficha 797 ya migró (_wic_neutral_url=1), así que hoy hay cadena: /things-to-do/cabo-adventures/ -> 301 -> /tour-operator/cabo-adventures/ -> 301 -> /listing/cabo-adventures/797/. Contradice literalmente el requisito Mapping del Gate 11.2 («sin cadenas») y muestra el patrón de riesgo: un mapa fijo que no sigue a get_permalink. (b) El post_content del home (357) conserva 5 hrefs a /tour/camel-ride-cabo/, /tour/ecocat-sunset-cruise/, /tour/wild-canyon-zipline-tour/, /tour/ocean-safari-orcas-whales-bajas-ocean-giants/ y /tour/canyon-safari-outdoor-4x4-zip-line/ (migradas en julio, hoy 301). El home vivo no los sirve (Elementor pinta desde _elementor_data, que no los contiene) pero Rank Math sí los lee: 5 filas en wp_rank_math_internal_links desde 357. El home se re-guardó el 2026-08-21 y el residuo sobrevivió. Ninguna de las 28 está implicada, pero el punto «0 residuos» los cuenta.
Fichas: Ninguna de las 28 (797, 2216, 2225, 2234, 1936, 1954 ya migradas)
Qué cambiar antes: Apuntar la línea 25 de wic-qs-preserving-redirects.php a get_permalink(797) para eliminar la cadena. Limpiar los 5 hrefs del post_content del home (home = solo widgets Elementor por regla del socio: OK de Iván). Regla para el runbook: revisar todo mapa de redirect fijo antes de cada lote.
Evidencia
curl -s -A "Mozilla/5.0" -L -o /dev/null -w '%{num_redirects} -> %{url_effective} (%{http_code})' https://whatsincabo.com/things-to-do/cabo-adventures/ -> 2 redirects -> https://whatsincabo.com/listing/cabo-adventures/797/ (200). ssh whatscabo "sed -n '23,26p' wp-content/mu-plugins/wic-qs-preserving-redirects.php"; wp post meta get 797 _wic_neutral_url -> 1. wp post get 357 --field=post_content | grep -oE 'href="https://whatsincabo.com/tour/[^"]+"' | sort -u -> 5; wp post meta get 357 _elementor_data | grep -oE 'tour\\?/(los 5 slugs)' -> 0; curl -s https://whatsincabo.com/ | grep -c de los 5 -> 0. wp db query "SELECT post_id,target_post_id,url FROM wp_rank_math_internal_links WHERE post_id=357 AND url LIKE '%/tour/%'" -> 5 filas.
baja La página /golf/ (5199) sigue publicada con 68 URLs absolutas a las 17 fichas de golf aunque hace 301El post_content de 5199 «Golf in Cabo» tiene 17 slugs x 4 URLs absolutas /golf-courses/<slug>/ (3 hrefs + 1 url de un ItemList JSON-LD por campo). La URL responde 301 a /golf-courses/ por wic-golf-hub-unify.php (WIC_GHU_ACTIVO=true; si se pone en false vuelve a servirse con esos enlaces). Rank Math la indexa: 56 filas de enlaces internos de 5199, 51 hacia fichas de golf. No se sirve, pero un pre-flight que cuente por base de datos la reportará como 68 enlaces vivos.
Fichas: Las 17 de golf
Qué cambiar antes: Decisión de Iván: incluir 5199 en el mapa de reescritura o despublicarla. En cualquier caso, dejar por escrito que WIC_GHU_ACTIVO no se apaga.
Evidencia
ssh whatscabo "wp post get 5199 --fields=ID,post_type,post_status,post_name --format=json" -> page, publish, golf; wp post get 5199 --field=post_content | grep -oE 'https://whatsincabo\.com/golf-courses/[a-z0-9-]+/' | wc -l -> 68 (17 únicos, 4 cada uno). curl -s -A "Mozilla/5.0" -D - -o /dev/null https://whatsincabo.com/golf/ -> 301, location https://whatsincabo.com/golf-courses/. grep -nE 'WIC_GHU_ACTIVO|WIC_GHU_VIEJO|wp_safe_redirect' wp-content/mu-plugins/wic-golf-hub-unify.php -> líneas 43-44, 72. wp db query "SELECT COUNT(*) FROM wp_rank_math_internal_links WHERE post_id=5199" -> 56.
baja Filtro latente en wic-golf-listing-v3.php: un release horneado de Palmilla con canonical tipado, hoy inertewic-golf-listing-v3.php engancha rank_math/frontend/canonical y og_url a prioridad 999 (líneas 905-906) devolviendo release.content.seo.canonical; ganaría al cinturón de wic-neutral-urls (99). Existe un release no revocado para 3227 (wic-golf-releases/3227/9af33544...json) con "canonical": "https://whatsincabo.com/golf-courses/palmilla-golf-club/". Hoy no actúa: ningún post tiene el meta _wic_golf_v3_active_release (0 filas) y el HTML vivo no lleva data-wic-golf-v3-root. La vía oficial de activación (skill wic-golf-listing, apply_release.php línea 167 -> wp_release_lib.php 105-138) compara expectedPermalink con get_permalink() y aborta con «CAS guard failed» si la ficha cambió de URL, así que el release viejo no puede activarse por el flujo normal tras migrar; solo estallaría si alguien escribe el meta a mano.
Fichas: 3227 Palmilla (y cualquier golf a la que se active un release v3 después)
Qué cambiar antes: Anotar en el mapa congelado: antes de activar cualquier release v3 tras migrar, regenerarlo con la URL /listing/ (el canonical y el schema salen del release); nunca activar por `wp post meta update` a mano.
Evidencia
ssh whatscabo "sed -n '895,915p;223,262p' wp-content/mu-plugins/wic-golf-listing-v3.php"; wp db query "SELECT COUNT(*) FROM wp_postmeta WHERE meta_key='_wic_golf_v3_active_release'" -> 0; ls /home/u237948674/domains/whatsincabo.com/wic-golf-releases/ -> solo 3227; grep -hoE '"canonical": *"[^"]+"' .../3227/*.json -> tipada. curl -s https://whatsincabo.com/golf-courses/palmilla-golf-club/ | grep -c data-wic-golf-v3-root -> 0. Local: sed -n '160,170p' "/config/workspace/whats in cabo /claude-code-template/.claude/skills/wic-golf-listing/scripts/apply_release.php" y '105,142p' wp_release_lib.php -> guard expectedPermalink.
baja Sellos JSON y datos de los assets 3D llevan la URL tipada (publishedUrl, listingUrl), pero nadie los leeLos sellos assets/*-3d-*/data/*-artifact-seal.json (palmilla 1.1.1-1.1.3, vidanta 1.0.0-1.0.2, querencia 1.0.1, campestre 1.0.1, puerto-los-cabos 1.0.2-1.0.3) y assets/cabo-real-v3/data/cabo-real-geometry.json llevan la URL tipada; los runtimes viejos de Cabo Real 3.2.1-3.2.5 conservan la misma validación de ruta pero no se encolan. palmilla-3d.js y vidanta-3d.js sí descargan el sello, pero solo usan seal.course, seal.mode y seal.artifact.sha256, nunca publishedUrl. Ningún PHP ni JS del sitio lee publishedUrl ni listingUrl. Son metadatos de procedencia; Costa Palmas ya los tiene con la neutral.
Fichas: 3227, 3231, 3228, 3191, 3230, 3201 (solo metadatos)
Qué cambiar antes: Nada antes del canary. Actualizar publishedUrl/listingUrl en el siguiente release de cada módulo para que la procedencia coincida con la URL viva.
Evidencia
ssh whatscabo "grep -rcF '/golf-courses/' wp-content/mu-plugins/assets --include='*.json' --include='*.js' | grep -v ':0$'" -> 17 archivos; grep -rl 'publishedUrl' wp-content/mu-plugins wp-content/themes --include='*.js' --include='*.php' -> vacío. grep -oE 'seal\.[A-Za-z_]+' assets/palmilla-3d-1.1.3/palmilla-3d.js -> artifact, course, json, mode.
baja El índice de enlaces internos de Rank Math no sirve como fuente del pre-flight: 168 filas huérfanas y 10 enlaces a URLs /tour/ ya migradaswp_rank_math_internal_links (1.484 filas) tiene 168 filas del post 3837, que ya no existe, y 10 filas hacia 5 URLs /tour/ viejas (desde 357 y 3837). Refleja post_content, no el HTML renderizado: el mapa (5228) tiene 0 filas aunque /map/ enlaza 17 fichas; el hub /golf-courses/ tiene 11 fichas en el índice frente a 17 en vivo. Si el pre-flight se apoya en él contará enlaces que no existen y omitirá los dinámicos.
Fichas: Ninguna directamente; afecta a la fiabilidad del pre-flight (Gate 11.2, fila Inlinks)
Qué cambiar antes: Usar el crawl vivo (este inventario y los HTML del scratchpad) como fuente del pre-flight y reindexar Rank Math después de migrar.
Evidencia
ssh whatscabo "wp db query \"SELECT COUNT(*) FROM wp_rank_math_internal_links l LEFT JOIN wp_posts p ON p.ID=l.post_id WHERE p.ID IS NULL\"" -> 168; wp db query "SELECT ID FROM wp_posts WHERE ID=3837" -> vacío; wp db query "SELECT url,COUNT(*),GROUP_CONCAT(DISTINCT post_id) FROM wp_rank_math_internal_links WHERE url REGEXP '/(golf-courses|tour)/[a-z0-9-]+/?$' GROUP BY url" -> 5 URLs /tour/ viejas x 2; wp db query "SELECT COUNT(*) FROM wp_rank_math_internal_links WHERE post_id=5228" -> 0 frente a 17 en curl https://whatsincabo.com/map/.
baja Dos tours no están enlazados desde su hub: su único enlace interno es el carrusel del homeEl hub /tours-activities/ (5441, editado por última vez el 2026-08-19) enlaza 9 de los 11 tours; 5129 ATV Desert Adventure y 5419 Beach Hopper Snorkel (creadas el 13 y 14 de agosto) no aparecen. Fuera del home no existe ningún otro enlace en todo el sitio (post_content, postmeta, opciones, mu-plugins, tema hijo, uploads). No es riesgo de migración (0 enlaces que reescribir), pero es la señal más débil del inventario y dificulta verificar el punto de inlinks en esas dos.
Fichas: 5129, 5419
Qué cambiar antes: Nada para el canary (no van en él). Añadirlas al hub /tours-activities/ antes o después de su lote para que la URL nueva tenga al menos un enlace estable además del carrusel.
Evidencia
curl -s -A "Mozilla/5.0" https://whatsincabo.com/tours-activities/ | grep -ciE 'atv-desert-adventure|beach-hopper' -> 0. ssh whatscabo "wp db query \"SELECT ID FROM wp_posts WHERE post_type<>'revision' AND ID NOT IN (5129,5419) AND (post_content LIKE '%atv-desert-adventure-cabo-adventures%' OR post_content LIKE '%beach-hopper-snorkel-cabo-expeditions%')\"" -> 0 filas; misma búsqueda en wp_postmeta -> 0; grep -rlE en mu-plugins/tema hijo/uploads/wic -> vacío. curl -s https://whatsincabo.com/ | grep -oE 'href="[^"]*(atv-desert-adventure|beach-hopper)[^"]*"' -> 1 cada uno.
baja Ninguna de las 28 muestra una miga visible en el HTML: el BreadcrumbList es solo JSON-LDEn las 28 fichas y en el control 3232 toda aparición de «breadcrumb» está dentro de bloques ld+json; no hay nav/ol con aria-label breadcrumb. Existe una miga visible en wic-golf-listing-v3.php (línea 497) pero solo se renderiza con el runtime v3 activo, que no lo está en ninguna ficha. Google pide que el marcado de migas refleje una miga visible; hoy es un rich result sin respaldo visual. No es una contradicción de URL.
Fichas: Las 28 y el control 3232
Qué cambiar antes: Nada para la migración; saber que el punto «Breadcrumbs correctos» solo se puede verificar leyendo el JSON-LD.
Evidencia
Barrido de 29 HTML (curl -s -A "Mozilla/5.0"; tabla en scratchpad/bc-audit.tsv): jsonld_BreadcrumbList=1 en todas; aria-label breadcrumb, rank-math-breadcrumb, c27-breadcrumb = 0; tras quitar los <script type="application/ld+json"> con perl, grep -oi breadcrumb -> 0 en los 29. ssh whatscabo "grep -rniE 'class=\"[^\"]*breadcrumb' wp-content/themes/my-listing-child wp-content/mu-plugins" -> solo wic-golf-listing-v3.php:497; wp db query "SELECT COUNT(*) FROM wp_postmeta WHERE meta_key='_wic_golf_v3_active_release'" -> 0.
Lo que ya está bien
- Inventario exacto y coincidente en tres fuentes (crawl de hoy, permalinks.tsv, wp post list): 268 fichas publicadas = 240 en /listing/ + 17 golf en /golf-courses/ + 11 tours en /tour/. Las 28 responden 200 sin Location, con self-canonical tipado, og:url igual, robots «follow, index», y están en job_listing-sitemap1.xml con su URL tipada; ninguna URL neutral de ellas aparece en ningún sitemap ni en ningún JSON-LD.
- El mecanismo de migración está en producción y probado en una ficha de golf con módulo 3D: Costa Palmas 3232 vive en /listing/costa-palmas-golf-club/3232/ (200), /golf-courses/costa-palmas-golf-club/ hace 301 de un salto a ella, y canonical, og:url, @id #golf-course, url, item 3 de la miga, sitemap, hub /golf-courses/ y guía best-golf ya la enlazan por la neutral. Rollback por ficha = `wp post meta delete <ID> _wic_neutral_url` + purga (Gate 11.2 §1.6).
- La regla ^listing/([^/]+)/([0-9]+)/?$ está registrada y resuelve por ID; el guard de wic-neutral-urls.php (template_redirect prioridad 5) devuelve 301 de un salto en GET y HEAD, con slug equivocado y sin barra final; el segmento de tipo equivocado (/other/, /restaurant/, /golf-course/) también hace 301 directo a la canónica. El 301 de hoy lleva cuerpo vacío, x-redirect-by: WordPress y sin X-Robots-Tag.
- Base limpia en base de datos: ninguna de las 28 tiene _wic_neutral_url, rank_math_canonical_url ni rank_math_robots; 0 referencias a fichas tipadas en _elementor_data (solo el slug del tipo y el enlace al hub); menús (3236, 1150) apuntan a hubs; la única opción con URL tipada es el transient del calentador; wp_termmeta 0; .htaccess 0; 27 redirecciones Rank Math activas y ninguna toca /golf-courses/<slug>/, /tour/<slug>/ ni /listing/; 404 Monitor activo. Sin colisiones ni ambigüedad sobre quién emite el redirect (requisito Mapping del Gate 11.2).
- Los módulos de golf filtran por ID de post o por _case27_listing_type, nunca por ruta (lección Costa Palmas ya aplicada): wic-golf-seo-fix.php, wic-golf-hub-unify.php, wic-golf-listing-v3.php y los seis 3D renderizan igual bajo /listing/.
- El cinturón de canonical de wic-neutral-urls.php (rank_math/frontend/canonical, prioridad 99) gana al filtro de Cabo Real (prioridad 10) y og:url de Rank Math sigue al canonical; el sitemap no se cachea (no-cache, no-store) y deriva de get_permalink.
- Las 11 fichas de golf sin módulo (schema del tema, @id = [[:url]]) y las 11 de tours (Product/TouristTrip y BreadcrumbList con get_permalink en wic-fix-theme-schema.php líneas 380/416 y wic-breadcrumb-schema.php línea 116) seguirán la migración sin tocar código.
- Los enlaces dinámicos ya siguen al permalink: tarjetas del hub /golf-courses/ (wic-golf-hub.php the_permalink líneas 193/208/210), carrusel de tours del home (functions.php 4100), hub de tours para las 3 ya migradas. wic-cabo-adventures-schema.php ya resuelve con get_permalink(797) (líneas 51-58): es el patrón a copiar en los 6 módulos de golf. Tema padre e hijo sin ninguna ruta fija de golf/tour; ningún mu-plugin construye '/tour/' a mano.
- Las fichas no se enlazan entre sí con rutas a mano (0 hrefs tipados entre fichas hermanas) y los adaptadores JS de los 5 módulos 3D no comparan la ruta de la página (solo Cabo Real lo hace).
- Antecedentes completos y localizables para repetir el procedimiento: guía Fase 5 (líneas 860-1031), paquete GATE-11.2-EVIDENCIA-CANARY-LISTING-2026-07-25.md (que ya nombraba wic-cabo-real-v3.php como deuda de golf), procedimiento real de los 3 lotes en "/config/workspace/whats in cabo /.claude/docs/EXECUTION-LOG-CODEX.md" líneas 261-420, y backups de julio todavía en el servidor (~/backups/exec/lote1-20260726/, mapa-old-new.txt formato ID|vieja|nueva).
- La auditoría fue estrictamente de solo lectura: wp rewrite list, wp post get/list/meta get, wp db query SELECT, wp option get, grep/cat/sed -n/ls en el servidor y curl -A Mozilla/5.0 en la web. Sin update/delete/purge/escritura. Evidencia local en el scratchpad: audit-tipadas-http.tsv, sitemap-locs.tsv, sitemap-28-lastmod.tsv, audit-cache-28.tsv, sd-extract.json, bc-audit.tsv y los HTML descargados.
Procedimiento (alineado al Gate 11.2 de la guía)
- 0. Go/no-go de Iván para PREPARAR (no ejecutar): golf y /map/ están fuera de límites en la memoria del proyecto; registrar fecha, muestra exacta (las 5 fichas) y ventana.
- 1. Actualizar la guía canónica (§3.1 línea 368 y §9.1 línea 1199) a 28 fichas y congelar el mapa 1:1 viejo/nuevo con las 28 (formato ID|vieja|nueva como en ~/backups/exec/lote1-20260726/mapa-old-new.txt), anotando por ficha qué archivo hay que revertir en el rollback (bandera sola: 11 golf A + 11 tours; bandera + constante línea 32: 5 golf B; bandera + 3 líneas + release nuevo: Cabo Real). Cumple Gate 11.2 fila Mapping.
- 2. Gate 11.2 fila 404 y redirects: confirmar Rank Math 404 Monitor y Redirections activos (ya lo están), guardar baseline del 404 log, y eliminar la cadena de 2 saltos de wic-qs-preserving-redirects.php línea 25 (get_permalink(797)).
- 3. Gate 11.2 fila Inlinks: pre-flight de enlaces sobre TODAS las fuentes (§2.3: mu-plugins con subdirectorios, tema, post_content, _elementor_data, metas de schema, absoluta Y relativa) usando el crawl vivo, no el índice de Rank Math. Lista congelada para las 5 del canary: 5201 (guía best-golf), 5512 (hub golf, si alguna elegida está en su post_content), wic-golf-polish.php (17 llamadas), wic-map-page.html (rebuild con build-map-page.py, OK de Iván), 5441 (hub tours), llms.txt. Decidir qué hacer con 5199 (/golf/) y con los 5 hrefs viejos del home 357.
- 4. Gate 11.2 fila Observabilidad: guardar línea base antes/después de cada una de las 5 fichas (cabeceras de vieja y nueva, canonical, og:url, bloque JSON-LD completo, migas, sitemap, enlaces entrantes, 404 log). Decidir la miga de tours ANTES ('hub' => '/tours-activities/' en wic-breadcrumb-schema.php, OK de Iván).
- 5. Gate 11.2 fila GSC: comprobar acceso operativo y URL Inspection con la cuenta de servicio wic-operador (memoria: no pedir más códigos) y dejar preparado el seguimiento de cobertura.
- 6. Backup: mysqldump --no-tablespaces de la base + copia fechada de cada archivo que se vaya a tocar (post_content de 5201/5512/5441 exportados, wic-golf-polish.php, wic-map-page.html, llms.txt, wic-breadcrumb-schema.php, wic-qs-preserving-redirects.php) en ~/backups/exec/canary-golf-tours-<fecha>/. Probar el rollback en una ficha (poner y quitar la bandera + purga) antes del canary real.
- 7. Gate 11.2 fila Aprobación humana: segundo OK de Iván para EJECUTAR el canary con la muestra exacta y la ventana.
- 8. Ejecución del canary, ficha por ficha, en la misma ventana: reescribir los enlaces a mano que apuntan a esa ficha (derivando de get_permalink, abortar si un reemplazo no da exactamente 1), poner `_wic_neutral_url=1`, purgar la ficha por un método verificable Y la URL neutral, comprobar `x-litespeed-cache: miss` en ambas en la primera petición, regenerar llms.txt y el mapa al final del lote.
- 9. Verificación de los 11 puntos de la Fase 5.4 por ficha, con cache-buster y repetida a las 24 h: URL vieja 301 directo (cuerpo vacío, un salto, sin X-Robots-Tag); URL nueva 200; self-canonical nuevo; og:url nuevo; schema @id y url nuevos (comparar el bloque completo, no solo @id); migas correctas (JSON-LD, posición 2 = hub indexable); sitemap con la nueva y sin la vieja; enlaces internos apuntando a la nueva (hub, guía, mapa, llms.txt, home); 0 cadenas y 0 residuos absolutos o relativos; GSC URL Inspection sin errores nuevos; rollback probado.
- 10. Observación 2-4 semanas (GSC, 404 log, sitemap, señales) y segundo go/no-go de Iván (Fase 5.5). Si pasa: lotes de menor a mayor riesgo (Fase 5.6): (a) resto de golf sin módulo + resto de tours; (b) las 5 con módulo 3D tras editar su constante línea 32 al patrón Costa Palmas, empezando por Palmilla como referencia del canon y con su propia verificación de 11 puntos; (c) Cabo Real sola, cuando exista el release nuevo del explorador con la ruta /listing/ y se hayan editado las 3 líneas de wic-cabo-real-v3.php. Al cerrar: actualizar guía, tablero roadmap y reindexar Rank Math.
- 11. Decidir si los skills wic-activity-listing y wic-golf-listing ponen la bandera al crear fichas nuevas, para no volver a generar deuda.
Inventario de las 28
| id | ficha | tipo | URL viva | URL neutral hoy | sitemap | notas |
|---|
| 3191 | Puerto Los Cabos Golf Club | golf-courses | /golf-courses/puerto-los-cabos-golf-club/ · 200 | /listing/puerto-los-cabos-golf-club/3191/ · 301 → /golf-courses/puerto-los-cabos-golf-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3201 | Cabo Real Golf Club | golf-courses | /golf-courses/cabo-real-golf-club/ · 200 | /listing/cabo-real-golf-club/3201/ · 301 → /golf-courses/cabo-real-golf-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3212 | Solmar Golf Links | golf-courses | /golf-courses/solmar-golf-links/ · 200 | /listing/solmar-golf-links/3212/ · 301 → /golf-courses/solmar-golf-links/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3215 | El Cardonal by TGR Design | golf-courses | /golf-courses/el-cardonal-by-tgr-design/ · 200 | /listing/el-cardonal-by-tgr-design/3215/ · 301 → /golf-courses/el-cardonal-by-tgr-design/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3216 | Dunes Course at Diamante | golf-courses | /golf-courses/dunes-course-at-diamante/ · 200 | /listing/dunes-course-at-diamante/3216/ · 301 → /golf-courses/dunes-course-at-diamante/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3218 | Oasis Short Course at Diamante | golf-courses | /golf-courses/oasis-short-course-at-diamante/ · 200 | /listing/oasis-short-course-at-diamante/3218/ · 301 → /golf-courses/oasis-short-course-at-diamante/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3219 | Quivira Golf Club | golf-courses | /golf-courses/quivira-golf-club/ · 200 | /listing/quivira-golf-club/3219/ · 301 → /golf-courses/quivira-golf-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3220 | Cabo San Lucas Country Club | golf-courses | /golf-courses/cabo-san-lucas-country-club/ · 200 | /listing/cabo-san-lucas-country-club/3220/ · 301 → /golf-courses/cabo-san-lucas-country-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3221 | Cabo del Sol Course | golf-courses | /golf-courses/cabo-del-sol-course/ · 200 | /listing/cabo-del-sol-course/3221/ · 301 → /golf-courses/cabo-del-sol-course/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3223 | Cove Club Golf Course | golf-courses | /golf-courses/cove-club-golf-course/ · 200 | /listing/cove-club-golf-course/3223/ · 301 → /golf-courses/cove-club-golf-course/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3224 | Twin Dolphin Golf Club | golf-courses | /golf-courses/twin-dolphin-golf-club/ · 200 | /listing/twin-dolphin-golf-club/3224/ · 301 → /golf-courses/twin-dolphin-golf-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3225 | Chileno Bay Golf & Beach Club | golf-courses | /golf-courses/chileno-bay-golf-and-beach-club/ · 200 | /listing/chileno-bay-golf-and-beach-club/3225/ · 301 → /golf-courses/chileno-bay-golf-and-beach-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3226 | El Dorado Golf & Beach Club | golf-courses | /golf-courses/el-dorado-golf-and-beach-club/ · 200 | /listing/el-dorado-golf-and-beach-club/3226/ · 301 → /golf-courses/el-dorado-golf-and-beach-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3227 | Palmilla Golf Club | golf-courses | /golf-courses/palmilla-golf-club/ · 200 | /listing/palmilla-golf-club/3227/ · 301 → /golf-courses/palmilla-golf-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada. Es la ficha de referencia del canon WIC Golf |
| 3228 | Querencia Golf & Beach Club | golf-courses | /golf-courses/querencia-golf-and-beach-club/ · 200 | /listing/querencia-golf-and-beach-club/3228/ · 301 → /golf-courses/querencia-golf-and-beach-club/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3230 | Club Campestre San Jose | golf-courses | /golf-courses/club-campestre-san-jose/ · 200 | /listing/club-campestre-san-jose/3230/ · 301 → /golf-courses/club-campestre-san-jose/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 3231 | Vidanta Golf Los Cabos | golf-courses | /golf-courses/vidanta-golf-los-cabos/ · 200 | /listing/vidanta-golf-los-cabos/3231/ · 301 → /golf-courses/vidanta-golf-los-cabos/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5121 | Luxury Two-Bay Snorkel by Cabo Adventures | tours-activities | /tour/luxury-two-bay-snorkel-cabo-adventures/ · 200 | /listing/luxury-two-bay-snorkel-cabo-adventures/5121/ · 301 → /tour/luxury-two-bay-snorkel-cabo-adventures/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5129 | Cabo ATV Tour & Desert Adventure by Cabo Adventures | tours-activities | /tour/atv-desert-adventure-cabo-adventures/ · 200 | /listing/atv-desert-adventure-cabo-adventures/5129/ · 301 → /tour/atv-desert-adventure-cabo-adventures/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5135 | Luxury Cabo Sailing by Cabo Adventures | tours-activities | /tour/luxury-day-sailing-cabo-adventures/ · 200 | /listing/luxury-day-sailing-cabo-adventures/5135/ · 301 → /tour/luxury-day-sailing-cabo-adventures/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5352 | Kayak & Snorkel at the Arch | tours-activities | /tour/kayak-snorkel-arch-high-tide/ · 200 | /listing/kayak-snorkel-arch-high-tide/5352/ · 301 → /tour/kayak-snorkel-arch-high-tide/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5366 | Horseback Riding Between Desert and Sea | tours-activities | /tour/horseback-riding-desert-sea-cactus-tours/ · 200 | /listing/horseback-riding-desert-sea-cactus-tours/5366/ · 301 → /tour/horseback-riding-desert-sea-cactus-tours/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5377 | 23 ft Panga Anamar Fishing Charter | tours-activities | /tour/panga-anamar-fishing-charter-cabo/ · 200 | /listing/panga-anamar-fishing-charter-cabo/5377/ · 301 → /tour/panga-anamar-fishing-charter-cabo/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5386 | Costa Azul Surf Lesson | tours-activities | /tour/costa-azul-surf-lesson-high-tide/ · 200 | /listing/costa-azul-surf-lesson-high-tide/5386/ · 301 → /tour/costa-azul-surf-lesson-high-tide/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5393 | Discover Scuba Diving with Dive Cabo | tours-activities | /tour/discover-scuba-diving-dive-cabo/ · 200 | /listing/discover-scuba-diving-dive-cabo/5393/ · 301 → /tour/discover-scuba-diving-dive-cabo/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5409 | UTV Canyon & Hanging Bridge Tour | tours-activities | /tour/wild-canyon-utv-hanging-bridge-tour/ · 200 | /listing/wild-canyon-utv-hanging-bridge-tour/5409/ · 301 → /tour/wild-canyon-utv-hanging-bridge-tour/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5419 | Beach Hopper Snorkel Tour | tours-activities | /tour/beach-hopper-snorkel-cabo-expeditions/ · 200 | /listing/beach-hopper-snorkel-cabo-expeditions/5419/ · 301 → /tour/beach-hopper-snorkel-cabo-expeditions/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
| 5429 | Small-Group Snorkeling with Cabo Trek | tours-activities | /tour/small-group-snorkeling-cabo-trek/ · 200 | /listing/small-group-snorkeling-cabo-trek/5429/ · 301 → /tour/small-group-snorkeling-cabo-trek/ | sí (job_listing-sitemap1.xml, URL tipada); la neutral NO está | Self-canonical tipado; flag apagada |
INVENTARIO (solo lectura, 2026-09-05). Hay exactamente 28 fichas publicadas de job_listing viviendo en URL tipada: 17 de tipo golf-courses en /golf-courses/{slug}/ (IDs 3191, 3201, 3212, 3215, 3216, 3218, 3219, 3220, 3221, 3223, 3224, 3225, 3226, 3227, 3228, 3230, 3231) y 11 de tipo tours-activities en /tour/{slug}/ (IDs 5121, 5129, 5135, 5352, 5366, 5377, 5386, 5393, 5409, 5419, 5429). Coinciden las tres fuentes: el crawl de hoy (crawl-hoy-detail.json, tras excluir los hubs /golf-courses/ y /tours-activities/), permalinks.tsv (268 líneas = 240 /listing/ + 17 golf-courses + 11 tour) y wp post list en el servidor (268 publicadas). Las cifras esperadas (~17 golf + ~11 tours) se cumplen sin desviación.
ESTADO UNIFORME DE LAS 28 (idéntico en todas, medido ficha por ficha): (a) URL tipada responde 200 sin Location; (b) canonical del HTML vivo = la misma URL tipada (self-canonical tipado); (c) la URL neutral /listing/{post_name}/{id}/ existe y responde 301 DE VUELTA a la tipada (un salto, sin cadena) — es el guard anti-leak del mu-plugin wp-content/mu-plugins/wic-neutral-urls.php (líneas 51-71: "sin flag: /listing/x/{id}/ -> /tipada/"), o sea que hoy la neutral es origen de redirect, no destino; (d) ninguna de las 28 tiene el meta _wic_neutral_url (la consulta SQL devolvió solo _case27_listing_type; el meta existe en 240 posts, justamente las migradas); (e) las 28 URLs tipadas están en job_listing-sitemap1.xml de Rank Math y ninguna URL neutral de ellas aparece en ningún sitemap. Mecanismo confirmado: la rewrite rule ^listing/([^/]+)/([0-9]+)/?$ -> index.php?post_type=job_listing&p=$matches[2] está registrada (wp rewrite list), y la bandera por ficha es _wic_neutral_url=1; con la bandera apagada no cambia nada, rollback = quitar la bandera (cabecera del plugin, línea 4).
DISCREPANCIA SECUNDARIA (fuera de las tipadas, explicada): el sitemap trae 238 /listing/ + 28 tipadas = 266 fichas frente a 268 publicadas; las 2 ausentes son eventos ya migrados a /listing/ (3708 festival-of-flavors-roberto-alcocer-2026 y 3784 san-jose-del-cabo-art-walk-2026) con rank_math_robots=noindex y meta robots "follow, noindex" en el HTML: su exclusión del sitemap es correcta. El crawl de hoy cuenta 238 /listing/ por la misma razón. El hub /golf-courses/ figura en page-sitemap.xml (es página, no ficha) y /tours-activities/ no aparece en ningún sitemap.
LECTURA FRENTE A LA GUÍA CANÓNICA (PLAN-SEO-FINAL-WIC-2026-06-30.md §3.1 líneas 358-396 y §9.1 línea 1199): la guía dice que el "único namespace tipado que queda" es golf (17); hoy hay un SEGUNDO namespace tipado, /tour/, con 11 fichas de tours-activities creadas después del 2026-07-26 (IDs 5121-5429) sin la bandera neutral, lo que contradice la "regla vigente" de §3.1 ("cualquier ficha que se migre después de esta fecha pasa por el mismo procedimiento") y la §9.1 que solo contempla golf. Implicación: si se decide migrar, el procedimiento de Fase 5 (líneas 860-1031) aplica a 28 fichas, no a 17, y el gate 11.2 (948-962) exige antes: pre-flight de enlaces internos en mu-plugins (hay 10+ mu-plugins wic-golf-*, wic-cabo-adventures-schema.php y wic-golf-seo-fix.php que pueden hardcodear /golf-courses/ o /tour/), post_content, _elementor_data y metas de schema en forma absoluta y relativa; backup; mapa 1:1 congelado (ya listo: este inventario); canary de 5; verificación de 11 puntos; 2-4 semanas y segundo go/no-go de Iván. Esta auditoría NO ejecutó nada de eso ni tocó el servidor: es solo el inventario y la línea base. Fichero de evidencia local: "/tmp/claude-1000/-config-workspace-whats-in-cabo-/caf607f6-a362-424a-88eb-09fab3f5d9dd/scratchpad/audit-tipadas-http.tsv" (28 filas: id, url viva, http, location, url neutral, http neutral, location, canonical) y "/tmp/claude-1000/-config-workspace-whats-in-cabo-/caf607f6-a362-424a-88eb-09fab3f5d9dd/scratchpad/sitemap-locs.tsv" (300 <loc> de los 4 sitemaps hijos). Nota: golf y /map/ están marcados "fuera de límites" en la memoria del proyecto (SEO en pausa 2026-07-26); cualquier acción sobre estas 28 requiere OK explícito.
Inspecciones y refutación
- rewrite-y-neutral: 11 confirmados, 1 refutados · bien: Resolución de las tipadas explicada y verificada: la regla de MyListing `(other|yacht-rental|photographers|golf-courses|hotels|beach|tour|tour-operator|transportation|restaurant|event)/([^/]+)/?$ -> index.php?job_listing=$matches[2]` sirve /golf-courses/{slug}/ y /tour/{slug}/ con 200 y self-canonical tipado (comando: wp rewrite list --format=csv | grep -E 'golf|tour|listing'). No hay regla `^tour/` suelta; los alias singulares (^golf-course/, ^hotel/, ^beach/, ^yacht-rental/, ^photographer/) también existen y 301-ean a la canónica. · La URL neutral /listing/{slug}/{id}/ de las 28 responde 301 de UN salto a la tipada, en GET y HEAD, con slug equivocado (/listing/wrong-slug/3227/) y sin barra final (/listing/palmilla-golf-club/3227): ni 200 duplicado ni 404. Lo hace el guard de wic-neutral-urls.php (template_redirect prioridad 5, líneas 58-74). La regla `^listing/([^/]+)/([0-9]+)/?$` está registrada en `top` y resuelve por ID (línea 18-22). · El segmento de tipo equivocado (/other/palmilla-golf-club/, /restaurant/palmilla-golf-club/, /golf-course/palmilla-golf-club/, /other/luxury-two-bay-snorkel-cabo-adventures/) 301-ea directo a la canónica: doble candado wic-listing-type-canonical-redirect.php (prioridad 1) + guard de wic-neutral-urls (prioridad 5). Sin cadenas. · El mecanismo de migración es simétrico y ya está probado en una ficha de golf con módulo 3D: Costa Palmas 3232 vive en /listing/costa-palmas-golf-club/3232/ (200, hit), /golf-courses/costa-palmas-golf-club/ 301-ea a ella, y canonical, og:url, @id #golf-course, url, item 3 del breadcrumb y job_listing-sitemap1.xml están todos en la neutral. Es el precedente exacto para las otras 17 de golf. Rollback por ficha = quitar `_wic_neutral_url` (cabecera del plugin, línea 4). · Las 28 fichas no tienen `rank_math_canonical_url` ni `rank_math_robots` en postmeta: canonical, og:url y sitemap siguen a get_permalink y se actualizarían solos al poner la bandera (y el 'cinturón' del plugin, filtro rank_math/frontend/canonical prioridad 99, cubre el caso de que apareciera uno viejo). · Los hubs enlazan a las fichas por get_permalink donde importa: wic-golf-hub.php usa the_permalink() (líneas 193, 208, 210) y en el hub vivo Costa Palmas ya sale como /listing/.../3232/; el hub de tours muestra /listing/ para las 3 migradas (2216, 2225, 2234); las tarjetas de tours del home vivo también (2234 sale como /listing/). El menú global (3236, 1150) apunta a los hubs, no a fichas. · wic-golf-hub-unify.php y wic-golf-seo-fix.php identifican las fichas de golf por `_case27_listing_type`, no por URL (el propio seo-fix documenta que empezó por ruta y se le escapaba Costa Palmas): seguirían funcionando en /listing/. El 301 de /golf/ -> /golf-courses/ es de un salto y su sitemap lo excluye. · No hay reglas de reescritura ni redirect para /golf-courses/, /tour/ ni /listing/ en .htaccess (solo cabeceras del preview privado de golf, líneas 136-144) ni en Rank Math Redirections (27 activas; ninguna toca esas rutas; cabecera 301; 404 monitor en modo simple). No habría cadenas ni conflictos de origen del redirect (requisito 'Mapping' del gate 11.2). · El tema hijo (functions.php de 346 KB y su .bak) y el tema padre no contienen ninguna ruta fija /golf-courses/, /tour/ ni /golf/ (grep recursivo con conteo por archivo = 0). Las rutas fijas viven todas en mu-plugins y en 4 post_content. · Ningún mu-plugin ni asset contiene la cadena '/tour/' como ruta interna (grep recursivo en mu-plugins, php/js/html/css/json = 0 hits); las 11 de tours dependen solo del post_content de 5441 y del breadcrumb. · wic-cabo-adventures-schema.php ya contempla la migración: compara la URL vieja fija con get_permalink(797) (líneas 55-58, comentario 'La URL canonica de la ficha 797 cambia con la migracion'). Es el patrón a copiar en los 6 módulos de golf. · Auditoría estrictamente de solo lectura: solo wp rewrite list, wp post get/list/meta, wp db query SELECT, wp option get, grep/cat/sed -n/ls en el servidor y curl -A Mozilla/5.0 en la web. Sin update/delete/purge/escritura.
- schema-migas-og: 7 confirmados, 1 refutados · bien: 28/28 fichas: <link rel=canonical>, og:url, schema url, schema @id (sin contar el fragmento #golf-course) y el último ListItem del BreadcrumbList son exactamente la URL viva; cero contradicciones entre ellos en el HTML de hoy (sd-extract.json). · 28/28: meta robots «follow, index, max-snippet:-1…»; todos los bloques JSON-LD parsean (0 inválidos); ninguna URL neutral /listing/{slug}/{id}/ aparece en ningún JSON-LD ni og:url de las 28. · 17/17 golf emiten @type GolfCourse; posición 2 de la miga = https://whatsincabo.com/golf-courses/ (hub 200, index, self-canonical) en las 17; ningún JSON-LD de golf referencia URLs del dominio distintas de home, el hub y la propia ficha. · Control Costa Palmas (3232, ya migrado): canonical = og:url = GolfCourse @id (#golf-course) = url = miga posición 3 = https://whatsincabo.com/listing/costa-palmas-golf-club/3232/; posición 2 = /golf-courses/; la vieja /golf-courses/costa-palmas-golf-club/ responde 301 directo a la nueva; el hub /golf-courses/ ya la enlaza por /listing/. Es la prueba de que el patrón funciona cuando la constante del *-3d.php se actualiza a mano. · 11/11 tours: @type ['Product','TouristTrip'] con @id y url = permalink, generados con get_permalink() en wic-fix-theme-schema.php (líneas 380 y 416), y BreadcrumbList con get_permalink() (wic-breadcrumb-schema.php línea 116): seguirán la migración sin tocar código. · 11 golf con schema del tema (3212-3226): el @id sale de [[:url]] (permalink) del tipo golf-courses (ID 3190) → también seguiría la migración automáticamente. · El hub /golf-courses/ construye sus enlaces con el permalink real (18 fichas, Costa Palmas ya en /listing/), así que no hace falta editarlo para migrar golf; /tours-activities/ (200, index, self-canonical) existe y enlaza a las fichas, aunque con href relativo escrito en post_content. · No se detectaron cadenas: tipada 200 sin Location, neutral 301 de un salto (del inventario), vieja Costa Palmas 301 directo.
- enlaces-internos: 7 confirmados, 0 refutados · bien: Las tarjetas del hub /golf-courses/ usan the_permalink() (wic-golf-hub.php:193, 208, 210): 3 de los 7-9 enlaces de cada campo se corrigen solos al migrar. Evidencia: grep -nE 'href' wic-golf-hub.php. · El carrusel de tours del home usa get_permalink() (my-listing-child/functions.php:4100 y 6122, clase wic-listing-card-main): el único inlink dinámico de los 11 tours sigue solo a la migración. Ningún mu-plugin construye '/tour/' a mano (grep -rnE "'/tour/'|\"/tour/\"" *.php → vacío). · Canonical, og:url y BreadcrumbList de las 11 fichas de golf que no son 3D y de los 11 tours salen de Rank Math/tema (get_permalink): en el HTML vivo no emiten @id tipado fijo y las migas de los tours son Home → /explore/ → taxonomía → ficha, todas dinámicas. · Menús: los 3 menús (94 footer, 95 principal, 96 woocommerce) solo enlazan hubs (/tours-activities/ ítem 1150 y /golf-courses/ ítem 3236); ningún ítem apunta a una ficha tipada. Evidencia: wp menu item list <id> --fields=db_id,type,title,link. · wp_options: la única opción con /golf-courses/ o /tour/ es _transient_wic_warmer_urls (lista del calentador de caché, se regenera cada 10 min). wp_termmeta: 0 filas. .htaccess: 0. Tema padre my-listing: 0. Plugins propios (wic-inquiry-forms-form-builder): 0. · wp_postmeta: ninguna meta contiene una URL tipada interna de las 28 fichas. Las 17 coincidencias son URLs EXTERNAS (_job_website/_reservation-link hacia cabo-adventures.com/en/tour/… y hightideloscabos.com/tour/…, _wic_yacht_source_urls/_yacht_options de 4438) más el canonical del hub en 5512 y la URL del hub en el ítem de menú 3236. Comprobado también en forma escapada (\/golf-courses\/): solo _elementor_data de 357 y 4925, y ambas enlazan únicamente el hub /golf-courses/. · Archivos estáticos: no hay ningún .html en wp-content/uploads con /golf-courses/<slug>/ ni /tour/<slug>/ (grep -rl … --include=*.html → vacío); el único .html con fichas tipadas en todo wp-content es mu-plugins/wic-map-page.html (17 hrefs de golf en el noscript, 0 de tours), regenerable con build-map-page.py. · Redirecciones: wp_rank_math_redirections tiene 27 reglas activas y ninguna toca fichas /golf-courses/ o /tour/ (solo taxonomías tag/golf-best-for → golf-access-type). El único emisor del 301 neutral→tipada es el mu-plugin wic-neutral-urls.php, así que el rollback por bandera es unívoco (requisito 'Mapping' del Gate 11.2). · Las 28 URLs neutrales /listing/<slug>/<id>/ responden 301 de un salto a la tipada y ninguna página viva enlaza hoy una URL /tour/ vieja de las migradas en julio (residuos solo en post_content no servido e índice de Rank Math). El 404 log de Rank Math solo registra /listing/palmilla-golf-club/3227/feed (1 vez, 2026-09-04) y listing/playa-punta-lobos/1 (1 vez). · Las fichas de golf y de tours no se enlazan entre sí (0 hrefs tipados entre fichas hermanas en las 28 páginas descargadas): no hay 'related listings' hardcodeados que reescribir dentro de las propias fichas. · Los adaptadores JS de las 5 fichas 3D (wic-golf-geometry-adapter.js) solo usan pathname para normalizar sourceUrl (quitar utm_ y barra final), no comparan con la URL de la página: a diferencia de Cabo Real, no hay gating por ruta en el JS de Palmilla, Querencia, Puerto Los Cabos, Vidanta ni Campestre.
- sitemap-gsc-historial: 5 confirmados, 3 refutados · bien: Sitemap: las 28 están en job_listing-sitemap1.xml con su URL tipada exacta (17 /golf-courses/<slug>/ y 11 /tour/<slug>/), sin entradas image:image; ninguna URL /listing/{slug}/{id}/ de las 28 aparece en los 4 sitemaps hijos; lastmod por URL guardado en scratchpad/sitemap-28-lastmod.tsv (golf 2026-07-12, Cabo Real 2026-07-21, tours 2026-08-13/14). Los hubs /golf-courses/ y /tours-activities/ están en page-sitemap.xml (esto corrige el inventario, que decía que /tours-activities/ no estaba en ningún sitemap). · El sitemap no se cachea: job_listing-sitemap1.xml responde `cache-control: no-cache, no-store` y `x-litespeed-cache-control: no-cache`; Rank Math deriva de get_permalink (`pt_job_listing_sitemap: on`), y la migrada Costa Palmas 3232 sale como /listing/costa-palmas-golf-club/3232/ en el sitemap, en el hub /golf-courses/, en la guía best-golf y en explore — prueba de que las fuentes dinámicas se autocorrigen. · Mecanismo vigente y probado: /listing/{slug}/{id}/ → 301 de un salto a la tipada con `x-redirect-by: WordPress` (guard de wic-neutral-urls.php, prioridad 5) en golf y tours; en la migrada, /golf-courses/costa-palmas-golf-club/ → 301 un salto → /listing/…/3232/ 200 con canonical, og:url, @id y url nuevos. Rewrite `^listing/([^/]+)/([0-9]+)/?$` registrada (wp rewrite list). Rollback por ficha = `wp post meta delete <ID> _wic_neutral_url` + purge (Gate 11.2 §1.6, log raíz). · Los módulos de golf gatean por `get_queried_object_id()` (los 3D y Cabo Real) o por `_case27_listing_type` (wic-golf-seo-fix.php líneas 33-44, wic-golf-listing-v3.php línea 751), no por ruta: la lección de Costa Palmas ya está aplicada y el módulo 3D renderiza igual bajo /listing/ (verificado en 3232). · El cinturón de canonical de wic-neutral-urls.php (`rank_math/frontend/canonical`, prioridad 99) gana al filtro de Cabo Real (prioridad 10), y og:url de Rank Math sigue al canonical (Costa Palmas). · Base limpia en DB: ninguna de las 28 tiene `_wic_neutral_url` ni `rank_math_canonical_url`; 0 referencias tipadas en `_elementor_data`; 0 ítems de menú apuntando a fichas (3236 apunta al hub); la única `rank_math_canonical_url` tipada es la del hub 5512 hacia sí mismo; 27 redirecciones Rank Math activas y ninguna toca /golf-courses/<slug>/, /tour/<slug>/ ni /listing/; 404 Monitor activo. · wic-cabo-adventures-schema.php ya resuelve la URL con get_permalink(797) (líneas 51-55): la deuda 3 del Lote 1 quedó pagada. El child theme (inc/wic-editorial-widgets.php) no tiene hrefs a fichas de golf/tour. · Home vivo: enlaza las 11 tours vigentes por URL absoluta generada por widgets (get_permalink); en julio se comprobó que el home se autocorrige sin tocar post_content ni _elementor_data (regla del socio). No hay hrefs a fichas de golf en el home. · Antecedentes completos y localizables: guía Fase 5 (líneas 860-1031: 11 puntos de la tabla §5.4, orden de lotes §5.6, plantilla Gate 11.2 con 7 requisitos), paquete GATE-11.2-EVIDENCIA-CANARY-LISTING-2026-07-25.md (muestra de 5 por vertical, 8/9 checks, deuda técnica §4 que ya nombraba wic-cabo-real-v3.php como pendiente de golf), y el procedimiento real de los 3 lotes en "/config/workspace/whats in cabo /.claude/docs/EXECUTION-LOG-CODEX.md" líneas 261-420: backup DB `mysqldump` → mapa viejo/nuevo congelado en ~/backups/exec/lote{1,2,3}-20260726/mapa-old-new.txt (formato `ID|vieja|nueva`) → flag por ficha → sustitución de hrefs derivando de get_permalink con aborto si un reemplazo no da exactamente 1 → verificación por ficha (vieja 301 un salto, nueva 200, og:url, canonical self omitido en noindex, 0 rastros absolutos Y relativos, sitemap con nueva/sin vieja, regresión de lotes previos) → rollback por ficha. Los backups siguen en el servidor (~/backups/exec/lote1-20260726/, pre-listing-migration-20260726-024140.sql.gz). · Callbacks de todos los buffers de golf devuelven el HTML intacto si llega vacío (palmilla, cabo-real, seo-fix, golf-listing-v3), así que hoy el 301 del guard sale limpio aunque los buffers ya estén abiertos.
Plan: fases · Tablero: roadmap. Golf y /map/ siguen fuera de límites hasta tu OK explícito.