Disponible · incorporación inmediata

ALEJANDRO GONZÁLEZ LÓPEZ

Angular Frontend Engineer

Especializado en Angular moderno, TypeScript, RxJS, Signals y NgRx, con experiencia construyendo aplicaciones web reales, SSR, arquitectura frontend, rendimiento e integración end-to-end.

  • ANGULAR 21
  • TYPESCRIPT
  • RXJS
  • SIGNALS
  • NGRX

Sobre mí

Soy Angular Frontend Engineer especializado en Angular moderno, TypeScript, RxJS, Signals y NgRx, con experiencia construyendo aplicaciones web reales desde la arquitectura frontend hasta producción.

Mi foco principal es la arquitectura mantenible, el estado reactivo, SSR, rendimiento, integración con APIs, autenticación, accesibilidad y experiencia de usuario. También trabajo con NestJS, GraphQL, PostgreSQL, Docker y AWS, lo que me permite entender el producto end-to-end sin diluir mi especialización frontend.

Proyectos

Casos reales

Productos desarrollados en entornos reales de cliente y startup.

  • Campus virtual de Finca El Sarao con catálogo de cursos y progreso del alumno

    Client project · May 2026 - Present

    En Desarrollo

    Finca El Sarao · Digital Platform & LMS

    Angular Frontend Engineer · Sole Developer

    Plataforma digital y LMS para un cliente real con web pública, catálogo de cursos, campus de alumnos, administración y reproducción de vídeo HLS.

    • Angular 21
    • TypeScript
    • RxJS
    • Signals
    • NestJS
    • Prisma
    • PostgreSQL
    • Tailwind CSS
    • +9
  • Panel de administración de GTVMOTOR con ingresos, citas pendientes y actividad reciente

    Client project · Dec 2024 - Present

    En Desarrollo

    GTVMOTOR · Automotive Web Platform

    Angular Frontend Engineer · Sole Developer

    Plataforma web para concesionaria con catálogo, reservas, tasación, panel administrativo y SSR híbrido desplegado en AWS EC2.

    • Angular
    • TypeScript
    • RxJS
    • Signals
    • Standalone Components
    • SSR
    • NestJS
    • Bun
    • +8
  • Ecosistema Slowork con landing pública, login, blog y panel de administración

    Startup product · Jan 2024 - Present

    En Desarrollo

    Slowork · Frontend Platform & Web Ecosystem

    Angular Frontend Engineer · Co-Founder

    Ecosistema digital para nómadas con sitio público SSR, blog editorial, portal Angular de operaciones y API GraphQL en AWS.

    • Angular 21
    • TypeScript
    • RxJS
    • Signals
    • NgRx
    • SignalStore
    • GraphQL
    • Apollo
    • +5

Proyecto personal

Portfolio técnico, documentación y experimentación.

  • Personal project

    CV interactivo & bitácora

    Astro 6 · TypeScript · Tailwind CSS v4 · Zod · Fuse.js

    Portfolio técnico vivo con design system dark-mode, bitácora de ingeniería y búsqueda difusa sobre problemas resueltos.

Influencias y referentes

Alan Buscaglia (ConquerBlocks Academy - Gentleman Programming)

Arquitectura Frontend con Angular - Buenas Prácticas - Clean Architecture

Evolución hacia la lógica estructural, Clean Architecture y adopción de Angular con tipado estricto como framework principal. También me introdujo a Engram y a una metodología de trabajo con agentes de IA.

LinkedIn

Bienvenido Sáez (ConquerBlocks Academy)

Rigor Técnico - Semántica - SEO

Construcción de cimientos técnicos robustos y adopción de un enfoque pragmático ante retos complejos. También me introdujo a la semántica y a la importancia del SEO en el desarrollo de aplicaciones web.

LinkedIn

Elena Hernández (ConquerBlocks Academy)

Metodología Base - Lógica de Programación - Estructuras de control

Asentamiento de la lógica de programación y el rigor algorítmico utilizando Python. Más que sintaxis, esta etapa forjó una disciplina metodológica esencial para aprender a abstraer problemas complejos y estructurar el pensamiento antes de escribir código en un lenguaje de programación.

LinkedIn

Santiago Corocotta (Upgrade-hub Academy)

Inmersión práctica en el ecosistema JavaScript

Su mentoría en Upgrade-hub no solo me impulsó a desarrollar interfaces interactivas, sino que sentó las bases de mis aptitudes de organización de código y el uso profesional de Git para el control de versiones.

LinkedIn

Bitácora

Aquí recopilo algunos de los fallos reales de los que he aprendido al diagnosticar y resolver problemas en producción, y que me recuerdan por qué nunca voy a dejar de aprender dentro de este maravilloso mundo del software.

Cada caso muestra un titular descriptivo y un resumen del incidente. Con Ver diagnóstico se abre el detalle: contexto, error, investigación, solución y aprendizaje. También puedes usar el buscador.

  • WhatsApp no mostraba el logo: los metadatos de Angular no estaban en el HTML inicial

    Los scrapers sociales no ejecutaban el JavaScript de Angular; añadí un fallback Open Graph estático y pospuse las tarjetas específicas por ruta hasta SSR o prerenderizado. Ver diagnóstico → Ocultar diagnóstico ↑

    Tras completar la Fase A de SEO de Finca El Sarao, la aplicación Angular CSR ya estaba desplegada en Vercel. El siguiente objetivo era conseguir que los enlaces compartidos en WhatsApp y otras redes generasen una vista previa corporativa reconocible, con una imagen Open Graph de 1200 × 630 píxeles, el logo de la marca y metadatos coherentes, sin introducir todavía SSR ni prerenderizado.

    La imagen Open Graph por defecto seguía apuntando a hero-cover.jpg, una fotografía de paisaje sin identidad corporativa. Aunque Angular actualizaba los metadatos al navegar, los scrapers sociales recibían únicamente el HTML inicial y no ejecutaban JavaScript, por lo que nunca veían esos cambios. Además, definir un og:url o un canonical fijo en index.html habría atribuido incorrectamente la misma URL a todas las rutas profundas de la aplicación.

    Comparé los metadatos que mostraba el DOM después de arrancar Angular con los incluidos en la respuesta HTML original. El problema no era la disponibilidad de la imagen ni el funcionamiento del SeoService: Vercel entregaba el mismo index.html para todas las rutas y los scrapers sociales no ejecutaban el JavaScript encargado de modificarlo. También descarté construir la URL de la imagen con window.location.origin, porque el crawler necesita encontrar una URL absoluta antes de que la aplicación se ejecute.

    Creé una tarjeta Open Graph corporativa de 1200 × 630 píxeles y dejé en index.html los metadatos invariantes que debían estar disponibles sin JavaScript. Convertí DEFAULT_OG_IMAGE en una URL absoluta, mantuve SITE_LOGO como recurso independiente para los datos estructurados y centralicé los metadatos variables de cada vista mediante SeoService.setPageMeta(). Evité declarar valores globales engañosos para og:url y canonical. La implementación y sus ajustes quedaron integrados en los PR #60 y #61.

    En una aplicación CSR, lo que no está en el HTML inicial no existe para un scraper sin JavaScript. Una tarjeta social estática puede ser fiable; si cada URL necesita una vista previa propia, la solución no es añadir más JavaScript, sino generar HTML por ruta mediante SSR o prerenderizado.

  • Vercel bloqueaba el despliegue de Angular por errores de tipos de NestJS

    El pipeline de Vercel fallaba tras un ng build correcto porque typechequeaba NestJS/Prisma del monorepo; fijé Root Directory en web con Include outside enabled. Ver diagnóstico → Ocultar diagnóstico ↑

    Estaba cerrando el deploy de Finca El Sarao (monorepo pnpm: Angular 21 en web/, NestJS 11 + Prisma 7 en api/) tras la Fase A SEO. El frontend va a Vercel (fincaelsarao.com); la API y PostgreSQL corren en Railway (api.fincaelsarao.com). No hay proxy same-origin como en GTVMOTOR: la SPA llama cross-origin con cookies HttpOnly para refresh. El build local y ng build en Vercel pasaban; el pipeline fallaba en fases posteriores o al cambiar Root Directory.

    Cadena de fallos en CI de Vercel: (1) `Command prisma:generate not found` al ejecutar scripts desde cwd incorrecto. (2) Con Root Directory = `./`, `ng build` terminaba bien pero después aparecían decenas de ciclos `Using TypeScript 5.9.3` y `TS2305: Module @prisma/client has no exported member UserRole` en `api/src/...`. Prisma sí generaba el cliente en postinstall. (3) Tras mover Root a `web` y desactivar «Include files outside the root directory», el install falló con `ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND` en `/vercel` porque `cd ..` / `pnpm --dir ..` no veía el `package.json` raíz del monorepo.

    El log mostraba que el error TS2305 ocurría **después** de Application bundle generation complete, no durante vercel-build. Eso apunta a la fase de Serverless Functions de Vercel, no al script de build del repo. Con Root = ./, Vercel reserva la carpeta api/ para compilar NestJS como functions aunque el buildCommand solo ejecute Prisma + Angular. Con Root = web, ../api queda fuera del proyecto Vercel (solo reservaría web/api/, que no existe). Pero un monorepo pnpm necesita el lockfile y workspaces en la raíz: sin «Include outside» el sandbox solo monta web/ y --dir .. apunta a /vercel vacío. Documenté la cronología en docs/DEPLOY.md y contrasté con GTVMOTOR (Nginx same-origin) vs Finca (cross-origin + rewrite único de /sitemap.xml).

    Config validada en main (ee3c489): Root Directory = web, «Include files outside…» = **Enabled**, overrides del dashboard vacíos. web/vercel.json: installCommand: HUSKY=0 pnpm install --dir .., buildCommand: pnpm run vercel-build. web/package.json delega: vercel-build → pnpm --dir .. run vercel-build. Script raíz: pnpm --filter api exec prisma generate && pnpm --filter web run build (sin nest build). .npmrc: node-linker=hoisted + hoist de @prisma/client. api/package.json: postinstall: prisma generate por si Vercel aísla installs sobre api/. Build ~1 min, deploy OK en fincaelsarao.com.

    En monorepos en Vercel, Root Directory y «Include outside» son dos ejes independientes: el primero evita que carpetas hermanas (api/) se traten como serverless; el segundo permite que el install vea el package.json raíz. Un ng build verde no garantiza deploy verde si la plataforma compila más código después. Si Prisma generate pasa pero TS2305 aparece en api/, sospecha de Serverless Functions, no de schema. Para SPA + API en otro host, documentar cross-origin (CORS + withCredentials) y no asumir que la receta same-origin de un proyecto anterior aplica tal cual.

  • Fuse.js mostraba entradas sin resaltar: el filtro fuzzy no coincidía con el DOM

    El buscador devolvía coincidencias aproximadas sin poder resaltarlas en el DOM; prioricé la coincidencia literal y limité el resaltado fuzzy a los matches reales de Fuse. Ver diagnóstico → Ocultar diagnóstico ↑

    El buscador de la bitácora (<logbook-search>) ya filtraba con Fuse.js, pero al añadir resaltado literal en el DOM apareció un fallo de confianza: entradas visibles sin la palabra buscada marcada en ningún sitio. Quería que cada resultado fuera comprobable (solo mostrar lo que se puede resaltar, contador honesto, <details> abiertos con sentido), aunque eso vaya en contra de lo que Fuse hace por defecto: tolerar similitudes sin exigir el texto exacto.

    Con Fuse.js como único filtro (`threshold: 0.35`), búsquedas como `inje` abrían entradas sin ningún resaltado visible. Fuse devolvía similitudes difusas; el `<mark>` solo pintaba subcadenas literales del input. El contador decía «4 veces · 4 entradas» pero tres artículos no mostraban la palabra buscada. Al pasar a solo DOM literal se corrigió el falso positivo, pero se perdía la tolerancia a typos y la relevancia fuzzy que justificaba mantener Fuse en `package.json`.

    Se evaluaron tres caminos: (1) eliminar Fuse y quedarse con substring en el DOM, simple pero sin aproximaciones; (2) volver a fuzzy puro alineando resaltado con includeMatches de Fuse, sin mostrar el query literal cuando no existe; (3) híbrido: prioridad a coincidencia exacta en el DOM, fallback fuzzy solo si Fuse encuentra la entrada y el DOM puede resaltar los spans de result.matches. También hizo falta alinear índice y DOM: research y learning entraron al JSON, y cada sección larga del caso lleva un identificador en el HTML para que el resaltado fuzzy caiga en el bloque correcto (investigación, solución o aprendizaje), no en cualquier párrafo.

    Lo quiero todo, adapatación al modelo híbrido. En LogbookSearch.astro, cada entrada pasa primero por #highlightLiteral(query) (teal). Si hits === 0, se consulta Fuse con includeMatches: true y #highlightFuseMatches pinta en ámbar el fragmento que Fuse emparejó, no el texto del input. Las aproximadas llevan badge ~aprox en el summary. Si Fuse sugiere una entrada pero el DOM no puede marcar nada, se oculta. El contador desglosa exactas y aprox (2 exactas · 1 aprox). LogbookSection.astro serializa el índice completo; en LogbookEntry.astro, cada sección larga del caso lleva un identificador en el HTML para que el resaltado fuzzy caiga en el bloque correcto (investigación, solución o aprendizaje), no en cualquier párrafo.

    Filtro y resaltado deben compartir criterio o el usuario ve resultados fantasma. Un híbrido honesto: exacto primero (predecible), fuzzy como segunda capa con visual distinto (ámbar + ~aprox) y highlight de lo que Fuse encontró, no de lo que el usuario escribió. Mantener Fuse tiene sentido si la segunda capa está acoplada a matches; si no, es dependencia muerta. Para pocos artículos el literal basta; el híbrido escala mejor cuando crezca la bitácora o haya typos en errores de consola largos.

  • Angular lanzaba NG0200: SignalStore y Apollo crearon una dependencia circular

    El bootstrap fallaba por el ciclo AppStore → HostPending → Apollo → AppStore; rompí la inyección eager con Injector y resolución perezosa en la acción. Ver diagnóstico → Ocultar diagnóstico ↑

    En el front de Slowork, el sidebar del admin muestra un badge con el total de solicitudes pendientes (creadores vía REST y hosts vía GraphQL). Ese número vive en AppStore, un NgRx SignalStore global. Tras tocar la carga de contadores en el store, la aplicación dejó de arrancar en /login: antes de poder validar si el badge volvía a funcionar, el bootstrap ya fallaba con un error de dependencias.

    RuntimeError NG0200: dependencia circular detectada para `SignalStore`. Ruta: `_Auth` → `SignalStore` → `_HostPendingService` → `_Apollo` → `InjectionToken APOLLO_OPTIONS` → `SignalStore`. La app fallaba al cargar `/login` antes de que el sidebar pudiera pedir los contadores.

    Se trazó el ciclo completo en el grafo de DI. AppStore inyectaba HostPendingService en el bloque withMethods al **construir** el store. Ese servicio depende de Apollo. La factory de provideApollo (apollo.config.ts) a su vez hace inject(AppStore) para leer el token JWT y montar el authLink. Angular no puede instanciar dos tokens que se necesitan mutuamente durante la misma resolución. Se descartó mover la lógica a CollaborationReviewService (también inyecta AppStore). Como alternativa válida quedó usar HttpClient con POST GraphQL crudo, pero duplica la query ya definida en host-pending.queries.ts.

    Inyección perezosa con Injector. En withMethods solo se resuelven dependencias seguras (Router, HttpClient, Injector). HostPendingService se obtiene **dentro** de loadAdminPendingCounts() con injector.get(HostPendingService), cuando el store ya está creado. La llamada a Hosts sigue siendo getPendingHosts(1, 1) y se guarda pageInfo.totalItems; Creadores usa REST con HttpClient. Ambas peticiones van en forkJoin con catchError(() => of(0)) para no romper el badge si una API falla.

    Un store global no debe inyectar en su constructor servicios que dependan de infraestructura configurada con el propio store. El ciclo no aparece al ejecutar un método: aparece al **registrar** inject(HostPendingService) en withMethods. Regla práctica: estado central (token, usuario) ↔ cliente HTTP/GraphQL exige lazy resolution, un TokenService intermedio sin dependencias, o peticiones REST aisladas. En SignalStore, inject(Injector) + get() en la acción es el patrón mínimo para romper el ciclo sin duplicar queries.

  • La animación de salida del toast no se veía: Angular destruía el nodo antes del CSS

    El @for eliminaba el toast al instante y cortaba la transición; usé @starting-style en la entrada y animate.leave para retener el nodo hasta el fin del CSS. Ver diagnóstico → Ocultar diagnóstico ↑

    En el front de Slowork (Angular 21 zoneless) estaba puliendo las notificaciones toast del panel admin. Cada aviso se renderiza en un @for con temporizador de cierre: al expirar, Angular elimina el nodo al instante y la transición de salida no se llega a ver. El objetivo era animar entrada y salida con CSS nativo, sin añadir @angular/animations ni penalizar Lighthouse.

    Colisión de animaciones y destrucción prematura de nodos en el DOM. Al expirar el tiempo del Toast, Angular destruye el nodo instantáneamente por el flujo de control `@for`, impidiendo que las transiciones CSS de salida se ejecuten o provocando cortes abruptos en la interfaz gráfica.

    Análisis del ciclo de vida del DOM moderno y evaluación de alternativas. Cargar el paquete tradicional @angular/animations añade una penalización de rendimiento innecesaria en aplicaciones Zoneless y va en contra de la optimización del Core Web Vitals (Lighthouse 100/100). Históricamente se retenía el nodo con JS, pero los estándares web actuales permiten delegar la carga directamente al motor de renderizado del navegador.

    Implementación de una estrategia híbrida nativa. Para la entrada, se utiliza la propiedad de CSS moderno @starting-style para definir los estilos del elemento antes de su primer renderizado en el navegador (solucionando la animación desde display: none). Para la salida, se utiliza el hook de animación nativo del framework (animate.leave), deteniendo la destrucción física del nodo en el DOM hasta que la transición CSS declare su finalización.

    Menos JavaScript, más CSS nativo. Descargar la lógica de transiciones en las APIs modernas del navegador mantiene el hilo principal libre y la aplicación ultra fluida. La evolución de las herramientas modernas prioriza los estándares web sobre las abstracciones pesadas del framework. Además, se inmuniza el entorno de desarrollo actualizando las reglas globales de contexto (.cursor/rules / .mdc) para asegurar que todo el equipo y los agentes adopten esta solución nativa en futuros componentes interactivos (modales, menús, etc.), evitando deuda técnica.

  • Fuse.js no encontraba resultados porque estaba indexando el objeto equivocado

    JSON.stringify de CollectionEntry producía {}; serialicé objetos planos en el servidor antes de pasar el índice al Web Component. Ver diagnóstico → Ocultar diagnóstico ↑

    Estaba montando el buscador de la bitácora en mi CV (Astro 6). Necesitaba filtrar entradas del timeline en el cliente sin React ni re-render del HTML del servidor: un Web Component con Fuse.js y un índice JSON generado en build. El primer paso era hacer llegar los datos de getCollection('logbook') al navegador de forma fiable.

    Al transportar el resultado de `getCollection('logbook')` al cliente, el índice de búsqueda llegaba vacío. Los objetos `CollectionEntry` de Astro contienen metadatos internos (símbolos, referencias a `.render()`) que no son JSON-serializables. `JSON.stringify` sobre la colección completa producía `{}` o fallaba en silencio, y Fuse.js no encontraba coincidencias aunque las entradas estuvieran en el DOM.

    Se revisó la documentación de Astro sobre el boundary servidor→cliente en islas y scripts de componente. La serialización de props con client:* exige datos planos; el mismo criterio aplica al pasar datos vía data-* attributes en un Custom Element. Se evaluó @astrojs/react con client:load, pero la interacción solo filtra nodos del DOM, así que no justifica el peso extra del runtime. Se optó por un HTMLElement con Fuse.js y manipulación directa de [data-entry-id].

    En LogbookSection.astro se mapeó la colección a objetos planos antes de serializar: entries.map(e => ({ id: e.id, context: e.data.context, error: e.data.error, technologies: e.data.technologies, solution: e.data.solution })). El JSON viaja al cliente como data-index en <logbook-search>. El Web Component parsea el atributo en connectedCallback, inicializa Fuse.js y muestra u oculta los <li> del timeline con display: none, sin re-renderizar el HTML generado en el servidor.

    En Astro, cualquier dato que cruce el boundary servidor→cliente, ya sea como prop de client:* o como atributo data-*, debe ser JSON puro. Los CollectionEntry hay que destruirlos a plain objects en el servidor. Para interacciones puntuales (filtrar, toggles, contadores), un Web Component en el <script> del .astro suele ser suficiente y evita dependencias de UI innecesarias.

  • Producción mostraba cero vehículos porque la API seguía conectada a localhost

    El catálogo en prod salía vacío con la DB llena porque apiUrl apuntaba a localhost; unifiqué SPA y API bajo /api same-origin con Nginx. Ver diagnóstico → Ocultar diagnóstico ↑

    Estaba cerrando el primer deploy en producción de GTVMOTOR en AWS EC2: Angular servido por Nginx en el host, API NestJS y PostgreSQL 16 en Docker, dominios gtvmotor.es y gtvmotor.co.uk con SSL. La infra de proxy se montó el 12/12/2025; al día siguiente, con todo desplegado, la web cargaba pero el catálogo de coches aparecía vacío para cualquier visitante.

    Tras el deploy, la web cargaba pero el inventario mostraba 0 vehículos pese a tener datos en PostgreSQL. `environment.prod.ts` seguía apuntando a `http://localhost:3000/api`: en el navegador del usuario esa URL no existe. Además, con varios dominios (.es, .co.uk, www) un front que llamara a otro host habría forzado CORS, preflight y cookies cruzadas. La API en Docker estaba bien acotada (`127.0.0.1:3000`), pero el cliente no pasaba por el reverse proxy de Nginx.

    Se depuró por capas desde la EC2: curl -s http://127.0.0.1:3000/api devolvía JSON con datos, mientras https://gtvmotor.es mostraba el SPA vacío. Se revisó el Security Group (solo 80/443 públicos, 3000 cerrado), el vhost en /etc/nginx/sites-available/gtvmotor y la diferencia entre /api y /api/ en proxy_pass. Se contrastó el enfoque Docker (nginx-proxy.conf con upstreams) con la estrategia final documentada en SPECIFICATION_PROD.md: Nginx en el host, estáticos en /var/www/gtvmotor/, proxy de /api/ hacia el contenedor local. Para SSL inicial se usó nginx-proxy.conf.temp-no-ssl hasta que Certbot validó los dominios.

    Se unificó todo bajo same-origin. En environment.prod.ts: apiUrl: '/api' e imageUrl: '/api', de modo que el navegador siempre habla con gtvmotor.es y Nginx enruta internamente. Reglas de proxy: /api/ → 127.0.0.1:3000/api/, /docs y /docs-json al backend, / con try_files $uri $uri/ /index.html para el SPA. Redirect explícito location = /api { return 301 /api/; }. Dominio canónico gtvmotor.es con 301 desde www y .co.uk. API expuesta solo en loopback (127.0.0.1:3000:3000 en docker-compose). Certbot en Nginx y HSTS en el vhost HTTPS.

    Un reverse proxy no es solo terminar SSL: es la fachada única que alinea SPA, API y documentación bajo un dominio. Si prod usa /api relativo, el front en dev debe tener un proxy equivalente o URLs coherentes; si no, funciona en un entorno y falla en otro. No exponer el puerto del backend: Nginx + binding localhost es defensa en profundidad. Los detalles de Nginx importan (barra final en proxy_pass, redirect /api → /api/). Documentar la estrategia real en un runbook (SPECIFICATION_PROD.md) evita repetir la depuración en el siguiente deploy.

IA en el flujo de trabajo

Contexto, memoria, tests y aplicación diaria

Mi integración de la IA comenzó con Spec-Driven Development: definir el qué antes del cómo me llevó a prompts atómicos y contexto acotado. Eso evolucionó hacia Engram para memoria por proyecto, un Cerebro Global sincronizado con Git (`brain-push` / `brain-pull`) y reglas Cursor por repo (`.cursor/rules/*.mdc`). En features críticas el criterio de éxito de la spec incluye tests: el agente lee `docs/TESTING.md`, genera specs siguiendo patrones del monorepo y ejecuta la suite antes de cerrar. Uso la IA como acelerador con reglas, no como sustituto de criterio.

  1. Spec-Driven Development

    Trabajo con specs acotadas y criterios de aceptación verificables: una intención por mensaje, sin mezclar refactor, diseño y bugfix. La spec incluye objetivo, restricciones, archivos de referencia obligatorios (`.cursor/rules/project.mdc`, `DESIGN.md`) y criterio de éxito verificable (typecheck, lint, tests concretos).

    spec-driven
    • Cursor
    • Prompts
    • Specs
  2. Testing con el agente

    Automatizo la calidad en el mismo hilo de implementación. El agente investiga el árbol de archivos, aplica patrones de `docs/TESTING.md` (Vitest + Angular Testing Library en web, Jest + Supertest en API), crea helpers reutilizables (`*.testing.ts`) y ejecuta suites acotadas (`ng test --include`, `pnpm --filter api run test`) antes de dar la tarea por cerrada. GitHub Actions replica el gate en cada PR.

    agent-testing
    • Vitest
    • Jest
    • GitHub Actions
  3. Memoria de Proyecto

    Documento decisiones técnicas, ADRs ligeros y aprendizajes de la bitácora en Engram. Estrategias de testing, convenciones Angular o incidencias de deploy quedan en `~/.engram/engram.db` y se exportan con `engram sync` para que el agente retome contexto en la siguiente sesión.

  4. Cerebro Global

    Centralizo memorias de agente en un repo Git aparte del código del producto (`cerebro-global`). Scripts Bash en `~/.zshrc` (`brain-push`, `brain-pull`, `brain-status`) sincronizan chunks entre la BD local, el export del proyecto y GitHub. Misma memoria en otra máquina sin copiar notas a mano.

  5. Revisión y Control

    Mantengo el criterio de producto y la responsabilidad final del código que mergeo. Reviso diff, corro lint/typecheck/tests en local y valido que la solución encaja con la spec. Las reglas del repo marcan qué es obligatorio testear (auth, permisos, dinero, validaciones); el resto no se fuerza por inercia.

    diff-review
    • Code Review
    • CI

El objetivo no es escribir más rápido: es mantener contexto, trazabilidad, tests reproducibles y criterio técnico cuando el asistente cambia en cada sesión. Lo que el agente valida en local, CI lo confirma en cada PR.