[ADN] Fase 10: Saneamiento y Consolidación - Un solo punto de verdad

- S1: Eliminar 'triggers' duplicado en run (líneas 109/131)
- S2: Eliminar cli/commit.rb (truncado, sin uso)
- S3: Eliminar cli/inicio.rb y cli/cierre.rb (legacy, jornada.rb los reemplaza)
- S4: Mover 4 planes obsoletos de docs/plan/adn/ a docs/_hist/plan/adn/
- S5: Mover 14 backups de planes y 3 docs técnicos a docs/_hist/
- S6: Eliminar manifiesto vacío P2604_proyecto_p2604.md
- S7: Corregir referencia a plan obsoleto en run
- S8: Dejar de cargar core/validador.rb obsoleto en run
- S9: Limpiar progreso duplicado en P2604_mejoras_ADN.md
- Agregar Fase 10 al plan P2604 con 10 tareas
- Un solo punto de verdad: adn/README.md
- Un solo plan: docs/proy/p2604_mejoras_ADN/P2604_mejoras_ADN.md
- Registro en bitácora: evento 1073
This commit is contained in:
Ricardo Monla
2026-03-15 14:33:32 -03:00
parent b13a389568
commit cfd0e8452a
36 changed files with 1563 additions and 238 deletions
+43 -4
View File
@@ -2,9 +2,12 @@
# adn/tools/core/constants.rb — Definición centralizada de rutas y constantes del ADN
# ==============================================================================
# Este módulo centraliza todas las constantes reutilizables del sistema ADN.
# Following: Menos es Más (una fuente de verdad)
module ADN
# El script 'run' está en adn/tools/run, por lo que la raíz es 2 niveles arriba
# === RUTAS DEL PROYECTO ===
PROJECT_ROOT = File.expand_path('../../..', __dir__)
NODOS_DIR = File.join(PROJECT_ROOT, 'nodos')
@@ -12,10 +15,46 @@ module ADN
BITACORAS_DIR = File.join(PROJECT_ROOT, 'docs', 'bitacoras')
LOGS_DIR = File.join(BITACORAS_DIR, '_hist')
# Directorios de herramientas
TOOLS_DIR = File.join(PROJECT_ROOT, 'adn', 'tools')
SEGURIDAD_DIR = File.join(TOOLS_DIR, 'seguridad')
# Bóveda
CANDADOS_PATH = File.join(SEGURIDAD_DIR, 'ns8-candados.rb')
# === ESTADOS DE BITÁCORA ===
ESTADOS = {
pendiente: '⏳',
completado: '✅',
fallido: '❌',
advertencia: '⚠️',
pausado: '⏸️',
en_proceso: '📍'
}.freeze
# === MODOS DE TRABAJO ===
MODOS = {
presencial: 'P',
remoto: 'R'
}.freeze
# === CONFIGURACIÓN DE BASE DE DATOS ===
DB_CONFIG = {
host: 'localhost',
puerto: 5433,
nombre: 'dtic_bitacoras',
schema: 'bitacoras'
}.freeze
# === EXCEPCIONES PERSONALIZADAS ===
class Error < StandardError; end
class ConfigError < Error; end
class DBError < Error; end
class ValidationError < Error; end
class NetworkError < Error; end
# === VERSIÓN ===
VERSION = '1.0.0'.freeze
end
+36
View File
@@ -0,0 +1,36 @@
# frozen_string_literal: true
# adn/tools/core/error_handler.rb — Helper para mensajes de error estandarizados
# ==============================================================================
# módulo reutilizable para formateo consistente de errores en CLI
module ADN
module ErrorHandler
module_function
def error(msg, code: 1)
puts "#{Color::RED}#{msg}#{Color::RESET}"
exit(code)
end
def warning(msg)
puts "#{Color::YELLOW}#{msg}#{Color::RESET}"
end
def info(msg)
puts "#{Color::CYAN} #{msg}#{Color::RESET}"
end
def success(msg)
puts "#{Color::GREEN}#{msg}#{Color::RESET}"
end
def debug(msg)
puts "#{Color::DIM}🔍 #{msg}#{Color::RESET}" if ENV['ADN_DEBUG']
end
def die(msg, code: 1)
error(msg, code: code)
end
end
end
+64
View File
@@ -0,0 +1,64 @@
# frozen_string_literal: true
# adn/tools/core/help_formatter.rb - Formateador de ayuda estandarizado
# ======================================================================
# Helper reutilizable para formateo consistente de --help en CLI
require_relative 'colores'
module ADN
module HelpFormatter
module_function
def format_header(titulo, descripcion = nil)
lines = []
lines << "#{Color::BOLD}#{titulo}#{Color::RESET}"
lines << "#{Color::DIM}#{'=' * titulo.length}#{Color::RESET}" if descripcion
lines << ""
lines << "#{Color::CYAN}#{descripcion}#{Color::RESET}" if descripcion
lines << ""
lines.join("\n")
end
def format_usage(comando, formato)
"Uso: #{Color::YELLOW}#{comando}#{Color::RESET} #{formato}"
end
def format_option(opciones, descripcion, default = nil)
opt_str = opciones.map { |o| o.length > 1 ? "--#{o}" : "-#{o}" }.join(', ')
default_str = default ? " (default: #{default})" : ""
" #{Color::GREEN}#{opt_str.ljust(20)}#{Color::RESET} #{descripcion}#{default_str}"
end
def format_example(comando, descripcion)
"#{Color::DIM} $ #{comando}#{Color::RESET}\n #{Color::CYAN}#{descripcion}#{Color::RESET}"
end
def format_section(titulo)
"\n#{Color::BOLD}#{titulo}#{Color::RESET}\n"
end
def generar_help(titulo:, descripcion:, uso:, opciones:, ejemplos: [])
lines = []
lines << format_header(titulo, descripcion)
lines << uso
lines << ""
if opciones.any?
lines << format_section("Opciones:")
opciones.each do |opt|
lines << format_option(opt[:names], opt[:desc], opt[:default])
end
end
if ejemplos.any?
lines << format_section("Ejemplos:")
ejemplos.each do |ej|
lines << format_example(ej[:cmd], ej[:desc])
end
end
lines.join("\n")
end
end
end
+50 -13
View File
@@ -4,7 +4,7 @@
# ====================================================================
# Este módulo proporciona logging estructurado en JSON para todas las
# herramientas del sistema ADN. Los logs se almacenan en `logs/adn/`
# con rotación diaria automática.
# con rotación diaria automática y por tamaño.
require 'json'
require 'fileutils'
@@ -13,32 +13,30 @@ require 'time'
module ADN
class Logger
# Niveles de log soportados
NIVELES = %w[DEBUG INFO EXITO ADVERTENCIA ERROR].freeze
# Configuración de rotación
MAX_TAMANO_MB = 10
MAX_ARCHIVOS = 7
require_relative 'colores'
# Inicializa el logger
#
# @param opts [Hash] Opciones de configuración
# @option opts [String] :directorio Directorio para logs (default: logs/adn/)
# @option opts [Boolean] :terminal Mostrar logs en terminal (default: true)
# @option opts [Boolean] :colores Usar colores en terminal (default: true)
# @option opts [String] :archivo Nombre base del archivo de log (default: fecha actual)
def initialize(opts = {})
@directorio = opts[:directorio] || File.join(File.expand_path('../../..', __dir__), 'logs', 'adn')
@mostrar_terminal = opts.fetch(:terminal, true)
@usar_colores = opts.fetch(:colores, true)
@archivo_base = opts[:archivo] || Date.today.iso8601
# Nivel mínimo de log (configurable)
@nivel_minimo = opts[:nivel] || 'DEBUG'
# Crear directorio si no existe
FileUtils.mkdir_p(@directorio) unless Dir.exist?(@directorio)
# Ruta completa del archivo de log
@ruta_log = File.join(@directorio, "#{@archivo_base}.log")
# Buffer para logs (útil para pruebas o modo batch)
@buffer = []
# Verificar rotación al iniciar
verificar_rotacion
end
# Registra un mensaje de nivel DEBUG
@@ -209,6 +207,45 @@ module ADN
'adn.desconocido'
end
# Verifica y ejecuta rotación de logs si es necesario
def verificar_rotacion
return unless File.exist?(@ruta_log)
tamano_mb = File.size(@ruta_log) / (1024.0 * 1024.0)
if tamano_mb > MAX_TAMANO_MB
rotar_por_tamano
end
end
# Rota el log por tamaño (archivo muy grande)
def rotar_por_tamano
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
archivo_rotado = "#{@ruta_log}.#{timestamp}"
FileUtils.mv(@ruta_log, archivo_rotado)
limpiar_logs_antiguos
puts "#{Color::YELLOW}⚠ Log rotado por tamaño: #{archivo_rotado}#{Color::RESET}"
end
# Limpia logs antiguos (más de MAX_ARCHIVOS)
def limpiar_logs_antiguos
patron = File.join(@directorio, '*.log.*')
archivos = Dir.glob(patron).sort_by { |f| File.mtime(f) }
archivos[0...-MAX_ARCHIVOS].each do |archivo|
File.delete(archivo) if File.exist?(archivo)
end
end
# Verifica si el nivel está habilitado
def nivel_habilitado?(nivel)
idx_actual = NIVELES.index(@nivel_minimo) || 0
idx_nivel = NIVELES.index(nivel) || 0
idx_nivel >= idx_actual
end
end
# Logger global preconfigurado para uso rápido