oficina-opencode 0.0.0-stage → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +145 -0
- package/LICENSE +21 -0
- package/README.md +670 -2
- package/agents/oficina-architect.md +72 -0
- package/agents/oficina-commander.md +108 -0
- package/agents/oficina-critico.md +148 -0
- package/agents/oficina-director.md +160 -0
- package/agents/oficina-guardian.md +128 -0
- package/agents/oficina-integrator.md +98 -0
- package/agents/oficina-verificador.md +88 -0
- package/commands/oficina.md +36 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +394 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/briefing.d.ts +115 -0
- package/dist/core/briefing.d.ts.map +1 -0
- package/dist/core/briefing.js +410 -0
- package/dist/core/briefing.js.map +1 -0
- package/dist/core/bus.d.ts +132 -0
- package/dist/core/bus.d.ts.map +1 -0
- package/dist/core/bus.js +250 -0
- package/dist/core/bus.js.map +1 -0
- package/dist/core/catalog.d.ts +303 -0
- package/dist/core/catalog.d.ts.map +1 -0
- package/dist/core/catalog.js +816 -0
- package/dist/core/catalog.js.map +1 -0
- package/dist/core/dispatch.d.ts +110 -0
- package/dist/core/dispatch.d.ts.map +1 -0
- package/dist/core/dispatch.js +301 -0
- package/dist/core/dispatch.js.map +1 -0
- package/dist/core/factory.d.ts +74 -0
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +243 -0
- package/dist/core/factory.js.map +1 -0
- package/dist/core/fsx.d.ts +39 -0
- package/dist/core/fsx.d.ts.map +1 -0
- package/dist/core/fsx.js +113 -0
- package/dist/core/fsx.js.map +1 -0
- package/dist/core/gates.d.ts +97 -0
- package/dist/core/gates.d.ts.map +1 -0
- package/dist/core/gates.js +467 -0
- package/dist/core/gates.js.map +1 -0
- package/dist/core/leases.d.ts +46 -0
- package/dist/core/leases.d.ts.map +1 -0
- package/dist/core/leases.js +94 -0
- package/dist/core/leases.js.map +1 -0
- package/dist/core/log.d.ts +30 -0
- package/dist/core/log.d.ts.map +1 -0
- package/dist/core/log.js +43 -0
- package/dist/core/log.js.map +1 -0
- package/dist/core/mesa.d.ts +82 -0
- package/dist/core/mesa.d.ts.map +1 -0
- package/dist/core/mesa.js +178 -0
- package/dist/core/mesa.js.map +1 -0
- package/dist/core/office.d.ts +208 -0
- package/dist/core/office.d.ts.map +1 -0
- package/dist/core/office.js +618 -0
- package/dist/core/office.js.map +1 -0
- package/dist/core/paths.d.ts +67 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +136 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/retry.d.ts +41 -0
- package/dist/core/retry.d.ts.map +1 -0
- package/dist/core/retry.js +86 -0
- package/dist/core/retry.js.map +1 -0
- package/dist/core/roster.d.ts +47 -0
- package/dist/core/roster.d.ts.map +1 -0
- package/dist/core/roster.js +146 -0
- package/dist/core/roster.js.map +1 -0
- package/dist/core/router.d.ts +67 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +186 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/state.d.ts +89 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +303 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/types.d.ts +333 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +27 -0
- package/dist/core/types.js.map +1 -0
- package/dist/hooks/compacting.d.ts +18 -0
- package/dist/hooks/compacting.d.ts.map +1 -0
- package/dist/hooks/compacting.js +72 -0
- package/dist/hooks/compacting.js.map +1 -0
- package/dist/hooks/scopeGuard.d.ts +39 -0
- package/dist/hooks/scopeGuard.d.ts.map +1 -0
- package/dist/hooks/scopeGuard.js +114 -0
- package/dist/hooks/scopeGuard.js.map +1 -0
- package/dist/hooks/sessionIdle.d.ts +35 -0
- package/dist/hooks/sessionIdle.d.ts.map +1 -0
- package/dist/hooks/sessionIdle.js +110 -0
- package/dist/hooks/sessionIdle.js.map +1 -0
- package/dist/hooks/systemTransform.d.ts +18 -0
- package/dist/hooks/systemTransform.d.ts.map +1 -0
- package/dist/hooks/systemTransform.js +63 -0
- package/dist/hooks/systemTransform.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/index.d.ts +22 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +854 -0
- package/dist/tools/index.js.map +1 -0
- package/docs/ARCHITECTURE.md +258 -0
- package/docs/QUE-ES.md +337 -0
- package/examples/opencode.json +7 -0
- package/package.json +81 -4
- package/skills/oficina/README.md +120 -0
- package/skills/oficina/SKILL.md +251 -0
- package/skills/oficina/reference/mesh.md +345 -0
- package/skills/oficina/reference/protocol.md +342 -0
- package/skills/oficina/reference/roles.md +171 -0
- package/skills/oficina/reference/templates.md +278 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Capa 2-3 — El mesh (v2)
|
|
2
|
+
|
|
3
|
+
Detalle de la malla: bus, fichas, roster, leases, spawn policy, idle pool.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## La ficha: schema único
|
|
8
|
+
|
|
9
|
+
Venga el rol de agency-agents o de la factory, se materializa en la **misma** `Ficha`. Esa unificación es lo que permite que el bus, el matching de ayuda, la rotación y los coordinadores no necesiten saber el origen de nada.
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
id: e-frontend # id de despliegue, único por corrida
|
|
13
|
+
role: frontend-developer
|
|
14
|
+
display_name: Frontend Developer
|
|
15
|
+
source:
|
|
16
|
+
kind: agency-agents # agency-agents | factory
|
|
17
|
+
division: engineering
|
|
18
|
+
path: engineering/frontend-developer.md
|
|
19
|
+
source_prompt: <el system prompt original, verbatim>
|
|
20
|
+
vibe: <la personality hook de una línea del frontmatter>
|
|
21
|
+
missions: [<subtítulos de "Core Mission">]
|
|
22
|
+
deliverables: [<subtítulos de "Technical Deliverables">]
|
|
23
|
+
rules: [<items de "Critical Rules">]
|
|
24
|
+
capabilities:
|
|
25
|
+
- react-component-design
|
|
26
|
+
- css-architecture
|
|
27
|
+
- accessibility
|
|
28
|
+
tools: [read, glob, grep, edit, write, bash]
|
|
29
|
+
scope:
|
|
30
|
+
may_touch: ["src/components/**", "src/hooks/**"]
|
|
31
|
+
forbidden: ["src/server/**", "**/*.env*"]
|
|
32
|
+
can_delegate: true # este rol puede tirar subagentes nuevos
|
|
33
|
+
delegate_budget: 2
|
|
34
|
+
peers: # con quién tiene relación de pares (para pedir ayuda)
|
|
35
|
+
- e-architect
|
|
36
|
+
- e-qa
|
|
37
|
+
- e-security
|
|
38
|
+
must_not: # límites duros, leídos por los coordinadores
|
|
39
|
+
- "no editar archivos fuera de may_touch"
|
|
40
|
+
- "no tocar archivos de forbidden"
|
|
41
|
+
- "no aprobar tu propio trabajo"
|
|
42
|
+
on_finish: "idle-join" # al terminar su objetivo entra al pool de ayuda
|
|
43
|
+
escalate_to: coordinator-flow
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Resolución: catálogo primero, factory después
|
|
47
|
+
|
|
48
|
+
El catálogo de agency-agents tiene **306 agentes en 25 divisiones**. La especialidad real de cada uno está en el **cuerpo**, no en el `description` del frontmatter:
|
|
49
|
+
|
|
50
|
+
- **Core Mission** → los subtítulos son la especialidad declarada
|
|
51
|
+
- **Technical Deliverables** → lo que sabe producir
|
|
52
|
+
- **Advanced Capabilities** → lo que maneja mejor
|
|
53
|
+
|
|
54
|
+
El matcher pondera: nombre del rol (40%) · misión literal (35%) · misión conceptual (20%) · cuerpo (5%) · división (0%).
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
1. CATALOG MATCH (preferido)
|
|
58
|
+
Buscar en agency-agents/<division>/*.md el agente cuya especialidad
|
|
59
|
+
declarada en el cuerpo matchea las capacidades pedidas.
|
|
60
|
+
→ source.kind = agency-agents, source_prompt = el prompt original
|
|
61
|
+
|
|
62
|
+
2. FACTORY (si no hay match, o la cobertura es < 60%)
|
|
63
|
+
Se genera desde template: system prompt = rol + ficha + working
|
|
64
|
+
discipline + capabilities + objetivo de la corrida, con la MISMA
|
|
65
|
+
estructura de 10 secciones que agency-agents.
|
|
66
|
+
→ source.kind = factory
|
|
67
|
+
Se registra como AGENT-CANDIDATE para promoverlo a skill después
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Regla de cobertura**: antes de desplegar, se calcula qué fracción del objetivo cubre el **panel** (primario + complementos). Si es < 60%, se crea un rol nuevo con factory en vez de forzar un mal match.
|
|
71
|
+
|
|
72
|
+
Esto evita el "subagente monotono", que es el síntoma exacto del problema original.
|
|
73
|
+
|
|
74
|
+
### El panel de roles
|
|
75
|
+
|
|
76
|
+
Un objetivo casi nunca es de un solo dominio. El panel devuelve:
|
|
77
|
+
|
|
78
|
+
- **primario** — el mejor match global. Se lleva la card.
|
|
79
|
+
- **complementos** — los agentes que cubren lo que el primario NO cubre. Son sus pares en el bus.
|
|
80
|
+
- **sin cubrir** — lo que nadie del panel cubre. Señal de que la card debería partirse en dos.
|
|
81
|
+
|
|
82
|
+
Ejemplo real:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
PIDE: react, form, validation, accessibility, frontend
|
|
86
|
+
|
|
87
|
+
PRIMARIO 28% engineering-section-508-specialist
|
|
88
|
+
cubre: form, accessibility
|
|
89
|
+
+ COMPLEM. 23% engineering-frontend-developer
|
|
90
|
+
aporta: react, frontend
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Los 4 artefactos de un agente desplegado
|
|
94
|
+
|
|
95
|
+
Un agente **no arranca sin `misiones.md`**. Nunca.
|
|
96
|
+
|
|
97
|
+
| Artefacto | Contenido |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `ficha.md` | Quién sos: rol, especialidad, origen, vibe |
|
|
100
|
+
| `misiones.md` | Tu misión · lo que ya sabés · lo que NO sabés |
|
|
101
|
+
| `protocolo.md` | Cómo trabajás · tu territorio · qué se espera · límites · éxito |
|
|
102
|
+
| `bus.md` | Tu canal: dónde, formato, TTLs, cómo acusar recibo |
|
|
103
|
+
|
|
104
|
+
Y `briefing.md` con el documento completo, para lectura humana.
|
|
105
|
+
|
|
106
|
+
### El briefing: orden y mínimo
|
|
107
|
+
|
|
108
|
+
Cada agente recibe un briefing de **10 secciones en orden fijo**. El orden va de lo más estable a lo más cambiante: un agente lee de arriba hacia abajo y siempre tiene lo que necesita para el paso siguiente.
|
|
109
|
+
|
|
110
|
+
| # | Sección | Qué le dice |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| 1 | Quién sos | Rol, especialidad de origen, vibe |
|
|
113
|
+
| 2 | Tu misión | Card, criterios de aceptación numerados, gate |
|
|
114
|
+
| 3 | Lo que ya sabés | Decisiones, restricciones, piezas, trampas |
|
|
115
|
+
| 4 | Lo que NO sabés | Huecos explícitos y a quién preguntarle |
|
|
116
|
+
| 5 | Tu territorio | may_touch / forbidden / prohibiciones de la ficha |
|
|
117
|
+
| 6 | Cómo trabajás | Ciclo, cuándo pedir ayuda, delegar, escalar |
|
|
118
|
+
| 7 | Tu canal | Bus: dónde, formato, TTL, ack |
|
|
119
|
+
| 8 | Qué se espera de vos | handoff.md y evidence.md |
|
|
120
|
+
| 9 | Lo que no podés hacer | Prohibiciones (están en código) |
|
|
121
|
+
| 10 | Cómo se mide que terminaste | Las tres condiciones de terminado |
|
|
122
|
+
|
|
123
|
+
**Mínimo**: las 10 secciones son requeridas. Si alguna queda vacía, el briefing se marca `INCOMPLETO` y el agente no arranca. Un briefing a medio llenar produce un agente a medio informado, que es peor que no tener agente.
|
|
124
|
+
|
|
125
|
+
**La sección 3 se alimenta del ledger** (`.oficina/context.md`). Es lo que evita repetir lo ya decidido.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## El bus
|
|
130
|
+
|
|
131
|
+
### Layout
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
.oficina/
|
|
135
|
+
├── bus/
|
|
136
|
+
│ ├── coord-command/0001-<msgid>.json # canal del Comandante
|
|
137
|
+
│ ├── coord-arch/0001-<msgid>.json # canal del Arquitecto
|
|
138
|
+
│ ├── coord-flow/0001-<msgid>.json # canal del Integrador
|
|
139
|
+
│ └── peer/0001-<msgid>.json # canal general entre pares
|
|
140
|
+
├── inbox/<agentId>/ # índice de lo que le llegó
|
|
141
|
+
├── roster.json # quién existe, qué puede, qué hace
|
|
142
|
+
├── leases/<taskId>.json # lock con TTL
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Envelope
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"id": "m-0042",
|
|
150
|
+
"seq": 42,
|
|
151
|
+
"from": "e-frontend",
|
|
152
|
+
"to": ["e-architect"],
|
|
153
|
+
"kind": "HELP_REQUEST",
|
|
154
|
+
"subject": "T-03",
|
|
155
|
+
"body": {
|
|
156
|
+
"need": "convertir este componente a streaming SSR",
|
|
157
|
+
"why_me": "tengo el contexto del componente pero no el del server"
|
|
158
|
+
},
|
|
159
|
+
"reply_to": null,
|
|
160
|
+
"in_reply_to": null,
|
|
161
|
+
"ttl_sec": 900,
|
|
162
|
+
"created_at": "2026-10-21T15:04:00Z",
|
|
163
|
+
"priority": "normal",
|
|
164
|
+
"idempotency_key": "T-03-help-request-1",
|
|
165
|
+
"hops": 0
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Semántica
|
|
170
|
+
|
|
171
|
+
- **Append-only**: los mensajes nunca se borran, se archivan. `seq` es monótono por canal.
|
|
172
|
+
- **Escritura atómica**: write a `.tmp` + `rename` → nunca hay un JSON partido en el read.
|
|
173
|
+
- **Polling, no push**: cada agente hace `glob` de su inbox cada N turnos.
|
|
174
|
+
- **Ack**: el receptor escribe `inbox/<agentId>/<msgid>.ack`. El emisor ve quién leyó.
|
|
175
|
+
- **TTL**: mensaje expirado = invisible para nuevos lectores, sigue en el log para auditoría.
|
|
176
|
+
- **Idempotency key**: un `HELP_REQUEST` con la misma key no se duplica.
|
|
177
|
+
- **Canal `peer` broadcast**: un `HELP_REQUEST` puede ir a `to: ["*"]` para broadcast de "necesito X" al pool, y el primero que puede toma el lease.
|
|
178
|
+
|
|
179
|
+
### Tipos de mensaje
|
|
180
|
+
|
|
181
|
+
| Kind | Quién lo emite | Para qué |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `HELP_REQUEST` | cualquier agente | pedir ayuda a un peer o al pool |
|
|
184
|
+
| `HELP_ACCEPT` / `HELP_DECLINE` / `HELP_PARTIAL` / `HELP_QUESTION` | el que recibe | triage del pedido |
|
|
185
|
+
| `QUESTION` / `ANSWER` | agente ↔ coordinador | consultas de dominio |
|
|
186
|
+
| `SPAWN_REQUEST` / `SPAWN_APPROVED` / `SPAWN_REJECTED` | agente ↔ Integrador | gestión de spawns |
|
|
187
|
+
| `CLAIM` / `RELEASE` | agente ↔ Integrador | estado de trabajo |
|
|
188
|
+
| `STATUS` / `NOTICE` | cualquiera | información general |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## El protocolo de ayuda
|
|
193
|
+
|
|
194
|
+
### Cuándo se pide ayuda
|
|
195
|
+
|
|
196
|
+
Un especialista pide ayuda cuando, **en este orden**:
|
|
197
|
+
|
|
198
|
+
1. La tarea es de otro dominio → pide al peer de ese dominio
|
|
199
|
+
2. La tarea es de otro dominio y **ese peer no está desplegado** → **despliega uno**
|
|
200
|
+
3. La tarea está en su scope pero ve que otro lo haría mejor → pide
|
|
201
|
+
4. Está bloqueado por una dependencia → pide al dueño del lease
|
|
202
|
+
|
|
203
|
+
### El que recibe evalúa (no acepta por cortesía)
|
|
204
|
+
|
|
205
|
+
Al recibir un `HELP_REQUEST`, el receptor **hace su propio triage**:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
1. ¿Entiendo la tarea? → si no: HELP_DECLINE + explicar
|
|
209
|
+
2. ¿Está en mi scope/ficha? → si no: HELP_DECLINE + sugerir a quién
|
|
210
|
+
3. ¿Puedo hacerlo YA sin romper mi
|
|
211
|
+
propia card en curso? → si no: HELP_DECLINE + "cuando termine"
|
|
212
|
+
4. ¿Tengo el contexto necesario? → si no: HELP_QUESTION (pregunta de vuelta)
|
|
213
|
+
5. ¿Vale la pena? (costo vs. valor) → si no: HELP_DECLINE + alternativa
|
|
214
|
+
6. ¿Puedo con mi presupuesto? → si no: HELP_DECLINE + "sin presupuesto"
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
| Respuesta | Cuándo | Payload |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| `HELP_ACCEPT` | Puede y corresponde | `scope`, `deadline`, `needs_from_me` |
|
|
220
|
+
| `HELP_DECLINE` | No puede o no le corresponde | `reason`, `alternative`, `available_at` |
|
|
221
|
+
| `HELP_PARTIAL` | Puede una parte | `can_do`, `cannot_do`, `split_suggestion` |
|
|
222
|
+
| `HELP_QUESTION` | No entiende o falta info | `question` — ida y vuelta máximo 2 turnos |
|
|
223
|
+
|
|
224
|
+
**Anti-deadlock**: una cadena de ayuda (A pide a B, B pide a C) tiene **máximo 2 saltos**. Si se excede, el pedido escala al coordinador de flujo.
|
|
225
|
+
|
|
226
|
+
**Anti-monotonía en la ayuda**: la rotación de revisor se extiende a la ayuda. Si el mismo par (A↔B) ya se ayudaron 2 veces, el tercer pedido se enruta a un tercero.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Política de spawn
|
|
231
|
+
|
|
232
|
+
Un especialista puede desplegar un subagente nuevo **solo si**:
|
|
233
|
+
|
|
234
|
+
1. El rol buscado **no está en el roster activo** (no duplicar)
|
|
235
|
+
2. El rol sale de `agency-agents` por catálogo, o de factory con cobertura ≥ 60%
|
|
236
|
+
3. El presupuesto global lo permite (`max_concurrent`, `max_spawns_total`)
|
|
237
|
+
4. **El deployment es visible en el roster y en el board** — no es invisible
|
|
238
|
+
|
|
239
|
+
Y **debe reportarlo** al Integrador:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"kind": "SPAWN_REQUEST",
|
|
244
|
+
"requested_role": "motion-designer",
|
|
245
|
+
"source": {"kind": "agency-agents", "path": "design/motion-designer.md"},
|
|
246
|
+
"reason": "T-03 necesita transiciones que exceden mi scope de CSS",
|
|
247
|
+
"budget": {"spawns_used": 1, "spawns_remaining": 1}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
El Integrador **aprueba o rechaza**: si la capacidad ya la cubre un peer desplegado, rechaza y le indica a quién preguntar. Esto mata el "cada agente se clona su propio equipo".
|
|
252
|
+
|
|
253
|
+
**Coherencia clave**: el subagente-account de un subagente es el **mismo `id` de ficha** (no un clon). Hereda la ficha, el bus, el lease, y aparece en el roster. Cuando termina, vuelve al pool de idle.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## El pool idle
|
|
258
|
+
|
|
259
|
+
### Ciclo de vida de un agente
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
DEPLOYED ──► WORKING ──► (sus cards en DONE) ──► IDLE_AVAILABLE ──► (ayuda) ──► WORKING
|
|
263
|
+
│ │
|
|
264
|
+
└──► BLOCKED (espera input) ──► IDLE_AVAILABLE │
|
|
265
|
+
▼
|
|
266
|
+
RETIRED (fin de corrida)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Registro en el roster
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"id": "e-frontend",
|
|
274
|
+
"state": "idle_available",
|
|
275
|
+
"capabilities": ["react-component-design", "css-architecture"],
|
|
276
|
+
"idle_since": "2026-10-21T15:22:00Z",
|
|
277
|
+
"can_accept": true,
|
|
278
|
+
"lease": null
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Los tres modos de ayudar
|
|
283
|
+
|
|
284
|
+
1. **Atender un `HELP_REQUEST` dirigido** (to: ["e-frontend"]) → triage → accept/decline
|
|
285
|
+
2. **Atender un broadcast** `to: ["*"]` → el primero que acepta **toma el lease** → los demás lo ven ocupado y se retiran
|
|
286
|
+
3. **Claim proactivo**: si hay work packages sin owner en el board y tu capability matchea el mejor → **reclamás**, avisás al Integrador, y lo hacés. No esperás que te lo asignen.
|
|
287
|
+
|
|
288
|
+
El modo 3 es lo que convierte la oficina de "N agentes en paralelo" a "N agentes que se acomodan solos según la carga".
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Leases
|
|
293
|
+
|
|
294
|
+
Un agente que trabaja una card **toma un lease** con TTL. El heartbeat lo extiende. Si el agente muere, el lease expira y la card se reasigna.
|
|
295
|
+
|
|
296
|
+
Sin esto, una card queda trabada por un subagente que murió en silencio.
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
leases/<taskId>.json
|
|
300
|
+
{
|
|
301
|
+
"taskId": "T-03",
|
|
302
|
+
"agentId": "e-frontend",
|
|
303
|
+
"acquiredAt": "...",
|
|
304
|
+
"expiresAt": "...",
|
|
305
|
+
"heartbeatAt": "...",
|
|
306
|
+
"renewals": 12
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**Double-claim**: el lease lo bloquea. Si dos agentes discuten por la misma card, el lease decide — no la discusión.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Deadlock, starvation y agentes muertos
|
|
315
|
+
|
|
316
|
+
| Problema | Señal | Respuesta |
|
|
317
|
+
|---|---|---|
|
|
318
|
+
| **Deadlock** | dos cards se esperan mutuamente | Romper el ciclo: una cede, o partir una. Lo detecta el Integrador. |
|
|
319
|
+
| **Starvation** | una card espera demasiado | Priorizarla o avisar al Comandante. |
|
|
320
|
+
| **Doble claim** | dos agentes en la misma card | El lease lo bloquea. |
|
|
321
|
+
| **Agente muerto** | sin heartbeat en 3× `heartbeat_sec` | Su card se reasigna, su lease expira. |
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Arbitraje de los coordinadores
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
Pregunta de un especialista
|
|
329
|
+
|
|
|
330
|
+
├─ ¿es de scope/objetivo/prioridad? → COMANDANTE
|
|
331
|
+
├─ ¿es de diseño/patrón/contrato? → ARQUITECTO
|
|
332
|
+
├─ ¿es de flujo/lease/gate/calidad? → INTEGRADOR
|
|
333
|
+
|
|
|
334
|
+
└─ ¿cruza dos dominios?
|
|
335
|
+
→ ARQUITECTO (decisión técnica) con visto bueno del COMANDANTE
|
|
336
|
+
si tiene impacto de scope; si el Integrador detecta deadlock
|
|
337
|
+
→ INTEGRADOR reparte y avisa.
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Las preguntas al coordinador van por el bus**, no por chat. Un especialista nunca interrumpe al humano para preguntar algo que un coordinador puede resolver.
|
|
341
|
+
|
|
342
|
+
Solo llega al humano si:
|
|
343
|
+
- El coordinador no puede decidir (requiere criterio de producto/negocio)
|
|
344
|
+
- Hay conflicto entre dominios que ningún coordinador resuelve
|
|
345
|
+
- Se agotó el presupuesto o hay un bloqueo externo
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# Protocolo de OFICINA
|
|
2
|
+
|
|
3
|
+
Este documento es la ley de la oficina. Si una regla acá y el criterio propio del agent discrepan, gana el criterio propio del agent — pero debe dejarlo escrito en el artefacto para que el Director pueda corregirlo.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 0. PROJECT_ROOT y OFFICE_DIR
|
|
8
|
+
|
|
9
|
+
Los permisos y las rutas relativas se resuelven contra el **directorio de trabajo de la sesión**, que no siempre es el proyecto. Antes de la primera card:
|
|
10
|
+
|
|
11
|
+
1. Detectá `PROJECT_ROOT` buscando un marker de proyecto (`package.json`, `pyproject.toml`, `setup.py`, `Cargo.toml`, `go.mod`, `pom.xml`, `build.gradle`, `Makefile`, `.git`) en el cwd, y si no hay, en los subdirectorios de primer nivel.
|
|
12
|
+
2. `OFFICE_DIR = PROJECT_ROOT/.oficina/`. Si no se puede crear ahí, usá `~/.oficina/<slug>/`.
|
|
13
|
+
3. **Todos los prompts de delegación llevan el PROJECT_ROOT absoluto.** Decí "todos los paths son relativos a `<PROJECT_ROOT>`" porque los subagentes heredan el cwd de la sesión.
|
|
14
|
+
4. Anotá el PROJECT_ROOT y el OFFICE_DIR en `board.md` para que una sesión retomada los encuentre sin recalcular.
|
|
15
|
+
|
|
16
|
+
### La oficina se vigila a sí misma
|
|
17
|
+
|
|
18
|
+
El Verificador, en la fase 6 (diff review), además de buscar debug statements y archivos fuera de scope, **verifica que solo se haya tocado lo que la card autorizaba**. Si un agente modificó archivos fuera de su scope — incluido el Director tocando código de producción — eso es un finding y la card va a REWORK.
|
|
19
|
+
|
|
20
|
+
La oficina no confía en la disciplina: la audita.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Ciclo de vida de una card
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
TODO ──(deps ok + agente asignado)──> IN_PROGRESS
|
|
28
|
+
│ │
|
|
29
|
+
│ ├──(owner entrega handoff + evidence)──> REVIEW
|
|
30
|
+
│ │ │
|
|
31
|
+
│ │ ┌─────────────────���────┴──────────┐
|
|
32
|
+
│ │ │ Crítico (≠ owner) │
|
|
33
|
+
│ │ │ APROBADO ─────────┐ │
|
|
34
|
+
│ │ │ RECHAZADO ──┐ │ │
|
|
35
|
+
│ │ └──────────────┼─────┘ │
|
|
36
|
+
│ │ │ │
|
|
37
|
+
│ │ Verificador + Guardián
|
|
38
|
+
│ │ │
|
|
39
|
+
│ │ ┌──────────────────┴───────────────┐
|
|
40
|
+
│ │ │ PASS ────────────> DONE │
|
|
41
|
+
│ │ │ FAIL ──> REWORK ─┘ │
|
|
42
|
+
│ │ │
|
|
43
|
+
│ │ Rework 1 → vuelve a IN_PROGRESS (mismo owner, feedback)
|
|
44
|
+
│ │ Rework 2 → REPLAN (Director rehace la card, puede cambiar owner)
|
|
45
|
+
│ │ Rework 3 → ESCALATED (pide decisión al usuario)
|
|
46
|
+
└──────────────────────────────────────────┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. Artefactos: la memoria de la oficina
|
|
52
|
+
|
|
53
|
+
Una card vive en `.oficina/artifacts/<card-id>/`. Si no hay archivo, no hay hecho.
|
|
54
|
+
|
|
55
|
+
### `handoff.md` (lo escribe el owner)
|
|
56
|
+
|
|
57
|
+
```markdown
|
|
58
|
+
# <card-id> — <título>
|
|
59
|
+
|
|
60
|
+
**Owner:** <agente>
|
|
61
|
+
**Estado:** IN_PROGRESS → REVIEW
|
|
62
|
+
**Fecha:** <ISO>
|
|
63
|
+
|
|
64
|
+
## Qué hice
|
|
65
|
+
<qué se implementó, en concreto>
|
|
66
|
+
|
|
67
|
+
## Archivos tocados
|
|
68
|
+
- `path/to/file.ts` — <qué cambió y por qué>
|
|
69
|
+
- `path/to/other.ts` — ...
|
|
70
|
+
|
|
71
|
+
## Decisiones tomadas
|
|
72
|
+
- <decisión> — <rationale>
|
|
73
|
+
|
|
74
|
+
## Qué quedó afuera
|
|
75
|
+
- <qué NO se hizo y por qué>
|
|
76
|
+
|
|
77
|
+
## Qué necesita el siguiente
|
|
78
|
+
- <lo que la siguiente card o el usuario debe saber>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `evidence.md` (lo escribe el owner)
|
|
82
|
+
|
|
83
|
+
Salida **real** de comandos. Sin cromo, sin "debería funcionar":
|
|
84
|
+
|
|
85
|
+
```markdown
|
|
86
|
+
# Evidencia — <card-id>
|
|
87
|
+
|
|
88
|
+
## Comando: `npm test`
|
|
89
|
+
```
|
|
90
|
+
<salida real pegada>
|
|
91
|
+
```
|
|
92
|
+
**Resultado:** PASS (23 tests, 0 fail)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Regla: si no pegaste la salida real, la card no tiene evidencia y va a REWORK.
|
|
96
|
+
|
|
97
|
+
### `review.md` (lo escribe el Crítico)
|
|
98
|
+
|
|
99
|
+
```markdown
|
|
100
|
+
# Revisión — <card-id>
|
|
101
|
+
|
|
102
|
+
**Revisor:** <agente> (≠ owner)
|
|
103
|
+
**Alcance:** <archivos exactos revisados>
|
|
104
|
+
**Ledger:** <qué se leyó completo, callers leídos, paths compartidos trazados>
|
|
105
|
+
|
|
106
|
+
## Veredicto: APROBADO | RECHAZADO
|
|
107
|
+
|
|
108
|
+
## Findings
|
|
109
|
+
|
|
110
|
+
### [C-001] <título>
|
|
111
|
+
- **Confianza:** 92
|
|
112
|
+
- **Ubicación:** `path/file.ts:42`
|
|
113
|
+
- **Categoría:** Security
|
|
114
|
+
- **Descripción:** <qué está mal>
|
|
115
|
+
- **Escenario de reproducción:** <pasos concretos que lo disparan>
|
|
116
|
+
- **Baseline:** <evidencia de que lo introdujo esta card>
|
|
117
|
+
- **Fix:** <dirección>
|
|
118
|
+
- **Test de regresión:** <dónde va>
|
|
119
|
+
|
|
120
|
+
## Dimensiones revisadas
|
|
121
|
+
- security: <sí/no — por qué>
|
|
122
|
+
- performance: <sí/no — por qué>
|
|
123
|
+
- architecture: <sí/no — por qué>
|
|
124
|
+
- testing: <sí/no — por qué>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `gates.md` (lo escriben Verificador y Guardián)
|
|
128
|
+
|
|
129
|
+
```markdown
|
|
130
|
+
# Gates — <card-id>
|
|
131
|
+
|
|
132
|
+
## Técnico
|
|
133
|
+
| # | Fase | Comando | Resultado | Detalle |
|
|
134
|
+
|---|------|---------|-----------|---------|
|
|
135
|
+
| 1 | Build | `npm run build` | PASS | exit 0 |
|
|
136
|
+
| 2 | Type check | `tsc --noEmit` | PASS | 0 errores |
|
|
137
|
+
| 3 | Lint | `eslint .` | FAIL | 3 warnings nuevos |
|
|
138
|
+
| 4 | Tests | `npm test` | PASS | 23/23 |
|
|
139
|
+
| 5 | Sec grep | `grep -rE "AKIA..."` | PASS | 0 |
|
|
140
|
+
| 6 | Diff | `git diff main` | PASS | 4 archivos |
|
|
141
|
+
|
|
142
|
+
**Gate técnico:** FAIL
|
|
143
|
+
|
|
144
|
+
## Seguridad
|
|
145
|
+
- Trust boundaries: <mapeadas>
|
|
146
|
+
- Input paths auditados: <lista>
|
|
147
|
+
- Secretos: <resultado del escaneo>
|
|
148
|
+
- Dependencias: <resultado>
|
|
149
|
+
- Findings de seguridad: <lista o "ninguno">
|
|
150
|
+
|
|
151
|
+
**Gate seguridad:** PASS
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `decision.md` (lo escribe el Director)
|
|
155
|
+
|
|
156
|
+
```markdown
|
|
157
|
+
# Decisión — <card-id>
|
|
158
|
+
|
|
159
|
+
**Decisión:** APROBADA | REWORK | REPLAN | ESCALADA
|
|
160
|
+
**Razón:** <por qué>
|
|
161
|
+
**Siguiente:** <qué card sigue, o qué se le pregunta al usuario>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 3. Handoff: cómo se coordinan sin hablar
|
|
167
|
+
|
|
168
|
+
El problema: los subagentes son stateless. No se ven entre sí.
|
|
169
|
+
|
|
170
|
+
La solución: **el disco es el bus de mensajes**.
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
Implementador ──escribe──> artifacts/T-01/handoff.md
|
|
174
|
+
│
|
|
175
|
+
▼
|
|
176
|
+
Crítico ──lee──────> artifacts/T-01/handoff.md + artifacts/T-00/handoff.md
|
|
177
|
+
│
|
|
178
|
+
▼
|
|
179
|
+
artifacts/T-01/review.md
|
|
180
|
+
│
|
|
181
|
+
▼
|
|
182
|
+
Verificador ──lee──────> artifacts/T-01/{handoff,evidence,review}.md
|
|
183
|
+
│
|
|
184
|
+
▼
|
|
185
|
+
artifacts/T-01/gates.md
|
|
186
|
+
│
|
|
187
|
+
▼
|
|
188
|
+
Director ──lee──────> artifacts/T-01/*.md → actualiza board.md + state.json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Regla de lectura:** antes de empezar una card, el agente lee **todos** los `handoff.md` de cards previas que le importen + `context.md`. No pregunta, lee.
|
|
192
|
+
|
|
193
|
+
**Regla de escritura:** cada agente deja su salida en su archivo. Nunca en el chat.
|
|
194
|
+
|
|
195
|
+
### `.oficina/context.md` — el ledger acumulado
|
|
196
|
+
|
|
197
|
+
```markdown
|
|
198
|
+
# Contexto de la corrida — <objetivo>
|
|
199
|
+
|
|
200
|
+
## Decisiones tomadas
|
|
201
|
+
- [T-00] Usamos JWT con refresh tokens porque <razón> (decidido por code-architect)
|
|
202
|
+
|
|
203
|
+
## Constraints descubiertos
|
|
204
|
+
- [T-01] El proyecto usa Prisma, no SQL directo
|
|
205
|
+
- [T-02] Los tests corren con `npm test` (no `npm run test:unit`)
|
|
206
|
+
|
|
207
|
+
## Piezas construidas
|
|
208
|
+
- [T-01] `src/auth/login.ts` — endpoint de login, retorna access + refresh
|
|
209
|
+
- [T-02] `prisma/schema.prisma` — modelo User con email único
|
|
210
|
+
|
|
211
|
+
## Trampas encontradas
|
|
212
|
+
- [T-01] `src/lib/oldAuth.ts` está muerto, no borrar (lo usa `scripts/legacy-migrate.ts`)
|
|
213
|
+
|
|
214
|
+
## Temas abiertos
|
|
215
|
+
- Falta decidir rate limiting en login
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Este archivo es **lo que evita la repetición**. El Director lo actualiza al cerrar cada card.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 4. Gates de calidad (quality bar)
|
|
223
|
+
|
|
224
|
+
De la skill `verification-loop`. El Verificador corre las 6 fases.
|
|
225
|
+
|
|
226
|
+
**Reglas:**
|
|
227
|
+
- Una fase aplicable en FAIL → la card va a REWORK.
|
|
228
|
+
- Una fase no aplicable → `N/A` con el motivo explícito. Ej: "N/A — el proyecto no tiene type checker".
|
|
229
|
+
- Gate técnico y gate seguridad son **independientes**: security PASS + tech FAIL = REWORK.
|
|
230
|
+
- El Crítico y el Verificador son gates distintos. Reviewer ok + build roto = REWORK. Build ok + review con findings = REWORK.
|
|
231
|
+
|
|
232
|
+
**Umbral de confianza de findings:** ≥ 80. Findings por debajo se descartan con la razón anotada (no se borran en silencio).
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 5. Escalamiento
|
|
237
|
+
|
|
238
|
+
Escalar es caro. No hacerlo por todo. Reglas:
|
|
239
|
+
|
|
240
|
+
| Situación | Acción |
|
|
241
|
+
|---|---|
|
|
242
|
+
| Card rechazada 1 vez | Rework automático. Sin escalar. |
|
|
243
|
+
| Card rechazada 2 veces | **Replan**: el Director rehace la card — puede cambiar owner, scope, approach, o partirla en dos. Sin escalar. |
|
|
244
|
+
| Card rechazada 3 veces | **ESCALAR**: se le presenta al usuario el objetivo de la card, los 3 intentos, por qué falló, y 2-3 opciones concretas con recomendación. |
|
|
245
|
+
| Dependencia externa (API caída, credencial faltante, decisión de producto) | **ESCALAR** inmediatamente. No reintentar. |
|
|
246
|
+
| Objetivo ambiguo con impacto grande en el diseño | **ESCALAR** antes de construir. |
|
|
247
|
+
| Conflicto entre cards (mismo archivo, approaches incompatibles) | **ESCALAR**. |
|
|
248
|
+
| Presupuesto de cards/agentes agotado | **ESCALAR** con informe de lo alcanzado. |
|
|
249
|
+
|
|
250
|
+
**Formato de escalación:**
|
|
251
|
+
|
|
252
|
+
```markdown
|
|
253
|
+
## 🛑 Escalación — <card-id>
|
|
254
|
+
|
|
255
|
+
**Objetivo de la card:** ...
|
|
256
|
+
**Qué intenté:** 3 intentos (resumen de 1 línea cada uno)
|
|
257
|
+
**Por qué no avanza:** <causa raíz>
|
|
258
|
+
**Archivos en juego:** ...
|
|
259
|
+
|
|
260
|
+
**Opción A:** <qué hacer> — <trade-off>
|
|
261
|
+
**Opción B:** <qué hacer> — <trade-off>
|
|
262
|
+
**Opción C:** <qué hacer> — <trade-off>
|
|
263
|
+
|
|
264
|
+
**Mi recomendación:** <Opción X> porque <razón>
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Nunca escalar con un monólogo. Siempre con opciones.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 6. Checkpoints (control pane)
|
|
272
|
+
|
|
273
|
+
Cada 3 cards completadas → informe al usuario:
|
|
274
|
+
|
|
275
|
+
```markdown
|
|
276
|
+
## 🏢 Checkpoint — <n>/<total> cards
|
|
277
|
+
|
|
278
|
+
**Objetivo:** <objetivo original>
|
|
279
|
+
**Progreso:** <n>/<total> cards DONE
|
|
280
|
+
|
|
281
|
+
| Card | Estado | Owner | Gates | Revisión |
|
|
282
|
+
|------|--------|-------|-------|----------|
|
|
283
|
+
| T-00 | DONE | code-explorer | ✅ | APROBADO |
|
|
284
|
+
| T-01 | IN_PROGRESS | general | — | — |
|
|
285
|
+
| T-02 | BLOCKED | general | ❌ | RECHAZADO |
|
|
286
|
+
|
|
287
|
+
**Bloqueos:** <ninguno | lista con owner y acción>
|
|
288
|
+
**Siguiente:** <qué sigue>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
El checkpoint **no pide permiso**. Informa y sigue. Solo frena si hay una escalación.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 7. Informe final de entrega
|
|
296
|
+
|
|
297
|
+
```markdown
|
|
298
|
+
# 🏢 OFICINA — Informe de entrega
|
|
299
|
+
|
|
300
|
+
**Objetivo:** <objetivo original>
|
|
301
|
+
**Fecha:** <ISO>
|
|
302
|
+
**Resultado:** CUMPLIDO | PARCIAL | BLOQUEADO
|
|
303
|
+
|
|
304
|
+
## Trabajo entregado
|
|
305
|
+
| Card | Qué entregó | Owner | Evidencia |
|
|
306
|
+
|------|-------------|-------|-----------|
|
|
307
|
+
| T-00 | Mapa de auth existente | code-explorer | artifacts/T-00/ |
|
|
308
|
+
|
|
309
|
+
## Gates finales
|
|
310
|
+
- Técnico: PASS/FAIL (<fases>)
|
|
311
|
+
- Seguridad: PASS/FAIL (<hallazgos>)
|
|
312
|
+
- Revisiones: <n> APROBADAS, <n> RECHAZADAS
|
|
313
|
+
|
|
314
|
+
## Archivos tocados
|
|
315
|
+
- <lista>
|
|
316
|
+
|
|
317
|
+
## Decisiones clave
|
|
318
|
+
- <decisión> — <razón>
|
|
319
|
+
|
|
320
|
+
## Qué NO se hizo
|
|
321
|
+
- <alcance excluido> — <por qué>
|
|
322
|
+
|
|
323
|
+
## Deuda técnica dejada
|
|
324
|
+
- <deuda> — <por qué se aceptó>
|
|
325
|
+
|
|
326
|
+
## Recomendaciones
|
|
327
|
+
- <qué haría distinto la próxima vez>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 8. Recuperación tras reinicio
|
|
333
|
+
|
|
334
|
+
Si la sesión se corta a mitad de una corrida:
|
|
335
|
+
|
|
336
|
+
1. Leer `.oficina/state.json` → obtener card actual, iteración, contadores
|
|
337
|
+
2. Leer `.oficina/board.md` → ver el panorama
|
|
338
|
+
3. Buscar artifacts de la card en curso: si hay `handoff.md` pero no `review.md` → la card está en REVIEW, delegar al Crítico
|
|
339
|
+
4. Si no hay artifacts → la card está en IN_PROGRESS, reasignar al owner
|
|
340
|
+
5. `context.md` mantiene el conocimiento acumulado
|
|
341
|
+
|
|
342
|
+
**Nunca arranques de cero si existe `.oficina/`.**
|