`page` y `query` se descargaban por separado, así que no había forma de saber
de qué búsquedas salían las impresiones de una URL. Con eso faltando, el dato
más llamativo del marcador (uss-nimitz: 1.254 impresiones en posición 6,7 y
CERO clics) admitía tres explicaciones incompatibles y no se podía elegir.
Ya se puede. El 91% de las impresiones del EN son ANÓNIMAS: consultas tan raras
que Google no las nombra. En el Nimitz, de 1.254 impresiones solo 29 tienen
consulta conocida. No es un problema de titular — reescribirlo no tocaría el
99% del volumen. En el ES el anónimo es el 66%, y el ES convierte 3x mejor.
Decisiones:
- tabla filas_pq aparte, no un `dim` más de `filas`: son DOS dimensiones y
con la clave concatenada cualquier agregado que olvidase filtrar por dim
contaría el mismo clic dos veces
- backfill troceado en ventanas de 90 días: con tres dimensiones las filas
se multiplican y un tirón de 16 meses puede pasarse del tope de paginación
- el informe enseña el HUECO lo primero, no lo esconde. Una página cuyo
volumen es casi todo anónimo no se arregla por el titular, y el informe lo
dice en voz alta al pasar del 70%
Verificado: filas_pq suma exactamente lo mismo que dim='query' en la misma
ventana (EN 405 imp, ES 144), que es la comprobación de que no se cuelan ni se
pierden filas por el camino.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Toda la caja miraba los enlaces en un solo sentido — a quién enlazo yo — y
esa es la pregunta del lector, no la de Google. Dos posts de EN publicados
el 21-jul seguían siendo «URL is unknown to Google» una semana después:
tenían enlaces entrantes, pero solo desde artículos con 8 y 12 impresiones
de por vida, páginas que Google casi no visita.
seo_semantic gana dos subcomandos: `padrinos <slug>`, que ordena candidatos
por tráfico REAL del origen (Search Console) en vez de por similitud, y
`descubrimiento`, que lista los publicados a los que no llega ni un enlace
desde el tercio fuerte del sitio. Salen 21 de 32 en EN y 21 de 31 en ES.
Ambos descartan los posts con canonical_url propio: Ghost los excluye del
sitemap por consolidados, así que apadrinar desde ahí no sirve de nada — el
de Grusch de mayo tiene 1.728 impresiones y habría encabezado la lista.
seo_gsc gana `inspecciona`, que pregunta el veredicto a Google en vez de
deducirlo. Es lo que separa «posiciona mal» de «no lo ha visto nunca», que
son problemas opuestos y se arreglan al revés.
Y el checklist del remate incorpora el paso 3b: el informe tiene que traer
el comando de enlace entrante listo para copiar. No lo aplica el agente
porque eso es editar otro post publicado, y eso sigue prohibido.
⚠️ Anotado en el docstring: contar enlaces internos desde el HTML público da
«cero huérfanos», porque el tema mete navegación y feed de relacionados que
parecen enlaces del cuerpo. La verdad está en el html del CLI de Ghost.
Recalibrado contra los datos reales del primer día, que desmintieron dos
supuestos del diseño.
El umbral de 150 impresiones dejaba fuera TODO: el mejor caso de los dos
blogs eran las 125 de Varginha en EN. El vigilante se habría callado para
siempre, la peor avería en algo que solo habla cuando hay noticias. Baja a
40, y entre 40 y 150 las líneas salen marcadas con «~» porque ahí el CTR es
orientativo, no firme.
Y el CTR no es la palanca a este volumen: con ~180 impresiones semanales en
EN, convertir el 10% de todas serían 78 clics al mes. El informe pasa a
liderar con cobertura — impresiones, páginas que reciben alguna, consultas
distintas, posición ponderada, y qué páginas empiezan o dejan de aparecer —
y avisa también cuando eso se mueve (±25% de impresiones o ±3 páginas), no
solo cuando hay CTR desperdiciado.
De propina, un fallo que solo se vio ejecutando el servicio dos veces
seguidas: avisaba las dos. Las listas nuevas ordenan un set de URLs por
impresiones y los empates los desempataba el orden de iteración del
conjunto, que Python aleatoriza por proceso; la huella cambiaba sin que
cambiara un solo dato. Todas las ordenaciones llevan ya desempate por
clave. Verificado: mismo texto con PYTHONHASHSEED 1, 2 y 3, y la segunda
ejecución del servicio se calla.
Estreno con credencial. Dos cosas que solo se ven con la API delante:
La propiedad ES no es de dominio sino de prefijo de URL
(https://zonadeexclusion.com/), así que sc-domain:zonadeexclusion.com no
existía y el canario lo cazó en el paso 4. Queda anotado que ese tipo de
propiedad no ve www ni http.
Y el histórico no es de 16 meses: las dos propiedades se dieron de alta en
mayo de 2026 y el dato más antiguo es del 2026-05-09. El manual prometía
comparar contra el año pasado y eso no va a existir nunca.
Toda la caja de herramientas miraba hacia dentro (enlaces, sitemap, reglas de
metas): decía si el sitio está bien construido, no si alguien lo encuentra. Los
únicos datos de resultado en la máquina eran una exportación manual del
2026-06-17, anterior al remate de metas del 23 de junio — o sea, cuarenta días
optimizando sin saber el resultado.
Cuenta de servicio en vez de OAuth, porque aquí no hay navegador (misma piedra
que con Gemini). Sin librerías de Google: el JWT se firma con PyJWT y se canjea
a mano, treinta líneas en lugar de media docena de dependencias.
Lo que de verdad aporta es `informe`: la lista de páginas que POSICIONAN Y NO
SE PINCHAN, con los clics que faltan si el CTR fuese el normal de su posición.
Y `antes-despues`, escrito para responder de una vez si el remate del 23-jun
sirvió: la API guarda 16 meses, así que el histórico está disponible desde el
primer minuto y no hay que esperar semanas para comparar.
Probado lo que se puede probar sin credencial: firma RS256 emitida y verificada
con su pública, los dos caminos de fallo de la credencial (ausente y con
permisos flojos), y la lógica de análisis contra datos sintéticos calcados del
caso Nimitz real (pos 6,44 / 2.452 impresiones / 0,16% CTR), que sale marcada
con +111 clics de hueco mientras la página sana no se marca.
El timer queda instalado y PARADO: sin credencial fallaría cada lunes, y un
timer que falla siempre enseña a ignorar los avisos.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>