@lcv-ideas-software/cross-review 4.2.0 → 4.2.2
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 +52 -1
- package/NOTICE +1 -1
- package/README.md +115 -90
- package/SECURITY.md +18 -37
- package/dist/scripts/provider-refresh-smoke.d.ts +1 -0
- package/dist/scripts/provider-refresh-smoke.js +49 -0
- package/dist/scripts/provider-refresh-smoke.js.map +1 -0
- package/dist/scripts/runtime-smoke.js.map +1 -1
- package/dist/scripts/smoke.js +146 -37
- package/dist/scripts/smoke.js.map +1 -1
- package/dist/src/core/caller-tokens.js +3 -2
- package/dist/src/core/caller-tokens.js.map +1 -1
- package/dist/src/core/config.d.ts +3 -3
- package/dist/src/core/config.js +17 -17
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/file-config.d.ts +1 -1
- package/dist/src/core/orchestrator.d.ts +69 -45
- package/dist/src/core/orchestrator.js +212 -3
- package/dist/src/core/orchestrator.js.map +1 -1
- package/dist/src/core/relator-lottery.js +5 -1
- package/dist/src/core/relator-lottery.js.map +1 -1
- package/dist/src/core/session-store.d.ts +9 -9
- package/dist/src/core/session-store.js +2 -2
- package/dist/src/core/session-store.js.map +1 -1
- package/dist/src/core/status.js +13 -0
- package/dist/src/core/status.js.map +1 -1
- package/dist/src/core/types.d.ts +166 -165
- package/dist/src/core/types.js +3 -3
- package/dist/src/core/types.js.map +1 -1
- package/dist/src/dashboard/server.js +12 -8
- package/dist/src/dashboard/server.js.map +1 -1
- package/dist/src/mcp/server.d.ts +13 -13
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/peers/base.d.ts +6 -6
- package/dist/src/peers/errors.js +14 -12
- package/dist/src/peers/errors.js.map +1 -1
- package/dist/src/peers/gemini.js +2 -2
- package/dist/src/peers/gemini.js.map +1 -1
- package/dist/src/peers/grok.js +5 -5
- package/dist/src/peers/grok.js.map +1 -1
- package/dist/src/peers/model-selection.js +6 -8
- package/dist/src/peers/model-selection.js.map +1 -1
- package/dist/src/peers/perplexity.js +8 -5
- package/dist/src/peers/perplexity.js.map +1 -1
- package/dist/src/peers/text.d.ts +3 -3
- package/docs/api-keys.md +2 -2
- package/docs/apresentacao-cross-review.md +769 -0
- package/docs/apresentacao.md +571 -0
- package/docs/architecture.md +2 -0
- package/docs/caching.md +9 -8
- package/docs/costs.md +11 -0
- package/docs/evidence-preflight.md +1 -1
- package/docs/model-selection.md +19 -14
- package/package.json +11 -8
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
# Cross-Review — Apresentação Técnica
|
|
2
|
+
|
|
3
|
+
> Documento de apresentação detalhada do `cross-review`: o que é, como
|
|
4
|
+
> funciona, características, instalação, configuração, dependências e um
|
|
5
|
+
> changelog resumido. As seções 1 a 3 e 8 a 9 são acessíveis a qualquer
|
|
6
|
+
> leitor; as seções 4 a 7 aprofundam os aspectos técnicos para profissionais
|
|
7
|
+
> de TI e pessoas desenvolvedoras.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Sumário
|
|
12
|
+
|
|
13
|
+
1. [Visão geral](#1-visão-geral)
|
|
14
|
+
2. [Como funciona](#2-como-funciona)
|
|
15
|
+
3. [Características](#3-características)
|
|
16
|
+
4. [Aspectos técnicos](#4-aspectos-técnicos)
|
|
17
|
+
5. [Instalação](#5-instalação)
|
|
18
|
+
6. [Configuração](#6-configuração)
|
|
19
|
+
7. [Dependências](#7-dependências)
|
|
20
|
+
8. [Changelog resumido](#8-changelog-resumido)
|
|
21
|
+
9. [Licença e recursos](#9-licença-e-recursos)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Visão geral
|
|
26
|
+
|
|
27
|
+
### O problema
|
|
28
|
+
|
|
29
|
+
Um modelo de linguagem (LLM) pode produzir uma resposta **errada com toda a
|
|
30
|
+
confiança**. Ele não sinaliza incerteza de forma confiável, não tem um
|
|
31
|
+
"segundo par de olhos" e não distingue, sozinho, entre uma afirmação que
|
|
32
|
+
verificou e uma que apenas presumiu. Quando esse resultado é usado para
|
|
33
|
+
decidir algo — aprovar um trecho de código, validar um documento, dar um
|
|
34
|
+
parecer — o erro passa adiante sem ninguém perceber.
|
|
35
|
+
|
|
36
|
+
### A solução
|
|
37
|
+
|
|
38
|
+
O `cross-review` resolve isso submetendo um rascunho ao escrutínio de
|
|
39
|
+
**vários modelos de IA de fornecedores diferentes**, que se revisam até
|
|
40
|
+
chegarem a um **acordo unânime**. Nenhum modelo decide sozinho. Um resultado
|
|
41
|
+
só é considerado "pronto" quando **todos** os revisores selecionados
|
|
42
|
+
concordam — e quando nenhum deles falhou ou deixou de emitir um veredito
|
|
43
|
+
legível por máquina.
|
|
44
|
+
|
|
45
|
+
A analogia mais próxima é um **colegiado** (um tribunal com vários juízes) ou
|
|
46
|
+
a **revisão por pares** da ciência: a decisão não vale pela autoridade de um
|
|
47
|
+
único avaliador, mas pela convergência de avaliadores independentes.
|
|
48
|
+
|
|
49
|
+
### O que é, em termos concretos
|
|
50
|
+
|
|
51
|
+
`cross-review` é um **servidor MCP** (Model Context Protocol) que orquestra
|
|
52
|
+
chamadas às APIs oficiais de seis provedores de IA e expõe esse fluxo de
|
|
53
|
+
revisão como um conjunto de ferramentas. Ele é distribuído como o pacote npm
|
|
54
|
+
`@lcv-ideas-software/cross-review`, licenciado sob Apache-2.0.
|
|
55
|
+
|
|
56
|
+
Os seis pares revisores (a "sexteto") são:
|
|
57
|
+
|
|
58
|
+
| Par revisor | Provedor | Acesso |
|
|
59
|
+
| ------------ | ---------- | -------------------------------- |
|
|
60
|
+
| `codex` | OpenAI | biblioteca cliente OpenAI |
|
|
61
|
+
| `claude` | Anthropic | biblioteca cliente Anthropic |
|
|
62
|
+
| `gemini` | Google | biblioteca cliente Google Gen AI |
|
|
63
|
+
| `deepseek` | DeepSeek | API compatível com OpenAI |
|
|
64
|
+
| `grok` | xAI | API compatível com OpenAI |
|
|
65
|
+
| `perplexity` | Perplexity | API Sonar, compatível com OpenAI |
|
|
66
|
+
|
|
67
|
+
As chamadas são **reais** por padrão — o servidor conversa com as APIs de
|
|
68
|
+
verdade. Versões simuladas ("stubs") existem apenas para testes de fumaça e
|
|
69
|
+
integração contínua, e só são ativadas com uma combinação explícita de
|
|
70
|
+
variáveis de ambiente, justamente para que um stub nunca valide uma decisão
|
|
71
|
+
de revisão por engano.
|
|
72
|
+
|
|
73
|
+
### Para quem é
|
|
74
|
+
|
|
75
|
+
- **Times e agentes de IA** que querem um portão de qualidade antes de
|
|
76
|
+
"fechar" um artefato (código, documento, parecer, especificação).
|
|
77
|
+
- **Pessoas desenvolvedoras** que usam assistentes de IA e desejam reduzir o
|
|
78
|
+
risco de aceitar uma saída plausível, porém incorreta.
|
|
79
|
+
- **Quem integra MCP** em seus fluxos e quer uma ferramenta de consenso
|
|
80
|
+
multi-modelo pronta para uso.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 2. Como funciona
|
|
85
|
+
|
|
86
|
+
### O fluxo, em linguagem simples
|
|
87
|
+
|
|
88
|
+
1. **Quem chama envia** uma tarefa (`task`) e um rascunho inicial
|
|
89
|
+
(`initial_draft`) — o artefato a ser revisado.
|
|
90
|
+
2. **O servidor abre uma sessão** durável e roda **rodadas** de revisão.
|
|
91
|
+
3. **A cada rodada**, os pares selecionados leem o artefato e devolvem um
|
|
92
|
+
**veredito legível por máquina**: `READY` (pronto), `NOT_READY` (não
|
|
93
|
+
pronto) ou `NEEDS_EVIDENCE` (falta evidência).
|
|
94
|
+
4. **O portão de convergência** verifica se houve **unanimidade**.
|
|
95
|
+
5. **Se ainda não convergiu**, o artefato é revisado e uma nova rodada
|
|
96
|
+
começa. Se convergiu — ou se um limite (de rodadas ou de orçamento) for
|
|
97
|
+
atingido —, a sessão é finalizada com um relatório.
|
|
98
|
+
|
|
99
|
+
```mermaid
|
|
100
|
+
flowchart TD
|
|
101
|
+
A[Quem chama envia tarefa + rascunho] --> B[Sessão aberta]
|
|
102
|
+
B --> C[Rodada de revisão pelos pares]
|
|
103
|
+
C --> D{Portão de convergência:<br/>unanimidade READY?}
|
|
104
|
+
D -- Sim --> E[Sessão convergida + relatório]
|
|
105
|
+
D -- Não --> F{Limite de rodadas<br/>ou orçamento?}
|
|
106
|
+
F -- Atingido --> G[Sessão finalizada: max-rounds]
|
|
107
|
+
F -- Não atingido --> H[Artefato revisado] --> C
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### A regra de unanimidade
|
|
111
|
+
|
|
112
|
+
Uma sessão **converge somente quando**:
|
|
113
|
+
|
|
114
|
+
- o status de quem chama é `READY`;
|
|
115
|
+
- **todos** os pares selecionados devolvem `READY`;
|
|
116
|
+
- **nenhum** par falhou nem omitiu um status legível por máquina.
|
|
117
|
+
|
|
118
|
+
Alguns problemas **sempre bloqueiam** a unanimidade até serem resolvidos —
|
|
119
|
+
por exemplo, uma resposta que não pôde ser interpretada mesmo após tentativa
|
|
120
|
+
de recuperação (`unparseable_after_recovery`), um prompt barrado pela
|
|
121
|
+
moderação do provedor (`prompt_flagged_by_moderation`) ou a detecção de que
|
|
122
|
+
um modelo foi silenciosamente rebaixado (`silent_model_downgrade`).
|
|
123
|
+
|
|
124
|
+
### Qualidade da decisão, par a par
|
|
125
|
+
|
|
126
|
+
Para cada par, a qualidade do veredito é classificada:
|
|
127
|
+
|
|
128
|
+
- **`clean`** — status interpretado sem ressalvas;
|
|
129
|
+
- **`format_warning`** — interpretado, com avisos não bloqueantes;
|
|
130
|
+
- **`recovered`** — recuperado via reparo de formato, nova tentativa segura
|
|
131
|
+
para moderação ou sanitização limitada;
|
|
132
|
+
- **`needs_operator_review`** — nenhum status interpretável restou após a
|
|
133
|
+
recuperação;
|
|
134
|
+
- **`failed`** — uma falha de provedor ou de seleção de modelo bloqueou o
|
|
135
|
+
par.
|
|
136
|
+
|
|
137
|
+
### Os três modos de deliberação
|
|
138
|
+
|
|
139
|
+
O modo é escolhido por quem chama e define o "rito" da revisão:
|
|
140
|
+
|
|
141
|
+
- **`ship`** (padrão) — o rascunho é o **artefato em refinamento**. Um par
|
|
142
|
+
designado como **relator** (`lead_peer`) produz, a cada rodada, uma **nova
|
|
143
|
+
versão revisada** em prosa, enquanto os demais pares votam em paralelo.
|
|
144
|
+
Indicado para levar um artefato até a versão final.
|
|
145
|
+
- **`review`** — o rascunho é o **objeto de avaliação**. O relator pode
|
|
146
|
+
emitir respostas estruturadas; os pares votam em paralelo. Indicado para
|
|
147
|
+
pareceres de aprovar/rejeitar sobre código ou material externo.
|
|
148
|
+
- **`circular`** — **custódia deliberativa serial**. Quem chama submete o
|
|
149
|
+
artefato; o **rotador da rodada** ou aprova sem mudanças ou revisa; a
|
|
150
|
+
convergência ocorre quando uma rotação completa termina **sem alteração
|
|
151
|
+
substantiva**. Não há votação paralela. É o modo indicado para produzir
|
|
152
|
+
**artefatos de prosa ou especificações** compartilhadas.
|
|
153
|
+
|
|
154
|
+
### O sorteio do relator e a regra anti-autorrevisão
|
|
155
|
+
|
|
156
|
+
Quando quem chama é um dos pares, omitir o `lead_peer` ativa um **sorteio do
|
|
157
|
+
relator**: o servidor escolhe aleatoriamente um par **diferente** de quem
|
|
158
|
+
chamou para atuar como relator — uma mecânica inspirada nos colegiados
|
|
159
|
+
judiciais. Indicar explicitamente um `lead_peer` igual a quem chamou é
|
|
160
|
+
**rejeitado** pelo servidor (`caller_cannot_be_lead_peer`): **um agente nunca
|
|
161
|
+
revisa a si mesmo**. Essa é uma trava rígida do projeto.
|
|
162
|
+
|
|
163
|
+
### A pré-checagem de evidência
|
|
164
|
+
|
|
165
|
+
Antes de qualquer chamada paga, as ferramentas que rodam até a unanimidade
|
|
166
|
+
executam uma **pré-checagem puramente textual de evidência**. Ela detecta o
|
|
167
|
+
caso de uma submissão que **afirma** ter concluído um trabalho ("os testes
|
|
168
|
+
passaram", "o build está verde") mas **não embute nenhuma evidência
|
|
169
|
+
concreta** (trechos de diff, saída de comando, hashes, referências de
|
|
170
|
+
linha). Nesse caso, a sessão é encerrada localmente com
|
|
171
|
+
`needs_evidence_preflight` — **sem gastar orçamento de API**. A pré-checagem
|
|
172
|
+
é deliberadamente conservadora: um plano de trabalho legítimo, sem diff,
|
|
173
|
+
passa normalmente.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 3. Características
|
|
178
|
+
|
|
179
|
+
- **Seis pares revisores** de fornecedores independentes, simétricos no
|
|
180
|
+
papel: cada par pode atuar como quem chama, relator ou revisor.
|
|
181
|
+
- **Portão de convergência unânime** — a marca central do produto: nada é
|
|
182
|
+
"pronto" sem acordo total.
|
|
183
|
+
- **Três modos de deliberação** (`ship`, `review`, `circular`) para
|
|
184
|
+
diferentes tipos de artefato.
|
|
185
|
+
- **Sessões duráveis** — cada sessão grava artefatos em JSON e Markdown em
|
|
186
|
+
disco, podendo ser lida, retomada e auditada depois.
|
|
187
|
+
- **Trabalhos em segundo plano** — rodadas longas podem rodar de forma
|
|
188
|
+
assíncrona, com acompanhamento por sondagem (`poll`), sem travar o cliente.
|
|
189
|
+
- **Fluxo de eventos** — cada sessão grava um stream durável de eventos
|
|
190
|
+
(`events.ndjson`) para acompanhamento de trabalhos longos.
|
|
191
|
+
- **Controles financeiros** — chamadas pagas ficam bloqueadas até que tetos
|
|
192
|
+
de orçamento e tabelas de preço sejam configurados; estimativas de custo
|
|
193
|
+
barram uma rodada antes de ela começar.
|
|
194
|
+
- **Cache de prompt** entre provedores, com telemetria uniforme e um
|
|
195
|
+
manifesto de cache por sessão.
|
|
196
|
+
- **Streaming de tokens** — progresso por contagem de caracteres, sem expor
|
|
197
|
+
texto sensível por padrão.
|
|
198
|
+
- **Observabilidade** — um log NDJSON por processo, além de relatórios de
|
|
199
|
+
sessão com convergência, falhas, qualidade da decisão e custos.
|
|
200
|
+
- **Painel local** — uma interface HTTP somente-leitura para inspecionar
|
|
201
|
+
sessões, eventos, relatórios, sondagens e métricas.
|
|
202
|
+
- **Seleção de modelo sem rebaixamento silencioso** — cada par é fixado em um
|
|
203
|
+
modelo canônico; problemas de disponibilidade aparecem de forma visível em
|
|
204
|
+
vez de se transformarem num modelo mais fraco.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 4. Aspectos técnicos
|
|
209
|
+
|
|
210
|
+
> Esta seção é voltada a profissionais de TI e pessoas desenvolvedoras.
|
|
211
|
+
|
|
212
|
+
### 4.1. As dez camadas de runtime
|
|
213
|
+
|
|
214
|
+
O `cross-review` é uma implementação **API-first**, organizada em camadas:
|
|
215
|
+
|
|
216
|
+
1. **Servidor MCP** — expõe as ferramentas de fluxo de trabalho via stdio.
|
|
217
|
+
2. **Orquestrador** — cria sessões, roda as revisões, verifica a unanimidade
|
|
218
|
+
e pede ao relator que revise o artefato.
|
|
219
|
+
3. **Adaptadores de par** — chamam as APIs oficiais e as bibliotecas cliente
|
|
220
|
+
dos provedores.
|
|
221
|
+
4. **Seleção de modelo** — consulta as APIs de modelos e valida o modelo
|
|
222
|
+
canônico fixado para cada par.
|
|
223
|
+
5. **Armazenamento de sessão** — grava artefatos duráveis em JSON e Markdown
|
|
224
|
+
sob `data/sessions`.
|
|
225
|
+
6. **Eventos de sessão** — grava streams `events.ndjson` por sessão para
|
|
226
|
+
trabalhos longos.
|
|
227
|
+
7. **Streaming de tokens** — emite eventos `peer.token.delta` e
|
|
228
|
+
`peer.token.completed` baseados em contagem.
|
|
229
|
+
8. **Relatórios** — grava `session-report.md` com convergência, falhas,
|
|
230
|
+
qualidade da decisão, custos e eventos recentes.
|
|
231
|
+
9. **Observabilidade** — grava um log NDJSON por processo sob `data/logs`.
|
|
232
|
+
10. **Painel** — interface HTTP local, somente leitura, para sessões,
|
|
233
|
+
eventos, relatórios, sondagens e métricas.
|
|
234
|
+
|
|
235
|
+
### 4.2. O protocolo MCP e o transporte
|
|
236
|
+
|
|
237
|
+
O servidor implementa o **Model Context Protocol** e se comunica com o host
|
|
238
|
+
(o cliente MCP) via **stdio**. Ele se registra como dois binários: o servidor
|
|
239
|
+
de revisão em si (`cross-review`) e o painel (`cross-review-dashboard`).
|
|
240
|
+
|
|
241
|
+
Como as rodadas de revisão real são intencionalmente **longas**, o tempo
|
|
242
|
+
limite da requisição entre host e servidor deve ser configurado para **pelo
|
|
243
|
+
menos 300 segundos**. Para hosts que não conseguem manter uma requisição MCP
|
|
244
|
+
aberta por tanto tempo, o servidor oferece ferramentas que criam um
|
|
245
|
+
**trabalho em segundo plano** e retornam imediatamente; o progresso é
|
|
246
|
+
acompanhado por sondagem.
|
|
247
|
+
|
|
248
|
+
### 4.3. Os adaptadores de par
|
|
249
|
+
|
|
250
|
+
Cada par tem um adaptador que normaliza a conversa com seu provedor:
|
|
251
|
+
|
|
252
|
+
- **OpenAI/Codex** — biblioteca cliente OpenAI, via Responses API.
|
|
253
|
+
- **Anthropic/Claude** — biblioteca cliente Anthropic.
|
|
254
|
+
- **Google/Gemini** — biblioteca cliente Google Gen AI.
|
|
255
|
+
- **DeepSeek** — API compatível com OpenAI, pela biblioteca cliente OpenAI.
|
|
256
|
+
- **xAI/Grok** — API compatível com OpenAI, pela biblioteca cliente OpenAI.
|
|
257
|
+
- **Perplexity** — API Sonar (`https://api.perplexity.ai`), compatível com o
|
|
258
|
+
formato Chat Completions da OpenAI.
|
|
259
|
+
|
|
260
|
+
O Perplexity tem particularidades relevantes: por padrão, **toda chamada faz
|
|
261
|
+
uma busca web em tempo real**, o que o torna um revisor com perfil de
|
|
262
|
+
"verificação de fatos"; quando atua como relator (síntese), a busca é
|
|
263
|
+
desativada. Seu preço tem uma dimensão extra: além de tokens de entrada e
|
|
264
|
+
saída, há uma taxa por mil requisições que varia com o tamanho do contexto
|
|
265
|
+
de busca.
|
|
266
|
+
|
|
267
|
+
### 4.4. Seleção de modelo: política de não-rebaixamento
|
|
268
|
+
|
|
269
|
+
Cada par é fixado em **exatamente um** modelo canônico. Na inicialização da
|
|
270
|
+
sessão ou da sondagem, o servidor consulta a API de modelos do provedor para
|
|
271
|
+
validar a disponibilidade. **Se o modelo fixado não aparecer na resposta, o
|
|
272
|
+
servidor mantém o modelo fixado mesmo assim** — nunca troca silenciosamente
|
|
273
|
+
por um modelo mais fraco. Assim, qualquer problema de disponibilidade aparece
|
|
274
|
+
de forma visível nas sondagens e nas rodadas, em vez de se transformar numa
|
|
275
|
+
degradação invisível de qualidade.
|
|
276
|
+
|
|
277
|
+
Modelos canônicos atuais (cada um substituível por uma variável de ambiente
|
|
278
|
+
explícita `CROSS_REVIEW_<PROVEDOR>_MODEL`):
|
|
279
|
+
|
|
280
|
+
| Par | Modelo canônico |
|
|
281
|
+
| ------------ | --------------------- |
|
|
282
|
+
| OpenAI/Codex | `gpt-5.5` |
|
|
283
|
+
| Anthropic | `claude-opus-4-8` |
|
|
284
|
+
| Google | `gemini-2.5-pro` |
|
|
285
|
+
| DeepSeek | `deepseek-v4-pro` |
|
|
286
|
+
| xAI/Grok | `grok-4.3` |
|
|
287
|
+
| Perplexity | `sonar-reasoning-pro` |
|
|
288
|
+
|
|
289
|
+
Como o `cross-review` é orientado à correção, os adaptadores pedem
|
|
290
|
+
explicitamente o maior nível de raciocínio que cada API oficial oferece. O
|
|
291
|
+
conteúdo bruto de "pensamento" (chain-of-thought) **não é solicitado nem
|
|
292
|
+
persistido**; a continuidade da sessão é representada por prompts, decisões
|
|
293
|
+
estruturadas dos pares, resumos e artefatos.
|
|
294
|
+
|
|
295
|
+
### 4.5. Modelo de execução real e stubs
|
|
296
|
+
|
|
297
|
+
O padrão de runtime é **execução real de API**. Os stubs ficam desativados a
|
|
298
|
+
não ser que `CROSS_REVIEW_STUB=1` esteja definido **junto** com uma
|
|
299
|
+
confirmação explícita (`CROSS_REVIEW_STUB_CONFIRMED=1` ou `NODE_ENV=test`).
|
|
300
|
+
Esse pareamento existe por segurança: um stub jamais deve validar, por
|
|
301
|
+
descuido, uma decisão de revisão real, e tampouco se deve cobrar de quem
|
|
302
|
+
deliberadamente pediu o modo simulado.
|
|
303
|
+
|
|
304
|
+
### 4.6. Streaming e moderação
|
|
305
|
+
|
|
306
|
+
Dois controles regem o streaming: `CROSS_REVIEW_STREAM_EVENTS` (eventos de
|
|
307
|
+
fluxo) e `CROSS_REVIEW_STREAM_TOKENS` (progresso de tokens), ambos ativos por
|
|
308
|
+
padrão. Por segurança, os eventos `peer.token.delta` carregam **contagens de
|
|
309
|
+
caracteres**, não o texto do provedor. Incluir o texto (redigido) é uma
|
|
310
|
+
opção explícita (`CROSS_REVIEW_STREAM_TEXT=1`), pensada para diagnósticos
|
|
311
|
+
locais confiáveis.
|
|
312
|
+
|
|
313
|
+
Para reduzir a chance de um prompt ser barrado pela moderação, o histórico
|
|
314
|
+
dos pares é resumido a partir de campos estruturados, em vez de reproduzir o
|
|
315
|
+
texto bruto do modelo. Se ainda assim um provedor barrar o prompt, o
|
|
316
|
+
orquestrador registra a classe da falha e tenta **uma vez** com um prompt
|
|
317
|
+
compacto e sanitizado — sem burlar a política do provedor.
|
|
318
|
+
|
|
319
|
+
### 4.7. Cache de prompt
|
|
320
|
+
|
|
321
|
+
O runtime integra-se ao cache de prompt de cada provedor compatível, emite um
|
|
322
|
+
evento uniforme `provider.cache.usage` e grava um `cache_manifest.json` por
|
|
323
|
+
sessão. Os modos de cache observados são: `auto` (OpenAI, DeepSeek, Grok),
|
|
324
|
+
`explicit` (Anthropic), `implicit` (Gemini) e `not_supported` (Perplexity,
|
|
325
|
+
cuja API Sonar não expõe superfície de cache). O cache pode ser desativado
|
|
326
|
+
globalmente com `CROSS_REVIEW_DISABLE_CACHE=true`.
|
|
327
|
+
|
|
328
|
+
### 4.8. As 28 ferramentas MCP
|
|
329
|
+
|
|
330
|
+
O servidor expõe 28 ferramentas. Agrupadas por finalidade:
|
|
331
|
+
|
|
332
|
+
**Descoberta e diagnóstico**
|
|
333
|
+
|
|
334
|
+
- `server_info` — informações e capacidades do servidor.
|
|
335
|
+
- `runtime_capabilities` — capacidades efetivas do runtime.
|
|
336
|
+
- `probe_peers` — testa a disponibilidade e a autenticação dos pares.
|
|
337
|
+
|
|
338
|
+
**Ciclo de vida da sessão**
|
|
339
|
+
|
|
340
|
+
- `session_init` — abre uma sessão.
|
|
341
|
+
- `session_list` — lista sessões (paginada, resumida por padrão).
|
|
342
|
+
- `session_read` — lê uma sessão completa.
|
|
343
|
+
- `session_finalize` — finaliza uma sessão.
|
|
344
|
+
- `session_recover_interrupted` — recupera sessões interrompidas.
|
|
345
|
+
- `session_sweep` — faz a varredura/limpeza de sessões.
|
|
346
|
+
|
|
347
|
+
**Execução da revisão**
|
|
348
|
+
|
|
349
|
+
- `ask_peers` — consulta os pares.
|
|
350
|
+
- `session_start_round` — inicia uma rodada (em segundo plano).
|
|
351
|
+
- `run_until_unanimous` — roda rodadas até a unanimidade (bloqueante).
|
|
352
|
+
- `session_start_unanimous` — roda até a unanimidade (em segundo plano).
|
|
353
|
+
- `session_cancel_job` — solicita o cancelamento cooperativo de um trabalho.
|
|
354
|
+
|
|
355
|
+
**Acompanhamento**
|
|
356
|
+
|
|
357
|
+
- `session_poll` — sonda o estado de uma sessão.
|
|
358
|
+
- `session_events` — lê o fluxo de eventos.
|
|
359
|
+
- `session_metrics` — métricas da sessão.
|
|
360
|
+
- `session_doctor` — diagnóstico e modo de reparo opcional.
|
|
361
|
+
- `session_report` — gera/lê o relatório da sessão.
|
|
362
|
+
- `session_check_convergence` — verifica a convergência.
|
|
363
|
+
|
|
364
|
+
**Evidência**
|
|
365
|
+
|
|
366
|
+
- `session_attach_evidence` — anexa evidência à sessão.
|
|
367
|
+
- `session_evidence_checklist_update` — atualiza a checklist de evidência.
|
|
368
|
+
- `session_evidence_judge_pass` — passada de juiz de evidência.
|
|
369
|
+
- `session_evidence_judge_consensus_pass` — passada de juiz por consenso.
|
|
370
|
+
- `session_judgment_precision_report` — relatório de precisão de julgamento.
|
|
371
|
+
|
|
372
|
+
**Governança e escalonamento**
|
|
373
|
+
|
|
374
|
+
- `contest_verdict` — contesta um veredito.
|
|
375
|
+
- `escalate_to_operator` — escala uma decisão ao operador humano.
|
|
376
|
+
- `regenerate_caller_tokens` — regenera os tokens de capacidade de quem
|
|
377
|
+
chama.
|
|
378
|
+
|
|
379
|
+
### 4.9. Armazenamento, eventos e relatórios
|
|
380
|
+
|
|
381
|
+
Cada sessão é durável: o **armazenamento de sessão** grava `meta.json`,
|
|
382
|
+
rascunhos por rodada e demais artefatos sob `data/sessions`. O **fluxo de
|
|
383
|
+
eventos** (`events.ndjson`) registra o andamento de forma append-only. Ao
|
|
384
|
+
final, o **relatório** (`session-report.md`) consolida convergência, falhas,
|
|
385
|
+
qualidade da decisão, custos e os eventos recentes. A **observabilidade**
|
|
386
|
+
grava ainda um log NDJSON por processo sob `data/logs`.
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## 5. Instalação
|
|
391
|
+
|
|
392
|
+
### Pré-requisitos
|
|
393
|
+
|
|
394
|
+
- **Node.js 22 ou superior**.
|
|
395
|
+
- Chaves de API dos provedores que se pretende usar (veja a seção 6).
|
|
396
|
+
|
|
397
|
+
### Instalação via npm
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
npm install -g @lcv-ideas-software/cross-review
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Alternativamente, pelo espelho do GitHub Packages:
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
npm install -g @lcv-ideas-software/cross-review --registry=https://npm.pkg.github.com
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
A instalação disponibiliza dois binários: `cross-review` (o servidor MCP) e
|
|
410
|
+
`cross-review-dashboard` (o painel local).
|
|
411
|
+
|
|
412
|
+
### Build a partir do código-fonte
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
npm install
|
|
416
|
+
npm run build
|
|
417
|
+
node dist/src/mcp/server.js
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Registro no host MCP
|
|
421
|
+
|
|
422
|
+
O servidor é iniciado pelo host MCP (por exemplo, um assistente compatível
|
|
423
|
+
com MCP), que o executa como um processo e se comunica via stdio. Configure o
|
|
424
|
+
**tempo limite de requisição do host para pelo menos 300 segundos**, já que
|
|
425
|
+
as rodadas de revisão real são longas.
|
|
426
|
+
|
|
427
|
+
### Testes de fumaça sem custo
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
# PowerShell
|
|
431
|
+
$env:CROSS_REVIEW_STUB = "1"
|
|
432
|
+
$env:CROSS_REVIEW_STUB_CONFIRMED = "1"
|
|
433
|
+
npm test
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Os testes de fumaça usam stubs e **não chamam** as APIs dos provedores.
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## 6. Configuração
|
|
441
|
+
|
|
442
|
+
A configuração é feita por **variáveis de ambiente** e por um **arquivo de
|
|
443
|
+
configuração central** (`config.json`) no diretório de dados, que elimina a
|
|
444
|
+
necessidade de declarar dezenas de variáveis em cada host.
|
|
445
|
+
|
|
446
|
+
### 6.1. Chaves de API
|
|
447
|
+
|
|
448
|
+
Cada provedor é autenticado por uma variável de ambiente. Exemplo
|
|
449
|
+
(PowerShell):
|
|
450
|
+
|
|
451
|
+
```powershell
|
|
452
|
+
[Environment]::SetEnvironmentVariable("OPENAI_API_KEY", "<chave>", "User")
|
|
453
|
+
[Environment]::SetEnvironmentVariable("ANTHROPIC_API_KEY", "<chave>", "User")
|
|
454
|
+
[Environment]::SetEnvironmentVariable("GEMINI_API_KEY", "<chave>", "User")
|
|
455
|
+
[Environment]::SetEnvironmentVariable("DEEPSEEK_API_KEY", "<chave>", "User")
|
|
456
|
+
[Environment]::SetEnvironmentVariable("GROK_API_KEY", "<chave>", "User")
|
|
457
|
+
[Environment]::SetEnvironmentVariable("PERPLEXITY_API_KEY", "<chave>", "User")
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Reinicie o terminal, o editor ou o host MCP após alterar variáveis de
|
|
461
|
+
ambiente. **Nunca** coloque chaves reais em arquivos `.env`, prompts, logs,
|
|
462
|
+
issues ou capturas de tela.
|
|
463
|
+
|
|
464
|
+
### 6.2. Controles financeiros (obrigatórios para chamadas pagas)
|
|
465
|
+
|
|
466
|
+
As chamadas pagas ficam **bloqueadas** até que tetos de orçamento e tabelas
|
|
467
|
+
de preço sejam configurados. O projeto, de propósito, **não embute preços**
|
|
468
|
+
de provedores, pois eles mudam com frequência. Se a configuração financeira
|
|
469
|
+
estiver ausente, a sessão é finalizada como `max-rounds` com o motivo
|
|
470
|
+
`financial_controls_missing` — **antes** de qualquer chamada paga.
|
|
471
|
+
|
|
472
|
+
```powershell
|
|
473
|
+
[Environment]::SetEnvironmentVariable("CROSS_REVIEW_MAX_SESSION_COST_USD", "20", "User")
|
|
474
|
+
[Environment]::SetEnvironmentVariable("CROSS_REVIEW_PREFLIGHT_MAX_ROUND_COST_USD", "20", "User")
|
|
475
|
+
[Environment]::SetEnvironmentVariable("CROSS_REVIEW_UNTIL_STOPPED_MAX_COST_USD", "20", "User")
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
As tabelas de preço são informadas por par, em dólares por milhão de tokens,
|
|
479
|
+
nas variáveis `CROSS_REVIEW_<PROVEDOR>_INPUT_USD_PER_MILLION` e
|
|
480
|
+
`CROSS_REVIEW_<PROVEDOR>_OUTPUT_USD_PER_MILLION`. O orçamento máximo de saída
|
|
481
|
+
é controlado por `CROSS_REVIEW_MAX_OUTPUT_TOKENS` (padrão `20000`).
|
|
482
|
+
|
|
483
|
+
### 6.3. Seleção de modelo e raciocínio
|
|
484
|
+
|
|
485
|
+
Use as variáveis de substituição apenas quando quiser fixar um modelo
|
|
486
|
+
diferente do canônico:
|
|
487
|
+
|
|
488
|
+
```powershell
|
|
489
|
+
[Environment]::SetEnvironmentVariable("CROSS_REVIEW_OPENAI_MODEL", "gpt-5.5", "User")
|
|
490
|
+
[Environment]::SetEnvironmentVariable("CROSS_REVIEW_OPENAI_REASONING_EFFORT", "xhigh", "User")
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### 6.4. Tempo limite e streaming
|
|
494
|
+
|
|
495
|
+
- `CROSS_REVIEW_TIMEOUT_MS` — tempo limite HTTP do lado do provedor; padrão
|
|
496
|
+
de 30 minutos.
|
|
497
|
+
- `CROSS_REVIEW_STREAM_EVENTS` e `CROSS_REVIEW_STREAM_TOKENS` — controlam o
|
|
498
|
+
streaming; ambos ativos por padrão.
|
|
499
|
+
- `CROSS_REVIEW_DISABLE_CACHE=true` — desativa o cache de prompt globalmente.
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## 7. Dependências
|
|
504
|
+
|
|
505
|
+
### Ambiente de execução
|
|
506
|
+
|
|
507
|
+
- **Node.js ≥ 22**, com módulos ECMAScript (`"type": "module"`).
|
|
508
|
+
- Escrito em **TypeScript**.
|
|
509
|
+
|
|
510
|
+
### Dependências de runtime
|
|
511
|
+
|
|
512
|
+
| Pacote | Função |
|
|
513
|
+
| --------------------------- | ---------------------------------------------------------- |
|
|
514
|
+
| `@modelcontextprotocol/sdk` | implementação do protocolo MCP |
|
|
515
|
+
| `@anthropic-ai/sdk` | cliente da API Anthropic (Claude) |
|
|
516
|
+
| `@google/genai` | cliente da API Google Gen AI (Gemini) |
|
|
517
|
+
| `openai` | cliente OpenAI — atende Codex, DeepSeek, Grok e Perplexity |
|
|
518
|
+
| `zod` | validação de esquemas de entrada |
|
|
519
|
+
| `pino` | logs estruturados (NDJSON) |
|
|
520
|
+
| `proper-lockfile` | trava de arquivo para concorrência segura |
|
|
521
|
+
|
|
522
|
+
### Dependências de desenvolvimento
|
|
523
|
+
|
|
524
|
+
Ferramentas de qualidade e build: `typescript`, `tsx`, `eslint` com
|
|
525
|
+
`typescript-eslint`, `@biomejs/biome`, `prettier` e os tipos auxiliares.
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## 8. Changelog resumido
|
|
530
|
+
|
|
531
|
+
O histórico completo está em [CHANGELOG.md](../CHANGELOG.md). A exibição
|
|
532
|
+
pública de versão segue o padrão `v00.00.00`; as versões do pacote npm seguem
|
|
533
|
+
SemVer. Marcos principais:
|
|
534
|
+
|
|
535
|
+
| Versão | Marco |
|
|
536
|
+
| ---------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
537
|
+
| `v2.0.0-alpha.0` | Primeiro servidor MCP, exclusivamente via API/SDK. |
|
|
538
|
+
| `v02.01.00` | Primeira versão estável do `cross-review`. |
|
|
539
|
+
| `v02.14.00` | Grok entra no painel de revisão. |
|
|
540
|
+
| `v02.21.00` | Cache de prompt entre provedores. |
|
|
541
|
+
| `v02.24.00` | Trava de proveniência de evidência. |
|
|
542
|
+
| `v02.25.00` | Terceiro modo de deliberação: `circular`. |
|
|
543
|
+
| `v03.00.00` | Perplexity entra como sexto par — o painel passa a sexteto. |
|
|
544
|
+
| `v03.01.00` | Arquivo de configuração central (`config.json`). |
|
|
545
|
+
| `v03.05.00` | Pré-checagem de evidência antes de chamadas pagas. |
|
|
546
|
+
| `v04.00.00` | Projeto renomeado de `cross-review-v2` para `cross-review`. |
|
|
547
|
+
| `v04.01.00` | Endurecimento de segurança: concorrência do armazenamento de sessão, superfície de DoS e redação de credenciais. |
|
|
548
|
+
| `v04.02.00` | Listagem de sessões paginada e semântica de cancelamento. |
|
|
549
|
+
| `v04.02.02` | Versão atual (pacote npm `4.2.2`), com refresh de providers, pins e rate cards. |
|
|
550
|
+
|
|
551
|
+
> Nota sobre o nome: até a versão 3.7.5, o projeto foi publicado como
|
|
552
|
+
> `@lcv-ideas-software/cross-review-v2`. A v4.0.0 é a primeira versão sob o
|
|
553
|
+
> nome canônico, mais curto, `@lcv-ideas-software/cross-review`, adotado
|
|
554
|
+
> depois que o projeto-companheiro `cross-review-v1` foi descontinuado e
|
|
555
|
+
> arquivado. As entradas históricas do changelog mantêm o nome anterior.
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## 9. Licença e recursos
|
|
560
|
+
|
|
561
|
+
- **Licença**: Apache-2.0. Consulte `LICENSE`, `NOTICE` e `THIRDPARTY.md`.
|
|
562
|
+
- **Site**: <https://cross-review.lcv.dev>
|
|
563
|
+
- **npm**: <https://www.npmjs.com/package/@lcv-ideas-software/cross-review>
|
|
564
|
+
- **GitHub**: <https://github.com/LCV-Ideas-Software/cross-review>
|
|
565
|
+
- **Divulgação de segurança**: consulte `SECURITY.md`.
|
|
566
|
+
- **Como contribuir**: consulte `CONTRIBUTING.md`.
|
|
567
|
+
- **Código de conduta**: consulte `CODE_OF_CONDUCT.md`.
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
<p align="center"><sub>Copyright © 2026 LCV Ideas & Software — Apache-2.0</sub></p>
|
package/docs/architecture.md
CHANGED
|
@@ -109,6 +109,8 @@ The peer adapters use the strongest official reasoning controls available for ea
|
|
|
109
109
|
- Anthropic uses adaptive thinking and omits raw thinking content from responses.
|
|
110
110
|
- Gemini enables thinking configuration for Gemini 3.x and the Gemini 2.5 fallback.
|
|
111
111
|
- DeepSeek enables Thinking Mode and follows the official multi-round guidance by resending the summarized session context in each stateless request.
|
|
112
|
+
- Grok runs the pinned `grok-4.3` model with explicit `reasoning.effort` clamped to xAI's supported values.
|
|
113
|
+
- Perplexity runs the pinned `sonar-reasoning-pro` model with an explicit `reasoning_effort` (`minimal`/`low`/`medium`/`high`); the shared effort scale is clamped down into that range.
|
|
112
114
|
|
|
113
115
|
Raw chain-of-thought is not persisted. Session continuity is represented through prompts, structured peer decisions, summaries and artifacts.
|
|
114
116
|
|
package/docs/caching.md
CHANGED
|
@@ -13,13 +13,14 @@ This document describes:
|
|
|
13
13
|
|
|
14
14
|
## Per-provider behavior matrix
|
|
15
15
|
|
|
16
|
-
| Peer (Provider)
|
|
17
|
-
|
|
|
18
|
-
| `codex` (OpenAI)
|
|
19
|
-
| `claude` (Anthropic)
|
|
20
|
-
| `gemini` (Google)
|
|
21
|
-
| `deepseek` (DeepSeek)
|
|
22
|
-
| `grok` (xAI)
|
|
16
|
+
| Peer (Provider) | Cache mode | Threshold | TTL surface | Telemetry source |
|
|
17
|
+
| ------------------------- | --------------- | --------------- | ---------------------------------------------- | --------------------------------------------------------------------- |
|
|
18
|
+
| `codex` (OpenAI) | `auto` | ~1k tokens | `prompt_cache_retention` (`in_memory` / `24h`) | `usage.prompt_tokens_details.cached_tokens` |
|
|
19
|
+
| `claude` (Anthropic) | `explicit` | ~4k tokens | `cache_control.ttl` (`5m` / `1h`) | `usage.cache_creation_input_tokens` + `usage.cache_read_input_tokens` |
|
|
20
|
+
| `gemini` (Google) | `implicit` | service-managed | n/a | `usageMetadata.cachedContentTokenCount` |
|
|
21
|
+
| `deepseek` (DeepSeek) | `auto` | service-managed | n/a | `usage.prompt_cache_hit_tokens` + `usage.prompt_cache_miss_tokens` |
|
|
22
|
+
| `grok` (xAI) | `auto` | service-managed | mirrors OpenAI | `usage.prompt_tokens_details.cached_tokens` |
|
|
23
|
+
| `perplexity` (Perplexity) | `not_supported` | n/a | n/a | none — Sonar API exposes no prompt-cache surface |
|
|
23
24
|
|
|
24
25
|
`mode` values follow the canonical `TokenUsage.cache_provider_mode` enum:
|
|
25
26
|
|
|
@@ -60,7 +61,7 @@ The cost layer (`src/core/cost.ts`) extends `CostEstimate` with two cache-relate
|
|
|
60
61
|
- `cache_savings_usd?: number` — populated when the rate card knows how to price the savings
|
|
61
62
|
- `cache_savings_unknown?: boolean` — set when cache telemetry was present but no rate card matched
|
|
62
63
|
|
|
63
|
-
Rate cards live in `
|
|
64
|
+
Rate cards live in `config.cost_rates`, loaded from environment variables or the central config file. Cache read/write rates use the same per-provider prefix as input/output, for example `CROSS_REVIEW_<PROVIDER>_CACHE_READ_USD_PER_MILLION` and `CROSS_REVIEW_<PROVIDER>_CACHE_WRITE_USD_PER_MILLION`. The rate card delta is computed from the configured rates: `(fresh_input_per_million - cached_input_per_million) × cache_read_tokens / 1e6`.
|
|
64
65
|
|
|
65
66
|
Adapters surface the read/write counts via `TokenUsage.cache_read_tokens` and `TokenUsage.cache_write_tokens`. The orchestrator reads them, emits a `provider.cache.usage` event, and appends a row to `<data_dir>/sessions/<session_id>/cache_manifest.json`.
|
|
66
67
|
|
package/docs/costs.md
CHANGED
|
@@ -18,6 +18,17 @@ The server records token usage returned by providers. Paid review/generation too
|
|
|
18
18
|
|
|
19
19
|
Set rates through Windows environment variables or the MCP host configuration before running paid calls. Values are USD per million tokens. Use current official provider pricing; this project intentionally does not ship default provider prices.
|
|
20
20
|
|
|
21
|
+
Current reference values verified on 2026-06-02 for the maintained model pins:
|
|
22
|
+
|
|
23
|
+
| Provider/model | Input | Output | Cached input / cache hit | Extended tier |
|
|
24
|
+
| -------------------------------- | ------- | ------ | ------------------------ | --------------------------------------------------------------------- |
|
|
25
|
+
| OpenAI `gpt-5.5` | `5` | `30` | `0.5` | `>272000` input tokens: input `10`, output `45` |
|
|
26
|
+
| Anthropic `claude-opus-4-8` | `5` | `25` | `0.5` | none |
|
|
27
|
+
| Gemini `gemini-2.5-pro` | `1.25` | `10` | `0.125` | `>200000` input tokens: input `2.5`, output `15`, cached input `0.25` |
|
|
28
|
+
| DeepSeek `deepseek-v4-pro` | `0.435` | `0.87` | `0.003625` | none |
|
|
29
|
+
| xAI `grok-4.3` | `1.25` | `2.5` | `0.2` | none |
|
|
30
|
+
| Perplexity `sonar-reasoning-pro` | `2` | `8` | n/a | request fee: low `6`, medium `10`, high `14` per 1000 requests |
|
|
31
|
+
|
|
21
32
|
```powershell
|
|
22
33
|
[Environment]::SetEnvironmentVariable("CROSS_REVIEW_MAX_SESSION_COST_USD", "20", "User")
|
|
23
34
|
[Environment]::SetEnvironmentVariable("CROSS_REVIEW_PREFLIGHT_MAX_ROUND_COST_USD", "20", "User")
|
|
@@ -33,7 +33,7 @@ The preflight trips **only** when **both** are true:
|
|
|
33
33
|
`tests? pass/passed/green`, `git diff --check`.
|
|
34
34
|
2. **Zero evidence markers** — the text contains none of: fenced code
|
|
35
35
|
blocks (` ``` `), `@@ -`/`@@ +` diff hunks, 7+ hex-char hashes,
|
|
36
|
-
`file.ext:NN` line refs,
|
|
36
|
+
`file.ext:NN` line refs, `$`/`>` command-prompt lines.
|
|
37
37
|
|
|
38
38
|
Mere keyword presence does **not** trip it. "I plan to write a patch"
|
|
39
39
|
or "here is the test plan" is a design review with legitimately no diff
|