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/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
- [![version](https://img.shields.io/badge/version-0.5.0-blue)](https://github.com/PerryLink/dsh-doublecheck/releases)
5
+ [![version](https://img.shields.io/badge/version-0.6.0-blue)](https://github.com/PerryLink/dsh-doublecheck/releases)
6
+ [![npm](https://img.shields.io/npm/v/dsh-doublecheck)](https://www.npmjs.com/package/dsh-doublecheck)
6
7
  [![license](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
7
8
  [![topics](https://img.shields.io/badge/topics-dsh%20%7C%20dsh--plugin-22c55e)](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 (v0.5)
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 a etapa dobrada; `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
+ - 🔁 **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.5.0.tgz
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.5.0"
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` | `false` | `true` ativa os portões de evidência red/green (v0.2). |
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.4). Trabalho futuro: cobertura de snapshot para as transcrições de revisão/relatório e formatação de relatório mais rica.
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
- [![version](https://img.shields.io/badge/version-0.5.0-blue)](https://github.com/PerryLink/dsh-doublecheck/releases)
5
+ [![version](https://img.shields.io/badge/version-0.6.0-blue)](https://github.com/PerryLink/dsh-doublecheck/releases)
6
+ [![npm](https://img.shields.io/npm/v/dsh-doublecheck)](https://www.npmjs.com/package/dsh-doublecheck)
6
7
  [![license](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
7
8
  [![topics](https://img.shields.io/badge/topics-dsh%20%7C%20dsh--plugin-22c55e)](https://github.com/topics/dsh-plugin)
8
9
  [![CI](https://img.shields.io/github/actions/workflow/status/PerryLink/dsh-doublecheck/ci.yml?branch=main)](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
- ## v0.5 功能
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。`remind` 只注入评审意见;`warn`/`block` 额外 steer 一轮让模型正面回应 findings。`adversaryModel` 可把评审路由到独立模型;critic 工具白名单默认只读。findings 随 `doublecheck-review` 消息源持久结构化落盘。评审会持久化重武装:最新评审记录之后的实现编辑会触发下一轮评审;取消回合会中止在途 critic。
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
- - ⌨️ **`/doublecheck` 会话命令** —— `status` 报告生效开关、已配置模块与折叠阶段;`report` 当场折叠交付报告;`on|off` 写入持久 `doublecheck/state` 事件并注入切换通知。
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.5.0.tgz
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.5.0"
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` —— 生效开关(持久覆盖优先于配置默认)、已配置模块与折叠阶段(spec 是否提交、red/green 颜色、评审是否在档)。
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.5)。真实转录回归夹具(`tests/fixtures/`)锁定持久事件形态。后续工作:更丰富的报告格式、Web UI 纪律状态徽章、以及从工作区 spec 文件播种跨会话状态。
264
+ 六阶段纪律环已全部完成:**grill → design → red → green → review → verify** 均随本包交付(v0.1 → v0.6)。真实转录回归夹具(`tests/fixtures/`)锁定持久事件形态。后续工作:更丰富的报告格式、Web UI 纪律状态徽章、以及从工作区 spec 文件播种跨会话状态。
249
265
 
250
266
  ## 开发
251
267
 
@@ -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.
@@ -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 path = args?.['file_path'];
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. */
@@ -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',
@@ -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 });
@@ -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. */
@@ -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
- ? `Double-check discipline was switched ON ${scope} (changed by the user): the requirements grill, the red/green gates, and the delivery review are active again.`
64
- : `Double-check discipline was switched OFF ${scope} (changed by the user): the discipline gates defer to the human approval chain until switched back on.`;
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: '/doublecheck needs an agent session to inspect.' };
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 = effectiveDoublecheckEnabled(session.events, config.enableByDefault);
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
- `Double-check discipline is ${enabled ? 'ON' : 'OFF'} for this session. (/doublecheck on|off)`,
89
- `Modules: grill=${config.modules.grill ? 'on' : 'off'}, tdd=${config.modules.tdd ? 'on' : 'off'}, adversary=${config.modules.adversary ? 'on' : 'off'} (cordis.yml).`,
90
- `Stage: spec=${discipline.hasSpec ? 'committed' : 'missing'}, tests=${discipline.color}, review=${snapshot.lastReviewSeq >= 0 ? 'on record' : 'not run'}.`,
91
- 'Usage: /doublecheck status|report|on|off',
92
- ].join('\n'),
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: `Unknown /doublecheck argument "${invocation.rawInput.trim()}". Usage: /doublecheck status|report|on|off` };
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: `Double-check discipline is already ${input.toUpperCase()} for this session.` };
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: durable
128
- ? `Double-check discipline ${input.toUpperCase()} for this session.`
129
- : `Double-check discipline ${input.toUpperCase()} for this process only (not durable on this harness).`,
131
+ text: target
132
+ ? durable ? prose.commandOnDurable : prose.commandOnLocal
133
+ : durable ? prose.commandOffDurable : prose.commandOffLocal,
130
134
  };
131
135
  };
132
136
  }
@@ -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.
@@ -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, effectiveDoublecheckEnabled, hostStampsIgnorable } from "./command.js";
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
- /** The effective switch for a session: the local override first, then the durable log fold. */
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) ?? effectiveDoublecheckEnabled(session.events, config.enableByDefault);
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 !== undefined && isTestFilePath(path, detection))
311
+ if (path === undefined || isTestFilePath(path, detection))
301
312
  return next();
302
313
  const reminder = nextTddRedReminder(snapshot);
303
314
  if (reminder !== undefined)
@@ -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>>;