Registro #71 y plan del webhook Jira FAC → vista previa de cotización BIND: app en modo escritura, token de Jira, ADR-001 intacto (confirmación humana) y 5 fases con una sola escritura auditada en Jira
This commit is contained in:
+14
-1
@@ -2,7 +2,20 @@
|
|||||||
|
|
||||||
Acciones vivas del proyecto. Formato: `[ ]` abierta · `[x]` cerrada (no se borran, dejan rastro). Cada una con responsable y, si aplica, fecha. Ver contexto en [REGISTRO.md](REGISTRO.md).
|
Acciones vivas del proyecto. Formato: `[ ]` abierta · `[x]` cerrada (no se borran, dejan rastro). Cada una con responsable y, si aplica, fecha. Ver contexto en [REGISTRO.md](REGISTRO.md).
|
||||||
|
|
||||||
_Última actualización: **2026-09-15** — corte de horas 10-ago→15-sep reconstruido y huecos de registro listados (seis sesiones sin bitácora). Anterior: **2026-08-21, mediodía** — ⭐ primera cotización real emitida (folio 1026; la 2.ª, corregida, pendiente de confirmar). Anterior: **2026-08-20, noche.** Cruce completo del buzón contra la bitácora (MCP ms365): se incorporaron 9 comunicaciones faltantes y se corrigieron 2 errores de registro. **Hay sesión mañana viernes 21-ago, 7:00 am** con Noé, Ara y Pedro sobre el formulario modificado y `FAC-106`. Ver [REGISTRO #65](REGISTRO.md)–[#68](REGISTRO.md)._
|
_Última actualización: **2026-09-29** — plan del webhook Jira FAC → vista previa de cotización ([#71](REGISTRO.md)). Anterior: **2026-09-15** — corte de horas 10-ago→15-sep reconstruido y huecos de registro listados (seis sesiones sin bitácora). Anterior: **2026-08-21, mediodía** — ⭐ primera cotización real emitida (folio 1026; la 2.ª, corregida, pendiente de confirmar). Anterior: **2026-08-20, noche.** Cruce completo del buzón contra la bitácora (MCP ms365): se incorporaron 9 comunicaciones faltantes y se corrigieron 2 errores de registro. **Hay sesión mañana viernes 21-ago, 7:00 am** con Noé, Ara y Pedro sobre el formulario modificado y `FAC-106`. Ver [REGISTRO #65](REGISTRO.md)–[#68](REGISTRO.md)._
|
||||||
|
|
||||||
|
## ⭐ 29-sep — webhook Jira FAC → vista previa de cotización ([REGISTRO #71](REGISTRO.md), [plan](../planeacion/Plan-Webhook-Jira-2026-09-29.md))
|
||||||
|
|
||||||
|
- [ ] 📄 **Archivar en `fuentes/` la autorización escrita de Pedro** para crear el webhook/regla en Jira; anotar su alcance (¿incluye crear un ticket de prueba en FAC?).
|
||||||
|
- [ ] 🔎 **Fase 1 (solo GET):** `myself` + `mypermissions` (ADMINISTER / ADMINISTER_PROJECTS en FAC) + inventario de webhooks → decide Vía A (webhook de sistema) o Vía B (regla de Automation).
|
||||||
|
- [ ] 🔴 **Decidir el evento:** creación del ticket, transición a *En proceso de facturación* (dura ~3 min), o ambos.
|
||||||
|
- [ ] 🔧 **Fase 2:** `POST /api/jira/webhook` con secreto, relectura del ticket, idempotencia y solo dry-run; pruebas con `FakeHandler`. Poller de respaldo.
|
||||||
|
- [ ] 🌐 **Fase 3:** ngrok con traffic policy de una sola ruta; API en ReadOnly mientras el túnel esté abierto.
|
||||||
|
- [ ] ✍️ **Fase 4:** alta del webhook/regla como operación numerada (id, URL, fecha, autorización).
|
||||||
|
- [ ] 🧪 **Fase 5:** ticket de prueba → "lista para revisar" → verificar que BIND no recibió escritura. Reversa documentada.
|
||||||
|
- [ ] ☁️ **Destino productivo del webhook:** ngrok con dominio fijo (de pago) vs Azure (sigue bloqueado por el cliente).
|
||||||
|
- [ ] ⚠️ **Una sola sesión de Claude Code dueña de la API** en sesiones de escritura (el 29-sep otra sesión la relanzó en ReadOnly).
|
||||||
|
- [ ] 📝 **Hueco de bitácora:** registrar el trabajo del 9/10-sep (puente a la UI de BIND, prefactura 2463, sonda #12 / remisión 1004) — hoy solo vive en `balam-plataforma/CLAUDE.md`.
|
||||||
|
|
||||||
## 🚨 Huecos de registro (detectados en el corte del 15-sep)
|
## 🚨 Huecos de registro (detectados en el corte del 15-sep)
|
||||||
|
|
||||||
|
|||||||
@@ -1610,6 +1610,32 @@ Se aplicó el protocolo del guion §6: parar, anunciar el diagnóstico, pivotear
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 71 · Sep 29, 2026 · Sesión de trabajo · App en modo escritura, token de Jira y plan del webhook FAC → vista previa de cotización ⭐
|
||||||
|
|
||||||
|
> Contexto: sesión de Johann con Claude Code. Nota: la bitácora no tiene entradas entre el #70 (26-ago) y este; el trabajo del 9/10-sep (puente a la UI de BIND para la prefactura) está documentado solo en `balam-plataforma/CLAUDE.md` y se sube al repo en esta misma fecha.
|
||||||
|
|
||||||
|
**1. Levantar la app en modo escritura.** Se arrancó Docker Desktop (estaba apagado) → Postgres `balam-postgres-dev` en 5438, frontend en 4200 y API en 5078 con `Bind__Mode=Write`, `Bind__Ui__Enabled=true` y `Bind__Ui__Mode=Write` como **variables de entorno de esa corrida** (los user-secrets siguen en `Bind:Mode=ReadOnly`, así que al apagar vuelve sola a solo lectura).
|
||||||
|
- ⚠️ **Choque entre dos sesiones de Claude Code:** otra sesión en la misma máquina levantó su propia API a las 13:24 (y de nuevo a las 15:28, `--no-build`) **sin** las variables de escritura. La API de esta sesión murió sin excepción en el log y el reinicio falló con `address already in use`. La API que quedó viva en 5078 era la de la otra sesión, en `Bind:Mode=ReadOnly`.
|
||||||
|
- Evidencia en pantalla: el paso 3 del wizard (*Vista previa y creación (ADR-001)*) generó la **operación #15 · `POST /api/Quotes`** como dry-run registrado en `WriteOperations`, con el botón *Crear cotización en BIND* deshabilitado: *"el servidor está en modo ReadOnly"*. **No se escribió nada en BIND.**
|
||||||
|
- Lección: una sola sesión debe ser dueña de la API durante una sesión de escritura; verificar el dueño del puerto (no solo que esté escuchando).
|
||||||
|
|
||||||
|
**2. Token de Jira.** Existe `Jira:ApiToken` en user-secrets de `Balam.Api` (junto con `Bind:ApiKey`, `Jwt:SigningKey`, etc.); configuración en `appsettings.Development.json`: `balam-jsm-temp.atlassian.net`, usuario `pedro.ayala@…` (token personal de **Pedro**), proyecto FAC, `Mode=ReadOnly`. No se verificó el valor (el clasificador de permisos bloqueó leerlo, correctamente). Prueba práctica de vigencia: que la bandeja de Solicitudes FAC cargue.
|
||||||
|
|
||||||
|
**3. ¿Se puede leer la creación de un ticket y automatizar la cotización?**
|
||||||
|
- **Leer: sí, hoy.** Polling con JQL delta (`SearchJqlAsync` + `SyncSchedulerService` + checkpoint con solape, ADR-002) o webhook. Recordatorio del 10-ago: el disparador acordado (*En proceso de facturación*) dura ~3 min, así que el poller debe leer el changelog, no el estatus actual.
|
||||||
|
- **Crear la cotización sin humano: técnicamente sí, pero rompe ADR-001** (dry-run + confirmación humana en cada escritura a BIND producción). Propuesta aceptada como camino: **el webhook/poller deja lista la vista previa y un humano confirma con un clic.** Automatización total solo con autorización escrita de Balam y enmienda a ADR-001.
|
||||||
|
|
||||||
|
**4. Webhook / Jira Automation — qué se necesita.** URL pública (ngrok ya instalado en la máquina de Johann; productivo = Azure o dominio fijo), endpoint nuestro `POST /api/jira/webhook` (no existe aún), y el disparador en Jira. Con el token actual: construir el endpoint sí (no requiere permisos de Jira); crear el webhook/regla en Jira solo si el usuario de Pedro es admin **y** con autorización expresa, porque es escribir en la configuración del Jira productivo a su nombre.
|
||||||
|
- **Johann confirma que existe autorización escrita de Pedro** para hacerlo. ⚠️ Aún no archivada en `fuentes/`.
|
||||||
|
|
||||||
|
**Resultado:** plan en [`../planeacion/Plan-Webhook-Jira-2026-09-29.md`](../planeacion/Plan-Webhook-Jira-2026-09-29.md): 5 fases (verificar permisos por GET → endpoint con secreto/idempotencia/relectura → ngrok con traffic policy de una sola ruta → una escritura auditada en Jira, preferentemente webhook de sistema → ticket de prueba y reversa).
|
||||||
|
|
||||||
|
**Repo de la plataforma:** se suben los cambios pendientes (puente de prefactura por la UI de BIND: `BindUiClient`, `PrefacturaUiEndpoints`, tarjeta del wizard y pruebas). Verificado antes del push: 107 pruebas en verde y frontend compilando; escaneo del diff sin secretos.
|
||||||
|
|
||||||
|
> **Lectura estratégica:** el webhook no cambia el protocolo de escritura: lo que acelera es la *captura*, no la *decisión*. Y el caso más valioso no es la creación del ticket sino el cruce de 3 minutos por *En proceso de facturación*, que el polling de 15 min pierde.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Resumen ejecutivo del hilo (para contexto rápido)
|
## Resumen ejecutivo del hilo (para contexto rápido)
|
||||||
|
|
||||||
### Datos duros confirmados por Balam
|
### Datos duros confirmados por Balam
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# Plan — Webhook Jira (FAC) → vista previa automática de cotización BIND
|
||||||
|
|
||||||
|
_Creado: 2026-09-29 · Responsable: Johann · Contexto: [REGISTRO #71](../bitacora/REGISTRO.md) · Antecedente técnico: [Investigacion-API-Jira.md §Bloque 3–4](Investigacion-API-Jira.md)_
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Que la plataforma se entere **en el momento** en que se crea (o avanza) un ticket de FAC y deje **preparada la vista previa (dry-run) de la cotización en BIND**, para que un humano solo revise y confirme con un clic.
|
||||||
|
|
||||||
|
**Fuera de alcance:** crear la cotización en BIND sin confirmación humana. ADR-001 sigue vigente: toda escritura en BIND producción = dry-run + confirmación humana + `Bind:Mode=Write` encendido solo durante la operación. La automatización total requeriría autorización escrita de Balam **y** enmienda de ADR-001, con reglas (solo clientes conocidos, IVA dentro del catálogo, tope de monto, interruptor de apagado).
|
||||||
|
|
||||||
|
## Decisiones previas (antes de escribir en Jira)
|
||||||
|
|
||||||
|
1. **¿Qué evento dispara?** Hay dos candidatos y no son equivalentes:
|
||||||
|
- `jira:issue_created`: el ticket nace. Es lo que se pidió el 29-sep.
|
||||||
|
- Transición a **`En proceso de facturación`**: es el disparador acordado en julio/agosto. Dura **~3 minutos** (FAC-100: 2m44s), así que un poller de 15 min se lo pierde; el webhook lo resuelve de raíz.
|
||||||
|
- **Propuesta:** escuchar `created` **y** `updated` (filtrado a cambio de estatus) en un solo webhook; la plataforma decide qué hacer con cada uno leyendo el changelog.
|
||||||
|
2. **Tickets de `Automation for Jira`** (10 de 69 al 10-ago, sin request type ni campos estructurados): el endpoint los recibe, pero los marca *"sin datos para cotizar"* en lugar de armar una vista previa.
|
||||||
|
3. **Autorización escrita de Pedro:** Johann confirma que existe (29-sep). **Falta archivarla en `fuentes/`** con fecha, y confirmar su alcance: ¿cubre solo crear el webhook/regla, o también crear un ticket de prueba en FAC?
|
||||||
|
|
||||||
|
## Fases
|
||||||
|
|
||||||
|
### Fase 1 — Verificación de permisos (solo lectura, riesgo cero)
|
||||||
|
|
||||||
|
- `GET /rest/api/3/myself`: el token es de Pedro y está vivo.
|
||||||
|
- `GET /rest/api/3/mypermissions?projectKey=FAC&permissions=ADMINISTER,ADMINISTER_PROJECTS`
|
||||||
|
- `ADMINISTER` = admin del sitio → **Vía A** (webhook de sistema).
|
||||||
|
- Solo `ADMINISTER_PROJECTS` → **Vía B** (regla de Automation del proyecto).
|
||||||
|
- Ninguno → Pedro lo crea en pantalla; nosotros solo damos los datos.
|
||||||
|
- `GET /rest/webhooks/1.0/webhook` (si Vía A): inventario de webhooks existentes, para no duplicar y para saber qué hay antes de tocar.
|
||||||
|
|
||||||
|
### Fase 2 — Endpoint en la plataforma (código nuestro, no toca Jira)
|
||||||
|
|
||||||
|
`POST /api/jira/webhook` en `Balam.Api/Jira/`:
|
||||||
|
|
||||||
|
- **Autenticación:** secreto compartido (Vía A: firma HMAC `X-Hub-Signature` si el sitio la ofrece; Vía B: header propio). Sin secreto válido → 401, sin leer el cuerpo. Secreto en user-secrets (`Jira:WebhookSecret`), nunca en el repo.
|
||||||
|
- **No confía en el payload:** solo extrae `issueKey`, valida que sea `FAC-\d+`, y **relee el ticket** con `JiraClient` (ReadOnly).
|
||||||
|
- **Idempotencia:** tabla/registro por `(issueKey, evento, timestamp)`; un reintento de Jira no genera una segunda vista previa.
|
||||||
|
- **Acción:** arma el body de `POST /api/Quotes` con el mismo mapeo del wizard (cliente del combo 11598, servicio, cantidad, IVA como tasa, moneda, días de crédito), lo registra en `WriteOperations` como `DryRun` y marca el ticket como **"Cotización lista para revisar"** en la bandeja. **Nunca llama a BIND en escritura.**
|
||||||
|
- **Responde rápido** (202) y procesa en segundo plano: Jira reintenta si el webhook tarda.
|
||||||
|
- **Pruebas** xUnit con `FakeHandler`: secreto inválido → 401; payload con clave ajena → 400; reintento → una sola vista previa; ticket de Automation sin campos → "sin datos"; jamás se invoca un método mutante de `BindClient`.
|
||||||
|
- El **poller** (JQL delta con solape, ADR-002) queda como respaldo: si la laptop está apagada y el webhook falla, el ticket se recupera en la siguiente pasada.
|
||||||
|
|
||||||
|
### Fase 3 — Exposición temporal con ngrok
|
||||||
|
|
||||||
|
- `ngrok http 5078` con **traffic policy** que solo deja pasar `POST /api/jira/webhook`; cualquier otra ruta → 403 en el borde. Login, cartera y emisión **no** quedan expuestos.
|
||||||
|
- Mientras el túnel esté abierto: API en `Bind:Mode=ReadOnly`.
|
||||||
|
- ⚠️ Con ngrok gratuito la URL cambia en cada reinicio y depende de la máquina de Johann encendida. Es **piloto**, no productivo. Productivo = dominio fijo de ngrok (de pago) o Azure (bloqueado por el cliente).
|
||||||
|
|
||||||
|
### Fase 4 — Alta del disparador en Jira (UNA escritura, auditada)
|
||||||
|
|
||||||
|
Operación puntual, numerada en la bitácora; **no** se cambia `Jira:Mode` de la app.
|
||||||
|
|
||||||
|
- **Vía A (preferida):** `POST /rest/webhooks/1.0/webhook` con `events: ["jira:issue_created", "jira:issue_updated"]`, `filters: { "issue-related-events-section": "project = FAC" }`, URL de ngrok y secreto. Reproducible, sin consumir cuota de Automation, reversible con `DELETE /rest/webhooks/1.0/webhook/{id}`.
|
||||||
|
- **Vía B:** regla de Automation del proyecto FAC: *Work item created* (y/o *Work item transitioned → En proceso de facturación*) → *Send web request* (POST, header del secreto, body `{"issueKey":"{{issue.key}}","evento":"{{webhookEvent}}"}`). Se crea en la UI de Automation (la API de reglas es menos estable). Consume cuota mensual de ejecuciones: verificar el plan con Pedro.
|
||||||
|
- Anotar en la bitácora: id del webhook/regla, URL, fecha, quién autorizó.
|
||||||
|
|
||||||
|
### Fase 5 — Prueba y reversa
|
||||||
|
|
||||||
|
- **Prueba:** un ticket de prueba en FAC (lo crea alguien de Balam, o Johann si la autorización lo cubre). Criterio de éxito: llega el aviso → aparece "lista para revisar" → `WriteOperations` tiene un `DryRun` → **BIND no recibió ninguna escritura** (verificar por lectura).
|
||||||
|
- **Reversa:** borrar webhook/regla (id anotado) + cerrar ngrok. El endpoint queda inerte sin secreto configurado.
|
||||||
|
|
||||||
|
## Riesgos
|
||||||
|
|
||||||
|
| Riesgo | Mitigación |
|
||||||
|
|---|---|
|
||||||
|
| API local expuesta a internet | Traffic policy de ngrok (una sola ruta), secreto, `Bind:Mode=ReadOnly` con túnel abierto |
|
||||||
|
| Escritura en el Jira productivo del cliente | Una sola operación, autorizada por escrito, numerada y reversible |
|
||||||
|
| Payload falsificado | Secreto/HMAC + relectura del ticket por API |
|
||||||
|
| Pérdida de eventos (laptop apagada, URL de ngrok cambió) | Poller de respaldo con checkpoint y solape |
|
||||||
|
| Cuota de Automation (Vía B) | Verificar plan; preferir Vía A |
|
||||||
|
| Dos sesiones de Claude Code levantando la API a la vez (29-sep) | Una sola sesión dueña de la API durante la prueba |
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] Archivar la autorización escrita de Pedro en `fuentes/` y anotar su alcance
|
||||||
|
- [ ] Fase 1 — verificar identidad y permisos del token (solo GET)
|
||||||
|
- [ ] Decidir evento(s): creación, transición a *En proceso de facturación*, o ambos
|
||||||
|
- [ ] Fase 2 — endpoint + idempotencia + pruebas
|
||||||
|
- [ ] Fase 3 — ngrok con traffic policy de una sola ruta
|
||||||
|
- [ ] Fase 4 — alta del webhook/regla (operación numerada)
|
||||||
|
- [ ] Fase 5 — ticket de prueba, verificación y registro en bitácora
|
||||||
|
- [ ] Definir el destino productivo (ngrok de pago vs Azure)
|
||||||
Reference in New Issue
Block a user