¿Qué hace wiki-lint?
Antes de la primera ejecución, lea
docs/GOTCHAS.mden el kit (falsos positivos conocidos, notas operativas).
Un escaneo de salud de solo lectura del wiki (wiki_root en wiki.config.json) más tres herramientas de reparación. Todo es biblioteca estándar de Python y funciona igual en Windows, macOS y Linux. Cada regla específica del wiki (carpetas, taxonomía de estados, mapa de campos, exenciones, lista de verbos, patrones de marcador de posición, umbrales) vive en la configuración, por lo que la lógica no cambia cuando el wiki agrega un nuevo campo, carpeta o verificación. Diseñado para mantenerse rápido más allá de 3000 notas (una ejecución completa toma segundos, excepto la Verificación 13 que tarda alrededor de medio minuto en ese tamaño).
Skills relacionados
wiki-ingest,wiki-ingest-book: escriben las notas que este skill verifica (misma configuración para decisiones de estado y enrutamiento de índice).wiki-synthesize: la Verificación 15 produce su lista de trabajo; la Verificación 32 audita sus sellos.vocabulary-update: el remedio para la Verificación 30.
Diseño
skills/wiki-lint/
SKILL.md
scripts/
lint.py # ejecuta las verificaciones (--only, --out-dir, --self-test)
fix.py # herramientas de reparación: propuesta de índice, Verificación 12 backlinks, promocionar borradores
references/
check-origins.md # POR QUÉ existe cada verificación (autoridad única)
cli-reference.md # banderas, claves de configuración, archivos de salida, forma del informe
wikikit/lint/ # la implementación: context.py, checks_*.py, fixes.py, defaults.json
tests/fixtures/lint/ # mini-wiki sintético que ejercita cada verificación + expected_counters.txt
Dónde vive cada tipo de hecho (nunca duplicar entre estos): references/check-origins.md = por qué existe una verificación; este archivo = qué hace; wikikit/lint/checks_*.py = cómo se implementa; tests/fixtures/lint/expected_counters.txt = por qué el valor del fixture es el que es. Al final de una ejecución en vivo, la herramienta imprime el puntero check-origins.md para cada verificación que se activó, para que el motivo llegue cuando esté decidiendo si un acierto es real o ruido.
Invariantes de ingeniería (no romper)
- Todo I/O de archivos pasa por
wikikit.textio(UTF-8 a nivel de bytes, sin BOM, los finales de línea nunca se traducen). Nunca useopen()en modo texto con el manejo de newline predeterminado: leer y reescribir un archivo CRLF de esa manera lo convierte silenciosamente. - Nunca use una expresión regular multilínea con
$sobre texto que pueda contener CRLF:$se sienta entre el CR y el LF y divide el par. Edite por líneas (frontmatter.set_key,fixes._insert_backlinks). - La configuración es la única fuente de verdad. Cuando el wiki gane un campo, carpeta, estado o exención, edite
wiki.config.json(secciónlint), no el código. Los valores predeterminados viven enwikikit/lint/defaults.json. - La exención de stub es uniforme.
lint.stub_exempt_checksenumera los números de verificación que omiten las notasstatus: stub; lea la lista de la configuración, nunca la repita en prosa. Las Verificaciones 10 y 11 excluyen stubs inline (cuentas del sistema de archivos). - La detección de marcador de posición está condicionada a no resolución. Un wikienlace que se resuelve en el índice del vault NUNCA se marca como marcador de posición, incluso si su nombre coincide con un patrón; las notas de abreviaturas legítimas en MAYÚSCULAS no deben marcarse.
- Autoprueba antes de enviar cambios de lógica. Cualquier edición a una verificación (nueva verificación, umbral cambiado, heurística refactorizada) debe ejecutarse en verde:
python skills/wiki-lint/scripts/lint.py --self-test. Actualicetests/fixtures/lint/yexpected_counters.txtjuntos cuando el cambio sea intencional. Demuestre que el fixture discrimina: revierta el cambio de lógica, vuelva a ejecutar y confirme que el contador se mueve. Un fixture que pasa tanto antes como después no protege nada. - Un cero es una medición del detector, no del wiki. Antes de confiar en el cero de una nueva verificación, inyecte un caso positivo y vea que se active.
- La propiedad del marcador de advertencia es posicional, nunca encadenada con regex (Verificación 22). El wiki puede usar tanto
[[Paper]]⚠como⚠[[Paper]]. El propietario de un glifo es el enlace inmediatamente a su IZQUIERDA si el texto recortado a la izquierda termina en]], de lo contrario, el enlace a su DERECHA; un glifo adyacente a ningún enlace es una advertencia en prosa. No resuelva esto con dos regex encadenados (el segundo se come el marcador del enlace vecino y desmarca silenciosamente una fuente no verificada). Cualquier edición masiva de marcadores necesita un censo por citekey antes y después.
Cómo funcionan las verificaciones
| # | Nombre | Qué marca | Exención |
|---|---|---|---|
| 1 | Conceptos huérfanos | Sin backlink de ningún artículo/libro (enlace inline o concepts_touched) Y ninguno de un concepto hermano (inline o related_concepts); la pata hermano evita que los hubs de síntesis marquen falsos positivos | stub |
| 2 | Métodos huérfanos | Sin backlink de un artículo/libro Y sin enlace saliente a ningún artículo/libro | stub |
| 3 | concepts_touched vacío | Una nota de artículo con el campo nulo, vacío o [] (también el canario para corrupción de frontmatter en ingestas por lotes) | stub |
| 4 | Afirmaciones sin fuente | Notas de concepto con al menos unsourced_threshold líneas que contienen un verbo de afirmación (verb_list) y sin wikienlace dentro de +/-unsourced_radius líneas ni debajo de un encabezado con wikienlace | stub |
| 5 | Wikienlaces rotos / marcador de posición | Destino no en el índice de todo el vault (roto), o coincide con placeholder_wikilink_patterns cuando no se resuelve (marcador de posición), o vacío ([[]], destino-vacío) | link_target_ignore_patterns (embebidos de Bases) |
| 6 | Borradores | promocionar: un borrador que cita al menos min_distinct_sources_for_final obras fuente DISTINTAS (un libro más sus capítulos es una obra). obsoleto: un borrador más antiguo que stale_days por debajo de eso, o sin fecha | ninguna |
| 7 | Cobertura del índice | Una nota cuyo nombre base no es un wikienlace en index.md | stub; omitido cuando index_mode es bases |
| 7b | Mala ubicación de sección del índice | Un - [[X]] bajo la sección ## incorrecta (index_sections) | omitido en modo bases |
| 8 | Sincronización de chapters_ingested del libro | El conjunto declarado difiere de los archivos {book}_ChNN.md en disco | stub capítulos |
| 9 | Capítulo sin book: | Una nota *_ChNN cuyo enlace book: falta o no se resuelve | ninguna |
| 10 | Desviación de Estadísticas Rápidas | Un conteo declarado en ## Quick Stats difiere del conteo en vivo de archivos de la carpeta mapeada (quick_stats_labels) | ninguna |
| 11 | Desviación de Cobertura por Campo | Las columnas de Conceptos/Métodos de ## Coverage by Field difieren de los conteos en vivo no stub (filas de campo por field:, filas de subcampo -> por subfield: clave desde el enlace [[MOC_*]]; un subcampo con notas y sin fila es MISSING-ROW) | stubs; notas sin campo son INFO |
| 12 | concepts_touched bidireccional | paper -> concepts_touched[token] cuyo destino carece del backlink [[paper]] (asimetría, auto-reparable) o se resuelve a ninguna nota (missing_target, triaje del token) | ninguna |
| 13 | Alias sin enlace | Un alias documentado (acrónimo distintivo o frase de varias palabras) usado en la prosa de otro concepto/método sin un enlace a su nota canónica. INFO | stub; alias_link_stoplist; las citas en bloque no se buscan |
| 14 | Radar de idioma | Un encabezado (H2-H6) en un idioma diferente al del wiki, o al menos prose_threshold aciertos de stopword en prosa. Solo informe; deshabilitado a menos que language_radar.enabled | stub |
| 15 | Radar de síntesis | Sobre conceptos/métodos finales: falta = califica para un bloque de Estado del Conocimiento y no tiene ninguno; obsoleto = bloque más antiguo que el last_verified más reciente de sus obras citadas | no final |
| 16 | Vocabulario de estado | status no en status_by_type[type], falta status o type, o una fuga de plantilla (draft | final) | ninguna |
| 17 | Sin source_verification | Un artículo sin sello; un valor que comienza con abstract NO es un sello. INFO backlog | stub |
| 18 | Verificado pero incompleto | Un artículo sellado que carece de volume o pages y sin un check18_exempt: "YYYY-MM-DD, reason" fechado; los artículos de conferencias y tesis están exentos por sede y forma | stub |
| 19 | evidence_level fuera de vocabulario | Valor no en evidence_level_vocab (solo artículos) | ninguna |
| 20 | Citas Útiles ]] | Una cita en bloque bajo ## Useful Quotes con un ]] filtrado | ninguna |
| 21 | Cohorte previo a texto completo | Un artículo fechado antes de synthesis.prefulltext_cutoff sin sello de nivel de texto completo. INFO | stub |
| 22 | Marcador pre-texto completo obsoleto | Un ⚠ cuyo wikienlace propietario es un artículo que ya tiene un sello de texto completo, después de tres exenciones de cola (advertencia en prosa, anotación itálica fechada, confianza (N src). Tiene falsos positivos reales | ninguna |
| 23 | Marcador de posición de afirmación sin llenar | Una línea que coincide con unfilled_claim_patterns (una fuente nombrada pero ninguna afirmación escrita) | ninguna |
| 24 | Clave de frontmatter obsoleta | Una clave en deprecated_frontmatter_keys (por defecto journal a venue); coincidencia exacta, por lo que no hay falsos positivos | ninguna |
| 25 | Figura sin citar | Una cifra estadística (numeric_claim_patterns) sin wikienlace dentro del radio, clasificada cited / block / methods / domain. INFO. Nada está exento | stub; citas en bloque omitidas |
| 26 | Discrepancia de columnas en tabla | Una tabla cuyas filas tienen un número diferente de celdas que la fila delimitadora (la única verificación estructural) | ninguna |
| 27 | Mojibake | Una línea que viaja de ida y vuelta a través de encode('cp1252').decode('utf-8') a una cadena diferente | ninguna |
| 28 | Sello sin last_verified | Un artículo/libro con source_verification y sin last_verified (invisible para la Verificación 15 obsoleto). INFO | ninguna |
| 29 | Enlace de slug fuera del vault | Un wikienlace a un slug en memory_slug_prefixes, en todo el vault. Deshabilitado a menos que la lista no esté vacía | memory_slug_exempt_files |
| 30 | Sincronización de vocabulario | _VOCABULARY.md falta un concepto/método en vivo (falta) o tiene una fila para una nota que ya no existe (fantasma). INFO; remedio /vocabulary-update | ninguna |
| 31 | Sello sin texto | Un sello de como máximo 60 caracteres sin texto de verificación descriptivo en ningún canal. INFO | ninguna |
| 32 | Par sin sello | Un par (nota, obra citada en su bloque de Estado del Conocimiento) cuyo sello de nota fuente no nombra la nota. Bloqueante; la deuda declarada en seal_debt_file se informa pero no se cuenta | deuda declarada |
| 33 | Sin título o sede | Un artículo con un title o venue vacío | stub |
Invocación
Desde el vault (el wiki.config.json más cercano hacia arriba es la configuración):
python skills/wiki-lint/scripts/lint.py # todas las verificaciones; las listas van a _temp/
python skills/wiki-lint/scripts/lint.py --only 5,12 # verificaciones seleccionadas
python skills/wiki-lint/scripts/lint.py --self-test # fixtures vs expected_counters.txt
python skills/wiki-lint/scripts/fix.py check12 --dry-run
python skills/wiki-lint/scripts/fix.py promote _temp/promote_batch.txt
python skills/wiki-lint/scripts/fix.py index # necesita index_mode "manual" y una ejecución de lint previa
Banderas, claves de configuración y los archivos que cada verificación escribe: references/cli-reference.md.
Cuándo editar qué
- Nueva carpeta del wiki (por ejemplo
datasets/): agréguela afoldersenwiki.config.json. Debe registrarse ANTES de que se creen las notas, o cada enlace a ella se leerá como roto. - Nuevo valor de estado: agréguelo a
lint.status_by_type.{type}. Nuevo tipo de nota: agregue el tipo con sus estados permitidos. - Nuevo campo para conceptos/métodos: agregue el slug a
field_titlecase_map. - Refinar Verificación 4:
lint.verb_list. Aflojar/ajustar su tolerancia:lint.defaults.unsourced_radius. - Nuevo patrón de marcador de posición:
lint.placeholder_wikilink_patterns(recuerde que solo se aplica a destinos que no se resuelven). - Una verificación debe omitir stubs: agregue su número a
lint.stub_exempt_checks. - Activar el radar de idioma: copie
examples/languages/es.json(o escriba sus propias listas) enlint.language_radar. - Agregar una nueva verificación: impleméntela en
wikikit/lint/checks_*.py, agregue sus contadores aexpected_counters.txt, agregue un fixture que la ejercite, escriba su entrada enreferences/check-origins.md. Una verificación cuyo motivo no esté escrito se deshabilita la primera vez que parece ruidosa. Si el motivo no se puede encontrar, escriba "no documentado"; nunca invente uno.
Reglas de seguridad
- El lint es de solo lectura contra el wiki; solo escribe las listas por verificación en
--out-dir(por defecto_temp/). fix.pymuta notas solo donde informa (backlinks, estado), mantiene los finales de línea de cada archivo y registra cada cambio para deshacer.- Cuando no esté seguro de si un cambio heurístico es correcto, escriba el fixture primero, observe que la autoprueba falle, luego haga el cambio.
- Los directorios de caché nunca tienen versiones (
_temp/y amigos permanecen en.gitignore). Una medición versionada se lee meses después como si fuera de hoy.