소스 정보
- 저장소
- Ntizar/MasterMind
- 최근 소스 활동
- 2026년 9월 8일 14:54
- 감지된 SKILL.md 언어
- 스페인어
- 스타
- 2
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/Ntizar/MasterMind --skill rail-accident-visors명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
Usa al medir reps o cadencia desde vídeo con pose.
Usa al predecir relaciones entre objetos en tiempo real.
Patrón de cálculo de sombras solares con Web Workers + Comlink para no bloquear la UI
| 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