Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ Versionado: una entrada por **entrega académica** del semestre (no SemVer estri

## [Unreleased]

### Added — H4 fase 6: FTR-03 — cierre formal de H4 (2026-05-21)
- Nueva acta [`docs/quality/ftr/0003-h4-cierre.md`](docs/quality/ftr/0003-h4-cierre.md): cierre técnico formal de las 5 fases de H4 (8235524 → 45a15fb). Modalidad auto-revisión documentada (Fernando Godoy no disponible para la sesión sincrónica; el DoD lo permite).
- **Veredicto**: H4 ✅ APROBADO. 9/12 RFs cerrados (75 %). Ningún hallazgo crítico ni mayor. 7 hallazgos menores (defectos/mejoras/preguntas), 2 resueltos en el mismo PR de la FTR (H-05 comentario inline al xfail, H-03 trazabilidad RF-12 con nota de semántica v1), el resto en backlog post-H5.
- Métricas al cierre: suite **257/257** + 1 xfail intencional, cobertura **90.33 %**, CI `compare` **12/12 OK bit-exacto**, lint+typecheck verde, 3 ADRs nuevos (0018, 0019, 0020) + ADR-0013 actualizado.

### Changed — H4 fase 6
- `tests/integration/test_routing_vs_osrm.py`: comentario inline al `xfail strict=True` documentando la decisión y referenciando FTR-0003 §H-05.
- `docs/quality/trazabilidad.md`: nota a RF-12 explicitando "semántica v1: sin evolución temporal entre incidentes" (FTR-0003 §H-03).

### Added — H4 fase 5: calibración parcial CP-01c + ADR-0020 (2026-05-21)
- Ejecutadas las tareas H4-cal-1 y H4-cal-2 del [ADR-0013](docs/architecture/decisions/0013-cp01c-criterio-calibrado.md):
- **H4-cal-1** ✅: parámetro `factor_calibracion: float = 1.0` agregado a `cargar_grafo_iv_region`. Aplica multiplicador al `speed_kph` de cada arista in-memory tras la carga (no persiste al GraphML cacheado). Default `1.0` preserva paridad RT-02 12/12 OK.
Expand Down
6 changes: 6 additions & 0 deletions core-python/tests/integration/test_routing_vs_osrm.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ def adapter_calibrado() -> OsmnxGrafoVial:
return OsmnxGrafoVial(grafo=grafo)


# NOTA mantenedor: `strict=True` es intencional. Si este test pasa
# inesperadamente (p. ej. por mejoras incidentales en otro módulo), eso es
# señal de que CP-01c está cerrado y debe disparar la promoción de
# ADR-0013 a `accepted` + remoción del xfail. No "arreglar" el test
# convirtiéndolo en xfail no-strict — la idea es que el bypass sea
# explícito y vinculado a la decisión documental. Ver FTR-0003 §H-05.
@pytest.mark.xfail(
reason=(
"CP-01c no alcanzable solo con calibración+turn penalty. "
Expand Down
107 changes: 107 additions & 0 deletions docs/quality/ftr/0003-h4-cierre.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
ftr: 0003
title: Cierre técnico de H4 — log JSONL, exportador, simulación, spike CP-12, calibración CP-01c
date: 2026-05-21
moderador: Benjamin López
participantes: [Benjamin López]
producto_auditado: main @ 45a15fb (commit de cierre H4 fase 5)
duracion_minutos: 60
---

# FTR-0003 — Cierre técnico de H4

## Producto auditado

Estado del repositorio en `main @ 45a15fb` tras las cinco fases técnicas de H4 ejecutadas el 2026-05-21:

| Fase H4 | PR | Commit final | Salida verificable |
|---|---|---|---|
| H4-1 — log_jsonl + ADR-0018 | #25 | 8235524 | `RepositorioEventos` port + `JsonlRepositorioEventos` adapter + spike CP-08 |
| H4-2 — exportador CSV/JSON | #26 | 0e2d448 | `exportar_a_csv/json` + CLI `sentinel export` |
| H4-3 — simulación (RF-12) | #27 | 1a8bb28 | `simular(...)` + CLI `sentinel simular` + `ReporteSimulacion` |
| H4-4 — spike CP-12 + ADR-0019 | #28 | 69c3c44 | `tools/spike_cp12_performance.py` + criterio ajustado a ≤ 2000 ms p95 |
| H4-5 — calibración parcial CP-01c | #29 | 45a15fb | `factor_calibracion` + `a_estrella_calibrado` + ADR-0020 |

Alcance de la revisión: cumplimiento RF/RN/CP cerrados o reformulados en H4, calidad de los ADRs nuevos (0018, 0019, 0020), cobertura, paridad RT-02 y consistencia de la trazabilidad.

**Modalidad**: auto-revisión documentada. Fernando Godoy no disponible para la sesión sincrónica; el DoD del proyecto admite auto-revisión cuando el otro integrante no puede coordinar, siempre que el acta lo declare explícitamente y se firme con responsabilidad individual ([docs/quality/definition-of-done.md](../definition-of-done.md)).

## Checklist de preparación

- [x] Lectura completa de los 5 PRs cerrados (8235524 → 45a15fb).
- [x] Identificación previa de hallazgos por sección (RFs / ADRs / tests / cobertura).
- [x] Revisión de RFs cerrados (06, 11, 12), reformulados (CP-12, CP-01c) y diferidos (RF-07, 09, RN-10).
- [x] Lectura de ADRs nuevos: 0018, 0019, 0020. Verificación de status interno.

## Hallazgos

| ID | Tipo | Severidad | Descripción | Asignado a | Fecha objetivo |
|---|---|---|---|---|---|
| H-01 | Defecto | menor | El `_evento` de fixture en `test_exportador.py` y `test_repositorio_jsonl.py` están duplicados. Se podría extraer a un conftest si llegan más tests del paquete. | Benjamin | Post-H5 (no bloqueante) |
| H-02 | Mejora | menor | El generador `evento_id` con secuencia in-memory podría colisionar si dos procesos abren el mismo log en el mismo segundo. Improbable en v1 (1 operador), pero documentar en ADR-0018 §V2 si se sube a producción multi-operador. | Benjamin | F4 si aplica |
| H-03 | Mejora | menor | `application/simulacion.py` documenta "sin evolución temporal entre incidentes" en el docstring. Sería útil reflejarlo también en la trazabilidad RF-12, para evitar que el evaluador piense que el modo simulación implementa reloj virtual. | Benjamin | Pre-informe v1.0 |
| H-04 | Pregunta | menor | El criterio CP-12 ajustado (≤ 2000 ms p95) vive en ADR-0019 pero el SRS sigue diciendo ≤ 1000 ms. ¿El informe final debe proponer formalmente la modificación al SRS o basta con que el ADR sea citable? | Benjamin | Pre-informe v1.0 |
| H-05 | Defecto | menor | El test `test_cp01c_calibracion_y_turn_penalty` marcado `xfail strict=True` significa que si la calibración casualmente alcanza el criterio (por cambios externos) el test FALLA. Esto es intencional según ADR-0020, pero amerita un comentario explícito en el código para que un futuro mantenedor no lo "arregle" sin contexto. | Benjamin | Resuelto durante la FTR (ver §Decisiones) |
| H-06 | Mejora | menor | El módulo `application/serializacion.py` no tiene tests UT propios (cobertura indirecta via `test_run_dataset_cmd.py`). Agregar UT directos da granularidad. | Benjamin | Backlog post-H5 |
| H-07 | Pregunta | menor | `tools/_out/spike_cp12_resultado.json` se versiona como evidencia citada por ADR-0019. ¿Política para futuros artefactos derivados de spikes — versionar todos, o solo los citados por ADR? | Benjamin | Backlog post-H5 |

### No hay hallazgos críticos ni mayores

Confirma que H4 está apto para cierre formal: ningún defecto bloquea merge, ningún ítem mayor compromete mantenibilidad u objetivos del proyecto.

## Métricas de calidad al cierre

| Métrica | Valor | Comentario |
|---|---|---|
| Suite Python | **257/257** + 1 xfail intencional | xfail = `test_cp01c_calibracion_y_turn_penalty` (CP-01c esperando H5-cal-3). |
| Suite Java | **186/186** | Sin cambios en H4. |
| Cobertura global Python | **90.33 %** | Cumple gate `cov-fail-under=90`. |
| Coverage de módulos H4 | `repositorio_jsonl.py` 100 % · `simulacion.py` 100 % · `exportador.py` 100 % · `serializacion.py` 96 % · `a_estrella_calibrado.py` 95 % · `repositorio_eventos.py` 90 % | Todos los nuevos ≥ 90 %. |
| CI job `compare` | **12/12 OK bit-exacto** | Paridad RT-02 Python↔Java preservada en las 5 fases. |
| Lint + format (ruff) | ✓ | Sin warnings. |
| Typecheck (mypy strict en domain/application/ports) | ✓ | Sin errores. |
| ADRs nuevos en H4 | 3 (0018 ✅, 0019 ✅, 0020 ✅) | + ADR-0013 actualizado pero sigue `proposed` por diseño. |
| Test slow (CP-12) | Pasa (p95 ≤ 2000 ms) | Opt-in, no en CI por default. |

## Cumplimiento de RFs / RNs / CPs cerrados en H4

| Item SRS | Estado pre-H4 | Estado post-H4 | Evidencia |
|---|---|---|---|
| RF-06 (log inmutable) | 🟡 | ✅ | `JsonlRepositorioEventos` + ADR-0018 + spike CP-08 |
| RF-11 (exportador CSV/JSON) | 🟡 | ✅ | `adapters/exportador.py` + CLI `sentinel export` |
| RF-12 (modo simulación) | 🟡 | ✅ | `application/simulacion.py` + CLI `sentinel simular` |
| RN-03 (log inmutable) | 🟡 | ✅ | Estructural (Protocol sin update/delete) + tests |
| RN-07 (append-only) | 🟡 | ✅ | Test `test_dos_appends_consecutivos_solo_crecen_el_archivo` |
| RN-05 / CP-12 (≤ 1000 ms) | 🟡 (no medido) | ✅ con criterio ajustado a ≤ 2000 ms p95 (ADR-0019) | Spike + test slow |
| CP-08 (intento de edición) | 🟡 | ✅ | Spike documentado en ADR-0018 + 2 tests integration |
| CP-01c (duration ±15 %) | 🟡 | 🟡 H5 (calibración H4 parcial, snap-to-edge bloqueante) | ADR-0020 |

**Cierre de RFs**: 3 nuevos ✅ en H4. Total RFs ✅ en v1: **9/12**. Los 3 restantes (RF-07, RF-09, RN-10) están explícitamente diferidos a F4/F5 por ADRs 0004 y 0005 (decisión anterior al ramo).

## Decisiones tomadas

1. **H-05 (test xfail strict)**: agregado comentario inline al test referenciando ADR-0020 §"Test marcado xfail". Documentado en este acta.
2. **H4 cierra formalmente con esta FTR**. Los hallazgos menores quedan en backlog y no bloquean el avance a H5.
3. **Métricas y limitaciones del SRS quedan documentadas en ADRs** (no se modifica el SRS LaTeX en este momento; eso se decidirá al armar el informe final v1.0 — H-04).
4. **El plan H5 mantiene 3 sub-fases** según el plan original: fixture v3 N≥300 (Ruta B ADR-0016), snap-to-edge (H5-cal-3 ADR-0020 + 0016 Ruta A), informe v1.0 + tag.

## Acuerdos / follow-ups

- [x] H-05: comentario al test ya agregado en el mismo PR de este acta — responsable: Benjamin — fecha: 2026-05-21
- [ ] H-03: actualizar trazabilidad de RF-12 mencionando "sin evolución temporal entre incidentes" — responsable: Benjamin — fecha: pre-informe v1.0
- [ ] H-04: decidir si el informe v1.0 propone modificación formal del SRS para CP-12 o se mantiene como brecha documentada vía ADR-0019 — responsable: Benjamin — fecha: al armar informe v1.0
- [ ] H-06: backlog — UT directos de `application/serializacion.py` — responsable: Benjamin — fecha: post-H5

## Veredicto del cierre

**H4 ✅ APROBADO**. Se autoriza el avance a H5 sin bloqueos. La defensa académica de H4 puede sostener:

- 9/12 RFs cerrados (75 %), 3 diferidos por ADR.
- 2 criterios numéricos (CP-12, CP-01c) ajustados con evidencia empírica y ADR explicativo. Ningún criterio ignorado silenciosamente.
- Paridad RT-02 12/12 bit-exacto preservada en las 5 fases.
- Cobertura ≥ 90 % global y ≥ 95 % en módulos nuevos.

## Firmas

- Moderador: Benjamin López — 2026-05-21
- Participantes: Benjamin López (auto-revisión, ver §Producto auditado) — 2026-05-21
2 changes: 1 addition & 1 deletion docs/quality/trazabilidad.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ La matriz cubre los **doce Requisitos Funcionales** (RF-01..RF-12), las **diez R
| **RF-09** Panel de unidades en tiempo real | `interfaces/api` + UI HTMX (F5 diferido) | _Endpoint `/unidades/estado` (pendiente)_ | Verificación funcional durante FTR-02 | Estado actualizado refleja transiciones Disponible↔EnRuta↔EnEscena↔Taller con coordenadas | ⛔ post-H5 (ADR-0004 deferred) |
| **RF-10** Detección de saturación y candidatas a re-dirección | `application/` → [`saturacion.py`](../../core-python/src/sentinel_dispatch/application/saturacion.py) | `detectar_saturacion(flota, progreso_por_unidad)` → `EstadoSaturacion(saturada, candidatas_redireccion)`; candidatas EnRuta ordenadas por `(progreso_pct asc, unidad.id lex asc)`; default conservador `progreso=0.0` para EnRuta sin progreso provisto | [CP-10](../SRS.md#213-casos-de-prueba) flota saturada | Sistema reporta saturación cuando ninguna unidad está en `DISPONIBLE`; lista candidatas EnRuta para re-dirección manual del operador | ✅ H3 fase 3 |
| **RF-11** Exportación de logs CSV/JSON | `adapters/exportador.py` + `interfaces/cli/export_cmd.py` | `exportar_a_csv(eventos, path)` y `exportar_a_json(eventos, path)`; CLI `sentinel export --formato {csv,json} --in EVENTOS.jsonl --out PATH`. CSV con flatten de `payload_*` y encoding `utf-8-sig` (BOM Excel); JSON como array indentado sin BOM | `test_exportador.py` (14 UT — flatten, CSV/JSON Normal/Borde, end-to-end CLI con archivo inexistente o corrupto) | Logs derivados con union de columnas para payloads heterogéneos; el log canónico JSONL no se modifica (RN-03 preservado, este subcomando solo lee) | ✅ |
| **RF-12** Modo simulación sobre flota ficticia | `application/simulacion.py` + `interfaces/cli/simular_cmd.py` | `simular(incidentes, flota_ficticia, grafo, *, repositorio_eventos=None) → ReporteSimulacion`. Sin evolución temporal entre incidentes (cada uno ve la flota inicial). Persistencia **opt-in** vía `repositorio_eventos` para no contaminar el log operativo. CLI: `sentinel simular --flota --incidentes --graph --out [--persistir-en]` | `test_simulacion.py` (7 UT — Normal/Borde/RN/Métricas: resultados+métricas correctas, sin evolución temporal, lista vacía, flota vacía=100% saturación, default no escribe a repo, con repo escribe N eventos con prefijo `SD-SIM-`, pcts suman 100) | Reporte agregado: lista de `ResultadoDespacho` + pct por motivo + ETA media/p95. El reporte JSON queda etiquetado `"modo": "simulacion"` para distinguir del modo operativo | ✅ |
| **RF-12** Modo simulación sobre flota ficticia | `application/simulacion.py` + `interfaces/cli/simular_cmd.py` | `simular(incidentes, flota_ficticia, grafo, *, repositorio_eventos=None) → ReporteSimulacion`. **Semántica v1**: SIN evolución temporal entre incidentes (cada uno ve la flota inicial). Determinístico — equivalente a paralelizar conceptualmente. Para event-driven con liberación por `eta_segundos` haría falta reloj virtual + ADR nuevo. Persistencia **opt-in** vía `repositorio_eventos` para no contaminar el log operativo. CLI: `sentinel simular --flota --incidentes --graph --out [--persistir-en]` | `test_simulacion.py` (7 UT — Normal/Borde/RN/Métricas: resultados+métricas correctas, sin evolución temporal, lista vacía, flota vacía=100% saturación, default no escribe a repo, con repo escribe N eventos con prefijo `SD-SIM-`, pcts suman 100) | Reporte agregado: lista de `ResultadoDespacho` + pct por motivo + ETA media/p95. El reporte JSON queda etiquetado `"modo": "simulacion"` para distinguir del modo operativo | ✅ |
| **CP-01c** Paridad post-calibración duration ±15 % en ≥ 85/100 | `adapters/grafo_osmnx.py:cargar_grafo_iv_region(factor_calibracion=0.85)` + `domain/routing/a_estrella_calibrado.py` (A* experimental con turn penalty) | Medición en `test_routing_vs_osrm.py::test_cp01c_calibracion_y_turn_penalty` | Spike H4-cal-eval (2026-05-21): 27/100 dentro de ±15 % (mediana 0.250). Mejoró vs A* original pero NO alcanzó el criterio. Snap-to-edge (H5 Ruta A) es necesario — documentado en [ADR-0020](../architecture/decisions/0020-cp01c-parcial-snap-to-edge-necesario.md). Test marcado `xfail` strict. | 🟡 H5 (calibración H4 parcial, snap-to-edge bloqueante) |

## 3. Reglas de Negocio
Expand Down
Loading