1189 lines
32 KiB
Markdown
1189 lines
32 KiB
Markdown
|
|
---
|
||
|
|
theme: default
|
||
|
|
title: jj + Radicle — VCS para la era de los agentes
|
||
|
|
titleTemplate: '%s'
|
||
|
|
layout: cover
|
||
|
|
keywords: jj,Jujutsu,Radicle,VCS,Git,AI,Agents,ontoref,vapora
|
||
|
|
download: true
|
||
|
|
exportFilename: jj-radicle-agents
|
||
|
|
monaco: true
|
||
|
|
remoteAssets: true
|
||
|
|
selectable: true
|
||
|
|
record: true
|
||
|
|
colorSchema: dark
|
||
|
|
lineNumbers: false
|
||
|
|
themeConfig:
|
||
|
|
primary: '#f74c00'
|
||
|
|
fonts:
|
||
|
|
mono: 'Victor Mono'
|
||
|
|
drawings:
|
||
|
|
enabled: true
|
||
|
|
persist: false
|
||
|
|
presenterOnly: false
|
||
|
|
syncAll: true
|
||
|
|
scripts:
|
||
|
|
- setup/image-overlay.ts
|
||
|
|
class: 'justify-center flex flex-cols'
|
||
|
|
---
|
||
|
|
|
||
|
|
<h1 class="pl-27 font-medium">jj + Radicle</h1>
|
||
|
|
|
||
|
|
<h2 class="flex justify-center mt-3 font-medium text-orange-400">
|
||
|
|
VCS para la era de los agentes
|
||
|
|
</h2>
|
||
|
|
|
||
|
|
<div class="flex justify-center mt-3 text-gray-400 text-lg italic">
|
||
|
|
Workspaces aislados. Parches soberanos. Sin staging area.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="flex justify-center">
|
||
|
|
<img class="mt-8 w-40" src="/jesusperez_w.svg">
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
h1, h2, p { z-index: 10; }
|
||
|
|
</style>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Abrir con calma. La audiencia probablemente usa git todos los días.
|
||
|
|
Vamos a cuestionar cosas que asumen como inamovibles.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: two-cols
|
||
|
|
---
|
||
|
|
|
||
|
|
<h1 class="w-300"> El problema con Git + agentes IA </h1>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Fricción acumulada**
|
||
|
|
|
||
|
|
<div class="mt-3 space-y-3">
|
||
|
|
|
||
|
|
<div class="flex items-start gap-2">
|
||
|
|
<span class="text-red-500 font-bold text-sm mt-1">🔴</span>
|
||
|
|
<div><span class="text-gray-200 font-semibold">Staging area compartida</span><br><span class="text-gray-400 text-sm">Humano y agente compiten por el índice.<br> Un <code>git add</code> pisotea al otro.</span></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="flex items-start gap-2">
|
||
|
|
<span class="text-red-500 font-bold text-sm mt-1">🔴</span>
|
||
|
|
<div><span class="text-gray-200 font-semibold">Worktrees frágiles</span><br><span class="text-gray-400 text-sm">Git worktrees necesitan branches nombradas.<br> Colisionan. No escalan a N agentes.</span></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="flex items-start gap-2">
|
||
|
|
<span class="text-orange-400 font-bold text-sm mt-1">🟠</span>
|
||
|
|
<div><span class="text-gray-200 font-semibold">Centralización invisible</span><br><span class="text-gray-400 text-sm">GitHub/GitLab son el single point of failure.<br> Tu código, sus reglas.</span></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
::right::
|
||
|
|
|
||
|
|
<div class="mt-24 ml-4">
|
||
|
|
|
||
|
|
<div class="flex items-start gap-2">
|
||
|
|
<span class="text-orange-400 font-bold text-sm mt-1">🟠</span>
|
||
|
|
<div><span class="text-gray-200 font-semibold">Push-to-review es un cuello de botella</span><br><span class="text-gray-400 text-sm">El agente necesita push + PR + aprobación.<br> ¿Le das acceso de escritura al remoto?</span></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
|
||
|
|
<div class="flex items-start gap-2 mt-3">
|
||
|
|
<span class="text-yellow-400 font-bold text-sm mt-1">🟡</span>
|
||
|
|
<div><span class="text-gray-200 font-semibold">Merge hell con N actores</span><br><span class="text-gray-400 text-sm">3 agentes + 1 humano = conflictos serializados.<br> Alguien siempre pierde.</span></div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-6 border-l-4 border-orange-500 pl-4 text-sm text-gray-300 leading-6">
|
||
|
|
Git fue diseñado para <em>humanos</em> que trabajan en <em>branches</em>.<br><br>
|
||
|
|
Hoy tenemos <strong>N actores concurrentes</strong> (humanos + agentes) que necesitan <strong>aislamiento real</strong>, no convenciones.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-1 font-mono text-xs text-gray-500 bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```text
|
||
|
|
Agente-1: git stash → trabaja → git stash pop
|
||
|
|
Agente-2: git stash → trabaja → CONFLICTO
|
||
|
|
Humano: "¿quién tocó mi working tree?"
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
No es que Git sea malo. Es que fue diseñado para un mundo con un actor por working copy.
|
||
|
|
Hacer la pregunta: "¿cuántos de vosotros habéis tenido conflictos con el copiloto o un agente?"
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# Jujutsu (jj) — el modelo mental
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 mt-4">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Cambio fundamental: no hay staging area**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-3 mt-3">
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300 font-semibold">Working copy IS a commit</span><br>
|
||
|
|
<span class="text-gray-400">En jj, <code>@</code> ya es un commit. Cada save es una snapshot automática. No hay <code>git add</code>, no hay índice.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300 font-semibold">Changes, no commits</span><br>
|
||
|
|
<span class="text-gray-400">jj trabaja con <em>change IDs</em> estables. Reescribir historia no cambia el ID del cambio — solo su commit ID muta.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300 font-semibold">Conflictos son first-class</span><br>
|
||
|
|
<span class="text-gray-400">Un conflicto no bloquea. Se materializa en el árbol de trabajo. Puedes commitear conflictos, resolverlos después.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300 font-semibold">Undo nativo</span><br>
|
||
|
|
<span class="text-gray-400"><code>jj undo</code> deshace <em>cualquier</em> operación. El operation log es persistente e inmutable.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 px-4 rounded space-y-2">
|
||
|
|
|
||
|
|
**Git mental model**
|
||
|
|
```text
|
||
|
|
working tree → staging → commit → branch → push
|
||
|
|
↓ ↓ ↓ ↓
|
||
|
|
(mutable) (mutable) (fixed) (named)
|
||
|
|
```
|
||
|
|
|
||
|
|
**jj mental model**
|
||
|
|
```text
|
||
|
|
working copy (@) = commit automático
|
||
|
|
↓
|
||
|
|
describe → bookmark → push
|
||
|
|
↓
|
||
|
|
(siempre snapshot, siempre revertible)
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-1 font-mono text-xs bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Git: 4 pasos para guardar trabajo
|
||
|
|
git add -A && git commit -m "wip"
|
||
|
|
|
||
|
|
# jj: 0 pasos — ya está guardado
|
||
|
|
jj describe -m "feat: add parser"
|
||
|
|
# El working copy YA era un commit
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
El cambio mental más importante: no hay staging.
|
||
|
|
El working copy ya ES un commit. Esto cambia todo el flujo.
|
||
|
|
"describe" no es "commit" — es poner nombre a algo que ya existe.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# jj Workspaces — aislamiento real para agentes
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 mt-2">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Problema resuelto: N actores, N working copies**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-2 mt-3">
|
||
|
|
|
||
|
|
- Cada `jj workspace add` crea un **working copy independiente**
|
||
|
|
- Sin branches de por medio — cada workspace tiene su propio `@`
|
||
|
|
- Los cambios son **visibles entre workspaces** (mismo repo)
|
||
|
|
- Merge entre workspaces: `jj new workspace_change main`
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-3 font-mono text-xs bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Crear workspace para un agente
|
||
|
|
jj workspace add .agents/architect-01
|
||
|
|
|
||
|
|
# Cada workspace tiene su propio @
|
||
|
|
# Sin conflictos con el working copy principal
|
||
|
|
|
||
|
|
# Cuando termina:
|
||
|
|
jj new agent_change main # merge
|
||
|
|
jj bookmark set main -r @ # avanzar bookmark
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
**Comparación directa**
|
||
|
|
|
||
|
|
<div class="mt-3 text-xs">
|
||
|
|
|
||
|
|
| | Git worktree | jj workspace |
|
||
|
|
|---|---|---|
|
||
|
|
| **Requiere branch** | Sí (named) | No |
|
||
|
|
| **Staging area** | Compartida | No existe |
|
||
|
|
| **Visibilidad** | Aislada | Repo-wide |
|
||
|
|
| **Merge** | Checkout + merge | `jj new A B` |
|
||
|
|
| **Cleanup** | Manual | `jj workspace forget` |
|
||
|
|
| **N agentes** | N branches | N workspaces |
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="-mt-1 border-l-4 border-orange-500 pl-4 text-sm text-gray-300">
|
||
|
|
Un agente IA puede tener <strong>su propio working copy</strong> sin afectar al humano.<br>
|
||
|
|
Sin <code>stash</code>. Sin <code>branch switch</code>. Sin locks.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Aquí es donde jj brilla para AI agents. Cada agente tiene su propia copia de trabajo,
|
||
|
|
literalmente un directorio separado, con su propio @. No hay colisión posible.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# Radicle — code hosting soberano
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 mt-4">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Git, pero peer-to-peer**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-3 mt-3">
|
||
|
|
|
||
|
|
<div class="border-l-2 border-green-500 pl-3">
|
||
|
|
<span class="text-green-300 font-semibold">Protocolo, no plataforma</span><br>
|
||
|
|
<span class="text-gray-400">Sin servidor central. Cada nodo tiene una copia completa. Las colaboraciones son <em>patches</em>, no PRs.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-green-500 pl-3">
|
||
|
|
<span class="text-green-300 font-semibold">Identidad criptográfica</span><br>
|
||
|
|
<span class="text-gray-400">Cada desarrollador tiene un <code>DID</code> (ed25519). No hay cuentas, no hay contraseñas. Tu clave <em>es</em> tu identidad.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-green-500 pl-3">
|
||
|
|
<span class="text-green-300 font-semibold">Patches > Pull Requests</span><br>
|
||
|
|
<span class="text-gray-400">Un patch es un objeto nativo del protocolo. Vive en el gossip network. No depende de ningún servicio web.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-green-500 pl-3">
|
||
|
|
<span class="text-green-300 font-semibold">Seed nodes = espejos voluntarios</span><br>
|
||
|
|
<span class="text-gray-400">Cualquiera puede ser un seed. Web UI opcional (<code>radicle-explorer</code>). Sin vendor lock-in.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 p-4 rounded space-y-2">
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Inicializar repo en Radicle
|
||
|
|
rad init
|
||
|
|
|
||
|
|
# Tu identidad
|
||
|
|
rad self
|
||
|
|
# did:key:z6Mk... (ed25519)
|
||
|
|
|
||
|
|
# Publicar un patch (≈ PR)
|
||
|
|
rad patch open --title "feat: parser"
|
||
|
|
|
||
|
|
# Listar patches
|
||
|
|
rad patch list
|
||
|
|
|
||
|
|
# Clonar desde la red
|
||
|
|
rad clone rad:z42...
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Radicle no es "otro GitLab self-hosted". Es un protocolo P2P.
|
||
|
|
Si GitHub cae mañana, tu código sigue existiendo en todos los nodos que lo replican.
|
||
|
|
Los patches son objetos del protocolo, no features de un servicio web.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: center
|
||
|
|
---
|
||
|
|
|
||
|
|
# Radicle — code hosting soberano
|
||
|
|
|
||
|
|
<div class="mt-11 text-xs">
|
||
|
|
|
||
|
|
| | GitHub/GitLab | Radicle |
|
||
|
|
|---|---|---|
|
||
|
|
| **Hosting** | Centralizado | P2P (gossip) |
|
||
|
|
| **Identidad** | Username+password | ed25519 DID |
|
||
|
|
| **Review** | Pull Request | Patch (nativo) |
|
||
|
|
| **CI** | Actions/Pipelines | ci-broker (adaptable) |
|
||
|
|
| **Disponibilidad** | SPOF | Seed mesh |
|
||
|
|
| **Coste** | SaaS / self-host | Gratis (tu nodo) |
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: center
|
||
|
|
---
|
||
|
|
|
||
|
|
# ¿Por qué juntos? jj + Radicle
|
||
|
|
|
||
|
|
<div class="mt-4 space-y-3">
|
||
|
|
|
||
|
|
<div class="border-l-4 border-blue-400 pl-4 py-2 bg-gray-900 rounded-r">
|
||
|
|
<div class="text-blue-300 font-mono font-semibold text-sm">COLOCATED MODE · .jj/ + .git/</div>
|
||
|
|
<div class="text-gray-400 text-sm mt-1">jj opera sobre el repo. Radicle ve los git refs. Ambos coexisten sin conflicto.</div>
|
||
|
|
<div class="text-gray-500 text-xs mt-1"><code>jj git init --colocate</code> — crea ambos directorios. Radicle no sabe que jj existe.</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-4 border-orange-400 pl-4 py-2 bg-gray-900 rounded-r">
|
||
|
|
<div class="text-orange-300 font-mono font-semibold text-sm">WORKFLOW · workspace → describe → publish → patch</div>
|
||
|
|
<div class="text-gray-400 text-sm mt-1">El agente trabaja en un jj workspace. Cuando termina, publica un Radicle patch. Sin push a ningún server.</div>
|
||
|
|
<div class="text-gray-500 text-xs mt-1">La revisión ocurre en el gossip network. El merge ocurre localmente con <code>jj new</code>.</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-4 border-green-400 pl-4 py-2 bg-gray-900 rounded-r">
|
||
|
|
<div class="text-green-300 font-mono font-semibold text-sm">SEGURIDAD · El agente nunca necesita push access</div>
|
||
|
|
<div class="text-gray-400 text-sm mt-1">Patches firmados criptográficamente. El humano decide si mergea. El agente propone, nunca impone.</div>
|
||
|
|
<div class="text-gray-500 text-xs mt-1">Sin tokens de GitHub. Sin claves SSH al servidor. La identidad es local.</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-4 border-purple-400 pl-4 py-2 bg-gray-900 rounded-r">
|
||
|
|
<div class="text-purple-300 font-mono font-semibold text-sm">SOBERANÍA · Tu infraestructura, tus reglas</div>
|
||
|
|
<div class="text-gray-400 text-sm mt-1">Sin dependencia de servicios cloud para colaborar. Seed nodes privados o públicos. Tú decides.</div>
|
||
|
|
<div class="text-gray-500 text-xs mt-1">Combinado con NATS para eventos → ecosistema completo sin vendor lock-in.</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
El colocated mode es la pieza clave. jj y Radicle no se conocen entre sí — solo comparten .git/.
|
||
|
|
Esto no es un hack: es diseño intencionado de jj para interoperabilidad.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# Implementación real — vapora-worktree
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 -mt-4">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Abstracción VCS en Rust**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-2 mt-1">
|
||
|
|
|
||
|
|
- Trait `WorkspaceBackend` con implementaciones jj y Git
|
||
|
|
- Detección automática: ¿hay `.jj/`? → `JjBackend`. ¿Solo `.git/`? → `GitBackend`
|
||
|
|
- `VcsCapabilities` declarativo — no hard-coded blocking
|
||
|
|
- Runtime dispatch via `Box<dyn WorkspaceBackend>`
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 p-3 rounded -mt-5">
|
||
|
|
|
||
|
|
```rust
|
||
|
|
#[async_trait]
|
||
|
|
pub trait WorkspaceBackend: Send + Sync {
|
||
|
|
async fn create_workspace(
|
||
|
|
&self, agent_id: &str, ws_id: &str,
|
||
|
|
) -> Result<WorkspaceInfo>;
|
||
|
|
async fn check_merge(
|
||
|
|
&self, ws_id: &str, target: &str,
|
||
|
|
) -> Result<MergeStatus>;
|
||
|
|
async fn merge_workspace(
|
||
|
|
&self, ws_id: &str, target: &str,
|
||
|
|
) -> Result<()>;
|
||
|
|
fn capabilities(&self) -> VcsCapabilities;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
**¿Por qué un trait, no un enum?**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 mt-2 space-y-2">
|
||
|
|
|
||
|
|
<div class="border-l-2 border-orange-500 pl-3">
|
||
|
|
<span class="text-orange-300">Extensibilidad</span><br>
|
||
|
|
<span class="text-gray-400">Añadir Pijul, Sapling, etc. = implementar el trait. Sin tocar el manager.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-orange-500 pl-3">
|
||
|
|
<span class="text-orange-300">Capabilities declarativas</span><br>
|
||
|
|
<span class="text-gray-400">Cada backend declara qué puede hacer. El orquestador no asume.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 p-3 rounded mt-4">
|
||
|
|
|
||
|
|
```rust
|
||
|
|
pub struct VcsCapabilities {
|
||
|
|
pub supports_workspaces: bool,
|
||
|
|
pub supports_patch_open: bool,
|
||
|
|
pub merge_is_atomic: bool,
|
||
|
|
pub has_operation_log: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
// JjBackend: all true
|
||
|
|
// GitBackend: workspaces partial, no patch,
|
||
|
|
// no atomic merge, no op log
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Esto no es abstracción teórica. Es el crate vapora-worktree, ya implementado y con tests.
|
||
|
|
El trait permite que el orchestrator no sepa si está hablando con jj o git.
|
||
|
|
Nota: VcsCapabilities es el pattern correcto. No boolean flags en el manager.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# Implementación real — ontoref vcs.nu
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 -mt-4">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Abstracción VCS en Nushell**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-2 -mt-2">
|
||
|
|
|
||
|
|
- Módulo `vcs.nu` — único punto de contacto con el VCS
|
||
|
|
- Detección por filesystem: `(.jj/ exists?)` → jj, else → git
|
||
|
|
- Todos los módulos de ontoref importan `vcs.nu`
|
||
|
|
- Ningún `^git` directo fuera de vcs.nu
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 p-3 rounded -mt-3">
|
||
|
|
|
||
|
|
```nushell
|
||
|
|
# vcs.nu — API uniforme
|
||
|
|
export def detect [] -> string {
|
||
|
|
if (".jj" | path exists) { "jj" }
|
||
|
|
else if (".git" | path exists) { "git" }
|
||
|
|
else { "none" }
|
||
|
|
}
|
||
|
|
export def current-ref [] -> string {
|
||
|
|
match (detect) {
|
||
|
|
"jj" => {
|
||
|
|
# @ es el working copy, @- es el parent
|
||
|
|
jj log -r "@-" --no-graph -T 'change_id'
|
||
|
|
}
|
||
|
|
"git" => { git rev-parse --short HEAD }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
**Decisión arquitectónica: ADR-013**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 -mt-4 space-y-2">
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300">Todo VCS pasa por vcs.nu</span><br>
|
||
|
|
<span class="text-gray-400">Hard constraint. Sin excepciones.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300">jj y rad nunca son required</span><br>
|
||
|
|
<span class="text-gray-400">Un proyecto consumer usa git puro. Sin configuración adicional.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300">Filesystem detection = la verdad</span><br>
|
||
|
|
<span class="text-gray-400">No env vars. No config files. El filesystem es autoritativo.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-4 text-xs text-gray-500 font-mono bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
**Alternativas rechazadas (ADR-013):**
|
||
|
|
```text
|
||
|
|
❌ Env var ONTOREF_VCS → estado mutable, desync
|
||
|
|
❌ Detección inline por módulo → duplicación
|
||
|
|
❌ Shim de traducción → frágil, no isomórfico
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Dos implementaciones de la misma idea: Rust para vapora (crate), Nushell para ontoref (módulo).
|
||
|
|
La detección por filesystem es clave: no hay configuración que pueda quedar desincronizada.
|
||
|
|
ADR-013 formaliza esto como una hard constraint.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# jjw — el lifecycle wrapper para agentes
|
||
|
|
|
||
|
|
<div class="-mt-4">
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```text
|
||
|
|
jjw agent create → workspace aislado + ontoref run start + metadata .ontoref-run
|
||
|
|
jjw agent step → report de paso (pass/fail/skip) al protocolo ontoref
|
||
|
|
jjw agent publish → push + rad patch open (o git push fallback)
|
||
|
|
jjw agent merge → merge al main + bookmark advance + cleanup
|
||
|
|
jjw agent discard → abandon workspace + cancel run
|
||
|
|
jjw agent status → lista workspaces activos con estado de run
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 -mt-7">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Flujo completo de un agente**
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 px-3 rounded -mt-4">
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 1. Agente recibe tarea
|
||
|
|
jjw agent create --agent architect-01 \
|
||
|
|
--mode develop --task "add parser module"
|
||
|
|
# 2. Trabaja en .agents/architect-01/
|
||
|
|
# (su propio working copy)
|
||
|
|
cd .agents/architect-01/
|
||
|
|
# ... edita archivos ...
|
||
|
|
# 3. Reporta progreso
|
||
|
|
jjw agent step develop parse-impl --status pass
|
||
|
|
# 4. Publica resultado
|
||
|
|
jjw agent publish
|
||
|
|
# → rad patch open (si hay remote rad)
|
||
|
|
# → git push (fallback)
|
||
|
|
# 5. Humano revisa y mergea
|
||
|
|
jjw agent merge
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**¿Qué ocurre por debajo?**
|
||
|
|
|
||
|
|
<div class="text-xs text-gray-300 space-y-2 mt-2">
|
||
|
|
|
||
|
|
- `create` → <small>`jj workspace add` + `ontoref run start` + JSON metadata</small>
|
||
|
|
- `step` → <small>`ontoref step report` (DAG de modo)</small>
|
||
|
|
- `publish` → <small>detecta remote `rad` via `jj git remote list`;<br> si existe → `rad patch open`; si no → `git push`</small>
|
||
|
|
- `merge` → <small>`jj new agent_change main` + `jj bookmark set main -r @`</small>
|
||
|
|
- `discard` → <small>`jj abandon` + `ontoref mode cancel`</small>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="mt-4 border-l-4 border-green-500 pl-3 text-sm text-gray-400">
|
||
|
|
El agente nunca toca el working copy del humano.<br>
|
||
|
|
El humano nunca necesita saber que jj existe.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
jjw es el pegamento. Conecta jj workspaces con el protocolo ontoref (runs, steps, modes)
|
||
|
|
y con Radicle (patches). Es un script Nushell de ~320 líneas.
|
||
|
|
Lo importante: el agente tiene un lifecycle formal — no es "haz lo que quieras en un branch".
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# NCL merge tool — resolver conflictos en .ontology/
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 mt-4">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**El problema**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-2 mt-2">
|
||
|
|
|
||
|
|
- Dos agentes editan `.ontology/core.ncl` en paralelo
|
||
|
|
- jj materializa el conflicto (no bloquea)
|
||
|
|
- Pero NCL no es texto plano — es un lenguaje tipado
|
||
|
|
- Un merge textual puede romper la semántica
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
**La solución: merge en dos capas**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-2 mt-3">
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300">Capa 1: git merge-file</span><br>
|
||
|
|
<span class="text-gray-400">3-way text merge. Resuelve adiciones disjuntas (la mayoría de casos).</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-orange-500 pl-3">
|
||
|
|
<span class="text-orange-300">Capa 2: nickel export</span><br>
|
||
|
|
<span class="text-gray-400">Valida que el resultado sea NCL válido. Si falla → conflicto manual.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Registrar el merge tool en jj config
|
||
|
|
# ~/.config/jj/config.toml
|
||
|
|
[merge-tools.ncl]
|
||
|
|
program = "nu"
|
||
|
|
args = [
|
||
|
|
"jjw-ncl-merge.nu",
|
||
|
|
"$left", "$right", "$base", "$output"
|
||
|
|
]
|
||
|
|
|
||
|
|
# Usar al resolver
|
||
|
|
jj resolve --tool ncl -- .ontology/core.ncl
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-4 font-mono text-xs bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```text
|
||
|
|
Solo mergea archivos ontológicos:
|
||
|
|
✓ core.ncl (nodos, edges, tensiones)
|
||
|
|
✓ state.ncl (FSM dimensiones)
|
||
|
|
✓ gate.ncl (quality gates)
|
||
|
|
✓ manifest.ncl (capabilities)
|
||
|
|
|
||
|
|
✗ Cualquier otro .ncl → merge manual
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Los conflictos en NCL son especiales. Un merge textual exitoso puede producir NCL inválido.
|
||
|
|
Por eso la segunda capa: nickel export como validador semántico.
|
||
|
|
Esto solo aplica a los archivos de ontología — el resto se resuelve normalmente.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# NATS como pegamento — radicle-nats-adapter
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 mt-2">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Tres mundos conectados sin acoplamiento**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-2 mt-3">
|
||
|
|
|
||
|
|
<div class="border-l-2 border-purple-500 pl-3">
|
||
|
|
<span class="text-purple-300 font-semibold">vapora → ontoref</span><br>
|
||
|
|
<span class="text-gray-400"><code>vapora.tasks.completed</code> filtra paths <code>.ontology/</code><br>→ publica <code>ecosystem.reflection.request</code></span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-green-500 pl-3">
|
||
|
|
<span class="text-green-300 font-semibold">radicle → ontoref</span><br>
|
||
|
|
<span class="text-gray-400"><code>radicle.patch.opened</code> / <code>updated</code><br>→ publica <code>ecosystem.reflection.request</code></span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-orange-500 pl-3">
|
||
|
|
<span class="text-orange-300 font-semibold">ontoref → vapora</span><br>
|
||
|
|
<span class="text-gray-400"><code>ecosystem.reflection.step.completed</code><br>→ publica <code>vapora.tracking.ontoref</code></span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-3 border-l-4 border-yellow-500 pl-3 text-sm text-gray-400">
|
||
|
|
<strong>Invariante crítico:</strong> subscribe-before-publish.<br>
|
||
|
|
El adapter se suscribe al resultado <em>antes</em> de publicar la solicitud. Previene race conditions.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```text
|
||
|
|
┌─────────┐ NATS ┌──────────┐
|
||
|
|
│ Radicle │ ────────→ │ Adapter │
|
||
|
|
│ node │ patches │ (bridge) │
|
||
|
|
└─────────┘ └────┬─────┘
|
||
|
|
│
|
||
|
|
┌─────────────┼──────────┐
|
||
|
|
↓ ↓ ↓
|
||
|
|
┌──────────┐ ┌─────────┐ ┌────────┐
|
||
|
|
│ ontoref │ │ vapora │ │ CI │
|
||
|
|
│ daemon │ │ tracker │ │(broker)│
|
||
|
|
└──────────┘ └─────────┘ └────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-3 font-mono text-xs bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Dos modos de ejecución
|
||
|
|
radicle-nats-adapter run
|
||
|
|
# → efímero: 1 evento CI, stdin/stdout
|
||
|
|
|
||
|
|
radicle-nats-adapter bridge all
|
||
|
|
# → long-running: 3 bridges concurrentes
|
||
|
|
# → tokio::select! + explicit .abort()
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
El adapter es un binario Rust standalone. No depende de ontoref ni de vapora.
|
||
|
|
Es puro glue: traduce eventos entre sistemas usando NATS subjects.
|
||
|
|
Las convenciones de subject (radicle.*, vapora.*, ecosystem.*) son el contrato.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
<h1 class="-mt-4"> Flujo completo — del agente al patch</h1>
|
||
|
|
|
||
|
|
<div class="-mt-8">
|
||
|
|
|
||
|
|
<div class="font-mono text-xs bg-gray-900 p-4 rounded leading-6">
|
||
|
|
```text
|
||
|
|
Agente IA jj Radicle Humano
|
||
|
|
───────── ──── ─────── ──────
|
||
|
|
│ jjw agent create │ │ │
|
||
|
|
│────────────────────→ │ │ │
|
||
|
|
│ workspace aislado │ │ │
|
||
|
|
│ .agents/agent-01/ │ │ │
|
||
|
|
│ │ │ │
|
||
|
|
│ ... trabaja ... │ │ │
|
||
|
|
│ jjw agent step ✓ │ │ │
|
||
|
|
│ │ │ │
|
||
|
|
│ jjw agent publish │ │ │
|
||
|
|
│────────────────────→ │ │ │
|
||
|
|
│ jj git push │ │ │
|
||
|
|
│ │──rad patch open──→ │ │
|
||
|
|
│ │ │──── notifica ────→ │
|
||
|
|
│ │ │ revisa patch │
|
||
|
|
│ │ │ ←── rad patch │
|
||
|
|
│ │ │ merge ─────────│
|
||
|
|
│ jjw agent merge │ │ │
|
||
|
|
│────────────────────→ │ │ │
|
||
|
|
│ jj new A main │ │ │
|
||
|
|
│ bookmark set main │ │ │
|
||
|
|
│ workspace forget │ │ │
|
||
|
|
│ │ │ │
|
||
|
|
▼ ▼ ▼ ▼
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-3 text-sm text-gray-300 grid grid-cols-3 gap-4">
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300">Aislamiento</span><br>
|
||
|
|
<span class="text-gray-400">El agente nunca toca el working copy del humano</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-green-500 pl-3">
|
||
|
|
<span class="text-green-300">Revisión</span><br>
|
||
|
|
<span class="text-gray-400">El patch es un objeto firmado en la red P2P</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-orange-500 pl-3">
|
||
|
|
<span class="text-orange-300">Control</span><br>
|
||
|
|
<span class="text-gray-400">El humano decide cuándo y qué se mergea</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Este es el flujo end-to-end. Nótese que el agente nunca necesita acceso de escritura al remoto.
|
||
|
|
Propone via patch. El humano mantiene el control. jj provee el aislamiento. Radicle provee la distribución.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: default
|
||
|
|
---
|
||
|
|
|
||
|
|
# Graceful degradation — jj es opcional
|
||
|
|
|
||
|
|
<div class="grid grid-cols-2 gap-8 mt-4">
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
**Principio: opt-in, never required**
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300 space-y-3 mt-3">
|
||
|
|
|
||
|
|
<div class="border-l-2 border-green-500 pl-3">
|
||
|
|
<span class="text-green-300">Si hay .jj/ → usa jj</span><br>
|
||
|
|
<span class="text-gray-400">Workspaces nativos, operation log, snapshots automáticas.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-blue-500 pl-3">
|
||
|
|
<span class="text-blue-300">Si solo hay .git/ → usa git</span><br>
|
||
|
|
<span class="text-gray-400">Todo funciona. Sin worktree isolation, pero funciona.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border-l-2 border-gray-500 pl-3">
|
||
|
|
<span class="text-gray-300">Radicle: igual</span><br>
|
||
|
|
<span class="text-gray-400">Si hay remote <code>rad</code> → patches. Si no → <code>git push</code> clásico.</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-4 font-mono text-xs bg-gray-900 p-3 rounded">
|
||
|
|
|
||
|
|
```nushell
|
||
|
|
# init-repo.nu — decisión de inicialización
|
||
|
|
if (".jj" | path exists) {
|
||
|
|
# Ya inicializado con jj
|
||
|
|
} else if (".git" | path exists) {
|
||
|
|
# Git puro — funciona sin cambios
|
||
|
|
} else {
|
||
|
|
# Nuevo proyecto → preferir colocated
|
||
|
|
^jj git init --colocate
|
||
|
|
# Crea .jj/ + .git/ → compatible con todo
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="text-sm text-gray-300">
|
||
|
|
|
||
|
|
**Lo que esto significa para adopción**
|
||
|
|
|
||
|
|
<div class="mt-3 space-y-4">
|
||
|
|
|
||
|
|
<div class="border border-gray-700 rounded p-3 bg-gray-900">
|
||
|
|
<div class="text-green-300 font-semibold text-xs">PROYECTO EXISTENTE (git)</div>
|
||
|
|
<div class="text-gray-400 text-xs mt-1">No cambias nada. Todo funciona. vcs.nu detecta git y usa git.</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border border-gray-700 rounded p-3 bg-gray-900">
|
||
|
|
<div class="text-blue-300 font-semibold text-xs">QUIERES PROBAR JJ</div>
|
||
|
|
<div class="text-gray-400 text-xs mt-1"><code>jj git init --colocate</code> en un repo existente. Listo. Git sigue funcionando.</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border border-gray-700 rounded p-3 bg-gray-900">
|
||
|
|
<div class="text-orange-300 font-semibold text-xs">QUIERES RADICLE</div>
|
||
|
|
<div class="text-gray-400 text-xs mt-1"><code>rad init</code> sobre el repo. Añade un remote. Los patches funcionan.</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border border-gray-700 rounded p-3 bg-gray-900">
|
||
|
|
<div class="text-purple-300 font-semibold text-xs">QUIERES TODO</div>
|
||
|
|
<div class="text-gray-400 text-xs mt-1">jj colocated + rad init. Agentes con jjw. Patches soberanos. 0 vendor lock-in.</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Esto es crítico para la adopción. Nadie tiene que migrar de golpe.
|
||
|
|
Un proyecto git puro sigue siendo un proyecto git puro.
|
||
|
|
jj y Radicle se añaden incrementalmente, sin romper nada.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: center
|
||
|
|
---
|
||
|
|
|
||
|
|
# Resumen — qué aporta cada pieza
|
||
|
|
|
||
|
|
<div class="mt-4 space-y-3">
|
||
|
|
|
||
|
|
<div class="grid grid-cols-4 gap-4 text-center">
|
||
|
|
|
||
|
|
<div class="border border-gray-700 rounded p-4 bg-gray-900">
|
||
|
|
<div class="text-blue-300 font-mono font-semibold text-lg">jj</div>
|
||
|
|
<div class="text-gray-400 text-xs mt-2">Aislamiento real<br>Sin staging area<br>Undo nativo<br>Workspaces nativos</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border border-gray-700 rounded p-4 bg-gray-900">
|
||
|
|
<div class="text-green-300 font-mono font-semibold text-lg">Radicle</div>
|
||
|
|
<div class="text-gray-400 text-xs mt-2">Soberanía<br>Patches firmados<br>P2P gossip<br>Sin vendor lock-in</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border border-gray-700 rounded p-4 bg-gray-900">
|
||
|
|
<div class="text-orange-300 font-mono font-semibold text-lg">jjw</div>
|
||
|
|
<div class="text-gray-400 text-xs mt-2">Lifecycle formal<br>Agent workspaces<br>Protocol bridge<br>NCL merge tool</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="border border-gray-700 rounded p-4 bg-gray-900">
|
||
|
|
<div class="text-purple-300 font-mono font-semibold text-lg">NATS</div>
|
||
|
|
<div class="text-gray-400 text-xs mt-2">Event mesh<br>Desacoplamiento<br>Bridge adapter<br>CI integration</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<v-click>
|
||
|
|
|
||
|
|
<div class="mt-6 border border-gray-700 rounded p-4 bg-gray-900 text-sm text-gray-300 font-mono leading-7 text-center">
|
||
|
|
|
||
|
|
```text
|
||
|
|
Git solo → funciona, pero no escala a N agentes concurrentes
|
||
|
|
+ jj → aislamiento real por workspace, undo, sin staging
|
||
|
|
+ Radicle → soberanía, patches firmados, sin plataforma central
|
||
|
|
+ jjw + NATS → lifecycle formal, eventos entre sistemas, CI adaptable
|
||
|
|
```
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</v-click>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Cada pieza añade una capa. Ninguna es obligatoria.
|
||
|
|
Puedes quedarte en cualquier nivel. El valor es aditivo, no todo-o-nada.
|
||
|
|
-->
|
||
|
|
|
||
|
|
---
|
||
|
|
layout: center
|
||
|
|
---
|
||
|
|
|
||
|
|
<h1 class="font-medium">Gracias</h1>
|
||
|
|
|
||
|
|
<div class="flex justify-center mt-4 text-gray-400 text-lg italic">
|
||
|
|
Tu código, tus reglas, tu infraestructura.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="mt-8 text-sm text-gray-500 space-y-1 text-center">
|
||
|
|
|
||
|
|
`jj` — https://martinvonz.github.io/jj/
|
||
|
|
|
||
|
|
`Radicle` — https://radicle.xyz/
|
||
|
|
|
||
|
|
`ontoref` — protocolo de auto-conocimiento para proyectos
|
||
|
|
|
||
|
|
`vapora` — orquestador de workspaces para agentes
|
||
|
|
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="flex justify-center">
|
||
|
|
<img class="mt-8 w-40" src="/jesusperez_w.svg">
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Footer />
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Cerrar con calma. El mensaje es soberanía + aislamiento + formalismo.
|
||
|
|
No es anti-GitHub. Es pro-independencia.
|
||
|
|
-->
|