<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Ramón Chancay: ingeniería web, móvil e IA</title>
    <link>https://www.ramonchancay.me/es/blog</link>
    <description>Escritura técnica sobre agentes de IA, ingeniería con LLMs, React Native e iOS nativo, por Ramón Chancay.</description>
    <language>es</language>
    <copyright>Ramón Chancay</copyright>
    <lastBuildDate>Sun, 13 Sep 2026 12:00:00 GMT</lastBuildDate>
    <atom:link href="https://www.ramonchancay.me/es/rss.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Lo que solo puedes hacer en nativo: WidgetKit, Live Activities, App Intents, StoreKit 2, Foundation Models y watchOS</title>
      <link>https://www.ramonchancay.me/es/blog/lo-que-solo-puedes-hacer-en-nativo-ios</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/lo-que-solo-puedes-hacer-en-nativo-ios</guid>
      <pubDate>Sun, 13 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Las capacidades de iOS que React Native no ofrece y que justifican Swift ante un cliente: WidgetKit, Live Activities, App Intents y Siri, StoreKit 2, el modelo de lenguaje en el dispositivo con Foundation Models y watchOS. Cierre de la serie y argumento de venta.

Widgets, Live Activities, App Intents y complications de watchOS corren fuera de tu proceso, renderizados por el sistema desde SwiftUI. No hay runtime de JavaScript ahí; la parte de la app que el usuario ve sin abrirla solo se escribe en Swift. StoreKit 2 y Foundation Models son APIs de Swift con async/await que ofrecen, sin servidor, lo que antes requería uno: verificación de compras en el dispositivo y un modelo de lenguaje local con salida estructurada. El argumento de venta no es &quot;nativo es mejor&quot;: es una lista de superficies del sistema donde el producto puede vivir, con el costo de cada una. El cliente elige cuáles valen el precio; tú tienes que poder construirlas.</description>
      <category>Móvil</category>
      <category>WidgetKit</category>
      <category>Live Activities</category>
      <category>App Intents</category>
      <category>StoreKit 2</category>
      <category>Foundation Models</category>
      <category>watchOS</category>
      <category>iOS</category>
    </item>
    <item>
      <title>Testing y tooling en iOS para seniors: Swift Testing, snapshots, mocks, Instruments y tiempos de compilación</title>
      <link>https://www.ramonchancay.me/es/blog/testing-y-tooling-en-ios-para-seniors</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/testing-y-tooling-en-ios-para-seniors</guid>
      <pubDate>Sat, 12 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo testear una app iOS viniendo de Jest y Testing Library: Swift Testing frente a XCTest, snapshot testing, mocks vía protocolos y URLProtocol sin jest.mock, XCUITest frente a Maestro, Instruments, SwiftLint y cómo modularizar para bajar los tiempos de compilación.

Swift Testing (@Test, #expect, tests parametrizados) es el framework para código nuevo; XCTest sigue siendo necesario para tests de UI y de rendimiento. Los dos conviven en el mismo target. Sin jest.mock, los mocks entran por diseño: un protocolo por dependencia con una implementación falsa, y URLProtocol para interceptar la red sin tocar el cliente. Los snapshots comparan imágenes renderizadas, y son el test más barato para una vista de SwiftUI. Los tiempos de compilación se bajan con arquitectura: packages pequeños, tipos explícitos en expresiones largas, y Tuist con caché. Instruments dice dónde se va el tiempo de la app; el compilador, con flags, dice dónde se va el tiempo del build.</description>
      <category>Móvil</category>
      <category>Swift Testing</category>
      <category>XCTest</category>
      <category>Snapshot testing</category>
      <category>Maestro</category>
      <category>Instruments</category>
      <category>SwiftLint</category>
      <category>iOS</category>
    </item>
    <item>
      <title>Build, firma y App Store: lo que EAS te escondía</title>
      <link>https://www.ramonchancay.me/es/blog/build-firma-y-app-store-lo-que-eas-te-escondia</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/build-firma-y-app-store-lo-que-eas-te-escondia</guid>
      <pubDate>Fri, 11 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Todo lo que EAS Build hacía por ti y ahora es tuyo: certificados, provisioning profiles y entitlements explicados de una vez, Fastlane match para compartir la firma, TestFlight, las guidelines de App Review que más rechazan, el Privacy Manifest y Tuist para acabar con los conflictos del .pbxproj.

La firma es tres cosas: un certificado (quién eres, con clave privada en tu Mac), un App ID con capacidades (qué puede hacer la app) y un provisioning profile (la unión de las dos con la lista de dispositivos, para desarrollo, o sin ella, para la tienda). Los entitlements son la copia dentro del binario de esas capacidades, y deben coincidir con el perfil. Fastlane match guarda certificados y perfiles cifrados en un repositorio de git y los instala en cualquier máquina o CI con un comando. Es lo que EAS hacía en sus servidores, y es la forma correcta de firmar en equipo. Tuist genera el proyecto de Xcode desde un archivo Swift: el .pbxproj deja de estar en git y los conflictos de merge desaparecen. Junto con los packages, es la diferencia entre un equipo que teme agregar archivos y uno que no.</description>
      <category>Móvil</category>
      <category>Xcode</category>
      <category>App Store</category>
      <category>Firma de código</category>
      <category>Fastlane</category>
      <category>TestFlight</category>
      <category>Tuist</category>
      <category>Privacy Manifest</category>
    </item>
    <item>
      <title>UI, theming y animaciones en SwiftUI sin NativeWind ni Reanimated</title>
      <link>https://www.ramonchancay.me/es/blog/ui-theming-y-animaciones-en-swiftui-sin-nativewind-ni-reanimated</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/ui-theming-y-animaciones-en-swiftui-sin-nativewind-ni-reanimated</guid>
      <pubDate>Thu, 10 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo hacer theming, animaciones y accesibilidad en SwiftUI viniendo de NativeWind y Reanimated: design tokens con Asset Catalog, Dynamic Type como estándar, SF Symbols, withAnimation, matchedGeometryEffect, PhaseAnimator, gestos nativos y Liquid Glass como API del día uno.

Los design tokens van en el Asset Catalog (colores con variante clara y oscura, y de alto contraste) y en extensiones de Color, Font y ShapeStyle. No hay clases utilitarias; hay modifiers propios y estilos de componente (ButtonStyle, LabelStyle). Una animación en SwiftUI no es un valor que interpolas: es una instrucción de que un cambio de estado se anime. withAnimation, .animation(_:value:), matchedGeometryEffect y PhaseAnimator cubren lo que Reanimated hacía con shared values y worklets, sin hilo de UI que administrar. Dynamic Type, VoiceOver y prefers-reduced-motion funcionan solos si usas estilos de texto del sistema y no fijas tamaños. Cada .font(.system(size: 14)) y cada .frame(height: 44) los rompe un poco.</description>
      <category>Móvil</category>
      <category>SwiftUI</category>
      <category>Animaciones</category>
      <category>Theming</category>
      <category>Accesibilidad</category>
      <category>Dynamic Type</category>
      <category>SF Symbols</category>
      <category>Liquid Glass</category>
    </item>
    <item>
      <title>Del ecosistema expo-* a los frameworks de Apple: la tabla de equivalencias por dominio</title>
      <link>https://www.ramonchancay.me/es/blog/del-ecosistema-expo-a-los-frameworks-de-apple</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/del-ecosistema-expo-a-los-frameworks-de-apple</guid>
      <pubDate>Wed, 09 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Qué framework de Apple reemplaza a cada paquete expo-* al pasar de React Native a iOS nativo: cámara, ubicación, autenticación, pagos, notificaciones, archivos y más, con los permisos de Info.plist y el crash silencioso que todos cometen.

Casi todo paquete expo-* es un envoltorio de un framework de Apple: AVFoundation, CoreLocation, UserNotifications, StoreKit, AuthenticationServices, PhotosUI. Aprender el framework es aprender lo que el paquete escondía, incluidas sus decisiones de diseño. Cada permiso tiene dos partes: una clave de uso en Info.plist con el texto que ve el usuario, y una llamada de solicitud en tiempo de ejecución. Si falta la clave, la app se cierra sin diálogo y sin error legible; es el crash silencioso de la primera semana. Para lo que Apple no cubre (analytics de terceros, mapas de Google, ciertos SDK), Swift Package Manager tiene la mayoría de los SDK oficiales. Lo que no existe en SPM es un aviso: probablemente tampoco existe una alternativa madura.</description>
      <category>Móvil</category>
      <category>Expo</category>
      <category>iOS</category>
      <category>Swift</category>
      <category>Frameworks de Apple</category>
      <category>Permisos</category>
      <category>Info.plist</category>
      <category>React Native</category>
    </item>
    <item>
      <title>Persistencia en iOS: SwiftData, GRDB y Keychain, y por qué Realm ya no es opción</title>
      <link>https://www.ramonchancay.me/es/blog/persistencia-en-ios-swiftdata-grdb-keychain</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/persistencia-en-ios-swiftdata-grdb-keychain</guid>
      <pubDate>Tue, 08 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo decidir entre SwiftData, GRDB y Core Data para persistir datos en una app iOS, por qué Realm dejó de ser una opción, por qué el Keychain sobrevive a la desinstalación, y cómo plantear offline-first y sincronización con CloudKit.

SwiftData para apps pequeñas y solo Apple, con vistas que usan @Query; GRDB cuando hay consultas reales, migraciones que van a evolucionar o sincronización con conflictos; Core Data solo si lo heredas. Realm está descontinuado y no debe entrar en un proyecto nuevo. El Keychain no se borra al desinstalar la app. Un token guardado ahí reaparece si el usuario reinstala; hay que detectar el primer arranque y limpiarlo a propósito. Offline-first es una arquitectura, no una librería: escrituras locales primero, una cola de cambios pendientes, y sincronización con resolución de conflictos explícita. CloudKit la da casi gratis entre dispositivos del mismo usuario; con un backend propio la escribes tú.</description>
      <category>Móvil</category>
      <category>SwiftData</category>
      <category>GRDB</category>
      <category>Core Data</category>
      <category>Keychain</category>
      <category>CloudKit</category>
      <category>Offline-first</category>
      <category>iOS</category>
    </item>
    <item>
      <title>Concurrencia en Swift: adiós al event loop. async let, TaskGroup, actores y Swift 6</title>
      <link>https://www.ramonchancay.me/es/blog/concurrencia-en-swift-adios-al-event-loop</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/concurrencia-en-swift-adios-al-event-loop</guid>
      <pubDate>Mon, 07 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Swift Concurrency para quien viene de JavaScript: por qué no hay event loop, cómo async let y TaskGroup reemplazan a Promise.all, qué son los actores y @MainActor, qué exige Sendable y por qué en Swift 6 un data race es un error de compilación.

No hay event loop: await suspende la función y libera el hilo, y la continuación puede correr en otro hilo. async let y TaskGroup son el Promise.all, pero con paralelismo real y cancelación que baja a las hijas. Un actor es una clase cuyo estado solo se toca desde dentro, una llamada a la vez. @MainActor es el actor del hilo principal, donde vive la UI. Sendable es la marca de que un valor puede cruzar entre actores sin riesgo. Con el modo estricto de Swift 6, compartir un dato mutable entre tareas sin protección no compila. Los errores son ruidosos al principio y valen cada minuto: eliminan la clase de bug más difícil de reproducir.</description>
      <category>Móvil</category>
      <category>Swift</category>
      <category>Concurrencia</category>
      <category>async/await</category>
      <category>Actores</category>
      <category>Swift 6</category>
      <category>Sendable</category>
      <category>iOS</category>
    </item>
    <item>
      <title>Datos sensibles fuera del prompt: una librería de TypeScript escrita en 39 minutos desde su especificación</title>
      <link>https://www.ramonchancay.me/es/blog/limpiar-datos-sensibles-antes-del-llm-sanitype</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/limpiar-datos-sensibles-antes-del-llm-sanitype</guid>
      <pubDate>Mon, 07 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>sanitype redacta, enmascara o elimina datos sensibles dentro del proceso antes de que un payload salga hacia un LLM, un log o un tercero. Así está implementada en TypeScript.

Combina dos estrategias: reglas por ruta de campo para lo que ya sabes que es sensible, y detectores sobre texto libre para el correo que alguien pegó en un campo de notas. Por defecto, entra un objeto y sale un objeto con la misma estructura. Cada llamada devuelve un reporte de qué se tocó, dónde y con qué detector, y ese reporte nunca contiene el valor original. El repositorio se escribió al revés de lo habitual: primero SPEC, ARCHITECTURE, ROADMAP y COMPARISON; después el código. Entre el commit de las especificaciones y el de la implementación pasaron 39 minutos.</description>
      <category>IA</category>
      <category>TypeScript</category>
      <category>PII</category>
      <category>LLM</category>
      <category>Seguridad</category>
      <category>Spec-Driven Development</category>
    </item>
    <item>
      <title>Data en Swift: lo que TanStack Query hacía por ti y cómo construir tu capa de caché</title>
      <link>https://www.ramonchancay.me/es/blog/data-en-swift-lo-que-tanstack-query-hacia-por-ti</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/data-en-swift-lo-que-tanstack-query-hacia-por-ti</guid>
      <pubDate>Sun, 06 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>En Swift no hay TanStack Query. Cómo diseñar la capa de datos de una app iOS con URLSession y Codable, un repositorio que emite con AsyncStream, SwiftData o GRDB como caché local, invalidación y supabase-swift.

URLSession con async/await y Codable cubre el transporte; no necesitas Alamofire para una API REST normal. Lo que falta es todo lo que TanStack Query ponía encima: caché, obsolescencia, deduplicación y refresco. El patrón que funciona es &quot;la base de datos local es la caché&quot;: el repositorio escribe en SwiftData o GRDB lo que trae de la red, y la vista observa la base de datos, no la red. Así el offline, el refresco y la consistencia entre pantallas salen del mismo diseño; la deduplicación de peticiones es un actor de diez líneas. La invalidación se modela explícitamente: una marca de tiempo por colección y una política por caso de uso. Es menos automático que staleTime y más fácil de razonar cuando algo se muestra viejo.</description>
      <category>Móvil</category>
      <category>Swift</category>
      <category>URLSession</category>
      <category>AsyncStream</category>
      <category>SwiftData</category>
      <category>GRDB</category>
      <category>TanStack Query</category>
      <category>Supabase</category>
    </item>
    <item>
      <title>Navegación en SwiftUI sin file-based routing: NavigationStack, un enum de rutas y deep links</title>
      <link>https://www.ramonchancay.me/es/blog/navegacion-en-swiftui-sin-file-based-routing</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/navegacion-en-swiftui-sin-file-based-routing</guid>
      <pubDate>Sat, 05 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo migrar mentalmente de Expo Router a SwiftUI: NavigationStack con un enum Route, el patrón Coordinator, deep links y Universal Links, y qué aporta swift-navigation cuando sheets y alerts se vuelven estado.

En SwiftUI la navegación es un array de valores (NavigationPath o [Route]) que tú controlas. Un enum Route con datos asociados reemplaza a la carpeta app/ de Expo Router, y navigationDestination(for:) reemplaza a los archivos. Un deep link es una función pura URL -&gt; [Route]. Como la pila es estado, abrir un enlace es asignar ese array; no hay que &quot;navegar&quot; paso a paso. Sheets, alerts y confirmaciones también son estado (un opcional que, cuando no es nil, presenta). swift-navigation lo formaliza con un enum de destinos por pantalla para que una vista no pueda mostrar dos cosas a la vez.</description>
      <category>Móvil</category>
      <category>SwiftUI</category>
      <category>Navegación</category>
      <category>NavigationStack</category>
      <category>Expo Router</category>
      <category>Deep links</category>
      <category>Universal Links</category>
    </item>
    <item>
      <title>Spec-Driven Development en React Native: un MVP que lista los sismos del mundo, especificado antes de escribir código</title>
      <link>https://www.ramonchancay.me/es/blog/spec-driven-development-react-native-app-sismos</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/spec-driven-development-react-native-app-sismos</guid>
      <pubDate>Sat, 05 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Apliqué Spec-Driven Development a un MVP en React Native que lista los sismos del mundo: la especificación decide qué entra en la lista y sus criterios de aceptación se convierten en los tests.

SDD separa tres artefactos: la especificación (qué y por qué), el plan técnico (cómo) y las tareas. El agente implementa contra ellos en vez de contra un prompt suelto. El valor real no es el documento: es que las ambigüedades quedan marcadas y resueltas por escrito antes de que el agente elija por ti. Qué sismos entran en la lista, en qué orden y qué pasa sin conexión son decisiones de producto, no de código. Los criterios de aceptación se escriben como una tabla de casos y esa misma tabla se convierte en el test. Si la fila no está en la tabla, el comportamiento no está definido.</description>
      <category>IA</category>
      <category>Spec-Driven Development</category>
      <category>React Native</category>
      <category>Expo</category>
      <category>Agentes de código</category>
      <category>MVP</category>
    </item>
    <item>
      <title>Agente de voz que agenda citas: ElevenLabs, Cal.com y un backend que no deja adivinar al modelo</title>
      <link>https://www.ramonchancay.me/es/blog/agente-de-voz-agenda-citas-elevenlabs-calcom</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/agente-de-voz-agenda-citas-elevenlabs-calcom</guid>
      <pubDate>Fri, 04 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo monté un agente de voz que agenda citas de 30 minutos con ElevenLabs y Cal.com, y por qué el backend en Fastify es lo que evita que el modelo invente horarios.

El backend le devuelve al agente como máximo tres opciones ya redactadas para decirse en voz alta, y la reserva se hace con un optionId (opt_1), nunca con una fecha que el modelo escriba. Reservar es la única acción irreversible: exige confirmación explícita, bloquea interrupciones mientras corre y es idempotente por conversación. Los errores de herramienta salen con HTTP 200 y una frase en español que el agente puede leer. Un 5xx dejaría al modelo improvisando en medio de una llamada.</description>
      <category>IA</category>
      <category>ElevenLabs</category>
      <category>Agentes de voz</category>
      <category>Cal.com</category>
      <category>Fastify</category>
      <category>Tool calling</category>
      <category>IA</category>
    </item>
    <item>
      <title>Estado y arquitectura en SwiftUI: de Zustand y Redux a @Observable y TCA</title>
      <link>https://www.ramonchancay.me/es/blog/estado-y-arquitectura-de-zustand-a-observable-y-tca</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/estado-y-arquitectura-de-zustand-a-observable-y-tca</guid>
      <pubDate>Fri, 04 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo manejar estado en SwiftUI viniendo de Zustand y Redux: MVVM con @Observable como default, cuándo TCA justifica su curva, inyección de dependencias con swift-dependencies o Factory y por qué modularizar en packages desde el día uno.

Una clase @Observable es el reemplazo de un store de Zustand: estado compartido, observación por propiedad y sin selectores. Un ViewModel por pantalla con esa clase es el default que escala sin ceremonia. TCA (The Composable Architecture) es Redux con efectos, cancelación y tests exhaustivos integrados. Vale la pena cuando el equipo es grande, el dominio es complejo y vas a testear cada transición; para una app de tamaño medio, su curva cuesta más de lo que devuelve. Inyecta dependencias desde el día uno con swift-dependencies o Factory, y parte la app en packages de SPM por capa. Los dos hábitos son baratos al inicio y carísimos de agregar después.</description>
      <category>Móvil</category>
      <category>SwiftUI</category>
      <category>Arquitectura</category>
      <category>TCA</category>
      <category>Observable</category>
      <category>Zustand</category>
      <category>Redux</category>
      <category>Swift Package Manager</category>
    </item>
    <item>
      <title>SwiftUI no es React, aunque se parezca: body, identidad, modifiers y layout</title>
      <link>https://www.ramonchancay.me/es/blog/swiftui-no-es-react</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/swiftui-no-es-react</guid>
      <pubDate>Thu, 03 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>SwiftUI explicado para quien viene de React: qué es body frente a render, por qué la identidad de una vista importa, cómo el orden de los modifiers cambia el resultado, el sistema de layout sin flexbox y el mapa de hooks a property wrappers.

Una vista de SwiftUI es un struct barato que describe la UI; se crea y se destruye constantemente. El estado no vive en el struct sino en un almacén que SwiftUI asocia a la identidad de la vista, y por eso la identidad (estructural o explícita con id) decide qué se conserva y qué se reinicia. Cada modifier envuelve la vista en otra vista. .padding().background() y .background().padding() son dos árboles distintos con dos resultados distintos; no hay hoja de estilos que se aplique al final. El layout es una negociación en tres pasos: el padre propone un tamaño, el hijo decide el suyo, el padre lo posiciona. flex: 1 no existe; existen Spacer, frame(maxWidth:) y layoutPriority.</description>
      <category>Móvil</category>
      <category>SwiftUI</category>
      <category>React</category>
      <category>React Native</category>
      <category>iOS</category>
      <category>Layout</category>
      <category>Property wrappers</category>
    </item>
    <item>
      <title>Swift para quien ya domina TypeScript: las cinco diferencias que cambian cómo diseñas</title>
      <link>https://www.ramonchancay.me/es/blog/swift-para-quien-domina-typescript</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/swift-para-quien-domina-typescript</guid>
      <pubDate>Wed, 02 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Swift explicado para desarrolladores de TypeScript: value vs reference semantics, opcionales como sistema de tipos, enums con datos, protocolos con extensiones y Codable en lugar de zod.

En Swift los struct se copian y las class se comparten. Casi todo tu modelo de datos debería ser struct; lo que en TypeScript resolvías con spread e inmutabilidad disciplinada, aquí lo garantiza el compilador. Un opcional no es un valor que puede ser null: es un tipo distinto que el compilador te obliga a abrir. Los enums con datos asociados reemplazan a las uniones discriminadas y son más estrictos que ellas. Los protocolos con extensiones y las conformidades retroactivas sustituyen a las interfaces estructurales. Codable cubre la mitad de lo que hacía zod (la forma de los datos, con errores que señalan el campo); la otra mitad, las reglas de dominio, se escribe en tipos validados.</description>
      <category>Móvil</category>
      <category>Swift</category>
      <category>TypeScript</category>
      <category>iOS</category>
      <category>React Native</category>
      <category>Codable</category>
      <category>Tipos</category>
    </item>
    <item>
      <title>Por qué un senior de React Native debería aprender nativo en 2026</title>
      <link>https://www.ramonchancay.me/es/blog/por-que-un-senior-de-react-native-deberia-aprender-nativo</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/por-que-un-senior-de-react-native-deberia-aprender-nativo</guid>
      <pubDate>Tue, 01 Sep 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cuándo iOS nativo le gana a React Native (widgets, APIs del día uno, ML en el dispositivo), cuándo pierde (dos plataformas, OTA, equipo) y por qué aprender Swift suma en vez de reemplazar.

Nativo gana donde React Native no llega o llega tarde: widgets y Live Activities, las APIs que Apple anuncia en junio y exige en septiembre, modelos de ML en el dispositivo y el rendimiento de las pantallas más exigentes. React Native gana donde el negocio lo necesita: dos plataformas con un equipo, actualizaciones OTA sin pasar por App Review y un mercado de contratación de JavaScript que Swift no tiene. Para un senior la decisión no es &quot;cuál&quot;, es &quot;cuándo&quot;. Saber nativo te hace mejor ingeniero de React Native (módulos, crashes, la New Architecture) y te deja vender el proyecto que sí necesita Swift.</description>
      <category>Móvil</category>
      <category>React Native</category>
      <category>Swift</category>
      <category>SwiftUI</category>
      <category>iOS</category>
      <category>Expo</category>
      <category>Arquitectura móvil</category>
    </item>
    <item>
      <title>Los límites duros de un agente autónomo: lo que evita el desastre</title>
      <link>https://www.ramonchancay.me/es/blog/limites-duros-agente-autonomo</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/limites-duros-agente-autonomo</guid>
      <pubDate>Mon, 31 Aug 2026 15:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Los límites que separan un experimento de un agente que dejarías corriendo: topes de iteraciones y tokens como presupuesto, allowlist de comandos y paths, kill switch, idempotencia y observabilidad.

Un límite pedido en el prompt es una preferencia; un límite duro es código que se ejecuta después de que el modelo decidió. Todo lo que importa —qué comandos corre, dónde escribe, cuánto gasta, cuándo para— va en el programa, no en las instrucciones. Los cuatro que no son opcionales: presupuesto por corrida (iteraciones, tokens y tiempo), allowlist de comandos sin shell, allowlist de paths resueltos con realpath, y un kill switch que alguien más pueda accionar sin desplegar nada. La parte difícil no es el código: es que los PRs valgan la pena revisar. Eso no depende del agente, depende de qué tan claros son tus tickets y qué tan buena es tu suite de tests.</description>
      <category>IA</category>
      <category>Agentes</category>
      <category>Agent loop</category>
      <category>Límites</category>
      <category>Seguridad</category>
      <category>Observabilidad</category>
      <category>IA</category>
    </item>
    <item>
      <title>Qué es un agent harness (y cuándo no usarlo): deepagents frente a LangGraph</title>
      <link>https://www.ramonchancay.me/es/blog/que-es-un-agent-harness-deepagents-langgraph</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/que-es-un-agent-harness-deepagents-langgraph</guid>
      <pubDate>Fri, 07 Aug 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Qué es un agent harness, qué añade deepagents sobre create_agent y LangGraph, sus cuatro capas (entorno, contexto, delegación, steering) y la tabla para saber cuándo bajar a LangGraph.

Agente = modelo + harness. El harness es todo el código que no es el modelo: loop, tools, filesystem, sandbox, resumen de contexto, subagentes y aprobaciones. deepagents es un harness ya montado; LangGraph es el runtime sobre el que corre. Son tres niveles de la misma pila: LangGraph (tú dibujas el grafo), create_agent (un loop de tools con middleware) y deepagents (ese loop más cuatro capas: entorno de ejecución, gestión de contexto, delegación y steering). Usa el harness cuando la tarea es abierta y el modelo tiene que decidir el siguiente paso. Baja a LangGraph cuando el orden de los pasos lo decides tú, necesitas estado tipado o cada transición tiene que ser explícita.</description>
      <category>IA</category>
      <category>deepagents</category>
      <category>Agent harness</category>
      <category>LangGraph</category>
      <category>LangChain</category>
      <category>Agentes</category>
      <category>IA</category>
    </item>
    <item>
      <title>Trabajos de anotación de datos: en qué consiste etiquetar y revisar respuestas de IA</title>
      <link>https://www.ramonchancay.me/es/blog/trabajos-anotacion-datos-revisar-respuestas-ia</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/trabajos-anotacion-datos-revisar-respuestas-ia</guid>
      <pubDate>Sat, 25 Jul 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Qué son los trabajos de anotación de datos y revisión de respuestas de IA: qué tareas incluyen, cómo alimentan el entrenamiento (RLHF) y cómo se mide la calidad.

Anotar datos es producir la respuesta correcta que un modelo necesita para aprender: etiquetas, transcripciones, respuestas de referencia y comparaciones entre salidas. Revisar respuestas de IA consiste en verificar los hechos, puntuar contra una rúbrica y elegir la mejor entre dos, con justificación escrita; esas preferencias entrenan al modelo. Las plataformas te miden con tareas de control y acuerdo entre anotadores; el criterio y la especialización (código, matemáticas, derecho) valen más que la velocidad.</description>
      <category>IA</category>
      <category>Anotación de datos</category>
      <category>RLHF</category>
      <category>Data labeling</category>
      <category>Evaluación de modelos</category>
      <category>Trabajo remoto</category>
    </item>
    <item>
      <title>Orca: el ADE para orquestar una flota de agentes de código en paralelo</title>
      <link>https://www.ramonchancay.me/es/blog/orca-ade-flota-agentes-paralelo</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/orca-ade-flota-agentes-paralelo</guid>
      <pubDate>Mon, 06 Jul 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Probé Orca, el Agent Development Environment (ADE) de código abierto, para correr una flota de agentes de código en paralelo: Claude Code, Codex y más, cada uno en su worktree aislado.

Orca corre cualquier agente de CLI (Claude Code, Codex, Gemini, Grok, Cursor CLI, OpenCode y 25+ más) en paralelo, cada uno en su worktree aislado, con tu propia suscripción. Tiene un CLI (orca worktree, orca terminal) y una capa de orquestación con tareas, despachos y decision gates para coordinar la flota desde un agente coordinador. Corre en macOS, Windows y Linux, más apps de iOS y Android para monitorear y dirigir agentes desde el teléfono. Instalación por brew o AUR. Shippean a diario.</description>
      <category>IA</category>
      <category>Orca</category>
      <category>Agentes de código</category>
      <category>ADE</category>
      <category>Worktrees</category>
      <category>Orquestación</category>
    </item>
    <item>
      <title>De un ticket de Jira a un PR: el sistema autónomo de punta a punta</title>
      <link>https://www.ramonchancay.me/es/blog/de-un-ticket-de-jira-a-un-pr</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/de-un-ticket-de-jira-a-un-pr</guid>
      <pubDate>Thu, 02 Jul 2026 15:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo convertí el agente en un sistema autónomo: un ticket de Jira lo dispara (webhook o polling), una cola con estado persistente lo procesa una sola vez y devuelve un PR y un comentario en Jira.

El disparador no ejecuta el agente: solo encola. Un webhook de Jira da latencia baja pero pierde eventos; un polling con JQL es lento pero no se pierde nada. Usa webhook para reaccionar rápido y polling como red que recupera lo que el webhook dejó pasar. La propiedad que sostiene todo es la idempotencia: procesar cada ticket una sola vez. Se consigue con un estado persistente en base de datos y un reclamo atómico, no con un flag en memoria que se pierde cuando el proceso se reinicia. Cada ticket corre en su propio workspace efímero (el worktree del post anterior) y la salida es un PR más un comentario en Jira con el enlace. El merge lo sigue decidiendo una persona.</description>
      <category>IA</category>
      <category>Agentes</category>
      <category>Agent loop</category>
      <category>Jira</category>
      <category>Automatización</category>
      <category>Pull request</category>
      <category>IA</category>
    </item>
    <item>
      <title>El agente dentro de un repo real: aislar tareas con git worktree y entregar un PR</title>
      <link>https://www.ramonchancay.me/es/blog/agente-en-un-repo-real</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/agente-en-un-repo-real</guid>
      <pubDate>Tue, 30 Jun 2026 21:30:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo poner un agent loop a trabajar sobre un repositorio real: aislar cada tarea con git worktree, reproducir el bug con un test, correr toda la suite y entregar un PR en vez de un merge.

No dejes que el agente corra sobre tu copia de trabajo. Dale a cada tarea su propia copia aislada con git worktree y una rama: un intento fallido es una rama que borras, y dos tareas a la vez no se pisan. El flujo que funciona con bugs es escribir primero el test que reproduce el fallo (rojo), arreglar hasta ponerlo verde, y al final correr TODA la suite para cazar regresiones. Que el test nuevo pase no basta. La salida del agente es un PR, no un merge. Propone un diff en su propia rama; una persona lo revisa y lo integra. Aislar el trabajo y entregarlo como PR es lo que te deja dejarlo correr sin vigilarlo línea por línea.</description>
      <category>IA</category>
      <category>Agentes</category>
      <category>Agent loop</category>
      <category>Git worktree</category>
      <category>Pull request</category>
      <category>IA</category>
    </item>
    <item>
      <title>De generar archivos a usar herramientas: el loop ReAct de un agente de código</title>
      <link>https://www.ramonchancay.me/es/blog/loop-react-herramientas-agente</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/loop-react-herramientas-agente</guid>
      <pubDate>Tue, 30 Jun 2026 19:30:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>El paso de generar la función entera a un agente que lee, busca, edita y corre con herramientas: el loop ReAct y por qué el agente construye su propio contexto en vez de recibirlo en el prompt.

El cambio es de &quot;genera la función entera&quot; a &quot;lee, busca, edita, corre&quot;. El modelo deja de emitir la solución y empieza a emitir llamadas a herramientas; el programa las ejecuta y le devuelve el resultado. El agente construye su propio contexto. En vez de que tú metas el repo en el prompt, lee y busca solo lo que necesita, cuando lo necesita. El contexto es el resultado de sus observaciones, no algo que precargas. Es el loop ReAct: razonar, actuar, observar. El mismo loop, evaluador y sandbox de los posts anteriores, pero la acción ya no es única —es elegir entre varias herramientas—, y esa elección es lo que lo hace sentir como un agente de código real.</description>
      <category>IA</category>
      <category>Agentes</category>
      <category>Agent loop</category>
      <category>ReAct</category>
      <category>Tool calling</category>
      <category>IA</category>
    </item>
    <item>
      <title>Quién escribe los tests del agente: tres formas de definir qué es correcto</title>
      <link>https://www.ramonchancay.me/es/blog/quien-escribe-los-tests-evaluador-agente</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/quien-escribe-los-tests-evaluador-agente</guid>
      <pubDate>Tue, 30 Jun 2026 18:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Quién escribe los tests de un agente define qué es correcto. Tres niveles de input: spec y tests a mano, ejemplos que el agente convierte en tests, o la suite del repo como evaluador.

El verdadero input de un agente no es describir la solución, sino definir cómo se reconoce una correcta. A esa pieza —los tests, una comprobación, otro modelo— la serie la llama el evaluador, y un agente no optimiza para hacer lo correcto: optimiza para pasarlo. Hay tres niveles según quién escribe el evaluador: tú a mano (spec + tests), el agente a partir de ejemplos que le das, o la suite del repo que ya existe. A más nivel, menos trabajo por tarea y menos control sobre el criterio. Cuanto más alto el nivel, más sube lo que tienes que revisar. En el nivel 2 revisas los tests que el agente se escribió, no solo su código: un agente que define su propio criterio puede ponerse uno laxo y pasar a verde sin estar bien.</description>
      <category>IA</category>
      <category>Agentes</category>
      <category>Agent loop</category>
      <category>Evaluador</category>
      <category>Tests</category>
      <category>IA</category>
    </item>
    <item>
      <title>El sandbox de un agent loop: del texto del modelo a una observación fiable</title>
      <link>https://www.ramonchancay.me/es/blog/sandbox-ejecutar-codigo-generado-llm</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/sandbox-ejecutar-codigo-generado-llm</guid>
      <pubDate>Tue, 30 Jun 2026 16:30:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>El sandbox de un agent loop convierte el texto del modelo en una observación fiable: extraer el código de la prosa, validar que parsea, ejecutar con timeout y aislar el proceso.

El sandbox es el paso ejecutar del loop, pero su trabajo es convertir el texto del modelo en una observación fiable. Pasa por cuatro fases: entrada, preparación, ejecución y aislamiento. Extrae el código de la prosa, comprueba que parsea, ejecútalo con un timeout y en un proceso aparte con entorno limpio. Ojo: el timeout solo corta el código que tarda —no el que agota memoria o procesos— y un subproceso no es una frontera de seguridad. Lo que más mueve la aguja es clasificar: que el sandbox devuelva un estado (no un booleano) y separe stdout de stderr, para que cada fallo vuelva al modelo como un mensaje distinto.</description>
      <category>IA</category>
      <category>Agentes</category>
      <category>Sandbox</category>
      <category>Ejecución de código</category>
      <category>Agent loop</category>
      <category>LLM</category>
    </item>
    <item>
      <title>El loop más simple que funciona: un agente write-test-fix paso a paso</title>
      <link>https://www.ramonchancay.me/es/blog/agente-write-test-fix</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/agente-write-test-fix</guid>
      <pubDate>Tue, 30 Jun 2026 15:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>El agente más simple que sirve: spec y tests a mano, el modelo escribe el código, se ejecuta y el fallo vuelve como contexto. Un loop write-test-fix completo y en código que corre.

El loop write-test-fix es el agente mínimo: spec y tests escritos a mano, el modelo genera el código, se ejecuta, y el fallo de los tests vuelve como contexto para el siguiente intento. La condición de parada te la dan los tests: verde y para, rojo y reintenta, hasta un tope de iteraciones. No hace falta que el modelo &quot;decida&quot; que terminó. El feedback es todo. Un test que solo dice &quot;falló&quot; no arregla nada; uno que dice &quot;esperaba [9,5,25] y recibí [null,0,0]&quot; le da al modelo exactamente lo que necesita para corregir.</description>
      <category>IA</category>
      <category>Agentes</category>
      <category>Agent loop</category>
      <category>write-test-fix</category>
      <category>Generación de código</category>
      <category>IA</category>
    </item>
    <item>
      <title>Qué es un agent loop (y qué no): estado, acción, parada</title>
      <link>https://www.ramonchancay.me/es/blog/que-es-un-agent-loop</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/que-es-un-agent-loop</guid>
      <pubDate>Tue, 30 Jun 2026 00:58:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Qué es un agent loop y qué no: sus tres componentes —estado, acción y condición de parada— y por qué &apos;hazme una app&apos; es input de chat, no de agente.

Un agent loop tiene tres piezas: un estado (lo que el agente sabe hasta ahora), una acción (lo que hace en cada vuelta) y una condición de parada (cuándo decide que terminó). La diferencia con un chat no es el modelo, es quién corre el ciclo: en un chat lo corres tú —lees, ejecutas, vuelves a preguntar—; en un agente lo corre el programa. &quot;Hazme una app&quot; es un prompt para un chat y un objetivo para un agente. La misma frase, dos arquitecturas distintas.</description>
      <category>IA</category>
      <category>Agentes</category>
      <category>Agent loop</category>
      <category>LLM</category>
      <category>Arquitectura de agentes</category>
      <category>IA</category>
    </item>
    <item>
      <title>Automaticé un medio de noticias con n8n: de 20 feeds RSS a borradores en WordPress</title>
      <link>https://www.ramonchancay.me/es/blog/pipeline-noticias-n8n-rss-wordpress</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/pipeline-noticias-n8n-rss-wordpress</guid>
      <pubDate>Mon, 29 Jun 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo construí en n8n un pipeline que lee 20 feeds RSS, deduplica con Postgres, cura y reescribe con IA por niveles, y deja el artículo como borrador en WordPress con imagen y aviso por Telegram.

Deduplica antes de gastar: hashea el link y deja que Postgres rechace lo repetido con ON CONFLICT DO NOTHING. Así no scrapeas ni reescribes dos veces la misma noticia. Usa un modelo barato para puntuar la relevancia de 0 a 100 antes de reescribir: solo lo que supera el umbral llega al modelo caro. El gasto premium es la excepción, no la regla. Deja todo en borrador, no en publicado. La automatización propone; tú apruebas. Y avísate por Telegram para revisar a tiempo.</description>
      <category>Automatización</category>
      <category>n8n</category>
      <category>Automatización</category>
      <category>WordPress</category>
      <category>RSS</category>
      <category>LLM</category>
      <category>OpenRouter</category>
    </item>
    <item>
      <title>Routing de modelos con OpenRouter y DeepSeek para minimizar costos sin perder calidad</title>
      <link>https://www.ramonchancay.me/es/blog/routing-modelos-openrouter-deepseek</link>
      <guid isPermaLink="true">https://www.ramonchancay.me/es/blog/routing-modelos-openrouter-deepseek</guid>
      <pubDate>Mon, 29 Jun 2026 12:00:00 GMT</pubDate>
      <dc:creator>Ramón Chancay</dc:creator>
      <description>Cómo enruté modelos en producción con OpenRouter usando DeepSeek para la mayor parte del tráfico y modelos premium solo donde importa, recortando costos de LLM sin degradar la calidad.

Manda la mayor parte del tráfico (clasificar, extraer, resumir) a un modelo barato como DeepSeek y reserva el premium (Claude) solo para lo difícil. Valida la salida del barato y escala al premium solo si falla. Limpia el JSON (fences, comas colgantes) antes de rendirte, para no escalar de más. Mídelo todo por modelo y feature. Con volumen y tareas de dificultad desigual, el ahorro ronda el 80-90%.</description>
      <category>IA</category>
      <category>OpenRouter</category>
      <category>DeepSeek</category>
      <category>LLM</category>
      <category>Optimización de costos</category>
      <category>Model routing</category>
    </item>
  </channel>
</rss>
