ソース情報
- リポジトリ
- Ntizar/MasterMind
- ソースの最終更新活動
- 2026年9月8日 14:54
- 検出された SKILL.md の言語
- スペイン語
- スター
- 2
- フォーク
- 0
インストール方法
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
ソースファイルを確認
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
メニュー
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/Ntizar/MasterMind --skill rail-accident-visorsコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
SKILL.md を表示中
Restore Hermes from a backup repo or migrate machines.
Usa al analizar ADN crudo: ascendencia, rasgos, sexo.
Usar al operar Mastermind en Windows: repo, ChromaDB, crons, gateway Telegram (colapso por token revocado y su restauración), backups y monitorización.
| name | rail-accident-visors |
| description | Use when building train-accident visors (ERA/eRAIL, CIAF). |
| version | 1.0.0 |
| tags | ["railways","accidents","era","erail","adif","geolocation","visor","pipeline"] |
Patrones verificados en CIAF-visor y era-visor (Ntizar). Para scraping de fuentes
gubernamentales españolas en general y parsing PDF ver el skill
government-data-pipelines (user-owned: leer, no editar) — este skill añade lo
específico del dominio ferroviario europeo y las preferencias de diseño del usuario.
scrape_pais.py — descubre informes en ERA. Listado: https://www.era.europa.eu/era-folder/{COD}-investigations; páginas de año /era-folder/{YYYY}-{N}. Nunca rel=next (el book Drupal global cuela otros países).descargar_pdfs.py — cortesía 8s; ante 429 backoff 120s×intento. 374 PDFs ES, 0 fallos.extraer_pais.py — PDF→md con PyMuPDF; los escaneados quedan "pendiente OCR" y se contabilizan (no se inventa texto).estructurar_pais.py — md→json con qwen (NaN API): prompt anti-invención, víctimas SIEMPRE del Excel eRAIL.enriquecer_ia.py — segunda pasada LLM añade taxonomía v2 (subsistema, ATP ASFA/ERTMS/LZB, tipo_red, explotación, precursores, mitigaciones, factores humanos, meteorología). Lee el .md completo (cabeza 8000+cola 8000).geocodificar_via.py — PK+línea → punto SOBRE la vía (ver abajo).revisar_localizacion.py — auditoría estricta: (a) distancia GEOMÉTRICA a las
polilíneas de los tramos ADIF (no al marcador PKTeoricos más cercano — los
marcadores son escasos: portal de túnel, tramo largo… y provocan falsos dudosos;
caso real Álora 111/2024: coord a 0 m de la línea 030 declarada pero a 501 m del
marcador de su tramo → "duda" falsa; el proxy de marcadores se conserva solo como
diagnóstico dist_via_m) Y (b) cruce PK↔línea: el veredicto lo manda la
distancia geométrica a la línea DECLARADA — si cae sobre otra vía, será grande.
(c) nodo de estación ≠ otra vía: la geometría de tramos solo tiene el EJE en
línea, no el manojo de vías del recinto. metodo_geo=estacion_ign + vía más
cercana = línea declarada + d ≤ ESTACION_RADIO (1200 m) → 'bien' (caso Manresa
70/2022: 604 m de SU línea 220, dg == dgl — el motivo 'OTRA vía' era mentira).
Señal de falsos positivos del auditor: dist_via_geo_m == dist_linea_geo_m. Solo (a) es ciego al peor bug posible — Caleyo
(Oviedo) caía sobre otra vía lejana y el auditor lo marcaba "bien" durante ciclos
enteros. Además: provincia declarada vs INE (con alias de cabecera Oviedo→Asturias,
Santander→Cantabria y denominaciones antiguas Gerona→Girona, Lerida→Lleida; y
candidatas múltiples si raíz y ubicacion declaran líneas distintas). Salida
data/revision/.
PITFALL "misma_linea" demasiado estricto (2026-09): un punto por ESTACIÓN puede ser
correcto estando sobre la red, aunque la distancia a la línea DECLARADA sea grande. Si la
línea declarada es un ramal interno o se trunca en ADIF, dgl engaña: Salou (pk 263 línea
600 — ADIF la trunca en pk 254) o Zaragoza-Delicias (ramal 060 sin geometría) se marcaban
"duda" con el punto correcto a 11 m de la vía real. Regla fijada: si metodo_geo es
estacion_* y dg (dist a la vía real MÁS CERCANA) ≤ ESTACION_RADIO → "bien", aunque
dgl sea grande. El matcher estricto de estación garantiza que el punto por estación es el
correcto por construcción; el cruce de línea declarado pasa a ser una etiqueta, no un error.revisar_json.py — revisor IA: revalida cada json contra su .md y corrige campos mal interpretados en sitio, con log de cambios. Determinista primero (fechas vacías, formatos), LLM después.consolidar.py — json/*→data/db/ con dedupe BIDIRECCIONAL por expediente: un solo
registro por expediente; gana quien tenga análisis (v3/hechos), empate → CIAF; el
perdedor aporta los campos que falten (lista amplia: v2, resumen, causa, tags, trenes,
entidades, geo...). El dedupe de un solo sentido ("CIAF gana si llega después") deja
duplicados cuando el orden de ficheros varía — 71 duplicados en producción.
Propagar metodo_geo al registro final. CRÍTICO — orden de escritura: cualquier
normalización de campos DEBE ejecutarse ANTES de write_text(reports/XX.json);
normalizar después de escribir no cambia nada en disco (bug real que costó varias
rondas de debugging).
CRÍTICO — index fantasma: el merge del index.json NO puede ser acumulativo
(merged = index + overlay). Tras archivar duplicados, el país re-consolidado hay
que REEMPLAZARLO entero (conservar solo claves de OTROS países) o el índice conserva
registros muertos — 619 entradas para 349 informes en producción, y el frontend los
renderiza con strings 'None'. Verificar siempre len(index) == nº de informes.
Autoridad de campos: las consecuencias del análisis v3 MANDAN sobre el JSON
base (fallecidos, heridos_graves, heridos_leves) para TODOS los registros —
la primera pasada sistemáticamente deja víctimas a 0 en informes recientes y el
usuario lo detecta en el gráfico de evolución ("seguro que no hay fallecidos desde
2014?"). Igual criterio: fecha = fecha del SUCESO, no del informe ni del
expediente ("lo importante de la fecha es cuando ocurre el siniestro").
Prioridad geo: en pk/linea/provincia, el bloque ubicacion verificado por
el geocodificador MANDA sobre los campos raíz del LLM (raíz tenía '244' erróneo y
ubicacion '422' correcto en Pedrera; al revés que las víctimas, donde v3 manda).extraer_completo.py — extracción v3 profunda (ver references/extraccion-v3.md): análisis IA de TODO el informe → cronología minuto a minuto, infraestructura, personal, causas (directa/contribuyentes/sistémicas), lecciones, recomendaciones con destinatario, idioma_original. Antes: limpiar_md() elimina el índice del .md (líneas con puntos de relleno ..... 12) — sin esto el índice acaba colado en el campo "descripción" y el usuario lo detecta al instante.verificar_todo.py — comprobación integral reutilizable (pdfs↔md↔json↔DB, dupes md5, magic bytes, fantasmas, 1-registro-por-expediente, coords gemelas, veredictos del auditor, cache-busting) → data/revision/XX-verificacion.md. --limpiar archiva duplicados md5 a _duplicados_descartados/; exit ≠ 0 si hay ERROR (apto como gate en cron). Su 1ª pasada encontró 3 duplicados que la limpieza manual previa dejó pasar — la comprobación debe ser script fija, no lista ad hoc por sesión. Ver references/comprobacion-integral.md.ORDEN crítico de la fase 8-11 (ida y vuelta obligatoria): el auditor revisar_localizacion.py lee las coords de la DB generada → debe correr DESPUÉS de consolidar.py; y consolidar.py propaga geo_veredicto/geo_motivo del auditor a cada registro → tras consolidar hay que re-auditar y re-consolidar hasta que las cifras del verificador cuadren. Secuencia estable: geocodificar → consolidar → auditar → consolidar → verificar_todo.
"Hay incidentes con mucha información y otros con muy poca o desordenada. Si no somos
capaces de hacerlo bien para España no seremos capaces para Europa." El schema mínimo
(tipo/fecha/pk/resumen) NO basta: cada registro debe llevar la extracción v3 completa.
Títulos en inglés sin normalizar también rechazados → titulo_normalizado en castellano
idioma_original. Ver references/extraccion-v3.md para el schema y los pitfalls de
la API (formato vs llaves, tokens de razonamiento, parseo tolerante).https://ideadif.adif.es/gservices/Tramificacion/wfs — capas
Tramificacion:PKTeoricos (~17.200 puntos sobre vía, con codtramo, pk,
id_provinc) y Tramificacion:TramosServicio (1.178 tramos: cod_eje,
pki/pkd, geometría). Descargar ambos a disco (~20 MB), trabajar local.cod_eje cuyo rango
pki-pkd contenga el PK → interpolar sobre la geometría. Cadena: PKTeoricos
exacto → interpolación → estación IGN → Nominatim.codtramo ADIF es
eje(2)+línea(3)+seq(4) (9 dígitos) — el código de línea CIAF vive en
codtramo[2:5], NUNCA en codtramo[:3]. Indexar tramos/pk por [:3] hace que
la línea 130 (Gijón–Venta de Baños) case con el eje '061' (Teruel–Sagunt) y
accidentes asturianos aterricen en Guadalajara. Al buscar por línea, usar el
prefijo numérico de cod_linea ('130-Gijón...' → '130') Y codtramo[2:5] como
claves del índice (un tramo puede pertenecer a ambas vistas).idp=0: resolver su provincia
vía texto provincia del tramo (con alias) o, en PKTeoricos, vía su codtramo →
tramo → provincia.geocodificar_via.py el fallback «línea+PK ignorando provincia» vive en una cadena
if/elif y, si queda detrás del elif que consume el fallo con provincia, es
INALCANZABLE — el script sale 0 y la coord ni se mueve (silencioso). Después de
tocar la cadena, verificar que la coord del registro objetivo CAMBIÓ, no solo el
exit code. Caso real 64/2012: el CIAF declara «Huesca» pero Villanueva de Gállego
es ZARAGOZA; los tramos 200 de Huesca no cubren el PK 25 y solo el match sin
provincia clava el punto correcto (interpolación sobre el tramo de la 200).
La provincia de un informe CIAF puede ser simplemente errónea: ante conflicto
provincia-vs-geometría, manda la línea+PK.parse_linea devuelve None → el geocodificador no puede casar por línea y caía al
fallback por provincia, aterrizando en OTRA línea (caso 34/2007 → pk 244,350 cruzado
a la línea 200 en Zaragoza). Fix: si parse_linea no da código, recuperarlo del
texto del informe (erail Location name/título/resumen) con regex
\(\s*(\d{3})\s*[A-Za-z] ('(600 Valencia-... railway line)') → interpolación por
línea correcta.id_provinc=0 (provincia sin resolver en INE). El
filtro if idps and to_int(idp) not in idps: continue los EXCLUÍa TODOS → match
vacío → se conservaba la coord previa stale/errónea (34/2007 en Zaragoza). Fix:
solo excluir provincias CONOCIDAS distintas — if idp not in (None,0) and idp not in idps: continue.
Así id_provinc=0 (válido) deja de ser descartado y el PK correcto de la línea entra.estacion explícito o el patrón de ubicación oficial del eRail Location name
("in the vicinity of X" / "en la estación de X"), con match estricto por PALABRA COMPLETA
(no substring) y desempate por el patrón de ubicación, no por longitud del nombre.
Matiz de precisión: cuando el informe tiene PK+línea resoluble, la interpolación sobre
la vía GANA — David eligió "precisión sobre la vía" para el 34/2007 aun estando el punto a
~17 km de la estación nombrada; el snap a estación es el fallback/cuando la ubica claramente.metodo_geo="poblacion" es legítimo → hay que enseñárselo al AUDITOR (2026-09). Cuando
la línea declarada NO existe en la geometría ADIF (p.ej. la CIAF escribe "510 Aljucén-Cáceres"
pero ADIF solo conserva 1 tramo de esa línea en otra zona, pk 140-142) y la estación no está
mapeada (Carmonita ni en el cache IGN de 2.000 ni en OSM), el punto correcto es la POBLACIÓN
declarada (regla de David): geocodificar el municipio vía Nominatim y escribir
metodo_geo="poblacion". OJO: el auditor revisar_localizacion.py mide distancia a la línea
y marca esos puntos como "mal" porque un centro urbano no cae sobre un raíl — hay que añadir una
regla al auditor (if metodo_geo.startswith("poblacion"): veredicto="bien", con motivo
"ubicación por población declarada; sin geometría de línea en ADIF"). Sin eso, el informe de
verificación listará la población como error indefinidamente. NO "snap a la línea ADIF más
cercana" — junto a Carmonita la ADIF más cercana es la 026 Plasencia, NO la 510: el pin dejaría
de estar en la localidad declarada y seguiría "mal" (además en la vía equivocada). Errores cross
reales fijados así esta sesión: 49/2010→Carmonita (estaba en Zafra a ~55 km) y 4/2016→Elx-Parc
(estaba en Sabadell-Parc del Nord/Barcelona por falso match de substring "Parc").idps_de y puntos_pk están
ANIDADAS en main() — al importar geocodificar_via no se exponen; g.idps_de(...)
devuelve None y enmascara que idps en realidad es [43] (Perdí 2 rondas de repro por
esto). Para reproducir la lógica hay que replicar idps_de inline. El observador DEFINITIVO
de un cross-line es inspeccionar los PKT (o tramos) candidatos con pk±tol y ver su
id_provinc/codtramo[2:5] — un punto de la línea correcta puede venir con id_provinc=0
y es justo el que el filtro excluía (caso 34/2007).[+,./] — la barra / es notación CIAF (124/573 = 124,573 km)
y sin ella ~14 informes por tanda se quedan sin geocodificar. Anclar con
(?!\d) tras los decimales, no con $ (los PK suelen llevar texto detrás).geocodificar_estacion.py): dataset
RedFerrocarrilesIGN FeatureServer (services1.arcgis.com/nCKYwcSONQTkPA4K,
~3.000 estaciones, CC-BY) para informes sin PK casable pero con estación.
Pitfall matcher: NO casar por contención de substring (n_est in e["norm"])
ni elegir el primer candidato — "León" cae dentro de nombres ajenos y todo el
lote acaba en un mismo punto falso. Reglas: coincidencia exacta, o contención
de PALABRA COMPLETA (len>=4, todas las palabras presentes), desempate por
provincia INE, y ABSTENERSE si queda más de un candidato (mejor sin coordenada
que mal puesta; metodo_geo="estacion_ign" para poder deshacer por clase).
Cache del dataset: copiar las 2 páginas del FeatureServer a data/ign-estaciones*.json
y leer de ahí — si solo viven en Temp, se pierden entre sesiones y el script
muere (el arreglo de Caleyo se bloqueó una sesión entera por esto).references/comprobacion-integral.md.mal del auditor se clasifican solos: coord fuera de vía real vs "pegado a
vía pero línea no cuadra" (PK ambiguo entre líneas / error de dígito del LLM en la
línea raíz). Para cada mal, mirar las líneas de los tramos a <1 km de la coord —
si una es pariente de la declarada (244 vs 422 Pedrera: transposición), es ruido del
auditor o del parser, no un re-geocodificado. No re-geocodificar a ciegas.060 Bifurcación Cambiador Zaragoza-Delicias… a veces NO tienen
tramo en adif-tramos.geojson; el punto correcto (el cambiador, a 11 m del recodo
enlazado a la 200) sale como 'OTRA vía' porque la línea declarada está a 1.4 km.
Señal: distancia global tiny (≤50 m) y la línea cercana es la matriz de la declarada
→ ubicación BUENA; documentarlo en fuente_geo del JSON, no mover la coord.son pocos, puedes comprobarlo — el usuario lo exige así):
por registro (1) abrir su JSON y el PDF fuente (PyMuPDF fitz si pypdf falla) y sacar
textualmente municipio, origen-destino del tren y PK; (2) localizar el punto EXACTO en
OpenStreetMap vía Overpass (nwr["railway"~"station|halt"]["name"~"…",i](bbox) con
urllib POST, o Nominatim para localidades) — el name del nodo confirmado en la
respuesta cuenta como verificación; (3) escribir lat/lng en el JSON con
metodo_geo="estacion_adif" y fuente_geo citando el node/<id> OSM (trazable,
CC BY 4.0); (4) relanzar revisar→consolidar→verificar y contestar con
zona+origen/destino+PK+coordenada POR CASO, no con estadísticas del lote. NUNCA
escribir una coord de memoria sin haberla visto en la respuesta del servidor.
Si Overpass no devuelve el nombre exacto (clasificaciones/yards sin etiquetar),
ensanchar bbox y quitar el filtro railway, y como último recurso usar el punto del
eje ADIF interpolado por PK y declararlo en fuente_geo. Receta completa con consultas
Overpass/Nominatim y nodos ya verificados: references/verificacion-puntual-osm.md.glob.glob('json/ES/*.json')
con stems que llevan corchetes literales (ID-211207-290408-CIAF[1].json) no casa —
[1] es una clase de caracteres del glob. Usar os.listdir + substring del basename,
o glob.escape.bien sea un cross-line.20+350, P.K. 429,825, 5/350, 11,907. Líneas: 010 Madrid Puerta de Atocha - Sevilla Santa Justa.https://www.ign.es/wmts/ign-base?...LAYER=IGNBase-gris...&FORMAT=image/jpeg
(FORMAT obligatorio o 400) con selector gris/topográfico/ortofoto y atribución
"© IGN — Instituto Geográfico Nacional (CC BY 4.0)". Detalle en el skill
ign-wmts-tiles (verificado: tile de prueba con curl file antes de pushear).TIPO_A_CATEGORIA) y el tipo fino queda como subtipo
mostrado "Categoría — Subtipo" en ficha; el filtro y el dashboard van por
categoría. Subdividir está permitido SOLO cumpliendo las 6.IF <código> (del expediente, sin ceros: 0041/2014 →
IF 41/2014), con el título descriptivo como subtítulo debajo. El usuario lo
pidió explícitamente: los títulos largos crudos del parser no valen.<style> — una clase
.detail-tag sin CSS produce "accidente colisión descarrilamiento" todo pegado