Files
ableton-mcp-ai/docs/sprint_4_bloque_A.md
OpenCode Agent 5ce8187c65 feat: Implement senior audio injection with 5 fallback methods
- Add _cmd_create_arrangement_audio_pattern with 5-method fallback chain
- Method 1: track.insert_arrangement_clip() [Live 12+]
- Method 2: track.create_audio_clip() [Live 11+]
- Method 3: arrangement_clips.add_new_clip() [Live 12+]
- Method 4: Session->duplicate_clip_to_arrangement [Legacy]
- Method 5: Session->Recording [Universal]

- Add _cmd_duplicate_clip_to_arrangement for session-to-arrangement workflow
- Update skills documentation
- Verified: 3 clips created at positions [0, 4, 8] in Arrangement View

Closes: Audio injection in Arrangement View
2026-04-12 14:02:32 -03:00

286 lines
13 KiB
Markdown

# SPRINT 4 — BLOQUE A: CARGA REAL, DIAGNÓSTICO Y ESTABILIZACIÓN (T001-T050)
> **Fecha**: 2026-04-11
> **Estado Sprint 3**: ✅ COMPLETO — 119 tools MCP, 64 handlers, 3 engines nuevos
> **Objetivo Sprint 4-A**: Que TODO lo que "dice" que hace, LO HAGA REALMENTE en Ableton
> **Revisión**: Qwen
---
## CONTEXTO
Sprint 3 entregó código que compila 100%. El problema: muchas acciones retornan
`"loaded": True` sin verificar que Ableton realmente las ejecutó. Este bloque se
enfoca en tres pilares:
1. **Verificación real** — cada handler confirma el estado POST-ejecución en Live
2. **Integración completa** — browser API ya implementada, ahora se usa en TODO el sistema
3. **Diagnóstico** — herramientas para que el usuario sepa exactamente qué funciona
---
## FASE A1: VERIFICACIÓN POST-EJECUCIÓN (T001-T010)
**T001**`_cmd_load_sample_to_clip`: Agregar `_verify_clip_has_audio(slot)` que
inspecciona `slot.has_clip` y `clip.length > 0` DESPUÉS de la carga.
Retorna `verified: true/false` con `duration_beats` real si el clip existe.
**T002**`_cmd_insert_device`: Agregar `_verify_device_on_track(track, device_name)`
que compara lista de devices ANTES y DESPUÉS. Retorna `verified: true` + `device_index`
real si el device apareció en `track.devices`.
**T003**`_cmd_create_arrangement_midi_clip`: Verificar si `arrangement_clips` API
funcionó chequeando el clip existe en el track. Si Session fallback, marcar
`view: "session_fallback"` y retornar `clip_index` + URL del slot real.
**T004**`_cmd_load_sample_to_drum_rack_pad`: Verificar que el pad tiene cadena
después del intento. Acceder a `pad.chains[0].devices[0].sample.file_path`
y comparar con el fname buscado. Retornar `verified_path`.
**T005**`_cmd_generate_dembow_clip`: Verificar que las notas se escribieron
exactamente. Leer el clip con `clip.get_notes()` y comparar count.
Retornar `notes_written: N, notes_verified: M`.
**T006**`_cmd_generate_midi_clip`: Agregar verificación de notas post-escritura.
Si `clip.get_notes()` retorna vacío cuando se enviaron notas, loguear el error
y reintentar con `replace_selected_notes` si disponible.
**T007**`_cmd_create_drum_kit`: Después de crear el Drum Rack, verificar que
`track.devices` contiene el device. Acceder a `device.drum_pads` y contar pads
activos. Retornar `pads_active`, `drum_rack_index`.
**T008**`_cmd_configure_eq`: Verificar que el EQ Eight está en la cadena.
Leer `device.parameters` y confirmar que se aplicaron los valores.
Retornar `parameters_verified: {band: value}`.
**T009**`_cmd_setup_sidechain`: Verificar que el Compressor tiene `sidechain_active`.
Acceder a `device.sidechain` si existe. Retornar `sidechain_confirmed: true/false`.
**T010** — Crear handler `_cmd_verify_track_setup(track_index)`:
- Lista todos los devices del track
- Lista clips activos en Session View
- Informa volumen, pan actual
- Retorna snapshot completo del track para debugging
---
## FASE A2: BROWSER API — USAR EN TODO EL SISTEMA (T011-T020)
**T011**`_cmd_load_samples_for_genre` (T008): Actualmente usa solo
`sample_selector.select_for_genre()` para paths. Integrar `_browser_load_audio()`
para cada sample, con fallback a `create_audio_clip`. Retornar qué método funcionó
por cada sample.
**T012**`_cmd_create_drum_kit` (T009): Actualmente crea Drum Rack via
`create_midi_track()` pero no carga el Drum Rack device. Integrar
`_browser_load_device(t, "Drum Rack", "instruments")` antes de cargar samples.
Verificar que el Drum Rack apareció antes de intentar cargar pads.
**T013**`_cmd_build_track_from_samples` (T010): Usar `_browser_load_audio()`
en lugar de confiar en `create_audio_clip`. Agregar lógica de fallback:
si browser falla, crear MIDI track con nota de instrucción.
**T014**`_cmd_insert_device` → extender lookup: Actualmente busca solo en una
sección. Agregar búsqueda secundaria en TODAS las secciones si la primera falla.
Orden: `instruments → audio_effects → midi_effects → packs`.
**T015** — Nuevo handler `_cmd_scan_browser_section(section_name, depth=2)`:
- Escanea una sección del browser Live y retorna árbol de items
- Sections: "instruments", "audio_effects", "sounds", "user_folders", "packs"
- Útil para debug: saber exactamente qué ve el sistema en el browser
- Retorna lista de items con `name`, `is_loadable`, `is_folder`
**T016** — Nuevo tool MCP `scan_browser_section(section, depth)` en `server.py`:
- Llama a `_cmd_scan_browser_section`
- Permite al usuario descubrir qué devices/samples tiene disponibles
- Retorna JSON con árbol navegable
**T017**`_cmd_configure_eq`: Si el device no existe en el track, PRIMERO
insertar EQ Eight via `_browser_load_device`, LUEGO configurar parámetros.
Secuencia: insert → verify → configure.
**T018**`_cmd_configure_compressor`: Si no hay Compressor, insertar via
browser antes de configurar. Verificar la inserción. Mismo patrón que T017.
**T019**`_cmd_setup_sidechain`: Insertar Compressor si no existe,
configurar la fuente de sidechain. Usar `device.sidechain_enabled = True` si disponible.
Retornar los parámetros realmente configurados.
**T020** — Nuevo handler `_cmd_add_libreria_to_browser()`:
- Lee path de `libreria/reggaeton` desde constante
- Intenta agregar el folder a Live's user library via `application().browser`
- Retorna `added: true/false` con instrucción manual si falla
---
## FASE A3: ARRANGEMENT VIEW — IMPLEMENTACIÓN COMPLETA (T021-T030)
**T021**`_cmd_create_arrangement_midi_clip`: Agregar soporte para `song.record_mode`.
Si `song.record_mode` está disponible, configurar overdub antes de fire.
Retornar `arrangement_mode_set: true/false`.
**T022** — Nuevo handler `_cmd_set_arrangement_position(bar)`:
- `song.current_song_time = bar * beats_per_bar`
- `app.view.show_view("Arranger")`
- Retorna posición actual del playhead
**T023** — Nuevo handler `_cmd_fire_clip_to_arrangement(track_index, clip_index, target_bar)`:
- Pos playhead en `target_bar`
- Activa `song.arrangement_overdub = True`
- Dispara el clip: `track.clip_slots[clip_index].fire()`
- Espera `clip.length` beats en la queue de `_pending_tasks`
- Desactiva overdub: `song.arrangement_overdub = False`
- Retorna `recorded_to_bar: target_bar`
**T024**`_cmd_duplicate_session_to_arrangement` (T014): Reescribir usando
`_cmd_fire_clip_to_arrangement` para cada clip+escena. Calcular posición en bars
basada en `scene_index * section_length`. Retorna clips colocados + posición.
**T025** — Nuevo handler `_cmd_get_arrangement_clips(track_index)`:
- Lee todos los clips de arrangement via `track.arrangement_clips` si disponible
- Retorna lista con `name`, `start_time`, `length`, `has_notes`
- Si no disponible, retorna vacío con `method: "not_available"`
**T026** — Nuevo handler `_cmd_show_arrangement_view()`:
- `app.view.show_view("Arranger")`
- `app.view.show_view("Detail/Clip")` para mostrar detalle
- Retorna `view: "arranger"`
**T027** — Nuevo handler `_cmd_show_session_view()`:
- `app.view.show_view("Session")`
- Retorna `view: "session"`
**T028**`_cmd_build_arrangement_structure`: Usa `_cmd_fire_clip_to_arrangement`
para colocar clips reales en posiciones de la estructura (Intro, Verse, Drop, etc.)
en lugar de solo crear escenas en session view.
**T029** — Nuevo handler `_cmd_loop_arrangement_region(start_bar, end_bar)`:
- `song.loop_start = start_bar * beats_per_bar`
- `song.loop_length = (end_bar - start_bar) * beats_per_bar`
- `song.loop_on = True`
- Retorna `loop_set: true`
**T030** — Nuevo handler `_cmd_capture_to_arrangement()`:
- Equivalente a "Capture" de Live: `app.get_document().capture_midi()` si disponible
- Fallback: instrucción de cómo usar Capture manualmente
- Retorna `captured: true/false`
---
## FASE A4: DIAGNÓSTICO Y MONITOREO (T031-T040)
**T031** — Nuevo handler `_cmd_get_live_version()`:
- `Live.Application.get_application().get_major_version()`
- `Live.Application.get_application().get_minor_version()`
- Retorna `version: "12.x.x"`, `build: N`
**T032** — Nuevo handler `_cmd_get_track_details(track_index)`:
- Snapshot completo de un track: devices, clips, volumes, routing
- Para debugging: `has_input`, `has_output`, `arm`, `mute`, `solo`
- Lista cada device con parámetros accesibles
**T033** — Nuevo handler `_cmd_get_device_parameters(track_index, device_index)`:
- Lista todos los parámetros de un device
- `device.parameters``{name, value, min, max, is_quantized}`
- Útil para saber cómo configurar el device vía API
**T034** — Nuevo handler `_cmd_set_device_parameter(track_index, device_index, param_name, value)`:
- Busca parámetro por nombre en `device.parameters`
- Setea `param.value = value`
- Verifica que el cambio se aplicó
- Retorna `parameter`, `old_value`, `new_value`
**T035** — Nuevo handler `_cmd_get_clip_notes(track_index, clip_index)`:
- Lee las notas de un MIDI clip via `clip.get_notes()`
- Retorna lista de `{pitch, start, duration, velocity, mute}`
- Con estadísticas: `note_count`, `min_pitch`, `max_pitch`, `duration_bars`
**T036** — Nuevo handler `_cmd_test_browser_connection()`:
- Verifica que `application().browser` es accesible
- Lista las secciones disponibles: sounds, instruments, audio_effects, etc.
- Retorna `browser_ok: true/false`, `sections: [...]`
**T037** — Nuevo handler `_cmd_test_sample_loading(sample_path)`:
- Tests: `os.path.isfile()` → path OK
- Tests: `_browser_load_audio()` → browser OK
- Tests: `create_audio_clip()` si disponible
- Retorna `path_ok`, `browser_ok`, `direct_ok`, `recommended_method`
**T038** — Nuevo handler `_cmd_get_session_state()`:
- `song.current_song_time` → posición actual
- `song.is_playing`, `song.tempo`, `song.signature_numerator`
- Lista clips activos por track
- Retorna snapshot completo del estado de Session
**T039** — Nuevo tool MCP `get_system_diagnostics()` en `server.py`:
- Combina: get_live_version + test_browser_connection + get_session_state
- Retorna JSON con estado completo del sistema
- Primer tool que ejecutar para diagnosticar problemas
**T040** — Nuevo tool MCP `test_real_loading(sample_path)` en `server.py`:
- Llama a `_cmd_test_sample_loading`
- Retorna qué métodos de carga funcionan en el Live actual
- Guía al usuario sobre qué esperar
---
## FASE A5: ROBUSTEZ Y ESTABILIDAD (T041-T050)
**T041** — Agregar timeout global a `_cmd_*` handlers: Si un handler tarda
más de 3s (detectado via `time.time()`), retornar `timeout: true` y limpiar
`_pending_tasks` parcialmente. Previene bloqueos de Ableton.
**T042**`_dispatch()`: Agregar manejo de `JSONDecodeError` y `KeyError`
explícitos. Retornar error descriptivo con el comando que falló.
Loguear en Ableton con `self.log_message`.
**T043** — Proteger `update_display()`: Atrapar excepciones dentro del loop
de `_pending_tasks`. Si una task lanza excepción, remover y continuar con la
siguiente. Nunca dejar que una task rota bloquee el drain.
**T044**`_tcp_server_thread`: Si la conexión se cierra abruptamente,
cerrar el socket limpiamente. Agregar `socket.SO_REUSEADDR` si no está presente.
Reiniciar listener automáticamente tras error de conexión.
**T045** — Agregar límite a `_pending_tasks`: Si la queue supera 100 items,
droppear las tareas más viejas y loguear warning. Previene acumulación sin límite
cuando Ableton está bajo carga y `update_display()` no puede drenar rápido.
**T046**`_cmd_get_tracks()`: Si un track da error al leer un atributo
(e.g., track sin nombre), continuar con el siguiente en lugar de fallar todo.
Agregar `try/except` granular por atributo.
**T047**`_cmd_generate_full_song()`: Si un sub-handler falla durante
el pipeline, continuar con los siguientes tracks. Retornar lista de errores
al final pero no abortar. Comportamiento "best effort" para producción completa.
**T048** — Todos los handlers que crean tracks: Verificar que el índice
solicitado no excede `len(song.tracks)`. Si se intenta acceder a track[N]
y N>=len, retornar error claro en lugar de IndexError sin contexto.
**T049**`_browser_search`: Agregar límite de tiempo: si la recursión
supera 5 segundos (verificar con `time.time()`), abortar y retornar `None`
en lugar de bloquear el thread de Ableton indefinidamente.
**T050** — Crear `_cmd_health_check()`:
- Ejecuta 5 checks: TCP OK, song accesible, tracks accesibles, browser accesible, update_display activo
- Retorna score 0-5 y descripción de cada check
- Tool MCP `health_check()` que llama a este handler
- Primero que ejecutar tras abrir Ableton
---
## ARCHIVOS A MODIFICAR (Bloque A)
| Archivo | Cambios |
|---------|---------|
| `__init__.py` | +25 handlers nuevos, robustez en handlers existentes |
| `mcp_server/server.py` | +10 tools MCP: scan_browser, health_check, get_system_diagnostics, test_real_loading, etc. |
## RESTRICCIONES
1. Compilar tras cada archivo: `python -m py_compile "<path>"`
2. `libreria/` → solo lectura
3. NO modificar engines del Sprint 1/2/3
4. Handlers de verificación son SOLO-LECTURA: no mutan estado
5. Retornar siempre JSON con `status` + `result` o `error`