seo-build-report

star 0

Use AFTER the 8 Phase 8 section skills (/seo-section-foda, /seo-section-tech-audit, /seo-section-history, /seo-section-ai-audit, /seo-section-competitive, /seo-section-benchmarks, /seo-section-context, /seo-section-action-plan) have written their JSON slices to ~/Documents/punto-rojo-clients/{slug}/sections/. Optionally consumes content-strategy.json (Phase 9 — alimenta sec-16 §11.3 Matriz Cluster × Etapa × Disciplinas, v3 redesign 2026-05-12; deprecó sec-20 standalone). This is a THIN ORCHESTRATOR that composes the slices into the assembled JSON consumed by report-builder/generate-report.js. It does NOT measure data — it reads slices from disk, deep-merges them, and renders the single-file HTML deliverable.

Nico-puntorojo By Nico-puntorojo schedule Updated 6/12/2026

name: seo-build-report description: Use AFTER the 8 Phase 8 section skills (/seo-section-foda, /seo-section-tech-audit, /seo-section-history, /seo-section-ai-audit, /seo-section-competitive, /seo-section-benchmarks, /seo-section-context, /seo-section-action-plan) have written their JSON slices to ~/Documents/punto-rojo-clients/{slug}/sections/. Optionally consumes content-strategy.json (Phase 9 — alimenta sec-16 §11.3 Matriz Cluster × Etapa × Disciplinas, v3 redesign 2026-05-12; deprecó sec-20 standalone). This is a THIN ORCHESTRATOR that composes the slices into the assembled JSON consumed by report-builder/generate-report.js. It does NOT measure data — it reads slices from disk, deep-merges them, and renders the single-file HTML deliverable.

MODO: EJECUCION DIRECTA — NO PLANIFICACION

Este skill es un ORQUESTADOR THIN con 7 fases secuenciales (0..6). NO hace measurements: lee 7 slices de disco, los compone, llama 1 CLI.

PROHIBIDO:

  • Llamar Ahrefs / GSC / GA4 / DataForSEO / diagnostic-mcp directamente. Toda la data ya vive en los slices ~/Documents/punto-rojo-clients/{slug}/sections/*.json. Si un slice falta o esta stale, STOP / WARN — NO regenerar la data inline.
  • Inventar data para campos que ningun slice cubre (ej. access_requirements, onboarding_checklist). Usar { available: false } o array vacio para que el template degrade graceful.
  • Pushear a git sin confirmacion EXPLICITA del Lead (Fase 5).
  • Persistir o copiar archivos al pr-toolkit con working tree dirty — abortar y pedir commit/stash primero.
  • Saltar fases o colapsar multiples fases en una sola respuesta.

OBLIGATORIO:

  • Operador = userEmail prefix del environment (ej: nico.m@puntorojo.com → "Nico"). NUNCA usar cliente-CLAUDE.md.Lead como nombre del operador. Ver shared/skill-execution-protocol.md §Regla 6.
  • Validar slug + period al inicio (Fase 0) — si falla, STOP.
  • Respetar checkpoints del Lead en Fase 3 (pre-render), el gate pre-upload review (entre Fase 4 y Fase 5) y Fase 5 (pre-push).
  • Citar la fuente de cada slice (sections/foda.json, sections/context.json, etc.).
  • Si action-plan slice no existe → STOP con "Run /seo-section-action-plan first." (skill 8/8 shipped v3.35.0 — slice REQUIRED igual que las otras 7). ⚠️ Fix F-07 (review 2026-06): el flag --no-action-plan que se documentaba acá NO existe en ningún código — compose-assembled.cjs trata action-plan como REQUIRED (exit 1 si falta). Si falta el slice, correr /seo-section-action-plan primero.

Protocolo: shared/skill-execution-protocol.md Context engineering: shared/context-engineering.md (Patron 5 process-and-discard)

/seo-build-report — Orquestador thin de slices Phase 8

Objetivo

Componer los 7-8 JSON slices Phase 8 en un assembled JSON canonico (shape de samples/chedraui-sample.json) y disparar generate-report.js para producir el HTML profesional single-file.

Thin orchestrator: NO mide nada. NO llama APIs externas. NO consume consolidated_json del backend. Las skills /seo-section-* ya hicieron el trabajo pesado y dejaron sus slices en disco.

Modelo recomendado: Sonnet (orquestacion + merge + 1 subprocess call — ningun razonamiento pesado).

Entregables:

  1. HTML local en report-builder/output/{slug}-diagnostico-{period}.html — share-able por email/drive a clientes externos.
  2. (Opcional) URL hosted https://pr-toolkit.puntorojo.com/reports/{slug}/{period} — auth-gated @puntorojo.com, para equipo interno.

Diferencia vs /seo-reporte: /seo-reporte hace consultas ad-hoc de performance y resuelve findings pendientes. Este skill empaqueta los slices ya producidos en un entregable visual profesional. Son complementarios.


Inputs

Parametro Tipo Default Descripcion
client_slug string (required) kebab-case del cliente (ej: chedraui). Regex /^[a-z0-9][a-z0-9-]*$/
period string (required) current month YYYY-MM del periodo del reporte. Regex /^\d{4}-\d{2}$/
--no-action-plan ELIMINADO (F-07 review 2026-06) Documentado históricamente como DEV-ONLY pero nunca existió en código. action-plan es REQUIRED en compose-assembled.cjs. Para iterar en dev sin slice real, generar un stub con /seo-section-action-plan --sample
--no-content-strategy flag (optional) false Skip explícito del slice content-strategy (Phase 9 — alimenta sec-16 §11.3 Matriz Cluster × Etapa × Disciplinas). Cuando NO se usa el flag y el slice EXISTE en disco → se consume + cross-validator obligatorio en Phase 5 + matriz §11.3 renderiza con clusters reales. Cuando se usa el flag o el slice NO existe → §11.3 simplemente NO renderiza (cero degradación visible — sec-16 mantiene P1-P4 intactos). Apropiado para clientes sin scope content/links o que aún no corrieron /seo-content-strategy. v3 redesign 2026-05-12: sec-20 standalone deprecada, integración total a sec-16.
--pdf flag (optional) false Placeholder — PDF aun no soportado. Responder que queda queued y continuar con HTML

Invocacion esperada del Lead:

/seo-build-report chedraui 2026-04
/seo-build-report chedraui 2026-04 --no-content-strategy  (cliente sin content/links scope o slice no generado aún)
/seo-build-report chedraui 2026-04 --pdf                  (warning: PDF aún no soportado, fallback a HTML)

Prerequisitos

  1. Carpeta del cliente en ~/Documents/punto-rojo-clients/{client_slug}/ con cliente-CLAUDE.md + intelligence/*.md.
  2. Slices en {cliente}/sections/:
    • REQUERIDOS (8) Phase 8 — si falta cualquiera, STOP con "Run /seo-section-{name} first.":
      • foda.json (1/8), tech-audit.json (2/8), history.json (3/8), ai-audit.json (4/8), competitive.json (5/8), benchmarks.json (6/8), context.json (7/8), action-plan.json (8/8).
    • OPCIONAL Phase 9content-strategy.json. Si EXISTE → se consume + cross-validator obligatorio + alimenta sec-16 §11.3 Matriz Cluster × Etapa × Disciplinas (v3 redesign 2026-05-12). Si NO existe → §11.3 simplemente no renderiza (sec-16 queda con P1-P4 sin matriz; cero degradación visible). No bloquea el reporte. Generado por /seo-content-strategy v3.
    • action-plan es REQUIRED — sin bypass (el flag --no-action-plan documentado históricamente nunca existió en código; F-07 review 2026-06).
  3. Node 20+ + report-builder/node_modules (si falta: cd report-builder && npm install).
  4. Repo pr-toolkit (solo si Lead elige upload) — default C:\Users\nicko\OneDrive\Escritorio\Proyectos\Punto-Rojo\pr-toolkit\ o $PR_TOOLKIT_PATH.

Flujo general

Inicio → Inputs + validacion →
  Fase 0: Context load (cliente-CLAUDE.md + actividad.md + intelligence/*)
  Fase 1: Verify slices exist + freshness check (warn si >30 dias)
          + Validate cada slice via su validator (lib/{name}-validator.js)
          + Cross-slice consistency: action-plan ↔ content-strategy (si existe slice opcional)
  Fase 2: Compose assembled JSON (deep-merge slices + meta + intelligence
          fallbacks para campos no cubiertos por slices)
          → persist a {client_repo}/output/assembled-{period}.json (re-render-friendly)
  Fase 3: CHECKPOINT OBLIGATORIO con el Lead (pre-generate — nunca saltarlo)
  Fase 4: Generar HTML via generate-report.js CLI
          → CHECKPOINT pre-upload (local-only vs push)
  Fase 5: (Opcional) Upload a pr-toolkit + git push + Vercel deploy
  Fase 6: Output al Lead + update actividad.md

Fase 0: Context load

0.1 Validar inputs

const SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
const PERIOD_RE = /^\d{4}-\d{2}$/;

if (!SLUG_RE.test(slug)) → STOP: "slug invalido: {slug}. Debe matchear /^[a-z0-9][a-z0-9-]*$/"
if (!PERIOD_RE.test(period)) → STOP: "period invalido: {period}. Debe ser YYYY-MM"

Si el Lead no proveyo period, default al mes actual (YYYY-MM desde currentDate).

0.2 Leer contexto del cliente

Usando Read tool (absolute paths):

  1. ~/Documents/punto-rojo-clients/{slug}/cliente-CLAUDE.md — S1 (dominio, lead, etapa), S3 (accesos), S5 (progreso skills).
  2. ~/Documents/punto-rojo-clients/{slug}/actividad.md — ultimas sesiones del equipo.
  3. ~/Documents/punto-rojo-clients/{slug}/intelligence/perfil.md — identidad, audiencia, tono.
  4. ~/Documents/punto-rojo-clients/{slug}/intelligence/objetivos.md — KPIs, targets.
  5. ~/Documents/punto-rojo-clients/{slug}/intelligence/productos-foco.md — productos priorizados.

Si falta alguno de los 3 obligatorios (perfil, objetivos, productos-foco): warning al Lead, no abortar — los slices son la fuente primaria; los intelligence files solo cubren campos auxiliares (access_requirements, onboarding_checklist, next_steps).

Intelligence opcionales (leer si existen): competidores.md, estacionalidad.md, contexto-historico.md.

0.3 Operador correcto

Ver shared/skill-execution-protocol.md §Regla 6. Operador = userEmail prefix (ej: nico.m@puntorojo.com → "Nico"). NUNCA usar cliente-CLAUDE.md.Lead como nombre del operador — ese es el account manager del cliente.


Fase 1: Verify + validate slices

1.1 Required slices presentes

SECTIONS_DIR = ~/Documents/punto-rojo-clients/{slug}/sections/

REQUIRED = ['foda', 'tech-audit', 'history', 'ai-audit',
            'competitive', 'benchmarks', 'context', 'action-plan']
OPTIONAL = ['content-strategy']        // Phase 9 — alimenta sec-16 §11.3 Matriz (v3 2026-05-12)
// (F-07 review 2026-06: el DEV_BYPASS_FLAG '--no-action-plan' fue eliminado — nunca existió en código)
OPT_OUT_FLAG    = '--no-content-strategy'  // skip content-strategy aunque exista

Para cada nombre en REQUIRED:

test -f "$SECTIONS_DIR/{name}.json"

Si NO existe → STOP con mensaje:

❌ Slice {name}.json no existe en {SECTIONS_DIR}.

Run /seo-section-{name} first.

(Este orquestador no genera la data — solo compone los 8 slices Phase 8 que ya
debieron correrse antes para este cliente/periodo.)

Excepción dev-only para action-plan.json:

  • Si existe → leer y validar (igual que las otras 7).
    Las secciones sec-16/17/18/19 (Roadmap + Build/Scale/Evolve + KPIs target)
    van a degradar a placeholders en el HTML.
    NO USAR en run productivo — el HTML entregado al cliente quedará incompleto.
    
    Continuo igual? [S] continuar / [N] STOP
    
  • Si NO existe → STOP (mismo mensaje que las otras REQUIRED — action-plan no tiene bypass).

Opcional content-strategy.json (Phase 9 — alimenta sec-16 §11.3 Matriz Cluster × Etapa × Disciplinas, v3 2026-05-12):

  • Si existe + Lead NO pasó --no-content-strategy → leer + validar + deep-merge (Fase 2) + cross-validator obligatorio (Fase 1.5) + sec-16 §11.3 renderiza con clusters reales.
  • Si existe + Lead pasó --no-content-strategy → IGNORAR el slice + §11.3 NO renderiza. Warn al Lead que el slice existe pero se omitió por flag.
  • Si NO existe + Lead NO pasó --no-content-strategy → §11.3 simplemente no renderiza (sec-16 mantiene P1-P4). Cero degradación visible. Nota al Lead en checkpoint 3 que el cliente aún no tiene content-strategy generado.
  • Si NO existe + Lead pasó --no-content-strategy → mismo que el caso anterior (flag confirma intencionalidad). Sin warn extra.

1.2 Freshness check (warn si >30 dias)

Para cada slice (incluido action-plan si existe):

# Edad del archivo en dias
file_age_days=$(( ( $(date +%s) - $(stat -c %Y "$SECTIONS_DIR/{name}.json") ) / 86400 ))

Si file_age_days > 30:

⚠️  Slice {name}.json tiene {N} dias (>30). La data puede estar desactualizada.

Recomendacion: corre /seo-section-{name} para refresh antes de generar el reporte.

Continuo igual? [S] continuar / [N] STOP / [R] re-correr esa seccion (te lo
indico — esta skill no la corre por vos)

NO bloquear si el Lead responde [S]. Documentar el warning en el output final del Lead (Fase 6) — "Generado con slices stale: {list}".

1.3 Validate cada slice via su validator

Por cada slice presente, leer + parsear + validar contra report-builder/lib/{name}-validator.js:

Slice Validator Funcion exportada
foda report-builder/lib/foda-validator.js validateFodaSlice
tech-audit report-builder/lib/tech-audit-validator.js validateTechAuditSlice
history report-builder/lib/history-validator.js validateHistorySlice
ai-audit report-builder/lib/ai-audit-validator.js validateAiAuditSlice
competitive report-builder/lib/competitive-validator.js validateCompetitiveSlice
benchmarks report-builder/lib/benchmarks-validator.js validateBenchmarksSlice
context report-builder/lib/context-validator.js validateContextSlice
action-plan report-builder/lib/action-plan-validator.js validateActionPlanSlice
content-strategy (opt.) report-builder/lib/content-strategy-validator.js validateContentStrategySlice

Estrategia: invocar todos los validators en una sola pasada Bash:

cd "${CLAUDE_PLUGIN_ROOT}/report-builder" && node -e '
const fs = require("fs");
const path = require("path");
// 8 slices REQUIRED (action-plan incluido — sin bypass, F-07 review 2026-06).
const slices = ["foda", "tech-audit", "history", "ai-audit", "competitive", "benchmarks", "context", "action-plan"];
const SECTIONS = "/path/to/sections/";
const results = {};
for (const name of slices) {
  const validator = require(`./lib/${name}-validator.js`);
  const fnName = `validate${name.replace(/-./g, m => m[1].toUpperCase()).replace(/^./, m => m.toUpperCase())}Slice`;
  const validate = validator[fnName];
  const slice = JSON.parse(fs.readFileSync(path.join(SECTIONS, `${name}.json`), "utf8"));
  results[name] = validate(slice);
}
process.stdout.write(JSON.stringify(results, null, 2));
'

Si cualquier slice falla validacion (valid === false) → STOP con el contenido de errors[] para que el Lead pueda corregirlo (re-correr la skill afectada).

NO intentar fixear el slice inline — el extractor de cada skill es el unico responsable de su shape.

1.4 Process-and-discard temprano

Despues de leer + validar cada slice, retener en contexto solo el slice parseado (no el raw text). Patron 5 de shared/context-engineering.md. El assembled JSON final se persiste al client repo (Fase 2.5) — no se retiene en context.

1.5 Cross-slice consistency (action-plan ↔ content-strategy)

Si content-strategy slice existe + se va a consumir (Lead no pasó --no-content-strategy): ejecutar el cross-validator (Q3 decision 2026-05-12, expandido v3 con 4 rules nuevas 2026-05-12) para detectar drift entre los highlights agregados de sec-16 (action-plan.roadmap[*].source_slice='content-strategy') y los items reales de sec-16 §11.3 Matriz Cluster × Etapa × Disciplinas (content_items + link_items del slice). Rules v3 adicionales: cluster_completeness, discipline_inconsistency, stage_mismatch, anchor_mix_invariant_violation (ver design doc §6.2).

cd "${CLAUDE_PLUGIN_ROOT}/report-builder" && node -e '
const fs = require("fs");
const { validateActionPlanContentStrategyConsistency } = require("./lib/cross-slice-validator");
const ap = JSON.parse(fs.readFileSync("/path/sections/action-plan.json", "utf8"));
const cs = JSON.parse(fs.readFileSync("/path/sections/content-strategy.json", "utf8"));
const r = validateActionPlanContentStrategyConsistency(ap, cs);
process.stdout.write(JSON.stringify(r, null, 2));
'

Outputs { valid, errors[], warnings[], drift[] }:

  • drift[].severity='critical' (missing_finding_id, orphan_finding_id, missing_content_strategy_slice) → STOP con detalle del drift y opciones:
    ❌ Drift detectado entre action-plan ↔ content-strategy:
    
    • Highlight "Pillar Despensa M1-M3" (sec-16) apunta a source_finding_id
      'content_build_99' pero ese ID no existe en content-strategy slice.
      → Probable causa: update content-strategy MOVE/REMOVE no propagado a action-plan.
    
    Opciones:
      [R] Re-correr /seo-section-action-plan para regenerar highlights sincronizados
      [E] Editar manualmente action-plan.json para arreglar el source_finding_id
      [F] Forzar continuar (drift queda documentado en actividad.md + audit_meta.cross_validator_warnings[])
    
  • drift[].severity='warning' (category_mismatch, malformed_id, out_of_range_index, unparseable_finding_id, empty_category) → reportar al Lead en Fase 3 (checkpoint pre-generate) pero NO bloquear. Documentar en actividad.md.
  • valid: true + drift vacío → continuar a Fase 2 sin warn.

NO intentar fixear inline — los slices son autoritativos. Solo orquestar la re-ejecución de la skill upstream que generó el drift.


Fase 2: Compose assembled JSON

2.1 Construir meta (no viene de ningun slice)

const meta = {
  client_name,         // de intelligence/perfil.md o cliente-CLAUDE.md
  client_slug: slug,
  site_url,            // de cliente-CLAUDE.md S1 (dominio)
  industry,            // de intelligence/perfil.md (snake_case)
  country,             // de cliente-CLAUDE.md S1 (ISO 3166-1 alpha-2 lowercase, ej "mx")
  segment,             // de intelligence/perfil.md o derivado (industry_country, ej "retail_mx")
  date: period + "-01",
  lead,                // operador (userEmail prefix — Fase 0.3)
  period,              // "YYYY-MM"
  generator: {
    toolkit_version: /* leer de package.json root del toolkit. NUNCA hardcodear. */
  }
};

Toolkit version:

node -e 'console.log("v" + require("'"${CLAUDE_PLUGIN_ROOT}"'/package.json").version)'

2.2 Deep-merge slices

El assembled JSON es la union de todos los slices + meta + fallbacks de intelligence files. Mapping slice → top-level keys:

Slice Emite top-level keys
foda.json scoring (overall, label, maturity_level, pillars, weights, foda, priorities, roadmap)
tech-audit.json diagnostic.{tech_seo, content_coverage, authority, audit_date, degradation}
history.json history (audit_date, ga4_traffic, ahrefs_visibility, market_trends, degradation)
ai-audit.json ai_visibility (audit_date, platforms, citability, queries_with_ai, geo_roadmap, kpis)
competitive.json competitive_enrichment + top_content_by_competitor + top_content_by_competitor_findings
benchmarks.json scoring.pillar_breakdowns (merge dentro de scoring) + audit_meta + degradation
context.json executive + project_data + context (objectives_table, scope_table, journey_stages, personas_table...) + audit_meta (merge con benchmarks audit_meta)
action-plan.json (si existe) roadmap, access_requirements, onboarding_meetings, checklist_meetings, next_steps_summary y scoring.roadmap (merge dentro de scoring). Keys canónicos post-v3.59.1 — los legacy onboarding_checklist/next_steps ya NO los emite el slice
content-strategy.json (opt.) content_strategy (top-level con calendar_build/scale/backlog × content/links) + merge en audit_meta (scope_budget.content/links + changelog + version + cross_section_status)

Merge logic (NO usar lodash — hacerlo a mano):

const assembled = {
  meta,
  ...techAuditSlice,         // emite { diagnostic }
  ...historySlice,           // emite { history }
  ...aiAuditSlice,           // emite { ai_visibility }
  executive: contextSlice.executive,
  project_data: contextSlice.project_data,
  context: contextSlice.context,
  competitive_enrichment: competitiveSlice.competitive_enrichment,
  top_content_by_competitor: competitiveSlice.top_content_by_competitor,
  top_content_by_competitor_findings: competitiveSlice.top_content_by_competitor_findings,
  // foda + benchmarks ambos emiten `scoring` — merge: foda da overall/foda/priorities/roadmap/pillars/weights,
  // benchmarks aporta pillar_breakdowns (sec-13)
  scoring: { ...fodaSlice.scoring, pillar_breakdowns: benchmarksSlice.scoring.pillar_breakdowns },
  // benchmarks + context ambos emiten `audit_meta` — context gana (mas campos), pero preservar
  // industry_p50_pillars + coherence_check de benchmarks
  audit_meta: {
    ...benchmarksSlice.audit_meta,
    ...contextSlice.audit_meta,
    industry_p50_pillars: benchmarksSlice.audit_meta?.industry_p50_pillars,
    coherence_check: benchmarksSlice.audit_meta?.coherence_check,
  },
  ...(benchmarksSlice.degradation ? { degradation: benchmarksSlice.degradation } : {}),
};

2.3 Action-plan slice

⚠️ Fix F-06 (review 2026-06): esta sección documentaba un compose manual con los keys LEGACY pre-v3.59.1 (onboarding_checklist/next_steps) que el slice 8/8 ya no emite — un Lead que lo siguiera al pie de la letra producía sec-17/18 vacías. El compose manual queda eliminado del flow: la Fase 2 entera delega en el script canónico, que es la única implementación mantenida:

node scripts/compose-assembled.cjs {slug} {period} [--strict] [--no-content-strategy]

El script carga los 8 slices requeridos (+ content-strategy opcional), valida cada uno con su validator, hace el merge canónico (keys post-v3.59.1: onboarding_meetings, checklist_meetings, next_steps_summary), re-deriva scope_budget y escribe output/assembled-{period}.json. Si necesitás entender el mapping slice→assembled, la fuente de verdad es scripts/compose-assembled.cjs (NO pseudocódigo en este doc — ya generó drift una vez).

Para referencia: el slice action-plan aporta roadmap (sec-16), access_requirements (sec-17), onboarding_meetings + checklist_meetings (sec-18), next_steps_summary (sec-19) y scoring.roadmap (merge dentro de scoring).

Nota --no-action-plan: este flag aparecía documentado como "DEV-ONLY bypass" pero no existe en ningún códigocompose-assembled.cjs trata action-plan como REQUIRED (exit 1 si falta). Si falta el slice, correr /seo-section-action-plan antes de componer.

2.3.b Content-strategy slice (opcional Phase 9 — alimenta sec-16 §11.3 Matriz)

⚠️ STOP GATE ANTI-OMISIÓN (I14): antes de Phase 2.4, confirmar explícitamente una rama. Enforcement programático (F-08 review 2026-06): además de este gate procedimental, generate-report.js ahora chequea al render: si sections/content-strategy.json existe y el assembled NO trae content_strategy → block (exit 1 bajo strict default) salvo opt-out explícito --no-content-strategy (existe en generate-report.js Y compose-assembled.cjs, y deja degradation entry auditable). El caso Pomelo ya no puede pasar silenciosamente.

  • [A] content-strategy.json EXISTE + no se pasó --no-content-strategyassembled.content_strategy DEBE quedar asignado. Si se omite el merge, §11.3 renderiza VACÍA en silencio (sin error — clusterMatrix devuelve [] → if-gate no renderiza → Lead no ve la matriz).
  • [B] slice no existe O flag activo → continuar; §11.3 no renderiza (por diseño).

OBLIGATORIO. No saltarlo aunque el Lead diga "continuar" o el modo sea Auto.

Si content-strategy.json existe + parsea OK + Lead NO pasó --no-content-strategy:

// v3 redesign 2026-05-12: el slice alimenta sec-16 §11.3 Matriz Cluster × Etapa
// × Disciplinas (template usa filter `clusterMatrix` de lib/sec16-matrix-builder.js
// para digerir content_strategy + roadmap por cluster temático).
assembled.content_strategy = contentStrategySlice.content_strategy;
// I14 — log explícito rama A para que el merge quede narrado y auditable.
console.log('[build-report] content_strategy merged — §11.3 matriz con', (contentStrategySlice.content_strategy?.calendar_build?.length || 0), 'build items');
// audit_meta merge: content-strategy aporta scope_budget.content + scope_budget.links
// + scope_constraints (v3: contract_months + stages_breakdown) + changelog +
// cross_section_status. NO pisa los campos de benchmarks/context — usa Object.assign
// defensivo.
//
// v3.46 — IMPORTANTE: re-derive scope_budget at compose-time (NO usar cs_meta.scope_budget
// directamente). Razón: slices generados con extractor antiguo tienen `links.*_allocated`
// en granularidad de PACKAGES en lugar de individual LINKS (1 link_item = 1 package mensual
// con N=links_count individuales — Q5 decision 2026-05-12). El bug de unidades reportaba
// utilización 13% para Chedraui (12 packages vs 96 link scope) cuando real era 100%
// (12 × 8 = 96 links). El extractor v3.46 suma `links_count` correctamente. Re-derive aplica
// el fix a slices legacy sin necesidad de regenerar — backward-compat 100%.
if (contentStrategySlice.audit_meta) {
  const cs_meta = contentStrategySlice.audit_meta;
  const { deriveContentScopeBudget } = require("./lib/content-strategy-extractor");
  const cs = contentStrategySlice.content_strategy || {};
  const freshScopeBudget = deriveContentScopeBudget(
    { build: cs.calendar_build || [], scale: cs.calendar_scale || [], backlog: cs.calendar_backlog || [] },
    { build: cs.link_calendar_build || [], scale: cs.link_calendar_scale || [], backlog: cs.link_calendar_backlog || [] },
    cs_meta.scope_constraints || {}
  );
  assembled.audit_meta = {
    ...assembled.audit_meta,
    scope_budget: {
      ...(assembled.audit_meta?.scope_budget || {}),
      ...(freshScopeBudget || cs_meta.scope_budget || {}),
    },
    // scope_constraints v3 (contract_months + stages_breakdown) viene de content-strategy
    scope_constraints: {
      ...(assembled.audit_meta?.scope_constraints || {}),
      ...(cs_meta.scope_constraints || {}),
    },
    // changelog vive solo en content-strategy — append-only audit trail del slice.
    content_strategy_changelog: cs_meta.changelog,
    content_strategy_version: cs_meta.version,
  };
}

Si NO existe O Lead pasó --no-content-strategy → omitir el merge. El template sec-16 §11.3 detecta data.content_strategy ausente (vía clusterMatrix filter que devuelve [] cuando no hay slice) y simplemente no renderiza la matriz — sec-16 sigue mostrando P1-P4 intactos. Cero degradación visible. El sidebar de base.njk mantiene count Nubank fidelity 18 fijo (sec-20 standalone deprecada en v3 — count dinámico 18↔19 ya no aplica).

2.4 Intelligence fallbacks (campos NO cubiertos por slices)

Si despues del merge algun campo critico esta vacio + viene de intelligence:

Campo Fuente fallback (si slice falta)
meta.industry intelligence/perfil.md line industry: ...
meta.country cliente-CLAUDE.md S1
meta.segment intelligence/perfil.md o industry + "_" + country
meta.client_name intelligence/perfil.md o cliente-CLAUDE.md S1
meta.site_url cliente-CLAUDE.md S1

NUNCA inventar metricas o data SEO. Solo metadata (nombres, URLs, country code) puede venir de intelligence cuando los slices no la cubren.

Si ningun fallback resuelve un campo requerido por generate-report.js (meta.client_name es el unico hard requirement), STOP con mensaje claro.

2.5 Escribir JSON al client repo (B11 fix v3.37.2)

El assembled JSON se persiste al client repo (no a tmp) para que el Lead pueda re-render con patches data-side sin re-correr el pipeline completo. Ejemplo de uso: inyectar SimilarWeb post-fetch, fixear insights manuales, agregar slices nuevos sin regenerar todo.

ASSEMBLED_JSON="$HOME/Documents/punto-rojo-clients/{slug}/output/assembled-{period}.json"
mkdir -p "$(dirname "$ASSEMBLED_JSON")"

Escribir el assembled JSON con Write tool a $ASSEMBLED_JSON.

Pre-v3.37.2 (legacy): el JSON se escribía a os.tmpdir() y se borraba en Fase 6.3 — imposible re-render con patches sin re-correr /seo-build-report completo. Bug B11 (post-prosegur-ar empirical 2026-05-05).

2.6 Enrichment automático — SimilarWeb + GA4 demographics + PSI/CWV + refdomains-history + PSI client mobile (v3.55.0)

Tras escribir assembled-{period}.json en 2.5, invocar los 6 scripts de enrichment que llenan los campos data-side que NO produce ningún slice de Phase 8 (traffic real, channel mix, demografía, CWV per competitor, velocity de adquisicion refdomains, PSI mobile client CWV para sec 11, snapshot de métricas clave).

Mapping script → sección que llena:

Script Sección Env var requerida
enrich-with-similarweb.cjs sec 07 traffic_real_vs_estimated, sec 08 channel_mix.organic_vs_paid, sec 09 demographics.client_geography SIMILARWEB_API_KEY
enrich-with-ga4-demographics.cjs sec 09 demographics.{gender_table, age_table} GA4 OAuth (tokens.json autoritativo, fallback .env)
enrich-with-psi-cwv.cjs sec 06 competitive_enrichment.benchmark_seo[].cwv (LCP/INP/CLS per competitor) GOOGLE_PSI_API_KEY (free tier 25k/día)
enrich-with-refdomains-history.cjs (v3.55.0) sec 13 diagnostic.authority.rows (Velocidad de adquisicion 12m) reads output/refdomains-history-{period}.json pre-generado via Ahrefs Site Explorer history endpoint
enrich-with-psi-client-cwv.cjs (v3.55.0) sec 11 diagnostic.tech_seo.cwv.rows[].mobile (LCP/INP/CLS/TTFB/FCP client mobile, CrUX field + Lighthouse lab) GOOGLE_PSI_API_KEY
enrich-with-metrics-snapshot.cjs (v3.78.0, I36) audit_meta.snapshot — canonical snapshot of 5 key metrics (refdomains_current, refdomains_yoy_growth_pct, organic_traffic_current, organic_traffic_yoy_pct, dr_current) + normaliza display fields (executive.kpi_cards, history.ahrefs_visibility.analysis[0].summary). Corre ÚLTIMO, después de enrich-with-refdomains-history.cjs (reutiliza refdomains_history_patch.growth_12m_pct ya escrito por ese script). ninguna (lee assembled JSON directamente)

Graceful degradation: las libs subyacentes (similarweb-fetcher.js línea 30-31, psi-fetcher.js línea 28-31) devuelven { error, data: null } sin throw cuando falta la API key. Los scripts iteran logging warnings + escriben el assembled con placeholders ('—', cwv_status: 'critical'). El pipeline padre nunca aborta por falta de cred.

Skip opt-out: si el Lead invocó con --no-enrich, saltar esta Fase entera (ver sección "Manejo de flags" más abajo).

Tiempo estimado: ~3-10 min total. PSI sequential ~30s × 7 dominios = ~3.5min. SimilarWeb 8 dominios × 3 endpoints = ~2-4min. GA4 demographics ~30s.

Bash invocation:

PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-C:/Users/nicko/OneDrive/Escritorio/Proyectos/Punto-Rojo/punto-rojo-claude-toolkit}"
cd "$PLUGIN_ROOT/report-builder" && \
  node scripts/enrich-with-similarweb.cjs "{slug}" "{period}" && \
  node scripts/enrich-with-ga4-demographics.cjs "{slug}" "{period}" && \
  node scripts/enrich-with-psi-cwv.cjs "{slug}" "{period}" && \
  node scripts/enrich-with-refdomains-history.cjs "{slug}" "{period}" && \
  node scripts/enrich-with-psi-client-cwv.cjs "{slug}" "{period}" && \
  node scripts/enrich-with-metrics-snapshot.cjs "{slug}" "{period}"

El && short-circuits SOLO en errores reales (bug en wireup → exit 1). Falta de API key NO produce exit 1 (graceful). Si un script sí sale 1, el siguiente no corre — la skill captura el exit code y reporta al Lead via run summary.

Backups: los 3 scripts ya escriben {file}.bak.pre-{psi|sw|ga4-demo}-{timestamp} antes de patchear (ver enrich-with-psi-cwv.cjs:86-87,99-100). Cleanup periódico de .bak.pre-* files es manual — no bloqueante.

Post-2.6: el assembled JSON ahora contiene SW + GA4 demo + CWV reales. Fase 3 (checkpoint) muestra los counts actualizados al Lead.


Fase 3: Checkpoint pre-generate

Presentar al Lead:

Voy a generar el reporte HTML para {client_name} del periodo {period}.

Slices Phase 8 cargados:
  ✓ foda.json           ({age} dias, {N} priorities)
  ✓ tech-audit.json     ({age} dias, audit_date {ISO})
  ✓ history.json        ({age} dias, {N} meses serie)
  ✓ ai-audit.json       ({age} dias, score citability {X}/10)
  ✓ competitive.json    ({age} dias, {N} competidores)
  ✓ benchmarks.json     ({age} dias, 3 pillars × {scores})
  ✓ context.json        ({age} dias, {N} personas, {N} stages journey)
  {✓|✗} action-plan.json  ({age} dias o "missing — REQUIRED, correr /seo-section-action-plan")

Stale slices (>30 dias): {list o "ninguno"}

Data assembled:
  overall_score: {X}/100
  maturity_level: {Y}
  pillar_breakdowns: 3/3
  intelligence loaded: {Z/3 obligatorios + opcionales}

Salida HTML:    ~/Documents/punto-rojo-clients/{slug}/output/{slug}-diagnostico-{period}.html
Salida JSON:    ~/Documents/punto-rojo-clients/{slug}/output/assembled-{period}.json (persistente — re-render-friendly)

¿Arrancamos la generacion?
[S] Si, generar HTML
[N] No, ajustar algo
[R] Revisar el JSON assembled primero

Esperar respuesta explicita. Si [R], hacer Read del $ASSEMBLED_JSON y mostrar al Lead antes de repetir el checkpoint.


Fase 4: Generar HTML

4.1 Invocar el CLI

Via Bash tool:

cd "${CLAUDE_PLUGIN_ROOT:-C:/Users/nicko/OneDrive/Escritorio/Proyectos/Punto-Rojo/punto-rojo-claude-toolkit}/report-builder" && \
node generate-report.js \
  --diagnostic-json "$ASSEMBLED_JSON" \
  --client "{slug}" \
  --period "{period}" \
  --json-summary

El CLI (B6 fix v3.37.2):

  • Escribe el HTML a ~/Documents/punto-rojo-clients/{slug}/output/{slug}-diagnostico-{period}.html (resolve automático con --client + --period, default --client-repo-base apunta al repo cliente persistente — ya NO escribe al install path del plugin).
  • Override con --output <path> para destinos custom (debe vivir dentro de report-builder/ o del client repo base — path traversal rejected).
  • Emite mensaje humano a stderr (✅ Report generated: ...).
  • Con --json-summary, emite un single-line JSON a stdout con {ok, output, size_kb, sections, duration_ms, ...}.

4.2 Parsear resultado

  • Exit code 0 + stdout JSON con ok: true → extraer output, size_kb, sections, duration_ms y continuar a Fase 5.
  • Exit code != 0 o ok: false → reportar al Lead:
    ❌ Generacion fallo: {error del stderr}
    
    El JSON assembled quedo en $ASSEMBLED_JSON para inspeccion + re-render
    con patches data-side.
    
    ¿Queres que inspeccione el JSON y re-intente? (S/N)
    
    Si acepta: Read del $ASSEMBLED_JSON + diagnosticar (puede ser un slice con shape mal validado por el orquestador). Si no: STOP con el JSON path documentado en actividad.md.

4.3 Validar output

Antes de Fase 5, verificar:

  • El archivo existe en la ruta reportada (Bash test -f).
  • size_kb > 50 (si el HTML es <50KB algo fallo — un reporte completo supera los 300-500 KB por CSS + JS inlined).
  • sections >= 14 (14 secciones IN minimo + algunas placeholder).

Si alguna validacion falla, reportar al Lead y ofrecer re-intentar.

4.4 Pre-render validators (v3.72.0 — Sprint 2 I26+I28+I33)

A partir de v3.72.0 el CLI generate-report.js corre automaticamente 3 validators sobre la baseline JSON ANTES de aplicar el SR overlay + render:

  1. cross-section-coherence (I33 + I34): detecta templating anti-patterns (palabras consecutivas duplicadas, circular templates) + number drift cross-section vs slice evidence.
  2. editorial (I28 + I30 + I32): aplica las reglas de report-builder/data/prohibited-phrases.json — jerga academica/SEO sin claridad (block), adjetivos cualitativos sin dato nearby (warn), typos es-LATAM (warn).
  3. source-coherence (I26 + I25 partial): cuando audit_meta.source_of_truth esta declarado (e.g. "Ahrefs Site Audit"), detecta mentions narrativas de fuentes mutuamente excluyentes (e.g. SF crawl).

Comportamiento default: strict — si encuentra al menos 1 finding con severity=block, exit 1 con mensaje claro indicando validator + path + match

  • comandos standalone para diagnosticar. Warns + infos se emiten a stderr siempre (visibles pero no bloquean).

Escape hatches (solo para iteracion local / debugging):

  • --no-strict-validators: emite warning + continua con render
  • --skip-validators: no corre validators (NO recomendado pre-cliente)
  • env var PR_DIAGNOSTICO_NO_STRICT_VALIDATORS=1: equivalente al flag
  • env var PR_DIAGNOSTICO_SKIP_VALIDATORS=1: skip total

Si el render falla por validators (exit 1):

  1. NO usar --skip-validators para "saltarlos" — los findings reportan bugs reales (templating, jerga, contradicciones).
  2. Corre el CLI standalone para investigar:
    cd ${PLUGIN_ROOT}/report-builder
    node scripts/validate-cross-section-coherence.cjs "$ASSEMBLED_JSON"
    node scripts/validate-editorial.cjs "$ASSEMBLED_JSON"
    node scripts/validate-source-coherence.cjs "$ASSEMBLED_JSON"
    
  3. Aplicar patches al $ASSEMBLED_JSON (rewriting narratives, fix templating, declarar audit_meta.source_of_truth o cambiarlo a "mixed-with-disclaimer").
  4. Re-correr generate-report.js sin --skip-validators.

Por que pre-render y no post-render: si el HTML ya esta renderizado con templating bug ("Sitemap sitemap") o jerga ("techo competitivo"), el ciclo de fix es generar + revisar + patch + regenerate. El validator pre-render elimina ese loop — encuentras los bugs ANTES del render. Caso real: Pomelo Phase 6 (3-agents review) detecto 30+ residuales que el sweep regex no detecto; pre-render validators los hubieran detectado en 1 corrida.


Fase 4.5: Revisión pre-upload (gate de calidad editorial/coherencia)

Tras renderizar el HTML (Fase 4) y ANTES de subirlo (Fase 5), corré el gate de juicio /seo-pre-upload-review. Complementa los validators determinísticos de §4.4 (aquéllos son estructurales pre-render; éste es holístico post-render): 3 reviewers independientes (SEO senior, Editor, Coherence) leen el HTML renderizado buscando lo que el determinismo no puede — completitud del roadmap, sustento de claims, coherencia estratégica end-to-end, claridad C-level. Precedente: los 30+ residuales que el 3-agents review manual cazó en Pomelo (engram #1317).

4.5.1 Invocar el gate

/seo-pre-upload-review {slug} {period}

El skill resuelve el HTML + $ASSEMBLED_JSON por convención, corre los reviewers (subagentes paralelos por default, inline fallback engram #1073), consolida por consenso (3/3 critical · 2/3 high · 1/3 sug) y muestra el gate al Lead.

4.5.2 Checkpoint al Lead

El gate es soft con override — surface-a, no prohíbe:

¿Qué hacés con los findings del pre-upload review?
  [A] Aplicar todo · [C] Solo críticos + high · [P] Ver plan de patches · [O] Override (subir igual)

Esperar respuesta. Si el Lead elige aplicar, los fixes van al assembled.json + re-render vía apply-patches.cjs (volvés a tener HTML fresco). Auto Mode NO autoriza skip del gate cliente-facing.

4.5.3 Escape hatch (DEV-ONLY)

--skip-pre-upload-review (o PR_DIAGNOSTICO_SKIP_PRE_UPLOAD_REVIEW=1) salta el gate. NO recomendado pre-cliente — solo para iteración de desarrollo.


Fase 5: Upload a pr-toolkit (opcional)

v3.65.0 (F12 — Lead feedback 2026-05-19): el flujo de upload fue extraído a scripts/upload-to-pr-toolkit.cjs (single source of truth). El skill sigue mostrando los CHECKPOINTS al Lead (L/U + S/N gates) pero la EJECUCIÓN delega al script. Más fácil de mantener + testear + reusable desde otros contextos (CI, batch).

5.1 Checkpoint al Lead

✅ HTML generado: {output_path} ({size_kb} KB, {sections} sections, {duration_ms}ms)

Opciones de distribucion:
  [L] Local only — lo dejas local, lo mandas por email/drive al cliente manualmente
  [U] Upload a pr-toolkit — commit + push, Vercel auto-deploya en ~60s a
      https://pr-toolkit.puntorojo.com/reports/{slug}/{period}
      (auth-gated @puntorojo.com, para equipo interno)

¿Que elegis? (L / U)

Si [L] local-only → saltar a Fase 6 directamente con el local path.

5.2 Upload via script wrapper (si [U])

El script report-builder/scripts/upload-to-pr-toolkit.cjs hace todo el trabajo: preflight (path + git repo + working tree limpio + branch) → copy HTML → commit local. NO pushea por default — el push espera el checkpoint del Lead en 5.5.

PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(pwd)}"
PR_TOOLKIT_PATH="${PR_TOOLKIT_PATH:-C:/Users/nicko/OneDrive/Escritorio/Proyectos/Punto-Rojo/pr-toolkit}" \
  node "$PLUGIN_ROOT/report-builder/scripts/upload-to-pr-toolkit.cjs" \
  "{slug}" "{period}" "{output_path}"

Exit codes del script:

  • 0 → preflight + copy + commit OK. Continuar a 5.5 checkpoint.
  • 1 → preflight failed (path inválido / no git repo / args inválidos). STOP al Lead con el mensaje del script.
  • 2 → copy/commit failed. STOP al Lead — el script imprime instrucción de fix.
  • 3 → push failed (solo si se llamó con --auto-push). STOP — commit ya está hecho local, retry manual.

El script imprime JSON summary al stdout ({ok, slug, period, branch, commit_message, expected_url}). Capturar y mostrar al Lead.

5.5 CHECKPOINT pre-push (OBLIGATORIO)

NUNCA pushear sin aprobacion explicita. Presentar:

✅ Commit local creado en pr-toolkit ({PR_TOOLKIT_BRANCH}):
  feat(reports): add {slug} diagnostic report {period}

Siguiente paso: `git push origin {PR_TOOLKIT_BRANCH}` → Vercel detecta + auto-deploya (~60s)
→ URL: https://pr-toolkit.puntorojo.com/reports/{slug}/{period}

¿Hago el push?
[S] Si, pushear ahora
[N] No, lo push-eo yo manualmente mas tarde

Esperar respuesta.

5.6 Push + Vercel deploy handoff

Si [S] → re-invocar el script wrapper con --auto-push (v3.65.0 F12):

PR_TOOLKIT_PATH="${PR_TOOLKIT_PATH:-C:/Users/nicko/OneDrive/Escritorio/Proyectos/Punto-Rojo/pr-toolkit}" \
  node "$PLUGIN_ROOT/report-builder/scripts/upload-to-pr-toolkit.cjs" \
  "{slug}" "{period}" "{output_path}" --auto-push

El script salta el commit (working tree ya tiene el commit de 5.2) y hace solo el git push. Si push exitoso (exit 0) → emite JSON summary con auto_pushed: true. Si push fallido (exit 3) → emite mensaje detallado de auth/non-fast-forward y deja el commit listo para retry manual.

Alternativa minimal (sin re-invocar script, solo push):

cd "$PR_TOOLKIT_PATH" && git push origin "$PR_TOOLKIT_BRANCH"

Error handling:

  • Auth failure (Permission denied, authentication failed) → reportar:
    ❌ Push fallo por auth. Opciones:
      1. Verificá `gh auth status` (GitHub CLI)
      2. Verificá tu SSH key (`ssh -T git@github.com`)
      3. Si son creds de Windows Credential Manager, re-logueate
    
    El commit local esta hecho — cuando resolvás, corré manualmente:
      cd {PR_TOOLKIT_PATH} && git push origin {PR_TOOLKIT_BRANCH}
    
  • Non-fast-forward → reportar que el remote cambio, sugerir git pull --rebase antes de reintentar.

Si el push exitoso → NO hacer sleep. Vercel desploya async en ~60-90s; en vez de bloquear, handoff explícito al Lead con prompt interactivo:

✅ Push exitoso — Vercel auto-deploy en curso (~60-90s)

🔗 URL final:  https://pr-toolkit.puntorojo.com/reports/{slug}/{period}
📊 Dashboard:  https://vercel.com/puntorojo/pr-toolkit/deployments

Opciones:
  [S] Ya validé la URL / seguí aunque no la haya abierto — Fase 6
  [W] Esperá vos — reintento el check en ~60s (instrucción: me escribís "listo" cuando quieras avanzar)
  [E] La URL no carga / devuelve error — pasame el síntoma y lo triageamos antes de Fase 6

Reglas de respuesta:

  • S → avanzar a Fase 6 inmediatamente.
  • W o "listo" → avanzar a Fase 6 (el Lead ya esperó).
  • E + síntoma → NO avanzar a Fase 6. Triage: 404 por deploy todavía en progreso (esperar 60s más), 500 por file tracing fallido (revisar next.config.ts > outputFileTracingIncludes), redirect loop por middleware (revisar matcher).

Fase 6: Output al Lead + update actividad.md

6.1 Reportar al Lead

✅ Reporte generado para {client_name}

🗂  Archivo local: {output_path} ({size_kb} KB, {sections} sections)
🔗 URL (auth-gated): https://pr-toolkit.puntorojo.com/reports/{slug}/{period}     (si hizo upload)
📦 Slices Phase 8 usados:
   • foda ({age}d) · tech-audit ({age}d) · history ({age}d)
   • ai-audit ({age}d) · competitive ({age}d) · benchmarks ({age}d)
   • context ({age}d) · action-plan ({age}d, REQUIRED — sin bypass)

⏱  Render: {duration_ms}ms

Stale slices (>30d): {list o "ninguno"}

**Share-ready**:
- Para clientes externos sin cuenta @puntorojo.com → mandá el archivo local por email/drive
- Para el equipo interno → pasá el URL hosted

**Tips antes de distribuir**:
1. Abrí el HTML local en Chrome y revisá el paginado
2. Validá que los nombres de Lead + equipo coincidan con el cliente actual
   correr /seo-section-action-plan + re-disparar este skill SIN el flag para producción
4. PDF queda queued para un patch futuro

Skill completada. Escribi /context para ver cuanto espacio queda antes de continuar.

6.2 Update actividad.md

Usar Edit tool sobre ~/Documents/punto-rojo-clients/{slug}/actividad.md. Agregar arriba del contenido existente (lo mas reciente primero):

## {YYYY-MM-DD} — {operador}

**Qué se hizo:**
- Generé reporte HTML del diagnóstico para período {period} usando 8 slices Phase 8
- {si upload:} Subí a pr-toolkit, URL: https://pr-toolkit.puntorojo.com/reports/{slug}/{period}
- {si local-only:} Dejé local en {output_path} — queda para compartir manual

**Decisiones tomadas:**
- Slices usados: foda, tech-audit, history, ai-audit, competitive, benchmarks, context, action-plan
- {si stale slices:} Slices stale (>30d): {list} — Lead aprobó continuar igual

**Pendiente:**
- Validar con cliente los hallazgos del reporte
- {si onboarding pending:} Cerrar items pendientes de onboarding

---

Mantener las ultimas 10 entradas — si hay mas, eliminar las mas antiguas.

6.3 Persistencia del assembled JSON (B11 fix v3.37.2)

El JSON assembled NO se borra al final del run. Vive persistente en ~/Documents/punto-rojo-clients/{slug}/output/assembled-{period}.json para permitir re-render con patches data-side sin re-correr el pipeline completo.

Casos de uso típicos:

  • Inyectar SimilarWeb post-fetch (handoff Prosegur AR pattern).
  • Reescribir insights manuales (top channel detection, narrativa per-cliente).
  • Agregar slices nuevos / cross-slice patches (ej. strip <strong> literal).
  • Re-render rápido tras editar 1 sub-campo (vs ~85min de re-pipeline).

Para re-render con patches:

cd report-builder && \
node generate-report.js \
  --diagnostic-json "$HOME/Documents/punto-rojo-clients/{slug}/output/assembled-{period}.json" \
  --client {slug} \
  --period {period}

No hay limpieza tmp — pre-v3.37.2 borraba os.tmpdir() JSON, comportamiento deprecado por Bug B11 (post-prosegur-ar empirical 2026-05-05).


Manejo de flags

--no-action-plan — ELIMINADO (F-07 review 2026-06)

Este flag estaba documentado como "DEV-ONLY bypass" pero nunca existió en código: compose-assembled.cjs trata action-plan como slice REQUIRED (exit 1 si falta).

Comportamiento real:

  • Slice existe → flow normal (validate + merge action-plan).
  • Slice missing → STOP "Run /seo-section-action-plan first." (Fase 1.1) — sin bypass.
  • Para iterar en dev sin slice real: generar un stub con /seo-section-action-plan --sample.

--no-content-strategy (opt-out apropiado para producción)

Skip explícito del slice content-strategy (Phase 9 — alimenta sec-16 §11.3 Matriz Cluster × Etapa × Disciplinas, v3 redesign 2026-05-12). Este flag SÍ es válido en producción para clientes cuyo scope contractual no incluye contenido/links, o cuando la skill /seo-content-strategy aún no se corrió (cliente recién onboardeado).

Comportamiento:

  • Sin la flag + slice existe → flow normal (validate + merge content-strategy + cross-validator) + §11.3 renderiza con clusters reales.
  • Sin la flag + slice missing → §11.3 NO renderiza (sec-16 mantiene P1-P4 sin matriz). Cero degradación visible.
  • Con la flag + slice existe → IGNORA el slice + §11.3 no renderiza + warn al Lead que el slice está disponible pero se omitió por flag.
  • Con la flag + slice missing → §11.3 no renderiza sin warn extra (flag confirma intencionalidad).

Cross-validator corre SOLO cuando ambos slices están presentes y se consumen (action-plan + content-strategy). v3 expansion: detecta cluster_completeness, discipline_inconsistency, stage_mismatch, anchor_mix_invariant_violation además de los 8 drift codes existentes (missing_finding_id, orphan_finding_id, etc.).

--no-enrich (opt-out apropiado para re-renders rápidos, v3.54.0)

Skip explícito de Fase 2.6 (enrichment SimilarWeb + GA4 demographics + PSI/CWV + refdomains-history + PSI client mobile + metrics-snapshot).

Casos de uso:

  • Re-render rápido tras patch manual de insights — querés evitar regenerar la data SW/PSI ya consolidada en el assembled previo.
  • Iteración visual del HTML — no querés esperar ~3-10min de API calls.
  • Debugging del render — Lead va a re-correr varias veces y no quiere burnear quota.

Comportamiento:

  • Sin la flag (default) → Fase 2.6 corre los 6 scripts secuencial. Graceful skip por-script si API key falta.
  • Con la flag → Fase 2.6 entera se saltea + warn [seo-build-report] enrichment skipped via --no-enrich. El assembled queda como esté (presumiblemente ya enriquecido en un run previo, o degradado si nunca corrió enrichment).

NO afecta la skill /seo-diagnostico-full: ese flow invoca /seo-build-report SIN flag y siempre enriquece automáticamente. --no-enrich solo es para invocaciones standalone del Lead a /seo-build-report (re-renders).

--pdf

Si el Lead invoca con --pdf:

PDF export aún no está disponible en este release. El HTML está listo ahora y
te lo genero de todas formas. PDF queda queued para un patch futuro —
va a usar Playwright headless contra el HTML generado.

Mientras tanto, dos workarounds rápidos:
  1. Abrir el HTML en Chrome → Ctrl+P → "Save as PDF"
  2. Esperar el patch (sin ETA comprometido todavía)

¿Arranco con HTML ahora? (S/N)

Si [S] → continuar normalmente (Fase 0). Si [N] → STOP.


Error handling (resumen)

Error Fase Accion
slug/period invalido 0 STOP con regex explicit
cliente-CLAUDE.md ausente 0 STOP — pedir setup del cliente
intelligence obligatorio faltante 0 Warning + continuar (los slices son la fuente primaria)
Required slice missing (incluye action-plan) 1.1 STOP — pedir correr /seo-section-{name} primero
action-plan missing 1.1 STOP — correr /seo-section-action-plan primero (sin bypass; F-07)
content-strategy missing (sin flag) 1.1 Warn ligero + continuar (§11.3 Matriz simplemente no renderiza — opcional Phase 9)
content-strategy missing + --no-content-strategy 1.1 Continuar sin warn (flag confirma intencionalidad)
Stale slice (>30 dias) 1.2 Warn + ask Lead [S]/[N]/[R]
Slice falla validator 1.3 STOP con errors[] — no fixear inline, pedir re-run de la skill afectada
Cross-validator drift critical 1.5 STOP — opciones [R]e-run skill upstream / [E]ditar manual / [F]orzar continuar
Cross-validator drift warning 1.5 Reportar en checkpoint 3 + documentar en actividad.md, NO bloquear
meta.client_name no resoluble 2.4 STOP — el CLI requiere ese campo (exit code 2)
generate-report.js exit != 0 4 Surface stderr + ofrecer inspeccionar $ASSEMBLED_JSON (persistente)
output <50KB o sections <14 4 Warning + ofrecer re-intento
pr-toolkit path no existe 5 STOP con instrucciones (env var o clonar)
pr-toolkit no es git repo 5 STOP
working tree dirty 5 STOP — pedir commit/stash
git commit falla (hook) 5 Surface error, NO hacer --amend
git push auth failure 5 Instrucciones para resolver + commit queda local
Lead reporta URL 404 post-push 5.6 Esperar 60s mas (deploy en curso) o link a vercel.com/puntorojo/pr-toolkit/deployments
Lead reporta URL 500 post-push 5.6 Probable file tracing fallido — revisar next.config.ts > outputFileTracingIncludes
Lead reporta redirect loop post-push 5.6 Middleware matcher problema — revisar middleware.ts

Reglas generales

  • NUNCA inventes datos: si un slice no tiene un campo, propagar { available: false } o array vacio. Solo metadata (nombre, country, URL) puede venir de intelligence files cuando los slices no la cubren.
  • Checkpoints obligatorios (no auto-aprobables): Fase 3 (pre-generate) + Fase 5 (pre-push).
  • Stateless: cada corrida lee los slices fresh desde disco, no cachea.
  • Single-file output: el CLI inlinea CSS + JS — share-able por email sin romperse.
  • Auth GSC/GA4: este skill no llama GSC/GA4. Si un slice trae degradation.{gsc|ga4} con oauth_reauth_required, el reporte degrada esa seccion — el orquestador no redispara reauth.
  • PDF: queued para patch futuro. No prometer timelines.

MCPs utilizados + costos

Este orquestador es file system + 1 CLI call. Solo built-in tools:

  • Read — cliente-CLAUDE, actividad, intelligence files, slices JSON.
  • Bashtest -f, stat, validator inline, mkdir, node CLI, git.
  • Write — assembled JSON persistente al client repo.
  • Edit — actividad.md (prepend entry en Fase 6).

NO usar (toda esa data ya vive en los slices upstream): diagnostic-mcp, memory-mcp, Ahrefs / GSC / GA4 / DataForSEO MCPs, Sheets, ClickUp.

Consumo APIs externas: $0 (0 Ahrefs units, 0 GSC/GA4 calls, 0 DataForSEO units). El consumo real se pago al correr las 7-8 skills /seo-section-* previas. Re-ejecutar este skill cuesta segundos + $0. Modelo Claude tampoco aplica (solo merge/render JSON + templates Nunjucks, sin razonamiento).


Diferencia vs otros skills

Las 8 skills /seo-section-* (Phase 8) producen los slices. Este skill los compone. /seo-reporte hace queries ad-hoc y resuelve findings — son complementarios, no overlapping.

Flujo tipico:

/seo-section-foda → tech-audit → history → ai-audit → competitive → benchmarks
→ context [→ action-plan]
→ /seo-build-report → entregar al cliente

Contexto historico (Phase 8 split): pre-Phase 8 este skill consumia consolidated_json del backend (diagnostic-mcp) + hacia enrichment Ahrefs inline + reproducia logica de /seo-scoring. Ese diseño quedo deprecado: las skills /seo-section-* pre-computan + persisten cada slice, y este orquestador solo compone. La ventaja: re-ejecutar el reporte es $0 APIs externas + segundos, y cada slice se puede regenerar/iterar independientemente.


Context window management

Patrones aplicados (shared/context-engineering.md): Patron 5 (process-and-discard slices despues de leer cada uno) + Patron 6 (backend pre-computation: toda la data pesada ya vive en los slices). Budget total: ~20-25 tool responses, sin riesgo de compactacion.


References

  • CLI: report-builder/generate-report.js (entry point — input/output contract)
  • Sample shape: report-builder/samples/chedraui-sample.json (estructura target del assembled JSON)
  • Slice samples: report-builder/samples/sections/{foda,tech-audit,history,ai-audit,competitive,benchmarks,context}.json
  • Validators (cero deps, validan W3.4 contract):
    • report-builder/lib/foda-validator.jsvalidateFodaSlice
    • report-builder/lib/tech-audit-validator.jsvalidateTechAuditSlice
    • report-builder/lib/history-validator.jsvalidateHistorySlice
    • report-builder/lib/ai-audit-validator.jsvalidateAiAuditSlice
    • report-builder/lib/competitive-validator.jsvalidateCompetitiveSlice
    • report-builder/lib/benchmarks-validator.jsvalidateBenchmarksSlice
    • report-builder/lib/context-validator.jsvalidateContextSlice
    • report-builder/lib/action-plan-validator.jsvalidateActionPlanSlice
    • report-builder/lib/content-strategy-validator.jsvalidateContentStrategySlice (opt., Phase 9)
  • Cross-slice validator (Sprint C Phase 9 — 2026-05-12):
    • report-builder/lib/cross-slice-validator.jsvalidateActionPlanContentStrategyConsistency(actionPlanSlice, contentStrategySlice) — detecta drift entre highlights agregados de sec-16 (source_slice='content-strategy') y items reales del slice content-strategy renderizados en §11.3 Matriz.
    • report-builder/lib/sec16-matrix-builder.jsbuildClusterMatrix(data) — registrado como filter clusterMatrix en generate-report.js. Digiere data.content_strategy.{calendar_*, link_calendar_*} + data.roadmap.* (con cluster_temático) por cluster temático, ordena por priority_score desc, devuelve array de clusters con stages (build/scale/evolve) × disciplines (technical/content/linkbuilding) para que el template renderice §11.3.
  • Schema W3.4: docs/superpowers/specs/schemas/w3.4-diagnostic-data.schema.json
  • Content-strategy schema: skills/seo-content-strategy/schema.json (Phase 9, strict W3.4)
  • /seo-pre-upload-review (skill) — gate de juicio pre-upload (Fase 4.5): 3 reviewers independientes sobre el HTML renderizado. Spec: docs/superpowers/specs/2026-05-28-seo-pre-upload-review-design.md.
  • pr-toolkit repo: C:\Users\nicko\OneDrive\Escritorio\Proyectos\Punto-Rojo\pr-toolkit\ (Next.js app, Vercel auto-deploy)
  • Skills upstream (Phase 8): skills/seo-section-{foda,tech-audit,history,ai-audit,competitive,benchmarks,context,action-plan}/
  • Skill upstream (Phase 9, optional): skills/seo-content-strategy/ v3 — emite content_items + link_items v3 (drip distribution + anchor_mix + outreach_mix + cluster_concurrency_group) que alimentan sec-16 §11.3 Matriz Cluster × Etapa × Disciplinas (v3 redesign 2026-05-12, deprecó la sec-20 standalone)
Install via CLI
npx skills add https://github.com/Nico-puntorojo/punto-rojo-claude-toolkit --skill seo-build-report
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
Nico-puntorojo
Nico-puntorojo Explore all skills →