oficina-opencode 2.0.1 → 2.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 (92) hide show
  1. package/README.md +164 -14
  2. package/commands/oficina.md +22 -9
  3. package/dist/core/atomic.d.ts +85 -0
  4. package/dist/core/atomic.d.ts.map +1 -0
  5. package/dist/core/atomic.js +186 -0
  6. package/dist/core/atomic.js.map +1 -0
  7. package/dist/core/bus.d.ts +76 -56
  8. package/dist/core/bus.d.ts.map +1 -1
  9. package/dist/core/bus.js +141 -100
  10. package/dist/core/bus.js.map +1 -1
  11. package/dist/core/catalog.d.ts +27 -0
  12. package/dist/core/catalog.d.ts.map +1 -1
  13. package/dist/core/catalog.js +41 -6
  14. package/dist/core/catalog.js.map +1 -1
  15. package/dist/core/cost.d.ts +275 -0
  16. package/dist/core/cost.d.ts.map +1 -0
  17. package/dist/core/cost.js +724 -0
  18. package/dist/core/cost.js.map +1 -0
  19. package/dist/core/docsync.d.ts +124 -0
  20. package/dist/core/docsync.d.ts.map +1 -0
  21. package/dist/core/docsync.js +191 -0
  22. package/dist/core/docsync.js.map +1 -0
  23. package/dist/core/evidence.d.ts +314 -0
  24. package/dist/core/evidence.d.ts.map +1 -0
  25. package/dist/core/evidence.js +602 -0
  26. package/dist/core/evidence.js.map +1 -0
  27. package/dist/core/fsx.d.ts.map +1 -1
  28. package/dist/core/fsx.js +7 -1
  29. package/dist/core/fsx.js.map +1 -1
  30. package/dist/core/gates.d.ts +9 -2
  31. package/dist/core/gates.d.ts.map +1 -1
  32. package/dist/core/gates.js +19 -3
  33. package/dist/core/gates.js.map +1 -1
  34. package/dist/core/ids.d.ts +48 -0
  35. package/dist/core/ids.d.ts.map +1 -0
  36. package/dist/core/ids.js +87 -0
  37. package/dist/core/ids.js.map +1 -0
  38. package/dist/core/leases.d.ts +45 -0
  39. package/dist/core/leases.d.ts.map +1 -1
  40. package/dist/core/leases.js +85 -17
  41. package/dist/core/leases.js.map +1 -1
  42. package/dist/core/office.d.ts +72 -1
  43. package/dist/core/office.d.ts.map +1 -1
  44. package/dist/core/office.js +173 -6
  45. package/dist/core/office.js.map +1 -1
  46. package/dist/core/paths.d.ts +11 -1
  47. package/dist/core/paths.d.ts.map +1 -1
  48. package/dist/core/paths.js +28 -4
  49. package/dist/core/paths.js.map +1 -1
  50. package/dist/core/recovery.d.ts +87 -0
  51. package/dist/core/recovery.d.ts.map +1 -0
  52. package/dist/core/recovery.js +325 -0
  53. package/dist/core/recovery.js.map +1 -0
  54. package/dist/core/sandbox.d.ts +179 -0
  55. package/dist/core/sandbox.d.ts.map +1 -0
  56. package/dist/core/sandbox.js +546 -0
  57. package/dist/core/sandbox.js.map +1 -0
  58. package/dist/core/state.d.ts +7 -0
  59. package/dist/core/state.d.ts.map +1 -1
  60. package/dist/core/state.js +15 -1
  61. package/dist/core/state.js.map +1 -1
  62. package/dist/core/stores.d.ts +65 -0
  63. package/dist/core/stores.d.ts.map +1 -0
  64. package/dist/core/stores.js +115 -0
  65. package/dist/core/stores.js.map +1 -0
  66. package/dist/core/tripwire.d.ts +87 -0
  67. package/dist/core/tripwire.d.ts.map +1 -0
  68. package/dist/core/tripwire.js +203 -0
  69. package/dist/core/tripwire.js.map +1 -0
  70. package/dist/core/types.d.ts +33 -1
  71. package/dist/core/types.d.ts.map +1 -1
  72. package/dist/core/types.js +1 -0
  73. package/dist/core/types.js.map +1 -1
  74. package/dist/hooks/scopeGuard.d.ts +86 -13
  75. package/dist/hooks/scopeGuard.d.ts.map +1 -1
  76. package/dist/hooks/scopeGuard.js +296 -62
  77. package/dist/hooks/scopeGuard.js.map +1 -1
  78. package/dist/index.d.ts +10 -0
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +185 -2
  81. package/dist/index.js.map +1 -1
  82. package/dist/tools/index.d.ts.map +1 -1
  83. package/dist/tools/index.js +158 -0
  84. package/dist/tools/index.js.map +1 -1
  85. package/docs/COMPATIBILITY.md +98 -0
  86. package/docs/GARANTIAS.md +190 -0
  87. package/docs/PLAN-V2.1.md +144 -0
  88. package/docs/QUE-ES.md +15 -0
  89. package/docs/catalog-facts.json +27 -0
  90. package/package.json +6 -2
  91. package/skills/oficina/SKILL.md +191 -69
  92. package/skills/oficina/reference/mesh.md +11 -2
package/README.md CHANGED
@@ -11,12 +11,26 @@
11
11
  [![coverage](https://img.shields.io/badge/coverage-87.3%25-7c3aed.svg)](https://vitest.dev)
12
12
  [![types](https://img.shields.io/badge/typescript-strict-3178c6.svg)](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/tsconfig.json)
13
13
 
14
- **4 hooks · 13 tools · 7 agentes · 6 gates · 6 invariantes duros · 306 roles de catálogo**
14
+ **Una malla de coordinadores + peer mesh sobre un roster de roles.**
15
+
16
+ <!-- BEGIN GENERATED: facts -->
17
+ | | |
18
+ |---|---|
19
+ | **Paquete** | `oficina-opencode` v2.1.1 |
20
+ | **Tools** | 16 |
21
+ | **Hooks** | 7 |
22
+ | **API de plugin** | `@opencode-ai/plugin@1.15.11` |
23
+ | **Node** | >=20 |
24
+ | **Catálogo** | 258 agentes resolubles en 18 divisiones |
25
+ | **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% |
28
+ <!-- END GENERATED: facts -->
15
29
 
16
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.
17
31
  > **¿Preferís el porqué técnico?** → [`docs/ARCHITECTURE.md`](https://github.com/SOCRAMbt/AI-OFFICE/blob/main/docs/ARCHITECTURE.md)
18
32
 
19
- [Documentación](#arquitectura) · [Instalación](#instalación) · [Uso](#uso) · [CLI](#cli-operación)
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) · [Instalación](#instalación) · [Uso](#uso) · [CLI](#cli-operación)
20
34
 
21
35
  </div>
22
36
 
@@ -141,6 +155,24 @@ Resolución real contra el catálogo:
141
155
 
142
156
  Reproducible: `npx tsx scripts/demo-roles.ts`
143
157
 
158
+ ### El benchmark: ¿la complejidad se justifica?
159
+
160
+ 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
+
162
+ | Estrategia | hit@1 | hit@3 | MRR |
163
+ |---|---|---|---|
164
+ | **El matcher de la oficina** | **71%** | 86% | **0.778** |
165
+ | Keywords (los pesos de la v1) | 48% | 81% | 0.623 |
166
+ | Solo el nombre del rol | 52% | 76% | 0.639 |
167
+
168
+ El matcher completo le gana por **23 puntos** de hit@1 a la versión anterior. La complejidad se justifica, y ahora está dicho con números en vez de con adjetivos.
169
+
170
+ También se probó una ponderación por **IDF** (los tokens raros pesan más) y **no se quedó**: bajaba el hit@1 a 67%. El código lo documenta y el benchmark lo demuestra. La complejidad que no se justifica hay que sacarla, no dejarla dormida en el repo.
171
+
172
+ ```bash
173
+ npm run bench:selection
174
+ ```
175
+
144
176
  ### El panel: primario + complementos
145
177
 
146
178
  Un objetivo casi nunca es de un solo dominio. *"Formulario de login en React con validación y accesibilidad"* necesita un frontend (que no sabe de ARIA) **y** un especialista en accesibilidad (que no sabe de React).
@@ -266,6 +298,67 @@ El que recibe un pedido de ayuda **hace su propio triage** — no acepta por cor
266
298
 
267
299
  ---
268
300
 
301
+ ## El scope es una restricción, no una instrucción
302
+
303
+ La versión anterior del guard comparaba strings: `"...".match(/^src\//)`. Eso se eludía por tres vías, y las tres eran reales.
304
+
305
+ ```
306
+ // El bug: la ruta se comparaba sin resolver.
307
+ const relative = path.relative(projectRoot, absolute)
308
+ if (!matchesScope(relative, mayTouch)) throw ...
309
+ ```
310
+
311
+ | Escape | Antes | Ahora |
312
+ |---|---|---|
313
+ | Symlink a fuera del proyecto | `src/link.ts` matcheaba `src/**` | Se resuelve el symlink y **se revalida** la contención |
314
+ | `..` con `mayTouch: ["**"]` | `**` matcheaba cualquier string | `**` significa *el proyecto*, nunca el filesystem |
315
+ | Path absoluto | `relative` empezaba en `..` y con `**` pasaba | La contención se verifica antes de cualquier glob |
316
+
317
+ Ahora la ruta se **resuelve** (absoluta → normalizada → symlinks resueltos → `realpath`) y recién ahí se decide, con `fail-closed`.
318
+
319
+ Además:
320
+
321
+ - **Todas las tools que escriben**, no solo `edit`/`write`. Los patches multi-archivo se chequean **todos**, no solo el primero.
322
+ - **Los comandos de shell** se analizan antes de ejecutarse: redirecciones, `cp`, `mv`, `rm`, `sed -i`, `dd`, `curl -o`, `git checkout`, interpreters embebidos.
323
+ - **Los subagentes heredan el scope** por la cadena de padres de sesión. Delegar no es un escalón para salirse.
324
+ - **Los archivos sensibles** (`.env`, `.npmrc`, `.git/**`, claves privadas) nunca son escribibles, pase lo que pase el scope de la card.
325
+ - **El tripwire** fotografía el árbol antes de un comando y compara después: detecta lo que el guard no pudo ver.
326
+
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.
328
+
329
+ ---
330
+
331
+ ## Una caída no destruye la corrida
332
+
333
+ 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
+
335
+ | Situación | Qué hace |
336
+ |---|---|
337
+ | Card en `review` sin `handoff.md` | El agente murió entregando. Se avisa. |
338
+ | Card en `done` sin `gates.md` | Se cerró sin gate. **Crítico.** |
339
+ | Card en progreso sin owner | Nadie la va a terminar. **Crítico.** |
340
+ | Lease de una card que no existe | Se libera. |
341
+ | Lease de una card terminada | Se libera. |
342
+ | Agente `working` sobre una card `done` | Se libera el agente. |
343
+ | Card bloqueada por dependencia irresoluble | Se reporta. |
344
+
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"*.
346
+
347
+ ---
348
+
349
+ ## El costo se controla, no solo la cantidad de agentes
350
+
351
+ 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
+
353
+ - El costo real viene del motor (`AssistantMessage.cost`), no se estima.
354
+ - Se contabiliza por **card**, por **agente** y por **fase** (implementación, review, reparación).
355
+ - Presupuestos por corrida, por card y por fase.
356
+ - Estimación previa a una corrida grande.
357
+ - Detección de **trabajo redundante**: releer el mismo path, repetir el mismo comando.
358
+ - **Freno de emergencia**: `oficina_stop` detiene la corrida; el auto-continuación y el despacho se niegan mientras esté detenida.
359
+
360
+ ---
361
+
269
362
  ## Capa 1 — El briefing: qué sabe cada agente antes de arrancar
270
363
 
271
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.
@@ -363,7 +456,7 @@ No acepta por cortesía. Seis preguntas, en orden:
363
456
 
364
457
  ---
365
458
 
366
- ## Los 4 hooks
459
+ ## Los 7 hooks
367
460
 
368
461
  Acá es donde el protocolo deja de ser una intención.
369
462
 
@@ -373,14 +466,18 @@ Acá es donde el protocolo deja de ser una intención.
373
466
  | `tool.execute.before` | Bloquea escrituras fuera del scope de la card | «No toques eso» pasa de instrucción a restricción |
374
467
  | `experimental.chat.system.transform` | Inyecta el estado de la oficina en cada llamada | El agente no repregunta lo que ya sabe ni repite trabajo |
375
468
  | `experimental.session.compacting` | Reinyecta el estado **leído del disco** al compactar | Sin esto, la oficina se olvida a mitad de camino |
469
+ | `tool.execute.after` | Tripwire: fotografía el árbol antes de un comando y compara después | Detecta lo que el guard no pudo ver |
470
+ | `permission.ask` | Segunda capa de enforcement, a nivel del framework | Deniega en el motor, no solo en el hook propio |
471
+ | `event` → `message.updated` | Contabilidad de costo con el número real del motor | El costo no se estima: el motor ya lo sabe |
472
+ | `experimental.compaction.autocontinue` | Freno de emergencia sobre la auto-continuación | Sin esto, una corrida se quema sola en tokens |
376
473
 
377
474
  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`.
378
475
 
379
476
  ---
380
477
 
381
- ## Los 6 gates
478
+ ## Los 7 gates
382
479
 
383
- De la skill `verification-loop`. Se ejecutan de verdad; el motor interpreta los exit codes.
480
+ Se ejecutan de verdad; el motor interpreta los exit codes.
384
481
 
385
482
  | # | Fase | Node | Python | Go | Rust |
386
483
  |---|---|---|---|---|---|
@@ -390,6 +487,33 @@ De la skill `verification-loop`. Se ejecutan de verdad; el motor interpreta los
390
487
  | 4 | Tests | `npm run test` | `pytest` | `go test` | `cargo test` |
391
488
  | 5 | Secretos | escaneo en código | ← | ← | ← |
392
489
  | 6 | Diff review | `git diff --stat` | ← | ← | ← |
490
+ | 7 | Requisitos | cada criterio de aceptación con su evidencia | ← | ← | ← |
491
+
492
+ ### La pirámide de verificación
493
+
494
+ 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
+
496
+ | 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 |
502
+
503
+ 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
+
505
+ ### El grafo requisito → evidencia
506
+
507
+ Cada criterio de aceptación es un requisito. Cada evidencia —un artefacto, un test, un gate, una inspección, una auditoría— se vincula a un requisito con un nivel. El grafo responde *qué falta para poder cerrar*:
508
+
509
+ ```
510
+ T-00 · Implementar login
511
+ [✓] endpoint de login nivel 2 artefacto
512
+ [ ] sin errores de validación nivel 0 — FALTA
513
+ [!] cubierto por revisión independiente nivel 4 auditoría
514
+ ```
515
+
516
+ Un artefacto llega a nivel 2; un test, a 2; un gate, a 1 (o 2 si es la fase `test`); una inspección, a 3; una auditoría —con revisor distinto del owner—, a 4. Una auto-revisión con cara de auditoría se **rechaza**: es peor que no tener auditoría.
393
517
 
394
518
  **Reglas de honestidad** — no negociables:
395
519
 
@@ -405,9 +529,12 @@ De la skill `verification-loop`. Se ejecutan de verdad; el motor interpreta los
405
529
 
406
530
  Los implementa el código. Un prompt puede pedirle al modelo que sea cuidadoso; el motor no puede romperlos.
407
531
 
532
+ > **Ocho invariantes duros**, y se evalúan **donde corresponde**: el de cierre en el cierre, el de presupuesto al gastar, el de ayuda al enrutar. Evaluar un invariante de `DONE` con la card todavía en `review` lo deja vacío — y así estaba antes: el invariante `gate_required_before_done` no disparaba nunca. Ahora se valida contra el estado al que la card llegaría.
533
+
408
534
  | Invariante | Valor |
409
535
  |---|---|
410
536
  | `gate_required_before_done` | `true` — sin gates en `PASS` no hay `DONE` |
537
+ | `evidence_required_before_done` | `true` — sin evidencia de los requisitos obligatorios no hay `DONE`. *Fail-closed*: si la evidencia no se evaluó, no se cierra |
411
538
  | `reviewer_neq_owner` | `true` — no hay auto-aprobación |
412
539
  | `max_concurrent_agents` | 10 base · 14 hard cap |
413
540
  | `max_spawn_depth` | 2 niveles bajo el coordinador |
@@ -439,9 +566,9 @@ Las preguntas de un especialista al coordinador **van por el bus**, no interrump
439
566
 
440
567
  ---
441
568
 
442
- ## Los 13 tools
569
+ ## Los 16 tools
443
570
 
444
- Trece, no treinta. Cada tool que se suma es ruido en el contexto de todos los agentes.
571
+ Dieciséis, no treinta. Cada tool que se suma es ruido en el contexto de todos los agentes.
445
572
 
446
573
  | Tool | Qué hace |
447
574
  |---|---|
@@ -458,6 +585,9 @@ Trece, no treinta. Cada tool que se suma es ruido en el contexto de todos los ag
458
585
  | `oficina_escalate` | Escala al usuario con 2-3 opciones concretas y recomendación |
459
586
  | `oficina_ficha` | Resuelve un rol a ficha y **despliega** el agente con su briefing |
460
587
  | `oficina_brief` | Muestra el briefing y el **panel de roles** sin desplegar: decisión del Comandante |
588
+ | `oficina_evidence` | El grafo de requisitos a evidencia: qué falta para poder cerrar |
589
+ | `oficina_cost` | Tokens y USD por card, agente y fase, con los presupuestos |
590
+ | `oficina_stop` | Freno de emergencia: detiene o reanuda la corrida |
461
591
 
462
592
  ---
463
593
 
@@ -601,7 +731,7 @@ El formato de artefactos es compatible con OFICINA v1 (la skill). El plugin **mi
601
731
 
602
732
  ## Código
603
733
 
604
- 24 archivos TypeScript · 14 módulos en `core/` · 1 toolset · 4 hooks.
734
+ 31 archivos TypeScript · 18 módulos en `core/` · 1 toolset · 7 hooks.
605
735
 
606
736
  ```
607
737
  src/
@@ -617,13 +747,22 @@ src/
617
747
  │ ├── factory.ts # generación de roles con briefing de 10 secciones
618
748
  │ ├── briefing.ts # el contrato de información del agente
619
749
  │ ├── router.ts # enrutado + rotación anti-monotonía
620
- │ ├── gates.ts # runner de los 6 gates + escaneo de secretos
750
+ │ ├── gates.ts # runner de los 7 gates + escaneo de secretos
751
+ │ ├── sandbox.ts # resolucion real de rutas + scope + sensibles
752
+ │ ├── atomic.ts # primitivas atomicas: O_EXCL, locks, seq
753
+ │ ├── evidence.ts # grafo requisito -> evidencia
754
+ │ ├── cost.ts # ledger de tokens y USD
755
+ │ ├── recovery.ts # reconciliacion estado vs artefactos
756
+ │ ├── tripwire.ts # deteccion de escrituras coladas
757
+ │ ├── stores.ts # persistencia de evidencia y costo
758
+ │ ├── docsync.ts # fuente unica de verdad de las cifras
621
759
  │ ├── retry.ts # política de 3 golpes
622
760
  │ ├── office.ts # la fachada que amarra todo
623
761
  │ ├── paths.ts # resolución de project root
624
762
  │ ├── fsx.ts # IO atómico
763
+ │ ├── ids.ts # ids seguros para usar como nombre de archivo
625
764
  │ └── log.ts # logging estructurado
626
- ├── tools/index.ts # las 11 tools
765
+ ├── tools/index.ts # las 16 tools
627
766
  └── hooks/
628
767
  ├── sessionIdle.ts # auto-continuación
629
768
  ├── scopeGuard.ts # enforcement de scope
@@ -640,8 +779,10 @@ Una sola, y es peer del host: `@opencode-ai/plugin`. Zod entra por `tool.schema`
640
779
  ## Testing
641
780
 
642
781
  ```bash
643
- npm test # 213 tests, 14 archivos
644
- npm run coverage # 90.2% stmts · 82.8% branches
782
+ npm test # 385 tests, 21 archivos
783
+ npm run coverage # cobertura por línea
784
+ npm run docs:check # la documentación coincide con el código
785
+ npm run bench:selection # el matcher le gana a las alternativas simples
645
786
  npm run typecheck # tsc --noEmit, strict
646
787
  npm run build # tsc → dist/
647
788
  ```
@@ -650,14 +791,21 @@ npm run build # tsc → dist/
650
791
  |---|---|
651
792
  | `bus.test.ts` | Append-only, ack, TTL, idempotencia, broadcast, prioridad, atomicidad |
652
793
  | `state.test.ts` | Transiciones, invariantes duros, selección de card, ciclo de corrida |
653
- | `gates.test.ts` | Detección de tooling, las 6 fases, N/A con motivo, consolidación, redacción |
794
+ | `gates.test.ts` | Detección de tooling, las 7 fases, N/A con motivo, consolidación, redacción |
654
795
  | `router.test.ts` | Enrutado, rotación, reviewer ≠ owner, política de 3 golpes, presupuesto |
655
796
  | `leases.test.ts` | Adquisición, heartbeat, expiración, double-claim, reasignación |
656
797
  | `catalog.test.ts` | Frontmatter, secciones, extracción, matching ponderado, panel, factory |
657
- | `catalogReal.test.ts` | El catálogo real de 306 agentes: estructuras, matching, división |
798
+ | `catalogReal.test.ts` | El catálogo real de agency-agents: estructuras, matching, división |
658
799
  | `briefing.test.ts` | Orden, mínimo, las 10 secciones, los 4 artefactos, INCOMPLETO |
659
800
  | `office.test.ts` | Ciclo completo end-to-end, invariantes, artefactos en disco |
660
801
  | `scopeGuard.test.ts` | Globs, extracción de path, enforcement de scope |
802
+ | `sandbox.test.ts` | **Adversarial**: symlinks, traversal, absolutos, sensibles, shell |
803
+ | `concurrency.test.ts` | Doble claim simultáneo, seq único, idempotencia atómica |
804
+ | `recovery.test.ts` | Entregas perdidas, invariantes vulnerados, huérfanos, reinicio |
805
+ | `evidence.test.ts` | El grafo requisito → evidencia y los 4 niveles |
806
+ | `cost.test.ts` | Ledger, presupuestos por fase, trabajo redundante |
807
+ | `tripwire.test.ts` | Detección de escrituras coladas por shell |
808
+ | `docsync.test.ts` | La documentación coincide con el código |
661
809
  | `plugin.test.ts` | El plugin contra un host mockeado: tools, hooks, auto-resume |
662
810
  | `migration.test.ts` | Migración v1 → v2 sin pérdida de datos |
663
811
  | `cli.test.ts` | init, status, board, install, audit, reset, idempotencia |
@@ -669,3 +817,5 @@ npm run build # tsc → dist/
669
817
  MIT — ver [LICENSE](https://github.com/SOCRAMbt/AI-OFFICE/tree/main/LICENSE).
670
818
 
671
819
  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.
820
+
821
+ 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.
@@ -1,34 +1,47 @@
1
1
  ---
2
- description: Abrir la Oficina de Agents para trabajar un objetivo de punta a punta con delegation, auto-revisión y gates de calidad
2
+ description: Abrir la Oficina de Agents para trabajar un objetivo de punta a punta con dispatch, auto-revisión, gates de calidad y evidencia por requisito
3
3
  subtask: false
4
4
  ---
5
5
 
6
- Cargá la skill `oficina` y seguí su protocolo exactamente. Si sus instrucciones no están ya en contexto, cargála primero.
7
-
8
- **Vos sos el Director.** Leé el agente `oficina-director` en `~/.config/opencode/agents/oficina-director.md` y adoptá su rol: planificás, delegás, decidís, escalás. **No escribas código de producción vos** — eso lo hacen los especialistas que delegás.
6
+ Cargá la skill `oficina` y seguí su protocolo exactamente. Si sus instrucciones no están ya en contexto, cargala primero.
9
7
 
10
8
  **Objetivo de la corrida:** $ARGUMENTS
11
9
 
12
10
  Si el objetivo está vacío, tomá el pedido más reciente del usuario en esta conversación como objetivo.
13
11
 
12
+ ## Paso 0 — ¿Plugin o fallback?
13
+
14
+ **[plugin]** Si tenés las tools `oficina_*` disponibles, usá el **motor duro**. Es lo que hace que los invariantes dejen de ser sugerencias: el guard de scope rechaza escrituras fuera del scope, el gate corre comandos de verdad, y una card no se cierra sin evidencia.
15
+
16
+ Comprobá si existe `oficina_plan`. Si existe:
17
+
18
+ 1. `oficina_plan` con el objetivo descompuesto en cards (ver abajo).
19
+ 2. `oficina_dispatch` → **arma el equipo**: resuelve los roles en el catálogo de agency-agents, materializa los agentes y los arranca en paralelo.
20
+ 3. `oficina_mesa` (view) → mirá quién trabaja en qué.
21
+ 4. Corré el ciclo con las tools: `oficina_assign` → trabajo → `oficina_submit` → `oficina_review` → `oficina_evidence` → `oficina_gate`.
22
+ 5. `oficina_cost` para el costo; `oficina_stop` si la corrida se descontrola.
23
+
24
+ **Si NO hay plugin**, seguí el flujo de artefactos en markdown de `reference/protocol.md`. Mismo protocolo, sin enforcement.
25
+
14
26
  ## Arrancá así — sin preguntar permiso
15
27
 
16
28
  1. Leé `reference/protocol.md` y `reference/roles.md` de la skill `oficina`.
17
29
  2. **Resolvé PROJECT_ROOT** siguiendo el Paso 0 del protocolo (buscá markers de proyecto; no asumas que el cwd es el proyecto). `OFFICE_DIR = PROJECT_ROOT/.oficina/`. Usá paths ABSOLUTOS en todo lo que escribas y en todos los prompts de delegación.
18
- 3. Creá `OFFICE_DIR/` con `board.md`, `state.json` y `context.md` (templates en `reference/templates.md`).
19
- 4. Descomponé el objetivo en cards con acceptance criteria **verificables** y gate explícito. Si el objetivo es vago, hacé las inferencias razonables y anotá cuáles son — no frenes por ambigüedad menor. Ordená las cards por **causa**: si el fix de B rompe el gate de A, B va primero.
30
+ 3. Descomponé el objetivo en cards con acceptance criteria **verificables** y gate explícito. Si el objetivo es vago, hacé las inferencias razonables y anotá cuáles son — no frenes por ambigüedad menor. Ordená las cards por **causa**: si el fix de B rompe el gate de A, B va primero.
31
+ 4. Cada card necesita: `scope` (qué archivos puede tocar), `acceptance` (criterios verificables) y `gate` (el comando que lo demuestra).
20
32
  5. Delegá por la **tabla de enrutado** de `reference/roles.md`. El Crítico nunca es el owner. El Verificador nunca es el Implementador. Cada subagente es stateless: el prompt lleva TODO el contexto, y los paths absolutos del PROJECT_ROOT.
21
- 6. Corré el ciclo: TRABAJO → AUTO-REVIEW → GATES → DONE, o REWORK/REPLAN/ESCALADA.
22
- 7. Checkpoint al usuario cada 3 cards — **informa y seguí**, no pidas permiso.
33
+ 6. Corré el ciclo: TRABAJO → AUTO-REVIEW → GATES + EVIDENCIA → DONE, o REWORK/REPLAN/ESCALADA.
34
+ 7. Checkpoint al usuario cada 3 cards — **informá y seguí**, no pidás permiso.
23
35
  8. Escalá solo con el formato de 2-3 opciones concretas + recomendación.
24
36
 
25
37
  ## Presupuesto
26
38
 
27
- 20 cards, 40 invocaciones de subagente por corrida. Si se agota, entregá informe parcial con lo alcanzado y preguntá.
39
+ 20 cards, 40 invocaciones de subagente, 20 spawns por corrida. **[plugin]** El costo real (tokens y USD) se ve con `oficina_cost`; si se dispara, `oficina_stop` frena la corrida.
28
40
 
29
41
  ## Lo que esta oficina garantiza
30
42
 
31
43
  - Ninguna card se marca DONE sin `gates.md` en PASS
44
+ - **[plugin]** Ninguna card se marca DONE sin evidencia para cada criterio de aceptación obligatorio
32
45
  - Ningún trabajo se auto-aprueba
33
46
  - Nada se pierde: todo está en `.oficina/artifacts/`
34
47
  - El objetivo se trabaja hasta cumplirlo o hasta un bloqueo real
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Primitivas atómicas sobre disco.
3
+ *
4
+ * ## Por qué existe
5
+ *
6
+ * `writeAtomic` (tmp + rename) garantiza que un lector nunca vea un JSON a
7
+ * medio escribir. No garantiza que **dos escritores no se pisen**. El
8
+ * patron "leo, evaluo, escribo" — un TOCTOU — es la raiz de las carreras
9
+ * que existian:
10
+ *
11
+ * - El lease: dos agentes leian "no hay lease", los dos pasaban el chequeo,
12
+ * los dos escribian, y el segundo gano. El primero creia que tenia la
13
+ * card y el trabajo se hacia dos veces.
14
+ * - El `seq` del bus: dos emisores concurrentes calculaban el mismo
15
+ * `max + 1` y dejaban dos mensajes con el mismo seq.
16
+ * - La idempotencia: el "existe ya?" se hacia antes de escribir, asi que
17
+ * dos envios concurrentes con la misma clave creaban dos mensajes.
18
+ *
19
+ * ## Las tres primitivas
20
+ *
21
+ * - `writeExclusive`: crea el archivo con `O_EXCL`. Si ya existe, **no**
22
+ * lo pisa: devuelve el contenido que hay. Es la primitiva de "reclamar".
23
+ * - `withLock`: mutex por directorio. `mkdir` es atomico en POSIX y en
24
+ * Windows, asi que solo un proceso puede tomar el lock. Tiene un
25
+ * breaker de locks vencidos, porque un proceso muerto no suelta el lock.
26
+ * - `allocateSeq`: numero de secuencia unico, con la asignacion y la
27
+ * escritura del mensaje bajo el mismo lock.
28
+ *
29
+ * ## Lo que NO resuelve
30
+ *
31
+ * Nada de esto es un sistema de transacciones. No hay rollback, ni
32
+ * commit en dos fases, ni journal. Es lo que hace falta para que las
33
+ * invariantes que el sistema ya declara (un lease, un seq, una clave de
34
+ * idempotencia) sean verdad en vez de una intencion.
35
+ */
36
+ export interface ExclusiveWriteResult {
37
+ /** true si este proceso creo el archivo. */
38
+ created: boolean;
39
+ /** Contenido final del archivo (el propio, o el que ya estaba). */
40
+ contents: string;
41
+ }
42
+ /**
43
+ * Escribe un archivo de forma exclusiva.
44
+ *
45
+ * Con `O_EXCL`, el kernel garantiza que solo un proceso puede crearlo:
46
+ * los demas reciben `EEXIST` en vez de pisar. Es la primitiva atomica de
47
+ * "reclamar un recurso con nombre".
48
+ */
49
+ export declare function writeExclusive(filePath: string, contents: string): Promise<ExclusiveWriteResult>;
50
+ /** ¿Existe un archivo? */
51
+ export declare function fileExists(filePath: string): Promise<boolean>;
52
+ export interface LockOptions {
53
+ /** Cuanto esperar por el lock antes de rendirse. */
54
+ timeoutMs?: number;
55
+ /** A partir de cuando un lock se considera muerto y se puede romper. */
56
+ staleMs?: number;
57
+ }
58
+ /**
59
+ * Mutex por directorio.
60
+ *
61
+ * `mkdir` es atomico: de miles de procesos, uno solo recibe exito. Los
62
+ * demas ven `EEXIST` y esperan.
63
+ *
64
+ * ## Locks vencidos
65
+ *
66
+ * Si el proceso que tomo el lock muere, el directorio queda ahi para
67
+ * siempre. Por eso un lock mas viejo que `staleMs` se rompe: se asume que
68
+ * su dueno murio. Sin eso, un crash deja la oficina trabada para siempre,
69
+ * que es peor que el riesgo de romper un lock de otro proceso vivo (y ese
70
+ * proceso, si sigue vivo, esta haciendo su trabajo y no necesita el lock
71
+ * para otra cosa).
72
+ */
73
+ export declare function withLock<T>(lockPath: string, fn: () => Promise<T>, options?: LockOptions): Promise<T>;
74
+ /**
75
+ * Asigna un numero de secuencia unico dentro de un directorio.
76
+ *
77
+ * La asignacion y la escritura tienen que ocurrir **bajo el mismo lock**.
78
+ * Si el numero se reservara y el lock se soltara antes de escribir, el
79
+ * siguiente emisor no veria el archivo todavia no escrito y calcularia el
80
+ * mismo numero.
81
+ */
82
+ export declare function allocateSeq(channelDir: string, write: (seq: number) => Promise<void>, options?: LockOptions): Promise<number>;
83
+ /** Path del lock de un canal. */
84
+ export declare function seqLockPath(channelDir: string): string;
85
+ //# sourceMappingURL=atomic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic.d.ts","sourceRoot":"","sources":["../../src/core/atomic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAKH,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,OAAO,EAAE,OAAO,CAAA;IAChB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CActG;AAED,0BAA0B;AAC1B,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAOD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,CAAC,CAAC,CAgEZ;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EACrC,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,iCAAiC;AACjC,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Primitivas atómicas sobre disco.
3
+ *
4
+ * ## Por qué existe
5
+ *
6
+ * `writeAtomic` (tmp + rename) garantiza que un lector nunca vea un JSON a
7
+ * medio escribir. No garantiza que **dos escritores no se pisen**. El
8
+ * patron "leo, evaluo, escribo" — un TOCTOU — es la raiz de las carreras
9
+ * que existian:
10
+ *
11
+ * - El lease: dos agentes leian "no hay lease", los dos pasaban el chequeo,
12
+ * los dos escribian, y el segundo gano. El primero creia que tenia la
13
+ * card y el trabajo se hacia dos veces.
14
+ * - El `seq` del bus: dos emisores concurrentes calculaban el mismo
15
+ * `max + 1` y dejaban dos mensajes con el mismo seq.
16
+ * - La idempotencia: el "existe ya?" se hacia antes de escribir, asi que
17
+ * dos envios concurrentes con la misma clave creaban dos mensajes.
18
+ *
19
+ * ## Las tres primitivas
20
+ *
21
+ * - `writeExclusive`: crea el archivo con `O_EXCL`. Si ya existe, **no**
22
+ * lo pisa: devuelve el contenido que hay. Es la primitiva de "reclamar".
23
+ * - `withLock`: mutex por directorio. `mkdir` es atomico en POSIX y en
24
+ * Windows, asi que solo un proceso puede tomar el lock. Tiene un
25
+ * breaker de locks vencidos, porque un proceso muerto no suelta el lock.
26
+ * - `allocateSeq`: numero de secuencia unico, con la asignacion y la
27
+ * escritura del mensaje bajo el mismo lock.
28
+ *
29
+ * ## Lo que NO resuelve
30
+ *
31
+ * Nada de esto es un sistema de transacciones. No hay rollback, ni
32
+ * commit en dos fases, ni journal. Es lo que hace falta para que las
33
+ * invariantes que el sistema ya declara (un lease, un seq, una clave de
34
+ * idempotencia) sean verdad en vez de una intencion.
35
+ */
36
+ import fs from "node:fs/promises";
37
+ import path from "node:path";
38
+ /**
39
+ * Escribe un archivo de forma exclusiva.
40
+ *
41
+ * Con `O_EXCL`, el kernel garantiza que solo un proceso puede crearlo:
42
+ * los demas reciben `EEXIST` en vez de pisar. Es la primitiva atomica de
43
+ * "reclamar un recurso con nombre".
44
+ */
45
+ export async function writeExclusive(filePath, contents) {
46
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
47
+ let handle;
48
+ try {
49
+ handle = await fs.open(filePath, "wx");
50
+ await handle.writeFile(contents, "utf8");
51
+ return { created: true, contents };
52
+ }
53
+ catch (err) {
54
+ if (err.code !== "EEXIST")
55
+ throw err;
56
+ const existing = await fs.readFile(filePath, "utf8").catch(() => "");
57
+ return { created: false, contents: existing };
58
+ }
59
+ finally {
60
+ await handle?.close().catch(() => { });
61
+ }
62
+ }
63
+ /** ¿Existe un archivo? */
64
+ export async function fileExists(filePath) {
65
+ try {
66
+ await fs.access(filePath);
67
+ return true;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ /** Espera un poco. */
74
+ function sleep(ms) {
75
+ return new Promise((resolve) => setTimeout(resolve, ms));
76
+ }
77
+ /**
78
+ * Mutex por directorio.
79
+ *
80
+ * `mkdir` es atomico: de miles de procesos, uno solo recibe exito. Los
81
+ * demas ven `EEXIST` y esperan.
82
+ *
83
+ * ## Locks vencidos
84
+ *
85
+ * Si el proceso que tomo el lock muere, el directorio queda ahi para
86
+ * siempre. Por eso un lock mas viejo que `staleMs` se rompe: se asume que
87
+ * su dueno murio. Sin eso, un crash deja la oficina trabada para siempre,
88
+ * que es peor que el riesgo de romper un lock de otro proceso vivo (y ese
89
+ * proceso, si sigue vivo, esta haciendo su trabajo y no necesita el lock
90
+ * para otra cosa).
91
+ */
92
+ export async function withLock(lockPath, fn, options = {}) {
93
+ const timeoutMs = options.timeoutMs ?? 10_000;
94
+ const staleMs = options.staleMs ?? 30_000;
95
+ const deadline = Date.now() + timeoutMs;
96
+ // El lock vive como directorio. Su padre puede no existir todavia (el
97
+ // canal del bus se crea con el primer mensaje), y `mkdir` sin `recursive`
98
+ // falla con ENOENT si el padre falta.
99
+ await fs.mkdir(path.dirname(lockPath), { recursive: true });
100
+ let acquired = false;
101
+ while (Date.now() < deadline) {
102
+ try {
103
+ await fs.mkdir(lockPath);
104
+ acquired = true;
105
+ break;
106
+ }
107
+ catch (err) {
108
+ const code = err.code;
109
+ if (code === "EEXIST") {
110
+ // Alguien mas tiene el lock. Normal.
111
+ }
112
+ else if (code === "EPERM") {
113
+ // ── Trampa de Windows ──
114
+ //
115
+ // Cuando el dueno anterior esta borrando el lock, un `mkdir`
116
+ // concurrente no da EEXIST: da EPERM, porque el path existe pero
117
+ // esta en un estado invalido. Es una carrera transitoria, no un
118
+ // error de permisos: se reintenta.
119
+ //
120
+ // Sin este caso, en Windows dos emisores concurrentes del bus se
121
+ // rompian con EPERM en vez de serializarse.
122
+ }
123
+ else {
124
+ throw err;
125
+ }
126
+ // ¿El lock esta vencido?
127
+ try {
128
+ const stat = await fs.stat(lockPath);
129
+ if (Date.now() - stat.mtimeMs > staleMs) {
130
+ await fs.rm(lockPath, { recursive: true, force: true }).catch(() => { });
131
+ continue;
132
+ }
133
+ }
134
+ catch {
135
+ // El lock desaparecio entre el mkdir y el stat: reintentar.
136
+ continue;
137
+ }
138
+ // Espera con jitter: evita que los procesos se alineen en la carrera.
139
+ await sleep(15 + Math.floor(Math.random() * 35));
140
+ }
141
+ }
142
+ if (!acquired) {
143
+ throw new Error(`[oficina] no se pudo tomar el lock ${lockPath} en ${timeoutMs}ms`);
144
+ }
145
+ try {
146
+ return await fn();
147
+ }
148
+ finally {
149
+ // `rmdir` en vez de `rm -r`: el lock esta siempre vacio, y una sola
150
+ // llamada al sistema deja menos ventana para la carrera de Windows.
151
+ await fs.rmdir(lockPath).catch(async () => {
152
+ await fs.rm(lockPath, { recursive: true, force: true }).catch(() => { });
153
+ });
154
+ }
155
+ }
156
+ /**
157
+ * Asigna un numero de secuencia unico dentro de un directorio.
158
+ *
159
+ * La asignacion y la escritura tienen que ocurrir **bajo el mismo lock**.
160
+ * Si el numero se reservara y el lock se soltara antes de escribir, el
161
+ * siguiente emisor no veria el archivo todavia no escrito y calcularia el
162
+ * mismo numero.
163
+ */
164
+ export async function allocateSeq(channelDir, write, options = {}) {
165
+ const lockPath = path.join(channelDir, ".seq.lock");
166
+ return withLock(lockPath, async () => {
167
+ const entries = await fs.readdir(channelDir).catch(() => []);
168
+ let max = 0;
169
+ for (const entry of entries) {
170
+ const m = /^(\d{4,})-/.exec(entry);
171
+ if (!m)
172
+ continue;
173
+ const n = Number.parseInt(m[1], 10);
174
+ if (Number.isFinite(n) && n > max)
175
+ max = n;
176
+ }
177
+ const seq = max + 1;
178
+ await write(seq);
179
+ return seq;
180
+ }, options);
181
+ }
182
+ /** Path del lock de un canal. */
183
+ export function seqLockPath(channelDir) {
184
+ return path.join(channelDir, ".seq.lock");
185
+ }
186
+ //# sourceMappingURL=atomic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../src/core/atomic.ts"],"names":[],"mappings":"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"}