ostacky 0.9.7 → 0.10.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.
@@ -27,12 +27,11 @@ BLOCKED: replan→INTERPRETATION_PENDING, abandon→DONE
27
27
  | controller_* | 5s | 1 | degraded |
28
28
  | engram_* | 5s | 1 | sin memoria |
29
29
 
30
- ## Cache
31
-
32
- - `src/cache-codegraph.ts`: `.opencode/cache/codegraph/<sha256(query)>.json` con `{ts, result, gitHead, gitDiffHash}`, TTL 1h, `OSTACKY_CACHE_DISABLE=1` bypass, LRU 50MB, invalidación `git diff --name-only` + `git status --porcelain --untracked-files=all` (hash combinado).
33
- - `src/discovery-cache.ts`: `.opencode/cache/codegraph/discovery-<sha256>.json` con `{codegraph, engramHits, gitDiffHash, gitHead, ts, query}`, mismo TTL/LRU. `getDiscoverySnapshot` → hit reusar sin llamar tools; miss → SHALL `putDiscoverySnapshot` antes de `record_discovery` (auditable `WARN:cache_miss_without_put`).
34
- - `record_cache_hit/miss` internos: `cache-codegraph.ts` hace write directo a `ostacky-state.json` incrementando `cacheHitCount/tokenSavingEstimate` sin tool LLM.
30
+ ## Discovery
35
31
 
32
+ - Sin cache de archivo: discovery = `codegraph_explore` + `mem_search`; el reuso es del output ya presente en el contexto del request. `src/discovery-cache.ts` y `src/cache-codegraph.ts` se retiraron (change `remove-discovery-cache-phantom`).
33
+ - `OSTACKY_CACHE_DISABLE` ya no existe: no hay cache filesystem que desactivar.
34
+ - Los contadores `cacheHitCount`/`tokenSavingEstimate` quedan en `get_metrics` por compatibilidad, en 0: nada los incrementa.
36
35
  ## Metrics (get_metrics)
37
36
 
38
37
  `revision, state, degraded, taskCounts{completed,pending,total,expected}, expectedTaskCount, auditSize, stateFileSize, diskFreeMB, uptimeMs, stateOversizedCount, codegraphBypassCount, degradedEditsCount, cacheHitCount, cacheMissCount, tokenSavingEstimate, sensitiveAccess, subagentFailedCount, discoveryCacheHitCount, redundantCallCount, cacheMissWithoutPutCount, stateCheckCount, toolCallCount`
@@ -56,6 +55,34 @@ Tras refino post-complete (archivo ya correcto, sin edición pendiente): `refres
56
55
 
57
56
  El plugin hace cumplir `PENDING` en `ctx.tool.hook("execute.before")`; `lastCheck={revision, result}` cachea ALLOW por revisión, revalida si cambia o >5 tools; `BLOCKED` nunca cacheado. Métricas `stateCheckCount` cuentan checks del plugin (no del LLM).
58
57
 
58
+ ## Gate de CodeGraph (alcance real)
59
+
60
+ Cubre `read`/`grep`/`glob`/`read_mcp_resource` y **`shell`** cuando el comando revela contenido (`cat`, `head`, `tail`, `sed` sin `-i`, `grep`, `rg`, `awk`, `diff`…). Predicado puro: `shellReadTargets(command)` devuelve los paths de código **revelados**, no los mencionados — `bun test x.ts`, `tsc src/x.ts` y `git diff` pasan.
61
+
62
+ **Fail-open:** redirección a archivo (`>`/`>>` a path), `sed -i` y verbo fuera de la allowlist → se permite. `read` falla cerrado porque es inequívoco; `shell` no, y un falso positivo rompe tests/builds sin escape hatch. Los redirects de stderr (`2>&1`, `1>&2`, `2>/dev/null`) NO cuentan como escritura (mismo criterio que `isShellMutation`): `cat src/x.ts 2>&1` sigue gateado.
63
+
64
+ **Exenciones `isShellPathExempt(p, directory, freshState)`:** paths FUERA del projectRoot (fixtures temporales, reportes generados — `cat /tmp/out.ts` nunca bloquea), specs/docs (openspec, `assets/docs`, `.md`, `.json`) y archivos ya trackeados en tasks/fileFingerprints/lastValidated.
65
+
66
+ **No es un sandbox.** Bypass residual por diseño: `git show`, `python -c`, `node -e`, `find -exec`, `xargs`, `base64`. El objetivo es quitar el bypass casual (`cat src/x.ts`, incluida su variante `cat src/x.ts 2>&1`), no probar imposibilidad.
67
+
68
+ `execute` (Code Mode) **no** se gatea: su catálogo no expone `read`/`grep`/`shell` ni filesystem. Si una versión futura lo expone, el gate debe extenderse.
69
+
70
+ **Evidencia válida de discovery:** únicamente un `codegraph_codegraph_explore` COMPLETADO en esta sesión (`discoveryHitByRequest`, memo limpiado con `session.deleted`). No hay fallback a cache en disco (el legado `getDiscoveryCacheHit` se retiró con discovery-cache).
71
+
72
+ ## Evidencia de ejecución (`test:`)
73
+
74
+ `test:<cmd>` no se puede verificar sin ejecutar. El plugin observa cada `shell` pero **sólo persiste** `{cmd, exit, ts}` cuando el comando matchea un `test:` declarado en `taskVerifications` (D10: `git`/`ls`/`builds` no son evidencia; cada persist cuesta lock + re-read + write + backups). `complete_task` exige comando exacto (trim), `exit 0` y `ts` posterior a `lastMutationTs` (que avanza en cada `edit`/`write`/`patch` completado y en cada `shell` que escribe con exit 0: `cp`, `mv`, `rm`, `sed -i`, `tee`, redirección). `verifyIntegrity` re-verifica en el cierre: una edición posterior vence la evidencia y bloquea.
75
+
76
+ **Fail-closed en orden de operaciones:** si el test corre antes de `record_execution_analysis` (que carga `taskVerifications` en el state), el plugin no lo registra como evidencia → `complete_task` pide re-correr con razón explícita. El orden correcto es analysis → tests → complete.
77
+
78
+ **No se usa `status`:** en OpenCode 2.0.32, `sh -c 'exit 3'` llega con `status: "completed"` y `result.metadata.status: "completed"`. El único campo veraz es `result.metadata.exit`.
79
+
80
+ **Límites declarados:** un test débil pasa (semántica, no mecánica); sin el plugin no hay `shellRuns` y `test:` falla cerrado.
81
+
82
+ ## Force gate (human confirmation, D4)
83
+
84
+ `implementation_complete({force:true})` exige una frase del USUARIO (`/forzar|confirmo|force/i`) en `lastUserPrompts` — no en el audit, que redacta el agente. El plugin registra cada prompt con `{text, ts}` antes de inyectar hints. Cada rechazo del gate setea `lastForceAttemptTs`: sólo cuenta una frase con `ts` POSTERIOR al último intento (un "¿qué hace force:true?" previo no habilita). Prompts legacy sin `ts` (formato viejo en disco) pasan sólo si nunca hubo intento registrado.
85
+
59
86
  ## Tiered Behaviour
60
87
 
61
88
  `isTrivial(msg,state) = state==DONE && msg.trim().length<30 && /^(hola|hey|gracias|buenas|hi|hello)\b/i.test(msg) && !/(necesito|quiero|agregá|fix|bug|feature|auth|spec|implementar)/i.test(msg)`
@@ -86,4 +113,4 @@ Principio: **eficacia > recorte** — si recorte rompiera caché y saliera más
86
113
 
87
114
  ## Propose Flow (1+)
88
115
 
89
- `openspec-propose` SHALL Paso 0: `getDiscoverySnapshot(query)` + `getEngramDedup` → si miss `codegraph_codegraph_explore` + `engram_mem_search` + `putDiscoverySnapshot` **antes** de `openspec new`. Cita símbolos existentes en `proposal/design`; sin esto dispara `WARN:codegraphBypass` y `design.md` alucina API. Ver `assets/skills/openspec-propose/SKILL.md: Paso 0` y `brainstorming` (router por nivel: `brainstorming` decide 0/0+1/1+, `openspec-explore` es stance sin nivel).
116
+ `openspec-propose` SHALL Paso 0: `codegraph_codegraph_explore` + `engram_mem_search` sobre el área (si no se hicieron ya en el request) **antes** de `openspec new`. Citá símbolos existentes en `proposal/design`; sin esto dispara `WARN:codegraphBypass` y `design.md` alucina API. Ver `assets/skills/openspec-propose/SKILL.md: Paso 0` y `brainstorming` (router por nivel: `brainstorming` decide 0/0+1/1+, `openspec-explore` es stance sin nivel).
@@ -141,6 +141,10 @@ export const DEFAULT_STATE = {
141
141
  pendingFileAccess: {},
142
142
  lastHeartbeat: 0,
143
143
  watchdogEnabled: true,
144
+ // multi-sesión: dueño de la request activa (sessionId de OpenCode vía _meta).
145
+ // null = sin dueño conocido. Es UN campo (se sobreescribe) — nunca un archivo por sesión.
146
+ ownerSessionId: null,
147
+ ownerHeartbeat: 0,
144
148
  ts: Date.now(),
145
149
  specNotInSync: false,
146
150
  engramBypassCount: 0,