oficina-opencode 3.0.5 → 3.1.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.
Files changed (145) hide show
  1. package/README.md +240 -208
  2. package/commands/oficina.md +7 -6
  3. package/dashboard/.dom-check.html +1257 -0
  4. package/dashboard/README-DASHBOARD.md +64 -0
  5. package/dashboard/assets/factory.css +1543 -0
  6. package/dashboard/assets/tokens.css +208 -0
  7. package/dashboard/build-kanban-data.mjs +279 -0
  8. package/dashboard/dashboard.css +753 -0
  9. package/dashboard/dashboard.html +121 -0
  10. package/dashboard/dashboard.js +610 -0
  11. package/dashboard/data/snapshot.json +883 -0
  12. package/dashboard/debate.html +134 -0
  13. package/dashboard/factory/build-snapshot.mjs +89 -0
  14. package/dashboard/factory/factory-app.js +1185 -0
  15. package/dashboard/factory/factory-core.js +541 -0
  16. package/dashboard/factory/snapshot.js +850 -0
  17. package/dashboard/factory.html +350 -0
  18. package/dashboard/kanban-data.json +671 -0
  19. package/dashboard/kanban-source.js +643 -0
  20. package/dashboard/kanban.check.mjs +454 -0
  21. package/dashboard/kanban.html +1257 -0
  22. package/dashboard/leadership.html +1008 -0
  23. package/dashboard/lib/debate-app.js +1149 -0
  24. package/dashboard/lib/debate-core.js +698 -0
  25. package/dashboard/lib/debate-data.js +163 -0
  26. package/dashboard/lib/debate-demo.js +110 -0
  27. package/dashboard/lib/debate.css +1155 -0
  28. package/dashboard/lib/leadership-model.js +946 -0
  29. package/dashboard/probe.mjs +35 -0
  30. package/dashboard/scripts/serve.mjs +122 -0
  31. package/dashboard/scripts/snapshot-data.mjs +93 -0
  32. package/dashboard/scripts/snapshot.mjs +28 -0
  33. package/dashboard/server.mjs +242 -0
  34. package/dashboard/shot.mjs +54 -0
  35. package/dashboard/shots/desktop-gates.png +0 -0
  36. package/dashboard/shots/desktop-light.png +0 -0
  37. package/dashboard/shots/desktop-real.png +0 -0
  38. package/dashboard/shots/mobile.png +0 -0
  39. package/dashboard/test/factory-core.test.mjs +244 -0
  40. package/dashboard/tests/debate-core.test.mjs +336 -0
  41. package/dashboard/tests/leadership-model.test.mjs +404 -0
  42. package/dist/cli.js +148 -26
  43. package/dist/cli.js.map +1 -1
  44. package/dist/core/bus.js +3 -3
  45. package/dist/core/bus.js.map +1 -1
  46. package/dist/core/catalog.d.ts +9 -1
  47. package/dist/core/catalog.d.ts.map +1 -1
  48. package/dist/core/catalog.js +77 -2
  49. package/dist/core/catalog.js.map +1 -1
  50. package/dist/core/dashboard.d.ts +33 -0
  51. package/dist/core/dashboard.d.ts.map +1 -0
  52. package/dist/core/dashboard.js +115 -0
  53. package/dist/core/dashboard.js.map +1 -0
  54. package/dist/core/dispatch.d.ts +1 -1
  55. package/dist/core/dispatch.d.ts.map +1 -1
  56. package/dist/core/dispatch.js +84 -12
  57. package/dist/core/dispatch.js.map +1 -1
  58. package/dist/core/docsync.d.ts +0 -9
  59. package/dist/core/docsync.d.ts.map +1 -1
  60. package/dist/core/docsync.js +12 -15
  61. package/dist/core/docsync.js.map +1 -1
  62. package/dist/core/environment.d.ts +28 -0
  63. package/dist/core/environment.d.ts.map +1 -0
  64. package/dist/core/environment.js +80 -0
  65. package/dist/core/environment.js.map +1 -0
  66. package/dist/core/factory.d.ts +16 -0
  67. package/dist/core/factory.d.ts.map +1 -1
  68. package/dist/core/factory.js +143 -0
  69. package/dist/core/factory.js.map +1 -1
  70. package/dist/core/fsx.d.ts +20 -2
  71. package/dist/core/fsx.d.ts.map +1 -1
  72. package/dist/core/fsx.js +10 -6
  73. package/dist/core/fsx.js.map +1 -1
  74. package/dist/core/gates.d.ts +23 -1
  75. package/dist/core/gates.d.ts.map +1 -1
  76. package/dist/core/gates.js +83 -8
  77. package/dist/core/gates.js.map +1 -1
  78. package/dist/core/leases.d.ts.map +1 -1
  79. package/dist/core/leases.js +4 -1
  80. package/dist/core/leases.js.map +1 -1
  81. package/dist/core/mesa.d.ts +1 -6
  82. package/dist/core/mesa.d.ts.map +1 -1
  83. package/dist/core/mesa.js +1 -6
  84. package/dist/core/mesa.js.map +1 -1
  85. package/dist/core/office.d.ts +3 -29
  86. package/dist/core/office.d.ts.map +1 -1
  87. package/dist/core/office.js +77 -90
  88. package/dist/core/office.js.map +1 -1
  89. package/dist/core/paths.d.ts.map +1 -1
  90. package/dist/core/paths.js +6 -1
  91. package/dist/core/paths.js.map +1 -1
  92. package/dist/core/retry.d.ts +2 -8
  93. package/dist/core/retry.d.ts.map +1 -1
  94. package/dist/core/retry.js +3 -34
  95. package/dist/core/retry.js.map +1 -1
  96. package/dist/core/roster.js +3 -3
  97. package/dist/core/roster.js.map +1 -1
  98. package/dist/core/sandbox.d.ts +38 -1
  99. package/dist/core/sandbox.d.ts.map +1 -1
  100. package/dist/core/sandbox.js +119 -15
  101. package/dist/core/sandbox.js.map +1 -1
  102. package/dist/core/state.d.ts +2 -2
  103. package/dist/core/state.d.ts.map +1 -1
  104. package/dist/core/state.js +5 -33
  105. package/dist/core/state.js.map +1 -1
  106. package/dist/core/stores.d.ts +3 -13
  107. package/dist/core/stores.d.ts.map +1 -1
  108. package/dist/core/stores.js +8 -32
  109. package/dist/core/stores.js.map +1 -1
  110. package/dist/core/systemPrompt.d.ts.map +1 -1
  111. package/dist/core/systemPrompt.js +4 -1
  112. package/dist/core/systemPrompt.js.map +1 -1
  113. package/dist/core/tripwire.d.ts +22 -8
  114. package/dist/core/tripwire.d.ts.map +1 -1
  115. package/dist/core/tripwire.js +71 -26
  116. package/dist/core/tripwire.js.map +1 -1
  117. package/dist/core/types.d.ts +6 -14
  118. package/dist/core/types.d.ts.map +1 -1
  119. package/dist/core/types.js +11 -3
  120. package/dist/core/types.js.map +1 -1
  121. package/dist/hooks/scopeGuard.d.ts +15 -1
  122. package/dist/hooks/scopeGuard.d.ts.map +1 -1
  123. package/dist/hooks/scopeGuard.js +88 -73
  124. package/dist/hooks/scopeGuard.js.map +1 -1
  125. package/dist/tools/index.d.ts +9 -1
  126. package/dist/tools/index.d.ts.map +1 -1
  127. package/dist/tools/index.js +29 -81
  128. package/dist/tools/index.js.bak +972 -0
  129. package/dist/tools/index.js.map +1 -1
  130. package/dist/v1.d.ts.map +1 -1
  131. package/dist/v1.js +30 -36
  132. package/dist/v1.js.map +1 -1
  133. package/dist/v2.d.ts +5 -0
  134. package/dist/v2.d.ts.map +1 -1
  135. package/dist/v2.js +79 -46
  136. package/dist/v2.js.bak +517 -0
  137. package/dist/v2.js.map +1 -1
  138. package/docs/GARANTIAS.md +2 -2
  139. package/docs/PRUEBA-E2E.md +4 -4
  140. package/docs/QUE-ES.md +4 -5
  141. package/docs/SEGURIDAD-REPO.md +83 -0
  142. package/docs/assets/banner.svg +42 -0
  143. package/docs/catalog-facts.json +3 -3
  144. package/docs/superpowers/specs/2026-10-29-factory-skill-creator-and-remove-budget.md +182 -0
  145. package/package.json +14 -6
package/README.md CHANGED
@@ -1,44 +1,82 @@
1
1
  <div align="center">
2
2
 
3
- # AI-OFFICE
3
+ <img src="docs/assets/banner.svg" width="1000" alt="AI-OFFICE — una oficina de agentes para OpenCode donde el trabajo no se detiene cuando el modelo dice «terminé»." />
4
4
 
5
- ### Una oficina de agentes para OpenCode donde el trabajo no se detiene cuando el modelo dice «terminé».
5
+ <br>
6
6
 
7
- [![npm](https://img.shields.io/npm/v/oficina-opencode?label=npm&color=cb3837)](https://www.npmjs.com/package/oficina-opencode)
8
- [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/SOCRAMbt/AI-OFFICE/tree/main/LICENSE)
9
- [![node](https://img.shields.io/badge/node-%3E%3D20-339933.svg)](https://nodejs.org)
10
- [![tests](https://img.shields.io/badge/tests-213%20passing-22a06b.svg)](https://github.com/SOCRAMbt/AI-OFFICE/tree/main/tests)
11
- [![coverage](https://img.shields.io/badge/coverage-87.3%25-7c3aed.svg)](https://vitest.dev)
12
- [![types](https://img.shields.io/badge/typescript-strict-3178c6.svg)](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/tsconfig.json)
7
+ <a href="https://www.npmjs.com/package/oficina-opencode"><img src="https://img.shields.io/npm/v/oficina-opencode?style=for-the-badge&labelColor=0d1117&color=cb3837" alt="npm" /></a>
8
+ <a href="https://github.com/SOCRAMbt/AI-OFFICE/blob/main/LICENSE"><img src="https://img.shields.io/badge/licencia-MIT-2ea44f?style=for-the-badge&labelColor=0d1117" alt="licencia MIT" /></a>
9
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D22-339933?style=for-the-badge&labelColor=0d1117" alt="node >= 22" /></a>
10
+ <a href="https://github.com/SOCRAMbt/AI-OFFICE/tree/main/tests"><img src="https://img.shields.io/badge/tests-456%20passing-1f6feb?style=for-the-badge&labelColor=0d1117" alt="456 tests passing" /></a>
11
+ <a href="https://github.com/SOCRAMbt/AI-OFFICE/blob/main/tsconfig.json"><img src="https://img.shields.io/badge/typescript-strict-3178c6?style=for-the-badge&labelColor=0d1117" alt="TypeScript strict" /></a>
12
+
13
+ <br>
14
+
15
+ [![CI](https://github.com/SOCRAMbt/AI-OFFICE/actions/workflows/ci.yml/badge.svg)](https://github.com/SOCRAMbt/AI-OFFICE/actions/workflows/ci.yml)
16
+ [![coverage](https://img.shields.io/badge/cobertura-75%25%20lines-7c3aed?style=flat-square)](https://vitest.dev)
17
+ [![downloads](https://img.shields.io/npm/dw/oficina-opencode?style=flat-square)](https://www.npmjs.com/package/oficina-opencode)
18
+ [![provenance](https://img.shields.io/badge/npm-publish%20con%20provenance-2ea44f?style=flat-square)](docs/SEGURIDAD-REPO.md)
19
+ [![security policy](https://img.shields.io/badge/seguridad-pol%C3%ADtica-2ea44f?style=flat-square)](SECURITY.md)
20
+ [![backwards compat](https://img.shields.io/badge/opencode-v1%20%2B%20v2-232b3e?style=flat-square)](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/COMPATIBILITY.md)
13
21
 
14
22
  **Una malla de coordinadores + peer mesh sobre un roster de roles.**
15
23
 
24
+ </div>
25
+
16
26
  <!-- BEGIN GENERATED: facts -->
17
27
  | | |
18
28
  |---|---|
19
- | **Paquete** | `oficina-opencode` v3.0.5 |
20
- | **Tools** | 16 |
29
+ | **Paquete** | `oficina-opencode` v3.1.1 |
30
+ | **Tools** | 15 |
21
31
  | **Hooks** | 7 |
22
32
  | **API de plugin** | `@opencode/plugin@2.1.2` |
23
- | **Node** | >=20 |
33
+ | **Node** | >=22 |
24
34
  | **Catálogo** | 258 agentes resolubles en 18 divisiones |
25
35
  | **Divisiones** | academic, design, engineering, finance, game-development, gis, healthcare, marketing, paid-media, product, project-management, research, sales, spatial-computing, specialized, strategy, support, testing |
26
- | **Límites** | 20 cards · 40 llamadas · 20 spawns · 10 agentes |
27
- | **Cobertura mínima** | 60% |
36
+ <!-- Catalog source: agency-agents @ 4e3ac3c -->
28
37
  <!-- END GENERATED: facts -->
29
38
 
30
- > **¿Preferís la versión en humano?** → [`docs/QUE-ES.md`](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/QUE-ES.md) — qué es, qué hace y por qué, sin jerga.
31
- > **¿Preferís el porqué técnico?** → [`docs/ARCHITECTURE.md`](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/ARCHITECTURE.md)
39
+ <div align="center">
32
40
 
33
- [Documentación](#arquitectura) · [Garantías](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/GARANTIAS.md) · [Compatibilidad](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/COMPATIBILITY.md) · [Prueba E2E](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/PRUEBA-E2E.md) · [Instalación](#instalación) · [Uso](#uso) · [CLI](#cli-operación)
41
+ | Documentación |
42
+ |:---:|
43
+ | [⚡ Inicio rápido](#inicio-rapido) · [🧩 El problema](#el-problema) · [🏛️ Qué es](#que-es) · [🏗️ Arquitectura](#arquitectura) · [🧠 Matching de roles](#capa-0) |
44
+ | [⚙️ Dispatch](#el-equipo-se-arma) · [🔒 Scope](#el-scope-es-una-restriccion) · [🛡️ Resiliencia](#resiliencia) · [💰 Costo](#costo) · [📚 Briefing](#el-briefing) |
45
+ | [📮 Bus](#el-bus) · [🧷 Hooks](#los-7-hooks) · [🚦 Gates](#los-7-gates) · [⚖️ Invariantes](#invariantes-duros) · [🧰 Tools](#las-16-tools) · [🤖 Agentes](#los-7-agentes) |
46
+ | [📦 Instalación](#instalacion) · [🚀 Uso](#uso) · [🖥️ CLI](#cli-operacion) · [📁 Estructura](#estructura-de-la-corrida) · [🔐 Seguridad](#seguridad) · [🧪 Testing](#testing) · [📄 Licencia](#licencia) |
34
47
 
35
48
  </div>
36
49
 
37
- ---
50
+ > **¿Preferís la versión en humano?** → [`docs/QUE-ES.md`](docs/QUE-ES.md) — qué es, qué hace y por qué, sin jerga.
51
+ > **¿Preferís el porqué técnico?** → [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
52
+ > **¿Querés saber qué promete y qué no?** → [`docs/GARANTIAS.md`](docs/GARANTIAS.md) — nada de hinted marketing: garantías verificadas y límites escritos.
38
53
 
39
- ## El problema
54
+ <a id="inicio-rapido"></a>
55
+ ## ⚡ Inicio rápido
56
+
57
+ Tres comandos y la oficina está corriendo dentro de OpenCode:
58
+
59
+ ```bash
60
+ # 1. Instalar el paquete y desplegar los assets:
61
+ npm install -g oficina-opencode
62
+ oficina install --target ~/.config/opencode
40
63
 
41
- Working con agents tiene cinco fallas concretas. No son falta de inteligencia: son falta de **estructura**.
64
+ # 2. Habilitar la delegación de subagentes en ~/.config/opencode/opencode.json:
65
+ # { "experimental": { "subagent_depth": 3 } }
66
+
67
+ # 3. Abrir OpenCode en tu proyecto y arrancar la oficina:
68
+ # /oficina Agregar autenticación JWT con refresh rotation
69
+ ```
70
+
71
+ La skill se carga, el plugin registra las tools y la corrida arranca sola:
72
+ el Comandante descompone el objetivo en cards, el dispatch crea el equipo de
73
+ especialistas y los arranca en paralelo. Y no se detienen cuando el modelo dice
74
+ «terminé»: se detienen cuando el estado dice que terminó.
75
+
76
+ <a id="el-problema"></a>
77
+ ## 🧩 El problema
78
+
79
+ Trabajar con agentes llega con cinco fallas concretas. No son falta de inteligencia: son falta de **estructura**.
42
80
 
43
81
  | Falla | Qué pasa en la práctica |
44
82
  |---|---|
@@ -48,11 +86,12 @@ Working con agents tiene cinco fallas concretas. No son falta de inteligencia: s
48
86
  | **El contexto se pierde** | Hay que repetir lo que ya se dijo, lo ya se decidió y lo ya se encontró. |
49
87
  | **Los subagentes son islands** | No se piden ayuda, no se coordinan, no se benefician del trabajo del otro. |
50
88
 
51
- Una skill de markdown no arregla ninguna de las cinco. Un sistema con reglas escritas en código, no: porque **no dependen de que el modelo tenga ganas de seguirlas**.
89
+ Una skill de markdown no arregla ninguna de las cinco. Un sistema con reglas
90
+ escritas en código, sí: porque **no dependen de que el modelo tenga ganas de
91
+ seguirlas**.
52
92
 
53
- ---
54
-
55
- ## Qué es
93
+ <a id="que-es"></a>
94
+ ## 🏛️ Qué es
56
95
 
57
96
  **AI-OFFICE** es un plugin de OpenCode que convierte la orquestación en **código ejecutable**, no en instrucciones.
58
97
 
@@ -61,7 +100,7 @@ Skill → "Hacé lo que corresponda" → depende de que el modelo
61
100
  Plugin → máquina de estados + hooks → no depende de la buena voluntad
62
101
  ```
63
102
 
64
- Cuatro piezas lo hacen posible:
103
+ Cinco piezas lo hacen posible:
65
104
 
66
105
  **1. Un hook que no deja que la sesión muera.** Cuando la sesión queda `idle` y quedan cards sin terminar, el motor manda un prompt sintético y la corrida sigue. El modelo **no puede declarar que terminó**: el estado lo dice.
67
106
 
@@ -71,13 +110,10 @@ Cuatro piezas lo hacen posible:
71
110
 
72
111
  **4. Invariantes que el código garantiza.** `gate_required_before_done` y `reviewer_neq_owner` no son recomendaciones: `assertInvariants` lanza y la transición no ocurre.
73
112
 
74
- Y una quinta, que es lo que hace confiable todo lo demás:
75
-
76
- **5. El roster se arma según el objetivo.** Los roles salen del catálogo real de agency-agents (**306 agentes, 25 divisiones**), matcheados contra lo que la card pide. Y cada agente recibe un **briefing** de 10 secciones con lo que sabe, lo que no, y qué tiene que entregar.
77
-
78
- ---
113
+ **5. El roster se arma según el objetivo.** Los roles salen del catálogo real de agency-agents (**258 agentes, 18 divisiones**), matcheados contra lo que la card pide. Cada agente recibe un **briefing** de 10 secciones con lo que sabe, lo que no, y qué tiene que entregar.
79
114
 
80
- ## Arquitectura
115
+ <a id="arquitectura"></a>
116
+ ## 🏗️ Arquitectura
81
117
 
82
118
  Cuatro capas. De abajo hacia arriba.
83
119
 
@@ -109,11 +145,10 @@ Cuatro capas. De abajo hacia arriba.
109
145
  └──────────────────────────────────────────────────────────────────────┘
110
146
  ```
111
147
 
112
- ---
113
-
114
- ## Capa 0 — De dónde sale la especialidad
148
+ <a id="capa-0"></a>
149
+ ## 🧠 Capa 0 — De dónde sale la especialidad
115
150
 
116
- El repo de agency-agents tiene **306 agentes en 25 divisiones**, y cada uno trae un briefing de 10 secciones. La especialidad real **no está en el `description`** del frontmatter: está en el cuerpo.
151
+ El repo de agency-agents tiene **258 agentes en 18 divisiones**, y cada uno trae un briefing de 10 secciones. La especialidad real **no está en el `description`** del frontmatter: está en el cuerpo.
117
152
 
118
153
  Un agente de statistics declara:
119
154
  - `description`: *"Expert in quantitative research methodology..."*
@@ -160,7 +195,7 @@ Reproducible: `npx tsx scripts/demo-roles.ts`
160
195
  Un matching textual puede elegir bien al rol y aun así no ser el mejor para la tarea. La pregunta no es *"¿funciona?"* sino **"¿vale la pena?"**. El benchmark compara tres estrategias sobre 21 tareas con resultados de referencia:
161
196
 
162
197
  | Estrategia | hit@1 | hit@3 | MRR |
163
- |---|---|---|---|
198
+ |---|:---:|:---:|:---:|
164
199
  | **El matcher de la oficina** | **71%** | 86% | **0.778** |
165
200
  | Keywords (los pesos de la v1) | 48% | 81% | 0.623 |
166
201
  | Solo el nombre del rol | 52% | 76% | 0.639 |
@@ -231,9 +266,8 @@ Por eso la factory emite **exactamente la misma estructura**: Identity & Memory,
231
266
 
232
267
  Y lo importante: el prompt fabricado **no es genérico**. Lleva las capabilities concretas, el scope, el objetivo de la corrida, los modos de fallo del rol y el contexto de la card.
233
268
 
234
- ---
235
-
236
- ## El equipo se arma
269
+ <a id="el-equipo-se-arma"></a>
270
+ ## ⚙️ El equipo se arma
237
271
 
238
272
  El flujo completo es: **doy un objetivo → el jefe lo divide → crea los subagentes → arrancan a trabajar juntos**.
239
273
 
@@ -257,9 +291,39 @@ El resultado:
257
291
 
258
292
  Los **pares libres** son los complementos: están en la mesa, pueden recibir un pedido de ayuda, y pueden reclamar una card que quedó sin dueño.
259
293
 
260
- ---
294
+ De aquí la vida de un agente, y el ciclo completo de la corrida:
295
+
296
+ ```mermaid
297
+ flowchart TD
298
+ OBJ["👤 Objetivo"] --> PLAN["📋 PLAN<br/>El Comandante descompone en cards"]
299
+ PLAN --> DEP{"Deps<br/>satisfechas?"}
300
+ DEP -->|sí| STANDUP["🗣️ STANDUP<br/>la card lista para trabajar"]
301
+ DEP -->|no| WAIT["⏸️ Espera / escalación"]
302
+ STANDUP --> WORK["🔧 TRABAJO<br/>owner + scope bloqueado"]
303
+ WORK --> ENT["📦 Entrega<br/>handoff.md + evidence.md"]
304
+ ENT --> REV["🔍 AUTO-REVIEW<br/>crítico ≠ owner"]
305
+ REV -->|rechaza| REWORK["♻️ REWORK"]
306
+ REWORK --> WORK
307
+ REV -->|aprueba| GATES["🚦 GATES<br/>7 fases · exit codes reales"]
308
+ GATES -->|FAIL| REWORK
309
+ GATES -->|PASS| DONE["✅ DONE<br/>evidencia vinculada requisito por requisito"]
310
+ ```
311
+
312
+ ```mermaid
313
+ flowchart LR
314
+ subgraph ciclo["Ciclo de un agente"]
315
+ DEPLOYED --> WORKING
316
+ WORKING -->|terminó| IDLE["IDLE_AVAILABLE"]
317
+ IDLE -->|recibe ayuda| WORKING
318
+ WORKING --> BLOCKED
319
+ BLOCKED --> IDLE
320
+ end
321
+ ```
261
322
 
262
- ## La mesa
323
+ Cuando un agente termina, **ofrece ayuda** para lo que sigue. No queda esperando.
324
+
325
+ <a id="la-mesa"></a>
326
+ ### 🪑 La mesa
263
327
 
264
328
  Los subagentes son procesos aislados: no se ven, no saben quién más trabaja, no saben a quién preguntarle. La **mesa** es la vista compartida — vive en disco, así que todos la leen sin memoria compartida.
265
329
 
@@ -272,17 +336,7 @@ Los subagentes son procesos aislados: no se ven, no saben quién más trabaja, n
272
336
 
273
337
  Y cuando la sesión queda `idle`, el hook de auto-continuación inyecta esa foto en el prompt del agente que arranca. Sin esto, el peer mesh es un bus al que nadie mira.
274
338
 
275
- ### El ciclo de un agente
276
-
277
- ```
278
- DEPLOYED → WORKING → (terminó) → IDLE_AVAILABLE → (ayuda) → WORKING
279
- ↓ ↑
280
- BLOCKED ──→ IDLE_AVAILABLE ──────────────────┘
281
- ```
282
-
283
- Cuando un agente termina, **ofrece ayuda** para lo que sigue. No queda esperando.
284
-
285
- ### Hablar por el bus
339
+ #### Hablar por el bus — el triage del receptor
286
340
 
287
341
  El que recibe un pedido de ayuda **hace su propio triage** — no acepta por cortesía:
288
342
 
@@ -296,9 +350,8 @@ El que recibe un pedido de ayuda **hace su propio triage** — no acepta por cor
296
350
  **Anti-deadlock**: máximo 2 saltos en una cadena de ayuda; después escala al Integrador.
297
351
  **Anti-monotonía**: si el par A↔B ya se ayudaron 2 veces, el tercer pedido va a un tercero.
298
352
 
299
- ---
300
-
301
- ## El scope es una restricción, no una instrucción
353
+ <a id="el-scope-es-una-restriccion"></a>
354
+ ## 🔒 El scope es una restricción, no una instrucción
302
355
 
303
356
  La versión anterior del guard comparaba strings: `"...".match(/^src\//)`. Eso se eludía por tres vías, y las tres eran reales.
304
357
 
@@ -324,11 +377,10 @@ Además:
324
377
  - **Los archivos sensibles** (`.env`, `.npmrc`, `.git/**`, claves privadas) nunca son escribibles, pase lo que pase el scope de la card.
325
378
  - **El tripwire** fotografía el árbol antes de un comando y compara después: detecta lo que el guard no pudo ver.
326
379
 
327
- > Lo que **no** se promete está en [`docs/GARANTIAS.md`](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/GARANTIAS.md). Un shell con interpreter embebido puede escribir afuera; el tripwire lo detecta después, pero no lo revierte. Preferimos decirlo a vender un aislamiento que no existe.
380
+ > **Lo que no se promete** está en [`docs/GARANTIAS.md`](docs/GARANTIAS.md). Un shell con interpreter embebido puede escribir afuera; el tripwire lo detecta después, pero no lo revierte. Preferimos decirlo a vender un aislamiento que no existe.
328
381
 
329
- ---
330
-
331
- ## Una caída no destruye la corrida
382
+ <a id="resiliencia"></a>
383
+ ## 🛡️ Una caída no destruye la corrida
332
384
 
333
385
  La oficina escribe en disco, en varios archivos, sin transacción. Un proceso que muere en el momento equivocado deja el estado incoherente. `reconcile()` compara **estado vs artefactos vs filesystem**:
334
386
 
@@ -341,27 +393,26 @@ La oficina escribe en disco, en varios archivos, sin transacción. Un proceso qu
341
393
  | Lease de una card terminada | Se libera. |
342
394
  | Agente `working` sobre una card `done` | Se libera el agente. |
343
395
  | Card bloqueada por dependencia irresoluble | Se reporta. |
396
+ | `state.json` corrupto | Se pone en cuarentena y se lanza `StateCorruptionError`. **No se crea una corrida vacía encima**: el operador recupera o pide explícitamente una nueva (`oficina init --force-new-run`). |
344
397
 
345
- Lo que **no** hace: completar cards ni inventar evidencia. Un sistema que se auto-repara inventando pruebas es peor que uno que dice *"esto está roto"*.
398
+ Lo que **no** hace: completar cards, inventar evidencia ni auto-repararse pisando datos. Un sistema que se auto-repara inventando pruebas es peor que uno que dice *"esto está roto"*.
346
399
 
347
- ---
348
-
349
- ## El costo se controla, no solo la cantidad de agentes
400
+ <a id="costo"></a>
401
+ ## 💰 El costo se controla, no solo la cantidad de agentes
350
402
 
351
403
  Limitar el número de agentes **no** es controlar el costo: un agente que relee el mismo contexto veinte veces cuesta diez veces lo que uno que lo hace bien.
352
404
 
353
405
  - El costo real viene del motor (`AssistantMessage.cost`), no se estima.
354
406
  - Se contabiliza por **card**, por **agente** y por **fase** (implementación, review, reparación).
355
407
  - Presupuestos por corrida, por card y por fase.
356
- - Estimación previa a una corrida grande.
408
+ - Estimación previa a una corrida grande (`oficina_cost` en modo `estimate`).
357
409
  - Detección de **trabajo redundante**: releer el mismo path, repetir el mismo comando.
358
410
  - **Freno de emergencia**: `oficina_stop` detiene la corrida; el auto-continuación y el despacho se niegan mientras esté detenida.
359
411
 
360
- ---
361
-
362
- ## Capa 1 — El briefing: qué sabe cada agente antes de arrancar
412
+ <a id="el-briefing"></a>
413
+ ## 📚 El briefing: qué sabe cada agente antes de arrancar
363
414
 
364
- Un subagente arranca con el contexto vacío. Si el Commander le pasa un prompt suelta (*"implementá el login"*), el agente tiene que adivinar: qué sabe del proyecto, qué espera de él, qué puede tocar, a quién preguntarle si se traba, y qué tiene que entregar. Adivinar es la fuente de todo el trabajo que se repite y de los agentes que se salen del scope.
415
+ Un subagente arranca con el contexto vacío. Si el Commander le pasa un prompt suelto (*"implementá el login"*), el agente tiene que adivinar: qué sabe del proyecto, qué espera de él, qué puede tocar, a quién preguntarle si se traba, y qué tiene que entregar. Adivinar es la fuente de todo el trabajo que se repite y de los agentes que se salen del scope.
365
416
 
366
417
  El briefing es el contrato de información. Tiene **orden fijo** y **mínimo verificable**.
367
418
 
@@ -370,7 +421,7 @@ El briefing es el contrato de información. Tiene **orden fijo** y **mínimo ver
370
421
  La información llega 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.
371
422
 
372
423
  | # | Sección | Qué le dice |
373
- |---|---|---|
424
+ |:-:|---|---|
374
425
  | 1 | **Quién sos** | Rol, especialidad de origen, de dónde viene tu briefing, tu vibe |
375
426
  | 2 | **Tu misión** | La card exacta, los criterios de aceptación numerados, el control de calidad |
376
427
  | 3 | **Lo que ya sabés** | Decisiones tomadas, restricciones descubiertas, piezas construidas, trampas |
@@ -405,9 +456,8 @@ El briefing se materializa en cuatro archivos en disco, porque un agente no "rec
405
456
 
406
457
  La sección 3 se alimenta del **contexto acumulado** de la corrida (`.oficina/context.md`), que registra decisiones, restricciones, piezas construidas y trampas. Es lo que evita que un agente repita lo que ya se decidió — y lo que convierte al bus en algo más que un tablón de avisos.
407
458
 
408
- ---
409
-
410
- ## Capa 2 — El bus
459
+ <a id="el-bus"></a>
460
+ ## 📮 El bus
411
461
 
412
462
  Los subagentes de OpenCode son **procesos aislados sin memoria compartida**. No pueden hablarse de facto. La solución coherente es un bus append-only sobre disco.
413
463
 
@@ -443,20 +493,8 @@ Los subagentes de OpenCode son **procesos aislados sin memoria compartida**. No
443
493
  | **Broadcast** | `to: ["*"]` al pool | El primero que acepta toma el lease |
444
494
  | **Anti-deadlock** | `hops` máximo 2 | Una cadena de ayuda no se enquista |
445
495
 
446
- ### El que recibe hace su propio triage
447
-
448
- No acepta por cortesía. Seis preguntas, en orden:
449
-
450
- 1. ¿Entiendo la tarea? → si no, `HELP_DECLINE` + explicar
451
- 2. ¿Está en mi scope? → si no, `HELP_DECLINE` + sugerir a quién
452
- 3. ¿Puedo hacerlo **ya** sin romper mi card? → si no, `HELP_DECLINE` + «cuando termine»
453
- 4. ¿Tengo el contexto? → si no, `HELP_QUESTION` por el bus
454
- 5. ¿Vale la pena? → si no, `HELP_DECLINE` + alternativa
455
- 6. ¿Tengo presupuesto? → si no, `HELP_DECLINE` + «sin presupuesto»
456
-
457
- ---
458
-
459
- ## Los 7 hooks
496
+ <a id="los-7-hooks"></a>
497
+ ## 🧷 Los 7 hooks
460
498
 
461
499
  Acá es donde el protocolo deja de ser una intención.
462
500
 
@@ -473,14 +511,13 @@ Acá es donde el protocolo deja de ser una intención.
473
511
 
474
512
  El hook de auto-continuación tiene guardas duras: solo reanuda si queda trabajo real, tope de 25 auto-reanudaciones por sesión, cooldown de 1,5 s entre reanudaciones, y nunca reanuda una corrida `paused`, `blocked` o `delivered`.
475
513
 
476
- ---
477
-
478
- ## Los 7 gates
514
+ <a id="los-7-gates"></a>
515
+ ## 🚦 Los 7 gates
479
516
 
480
517
  Se ejecutan de verdad; el motor interpreta los exit codes.
481
518
 
482
519
  | # | Fase | Node | Python | Go | Rust |
483
- |---|---|---|---|---|---|
520
+ |:-:|---|---|---|---|---|
484
521
  | 1 | Build | `npm run build` | `python -m build` | `go build ./...` | `cargo build` |
485
522
  | 2 | Type check | `npm run typecheck` | `mypy` | `go vet ./...` | `cargo check` |
486
523
  | 3 | Lint | `npm run lint` | `ruff` | `golangci-lint` | `cargo clippy` |
@@ -494,11 +531,11 @@ Se ejecutan de verdad; el motor interpreta los exit codes.
494
531
  Los seis gates técnicos dicen que **el código está sano**. Dicen nada de si el **objetivo del usuario está cumplido**: un proyecto puede compilar, pasar todos sus tests y tener la autenticación mal porque ningún test contemplaba ese escenario.
495
532
 
496
533
  | Nivel | Qué comprueba |
497
- |---|---|
498
- | 1 — Estático | Tipos, lint, secretos, dependencias |
499
- | 2 — Comportamiento | Unitarias, integración, regresión, casos límite |
500
- | 3 — Requisitos | Cada criterio de aceptación enlazado a su evidencia |
501
- | 4 — Auditoría | Revisión independiente, riesgos, coherencia de la evidencia |
534
+ |:-:|---|
535
+ | 1 | **Estático** — tipos, lint, secretos, dependencias |
536
+ | 2 | **Comportamiento** — unitarias, integración, regresión, casos límite |
537
+ | 3 | **Requisitos** — cada criterio de aceptación enlazado a su evidencia |
538
+ | 4 | **Auditoría** — revisión independiente, riesgos, coherencia de la evidencia |
502
539
 
503
540
  El gate 7 es el que cierra el círculo: **una card no se cierra si falta evidencia de un requisito obligatorio**, aunque los otros seis estén en verde.
504
541
 
@@ -523,9 +560,8 @@ Un artefacto llega a nivel 2; un test, a 2; un gate, a 1 (o 2 si es la fase `tes
523
560
  - Una fase aplicable en `FAIL` = gate `FAIL`. No se promedia
524
561
  - El escaneo de secretos corre **en TypeScript**, no shelling out a `grep` (que no existe en Windows), y **redacta** el valor: un log de gate que imprime la credencial completa la acaba de filtrar
525
562
 
526
- ---
527
-
528
- ## Invariantes duros
563
+ <a id="invariantes-duros"></a>
564
+ ## ⚖️ Invariantes duros
529
565
 
530
566
  Los implementa el código. Un prompt puede pedirle al modelo que sea cuidadoso; el motor no puede romperlos.
531
567
 
@@ -547,9 +583,8 @@ Los implementa el código. Un prompt puede pedirle al modelo que sea cuidadoso;
547
583
  | `max_review_rejections` | 3 → escalación |
548
584
  | `max_cards` · `max_agent_calls` | 20 · 40 por corrida |
549
585
 
550
- ---
551
-
552
- ## Los 3 coordinadores
586
+ <a id="los-3-coordinadores"></a>
587
+ ## 🎙️ Los 3 coordinadores
553
588
 
554
589
  Un solo coordinador es cuello de botella y punto único de falla.
555
590
 
@@ -564,9 +599,8 @@ Un solo coordinador es cuello de botella y punto único de falla.
564
599
 
565
600
  Las preguntas de un especialista al coordinador **van por el bus**, no interrumpen al humano. Al usuario solo llega lo que ningún coordinador puede resolver.
566
601
 
567
- ---
568
-
569
- ## Los 16 tools
602
+ <a id="las-16-tools"></a>
603
+ ## 🧰 Las 16 tools
570
604
 
571
605
  Dieciséis, no treinta. Cada tool que se suma es ruido en el contexto de todos los agentes.
572
606
 
@@ -589,23 +623,21 @@ Dieciséis, no treinta. Cada tool que se suma es ruido en el contexto de todos l
589
623
  | `oficina_cost` | Tokens y USD por card, agente y fase, con los presupuestos |
590
624
  | `oficina_stop` | Freno de emergencia: detiene o reanuda la corrida |
591
625
 
592
- ---
593
-
594
- ## Los 7 agentes
626
+ <a id="los-7-agentes"></a>
627
+ ## 🤖 Los 7 agentes
595
628
 
596
629
  | Agente | Rol | Aprueba? |
597
- |---|---|---|
630
+ |---|---|:---:|
598
631
  | `oficina-commander` | Scope, objetivo, prioridades | No revisa técnico |
599
632
  | `oficina-architect` | Decisiones técnicas cross-cutting | No revisa técnico |
600
633
  | `oficina-integrator` | DAG, leases, gates, presupuesto | No revisa técnico |
601
- | `oficina-critico` | Revisión con confianza calibrada (umbral 80) | Sí |
602
- | `oficina-guardian` | Secretos, inyecciones, deps, auth | Sí |
603
- | `oficina-verificador` | Ejecuta build/typecheck/lint/test | Sí |
634
+ | `oficina-critico` | Revisión con confianza calibrada (umbral 80) | **Sí** |
635
+ | `oficina-guardian` | Secretos, inyecciones, deps, auth | **Sí** |
636
+ | `oficina-verificador` | Ejecuta build/typecheck/lint/test | **Sí** |
604
637
  | `oficina-director` | Dirección de la oficina v1 (fallback) | No |
605
638
 
606
- ---
607
-
608
- ## Instalación
639
+ <a id="instalacion"></a>
640
+ ## 📦 Instalación
609
641
 
610
642
  ### Desde npm
611
643
 
@@ -614,15 +646,9 @@ npm install -g oficina-opencode
614
646
  oficina install --target ~/.config/opencode
615
647
  ```
616
648
 
617
- `oficina install` copia la skill, los 7 agentes, el comando `/oficina` y el
618
- **entry point del plugin**.
649
+ `oficina install` copia la skill, los 7 agentes, el comando `/oficina` y el **entry point del plugin**.
619
650
 
620
- > **El entry point no es un import pelado.** El loader de plugins de OpenCode
621
- > v2 no resuelve bare specifiers desde `~/.config/opencode/plugins/`, así que
622
- > un `export { default } from "oficina-opencode"` falla. El entry que instala
623
- > el comando **busca el paquete en runtime** —en la config, en la cache de
624
- > npm de OpenCode, o subiendo desde el propio archivo— y lo importa por ruta
625
- > absoluta. Por eso reinstalar el paquete en otro lugar no rompe nada.
651
+ > **El entry point no es un import pelado.** El loader de plugins de OpenCode v2 no resuelve bare specifiers desde `~/.config/opencode/plugins/`, así que un `export { default } from "oficina-opencode"` falla. El entry que instala el comando **busca el paquete en runtime** —en la config, en la cache de npm de OpenCode, o subiendo desde el propio archivo— y lo importa por ruta absoluta. Por eso reinstalar el paquete en otro lugar no rompe nada.
626
652
 
627
653
  ### Local (desarrollo)
628
654
 
@@ -639,19 +665,16 @@ node /ruta/a/AI-OFFICE/dist/cli.js install --target ~/.config/opencode
639
665
 
640
666
  ### Compatibilidad con la API de plugins
641
667
 
642
- **OpenCode v1 y v2 tienen APIs de plugins incompatibles.** No es que cambiara
643
- una firma: cambió la forma (v1 exporta una función con hooks; v2 exige
644
- `export default { id, setup(ctx) }`).
668
+ **OpenCode v1 y v2 tienen APIs de plugins incompatibles.** No es que cambiara una firma: cambió la forma (v1 exporta una función con hooks; v2 exige `export default { id, setup(ctx) }`).
645
669
 
646
- El motor no depende de ninguna de las dos: define las tools en una forma
647
- neutra y cada entry la traduce.
670
+ El motor no depende de ninguna de las dos: define las tools en una forma neutra y cada entry la traduce.
648
671
 
649
672
  | Entry | Versión de OpenCode |
650
673
  |---|---|
651
674
  | `oficina-opencode` | **v2** — la principal |
652
675
  | `oficina-opencode/v1` | v1 (legacy) |
653
676
 
654
- Detalle completo en [`docs/COMPATIBILITY.md`](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/COMPATIBILITY.md).
677
+ Detalle completo en [`docs/COMPATIBILITY.md`](docs/COMPATIBILITY.md).
655
678
 
656
679
  ### Configuración
657
680
 
@@ -675,9 +698,8 @@ oficina install --target ~/.config/opencode
675
698
 
676
699
  Instala la skill, los 7 agentes, el comando y el entry point del plugin.
677
700
 
678
- ---
679
-
680
- ## Uso
701
+ <a id="uso"></a>
702
+ ## 🚀 Uso
681
703
 
682
704
  Desde la raíz del proyecto:
683
705
 
@@ -685,28 +707,10 @@ Desde la raíz del proyecto:
685
707
  /oficina Agregar autenticación JWT con refresh rotation
686
708
  ```
687
709
 
688
- El comando carga la skill `oficina`, el plugin registra las tools, y la corrida arranca sola.
689
-
690
- El ciclo:
691
-
692
- ```
693
- OBJETIVO → PLAN (Comandante)
694
- ↓
695
- STANDUP — la card con deps satisfechas
696
- ↓
697
- TRABAJO (owner) → handoff.md + evidence.md
698
- ↓
699
- AUTO-REVIEW (oficina-critico, ≠ owner)
700
- ↓
701
- GATES (oficina-verificador + oficina-guardian)
702
- ↓
703
- ┌────────┴────────┐
704
- DONE REWORK → REPLAN → ESCALADA
705
- ```
706
-
707
- ---
710
+ El comando carga la skill `oficina`, el plugin registra las tools, y la corrida arranca sola. La oficina escribe todo en `.oficina/`: si cerrás OpenCode a mitad de camino, al volver, la corrida retoma donde quedó.
708
711
 
709
- ## CLI — operación
712
+ <a id="cli-operacion"></a>
713
+ ## 🖥️ CLI — operación
710
714
 
711
715
  ```bash
712
716
  oficina init [objetivo] # Crea .oficina/ en el proyecto
@@ -720,9 +724,12 @@ oficina catalog --agency-root <path> # Lista el catálogo de roles disponi
720
724
  oficina reset --force # Abandona la corrida
721
725
  ```
722
726
 
723
- ---
727
+ Flags útiles: `--json` (salida máquina), `--directory <path>` (otro proyecto),
728
+ `--agency-root <path>` (catálogo alternativo), `--force-new-run`
729
+ (crear corrida nueva sobre un estado corrupto ya puesto en cuarentena).
724
730
 
725
- ## Estructura de la corrida
731
+ <a id="estructura-de-la-corrida"></a>
732
+ ## 📁 Estructura de la corrida
726
733
 
727
734
  ```
728
735
  <proyecto>/.oficina/
@@ -749,9 +756,81 @@ oficina reset --force # Abandona la corrida
749
756
 
750
757
  El formato de artefactos es compatible con OFICINA v1 (la skill). El plugin **migra el estado v1 automáticamente**: objetivo, cards y resultados se conservan.
751
758
 
759
+ Si `state.json` está corrupto, no se destruye: se **pone en cuarentena** y `loadState` lanza `StateCorruptionError` con la ruta exacta del archivo preservado. Recuperar es una decisión del operador, nunca un efecto colateral.
760
+
761
+ <a id="seguridad"></a>
762
+ ## 🔐 Seguridad
763
+
764
+ La seguridad de la corrida y la seguridad del paquete son dos frentes distintos, y los dos están cubiertos:
765
+
766
+ | Frente | Mecanismo |
767
+ |---|---|
768
+ | **En la corrida** | Scope guard fail-closed (`realpath` + revalidación), archivos sensibles nunca escribibles, tripwire de detección post-comando, herencia de scope en subagentes, y gates con escaneo de secretos que redacta el valor en los logs |
769
+ | **En el estado** | Escrituras atómicas (`O_EXCL`, `.tmp`+`rename`), leases con TTL+heartbeat, locks por corrida contra doble-claim, y corrupción en cuarentena con `StateCorruptionError` — nunca una corrida vacía encima de datos recuperables |
770
+ | **En el repo** | `CODEOWNERS` (todo el árbol requiere review del owner), Dependabot semanal, branch protection documentada y estado `state.json` de soporte → [`docs/SEGURIDAD-REPO.md`](docs/SEGURIDAD-REPO.md) |
771
+ | **En npm** | Publicación por OIDC con **provenance SLSA** (nada de tokens de larga vida), environment `npm` protegido con aprobación manual, 2FA obligatoria — ver [`docs/SEGURIDAD-REPO.md`](docs/SEGURIDAD-REPO.md) |
772
+
773
+ **¿Encontraste una vulnerabilidad?** No abras un issue público. Seguí la
774
+ [política de seguridad](SECURITY.md): reporta por GitHub Security Advisories
775
+ pestaña *Security → Report a vulnerability*.
776
+
777
+ <a id="testing"></a>
778
+ ## 🧪 Testing
779
+
780
+ ```bash
781
+ npm test # 456 tests, 27 archivos
782
+ npm run coverage # cobertura por línea
783
+ npm run docs:check # la documentación coincide con el código
784
+ npm run bench:selection # el matcher le gana a las alternativas simples
785
+ npm run typecheck # tsc --noEmit, strict
786
+ npm run build # tsc → dist/
787
+ ```
788
+
789
+ | Suite | Cubre |
790
+ |---|---|
791
+ | `bus.test.ts` | Append-only, ack, TTL, idempotencia, broadcast, prioridad, atomicidad |
792
+ | `state.test.ts` | Transiciones, invariantes duros, selección de card, ciclo de corrida |
793
+ | `gates.test.ts` | Detección de tooling, las 7 fases, N/A con motivo, consolidación, redacción |
794
+ | `router.test.ts` | Enrutado, rotación, reviewer ≠ owner, política de 3 golpes, presupuesto |
795
+ | `leases.test.ts` | Adquisición, heartbeat, expiración, double-claim, reasignación |
796
+ | `catalog.test.ts` | Frontmatter, secciones, extracción, matching ponderado, panel, factory |
797
+ | `catalogReal.test.ts` | El catálogo real de agency-agents: estructuras, matching, división |
798
+ | `briefing.test.ts` | Orden, mínimo, las 10 secciones, los 4 artefactos, INCOMPLETO |
799
+ | `office.test.ts` | Ciclo completo end-to-end, invariantes, artefactos en disco |
800
+ | `scopeGuard.test.ts` | Globs, extracción de path, enforcement de scope, fail-closed |
801
+ | `sandbox.test.ts` | **Adversarial**: symlinks, traversal, absolutos, sensibles, shell |
802
+ | `concurrency.test.ts` | Doble claim simultáneo, seq único, idempotencia atómica |
803
+ | `recovery.test.ts` | Entregas perdidas, invariantes vulnerados, huérfanos, reinicio, corrupción en cuarentena |
804
+ | `evidence.test.ts` | El grafo requisito → evidencia y los 4 niveles |
805
+ | `cost.test.ts` | Ledger, presupuestos por fila, trabajo redundante |
806
+ | `tripwire.test.ts` | Detección de escrituras coladas por shell, arm/check, `prune(maxAge)` real |
807
+ | `docsync.test.ts` | La documentación coincide con el código |
808
+ | `plugin.test.ts` | El plugin contra un host mockeado: tools, hooks, auto-resume |
809
+ | `migration.test.ts` | Migración v1 → v2 sin pérdida de datos |
810
+ | `cli.test.ts` | init, status, board, install, audit, reset, idempotencia, estado corrupto |
811
+ | `dispatch.test.ts` | IDs de agente únicos por card sin colisiones, reintentos idempotentes, slugs largos |
812
+ | `sandbox-83.test.ts` | Aserciones semánticas de canonicalización Windows 8.3 |
813
+
814
+ *(27 archivos en total — los suites críticos están listados arriba, los demás cubren paths, IDs, logging y parsing.)*
815
+
816
+ ## 📊 Dependencias
817
+
818
+ **Dos, y ninguna es del host:**
819
+
820
+ | Paquete | Para qué |
821
+ |---|---|
822
+ | `@opencode/plugin` | Los tipos y el helper del API de plugins **v2**. Es la dependencia real. |
823
+ | `zod` | Los schemas de los argumentos de las tools. Rango semver `^4.6.7` (mínimo 4.6.7 por CVE). |
824
+
825
+ `@opencode-ai/plugin` (v1) es un **peer opcional**: solo lo necesita el entry `oficina-opencode/v1`, para los hosts que todavía corren la API vieja.
826
+
827
+ El motor (`core/`, `tools/`) no importa ninguno de los dos: define las tools en una forma neutra y cada entry las traduce. Por eso el mismo motor corre en v1 y en v2.
828
+
829
+ Vulnerabilidades y overrides reportados en el [informe de cierre](REMEDIATION_CLOSURE_REPORT.md): **0 en producción** (`npm audit --omit=dev`); las 2 residuales en el árbol dev-only de `@opencode-ai/plugin` quedan documentadas con su justificación técnica.
830
+
752
831
  ---
753
832
 
754
- ## Código
833
+ ## 🗺️ Código
755
834
 
756
835
  33 archivos TypeScript · 19 módulos en `core/` · 1 toolset · 7 hooks.
757
836
 
@@ -794,64 +873,17 @@ src/
794
873
  └── compacting.ts
795
874
  ```
796
875
 
797
- ### Dependencias
798
-
799
- **Dos, y ninguna es del host:**
800
-
801
- | Paquete | Para qué |
802
- |---|---|
803
- | `@opencode/plugin` | Los tipos y el helper del API de plugins **v2**. Es la dependencia real. |
804
- | `zod` | Los schemas de los argumentos de las tools. Versión pineada para que los tipos no divergan. |
805
-
806
- `@opencode-ai/plugin` (v1) es un **peer opcional**: solo lo necesita el entry
807
- `oficina-opencode/v1`, para los hosts que todavía corren la API vieja.
808
-
809
- El motor (`core/`, `tools/`) no importa ninguno de los dos: define las tools
810
- en una forma neutra y cada entry las traduce. Por eso el mismo motor corre
811
- en v1 y en v2.
812
-
813
- ---
814
-
815
- ## Testing
816
-
817
- ```bash
818
- npm test # 385 tests, 21 archivos
819
- npm run coverage # cobertura por línea
820
- npm run docs:check # la documentación coincide con el código
821
- npm run bench:selection # el matcher le gana a las alternativas simples
822
- npm run typecheck # tsc --noEmit, strict
823
- npm run build # tsc → dist/
824
- ```
876
+ <a id="licencia"></a>
877
+ ## 📄 Licencia
825
878
 
826
- | Suite | Cubre |
827
- |---|---|
828
- | `bus.test.ts` | Append-only, ack, TTL, idempotencia, broadcast, prioridad, atomicidad |
829
- | `state.test.ts` | Transiciones, invariantes duros, selección de card, ciclo de corrida |
830
- | `gates.test.ts` | Detección de tooling, las 7 fases, N/A con motivo, consolidación, redacción |
831
- | `router.test.ts` | Enrutado, rotación, reviewer ≠ owner, política de 3 golpes, presupuesto |
832
- | `leases.test.ts` | Adquisición, heartbeat, expiración, double-claim, reasignación |
833
- | `catalog.test.ts` | Frontmatter, secciones, extracción, matching ponderado, panel, factory |
834
- | `catalogReal.test.ts` | El catálogo real de agency-agents: estructuras, matching, división |
835
- | `briefing.test.ts` | Orden, mínimo, las 10 secciones, los 4 artefactos, INCOMPLETO |
836
- | `office.test.ts` | Ciclo completo end-to-end, invariantes, artefactos en disco |
837
- | `scopeGuard.test.ts` | Globs, extracción de path, enforcement de scope |
838
- | `sandbox.test.ts` | **Adversarial**: symlinks, traversal, absolutos, sensibles, shell |
839
- | `concurrency.test.ts` | Doble claim simultáneo, seq único, idempotencia atómica |
840
- | `recovery.test.ts` | Entregas perdidas, invariantes vulnerados, huérfanos, reinicio |
841
- | `evidence.test.ts` | El grafo requisito → evidencia y los 4 niveles |
842
- | `cost.test.ts` | Ledger, presupuestos por fase, trabajo redundante |
843
- | `tripwire.test.ts` | Detección de escrituras coladas por shell |
844
- | `docsync.test.ts` | La documentación coincide con el código |
845
- | `plugin.test.ts` | El plugin contra un host mockeado: tools, hooks, auto-resume |
846
- | `migration.test.ts` | Migración v1 → v2 sin pérdida de datos |
847
- | `cli.test.ts` | init, status, board, install, audit, reset, idempotencia |
879
+ MIT — ver [LICENSE](LICENSE).
848
880
 
849
881
  ---
850
882
 
851
- ## Licencia
883
+ <div align="center">
852
884
 
853
- MIT — ver [LICENSE](https://github.com/SOCRAMbt/AI-OFFICE/tree/main/LICENSE).
885
+ **El diseño se apoya en los patrones de `durable-execution` y `workflow-engine-design`: estado durable antes de los efectos, leases con TTL, transiciones nombradas explícitamente y at-least-once con reconciliación por idempotency key.**
854
886
 
855
- El diseño se apoya en los patrones de `durable-execution` y `workflow-engine-design`: estado durable antes de los efectos, leases con TTL, transiciones nombradas explícitamente y at-least-once con reconciliación por idempotency key.
887
+ Lo que **no** se promete está escrito en [`docs/GARANTIAS.md`](docs/GARANTIAS.md): un plugin de aplicación no es un sandbox, y un README que vende más de lo que el código hace es peor que uno modesto.
856
888
 
857
- Lo que **no** se promete está escrito en [`docs/GARANTIAS.md`](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/GARANTIAS.md): un plugin de aplicación no es un sandbox, y un README que vende más de lo que el código hace es peor que uno modesto.
889
+ </div>