Elecciones generales el 29 de noviembre

Metodología

Todo con cita. Nada sin página.

Cómo pasamos de un PDF de cien páginas a fichas que se pueden comprobar en diez segundos.

Ahora mismo la web usa los programas de las generales del 23 de julio de 2023 como datos de prueba. Se sustituirán por los de 2026 en cuanto se publiquen.

  1. 1

    Fuente oficial

    Descargamos el PDF del programa de la web del partido y guardamos su URL, la fecha y su huella SHA-256, para que cualquiera pueda comprobar que es el mismo documento. Los enlaces a cada página llevan al PDF en la web del partido; si el partido lo cambia, la huella deja de coincidir y lo revisamos.
  2. 2

    Texto página a página

    Extraemos el texto conservando el número de página real del PDF, que es el que enlazamos (puede no coincidir con el número impreso en el pie).
  3. 3

    Extracción con IA

    Una IA (Claude Code, de Anthropic) recorre el programa tema a tema siguiendo el mismo procedimiento para todos los partidos (publicado abajo): como máximo diez medidas concretas por tema, cada una con resumen en lenguaje llano, cita literal y página. También registra los temas que el programa no menciona.
  4. 4

    Verificación automática de la cita

    Un programa comprueba que cada cita aparece literalmente en la página indicada del PDF. Solo se toleran diferencias de maquetación: espacios, saltos de línea, comillas tipográficas y palabras partidas con guion. Si la cita no aparece, la ficha se descarta. Esta comprobación se repite en cada publicación de la web.
  5. 5

    Segunda revisión independiente

    Otra sesión de IA, sin acceso al trabajo de la primera, revisa que cada resumen sea fiel a la cita, neutral y sin adjetivos, que sea una propuesta concreta y que no haya duplicados. Las fichas que no pasan quedan rechazadas con su motivo. Se aplica una única ronda de corrección, igual para todos los partidos, y una revisión final. Por último, una pasada de coherencia aplica a los cuatro partidos a la vez los criterios en los que las revisiones habían discrepado (registrada en el repositorio).
  6. 6

    Publicación

    Solo se publican las fichas que superan las dos comprobaciones. La web se genera de forma estática a partir de esos datos y falla si alguno no cumple el formato.

Programas procesados

Partido Popular

Fichas publicadas
125
Borradores extraídos
132
Descartadas por la cita
0
Rechazadas en revisión
7
Páginas del PDF
112

SHA-256 c93baafc88bdcb33adcd7e8be9dc309b09056362183ad7b7767f3d5134313108

PDF oficial

Partido Socialista Obrero Español

Fichas publicadas
138
Borradores extraídos
147
Descartadas por la cita
0
Rechazadas en revisión
9
Páginas del PDF
272

SHA-256 02d4972a848250fec9b8b2cffc5aaa78733acb0ac010e79ce1787161f91a42f2

PDF oficial

VOX

Fichas publicadas
124
Borradores extraídos
136
Descartadas por la cita
0
Rechazadas en revisión
12
Páginas del PDF
178

SHA-256 4573922e4bf755a428cdd528662c67dd71842317595e7d9a5bbe6b5886507d31

PDF oficial

SUMAR

Fichas publicadas
124
Borradores extraídos
148
Descartadas por la cita
0
Rechazadas en revisión
24
Páginas del PDF
182

SHA-256 d5ccdac0d20f1b93a5f703f180d086041bd988835072dd5cb5d1378bf0195820

PDF oficial

Reglas de neutralidad

  • Mismo procedimiento, mismos temas, mismo límite de fichas y de caracteres para todos los partidos.
  • Los resúmenes describen la medida; no opinan sobre su coste, viabilidad o conveniencia.
  • Los partidos se ordenan por escaños en la legislatura anterior; en el test y el juego, al azar.
  • Cada partido aparece con su color y sus siglas, con el mismo tamaño y en la misma posición.
  • El test a ciegas no muestra colores ni nombres hasta el resultado.
  • En el test, el porcentaje de cada partido es el de coincidencias entre tus respuestas "de acuerdo" y "en desacuerdo" sobre las que ese partido se pronuncia en su programa. Si no dice nada sobre una afirmación, esa respuesta no cuenta para él, ni a favor ni en contra.

Las instrucciones exactas

Estos son los procedimientos (skills) que sigue la IA, tal cual. Son los mismos para todos los partidos.

/extraer-programa
---
name: extraer-programa
description: Extrae fichas de propuestas (resumen, cita literal, página, subtema, perfiles) del texto paginado de un programa electoral y las escribe como borradores JSON en /pipeline/drafts/<partido>/. Uso - /extraer-programa <partido> [tema…]
---

# /extraer-programa `<partido>` `[tema…]`

Convierte el texto paginado de un programa electoral en borradores de fichas.
Se aplica **exactamente igual a todos los partidos**: mismas reglas, mismos
temas, mismo límite de fichas y de caracteres. Este fichero se publica en la
página de metodología.

Si se pasan temas, procesa solo esos (sirve para repartir el trabajo entre
varias sesiones); si no, procesa todos los de `data/topics.json`.

## Entradas

- `pipeline/text/<partido>/programa.txt`: texto completo con marcadores
  `=== PÁGINA N ===`. **N es la página real del PDF** (la que se enlaza), no
  el número impreso en el pie.
- `pipeline/text/<partido>/pages.json`: el mismo texto como `[{page, text}]`.
- `data/topics.json` y `data/profiles.json`: temas y perfiles válidos.

Si no existe el texto, ejecuta antes
`pnpm --filter @p29n/pipeline text <partido>`.

## Procedimiento

1. **Localiza el tema.** Lee el índice (primeras páginas) para saber qué
   capítulos tratan cada tema. Después busca en `programa.txt` con palabras
   clave del tema (Grep), porque las medidas de un tema pueden estar repartidas
   en varios capítulos. Lee **enteras** las páginas relevantes antes de elegir.
2. **Elige las propuestas.** Una propuesta es una medida concreta que el
   partido se compromete a adoptar ("crearemos", "bajaremos", "aprobaremos una
   ley…"). No son propuestas los diagnósticos, las críticas a otros partidos,
   las declaraciones de valores ni los objetivos genéricos sin medida
   ("apostamos por una sanidad de calidad").
   - **Máximo 10 fichas por tema y partido.** Si el programa tiene más
     medidas, prioriza en este orden: (a) las más concretas o cuantificadas
     (cifras, plazos, leyes con nombre), (b) las que afectan de forma directa a
     la ciudadanía, (c) variedad de subtemas. Evita medidas casi iguales.
   - Si el programa trata el tema con pocas medidas, extrae las que haya; no
     rellenes con medidas de otro tema.
3. **Escribe cada ficha:**
   - `quote` — **cita literal** del programa, copiada carácter a carácter del
     texto de **una sola página**: mismas palabras, tildes, mayúsculas y
     puntuación. Puedes sustituir saltos de línea por espacios y unir las
     palabras partidas con guion al final de línea ("adqui-\nsición" →
     "adquisición"). Nada más: sin "[…]", sin omitir palabras, sin corregir
     erratas, sin viñetas ni números de página. Entre 25 y 700 caracteres;
     preferiblemente una o dos frases completas que contengan la medida. Si la
     medida continúa en la página siguiente, cita solo la parte de una página.
   - `page` — la N del marcador `=== PÁGINA N ===` donde está la cita.
   - `summary` — la medida en lenguaje llano, **máximo 200 caracteres**
     (objetivo: menos de 160). Empieza por un verbo en infinitivo ("Eliminar
     el IVA en la compra de la primera vivienda habitual."). Reglas de
     neutralidad: describe la medida y nada más; sin adjetivos valorativos
     ("histórico", "ambicioso", "polémico", "necesario"…), sin opinar sobre
     su coste, viabilidad o conveniencia, sin nombrar al partido ni a sus
     líderes y sin añadir datos que no estén en la cita.
   - `subtopic` — etiqueta corta en minúsculas (2-40 caracteres) que agrupa la
     medida dentro del tema: "alquiler", "smi", "listas de espera"…
   - `profiles` — ids de `data/profiles.json` a los que la medida afecta **de
     forma directa** (p. ej. una ayuda al alquiler → `inquilino`). Vacío si no
     afecta claramente a ninguno.
4. **Cobertura.** Si el programa no menciona el tema en absoluto, escribe el
   fichero con `"mentioned": false` y `"proposals": []`. Si lo menciona pero
   sin medidas concretas, `"mentioned": true` y `"proposals": []`.
5. **Guarda** un fichero por tema en `pipeline/drafts/<partido>/<tema>.json`:

   ```json
   {
     "partyId": "<partido>",
     "topicId": "<tema>",
     "mentioned": true,
     "proposals": [
       {
         "summary": "…",
         "quote": "…",
         "page": 40,
         "subtopic": "…",
         "profiles": ["inquilino"]
       }
     ]
   }
   ```

6. **Verifica.** Ejecuta `pnpm --filter @p29n/pipeline verify <partido>`.
   Para cada cita rechazada, vuelve a copiarla de la página correcta y repite.
   Si no consigues que una cita supere la verificación, elimina la ficha. No
   cambies nunca el texto de una cita para que "encaje": la cita debe salir
   del programa.

## Ronda única de corrección (después de /revisar-fichas)

Se hace **una sola vez y para todos los partidos por igual**. Ejecuta
`pnpm --filter @p29n/pipeline rechazos <partido>` y lee
`pipeline/reviews/_rechazos/<partido>.json`. Para cada ficha rechazada,
según el motivo:

- **Resumen no fiel, no neutral o de formato:** reescribe solo el `summary`
  en su fichero de borrador, corrigiendo exactamente lo que señala el motivo
  (conserva condiciones y el grado de compromiso de la cita, usa verbos
  descriptivos). No toques la cita.
- **No es una propuesta concreta:** elimina la ficha. Si el tema queda con
  menos de 10, puedes sustituirla por otra medida concreta del mismo tema
  siguiendo el procedimiento normal.
- **Tema incorrecto:** muévela al fichero del tema indicado (si ese tema
  ya tiene 10, elimínala).
- **Duplicada:** elimina la ficha rechazada.

Después ejecuta `verify` y deja todas las citas verificadas. La revisión
siguiente (`/revisar-fichas`) se hace de nuevo sobre todas las fichas del
partido, en una sesión nueva; lo que se rechace entonces queda rechazado.

## Reparto de temas

Para que la misma medida no acabe en dos temas:

| Si la medida trata de… | va en |
| --- | --- |
| alquiler, compra de vivienda, okupación, suelo, vivienda pública | `vivienda` |
| SMI, contratos, desempleo, jornada, autónomos (laboral), conciliación laboral | `empleo` |
| IRPF, IVA, sociedades, patrimonio, sucesiones, deducciones, fraude fiscal | `impuestos` |
| sistema de pensiones, jubilación, revalorización | `pensiones` |
| sanidad pública, listas de espera, salud mental, farmacia, dependencia sanitaria | `sanidad` |
| escuela, universidad, FP, becas, lenguas vehiculares en la escuela | `educacion` |
| fronteras, regularización, asilo, integración, nacionalidad | `inmigracion` |
| energía, renovables, nuclear, emisiones, agua, medio ambiente | `energia-clima` |
| igualdad de género, violencia de género, LGTBI, discapacidad, familia y natalidad | `igualdad` |
| poder judicial, policía, terrorismo, okupación penal, prisiones, corrupción | `justicia-seguridad` |
| autonomías, financiación autonómica, Cataluña, lenguas cooficiales, Constitución | `modelo-territorial` |
| UE, OTAN, gasto en defensa, cooperación, relaciones internacionales | `exterior-defensa` |
| agricultura, ganadería, pesca, despoblación, mundo rural | `campo-rural` |
| I+D+i, ciencia, digitalización, IA, telecomunicaciones | `ciencia-digital` |
| cultura, patrimonio, deporte, medios de comunicación, toros | `cultura` |

Si una medida encaja en dos temas, elige el más específico y no la dupliques.
/revisar-fichas
---
name: revisar-fichas
description: Segunda pasada independiente sobre las fichas cuya cita ya superó la verificación automática. Comprueba fidelidad, neutralidad y duplicados y escribe /pipeline/reviews/<partido>.json. Uso - /revisar-fichas <partido>
---

# /revisar-fichas `<partido>`

Revisión independiente. Debe ejecutarse en una **sesión nueva, sin el
contexto de la extracción**: no leas los borradores originales ni el
razonamiento de quien los escribió; solo la entrada que se indica abajo.
Se aplica igual a todos los partidos. Este fichero se publica en la página de
metodología.

## Entrada

1. Ejecuta `pnpm --filter @p29n/pipeline review-input <partido>`.
2. Lee `pipeline/reviews/_input/<partido>.json`. Cada elemento tiene `id`,
   `topic`, `subtopic`, `summary`, `quote`, `page`, `profiles` y, a veces,
   `notes` con avisos automáticos (p. ej. posible duplicado).

Todas las citas de la entrada ya aparecen literalmente en el PDF; no hace
falta comprobarlo otra vez.

## Qué comprobar en cada ficha

Marca `"verdict": "reject"` con uno o más motivos si ocurre cualquiera de
estas cosas; si no, `"verdict": "ok"`.

1. **Fidelidad.** El resumen dice lo mismo que la cita: no añade datos,
   cifras, plazos ni matices que la cita no contenga, no omite una condición
   esencial y no cambia el sentido. Motivo: `"resumen no fiel: …"`.
2. **Neutralidad.** El resumen describe la medida sin valorarla: sin
   adjetivos valorativos ni enfáticos, sin opinión sobre su coste,
   viabilidad o conveniencia, sin el nombre del partido ni de sus líderes y
   sin el tono propagandístico del original. Motivo: `"resumen no neutral: …"`.
3. **Es una propuesta.** La cita contiene una medida concreta que el partido
   se compromete a adoptar, no un diagnóstico, una crítica o una declaración
   genérica de intenciones. Motivo: `"no es una propuesta concreta"`.
4. **Formato.** Resumen de 200 caracteres como máximo, en castellano
   correcto, que se entienda sin leer la cita. Motivo: `"formato: …"`.
5. **Tema.** La ficha pertenece claramente al tema indicado. Motivo:
   `"tema incorrecto: debería ir en …"`.
6. **Duplicados.** Si dos fichas recogen la misma medida, aprueba la más
   completa y rechaza la otra con `"duplicateOf": "<id de la que se queda>"` y
   motivo `"duplicada"`.

Los perfiles (`profiles`) no son motivo de rechazo.

### Criterios de calibración (iguales para todos los partidos)

- "Impulsar", "fomentar", "reforzar" o "apoyar" X **pasa** si X es un
  instrumento, programa, organismo, tecnología o norma concretos (un plan con
  nombre, una agencia, una deducción, un tipo de energía); **se rechaza** si X
  es solo un objetivo genérico sin medida ("reforzar las políticas de…").
- Comprometerse solo a "estudiar", "evaluar", "analizar" o "abrir un debate"
  **se rechaza**, salvo que la cita fije una medida concreta como objetivo del
  estudio (p. ej. "estudiar ampliar hasta un 15 % las plazas").
- Exigir a otros que actúen, o describir medidas ya adoptadas, no es una
  propuesta.
- Las posiciones de política exterior explícitas (reconocer, apoyar o
  rechazar algo concreto) **pasan** como propuestas.
- El resumen puede omitir una cláusula de procedimiento no esencial ("previa
  consulta a las CCAA"), pero no una condición que limite el alcance
  ("dentro de las posibilidades existentes", "en su caso"), ni cambiar el
  grado del compromiso ("defenderemos" ≠ "garantizar").
- **Grado de compromiso (regla única).** Un programa dice lo que el partido
  haría si gobernase. "Impulsaremos / promoveremos / propondremos /
  apostaremos por" + una medida que adoptan el Gobierno o las Cortes
  (aprobar una ley, crear un plan, un organismo, una deducción, reformar una
  norma estatal) **puede** resumirse con el verbo directo ("Aprobar una ley
  de…", "Crear…"). **Debe** conservarse el verbo matizado ("Impulsar",
  "Promover", "Defender", "Pedir") cuando la medida depende de actores que
  el Gobierno no controla (UE, OTAN, ONU, otros Estados, comunidades
  autónomas o ayuntamientos en sus competencias) y cuando la cita la
  presenta como objetivo o aspiración ("avanzar hacia", "trabajar por",
  "el objetivo es", "defenderemos", "mientras sea necesario").
- Copiar en el resumen verbos o adjetivos enfáticos del original
  ("blindar", "histórico", "decidido") es **no neutral**: el resumen debe usar
  verbos descriptivos ("incluir en la Constitución", "proteger por ley").
- Diferencias de matiz con carga política cuentan como infidelidad
  ("financiadas con fondos públicos" ≠ "públicas").

Sé estricto pero no arbitrario: el criterio debe ser el mismo para todas las
fichas y todos los partidos. Ante la duda sobre neutralidad o fidelidad,
rechaza.

## Salida

Escribe `pipeline/reviews/<partido>.json` con **una entrada por cada ficha**
de la entrada:

```json
{
  "partyId": "<partido>",
  "reviewedAt": "AAAA-MM-DD",
  "entries": [
    { "id": "pp-0123456789", "verdict": "ok", "reasons": [] },
    { "id": "pp-abcdef0123", "verdict": "reject", "reasons": ["resumen no neutral: 'ambicioso plan'"] },
    { "id": "pp-9876543210", "verdict": "reject", "reasons": ["duplicada"], "duplicateOf": "pp-0123456789" }
  ]
}
```

Después ejecuta `pnpm --filter @p29n/pipeline publicar <partido>`, que aprueba
automáticamente las fichas con cita verificada y `"ok"`, rechaza el resto con
su motivo (`pipeline/reports/<partido>.json`) y actualiza `/data`.
/generar-test
---
name: generar-test
description: Genera las afirmaciones del test a ciegas a partir de fichas aprobadas, equilibradas por partido y tema y sin pistas del partido. Escribe /pipeline/drafts/_test/statements.json y las verifica. Uso - /generar-test
---

# /generar-test

Construye el banco de afirmaciones del test a ciegas. Solo usa fichas
aprobadas de `data/proposals.json`. Se publica en la página de metodología.

## Reglas

1. **Banco.** Entre 24 y 36 afirmaciones; el test muestra `length` (12 por
   defecto) elegidas al azar repartiendo temas. Cubre al menos 10 temas
   distintos y no más de 3 afirmaciones por tema.
2. **Cada afirmación** es una medida concreta reescrita como frase que se
   pueda apoyar o rechazar: "Hay que eliminar el IVA en la compra de la primera
   vivienda." Máximo 220 caracteres, en lenguaje llano.
3. **Sin pistas.** Nada de nombres de partidos, siglas, líderes, lemas ni
   vocabulario que delate a un partido (por ejemplo, términos que solo usa uno
   de ellos). Formula la medida con palabras neutras.
4. **Posiciones** (`partyPositions`) para **todos** los partidos de
   `data/parties.json`:
   - `1` (a favor): el programa del partido propone esa medida o una
     equivalente. Debe haber al menos una ficha de ese partido en
     `proposalIds` que lo muestre.
   - `-1` (en contra): el programa propone explícitamente lo contrario o una
     medida incompatible. Debe haber al menos una ficha de ese partido en
     `proposalIds` que lo muestre.
   - `0` (no se pronuncia): el programa no dice nada claro sobre ello. En ese
     caso no cites fichas de ese partido.
   Nunca deduzcas una posición de lo que el partido haya dicho fuera del
   programa ni de su ideología: solo de sus fichas aprobadas.
5. **Equilibrio.** Cada partido debe tener posiciones respaldadas por fichas
   propias en un número parecido de afirmaciones, y las afirmaciones deben
   salir de propuestas de todos los partidos, no solo de uno. Evita que un
   partido esté a favor de todas o en contra de todas.
6. **Ids** estables en minúsculas con guiones (`vivienda-iva-primera`), que
   no cambien si se regenera el banco con la misma afirmación.

## Salida

`pipeline/drafts/_test/statements.json`:

```json
{
  "length": 12,
  "statements": [
    {
      "id": "vivienda-iva-primera",
      "text": "…",
      "topicId": "vivienda",
      "proposalIds": ["pp-0123456789"],
      "partyPositions": { "pp": 1, "psoe": 0, "vox": 1, "sumar": 0 }
    }
  ]
}
```

Después ejecuta `pnpm --filter @p29n/pipeline test-verify`. La verificación
comprueba esquema, que las fichas existan y estén aprobadas, que cada posición
distinta de 0 esté respaldada por una ficha de ese partido, que ninguna ficha
citada sea de un partido marcado con 0, que el texto no delate al partido y el
equilibrio entre partidos. Corrige y repite hasta que pase; solo entonces se
escribe `data/test.json`.
/verificar-bulo
---
name: verificar-bulo
description: (v2) Genera la página estática de un bulo frecuente a partir de las preguntas de /pipeline/hoaxes-inbox, con veredicto, explicación y citas de fichas aprobadas. Uso - /verificar-bulo [fichero]
---

# /verificar-bulo `[fichero]`

> **v2.** Este skill forma parte de la sección de bulos, que llega con la v2.
> Se deja definido para que el procedimiento sea público desde el principio.

Para cada pregunta frecuente de `pipeline/hoaxes-inbox/*.json`:

1. Identifica la afirmación que circula y el partido al que se atribuye.
2. Busca en `data/proposals.json` las fichas **aprobadas** de ese partido que
   tratan el asunto. Solo cuenta lo que dice el programa oficial: no mítines,
   entrevistas ni opiniones, y no valores si la medida es buena.
3. Veredicto:
   - `si`: alguna ficha recoge esa medida tal como se afirma.
   - `no`: las fichas dicen otra cosa o el programa no lo menciona (en ese
     caso `proposalIds` queda vacío y la explicación lo dice).
   - `matices`: el programa recoge algo parecido pero con diferencias
     relevantes (cuantía, plazo, condiciones).
4. Explicación neutral de 400 caracteres como máximo.
5. Añade el bulo a `data/hoaxes.json` (`slug`, `claim`, `partyId`,
   `verdict`, `explanation`, `proposalIds`, `publishedAt`) y ejecuta
   `pnpm --filter @p29n/pipeline validate`. Solo se publica si todas sus
   citas superan la verificación automática.

Errores y correcciones

Si ves un error, escríbenos a elecciones29@proton.me. Las correcciones se publican con fecha en este registro.

# Registro de correcciones

Cada corrección de una ficha publicada se anota aquí con fecha, identificador
de la ficha y qué cambió.

- 2026-10-14 · Publicación inicial de los datos de prueba (programas de 2023).

Quién está detrás

Un proyecto independiente, sin financiación de partidos. El código, los datos y estas instrucciones son públicos. Versión de datos: 2023-prueba.