Con los números de preguntas y secciones
This commit is contained in:
10
BACKLOG.md
10
BACKLOG.md
@@ -48,6 +48,16 @@
|
|||||||
|
|
||||||
*(Sprint 3 y posteriores — ver ARCHITECTURE.md y BRIEF por sprint)*
|
*(Sprint 3 y posteriores — ver ARCHITECTURE.md y BRIEF por sprint)*
|
||||||
|
|
||||||
|
### [BACKLOG-001] Consentimiento inicial editable desde admin
|
||||||
|
- **Estado:** hoy el texto del consentimiento está hardcoded en `consent-screen.tsx`.
|
||||||
|
- **Necesidad:** cuando se construya el motor de formularios (builder), el texto del
|
||||||
|
consentimiento debe ser editable por encuesta desde la administración, para reflejar
|
||||||
|
cambios en política de privacidad o ajustes específicos por tipo de relevamiento.
|
||||||
|
- **Bloquea:** implementación del builder visual.
|
||||||
|
- **Decisión cerrada:** la edición es desde admin, no desde el código.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
- Dashboard / reportes (Sprint 3)
|
- Dashboard / reportes (Sprint 3)
|
||||||
- Formulario de consentimiento + persistencia de respuestas (Etapa 2B)
|
- Formulario de consentimiento + persistencia de respuestas (Etapa 2B)
|
||||||
- Builder visual de encuestas (post-validación del caso fijo)
|
- Builder visual de encuestas (post-validación del caso fijo)
|
||||||
|
|||||||
@@ -147,16 +147,6 @@ body {
|
|||||||
border-radius: 0 var(--radius) var(--radius) 0;
|
border-radius: 0 var(--radius) var(--radius) 0;
|
||||||
color: var(--color-primary-hover);
|
color: var(--color-primary-hover);
|
||||||
}
|
}
|
||||||
.section-sensitive-note {
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
color: #92400e;
|
|
||||||
background: var(--color-sensitive);
|
|
||||||
border: 1px solid var(--color-sensitive-border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: 8px 12px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Question wrapper ── */
|
/* ── Question wrapper ── */
|
||||||
.question-wrapper {
|
.question-wrapper {
|
||||||
background: var(--color-surface);
|
background: var(--color-surface);
|
||||||
@@ -183,6 +173,12 @@ body {
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
.required-mark { color: var(--color-error); }
|
.required-mark { color: var(--color-error); }
|
||||||
|
.q-code {
|
||||||
|
font-size: 0.78em;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #7A8FA0;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Options list ── */
|
/* ── Options list ── */
|
||||||
.options-list { display: flex; flex-direction: column; gap: 8px; }
|
.options-list { display: flex; flex-direction: column; gap: 8px; }
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export function QuestionRenderer({
|
|||||||
const error = errors[code]
|
const error = errors[code]
|
||||||
|
|
||||||
// A1 (Departamento) renders a combined geo widget that also handles A2 (Ciudad)
|
// A1 (Departamento) renders a combined geo widget that also handles A2 (Ciudad)
|
||||||
|
// Code badges for A1/A2 are rendered inside GeoSelectWidget.
|
||||||
if (code === 'A1') {
|
if (code === 'A1') {
|
||||||
return (
|
return (
|
||||||
<GeoSelectWidget
|
<GeoSelectWidget
|
||||||
@@ -47,11 +48,13 @@ export function QuestionRenderer({
|
|||||||
// A2 is rendered inside the GeoSelectWidget above — skip here to avoid duplication
|
// A2 is rendered inside the GeoSelectWidget above — skip here to avoid duplication
|
||||||
if (code === 'A2') return null
|
if (code === 'A2') return null
|
||||||
|
|
||||||
|
const legend = <><span className="q-code">{code} ·</span> {prompt}</>
|
||||||
|
|
||||||
if (type === 'single_choice' && isArrayOptions(options)) {
|
if (type === 'single_choice' && isArrayOptions(options)) {
|
||||||
return (
|
return (
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
name={code}
|
name={code}
|
||||||
legend={prompt}
|
legend={legend}
|
||||||
options={options}
|
options={options}
|
||||||
value={answers[code] as string | undefined}
|
value={answers[code] as string | undefined}
|
||||||
required={required}
|
required={required}
|
||||||
@@ -65,7 +68,7 @@ export function QuestionRenderer({
|
|||||||
return (
|
return (
|
||||||
<CheckboxGroup
|
<CheckboxGroup
|
||||||
name={code}
|
name={code}
|
||||||
legend={prompt}
|
legend={legend}
|
||||||
options={options}
|
options={options}
|
||||||
value={(answers[code] as string[]) ?? []}
|
value={(answers[code] as string[]) ?? []}
|
||||||
maxSelect={max_select}
|
maxSelect={max_select}
|
||||||
@@ -80,7 +83,7 @@ export function QuestionRenderer({
|
|||||||
return (
|
return (
|
||||||
<RatingScale
|
<RatingScale
|
||||||
name={code}
|
name={code}
|
||||||
legend={prompt}
|
legend={legend}
|
||||||
ratingOptions={options}
|
ratingOptions={options}
|
||||||
value={answers[code] as number | undefined}
|
value={answers[code] as number | undefined}
|
||||||
required={required}
|
required={required}
|
||||||
@@ -94,7 +97,7 @@ export function QuestionRenderer({
|
|||||||
return (
|
return (
|
||||||
<TextShortField
|
<TextShortField
|
||||||
name={code}
|
name={code}
|
||||||
label={prompt}
|
label={legend}
|
||||||
value={(answers[code] as string) ?? ''}
|
value={(answers[code] as string) ?? ''}
|
||||||
required={required}
|
required={required}
|
||||||
onChange={(v) => onChange(code, v)}
|
onChange={(v) => onChange(code, v)}
|
||||||
@@ -107,7 +110,7 @@ export function QuestionRenderer({
|
|||||||
return (
|
return (
|
||||||
<TextAreaField
|
<TextAreaField
|
||||||
name={code}
|
name={code}
|
||||||
label={prompt}
|
label={legend}
|
||||||
value={(answers[code] as string) ?? ''}
|
value={(answers[code] as string) ?? ''}
|
||||||
required={required}
|
required={required}
|
||||||
onChange={(v) => onChange(code, v)}
|
onChange={(v) => onChange(code, v)}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import { ProgressBar } from './progress-bar'
|
|||||||
import { QuestionRenderer } from './question-renderer'
|
import { QuestionRenderer } from './question-renderer'
|
||||||
|
|
||||||
// Section metadata (code prefix → display title)
|
// Section metadata (code prefix → display title)
|
||||||
const SECTIONS: { prefix: string; title: string; sensitive?: boolean }[] = [
|
const SECTIONS: { prefix: string; title: string }[] = [
|
||||||
{ prefix: 'A', title: 'Datos Generales' },
|
{ prefix: 'A', title: 'Datos Generales' },
|
||||||
{ prefix: '1', title: 'Prevalencia: cuidado y discapacidad', sensitive: true },
|
{ prefix: '1', title: 'Prevalencia: cuidado y discapacidad' },
|
||||||
{ prefix: '2', title: 'Intensidad y rol del cuidado' },
|
{ prefix: '2', title: 'Intensidad y rol del cuidado' },
|
||||||
{ prefix: '3', title: 'Impacto económico del cuidado' },
|
{ prefix: '3', title: 'Impacto económico del cuidado' },
|
||||||
{ prefix: '4', title: 'Tiempo y descanso' },
|
{ prefix: '4', title: 'Tiempo y descanso' },
|
||||||
@@ -190,13 +190,13 @@ export function SurveyForm({ survey, questions }: SurveyFormProps) {
|
|||||||
return v !== null && v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0)
|
return v !== null && v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0)
|
||||||
}).length
|
}).length
|
||||||
|
|
||||||
type Section = { prefix: string; title: string; sensitive?: boolean; questions: Question[] }
|
type Section = { prefix: string; title: string; questions: Question[] }
|
||||||
const sectionMap = new Map<string, Section>()
|
const sectionMap = new Map<string, Section>()
|
||||||
for (const q of visibleQuestions) {
|
for (const q of visibleQuestions) {
|
||||||
const prefix = getSectionPrefix(q.code)
|
const prefix = getSectionPrefix(q.code)
|
||||||
if (!sectionMap.has(prefix)) {
|
if (!sectionMap.has(prefix)) {
|
||||||
const meta = SECTIONS.find((s) => s.prefix === prefix)
|
const meta = SECTIONS.find((s) => s.prefix === prefix)
|
||||||
sectionMap.set(prefix, { prefix, title: meta?.title ?? prefix, sensitive: meta?.sensitive, questions: [] })
|
sectionMap.set(prefix, { prefix, title: meta?.title ?? prefix, questions: [] })
|
||||||
}
|
}
|
||||||
sectionMap.get(prefix)!.questions.push(q)
|
sectionMap.get(prefix)!.questions.push(q)
|
||||||
}
|
}
|
||||||
@@ -242,13 +242,7 @@ export function SurveyForm({ survey, questions }: SurveyFormProps) {
|
|||||||
aria-labelledby={`section-${section.prefix}`}
|
aria-labelledby={`section-${section.prefix}`}
|
||||||
>
|
>
|
||||||
<div className="section-header">
|
<div className="section-header">
|
||||||
<h2 id={`section-${section.prefix}`} className="section-title">{section.title}</h2>
|
<h2 id={`section-${section.prefix}`} className="section-title">Sección {section.prefix} · {section.title}</h2>
|
||||||
{section.sensitive && (
|
|
||||||
<p className="section-sensitive-note" role="note">
|
|
||||||
Esta sección contiene preguntas sensibles sobre salud o discapacidad.
|
|
||||||
Responder es completamente voluntario.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{section.questions.map((q) => (
|
{section.questions.map((q) => (
|
||||||
<div key={q.id} className="question-wrapper" data-code={q.code}>
|
<div key={q.id} className="question-wrapper" data-code={q.code}>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
import type { QuestionOption } from '@/lib/types'
|
import type { QuestionOption } from '@/lib/types'
|
||||||
|
|
||||||
interface CheckboxGroupProps {
|
interface CheckboxGroupProps {
|
||||||
name: string
|
name: string
|
||||||
legend: string
|
legend: ReactNode
|
||||||
options: QuestionOption[]
|
options: QuestionOption[]
|
||||||
value: string[]
|
value: string[]
|
||||||
maxSelect: number | null
|
maxSelect: number | null
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function GeoSelectWidget({
|
|||||||
{/* Departamento */}
|
{/* Departamento */}
|
||||||
<div className="geo-select-group">
|
<div className="geo-select-group">
|
||||||
<label className="question-legend" htmlFor="A1">
|
<label className="question-legend" htmlFor="A1">
|
||||||
Departamento
|
<span className="q-code">A1 ·</span> Departamento
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id="A1"
|
id="A1"
|
||||||
@@ -58,7 +58,7 @@ export function GeoSelectWidget({
|
|||||||
className={`question-legend${cityDisabled ? ' geo-label-disabled' : ''}`}
|
className={`question-legend${cityDisabled ? ' geo-label-disabled' : ''}`}
|
||||||
htmlFor="A2"
|
htmlFor="A2"
|
||||||
>
|
>
|
||||||
Ciudad
|
<span className="q-code">A2 ·</span> Ciudad
|
||||||
{cityDisabled && (
|
{cityDisabled && (
|
||||||
<span className="geo-disabled-hint"> (seleccioná un departamento primero)</span>
|
<span className="geo-disabled-hint"> (seleccioná un departamento primero)</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
import type { QuestionOption } from '@/lib/types'
|
import type { QuestionOption } from '@/lib/types'
|
||||||
|
|
||||||
interface RadioGroupProps {
|
interface RadioGroupProps {
|
||||||
name: string
|
name: string
|
||||||
legend: string
|
legend: ReactNode
|
||||||
options: QuestionOption[]
|
options: QuestionOption[]
|
||||||
value: string | undefined
|
value: string | undefined
|
||||||
required: boolean
|
required: boolean
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
import type { RatingOptions } from '@/lib/types'
|
import type { RatingOptions } from '@/lib/types'
|
||||||
|
|
||||||
interface RatingScaleProps {
|
interface RatingScaleProps {
|
||||||
name: string
|
name: string
|
||||||
legend: string
|
legend: ReactNode
|
||||||
ratingOptions: RatingOptions
|
ratingOptions: RatingOptions
|
||||||
value: number | undefined
|
value: number | undefined
|
||||||
required: boolean
|
required: boolean
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
|
|
||||||
interface TextAreaFieldProps {
|
interface TextAreaFieldProps {
|
||||||
name: string
|
name: string
|
||||||
label: string
|
label: ReactNode
|
||||||
value: string
|
value: string
|
||||||
required: boolean
|
required: boolean
|
||||||
onChange: (value: string) => void
|
onChange: (value: string) => void
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
import type { ReactNode } from 'react'
|
||||||
|
|
||||||
interface TextShortFieldProps {
|
interface TextShortFieldProps {
|
||||||
name: string
|
name: string
|
||||||
label: string
|
label: ReactNode
|
||||||
value: string
|
value: string
|
||||||
required: boolean
|
required: boolean
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
|
|||||||
184
sprints/sprint-2/ETAPA_2F_PROMPT.md
Normal file
184
sprints/sprint-2/ETAPA_2F_PROMPT.md
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
# ETAPA 2F — Formulario: numeración visible + eliminar disclaimers por sección
|
||||||
|
|
||||||
|
## 1. Objetivo
|
||||||
|
Dos cambios cosméticos sobre el formulario que ve el respondente:
|
||||||
|
1. Mostrar el número de sección en su encabezado y el código de pregunta junto a cada pregunta.
|
||||||
|
2. Eliminar los disclaimers de "esta sección contiene preguntas sensibles" que aparecen
|
||||||
|
debajo del encabezado de cada sección. La encuesta se trata como un todo atómico:
|
||||||
|
la persona acepta responderla entera al inicio (los 2 checks ya implementados); no se
|
||||||
|
re-pide consentimiento por sección.
|
||||||
|
|
||||||
|
**Etapa Nivel 2 — cambio visible al usuario, sin tocar datos ni lógica.** No toca el
|
||||||
|
seed, ni las RPC, ni los componentes de captura, ni el componente de consentimiento.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Contexto previo
|
||||||
|
- Etapa 2E cerrada (refactor de dashboard alineado al instrumento real).
|
||||||
|
- El director observó visualmente el formulario y detectó dos puntos de fricción:
|
||||||
|
(a) las preguntas no muestran su código, lo que dificulta dar feedback con precisión;
|
||||||
|
(b) hay disclaimers por sección que crean fricción innecesaria.
|
||||||
|
- El consentimiento inicial con dos checks (operativo + macro) ya advierte de manera
|
||||||
|
general sobre el tratamiento de datos. Ese es el contrato — no se duplica por sección.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. DECISIONES YA TOMADAS — no consultar de nuevo
|
||||||
|
|
||||||
|
### 3.1 Numeración de secciones
|
||||||
|
Cada encabezado de sección muestra el número antes del título.
|
||||||
|
Formato: **"Sección {N} · {Título}"** donde {N} es A, 1, 2, 3, 4, 5, 6 o 7.
|
||||||
|
|
||||||
|
Ejemplos finales:
|
||||||
|
- "Sección A · Datos Generales"
|
||||||
|
- "Sección 1 · Prevalencia: cuidado y discapacidad"
|
||||||
|
- "Sección 2 · Intensidad y rol del cuidado"
|
||||||
|
- "Sección 3 · Impacto económico del cuidado"
|
||||||
|
- "Sección 4 · Tiempo y descanso"
|
||||||
|
- "Sección 5 · Impacto en el trabajo"
|
||||||
|
- "Sección 6 · Conciencia organizacional"
|
||||||
|
- "Sección 7 · Para cerrar"
|
||||||
|
|
||||||
|
Los nombres de cada sección se toman literalmente de `SEED_SPEC.md` (sin reinterpretar
|
||||||
|
ni acortar).
|
||||||
|
|
||||||
|
### 3.2 Código de pregunta junto a cada pregunta
|
||||||
|
Cada pregunta muestra su código como tag/etiqueta pequeña antes del texto de la pregunta.
|
||||||
|
|
||||||
|
Especificaciones visuales:
|
||||||
|
- Color: `#43BBC8` (teal RE) o `#7A8FA0` (muted) — elegir el que dé mejor contraste sin
|
||||||
|
competir con el texto principal de la pregunta.
|
||||||
|
- Peso: bold o semibold (que el código se distinga visualmente del texto).
|
||||||
|
- Tamaño: ~0.8× del texto de la pregunta.
|
||||||
|
- Posición: en la misma línea que el inicio del texto, separado por un punto medio (·)
|
||||||
|
o por un espacio + tag con borde. Ejemplo: `1.1 · ¿Vos mismo/a vivís con alguna...?`
|
||||||
|
- Las preguntas dentro de bloques condicionales (1.2-1.5, 1.7-1.12, 1.14-1.16) muestran
|
||||||
|
su código tal cual está en el seed. Los saltos visibles (1.1 → 1.6 si no se dispara
|
||||||
|
el bloque) son intencionales — el respondente ve que existe lógica condicional.
|
||||||
|
|
||||||
|
### 3.3 Eliminación de disclaimers por sección
|
||||||
|
Eliminar TODO disclaimer que aparezca debajo del encabezado de una sección con texto del
|
||||||
|
estilo:
|
||||||
|
- "Esta sección contiene preguntas sensibles..."
|
||||||
|
- "Responder es voluntario..."
|
||||||
|
- Cualquier variante semánticamente equivalente.
|
||||||
|
|
||||||
|
El disclaimer confirmado a eliminar (visto en captura):
|
||||||
|
> "Esta sección contiene preguntas sensibles sobre salud o discapacidad. Responder es completamente voluntario."
|
||||||
|
|
||||||
|
**Verificar si existen disclaimers similares en OTRAS secciones** (ej: Sección 3 por
|
||||||
|
3.4 salario) y eliminarlos también. La eliminación es consistente: ninguna sección lleva
|
||||||
|
disclaimer propio.
|
||||||
|
|
||||||
|
### 3.4 NO tocar el componente de consentimiento
|
||||||
|
El `consent-screen.tsx` (o equivalente) con los dos checks (operativo + macro) **queda
|
||||||
|
intacto**. Esa es la advertencia que la persona acepta al inicio, y es suficiente
|
||||||
|
porque cubre el tratamiento de datos del formulario completo.
|
||||||
|
|
||||||
|
### 3.5 Backlog futuro (NO esta etapa)
|
||||||
|
El texto del consentimiento inicial debería ser editable cuando se construya el motor
|
||||||
|
de formularios (sprint futuro del builder). **Agregar como ítem al BACKLOG.md** (no a
|
||||||
|
TECH_DEBT, porque no es deuda — es feature pendiente):
|
||||||
|
|
||||||
|
```
|
||||||
|
[BACKLOG-XXX] Consentimiento inicial editable desde admin
|
||||||
|
- Estado: hoy el texto del consentimiento está hardcoded en consent-screen.tsx.
|
||||||
|
- Necesidad: cuando se construya el motor de formularios (builder), el texto del
|
||||||
|
consentimiento debe ser editable por encuesta desde la administración, para reflejar
|
||||||
|
cambios en política de privacidad o ajustes específicos por tipo de relevamiento.
|
||||||
|
- Bloquea: implementación del builder visual.
|
||||||
|
- Decisión cerrada: la edición es desde admin, no desde el código.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Tareas
|
||||||
|
|
||||||
|
### 4.1 Verificación previa (Patrón C.8)
|
||||||
|
Antes de tocar nada, verificar contra `SEED_SPEC.md`:
|
||||||
|
- Los 8 nombres exactos de las secciones (A, 1, 2, 3, 4, 5, 6, 7).
|
||||||
|
- Los códigos de pregunta de la sección A y de cada sección numérica.
|
||||||
|
|
||||||
|
Si encontrás discrepancias entre los nombres de las secciones en el código actual y los
|
||||||
|
del seed, **detenete y reportá** antes de aplicar (Patrón C.8). Aplica el aprendizaje
|
||||||
|
de la Etapa 2E: los códigos y nombres son del seed, no de archivos secundarios.
|
||||||
|
|
||||||
|
### 4.2 Localizar dónde se renderizan los encabezados de sección
|
||||||
|
`grep -rE "Prevalencia|Datos Generales|Intensidad y rol|Impacto económico|Tiempo y descanso|Impacto en el trabajo|Conciencia organizacional|Para cerrar"` en `components/` y `app/(survey)/` (o equivalente).
|
||||||
|
|
||||||
|
Encontrar el componente que renderiza el header de cada sección. Probablemente es uno
|
||||||
|
solo que recibe el nombre de sección como prop o lo deriva del seed.
|
||||||
|
|
||||||
|
### 4.3 Modificar el header de sección
|
||||||
|
Aplicar el formato **"Sección {N} · {Título}"** una sola vez en el componente, leyendo
|
||||||
|
el número desde una propiedad del seed o pasándolo como prop. Mantener el estilo
|
||||||
|
visual actual (color teal, tamaño, peso) — solo se agrega el prefijo "Sección {N} · ".
|
||||||
|
|
||||||
|
### 4.4 Modificar el componente de pregunta
|
||||||
|
Localizar el componente que renderiza el texto de cada pregunta. Agregar el tag con el
|
||||||
|
código de la pregunta antes del texto, según las especificaciones de la sección 3.2.
|
||||||
|
|
||||||
|
El código de cada pregunta ya está en la base de datos (`questions.code`) y debería
|
||||||
|
estar disponible en el objeto que recibe el componente. Si no está, propagarlo en una
|
||||||
|
prop (no agregarle un fetch).
|
||||||
|
|
||||||
|
### 4.5 Eliminar disclaimers por sección
|
||||||
|
Eliminar el bloque ámbar con "Esta sección contiene preguntas sensibles...". Buscar
|
||||||
|
todas las instancias:
|
||||||
|
```bash
|
||||||
|
grep -rn "preguntas sensibles\|Responder es completamente voluntario\|Responder es voluntario" \
|
||||||
|
components/ app/(survey)/ 2>/dev/null
|
||||||
|
```
|
||||||
|
Eliminar tanto el JSX como, si existe, el dato del seed/constante que las alimenta. Si
|
||||||
|
el disclaimer está condicionado por `is_sensitive=true` o similar, eliminar también esa
|
||||||
|
lógica de renderizado.
|
||||||
|
|
||||||
|
### 4.6 Backlog
|
||||||
|
Agregar el ítem BACKLOG-XXX descrito en la sección 3.5 a `BACKLOG.md` (crear el archivo
|
||||||
|
si no existe). Numerar según el siguiente correlativo disponible.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Entregables (DoD)
|
||||||
|
|
||||||
|
- [ ] Verificación C.8 ejecutada — nombres de secciones match SEED_SPEC.md.
|
||||||
|
- [ ] Los 8 encabezados de sección muestran el prefijo "Sección {N} · ".
|
||||||
|
- [ ] Cada pregunta muestra su código (1.1, A5, 6.2, etc.) antes del texto.
|
||||||
|
- [ ] `grep -rE "preguntas sensibles|Responder es .*voluntario"` retorna vacío.
|
||||||
|
- [ ] `consent-screen.tsx` (o equivalente) sin cambios — confirmar con `git diff`.
|
||||||
|
- [ ] BACKLOG.md actualizado con el ítem del consentimiento editable.
|
||||||
|
- [ ] Build sin errores (`next build`).
|
||||||
|
- [ ] `git diff --name-only HEAD` solo en componentes del formulario y BACKLOG.md.
|
||||||
|
- [ ] Captura del formulario en sección 1 mostrando: encabezado con "Sección 1 · ", al
|
||||||
|
menos 2 preguntas con sus códigos visibles, y SIN disclaimer ámbar.
|
||||||
|
|
||||||
|
## 6. Qué reportar
|
||||||
|
```
|
||||||
|
Verificado:
|
||||||
|
- Verificación C.8 (nombres de secciones vs SEED_SPEC.md): match
|
||||||
|
- Archivos modificados en diff: [lista]
|
||||||
|
- Headers de sección con "Sección {N} · ": confirmado
|
||||||
|
- Códigos de pregunta visibles: [ejemplo de 2-3]
|
||||||
|
- grep de disclaimers: vacío
|
||||||
|
- consent-screen.tsx intacto: confirmado
|
||||||
|
- BACKLOG.md actualizado: ítem [número]
|
||||||
|
- Build exitoso
|
||||||
|
|
||||||
|
Asumido:
|
||||||
|
- [si hay decisión cosmética sobre el estilo del tag de código]
|
||||||
|
|
||||||
|
Decisiones proactivas (Nivel 2):
|
||||||
|
- [si las hubo]
|
||||||
|
|
||||||
|
Conflictos de scope (Patrón C.8):
|
||||||
|
- [si los hubo, listarlos AQUÍ y detener]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Qué NO hacer
|
||||||
|
- ❌ No tocar `consent-screen.tsx` ni el componente de consentimiento de ninguna forma.
|
||||||
|
- ❌ No modificar el seed, las preguntas, ni `SEED_SPEC.md`.
|
||||||
|
- ❌ No tocar las RPCs ni la migración del dashboard.
|
||||||
|
- ❌ No cambiar la lógica condicional de los bloques (1.1→1.2-1.5, etc.).
|
||||||
|
- ❌ No renumerar preguntas — los códigos quedan tal cual están en el seed.
|
||||||
|
- ❌ No agregar dependencias npm.
|
||||||
|
- ❌ No resolver ítems de TECH_DEBT.md ni del BACKLOG salvo el ítem nuevo de esta etapa.
|
||||||
142
sprints/sprint-2/ETAPA_2G_PROMPT.md
Normal file
142
sprints/sprint-2/ETAPA_2G_PROMPT.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
# ETAPA 2G — Cierre de Sprint 2: promoción de C.8 y documentación de AP.10 + AP.11
|
||||||
|
|
||||||
|
## 1. Objetivo
|
||||||
|
Documentar formalmente en el método los aprendizajes que el Sprint 2 produjo:
|
||||||
|
- **Promover C.8 a estable** en `method/PATTERNS.md` (validado en 3 etapas del proyecto).
|
||||||
|
- **Agregar AP.10 (propagación silenciosa de inconsistencia)** a `method/ANTI_PATTERNS.md`.
|
||||||
|
- **Agregar AP.11 (contrato desactualizado por código)** a `method/ANTI_PATTERNS.md`.
|
||||||
|
|
||||||
|
Etapa de documentación pura. **No toca código, no toca seed, no toca dashboard.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Contexto previo
|
||||||
|
- Sprint 2 cerrado con Etapas 2A-2F entregadas.
|
||||||
|
- Etapa 2D produjo el caso real de AP.10 (subtítulos con códigos discrepantes del seed).
|
||||||
|
- Etapa 2E aplicó por primera vez C.8 y detectó el conflicto A1b-vs-A1 antes de ejecutar
|
||||||
|
(caso real de AP.11 — SEED_SPEC.md desactualizado por Etapa 1D).
|
||||||
|
- Etapa 2F ejecutó con C.8 sin discrepancias (validación de patrón en uso limpio).
|
||||||
|
- Tres usos en proyecto Datos País → patrón estable según el método (`PATTERNS.md`,
|
||||||
|
criterio "tras aplicarlo en un 2do proyecto distinto se promueve a estable" —
|
||||||
|
aplica también a 2 usos limpios consecutivos en un proyecto vivo).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Tareas
|
||||||
|
|
||||||
|
### 3.1 Agregar C.8 a `method/PATTERNS.md`
|
||||||
|
Insertar en la **Categoría C — Validación de output**, después de C.7 (Control preventivo
|
||||||
|
de scope con `git diff`):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### C.8 — Verificación de códigos contra fuente única · *deriva de P3, P4 · origen: 2do proyecto*
|
||||||
|
Cuando un prompt menciona códigos de un instrumento, esquema o contrato (preguntas, columnas,
|
||||||
|
campos), el agente verifica cada código contra la fuente única (`SEED_SPEC.md`, `schema.sql`,
|
||||||
|
ADRs) **antes** de ejecutar. Si detecta una discrepancia entre el código nombrado en el
|
||||||
|
prompt y su definición en la fuente única, **DETIENE la ejecución y reporta el conflicto**.
|
||||||
|
Ejecutar "literalmente" y delegar la verificación semántica al director es AP.10.
|
||||||
|
- ✅ **Correcto:** el reporte del agente incluye una sección "Verificación C.8" con cada
|
||||||
|
código del prompt mapeado a su fila en la fuente única, antes de la ejecución. Si hay
|
||||||
|
discrepancia, la ejecución no arranca hasta que el director resuelva.
|
||||||
|
- ❌ **Violación:** el agente ejecuta el prompt al pie de la letra y reporta "los textos son
|
||||||
|
los solicitados literalmente; la validación semántica la hace el director" — exporta el
|
||||||
|
trabajo de verificar al humano y propaga la inconsistencia.
|
||||||
|
|
||||||
|
**Cómo se incorpora a un prompt:** agregar un paso "Verificación C.8" como primera tarea
|
||||||
|
del DoD, listando los códigos a verificar y la ruta exacta de la fuente única.
|
||||||
|
**Casos reales en Datos País:** Etapa 2D (no se aplicó — daño AP.10), Etapa 2E (se aplicó —
|
||||||
|
detectó conflicto A1/A1b antes de ejecutar), Etapa 2F (se aplicó — sin discrepancias).
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 Agregar AP.10 a `method/ANTI_PATTERNS.md`
|
||||||
|
Insertar al final del listado, antes de "Cómo agregar un anti-patrón":
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## AP.10 — Propagación silenciosa de inconsistencia entre fuentes
|
||||||
|
**Comportamiento:** un prompt contiene una referencia (código, ruta, esquema) que el agente
|
||||||
|
podría verificar contra otra fuente del repo. En lugar de hacerlo, el agente ejecuta
|
||||||
|
literalmente y traslada la responsabilidad al director con frases como "la validación
|
||||||
|
semántica la hace el director" o "los textos son los solicitados literalmente".
|
||||||
|
**Daño:** las inconsistencias se propagan a producción con apariencia de precisión técnica.
|
||||||
|
*Caso real:* en Etapa 2D del proyecto Datos País, 11 de 12 subtítulos del dashboard
|
||||||
|
quedaron con códigos de pregunta discrepantes del seed real. El dashboard mostraba
|
||||||
|
métricas mal etiquetadas: el KPI "Ausencia promedio · 3.1 · días por mes" referenciaba
|
||||||
|
una pregunta que en el seed real era *gasto en cuidado*, no ausencias. Detectado en la
|
||||||
|
auditoría adversarial posterior, no antes. Si hubieran cargado datos reales, RRHH habría
|
||||||
|
visto guaraníes presentados como días.
|
||||||
|
**Detectar:** el reporte del agente declara "los textos son los solicitados literalmente";
|
||||||
|
hay códigos, rutas o nombres que el agente pudo verificar contra otra fuente y no lo hizo;
|
||||||
|
el agente delega la verificación semántica al humano sin haber verificado lo verificable.
|
||||||
|
**Evitar:** Patrón C.8 + cláusula explícita en el prompt: *"si detectás una discrepancia
|
||||||
|
entre el prompt y la fuente única, DETENÉ la ejecución y reportá antes de avanzar"*.
|
||||||
|
**Distinción:** ≠ AP.3 — AP.3 minimiza un problema observado; AP.10 evita verificar lo
|
||||||
|
verificable. ≠ AP.9 — AP.9 resuelve restricciones contradictorias del propio prompt;
|
||||||
|
AP.10 propaga inconsistencias entre el prompt y otra fuente.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 Agregar AP.11 a `method/ANTI_PATTERNS.md`
|
||||||
|
Insertar inmediatamente después de AP.10:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## AP.11 — Contrato desactualizado por código
|
||||||
|
**Comportamiento:** una etapa modifica el comportamiento real del sistema (seed, esquema,
|
||||||
|
RLS, RPCs) pero no actualiza el documento que sirve como contrato o fuente única
|
||||||
|
(`SEED_SPEC.md`, `ARCHITECTURE.md`, ADRs). El documento queda mintiendo por silencio.
|
||||||
|
**Daño:** el próximo agente o autor de prompts que lo lea va a tomar decisiones basadas
|
||||||
|
en información falsa. *Caso real:* en Etapa 1D del proyecto Datos País se modificó
|
||||||
|
`seed.sql` para que A1/A2/A3 fueran widgets geo (Departamento/Ciudad/Barrio en lugar de
|
||||||
|
texto libre País/Ciudad/Sucursal), pero no se actualizó `SEED_SPEC.md`. Dos sprints
|
||||||
|
después, Etapa 2E se escribió tomando SEED_SPEC como verdad y proponiendo agregar una
|
||||||
|
pregunta A1b que duplicaba la A1 ya existente. El conflicto se detectó antes de ejecutar
|
||||||
|
porque el agente aplicó el Patrón C.8; sin C.8, se habría creado una pregunta duplicada
|
||||||
|
en producción.
|
||||||
|
**Detectar:** cuando una etapa cambia comportamiento del sistema, preguntar explícitamente
|
||||||
|
"¿qué documento de contrato describe esto, y lo actualicé?". Si la respuesta es "no sé"
|
||||||
|
o "después lo veo", AP.11 latente. También: cuando una etapa posterior asume contenido
|
||||||
|
del contrato que ya no coincide con la realidad.
|
||||||
|
**Evitar:** todo prompt que modifique un sistema documentado en un contrato debe incluir,
|
||||||
|
como parte del DoD, "actualizar el documento de contrato correspondiente con los cambios".
|
||||||
|
Es Principio 4 aplicado al mantenimiento, no solo a la creación.
|
||||||
|
**Distinción:** ≠ AP.4 — AP.4 es decisión no registrada; AP.11 es decisión registrada en
|
||||||
|
código pero NO actualizada en el contrato escrito.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 (Opcional) Agregar referencia cruzada en C.1 del método
|
||||||
|
En `method/PATTERNS.md`, dentro de C.1 (Validación visual mandatoria del artefacto final),
|
||||||
|
si existe la oportunidad, agregar como nota: *"Cuando el output incluye textos que
|
||||||
|
referencian otras fuentes del repo (códigos, IDs, rutas), aplicar también C.8."*
|
||||||
|
|
||||||
|
Esta tarea es opcional — solo si encaja naturalmente. No forzarla.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Entregables (DoD)
|
||||||
|
- [ ] `method/PATTERNS.md` actualizado con C.8 como patrón estable.
|
||||||
|
- [ ] `method/ANTI_PATTERNS.md` actualizado con AP.10 y AP.11.
|
||||||
|
- [ ] Tres bloques markdown insertados exactamente como están en este prompt (no
|
||||||
|
reescribir, no resumir, no parafrasear el contenido — copiar literal).
|
||||||
|
- [ ] `git diff --name-only HEAD` retorna **solo** los 2 archivos del método.
|
||||||
|
- [ ] Build no aplica (no hay cambios de código).
|
||||||
|
|
||||||
|
## 5. Qué reportar
|
||||||
|
```
|
||||||
|
Verificado:
|
||||||
|
- archivos en diff: method/PATTERNS.md, method/ANTI_PATTERNS.md
|
||||||
|
- C.8 insertado en Categoría C después de C.7: confirmado
|
||||||
|
- AP.10 y AP.11 insertados al final del listado: confirmado
|
||||||
|
- Texto copiado literal del prompt: confirmado (sin reescritura)
|
||||||
|
- Tarea opcional 3.4: [aplicada / no aplicada — justificación corta]
|
||||||
|
|
||||||
|
Asumido:
|
||||||
|
- [solo si el método tiene una estructura distinta a la esperada]
|
||||||
|
|
||||||
|
Decisiones proactivas (Nivel 2):
|
||||||
|
- [si las hubo]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Qué NO hacer
|
||||||
|
- ❌ No reescribir, resumir ni parafrasear los textos de C.8, AP.10 o AP.11 — copiar literal.
|
||||||
|
- ❌ No agregar otros patrones o anti-patrones por iniciativa.
|
||||||
|
- ❌ No modificar PRINCIPLES.md ni AUTOMATION_STRATEGY.md.
|
||||||
|
- ❌ No tocar nada del proyecto Datos País (esto es del método, no del producto).
|
||||||
|
- ❌ No crear archivos nuevos — solo editar los dos existentes.
|
||||||
Reference in New Issue
Block a user