feat(F-112): completed feature

This commit is contained in:
chattie
2026-08-21 13:31:28 +02:00
parent da919d705f
commit 77133c0ecf
16 changed files with 207 additions and 46 deletions

View File

@@ -0,0 +1,18 @@
# F-112 — Arquitectura: disclaimer de contenido asistido por IA en la ficha de producto
## Decisiones
1. **Flag único `ai_assisted`**: una columna `boolean NOT NULL DEFAULT false` en `catalog_products`.
- Más simple que flags por campo y suficiente para el aviso legal.
2. **Migración 040**: `ALTER TABLE catalog_products ADD COLUMN IF NOT EXISTS ai_assisted boolean NOT NULL DEFAULT false;`
3. **Dominio/serialización**: `Product.aiAssisted` propagado en rutas, repositorio y `serializeProduct`.
4. **Set del flag**: cuando se ejecuta `POST /products/:id/generate-seo` y el flujo completa al menos una generación, el flag se activa en true. El admin también puede alternarlo manualmente en el editor (futuro; no se requiere para este ticket).
5. **Frontend** (`apps/frontend/src/app/products/[slug]/page.tsx`): cuando `product.aiAssisted === true`, renderizar un bloque destacado en la ficha con los dos avisos.
## Tests
- `serializeProduct` debe incluir `aiAssisted` (registrable en tests de product-use-cases).
- `productsAiAssisted.test.ts` (puerta frontend): no es trivial sin jsdom; lo dejamos para smoke.
## Fuera de alcance
- Sin cambios en el flujo admin de IA.
- Sin nuevos endpoints.
- Sin traducciones.

View File

@@ -0,0 +1,24 @@
# F-112 — Disclaimer de contenido asistido por IA
## Backend
- **Migración 040** (`040_product_ai_assisted.js`): `catalog_products.ai_assisted boolean NOT NULL DEFAULT false` + índice parcial. Aplicada (`db:status`).
- **Dominio** (`product.ts`): `Product.aiAssisted: boolean`, `NewProduct.aiAssisted?: boolean`.
- **Repositorio** (`pg-product-repository.ts`): columna en `ProductRow`, mapeo en `toProduct`, `UPDATABLE` permite editar, INSERT incluye `ai_assisted`.
- **Rutas** (`catalog.routes.ts`):
- `newProductSchema` admite `aiAssisted`.
- `serializeProduct` expone `aiAssisted`.
- `POST /products/:id/generate-seo`: si la IA generó al menos un campo, fuerza `aiAssisted = true` en el patch.
## Frontend (apps/frontend)
- `types/api.ts`: `Product.aiAssisted?: boolean`.
- `app/products/[slug]/page.tsx`: cuando `product.aiAssisted === true`, renderiza un `<aside>` con los dos avisos requeridos.
## Tests
- `product-use-cases.test.ts` + `image-use-cases.test.ts`: actualizados para incluir `aiAssisted: false` en fixtures.
- `npm run typecheck` (backend) OK.
- `npm test`: 169 passed / 0 failed.
- `apps/frontend tsc --noEmit` OK.
## Evidencia
- DB: `SELECT column_name FROM information_schema.columns WHERE table_name='catalog_products' AND column_name='ai_assisted'` → presente.
- Smoke: `UPDATE catalog_products SET ai_assisted = true WHERE id = '<sample>'` → la ficha de cliente muestra el aviso.

View File

@@ -0,0 +1,15 @@
{
"feature_id": "F-112",
"agent": "leader",
"verdict": "APPROVED",
"summary": "F-112 adds an ai_assisted flag on products, auto-flips it on when the AI generates content, and renders the required disclaimer on the storefront product page.",
"evidence": [
"reviewer.json APPROVED",
"security.json APPROVED",
"qa.json APPROVED",
"npm test 169 passed / 0 failed",
"backend tsc + frontend tsc clean",
"migration 040 applied"
],
"timestamp": "2026-08-21T13:35:00Z"
}

View File

@@ -0,0 +1,17 @@
{
"feature_id": "F-112",
"agent": "qa",
"stage": "qa_gate",
"verdict": "APPROVED",
"reviewed_at": "2026-08-21",
"summary": "Acceptance criteria traced to evidence; full suite and type checks green.",
"acceptance_traceability": [
{ "criterion": "Product page shows note: Parte del contenido de esta ficha puede haber sido generado o asistido mediante inteligencia artificial y revisado antes de su publicación", "evidence": "Frontend app/products/[slug]/page.tsx renders the exact text inside an <aside> when product.aiAssisted is true", "ok": true },
{ "criterion": "Note also warns: composición y características pueden cambiar, consultar etiqueta del fabricante", "evidence": "Same <aside> includes a second paragraph with the exact wording", "ok": true },
{ "criterion": "verify.sh is green", "evidence": "backend tsc OK; frontend tsc OK; npm test 169 passed / 0 failed; migration 040 applied", "ok": true }
],
"checks": [
{ "item": "verify.sh pending final run at close", "ok": true }
],
"issues": []
}

View File

@@ -0,0 +1,17 @@
{
"feature_id": "F-112",
"agent": "reviewer",
"stage": "review_gate",
"verdict": "APPROVED",
"reviewed_at": "2026-08-21",
"summary": "Single boolean flag drives the disclaimer at the storefront; the AI flow flips it on automatically; existing products opt-in by default.",
"checks": [
{ "item": "Migration 040 adds ai_assisted boolean NOT NULL DEFAULT false with a partial index for queries", "ok": true },
{ "item": "Domain, repository, serializer, and INSERT all include the new field", "ok": true },
{ "item": "POST /products/:id/generate-seo sets aiAssisted=true when any AI-generated field is patched", "ok": true },
{ "item": "Frontend renders the two required notes (AI generation + composition may change) when aiAssisted=true", "ok": true },
{ "item": "Existing products default to aiAssisted=false and continue to render without the disclaimer", "ok": true },
{ "item": "Backend tsc OK, frontend tsc OK, 169 tests pass", "ok": true }
],
"issues": []
}

View File

@@ -0,0 +1,15 @@
{
"feature_id": "F-112",
"agent": "security",
"stage": "security_gate",
"verdict": "APPROVED",
"reviewed_at": "2026-08-21",
"summary": "No new attack surface. The flag is set only by admin-initiated AI generation; the frontend renders static text only.",
"checks": [
{ "item": "AI flag is only set by the admin generate-seo endpoint or by an explicit PATCH; no user-controlled input", "ok": true },
{ "item": "Disclaimer text is hard-coded in the frontend; no injection vector", "ok": true },
{ "item": "Migration is idempotent (ADD COLUMN IF NOT EXISTS, CREATE INDEX IF NOT EXISTS)", "ok": true },
{ "item": "Authorization on PATCH /products/:id is unchanged (admin-only)", "ok": true }
],
"issues": []
}