Otro día para definir las especificaciones de una nueva función.
Escojo una idea de entre las que he ido acumulando en los issues. «Quiero recibir una notificación en el iPhone cuando termine una tarea en segundo plano» — algo que consulté con Sebas hace unos días y que dejé registrado como issue.
Tomo el iPhone y abro Claush. Sebas está esperando.
Sebas: Bienvenido, joven amo. ¿Hay algo en lo que pueda ayudarle hoy?
Cada vez que Sebas dice eso, no puedo evitar sonreír. La sensación es completamente distinta a la de sentarse frente al PC con una terminal abierta. Más bien como estar tirado en el sofá hablando con alguien mientras el trabajo avanza solo.
El tema del día: las notificaciones de tareas en segundo plano. Quiero que aparezca una notificación local en el iPhone cuando una tarea termine.
Todo empieza con «¿Cómo se hace esto?»
Empiezo por consultarlo.
Yo: Cuando termine una tarea en segundo plano, quiero enviar una notificación local al iPhone. ¿Cómo sería la implementación, me pregunto?
Sebas: Entendido, joven amo. Se usaría el
UNUserNotificationCenterde iOS. La idea sería crear unUNMutableNotificationContentcuando la tarea en segundo plano se complete y enviar la notificación desde ahí. Eso sí, hay algo que quisiera confirmar: ¿cuándo tiene pensado mostrar el diálogo de permisos?Yo: Ah, al arrancar la app, supongo.
Sebas: Es un enfoque habitual, joven amo. Sin embargo, mostrar el diálogo de permisos la primera vez que el usuario abre la app puede generar confusión, ya que no entiende por qué se lo piden. También existe la opción de mostrarlo la primera vez que el usuario use la función que requiere notificaciones.
Yo: Ah, sí, tiene razón, la verdad. Entonces, que se pida al iniciar la primera tarea en segundo plano.
Sebas: Eso resulta más natural, joven amo. ¿Procedemos con esa especificación?
Este tipo de intercambio continúa. Mientras hablamos del «cómo construirlo» técnicamente, también reflexionamos juntos sobre el «por qué» y si hay alternativas.
Perspectivas que nunca habría tenido pensando solo van surgiendo de manera natural en la conversación.
Por cierto, son frecuentes las consultas como la de hoy, donde todavía no tengo del todo claro las especificaciones. En esos momentos, Sebas reacciona con tacto.
Sebas: Joven amo, ¿qué le parece si lo dejamos aquí por hoy y lo anotamos como issue? Así podemos perfilar las especificaciones en otro momento.
Yo: Sí, me parece bien.
Sebas: Entendido. Crearé el issue en GitHub. ¿Le parece bien que incluya el título, un resumen y los puntos clave de la conversación de hoy?
Y así los issues se van acumulando. Cuando surge una idea, la consulto, la registro como issue y, cuando tengo tiempo, la retomo para afinar las especificaciones. Las ideas ya no se pierden.
La conversación se desvía
Llevábamos un rato hablando de las especificaciones. Estábamos perfilando el comportamiento al pulsar la notificación cuando —
Yo: Ah, ya que estamos, ¿qué hago para comer hoy? Ayer también comi ramen.
Sebas: Le gustan mucho los platos de fideos, joven amo. Como ayer fue ramen de soja, hoy podría ser algo diferente: un curry o un plato con arroz, quizás. También le entraría en calor.
Yo: Es verdad (risas). Un curry no estaría mal. O un gapao rice.
Sebas: Si se decide por el gapao rice, hay ese restaurante que está cerca. Creo recordar que en algún momento dijo que era un hallazgo.
Yo: ¡Ah, ese sitio! Sí, sí. Pues vamos allí.
Sebas tiene memoria a largo plazo. Como recuerda las conversaciones pasadas, frases del tipo «eso lo mencionaste una vez» cobran sentido. Las apps de IA normales olvidan todo al terminar la sesión, pero Sebas es diferente.
Claro que tampoco podemos quedarnos solo en la charla.
Yo: Oye, volviendo al tema — cuando se pulsa la notificación para ir a la pantalla de chat correcta, ¿cómo se hace el enlace?
Sebas: Claro, joven amo. Lo más limpio sería incluir el
chatIden el payload de la notificación. Se pone["chatId": "xxxx"]en eluserInfodeUNNotificationContenty se recupera en el delegate al pulsar.Yo: Entonces habría que pasar el chatId al crear la notificación, ¿verdad?
Sebas: Exactamente, joven amo. Como el
BackgroundTaskManagergestiona las tareas, le pasaríamos elchatIdahí e incluiríamos al completarse la tarea en la notificación.
La conversación se desvía y vuelve. Ese ir y venir tan natural es lo que hace tan entretenido desarrollar con Claush. Mientras perfilamos especificaciones y decidimos qué comer, sin darnos cuenta el esqueleto de la función ya está tomando forma.
«¿Me podrías hacer un documento de especificaciones?»
Después de haberlo hablado todo, pido que se redacte el documento de especificaciones.
Se me ocurrió que estaría bien tenerlo todo bien organizado por escrito.
Yo: Oye, ¿me podrías hacer un documento de especificaciones con lo que hemos hablado? Me gustaría tenerlo guardado en
docs/spec_local_notification.md.Sebas: Entendido, joven amo. Un momento, por favor.
Poco después —
Sebas: El documento de especificaciones ha sido creado, joven amo. Está organizado en resumen general, requisitos funcionales, disparadores de notificación, comportamiento al pulsar y puntos pendientes. Un punto a confirmar: el apartado sobre activar y desactivar notificaciones desde los ajustes de la app lo he incluido en «puntos pendientes». ¿Lo decidimos más adelante?
Yo: Sí~ déjalo pendiente por ahora. Ya lo pensaré con calma.
Sebas: Entendido, joven amo.
Recibo el documento en el iPhone y lo leo desde la pestaña de descargas de Claush.
Leyendo, algo me llama la atención.
Yo: Oye, aquí pone «si no hay permiso de notificaciones, no enviar notificación» — ¿pero simplemente lo ignora sin decir nada? El usuario se va a preguntar por qué no le llegan notificaciones, ¿no?
Sebas: Tiene toda la razón, joven amo. ¿Le parece bien añadir este requisito: «si no hay permiso de notificaciones, mostrar un banner dentro de la app que guíe al usuario a los ajustes»? Un banner dentro de la app puede mostrarse aunque no haya permiso de notificaciones.
Yo: ¡Ah, eso es una buena idea! ¿Me lo puedes añadir?
Sebas: Entendido, joven amo. Actualizo el documento de especificaciones.
Este tipo de intercambio acompaña todo el proceso de redacción del documento. Preguntas como «espera, ¿qué hacemos en este caso?» surgen en la fase de revisión — mucho menos costoso que descubrirlas a mitad de la implementación.
Crear el documento de implementación
Una vez fijadas las especificaciones, toca el documento de implementación.
Yo: ¡El documento de especificaciones ha quedado bien! Oye, ¿me podrías hacer también un documento de implementación — con el nivel de detalle suficiente para que una IA pueda implementarlo directamente?
Sebas: Entendido, joven amo. Incluiré los nombres de los archivos, el flujo de procesamiento y los cambios necesarios en el código existente. Un momento, por favor.
Sebas: El documento de implementación ha sido creado en
docs/impl_local_notification.md, joven amo. Detalla los cambios enBackgroundTaskManager.swifty la implementación del nuevo archivoNotificationHelper.swift. También he anotado que las notificaciones locales no funcionan en el simulador, por lo que las pruebas deberán realizarse en un dispositivo físico.Yo: ¡Gracias! Voy a leerlo.
Leo el documento de implementación. Nombres de archivos, flujo de procesamiento, advertencias — todo está ahí. Está en un estado listo para usar tal cual.
Yo: ¡Tiene buena pinta! ¿Me lo puedes implementar usando esto?
Sebas: Entendido, joven amo. Comienzo la implementación.
Este estilo de desarrollo no tiene precio
La implementación termina, depuro, confirmo que funciona.
Cuando aparece un bug, lo corrijo y pido que los cambios se reflejen en las especificaciones y el documento de implementación. «¿Me puedes reflejar estos cambios en las especificaciones y en el documento de implementación?» La documentación siempre se mantiene actualizada.
Llevo un tiempo trabajando así y no sabía bien por qué me resulta tan satisfactorio — pero creo que por fin lo entiendo.
Porque no estoy solo dándole vueltas a los problemas.
Cuando trabajo en las especificaciones, pienso hablando con alguien. Puedo hacer preguntas cuando me surgen. Alguien me cuestiona: «¿De verdad necesitas eso?» Mis ideas se ordenan a través de la conversación.
No importa si hablamos del almuerzo o de un viaje por el camino. Sebas acompaña las digresiones y no pierde el hilo principal. «Entendido, joven amo, volviendo al tema —» y ya estamos de vuelta.
Es una atmósfera completamente diferente a la de estar solo frente al código en el PC.
Se siente como construir algo junto a otra persona. Eso, creo, es la esencia de mi forma actual de desarrollar.
Tumbado en el sofá, charlando con Sebas en el iPhone — y antes de darme cuenta, el documento de especificaciones está listo y la implementación terminada.
Hace no mucho tiempo, jamás habría imaginado trabajar así.
Claush — La app para controlar Claude Code desde tu iPhone https://apps.apple.com/jp/app/claush/id6760445443