dsh-doublecheck 0.9.24 → 0.9.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README-es.md +31 -3
- package/README-hi.md +31 -3
- package/README-pt.md +31 -3
- package/README-zh.md +31 -3
- package/README.md +31 -3
- package/lib/guard/activation.d.ts +31 -0
- package/lib/guard/activation.js +1 -0
- package/lib/guard/gate.d.ts +2 -0
- package/lib/guard/gate.js +33 -9
- package/lib/guard/index.d.ts +4 -0
- package/lib/guard/index.js +14 -2
- package/lib/guard/review.d.ts +2 -0
- package/lib/guard/review.js +31 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.26] - 2026-10-18
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Add a 1280×720 hero and a 960×540, 4-second terminal GIF rendered from the test run; `screenshots.json` is an object array and `capture.json` records the command.
|
|
8
|
+
|
|
9
|
+
## [0.9.25] - 2026-10-18
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Reviewers use `subagents.startActivation` on 0.2.1+.** The adversary and gate reviewers detect the new API, require a read-only Agent Preset (`adversaryPreset` / `gate.consistency|review.preset`), and fail closed without one; alpha hosts keep the old `start()` + `toolFilter` path. This closes the functional failure where every reviewer call on 0.2.1 was rejected.
|
|
14
|
+
|
|
3
15
|
All notable changes to dsh-doublecheck are recorded here, newest first.
|
|
4
16
|
|
|
17
|
+
## [Unreleased]
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **The `doublecheck` session projection could never be read in full.** Its wire value was a JSON object of eight fields costing **131 characters when empty** and 139 with a short goal, while the one-line renderer a chat bridge uses keeps a value only up to 120 characters and otherwise cuts it to 117 plus `…`. Every possible value was truncated: the empty state lost `gateVerdict` to `"ga…` and `gateRedCount` entirely, and as soon as a spec goal existed `specGoal`, `reviewed`, `editCount`, `gateVerdict` and `gateRedCount` all disappeared — exactly the verdict and red-light count a user opens `/status` on a phone to see. A JSON object cannot be fixed by shortening fields either: the eight keys alone put its theoretical minimum at 126 characters.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- The wire value is now one string this package renders itself, led by the three facts a user reads first and bounded to **120 characters worst case** by construction: `gate:rework r2 | stage:green | test:green edits:12 spec:yes review:yes | goal:ship parser fix`. Every variable field is capped (counts print as `9999+` / `99999+`, the goal excerpt is sanitized to one line and clipped to the remaining budget, at most 40 characters, ending in `…`), so no field can push the line over budget. `goal:` is the only lossy segment; the complete goal stays in `/doublecheck status` and the delivery report. The READMEs document how to read the line.
|
|
26
|
+
- The projection's `stateVersion` moves **2 → 3** (the wire value changed type). A persisted projection-cache row written at `ver: 2` is discarded and the key refolds from the session log, which is the host's documented version-mismatch path — no migration code, and a pre-upgrade session never fails to load. The persisted fold state is unchanged.
|
|
27
|
+
- A string wire value also quiets the change feed: the host publishes a projection change only when `Object.is` differs from the previous raw view, and equal strings compare equal, where the old fresh object literal published on every state change.
|
|
28
|
+
|
|
5
29
|
## [0.9.24] - 2026-10-10
|
|
6
30
|
|
|
7
31
|
### Changed
|
package/README-es.md
CHANGED
|
@@ -41,7 +41,7 @@ La puerta de calidad de entrega para DeepSeek Harness: interroga los requisitos,
|
|
|
41
41
|
|
|
42
42
|
Los requisitos se interrogan antes de la primera edición; la entrega se demuestra, nunca se afirma.
|
|
43
43
|
|
|
44
|
-
](https://raw.githubusercontent.com/PerryLink/dsh-doublecheck/main/docs/assets/dsh-doublecheck-hero.png)
|
|
45
45
|
|
|
46
46
|

|
|
47
47
|
|
|
@@ -172,6 +172,34 @@ La mala configuración falla en voz alta al cargar: regex inválidas, listas de
|
|
|
172
172
|
| `gate` | campo de configuración en vivo | La lista enchufable es el único campo `.volatile()` de la fila del guard: se edita desde la tarjeta de ajustes de la fila (espacio de nombres = el id de entrada de perfil de la fila) y se lee una vez al cargar. |
|
|
173
173
|
| `strict.patch.yml` | capa superpuesta | Cada puerta activa con intensidad `block` más el requisito de cobertura, en una capa de parche. |
|
|
174
174
|
| `dsh-doublecheck/invariant` | fila acompañante | Informa contradicciones de ruta de escritura propias del paquete a través del registro `invariants` del host. |
|
|
175
|
+
| `doublecheck` | proyección de sesión | Una línea de estado acotada que el cliente muestra: etapa, último veredicto de la puerta, cuenta de rojos, ediciones y marcas spec/review. Ver [Cómo leer la línea de estado](#cómo-leer-la-línea-de-estado). |
|
|
176
|
+
|
|
177
|
+
### Cómo leer la línea de estado
|
|
178
|
+
|
|
179
|
+
La proyección de sesión `doublecheck` es una **cadena**, y es deliberadamente una sola línea: la idea es que cualquier cliente pueda mostrarla sin recortar nada. El `/status` de un puente de chat, una barra de estado o cualquier consumidor propio la muestra tal cual:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
gate:rework r2 | stage:green | test:green edits:12 spec:yes review:yes | goal:ship parser fix
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
| Segmento | Se lee como |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `gate:<veredicto> r<n>` | El último veredicto de la puerta de entrega — `none` antes de cualquier ejecución, `deliverable` o `rework` — y su cuenta de rojos `r<n>`. |
|
|
188
|
+
| `stage:<etapa>` | La etapa de disciplina alcanzada con evidencia durable: `grill`, `design`, `red`, `green`, `review`, `verify`. |
|
|
189
|
+
| `test:<color>` | El color de la última ejecución de pruebas: `none`, `red`, `green`. |
|
|
190
|
+
| `edits:<n>` | Ediciones de implementación plegadas del registro de sesión (escribir archivos de prueba no cuenta). |
|
|
191
|
+
| `spec:yes` / `spec:no` | Si existe un `doublecheck_spec` confirmado. |
|
|
192
|
+
| `review:yes` / `review:no` | Si existe un registro de revisión adversaria. |
|
|
193
|
+
| `goal:<extracto>` | El objetivo del spec confirmado, recortado. Solo aparece con `spec:yes`. |
|
|
194
|
+
|
|
195
|
+
La línea está acotada **por construcción**, así que ningún renderizador la trunca:
|
|
196
|
+
|
|
197
|
+
- Presupuesto duro: **120 caracteres**. Un renderizador que conserva los valores de hasta 120 caracteres — el renderizador genérico de proyecciones del puente de WeChat es uno de ellos — muestra la línea completa. La forma de objeto anterior tenía 131 caracteres **en vacío**, y por eso el veredicto y la cuenta de rojos se recortaban.
|
|
198
|
+
- Los tres primeros segmentos son **sin pérdida**. Las cuentas por encima de `9999` rojos o `99999` ediciones se imprimen como `9999+` / `99999+` en lugar de crecer sin límite; una cuenta no finita o negativa se imprime como `0`.
|
|
199
|
+
- `goal:` es el único segmento con pérdida. Se aplana a una sola línea (los saltos de línea y los caracteres de control pasan a espacios, `|` pasa a `/`) y se recorta al presupuesto restante, como máximo 40 caracteres, terminando en `…` cuando se recortó. El objetivo completo está en `/doublecheck status` y en el informe de entrega.
|
|
200
|
+
- Esto es un vistazo, no el registro: `/gate status` y `/doublecheck status` muestran el estado completo.
|
|
201
|
+
|
|
202
|
+
El `stateVersion` de la proyección es **3**. Incrementarlo descarta una fila de caché de proyección escrita por una versión anterior y vuelve a plegar la clave desde el registro de sesión, de modo que una sesión actualizada recalcula la línea en lugar de mostrar una obsoleta. El estado plegado en sí no cambia, y una sesión anterior a la actualización nunca falla al cargar.
|
|
175
203
|
|
|
176
204
|
## Fases de la puerta
|
|
177
205
|
|
|
@@ -308,7 +336,7 @@ Evidencia estática: `dsh-plugin-doctor` K10–K13 dan `pass` en todas las compr
|
|
|
308
336
|
|
|
309
337
|
## PerryLink DSH Plugin Family
|
|
310
338
|
|
|
311
|
-
Este proyecto es uno de los **
|
|
339
|
+
Este proyecto es uno de los **34 plugins activamente mantenidos** de DeepSeek Harness de [PerryLink](https://github.com/PerryLink): el registro tiene **42**, de los cuales **5** están congelados y **3** retirados; cada uno conserva su fila abajo, con el motivo en la columna Estado. Si este te ayuda, probablemente los demás también:
|
|
312
340
|
|
|
313
341
|
| Plugin | En una línea | Estado |
|
|
314
342
|
|---|---|---|
|
|
@@ -336,7 +364,7 @@ Este proyecto es uno de los **33 plugins activamente mantenidos** de DeepSeek Ha
|
|
|
336
364
|
| **[dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions)** | LSP diagnostics, formatting, completion, code actions and rename over language servers | |
|
|
337
365
|
| **[dsh-mask](https://github.com/PerryLink/dsh-mask)** | PII masking middleware: anonymize at the model boundary, restore at the display layer | |
|
|
338
366
|
| **[dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel)** | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors | |
|
|
339
|
-
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
|
|
367
|
+
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool | |
|
|
340
368
|
| **[dsh-observe](https://github.com/PerryLink/dsh-observe)** | OpenTelemetry and Langfuse observability exporter for DeepSeek Harness. | |
|
|
341
369
|
| **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles-equivalent runtime style switching | |
|
|
342
370
|
| **[dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules)** | Claude Code-style declarative allow/deny/ask permission rules with audit | |
|
package/README-hi.md
CHANGED
|
@@ -41,7 +41,7 @@ DeepSeek Harness के लिए डिलीवरी गुणवत्ता
|
|
|
41
41
|
|
|
42
42
|
पहली एडिट से पहले आवश्यकताओं की पड़ताल होती है; डिलीवरी साबित की जाती है, दावा नहीं किया जाता।
|
|
43
43
|
|
|
44
|
-
](https://raw.githubusercontent.com/PerryLink/dsh-doublecheck/main/docs/assets/dsh-doublecheck-hero.png)
|
|
45
45
|
|
|
46
46
|

|
|
47
47
|
|
|
@@ -172,6 +172,34 @@ dsh --profile web --dump-config | grep -E -A3 'id: doublecheck-(grill|guard)'
|
|
|
172
172
|
| `gate` | लाइव कॉन्फ़िग फ़ील्ड | प्लग करने योग्य सूची guard पंक्ति की एकमात्र `.volatile()` फ़ील्ड है: इसे पंक्ति के सेटिंग्स कार्ड से संपादित किया जाता है (नेमस्पेस = पंक्ति की profile प्रविष्टि id) और लोड पर एक बार पढ़ा जाता है। |
|
|
173
173
|
| `strict.patch.yml` | ओवरले | `block` तीव्रता पर हर द्वार चालू और कवरेज आवश्यकता, एक पैच परत में। |
|
|
174
174
|
| `dsh-doublecheck/invariant` | सहयोगी पंक्ति | host `invariants` रजिस्ट्री के माध्यम से पैकेज-स्वामित्व वाले लेखन-पथ विरोधाभासों की रिपोर्ट करता है। |
|
|
175
|
+
| `doublecheck` | सत्र प्रोजेक्शन | क्लाइंट द्वारा दिखाई जाने वाली एक सीमित स्थिति पंक्ति: चरण, नवीनतम द्वार निर्णय, लाल-बत्ती गिनती, संपादन और spec/review ध्वज। देखें [स्थिति पंक्ति को कैसे पढ़ें](#स्थिति-पंक्ति-को-कैसे-पढ़ें)। |
|
|
176
|
+
|
|
177
|
+
### स्थिति पंक्ति को कैसे पढ़ें
|
|
178
|
+
|
|
179
|
+
`doublecheck` सत्र प्रोजेक्शन एक **स्ट्रिंग** है, और यह जानबूझकर एक ही पंक्ति है — उद्देश्य यही है कि कोई भी क्लाइंट इसे बिना कुछ काटे पूरा दिखा सके। चैट ब्रिज का `/status`, कोई स्टेटस बार, या कोई भी अपना उपभोक्ता इसे जैसा है वैसा ही दिखाता है:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
gate:rework r2 | stage:green | test:green edits:12 spec:yes review:yes | goal:ship parser fix
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
| खंड | पढ़ने का अर्थ |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `gate:<निर्णय> r<n>` | नवीनतम डिलीवरी-द्वार निर्णय — किसी भी रन से पहले `none`, अन्यथा `deliverable` या `rework` — और उसकी लाल-बत्ती गिनती `r<n>`। |
|
|
188
|
+
| `stage:<चरण>` | टिकाऊ साक्ष्य पर पहुँचा अनुशासन चरण: `grill`, `design`, `red`, `green`, `review`, `verify`। |
|
|
189
|
+
| `test:<रंग>` | नवीनतम परीक्षण रन का रंग: `none`, `red`, `green`। |
|
|
190
|
+
| `edits:<n>` | सत्र लॉग से मोड़े गए कार्यान्वयन संपादन (परीक्षण फ़ाइल लिखना नहीं गिना जाता)। |
|
|
191
|
+
| `spec:yes` / `spec:no` | क्या कोई प्रतिबद्ध `doublecheck_spec` मौजूद है। |
|
|
192
|
+
| `review:yes` / `review:no` | क्या कोई प्रतिद्वंद्वी समीक्षा रिकॉर्ड मौजूद है। |
|
|
193
|
+
| `goal:<अंश>` | प्रतिबद्ध spec लक्ष्य, छाँटा हुआ। केवल `spec:yes` पर दिखता है। |
|
|
194
|
+
|
|
195
|
+
यह पंक्ति **रचना से ही सीमित** है, इसलिए कोई भी रेंडरर इसे काटता नहीं:
|
|
196
|
+
|
|
197
|
+
- कठोर बजट: **120 अक्षर**। जो रेंडरर 120 अक्षर तक के मान बिना बदले रखता है — WeChat ब्रिज का सामान्य प्रोजेक्शन रेंडरर उनमें से एक है — वह पूरी पंक्ति दिखाता है। पहले का ऑब्जेक्ट रूप **खाली अवस्था में ही 131 अक्षर** था, और इसीलिए निर्णय तथा लाल-बत्ती गिनती कट जाते थे।
|
|
198
|
+
- पहले तीन खंड **हानिरहित** हैं। `9999` से अधिक लाल बत्तियाँ या `99999` से अधिक संपादन `9999+` / `99999+` के रूप में छपते हैं, बिना सीमा बढ़ाए; अशून्य-असीमित या ऋणात्मक गिनती `0` छपती है।
|
|
199
|
+
- `goal:` एकमात्र हानिकर खंड है। इसे एक पंक्ति में समतल किया जाता है (नई पंक्तियाँ और नियंत्रण अक्षर रिक्त स्थान बन जाते हैं, `|` बन जाता है `/`) और बचे बजट तक छाँटा जाता है — अधिकतम 40 अक्षर — कटने पर `…` पर समाप्त होता है। पूरा लक्ष्य `/doublecheck status` और डिलीवरी रिपोर्ट में है।
|
|
200
|
+
- यह एक नज़र है, रिकॉर्ड नहीं: `/gate status` और `/doublecheck status` पूरी स्थिति दिखाते हैं।
|
|
201
|
+
|
|
202
|
+
प्रोजेक्शन का `stateVersion` **3** है। इसे बढ़ाने पर पुराने संस्करण द्वारा लिखी गई स्थायी प्रोजेक्शन कैश पंक्ति हटा दी जाती है और कुंजी सत्र लॉग से फिर से मोड़ी जाती है, इसलिए उन्नयन के बाद का सत्र पुरानी पंक्ति दिखाने के बजाय उसे दोबारा गणना करता है। मोड़ी गई स्थिति स्वयं नहीं बदलती, और उन्नयन-पूर्व सत्र कभी लोड होने में विफल नहीं होता।
|
|
175
203
|
|
|
176
204
|
## द्वार चरण
|
|
177
205
|
|
|
@@ -308,7 +336,7 @@ pnpm run pack:check # build + pack the tarball
|
|
|
308
336
|
|
|
309
337
|
## PerryLink DSH Plugin Family
|
|
310
338
|
|
|
311
|
-
यह प्रोजेक्ट [PerryLink](https://github.com/PerryLink) के DeepSeek Harness प्लगइन परिवार का हिस्सा है — **
|
|
339
|
+
यह प्रोजेक्ट [PerryLink](https://github.com/PerryLink) के DeepSeek Harness प्लगइन परिवार का हिस्सा है — **34 सक्रिय रूप से अनुरक्षित**, कुल सूची **42** है जिसमें **5 फ़्रोज़न** और **3 सेवानिवृत्त** हैं; हर एक की पंक्ति नीचे बनी रहती है, कारण “स्थिति” कॉलम में है। अगर यह उपयोगी लगे, तो अन्य भी मददगार होंगे:
|
|
312
340
|
|
|
313
341
|
| Plugin | एक पंक्ति में | स्थिति |
|
|
314
342
|
|---|---|---|
|
|
@@ -336,7 +364,7 @@ pnpm run pack:check # build + pack the tarball
|
|
|
336
364
|
| **[dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions)** | LSP diagnostics, formatting, completion, code actions and rename over language servers | |
|
|
337
365
|
| **[dsh-mask](https://github.com/PerryLink/dsh-mask)** | PII masking middleware: anonymize at the model boundary, restore at the display layer | |
|
|
338
366
|
| **[dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel)** | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors | |
|
|
339
|
-
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
|
|
367
|
+
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool | |
|
|
340
368
|
| **[dsh-observe](https://github.com/PerryLink/dsh-observe)** | OpenTelemetry and Langfuse observability exporter for DeepSeek Harness. | |
|
|
341
369
|
| **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles-equivalent runtime style switching | |
|
|
342
370
|
| **[dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules)** | Claude Code-style declarative allow/deny/ask permission rules with audit | |
|
package/README-pt.md
CHANGED
|
@@ -41,7 +41,7 @@ O portão de qualidade de entrega para o DeepSeek Harness: interrogue os requisi
|
|
|
41
41
|
|
|
42
42
|
Os requisitos são interrogados antes da primeira edição; a entrega é comprovada, nunca afirmada.
|
|
43
43
|
|
|
44
|
-
](https://raw.githubusercontent.com/PerryLink/dsh-doublecheck/main/docs/assets/dsh-doublecheck-hero.png)
|
|
45
45
|
|
|
46
46
|

|
|
47
47
|
|
|
@@ -172,6 +172,34 @@ A má configuração falha em voz alta ao carregar: regex inválidas, listas de
|
|
|
172
172
|
| `gate` | campo de configuração ao vivo | A lista plugável é o único campo `.volatile()` da linha do guard: é editada no cartão de configurações da linha (namespace = o id de entrada de perfil da linha) e lida uma vez ao carregar. |
|
|
173
173
|
| `strict.patch.yml` | camada de sobreposição | Cada portão ativo com intensidade `block` mais o requisito de cobertura, em uma camada de patch. |
|
|
174
174
|
| `dsh-doublecheck/invariant` | linha acompanhante | Reporta contradições de caminho de escrita próprias do pacote por meio do registro `invariants` do host. |
|
|
175
|
+
| `doublecheck` | projeção de sessão | Uma linha de status limitada que o cliente exibe: etapa, último veredicto do portão, contagem de vermelhos, edições e marcas spec/review. Ver [Como ler a linha de status](#como-ler-a-linha-de-status). |
|
|
176
|
+
|
|
177
|
+
### Como ler a linha de status
|
|
178
|
+
|
|
179
|
+
A projeção de sessão `doublecheck` é uma **string**, e é deliberadamente uma única linha: a ideia é que qualquer cliente possa exibi-la sem cortar nada. O `/status` de uma ponte de chat, uma barra de status ou qualquer consumidor próprio a exibe como está:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
gate:rework r2 | stage:green | test:green edits:12 spec:yes review:yes | goal:ship parser fix
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
| Segmento | Lê-se como |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `gate:<veredicto> r<n>` | O último veredicto do portão de entrega — `none` antes de qualquer execução, `deliverable` ou `rework` — e sua contagem de vermelhos `r<n>`. |
|
|
188
|
+
| `stage:<etapa>` | A etapa de disciplina alcançada com evidência durável: `grill`, `design`, `red`, `green`, `review`, `verify`. |
|
|
189
|
+
| `test:<cor>` | A cor da última execução de testes: `none`, `red`, `green`. |
|
|
190
|
+
| `edits:<n>` | Edições de implementação dobradas do registro de sessão (escrever arquivos de teste não conta). |
|
|
191
|
+
| `spec:yes` / `spec:no` | Se existe um `doublecheck_spec` confirmado. |
|
|
192
|
+
| `review:yes` / `review:no` | Se existe um registro de revisão adversária. |
|
|
193
|
+
| `goal:<trecho>` | O objetivo do spec confirmado, recortado. Só aparece com `spec:yes`. |
|
|
194
|
+
|
|
195
|
+
A linha é limitada **por construção**, então nenhum renderizador a trunca:
|
|
196
|
+
|
|
197
|
+
- Orçamento rígido: **120 caracteres**. Um renderizador que preserva valores de até 120 caracteres — o renderizador genérico de projeções da ponte do WeChat é um deles — mostra a linha inteira. A forma de objeto anterior tinha 131 caracteres **vazia**, e é por isso que o veredicto e a contagem de vermelhos eram cortados.
|
|
198
|
+
- Os três primeiros segmentos são **sem perda**. Contagens acima de `9999` vermelhos ou `99999` edições são impressas como `9999+` / `99999+` em vez de crescer sem limite; uma contagem não finita ou negativa é impressa como `0`.
|
|
199
|
+
- `goal:` é o único segmento com perda. Ele é achatado em uma única linha (quebras de linha e caracteres de controle viram espaços, `|` vira `/`) e recortado pelo orçamento restante, no máximo 40 caracteres, terminando em `…` quando foi recortado. O objetivo completo está em `/doublecheck status` e no relatório de entrega.
|
|
200
|
+
- Isto é um relance, não o registro: `/gate status` e `/doublecheck status` mostram o estado completo.
|
|
201
|
+
|
|
202
|
+
O `stateVersion` da projeção é **3**. Incrementá-lo descarta uma linha de cache de projeção escrita por uma versão anterior e dobra a chave novamente a partir do registro de sessão, de modo que uma sessão atualizada recalcula a linha em vez de mostrar uma obsoleta. O estado dobrado em si não muda, e uma sessão anterior à atualização nunca falha ao carregar.
|
|
175
203
|
|
|
176
204
|
## Fases do portão
|
|
177
205
|
|
|
@@ -308,7 +336,7 @@ Evidência estática: `dsh-plugin-doctor` K10–K13 retornam `pass` em todas as
|
|
|
308
336
|
|
|
309
337
|
## PerryLink DSH Plugin Family
|
|
310
338
|
|
|
311
|
-
Este projeto é um dos **
|
|
339
|
+
Este projeto é um dos **34 plugins ativamente mantidos** do DeepSeek Harness da [PerryLink](https://github.com/PerryLink): o registro tem **42**, dos quais **5** estão congelados e **3** aposentados; cada um mantém sua linha abaixo, com o motivo na coluna Status. Se este ajudou você, provavelmente os outros também ajudarão:
|
|
312
340
|
|
|
313
341
|
| Plugin | Em uma linha | Status |
|
|
314
342
|
|---|---|---|
|
|
@@ -336,7 +364,7 @@ Este projeto é um dos **33 plugins ativamente mantidos** do DeepSeek Harness da
|
|
|
336
364
|
| **[dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions)** | LSP diagnostics, formatting, completion, code actions and rename over language servers | |
|
|
337
365
|
| **[dsh-mask](https://github.com/PerryLink/dsh-mask)** | PII masking middleware: anonymize at the model boundary, restore at the display layer | |
|
|
338
366
|
| **[dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel)** | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors | |
|
|
339
|
-
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
|
|
367
|
+
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool | |
|
|
340
368
|
| **[dsh-observe](https://github.com/PerryLink/dsh-observe)** | OpenTelemetry and Langfuse observability exporter for DeepSeek Harness. | |
|
|
341
369
|
| **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles-equivalent runtime style switching | |
|
|
342
370
|
| **[dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules)** | Claude Code-style declarative allow/deny/ask permission rules with audit | |
|
package/README-zh.md
CHANGED
|
@@ -41,7 +41,7 @@ DeepSeek Harness 的交付质量门禁:先拷问需求,再测试实现,最
|
|
|
41
41
|
|
|
42
42
|
需求在第一次改动之前就被拷问清楚;交付是被证明的,而不是被口头宣称的。
|
|
43
43
|
|
|
44
|
-
](https://raw.githubusercontent.com/PerryLink/dsh-doublecheck/main/docs/assets/dsh-doublecheck-hero.png)
|
|
45
45
|
|
|
46
46
|

|
|
47
47
|
|
|
@@ -172,6 +172,34 @@ dsh --profile web --dump-config | grep -E -A3 'id: doublecheck-(grill|guard)'
|
|
|
172
172
|
| `gate` | 活动配置字段 | 可插拔清单是守卫行唯一的 `.volatile()` 字段:从该行的设置卡片编辑(命名空间 = 该行的 profile 条目 id),在加载时读取一次。 |
|
|
173
173
|
| `strict.patch.yml` | 覆盖层 | 一个补丁层内以 `block` 强度开启每个门禁并启用覆盖率要求。 |
|
|
174
174
|
| `dsh-doublecheck/invariant` | 伴生行 | 通过宿主 `invariants` 注册表报告包自有写路径矛盾。 |
|
|
175
|
+
| `doublecheck` | 会话投影 | 客户端渲染的一行有界状态:阶段、最近裁决、红灯数、编辑数与 spec/review 标记。见[如何读这一行](#如何读这一行)。 |
|
|
176
|
+
|
|
177
|
+
### 如何读这一行
|
|
178
|
+
|
|
179
|
+
`doublecheck` 会话投影是一个**字符串**,而且刻意只有一行——这样任何客户端都能完整显示它、不会切掉任何内容。聊天桥的 `/status`、状态栏或任何自定义消费者都原样渲染:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
gate:rework r2 | stage:green | test:green edits:12 spec:yes review:yes | goal:ship parser fix
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
| 片段 | 读法 |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `gate:<裁决> r<n>` | 最近一次交付门禁的裁决——未运行时是 `none`,否则是 `deliverable` 或 `rework`——以及它的红灯数 `r<n>`。 |
|
|
188
|
+
| `stage:<阶段>` | 依据持久证据到达的纪律阶段:`grill`、`design`、`red`、`green`、`review`、`verify`。 |
|
|
189
|
+
| `test:<颜色>` | 最近一次测试运行的颜色:`none`、`red`、`green`。 |
|
|
190
|
+
| `edits:<n>` | 从会话日志折叠出的实现编辑数(写测试文件不计入)。 |
|
|
191
|
+
| `spec:yes` / `spec:no` | 日志中是否存在已提交的 `doublecheck_spec`。 |
|
|
192
|
+
| `review:yes` / `review:no` | 是否存在对手审查记录。 |
|
|
193
|
+
| `goal:<摘录>` | 已提交的 spec 目标,会被裁剪。仅当 `spec:yes` 时出现。 |
|
|
194
|
+
|
|
195
|
+
这一行的长度**由构造保证有界**,因此不会被任何渲染器截断:
|
|
196
|
+
|
|
197
|
+
- 硬预算:**120 个字符**。凡是原样保留 120 字符以内取值的渲染器——微信桥的通用投影渲染器就是其中之一——都会完整显示这一行。旧的「对象」形态**空态就有 131 个字符**,这正是裁决与红灯数过去总被切掉的原因。
|
|
198
|
+
- 前三段是**无损的**。红灯数超过 `9999`、编辑数超过 `99999` 时打印为 `9999+` / `99999+`,而不会无限增长;非有限值或负数打印为 `0`。
|
|
199
|
+
- `goal:` 是唯一有损的一段。它会被压成单行(换行与控制字符变空格,`|` 变 `/`),并裁剪到剩余预算,最多 40 个字符,被裁剪时以 `…` 结尾。完整目标见 `/doublecheck status` 与交付报告。
|
|
200
|
+
- 这是一眼速览,不是记录本身:`/gate status` 与 `/doublecheck status` 渲染完整状态。
|
|
201
|
+
|
|
202
|
+
该投影的 `stateVersion` 为 **3**。递增它会丢弃旧版本写入的持久化投影缓存行,并以该键从会话日志重新折叠,因此升级后的会话会重算这一行,而不是显示过期值。折叠状态本身没有变化,升级前的会话不会因此加载失败。
|
|
175
203
|
|
|
176
204
|
## 门禁阶段
|
|
177
205
|
|
|
@@ -308,7 +336,7 @@ pnpm run pack:check # build + pack the tarball
|
|
|
308
336
|
|
|
309
337
|
## PerryLink DSH Plugin Family
|
|
310
338
|
|
|
311
|
-
本项目是 [PerryLink](https://github.com/PerryLink) 维护的 DeepSeek Harness 插件家族成员:**
|
|
339
|
+
本项目是 [PerryLink](https://github.com/PerryLink) 维护的 DeepSeek Harness 插件家族成员:**34 个活跃维护**,名册共 **42** 个,其中 **5 个已冻结**、**3 个已退役**——每一个都在下表保留自己的行,原因见「状态」列。如果它能帮到你,其他的也会:
|
|
312
340
|
|
|
313
341
|
| Plugin | 一句话 | 状态 |
|
|
314
342
|
|---|---|---|
|
|
@@ -336,7 +364,7 @@ pnpm run pack:check # build + pack the tarball
|
|
|
336
364
|
| **[dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions)** | LSP diagnostics, formatting, completion, code actions and rename over language servers | |
|
|
337
365
|
| **[dsh-mask](https://github.com/PerryLink/dsh-mask)** | PII masking middleware: anonymize at the model boundary, restore at the display layer | |
|
|
338
366
|
| **[dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel)** | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors | |
|
|
339
|
-
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
|
|
367
|
+
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool | |
|
|
340
368
|
| **[dsh-observe](https://github.com/PerryLink/dsh-observe)** | OpenTelemetry and Langfuse observability exporter for DeepSeek Harness. | |
|
|
341
369
|
| **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles-equivalent runtime style switching | |
|
|
342
370
|
| **[dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules)** | Claude Code-style declarative allow/deny/ask permission rules with audit | |
|
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ The delivery quality gate for DeepSeek Harness: grill the requirements, test the
|
|
|
42
42
|
|
|
43
43
|
Requirements get interrogated before the first edit; delivery is proven, never claimed.
|
|
44
44
|
|
|
45
|
-
](https://raw.githubusercontent.com/PerryLink/dsh-doublecheck/main/docs/assets/dsh-doublecheck-hero.png)
|
|
46
46
|
|
|
47
47
|

|
|
48
48
|
|
|
@@ -180,6 +180,34 @@ Misconfiguration fails loud at load: invalid regexes, empty or duplicated name l
|
|
|
180
180
|
| `gate` | live config field | The pluggable checklist is the guard row's one `.volatile()` field: it is edited from the row's settings card (namespace = the row's profile entry id) and read once at load. |
|
|
181
181
|
| `strict.patch.yml` | overlay | Every gate on at `block` intensity plus the coverage requirement, in one patch layer. |
|
|
182
182
|
| `dsh-doublecheck/invariant` | companion row | Reports package-owned write-path contradictions through the host `invariants` registry. |
|
|
183
|
+
| `doublecheck` | session projection | One bounded status line a client renders: stage, latest gate verdict, red-light count, edits, spec/review flags. See [Reading the status line](#reading-the-status-line). |
|
|
184
|
+
|
|
185
|
+
### Reading the status line
|
|
186
|
+
|
|
187
|
+
The `doublecheck` session projection is a plain **string**, and it is deliberately one line — the whole point is that a client can show it without cutting anything off. A chat bridge's `/status`, a status bar, or any custom consumer renders it as it is:
|
|
188
|
+
|
|
189
|
+
```text
|
|
190
|
+
gate:rework r2 | stage:green | test:green edits:12 spec:yes review:yes | goal:ship parser fix
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
| Segment | Reads as |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `gate:<verdict> r<n>` | The latest delivery-gate verdict — `none` before any run, `deliverable`, or `rework` — and its red-light count `r<n>`. |
|
|
196
|
+
| `stage:<stage>` | The discipline stage reached on durable evidence: `grill`, `design`, `red`, `green`, `review`, `verify`. |
|
|
197
|
+
| `test:<color>` | The latest test-run color: `none`, `red`, `green`. |
|
|
198
|
+
| `edits:<n>` | Implementation edits folded from the session log (test-file writes are not counted). |
|
|
199
|
+
| `spec:yes` / `spec:no` | Whether a committed `doublecheck_spec` exists. |
|
|
200
|
+
| `review:yes` / `review:no` | Whether an adversary review record exists. |
|
|
201
|
+
| `goal:<excerpt>` | The committed spec goal, clipped. Present only when `spec:yes`. |
|
|
202
|
+
|
|
203
|
+
The line is bounded **by construction**, so no renderer truncates it:
|
|
204
|
+
|
|
205
|
+
- Hard budget: **120 characters**. A renderer that keeps values up to 120 characters — the WeChat bridge's generic projection renderer is one — shows the entire line. The old object-shaped payload was 131 characters when *empty*, which is why the verdict and the red count used to be cut off.
|
|
206
|
+
- The first three segments are **lossless**. Counts above `9999` red lights or `99999` edits print as `9999+` / `99999+` instead of growing without bound; a non-finite or negative count prints as `0`.
|
|
207
|
+
- `goal:` is the one lossy segment. It is flattened to a single line (newlines and control characters become spaces, `|` becomes `/`) and clipped to the budget that is left, at most 40 characters, ending in `…` when it was clipped. The complete goal is in `/doublecheck status` and the delivery report.
|
|
208
|
+
- This is a glance, not the record: `/gate status` and `/doublecheck status` render the full state.
|
|
209
|
+
|
|
210
|
+
The projection's `stateVersion` is **3**. Bumping it discards a persisted projection-cache row written by an older version and refolds the key from the session log, so an upgraded session recomputes the line instead of showing a stale one. The persisted fold state itself is unchanged, and a pre-upgrade session never fails to load.
|
|
183
211
|
|
|
184
212
|
## Gate phases
|
|
185
213
|
|
|
@@ -316,7 +344,7 @@ Static evidence: `dsh-plugin-doctor` K10–K13 report `pass` for every check on
|
|
|
316
344
|
|
|
317
345
|
## PerryLink DSH Plugin Family
|
|
318
346
|
|
|
319
|
-
This project is one of the **
|
|
347
|
+
This project is one of the **34 actively maintained** DeepSeek Harness plugins from [PerryLink](https://github.com/PerryLink) — the roster is **42**, of which **5** are frozen and **3** retired; every one keeps its row below, with the reason in the Status column. If this one helps you, the others likely will too:
|
|
320
348
|
|
|
321
349
|
| Plugin | One-liner | Status |
|
|
322
350
|
|---|---|---|
|
|
@@ -344,7 +372,7 @@ This project is one of the **33 actively maintained** DeepSeek Harness plugins f
|
|
|
344
372
|
| **[dsh-lsp-actions](https://github.com/PerryLink/dsh-lsp-actions)** | LSP diagnostics, formatting, completion, code actions and rename over language servers | |
|
|
345
373
|
| **[dsh-mask](https://github.com/PerryLink/dsh-mask)** | PII masking middleware: anonymize at the model boundary, restore at the display layer | |
|
|
346
374
|
| **[dsh-mcp-panel](https://github.com/PerryLink/dsh-mcp-panel)** | Read-only MCP runtime panel: /mcp command + Settings tab with status, tools and errors | |
|
|
347
|
-
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool |
|
|
375
|
+
| **[dsh-memento](https://github.com/PerryLink/dsh-memento)** | Approval-gated cross-session memory: ctx.memory seam + SQLite + memory tool | |
|
|
348
376
|
| **[dsh-observe](https://github.com/PerryLink/dsh-observe)** | OpenTelemetry and Langfuse observability exporter for DeepSeek Harness. | |
|
|
349
377
|
| **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles-equivalent runtime style switching | |
|
|
350
378
|
| **[dsh-permission-rules](https://github.com/PerryLink/dsh-permission-rules)** | Claude Code-style declarative allow/deny/ask permission rules with audit | |
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal shape of the 0.2.1+ `subagents.startActivation` surface. The alpha
|
|
3
|
+
* host line only exposes `start(provider, request)`; keeping this structural
|
|
4
|
+
* lets the same guard code compile against both.
|
|
5
|
+
*/
|
|
6
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
7
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
8
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
9
|
+
export interface GuardActivationService {
|
|
10
|
+
startActivation?: (spec: {
|
|
11
|
+
provider: string;
|
|
12
|
+
label: string;
|
|
13
|
+
request: {
|
|
14
|
+
prompt: ContentBlock[];
|
|
15
|
+
parent: Agent;
|
|
16
|
+
outputSchema: unknown;
|
|
17
|
+
maxDepth?: number;
|
|
18
|
+
agentOptions?: Record<string, unknown>;
|
|
19
|
+
agentPreset: string;
|
|
20
|
+
};
|
|
21
|
+
signal: AbortSignal;
|
|
22
|
+
delivery: 'caller';
|
|
23
|
+
}) => Promise<{
|
|
24
|
+
childId: SessionId;
|
|
25
|
+
result: Promise<{
|
|
26
|
+
stopReason: string;
|
|
27
|
+
structured?: unknown;
|
|
28
|
+
}>;
|
|
29
|
+
dispose(): Promise<void>;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/guard/gate.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface GateReviewerConfig {
|
|
|
38
38
|
provider: string;
|
|
39
39
|
/** Model route for the local reviewer; null means the main model reviews. */
|
|
40
40
|
model: string | null;
|
|
41
|
+
/** Read-only Agent Preset for the startActivation path. */
|
|
42
|
+
preset?: string;
|
|
41
43
|
/** Tools the local reviewer may call (read-only by default). */
|
|
42
44
|
tools: string[];
|
|
43
45
|
/** Hard time budget for one local reviewer run. */
|
package/lib/guard/gate.js
CHANGED
|
@@ -114,6 +114,7 @@ export const GateConfigSchema = z.object({
|
|
|
114
114
|
consistency: z.object({
|
|
115
115
|
enabled: z.boolean().default(true),
|
|
116
116
|
provider: z.string().min(1).default('fork'),
|
|
117
|
+
preset: z.string(),
|
|
117
118
|
model: z.union([z.string(), z.const(null)]).default(null),
|
|
118
119
|
tools: z.array(z.string()).default(['read', 'glob', 'grep']),
|
|
119
120
|
timeoutMs: z.number().default(120000),
|
|
@@ -123,6 +124,7 @@ export const GateConfigSchema = z.object({
|
|
|
123
124
|
enabled: z.boolean().default(true),
|
|
124
125
|
engine: z.union(['auto', 'local']).default('auto'),
|
|
125
126
|
provider: z.string().min(1).default('fork'),
|
|
127
|
+
preset: z.string(),
|
|
126
128
|
model: z.union([z.string(), z.const(null)]).default(null),
|
|
127
129
|
tools: z.array(z.string()).default(['read', 'glob', 'grep']),
|
|
128
130
|
timeoutMs: z.number().default(120000),
|
|
@@ -248,15 +250,37 @@ async function runLocalReviewer(ctx, reviewer, agent, task, label, signal) {
|
|
|
248
250
|
const runSignal = AbortSignal.any([signal, timeout]);
|
|
249
251
|
let run;
|
|
250
252
|
try {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
253
|
+
const activation = subagents;
|
|
254
|
+
if (typeof activation.startActivation === 'function') {
|
|
255
|
+
if (reviewer.preset === undefined) {
|
|
256
|
+
return { findings: null, unavailable: 'this host uses subagents.startActivation; set the reviewer preset to a read-only Agent Preset' };
|
|
257
|
+
}
|
|
258
|
+
const started = await activation.startActivation({
|
|
259
|
+
provider: reviewer.provider,
|
|
260
|
+
label,
|
|
261
|
+
request: {
|
|
262
|
+
prompt: [{ type: 'text', text: task }],
|
|
263
|
+
parent: agent,
|
|
264
|
+
outputSchema: GATE_FINDINGS_SCHEMA,
|
|
265
|
+
agentPreset: reviewer.preset,
|
|
266
|
+
...reviewer.model !== null ? { agentOptions: { model: reviewer.model } } : {},
|
|
267
|
+
},
|
|
268
|
+
signal: runSignal,
|
|
269
|
+
delivery: 'caller',
|
|
270
|
+
});
|
|
271
|
+
run = started;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
run = await subagents.start(reviewer.provider, {
|
|
275
|
+
label,
|
|
276
|
+
prompt: [{ type: 'text', text: task }],
|
|
277
|
+
parent: agent,
|
|
278
|
+
signal: runSignal,
|
|
279
|
+
...reviewer.model !== null ? { agentOptions: { model: reviewer.model } } : {},
|
|
280
|
+
outputSchema: GATE_FINDINGS_SCHEMA,
|
|
281
|
+
toolFilter: { allow: reviewer.tools },
|
|
282
|
+
});
|
|
283
|
+
}
|
|
260
284
|
}
|
|
261
285
|
catch (error) {
|
|
262
286
|
return { findings: null, unavailable: String(error) };
|
package/lib/guard/index.d.ts
CHANGED
|
@@ -58,6 +58,8 @@ export interface Config {
|
|
|
58
58
|
adversaryModel: string | null;
|
|
59
59
|
/** Subagent provider name the critic runs on (default `fork`). */
|
|
60
60
|
adversaryProvider: string;
|
|
61
|
+
/** Read-only Agent Preset for hosts that use startActivation. */
|
|
62
|
+
adversaryPreset?: string;
|
|
61
63
|
/** Maximum structured findings injected into the session. */
|
|
62
64
|
adversaryMaxFindings: number;
|
|
63
65
|
/** Tools the critic may call (read-only by default; never mutation tools). */
|
|
@@ -125,6 +127,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
125
127
|
}>>, "defined">;
|
|
126
128
|
adversaryModel: z<string | null, string | null, "defined">;
|
|
127
129
|
adversaryProvider: z<string, string, "defined">;
|
|
130
|
+
adversaryPreset: z<string, string, "plain">;
|
|
128
131
|
adversaryMaxFindings: z<number, number, "defined">;
|
|
129
132
|
adversaryTools: z<string[], string[], "defined">;
|
|
130
133
|
adversaryTimeoutMs: z<number, number, "defined">;
|
|
@@ -150,6 +153,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
150
153
|
}>>, "defined">;
|
|
151
154
|
adversaryModel: z<string | null, string | null, "defined">;
|
|
152
155
|
adversaryProvider: z<string, string, "defined">;
|
|
156
|
+
adversaryPreset: z<string, string, "plain">;
|
|
153
157
|
adversaryMaxFindings: z<number, number, "defined">;
|
|
154
158
|
adversaryTools: z<string[], string[], "defined">;
|
|
155
159
|
adversaryTimeoutMs: z<number, number, "defined">;
|
package/lib/guard/index.js
CHANGED
|
@@ -94,6 +94,7 @@ export const Config = z.object({
|
|
|
94
94
|
}).default({ grill: true, tdd: true, adversary: false }),
|
|
95
95
|
adversaryModel: z.union([z.string(), z.const(null)]).default(null),
|
|
96
96
|
adversaryProvider: z.string().min(1).default('fork'),
|
|
97
|
+
adversaryPreset: z.string(),
|
|
97
98
|
adversaryMaxFindings: z.number().default(5),
|
|
98
99
|
adversaryTools: z.array(z.string()).default(['read', 'glob', 'grep']),
|
|
99
100
|
adversaryTimeoutMs: z.number().default(120000),
|
|
@@ -577,7 +578,8 @@ export function apply(ctx, config) {
|
|
|
577
578
|
}
|
|
578
579
|
});
|
|
579
580
|
// The `doublecheck` session projection: folds the same durable discipline
|
|
580
|
-
// facts the guard reads into
|
|
581
|
+
// facts the guard reads into the wire value a client renders — one bounded
|
|
582
|
+
// status line (`renderDoublecheckLine`), not a JSON object.
|
|
581
583
|
// Activates only when a projection registry is composed.
|
|
582
584
|
ctx.inject(['sessionProjections'], (projectionCtx) => {
|
|
583
585
|
const registry = projectionCtx.get('sessionProjections');
|
|
@@ -597,7 +599,16 @@ export function apply(ctx, config) {
|
|
|
597
599
|
viewSchema: doublecheckViewSchema,
|
|
598
600
|
view: viewDoublecheck,
|
|
599
601
|
},
|
|
600
|
-
|
|
602
|
+
// v3: the wire value changed type (object -> one bounded line). The
|
|
603
|
+
// bump is the persisted-cache invalidation the registry documents, and
|
|
604
|
+
// it IS the backward-compatibility story: the state fields themselves
|
|
605
|
+
// are unchanged and still pass `doublecheckStateSchema`, but a row
|
|
606
|
+
// written at `ver: 2` is discarded on a `ver: 3` unit and the key
|
|
607
|
+
// refolds from `init` over the full log (the host's `restoreFloor`
|
|
608
|
+
// drops to seq 0 on any version mismatch). No migration code, and a
|
|
609
|
+
// pre-upgrade session degrades to a recomputed line instead of a
|
|
610
|
+
// stale one or a crash.
|
|
611
|
+
stateVersion: 3,
|
|
601
612
|
}), 'dsh-doublecheck: projection');
|
|
602
613
|
});
|
|
603
614
|
// The invariant companion, registered from the main plugin so its checks
|
|
@@ -616,6 +627,7 @@ export function apply(ctx, config) {
|
|
|
616
627
|
function adversaryConfig(config) {
|
|
617
628
|
return {
|
|
618
629
|
adversaryProvider: config.adversaryProvider,
|
|
630
|
+
adversaryPreset: config.adversaryPreset,
|
|
619
631
|
adversaryModel: config.adversaryModel ?? null,
|
|
620
632
|
adversaryMaxFindings: config.adversaryMaxFindings,
|
|
621
633
|
adversaryTools: config.adversaryTools,
|
package/lib/guard/review.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ import { type GuardProse, type ProseLanguage } from './prose.js';
|
|
|
21
21
|
/** The adversary knobs the review runner reads. */
|
|
22
22
|
export interface AdversaryConfig {
|
|
23
23
|
adversaryProvider: string;
|
|
24
|
+
/** Read-only Agent Preset for the startActivation path. */
|
|
25
|
+
adversaryPreset?: string;
|
|
24
26
|
adversaryModel: string | null;
|
|
25
27
|
adversaryMaxFindings: number;
|
|
26
28
|
adversaryTools: string[];
|
package/lib/guard/review.js
CHANGED
|
@@ -99,15 +99,37 @@ export async function runAdversaryReview(ctx, config, agent, turnSignal) {
|
|
|
99
99
|
const signal = turnSignal === undefined ? timeout : AbortSignal.any([turnSignal, timeout]);
|
|
100
100
|
let run;
|
|
101
101
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
const activation = subagents;
|
|
103
|
+
if (typeof activation.startActivation === 'function') {
|
|
104
|
+
if (config.adversaryPreset === undefined) {
|
|
105
|
+
return { verdict: 'unavailable', findings: [], text: prose.reviewUnavailableFailed('this host uses subagents.startActivation; set adversaryPreset to a read-only Agent Preset') };
|
|
106
|
+
}
|
|
107
|
+
const started = await activation.startActivation({
|
|
108
|
+
provider: config.adversaryProvider,
|
|
109
|
+
label: 'doublecheck-adversary',
|
|
110
|
+
request: {
|
|
111
|
+
prompt: [{ type: 'text', text: prose.criticTask }],
|
|
112
|
+
parent: agent,
|
|
113
|
+
outputSchema: REVIEW_OUTPUT_SCHEMA,
|
|
114
|
+
agentPreset: config.adversaryPreset,
|
|
115
|
+
...config.adversaryModel !== null ? { agentOptions: { model: config.adversaryModel } } : {},
|
|
116
|
+
},
|
|
117
|
+
signal,
|
|
118
|
+
delivery: 'caller',
|
|
119
|
+
});
|
|
120
|
+
run = started;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
run = await subagents.start(config.adversaryProvider, {
|
|
124
|
+
label: 'doublecheck-adversary',
|
|
125
|
+
prompt: [{ type: 'text', text: prose.criticTask }],
|
|
126
|
+
parent: agent,
|
|
127
|
+
signal,
|
|
128
|
+
...config.adversaryModel !== null ? { agentOptions: { model: config.adversaryModel } } : {},
|
|
129
|
+
outputSchema: REVIEW_OUTPUT_SCHEMA,
|
|
130
|
+
toolFilter: { allow: config.adversaryTools },
|
|
131
|
+
});
|
|
132
|
+
}
|
|
111
133
|
}
|
|
112
134
|
catch (error) {
|
|
113
135
|
// Provider absence, capability rejection, or a child that could not be
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-doublecheck",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.26",
|
|
4
4
|
"description": "Delivery quality gate for DeepSeek Harness: grill the requirements, test the implementation, prove the delivery, and gate the handoff with a deliverable/rework decision.",
|
|
5
5
|
"packageManager": "pnpm@11.7.0",
|
|
6
6
|
"repository": {
|