OrganiLAB 1.6.1: organizador, actualizador, interfaz 1.6 y publicación
Primer commit del proyecto. Incluye el rediseño 1.6 (riel de pasos en la barra lateral), el actualizador con reintentos y respaldo, publicar.sh y la nueva opción de desconectar carpetas (1.6.1). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Este commit está contenido en:
261
AUDITORIA-2026-09-15.html
Archivo normal
261
AUDITORIA-2026-09-15.html
Archivo normal
@@ -0,0 +1,261 @@
|
||||
<title>Auditoría OrganiLAB 1.4.3</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap">
|
||||
<style>
|
||||
:root{
|
||||
--bg:#f6f7f5; --panel:#ffffff; --ink:#1c2733; --ink-2:#4a5561; --ink-3:#7a8490;
|
||||
--line:#dde2e0; --accent:#2457b8; --accent-soft:#e6eefb;
|
||||
--crit:#b3261e; --crit-soft:#fbe9e7; --high:#b85a0c; --high-soft:#fdeee0;
|
||||
--med:#8a6d00; --med-soft:#fbf3d5; --low:#526070; --low-soft:#e9edf1;
|
||||
--mono:"IBM Plex Mono",ui-monospace,Consolas,monospace;
|
||||
--sans:"IBM Plex Sans",system-ui,Segoe UI,sans-serif;
|
||||
}
|
||||
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]){
|
||||
--bg:#151a1f; --panel:#1d242b; --ink:#e7ebee; --ink-2:#b4bcc5; --ink-3:#7f8994;
|
||||
--line:#2e373f; --accent:#7ea6f0; --accent-soft:#1f2c42;
|
||||
--crit:#ff7b72; --crit-soft:#3a1f1d; --high:#f0a35c; --high-soft:#3a2a18;
|
||||
--med:#e3c15a; --med-soft:#353017; --low:#a9b4bf; --low-soft:#262d34;
|
||||
}}
|
||||
:root[data-theme="dark"]{
|
||||
--bg:#151a1f; --panel:#1d242b; --ink:#e7ebee; --ink-2:#b4bcc5; --ink-3:#7f8994;
|
||||
--line:#2e373f; --accent:#7ea6f0; --accent-soft:#1f2c42;
|
||||
--crit:#ff7b72; --crit-soft:#3a1f1d; --high:#f0a35c; --high-soft:#3a2a18;
|
||||
--med:#e3c15a; --med-soft:#353017; --low:#a9b4bf; --low-soft:#262d34;
|
||||
}
|
||||
body{background:var(--bg);color:var(--ink);font-family:var(--sans);font-size:15px;line-height:1.55;padding:0 20px;padding-block:32px 64px}
|
||||
.wrap{max-width:820px;margin:0 auto}
|
||||
h1{font-size:28px;font-weight:600;margin:0 0 6px;text-wrap:balance;letter-spacing:-.01em}
|
||||
h2{font-size:18px;font-weight:600;margin:40px 0 12px;padding-top:16px;border-top:1px solid var(--line)}
|
||||
p{margin:0 0 12px;max-width:68ch}
|
||||
.meta{color:var(--ink-2);font-size:14px;margin-bottom:24px}
|
||||
.meta code{font-family:var(--mono);font-size:13px}
|
||||
.tally{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin:20px 0 8px}
|
||||
.tally div{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:10px 12px}
|
||||
.tally b{display:block;font-size:24px;font-weight:600;font-variant-numeric:tabular-nums;line-height:1.1}
|
||||
.tally span{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-3)}
|
||||
.tally .c b{color:var(--crit)} .tally .h b{color:var(--high)} .tally .m b{color:var(--med)} .tally .l b{color:var(--low)}
|
||||
.verdict{background:var(--accent-soft);border-left:3px solid var(--accent);padding:12px 16px;border-radius:0 6px 6px 0;margin:16px 0 8px}
|
||||
.verdict p:last-child{margin:0}
|
||||
ol.top{padding-left:22px;margin:0}
|
||||
ol.top li{margin-bottom:8px}
|
||||
.f{display:grid;grid-template-columns:78px 1fr;gap:12px;padding:14px 0;border-bottom:1px solid var(--line)}
|
||||
.f:last-child{border-bottom:0}
|
||||
.chip{align-self:start;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;padding:3px 8px;border-radius:4px;text-align:center}
|
||||
.chip.c{background:var(--crit-soft);color:var(--crit)} .chip.h{background:var(--high-soft);color:var(--high)}
|
||||
.chip.m{background:var(--med-soft);color:var(--med)} .chip.l{background:var(--low-soft);color:var(--low)}
|
||||
.f h3{font-size:15px;font-weight:600;margin:0 0 4px}
|
||||
.f p{font-size:14px;color:var(--ink-2);margin:0 0 6px}
|
||||
.f p:last-child{margin:0}
|
||||
.loc{font-family:var(--mono);font-size:12.5px;color:var(--ink-3)}
|
||||
code{font-family:var(--mono);font-size:13px;background:var(--low-soft);padding:1px 5px;border-radius:3px}
|
||||
.ok{color:var(--ink-2);font-size:14px}
|
||||
ul{padding-left:20px;margin:0 0 12px} li{margin-bottom:4px}
|
||||
@media (max-width:520px){.tally{grid-template-columns:repeat(2,1fr)} .f{grid-template-columns:1fr;gap:6px} .chip{justify-self:start}}
|
||||
</style>
|
||||
<div class="wrap">
|
||||
<h1>Auditoría OrganiLAB 1.4.3</h1>
|
||||
<p class="meta">Organizador Labstream · código en <code>C:\Users\Munka\Claude\labstream-organizador</code> · 15 sep 2026 · solo lectura, no se modificó nada.</p>
|
||||
|
||||
<div class="verdict">
|
||||
<p><strong>Veredicto.</strong> La app cumple hoy su promesa de no borrar: en la bitácora real (raíz <code>V:\Galeano</code>, 1.154 movimientos, 6 lotes) no hay un solo error. Pero hay dos fallas que sí pueden destruir material de cliente por una ruta que la promesa no cubre: la <strong>conversión a 10 bits</strong> sobrescribe salidas cuando un clip existe en MP4 y MOV, y el <strong>renombrado silencioso</strong> con «(2)» rompe el emparejamiento de clips Sony con sus XML. Además, el servidor local acepta órdenes de cualquier página web abierta en el navegador, lo que permite mover archivos o instalar un ejecutable ajeno.</p>
|
||||
</div>
|
||||
|
||||
<div class="tally">
|
||||
<div class="c"><b>4</b><span>Críticos</span></div>
|
||||
<div class="h"><b>11</b><span>Altos</span></div>
|
||||
<div class="m"><b>15</b><span>Medios</span></div>
|
||||
<div class="l"><b>12</b><span>Bajos</span></div>
|
||||
</div>
|
||||
|
||||
<h2>Qué arreglar primero</h2>
|
||||
<ol class="top">
|
||||
<li><strong>Salidas de conversión a prueba de colisión.</strong> Conservar la extensión original en el nombre (<code>clip.mov_10bit.mp4</code>), rechazar encolar si otro trabajo ya reclama esa salida, y nunca hacer <code>os.remove</code> ni <code>os.replace</code> sobre un archivo existente. En «Reemplazar», volver a comprobar que la salida existe y usar un lote único por reemplazo.</li>
|
||||
<li><strong>Cerrar el API local.</strong> Rechazar peticiones cuyo <code>Host</code> no sea <code>127.0.0.1:8765</code>, exigir <code>Content-Type: application/json</code> y un token aleatorio por arranque que la interfaz envíe. Quitar <code>ffmpeg</code>, <code>ffprobe</code>, <code>python_resolve</code> y <code>actualizaciones.url</code> de lo que <code>/api/config</code> puede cambiar.</li>
|
||||
<li><strong>Nunca renombrar por colisión.</strong> Convertir la colisión en aviso en el plan y en error en ejecución (saltar el archivo y registrarlo), en vez del sufijo «(2)». Reconocer el sidecar Sony <code>C0001M01.XML</code> en las reglas.</li>
|
||||
<li><strong>Contención de rutas.</strong> En aplicar, stock a papelera, duplicados y respaldo, rechazar cualquier origen o destino que no cuelgue de la raíz. Corregir la comprobación de papelera, que aún busca «papelera» y no «_Para borrar».</li>
|
||||
<li><strong>Actualizador seguro.</strong> Validar versión y nombre de archivo del manifiesto, exigir sha256 y HTTPS del mismo host, no actualizar con trabajos en curso, y hacer copia de la instalación antes del robocopy para poder volver atrás.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Seguridad de los datos</h2>
|
||||
|
||||
<div class="f"><span class="chip c">Crítico</span><div>
|
||||
<h3>MP4 y MOV del mismo clip comparten una sola salida de conversión</h3>
|
||||
<p>La salida se forma quitando la extensión y añadiendo <code>_10bit.mp4</code>. <code>clip.mov</code> y <code>clip.mp4</code> producen el mismo destino; el segundo trabajo sobrescribe al primero, la verificación aprueba a ambos y «Reemplazar» manda los dos originales a <code>_Para borrar/originales</code>. Solo sobrevive un convertido. El puente con Resolve lo hace explícito con <code>os.remove(dst)</code>. El asistente automatiza toda la cadena sin revisión humana.</p>
|
||||
<p class="loc">organizador.py:1272-1278, 1289, 1373, 1487-1553 · resolve_bridge.py:240-241 · asistente.py:267-270</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>Colisiones de nombre se resuelven renombrando con «(2)»</h3>
|
||||
<p>Al fusionar grabaciones de varias tarjetas Sony en un mismo <code>00_Grabacion/SONY/</code>, el segundo <code>C0001.MP4</code> pasa a <code>C0001 (2).MP4</code>. Ese nombre ya no se reconoce como cámara y el siguiente plan lo propone mover a Exportados. Su XML queda huérfano y Resolve o Premiere no pueden reenlazar. No hay aviso en el plan.</p>
|
||||
<p class="loc">organizador.py:868-875, 894, 692-750, 1541 · reglas.py:102</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>La carpeta «_Para borrar» se vuelve a planificar como basura en cada pasada</h3>
|
||||
<p>La comprobación sigue buscando el nombre viejo «papelera». Como el escaneo marca <code>_Para borrar</code> como basura, el plan propone moverla dentro de sí misma con confianza «alta» y marcada por defecto. Hoy falla con «Cannot move a directory into itself» y deja una carpeta parásita; cualquier cambio futuro en <code>mover()</code> lo convertiría en un automovimiento recursivo.</p>
|
||||
<p class="loc">organizador.py:816, 827 · reglas.py:539-540</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>Archivos bloqueados dejan copias huérfanas sin registrar</h3>
|
||||
<p>Si un clip está abierto en Resolve, <code>os.rename</code> falla y el respaldo <code>shutil.move</code> copia el archivo pero no puede borrar el origen. La excepción sale antes de escribir la bitácora: origen intacto, copia de varios GB en destino, nada registrado. Reintentar crea copias «(2)».</p>
|
||||
<p class="loc">organizador.py:895-899</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>Todos los reemplazos comparten el lote «optimizar»</h3>
|
||||
<p>Deshacer ese lote devuelve a su sitio todos los originales reemplazados en la historia de la app. «Reemplazar» tampoco vuelve a comprobar que el convertido siga existiendo: si se borró entre la verificación y el reemplazo, el original se va sin sustituto.</p>
|
||||
<p class="loc">organizador.py:1538, 972-994</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>Destinos editables fuera de la raíz y movimientos entre unidades sin verificar</h3>
|
||||
<p>El campo de destino del plan acepta rutas absolutas y <code>..\..</code> sin comprobar contención. Entre unidades, <code>shutil.move</code> copia y borra sin comparar tamaño ni hash, y «Deshacer» solo usa <code>os.rename</code>, así que no puede revertirlo.</p>
|
||||
<p class="loc">organizador.py:2104, 436-437, 878-900, 985</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip m">Medio</span><div>
|
||||
<h3>Deshacer omite entradas en silencio y mezcla lotes del mismo segundo</h3>
|
||||
<p>Si el destino ya no existe o el origen está ocupado, la entrada se salta sin avisar. El id de lote tiene resolución de segundo; la bitácora del proyecto ya muestra un lote con dos «fin». Las carpetas vaciadas se borran sin registro.</p>
|
||||
<p class="loc">organizador.py:983, 907, 957-969</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip m">Medio</span><div>
|
||||
<h3>Aplicación parcial ante rutas largas</h3>
|
||||
<p>Una fila de 500 archivos se detiene en el primer fallo. Las rutas bajo <code>_Para borrar/basura/</code> superan fácilmente 260 caracteres en NAS; no hay comprobación previa ni prefijo <code>\\?\</code>.</p>
|
||||
<p class="loc">organizador.py:913-928</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip m">Medio</span><div>
|
||||
<h3>El puente con Resolve modifica y guarda el proyecto abierto del editor</h3>
|
||||
<p>Si no puede crear su propio proyecto, usa el que el editor tiene abierto: cambia resolución y fps, borra todos los trabajos de render en cola, añade bins y timelines, y guarda. Los temporales <code>_render_*</code> quedan dentro de la carpeta de cámara del cliente si algo falla o se cancela.</p>
|
||||
<p class="loc">resolve_bridge.py:105-114, 155-159, 185, 260, 151, 250-255 · organizador.py:1440</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip l">Bajo</span><div>
|
||||
<h3>Masters dentro de una pieza son candidatos a conversión</h3>
|
||||
<p>Solo se excluye el contexto «Exportados». Un <code>01- Pieza/final.mp4</code> se ofrece para convertir y tras «Reemplazar» el master entregado acaba en <code>_Para borrar/originales</code>. «Apartar duplicados» sin selección aplica todos los grupos.</p>
|
||||
<p class="loc">organizador.py:1257, 2166</p>
|
||||
</div></div>
|
||||
|
||||
<h2>Reglas de reconocimiento</h2>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>El sidecar Sony C0001M01.XML no se reconoce</h3>
|
||||
<p>El patrón solo admite <code>c0001.xml</code>. El XML va a <code>OTROS/</code> o a <code>01_Proyectos/Edicion</code> mientras el MP4 va a <code>SONY/</code>. Sin embargo «Reemplazar» depende de ese nombre exacto para moverlo junto al clip.</p>
|
||||
<p class="loc">reglas.py:101-102 · organizador.py:1541</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip m">Medio</span><div>
|
||||
<h3>BRAW nunca llega a ser candidato a conversión</h3>
|
||||
<p>ffprobe no lee BRAW, el análisis lo marca como error y los candidatos exigen análisis correcto. La rama BRAW es inalcanzable: toda la vía Resolve está muerta desde la interfaz.</p>
|
||||
<p class="loc">organizador.py:1218-1226, 1241</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip m">Medio</span><div>
|
||||
<h3>Duplicados sin comprobar contenido</h3>
|
||||
<p>Se agrupan solo por nombre y tamaño, sin hash ni siquiera parcial. Clips de cámara a bitrate fijo con igual duración se declaran idénticos y uno se aparta. La pareja «versión» separa también un MOV intermedio de su MP4 master.</p>
|
||||
<p class="loc">organizador.py:1697-1723</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip m">Medio</span><div>
|
||||
<h3>Carpetas «1» a «12» bajo un año se renombran como meses con confianza alta</h3>
|
||||
<p>Piezas numeradas sin título (<code>2025/3</code>) pasan a <code>03- Marzo</code> marcadas por defecto.</p>
|
||||
<p class="loc">reglas.py:62 · organizador.py:187-195</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip l">Bajo</span><div>
|
||||
<h3>Otros</h3>
|
||||
<p>Raíces de unidad (<code>N:\</code>) rompen las consultas por prefijo y dejan el mapa vacío. <code>.dat</code> e <code>.idx</code> se tratan como basura. Cualquier carpeta llamada «Cache» se clasifica como proxy. El año de clips Blackmagic se infiere de la fecha de modificación.</p>
|
||||
<p class="loc">organizador.py:1066-1071, 1802 · reglas.py:156, 159, 268-277</p>
|
||||
</div></div>
|
||||
|
||||
<h2>Seguridad</h2>
|
||||
|
||||
<div class="f"><span class="chip c">Crítico</span><div>
|
||||
<h3>Cualquier página web puede dar órdenes al servidor local</h3>
|
||||
<p>Escucha solo en 127.0.0.1, pero no valida <code>Host</code> ni <code>Origin</code>, no pide token y parsea JSON sin mirar <code>Content-Type</code>. Un <code>fetch</code> en modo no-cors desde cualquier sitio abierto en el navegador pasa sin preflight. Con eso se puede reorganizar cualquier carpeta, mover rutas fuera de la raíz vía <code>/api/stock/papelera</code> o copiar el perfil entero a un recurso de red ajeno vía respaldo. <code>/api/respaldo/plan</code> es un GET que escribe config.</p>
|
||||
<p class="loc">organizador.py:2459, 1916-1923, 1615-1629, 2025-2034, 2169-2177</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip c">Crítico</span><div>
|
||||
<h3>Dos cadenas de ejecución de código desde ese mismo agujero</h3>
|
||||
<p>Uno: <code>/api/config</code> acepta cualquier clave, incluida la ruta de <code>ffprobe</code>; un UNC de atacante se ejecuta al analizar. Dos: cambiar <code>actualizaciones.url</code> y llamar a <code>/api/actualizacion/instalar</code> descarga un zip ajeno, lo comprueba contra el sha256 del propio atacante y lo lanza como ejecutable.</p>
|
||||
<p class="loc">organizador.py:2060-2067, 144-150, 1181-1185, 2113 · actualizador.py:47, 62, 115</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip c">Crítico</span><div>
|
||||
<h3>El actualizador confía en todo lo que dice el manifiesto</h3>
|
||||
<p>Sin firma. El sha256 se omite si el campo falta. La URL de descarga puede apuntar a otro host y a http. La cadena de versión se inserta sin validar en el <code>.bat</code> (inyección de comandos) y el nombre de archivo permite <code>..\</code> para escribir fuera de la carpeta de actualización.</p>
|
||||
<p class="loc">actualizador.py:49-51, 62, 99, 114-116, 138, 144</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>La actualización mata la app con trabajos en curso y no puede volver atrás</h3>
|
||||
<p><code>os._exit</code> a los 1,5 s salta la espera de conversión, respaldo y asistente. El bat solo espera a ese PID, no a otras instancias del exe. Robocopy sobrescribe archivo a archivo sin copia previa; un fallo a mitad deja exe viejo con <code>_internal</code> nuevo. El bat se escribe en cp1252 sin <code>chcp</code>, así que rutas con acentos fallan en consola OEM 850.</p>
|
||||
<p class="loc">actualizador.py:159, 147-149, 142</p>
|
||||
</div></div>
|
||||
|
||||
<p class="ok">Correcto: no hay <code>shell=True</code>, el handler de <code>/static/</code> no permite traversal, enlaces simbólicos no se siguen, <code>#recycle</code> y <code>@eadir</code> se omiten, <code>datos\</code> y <code>ffmpeg\</code> quedan fuera del robocopy.</p>
|
||||
|
||||
<h2>Interfaz</h2>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>Estado viejo al cambiar de raíz</h3>
|
||||
<p>Cambiar la raíz solo limpia plan y candidatos. Con la tabla de stock de la raíz A en pantalla y la raíz B seleccionada, «Mover a _Para borrar» envía rutas de A con raíz B; el servidor no valida y <code>relpath</code> con <code>..</code> saca el destino fuera de la papelera. «Copiar respaldo» sigue activo con el plan de otra raíz.</p>
|
||||
<p class="loc">index.html:461, 478, 487 · organizador.py:1615-1629, 2171</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>Errores del servidor se tratan como éxito</h3>
|
||||
<p><code>api()</code> alerta pero devuelve el objeto de error; los llamadores hacen <code>.filter</code> o <code>.map</code> sobre él y revientan. Si el servidor no responde JSON, la promesa muere sin manejar y botones como «Calcular plan» quedan deshabilitados para siempre.</p>
|
||||
<p class="loc">index.html:419, 470, 481, 584-590, 614, 625</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>Doble clic duplica trabajos de conversión</h3>
|
||||
<p>«Encolar y arrancar» no se deshabilita y <code>encolar()</code> hace SELECT y luego INSERT sin bloqueo en hilos distintos. Igual con Verificar, Calcular respaldo y Stock a papelera.</p>
|
||||
<p class="loc">index.html:625, 635, 487, 478 · organizador.py:1289-1295</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip m">Medio</span><div>
|
||||
<h3>Otros de interfaz</h3>
|
||||
<ul>
|
||||
<li>Ajustes se repinta desde el sondeo cada 2,5 s y descarta lo que se está escribiendo.</li>
|
||||
<li>Claves de raíz sin normalizar: una raíz con barra final aparece «no escaneada» para siempre.</li>
|
||||
<li>El plan pinta todas las filas sin límite y se repinta entero en cada tecla del filtro; con 25.000 archivos son MB de JSON.</li>
|
||||
<li>Valores de perfil, croma y nombres de plantilla se pintan sin escapar; combinado con el CSRF es XSS persistente. Los nombres de archivo sí se escapan.</li>
|
||||
<li>Doble escape en el título del asistente: «Brigard & Urrutia» se ve con <code>&amp;</code>.</li>
|
||||
<li>Modal sin rol, sin Escape ni foco; enlaces sin <code>href</code> no operables por teclado; contraste del ámbar por debajo de AA. Google Fonts cargado en una herramienta que puede estar sin red.</li>
|
||||
</ul>
|
||||
<p class="loc">index.html:648-659, 463, 441, 585-601, 630, 655, 717, 414, 190-197, 8</p>
|
||||
</div></div>
|
||||
|
||||
<h2>Robustez y rendimiento</h2>
|
||||
|
||||
<div class="f"><span class="chip h">Alto</span><div>
|
||||
<h3>Hilos de fondo mueren en silencio y dejan banderas atascadas</h3>
|
||||
<p><code>analizar()</code> no captura excepciones: un «database is locked» durante un escaneo grande deja el análisis en «corriendo» hasta reiniciar. <code>bucle_cola()</code> sin try/finally deja <code>COLA.activa</code> en true y el cierre de ventana nunca termina. <code>copiar_respaldo</code> escribe «corriendo» pero el cierre espera «copiando».</p>
|
||||
<p class="loc">organizador.py:1211-1229, 1452-1474, 2129, 1833, 2486</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip m">Medio</span><div>
|
||||
<h3>Consultas que escanean la tabla entera</h3>
|
||||
<p><code>archivos_bajo()</code> usa LIKE sobre índice binario y hace full scan; se llama una vez por carpeta no reconocida y una vez por cada elemento movido. Mover 12.000 autoguardados de After Effects son 12.000 barridos de 25.000 filas. Duplicados y Mapa hacen <code>os.path.exists</code> por SMB en cada archivo de más de 5 MB.</p>
|
||||
<p class="loc">organizador.py:242-247, 265, 949, 1694, 1059</p>
|
||||
</div></div>
|
||||
|
||||
<div class="f"><span class="chip l">Bajo</span><div>
|
||||
<h3>Otros</h3>
|
||||
<ul>
|
||||
<li>El modo <code>python_resolve</code> apunta a un archivo que no se distribuye en el empaquetado.</li>
|
||||
<li>El zip portátil lleva la lista completa de clientes, alias y mapa de unidades del estudio.</li>
|
||||
<li>Los <code>resolve_lote_*.json</code> se acumulan en <code>datos\</code>; los bat generados llevan <code>\r\r\n</code>.</li>
|
||||
<li>Endpoints muertos: <code>/api/actualizacion/buscar</code>, <code>/api/duplicados</code> GET, y <code>modalTexto</code> nunca conectado.</li>
|
||||
<li>Sin pruebas automáticas; la prueba manual del README es anterior al cambio de nombre de la papelera, por eso se coló ese bug.</li>
|
||||
</ul>
|
||||
<p class="loc">organizador.py:1400-1401, 2023, 2110 · empaquetar.py:47-57 · index.html:510</p>
|
||||
</div></div>
|
||||
|
||||
<h2>Método</h2>
|
||||
<p>Tres revisiones en paralelo sobre <code>organizador.py</code>, <code>reglas.py</code>, <code>resolve_bridge.py</code>, <code>actualizador.py</code>, <code>empaquetar.py</code> y <code>static/index.html</code>, con ejecución de las funciones de reglas contra casos límite y comprobación del contenido del zip de la app. Los hallazgos críticos se verificaron por segunda vez leyendo el código. Se revisó además la bitácora real de la copia portátil 1.4.1 en Descargas, que es la que se ha usado sobre el NAS.</p>
|
||||
</div>
|
||||
Referencia en una nueva incidencia
Block a user