Saltar a contenido

ADR-016 · Artefactos compartidos de ecosistema (core/py-common, core/ts-common, service-template, ECOSYSTEM.md)

Fecha: 2026-04-22 Source: /srv/projects/cis/cis-plan/DECISIONS.md (do not edit here — re-split desde la fuente)


Mergeado 2026-04-29 desde ADRs-borrador-013-015-016.md (P0.3). Numeración renumerada 2026-04-27 (era ADR-012 en el borrador; ese número fue tomado por S2S auth shared-key header).

Contexto

Hoy existen shared libs parciales: - core/core-style — tokens CSS, shell JS, brand logos, graphs (FS/CDN). Consumido por 15+ frontends. 🟢. - core/core-auth-lib — Python OIDC dep injection (illanes-auth). Consumido por backends Python. 🟢. - cis-core — infra REST / MCP. Consumido por admin/Claudia. 🟢.

Problema observado: cada servicio nuevo reimplementa: - Error handling (mapping exception → HTTP + logging) - Structured logging (structlog setup) - Vault client con cache (cada repo se escribe su propio vault_client.py: cis-sign, cis-core, cis-admin, etc.) - Retry decorators (tenacity wrapper) - Prometheus helpers (cis-sii-gateway tiene 9 metrics; cis-platform tiene otras 8; no comparten setup)

15+ frontends copipastean: - Auth hooks (useAuthUser, useAuthGate) - API fetch client con credentials: 'include' + error mapping - Setup del custom element <cis-shell> (navbar/footer) - Error display (toast/modal)

El plan maestro (§1.3 RBAC, §cross-cutting core) ya apunta a consolidar, pero nadie agendó el trabajo.

Propuesta

Crear 4 artefactos:

  1. core/py-common (/srv/projects/core/py-common/, pip editable)
  2. Módulos: oidc.py (depends on illanes-auth), errors.py (TypedException → HTTPException map), logging.py (structlog JSON config), vault.py (httpx client con TTL cache), retry.py (tenacity wrapper con settings), metrics.py (Prometheus helpers).
  3. Consumidor: todo backend Python nuevo + migración gradual de existentes.

  4. core/ts-common (/srv/projects/core/ts-common/, npm workspace package @cis/common)

  5. Módulos: useAuthUser.ts, useAuthGate.tsx, apiClient.ts (fetch wrapper con credentials + error mapping), shell.ts (wrappers del custom element), toast.ts.
  6. Consumidor: todo frontend Next/Vite nuevo.

  7. @cis/service-template (cookiecutter en /srv/projects/core/service-template/)

  8. Un comando: cis-create-service <nombre> <tipo=fastapi|next|express>.
  9. Deja listo: estructura backend o frontend, CLAUDE.md stub, AGENT-CONTEXT.md stub con campos pre-llenados, systemd unit adaptado al puerto reservado, Caddy block, .env.example, pre-commit hooks, secrets manifest (lista de keys vault que el servicio necesitará).

  10. /srv/projects/ECOSYSTEM.md (auto-generado)

  11. Script /srv/projects/bin/regenerate-ecosystem.sh lee:
    • CONTRACTS.md (matriz productor/consumer)
    • systemctl list-units '*.service' del host
    • AGENT-CONTEXT.md de cada repo
  12. Regenera /srv/projects/ECOSYSTEM.md con:
    • Inventory tabulado de servicios + puertos + units + owners
    • Mapa visual (Mermaid graph o Graphviz DOT) de dependencias
  13. Se corre en CI y en hook post-deploy.

Alternativas consideradas

  • No hacer nada: seguir copipasteando. Funciona pero duplica trabajo indefinidamente, los bugs se arreglan en un lugar y no en los otros.
  • Un único mega-repo monorepo: forzar toda la organización. Demasiado disruptivo ahora; ADR-007 explícitamente mantiene divisiones.
  • Adoptar un framework existente (ej. https://github.com/tiangolo/full-stack-fastapi-template): perdemos control y el stack del ecosistema es opinionated (structlog, illanes-auth, vault propio, cis-shell).

Decisión propuesta

Implementar los 4 artefactos en ese orden: py-commonts-commonservice-templateECOSYSTEM.md auto-gen.

Justificación:

  • py-common paga por sí solo: cada vault_client reimplementado es un bug esperando. Con TTL cache central, ahorramos N requests/min al core-server.
  • ts-common paga con los frontends pendientes (cis-portal, cis-librospa backend-connected, indieweb v2, cis-inbox extensions).
  • service-template acelera el onboarding — hoy un servicio nuevo toma ~2 días de boilerplate; con template <2 horas.
  • ECOSYSTEM.md resuelve el problema que este ADR surge: visibilidad del mapa completo en un solo lugar.

Consecuencias

  • Positivo: 3-4 semanas bootstrap; después cada servicio nuevo se hace en horas, no días. Bugs de infra se arreglan en 1 sitio. Los AGENT-CONTEXT.md se mantienen coherentes porque el template los prescribe.
  • Negativo: versionado de libs compartidas (semver estricto, ADR nuevo para cambios mayores — convenciones ya establecidas en CONTRACTS.md §"Convenciones de versionado"). Coordinación de PRs que afectan múltiples repos.
  • Migración gradual: los servicios existentes (cis-sign, cis-core, cis-admin, etc.) no se obligan a migrar de una. Regla: servicios nuevos usan py-common/ts-common por default; servicios existentes migran cuando toquen el archivo relevante.
  • Riesgo de lock-in: si una lib compartida tiene bug, afecta a todos los consumers. Mitigación: tests extensivos + release canaria (un servicio primero, el resto después).
  • Dependencia de autoridad: ECOSYSTEM.md auto-generado requiere que todos los AGENT-CONTEXT.md estén presentes y consistentes. Este ADR se apoya en el trabajo previo de crear AGENT-CONTEXT.md en todos los repos (hecho 2026-04-22).
  • Naming: core/ hoy alberga 2 items (core-style, core-auth-lib). Agregar 3 items más (py-common, ts-common, service-template) lo convierte en un contenedor estándar. Considerar si renombrar a core/libs/ en el futuro.