scripts/apply_all_migrations.sh: aplica todas las migraciones de
supabase/migrations/ en orden de timestamp, una por una, con
ON_ERROR_STOP=1 (sin esto psql sigue tras un error SQL y devuelve exit
0 igual — set -e nunca lo vería).
scripts/setup_all.sh: orquesta la recuperación completa — migraciones,
catálogo de encuestas (supabase/seed.sql, paso agregado: sin él
surveys/questions quedan vacías y seed_prueba_50.sql no tiene nada que
referenciar), usuario platform_admin vía ADMIN_EMAIL/ADMIN_PASSWORD, y
datos de prueba (docs/dashboard/seed_prueba_50.sql).
No ejecutado — solo los archivos, a la espera de uso manual en el VPS.
dashboard-propuesta/seed_prueba_50.sql quedó con question_id de una
instancia anterior de la DB — rpc_submit_response rechazaba todo con
invalid_answer_reference. Mapeo code→question_id reconstruido por
vocabulario de opciones contra supabase/seed.sql (sin ambigüedad,
incluye desambiguar 6.1/6.2/6.4 por sus opciones únicas), UUIDs reales
tomados de la DB de producción. Mismas 50 filas, misma distribución
(seed=42), mismo formato de llamada — solo cambian los 13 question_id.
Idempotencia: DELETE acotado a survey_id + ip_hash LIKE 'seed-prueba-%'
en vez de TRUNCATE de las tablas completas (answers/responses pueden
tener datos reales de otras encuestas). Survey Demo queda en 'live' al
inicio y al final, explícito.
NO aplicado en VPS — solo el archivo, a la espera de aprobación.
/admin/responses: filtro survey dinámico (select, no hardcodeado),
paginación 50/página, link a vista detallada, conteos consistentes con
el filtro de preguntas permitidas (evita página fantasma al final).
/admin/responses/[id]: respuestas agrupadas por sección (derivado de
type='section', igual que el wizard — no existen section_number/
section_title, ver TECH-DEBT-011), instancias bajo "Familiar N".
RLS y exclusión de sensibles intactos en ambas vistas.
Fixes de revisión antes de cerrar: toggle expandir/colapsar movido a
un <button> real (evita anidar el link "Ver" dentro de un role=button),
formatDate/renderValue unificados en admin-format.ts, queries
independientes en paralelo (Promise.all).
P1: color de texto teal -> #494F4F en 5 clases del wizard (WCAG AA).
P2: try/catch en handleSubmit - error de red muestra estado error del wizard.
P3: flex-wrap + column-reverse en bottombar <=480px.
P4: onTouchEnd fallback en autocomplete barrio y ciudad.
Fix A: botón inline con ícono SVG (ojo/ojo tachado) en el campo
password de /login, alterna type password/text, aria-label según
estado. Sin librería nueva.
Fix B: .db-widget-star pierde el gradiente, hereda background blanco
de .db-widget igual que los demás cards. Borde y título teal intactos.
Tabla audit_log con RLS (solo platform_admin), trigger BEFORE UPDATE/DELETE
que aborta (append-only enforcement), triggers AFTER en user_roles y
surveys.status. Patrón SECURITY DEFINER reutilizado de auth_user_roles.
Event trigger DDL incluido (confirmado viable sin extensiones, ver
diagnóstico en ETAPA_AUDIT_LOG_PROMPT.md §4.1). Gap SEC-003 (TRUNCATE por
superusuario) documentado y aceptado, no resuelto.
NO aplicada en VPS — pendiente de aprobación.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sprint 4 completo (widgets Mirada Empresa, tema claro, 4F, 4I/release ya
estaban hechos antes de esta sesión). Documenta el incidente de colisión de
container_name en el VPS y su recuperación, y el cierre de TECH-DEBT-008
(auth de staff) en esta sesión. Próximos pasos quedan abiertos a decisión
del director (dashboard País/Humana, sprint de compliance, o mantenimiento
de dependencias).
GoTrue fallaba con "Database error querying schema" al hacer login porque
estas dos columnas de auth.users no tienen DEFAULT en este esquema y quedaban
NULL — el driver Go espera string, no NULL. Verificado en VPS: login
funciona después de este fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reescribir con CTE INSERT...RETURNING para encadenar auth.users + user_roles
en una sola sentencia SQL plana donde :variable sí funciona.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Protege /admin/* con Supabase Auth. Tabla user_roles con custom claims
(app_role, org_id) en JWT. Middleware Next.js redirige a /login sin sesión.
Página /login con email/password. /admin/responses migrado de service_role
a sesión de usuario autenticado.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mover el filtro 'Ninguna de las anteriores' del WHERE sobre a.value
al WHERE del subquery expandido, donde opt es ya un elemento individual
resultado de jsonb_array_elements_text. El operador #>> '{}' aplicado
sobre un array jsonb no filtraba elementos sino la representación textual
del array completo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Etapas 4A→4I completadas. Motor de flujo puro, wizard renderer,
instancias familiares, submit con validación server-side, dashboard
tres miradas (Mirada Empresa funcional), autocomplete barrios/ciudad.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CiudadSelectField: autocomplete sobre Object.keys(geoData[depto]),
mismo patrón que BarrioAutocompleteField (position:fixed, getBoundingClientRect,
scroll listener, teclado ARIA). Reutiliza clases CSS .barrio-autocomplete-*.
- Limpia el valor de 2.2 via useEffect cuando cambia departamento (2.1),
usando un ref estable para onChange para evitar dep cíclica.
- QuestionRenderer: branch code==='2.2' dentro del bloque single_choice;
pasa answers['2.1:default'] como prop departamento.
- Sin cambios al seed, form-engine ni RPCs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Issue A: barrio-autocomplete-list cambia a position:fixed + getBoundingClientRect()
para escapar el overflow-y:auto de wizard-answer-area que clippeaba el dropdown.
z-index: 20 → 30 (clearear sticky topbar/bottombar z-index:10 en stacking context raíz).
Listener de scroll cierra el dropdown si el contenedor scrollea.
Issue B: wizard-answer-area max-height 360px → 480px (desktop) / 320px → 400px (mobile)
para mostrar ~8 opciones completas. .wizard-answer-area.has-overflow { padding-bottom: 44px }
asegura que el fade ::after (28px) cubre espacio vacío, no texto de opción.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 RPCs de Mirada Empresa (E1-E10 + estrella Talento en riesgo) sobre
instrumento v3, k≥5 en dos niveles, helper _dash_es_cuidador (criterio
UNFPA). Fix de _dash_responses: A6/A7 → 2.5/2.7. Verificado contra 50
respuestas reales: métricas núcleo exactas, k-anonimato suprime segmentos
n<5, umbral de la estrella parametrizable. Modalidad por JOIN (TECH-DEBT-010).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RPC transaccional SECURITY DEFINER (rpc_submit_response) como único camino
de escritura. Dropea las policies WITH CHECK(true): cierra la falla donde
anon podía insertar cualquier cosa. Validación server-side (7 checks)
reusando el motor. Persistencia atómica con instance_id por instancia.
Verificado contra Postgres real: instancias separadas, atomicidad, RLS.
Riesgo residual de bypass por REST documentado (SEC-004).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
applyAnswer y isRequiredAndUnanswered pasan de wizard-form.tsx al motor
como funciones puras — eran lógica de flujo en el presentador (viola ADR-005).
Limpieza de respuestas huérfanas al remover un vínculo y validación required
por instancia, ahora testeables. 20/20 tests. Verificado end-to-end con navegador.
4E sin construcción pendiente: el mini-wizard es propiedad emergente del motor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Módulo puro que interpreta el esquema v3 y resuelve pantallas.
5 operadores (eq, eq_any, not_in, contains, eq_sole), acciones show/hide
(hide = veto absoluto). Instancias con ID estable por posición de opción,
secciones-pantalla. Navegación por ID estable. 16/16 tests Node en verde.
No reemplaza aún lib/conditional.ts de v2 (lo hace 4D al migrar el componente).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extiende questions con instance_generator/instance_of/hint.
Instrumento v3 completo (10 secciones, 50 preguntas) como datos declarativos.
Operadores de flujo para multiple_choice: contains, eq_sole.
Flujo del formulario en el esquema, no en presentación (ADR-005).
Seed solo reemplaza catálogo; no toca evidencia de consentimiento (ADR-003).
Extiende questions con instance_generator/instance_of/hint.
Instrumento v3 completo (10 secciones, 50 preguntas) como datos declarativos.
Operadores de flujo para multiple_choice: contains, eq_sole.
Flujo del formulario en el esquema, no en presentación (ADR-005).
Seed solo reemplaza catálogo; no toca evidencia de consentimiento (ADR-003).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extiende questions con instance_generator/instance_of/hint y documenta
los operadores contains/eq_sole en conditional_rules (ADR-005).
Reescribe seed.sql con el instrumento v3.0.0 completo: 10 secciones,
40 preguntas (2 consentimiento + 38), contenido 100% desde
Formulario_Mapeo_Corregido.xlsx (versión autoritativa). Flujo
declarativo en datos, no en presentación: 3.1='No' oculta 3.2-3.5;
"Ninguna" en 4.1 salta a S10; "solo niño sin discapacidad" deja
visibles 5.3, 8.4, 9.1, 9.4 y oculta el resto (decisión confirmada
sobre el Excel corregido).
SEED_SPEC.md reescrito como contrato v3.0.0.
No ejecutado en producción — solo archivos de migración y seed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Habilita respuestas múltiples por pregunta (mini-wizard de vínculos).
Nueva constraint UNIQUE(response_id, question_id, instance_id).
Aplicada en producción. Parte del Sprint 4 hacia v3.0.0.
Agrega organizations + surveys para los dos pilotos en espacio de pruebas
real, con su propio set de 44 preguntas (mismo instrumento que el survey
demo, IDs namespaced 02xx/03xx). Survey demo (...0001) intacto.