dsh-doublecheck 0.5.0 → 0.6.0
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 +17 -0
- package/README.es.md +39 -12
- package/README.hi.md +56 -21
- package/README.md +29 -13
- package/README.pt.md +39 -12
- package/README.zh.md +28 -12
- package/lib/domain/evidence.d.ts +4 -1
- package/lib/domain/evidence.js +7 -2
- package/lib/domain/vocabulary.d.ts +2 -0
- package/lib/domain/vocabulary.js +4 -0
- package/lib/grill/index.js +8 -1
- package/lib/guard/command.d.ts +7 -0
- package/lib/guard/command.js +25 -21
- package/lib/guard/index.d.ts +2 -0
- package/lib/guard/index.js +16 -5
- package/lib/guard/prose.d.ts +47 -0
- package/lib/guard/prose.js +65 -0
- package/lib/guard/review.d.ts +8 -0
- package/lib/guard/review.js +20 -20
- package/lib/invariant.js +2 -3
- package/package.json +13 -4
- package/strict.patch.yml +33 -0
package/README.pt.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> **Verifique duas vezes antes de publicar: interrogue os requisitos, teste a implementação, comprove a entrega.**
|
|
4
4
|
|
|
5
|
-
[](https://github.com/PerryLink/dsh-doublecheck/releases)
|
|
6
|
+
[](https://www.npmjs.com/package/dsh-doublecheck)
|
|
6
7
|
[](LICENSE)
|
|
7
8
|
[](https://github.com/topics/dsh-plugin)
|
|
8
9
|
|
|
@@ -35,20 +36,23 @@ grill ──▶ design ──▶ red ──▶ green ──▶ review ──▶
|
|
|
35
36
|
| **review** | Um crítico adversário bifurcado audita a entrega contra o spec. | ✅ v0.3 |
|
|
36
37
|
| **verify** | `doublecheck_report` + um workflow de verificação por dimensão comprovam a entrega. | ✅ v0.4 |
|
|
37
38
|
|
|
38
|
-
## Recursos
|
|
39
|
+
## Recursos
|
|
39
40
|
|
|
40
41
|
- 🔥 **Skill `grill-requirements`** — um skill empacotado no formato Agent Skills que interroga a tarefa em seis dimensões (**objetivo, escopo, critérios de aceite, modos de falha, prioridades, não-objetivos**) usando a UI nativa `ask_user_question` do DSH, recusa escrever código até o consenso e registra o contrato.
|
|
41
42
|
- 🧰 **Skills de etapa para o ciclo inteiro** — `red-green-tdd` (escreva o teste que falha, rode red, implemente, rode green), `delivery-review` (autorrevisão adversária contra o spec uma vez em green) e `delivery-proof` (consolide a evidência no relatório de entrega antes de declarar concluído) juntam-se ao `grill-requirements`: as seis etapas têm orientação de modelo, não só a primeira.
|
|
42
|
-
- 📜 **Ferramenta `doublecheck_spec`** — grava o spec acordado no log da sessão e escreve uma cópia em markdown no workspace, para o contrato sobreviver à conversa.
|
|
43
|
+
- 📜 **Ferramenta `doublecheck_spec`** — grava o spec acordado no log da sessão e escreve uma cópia em markdown no workspace, para o contrato sobreviver à conversa. Dimensões vazias ou só com espaços são rejeitadas no commit (v0.6): o grill precisa assentar as seis antes de o spec contar.
|
|
43
44
|
- 🔄 **Re-grill na mudança de tarefa** — um spec registrado cobre a própria tarefa: um novo pedido direto do usuário após o último spec reabre o portão grill para esse follow-up, em vez de herdar o contrato anterior em silêncio.
|
|
44
45
|
- 🛡️ **Guard de disciplina** — um portão suave no pipeline de políticas de ferramentas. Tarefa vaga + sem spec + rumo a `edit`/`write` → **lembrar**, **pedir aprovação humana** ou **bloquear**, conforme `intensity`.
|
|
45
|
-
- 🟥🟩 **Portões de evidência red/green** (`modules.tdd`) — verificações duras sobre o log da sessão: uma edição de implementação exige um **teste que falha registrado** desde o último teste que passa (escrever arquivos de teste é sempre permitido — é assim que o passo red acontece), e um turno que termina com edições mas sem nenhum teste que passa recebe um lembrete green injetado.
|
|
46
|
-
- 👁️ **Revisão adversária** (`modules.adversary`) — assim que a entrega chega ao green, um subagente crítico bifurcado (seam nativo de subagentes do DSH, provider `fork` por padrão) audita a sessão contra o spec registrado com postura adversária e devolve achados estruturados. `remind` injeta a crítica; `warn`/`block` ainda direcionam uma rodada para o modelo responder aos achados. `adversaryModel` roteia o crítico para um modelo separado; a allowlist de ferramentas do crítico é somente leitura por padrão. Os achados trafegam pela fonte de mensagens durável `doublecheck-review`. A revisão se rearma quando o crítico termina: edições de implementação após o último registro de revisão disparam outra rodada, e cancelar o turno aborta o crítico em voo.
|
|
46
|
+
- 🟥🟩 **Portões de evidência red/green** (`modules.tdd`) — verificações duras sobre o log da sessão: uma edição de implementação exige um **teste que falha registrado** desde o último teste que passa (escrever arquivos de teste é sempre permitido — é assim que o passo red acontece), e um turno que termina com edições mas sem nenhum teste que passa recebe um lembrete green injetado. Ferramentas guard personalizadas funcionam de cara: os portões leem as chaves de argumento `file_path` e `path`, e uma chamada que não nomeia arquivo nenhum não é tratada como edição de implementação.
|
|
47
|
+
- 👁️ **Revisão adversária** (`modules.adversary`) — assim que a entrega chega ao green, um subagente crítico bifurcado (seam nativo de subagentes do DSH, provider `fork` por padrão) audita a sessão contra o spec registrado com postura adversária e devolve achados estruturados, ordenados com blockers primeiro. `remind` injeta a crítica; `warn`/`block` ainda direcionam uma rodada para o modelo responder aos achados. `adversaryModel` roteia o crítico para um modelo separado; a allowlist de ferramentas do crítico é somente leitura por padrão. Os achados trafegam pela fonte de mensagens durável `doublecheck-review`. A revisão se rearma quando o crítico termina: edições de implementação após o último registro de revisão disparam outra rodada, e cancelar o turno aborta o crítico em voo.
|
|
48
|
+
- 🌐 **Superfície de modelo totalmente localizada** — toda string visível ao modelo que o pacote injeta ou responde (lembretes, feedback de negação/consulta, direcionamento de revisão, avisos de troca, respostas do `/doublecheck` e o prompt de tarefa do crítico) respeita `language: 'en' | 'zh'`; os documentos spec/report do workspace mantêm seus cabeçalhos estáveis em inglês.
|
|
47
49
|
- 📊 **Relatório doublecheck + workflow de verificação** (`doublecheck_report`, v0.4) — consolida a evidência de disciplina da sessão (spec, cronologia red/green, achados da revisão, edições) em um relatório de entrega com um veredito derivado (`grill → draft → red → green → objections/verified → proven/challenged/unverified`), gravado no workspace. Com `verify`, os verificadores por dimensão rodam pelo seam de workflow do DSH (`verifyMode: all` lança um verificador paralelo por dimensão; `single` executa um combinado) e os vereditos se dobram no relatório — `proven` exige um veredito para cada dimensão.
|
|
48
50
|
- 🚦 **Portão de entrega** — no limite do turno, uma entrega que chegou ao green sem `doublecheck_report` registrado recebe um lembrete de relatório esperado antes de declarar concluído; um relatório bem-sucedido avança a etapa para `verify`.
|
|
49
|
-
- 🔁 **Estado durável** — todo artefato visível ao modelo (spec, lembretes, feedback de negação, achados da revisão, o interruptor `/doublecheck on|off`) fica no log da sessão; as decisões dos portões derivam só do log (`tool/call` + `tool/result`, incluindo os sub-despachos do Code Mode), então sessões retomadas ou bifurcadas se comportam igual. `remindOnce` também é durável: uma sessão que já recebeu um lembrete nunca o recebe duas vezes, mesmo após reiniciar.
|
|
50
|
-
- ⌨️ **Comando de sessão `/doublecheck`** — `status` informa o interruptor efetivo, os módulos configurados e
|
|
51
|
+
- 🔁 **Estado durável** — todo artefato visível ao modelo (spec, lembretes, feedback de negação, achados da revisão, o interruptor `/doublecheck on|off`) fica no log da sessão; as decisões dos portões derivam só do log (`tool/call` + `tool/result`, incluindo os sub-despachos do Code Mode), então sessões retomadas ou bifurcadas se comportam igual. `remindOnce` também é durável: uma sessão que já recebeu um lembrete nunca o recebe duas vezes, mesmo após reiniciar. A dobra do interruptor viaja num snapshot incremental, então sessões longas ficam em O(eventos novos) por chamada de ferramenta.
|
|
52
|
+
- ⌨️ **Comando de sessão `/doublecheck`** — `status` informa o interruptor efetivo, os módulos configurados, a intensidade de aplicação e os fatos de etapa dobrados (spec, cor dos testes, revisão, contagem de edições); `report` dobra o relatório de entrega na hora; `on|off` grava o override durável `doublecheck/state` e injeta um aviso de troca.
|
|
51
53
|
- 📚 **Ferramenta `doublecheck_skills`** — lista e carrega os skills do pacote pelo seam oficial do registro de skills.
|
|
54
|
+
- 🔒 **Overlay estrito** — `strict.patch.yml` liga todos os portões com intensidade `block` numa única camada de patch (vem com o pacote).
|
|
55
|
+
- 🧩 **Companheiro invariante independente** — a linha `dsh-doublecheck/invariant` é uma exportação de subrota real: relata contradições do caminho de escrita do pacote (forma spec/report/review) pelo registro `invariants` do host sem carregar o guard.
|
|
52
56
|
|
|
53
57
|
## Demo
|
|
54
58
|
|
|
@@ -76,13 +80,19 @@ As duas linhas de plugin ativam automaticamente com o profile. Instalação por
|
|
|
76
80
|
|
|
77
81
|
```sh
|
|
78
82
|
pnpm pack
|
|
79
|
-
dsh plugin --profile <name> add ./dsh-doublecheck-0.
|
|
83
|
+
dsh plugin --profile <name> add ./dsh-doublecheck-0.6.0.tgz
|
|
80
84
|
```
|
|
81
85
|
|
|
82
86
|
Instalação por git dispensa npm:
|
|
83
87
|
|
|
84
88
|
```sh
|
|
85
|
-
dsh plugin --profile <name> add "github:PerryLink/dsh-doublecheck#v0.
|
|
89
|
+
dsh plugin --profile <name> add "github:PerryLink/dsh-doublecheck#v0.6.0"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Para um modo estrito sem configuração (todos os portões ligados, intensidade `block`), aplique o overlay incluído por cima do bundle patch:
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
dsh --profile <name> --patch ./node_modules/dsh-doublecheck/strict.patch.yml
|
|
86
96
|
```
|
|
87
97
|
|
|
88
98
|
## Desinstalação
|
|
@@ -131,6 +141,7 @@ Sobrescreva qualquer linha **por id** no `cordis.patch.yml` do profile. Um patch
|
|
|
131
141
|
- '(?:^|[;&|]\s*)(?:(?:pnpm|npm|npx|yarn|bun)(?:\s+run)?\s+(?:test|vitest|jest|mocha)(?:\s|$))'
|
|
132
142
|
- '(?:^|[;&|]\s*)(?:(?:pytest|go\s+test|cargo\s+test|make\s+test|ctest)(?:\s|$))'
|
|
133
143
|
- '(?:^|[;&|]\s*)(?:node\s+--test(?:\s|$))'
|
|
144
|
+
- '(?:^|[;&|]\s*)(?:deno\s+test|uv\s+run\s+pytest)(?:\s|$)'
|
|
134
145
|
reportMutationTools: ['edit', 'write']
|
|
135
146
|
reportTestFilePatterns:
|
|
136
147
|
- '(^|[\\/])(tests?|__tests__|specs?)([\\/]|$)'
|
|
@@ -156,11 +167,14 @@ Sobrescreva qualquer linha **por id** no `cordis.patch.yml` do profile. Um patch
|
|
|
156
167
|
- '(?:^|[;&|]\s*)(?:(?:pnpm|npm|npx|yarn|bun)(?:\s+run)?\s+(?:test|vitest|jest|mocha)(?:\s|$))'
|
|
157
168
|
- '(?:^|[;&|]\s*)(?:(?:pytest|go\s+test|cargo\s+test|make\s+test|ctest)(?:\s|$))'
|
|
158
169
|
- '(?:^|[;&|]\s*)(?:node\s+--test(?:\s|$))'
|
|
170
|
+
- '(?:^|[;&|]\s*)(?:deno\s+test|uv\s+run\s+pytest)(?:\s|$)'
|
|
159
171
|
testFilePatterns:
|
|
160
172
|
- '(^|[\\/])(tests?|__tests__|specs?)([\\/]|$)'
|
|
161
173
|
- '\\.(test|spec)\\.[A-Za-z0-9]+$'
|
|
162
174
|
```
|
|
163
175
|
|
|
176
|
+
O `strict.patch.yml` incluído é exatamente esta linha guard com `intensity: block` e todos os módulos ligados — aplique-o como camada de patch depois do bundle patch para o modo estrito sem editar um profile à mão.
|
|
177
|
+
|
|
164
178
|
### `intensity`
|
|
165
179
|
|
|
166
180
|
| Valor | Comportamento diante de um `edit`/`write` controlado pelo portão |
|
|
@@ -174,13 +188,13 @@ Sobrescreva qualquer linha **por id** no `cordis.patch.yml` do profile. Um patch
|
|
|
174
188
|
| Chave | Padrão | Significado |
|
|
175
189
|
|---|---|---|
|
|
176
190
|
| `modules.grill` | `true` | `false` desativa o portão grill. O interruptor dos skills/ferramentas grill é o flag `disabled` da linha deles. |
|
|
177
|
-
| `modules.tdd` | `
|
|
191
|
+
| `modules.tdd` | `true` | `true` ativa os portões de evidência red/green (v0.2); ativado por padrão desde v0.5. |
|
|
178
192
|
| `modules.adversary` | `false` | `true` ativa a revisão do crítico bifurcado no green (v0.3); usa o seam `ctx.subagents` — um seam ausente se resolve como um aviso «indisponível». |
|
|
179
193
|
| `guardTools` | `['edit', 'write']` | Nomes de ferramentas de mutação que o guard vigia. |
|
|
180
194
|
| `vagueTaskMaxChars` | `200` | Tarefas mais longas nunca são tratadas como vagas. Tarefas breves que citam arquivo, caminho, URL, palavra-chave com sublinhado ou palavra-chave hifenizada são concretas. |
|
|
181
195
|
| `remindOnce` | `true` | Injetar o lembrete de cada portão no máximo uma vez por sessão. |
|
|
182
196
|
| `testToolNames` | `['bash', 'pwsh']` | Nomes de ferramentas de shell que podem executar testes. |
|
|
183
|
-
| `testCommandPatterns` | *(pnpm/npm/yarn/bun test, pytest, go/cargo/make test, node --test)* | Expressões regulares com as quais um comando deve coincidir para contar como execução de teste. |
|
|
197
|
+
| `testCommandPatterns` | *(pnpm/npm/yarn/bun test, pytest, go/cargo/make test, node --test, deno test, uv run pytest)* | Expressões regulares com as quais um comando deve coincidir para contar como execução de teste. |
|
|
184
198
|
| `testFilePatterns` | *(diretórios de teste, `*.test.*` / `*.spec.*`)* | Expressões regulares que identificam arquivos de teste — sempre editáveis, isentos do portão red. |
|
|
185
199
|
| `adversaryModel` | `null` | Rota do modelo crítico; `null` = o modelo principal se autorrevisa. |
|
|
186
200
|
| `adversaryProvider` | `'fork'` | Nome do provider de subagentes onde o crítico roda. |
|
|
@@ -228,10 +242,23 @@ Sem mudanças no agent-loop. Todo registro é um `ctx.effect` / `ctx.on` / `regi
|
|
|
228
242
|
- Os lembretes chegam como contexto `{kind:'plugin'}`, então as UIs de transcrição os exibem como metadados de injeção.
|
|
229
243
|
- A crítica do adversário chega da mesma forma depois que o crítico se estabelece, com achados marcados por severidade; sob `warn`/`block` o ciclo é direcionado uma rodada para que o modelo os responda.
|
|
230
244
|
- `doublecheck_report` devolve o relatório consolidado como resultado de ferramenta (spec, cronologia de testes, revisão, verificação, veredito), então «comprovar a entrega» está a uma chamada de distância.
|
|
245
|
+
- `/doublecheck` responde direto na transcrição: `status` mostra o interruptor, os módulos, a intensidade e os fatos de etapa; `report` imprime o relatório dobrado; `on|off` muda o interruptor da sessão.
|
|
246
|
+
|
|
247
|
+
## Comando de sessão
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
/doublecheck status|report|on|off
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
- `status` — interruptor efetivo (o override durável vence o padrão da config), módulos configurados, intensidade de aplicação e os fatos de etapa dobrados (spec registrado, cor red/green, revisão em registro, contagem de edições).
|
|
254
|
+
- `report` — dobra o relatório de entrega a partir do log da sessão na hora (sem workflow de verificação; `doublecheck_report` é dono desse caminho).
|
|
255
|
+
- `on` / `off` — grava o evento durável `doublecheck/state` (sobrevive a reinício, retomada e bifurcação — o replay É o estado) e injeta um aviso de troca visível ao modelo.
|
|
256
|
+
|
|
257
|
+
Todas as respostas do comando respeitam o ajuste `language` da linha guard.
|
|
231
258
|
|
|
232
259
|
## Roadmap
|
|
233
260
|
|
|
234
|
-
O ciclo de disciplina de seis etapas está completo: **grill → design → red → green → review → verify** — todas embarcam neste pacote (v0.1 → v0.
|
|
261
|
+
O ciclo de disciplina de seis etapas está completo: **grill → design → red → green → review → verify** — todas embarcam neste pacote (v0.1 → v0.6). Fixtures de regressão com transcrições reais fixam as formas dos eventos duráveis (`tests/fixtures/`). Trabalho futuro: formatação de relatório mais rica, um selo de status de disciplina na Web UI e a semeadura de spec entre sessões a partir do arquivo do workspace.
|
|
235
262
|
|
|
236
263
|
## Desenvolvimento
|
|
237
264
|
|
package/README.zh.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> **发布前先双重检查:追问需求、验证实现、证明交付。**
|
|
4
4
|
|
|
5
|
-
[](https://github.com/PerryLink/dsh-doublecheck/releases)
|
|
6
|
+
[](https://www.npmjs.com/package/dsh-doublecheck)
|
|
6
7
|
[](LICENSE)
|
|
7
8
|
[](https://github.com/topics/dsh-plugin)
|
|
8
9
|
[](https://github.com/PerryLink/dsh-doublecheck/actions/workflows/ci.yml)
|
|
@@ -35,20 +36,23 @@ grill ──▶ design ──▶ red ──▶ green ──▶ review ──▶
|
|
|
35
36
|
| **review** | 派生对抗 critic 子代理,把交付与 spec 逐条对质。 | ✅ v0.3 |
|
|
36
37
|
| **verify** | `doublecheck_report` + 逐维度核对 workflow,证明交付。 | ✅ v0.4 |
|
|
37
38
|
|
|
38
|
-
##
|
|
39
|
+
## 功能
|
|
39
40
|
|
|
40
41
|
- 🔥 **`grill-requirements` 技能** —— 按通用 Agent Skills 格式打包的技能,围绕六个维度(**目标、边界、验收标准、失败模式、优先级、非目标**)连环追问,使用 DSH 原生 `ask_user_question` 界面;共识达成前拒绝写代码,并记录契约。
|
|
41
42
|
- 🧰 **覆盖全循环的阶段技能** —— `red-green-tdd`(先写失败测试、跑红、实现、跑绿)、`delivery-review`(green 之后对照 spec 做对抗式自审)、`delivery-proof`(把证据汇总成交付报告后再宣称完成)与 `grill-requirements` 一起发货,六个阶段都有模型指引,而不再只有第一阶段。
|
|
42
|
-
- 📜 **`doublecheck_spec` 工具** —— 把拷问出的 spec 写入会话日志,并在工作区落一份 markdown
|
|
43
|
+
- 📜 **`doublecheck_spec` 工具** —— 把拷问出的 spec 写入会话日志,并在工作区落一份 markdown,让契约不随对话消失。提交时拒绝空或纯空白的维度(v0.6):grill 必须把六个维度全部敲定,spec 才算数。
|
|
43
44
|
- 🔄 **任务变更重新 grill** —— 已提交的 spec 只覆盖它自己的任务:spec 提交之后用户新发的直接请求会让 grill 门对该后续请求重新开启,而不是悄悄沿用旧契约。
|
|
44
45
|
- 🛡️ **纪律 guard** —— 挂在工具策略管线上的软门。模糊任务 + 没有 spec + 直奔 `edit`/`write` → 按 `intensity` 分别**提醒**、**请求人工批准**或**拦截**。
|
|
45
|
-
- 🟥🟩 **红/绿证据门**(`modules.tdd`)—— 会话日志硬校验:实现改动要求日志里**存在失败测试运行**(自上次通过以来;写测试文件永远放行——那正是 red 步骤);回合结束时若有改动却没有通过测试运行,则注入 green
|
|
46
|
-
- 👁️ **对抗评审**(`modules.adversary`)—— 交付到达 green 后,经 DSH 原生 subagent 接缝(默认 `fork` provider)派生一个 critic 子代理,以对抗视角核对会话与已提交 spec,产出结构化 findings
|
|
46
|
+
- 🟥🟩 **红/绿证据门**(`modules.tdd`)—— 会话日志硬校验:实现改动要求日志里**存在失败测试运行**(自上次通过以来;写测试文件永远放行——那正是 red 步骤);回合结束时若有改动却没有通过测试运行,则注入 green 提醒。自定义 guard 工具开箱即用:门会读取 `file_path` 与 `path` 两种参数键,完全未命名任何文件的调用不视为实现改动。
|
|
47
|
+
- 👁️ **对抗评审**(`modules.adversary`)—— 交付到达 green 后,经 DSH 原生 subagent 接缝(默认 `fork` provider)派生一个 critic 子代理,以对抗视角核对会话与已提交 spec,产出结构化 findings,并按 blocker 优先排序返回。`remind` 只注入评审意见;`warn`/`block` 额外 steer 一轮让模型正面回应 findings。`adversaryModel` 可把评审路由到独立模型;critic 工具白名单默认只读。findings 随 `doublecheck-review` 消息源持久结构化落盘。评审会持久化重武装:最新评审记录之后的实现编辑会触发下一轮评审;取消回合会中止在途 critic。
|
|
47
48
|
- 📊 **Doublecheck 报告 + 核对 workflow**(`doublecheck_report`,v0.4)—— 把会话纪律证据(spec、红/绿时间线、评审 findings、编辑数)汇总成交付报告并推导 verdict(`grill → draft → red → green → objections/verified → proven/challenged/unverified`),落盘工作区。开启 `verify` 时,经 DSH workflow 接缝派发逐维度核对员(`verifyMode: all` 每维度并行一个;`single` 合并为一个核对员)并并入报告——`proven` 要求每个维度都有裁决。
|
|
48
49
|
- 🚦 **交付门** —— 回合结束时,已到 green 但没有 `doublecheck_report` 记录的交付会收到「报告待补」提醒;成功报告会把阶段折叠推进到 `verify`。
|
|
49
|
-
- 🔁 **持久化状态** —— 所有模型可见内容(spec、提醒、拒绝反馈、评审 findings、`/doublecheck on|off` 开关)都落会话日志;门禁判定完全由日志(`tool/call` + `tool/result`,含 Code Mode 子派发)推导,恢复/派生会话同样生效。`remindOnce`
|
|
50
|
-
-
|
|
50
|
+
- 🔁 **持久化状态** —— 所有模型可见内容(spec、提醒、拒绝反馈、评审 findings、`/doublecheck on|off` 开关)都落会话日志;门禁判定完全由日志(`tool/call` + `tool/result`,含 Code Mode 子派发)推导,恢复/派生会话同样生效。`remindOnce` 同样持久化:已收到提醒的会话重启后不会重复收到。开关折叠基于增量快照,长会话每次工具调用只摊 O(新增事件) 成本。
|
|
51
|
+
- 🌐 **全本地化的模型界面** —— 本包注入或回答的每一个模型可见字符串(提醒、拒绝/询问反馈、评审 steering、切换通知、`/doublecheck` 回复、critic 的任务提示词)都遵循 `language: 'en' | 'zh'`;工作区 spec/report 文档保持其稳定的英文标题。
|
|
52
|
+
- ⌨️ **`/doublecheck` 会话命令** —— `status` 报告生效开关、已配置模块、执行强度与折叠阶段事实(spec、测试颜色、评审、编辑数);`report` 当场折叠交付报告;`on|off` 写入持久 `doublecheck/state` 事件并注入切换通知。
|
|
51
53
|
- 📚 **`doublecheck_skills` 工具** —— 通过官方技能注册表接缝列出与加载本包技能。
|
|
54
|
+
- 🔒 **严格 overlay** —— `strict.patch.yml` 在一个 patch 层中把每道门都以 `block` 强度打开(随包发布)。
|
|
55
|
+
- 🧩 **独立 invariant 伴生行** —— `dsh-doublecheck/invariant` 子路径导出真实可用:无需加载 guard,即可通过宿主 `invariants` 注册表上报本包写入路径的矛盾(spec/report/review 结构)。
|
|
52
56
|
|
|
53
57
|
## 演示
|
|
54
58
|
|
|
@@ -76,13 +80,19 @@ dsh --profile <name> --dump-config # 应看到 "# == dsh-doublecheck" 层
|
|
|
76
80
|
|
|
77
81
|
```sh
|
|
78
82
|
pnpm pack
|
|
79
|
-
dsh plugin --profile <name> add ./dsh-doublecheck-0.
|
|
83
|
+
dsh plugin --profile <name> add ./dsh-doublecheck-0.6.0.tgz
|
|
80
84
|
```
|
|
81
85
|
|
|
82
86
|
git 安装不需要 npm:
|
|
83
87
|
|
|
84
88
|
```sh
|
|
85
|
-
dsh plugin --profile <name> add "github:PerryLink/dsh-doublecheck#v0.
|
|
89
|
+
dsh plugin --profile <name> add "github:PerryLink/dsh-doublecheck#v0.6.0"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
想要零配置的严格模式(所有门开启、`block` 强度),在 bundle patch 之上叠加随包发布的 overlay:
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
dsh --profile <name> --patch ./node_modules/dsh-doublecheck/strict.patch.yml
|
|
86
96
|
```
|
|
87
97
|
|
|
88
98
|
## 卸载
|
|
@@ -131,6 +141,7 @@ dsh plugin --profile <name> remove dsh-doublecheck
|
|
|
131
141
|
- '(?:^|[;&|]\s*)(?:(?:pnpm|npm|npx|yarn|bun)(?:\s+run)?\s+(?:test|vitest|jest|mocha)(?:\s|$))'
|
|
132
142
|
- '(?:^|[;&|]\s*)(?:(?:pytest|go\s+test|cargo\s+test|make\s+test|ctest)(?:\s|$))'
|
|
133
143
|
- '(?:^|[;&|]\s*)(?:node\s+--test(?:\s|$))'
|
|
144
|
+
- '(?:^|[;&|]\s*)(?:deno\s+test|uv\s+run\s+pytest)(?:\s|$)'
|
|
134
145
|
reportMutationTools: ['edit', 'write']
|
|
135
146
|
reportTestFilePatterns:
|
|
136
147
|
- '(^|[\\/])(tests?|__tests__|specs?)([\\/]|$)'
|
|
@@ -156,11 +167,14 @@ dsh plugin --profile <name> remove dsh-doublecheck
|
|
|
156
167
|
- '(?:^|[;&|]\s*)(?:(?:pnpm|npm|npx|yarn|bun)(?:\s+run)?\s+(?:test|vitest|jest|mocha)(?:\s|$))'
|
|
157
168
|
- '(?:^|[;&|]\s*)(?:(?:pytest|go\s+test|cargo\s+test|make\s+test|ctest)(?:\s|$))'
|
|
158
169
|
- '(?:^|[;&|]\s*)(?:node\s+--test(?:\s|$))'
|
|
170
|
+
- '(?:^|[;&|]\s*)(?:deno\s+test|uv\s+run\s+pytest)(?:\s|$)'
|
|
159
171
|
testFilePatterns:
|
|
160
172
|
- '(^|[\\/])(tests?|__tests__|specs?)([\\/]|$)'
|
|
161
173
|
- '\\.(test|spec)\\.[A-Za-z0-9]+$'
|
|
162
174
|
```
|
|
163
175
|
|
|
176
|
+
随包发布的 `strict.patch.yml` 正是这行 guard 配置在 `intensity: block` 且所有模块开启——把它作为 patch 层叠加在 bundle patch 之后,即可获得严格模式而无需手动编辑 profile。
|
|
177
|
+
|
|
164
178
|
### `intensity`
|
|
165
179
|
|
|
166
180
|
| 取值 | 对被门禁拦截的 `edit`/`write` 的处理 |
|
|
@@ -182,7 +196,7 @@ dsh plugin --profile <name> remove dsh-doublecheck
|
|
|
182
196
|
| `vagueTaskMaxChars` | `200` | 超过此长度的任务一律不算模糊;简短任务提到文件名、路径、URL、下划线关键词或连字符关键词即为具体。 |
|
|
183
197
|
| `remindOnce` | `true` | 每道门每个会话最多注入一次提醒——持久化:从日志折叠,重启/恢复后不会重复。 |
|
|
184
198
|
| `testToolNames` | `['bash', 'pwsh']` | 可运行测试的 shell 工具名。 |
|
|
185
|
-
| `testCommandPatterns` | *(pnpm/npm/yarn/bun test、pytest、go/cargo/make test、node --test)* | 命令需匹配的正则才算测试运行。 |
|
|
199
|
+
| `testCommandPatterns` | *(pnpm/npm/yarn/bun test、pytest、go/cargo/make test、node --test、deno test、uv run pytest)* | 命令需匹配的正则才算测试运行。 |
|
|
186
200
|
| `testFilePatterns` | *(测试目录、`*.test.*`/`*.spec.*`)* | 识别测试文件的正则——永远可编辑,豁免 red 门。 |
|
|
187
201
|
| `adversaryModel` | `null` | critic 模型路由;`null` = 主模型自评。 |
|
|
188
202
|
| `adversaryProvider` | `'fork'` | critic 使用的 subagent provider 名。 |
|
|
@@ -239,13 +253,15 @@ dsh plugin --profile <name> remove dsh-doublecheck
|
|
|
239
253
|
/doublecheck status|report|on|off
|
|
240
254
|
```
|
|
241
255
|
|
|
242
|
-
- `status` ——
|
|
256
|
+
- `status` —— 生效开关(持久覆盖优先于配置默认)、已配置模块、执行强度与折叠阶段事实(spec 是否提交、red/green 颜色、评审是否在档、编辑数)。
|
|
243
257
|
- `report` —— 当场从会话日志折叠交付报告(不跑验证工作流;验证属于 `doublecheck_report` 工具)。
|
|
244
258
|
- `on` / `off` —— 写入持久 `doublecheck/state` 事件(跨重启/恢复/派生生效——重放即状态)并注入模型可见的切换通知。
|
|
245
259
|
|
|
260
|
+
所有命令回复都遵循 guard 行的 `language` 设置。
|
|
261
|
+
|
|
246
262
|
## 路线图
|
|
247
263
|
|
|
248
|
-
六阶段纪律环已全部完成:**grill → design → red → green → review → verify** 均随本包交付(v0.1 → v0.
|
|
264
|
+
六阶段纪律环已全部完成:**grill → design → red → green → review → verify** 均随本包交付(v0.1 → v0.6)。真实转录回归夹具(`tests/fixtures/`)锁定持久事件形态。后续工作:更丰富的报告格式、Web UI 纪律状态徽章、以及从工作区 spec 文件播种跨会话状态。
|
|
249
265
|
|
|
250
266
|
## 开发
|
|
251
267
|
|
package/lib/domain/evidence.d.ts
CHANGED
|
@@ -70,7 +70,10 @@ export declare function shellCommand(name: string, args: Record<string, unknown>
|
|
|
70
70
|
export declare function isTestCommand(command: string, detection: TestRunDetection): boolean;
|
|
71
71
|
/**
|
|
72
72
|
* The mutation target path of a tool call, when the tool is a configured
|
|
73
|
-
* mutation tool and its arguments name a file.
|
|
73
|
+
* mutation tool and its arguments name a file. Accepts both the DSH-native
|
|
74
|
+
* `file_path` key (`edit` / `write`) and the `path` key custom guard tools
|
|
75
|
+
* may use, so a configured tool is never mis-gated merely for its argument
|
|
76
|
+
* shape.
|
|
74
77
|
* @param name - the called tool name.
|
|
75
78
|
* @param args - the parsed arguments record.
|
|
76
79
|
* @param detection - the compiled detection knobs.
|
package/lib/domain/evidence.js
CHANGED
|
@@ -16,6 +16,7 @@ export const DEFAULT_TEST_COMMAND_PATTERNS = [
|
|
|
16
16
|
'(?:^|[;&|]\\s*)(?:(?:pnpm|npm|npx|yarn|bun)(?:\\s+run)?\\s+(?:test|vitest|jest|mocha)(?:\\s|$))',
|
|
17
17
|
'(?:^|[;&|]\\s*)(?:(?:pytest|go\\s+test|cargo\\s+test|make\\s+test|ctest)(?:\\s|$))',
|
|
18
18
|
'(?:^|[;&|]\\s*)(?:node\\s+--test(?:\\s|$))',
|
|
19
|
+
'(?:^|[;&|]\\s*)(?:deno\\s+test|uv\\s+run\\s+pytest)(?:\\s|$)',
|
|
19
20
|
];
|
|
20
21
|
/** Shared defaults: mutation tools and test-file path patterns. */
|
|
21
22
|
export const DEFAULT_MUTATION_TOOLS = ['edit', 'write'];
|
|
@@ -103,7 +104,10 @@ export function isTestCommand(command, detection) {
|
|
|
103
104
|
}
|
|
104
105
|
/**
|
|
105
106
|
* The mutation target path of a tool call, when the tool is a configured
|
|
106
|
-
* mutation tool and its arguments name a file.
|
|
107
|
+
* mutation tool and its arguments name a file. Accepts both the DSH-native
|
|
108
|
+
* `file_path` key (`edit` / `write`) and the `path` key custom guard tools
|
|
109
|
+
* may use, so a configured tool is never mis-gated merely for its argument
|
|
110
|
+
* shape.
|
|
107
111
|
* @param name - the called tool name.
|
|
108
112
|
* @param args - the parsed arguments record.
|
|
109
113
|
* @param detection - the compiled detection knobs.
|
|
@@ -112,7 +116,8 @@ export function isTestCommand(command, detection) {
|
|
|
112
116
|
export function mutationTargetPath(name, args, detection) {
|
|
113
117
|
if (!detection.mutationTools.includes(name))
|
|
114
118
|
return undefined;
|
|
115
|
-
const
|
|
119
|
+
const filePath = args?.['file_path'];
|
|
120
|
+
const path = filePath ?? args?.['path'];
|
|
116
121
|
return typeof path === 'string' && path.length > 0 ? path : undefined;
|
|
117
122
|
}
|
|
118
123
|
/**
|
|
@@ -20,6 +20,8 @@ export interface GrilledSpec {
|
|
|
20
20
|
/** What the user explicitly does not want. */
|
|
21
21
|
nonGoals: string;
|
|
22
22
|
}
|
|
23
|
+
/** All six spec fields, in declaration order. */
|
|
24
|
+
export declare const SPEC_FIELD_NAMES: readonly (keyof GrilledSpec)[];
|
|
23
25
|
/** One structured objection from the adversary review. */
|
|
24
26
|
export interface ReviewFinding {
|
|
25
27
|
/** How much the finding threatens the delivery claim. */
|
package/lib/domain/vocabulary.js
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module dsh-doublecheck/domain/vocabulary
|
|
7
7
|
*/
|
|
8
|
+
/** All six spec fields, in declaration order. */
|
|
9
|
+
export const SPEC_FIELD_NAMES = [
|
|
10
|
+
'goal', 'scope', 'acceptanceCriteria', 'failureModes', 'priorities', 'nonGoals',
|
|
11
|
+
];
|
|
8
12
|
/** All verify dimensions, in spec order. */
|
|
9
13
|
export const VERIFY_DIMENSIONS = [
|
|
10
14
|
'goal',
|
package/lib/grill/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import z from '@deepseek-ai/schemastery';
|
|
|
18
18
|
import { DEFAULT_MUTATION_TOOLS, DEFAULT_TEST_COMMAND_PATTERNS, DEFAULT_TEST_FILE_PATTERNS, DEFAULT_TEST_TOOL_NAMES, compileDetection, } from "../domain/evidence.js";
|
|
19
19
|
import { SPEC_TOOL_NAME } from "../domain/stages.js";
|
|
20
20
|
import { buildVerifyScript, deriveReportVerdict, foldReportFacts, renderReportMarkdown, VERIFY_META, } from "../domain/report.js";
|
|
21
|
-
import { VERIFY_DIMENSIONS } from "../domain/vocabulary.js";
|
|
21
|
+
import { SPEC_FIELD_NAMES, VERIFY_DIMENSIONS } from "../domain/vocabulary.js";
|
|
22
22
|
import { BundledSkillProvider, PROVIDER_NAME } from "./provider.js";
|
|
23
23
|
export const name = 'doublecheck-grill';
|
|
24
24
|
export const inject = ['skills', 'tools'];
|
|
@@ -215,6 +215,13 @@ export function apply(ctx, config) {
|
|
|
215
215
|
priorities: args.priorities,
|
|
216
216
|
nonGoals: args.nonGoals,
|
|
217
217
|
};
|
|
218
|
+
// Fail fast at the commit: an empty dimension would satisfy the gate
|
|
219
|
+
// shape while recording an uncheckable contract. The grill must settle
|
|
220
|
+
// every dimension before the spec counts.
|
|
221
|
+
const empty = SPEC_FIELD_NAMES.filter(field => spec[field].trim() === '');
|
|
222
|
+
if (empty.length > 0) {
|
|
223
|
+
throw new Error(`doublecheck_spec: the spec dimension(s) ${empty.join(', ')} must not be empty; finish the requirements grill before committing the spec`);
|
|
224
|
+
}
|
|
218
225
|
const outcome = await writeSpecFile(ctx, spec, args.filePath ?? config.specFile, exec);
|
|
219
226
|
if (exec.agent !== undefined) {
|
|
220
227
|
ctx.emit('doublecheck/spec', { session: exec.agent.session, spec, ...outcome });
|
package/lib/guard/command.d.ts
CHANGED
|
@@ -34,6 +34,13 @@ export interface CommandDeps {
|
|
|
34
34
|
snapshotOf: (session: Session) => Snapshot;
|
|
35
35
|
/** The compiled test-run detection shared with the tdd/adversary gates. */
|
|
36
36
|
detection: TestRunDetection;
|
|
37
|
+
/**
|
|
38
|
+
* The effective session switch — the exact reader the gates use (local
|
|
39
|
+
* override → durable `doublecheck/state` → configured default), so the
|
|
40
|
+
* command answers consistently with what the gates enforce, including on
|
|
41
|
+
* rc.6 hosts where the override is process-local.
|
|
42
|
+
*/
|
|
43
|
+
effectiveEnabled: (session: Session) => boolean;
|
|
37
44
|
/** Whether the host append surface stamps `ignorable` (see {@link hostStampsIgnorable}). */
|
|
38
45
|
stampsIgnorable: () => boolean;
|
|
39
46
|
/** Record a process-local switch override when the durable write is unavailable. */
|
package/lib/guard/command.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
17
17
|
import { Session, SessionId } from '@deepseek-ai/dsh-session';
|
|
18
18
|
import { deriveReportVerdict, foldReportFacts, renderReportMarkdown } from "../domain/report.js";
|
|
19
|
+
import { PROSE } from "./prose.js";
|
|
19
20
|
/**
|
|
20
21
|
* Whether this host's `Session.append` stamps the `ignorable` envelope marker.
|
|
21
22
|
* Detected once per process on a detached probe session: hosts without the
|
|
@@ -54,14 +55,11 @@ export function effectiveDoublecheckEnabled(events, fallback) {
|
|
|
54
55
|
return fallback;
|
|
55
56
|
}
|
|
56
57
|
/** The switch notice injected after `/doublecheck on|off`. */
|
|
57
|
-
function switchNotice(enabled, durable) {
|
|
58
|
+
function switchNotice(enabled, durable, prose) {
|
|
58
59
|
const source = { kind: 'plugin', plugin: 'dsh-doublecheck', form: 'notice', summary: 'doublecheck state' };
|
|
59
|
-
const scope = durable
|
|
60
|
-
? 'for this session'
|
|
61
|
-
: 'for this process only (this harness build predates the ignorable append surface, so the switch is not durable)';
|
|
62
60
|
const text = enabled
|
|
63
|
-
?
|
|
64
|
-
:
|
|
61
|
+
? durable ? prose.switchOnDurable : prose.switchOnLocal
|
|
62
|
+
: durable ? prose.switchOffDurable : prose.switchOffLocal;
|
|
65
63
|
return createUserMessage({ content: [{ type: 'text', text }], source });
|
|
66
64
|
}
|
|
67
65
|
/**
|
|
@@ -70,26 +68,32 @@ function switchNotice(enabled, durable) {
|
|
|
70
68
|
* @returns the command handler for `ctx.commands.register`.
|
|
71
69
|
*/
|
|
72
70
|
export function doublecheckHandler(deps) {
|
|
73
|
-
const { config, snapshotOf, detection, stampsIgnorable, setLocalOverride } = deps;
|
|
71
|
+
const { config, snapshotOf, detection, stampsIgnorable, setLocalOverride, effectiveEnabled } = deps;
|
|
72
|
+
const prose = PROSE[config.language];
|
|
74
73
|
return (invocation) => {
|
|
75
74
|
const agent = invocation.agent;
|
|
76
75
|
if (agent === undefined) {
|
|
77
|
-
return { kind: 'error', text:
|
|
76
|
+
return { kind: 'error', text: prose.commandNoAgent };
|
|
78
77
|
}
|
|
79
78
|
const session = agent.session;
|
|
80
79
|
const input = invocation.rawInput.trim().toLowerCase();
|
|
81
|
-
const enabled =
|
|
80
|
+
const enabled = effectiveEnabled(session);
|
|
82
81
|
if (input === 'status' || input === '') {
|
|
83
82
|
const snapshot = snapshotOf(session);
|
|
84
83
|
const discipline = snapshot.discipline;
|
|
85
84
|
return {
|
|
86
85
|
kind: 'success',
|
|
87
|
-
text:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
text: prose.commandStatus({
|
|
87
|
+
enabled,
|
|
88
|
+
defaultEnabled: config.enableByDefault,
|
|
89
|
+
modules: config.modules,
|
|
90
|
+
intensity: config.intensity,
|
|
91
|
+
remindOnce: config.remindOnce,
|
|
92
|
+
hasSpec: discipline.hasSpec,
|
|
93
|
+
color: discipline.color,
|
|
94
|
+
reviewed: snapshot.lastReviewSeq >= 0,
|
|
95
|
+
editCount: discipline.editCount,
|
|
96
|
+
}),
|
|
93
97
|
};
|
|
94
98
|
}
|
|
95
99
|
if (input === 'report') {
|
|
@@ -104,11 +108,11 @@ export function doublecheckHandler(deps) {
|
|
|
104
108
|
return { kind: 'success', text: renderReportMarkdown(report) };
|
|
105
109
|
}
|
|
106
110
|
if (input !== 'on' && input !== 'off') {
|
|
107
|
-
return { kind: 'error', text:
|
|
111
|
+
return { kind: 'error', text: prose.commandUnknown(invocation.rawInput.trim()) };
|
|
108
112
|
}
|
|
109
113
|
const target = input === 'on';
|
|
110
114
|
if (enabled === target) {
|
|
111
|
-
return { kind: 'success', text:
|
|
115
|
+
return { kind: 'success', text: target ? prose.commandAlreadyOn : prose.commandAlreadyOff };
|
|
112
116
|
}
|
|
113
117
|
// Adaptive durable write: only hosts that stamp `ignorable` may write the
|
|
114
118
|
// state event; on rc.6 the options bag is ignored and an unmarked foreign
|
|
@@ -121,12 +125,12 @@ export function doublecheckHandler(deps) {
|
|
|
121
125
|
else {
|
|
122
126
|
setLocalOverride(session, target);
|
|
123
127
|
}
|
|
124
|
-
agent.inject(switchNotice(target, durable));
|
|
128
|
+
agent.inject(switchNotice(target, durable, prose));
|
|
125
129
|
return {
|
|
126
130
|
kind: 'success',
|
|
127
|
-
text:
|
|
128
|
-
?
|
|
129
|
-
:
|
|
131
|
+
text: target
|
|
132
|
+
? durable ? prose.commandOnDurable : prose.commandOnLocal
|
|
133
|
+
: durable ? prose.commandOffDurable : prose.commandOffLocal,
|
|
130
134
|
};
|
|
131
135
|
};
|
|
132
136
|
}
|
package/lib/guard/index.d.ts
CHANGED
|
@@ -102,6 +102,8 @@ export interface Snapshot {
|
|
|
102
102
|
lastReviewSeq: number;
|
|
103
103
|
/** Implementation edits folded after {@link lastReviewSeq}. */
|
|
104
104
|
editsAfterReview: number;
|
|
105
|
+
/** The last durable `doublecheck/state` switch on record, when one exists. */
|
|
106
|
+
stateEnabled?: boolean;
|
|
105
107
|
}
|
|
106
108
|
/**
|
|
107
109
|
* Install the guard listeners.
|
package/lib/guard/index.js
CHANGED
|
@@ -36,7 +36,7 @@ import { PROSE } from "./prose.js";
|
|
|
36
36
|
import { applyDoublecheckEvent, emptyDoublecheckState, viewDoublecheck, } from "../domain/projection.js";
|
|
37
37
|
import { doublecheckViewSchema } from "../types.js";
|
|
38
38
|
import { installInvariant, PACKAGE_NAME } from "../invariant.js";
|
|
39
|
-
import { doublecheckHandler,
|
|
39
|
+
import { doublecheckHandler, hostStampsIgnorable } from "./command.js";
|
|
40
40
|
export const name = 'doublecheck-guard';
|
|
41
41
|
export const inject = ['commands'];
|
|
42
42
|
export const Config = z.object({
|
|
@@ -91,9 +91,14 @@ export function apply(ctx, config) {
|
|
|
91
91
|
const pendingReminders = new WeakMap();
|
|
92
92
|
/** Process-local switch overrides written by `/doublecheck on|off` on hosts that cannot store the durable event. */
|
|
93
93
|
const localOverrides = new WeakMap();
|
|
94
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* The effective switch for a session: the local override first, then the
|
|
96
|
+
* durable `doublecheck/state` fold, then the configured default. The fold
|
|
97
|
+
* rides the same incremental snapshot as the discipline facts, so the read
|
|
98
|
+
* costs O(new events) instead of rescanning the log per tool call.
|
|
99
|
+
*/
|
|
95
100
|
function doublecheckEnabled(session) {
|
|
96
|
-
return localOverrides.get(session) ??
|
|
101
|
+
return localOverrides.get(session) ?? snapshotOf(session).stateEnabled ?? config.enableByDefault;
|
|
97
102
|
}
|
|
98
103
|
const prose = PROSE[config.language];
|
|
99
104
|
/**
|
|
@@ -138,6 +143,9 @@ export function apply(ctx, config) {
|
|
|
138
143
|
}
|
|
139
144
|
}
|
|
140
145
|
}
|
|
146
|
+
else if (event.type === 'doublecheck/state') {
|
|
147
|
+
snapshot.stateEnabled = event.data.enabled;
|
|
148
|
+
}
|
|
141
149
|
else if (event.type === 'tool/call' && event.seq > snapshot.lastReviewSeq) {
|
|
142
150
|
// Implementation edits after the latest review re-arm the adversary
|
|
143
151
|
// trigger for a second review round.
|
|
@@ -224,6 +232,7 @@ export function apply(ctx, config) {
|
|
|
224
232
|
config,
|
|
225
233
|
snapshotOf,
|
|
226
234
|
detection,
|
|
235
|
+
effectiveEnabled: doublecheckEnabled,
|
|
227
236
|
stampsIgnorable: hostStampsIgnorable,
|
|
228
237
|
setLocalOverride: (session, enabled) => localOverrides.set(session, enabled),
|
|
229
238
|
}),
|
|
@@ -293,11 +302,13 @@ export function apply(ctx, config) {
|
|
|
293
302
|
}
|
|
294
303
|
}
|
|
295
304
|
// Red gate: implementation edits need a failing test on record. Writing
|
|
296
|
-
// test files is the red step itself and always delegates
|
|
305
|
+
// test files is the red step itself and always delegates, and a call that
|
|
306
|
+
// names no file at all (a custom guard tool with another argument shape)
|
|
307
|
+
// is not an implementation edit either.
|
|
297
308
|
if (config.modules.tdd && snapshot.discipline.color !== 'red') {
|
|
298
309
|
const args = exec.arguments;
|
|
299
310
|
const path = mutationTargetPath(exec.name, args, detection);
|
|
300
|
-
if (path
|
|
311
|
+
if (path === undefined || isTestFilePath(path, detection))
|
|
301
312
|
return next();
|
|
302
313
|
const reminder = nextTddRedReminder(snapshot);
|
|
303
314
|
if (reminder !== undefined)
|
package/lib/guard/prose.d.ts
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
* `green gate`) stay English: they are stable session-log ids the durable
|
|
6
6
|
* once-semantics fold matches, and translating them would silently break
|
|
7
7
|
* `remindOnce` after a language switch. Only the model-facing text localizes.
|
|
8
|
+
*
|
|
9
|
+
* Scope of localization: every model-visible string the package injects or
|
|
10
|
+
* answers with (gate prose, the critic task, `/doublecheck` replies, the
|
|
11
|
+
* switch notice) is localized. The workspace documents (`doublecheck-spec.md`
|
|
12
|
+
* / `doublecheck-report.md`) keep their English section headings: they are
|
|
13
|
+
* stable artifacts whose structure other tooling may match.
|
|
8
14
|
* @module dsh-doublecheck/guard/prose
|
|
9
15
|
*/
|
|
10
16
|
/** Supported prose languages for guard injections. */
|
|
@@ -29,6 +35,47 @@ export interface GuardProse {
|
|
|
29
35
|
reviewUnavailableNoFindings: string;
|
|
30
36
|
reviewFindingsHeader: (count: number) => string;
|
|
31
37
|
reviewFindingsFooter: string;
|
|
38
|
+
reviewHeldBack: (held: number) => string;
|
|
39
|
+
/** The critic's task prompt (model-facing behavior of the review run). */
|
|
40
|
+
criticTask: string;
|
|
41
|
+
switchOnDurable: string;
|
|
42
|
+
switchOnLocal: string;
|
|
43
|
+
switchOffDurable: string;
|
|
44
|
+
switchOffLocal: string;
|
|
45
|
+
commandNoAgent: string;
|
|
46
|
+
commandUnknown: (input: string) => string;
|
|
47
|
+
commandAlreadyOn: string;
|
|
48
|
+
commandAlreadyOff: string;
|
|
49
|
+
commandOnDurable: string;
|
|
50
|
+
commandOnLocal: string;
|
|
51
|
+
commandOffDurable: string;
|
|
52
|
+
commandOffLocal: string;
|
|
53
|
+
commandStatus: (facts: CommandStatusFacts) => string;
|
|
54
|
+
}
|
|
55
|
+
/** The folded facts the localized `/doublecheck status` reply renders. */
|
|
56
|
+
export interface CommandStatusFacts {
|
|
57
|
+
/** The effective session switch. */
|
|
58
|
+
enabled: boolean;
|
|
59
|
+
/** The configured default for sessions without a state record. */
|
|
60
|
+
defaultEnabled: boolean;
|
|
61
|
+
/** Configured module switches. */
|
|
62
|
+
modules: {
|
|
63
|
+
grill: boolean;
|
|
64
|
+
tdd: boolean;
|
|
65
|
+
adversary: boolean;
|
|
66
|
+
};
|
|
67
|
+
/** Configured enforcement strength. */
|
|
68
|
+
intensity: string;
|
|
69
|
+
/** Whether reminder repetition is capped per session. */
|
|
70
|
+
remindOnce: boolean;
|
|
71
|
+
/** Whether a committed spec exists in the log. */
|
|
72
|
+
hasSpec: boolean;
|
|
73
|
+
/** The latest test-run color (`none` / `red` / `green`). */
|
|
74
|
+
color: string;
|
|
75
|
+
/** Whether an adversary review record exists in the log. */
|
|
76
|
+
reviewed: boolean;
|
|
77
|
+
/** Total implementation edits folded so far. */
|
|
78
|
+
editCount: number;
|
|
32
79
|
}
|
|
33
80
|
/** The localized prose tables by language. */
|
|
34
81
|
export declare const PROSE: Readonly<Record<ProseLanguage, GuardProse>>;
|