{
  "name": "AlterEgo",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "kb",
        "authentication": "headerAuth",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -720,
        -144
      ],
      "id": "ac353611-6c0c-41e8-81bc-f2d29cc0800c",
      "name": "Webhook",
      "webhookId": "748ff501-1678-429b-a558-df8052dc1280"
    },
    {
      "parameters": {
        "url": "https://api.github.com/repos/ricardPlana/AlterEgo/git/ref/heads/main",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        -240,
        -144
      ],
      "id": "bf697bbd-f0c7-4bfd-a68f-1406d50dc659",
      "name": "GET sha main",
      "executeOnce": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.github.com/repos/ricardPlana/AlterEgo/git/refs",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"ref\": \"refs/heads/{{ $('Code').first().json.branch }}\",\n  \"sha\": \"{{ $json.object.sha }}\"\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        -16,
        -144
      ],
      "id": "a1b7ef53-552d-4a20-925f-a308b83c6304",
      "name": "POST rama",
      "executeOnce": true
    },
    {
      "parameters": {
        "jsCode": "const raw = $input.first().json;\n\n// El webhook deja el cuerpo en $json.body (objeto si llegó como JSON, string si llegó como texto).\nlet doc = raw.body ?? raw;\nif (doc && doc.document) doc = doc.document;\nif (typeof doc === 'string') {\n  const s = doc.indexOf('{');\n  const e = doc.lastIndexOf('}');\n  if (s < 0 || e < 0) throw new Error('No se encontró JSON en el cuerpo');\n  doc = JSON.parse(doc.slice(s, e + 1));\n}\nif (!doc.items || !doc.control) {\n  throw new Error('El JSON no tiene items/control. Claves recibidas: ' + Object.keys(doc || {}).join(', '));\n}\n\nconst slug = (s, max = 60) => {\n  let t = (s ?? '')\n    .toString()\n    .replace(/\\[no consta\\]/gi, 'sin-asignar')\n    .normalize('NFD')\n    .replace(/[\\u0300-\\u036f]/g, '')\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '');\n  if (t.length > max) {\n    t = t.slice(0, max);\n    const i = t.lastIndexOf('-');\n    if (i > 20) t = t.slice(0, i);\n    t = t.replace(/-(y|e|o|de|del|en|a|el|la|los|las|un|una|con|por|para)$/, '');\n  }\n  return t.replace(/^-+|-+$/g, '') || 'sin-asignar';\n};\n\nconst q = (v) => JSON.stringify(v ?? '');\nconst esc = (s) => String(s ?? '').replace(/\\|/g, '\\\\|');\nconst list = (arr) => (arr && arr.length ? arr.map((x) => `- ${x}`).join('\\n') : '- Sin registros en este informe');\n\n// Sufijo con la hora (HHMMSS) para poder relanzar el mismo informe sin colisión de rama\nconst stamp = new Date().toISOString().slice(11, 19).replace(/:/g, '');\nconst branch = `ingest/${doc.informe}-${doc.fuente}-${stamp}`;\n\n// Descripción del PR\nconst c = doc.control;\nconst pendientes = doc.items.flatMap((it) =>\n  (it.pendientes || []).map((p) => `- [ ] ${p.accion} — **${p.responsable}** — ${p.vencimiento}`)\n);\nconst descartados = c.descartados || [];\nconst totalDescartados = descartados.reduce((a, d) => a + (d.n || 0), 0);\n\nconst prBody = [\n  `## Informe ${doc.informe} · ${doc.fuente}`,\n  `Generado ${doc.generado} · ventana ${doc.ventana}`,\n  '',\n  '### Cobertura',\n  '| Hilos detectados | Leídos | Con ítem | Ítems sin proyecto |',\n  '|:-:|:-:|:-:|:-:|',\n  `| ${c.hilos_detectados} | ${c.hilos_leidos} | ${c.hilos_con_item} | ${c.items_sin_proyecto} |`,\n  '',\n  c.cobertura_completa\n    ? '> **Cobertura completa.** Se han leído todos los hilos de la ventana.'\n    : `> **Atención: cobertura incompleta.** ${c.motivo_cobertura}`,\n  '',\n  `### Notas incluidas (${doc.items.length})`,\n  '| Cliente | Proyecto | Nota |',\n  '|---|---|---|',\n  ...doc.items.map((it) => `| ${esc(it.cliente)} | ${esc(it.proyecto)} | ${esc(it.tema)} |`),\n  '',\n  '### Pendientes con fecha',\n  ...(pendientes.length ? pendientes : ['- Ninguno']),\n  '',\n  '### Checklist de revisión',\n  '- [ ] Cada nota nombra el cliente y el proyecto correctos',\n  '- [ ] Las cifras y las fechas coinciden con el correo original',\n  '- [ ] Si una nota sustituye a otra, lo dice de forma explícita',\n  '- [ ] No hay ítems sin proyecto pendientes de asignar',\n  '',\n  `<details><summary>Descartados (${totalDescartados})</summary>`,\n  '',\n  ...descartados.map((d) => `- ${d.motivo} (${d.n}): ${(d.ejemplos || []).join('; ')}`),\n  '',\n  '</details>',\n].join('\\n');\n\nconst out = [];\n\nfor (const it of doc.items) {\n  const path = `knowledge/${slug(it.cliente)}/${slug(it.proyecto)}/${it.fecha}-${slug(it.tema)}.md`;\n\n  const frontmatter = [\n    '---',\n    `tema: ${q(it.tema)}`,\n    `tipo: ${it.tipo}`,\n    `cliente: ${q(it.cliente)}`,\n    `proyecto: ${q(it.proyecto)}`,\n    `fecha: ${it.fecha}`,\n    `fuente: ${doc.fuente}`,\n    `informe: ${doc.informe}`,\n    `canal: ${q(it.canal)}`,\n    `asunto: ${q(it.asunto)}`,\n    `participantes: ${q((it.participantes || []).map((p) => `${p.nombre} (${p.organizacion})`))}`,\n    '---',\n  ].join('\\n');\n\n  const sections = [\n    `# ${it.tema}`,\n    '',\n    '## Contexto',\n    it.contexto || '[no consta]',\n    '',\n    '## Datos',\n    list(it.datos),\n    '',\n    '## Decisiones',\n    list((it.decisiones || []).map((d) => `${d.decision} (${d.quien}; alcance: ${d.alcance})`)),\n    '',\n    '## Pendientes',\n    list((it.pendientes || []).map((p) => `${p.accion} — ${p.responsable} — ${p.vencimiento}`)),\n    '',\n  ];\n\n  if (it.posiciones && it.posiciones.length) {\n    sections.push('## Posiciones', list(it.posiciones.map((p) => `${p.parte}: ${p.postura} (${p.argumento})`)), '');\n  }\n  if (it.estado && it.estado.nuevo && it.estado.nuevo !== 'no aplica') {\n    sections.push('## Estado', `${it.estado.anterior} → ${it.estado.nuevo}`, '');\n  }\n\n  out.push({\n    json: {\n      path,\n      content: `${frontmatter}\\n\\n${sections.join('\\n')}`,\n      branch,\n      informe: doc.informe,\n      fuente: doc.fuente,\n      n: doc.items.length,\n      tema: it.tema,\n      cliente: it.cliente,\n      proyecto: it.proyecto,\n      fecha: it.fecha,\n      pendientes: it.pendientes || [],\n      control: doc.control,\n      prBody,\n    },\n  });\n}\n\nif (!out.length) throw new Error('Informe sin ítems: no se crea PR');\nreturn out;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -464,
        -144
      ],
      "id": "96b92a7b-525e-474c-b797-f380fe06b892",
      "name": "Code"
    },
    {
      "parameters": {
        "jsCode": "return $('Code').all();"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        176,
        -144
      ],
      "id": "7b9095e5-3636-49f5-9efb-ef436cab6675",
      "name": "Recuperar items"
    },
    {
      "parameters": {
        "resource": "file",
        "owner": {
          "__rl": true,
          "value": "ricardPlana",
          "mode": "name"
        },
        "repository": {
          "__rl": true,
          "value": "AlterEgo",
          "mode": "name"
        },
        "filePath": "={{ $json.path }}",
        "fileContent": "={{ $json.content }}",
        "commitMessage": "=kb: {{ $json.path }}",
        "additionalParameters": {
          "branch": {
            "branch": "={{ $json.branch }}"
          }
        }
      },
      "type": "n8n-nodes-base.github",
      "typeVersion": 1.1,
      "position": [
        400,
        -144
      ],
      "id": "975b1c42-689c-475c-904a-3f40026041a2",
      "name": "Crear nota",
      "webhookId": "b84c70e2-70af-4408-a63f-eaa1892cefd1"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.github.com/repos/ricardPlana/AlterEgo/pulls",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({\n  title: \"KB \" + $('Code').first().json.informe + \" \" + $('Code').first().json.fuente,\n  head: $('Code').first().json.branch,\n  base: \"main\",\n  body: $('Montar contexto').first().json.prBody\n}) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        1296,
        -144
      ],
      "id": "60226336-d8e7-4b54-89f0-c15d3970216b",
      "name": "Crear PR",
      "executeOnce": true
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { pr: $json.html_url, numero: $json.number, notas: $('Code').all().length } }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        1520,
        -144
      ],
      "id": "bd836b7e-8101-4e02-9059-729228d4a71f",
      "name": "Respond to Webhook",
      "executeOnce": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 3
          },
          "conditions": [
            {
              "id": "9e7c52d7-3834-4497-8ff5-ea94f6bb916c",
              "leftValue": "={{ $('Code').first().json.control.cobertura_completa }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              }
            },
            {
              "id": "cab20319-11bc-4f98-8237-63c082d8d36f",
              "leftValue": "={{ $('Code').first().json.control.items_sin_proyecto }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "or"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1744,
        -144
      ],
      "id": "1a13032d-eb2b-407f-ad9f-50b9762afc0f",
      "name": "¿Necesita atención?"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.github.com/repos/ricardPlana/AlterEgo/issues/{{ $('Crear PR').first().json.number }}/labels",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ labels: [\n  ...($('Code').first().json.control.cobertura_completa ? [] : ['cobertura-incompleta']),\n  ...($('Code').first().json.control.items_sin_proyecto > 0 ? ['sin-proyecto'] : [])\n] }) }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        1968,
        -240
      ],
      "id": "607185a9-084a-43a6-893e-db0a1ede0da2",
      "name": "Etiquetar PR",
      "executeOnce": true
    },
    {
      "parameters": {
        "method": "POST",
        "url": "REPLACE_WITH_YOUR_OWN_POWER_AUTOMATE_TRIGGER_URL",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ (() => {\n  const c = $('Code').first().json.control;\n  const notas = $('Code').all().map(i => i.json);\n  const pr = $('Crear PR').first().json;\n  const ok = c.cobertura_completa && c.items_sin_proyecto === 0;\n  const pend = notas.flatMap(n => (n.pendientes || []).map(p => ({ accion: p.accion, responsable: p.responsable, vencimiento: p.vencimiento })));\n  pend.sort((a, b) => String(a.vencimiento).localeCompare(String(b.vencimiento)));\n  const kpi = (valor, etiqueta, color) => ({\n    type: \"Column\",\n    width: \"stretch\",\n    style: \"emphasis\",\n    items: [\n      { type: \"TextBlock\", text: String(valor), size: \"ExtraLarge\", weight: \"Bolder\", horizontalAlignment: \"Center\", color: color || \"Default\" },\n      { type: \"TextBlock\", text: etiqueta, size: \"Small\", isSubtle: true, horizontalAlignment: \"Center\", spacing: \"None\", wrap: true }\n    ]\n  });\n  const grupos = {};\n  notas.forEach(n => { (grupos[n.proyecto] = grupos[n.proyecto] || []).push(n.tema); });\n  const bloquesNotas = Object.entries(grupos).flatMap(([proy, temas]) => [\n    { type: \"TextBlock\", text: proy, weight: \"Bolder\", color: \"Accent\", spacing: \"Medium\", wrap: true },\n    ...temas.map(t => ({ type: \"TextBlock\", text: \"• \" + t, wrap: true, spacing: \"Small\" }))\n  ]);\n  const estado = ok\n    ? \"Cobertura completa: no hay nada raro que mirar.\"\n    : (c.cobertura_completa\n      ? \"Hay \" + c.items_sin_proyecto + \" ítem(s) sin proyecto asignado.\"\n      : \"Cobertura incompleta: \" + c.motivo_cobertura);\n  return JSON.stringify({\n    type: \"message\",\n    attachments: [{\n      contentType: \"application/vnd.microsoft.card.adaptive\",\n      content: {\n        \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",\n        type: \"AdaptiveCard\",\n        version: \"1.4\",\n        msteams: { width: \"Full\" },\n        body: [\n          {\n            type: \"Container\",\n            style: \"emphasis\",\n            items: [\n              { type: \"TextBlock\", text: \"Conocimiento pendiente de revisión\", size: \"Large\", weight: \"Bolder\", wrap: true },\n              { type: \"TextBlock\", text: \"Informe \" + notas[0].informe + \" · \" + notas[0].fuente, isSubtle: true, spacing: \"None\" }\n            ]\n          },\n          {\n            type: \"ColumnSet\",\n            spacing: \"Medium\",\n            columns: [\n              kpi(notas.length, \"Notas nuevas\"),\n              kpi(c.hilos_leidos + \"/\" + c.hilos_detectados, \"Hilos leídos\", c.cobertura_completa ? \"Good\" : \"Warning\"),\n              kpi(pend.length, \"Pendientes con fecha\"),\n              kpi(c.items_sin_proyecto, \"Sin proyecto\", c.items_sin_proyecto > 0 ? \"Attention\" : \"Good\")\n            ]\n          },\n          {\n            type: \"Container\",\n            style: ok ? \"good\" : \"warning\",\n            spacing: \"Medium\",\n            items: [\n              { type: \"TextBlock\", text: estado, weight: \"Bolder\", wrap: true }\n            ]\n          },\n          { type: \"TextBlock\", text: \"Notas incluidas\", weight: \"Bolder\", size: \"Medium\", separator: true, spacing: \"Large\" },\n          ...bloquesNotas,\n          ...(pend.length ? [\n            { type: \"TextBlock\", text: \"Próximos pendientes\", weight: \"Bolder\", size: \"Medium\", separator: true, spacing: \"Large\" },\n            { type: \"FactSet\", facts: pend.slice(0, 6).map(p => ({ title: p.vencimiento, value: p.accion + \" (\" + p.responsable + \")\" })) }\n          ] : [])\n        ],\n        actions: [\n          { type: \"Action.OpenUrl\", title: \"Revisar PR\", style: \"positive\", url: pr.html_url },\n          { type: \"Action.OpenUrl\", title: \"Ver ficheros\", url: pr.html_url + \"/files\" }\n        ]\n      }\n    }]\n  });\n})() }}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        2208,
        48
      ],
      "id": "38522906-caad-4230-8a65-cb2188205bc7",
      "name": "Avisar revisor",
      "executeOnce": true
    },
    {
      "parameters": {
        "jsCode": "const nuevas = $('Code').all().map(i => i.json);\nconst dirs = [...new Set(nuevas.map(n => n.path.split('/').slice(0, 3).join('/')))];\nreturn dirs.map(dir => ({ json: { dir } }));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        624,
        -144
      ],
      "id": "ef2e622d-2a52-45d6-9466-b1fb108343f3",
      "name": "Carpetas de proyecto"
    },
    {
      "parameters": {
        "url": "=https://api.github.com/repos/ricardPlana/AlterEgo/contents/{{ $json.dir }}?ref=main",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        848,
        -144
      ],
      "id": "661de771-adbb-4f9d-820b-0f2b3a97c31c",
      "name": "Listar notas publicadas",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "jsCode": "const base = $('Code').first().json;\nconst nuevas = $('Code').all().map(i => i.json);\nconst publicadas = $input.all().map(i => i.json)\n  .filter(f => f.type === 'file' && f.name && f.name.endsWith('.md'));\n\nconst porDir = {};\nfor (const f of publicadas) {\n  const dir = f.path.split('/').slice(0, 3).join('/');\n  (porDir[dir] = porDir[dir] || []).push(f);\n}\n\nconst humanizar = (name) => {\n  const t = name.replace(/^\\d{4}-\\d{2}-\\d{2}-/, '').replace(/\\.md$/, '').replace(/-/g, ' ');\n  return t.charAt(0).toUpperCase() + t.slice(1);\n};\n\nconst lineas = [];\nfor (const [dir, files] of Object.entries(porDir)) {\n  const nombreProyecto = (nuevas.find(n => n.path.startsWith(dir + '/')) || {}).proyecto || dir;\n  files.sort((a, b) => b.name.localeCompare(a.name));\n  lineas.push(\n    `**${nombreProyecto}**`,\n    '',\n    '| Fecha | Nota ya publicada |',\n    '|---|---|',\n    ...files.slice(0, 5).map(f => `| ${f.name.slice(0, 10)} | [${humanizar(f.name)}](${f.html_url}) |`),\n    ''\n  );\n}\n\nconst contexto = lineas.length\n  ? [\n      '### Notas ya publicadas de estos proyectos',\n      '',\n      'Revisa si alguna de las nuevas las completa o las sustituye.',\n      '',\n      ...lineas,\n    ].join('\\n')\n  : '';\n\nreturn [{ json: { prBody: base.prBody + '\\n\\n' + contexto } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1072,
        -144
      ],
      "id": "251ef711-d749-4e32-9080-55cd523c0508",
      "name": "Montar contexto",
      "alwaysOutputData": true
    }
  ],
  "pinData": {
    "Webhook": [
      {
        "json": {
          "body": {
            "informe": "2026-09-25",
            "ventana": "2026-09-25 00:00–23:59",
            "fuente": "correo",
            "generado": "2026-09-26 06:30",
            "items": [
              {
                "tema": "Norvent Rollout: aceptada la petición de cambio CR-004 (producción en la Fase 1, go-live 2027-02-01)",
                "tipo": "correo",
                "cliente": "Norvent Climatización",
                "proyecto": "Norvent — Rollout D365 F&O",
                "fecha": "2026-09-25",
                "canal": [
                  "Correo"
                ],
                "asunto": "Norvent · CR-004 firmada",
                "participantes": [
                  {
                    "nombre": "Andreu Solé",
                    "organizacion": "Norvent Climatización, dirección de operaciones"
                  },
                  {
                    "nombre": "Marta Vilalta",
                    "organizacion": "Algoritmia, PM"
                  },
                  {
                    "nombre": "Ricard Plana",
                    "organizacion": "Algoritmia"
                  },
                  {
                    "nombre": "Iván Font",
                    "organizacion": "Algoritmia"
                  }
                ],
                "contexto": "Andreu Solé aceptó la propuesta de Algoritmia y firmó la petición de cambio CR-004. La Fase 1 incluye ahora las órdenes de producción sencillas con listas de materiales y rutas para tres familias de producto. El precio cerrado pasa de 386.500 EUR a 414.400 EUR y la salida a producción del 2027-01-11 al 2027-02-01. Las inspecciones de calidad siguen en la Fase 2. Este alcance sustituye al del resumen del 2026-09-21.",
                "datos": [
                  "CR-004 firmada el 2026-09-25",
                  "Fase 1 ampliada con órdenes de producción sencillas, listas de materiales y rutas para 3 familias de producto",
                  "Precio cerrado: 414.400 EUR (antes 386.500 EUR)",
                  "Go-live: 2027-02-01 (antes 2027-01-11)",
                  "Calidad (inspecciones) permanece en la Fase 2"
                ],
                "decisiones": [
                  {
                    "decision": "Incluir las órdenes de producción sencillas en la Fase 1 con precio cerrado de 414.400 EUR y go-live el 2027-02-01 (CR-004)",
                    "quien": "Andreu Solé",
                    "alcance": "Norvent — Rollout D365 F&O"
                  }
                ],
                "pendientes": [
                  {
                    "accion": "Actualizar el plan del proyecto con el go-live del 2027-02-01",
                    "responsable": "Marta Vilalta",
                    "vencimiento": "2026-09-30"
                  }
                ],
                "posiciones": [],
                "estado": {
                  "anterior": "Propuesta de 180 horas pendiente de aceptación",
                  "nuevo": "CR-004 firmada: alcance, precio y fecha vigentes"
                }
              },
              {
                "tema": "Norvent Rollout: días de los usuarios clave en noviembre",
                "tipo": "correo",
                "cliente": "Norvent Climatización",
                "proyecto": "Norvent — Rollout D365 F&O",
                "fecha": "2026-09-25",
                "canal": [
                  "Correo"
                ],
                "asunto": "Norvent · Disponibilidad usuarios clave",
                "participantes": [
                  {
                    "nombre": "Andreu Solé",
                    "organizacion": "Norvent Climatización, dirección de operaciones"
                  },
                  {
                    "nombre": "Marta Vilalta",
                    "organizacion": "Algoritmia, PM"
                  }
                ],
                "contexto": "Andreu Solé confirmó que los usuarios clave de Norvent podrán dedicar 8 días al proyecto en noviembre, repartidos en las semanas del 2 y del 16, frente a los 10 días que había planificado Algoritmia. Marta Vilalta indicó que con 8 días las pruebas de aceptación tendrán que concentrarse en dos bloques de cuatro días.",
                "datos": [
                  "Usuarios clave: 8 días en noviembre (semanas del 2 y del 16); planificados 10",
                  "Pruebas de aceptación en 2 bloques de 4 días"
                ],
                "decisiones": [],
                "pendientes": [
                  {
                    "accion": "Reorganizar las pruebas de aceptación en dos bloques de cuatro días",
                    "responsable": "Marta Vilalta",
                    "vencimiento": "2026-10-02"
                  }
                ],
                "posiciones": [
                  {
                    "parte": "Marta Vilalta",
                    "postura": "Con 8 días hay que concentrar las pruebas en dos bloques",
                    "argumento": "Ocho días no permiten repartir las pruebas como estaba planificado"
                  }
                ],
                "estado": {
                  "anterior": "10 días planificados",
                  "nuevo": "8 días confirmados"
                }
              },
              {
                "tema": "Norvent Portal de proveedores: piloto con 15 proveedores y lectura automática de facturas solo en el piloto",
                "tipo": "correo",
                "cliente": "Norvent Climatización",
                "proyecto": "Norvent — Portal de proveedores (Power Pages)",
                "fecha": "2026-09-25",
                "canal": [
                  "Correo"
                ],
                "asunto": "Portal proveedores · Decisión de piloto",
                "participantes": [
                  {
                    "nombre": "Carla Ribó",
                    "organizacion": "Norvent Climatización, responsable de compras"
                  },
                  {
                    "nombre": "Laia Puig",
                    "organizacion": "Algoritmia, consultora Power Platform"
                  },
                  {
                    "nombre": "Marta Vilalta",
                    "organizacion": "Algoritmia, PM"
                  }
                ],
                "contexto": "Carla Ribó decidió que el piloto del portal arranque con 15 proveedores el 2026-11-16 e incluya la subida de facturas con lectura automática de AI Builder, solo para el piloto. Al terminar se decidirá con datos reales de precisión y coste si se extiende a los 200 proveedores activos. Las facturas con confianza inferior al 90 % irán a revisión manual.",
                "datos": [
                  "Piloto: 15 proveedores, inicio 2026-11-16",
                  "Lectura automática de facturas con AI Builder solo en el piloto",
                  "Umbral de revisión manual: confianza inferior al 90 %",
                  "Extensión a los 200 proveedores activos: se decide tras el piloto"
                ],
                "decisiones": [
                  {
                    "decision": "Arrancar el piloto con 15 proveedores el 2026-11-16 con lectura automática de facturas solo en el piloto",
                    "quien": "Carla Ribó",
                    "alcance": "Norvent — Portal de proveedores (Power Pages)"
                  }
                ],
                "pendientes": [
                  {
                    "accion": "Preparar la lista de los 15 proveedores del piloto",
                    "responsable": "Carla Ribó",
                    "vencimiento": "2026-10-30"
                  }
                ],
                "posiciones": [],
                "estado": {
                  "anterior": "Estimación pendiente de decisión",
                  "nuevo": "Piloto decidido"
                }
              },
              {
                "tema": "Alvear WMS-F&O: idempotencia implementada en DEV y repetición de la prueba sin duplicados",
                "tipo": "correo",
                "cliente": "Grupo Alvear Logística",
                "proyecto": "Alvear — Integración WMS-F&O",
                "fecha": "2026-09-25",
                "canal": [
                  "Correo"
                ],
                "asunto": "WMS · Resultado de la prueba de idempotencia",
                "participantes": [
                  {
                    "nombre": "Joan Escolà",
                    "organizacion": "Algoritmia, desarrollador F&O"
                  },
                  {
                    "nombre": "Pau Ferrer",
                    "organizacion": "Grupo Alvear Logística, IT manager"
                  }
                ],
                "contexto": "Joan Escolà implementó la idempotencia con WmsMessageId en DEV antes de lo previsto y repitió la prueba: de 500 recepciones, 0 duplicados, con 61 reintentos del WMS descartados por la clave. Pau Ferrer confirmó los resultados y pidió repetir la prueba con 2.000 recepciones para reproducir un día completo de volumen.",
                "datos": [
                  "Prueba: 500 recepciones, 0 duplicados",
                  "61 reintentos del WMS descartados por WmsMessageId",
                  "Siguiente prueba: 2.000 recepciones (volumen de un día completo)"
                ],
                "decisiones": [],
                "pendientes": [
                  {
                    "accion": "Repetir la prueba con 2.000 recepciones",
                    "responsable": "Joan Escolà",
                    "vencimiento": "2026-09-30"
                  }
                ],
                "posiciones": [],
                "estado": {
                  "anterior": "Duplicados del 7,4 %",
                  "nuevo": "0 duplicados en la prueba de 500 recepciones"
                }
              }
            ],
            "control": {
              "fuente": "correo",
              "hilos_detectados": 40,
              "hilos_leidos": 40,
              "hilos_con_item": 4,
              "cobertura_completa": true,
              "motivo_cobertura": "ninguno",
              "items_sin_proyecto": 0,
              "descartados": [
                {
                  "motivo": "notificaciones automáticas y avisos de sistema",
                  "n": 31,
                  "ejemplos": [
                    "Resumen semanal de errores de integración",
                    "Confirmación de copia de seguridad nocturna"
                  ]
                },
                {
                  "motivo": "newsletters y marketing",
                  "n": 3,
                  "ejemplos": [
                    "Novedades de la comunidad de Dynamics 365"
                  ]
                },
                {
                  "motivo": "invitaciones y respuestas de calendario sin cuerpo",
                  "n": 2,
                  "ejemplos": [
                    "Cierre de semana"
                  ]
                }
              ]
            }
          }
        }
      }
    ]
  },
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GET sha main": {
      "main": [
        [
          {
            "node": "POST rama",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "GET sha main",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "POST rama": {
      "main": [
        [
          {
            "node": "Recuperar items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Recuperar items": {
      "main": [
        [
          {
            "node": "Crear nota",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Crear nota": {
      "main": [
        [
          {
            "node": "Carpetas de proyecto",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Crear PR": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond to Webhook": {
      "main": [
        [
          {
            "node": "¿Necesita atención?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "¿Necesita atención?": {
      "main": [
        [
          {
            "node": "Etiquetar PR",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Avisar revisor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Etiquetar PR": {
      "main": [
        [
          {
            "node": "Avisar revisor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Carpetas de proyecto": {
      "main": [
        [
          {
            "node": "Listar notas publicadas",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Listar notas publicadas": {
      "main": [
        [
          {
            "node": "Montar contexto",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Montar contexto": {
      "main": [
        [
          {
            "node": "Crear PR",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "8af97bae-37a4-4f2c-9211-0cf480f631a6",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "a73bb1ca61cbc25a95cd316c9ddc9ab0172dc30908c6ff8a7eeadbe99d80f4e2"
  },
  "nodeGroups": [],
  "id": "a2CW2YwckhMX3zkh",
  "tags": []
}
