# Progress Tracker & Master Document: Cloud Storage Folder Path Resolution

## Status
- [ ] Phase 0: Initialization (Setup)
- [x] Phase 1: Context Discovery (Explorer)
- [x] Phase 2: Technical Design (Architect)
- [x] Phase 3: Technical Planning (Planner)
- [x] Phase 4: Implementation (Phase 1 MVP)
- [ ] Phase 5: Verification & Handoff

## Phase 0: Initialization
- [x] Create feature branch `feature/cloud-storage-path-resolution`
- [x] Initialize blackboard `php artisan agent:mem:init`
- [x] Create Progress Tracker (this file)

---

# 📖 MASTER IMPLEMENTATION DOCUMENT

This document guarantees context persistence across agent sessions for the two-phased roadmap.

## Fase 1: MVP + Folder Resolution Caching (Entrega Inicial)

**Objetivo:** Resolver el problema crítico de la subida a raíz, implementar soporte absoluto de `path` y `folder_id`, y optimizar la recursividad de Google Drive para evitar fallos de API limits (N+1).

### 1.1 Core Infrastructure & Normalization
- **Archivo:** `src/App/Services/CloudServices/Helpers/CloudStoragePathHelper.php` (Nuevo)
- **Implementación:**
  - Crear un Trait/Clase Estática `CloudStoragePathHelper`.
  - Método `normalizePath(string $path): string`. Reglas: Trim slashes, rechazar `.` y `..` (lanzar `InvalidPathException` HTTP 422).
  - Validar máximo 5 niveles de profundidad y 255 caracteres totales.
  - Rechazar caracteres inválidos según el proveedor evaluado.

### 1.2 Actualización de Contratos
- **Archivos:** `CloudStorageProvider.php`, `CloudStorageService.php`
- **Implementación:**
  - Agregar parámetro a la interfaz: `public function upload(string $path, $contents, ?string $filename = null, ?string $folderId = null): string;`
  - Inyectar desde el form request `$folderId` a `$tempStorageService->uploadFileFromUpload()` en el `CloudStorageController`.

### 1.3 Microsoft OneDrive Integration
- **Archivo:** `MicrosoftGraphProvider.php`
- **Implementación:**
  - Si `$folderId` existe y no es 'root', construir endpoint tipo ID: `items/{$folderId}:/{$filename}:/content`.
  - Si se usa ruta, usar path addressing: `items/root:/{$normalizedPath}/{$filename}:/content`.
  - Check de Muerte (GET a ruta padre antes de POST). Si Graph da 404, disparar `InvalidPathException` ("La ruta no existe"). Sin auto-creación.

### 1.4 Google Drive Integration & Cache Isolation (Mejora Crítica)
- **Archivo:** `GoogleWorkspaceProvider.php`
- **Implementación:**
  - Eliminar el ineficiente `dirname()`.
  - Implementar Caminata Recursiva (Folder Walk): `resolveFolderChain(string $path): string`.
  - **Aislamiento de Caché (IMPORTANTE):** Para evitar mezclar llaves en Redis, usar la propiedad única de la configuración que amarra al tenant. E.j., `Cache::remember("gdrive_folder_config_{$this->config->id}_" . md5($path), 3600, ... )`. 
  - Si existe ambigüedad o falta una carpeta (404 virtual), fallar rápido con HTT 422.

### 1.5 Controller & Error Catching
- **Archivo:** `CloudStorageController.php`
- **Implementación:**
  - Try/catch envolviendo la llamada al proveedor.
  - Regla de Precedencia: `folder_id` sobreescribe `path` si ambos llegan.
  - Loguear errores a nivel aplicación sin exponer stack traces al JSON client. Devolver `success: false` y un `message` claro y humanizado.

---

## Fase 2: Enterprise Features (Futura Iteración/PR Separado)

**Objetivo:** Escalabilidad corporativa (Auto-create, Tenant Isolation, Async Tasks).

### 2.1 Auto-Creación Recursiva de Carpetas
- **Flujo Google:** En el "Folder Walk", en lugar de fallar, inyectar un POST a `files.create` mimeType Folder en el Level actual y continuar recursión.
- **Flujo Microsoft:** Emplear la sintaxis `@microsoft.graph.conflictBehavior = 'rename'` desde la raíz creando secuencialmente. 

### 2.2 Inversión de Root Context y Aislamiento Tenancy (Root Jail)
- Inyectar en BD o Service Context un "Master Folder ID" de ese tenant.
- En `CloudStoragePathHelper`, prepender este Jail Hash a todo payload de usuario (e.j. `Facturas/` -> `[ROOT_UUID]/Facturas/`).

### 2.3 Estrategias y Políticas de Conflicto Configurable
- Permitir configuración UI: `rename`, `replace`, `abort`.
- Alterar las banderas API (ej: Drive API upload `uploadType=multipart` junto con un Delete/Update del ID detectado si la política es `replace`).

### 2.4 Hacia el Event Backbone (Subidas Asíncronas)
- Recibir multi-part de Nginx. Escribir temporal. Enviar Dispatch a Work Queue (Cloud Tasks/PubSub/Rabbit).
- Worker procesa la validación larga (Folder Walk). Notifica cliente vía websockets (Laravel Echo).

---

## Fase 3: Consolidación UX & Cumplimiento (Sesión Actual)

### 3.1 Unificación de Interfaz (Combobox)
- [x] Implementar un `<input>` con `<datalist>` dinámico en `media/form.blade.php`.
- [x] Vincular selección de carpetas con el campo oculto `folder_id` mediante Alpine.js.
- [x] Limpiar estados al cambiar de proveedor para evitar IDs cruzados.

### 3.2 Cumplimiento Técnico (FR-4 & FR-5)
- [x] **FR-4 Precedencia**: Registrar `Log::warning` si llegan simultáneamente `path` e `id` en el controlador.
- [x] **FR-5 Observabilidad**: Enriquecer cada `Log::error` con `[provider, user_id, tenant_id, path, folder_id]`.
- [x] **Validación**: Verificar que el `folder_id` sea localizable antes de delegar al Job asíncrono.

### 3.3 Estabilidad de UI
- [x] Corregir el evento global `fileuploadsuccess` para garantizar el refresco del componente `MediaTable`.

## Fase 4: Verificación Final (Criterios de Aceptación)
- [x] **AC-6**: Validación de rechazo de rutas relativas (`../`).
- [x] **AC-8**: Validación de manejo de ambigüedad en Google Drive.
- [x] Cierre de rama y Walkthrough final.

---

## 3. Progress Log

| ID | Phase | Task / Bug | Status | Notes |
|----|-------|------------|--------|-------|
| 01 | **Fix** | **Bug 1 — Test fixture uses invalid conflict_strategy value** | ✅ Done | Replaced `'conflict_strategy' => 'fail'` with `'abort'` in `CloudStorageControllerTest.php` beforeEach fixture. 25 tests pass. |
| 02 | **Fix** | **Bug 2 — Renamed route names not updated in blade view** | ✅ Done | Updated `resources/views/tenant/integrations/mappings/sync/index.blade.php` to use `route('import')` and `route('import.status')` instead of old `importMapping`/`statusMapping`. New routes confirmed registered in `routes/import.php`. |
| 03 | **Rule Enrichment** | **Heuristic: Rename Completeness check** | ✅ Done | Added "Rename Completeness" check to `.agent/workflows/pr-reviewer.md` Step 6 and `.agent/workflows/backend-specialist.md` Protocol 5, §7. Prevents future renames from leaving orphaned callers in blade views and route files. |
| 04 | **Fix** | **Bug 3 — Google Drive conflict strategy validated only during collisions (fail-open)** | ✅ Done | Moved `$conflictStrategy` validation to unconditional method entry in `GoogleWorkspaceProvider::upload()`. Added regression test covering no-collision path. Amended `backend-specialist.md` Protocol 3 with "unconditional entry" heuristic. 11 tests pass, PHPStan clean. |
| 05 | **Fix** | **Bug 6 — Microsoft Graph path URLs lack percent-encoding for filenames and folder paths** | ✅ Done | Added `encodePathSegments()` private helper to `MicrosoftGraphProvider`. Applied per-segment `rawurlencode()` to `$actualFilename`, `$cleanPath`, `$path` (validatePathExists), and `$rootFolderPath` (assertFolderWithinRootJail) — 5 URL constructions total. `rawurlencode($graphConflictBehavior)` (already correct) not touched. 3 regression tests added (space in filename → %20, # in filename → %23, space in folder path → %20). 14 tests pass, 21 assertions, PHPStan clean. |
| 06 | **Fix** | **Bug 7 — Blade `{{ }}` HTML-escapes path/folderId inside Alpine x-data JS literals** | ✅ Done | Replaced `'{{ $path ?? '/'\ }}'\ and `'{{ $folderId ?? '' }}'\ with `@js($path ?? '/')`  and `@js($folderId ?? '')`  in `resources/views/livewire/media/form.blade.php` lines 101-102. `@js()` emits a JSON-encoded JS-safe value with own quoting; `{{ }}` applied `htmlspecialchars()`, garbling special chars (e.g. `'` → `&#039;`). Companion `cloud-services/form.blade.php` already used `@js()` correctly. 30 tests pass, PHPStan clean. |
| 07 | **Fix** | **Bug 8 — Redundant `method_exists` always-true guard removed from `CloudStorageController::listFiles()`** | ✅ Done | Removed `method_exists($tempStorageService, 'listAllFolders')` guard from `CloudStorageController.php`. `CloudStorageService::listAllFolders()` is unconditionally defined; provider-level guard already lives inside that method. Dead code removed; behavior identical. 30 tests pass. |
