{"id":"cs-1782305637952-6qx0x15g","status":"done","createdAt":1782305637952,"finishedAt":1782305693330,"meta":{"projectName":"ALimpiezas","projectPath":"C:\\xampp\\htdocs\\ALimpiezas","messagePreview":"Consolida el CONOCIMIENTO del proyecto para uso futuro del agente.\n\nEntrega en Markdown:\n1. **Resumen ejecutivo** (5 lín","phaseId":"knowledge"},"events":[{"event":"status","data":{"text":"Modo completo — hasta 80 herramientas, usa solo las necesarias"},"at":1782305637961},{"event":"status","data":{"text":"Agente + Serena (project-mcp.json)"},"at":1782305637974},{"event":"agent","data":{"agentId":"agent-006b8c1f-384f-440c-a684-af98788d1416","projectPath":"C:\\xampp\\htdocs\\ALimpiezas"},"at":1782305637974},{"event":"changes","data":{"sessionId":"cs-1782305637961-gs6fxx","pending":0},"at":1782305637974},{"event":"status","data":{"text":"Procesando…"},"at":1782305638954},{"event":"tool_activity","data":{"activity":{"id":"tool_ba31df76-024a-48ca-8933-d7eba1c2a62","name":"mcp","status":"running","summary":"MCP list_memories","detail":"{\"providerIdentifier\":\"serena\",\"toolName\":\"list_memories\",\"args\":{}}","at":"2026-06-24T12:54:09.325Z"}},"at":1782305649325},{"event":"tool_activity","data":{"activity":{"id":"tool_7af5644d-5074-4093-9056-df0cd4852bc","name":"glob","status":"running","summary":"glob: ","detail":"","at":"2026-06-24T12:54:09.328Z"}},"at":1782305649328},{"event":"tool_activity","data":{"activity":{"id":"tool_634dd420-9a30-4a2d-bdc4-ac21a9d9363","name":"glob","status":"running","summary":"glob: ","detail":"","at":"2026-06-24T12:54:09.331Z"}},"at":1782305649331},{"event":"tool_activity","data":{"activity":{"id":"tool_087e255a-c3eb-4d53-8cd5-0b88e29d5a4","name":"glob","status":"running","summary":"glob: ","detail":"","at":"2026-06-24T12:54:09.339Z"}},"at":1782305649339},{"event":"text","data":{"chunk":"Consolidaré el conocimiento del proyecto leyendo memorias existentes y archivos clave fuera de Core/Dinamic.\n"},"at":1782305693330},{"event":"tool_activity","data":{"activity":{"id":"tool_ba31df76-024a-48ca-8933-d7eba1c2a62","name":"mcp","status":"completed","summary":"MCP list_memories","detail":"{\"providerIdentifier\":\"serena\",\"toolName\":\"list_memories\",\"args\":{}}","at":"2026-06-24T12:54:09.790Z"}},"at":1782305649790},{"event":"tool_activity","data":{"activity":{"id":"tool_087e255a-c3eb-4d53-8cd5-0b88e29d5a4","name":"glob","status":"completed","summary":"glob: ","detail":"","at":"2026-06-24T12:54:10.228Z"}},"at":1782305650228},{"event":"tool_activity","data":{"activity":{"id":"tool_634dd420-9a30-4a2d-bdc4-ac21a9d9363","name":"glob","status":"completed","summary":"glob: ","detail":"","at":"2026-06-24T12:54:10.310Z"}},"at":1782305650310},{"event":"tool_activity","data":{"activity":{"id":"tool_7af5644d-5074-4093-9056-df0cd4852bc","name":"glob","status":"completed","summary":"glob: ","detail":"","at":"2026-06-24T12:54:18.795Z"}},"at":1782305658795},{"event":"tool_activity","data":{"activity":{"id":"tool_e67f66f5-4059-4396-be40-9cab29812d1","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\.cursor-bridge\\knowledge.json","detail":"","at":"2026-06-24T12:54:20.218Z"}},"at":1782305660218},{"event":"tool_activity","data":{"activity":{"id":"tool_fcc5fcb8-9023-44d4-8a62-f58393706e1","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\docs\\MANUAL-AGENTE-IA.md","detail":"","at":"2026-06-24T12:54:20.224Z"}},"at":1782305660224},{"event":"tool_activity","data":{"activity":{"id":"tool_7724406a-7dd4-49da-b902-1262c3683fa","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\docs\\MOBILE-BRIDGE-TEST.md","detail":"","at":"2026-06-24T12:54:20.225Z"}},"at":1782305660225},{"event":"tool_activity","data":{"activity":{"id":"tool_a3fe41e4-dd1d-4450-bd72-889a2feeb8e","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\docs\\MANUAL-USUARIO.md","detail":"","at":"2026-06-24T12:54:20.226Z"}},"at":1782305660226},{"event":"tool_activity","data":{"activity":{"id":"tool_df716b5e-5eda-4b74-87a2-0f2c14644b0","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\mobile.html","detail":"","at":"2026-06-24T12:54:20.277Z"}},"at":1782305660277},{"event":"tool_activity","data":{"activity":{"id":"tool_0f5e11e7-ad75-49d2-8d7f-32fbb2ad52f","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\tools\\start-stack.ps1","detail":"","at":"2026-06-24T12:54:20.327Z"}},"at":1782305660327},{"event":"tool_activity","data":{"activity":{"id":"tool_e67f66f5-4059-4396-be40-9cab29812d1","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\.cursor-bridge\\knowledge.json","detail":"","at":"2026-06-24T12:54:20.357Z"}},"at":1782305660357},{"event":"tool_activity","data":{"activity":{"id":"tool_7724406a-7dd4-49da-b902-1262c3683fa","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\docs\\MOBILE-BRIDGE-TEST.md","detail":"","at":"2026-06-24T12:54:20.358Z"}},"at":1782305660358},{"event":"tool_activity","data":{"activity":{"id":"tool_fcc5fcb8-9023-44d4-8a62-f58393706e1","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\docs\\MANUAL-AGENTE-IA.md","detail":"","at":"2026-06-24T12:54:20.363Z"}},"at":1782305660363},{"event":"tool_activity","data":{"activity":{"id":"tool_a3fe41e4-dd1d-4450-bd72-889a2feeb8e","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\docs\\MANUAL-USUARIO.md","detail":"","at":"2026-06-24T12:54:20.368Z"}},"at":1782305660368},{"event":"tool_activity","data":{"activity":{"id":"tool_df716b5e-5eda-4b74-87a2-0f2c14644b0","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\mobile.html","detail":"","at":"2026-06-24T12:54:20.377Z"}},"at":1782305660377},{"event":"tool_activity","data":{"activity":{"id":"tool_0f5e11e7-ad75-49d2-8d7f-32fbb2ad52f","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\tools\\start-stack.ps1","detail":"","at":"2026-06-24T12:54:20.458Z"}},"at":1782305660458},{"event":"tool_activity","data":{"activity":{"id":"tool_8611a9ee-4082-4fbf-b656-b6798611705","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\composer.json","detail":"","at":"2026-06-24T12:54:21.683Z"}},"at":1782305661683},{"event":"tool_activity","data":{"activity":{"id":"tool_79db2363-2b6e-467f-9a14-8c2a41f767f","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\Plugins\\ALimpiezasMobile\\facturascripts.ini","detail":"","at":"2026-06-24T12:54:21.685Z"}},"at":1782305661685},{"event":"tool_activity","data":{"activity":{"id":"tool_cbbe4dd2-46c1-4403-9e0f-aa0b81211c3","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\Plugins\\ALimpiezasMobile\\Init.php","detail":"","at":"2026-06-24T12:54:21.686Z"}},"at":1782305661686},{"event":"tool_activity","data":{"activity":{"id":"tool_dddd316a-b08d-44e3-8c87-3f19459ac9f","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\ai-service\\main.py","detail":"","at":"2026-06-24T12:54:21.686Z"}},"at":1782305661686},{"event":"tool_activity","data":{"activity":{"id":"tool_f4f63bce-6ef1-4055-a31f-bf51f5d6d3b","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\.htaccess","detail":"","at":"2026-06-24T12:54:21.687Z"}},"at":1782305661687},{"event":"tool_activity","data":{"activity":{"id":"tool_cf8c63f4-cefb-4011-a801-1e9230afa68","name":"mcp","status":"running","summary":"MCP get_symbols_overview","detail":"{\"providerIdentifier\":\"serena\",\"toolName\":\"get_symbols_overview\",\"args\":{\"relative_path\":\"Plugins/ALimpiezasMobile/Controller\",\"depth\":0}}","at":"2026-06-24T12:54:21.692Z"}},"at":1782305661692},{"event":"tool_activity","data":{"activity":{"id":"tool_8611a9ee-4082-4fbf-b656-b6798611705","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\composer.json","detail":"","at":"2026-06-24T12:54:21.803Z"}},"at":1782305661803},{"event":"tool_activity","data":{"activity":{"id":"tool_79db2363-2b6e-467f-9a14-8c2a41f767f","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\Plugins\\ALimpiezasMobile\\facturascripts.ini","detail":"","at":"2026-06-24T12:54:21.804Z"}},"at":1782305661804},{"event":"tool_activity","data":{"activity":{"id":"tool_cbbe4dd2-46c1-4403-9e0f-aa0b81211c3","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\Plugins\\ALimpiezasMobile\\Init.php","detail":"","at":"2026-06-24T12:54:21.805Z"}},"at":1782305661805},{"event":"tool_activity","data":{"activity":{"id":"tool_f4f63bce-6ef1-4055-a31f-bf51f5d6d3b","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\.htaccess","detail":"","at":"2026-06-24T12:54:21.806Z"}},"at":1782305661806},{"event":"tool_activity","data":{"activity":{"id":"tool_dddd316a-b08d-44e3-8c87-3f19459ac9f","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\ai-service\\main.py","detail":"","at":"2026-06-24T12:54:21.809Z"}},"at":1782305661809},{"event":"tool_activity","data":{"activity":{"id":"tool_cf8c63f4-cefb-4011-a801-1e9230afa68","name":"mcp","status":"completed","summary":"MCP get_symbols_overview","detail":"{\"providerIdentifier\":\"serena\",\"toolName\":\"get_symbols_overview\",\"args\":{\"relative_path\":\"Plugins/ALimpiezasMobile/Controller\",\"depth\":0}}","at":"2026-06-24T12:54:21.924Z"}},"at":1782305661924},{"event":"tool_activity","data":{"activity":{"id":"tool_7effb9ef-fff2-491f-9f9a-a834fb832fe","name":"glob","status":"running","summary":"glob: ","detail":"","at":"2026-06-24T12:54:23.531Z"}},"at":1782305663531},{"event":"tool_activity","data":{"activity":{"id":"tool_847e6090-9bb5-403d-b4bc-edf519920ed","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\tools\\bootstrap-plugin.php","detail":"","at":"2026-06-24T12:54:23.535Z"}},"at":1782305663535},{"event":"tool_activity","data":{"activity":{"id":"tool_7f040952-4c2b-4406-9c29-a61bb47e8f7","name":"glob","status":"running","summary":"glob: ","detail":"","at":"2026-06-24T12:54:23.536Z"}},"at":1782305663536},{"event":"tool_activity","data":{"activity":{"id":"tool_fb8567a4-42b7-4be3-9275-f9e33db68be","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\config.php","detail":"","at":"2026-06-24T12:54:23.537Z"}},"at":1782305663537},{"event":"tool_activity","data":{"activity":{"id":"tool_8b0eb6e3-0e25-4675-b355-e045ebe52a5","name":"mcp","status":"completed","summary":"MCP get_symbols_overview","detail":"{\"providerIdentifier\":\"serena\",\"toolName\":\"get_symbols_overview\",\"args\":{\"depth\":0,\"relative_path\":\"Plugins/ALimpiezasMobile/Lib/AiExpenseProcessor.php\"}}","at":"2026-06-24T12:54:23.812Z"}},"at":1782305663812},{"event":"tool_activity","data":{"activity":{"id":"tool_847e6090-9bb5-403d-b4bc-edf519920ed","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\tools\\bootstrap-plugin.php","detail":"","at":"2026-06-24T12:54:23.818Z"}},"at":1782305663818},{"event":"tool_activity","data":{"activity":{"id":"tool_fb8567a4-42b7-4be3-9275-f9e33db68be","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\config.php","detail":"","at":"2026-06-24T12:54:23.819Z"}},"at":1782305663819},{"event":"tool_activity","data":{"activity":{"id":"tool_7effb9ef-fff2-491f-9f9a-a834fb832fe","name":"glob","status":"completed","summary":"glob: ","detail":"","at":"2026-06-24T12:54:23.854Z"}},"at":1782305663854},{"event":"tool_activity","data":{"activity":{"id":"tool_7f040952-4c2b-4406-9c29-a61bb47e8f7","name":"glob","status":"completed","summary":"glob: ","detail":"","at":"2026-06-24T12:54:23.960Z"}},"at":1782305663960},{"event":"tool_activity","data":{"activity":{"id":"tool_b942acc0-d813-4d61-8075-1c4dd37217c","name":"read","status":"running","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\Plugins\\ALimpiezasMobile\\Lib\\MobileConfig.php","detail":"","at":"2026-06-24T12:54:25.345Z"}},"at":1782305665345},{"event":"tool_activity","data":{"activity":{"id":"tool_e9b14d71-45eb-43bd-94f3-ad739825360","name":"read","status":"running","summary":"read: …ampp\\htdocs\\ALimpiezas\\Plugins\\ALimpiezasMobile\\Lib\\ExpenseInvoiceCreator.php","detail":"","at":"2026-06-24T12:54:25.346Z"}},"at":1782305665346},{"event":"tool_activity","data":{"activity":{"id":"tool_b942acc0-d813-4d61-8075-1c4dd37217c","name":"read","status":"completed","summary":"read: C:\\xampp\\htdocs\\ALimpiezas\\Plugins\\ALimpiezasMobile\\Lib\\MobileConfig.php","detail":"","at":"2026-06-24T12:54:25.467Z"}},"at":1782305665467},{"event":"tool_activity","data":{"activity":{"id":"tool_e9b14d71-45eb-43bd-94f3-ad739825360","name":"read","status":"completed","summary":"read: …ampp\\htdocs\\ALimpiezas\\Plugins\\ALimpiezasMobile\\Lib\\ExpenseInvoiceCreator.php","detail":"","at":"2026-06-24T12:54:25.503Z"}},"at":1782305665503},{"event":"tool_activity","data":{"activity":{"id":"tool_22fb1608-2951-4467-9e81-d4eb29ff456","name":"mcp","status":"running","summary":"MCP write_memory","detail":"{\"providerIdentifier\":\"serena\",\"toolName\":\"write_memory\",\"args\":{\"memory_name\":\"alimpiezas/project-knowledge\",\"content\":\"# ALimpiezas — Conocimiento del proyec…","at":"2026-06-24T12:54:32.591Z"}},"at":1782305672591},{"event":"text","data":{"chunk":"Guardando el conocimiento consolidado en memoria Serena para futuras sesiones.\n"},"at":1782305693330},{"event":"tool_activity","data":{"activity":{"id":"tool_22fb1608-2951-4467-9e81-d4eb29ff456","name":"mcp","status":"completed","summary":"MCP write_memory","detail":"{\"providerIdentifier\":\"serena\",\"toolName\":\"write_memory\",\"args\":{\"memory_name\":\"alimpiezas/project-knowledge\",\"content\":\"# ALimpiezas — Conocimiento del proyec…","at":"2026-06-24T12:54:32.852Z"}},"at":1782305672852},{"event":"text","data":{"chunk":"# ALimpiezas — Base de conocimiento del proyecto\n\n> **Proyecto:** `C:\\xampp\\htdocs\\ALimpiezas`  \n> **Actualizado:** 2026-06-24 · Memoria Serena: `alimpiezas/project-knowledge`  \n> **Referencia técnica:** `docs/MANUAL-AGENTE-IA.md`\n\n---\n\n## 1. Resumen ejecutivo\n\n**ALimpiezas** es un ERP basado en **FacturaScripts 2026.4** (PHP 8.1, Twig, Bootstrap 5) desplegado en **XAMPP** con ruta web `/ALimpiezas` y base de datos MariaDB `alimpiezas_fs`. La personalización principal es el plugin **ALimpiezasMobile**: PWA mobile-first para autónomos (gastos, fiscal 303/130, voz). El código editable vive en `Plugins/`; `Dinamic/` es generado por `Plugins::deploy()` — no editar directamente. Un microservicio **FastAPI** en puerto **8090** (`ai-service/`) ofrece OCR local (Tesseract), extracción PDF y transcripción Whisper opcional. El flujo de gastos usa cola `ai_expense_queue` → confirmación humana → `FacturaProveedor` vía `ExpenseInvoiceCreator`. Scripts en `tools/` automatizan arranque (Apache, MySQL, IA, bandeja sistema).\n\n---\n\n## 2. Glosario\n\n| Término | Significado |\n|---------|-------------|\n| **FacturaScripts (FS)** | ERP open source LGPL; núcleo en `Core/`, capa dinámica en `Dinamic/` |\n| **Dinamic/** | Copia/symlink desplegada de plugins activos; clases `FacturaScripts\\Dinamic\\...` |\n| **Plugin** | Extensión en `Plugins/<Nombre>/`; se activa y despliega con `Plugins::enable` + `deploy` |\n| **ALimpiezasMobile** | Plugin PWA móvil: gastos, fiscal, voz, configuración independiente |\n| **FacturaProveedor** | Factura de compra/gasto recibida de un proveedor |\n| **Proveedor** | Tercero emisor de facturas de gasto; se crea/busca por CIF en el flujo IA |\n| **ai_expense_queue** | Tabla/cola de revisión: OCR/manual → usuario confirma → factura |\n| **expense_mode** | Modo gastos: `manual` (sin IA), `ocr` (Tesseract local), `ai` (OCR+reglas) |\n| **303 / 130** | Modelos fiscales trimestrales (IVA y pago fraccionado IRPF autónomo) |\n| **MobileConfig** | Lib que lee settings grupo `alimpiezas` (modo, URL servicio, régimen) |\n| **ExpenseInvoiceCreator** | Crea `FacturaProveedor` + línea IVA21 + adjunto desde cola aprobada |\n| **AiExpenseProcessor** | Upload, OCR vía servicio, CRUD cola, confirm/reject |\n| **VoiceCommandBridge** | Parsea texto voz → intención navegación (`MobileExpenses`, etc.) |\n| **FiscalModels / FiscalCalendar** | Borradores 303/130 y vencimientos AEAT |\n| **PWA** | App instalable: `manifest.json`, `sw.js` en `Dinamic/Assets/` |\n| **bootstrap-plugin.php** | CLI: activa ALimpiezasMobile + rebuild Dinamic + limpia caché |\n| **ai-service-manager.ps1** | Start/stop/restart Uvicorn :8090 |\n| **start-stack.ps1** | Arranque completo: Apache, MySQL, IA, bandeja tray |\n| **Tools::settings('alimpiezas', ...)** | API FacturaScripts para configuración persistente |\n| **AttachedFileRelation** | Vincula imagen/PDF original a la factura creada |\n| **Cursor Bridge** | Sincronización agente IDE ↔ iPhone vía `.cursor-bridge/` |\n\n---\n\n## 3. FAQ dev\n\n### ¿Dónde edito código móvil vs ERP core?\n\nEdita **solo** `Plugins/ALimpiezasMobile/` (Controller, Lib, View, Assets, Model, Table). Tras cambios ejecuta `php tools/bootstrap-plugin.php`. **No** modifiques `Dinamic/` a mano salvo depuración puntual — se sobrescribe en deploy.\n\n### ¿Por qué los CSS/JS dan 403 Forbidden?\n\nApache reescribe todo a `index.php` excepto `Dinamic/Assets/` y `node_modules/`. Las URLs públicas deben ser `asset('Dinamic/Assets/...')`, nunca `Plugins/.../Assets/`. Tras cambiar Assets del plugin → bootstrap.\n\n### ¿Cuándo necesito el puerto 8090?\n\nSolo si `expense_mode` es `ocr` o `ai`, o si `voice_use_service=1`. Modo **manual**, fiscal y voz local **no** requieren el microservicio. Comprobar: `curl http://127.0.0.1:8090/health`.\n\n### ¿Cómo funciona el flujo de un gasto con foto?\n\nPOST `upload` → archivo guardado → si OCR/AI, llamada `/api/v1/extract-invoice` → fila en `ai_expense_queue` (pending) → modal confirmación → POST `confirm` → `ExpenseInvoiceCreator::createFromQueue` → `FacturaProveedor` + adjunto → cola `approved`.\n\n### ¿Cómo añado un setting nuevo?\n\n1. Default en `Init::update()` con `Tools::settingsSet('alimpiezas', ...)`.  \n2. Lectura en `MobileConfig` o Lib correspondiente.  \n3. UI en `MobileSettings` (form POST separado de Gastos).  \n4. Bootstrap plugin.\n\n### ¿Qué hace `Plugins::deploy(true, true)`?\n\nRegenera `Dinamic/` (clases, vistas, assets copiados), actualiza tablas vía `Init::update()` y limpia caché Twig/campos. Obligatorio tras cambiar estructura plugin o assets.\n\n### Error Errno 10048 / puerto 8090 ocupado\n\nProceso zombie Uvicorn. Usar `.\\tools\\ai-service-manager.ps1 -Restart` o `.\\tools\\port-services.ps1 -Port 8090` para identificar PID y liberar.\n\n### ¿Cómo pruebo desde iPhone en LAN?\n\nEn móvil usar `http://<IP-PC>/ALimpiezas/MobileHome`, **nunca** `localhost`. PC con XAMPP + stack arrancado (`.\\tools\\start-stack.ps1`). PWA: Safari → Compartir → Añadir a pantalla de inicio.\n\n---\n\n## 4. Checklist onboarding\n\n- [ ] **Entorno:** XAMPP (Apache + MySQL), PHP ≥8.1, Composer, Python 3 + venv para `ai-service/`\n- [ ] **Clonar/abrir:** `C:\\xampp\\htdocs\\ALimpiezas`\n- [ ] **Dependencias PHP:** `composer install --ignore-platform-req=ext-pgsql`\n- [ ] **Config:** revisar `config.php` (BD `alimpiezas_fs`, ruta `/ALimpiezas`); no commitear secretos\n- [ ] **Plugin:** `php tools/bootstrap-plugin.php` → URL móvil en salida\n- [ ] **IA (opcional):** instalar Tesseract OCR; `pip install -r ai-service/requirements.txt`; `.\\tools\\ai-service-manager.ps1 -Start`\n- [ ] **Stack completo:** `.\\tools\\start-stack.ps1` (Apache, MySQL, IA, bandeja)\n- [ ] **Smoke test:** login `http://localhost/ALimpiezas/MobileHome`, `/health`, CSS `Dinamic/Assets/CSS/mobile.css`\n- [ ] **Leer docs:** `docs/MANUAL-AGENTE-IA.md`, `docs/MANUAL-USUARIO.md`\n- [ ] **Convención edit:** `Plugins/ALimpiezasMobile/` → bootstrap → probar en móvil y escritorio\n- [ ] **Serena:** usar `find_symbol` / `replace_symbol_body` en símbolos concretos; evitar grep masivo en `Core/`, `Dinamic/`, `vendor/`\n- [ ] **Bridge móvil:** restricciones modo ligero/completo en prompts desde iPhone (`.cursor-bridge/`)\n\n---\n\n## 5. Prompts guardados\n\n### Bugfix\n\n```\n[Bugfix] En ALimpiezas, al confirmar un gasto en MobileExpenses aparece error X.\nRevisa Plugins/ALimpiezasMobile/Lib/ExpenseInvoiceCreator.php y AiExpenseProcessor.php.\nUsa Serena find_symbol antes de editar. Tras fix: php tools/bootstrap-plugin.php.\nNo escanees Core/ ni Dinamic/ salvo referencia.\n```\n\n```\n[Bugfix] Assets móvil 403 Forbidden en /ALimpiezas/Dinamic/Assets/...\nVerifica .htaccess, rutas asset() en Twig Master/MobileLayout.html.twig,\ny que bootstrap-plugin haya copiado Assets a Dinamic/.\n```\n\n```\n[Bugfix] Microservicio IA puerto 8090 no responde /health.\nDiagnostica con tools/port-services.ps1 y ai-service-manager.ps1 -Restart.\nRevisa Tesseract en ai-service/main.py si OCR falla.\n```\n\n### Feature\n\n```\n[Feature] Añadir campo Y al formulario confirmación gastos móvil.\nArchivos: View/MobileExpenses.html.twig, Assets/JS/mobile.js,\nModel/Table ai_expense_queue, Init.php migración, AiExpenseProcessor confirm.\nDeploy: bootstrap-plugin.php. Mantén modo manual sin depender de 8090.\n```\n\n```\n[Feature] Nueva intención de voz \"ir a ayuda\" en VoiceCommandBridge.\nActualiza VoiceCommandBridge.php, MobileVoice.php, voice.js y traducción es_ES.json.\nFallback localParse debe funcionar sin servicio IA.\n```\n\n```\n[Feature] Mejorar extracción OCR de facturas ES en ai-service/main.py.\nReglas regex/NIF en main.py; probar con MyFiles/ALimpiezasMobile/test_ocr_invoice.pdf.\nNo romper endpoint /api/v1/extract-invoice contrato JSON existente.\n```\n\n### Refactor\n\n```\n[Refactor] Extraer lógica fiscal duplicada en FiscalModels.php sin cambiar cálculos 303/130.\nUsa replace_symbol_body Serena. Tests manuales: MobileFiscal borradores trimestre actual.\nScope mínimo; no tocar Dinamic/.\n```\n\n```\n[Refactor] Unificar lectura settings alimpiezas en MobileConfig.\nBusca Tools::settings('alimpiezas' en Plugins/ALimpiezasMobile solo.\nMantén compatibilidad expense_mode manual|ocr|ai.\n```\n\n### Deploy\n\n```\n[Deploy] Arrancar stack ALimpiezas en Windows limpio.\nEjecuta tools/start-stack.ps1, verifica Apache/mysql, curl :8090/health y /ALimpiezas.\nDocumenta IP LAN para iPhone. Opcional: tools/install-autostart.ps1.\n```\n\n```\n[Deploy] Rebuild completo plugin tras merge.\nSecuencia: composer install, php tools/bootstrap-plugin.php,\nai-service-manager -Restart, limpiar MyFiles/Cache/Twig si vistas rotas.\n```\n\n### QA\n\n```\n[QA] Checklist regresión móvil ALimpiezasMobile.\nProbar: login, MobileHome, gasto manual, gasto OCR (8090 up), MobileFiscal 303,\nMobileVoice comando local, MobileSettings cambio modo, PWA manifest/sw.js.\nReportar rutas y capturas de error.\n```\n\n```\n[QA] Verificar flujo cola ai_expense_queue end-to-end.\nupload → pending → get_item JSON → confirm → FacturaProveedor en ERP escritorio.\nreject debe marcar rejected sin crear factura.\n```\n\n---\n\n## Mapa rápido de archivos clave\n\n| Tarea | Ruta |\n|-------|------|\n| Settings / modos | `Plugins/ALimpiezasMobile/Lib/MobileConfig.php` |\n| Cola y upload | `Plugins/ALimpiezasMobile/Lib/AiExpenseProcessor.php` |\n| Crear factura gasto | `Plugins/ALimpiezasMobile/Lib/ExpenseInvoiceCreator.php` |\n| OCR / API Python | `ai-service/main.py` |\n| UI layout móvil | `Plugins/ALimpiezasMobile/View/Master/MobileLayout.html.twig` |\n| JS gastos | `Plugins/ALimpiezasMobile/Assets/JS/mobile.js` |\n| Deploy plugin | `tools/bootstrap-plugin.php` |\n| Arranque stack | `tools/start-stack.ps1` |\n| Manual agente | `docs/MANUAL-AGENTE-IA.md` |\n\n---\n\n## Restricciones agente (Bridge móvil)\n\n- Preguntas simples → responder sin herramientas.\n- No grep/glob recursivo en `vendor/`, `node_modules/`, `Core/`, `Dinamic/` salvo petición explícita.\n- Preferir archivos concretos y herramientas Serena simbólicas.\n- Editar `Plugins/`, desplegar con bootstrap; assets públicos solo vía `Dinamic/Assets/`.\n\n---\n\n*Memoria Serena persistida en `alimpiezas/project-knowledge` para sesiones futuras.*"},"at":1782305693330},{"event":"status","data":{"text":"Procesando…"},"at":1782305693310},{"event":"phase_saved","data":{"phaseId":"knowledge","progress":{"status":"done","completedAt":"2026-06-24T12:54:53.323Z","name":"Conocimiento","summary":"Consolidaré el conocimiento del proyecto leyendo memorias existentes y archivos clave fuera de Core/Dinamic.\nGuardando el conocimiento consolidado en memoria Serena para futuras sesiones.\n# ALimpiezas — Base de conocimiento del proyecto\n\n> **Proyecto:** `C:\\xampp\\htdocs\\ALimpiezas`  \n> **Actualizado:** 2026-06-24 · Memoria Serena: `alimpiezas/project-knowledge`  \n> **Referencia técnica:** `docs/MANUAL-AGENTE-IA.md`\n\n---\n\n## 1. Resumen ejecutivo\n\n**ALimpiezas** es un ERP basado en **FacturaScripts 2026.4** (PHP 8.1, Twig, Bootstrap 5) desplegado en **XAMPP** con ruta web `/ALimpiezas` y base de","fullText":"Consolidaré el conocimiento del proyecto leyendo memorias existentes y archivos clave fuera de Core/Dinamic.\nGuardando el conocimiento consolidado en memoria Serena para futuras sesiones.\n# ALimpiezas — Base de conocimiento del proyecto\n\n> **Proyecto:** `C:\\xampp\\htdocs\\ALimpiezas`  \n> **Actualizado:** 2026-06-24 · Memoria Serena: `alimpiezas/project-knowledge`  \n> **Referencia técnica:** `docs/MANUAL-AGENTE-IA.md`\n\n---\n\n## 1. Resumen ejecutivo\n\n**ALimpiezas** es un ERP basado en **FacturaScripts 2026.4** (PHP 8.1, Twig, Bootstrap 5) desplegado en **XAMPP** con ruta web `/ALimpiezas` y base de datos MariaDB `alimpiezas_fs`. La personalización principal es el plugin **ALimpiezasMobile**: PWA mobile-first para autónomos (gastos, fiscal 303/130, voz). El código editable vive en `Plugins/`; `Dinamic/` es generado por `Plugins::deploy()` — no editar directamente. Un microservicio **FastAPI** en puerto **8090** (`ai-service/`) ofrece OCR local (Tesseract), extracción PDF y transcripción Whisper opcional. El flujo de gastos usa cola `ai_expense_queue` → confirmación humana → `FacturaProveedor` vía `ExpenseInvoiceCreator`. Scripts en `tools/` automatizan arranque (Apache, MySQL, IA, bandeja sistema).\n\n---\n\n## 2. Glosario\n\n| Término | Significado |\n|---------|-------------|\n| **FacturaScripts (FS)** | ERP open source LGPL; núcleo en `Core/`, capa dinámica en `Dinamic/` |\n| **Dinamic/** | Copia/symlink desplegada de plugins activos; clases `FacturaScripts\\Dinamic\\...` |\n| **Plugin** | Extensión en `Plugins/<Nombre>/`; se activa y despliega con `Plugins::enable` + `deploy` |\n| **ALimpiezasMobile** | Plugin PWA móvil: gastos, fiscal, voz, configuración independiente |\n| **FacturaProveedor** | Factura de compra/gasto recibida de un proveedor |\n| **Proveedor** | Tercero emisor de facturas de gasto; se crea/busca por CIF en el flujo IA |\n| **ai_expense_queue** | Tabla/cola de revisión: OCR/manual → usuario confirma → factura |\n| **expense_mode** | Modo gastos: `manual` (sin IA), `ocr` (Tesseract local), `ai` (OCR+reglas) |\n| **303 / 130** | Modelos fiscales trimestrales (IVA y pago fraccionado IRPF autónomo) |\n| **MobileConfig** | Lib que lee settings grupo `alimpiezas` (modo, URL servicio, régimen) |\n| **ExpenseInvoiceCreator** | Crea `FacturaProveedor` + línea IVA21 + adjunto desde cola aprobada |\n| **AiExpenseProcessor** | Upload, OCR vía servicio, CRUD cola, confirm/reject |\n| **VoiceCommandBridge** | Parsea texto voz → intención navegación (`MobileExpenses`, etc.) |\n| **FiscalModels / FiscalCalendar** | Borradores 303/130 y vencimientos AEAT |\n| **PWA** | App instalable: `manifest.json`, `sw.js` en `Dinamic/Assets/` |\n| **bootstrap-plugin.php** | CLI: activa ALimpiezasMobile + rebuild Dinamic + limpia caché |\n| **ai-service-manager.ps1** | Start/stop/restart Uvicorn :8090 |\n| **start-stack.ps1** | Arranque completo: Apache, MySQL, IA, bandeja tray |\n| **Tools::settings('alimpiezas', ...)** | API FacturaScripts para configuración persistente |\n| **AttachedFileRelation** | Vincula imagen/PDF original a la factura creada |\n| **Cursor Bridge** | Sincronización agente IDE ↔ iPhone vía `.cursor-bridge/` |\n\n---\n\n## 3. FAQ dev\n\n### ¿Dónde edito código móvil vs ERP core?\n\nEdita **solo** `Plugins/ALimpiezasMobile/` (Controller, Lib, View, Assets, Model, Table). Tras cambios ejecuta `php tools/bootstrap-plugin.php`. **No** modifiques `Dinamic/` a mano salvo depuración puntual — se sobrescribe en deploy.\n\n### ¿Por qué los CSS/JS dan 403 Forbidden?\n\nApache reescribe todo a `index.php` excepto `Dinamic/Assets/` y `node_modules/`. Las URLs públicas deben ser `asset('Dinamic/Assets/...')`, nunca `Plugins/.../Assets/`. Tras cambiar Assets del plugin → bootstrap.\n\n### ¿Cuándo necesito el puerto 8090?\n\nSolo si `expense_mode` es `ocr` o `ai`, o si `voice_use_service=1`. Modo **manual**, fiscal y voz local **no** requieren el microservicio. Comprobar: `curl http://127.0.0.1:8090/health`.\n\n### ¿Cómo funciona el flujo de un gasto con foto?\n\nPOST `upload` → archivo guardado → si OCR/AI, llamada `/api/v1/extract-invoice` → fila en `ai_expense_queue` (pending) → modal confirmación → POST `confirm` → `ExpenseInvoiceCreator::createFromQueue` → `FacturaProveedor` + adjunto → cola `approved`.\n\n### ¿Cómo añado un setting nuevo?\n\n1. Default en `Init::update()` con `Tools::settingsSet('alimpiezas', ...)`.  \n2. Lectura en `MobileConfig` o Lib correspondiente.  \n3. UI en `MobileSettings` (form POST separado de Gastos).  \n4. Bootstrap plugin.\n\n### ¿Qué hace `Plugins::deploy(true, true)`?\n\nRegenera `Dinamic/` (clases, vistas, assets copiados), actualiza tablas vía `Init::update()` y limpia caché Twig/campos. Obligatorio tras cambiar estructura plugin o assets.\n\n### Error Errno 10048 / puerto 8090 ocupado\n\nProceso zombie Uvicorn. Usar `.\\tools\\ai-service-manager.ps1 -Restart` o `.\\tools\\port-services.ps1 -Port 8090` para identificar PID y liberar.\n\n### ¿Cómo pruebo desde iPhone en LAN?\n\nEn móvil usar `http://<IP-PC>/ALimpiezas/MobileHome`, **nunca** `localhost`. PC con XAMPP + stack arrancado (`.\\tools\\start-stack.ps1`). PWA: Safari → Compartir → Añadir a pantalla de inicio.\n\n---\n\n## 4. Checklist onboarding\n\n- [ ] **Entorno:** XAMPP (Apache + MySQL), PHP ≥8.1, Composer, Python 3 + venv para `ai-service/`\n- [ ] **Clonar/abrir:** `C:\\xampp\\htdocs\\ALimpiezas`\n- [ ] **Dependencias PHP:** `composer install --ignore-platform-req=ext-pgsql`\n- [ ] **Config:** revisar `config.php` (BD `alimpiezas_fs`, ruta `/ALimpiezas`); no commitear secretos\n- [ ] **Plugin:** `php tools/bootstrap-plugin.php` → URL móvil en salida\n- [ ] **IA (opcional):** instalar Tesseract OCR; `pip install -r ai-service/requirements.txt`; `.\\tools\\ai-service-manager.ps1 -Start`\n- [ ] **Stack completo:** `.\\tools\\start-stack.ps1` (Apache, MySQL, IA, bandeja)\n- [ ] **Smoke test:** login `http://localhost/ALimpiezas/MobileHome`, `/health`, CSS `Dinamic/Assets/CSS/mobile.css`\n- [ ] **Leer docs:** `docs/MANUAL-AGENTE-IA.md`, `docs/MANUAL-USUARIO.md`\n- [ ] **Convención edit:** `Plugins/ALimpiezasMobile/` → bootstrap → probar en móvil y escritorio\n- [ ] **Serena:** usar `find_symbol` / `replace_symbol_body` en símbolos concretos; evitar grep masivo en `Core/`, `Dinamic/`, `vendor/`\n- [ ] **Bridge móvil:** restricciones modo ligero/completo en prompts desde iPhone (`.cursor-bridge/`)\n\n---\n\n## 5. Prompts guardados\n\n### Bugfix\n\n```\n[Bugfix] En ALimpiezas, al confirmar un gasto en MobileExpenses aparece error X.\nRevisa Plugins/ALimpiezasMobile/Lib/ExpenseInvoiceCreator.php y AiExpenseProcessor.php.\nUsa Serena find_symbol antes de editar. Tras fix: php tools/bootstrap-plugin.php.\nNo escanees Core/ ni Dinamic/ salvo referencia.\n```\n\n```\n[Bugfix] Assets móvil 403 Forbidden en /ALimpiezas/Dinamic/Assets/...\nVerifica .htaccess, rutas asset() en Twig Master/MobileLayout.html.twig,\ny que bootstrap-plugin haya copiado Assets a Dinamic/.\n```\n\n```\n[Bugfix] Microservicio IA puerto 8090 no responde /health.\nDiagnostica con tools/port-services.ps1 y ai-service-manager.ps1 -Restart.\nRevisa Tesseract en ai-service/main.py si OCR falla.\n```\n\n### Feature\n\n```\n[Feature] Añadir campo Y al formulario confirmación gastos móvil.\nArchivos: View/MobileExpenses.html.twig, Assets/JS/mobile.js,\nModel/Table ai_expense_queue, Init.php migración, AiExpenseProcessor confirm.\nDeploy: bootstrap-plugin.php. Mantén modo manual sin depender de 8090.\n```\n\n```\n[Feature] Nueva intención de voz \"ir a ayuda\" en VoiceCommandBridge.\nActualiza VoiceCommandBridge.php, MobileVoice.php, voice.js y traducción es_ES.json.\nFallback localParse debe funcionar sin servicio IA.\n```\n\n```\n[Feature] Mejorar extracción OCR de facturas ES en ai-service/main.py.\nReglas regex/NIF en main.py; probar con MyFiles/ALimpiezasMobile/test_ocr_invoice.pdf.\nNo romper endpoint /api/v1/extract-invoice contrato JSON existente.\n```\n\n### Refactor\n\n```\n[Refactor] Extraer lógica fiscal duplicada en FiscalModels.php sin cambiar cálculos 303/130.\nUsa replace_symbol_body Serena. Tests manuales: MobileFiscal borradores trimestre actual.\nScope mínimo; no tocar Dinamic/.\n```\n\n```\n[Refactor] Unificar lectura settings alimpiezas en MobileConfig.\nBusca Tools::settings('alimpiezas' en Plugins/ALimpiezasMobile solo.\nMantén compatibilidad expense_mode manual|ocr|ai.\n```\n\n### Deploy\n\n```\n[Deploy] Arrancar stack ALimpiezas en Windows limpio.\nEjecuta tools/start-stack.ps1, verifica Apache/mysql, curl :8090/health y /ALimpiezas.\nDocumenta IP LAN para iPhone. Opcional: tools/install-autostart.ps1.\n```\n\n```\n[Deploy] Rebuild completo plugin tras merge.\nSecuencia: composer install, php tools/bootstrap-plugin.php,\nai-service-manager -Restart, limpiar MyFiles/Cache/Twig si vistas rotas.\n```\n\n### QA\n\n```\n[QA] Checklist regresión móvil ALimpiezasMobile.\nProbar: login, MobileHome, gasto manual, gasto OCR (8090 up), MobileFiscal 303,\nMobileVoice comando local, MobileSettings cambio modo, PWA manifest/sw.js.\nReportar rutas y capturas de error.\n```\n\n```\n[QA] Verificar flujo cola ai_expense_queue end-to-end.\nupload → pending → get_item JSON → confirm → FacturaProveedor en ERP escritorio.\nreject debe marcar rejected sin crear factura.\n```\n\n---\n\n## Mapa rápido de archivos clave\n\n| Tarea | Ruta |\n|-------|------|\n| Settings / modos | `Plugins/ALimpiezasMobile/Lib/MobileConfig.php` |\n| Cola y upload | `Plugins/ALimpiezasMobile/Lib/AiExpenseProcessor.php` |\n| Crear factura gasto | `Plugins/ALimpiezasMobile/Lib/ExpenseInvoiceCreator.php` |\n| OCR / API Python | `ai-service/main.py` |\n| UI layout móvil | `Plugins/ALimpiezasMobile/View/Master/MobileLayout.html.twig` |\n| JS gastos | `Plugins/ALimpiezasMobile/Assets/JS/mobile.js` |\n| Deploy plugin | `tools/bootstrap-plugin.php` |\n| Arranque stack | `tools/start-stack.ps1` |\n| Manual agente | `docs/MANUAL-AGENTE-IA.md` |\n\n---\n\n## Restricciones agente (Bridge móvil)\n\n- Preguntas simples → responder sin herramientas.\n- No grep/glob recursivo en `vendor/`, `node_modules/`, `Core/`, `Dinamic/` salvo petición explícita.\n- Preferir archivos concretos y herramientas Serena simbólicas.\n- Editar `Plugins/`, desplegar con bootstrap; assets públicos solo vía `Dinamic/Assets/`.\n\n---\n\n*Memoria Serena persistida en `alimpiezas/project-knowledge` para sesiones futuras.*"}},"at":1782305693325},{"event":"done","data":{"ok":true,"bridgeSessionId":"bs-1782305693318-mtk83g","agentId":"agent-006b8c1f-384f-440c-a684-af98788d1416","runId":"run-0dabf42e-c317-4c81-9e6f-c907d3e15f79","status":"finished","result":"# ALimpiezas — Base de conocimiento del proyecto\n\n> **Proyecto:** `C:\\xampp\\htdocs\\ALimpiezas`  \n> **Actualizado:** 2026-06-24 · Memoria Serena: `alimpiezas/project-knowledge`  \n> **Referencia técnica:** `docs/MANUAL-AGENTE-IA.md`\n\n---\n\n## 1. Resumen ejecutivo\n\n**ALimpiezas** es un ERP basado en **FacturaScripts 2026.4** (PHP 8.1, Twig, Bootstrap 5) desplegado en **XAMPP** con ruta web `/ALimpiezas` y base de datos MariaDB `alimpiezas_fs`. La personalización principal es el plugin **ALimpiezasMobile**: PWA mobile-first para autónomos (gastos, fiscal 303/130, voz). El código editable vive en `Plugins/`; `Dinamic/` es generado por `Plugins::deploy()` — no editar directamente. Un microservicio **FastAPI** en puerto **8090** (`ai-service/`) ofrece OCR local (Tesseract), extracción PDF y transcripción Whisper opcional. El flujo de gastos usa cola `ai_expense_queue` → confirmación humana → `FacturaProveedor` vía `ExpenseInvoiceCreator`. Scripts en `tools/` automatizan arranque (Apache, MySQL, IA, bandeja sistema).\n\n---\n\n## 2. Glosario\n\n| Término | Significado |\n|---------|-------------|\n| **FacturaScripts (FS)** | ERP open source LGPL; núcleo en `Core/`, capa dinámica en `Dinamic/` |\n| **Dinamic/** | Copia/symlink desplegada de plugins activos; clases `FacturaScripts\\Dinamic\\...` |\n| **Plugin** | Extensión en `Plugins/<Nombre>/`; se activa y despliega con `Plugins::enable` + `deploy` |\n| **ALimpiezasMobile** | Plugin PWA móvil: gastos, fiscal, voz, configuración independiente |\n| **FacturaProveedor** | Factura de compra/gasto recibida de un proveedor |\n| **Proveedor** | Tercero emisor de facturas de gasto; se crea/busca por CIF en el flujo IA |\n| **ai_expense_queue** | Tabla/cola de revisión: OCR/manual → usuario confirma → factura |\n| **expense_mode** | Modo gastos: `manual` (sin IA), `ocr` (Tesseract local), `ai` (OCR+reglas) |\n| **303 / 130** | Modelos fiscales trimestrales (IVA y pago fraccionado IRPF autónomo) |\n| **MobileConfig** | Lib que lee settings grupo `alimpiezas` (modo, URL servicio, régimen) |\n| **ExpenseInvoiceCreator** | Crea `FacturaProveedor` + línea IVA21 + adjunto desde cola aprobada |\n| **AiExpenseProcessor** | Upload, OCR vía servicio, CRUD cola, confirm/reject |\n| **VoiceCommandBridge** | Parsea texto voz → intención navegación (`MobileExpenses`, etc.) |\n| **FiscalModels / FiscalCalendar** | Borradores 303/130 y vencimientos AEAT |\n| **PWA** | App instalable: `manifest.json`, `sw.js` en `Dinamic/Assets/` |\n| **bootstrap-plugin.php** | CLI: activa ALimpiezasMobile + rebuild Dinamic + limpia caché |\n| **ai-service-manager.ps1** | Start/stop/restart Uvicorn :8090 |\n| **start-stack.ps1** | Arranque completo: Apache, MySQL, IA, bandeja tray |\n| **Tools::settings('alimpiezas', ...)** | API FacturaScripts para configuración persistente |\n| **AttachedFileRelation** | Vincula imagen/PDF original a la factura creada |\n| **Cursor Bridge** | Sincronización agente IDE ↔ iPhone vía `.cursor-bridge/` |\n\n---\n\n## 3. FAQ dev\n\n### ¿Dónde edito código móvil vs ERP core?\n\nEdita **solo** `Plugins/ALimpiezasMobile/` (Controller, Lib, View, Assets, Model, Table). Tras cambios ejecuta `php tools/bootstrap-plugin.php`. **No** modifiques `Dinamic/` a mano salvo depuración puntual — se sobrescribe en deploy.\n\n### ¿Por qué los CSS/JS dan 403 Forbidden?\n\nApache reescribe todo a `index.php` excepto `Dinamic/Assets/` y `node_modules/`. Las URLs públicas deben ser `asset('Dinamic/Assets/...')`, nunca `Plugins/.../Assets/`. Tras cambiar Assets del plugin → bootstrap.\n\n### ¿Cuándo necesito el puerto 8090?\n\nSolo si `expense_mode` es `ocr` o `ai`, o si `voice_use_service=1`. Modo **manual**, fiscal y voz local **no** requieren el microservicio. Comprobar: `curl http://127.0.0.1:8090/health`.\n\n### ¿Cómo funciona el flujo de un gasto con foto?\n\nPOST `upload` → archivo guardado → si OCR/AI, llamada `/api/v1/extract-invoice` → fila en `ai_expense_queue` (pending) → modal confirmación → POST `confirm` → `ExpenseInvoiceCreator::createFromQueue` → `FacturaProveedor` + adjunto → cola `approved`.\n\n### ¿Cómo añado un setting nuevo?\n\n1. Default en `Init::update()` con `Tools::settingsSet('alimpiezas', ...)`.  \n2. Lectura en `MobileConfig` o Lib correspondiente.  \n3. UI en `MobileSettings` (form POST separado de Gastos).  \n4. Bootstrap plugin.\n\n### ¿Qué hace `Plugins::deploy(true, true)`?\n\nRegenera `Dinamic/` (clases, vistas, assets copiados), actualiza tablas vía `Init::update()` y limpia caché Twig/campos. Obligatorio tras cambiar estructura plugin o assets.\n\n### Error Errno 10048 / puerto 8090 ocupado\n\nProceso zombie Uvicorn. Usar `.\\tools\\ai-service-manager.ps1 -Restart` o `.\\tools\\port-services.ps1 -Port 8090` para identificar PID y liberar.\n\n### ¿Cómo pruebo desde iPhone en LAN?\n\nEn móvil usar `http://<IP-PC>/ALimpiezas/MobileHome`, **nunca** `localhost`. PC con XAMPP + stack arrancado (`.\\tools\\start-stack.ps1`). PWA: Safari → Compartir → Añadir a pantalla de inicio.\n\n---\n\n## 4. Checklist onboarding\n\n- [ ] **Entorno:** XAMPP (Apache + MySQL), PHP ≥8.1, Composer, Python 3 + venv para `ai-service/`\n- [ ] **Clonar/abrir:** `C:\\xampp\\htdocs\\ALimpiezas`\n- [ ] **Dependencias PHP:** `composer install --ignore-platform-req=ext-pgsql`\n- [ ] **Config:** revisar `config.php` (BD `alimpiezas_fs`, ruta `/ALimpiezas`); no commitear secretos\n- [ ] **Plugin:** `php tools/bootstrap-plugin.php` → URL móvil en salida\n- [ ] **IA (opcional):** instalar Tesseract OCR; `pip install -r ai-service/requirements.txt`; `.\\tools\\ai-service-manager.ps1 -Start`\n- [ ] **Stack completo:** `.\\tools\\start-stack.ps1` (Apache, MySQL, IA, bandeja)\n- [ ] **Smoke test:** login `http://localhost/ALimpiezas/MobileHome`, `/health`, CSS `Dinamic/Assets/CSS/mobile.css`\n- [ ] **Leer docs:** `docs/MANUAL-AGENTE-IA.md`, `docs/MANUAL-USUARIO.md`\n- [ ] **Convención edit:** `Plugins/ALimpiezasMobile/` → bootstrap → probar en móvil y escritorio\n- [ ] **Serena:** usar `find_symbol` / `replace_symbol_body` en símbolos concretos; evitar grep masivo en `Core/`, `Dinamic/`, `vendor/`\n- [ ] **Bridge móvil:** restricciones modo ligero/completo en prompts desde iPhone (`.cursor-bridge/`)\n\n---\n\n## 5. Prompts guardados\n\n### Bugfix\n\n```\n[Bugfix] En ALimpiezas, al confirmar un gasto en MobileExpenses aparece error X.\nRevisa Plugins/ALimpiezasMobile/Lib/ExpenseInvoiceCreator.php y AiExpenseProcessor.php.\nUsa Serena find_symbol antes de editar. Tras fix: php tools/bootstrap-plugin.php.\nNo escanees Core/ ni Dinamic/ salvo referencia.\n```\n\n```\n[Bugfix] Assets móvil 403 Forbidden en /ALimpiezas/Dinamic/Assets/...\nVerifica .htaccess, rutas asset() en Twig Master/MobileLayout.html.twig,\ny que bootstrap-plugin haya copiado Assets a Dinamic/.\n```\n\n```\n[Bugfix] Microservicio IA puerto 8090 no responde /health.\nDiagnostica con tools/port-services.ps1 y ai-service-manager.ps1 -Restart.\nRevisa Tesseract en ai-service/main.py si OCR falla.\n```\n\n### Feature\n\n```\n[Feature] Añadir campo Y al formulario confirmación gastos móvil.\nArchivos: View/MobileExpenses.html.twig, Assets/JS/mobile.js,\nModel/Table ai_expense_queue, Init.php migración, AiExpenseProcessor confirm.\nDeploy: bootstrap-plugin.php. Mantén modo manual sin depender de 8090.\n```\n\n```\n[Feature] Nueva intención de voz \"ir a ayuda\" en VoiceCommandBridge.\nActualiza VoiceCommandBridge.php, MobileVoice.php, voice.js y traducción es_ES.json.\nFallback localParse debe funcionar sin servicio IA.\n```\n\n```\n[Feature] Mejorar extracción OCR de facturas ES en ai-service/main.py.\nReglas regex/NIF en main.py; probar con MyFiles/ALimpiezasMobile/test_ocr_invoice.pdf.\nNo romper endpoint /api/v1/extract-invoice contrato JSON existente.\n```\n\n### Refactor\n\n```\n[Refactor] Extraer lógica fiscal duplicada en FiscalModels.php sin cambiar cálculos 303/130.\nUsa replace_symbol_body Serena. Tests manuales: MobileFiscal borradores trimestre actual.\nScope mínimo; no tocar Dinamic/.\n```\n\n```\n[Refactor] Unificar lectura settings alimpiezas en MobileConfig.\nBusca Tools::settings('alimpiezas' en Plugins/ALimpiezasMobile solo.\nMantén compatibilidad expense_mode manual|ocr|ai.\n```\n\n### Deploy\n\n```\n[Deploy] Arrancar stack ALimpiezas en Windows limpio.\nEjecuta tools/start-stack.ps1, verifica Apache/mysql, curl :8090/health y /ALimpiezas.\nDocumenta IP LAN para iPhone. Opcional: tools/install-autostart.ps1.\n```\n\n```\n[Deploy] Rebuild completo plugin tras merge.\nSecuencia: composer install, php tools/bootstrap-plugin.php,\nai-service-manager -Restart, limpiar MyFiles/Cache/Twig si vistas rotas.\n```\n\n### QA\n\n```\n[QA] Checklist regresión móvil ALimpiezasMobile.\nProbar: login, MobileHome, gasto manual, gasto OCR (8090 up), MobileFiscal 303,\nMobileVoice comando local, MobileSettings cambio modo, PWA manifest/sw.js.\nReportar rutas y capturas de error.\n```\n\n```\n[QA] Verificar flujo cola ai_expense_queue end-to-end.\nupload → pending → get_item JSON → confirm → FacturaProveedor en ERP escritorio.\nreject debe marcar rejected sin crear factura.\n```\n\n---\n\n## Mapa rápido de archivos clave\n\n| Tarea | Ruta |\n|-------|------|\n| Settings / modos | `Plugins/ALimpiezasMobile/Lib/MobileConfig.php` |\n| Cola y upload | `Plugins/ALimpiezasMobile/Lib/AiExpenseProcessor.php` |\n| Crear factura gasto | `Plugins/ALimpiezasMobile/Lib/ExpenseInvoiceCreator.php` |\n| OCR / API Python | `ai-service/main.py` |\n| UI layout móvil | `Plugins/ALimpiezasMobile/View/Master/MobileLayout.html.twig` |\n| JS gastos | `Plugins/ALimpiezasMobile/Assets/JS/mobile.js` |\n| Deploy plugin | `tools/bootstrap-plugin.php` |\n| Arranque stack | `tools/start-stack.ps1` |\n| Manual agente | `docs/MANUAL-AGENTE-IA.md` |\n\n---\n\n## Restricciones agente (Bridge móvil)\n\n- Preguntas simples → responder sin herramientas.\n- No grep/glob recursivo en `vendor/`, `node_modules/`, `Core/`, `Dinamic/` salvo petición explícita.\n- Preferir archivos concretos y herramientas Serena simbólicas.\n- Editar `Plugins/`, desplegar con bootstrap; assets públicos solo vía `Dinamic/Assets/`.\n\n---\n\n*Memoria Serena persistida en `alimpiezas/project-knowledge` para sesiones futuras.*","changeSessionId":"cs-1782305637961-gs6fxx","pendingChanges":0,"changes":[],"transcriptPath":null,"notifyTitle":"Agente terminado","notifyBody":"Consolidaré el conocimiento del proyecto leyendo memorias existentes y archivos clave fuera de Core/Dinamic.\nGuardando e"},"at":1782305693325}]}
