El primer resultado (promedio simple por ejemplo) daba 0.5185 vs 0.275 de Fase 3, señal de
alarma segun el propio script. La causa era metodologica, no un bug de merge: el bucket
replay concentra 112927 de los ~128849 tokens assistant del split de eval (87%), mientras
que buckets dificiles como negativos/skills_adherencia/delegacion_subagentes tienen pocos
ejemplos pero loss alto -- un promedio por ejemplo les da el mismo peso que a replay,
inflando el global. transformers.Trainer pondera por token, no por ejemplo. Con el mismo
ponderado por token: 0.2560 vs 0.275 de Fase 3 (diff=0.019, dentro del margen esperado) --
confirma que el merge es correcto.
- scripts/30_eval_suite.py --gate 1: eval-loss sobre el checkpoint mergeado, agrupado por
meta.bucket (aislando replay), comparado contra eval_loss=0.275 de Fase 3.
- docker-compose.eval.yml: servicio vllm-eval propio (puerto 8001), sirviendo el
checkpoint mergeado en BF16, con tool-call-parser=qwen3_coder y reasoning-parser=qwen3.
No se pudo leer el compose real de produccion (/data/compose/43/docker-compose.yml no
existe en spark, probablemente vive en el host del servidor Portainer) -- flags basados
en la arquitectura conocida del modelo.
- scripts/31_build_holdout_prompts.py: genera data/holdout_prompts.jsonl (200 prompts,
40 por MCP, sin overlap verificado contra train.jsonl/eval.jsonl).
- scripts/32_gate2_toolcalls.py: valida tool-calls devueltas por vllm-eval (parser real
de vLLM, nunca una regex propia) contra los 200 prompts held-out.
- scripts/33_gate3_adherencia.py: checklists de adherencia por skill + no-activacion,
con baseline opcional contra vllm-qwen36 si esta corriendo.
- scripts/34_gate4_e2e.py: arma el plan de llamadas E2E contra los 5 MCPs y 5 skills via
el checkpoint mergeado, para que el agente orquestador las ejecute con sus MCPs reales.
Remapea nombres de modulo (model.layers.* del adapter -> model.language_model.layers.*
del checkpoint base multimodal), preserva tensores mtp.*/visual.* al copiarlos sin
modificar, y toma chat_template.jinja de MODEL_PATH (nunca del adapter, que tiene el
template de masking de training). Verificacion automatica en verde: 310/310 tensores
LoRA-target mergeados, 1045/1045 tensores totales preservados, sin NaN/Inf, muestra de
200 tensores no-target byte-identica al base.
El chat_template.jinja de produccion no tiene tags {% generation %}, por lo que
return_assistant_tokens_mask salia vacio para el 100% de los ejemplos en el primer run.
Se genero data/chat_template_train.jinja (copia exacta del template real, con {%- generation -%}
envolviendo solo el contenido/tool_calls/im_end de cada turno assistant) para el fallback
de masking ya anticipado en la Decision de diseno #4 del plan principal -- el chat_template.jinja
original no se toca, sigue siendo el que sirve produccion.
El script debe correr directo en el host de spark (sin contenedor), donde /workspace no existe.
Corrige a la ruta real del repo en el NFS compartido: /mnt/docker-nas/projects/ai-projects/qwen3-6-lora/data/raw/replay.jsonl
data/schemas/*.json: dump fiel de las tool definitions reales de
Penpot (4), Gitea (53), GitHub-personal (43), Docmost (17) y
Atlassian (37), obtenidas directo de las definiciones ya cargadas
en la sesion de Claude Code (no se escribio un cliente MCP nuevo,
para no arriesgar desviarse del esquema real).
PENPOT_DEPLOYMENT_NOTES.md: investigacion del codigo fuente oficial
de @penpot/mcp confirma que import_image y export_shape.filePath
estan ausentes porque este deployment corre en modo remoto/multi-
usuario (isFileSystemAccessEnabled() = !isRemoteMode()) -- documentado
tambien en una subpagina nueva de Docmost.
scripts/03_build_replay.py: genera ~700 ejemplos de replay (anti-
forgetting) contra el vLLM de produccion, 80 prompts semilla
(conversacion general/codigo/razonamiento) x 9 pasadas variando
temperatura. Mapea el campo "reasoning" de la API de vLLM a
"reasoning_content" para la convencion de chat template.
00_verify_hardware.py: flash_attn, sdpa y flash-linear-attention
funcionan los tres en el GB10 (SM121) via Triton JIT -- mejor de lo
esperado, ya no hace falta el fallback lento de PyTorch para Gated
DeltaNet. attn_implementation recomendado: flash_attention_2.
01_inspect_modules.py: resuelve la discrepancia de nombres de las
proyecciones de Gated DeltaNet inspeccionando la arquitectura real
(device_map=meta, sin pesos). Resultado: parcialmente fusionado --
in_proj_qkv (q+k+v en un solo Linear) pero in_proj_z, in_proj_a e
in_proj_b por separado. Confirma tambien que mlp.experts.{gate_up,down}_proj
son nn.Parameter 3D (no LoRA-ables) y que shared_expert.{gate,up,down}_proj
son nn.Linear normales, como anticipaba el plan.
constraints.txt: fija la version exacta de torch de la imagen NGC
(2.10.0a0+b4e4ee81d3.nv25.12) para instalar transformers/peft/trl/
accelerate/bitsandbytes sin romper el build ARM64/Blackwell.